diff --git a/.clang-format b/.clang-format index b01157b05183..3971384a3659 100644 --- a/.clang-format +++ b/.clang-format @@ -1,78 +1,207 @@ +# SPDX-License-Identifier: GPL-2.0 +# clang-format configuration file. Intended for clang-format >= 11. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# --- -BasedOnStyle: LLVM -Language: Cpp -IndentWidth: 8 -UseTab: Always -BreakBeforeBraces: Linux -AlwaysBreakBeforeMultilineStrings: true -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AllowShortFunctionsOnASingleLine: false -IndentCaseLabels: false -AlignEscapedNewlinesLeft: false +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +# FRR: Right +AlignEscapedNewlines: Right +AlignOperands: Align +# FRR: true AlignTrailingComments: true +# FRR: true +AlignConsecutiveMacros: true AllowAllParametersOfDeclarationOnNextLine: false -AlignAfterOpenBracket: true -SpaceAfterCStyleCast: false -MaxEmptyLinesToKeep: 2 -BreakBeforeBinaryOperators: None +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: false +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false -SortIncludes: false -IncludeCategories: - - Regex: '^(<|lib)' - Priority: 0 +ColumnLimit: 80 +# Linux: CommentPragmas: '^ IWYU pragma:' CommentPragmas: '\$(FRR|clippy)' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +# Some taken from: +# git grep -h '^#define [^[:space:]]*frr_(each|with)[^[:space:]]*(' ./ \ +# | sed "s,^#define \([^[:space:]]*frr_(each|with)[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +# and +# git grep -h '^#define [^[:space:]]*FOREACH[^[:space:]]*(' ./ +# | sed "s,^#define \([^[:space:]]*FOREACH[^)]*\)(.*, - '\1'," +# | LC_ALL=C sort -u ForEachMacros: - # lib - - frr_each - - frr_each_safe - - frr_each_from - - frr_rev_each - - frr_rev_each_safe - - frr_rev_each_from - - frr_with_mutex - - frr_with_privs - - LIST_FOREACH - - LIST_FOREACH_SAFE - - SLIST_FOREACH - - SLIST_FOREACH_SAFE - - SLIST_FOREACH_PREVPTR - - STAILQ_FOREACH - - STAILQ_FOREACH_SAFE - - TAILQ_FOREACH - - TAILQ_FOREACH_SAFE - - TAILQ_FOREACH_REVERSE - - TAILQ_FOREACH_REVERSE_SAFE - - RB_FOREACH - - RB_FOREACH_SAFE - - RB_FOREACH_REVERSE - - RB_FOREACH_REVERSE_SAFE - - SPLAY_FOREACH - - FOR_ALL_INTERFACES - - FOR_ALL_INTERFACES_ADDRESSES - - JSON_FOREACH - # libyang - - LY_FOR_KEYS - - LY_LIST_FOR - - LY_TREE_FOR - - LY_TREE_DFS_BEGIN - - LYD_TREE_DFS_BEGIN - # zebra - - RE_DEST_FOREACH_ROUTE - - RE_DEST_FOREACH_ROUTE_SAFE - - RNODE_FOREACH_RE - - RNODE_FOREACH_RE_SAFE - # bgpd - - UPDGRP_FOREACH_SUBGRP - - UPDGRP_FOREACH_SUBGRP_SAFE - - SUBGRP_FOREACH_PEER - - SUBGRP_FOREACH_PEER_SAFE - - SUBGRP_FOREACH_ADJ - - SUBGRP_FOREACH_ADJ_SAFE - - AF_FOREACH - - FOREACH_AFI_SAFI - - FOREACH_AFI_SAFI_NSF - - FOREACH_SAFI - # ospfd - - LSDB_LOOP + # lib: outliers: + - 'FOR_ALL_INTERFACES' + - 'FOR_ALL_INTERFACES_ADDRESSES' + # libyang outliers: + - 'LY_FOR_KEYS' + - 'LY_LIST_FOR' + - 'LY_TREE_FOR' + - 'LY_TREE_DFS_BEGIN' + - 'LYD_TREE_DFS_BEGIN' + # ospfd outliers: + - 'LSDB_LOOP' + # first git grep + - 'darr_foreach_p' + - 'darr_foreach_i' + - 'frr_each' + - 'frr_each_safe' + - 'frr_each_from' + - 'frr_rev_each' + - 'frr_rev_each_safe' + - 'frr_rev_each_from' + - 'frr_with_mutex' + - 'frr_with_privs' + # second git grep + - 'AF_FOREACH' + - 'FOREACH_ADAPTER_IN_LIST' + - 'FOREACH_AFI_SAFI' + - 'FOREACH_AFI_SAFI_NSF' + - 'FOREACH_BE_APPLY_BATCH_IN_LIST' + - 'FOREACH_BE_TXN_BATCH_IN_LIST' + - 'FOREACH_BE_TXN_IN_LIST' + - 'FOREACH_CMT_REC' + - 'FOREACH_MGMTD_BE_CLIENT_ID' + - 'FOREACH_MGMTD_DS_ID' + - 'FOREACH_SAFI' + - 'FOREACH_SESSION_IN_LIST' + - 'FOREACH_TXN_CFG_BATCH_IN_LIST' + - 'FOREACH_TXN_IN_LIST' + - 'FOREACH_TXN_REQ_IN_LIST' + - 'JSON_FOREACH' + - 'LIST_FOREACH' + - 'LIST_FOREACH_SAFE' + - 'RB_FOREACH' + - 'RB_FOREACH_REVERSE' + - 'RB_FOREACH_REVERSE_SAFE' + - 'RB_FOREACH_SAFE' + - 'RE_DEST_FOREACH_ROUTE' + - 'RE_DEST_FOREACH_ROUTE_SAFE' + - 'RNODE_FOREACH_RE' + - 'RNODE_FOREACH_RE_SAFE' + - 'SIMPLEQ_FOREACH' + - 'SIMPLEQ_FOREACH_SAFE' + - 'SLIST_FOREACH' + - 'SLIST_FOREACH_PREVPTR' + - 'SLIST_FOREACH_SAFE' + - 'SPLAY_FOREACH' + - 'STAILQ_FOREACH' + - 'STAILQ_FOREACH_SAFE' + - 'SUBGRP_FOREACH_ADJ' + - 'SUBGRP_FOREACH_ADJ_SAFE' + - 'SUBGRP_FOREACH_PEER' + - 'SUBGRP_FOREACH_PEER_SAFE' + - 'TAILQ_FOREACH' + - 'TAILQ_FOREACH_REVERSE' + - 'TAILQ_FOREACH_REVERSE_SAFE' + - 'TAILQ_FOREACH_SAFE' + - 'UPDGRP_FOREACH_SUBGRP' + - 'UPDGRP_FOREACH_SUBGRP_SAFE' + - 'XSIMPLEQ_FOREACH' + - 'XSIMPLEQ_FOREACH_SAFE' +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^(<|lib)' + Priority: 0 +## New: XXX whats it mean? +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +## Linux: MaxEmptyLinesToKeep: 1 +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +## Lowest Penalty Value wins. Values are used by clang-format to influence +## the brak decisions, it's a bit of voodoo magic though. +## Originally from linux which was "Taken from git's rules" +PenaltyBreakAssignment: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +# Don't break a string into multi-string-fragments +PenaltyBreakString: 1000 +# Allow going past the ColumnLimit to keep function arguments aligned +# with the open parenthesis. +PenaltyBreakBeforeFirstCallParameter: 1000 +# Try and stay under ColumnLimit, but not at the cost of incomprehensible code. +PenaltyExcessCharacter: 30 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000000..e0ea542fd287 --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203 \ No newline at end of file diff --git a/.github/commitlint.config.js b/.github/commitlint.config.js index bee965b814ea..2b420b6fbfd6 100644 --- a/.github/commitlint.config.js +++ b/.github/commitlint.config.js @@ -18,10 +18,12 @@ module.exports = { 'isisd', 'ldpd', 'lib', + 'mgmtd', 'multi', 'nhrpd', 'ospf6d', 'ospfd', + 'pathd', 'pbrd', 'pimd', 'pim6d', @@ -40,6 +42,5 @@ module.exports = { ], 'subject-empty': [2, 'never'], 'subject-full-stop': [2, 'never', '.'], - 'subject-case': [2, 'always', 'sentence-case'], }, }; diff --git a/.github/workflows/behind-base.yml b/.github/workflows/behind-base.yml new file mode 100644 index 000000000000..16b643477dd2 --- /dev/null +++ b/.github/workflows/behind-base.yml @@ -0,0 +1,28 @@ +name: Add rebase label if the branch is > 50 commits behind + +on: + pull_request_target: + types: [synchronize, opened, reopened, labeled, unlabeled] + +jobs: + behind: + if: github.repository == 'frrouting/frr' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Set custom variables + id: vars + run: | + echo "behind_by=$(git log --oneline origin/${{ github.base_ref }} ^${{ github.event.pull_request.head.sha }} | wc -l)" >> $GITHUB_OUTPUT + - name: Add rebase label if needed + if: ${{ steps.vars.outputs.behind_by > 50 }} + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: rebase diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index f5a2e7d360e6..96cd118f3ae1 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -10,8 +10,9 @@ on: - unlabeled jobs: - lint: - if: github.repository == 'frrouting/frr' + commitlint: + if: ${{ github.repository == 'frrouting/frr' }} && ${{ github.base_ref == 'refs/heads/master' }} + name: Check if the commits meet the requirements of the guidelines permissions: contents: read pull-requests: read @@ -26,3 +27,4 @@ jobs: uses: wagoid/commitlint-github-action@v5 with: configFile: .github/commitlint.config.js + helpURL: 'https://docs.frrouting.org/projects/dev-guide/en/latest/workflow.html#submitting-patches-and-enhancements' diff --git a/.github/workflows/conflicts.yml b/.github/workflows/conflicts.yml index 3f51e7f4de30..100f557e02e0 100644 --- a/.github/workflows/conflicts.yml +++ b/.github/workflows/conflicts.yml @@ -1,4 +1,4 @@ -name: Add a conflict label is PR needs to rebase +name: Add a conflict label if PR needs to rebase on: push: diff --git a/.gitignore b/.gitignore index fb40ee52fed9..a66e3ccd3c6a 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,7 @@ {arch} build .cache +.dir-locals.el .msg .rebase-* *~ @@ -105,6 +106,7 @@ GSYMS GRTAGS GPATH compile_commands.json +.ccls .ccls-cache .dirstamp refix diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 000000000000..f238bf7ea137 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +profile = black diff --git a/.pylintrc b/.pylintrc index 83a719748131..ba9430ba6b37 100644 --- a/.pylintrc +++ b/.pylintrc @@ -2,5 +2,8 @@ init-hook="import sys; sys.path.insert(0, '..')" signature-mutators=common_config.retry,retry +[FORMAT] +max-line-length = 88 + [MESSAGES CONTROL] disable=I,C,R,W diff --git a/Makefile.am b/Makefile.am index 44d2ab8e72b8..f56e1b8e0bac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -155,6 +155,21 @@ $(AUTOMAKE_DUMMY)install-moduleLTLIBRARIES: install-libLTLIBRARIES $(AUTOMAKE_DUMMY)install-binPROGRAMS: install-libLTLIBRARIES $(AUTOMAKE_DUMMY)install-sbinPROGRAMS: install-libLTLIBRARIES +# Include default rules to compile protobuf message sources +SUFFIXES += .proto .pb-c.c .pb-c.h + +# Rules + +AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V)) +am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY)) +am__v_PROTOC_C_0 = @echo " PROTOC_C" $@; +am__v_PROTOC_C_1 = + +%.pb-c.c %.pb-c.h : %.proto + $(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir) --c_out=$(top_builddir) $^ + $(AM_V_GEN)$(SED) -i -e '1i\ + #include "config.h"' $@ + include doc/subdir.am include doc/user/subdir.am include doc/manpages/subdir.am @@ -169,6 +184,8 @@ include fpm/subdir.am include grpc/subdir.am include tools/subdir.am +include mgmtd/subdir.am + include bgpd/subdir.am include bgpd/rfp-example/librfp/subdir.am include bgpd/rfp-example/rfptest/subdir.am @@ -207,6 +224,7 @@ rc_SCRIPTS = \ pkgsrc/ripd.sh \ pkgsrc/ripngd.sh \ pkgsrc/zebra.sh \ + pkgsrc/mgmtd.sh \ # end endif @@ -244,6 +262,7 @@ EXTRA_DIST += \ snapcraft/helpers \ snapcraft/snap \ babeld/Makefile \ + mgmtd/Makefile \ bgpd/Makefile \ bgpd/rfp-example/librfp/Makefile \ bgpd/rfp-example/rfptest/Makefile \ @@ -321,7 +340,7 @@ redistclean: $(MAKE) distclean CONFIG_CLEAN_FILES="$(filter-out $(EXTRA_DIST), $(CONFIG_CLEAN_FILES))" indent: - tools/indent.py `find sharpd bgpd eigrpd include isisd lib nhrpd ospf6d ospfd pimd qpb ripd vtysh zebra -name '*.[ch]' | grep -v include/linux` + tools/indent.py `find sharpd bgpd mgmtd eigrpd include isisd lib nhrpd ospf6d ospfd pimd qpb ripd vtysh zebra -name '*.[ch]' | grep -v include/linux` if HAVE_GCOV diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index fef7a61ccc89..fd3c02f47e69 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -18,7 +18,8 @@ makedepends="ncurses-dev net-snmp-dev gawk texinfo perl ncurses-libs ncurses-terminfo ncurses-terminfo-base patch pax-utils pcre2 perl pkgconf python3 python3-dev readline readline-dev sqlite-libs pcre2-dev squashfs-tools sudo tar texinfo xorriso xz-libs py-pip rtrlib rtrlib-dev - py3-sphinx elfutils elfutils-dev libyang-dev" + py3-sphinx elfutils elfutils-dev libyang-dev protobuf-c-compiler protobuf-c-dev + lua5.3-dev lua5.3" checkdepends="pytest py-setuptools" install="$pkgname.pre-install $pkgname.pre-deinstall $pkgname.post-deinstall" subpackages="$pkgname-dev $pkgname-doc $pkgname-dbg" @@ -47,7 +48,8 @@ build() { --enable-vty-group=frrvty \ --enable-user=$_user \ --enable-group=$_user \ - --enable-pcre2posix + --enable-pcre2posix \ + --enable-scripting make -j $(nproc) } diff --git a/babeld/babel_main.c b/babeld/babel_main.c index 0869c120ac82..b6126d5b7d19 100644 --- a/babeld/babel_main.c +++ b/babeld/babel_main.c @@ -8,7 +8,7 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek #include "getopt.h" #include "if.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "privs.h" #include "sigevent.h" #include "lib/version.h" @@ -37,7 +37,7 @@ static void babel_exit_properly(void); static void babel_save_state_file(void); -struct thread_master *master; /* quagga's threads handler */ +struct event_loop *master; /* quagga's threads handler */ struct timeval babel_now; /* current time */ unsigned char myid[8]; /* unique id (mac address of an interface) */ diff --git a/babeld/babel_main.h b/babeld/babel_main.h index 659ddc70cf4b..0f9792b185ea 100644 --- a/babeld/babel_main.h +++ b/babeld/babel_main.h @@ -9,7 +9,7 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek #include "vty.h" extern struct timeval babel_now; /* current time */ -extern struct thread_master *master; /* quagga's threads handler */ +extern struct event_loop *master; /* quagga's threads handler */ extern int debug; extern int resend_delay; diff --git a/babeld/babeld.c b/babeld/babeld.c index 510415593663..ebf8474f4ccf 100644 --- a/babeld/babeld.c +++ b/babeld/babeld.c @@ -37,11 +37,11 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek DEFINE_MGROUP(BABELD, "babeld"); DEFINE_MTYPE_STATIC(BABELD, BABEL, "Babel Structure"); -static void babel_init_routing_process(struct thread *thread); +static void babel_init_routing_process(struct event *thread); static void babel_get_myid(void); static void babel_initial_noise(void); -static void babel_read_protocol(struct thread *thread); -static void babel_main_loop(struct thread *thread); +static void babel_read_protocol(struct event *thread); +static void babel_main_loop(struct event *thread); static void babel_set_timer(struct timeval *timeout); static void babel_fill_with_next_timeout(struct timeval *tv); static void @@ -108,14 +108,14 @@ babel_config_write (struct vty *vty) /* list redistributed protocols */ for (afi = AFI_IP; afi <= AFI_IP6; afi++) { for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (i != zclient->redist_default && - vrf_bitmap_check (zclient->redist[afi][i], VRF_DEFAULT)) { - vty_out (vty, " redistribute %s %s\n", - (afi == AFI_IP) ? "ipv4" : "ipv6", - zebra_route_string(i)); - lines++; - } - } + if (i != zclient->redist_default && + vrf_bitmap_check(&zclient->redist[afi][i], VRF_DEFAULT)) { + vty_out(vty, " redistribute %s %s\n", + (afi == AFI_IP) ? "ipv4" : "ipv6", + zebra_route_string(i)); + lines++; + } + } } lines += config_write_distribute (vty, babel_routing_process->distribute_ctx); @@ -148,9 +148,11 @@ babel_create_routing_process (void) } /* Threads. */ - thread_add_read(master, babel_read_protocol, NULL, protocol_socket, &babel_routing_process->t_read); + event_add_read(master, babel_read_protocol, NULL, protocol_socket, + &babel_routing_process->t_read); /* wait a little: zebra will announce interfaces, addresses, routes... */ - thread_add_timer_msec(master, babel_init_routing_process, NULL, 200L, &babel_routing_process->t_update); + event_add_timer_msec(master, babel_init_routing_process, NULL, 200L, + &babel_routing_process->t_update); /* Distribute list install. */ babel_routing_process->distribute_ctx = distribute_list_ctx_create (vrf_lookup_by_id(VRF_DEFAULT)); @@ -163,7 +165,7 @@ babel_create_routing_process (void) } /* thread reading entries form others babel daemons */ -static void babel_read_protocol(struct thread *thread) +static void babel_read_protocol(struct event *thread) { int rc; struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -193,13 +195,14 @@ static void babel_read_protocol(struct thread *thread) } /* re-add thread */ - thread_add_read(master, &babel_read_protocol, NULL, protocol_socket, &babel_routing_process->t_read); + event_add_read(master, &babel_read_protocol, NULL, protocol_socket, + &babel_routing_process->t_read); } /* Zebra will give some information, especially about interfaces. This function must be call with a litte timeout wich may give zebra the time to do his job, making these inits have sense. */ -static void babel_init_routing_process(struct thread *thread) +static void babel_init_routing_process(struct event *thread) { myseqno = (frr_weak_random() & 0xFFFF); babel_get_myid(); @@ -303,15 +306,15 @@ babel_clean_routing_process(void) babel_interface_close_all(); /* cancel events */ - thread_cancel(&babel_routing_process->t_read); - thread_cancel(&babel_routing_process->t_update); + event_cancel(&babel_routing_process->t_read); + event_cancel(&babel_routing_process->t_update); distribute_list_delete(&babel_routing_process->distribute_ctx); XFREE(MTYPE_BABEL, babel_routing_process); } /* Function used with timeout. */ -static void babel_main_loop(struct thread *thread) +static void babel_main_loop(struct event *thread) { struct timeval tv; struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); @@ -441,37 +444,39 @@ babel_fill_with_next_timeout(struct timeval *tv) #if (defined NO_DEBUG) #define printIfMin(a,b,c,d) #else -#define printIfMin(a,b,c,d) \ - if (UNLIKELY(debug & BABEL_DEBUG_TIMEOUT)) {printIfMin(a,b,c,d);} - - struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); - struct interface *ifp = NULL; +#define printIfMin(a, b, c, d) \ + if (unlikely(debug & BABEL_DEBUG_TIMEOUT)) { \ + printIfMin(a, b, c, d); \ + } - *tv = check_neighbours_timeout; - printIfMin(tv, 0, "check_neighbours_timeout", NULL); - timeval_min_sec(tv, expiry_time); - printIfMin(tv, 1, "expiry_time", NULL); - timeval_min_sec(tv, source_expiry_time); - printIfMin(tv, 1, "source_expiry_time", NULL); - timeval_min(tv, &resend_time); - printIfMin(tv, 1, "resend_time", NULL); - FOR_ALL_INTERFACES(vrf, ifp) { - babel_interface_nfo *babel_ifp = NULL; - if(!if_up(ifp)) - continue; - babel_ifp = babel_get_if_nfo(ifp); - timeval_min(tv, &babel_ifp->flush_timeout); - printIfMin(tv, 1, "flush_timeout", ifp->name); - timeval_min(tv, &babel_ifp->hello_timeout); - printIfMin(tv, 1, "hello_timeout", ifp->name); - timeval_min(tv, &babel_ifp->update_timeout); - printIfMin(tv, 1, "update_timeout", ifp->name); - timeval_min(tv, &babel_ifp->update_flush_timeout); - printIfMin(tv, 1, "update_flush_timeout",ifp->name); - } - timeval_min(tv, &unicast_flush_timeout); - printIfMin(tv, 1, "unicast_flush_timeout", NULL); - printIfMin(tv, 2, NULL, NULL); + struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); + struct interface *ifp = NULL; + + *tv = check_neighbours_timeout; + printIfMin(tv, 0, "check_neighbours_timeout", NULL); + timeval_min_sec(tv, expiry_time); + printIfMin(tv, 1, "expiry_time", NULL); + timeval_min_sec(tv, source_expiry_time); + printIfMin(tv, 1, "source_expiry_time", NULL); + timeval_min(tv, &resend_time); + printIfMin(tv, 1, "resend_time", NULL); + FOR_ALL_INTERFACES (vrf, ifp) { + babel_interface_nfo *babel_ifp = NULL; + if (!if_up(ifp)) + continue; + babel_ifp = babel_get_if_nfo(ifp); + timeval_min(tv, &babel_ifp->flush_timeout); + printIfMin(tv, 1, "flush_timeout", ifp->name); + timeval_min(tv, &babel_ifp->hello_timeout); + printIfMin(tv, 1, "hello_timeout", ifp->name); + timeval_min(tv, &babel_ifp->update_timeout); + printIfMin(tv, 1, "update_timeout", ifp->name); + timeval_min(tv, &babel_ifp->update_flush_timeout); + printIfMin(tv, 1, "update_flush_timeout", ifp->name); + } + timeval_min(tv, &unicast_flush_timeout); + printIfMin(tv, 1, "unicast_flush_timeout", NULL); + printIfMin(tv, 2, NULL, NULL); #undef printIfMin #endif } @@ -482,8 +487,9 @@ static void babel_set_timer(struct timeval *timeout) { long msecs = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; - thread_cancel(&(babel_routing_process->t_update)); - thread_add_timer_msec(master, babel_main_loop, NULL, msecs, &babel_routing_process->t_update); + event_cancel(&(babel_routing_process->t_update)); + event_add_timer_msec(master, babel_main_loop, NULL, msecs, + &babel_routing_process->t_update); } void diff --git a/babeld/babeld.h b/babeld/babeld.h index a9ffb7ee435a..619550f651f7 100644 --- a/babeld/babeld.h +++ b/babeld/babeld.h @@ -26,12 +26,8 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek #if defined(__GNUC__) && (__GNUC__ >= 3) #define ATTRIBUTE(x) __attribute__ (x) -#define LIKELY(_x) __builtin_expect(!!(_x), 1) -#define UNLIKELY(_x) __builtin_expect(!!(_x), 0) #else #define ATTRIBUTE(x) /**/ -#define LIKELY(_x) !!(_x) -#define UNLIKELY(_x) !!(_x) #endif #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) @@ -84,8 +80,8 @@ Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek struct babel { /* Babel threads. */ - struct thread *t_read; /* on Babel protocol's socket */ - struct thread *t_update; /* timers */ + struct event *t_read; /* on Babel protocol's socket */ + struct event *t_update; /* timers */ /* distribute_ctx */ struct distribute_ctx *distribute_ctx; }; diff --git a/babeld/kernel.c b/babeld/kernel.c index f89fe268df37..4fe5bcfea68c 100644 --- a/babeld/kernel.c +++ b/babeld/kernel.c @@ -29,7 +29,7 @@ Copyright 2011, 2012 by Matthieu Boutier and Juliusz Chroboczek #include "command.h" #include "vty.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "nexthop.h" #include "util.h" diff --git a/babeld/message.c b/babeld/message.c index 687f76844694..f8549329c603 100644 --- a/babeld/message.c +++ b/babeld/message.c @@ -48,6 +48,13 @@ static const unsigned char tlv_min_length[MESSAGE_MAX + 1] = [ MESSAGE_MH_REQUEST ] = 14, }; +/* Checks whether an AE exists or must be silently ignored */ +static bool +known_ae(int ae) +{ + return ae <= 4; +} + /* Parse a network prefix, encoded in the somewhat baroque compressed representation used by Babel. Return the number of bytes parsed. */ static int @@ -276,6 +283,62 @@ parse_ihu_subtlv(const unsigned char *a, int alen, return ret; } +static int +parse_request_subtlv(int ae, const unsigned char *a, int alen, + unsigned char *src_prefix, unsigned char *src_plen) +{ + int type, len, i = 0; + int have_src_prefix = 0; + + while(i < alen) { + type = a[0]; + if(type == SUBTLV_PAD1) { + i++; + continue; + } + + if(i + 2 > alen) + goto fail; + + len = a[i + 1]; + if(i + 2 + len > alen) + goto fail; + + if(type == SUBTLV_PADN) { + /* Nothing to do. */ + } else if(type == SUBTLV_SOURCE_PREFIX) { + int rc; + if(len < 1) + goto fail; + if(a[i + 2] == 0) + goto fail; + if(have_src_prefix != 0) + goto fail; + rc = network_prefix(ae, a[i + 2], 0, a + i + 3, NULL, + len - 1, src_prefix); + if(rc < 0) + goto fail; + if(ae==1) + *src_plen = a[i + 2] + 96; + else + *src_plen = a[i + 2]; + have_src_prefix = 1; + } else { + debugf(BABEL_DEBUG_COMMON,"Received unknown%s Route Request sub-TLV %d.", + ((type & 0x80) != 0) ? " mandatory" : "", type); + if((type & 0x80) != 0) + return -1; + } + + i += len + 2; + } + return 1; + + fail: + flog_err(EC_BABEL_PACKET, "Received truncated sub-TLV on Route Request."); + return -1; +} + static int network_address(int ae, const unsigned char *a, unsigned int len, unsigned char *a_r) @@ -413,18 +476,6 @@ parse_packet(const unsigned char *from, struct interface *ifp, #define BABEL_UNICAST_HELLO 0x8000 DO_NTOHS(flags, message + 2); - /* - * RFC 8966 4.6.5 - * All other bits MUST be sent as a 0 and silently - * ignored on reception - */ - if (CHECK_FLAG(flags, ~BABEL_UNICAST_HELLO)) { - debugf(BABEL_DEBUG_COMMON, - "Received Hello from %s on %s that does not have all 0's in the unused section of flags, ignoring", - format_address(from), ifp->name); - continue; - } - /* * RFC 8966 Appendix F * TL;DR -> Please ignore Unicast hellos until FRR's @@ -434,7 +485,7 @@ parse_packet(const unsigned char *from, struct interface *ifp, debugf(BABEL_DEBUG_COMMON, "Received Unicast Hello from %s on %s that FRR is not prepared to understand yet", format_address(from), ifp->name); - continue; + goto done; } DO_NTOHS(seqno, message + 4); @@ -452,7 +503,7 @@ parse_packet(const unsigned char *from, struct interface *ifp, debugf(BABEL_DEBUG_COMMON, "Received hello from %s on %s should be ignored as that this version of FRR does not know how to properly handle interval == 0", format_address(from), ifp->name); - continue; + goto done; } changed = update_neighbour(neigh, seqno, interval); @@ -505,7 +556,7 @@ parse_packet(const unsigned char *from, struct interface *ifp, int rc; rc = network_address(message[2], message + 4, len - 2, nh); - if(rc < 0) { + if(rc <= 0) { have_v4_nh = 0; have_v6_nh = 0; goto fail; @@ -624,8 +675,14 @@ parse_packet(const unsigned char *from, struct interface *ifp, interval, neigh, nh, channels, channels_len(channels)); } else if(type == MESSAGE_REQUEST) { - unsigned char prefix[16], plen; - int rc; + unsigned char prefix[16], src_prefix[16], plen, src_plen; + int rc, is_ss; + if(len < 2) goto fail; + if(!known_ae(message[2])) { + debugf(BABEL_DEBUG_COMMON,"Received request with unknown AE %d. Ignoring.", + message[2]); + goto done; + } rc = network_prefix(message[2], message[3], 0, message + 4, NULL, len - 2, prefix); if(rc < 0) goto fail; @@ -633,8 +690,26 @@ parse_packet(const unsigned char *from, struct interface *ifp, debugf(BABEL_DEBUG_COMMON,"Received request for %s from %s on %s.", message[2] == 0 ? "any" : format_prefix(prefix, plen), format_address(from), ifp->name); + if(message[2] == 1) { + v4tov6(src_prefix, zeroes); + src_plen = 96; + } else { + memcpy(src_prefix, zeroes, 16); + src_plen = 0; + } + rc = parse_request_subtlv(message[2], message + 4 + rc, + len - 2 - rc, src_prefix, &src_plen); + if(rc < 0) + goto done; + is_ss = !is_default(src_prefix, src_plen); if(message[2] == 0) { struct babel_interface *neigh_ifp =babel_get_if_nfo(neigh->ifp); + if(is_ss) { + /* Wildcard requests don't carry a source prefix. */ + flog_err(EC_BABEL_PACKET, + "Received source-specific wildcard request."); + goto done; + } /* If a neighbour is requesting a full route dump from us, we might as well send it an IHU. */ send_ihu(neigh, NULL); @@ -656,7 +731,7 @@ parse_packet(const unsigned char *from, struct interface *ifp, DO_NTOHS(seqno, message + 4); rc = network_prefix(message[2], message[3], 0, message + 16, NULL, len - 14, prefix); - if(rc < 0) goto fail; + if(rc <= 0) goto fail; plen = message[3] + (message[2] == 1 ? 96 : 0); debugf(BABEL_DEBUG_COMMON,"Received request (%d) for %s from %s on %s (%s, %d).", message[6], diff --git a/babeld/message.h b/babeld/message.h index 0797a5a77de4..7cf062a88824 100644 --- a/babeld/message.h +++ b/babeld/message.h @@ -34,6 +34,7 @@ Copyright (c) 2007, 2008 by Juliusz Chroboczek #define SUBTLV_PADN 1 #define SUBTLV_DIVERSITY 2 /* Also known as babelz. */ #define SUBTLV_TIMESTAMP 3 /* Used to compute RTT. */ +#define SUBTLV_SOURCE_PREFIX 128 /* Source-specific routing. */ #define SUBTLV_MANDATORY 0x80 extern unsigned short myseqno; diff --git a/babeld/util.h b/babeld/util.h index 5e5843543a22..ddc6a70d43b5 100644 --- a/babeld/util.h +++ b/babeld/util.h @@ -104,6 +104,12 @@ void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src); int daemonise(void); extern const unsigned char v4prefix[16]; +static inline bool +is_default(const unsigned char *prefix, int plen) +{ + return plen == 0 || (plen == 96 && v4mapped(prefix)); +} + /* If debugging is disabled, we want to avoid calling format_address for every omitted debugging message. So debug is a macro. But vararg macros are not portable. */ @@ -122,10 +128,11 @@ extern const unsigned char v4prefix[16]; #define BABEL_DEBUG_ROUTE (1 << 5) #define BABEL_DEBUG_ALL (0xFFFF) -#define debugf(level, ...) \ -do { \ -if(UNLIKELY(debug & level)) zlog_debug(__VA_ARGS__); \ -} while(0) +#define debugf(level, ...) \ + do { \ + if (unlikely(debug & level)) \ + zlog_debug(__VA_ARGS__); \ + } while (0) #endif /* NO_DEBUG */ diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 2586c0b99cad..3096f47d5c2b 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -618,25 +618,25 @@ struct bfd_session *ptm_bfd_sess_find(struct bfd_pkt *cp, return bfd_key_lookup(key); } -void bfd_xmt_cb(struct thread *t) +void bfd_xmt_cb(struct event *t) { - struct bfd_session *bs = THREAD_ARG(t); + struct bfd_session *bs = EVENT_ARG(t); ptm_bfd_xmt_TO(bs, 0); } -void bfd_echo_xmt_cb(struct thread *t) +void bfd_echo_xmt_cb(struct event *t) { - struct bfd_session *bs = THREAD_ARG(t); + struct bfd_session *bs = EVENT_ARG(t); if (bs->echo_xmt_TO > 0) ptm_bfd_echo_xmt_TO(bs); } /* Was ptm_bfd_detect_TO() */ -void bfd_recvtimer_cb(struct thread *t) +void bfd_recvtimer_cb(struct event *t) { - struct bfd_session *bs = THREAD_ARG(t); + struct bfd_session *bs = EVENT_ARG(t); switch (bs->ses_state) { case PTM_BFD_INIT: @@ -647,9 +647,9 @@ void bfd_recvtimer_cb(struct thread *t) } /* Was ptm_bfd_echo_detect_TO() */ -void bfd_echo_recvtimer_cb(struct thread *t) +void bfd_echo_recvtimer_cb(struct event *t) { - struct bfd_session *bs = THREAD_ARG(t); + struct bfd_session *bs = EVENT_ARG(t); switch (bs->ses_state) { case PTM_BFD_INIT: @@ -1957,23 +1957,23 @@ static int bfd_vrf_enable(struct vrf *vrf) bvrf->bg_echov6 = bp_echov6_socket(vrf); if (!bvrf->bg_ev[0] && bvrf->bg_shop != -1) - thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, - &bvrf->bg_ev[0]); + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_shop, + &bvrf->bg_ev[0]); if (!bvrf->bg_ev[1] && bvrf->bg_mhop != -1) - thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop, - &bvrf->bg_ev[1]); + event_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]); + event_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]); + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_mhop6, + &bvrf->bg_ev[3]); if (!bvrf->bg_ev[4] && bvrf->bg_echo != -1) - thread_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echo, - &bvrf->bg_ev[4]); + event_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]); + event_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); @@ -1999,12 +1999,12 @@ static int bfd_vrf_disable(struct vrf *vrf) 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]); + EVENT_OFF(bvrf->bg_ev[0]); + EVENT_OFF(bvrf->bg_ev[1]); + EVENT_OFF(bvrf->bg_ev[2]); + EVENT_OFF(bvrf->bg_ev[3]); + EVENT_OFF(bvrf->bg_ev[4]); + EVENT_OFF(bvrf->bg_ev[5]); /* Close all descriptors. */ socket_close(&bvrf->bg_echo); diff --git a/bfdd/bfd.h b/bfdd/bfd.h index 97e45bb2c2c5..6c5a1e921618 100644 --- a/bfdd/bfd.h +++ b/bfdd/bfd.h @@ -32,6 +32,11 @@ DECLARE_MGROUP(BFDD); DECLARE_MTYPE(BFDD_CONTROL); DECLARE_MTYPE(BFDD_NOTIFICATION); +/* bfd Authentication Type. */ +#define BFD_AUTH_NULL 0 +#define BFD_AUTH_SIMPLE 1 +#define BFD_AUTH_CRYPTOGRAPHIC 2 + struct bfd_timers { uint32_t desired_min_tx; uint32_t required_min_rx; @@ -60,6 +65,15 @@ struct bfd_pkt { struct bfd_timers timers; }; +/* + * Format of authentification. + */ +struct bfd_auth { + uint8_t type; + uint8_t length; +}; + + /* * Format of Echo packet. */ @@ -79,7 +93,7 @@ struct bfd_echo_pkt { /* Macros for manipulating control packets */ -#define BFD_VERMASK 0x03 +#define BFD_VERMASK 0x07 #define BFD_DIAGMASK 0x1F #define BFD_GETVER(diag) ((diag >> 5) & BFD_VERMASK) #define BFD_SETVER(diag, val) ((diag) |= (val & BFD_VERMASK) << 5) @@ -265,12 +279,12 @@ struct bfd_session { struct bfd_config_timers timers; struct bfd_timers cur_timers; uint64_t detect_TO; - struct thread *echo_recvtimer_ev; - struct thread *recvtimer_ev; + struct event *echo_recvtimer_ev; + struct event *recvtimer_ev; uint64_t xmt_TO; uint64_t echo_xmt_TO; - struct thread *xmttimer_ev; - struct thread *echo_xmttimer_ev; + struct event *xmttimer_ev; + struct event *echo_xmttimer_ev; uint64_t echo_detect_TO; /* software object state */ @@ -401,8 +415,8 @@ struct bfd_control_socket { TAILQ_ENTRY(bfd_control_socket) bcs_entry; int bcs_sd; - struct thread *bcs_ev; - struct thread *bcs_outev; + struct event *bcs_ev; + struct event *bcs_outev; struct bcqueue bcs_bcqueue; /* Notification data */ @@ -422,7 +436,7 @@ int control_init(const char *path); void control_shutdown(void); int control_notify(struct bfd_session *bs, uint8_t notify_state); int control_notify_config(const char *op, struct bfd_session *bs); -void control_accept(struct thread *t); +void control_accept(struct event *t); /* @@ -439,7 +453,7 @@ struct bfd_vrf_global { int bg_echov6; struct vrf *vrf; - struct thread *bg_ev[6]; + struct event *bg_ev[6]; }; /* Forward declaration of data plane context struct. */ @@ -448,7 +462,7 @@ TAILQ_HEAD(dplane_queue, bfd_dplane_ctx); struct bfd_global { int bg_csock; - struct thread *bg_csockev; + struct event *bg_csockev; struct bcslist bg_bcslist; struct pllist bg_pllist; @@ -466,7 +480,7 @@ struct bfd_global { /* Distributed BFD items. */ bool bg_use_dplane; int bg_dplane_sock; - struct thread *bg_dplane_sockev; + struct event *bg_dplane_sockev; struct dplane_queue bg_dplaneq; /* Debug options. */ @@ -553,7 +567,7 @@ void ptm_bfd_snd(struct bfd_session *bfd, int fbit); void ptm_bfd_echo_snd(struct bfd_session *bfd); void ptm_bfd_echo_fp_snd(struct bfd_session *bfd); -void bfd_recv_cb(struct thread *t); +void bfd_recv_cb(struct event *t); /* @@ -561,7 +575,7 @@ void bfd_recv_cb(struct thread *t); * * Contains the code related with event loop. */ -typedef void (*bfd_ev_cb)(struct thread *t); +typedef void (*bfd_ev_cb)(struct event *t); void bfd_recvtimer_update(struct bfd_session *bs); void bfd_echo_recvtimer_update(struct bfd_session *bs); @@ -686,12 +700,12 @@ 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; +extern struct event_loop *master; -void bfd_recvtimer_cb(struct thread *t); -void bfd_echo_recvtimer_cb(struct thread *t); -void bfd_xmt_cb(struct thread *t); -void bfd_echo_xmt_cb(struct thread *t); +void bfd_recvtimer_cb(struct event *t); +void bfd_echo_recvtimer_cb(struct event *t); +void bfd_xmt_cb(struct event *t); +void bfd_echo_xmt_cb(struct event *t); extern struct in6_addr zero_addr; diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index 7be235326c5e..0c72ee75816d 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -140,7 +140,7 @@ int _ptm_bfd_send(struct bfd_session *bs, uint16_t *port, const void *data, * sizeof(*pkt) * * ip - * IP address that pkt will be transmitted from and too. + * IP address that pkt will be transmitted from and to. * * Returns: * Checksum in network byte order. @@ -481,12 +481,6 @@ ssize_t bfd_recv_ipv4_fp(int sd, uint8_t *msgbuf, size_t msgbuflen, *ttl = ip->ttl; if (*ttl != 254) { - /* Echo should be looped in peer's forwarding plane, but it also - * comes up to BFD so silently drop it - */ - if (ip->daddr == ip->saddr) - return -1; - if (bglobal.debug_network) zlog_debug("%s: invalid TTL: %u", __func__, *ttl); return -1; @@ -707,29 +701,29 @@ ssize_t bfd_recv_ipv6(int sd, uint8_t *msgbuf, size_t msgbuflen, uint8_t *ttl, static void bfd_sd_reschedule(struct bfd_vrf_global *bvrf, int sd) { 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]); + EVENT_OFF(bvrf->bg_ev[0]); + event_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]); + EVENT_OFF(bvrf->bg_ev[1]); + event_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]); + EVENT_OFF(bvrf->bg_ev[2]); + event_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]); + EVENT_OFF(bvrf->bg_ev[3]); + event_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]); + EVENT_OFF(bvrf->bg_ev[4]); + event_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]); + EVENT_OFF(bvrf->bg_ev[5]); + event_add_read(master, bfd_recv_cb, bvrf, bvrf->bg_echov6, + &bvrf->bg_ev[5]); } } @@ -774,9 +768,40 @@ static void cp_debug(bool mhop, struct sockaddr_any *peer, mhop ? "yes" : "no", peerstr, localstr, portstr, vrfstr); } -void bfd_recv_cb(struct thread *t) +static bool bfd_check_auth(const struct bfd_session *bfd, + const struct bfd_pkt *cp) +{ + if (CHECK_FLAG(cp->flags, BFD_ABIT)) { + /* RFC5880 4.1: Authentication Section is present. */ + struct bfd_auth *auth = (struct bfd_auth *)(cp + 1); + uint16_t pkt_auth_type = ntohs(auth->type); + + if (cp->len < BFD_PKT_LEN + sizeof(struct bfd_auth)) + return false; + + if (cp->len < BFD_PKT_LEN + auth->length) + return false; + + switch (pkt_auth_type) { + case BFD_AUTH_NULL: + return false; + case BFD_AUTH_SIMPLE: + /* RFC5880 6.7: To be finshed. */ + return false; + case BFD_AUTH_CRYPTOGRAPHIC: + /* RFC5880 6.7: To be finshed. */ + return false; + default: + /* RFC5880 6.7: To be finshed. */ + return false; + } + } + return true; +} + +void bfd_recv_cb(struct event *t) { - int sd = THREAD_FD(t); + int sd = EVENT_FD(t); struct bfd_session *bfd; struct bfd_pkt *cp; bool is_mhop; @@ -787,7 +812,7 @@ void bfd_recv_cb(struct thread *t) struct sockaddr_any local, peer; uint8_t msgbuf[1516]; struct interface *ifp = NULL; - struct bfd_vrf_global *bvrf = THREAD_ARG(t); + struct bfd_vrf_global *bvrf = EVENT_ARG(t); /* Schedule next read. */ bfd_sd_reschedule(bvrf, sd); @@ -884,7 +909,7 @@ void bfd_recv_cb(struct thread *t) /* * We may have a situation where received packet is on wrong vrf */ - if (bfd && bfd->vrf && bfd->vrf != bvrf->vrf) { + if (bfd && bfd->vrf && bfd->vrf->vrf_id != vrfid) { cp_debug(is_mhop, &peer, &local, ifindex, vrfid, "wrong vrfid."); return; @@ -938,6 +963,13 @@ void bfd_recv_cb(struct thread *t) bfd->discrs.remote_discr = ntohl(cp->discrs.my_discr); + /* Check authentication. */ + if (!bfd_check_auth(bfd, cp)) { + cp_debug(is_mhop, &peer, &local, ifindex, vrfid, + "Authentication failed"); + return; + } + /* Save remote diagnostics before state switch. */ bfd->remote_diag = cp->diag & BFD_DIAGMASK; diff --git a/bfdd/bfdd.c b/bfdd/bfdd.c index a3751e4d330d..95066b97ce71 100644 --- a/bfdd/bfdd.c +++ b/bfdd/bfdd.c @@ -28,11 +28,11 @@ * FRR related code. */ 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"); +DEFINE_MTYPE(BFDD, BFDD_CONTROL, "control socket memory"); +DEFINE_MTYPE(BFDD, BFDD_NOTIFICATION, "control notification data"); /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* BFDd privileges */ static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_SYS_ADMIN, ZCAP_NET_RAW}; @@ -375,8 +375,8 @@ int main(int argc, char *argv[]) /* Initialize zebra connection. */ bfdd_zclient_init(&bglobal.bfdd_privs); - thread_add_read(master, control_accept, NULL, bglobal.bg_csock, - &bglobal.bg_csockev); + event_add_read(master, control_accept, NULL, bglobal.bg_csock, + &bglobal.bg_csockev); /* Install commands. */ bfdd_vty_init(); diff --git a/bfdd/bfdd_nb.c b/bfdd/bfdd_nb.c index 7135c507638a..114fbc2bdd87 100644 --- a/bfdd/bfdd_nb.c +++ b/bfdd/bfdd_nb.c @@ -74,7 +74,6 @@ const struct frr_yang_module_info frr_bfdd_info = { .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, } }, @@ -361,7 +360,6 @@ const struct frr_yang_module_info frr_bfdd_info = { .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, } }, diff --git a/bfdd/bfdd_nb.h b/bfdd/bfdd_nb.h index 7a0e724d28e2..b5b00b57e4b0 100644 --- a/bfdd/bfdd_nb.h +++ b/bfdd/bfdd_nb.h @@ -25,7 +25,6 @@ int bfdd_bfd_profile_required_receive_interval_modify( 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); @@ -128,8 +127,6 @@ 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); diff --git a/bfdd/bfdd_nb_config.c b/bfdd/bfdd_nb_config.c index e4e97404d8b9..8cf2f0a6f12c 100644 --- a/bfdd/bfdd_nb_config.c +++ b/bfdd/bfdd_nb_config.c @@ -423,20 +423,6 @@ int bfdd_bfd_profile_minimum_ttl_modify(struct nb_cb_modify_args *args) 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 */ @@ -859,27 +845,3 @@ int bfdd_bfd_sessions_multi_hop_minimum_ttl_modify( 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/control.c b/bfdd/control.c index a11ed3d895f1..f435358f33c3 100644 --- a/bfdd/control.c +++ b/bfdd/control.c @@ -39,8 +39,8 @@ struct bfd_notify_peer *control_notifypeer_find(struct bfd_control_socket *bcs, struct bfd_control_socket *control_new(int sd); static void control_free(struct bfd_control_socket *bcs); static void control_reset_buf(struct bfd_control_buffer *bcb); -static void control_read(struct thread *t); -static void control_write(struct thread *t); +static void control_read(struct event *t); +static void control_write(struct event *t); static void control_handle_request_add(struct bfd_control_socket *bcs, struct bfd_control_msg *bcm); @@ -132,7 +132,7 @@ void control_shutdown(void) { struct bfd_control_socket *bcs; - thread_cancel(&bglobal.bg_csockev); + event_cancel(&bglobal.bg_csockev); socket_close(&bglobal.bg_csock); @@ -142,9 +142,9 @@ void control_shutdown(void) } } -void control_accept(struct thread *t) +void control_accept(struct event *t) { - int csock, sd = THREAD_FD(t); + int csock, sd = EVENT_FD(t); csock = accept(sd, NULL, 0); if (csock == -1) { @@ -154,7 +154,7 @@ void control_accept(struct thread *t) control_new(csock); - thread_add_read(master, control_accept, NULL, sd, &bglobal.bg_csockev); + event_add_read(master, control_accept, NULL, sd, &bglobal.bg_csockev); } @@ -171,7 +171,7 @@ struct bfd_control_socket *control_new(int sd) bcs->bcs_notify = 0; bcs->bcs_sd = sd; - thread_add_read(master, control_read, bcs, sd, &bcs->bcs_ev); + event_add_read(master, control_read, bcs, sd, &bcs->bcs_ev); TAILQ_INIT(&bcs->bcs_bcqueue); TAILQ_INIT(&bcs->bcs_bnplist); @@ -185,8 +185,8 @@ static void control_free(struct bfd_control_socket *bcs) struct bfd_control_queue *bcq; struct bfd_notify_peer *bnp; - thread_cancel(&(bcs->bcs_ev)); - thread_cancel(&(bcs->bcs_outev)); + event_cancel(&(bcs->bcs_ev)); + event_cancel(&(bcs->bcs_outev)); close(bcs->bcs_sd); @@ -286,13 +286,13 @@ static int control_queue_dequeue(struct bfd_control_socket *bcs) bcs->bcs_bout = &bcq->bcq_bcb; bcs->bcs_outev = NULL; - thread_add_write(master, control_write, bcs, bcs->bcs_sd, - &bcs->bcs_outev); + event_add_write(master, control_write, bcs, bcs->bcs_sd, + &bcs->bcs_outev); return 1; empty_list: - thread_cancel(&(bcs->bcs_outev)); + event_cancel(&(bcs->bcs_outev)); bcs->bcs_bout = NULL; return 0; } @@ -315,8 +315,8 @@ static int control_queue_enqueue(struct bfd_control_socket *bcs, bcs->bcs_bout = bcb; /* New messages, active write events. */ - thread_add_write(master, control_write, bcs, bcs->bcs_sd, - &bcs->bcs_outev); + event_add_write(master, control_write, bcs, bcs->bcs_sd, + &bcs->bcs_outev); } return 0; @@ -379,9 +379,9 @@ static void control_reset_buf(struct bfd_control_buffer *bcb) bcb->bcb_left = 0; } -static void control_read(struct thread *t) +static void control_read(struct event *t) { - struct bfd_control_socket *bcs = THREAD_ARG(t); + struct bfd_control_socket *bcs = EVENT_ARG(t); struct bfd_control_buffer *bcb = &bcs->bcs_bin; int sd = bcs->bcs_sd; struct bfd_control_msg bcm; @@ -511,12 +511,12 @@ static void control_read(struct thread *t) schedule_next_read: bcs->bcs_ev = NULL; - thread_add_read(master, control_read, bcs, sd, &bcs->bcs_ev); + event_add_read(master, control_read, bcs, sd, &bcs->bcs_ev); } -static void control_write(struct thread *t) +static void control_write(struct event *t) { - struct bfd_control_socket *bcs = THREAD_ARG(t); + struct bfd_control_socket *bcs = EVENT_ARG(t); struct bfd_control_buffer *bcb = bcs->bcs_bout; int sd = bcs->bcs_sd; ssize_t bwrite; @@ -529,8 +529,8 @@ static void control_write(struct thread *t) if (bwrite < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { bcs->bcs_outev = NULL; - thread_add_write(master, control_write, bcs, - bcs->bcs_sd, &bcs->bcs_outev); + event_add_write(master, control_write, bcs, bcs->bcs_sd, + &bcs->bcs_outev); return; } @@ -543,8 +543,8 @@ static void control_write(struct thread *t) bcb->bcb_left -= bwrite; if (bcb->bcb_left > 0) { bcs->bcs_outev = NULL; - thread_add_write(master, control_write, bcs, bcs->bcs_sd, - &bcs->bcs_outev); + event_add_write(master, control_write, bcs, bcs->bcs_sd, + &bcs->bcs_outev); return; } diff --git a/bfdd/dplane.c b/bfdd/dplane.c index 99bd1886f48c..d8539812e097 100644 --- a/bfdd/dplane.c +++ b/bfdd/dplane.c @@ -26,7 +26,7 @@ #include "lib/network.h" #include "lib/printfrr.h" #include "lib/stream.h" -#include "lib/thread.h" +#include "lib/frrevent.h" #include "bfd.h" #include "bfddp_packet.h" @@ -63,11 +63,11 @@ struct bfd_dplane_ctx { /** Output buffer data. */ struct stream *outbuf; /** Input event data. */ - struct thread *inbufev; + struct event *inbufev; /** Output event data. */ - struct thread *outbufev; + struct event *outbufev; /** Connection event. */ - struct thread *connectev; + struct event *connectev; /** Amount of bytes read. */ uint64_t in_bytes; @@ -94,7 +94,7 @@ struct bfd_dplane_ctx { */ typedef void (*bfd_dplane_expect_cb)(struct bfddp_message *msg, void *arg); -static void bfd_dplane_client_connect(struct thread *t); +static void bfd_dplane_client_connect(struct event *t); static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx *bdc); static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc); static int _bfd_dplane_add_session(struct bfd_dplane_ctx *bdc, @@ -307,14 +307,14 @@ static ssize_t bfd_dplane_flush(struct bfd_dplane_ctx *bdc) stream_pulldown(bdc->outbuf); /* Disable write ready events. */ - THREAD_OFF(bdc->outbufev); + EVENT_OFF(bdc->outbufev); return total; } -static void bfd_dplane_write(struct thread *t) +static void bfd_dplane_write(struct event *t) { - struct bfd_dplane_ctx *bdc = THREAD_ARG(t); + struct bfd_dplane_ctx *bdc = EVENT_ARG(t); /* Handle connection stage. */ if (bdc->connecting && bfd_dplane_client_connecting(bdc)) @@ -429,8 +429,8 @@ static int bfd_dplane_enqueue(struct bfd_dplane_ctx *bdc, const void *buf, /* Schedule if it is not yet. */ if (bdc->outbufev == NULL) - thread_add_write(master, bfd_dplane_write, bdc, bdc->sock, - &bdc->outbufev); + event_add_write(master, bfd_dplane_write, bdc, bdc->sock, + &bdc->outbufev); return 0; } @@ -599,9 +599,9 @@ static int bfd_dplane_expect(struct bfd_dplane_ctx *bdc, uint16_t id, return 0; } -static void bfd_dplane_read(struct thread *t) +static void bfd_dplane_read(struct event *t) { - struct bfd_dplane_ctx *bdc = THREAD_ARG(t); + struct bfd_dplane_ctx *bdc = EVENT_ARG(t); int rv; rv = bfd_dplane_expect(bdc, 0, bfd_dplane_handle_message, NULL); @@ -609,7 +609,7 @@ static void bfd_dplane_read(struct thread *t) return; stream_pulldown(bdc->inbuf); - thread_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev); + event_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev); } static void _bfd_session_register_dplane(struct hash_bucket *hb, void *arg) @@ -641,7 +641,7 @@ static struct bfd_dplane_ctx *bfd_dplane_ctx_new(int sock) if (sock == -1) return bdc; - thread_add_read(master, bfd_dplane_read, bdc, sock, &bdc->inbufev); + event_add_read(master, bfd_dplane_read, bdc, sock, &bdc->inbufev); /* Register all unattached sessions. */ bfd_key_iterate(_bfd_session_register_dplane, bdc); @@ -672,7 +672,7 @@ static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc) /* Client mode has special treatment. */ if (bdc->client) { /* Disable connection event if any. */ - THREAD_OFF(bdc->connectev); + EVENT_OFF(bdc->connectev); /* Normal treatment on shutdown. */ if (bglobal.bg_shutdown) @@ -680,10 +680,10 @@ static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc) /* Attempt reconnection. */ socket_close(&bdc->sock); - THREAD_OFF(bdc->inbufev); - THREAD_OFF(bdc->outbufev); - thread_add_timer(master, bfd_dplane_client_connect, bdc, 3, - &bdc->connectev); + EVENT_OFF(bdc->inbufev); + EVENT_OFF(bdc->outbufev); + event_add_timer(master, bfd_dplane_client_connect, bdc, 3, + &bdc->connectev); return; } @@ -699,8 +699,8 @@ static void bfd_dplane_ctx_free(struct bfd_dplane_ctx *bdc) socket_close(&bdc->sock); stream_free(bdc->inbuf); stream_free(bdc->outbuf); - THREAD_OFF(bdc->inbufev); - THREAD_OFF(bdc->outbufev); + EVENT_OFF(bdc->inbufev); + EVENT_OFF(bdc->outbufev); XFREE(MTYPE_BFDD_DPLANE_CTX, bdc); } @@ -819,9 +819,9 @@ static uint16_t bfd_dplane_request_counters(const struct bfd_session *bs) /* * Data plane listening socket. */ -static void bfd_dplane_accept(struct thread *t) +static void bfd_dplane_accept(struct event *t) { - struct bfd_global *bg = THREAD_ARG(t); + struct bfd_global *bg = EVENT_ARG(t); struct bfd_dplane_ctx *bdc; int sock; @@ -840,8 +840,8 @@ static void bfd_dplane_accept(struct thread *t) zlog_debug("%s: new data plane client connected", __func__); reschedule_and_return: - thread_add_read(master, bfd_dplane_accept, bg, bg->bg_dplane_sock, - &bglobal.bg_dplane_sockev); + event_add_read(master, bfd_dplane_accept, bg, bg->bg_dplane_sock, + &bglobal.bg_dplane_sockev); } /* @@ -856,7 +856,7 @@ static void _bfd_dplane_client_bootstrap(struct bfd_dplane_ctx *bdc) stream_reset(bdc->outbuf); /* Ask for read notifications. */ - thread_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev); + event_add_read(master, bfd_dplane_read, bdc, bdc->sock, &bdc->inbufev); /* Remove all sessions then register again to send them all. */ bfd_key_iterate(_bfd_session_unregister_dplane, bdc); @@ -899,9 +899,9 @@ static bool bfd_dplane_client_connecting(struct bfd_dplane_ctx *bdc) } } -static void bfd_dplane_client_connect(struct thread *t) +static void bfd_dplane_client_connect(struct event *t) { - struct bfd_dplane_ctx *bdc = THREAD_ARG(t); + struct bfd_dplane_ctx *bdc = EVENT_ARG(t); int rv, sock; socklen_t rvlen = sizeof(rv); @@ -938,8 +938,8 @@ static void bfd_dplane_client_connect(struct thread *t) /* If we are not connected yet, ask for write notifications. */ bdc->connecting = true; - thread_add_write(master, bfd_dplane_write, bdc, bdc->sock, - &bdc->outbufev); + event_add_write(master, bfd_dplane_write, bdc, bdc->sock, + &bdc->outbufev); } else { if (bglobal.debug_dplane) zlog_debug("%s: server connection: %d", __func__, sock); @@ -949,11 +949,11 @@ static void bfd_dplane_client_connect(struct thread *t) } reschedule_connect: - THREAD_OFF(bdc->inbufev); - THREAD_OFF(bdc->outbufev); + EVENT_OFF(bdc->inbufev); + EVENT_OFF(bdc->outbufev); socket_close(&sock); - thread_add_timer(master, bfd_dplane_client_connect, bdc, 3, - &bdc->connectev); + event_add_timer(master, bfd_dplane_client_connect, bdc, 3, + &bdc->connectev); } static void bfd_dplane_client_init(const struct sockaddr *sa, socklen_t salen) @@ -974,8 +974,8 @@ static void bfd_dplane_client_init(const struct sockaddr *sa, socklen_t salen) bdc->client = true; - thread_add_timer(master, bfd_dplane_client_connect, bdc, 0, - &bdc->connectev); + event_add_timer(master, bfd_dplane_client_connect, bdc, 0, + &bdc->connectev); /* Insert into data plane lists. */ TAILQ_INSERT_TAIL(&bglobal.bg_dplaneq, bdc, entry); @@ -997,7 +997,7 @@ static int bfd_dplane_finish_late(void) bfd_dplane_ctx_free(bdc); /* Cancel accept thread and close socket. */ - THREAD_OFF(bglobal.bg_dplane_sockev); + EVENT_OFF(bglobal.bg_dplane_sockev); close(bglobal.bg_dplane_sock); return 0; @@ -1067,8 +1067,8 @@ void bfd_dplane_init(const struct sockaddr *sa, socklen_t salen, bool client) } bglobal.bg_dplane_sock = sock; - thread_add_read(master, bfd_dplane_accept, &bglobal, sock, - &bglobal.bg_dplane_sockev); + event_add_read(master, bfd_dplane_accept, &bglobal, sock, + &bglobal.bg_dplane_sockev); } int bfd_dplane_add_session(struct bfd_session *bs) diff --git a/bfdd/event.c b/bfdd/event.c index 5a9ae39c3e4e..e797e71f050a 100644 --- a/bfdd/event.c +++ b/bfdd/event.c @@ -36,8 +36,8 @@ void bfd_recvtimer_update(struct bfd_session *bs) tv_normalize(&tv); - thread_add_timer_tv(master, bfd_recvtimer_cb, bs, &tv, - &bs->recvtimer_ev); + event_add_timer_tv(master, bfd_recvtimer_cb, bs, &tv, + &bs->recvtimer_ev); } void bfd_echo_recvtimer_update(struct bfd_session *bs) @@ -54,8 +54,8 @@ void bfd_echo_recvtimer_update(struct bfd_session *bs) tv_normalize(&tv); - thread_add_timer_tv(master, bfd_echo_recvtimer_cb, bs, &tv, - &bs->echo_recvtimer_ev); + event_add_timer_tv(master, bfd_echo_recvtimer_cb, bs, &tv, + &bs->echo_recvtimer_ev); } void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter) @@ -72,7 +72,7 @@ void bfd_xmttimer_update(struct bfd_session *bs, uint64_t jitter) tv_normalize(&tv); - thread_add_timer_tv(master, bfd_xmt_cb, bs, &tv, &bs->xmttimer_ev); + event_add_timer_tv(master, bfd_xmt_cb, bs, &tv, &bs->xmttimer_ev); } void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter) @@ -89,26 +89,26 @@ void bfd_echo_xmttimer_update(struct bfd_session *bs, uint64_t jitter) tv_normalize(&tv); - thread_add_timer_tv(master, bfd_echo_xmt_cb, bs, &tv, - &bs->echo_xmttimer_ev); + event_add_timer_tv(master, bfd_echo_xmt_cb, bs, &tv, + &bs->echo_xmttimer_ev); } void bfd_recvtimer_delete(struct bfd_session *bs) { - THREAD_OFF(bs->recvtimer_ev); + EVENT_OFF(bs->recvtimer_ev); } void bfd_echo_recvtimer_delete(struct bfd_session *bs) { - THREAD_OFF(bs->echo_recvtimer_ev); + EVENT_OFF(bs->echo_recvtimer_ev); } void bfd_xmttimer_delete(struct bfd_session *bs) { - THREAD_OFF(bs->xmttimer_ev); + EVENT_OFF(bs->xmttimer_ev); } void bfd_echo_xmttimer_delete(struct bfd_session *bs) { - THREAD_OFF(bs->echo_xmttimer_ev); + EVENT_OFF(bs->echo_xmttimer_ev); } diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c index 7f746541ff24..de4b4a48afb5 100644 --- a/bgpd/bgp_addpath.c +++ b/bgpd/bgp_addpath.c @@ -25,7 +25,14 @@ static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = { .human_description = "Advertise bestpath per AS via addpath", .type_json_name = "addpathTxBestpathPerAS", .id_json_name = "addpathTxIdBestPerAS" - } + }, + { + .config_name = "addpath-tx-best-selected", + .human_name = "Best-Selected", + .human_description = "Advertise best N selected paths via addpath", + .type_json_name = "addpathTxBestSelectedPaths", + .id_json_name = "addpathTxIdBestSelected" + }, }; static const struct bgp_addpath_strategy_names unknown_names = { @@ -161,6 +168,8 @@ bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, struct bgp_path_info *pi) return true; else return false; + case BGP_ADDPATH_BEST_SELECTED: + return true; case BGP_ADDPATH_MAX: return false; } @@ -356,7 +365,8 @@ void bgp_addpath_type_changed(struct bgp *bgp) * change take effect. */ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, - enum bgp_addpath_strat addpath_type) + enum bgp_addpath_strat addpath_type, + uint8_t paths) { struct bgp *bgp = peer->bgp; enum bgp_addpath_strat old_type; @@ -367,6 +377,8 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, if (safi == SAFI_LABELED_UNICAST) safi = SAFI_UNICAST; + peer->addpath_best_selected[afi][safi] = paths; + old_type = peer->addpath_type[afi][safi]; if (addpath_type == old_type) return; @@ -411,10 +423,9 @@ void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi, tmp_peer)) { if (tmp_peer->addpath_type[afi][safi] == old_type) { - bgp_addpath_set_peer_type(tmp_peer, - afi, - safi, - addpath_type); + bgp_addpath_set_peer_type( + tmp_peer, afi, safi, + addpath_type, paths); } } } diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h index 909a9710c44d..2b91d450bc6f 100644 --- a/bgpd/bgp_addpath.h +++ b/bgpd/bgp_addpath.h @@ -50,7 +50,8 @@ bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, * 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); + enum bgp_addpath_strat addpath_type, + uint8_t paths); void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi); diff --git a/bgpd/bgp_addpath_types.h b/bgpd/bgp_addpath_types.h index 3de9546a6d0b..8e32b0444abe 100644 --- a/bgpd/bgp_addpath_types.h +++ b/bgpd/bgp_addpath_types.h @@ -12,6 +12,7 @@ enum bgp_addpath_strat { BGP_ADDPATH_ALL = 0, BGP_ADDPATH_BEST_PER_AS, + BGP_ADDPATH_BEST_SELECTED, BGP_ADDPATH_MAX, BGP_ADDPATH_NONE, }; diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 3469d129e4c5..9686b08a983e 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -9,7 +9,7 @@ #include "memory.h" #include "prefix.h" #include "hash.h" -#include "thread.h" +#include "frrevent.h" #include "queue.h" #include "filter.h" diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 2cc7e46573a4..22c9fe0e6686 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -1598,6 +1598,24 @@ struct aspath *aspath_filter_exclude(struct aspath *source, return newpath; } +struct aspath *aspath_filter_exclude_all(struct aspath *source) +{ + struct aspath *newpath; + + newpath = aspath_new(source->asnotation); + + aspath_str_update(newpath, false); + /* We are happy returning even an empty AS_PATH, because the + * administrator + * might expect this very behaviour. There's a mean to avoid this, if + * necessary, + * by having a match rule against certain AS_PATH regexps in the + * route-map index. + */ + aspath_free(source); + return newpath; +} + /* Add specified AS to the leftmost of aspath. */ static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno, uint8_t type, unsigned num) @@ -2095,9 +2113,7 @@ void aspath_init(void) void aspath_finish(void) { - hash_clean(ashash, (void (*)(void *))aspath_free); - hash_free(ashash); - ashash = NULL; + hash_clean_and_free(&ashash, (void (*)(void *))aspath_free); if (snmp_stream) stream_free(snmp_stream); @@ -2157,11 +2173,15 @@ static void bgp_aggr_aspath_prepare(struct hash_bucket *hb, void *arg) { struct aspath *hb_aspath = hb->data; struct aspath **aggr_aspath = arg; + struct aspath *aspath = NULL; - if (*aggr_aspath) - *aggr_aspath = aspath_aggregate(*aggr_aspath, hb_aspath); - else + if (*aggr_aspath) { + aspath = aspath_aggregate(*aggr_aspath, hb_aspath); + aspath_free(*aggr_aspath); + *aggr_aspath = aspath; + } else { *aggr_aspath = aspath_dup(hb_aspath); + } } void bgp_aggr_aspath_remove(void *arg) diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index 18af375c1331..a3aae14f8f7e 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -76,6 +76,7 @@ extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2); extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2); extern struct aspath *aspath_filter_exclude(struct aspath *source, struct aspath *exclude_list); +extern struct aspath *aspath_filter_exclude_all(struct aspath *source); extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno, unsigned num); extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index de77bd530463..4a5b0ba06617 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -186,9 +186,7 @@ static void cluster_init(void) static void cluster_finish(void) { - hash_clean(cluster_hash, (void (*)(void *))cluster_free); - hash_free(cluster_hash); - cluster_hash = NULL; + hash_clean_and_free(&cluster_hash, (void (*)(void *))cluster_free); } static struct hash *encap_hash = NULL; @@ -372,13 +370,9 @@ static void encap_init(void) static void encap_finish(void) { - hash_clean(encap_hash, (void (*)(void *))encap_free); - hash_free(encap_hash); - encap_hash = NULL; + hash_clean_and_free(&encap_hash, (void (*)(void *))encap_free); #ifdef ENABLE_BGP_VNC - hash_clean(vnc_hash, (void (*)(void *))encap_free); - hash_free(vnc_hash); - vnc_hash = NULL; + hash_clean_and_free(&vnc_hash, (void (*)(void *))encap_free); #endif } @@ -694,12 +688,9 @@ static void srv6_init(void) 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; + hash_clean_and_free(&srv6_l3vpn_hash, + (void (*)(void *))srv6_l3vpn_free); + hash_clean_and_free(&srv6_vpn_hash, (void (*)(void *))srv6_vpn_free); } static unsigned int transit_hash_key_make(const void *p) @@ -726,9 +717,7 @@ static void transit_init(void) static void transit_finish(void) { - hash_clean(transit_hash, (void (*)(void *))transit_free); - hash_free(transit_hash); - transit_hash = NULL; + hash_clean_and_free(&transit_hash, (void (*)(void *))transit_free); } /* Attribute hash routines. */ @@ -810,55 +799,55 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->med == attr2->med && attr1->local_pref == attr2->local_pref && attr1->rmap_change_flags == attr2->rmap_change_flags) { - if (attr1->aggregator_as == attr2->aggregator_as - && attr1->aggregator_addr.s_addr - == attr2->aggregator_addr.s_addr - && attr1->weight == attr2->weight - && attr1->tag == attr2->tag - && attr1->label_index == attr2->label_index - && attr1->mp_nexthop_len == attr2->mp_nexthop_len - && bgp_attr_get_ecommunity(attr1) - == bgp_attr_get_ecommunity(attr2) - && bgp_attr_get_ipv6_ecommunity(attr1) - == bgp_attr_get_ipv6_ecommunity(attr2) - && bgp_attr_get_lcommunity(attr1) - == bgp_attr_get_lcommunity(attr2) - && bgp_attr_get_cluster(attr1) - == bgp_attr_get_cluster(attr2) - && bgp_attr_get_transit(attr1) - == bgp_attr_get_transit(attr2) - && bgp_attr_get_aigp_metric(attr1) - == bgp_attr_get_aigp_metric(attr2) - && attr1->rmap_table_id == attr2->rmap_table_id - && (attr1->encap_tunneltype == attr2->encap_tunneltype) - && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs) + if (attr1->aggregator_as == attr2->aggregator_as && + attr1->aggregator_addr.s_addr == + attr2->aggregator_addr.s_addr && + attr1->weight == attr2->weight && + attr1->tag == attr2->tag && + attr1->label_index == attr2->label_index && + attr1->mp_nexthop_len == attr2->mp_nexthop_len && + bgp_attr_get_ecommunity(attr1) == + bgp_attr_get_ecommunity(attr2) && + bgp_attr_get_ipv6_ecommunity(attr1) == + bgp_attr_get_ipv6_ecommunity(attr2) && + bgp_attr_get_lcommunity(attr1) == + bgp_attr_get_lcommunity(attr2) && + bgp_attr_get_cluster(attr1) == + bgp_attr_get_cluster(attr2) && + bgp_attr_get_transit(attr1) == + bgp_attr_get_transit(attr2) && + bgp_attr_get_aigp_metric(attr1) == + bgp_attr_get_aigp_metric(attr2) && + attr1->rmap_table_id == attr2->rmap_table_id && + (attr1->encap_tunneltype == attr2->encap_tunneltype) && + encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs) #ifdef ENABLE_BGP_VNC && encap_same(bgp_attr_get_vnc_subtlvs(attr1), bgp_attr_get_vnc_subtlvs(attr2)) #endif && IPV6_ADDR_SAME(&attr1->mp_nexthop_global, - &attr2->mp_nexthop_global) - && IPV6_ADDR_SAME(&attr1->mp_nexthop_local, - &attr2->mp_nexthop_local) - && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, - &attr2->mp_nexthop_global_in) - && 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->df_pref == attr2->df_pref - && attr1->df_alg == attr2->df_alg - && attr1->nh_ifindex == attr2->nh_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 - && attr1->nh_type == attr2->nh_type - && attr1->bh_type == attr2->bh_type - && attr1->otc == attr2->otc) + &attr2->mp_nexthop_global) && + IPV6_ADDR_SAME(&attr1->mp_nexthop_local, + &attr2->mp_nexthop_local) && + IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, + &attr2->mp_nexthop_global_in) && + 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->df_pref == attr2->df_pref && + attr1->df_alg == attr2->df_alg && + attr1->nh_ifindex == attr2->nh_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 && + attr1->nh_type == attr2->nh_type && + attr1->bh_type == attr2->bh_type && + attr1->otc == attr2->otc) return true; } @@ -881,9 +870,7 @@ static void attr_vfree(void *attr) static void attrhash_finish(void) { - hash_clean(attrhash, attr_vfree); - hash_free(attrhash); - attrhash = NULL; + hash_clean_and_free(&attrhash, attr_vfree); } static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty) @@ -1095,9 +1082,6 @@ struct attr *bgp_attr_aggregate_intern( attr.aspath = aspath_empty(bgp->asnotation); attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH); - /* Next hop attribute. */ - attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); - if (community) { uint32_t gshut = COMMUNITY_GSHUT; @@ -1134,6 +1118,21 @@ struct attr *bgp_attr_aggregate_intern( attr.aggregator_as = bgp->as; attr.aggregator_addr = bgp->router_id; + /* Aggregate are done for IPv4/IPv6 so checking ipv4 family, + * This should only be set for IPv4 AFI type + * based on RFC-4760: + * "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 (p->family == AF_INET) { + /* Next hop attribute. */ + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + attr.mp_nexthop_len = IPV4_MAX_BYTELEN; + } + /* Apply route-map */ if (aggregate->rmap.name) { struct attr attr_tmp = attr; @@ -1480,7 +1479,7 @@ const uint8_t attr_flags_values[] = { [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, - [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_AIGP] = BGP_ATTR_FLAG_OPTIONAL, }; static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; @@ -2139,6 +2138,15 @@ bgp_attr_originator_id(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; + /* if the ORIGINATOR_ID attribute is received from an external + * neighbor, it SHALL be discarded using the approach of "attribute + * discard". + */ + if (peer->sort == BGP_PEER_EBGP) { + stream_forward_getp(peer->curr, length); + return BGP_ATTR_PARSE_PROCEED; + } + /* 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- @@ -2175,6 +2183,15 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) struct attr *const attr = args->attr; const bgp_size_t length = args->length; + /* if the CLUSTER_LIST attribute is received from an external + * neighbor, it SHALL be discarded using the approach of "attribute + * discard". + */ + if (peer->sort == BGP_PEER_EBGP) { + stream_forward_getp(peer->curr, length); + return BGP_ATTR_PARSE_PROCEED; + } + /* 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 @@ -2207,6 +2224,16 @@ bgp_attr_cluster_list(struct bgp_attr_parser_args *args) return bgp_attr_ignore(peer, args->type); } +/* get locally configure or received srte-color value*/ +uint32_t bgp_attr_get_color(struct attr *attr) +{ + if (attr->srte_color) + return attr->srte_color; + if (attr->ecommunity) + return ecommunity_select_color(attr->ecommunity); + return 0; +} + /* Multiprotocol reachability information parse. */ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, struct bgp_nlri *mp_update) @@ -2649,7 +2676,9 @@ static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */ if (BGP_ATTR_ENCAP == type) { subtype = stream_getc(BGP_INPUT(peer)); - sublength = stream_getc(BGP_INPUT(peer)); + sublength = (subtype < 128) + ? stream_getc(BGP_INPUT(peer)) + : stream_getw(BGP_INPUT(peer)); length -= 2; #ifdef ENABLE_BGP_VNC } else { @@ -2940,9 +2969,21 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length, int srgb_count; uint8_t sid_type, sid_flags; + /* + * 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 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); + } + if (type == BGP_PREFIX_SID_LABEL_INDEX) { - if (STREAM_READABLE(peer->curr) < length - || length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) { + if (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); @@ -2964,12 +3005,8 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length, /* Store label index; subsequently, we'll check on * address-family */ attr->label_index = label_index; - } - - /* Placeholder code for the IPv6 SID type */ - else if (type == BGP_PREFIX_SID_IPV6) { - if (STREAM_READABLE(peer->curr) < length - || length != BGP_PREFIX_SID_IPV6_LENGTH) { + } else if (type == BGP_PREFIX_SID_IPV6) { + if (length != BGP_PREFIX_SID_IPV6_LENGTH) { flog_err(EC_BGP_ATTR_LEN, "Prefix SID IPv6 length is %hu instead of %u", length, BGP_PREFIX_SID_IPV6_LENGTH); @@ -2983,10 +3020,7 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length, stream_getw(peer->curr); stream_get(&ipv6_sid, peer->curr, 16); - } - - /* Placeholder code for the Originator SRGB type */ - else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) { + } else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) { /* * ietf-idr-bgp-prefix-sid-05: * Length is the total length of the value portion of the @@ -3011,19 +3045,6 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length, 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 @@ -3047,12 +3068,8 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length, stream_get(&srgb_base, peer->curr, 3); stream_get(&srgb_range, peer->curr, 3); } - } - - /* 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) { + } else if (type == BGP_PREFIX_SID_VPN_SID) { + if (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); @@ -3086,39 +3103,22 @@ bgp_attr_psid_sub(uint8_t type, uint16_t length, attr->srv6_vpn->sid_flags = sid_flags; sid_copy(&attr->srv6_vpn->sid, &ipv6_sid); attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn); - } - - /* Placeholder code for the SRv6 L3 Service type */ - else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) { - if (STREAM_READABLE(peer->curr) < length) { + } else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) { + if (STREAM_READABLE(peer->curr) < 1) { flog_err( EC_BGP_ATTR_LEN, - "Prefix SID SRv6 L3-Service length is %hu, but only %zu bytes remain", - length, STREAM_READABLE(peer->curr)); - return bgp_attr_malformed(args, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, - args->total); + "Prefix SID SRV6 L3 Service not enough data left, it must be at least 1 byte"); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); } - /* ignore reserved */ stream_getc(peer->curr); return bgp_attr_srv6_service(args); } - /* 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", @@ -4712,6 +4712,10 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, * there! (JK) * Folks, talk to me: what is reasonable here!? */ + + /* Make sure dup aspath before the modification */ + if (aspath == attr->aspath) + aspath = aspath_dup(attr->aspath); aspath = aspath_delete_confed_seq(aspath); stream_putc(s, @@ -4782,7 +4786,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, */ uint8_t attr_len = BGP_AIGP_TLV_METRIC_LEN; - stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS); + stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); stream_putc(s, BGP_ATTR_AIGP); stream_putc(s, attr_len); stream_put_bgp_aigp_tlv_metric(s, bpi); @@ -5215,3 +5219,18 @@ enum bgp_attr_parse_ret bgp_attr_ignore(struct peer *peer, uint8_t type) return withdraw ? BGP_ATTR_PARSE_WITHDRAW : BGP_ATTR_PARSE_PROCEED; } + +bool route_matches_soo(struct bgp_path_info *pi, struct ecommunity *soo) +{ + struct attr *attr = pi->attr; + struct ecommunity *ecom; + + if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) + return false; + + ecom = attr->ecommunity; + if (!ecom || !ecom->size) + return false; + + return soo_in_ecom(ecom, soo); +} diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1c7e88a4f9e2..415df2ce53ed 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -466,6 +466,8 @@ extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt); extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer, struct attr *attr); +extern uint32_t bgp_attr_get_color(struct attr *attr); + static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags, uint32_t in_rmap_flags) { @@ -637,4 +639,6 @@ bgp_attr_set_vnc_subtlvs(struct attr *attr, #endif } +extern bool route_matches_soo(struct bgp_path_info *pi, struct ecommunity *soo); + #endif /* _QUAGGA_BGP_ATTR_H */ diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index 4a81b69ced30..d1ddfd046025 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -11,7 +11,7 @@ #include "linklist.h" #include "memory.h" #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "stream.h" #include "vrf.h" @@ -55,7 +55,7 @@ static void bfd_session_status_update(struct bfd_session_params *bsp, } peer->last_reset = PEER_DOWN_BFD_DOWN; - /* draft-ietf-idr-bfd-subcode */ + /* rfc9384 */ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_BFD_DOWN); @@ -609,7 +609,7 @@ DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd, } #endif /* HAVE_BFDD */ -void bgp_bfd_init(struct thread_master *tm) +void bgp_bfd_init(struct event_loop *tm) { /* Initialize BFD client functions */ bfd_protocol_integration_init(zclient, tm); diff --git a/bgpd/bgp_bfd.h b/bgpd/bgp_bfd.h index c4045d474507..61b4b76605a3 100644 --- a/bgpd/bgp_bfd.h +++ b/bgpd/bgp_bfd.h @@ -12,7 +12,7 @@ ((((peer)->sort == BGP_PEER_IBGP) && !(peer)->shared_network) \ || is_ebgp_multihop_configured((peer))) -extern void bgp_bfd_init(struct thread_master *tm); +extern void bgp_bfd_init(struct event_loop *tm); extern void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer, const char *addr); diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c index 92d92ada2baa..baf164679c72 100644 --- a/bgpd/bgp_bmp.c +++ b/bgpd/bgp_bmp.c @@ -11,7 +11,7 @@ #include "sockunion.h" #include "command.h" #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "queue.h" #include "pullwr.h" @@ -389,13 +389,13 @@ static struct stream *bmp_peerstate(struct peer *peer, bool down) /* Local Port, Remote Port */ if (peer->su_local->sa.sa_family == AF_INET6) - stream_putw(s, peer->su_local->sin6.sin6_port); + stream_putw(s, htons(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); + stream_putw(s, htons(peer->su_local->sin.sin_port)); if (peer->su_remote->sa.sa_family == AF_INET6) - stream_putw(s, peer->su_remote->sin6.sin6_port); + stream_putw(s, htons(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); + stream_putw(s, htons(peer->su_remote->sin.sin_port)); static const uint8_t dummy_open[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -1175,8 +1175,8 @@ static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) (bqe->safi == SAFI_MPLS_VPN); struct prefix_rd *prd = is_vpn ? &bqe->rd : NULL; - bn = bgp_afi_node_lookup(bmp->targets->bgp->rib[afi][safi], afi, safi, - &bqe->p, prd); + bn = bgp_safi_node_lookup(bmp->targets->bgp->rib[afi][safi], safi, + &bqe->p, prd); if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { @@ -1335,17 +1335,17 @@ static void bmp_stat_put_u32(struct stream *s, size_t *cnt, uint16_t type, (*cnt)++; } -static void bmp_stats(struct thread *thread) +static void bmp_stats(struct event *thread) { - struct bmp_targets *bt = THREAD_ARG(thread); + struct bmp_targets *bt = EVENT_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); + event_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, + &bt->t_stats); gettimeofday(&tv, NULL); @@ -1388,9 +1388,9 @@ static void bmp_stats(struct thread *thread) } /* read from the BMP socket to detect session termination */ -static void bmp_read(struct thread *t) +static void bmp_read(struct event *t) { - struct bmp *bmp = THREAD_ARG(t); + struct bmp *bmp = EVENT_ARG(t); char buf[1024]; ssize_t n; @@ -1409,7 +1409,7 @@ static void bmp_read(struct thread *t) return; } - thread_add_read(bm->master, bmp_read, bmp, bmp->socket, &bmp->t_read); + event_add_read(bm->master, bmp_read, bmp, bmp->socket, &bmp->t_read); } static struct bmp *bmp_open(struct bmp_targets *bt, int bmp_sock) @@ -1485,21 +1485,21 @@ static struct bmp *bmp_open(struct bmp_targets *bt, int bmp_sock) bmp->state = BMP_PeerUp; bmp->pullwr = pullwr_new(bm->master, bmp_sock, bmp, bmp_wrfill, bmp_wrerr); - thread_add_read(bm->master, bmp_read, bmp, bmp_sock, &bmp->t_read); + event_add_read(bm->master, bmp_read, bmp, bmp_sock, &bmp->t_read); bmp_send_initiation(bmp); return bmp; } /* Accept BMP connection. */ -static void bmp_accept(struct thread *thread) +static void bmp_accept(struct event *thread) { union sockunion su; - struct bmp_listener *bl = THREAD_ARG(thread); + struct bmp_listener *bl = EVENT_ARG(thread); int bmp_sock; /* We continue hearing BMP socket. */ - thread_add_read(bm->master, bmp_accept, bl, bl->sock, &bl->t_accept); + event_add_read(bm->master, bmp_accept, bl, bl->sock, &bl->t_accept); memset(&su, 0, sizeof(union sockunion)); @@ -1517,7 +1517,7 @@ static void bmp_close(struct bmp *bmp) struct bmp_queue_entry *bqe; struct bmp_mirrorq *bmq; - THREAD_OFF(bmp->t_read); + EVENT_OFF(bmp->t_read); if (bmp->active) bmp_active_disconnected(bmp->active); @@ -1529,7 +1529,7 @@ static void bmp_close(struct bmp *bmp) if (!bqe->refcount) XFREE(MTYPE_BMP_QUEUE, bqe); - THREAD_OFF(bmp->t_read); + EVENT_OFF(bmp->t_read); pullwr_del(bmp->pullwr); close(bmp->socket); } @@ -1644,7 +1644,7 @@ static void bmp_targets_put(struct bmp_targets *bt) struct bmp *bmp; struct bmp_active *ba; - THREAD_OFF(bt->t_stats); + EVENT_OFF(bt->t_stats); frr_each_safe (bmp_actives, &bt->actives, ba) bmp_active_put(ba); @@ -1721,7 +1721,7 @@ static void bmp_listener_start(struct bmp_listener *bl) goto out_sock; bl->sock = sock; - thread_add_read(bm->master, bmp_accept, bl, sock, &bl->t_accept); + event_add_read(bm->master, bmp_accept, bl, sock, &bl->t_accept); return; out_sock: close(sock); @@ -1729,7 +1729,7 @@ static void bmp_listener_start(struct bmp_listener *bl) static void bmp_listener_stop(struct bmp_listener *bl) { - THREAD_OFF(bl->t_accept); + EVENT_OFF(bl->t_accept); if (bl->sock != -1) close(bl->sock); @@ -1768,9 +1768,9 @@ static struct bmp_active *bmp_active_get(struct bmp_targets *bt, static void bmp_active_put(struct bmp_active *ba) { - THREAD_OFF(ba->t_timer); - THREAD_OFF(ba->t_read); - THREAD_OFF(ba->t_write); + EVENT_OFF(ba->t_timer); + EVENT_OFF(ba->t_read); + EVENT_OFF(ba->t_write); bmp_actives_del(&ba->targets->actives, ba); @@ -1902,18 +1902,18 @@ static void bmp_active_resolved(struct resolver_query *resq, const char *errstr, bmp_active_connect(ba); } -static void bmp_active_thread(struct thread *t) +static void bmp_active_thread(struct event *t) { - struct bmp_active *ba = THREAD_ARG(t); + struct bmp_active *ba = EVENT_ARG(t); socklen_t slen; int status, ret; vrf_id_t vrf_id; /* 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); + EVENT_OFF(ba->t_timer); + EVENT_OFF(ba->t_read); + EVENT_OFF(ba->t_write); ba->last_err = NULL; @@ -1967,9 +1967,9 @@ static void bmp_active_disconnected(struct bmp_active *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); + EVENT_OFF(ba->t_timer); + EVENT_OFF(ba->t_read); + EVENT_OFF(ba->t_write); if (ba->bmp) return; @@ -1980,12 +1980,12 @@ static void bmp_active_setup(struct bmp_active *ba) ba->curretry = ba->maxretry; if (ba->socket == -1) - thread_add_timer_msec(bm->master, bmp_active_thread, ba, - ba->curretry, &ba->t_timer); + event_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, + event_add_read(bm->master, bmp_active_thread, ba, ba->socket, + &ba->t_read); + event_add_write(bm->master, bmp_active_thread, ba, ba->socket, &ba->t_write); } } @@ -2190,7 +2190,7 @@ DEFPY(bmp_stats_cfg, { VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); - THREAD_OFF(bt->t_stats); + EVENT_OFF(bt->t_stats); if (no) bt->stat_msec = 0; else if (interval_str) @@ -2199,8 +2199,8 @@ DEFPY(bmp_stats_cfg, 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); + event_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, + &bt->t_stats); return CMD_SUCCESS; } @@ -2408,7 +2408,7 @@ DEFPY(show_bmp, uptime[0] = '\0'; if (ba->t_timer) { - long trem = thread_timer_remain_second( + long trem = event_timer_remain_second( ba->t_timer); peer_uptime(monotime(NULL) - trem, @@ -2526,7 +2526,7 @@ static int bmp_config_write(struct bgp *bgp, struct vty *vty) return 0; } -static int bgp_bmp_init(struct thread_master *tm) +static int bgp_bmp_init(struct event_loop *tm) { install_node(&bmp_node); install_default(BMP_NODE); diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h index 0c909e139bce..ab7463fadcad 100644 --- a/bgpd/bgp_bmp.h +++ b/bgpd/bgp_bmp.h @@ -112,7 +112,7 @@ struct bmp { int socket; char remote[SU_ADDRSTRLEN + 6]; - struct thread *t_read; + struct event *t_read; struct pullwr *pullwr; @@ -176,7 +176,7 @@ struct bmp_active { union sockunion addrs[8]; int socket; const char *last_err; - struct thread *t_timer, *t_read, *t_write; + struct event *t_timer, *t_read, *t_write; }; /* config & state for passive / listening sockets */ @@ -190,7 +190,7 @@ struct bmp_listener { union sockunion addr; int port; - struct thread *t_accept; + struct event *t_accept; int sock; }; @@ -226,7 +226,7 @@ struct bmp_targets { struct bmp_actives_head actives; - struct thread *t_stats; + struct event *t_stats; struct bmp_session_head sessions; struct bmp_qhash_head updhash; diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 1d2ba3bf5845..f3c308afb9e3 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -659,9 +659,6 @@ 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; - if (entry->style == COMMUNITY_LIST_STANDARD) { if (community_include(entry->u.com, COMMUNITY_INTERNET)) return entry->direct == COMMUNITY_PERMIT; @@ -681,9 +678,6 @@ 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; - if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) { if (lcommunity_match(lcom, entry->u.lcom)) return entry->direct == COMMUNITY_PERMIT; @@ -705,9 +699,6 @@ bool lcommunity_list_exact_match(struct lcommunity *lcom, 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.lcom)) return entry->direct == COMMUNITY_PERMIT; @@ -724,9 +715,6 @@ 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; - if (entry->style == EXTCOMMUNITY_LIST_STANDARD) { if (ecommunity_match(ecom, entry->u.ecom)) return entry->direct == COMMUNITY_PERMIT; @@ -746,9 +734,6 @@ bool community_list_exact_match(struct community *com, struct community_entry *entry; for (entry = list->head; entry; entry = entry->next) { - if (entry->any) - return entry->direct == COMMUNITY_PERMIT; - if (entry->style == COMMUNITY_LIST_STANDARD) { if (community_include(entry->u.com, COMMUNITY_INTERNET)) return entry->direct == COMMUNITY_PERMIT; @@ -781,28 +766,18 @@ struct community *community_list_match_delete(struct community *com, val = community_val_get(com, i); for (entry = list->head; entry; entry = entry->next) { - if (entry->any) { + if ((entry->style == COMMUNITY_LIST_STANDARD) && + (community_include(entry->u.com, + COMMUNITY_INTERNET) || + community_include(entry->u.com, val))) { if (entry->direct == COMMUNITY_PERMIT) { com_index_to_delete[delete_index] = i; delete_index++; } break; - } - - else if ((entry->style == COMMUNITY_LIST_STANDARD) - && (community_include(entry->u.com, - COMMUNITY_INTERNET) - || community_include(entry->u.com, val))) { - if (entry->direct == COMMUNITY_PERMIT) { - com_index_to_delete[delete_index] = i; - delete_index++; - } - break; - } - - else if ((entry->style == COMMUNITY_LIST_EXPANDED) - && community_regexp_include(entry->reg, com, - i)) { + } else if ((entry->style == COMMUNITY_LIST_EXPANDED) && + community_regexp_include(entry->reg, com, + i)) { if (entry->direct == COMMUNITY_PERMIT) { com_index_to_delete[delete_index] = i; delete_index++; @@ -836,12 +811,6 @@ static bool community_list_dup_check(struct community_list *list, if (entry->direct != new->direct) continue; - if (entry->any != new->any) - continue; - - if (entry->any) - return true; - switch (entry->style) { case COMMUNITY_LIST_STANDARD: if (community_cmp(entry->u.com, new->u.com)) @@ -899,20 +868,17 @@ int community_list_set(struct community_list_handler *ch, const char *name, } } - if (str) { - if (style == COMMUNITY_LIST_STANDARD) - com = community_str2com(str); - else - regex = bgp_regcomp(str); + if (style == COMMUNITY_LIST_STANDARD) + com = community_str2com(str); + else + regex = bgp_regcomp(str); - if (!com && !regex) - return COMMUNITY_LIST_ERR_MALFORMED_VAL; - } + if (!com && !regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; entry = community_entry_new(); entry->direct = direct; entry->style = style; - entry->any = (str ? false : true); entry->u.com = com; entry->reg = regex; entry->seq = seqnum; @@ -989,16 +955,8 @@ struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom, for (i = 0; i < lcom->size; i++) { ptr = lcom->val + (i * LCOMMUNITY_SIZE); for (entry = list->head; entry; entry = entry->next) { - if (entry->any) { - if (entry->direct == COMMUNITY_PERMIT) { - com_index_to_delete[delete_index] = i; - delete_index++; - } - break; - } - - else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) - && lcommunity_include(entry->u.lcom, ptr)) { + if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) && + lcommunity_include(entry->u.lcom, ptr)) { if (entry->direct == COMMUNITY_PERMIT) { com_index_to_delete[delete_index] = i; delete_index++; @@ -1006,9 +964,10 @@ struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom, break; } - else if ((entry->style == LARGE_COMMUNITY_LIST_EXPANDED) - && lcommunity_regexp_include(entry->reg, lcom, - i)) { + else if ((entry->style == + LARGE_COMMUNITY_LIST_EXPANDED) && + lcommunity_regexp_include(entry->reg, lcom, + i)) { if (entry->direct == COMMUNITY_PERMIT) { com_index_to_delete[delete_index] = i; delete_index++; @@ -1127,7 +1086,6 @@ 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 ? false : true); entry->u.lcom = lcom; entry->reg = regex; entry->seq = seqnum; @@ -1248,7 +1206,6 @@ int extcommunity_list_set(struct community_list_handler *ch, const char *name, entry = community_entry_new(); entry->direct = direct; entry->style = style; - entry->any = false; if (ecom) entry->config = ecommunity_ecom2str( ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0); diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 7a9b28038c2c..8e5d637babee 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -65,9 +65,6 @@ struct community_entry { /* Standard or expanded. */ uint8_t style; - /* Any match. */ - bool any; - /* Sequence number. */ int64_t seq; diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index ff6e477355b6..f56bfc8bdc56 100644 --- a/bgpd/bgp_community.c +++ b/bgpd/bgp_community.c @@ -919,9 +919,7 @@ static void community_hash_free(void *data) void community_finish(void) { - hash_clean(comhash, community_hash_free); - hash_free(comhash); - comhash = NULL; + hash_clean_and_free(&comhash, community_hash_free); } static struct community *bgp_aggr_community_lookup( diff --git a/bgpd/bgp_community_alias.c b/bgpd/bgp_community_alias.c index 1ebe7c4c8c14..96dc1ec8f9aa 100644 --- a/bgpd/bgp_community_alias.c +++ b/bgpd/bgp_community_alias.c @@ -74,10 +74,8 @@ static void bgp_ca_free(void *ca) void bgp_community_alias_finish(void) { - hash_clean(bgp_ca_community_hash, bgp_ca_free); - hash_free(bgp_ca_community_hash); - hash_clean(bgp_ca_alias_hash, bgp_ca_free); - hash_free(bgp_ca_alias_hash); + hash_clean_and_free(&bgp_ca_community_hash, bgp_ca_free); + hash_clean_and_free(&bgp_ca_alias_hash, bgp_ca_free); } static void bgp_community_alias_show_iterator(struct hash_bucket *hb, diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c index 051e336e525b..53652f7dced0 100644 --- a/bgpd/bgp_conditional_adv.c +++ b/bgpd/bgp_conditional_adv.c @@ -150,7 +150,7 @@ static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, /* Handler of conditional advertisement timer event. * Each route in the condition-map is evaluated. */ -static void bgp_conditional_adv_timer(struct thread *t) +static void bgp_conditional_adv_timer(struct event *t) { afi_t afi; safi_t safi; @@ -165,11 +165,11 @@ static void bgp_conditional_adv_timer(struct thread *t) route_map_result_t ret; bool advmap_table_changed = false; - bgp = THREAD_ARG(t); + bgp = EVENT_ARG(t); assert(bgp); - thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, - bgp->condition_check_period, &bgp->t_condition_check); + event_add_timer(bm->master, bgp_conditional_adv_timer, bgp, + bgp->condition_check_period, &bgp->t_condition_check); /* loop through each peer and check if we have peers with * advmap_table_change attribute set, to make sure we send @@ -328,9 +328,9 @@ void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi) } /* Register for conditional routes polling timer */ - if (!thread_is_scheduled(bgp->t_condition_check)) - thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0, - &bgp->t_condition_check); + if (!event_is_scheduled(bgp->t_condition_check)) + event_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0, + &bgp->t_condition_check); } void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi) @@ -351,7 +351,7 @@ void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi) } /* Last filter removed. So cancel conditional routes polling thread. */ - THREAD_OFF(bgp->t_condition_check); + EVENT_OFF(bgp->t_condition_check); } static void peer_advertise_map_filter_update(struct peer *peer, afi_t afi, diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index 54bbf9b9ca9d..a6d0e74dc0df 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -10,7 +10,7 @@ #include "memory.h" #include "command.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "queue.h" #include "filter.h" @@ -98,17 +98,17 @@ int bgp_damp_decay(time_t tdiff, int penalty, struct bgp_damp_config *bdc) /* Handler of reuse timer event. Each route in the current reuse-list is evaluated. RFC2439 Section 4.8.7. */ -static void bgp_reuse_timer(struct thread *t) +static void bgp_reuse_timer(struct event *t) { struct bgp_damp_info *bdi; struct bgp_damp_info *next; time_t t_now, t_diff; - struct bgp_damp_config *bdc = THREAD_ARG(t); + struct bgp_damp_config *bdc = EVENT_ARG(t); bdc->t_reuse = NULL; - thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, - &bdc->t_reuse); + event_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, + &bdc->t_reuse); t_now = monotime(NULL); @@ -395,8 +395,8 @@ int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half, bgp_damp_parameter_set(half, reuse, suppress, max, bdc); /* Register reuse timer. */ - thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, - &bdc->t_reuse); + event_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, + &bdc->t_reuse); return 0; } @@ -451,7 +451,7 @@ int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi) return 0; /* Cancel reuse event. */ - THREAD_OFF(bdc->t_reuse); + EVENT_OFF(bdc->t_reuse); /* Clean BGP dampening information. */ bgp_damp_info_clean(afi, safi); diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index 129e44416456..5708e6fd55d9 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -92,7 +92,7 @@ struct bgp_damp_config { struct bgp_damp_info *no_reuse_list; /* Reuse timer thread per-set base. */ - struct thread *t_reuse; + struct event *t_reuse; afi_t afi; safi_t safi; diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index 794dd7b8b65c..fe77e7e25006 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -10,7 +10,7 @@ #include "sockunion.h" #include "command.h" #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "queue.h" #include "memory.h" @@ -69,11 +69,11 @@ struct bgp_dump { char *interval_str; - struct thread *t_interval; + struct event *t_interval; }; static int bgp_dump_unset(struct bgp_dump *bgp_dump); -static void bgp_dump_interval_func(struct thread *); +static void bgp_dump_interval_func(struct event *); /* BGP packet dump output buffer. */ struct stream *bgp_dump_obuf; @@ -154,13 +154,13 @@ static int bgp_dump_interval_add(struct bgp_dump *bgp_dump, int interval) interval = interval - secs_into_day % interval; /* always > 0 */ } - thread_add_timer(bm->master, bgp_dump_interval_func, bgp_dump, - interval, &bgp_dump->t_interval); + event_add_timer(bm->master, bgp_dump_interval_func, bgp_dump, + interval, &bgp_dump->t_interval); } else { /* One-off dump: execute immediately, don't affect any scheduled * dumps */ - thread_add_event(bm->master, bgp_dump_interval_func, bgp_dump, - 0, &bgp_dump->t_interval); + event_add_event(bm->master, bgp_dump_interval_func, bgp_dump, 0, + &bgp_dump->t_interval); } return 0; @@ -428,10 +428,10 @@ static unsigned int bgp_dump_routes_func(int afi, int first_run, return seq; } -static void bgp_dump_interval_func(struct thread *t) +static void bgp_dump_interval_func(struct event *t) { struct bgp_dump *bgp_dump; - bgp_dump = THREAD_ARG(t); + bgp_dump = EVENT_ARG(t); /* Reschedule dump even if file couldn't be opened this time... */ if (bgp_dump_open_file(bgp_dump) != NULL) { @@ -691,7 +691,7 @@ static int bgp_dump_unset(struct bgp_dump *bgp_dump) } /* Removing interval event. */ - THREAD_OFF(bgp_dump->t_interval); + EVENT_OFF(bgp_dump->t_interval); bgp_dump->interval = 0; @@ -845,8 +845,7 @@ void bgp_dump_init(void) memset(&bgp_dump_routes, 0, sizeof(bgp_dump_routes)); bgp_dump_obuf = - stream_new((BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE * 2) - + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE); + stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW); install_node(&bgp_dump_node); diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 0a4ebc513085..c408edb16677 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -355,6 +355,22 @@ bool ecommunity_cmp(const void *arg1, const void *arg2) ecom1->unit_size) == 0); } +static void ecommunity_color_str(char *buf, size_t bufsz, uint8_t *ptr) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x03 | Sub-Type(0x0b) | Flags | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Color Value | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + uint32_t colorid; + + memcpy(&colorid, ptr + 3, 4); + colorid = ntohl(colorid); + snprintf(buf, bufsz, "Color:%d", colorid); +} + /* Initialize Extended Comminities related hash. */ void ecommunity_init(void) { @@ -364,16 +380,16 @@ void ecommunity_init(void) void ecommunity_finish(void) { - hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free); - hash_free(ecomhash); - ecomhash = NULL; + hash_clean_and_free(&ecomhash, (void (*)(void *))ecommunity_hash_free); } /* Extended Communities token enum. */ enum ecommunity_token { ecommunity_token_unknown = 0, ecommunity_token_rt, + ecommunity_token_nt, ecommunity_token_soo, + ecommunity_token_color, ecommunity_token_val, ecommunity_token_rt6, ecommunity_token_val6, @@ -417,6 +433,58 @@ static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz, (void)ptr; /* consume value */ } +bool ecommunity_node_target_match(struct ecommunity *ecom, + struct in_addr *local_id) +{ + uint32_t i; + bool match = false; + + if (!ecom || !ecom->size) + return NULL; + + for (i = 0; i < ecom->size; i++) { + const uint8_t *pnt; + uint8_t type, sub_type; + + pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); + type = *pnt++; + sub_type = *pnt++; + + if (type == ECOMMUNITY_ENCODE_IP && + sub_type == ECOMMUNITY_NODE_TARGET) { + /* Node Target ID is encoded as A.B.C.D:0 */ + if (IPV4_ADDR_SAME((struct in_addr *)pnt, local_id)) + match = true; + (void)pnt; + } + } + + return match; +} + +static void ecommunity_node_target_str(char *buf, size_t bufsz, uint8_t *ptr, + int format) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Target BGP Identifier (cont.) | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + struct in_addr node_id = {}; + + IPV4_ADDR_COPY(&node_id, (struct in_addr *)ptr); + + + snprintfrr(buf, bufsz, "%s%pI4%s", + format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? "nt " : "NT:", + &node_id, + format == ECOMMUNITY_FORMAT_COMMUNITY_LIST ? ":0" : ""); + + (void)ptr; /* consume value */ +} + static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, int trans, as_t as, struct in_addr *ip, @@ -448,28 +516,22 @@ static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = sub_type; if (type == ECOMMUNITY_ENCODE_AS) { - eval->val[2] = (as >> 8) & 0xff; - eval->val[3] = as & 0xff; - eval->val[4] = (val >> 24) & 0xff; - eval->val[5] = (val >> 16) & 0xff; - eval->val[6] = (val >> 8) & 0xff; - eval->val[7] = val & 0xff; + encode_route_target_as(as, val, eval, trans); } else if (type == ECOMMUNITY_ENCODE_IP) { - memcpy(&eval->val[2], ip, sizeof(struct in_addr)); - eval->val[6] = (val >> 8) & 0xff; - eval->val[7] = val & 0xff; + if (sub_type == ECOMMUNITY_NODE_TARGET) + encode_node_target(ip, eval, trans); + else + encode_route_target_ip(ip, val, eval, trans); } 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 if (type == ECOMMUNITY_ENCODE_OPAQUE && + sub_type == ECOMMUNITY_COLOR) { + encode_color(val, eval); } else { - eval->val[2] = (as >> 24) & 0xff; - eval->val[3] = (as >> 16) & 0xff; - eval->val[4] = (as >> 8) & 0xff; - eval->val[5] = as & 0xff; - eval->val[6] = (val >> 8) & 0xff; - eval->val[7] = val & 0xff; + encode_route_target_as4(as, val, eval, trans); } return 0; @@ -488,9 +550,8 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, } /* Get next Extended Communities token from the string. */ -static const char *ecommunity_gettoken(const char *str, - void *eval_ptr, - enum ecommunity_token *token) +static const char *ecommunity_gettoken(const char *str, void *eval_ptr, + enum ecommunity_token *token, int type) { int ret; int dot = 0; @@ -502,9 +563,16 @@ static const char *ecommunity_gettoken(const char *str, struct in6_addr ip6; as_t as = 0; uint32_t val = 0; - uint8_t ecomm_type; + uint32_t val_color = 0; + uint8_t ecomm_type = 0; + uint8_t sub_type = 0; char buf[INET_ADDRSTRLEN + 1]; struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; + uint64_t tmp_as = 0; + static const char str_color[5] = "color"; + const char *ptr_color; + bool val_color_set = false; + /* Skip white space. */ while (isspace((unsigned char)*p)) { p++; @@ -515,40 +583,49 @@ static const char *ecommunity_gettoken(const char *str, if (*p == '\0') return NULL; - /* "rt" and "soo" keyword parse. */ - if (!isdigit((unsigned char)*p)) { - /* "rt" match check. */ - if (tolower((unsigned char)*p) == 'r') { + /* "rt", "nt", "soo", and "color" keyword parse. */ + /* "rt" */ + if (tolower((unsigned char)*p) == 'r') { + p++; + if (tolower((unsigned char)*p) == 't') { p++; - if (tolower((unsigned char)*p) == 't') { - p++; - if (*p != '\0' && tolower((int)*p) == '6') - *token = ecommunity_token_rt6; - else - *token = ecommunity_token_rt; - return p; - } - if (isspace((unsigned char)*p) || *p == '\0') { + if (*p != '\0' && tolower((int)*p) == '6') + *token = ecommunity_token_rt6; + else *token = ecommunity_token_rt; - return p; - } - goto error; + return p; + } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_rt; + return p; + } + goto error; + } + + /* "nt" */ + if (tolower((unsigned char)*p) == 'n') { + p++; + if (tolower((unsigned char)*p) == 't') { + p++; + *token = ecommunity_token_nt; + return p; } - /* "soo" match check. */ - else if (tolower((unsigned char)*p) == 's') { + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_nt; + return p; + } + goto error; + } + + /* "soo" */ + if (tolower((unsigned char)*p) == 's') { + p++; + if (tolower((unsigned char)*p) == 'o') { p++; if (tolower((unsigned char)*p) == 'o') { p++; - if (tolower((unsigned char)*p) == 'o') { - p++; - *token = ecommunity_token_soo; - return p; - } - if (isspace((unsigned char)*p) || *p == '\0') { - *token = ecommunity_token_soo; - return p; - } - goto error; + *token = ecommunity_token_soo; + return p; } if (isspace((unsigned char)*p) || *p == '\0') { *token = ecommunity_token_soo; @@ -556,9 +633,29 @@ static const char *ecommunity_gettoken(const char *str, } goto error; } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_soo; + return p; + } goto error; } + /* "color" */ + if (tolower((unsigned char)*p) == 'c') { + ptr_color = &str_color[0]; + for (unsigned int i = 0; i < 5; i++) { + if (tolower((unsigned char)*p) != *ptr_color) + break; + + p++; + ptr_color++; + } + if (isspace((unsigned char)*p) || *p == '\0') { + *token = ecommunity_token_color; + return p; + } + goto error; + } /* What a mess, there are several possibilities: * * a) A.B.C.D:MN @@ -583,9 +680,18 @@ static const char *ecommunity_gettoken(const char *str, goto error; endptr++; - as = strtoul(endptr, &endptr, 10); - if (*endptr != '\0' || as == BGP_AS4_MAX) + errno = 0; + tmp_as = strtoul(endptr, &endptr, 10); + /* 'unsigned long' is a uint64 on 64-bit + * systems, and uint32 on 32-bit systems. So for + * 64-bit we can just directly check the value + * against BGP_AS4_MAX/UINT32_MAX, and for + * 32-bit we can check for errno (set to ERANGE + * upon overflow). + */ + if (*endptr != '\0' || tmp_as == BGP_AS4_MAX || errno) goto error; + as = (as_t)tmp_as; memcpy(buf, p, (limit - p)); buf[limit - p] = '\0'; @@ -627,9 +733,19 @@ static const char *ecommunity_gettoken(const char *str, goto error; } else { /* ASN */ - as = strtoul(buf, &endptr, 10); - if (*endptr != '\0' || as == BGP_AS4_MAX) + errno = 0; + tmp_as = strtoul(buf, &endptr, 10); + /* 'unsigned long' is a uint64 on 64-bit + * systems, and uint32 on 32-bit systems. So for + * 64-bit we can just directly check the value + * against BGP_AS4_MAX/UINT32_MAX, and for + * 32-bit we can check for errno (set to ERANGE + * upon overflow). + */ + if (*endptr != '\0' || tmp_as > BGP_AS4_MAX || + errno) goto error; + as = (as_t)tmp_as; } } else if (*p == '.') { if (separator) @@ -640,17 +756,24 @@ static const char *ecommunity_gettoken(const char *str, } else { digit = 1; - /* We're past the IP/ASN part */ + /* We're past the IP/ASN part, + * or we have a color + */ if (separator) { val *= 10; val += (*p - '0'); + val_color_set = false; + } else { + val_color *= 10; + val_color += (*p - '0'); + val_color_set = true; } } p++; } /* Low digit part must be there. */ - if (!digit || !separator) + if (!digit && (!separator || !val_color_set)) goto error; /* Encode result into extended community. */ @@ -658,9 +781,15 @@ static const char *ecommunity_gettoken(const char *str, ecomm_type = ECOMMUNITY_ENCODE_IP; else if (as > BGP_AS_MAX) ecomm_type = ECOMMUNITY_ENCODE_AS4; - else + else if (as > 0) ecomm_type = ECOMMUNITY_ENCODE_AS; - if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval)) + else if (val_color) { + ecomm_type = ECOMMUNITY_ENCODE_OPAQUE; + sub_type = ECOMMUNITY_COLOR; + val = val_color; + } + + if (ecommunity_encode(ecomm_type, sub_type, 1, as, ip, val, eval)) goto error; *token = ecommunity_token_val; return p; @@ -681,11 +810,13 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, if (is_ipv6_extcomm) token = ecommunity_token_rt6; - while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { + while ((str = ecommunity_gettoken(str, (void *)&eval, &token, type))) { switch (token) { case ecommunity_token_rt: + case ecommunity_token_nt: case ecommunity_token_rt6: case ecommunity_token_soo: + case ecommunity_token_color: if (!keyword_included || keyword) { if (ecom) ecommunity_free(&ecom); @@ -694,12 +825,14 @@ static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, keyword = 1; if (token == ecommunity_token_rt || - token == ecommunity_token_rt6) { + token == ecommunity_token_rt6) type = ECOMMUNITY_ROUTE_TARGET; - } - if (token == ecommunity_token_soo) { + if (token == ecommunity_token_soo) type = ECOMMUNITY_SITE_ORIGIN; - } + if (token == ecommunity_token_nt) + type = ECOMMUNITY_NODE_TARGET; + if (token == ecommunity_token_color) + type = ECOMMUNITY_COLOR; break; case ecommunity_token_val: if (keyword_included) { @@ -910,26 +1043,26 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt, } /* 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:2soo 100:3" - - extcommunity-list - "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: - - ECOMMUNITY_FORMAT_ROUTE_MAP - ECOMMUNITY_FORMAT_COMMUNITY_LIST - ECOMMUNITY_FORMAT_DISPLAY - - Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases. - 0 value displays all -*/ + * Due to historical reason of industry standard implementation, there + * are three types of format: + * + * route-map set extcommunity format: + * "rt 100:1 100:2soo 100:3" + * + * extcommunity-list: + * "rt 100:1 rt 100:2 soo 100:3" + * + * show bgp: + * "RT:100:1 RT:100:2 SoO:100:3" + * + * For each format please use below definition for format: + * ECOMMUNITY_FORMAT_ROUTE_MAP + * ECOMMUNITY_FORMAT_COMMUNITY_LIST + * ECOMMUNITY_FORMAT_DISPLAY + * + * Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases. + * 0 value displays all. + */ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) { uint32_t i; @@ -981,6 +1114,11 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecommunity_lb_str( encbuf, sizeof(encbuf), pnt, ecom->disable_ieee_floating); + } else if (sub_type == ECOMMUNITY_NODE_TARGET && + type == ECOMMUNITY_ENCODE_IP) { + ecommunity_node_target_str( + encbuf, sizeof(encbuf), pnt, + format); } else unk_ecom = 1; } else { @@ -1001,6 +1139,9 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) { strlcpy(encbuf, "Default Gateway", sizeof(encbuf)); + } else if (*pnt == ECOMMUNITY_COLOR) { + ecommunity_color_str(encbuf, sizeof(encbuf), + pnt); } else { unk_ecom = 1; } @@ -1190,6 +1331,13 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) ecom->disable_ieee_floating); else unk_ecom = 1; + } else if (type == ECOMMUNITY_ENCODE_IP_NON_TRANS) { + sub_type = *pnt++; + if (sub_type == ECOMMUNITY_NODE_TARGET) + ecommunity_node_target_str( + encbuf, sizeof(encbuf), pnt, format); + else + unk_ecom = 1; } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) { sub_type = *pnt++; if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE) @@ -1261,6 +1409,29 @@ bool ecommunity_match(const struct ecommunity *ecom1, return false; } +/* return last occurence of color */ +/* it will be the greatest color value */ +extern uint32_t ecommunity_select_color(const struct ecommunity *ecom) +{ + + uint32_t aux_color = 0; + uint8_t *p; + uint32_t c = 0; + + /* If the value already exists in the structure return 0. */ + + for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { + if (p == NULL) + break; + + if (p[0] == ECOMMUNITY_ENCODE_OPAQUE && + p[1] == ECOMMUNITY_COLOR) + ptr_get_be32((const uint8_t *)&p[4], &aux_color); + } + return aux_color; +} + + /* return first occurence of type */ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, uint8_t type, uint8_t subtype) @@ -1673,3 +1844,18 @@ struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom, return new; } + +bool soo_in_ecom(struct ecommunity *ecom, struct ecommunity *soo) +{ + if (ecom && soo) { + if ((ecommunity_lookup(ecom, ECOMMUNITY_ENCODE_AS, + ECOMMUNITY_SITE_ORIGIN) || + ecommunity_lookup(ecom, ECOMMUNITY_ENCODE_AS4, + ECOMMUNITY_SITE_ORIGIN) || + ecommunity_lookup(ecom, ECOMMUNITY_ENCODE_IP, + ECOMMUNITY_SITE_ORIGIN)) && + ecommunity_include(ecom, soo)) + return true; + } + return false; +} diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h index 83a1584489a9..7dc04d206af9 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -46,6 +46,8 @@ #define ECOMMUNITY_REDIRECT_VRF 0x08 #define ECOMMUNITY_TRAFFIC_MARKING 0x09 #define ECOMMUNITY_REDIRECT_IP_NH 0x00 +#define ECOMMUNITY_COLOR 0x0b /* RFC9012 - color */ + /* from IANA: bgp-extended-communities/bgp-extended-communities.xhtml * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect */ @@ -101,6 +103,10 @@ enum ecommunity_origin_validation_states { /* Extended Community readable string length */ #define ECOMMUNITY_STRLEN 64 +/* Node Target Extended Communities */ +#define ECOMMUNITY_NODE_TARGET 0x09 +#define ECOMMUNITY_NODE_TARGET_RESERVED 0 + /* Extended Communities attribute. */ struct ecommunity { /* Reference counter. */ @@ -155,9 +161,12 @@ struct ecommunity_val_ipv6 { * Encode BGP Route Target AS:nn. */ static inline void encode_route_target_as(as_t as, uint32_t val, - struct ecommunity_val *eval) + struct ecommunity_val *eval, + bool trans) { eval->val[0] = ECOMMUNITY_ENCODE_AS; + if (!trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[2] = (as >> 8) & 0xff; eval->val[3] = as & 0xff; @@ -170,12 +179,15 @@ static inline void encode_route_target_as(as_t as, uint32_t val, /* * Encode BGP Route Target IP:nn. */ -static inline void encode_route_target_ip(struct in_addr ip, uint16_t val, - struct ecommunity_val *eval) +static inline void encode_route_target_ip(struct in_addr *ip, uint16_t val, + struct ecommunity_val *eval, + bool trans) { eval->val[0] = ECOMMUNITY_ENCODE_IP; + if (!trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = ECOMMUNITY_ROUTE_TARGET; - 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; } @@ -184,9 +196,12 @@ static inline void encode_route_target_ip(struct in_addr ip, uint16_t val, * Encode BGP Route Target AS4:nn. */ static inline void encode_route_target_as4(as_t as, uint16_t val, - struct ecommunity_val *eval) + struct ecommunity_val *eval, + bool trans) { eval->val[0] = ECOMMUNITY_ENCODE_AS4; + if (!trans) + eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; eval->val[1] = ECOMMUNITY_ROUTE_TARGET; eval->val[2] = (as >> 24) & 0xff; eval->val[3] = (as >> 16) & 0xff; @@ -257,6 +272,55 @@ static inline void encode_origin_validation_state(enum rpki_states state, eval->val[7] = ovs_state; } +static inline void encode_node_target(struct in_addr *node_id, + struct ecommunity_val *eval, bool trans) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x01 or 0x41 | Sub-Type(0x09) | Target BGP Identifier | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Target BGP Identifier (cont.) | Reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_IP; + if (!trans) + eval->val[0] |= ECOMMUNITY_ENCODE_IP_NON_TRANS; + eval->val[1] = ECOMMUNITY_NODE_TARGET; + memcpy(&eval->val[2], node_id, sizeof(*node_id)); + eval->val[6] = ECOMMUNITY_NODE_TARGET_RESERVED; + eval->val[7] = ECOMMUNITY_NODE_TARGET_RESERVED; +} + +/* + * Encode BGP Color extended community + * is's a transitive opaque Extended community (RFC 9012 4.3) + * flag is set to 0 + * RFC 9012 14.10: No values have currently been registered. + * 4.3: this field MUST be set to zero by the originator + * and ignored by the receiver; + * + */ +static inline void encode_color(uint32_t color_id, struct ecommunity_val *eval) +{ + /* + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | 0x03 | Sub-Type(0x0b) | Flags | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Color Value | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + memset(eval, 0, sizeof(*eval)); + eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE; + eval->val[1] = ECOMMUNITY_COLOR; + eval->val[2] = 0x00; + eval->val[3] = 0x00; + eval->val[4] = (color_id >> 24) & 0xff; + eval->val[5] = (color_id >> 16) & 0xff; + eval->val[6] = (color_id >> 8) & 0xff; + eval->val[7] = color_id & 0xff; +} + extern void ecommunity_init(void); extern void ecommunity_finish(void); extern void ecommunity_free(struct ecommunity **); @@ -281,10 +345,11 @@ extern void ecommunity_strfree(char **s); extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2); extern bool ecommunity_match(const struct ecommunity *, const struct ecommunity *); -extern char *ecommunity_str(struct ecommunity *); +extern char *ecommunity_str(struct ecommunity *ecom); extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *, uint8_t, uint8_t); +extern uint32_t ecommunity_select_color(const struct ecommunity *ecom); extern bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, bool unique, bool overwrite); @@ -327,6 +392,8 @@ extern struct ecommunity *ecommunity_replace_linkbw(as_t as, uint64_t cum_bw, bool disable_ieee_floating); +extern bool soo_in_ecom(struct ecommunity *ecom, struct ecommunity *soo); + static inline void ecommunity_strip_rts(struct ecommunity *ecom) { uint8_t subtype = ECOMMUNITY_ROUTE_TARGET; @@ -338,4 +405,9 @@ static inline void ecommunity_strip_rts(struct ecommunity *ecom) extern struct ecommunity * ecommunity_add_origin_validation_state(enum rpki_states rpki_state, struct ecommunity *ecom); +extern struct ecommunity *ecommunity_add_node_target(struct in_addr *node_id, + struct ecommunity *old, + bool non_trans); +extern bool ecommunity_node_target_match(struct ecommunity *ecomm, + struct in_addr *local_id); #endif /* _QUAGGA_BGP_ECOMMUNITY_H */ diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index a8560ab53922..a33f59cf5b10 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -41,6 +41,7 @@ #include "bgpd/bgp_nht.h" #include "bgpd/bgp_trace.h" #include "bgpd/bgp_mpath.h" +#include "bgpd/bgp_packet.h" /* * Definitions and external declarations. @@ -48,6 +49,7 @@ DEFINE_QOBJ_TYPE(bgpevpn); DEFINE_QOBJ_TYPE(bgp_evpn_es); +DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); DEFINE_MTYPE_STATIC(BGPD, VRF_ROUTE_TARGET, "L3 Route Target"); /* @@ -577,7 +579,7 @@ static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl, if (bgp->advertise_autort_rfc8365) vni |= EVPN_AUTORT_VXLAN; - encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true); ecomadd = ecommunity_new(); ecommunity_add_val(ecomadd, &eval, false, false); @@ -674,11 +676,9 @@ struct bgp_dest *bgp_evpn_global_node_get(struct bgp_table *table, afi_t afi, /* * Wrapper for node lookup in global table. */ -struct bgp_dest * -bgp_evpn_global_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, - const struct prefix_evpn *evp, - struct prefix_rd *prd, - const struct bgp_path_info *local_pi) +struct bgp_dest *bgp_evpn_global_node_lookup( + struct bgp_table *table, safi_t safi, const struct prefix_evpn *evp, + struct prefix_rd *prd, const struct bgp_path_info *local_pi) { struct prefix_evpn global_p; @@ -709,7 +709,7 @@ bgp_evpn_global_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, evp = &global_p; } - return bgp_afi_node_lookup(table, afi, safi, (struct prefix *)evp, prd); + return bgp_safi_node_lookup(table, safi, (struct prefix *)evp, prd); } /* @@ -936,7 +936,10 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn, frrtrace(5, frr_bgp, evpn_mac_ip_zsend, add, vpn, p, remote_vtep_ip, esi); - return zclient_send_message(zclient); + if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE) + return -1; + + return 0; } /* @@ -988,7 +991,10 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn, frrtrace(3, frr_bgp, evpn_bum_vtep_zsend, add, vpn, p); - return zclient_send_message(zclient); + if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE) + return -1; + + return 0; } /* @@ -1052,7 +1058,8 @@ static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf, * type-2 routes. */ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, - int add_l3_ecomm) + int add_l3_ecomm, + struct ecommunity *macvrf_soo) { struct ecommunity ecom_encap; struct ecommunity ecom_sticky; @@ -1149,6 +1156,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr, attr, ecommunity_merge(bgp_attr_get_ecommunity(attr), &ecom_na)); } + + /* Add MAC-VRF SoO, if configured */ + if (macvrf_soo) + bgp_attr_set_ecommunity( + attr, ecommunity_merge(attr->ecommunity, macvrf_soo)); } /* @@ -1358,7 +1370,7 @@ static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn, * L3VPN routes. */ global_dest = bgp_evpn_global_node_lookup( - bgp->rib[afi][safi], afi, safi, + bgp->rib[afi][safi], safi, (const struct prefix_evpn *)bgp_dest_get_prefix(dest), &vpn->prd, old_local); if (global_dest) { @@ -1515,14 +1527,9 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn, struct bgp_path_info *tmp_pi = NULL; *route_changed = 0; - /* locate the local route entry if any */ - 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 - && tmp_pi->sub_type == BGP_ROUTE_STATIC) - local_pi = tmp_pi; - } + + /* See if this is an update of an existing route, or a new add. */ + local_pi = bgp_evpn_route_get_local_path(bgp_evpn, dest); /* * create a new route entry if one doesn't exist. @@ -1837,6 +1844,7 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, struct bgp_path_info *tmp_pi; struct bgp_path_info *local_pi; struct attr *attr_new; + struct attr local_attr; mpls_label_t label[BGP_MAX_LABELS]; uint32_t num_labels = 1; int route_change = 1; @@ -1870,13 +1878,15 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, add_mac_mobility_to_attr(seq, attr); if (!local_pi) { - /* Add (or update) attribute to hash. */ - attr_new = bgp_attr_intern(attr); + local_attr = *attr; /* Extract MAC mobility sequence number, if any. */ - attr_new->mm_seqnum = - bgp_attr_mac_mobility_seqnum(attr_new, &sticky); - attr_new->sticky = sticky; + local_attr.mm_seqnum = + bgp_attr_mac_mobility_seqnum(&local_attr, &sticky); + local_attr.sticky = sticky; + + /* Add (or update) attribute to hash. */ + attr_new = bgp_attr_intern(&local_attr); /* Create new route with its attribute. */ tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, @@ -1952,14 +1962,16 @@ 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); + local_attr = *attr; bgp_path_info_set_flag(dest, tmp_pi, BGP_PATH_ATTR_CHANGED); /* Extract MAC mobility sequence number, if any. */ - attr_new->mm_seqnum = - bgp_attr_mac_mobility_seqnum(attr_new, &sticky); - attr_new->sticky = sticky; + local_attr.mm_seqnum = bgp_attr_mac_mobility_seqnum( + &local_attr, &sticky); + local_attr.sticky = sticky; + + attr_new = bgp_attr_intern(&local_attr); /* Restore route, if needed. */ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED)) @@ -2070,6 +2082,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, int route_change; bool old_is_sync = false; bool mac_only = false; + struct ecommunity *macvrf_soo = NULL; memset(&attr, 0, sizeof(attr)); @@ -2127,8 +2140,11 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok( vpn, p, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); + if (bgp->evpn_info) + macvrf_soo = bgp->evpn_info->soo; + /* Set up extended community. */ - build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); + build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm, macvrf_soo); /* First, create (or fetch) route node within the VNI. * NOTE: There is no RD here. @@ -2255,8 +2271,8 @@ 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 */ - dest = bgp_evpn_global_node_lookup(bgp_evpn->rib[afi][safi], afi, safi, - evp, &bgp_vrf->vrf_prd, NULL); + dest = bgp_evpn_global_node_lookup(bgp_evpn->rib[afi][safi], safi, evp, + &bgp_vrf->vrf_prd, NULL); if (!dest) return 0; @@ -2292,8 +2308,8 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn, * this table is a 2-level tree (RD-level + Prefix-level) similar to * L3VPN routes. */ - global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], afi, - safi, p, &vpn->prd, NULL); + global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], safi, p, + &vpn->prd, NULL); if (global_dest) { /* Delete route entry in the global EVPN table. */ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); @@ -2335,6 +2351,7 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, struct prefix_evpn evp; int route_change; bool old_is_sync = false; + struct ecommunity *macvrf_soo = NULL; if (CHECK_FLAG(local_pi->flags, BGP_PATH_REMOVED)) return; @@ -2382,8 +2399,11 @@ void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn, vpn, &evp, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL); + if (bgp->evpn_info) + macvrf_soo = bgp->evpn_info->soo; + /* Set up extended community. */ - build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm); + build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm, macvrf_soo); seq = mac_mobility_seqnum(local_pi->attr); if (bgp_debug_zebra(NULL)) { @@ -2675,6 +2695,21 @@ int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn) return 0; } +/* Update Type-2/3 Routes for L2VNI. + * Called by hash_iterate() + */ +static void update_routes_for_vni_hash(struct hash_bucket *bucket, + struct bgp *bgp) +{ + struct bgpevpn *vpn; + + if (!bucket) + return; + + vpn = (struct bgpevpn *)bucket->data; + update_routes_for_vni(bgp, vpn); +} + /* * Delete (and withdraw) local routes for specified VNI from the global * table and per-VNI table. After this, remove all other routes from @@ -2722,43 +2757,60 @@ static int bgp_evpn_mcast_grp_change(struct bgp *bgp, struct bgpevpn *vpn, } /* - * There is a tunnel endpoint IP address change for this VNI, delete - * prior type-3 route (if needed) and update. + * If there is a tunnel endpoint IP address (VTEP-IP) change for this VNI. + - Deletes tip_hash entry for old VTEP-IP + - Adds tip_hash entry/refcount for new VTEP-IP + - Deletes prior type-3 route for L2VNI (if needed) + - Updates originator_ip * Note: Route re-advertisement happens elsewhere after other processing * other changes. */ -static void handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, +static void handle_tunnel_ip_change(struct bgp *bgp_vrf, struct bgp *bgp_evpn, + struct bgpevpn *vpn, struct in_addr originator_ip) { struct prefix_evpn p; + struct in_addr old_vtep_ip; + + if (bgp_vrf) /* L3VNI */ + old_vtep_ip = bgp_vrf->originator_ip; + else /* L2VNI */ + old_vtep_ip = vpn->originator_ip; - if (IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)) + /* TIP didn't change, nothing to do */ + if (IPV4_ADDR_SAME(&old_vtep_ip, &originator_ip)) return; - /* If VNI is not live, we only need to update the originator ip */ - if (!is_vni_live(vpn)) { + /* If L2VNI is not live, we only need to update the originator_ip. + * L3VNIs are updated immediately, so we can't bail out early. + */ + if (!bgp_vrf && !is_vni_live(vpn)) { vpn->originator_ip = originator_ip; return; } /* Update the tunnel-ip hash */ - bgp_tip_del(bgp, &vpn->originator_ip); - if (bgp_tip_add(bgp, &originator_ip)) + bgp_tip_del(bgp_evpn, &old_vtep_ip); + if (bgp_tip_add(bgp_evpn, &originator_ip)) /* The originator_ip was not already present in the * bgp martian next-hop table as a tunnel-ip, so we * need to go back and filter routes matching the new * martian next-hop. */ - bgp_filter_evpn_routes_upon_martian_nh_change(bgp); + bgp_filter_evpn_routes_upon_martian_change(bgp_evpn, + BGP_MARTIAN_TUN_IP); - /* Need to withdraw type-3 route as the originator IP is part - * of the key. - */ - build_evpn_type3_prefix(&p, vpn->originator_ip); - delete_evpn_route(bgp, vpn, &p); + if (!bgp_vrf) { + /* Need to withdraw type-3 route as the originator IP is part + * of the key. + */ + build_evpn_type3_prefix(&p, vpn->originator_ip); + delete_evpn_route(bgp_evpn, vpn, &p); + + vpn->originator_ip = originator_ip; + } else + bgp_vrf->originator_ip = originator_ip; - /* Update the tunnel IP and re-advertise all routes for this VNI. */ - vpn->originator_ip = originator_ip; return; } @@ -3273,6 +3325,9 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf, bgp_aggregate_decrement(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi, safi); + /* Force deletion */ + SET_FLAG(dest->flags, BGP_NODE_PROCESS_CLEAR); + /* Mark entry for deletion */ bgp_path_info_delete(dest, pi); @@ -3368,7 +3423,7 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn, /* * Given a route entry and a VRF, see if this route entry should be - * imported into the VRF i.e., RTs match. + * imported into the VRF i.e., RTs match + Site-of-Origin check passes. */ static int is_route_matching_for_vrf(struct bgp *bgp_vrf, struct bgp_path_info *pi) @@ -3500,6 +3555,41 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn, return 0; } +static bool bgp_evpn_route_matches_macvrf_soo(struct bgp_path_info *pi, + const struct prefix_evpn *evp) +{ + struct bgp *bgp_evpn = bgp_get_evpn(); + struct ecommunity *macvrf_soo; + bool ret = false; + + if (!bgp_evpn->evpn_info) + return false; + + /* We only stamp the mac-vrf soo on routes from our local L2VNI. + * No need to filter additional EVPN routes that originated outside + * the MAC-VRF/L2VNI. + */ + if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE && + evp->prefix.route_type != BGP_EVPN_IMET_ROUTE) + return false; + + macvrf_soo = bgp_evpn->evpn_info->soo; + ret = route_matches_soo(pi, macvrf_soo); + + if (ret && bgp_debug_zebra(NULL)) { + char *ecom_str; + + ecom_str = ecommunity_ecom2str(macvrf_soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + zlog_debug( + "import of evpn prefix %pFX skipped, local mac-vrf soo %s", + evp, ecom_str); + ecommunity_strfree(&ecom_str); + } + + return ret; +} + /* 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. @@ -3585,8 +3675,9 @@ int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf, return 0; /* don't import hosts that are locally attached */ - if (install && bgp_evpn_skip_vrf_import_of_local_es( - bgp_vrf, evp, pi, install)) + if (install && (bgp_evpn_skip_vrf_import_of_local_es( + bgp_vrf, evp, pi, install) || + bgp_evpn_route_matches_macvrf_soo(pi, evp))) return 0; if (install) @@ -3715,30 +3806,35 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp, && pi->sub_type == BGP_ROUTE_NORMAL)) continue; - if (is_route_matching_for_vni(bgp, vpn, pi)) { - if (install) - ret = install_evpn_route_entry( - bgp, vpn, evp, pi); - else - ret = uninstall_evpn_route_entry( - bgp, vpn, evp, pi); - - if (ret) { - flog_err( - EC_BGP_EVPN_FAIL, - "%u: Failed to %s EVPN %s route in VNI %u", - bgp->vrf_id, - install ? "install" - : "uninstall", - rtype == BGP_EVPN_MAC_IP_ROUTE - ? "MACIP" - : "IMET", - vpn->vni); - - bgp_dest_unlock_node(rd_dest); - bgp_dest_unlock_node(dest); - return ret; - } + if (!is_route_matching_for_vni(bgp, vpn, pi)) + continue; + + if (install) { + if (bgp_evpn_route_matches_macvrf_soo( + pi, evp)) + continue; + + ret = install_evpn_route_entry(bgp, vpn, + evp, pi); + } else + ret = uninstall_evpn_route_entry( + bgp, vpn, evp, pi); + + if (ret) { + flog_err( + EC_BGP_EVPN_FAIL, + "%u: Failed to %s EVPN %s route in VNI %u", + bgp->vrf_id, + install ? "install" + : "uninstall", + rtype == BGP_EVPN_MAC_IP_ROUTE + ? "MACIP" + : "IMET", + vpn->vni); + + bgp_dest_unlock_node(rd_dest); + bgp_dest_unlock_node(dest); + return ret; } } } @@ -3944,6 +4040,12 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi, if (!ecom || !ecom->size) return -1; + /* Filter routes carrying a Site-of-Origin that matches our + * local MAC-VRF SoO. + */ + if (import && bgp_evpn_route_matches_macvrf_soo(pi, evp)) + return 0; + /* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs * attached to the route */ for (i = 0; i < ecom->size; i++) { @@ -4312,8 +4414,8 @@ 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_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], afi, - safi, &p, &vpn->prd, NULL); + global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], safi, &p, + &vpn->prd, NULL); if (global_dest) { /* Delete route entry in the global EVPN table. */ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi); @@ -5165,7 +5267,7 @@ void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl, if (bgp->advertise_autort_rfc8365) vni |= EVPN_AUTORT_VXLAN; - encode_route_target_as((bgp->as & 0xFFFF), vni, &eval); + encode_route_target_as((bgp->as & 0xFFFF), vni, &eval, true); ecom_auto = ecommunity_new(); ecommunity_add_val(ecom_auto, &eval, false, false); @@ -5491,6 +5593,46 @@ void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn, update_advertise_vni_routes(bgp, vpn); } +/* "mac-vrf soo" vty handler + * Handle change to the global MAC-VRF Site-of-Origin: + * - Unimport routes with new SoO from VNI/VRF + * - Import routes with old SoO into VNI/VRF + * - Update SoO on local VNI routes + re-advertise + */ +void bgp_evpn_handle_global_macvrf_soo_change(struct bgp *bgp, + struct ecommunity *new_soo) +{ + struct ecommunity *old_soo; + + old_soo = bgp->evpn_info->soo; + + /* cleanup and bail out if old_soo == new_soo */ + if (ecommunity_match(old_soo, new_soo)) { + ecommunity_free(&new_soo); + return; + } + + /* set new_soo */ + bgp->evpn_info->soo = new_soo; + + /* Unimport routes matching the new_soo */ + bgp_filter_evpn_routes_upon_martian_change(bgp, BGP_MARTIAN_SOO); + + /* Reimport routes with old_soo and !new_soo. + */ + bgp_reimport_evpn_routes_upon_martian_change( + bgp, BGP_MARTIAN_SOO, (void *)old_soo, (void *)new_soo); + + /* Update locally originated routes for all L2VNIs */ + hash_iterate(bgp->vnihash, + (void (*)(struct hash_bucket *, + void *))update_routes_for_vni_hash, + bgp); + + /* clear old_soo */ + ecommunity_free(&old_soo); +} + /* * Install routes for this VNI. Invoked upon change to Import RT. */ @@ -5676,7 +5818,7 @@ void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, } int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw) + struct bgp_nlri *packet, bool withdraw) { uint8_t *pnt; uint8_t *lim; @@ -6058,8 +6200,12 @@ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, return install_uninstall_evpn_route(bgp, afi, safi, p, pi, 0); } -/* filter routes which have martian next hops */ -int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) +/* Refresh previously-discarded EVPN routes carrying "self" MAC-VRF SoO. + * Walk global EVPN rib + import remote routes with old_soo && !new_soo. + */ +void bgp_reimport_evpn_routes_upon_macvrf_soo_change(struct bgp *bgp, + struct ecommunity *old_soo, + struct ecommunity *new_soo) { afi_t afi; safi_t safi; @@ -6070,12 +6216,9 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) afi = AFI_L2VPN; safi = SAFI_EVPN; - /* Walk entire global routing table and evaluate routes which could be - * imported into this VPN. Note that we cannot just look at the routes - * for the VNI's RD - - * remote routes applicable for this VNI could have any RD. + /* EVPN routes are a 2-level table: outer=prefix_rd, inner=prefix_evpn. + * A remote route could have any RD, so we need to walk them all. */ - /* EVPN routes are a 2-level table. */ 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); @@ -6084,21 +6227,132 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + const struct prefix *p; + struct prefix_evpn *evp; + + p = bgp_dest_get_prefix(dest); + evp = (struct prefix_evpn *)p; + + /* On export we only add MAC-VRF SoO to RT-2/3, so we + * can skip evaluation of other RTs. + */ + if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE && + evp->prefix.route_type != BGP_EVPN_IMET_ROUTE) + continue; for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { + bool old_soo_fnd = false; + bool new_soo_fnd = false; - /* Consider "valid" remote routes applicable for - * this VNI. */ + /* Only consider routes learned from peers */ + if (!(pi->type == ZEBRA_ROUTE_BGP && + pi->sub_type == BGP_ROUTE_NORMAL)) + continue; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)) + continue; + + old_soo_fnd = route_matches_soo(pi, old_soo); + new_soo_fnd = route_matches_soo(pi, new_soo); + + if (old_soo_fnd && !new_soo_fnd) { + if (bgp_debug_update(pi->peer, p, NULL, + 1)) { + char attr_str[BUFSIZ] = {0}; + + bgp_dump_attr(pi->attr, + attr_str, BUFSIZ); + + zlog_debug( + "mac-vrf soo changed: evaluating reimport of prefix %pBD with attr %s", + dest, attr_str); + } + + bgp_evpn_import_route(bgp, afi, safi, p, + pi); + } + } + } + } +} + +/* Filter learned (!local) EVPN routes carrying "self" attributes. + * Walk the Global EVPN loc-rib unimporting martian routes from the appropriate + * L2VNIs (MAC-VRFs) / L3VNIs (IP-VRFs), and deleting them from the Global + * loc-rib when applicable (based on martian_type). + * This function is the handler for new martian entries, which is triggered by + * events occurring on the local system, + * e.g. + * - New VTEP-IP + * + bgp_zebra_process_local_vni + * + bgp_zebra_process_local_l3vni + * - New MAC-VRF Site-of-Origin + * + bgp_evpn_handle_global_macvrf_soo_change + * This will likely be extended in the future to cover these events too: + * - New Interface IP + * + bgp_interface_address_add + * - New Interface MAC + * + bgp_ifp_up + * + bgp_ifp_create + * - New RMAC + * + bgp_zebra_process_local_l3vni + */ +void bgp_filter_evpn_routes_upon_martian_change( + struct bgp *bgp, enum bgp_martian_type martian_type) +{ + afi_t afi; + safi_t safi; + struct bgp_dest *rd_dest, *dest; + struct bgp_table *table; + struct bgp_path_info *pi; + struct ecommunity *macvrf_soo; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + macvrf_soo = bgp->evpn_info->soo; + + /* EVPN routes are a 2-level table: outer=prefix_rd, inner=prefix_evpn. + * A remote route could have any RD, so we need to walk them all. + */ + 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 (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + bool affected = false; + const struct prefix *p; + + /* Only consider routes learned from peers */ 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); + p = bgp_dest_get_prefix(dest); + + switch (martian_type) { + case BGP_MARTIAN_TUN_IP: + affected = bgp_nexthop_self( + bgp, afi, pi->type, + pi->sub_type, pi->attr, dest); + break; + case BGP_MARTIAN_SOO: + affected = route_matches_soo( + pi, macvrf_soo); + break; + case BGP_MARTIAN_IF_IP: + case BGP_MARTIAN_IF_MAC: + case BGP_MARTIAN_RMAC: + break; + } + + if (affected) { if (bgp_debug_update(pi->peer, p, NULL, 1)) { char attr_str[BUFSIZ] = {0}; @@ -6108,21 +6362,116 @@ int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp) sizeof(attr_str)); zlog_debug( - "%u: prefix %pBD with attr %s - DENIED due to martian or self nexthop", + "%u: prefix %pBD with attr %s - DISCARDED due to Martian/%s", bgp->vrf_id, dest, - attr_str); + attr_str, + bgp_martian_type2str( + martian_type)); } + + bgp_evpn_unimport_route(bgp, afi, safi, p, pi); - bgp_rib_remove(dest, pi, pi->peer, afi, - safi); + /* For now, retain existing handling of + * tip_hash updates: (Self SoO routes + * are unimported from L2VNI/VRF but + * retained in global loc-rib, but Self + * IP/MAC routes are also deleted from + * global loc-rib). + * TODO: use consistent handling for all + * martian types + */ + if (martian_type == BGP_MARTIAN_TUN_IP) + bgp_rib_remove(dest, pi, + pi->peer, afi, + safi); } } } } +} - return 0; +/* Refresh previously-discarded EVPN routes carrying "self" attributes. + * This function is the handler for deleted martian entries, which is triggered + * by events occurring on the local system, + * e.g. + * - Del MAC-VRF Site-of-Origin + * + bgp_evpn_handle_global_macvrf_soo_change + * This will likely be extended in the future to cover these events too: + * - Del VTEP-IP + * + bgp_zebra_process_local_vni + * + bgp_zebra_process_local_l3vni + * - Del Interface IP + * + bgp_interface_address_delete + * - Del Interface MAC + * + bgp_ifp_down + * + bgp_ifp_destroy + * - Del RMAC + * + bgp_zebra_process_local_l3vni + */ +void bgp_reimport_evpn_routes_upon_martian_change( + struct bgp *bgp, enum bgp_martian_type martian_type, void *old_martian, + void *new_martian) +{ + struct listnode *node; + struct peer *peer; + safi_t safi; + afi_t afi; + struct ecommunity *old_soo, *new_soo; + + afi = AFI_L2VPN; + safi = SAFI_EVPN; + + /* Self-SoO routes are held in the global EVPN loc-rib, so we can + * reimport routes w/o triggering soft-reconfig/route-refresh. + */ + if (martian_type == BGP_MARTIAN_SOO) { + old_soo = (struct ecommunity *)old_martian; + new_soo = (struct ecommunity *)new_martian; + + /* If !old_soo, then we can skip the reimport because we + * wouldn't have filtered anything via the self-SoO import check + */ + if (old_martian) + bgp_reimport_evpn_routes_upon_macvrf_soo_change( + bgp, old_soo, new_soo); + + return; + } + + /* Self-TIP/IP/MAC/RMAC routes are deleted from the global EVPN + * loc-rib, so we need to re-learn the routes via soft-reconfig/ + * route-refresh. + */ + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + + if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) + continue; + + if (peer->status != Established) + continue; + + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_SOFT_RECONFIG)) { + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug( + "Processing EVPN Martian/%s change on peer %s (inbound, soft-reconfig)", + bgp_martian_type2str(martian_type), + peer->host); + + bgp_soft_reconfig_in(peer, afi, safi); + } else { + if (bgp_debug_update(peer, NULL, NULL, 1)) + zlog_debug( + "Processing EVPN Martian/%s change on peer %s", + bgp_martian_type2str(martian_type), + peer->host); + bgp_route_refresh_send(peer, afi, safi, 0, + REFRESH_IMMEDIATE, 0, + BGP_ROUTE_REFRESH_NORMAL); + } + } } /* @@ -6232,6 +6581,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, l3vni); return -1; } + + if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + flog_err(EC_BGP_NO_DFLT, + "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down", + l3vni); + return -1; + } + as = bgp_evpn->as; /* if the BGP vrf instance doesn't exist - create one */ @@ -6263,10 +6620,14 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id, /* associate the vrf with l3vni and related parameters */ bgp_vrf->l3vni = l3vni; - bgp_vrf->originator_ip = originator_ip; bgp_vrf->l3vni_svi_ifindex = svi_ifindex; bgp_vrf->evpn_info->is_anycast_mac = is_anycast_mac; + /* Update tip_hash of the EVPN underlay BGP instance (bgp_evpn) + * if the VTEP-IP (originator_ip) has changed + */ + handle_tunnel_ip_change(bgp_vrf, bgp_evpn, vpn, originator_ip); + /* copy anycast MAC from VRR MAC */ memcpy(&bgp_vrf->rmac, vrr_rmac, ETH_ALEN); /* copy sys RMAC from SVI MAC */ @@ -6374,6 +6735,13 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id) return -1; } + if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + flog_err(EC_BGP_NO_DFLT, + "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down", + l3vni); + return -1; + } + /* Remove remote routes from BGT VRF even if BGP_VRF_AUTO is configured, * bgp_delete would not remove/decrement bgp_path_info of the ip_prefix * routes. This will uninstalling the routes from zebra and decremnt the @@ -6384,6 +6752,11 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id) /* delete/withdraw all type-5 routes */ delete_withdraw_vrf_routes(bgp_vrf); + /* Tunnel is no longer active. + * Delete VTEP-IP from EVPN underlay's tip_hash. + */ + bgp_tip_del(bgp_evpn, &bgp_vrf->originator_ip); + /* remove the l3vni from vrf instance */ bgp_vrf->l3vni = 0; @@ -6448,8 +6821,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn); vpn->svi_ifindex = 0; - /* - * tunnel is no longer active, del tunnel ip address from tip_hash + /* Tunnel is no longer active. + * Delete VTEP-IP from EVPN underlay's tip_hash. */ bgp_tip_del(bgp, &vpn->originator_ip); @@ -6473,6 +6846,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, { struct bgpevpn *vpn; struct prefix_evpn p; + struct bgp *bgp_evpn = bgp_get_evpn(); /* Lookup VNI. If present and no change, exit. */ vpn = bgp_evpn_lookup_vni(bgp, vni); @@ -6545,7 +6919,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* If tunnel endpoint IP has changed, update (and delete prior * type-3 route, if needed.) */ - handle_tunnel_ip_change(bgp, vpn, originator_ip); + handle_tunnel_ip_change(NULL, bgp, vpn, originator_ip); /* Update all routes with new endpoint IP and/or export RT * for VRFs @@ -6565,14 +6939,17 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, /* Mark as "live" */ SET_FLAG(vpn->flags, VNI_FLAG_LIVE); - /* tunnel is now active, add tunnel-ip to db */ + /* Tunnel is newly active. + * Add TIP to tip_hash of the EVPN underlay instance (bgp_get_evpn()). + */ if (bgp_tip_add(bgp, &originator_ip)) /* The originator_ip was not already present in the * bgp martian next-hop table as a tunnel-ip, so we * need to go back and filter routes matching the new * martian next-hop. */ - bgp_filter_evpn_routes_upon_martian_nh_change(bgp); + bgp_filter_evpn_routes_upon_martian_change(bgp_evpn, + BGP_MARTIAN_TUN_IP); /* * Create EVPN type-3 route and schedule for processing. @@ -6647,25 +7024,30 @@ void bgp_evpn_cleanup(struct bgp *bgp) (void (*)(struct hash_bucket *, void *))free_vni_entry, bgp); - hash_clean(bgp->import_rt_hash, (void (*)(void *))hash_import_rt_free); - hash_free(bgp->import_rt_hash); - bgp->import_rt_hash = NULL; + hash_clean_and_free(&bgp->import_rt_hash, + (void (*)(void *))hash_import_rt_free); - hash_clean(bgp->vrf_import_rt_hash, - (void (*)(void *))hash_vrf_import_rt_free); - hash_free(bgp->vrf_import_rt_hash); - bgp->vrf_import_rt_hash = NULL; + hash_clean_and_free(&bgp->vrf_import_rt_hash, + (void (*)(void *))hash_vrf_import_rt_free); - hash_clean(bgp->vni_svi_hash, (void (*)(void *))hash_evpn_free); - hash_free(bgp->vni_svi_hash); - bgp->vni_svi_hash = NULL; - hash_free(bgp->vnihash); - bgp->vnihash = NULL; + hash_clean_and_free(&bgp->vni_svi_hash, + (void (*)(void *))hash_evpn_free); + + /* + * Why is the vnihash freed at the top of this function and + * then deleted here? + */ + hash_clean_and_free(&bgp->vnihash, NULL); list_delete(&bgp->vrf_import_rtl); list_delete(&bgp->vrf_export_rtl); list_delete(&bgp->l2vnis); + if (bgp->evpn_info) { + ecommunity_free(&bgp->evpn_info->soo); + XFREE(MTYPE_BGP_EVPN_INFO, bgp->evpn_info); + } + if (bgp->vrf_prd_pretty) XFREE(MTYPE_BGP, bgp->vrf_prd_pretty); } @@ -6699,6 +7081,8 @@ void bgp_evpn_init(struct bgp *bgp) bgp->vrf_export_rtl->del = evpn_vrf_rt_del; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = vni_list_cmp; + bgp->evpn_info = + XCALLOC(MTYPE_BGP_EVPN_INFO, sizeof(struct bgp_evpn_info)); /* By default Duplicate Address Dection is enabled. * Max-moves (N) 5, detection time (M) 180 * default action is warning-only diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h index 1dce4820d680..076248c9f716 100644 --- a/bgpd/bgp_evpn.h +++ b/bgpd/bgp_evpn.h @@ -150,14 +150,23 @@ extern void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p, struct attr *attr, bool addpath_capable, uint32_t addpath_tx_id); extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw); + struct bgp_nlri *packet, bool withdraw); extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi, const struct prefix *p, struct bgp_path_info *ri); extern int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi, const struct prefix *p, struct bgp_path_info *ri); -extern int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp); +extern void +bgp_reimport_evpn_routes_upon_macvrf_soo_change(struct bgp *bgp, + struct ecommunity *old_soo, + struct ecommunity *new_soo); +extern void bgp_reimport_evpn_routes_upon_martian_change( + struct bgp *bgp, enum bgp_martian_type martian_type, void *old_martian, + void *new_martian); +extern void +bgp_filter_evpn_routes_upon_martian_change(struct bgp *bgp, + enum bgp_martian_type martian_type); extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac, struct ipaddr *ip, int state); diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c index 2f95023aa977..efadda17b86d 100644 --- a/bgpd/bgp_evpn_mh.c +++ b/bgpd/bgp_evpn_mh.c @@ -65,7 +65,7 @@ static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es, bool is_local); esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; -static void bgp_evpn_run_consistency_checks(struct thread *t); +static void bgp_evpn_run_consistency_checks(struct event *t); static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info); static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info); @@ -487,8 +487,8 @@ static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es, /* Next, locate route node in the global EVPN routing table. * Note that this table is a 2-level tree (RD-level + Prefix-level) */ - global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], afi, - safi, p, prd, NULL); + global_dest = bgp_evpn_global_node_lookup(bgp->rib[afi][safi], safi, p, + prd, NULL); if (global_dest) { /* Delete route entry in the global EVPN table. */ @@ -1071,7 +1071,8 @@ void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn) continue; /* Update EAD-ES */ - bgp_evpn_ead_es_route_update(bgp, es); + if (bgp_evpn_local_es_is_active(es)) + bgp_evpn_ead_es_route_update(bgp, es); /* Update EAD-EVI */ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) { @@ -2457,6 +2458,7 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps, { json_object *json_vtep_entry; json_object *json_flags; + char alg_buf[EVPN_DF_ALG_STR_LEN]; json_vtep_entry = json_object_new_object(); @@ -2473,8 +2475,10 @@ static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps, if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) { json_object_int_add(json_vtep_entry, "dfPreference", es_vtep->df_pref); - json_object_int_add(json_vtep_entry, "dfAlgorithm", - es_vtep->df_pref); + json_object_string_add( + json_vtep_entry, "dfAlgorithm", + evpn_es_df_alg2str(es_vtep->df_alg, alg_buf, + sizeof(alg_buf))); } } @@ -2564,7 +2568,8 @@ static void bgp_evpn_es_show_entry(struct vty *vty, bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str)); vty_out(vty, "%-30s %-5s %-21pRDP %-8d %s\n", es->esi_str, - type_str, &es->es_base_frag->prd, + type_str, + es->es_base_frag ? &es->es_base_frag->prd : NULL, listcount(es->es_evi_list), vtep_str); } } @@ -2607,6 +2612,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, listcount(es->macip_global_path_list)); json_object_int_add(json, "inconsistentVniVtepCount", es->incons_evi_vtep_cnt); + if (es->flags & BGP_EVPNES_LOCAL) + json_object_int_add(json, "localEsDfPreference", + es->df_pref); if (listcount(es->es_vtep_list)) { json_vteps = json_object_new_array(); for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, @@ -2640,7 +2648,8 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty, vty_out(vty, "ESI: %s\n", es->esi_str); vty_out(vty, " Type: %s\n", type_str); - vty_out(vty, " RD: %pRDP\n", &es->es_base_frag->prd); + vty_out(vty, " RD: %pRDP\n", + es->es_base_frag ? &es->es_base_frag->prd : NULL); vty_out(vty, " Originator-IP: %pI4\n", &es->originator_ip); if (es->flags & BGP_EVPNES_LOCAL) vty_out(vty, " Local ES DF preference: %u\n", @@ -4172,9 +4181,9 @@ static void bgp_evpn_es_cons_checks_timer_start(void) if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) zlog_debug("periodic consistency checking started"); - thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL, - BGP_EVPN_CONS_CHECK_INTERVAL, - &bgp_mh_info->t_cons_check); + event_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL, + BGP_EVPN_CONS_CHECK_INTERVAL, + &bgp_mh_info->t_cons_check); } /* queue up the es for background consistency checks */ @@ -4358,7 +4367,7 @@ static uint32_t bgp_evpn_es_run_consistency_checks(struct bgp_evpn_es *es) return proc_cnt; } -static void bgp_evpn_run_consistency_checks(struct thread *t) +static void bgp_evpn_run_consistency_checks(struct event *t) { int proc_cnt = 0; struct listnode *node; @@ -4378,7 +4387,7 @@ static void bgp_evpn_run_consistency_checks(struct thread *t) } /* restart the timer */ - thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL, + event_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL, BGP_EVPN_CONS_CHECK_INTERVAL, &bgp_mh_info->t_cons_check); } @@ -4593,9 +4602,8 @@ void bgp_evpn_nh_finish(struct bgp *bgp_vrf) bgp_vrf->evpn_nh_table, (void (*)(struct hash_bucket *, void *))bgp_evpn_nh_flush_cb, NULL); - hash_clean(bgp_vrf->evpn_nh_table, (void (*)(void *))hash_evpn_nh_free); - hash_free(bgp_vrf->evpn_nh_table); - bgp_vrf->evpn_nh_table = NULL; + hash_clean_and_free(&bgp_vrf->evpn_nh_table, + (void (*)(void *))hash_evpn_nh_free); } static void bgp_evpn_nh_update_ref_pi(struct bgp_evpn_nh *nh) @@ -4935,7 +4943,7 @@ void bgp_evpn_mh_finish(void) bgp_evpn_es_local_info_clear(es, true); } if (bgp_mh_info->t_cons_check) - THREAD_OFF(bgp_mh_info->t_cons_check); + EVENT_OFF(bgp_mh_info->t_cons_check); list_delete(&bgp_mh_info->local_es_list); list_delete(&bgp_mh_info->pend_es_list); list_delete(&bgp_mh_info->ead_es_export_rtl); diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h index 613fece4d45e..ee1f74989b77 100644 --- a/bgpd/bgp_evpn_mh.h +++ b/bgpd/bgp_evpn_mh.h @@ -299,7 +299,7 @@ struct bgp_evpn_mh_info { /* List of ESs with pending/periodic processing */ struct list *pend_es_list; /* periodic timer for running background consistency checks */ - struct thread *t_cons_check; + struct event *t_cons_check; /* config knobs for optimizing or interop */ /* Generate EAD-EVI routes even if the ES is oper-down. This can be diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index fbf3b19c37cb..8cee048b69d3 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -162,6 +162,13 @@ struct bgp_evpn_info { /* EVPN enable - advertise svi macip routes */ int advertise_svi_macip; + /* MAC-VRF Site-of-Origin + * - added to all routes exported from L2VNI + * - Type-2/3 routes with matching SoO not imported into L2VNI + * - Type-2/5 routes with matching SoO not imported into L3VNI + */ + struct ecommunity *soo; + /* PIP feature knob */ bool advertise_pip; /* PIP IP (sys ip) */ @@ -680,6 +687,8 @@ extern void bgp_evpn_handle_autort_change(struct bgp *bgp); extern void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw); extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn, int withdraw); +void bgp_evpn_handle_global_macvrf_soo_change(struct bgp *bgp, + struct ecommunity *new_soo); extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn); extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn); extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf); @@ -712,11 +721,9 @@ extern struct bgp_dest * bgp_evpn_global_node_get(struct bgp_table *table, afi_t afi, safi_t safi, const struct prefix_evpn *evp, struct prefix_rd *prd, const struct bgp_path_info *local_pi); -extern struct bgp_dest * -bgp_evpn_global_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi, - const struct prefix_evpn *evp, - struct prefix_rd *prd, - const struct bgp_path_info *local_pi); +extern struct bgp_dest *bgp_evpn_global_node_lookup( + struct bgp_table *table, safi_t safi, const struct prefix_evpn *evp, + struct prefix_rd *prd, const struct bgp_path_info *local_pi); extern struct bgp_dest * bgp_evpn_vni_ip_node_get(struct bgp_table *const table, const struct prefix_evpn *evp, diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index e28a8c8057ed..77a997d178e7 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -362,10 +362,11 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, char *ecom_str; struct listnode *node, *nnode; struct vrf_route_target *l3rt; + struct bgp *bgp_evpn = NULL; json_object *json_import_rtl = NULL; json_object *json_export_rtl = NULL; - char buf2[ETHER_ADDR_STRLEN]; + bgp_evpn = bgp_get_evpn(); json_import_rtl = json_export_rtl = 0; if (json) { @@ -379,19 +380,26 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, &bgp_vrf->vrf_prd); json_object_string_addf(json, "originatorIp", "%pI4", &bgp_vrf->originator_ip); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + json_object_string_add(json, "siteOfOrigin", ecom_str); + ecommunity_strfree(&ecom_str); + } json_object_string_add(json, "advertiseGatewayMacip", "n/a"); json_object_string_add(json, "advertiseSviMacIp", "n/a"); - json_object_string_add(json, "advertisePip", - bgp_vrf->evpn_info->advertise_pip ? - "Enabled" : "Disabled"); - json_object_string_addf(json, "sysIP", "%pI4", - &bgp_vrf->evpn_info->pip_ip); - 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))); + if (bgp_vrf && bgp_vrf->evpn_info) { + json_object_string_add(json, "advertisePip", + bgp_vrf->evpn_info->advertise_pip + ? "Enabled" + : "Disabled"); + json_object_string_addf(json, "sysIP", "%pI4", + &bgp_vrf->evpn_info->pip_ip); + json_object_string_addf(json, "sysMac", "%pEA", + &bgp_vrf->evpn_info->pip_rmac); + } + json_object_string_addf(json, "rmac", "%pEA", &bgp_vrf->rmac); } else { vty_out(vty, "VNI: %d", bgp_vrf->l3vni); vty_out(vty, " (known to the kernel)"); @@ -406,18 +414,26 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, vty_out(vty, "\n"); vty_out(vty, " Originator IP: %pI4\n", &bgp_vrf->originator_ip); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " MAC-VRF Site-of-Origin: %s\n", + ecom_str); + ecommunity_strfree(&ecom_str); + } 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: %pI4\n", - &bgp_vrf->evpn_info->pip_ip); - 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 (bgp_vrf->evpn_info) { + vty_out(vty, " Advertise-pip: %s\n", + bgp_vrf->evpn_info->advertise_pip ? "Yes" + : "No"); + vty_out(vty, " System-IP: %pI4\n", + &bgp_vrf->evpn_info->pip_ip); + vty_out(vty, " System-MAC: %pEA\n", + &bgp_vrf->evpn_info->pip_rmac); + } + vty_out(vty, " Router-MAC: %pEA\n", &bgp_vrf->rmac); } if (!json) @@ -433,7 +449,7 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, else vty_out(vty, " %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + ecommunity_strfree(&ecom_str); } if (json) @@ -451,7 +467,7 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf, else vty_out(vty, " %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + ecommunity_strfree(&ecom_str); } if (json) @@ -484,6 +500,13 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) &vpn->originator_ip); json_object_string_addf(json, "mcastGroup", "%pI4", &vpn->mcast_grp); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + json_object_string_add(json, "siteOfOrigin", ecom_str); + ecommunity_strfree(&ecom_str); + } /* per vni knob is enabled -- Enabled * Global knob is enabled -- Active * default -- Disabled @@ -499,6 +522,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) json_object_string_add(json, "advertiseGatewayMacip", "Disabled"); if (!vpn->advertise_svi_macip && bgp_evpn && + bgp_evpn->evpn_info && bgp_evpn->evpn_info->advertise_svi_macip) json_object_string_add(json, "advertiseSviMacIp", "Active"); @@ -525,6 +549,14 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) vty_out(vty, "\n"); vty_out(vty, " Originator IP: %pI4\n", &vpn->originator_ip); vty_out(vty, " Mcast group: %pI4\n", &vpn->mcast_grp); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " MAC-VRF Site-of-Origin: %s\n", + ecom_str); + ecommunity_strfree(&ecom_str); + } if (!vpn->advertise_gw_macip && bgp_evpn && bgp_evpn->advertise_gw_macip) vty_out(vty, " Advertise-gw-macip : %s\n", @@ -536,6 +568,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) vty_out(vty, " Advertise-gw-macip : %s\n", "Disabled"); if (!vpn->advertise_svi_macip && bgp_evpn && + bgp_evpn->evpn_info && bgp_evpn->evpn_info->advertise_svi_macip) vty_out(vty, " Advertise-svi-macip : %s\n", "Active"); @@ -562,7 +595,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else vty_out(vty, " %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + ecommunity_strfree(&ecom_str); } if (json) @@ -580,7 +613,7 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json) else vty_out(vty, " %s\n", ecom_str); - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + ecommunity_strfree(&ecom_str); } if (json) @@ -981,10 +1014,13 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, char *ecom_str; struct listnode *node, *nnode; struct vrf_route_target *l3rt; + struct bgp *bgp_evpn; if (!bgp->l3vni) return; + bgp_evpn = bgp_get_evpn(); + if (json) { json_vni = json_object_new_object(); json_import_rtl = json_object_new_array(); @@ -1041,7 +1077,7 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, vty_out(vty, " %-25s", rt_buf); } - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + ecommunity_strfree(&ecom_str); /* If there are multiple import RTs we break here and show only * one */ @@ -1069,12 +1105,19 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp, vty_out(vty, " %-25s", rt_buf); } - XFREE(MTYPE_ECOMMUNITY_STR, ecom_str); + ecommunity_strfree(&ecom_str); /* If there are multiple export RTs we break here and show only * one */ if (!json) { - vty_out(vty, "%-37s", vrf_id_to_name(bgp->vrf_id)); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " %-25s", ecom_str); + ecommunity_strfree(&ecom_str); + } + vty_out(vty, " %-37s", vrf_id_to_name(bgp->vrf_id)); break; } } @@ -1083,11 +1126,18 @@ 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); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + json_object_string_add(json_vni, "siteOfOrigin", + ecom_str); + ecommunity_strfree(&ecom_str); + } snprintf(vni_str, sizeof(vni_str), "%u", bgp->l3vni); json_object_object_add(json, vni_str, json_vni); - } else { + } else vty_out(vty, "\n"); - } } static void show_vni_entry(struct hash_bucket *bucket, void *args[]) @@ -1213,7 +1263,14 @@ static void show_vni_entry(struct hash_bucket *bucket, void *args[]) /* If there are multiple export RTs we break here and show only * one */ if (!json) { - vty_out(vty, "%-37s", + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " %-25s", ecom_str); + ecommunity_strfree(&ecom_str); + } + vty_out(vty, " %-37s", vrf_id_to_name(vpn->tenant_vrf_id)); break; } @@ -1223,11 +1280,18 @@ 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); + if (bgp_evpn && bgp_evpn->evpn_info) { + ecom_str = ecommunity_ecom2str( + bgp_evpn->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + json_object_string_add(json_vni, "siteOfOrigin", + ecom_str); + ecommunity_strfree(&ecom_str); + } snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni); json_object_object_add(json, vni_str, json_vni); - } else { + } else vty_out(vty, "\n"); - } } static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd, @@ -2228,6 +2292,9 @@ static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd, */ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1); + if (bgp_vrf->vrf_prd_pretty) + XFREE(MTYPE_BGP, bgp_vrf->vrf_prd_pretty); + /* update RD */ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd)); bgp_vrf->vrf_prd_pretty = XSTRDUP(MTYPE_BGP, rd_pretty); @@ -2716,8 +2783,8 @@ 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); - dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&p, prd); + dest = bgp_safi_node_lookup(bgp->rib[afi][safi], 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"); @@ -2946,9 +3013,9 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp, * then search the l2vpn evpn table for it. */ build_evpn_type2_prefix(&ep, mac, ip); - dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, - (struct prefix *)&ep, - (struct prefix_rd *)rd_destp); + dest = bgp_safi_node_lookup(bgp->rib[afi][safi], safi, + (struct prefix *)&ep, + (struct prefix_rd *)rd_destp); if (!dest) continue; @@ -3217,7 +3284,14 @@ int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, evpn_show_all_routes(vty, bgp, type, json, detail, false); if (use_json) - vty_json(vty, json); + /* + * We are using no_pretty here because under extremely high + * settings (lots of routes with many different paths) this can + * save several minutes of output when FRR is run on older cpu's + * or more underperforming routers out there. So for route + * scale, we need to use no_pretty json. + */ + vty_json_no_pretty(vty, json); return CMD_SUCCESS; } @@ -3269,8 +3343,9 @@ static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp, if (!json) { vty_out(vty, "Flags: * - Kernel\n"); - vty_out(vty, " %-10s %-4s %-21s %-25s %-25s %-37s\n", "VNI", - "Type", "RD", "Import RT", "Export RT", "Tenant VRF"); + vty_out(vty, " %-10s %-4s %-21s %-25s %-25s %-25s %-37s\n", + "VNI", "Type", "RD", "Import RT", "Export RT", + "MAC-VRF Site-of-Origin", "Tenant VRF"); } /* print all L2 VNIS */ @@ -3916,6 +3991,58 @@ DEFPY(bgp_evpn_advertise_svi_ip_vni, return CMD_SUCCESS; } +DEFPY(macvrf_soo_global, macvrf_soo_global_cmd, + "mac-vrf soo ASN:NN_OR_IP-ADDRESS:NN$soo", + "EVPN MAC-VRF\n" + "Site-of-Origin extended community\n" + "VPN extended community\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct bgp *bgp_evpn = bgp_get_evpn(); + struct ecommunity *ecomm_soo; + + if (!bgp || !bgp_evpn || !bgp_evpn->evpn_info) + return CMD_WARNING; + + if (bgp != bgp_evpn) { + vty_out(vty, + "%% Please configure MAC-VRF SoO in the EVPN underlay: %s\n", + bgp_evpn->name_pretty); + return CMD_WARNING_CONFIG_FAILED; + } + + ecomm_soo = ecommunity_str2com(soo, ECOMMUNITY_SITE_ORIGIN, 0); + if (!ecomm_soo) { + vty_out(vty, "%% Malformed SoO extended community\n"); + return CMD_WARNING_CONFIG_FAILED; + } + ecommunity_str(ecomm_soo); + + bgp_evpn_handle_global_macvrf_soo_change(bgp_evpn, ecomm_soo); + + return CMD_SUCCESS; +} + +DEFPY(no_macvrf_soo_global, no_macvrf_soo_global_cmd, + "no mac-vrf soo [ASN:NN_OR_IP-ADDRESS:NN$soo]", + NO_STR + "EVPN MAC-VRF\n" + "Site-of-Origin extended community\n" + "VPN extended community\n") +{ + struct bgp *bgp = VTY_GET_CONTEXT(bgp); + struct bgp *bgp_evpn = bgp_get_evpn(); + + if (!bgp || !bgp_evpn || !bgp_evpn->evpn_info) + return CMD_WARNING; + + if (bgp_evpn) + bgp_evpn_handle_global_macvrf_soo_change(bgp_evpn, + NULL /* new_soo */); + + return CMD_SUCCESS; +} + DEFUN_HIDDEN (bgp_evpn_advertise_vni_subnet, bgp_evpn_advertise_vni_subnet_cmd, "advertise-subnet", @@ -4305,7 +4432,7 @@ DEFPY (bgp_evpn_advertise_pip_ip_mac, struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */ struct bgp *bgp_evpn = NULL; - if (EVPN_ENABLED(bgp_vrf)) { + if (!bgp_vrf || EVPN_ENABLED(bgp_vrf)) { vty_out(vty, "This command is supported under L3VNI BGP EVPN VRF\n"); return CMD_WARNING_CONFIG_FAILED; @@ -7151,6 +7278,15 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->evpn_info->advertise_svi_macip) vty_out(vty, " advertise-svi-ip\n"); + if (bgp->evpn_info->soo) { + char *ecom_str; + + ecom_str = ecommunity_ecom2str(bgp->evpn_info->soo, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " mac-vrf soo %s\n", ecom_str); + ecommunity_strfree(&ecom_str); + } + if (bgp->resolve_overlay_index) vty_out(vty, " enable-resolve-overlay-index\n"); @@ -7383,6 +7519,8 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_svi_ip_cmd); + install_element(BGP_EVPN_NODE, &macvrf_soo_global_cmd); + install_element(BGP_EVPN_NODE, &no_macvrf_soo_global_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd); diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c index f9debe43cd45..6165bf892e72 100644 --- a/bgpd/bgp_flowspec.c +++ b/bgpd/bgp_flowspec.c @@ -82,7 +82,7 @@ static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len, } int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw) + struct bgp_nlri *packet, bool withdraw) { uint8_t *pnt; uint8_t *lim; @@ -98,6 +98,13 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, afi = packet->afi; safi = packet->safi; + /* + * All other AFI/SAFI's treat no attribute as a implicit + * withdraw. Flowspec should as well. + */ + if (!attr) + withdraw = true; + if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) { flog_err(EC_BGP_FLOWSPEC_PACKET, "BGP flowspec nlri length maximum reached (%u)", @@ -182,13 +189,16 @@ int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, zlog_info("%s", local_string); } /* Process the route. */ - if (!withdraw) + if (!withdraw) { bgp_update(peer, &p, 0, attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0, 0, NULL); - else + } else { bgp_withdraw(peer, &p, 0, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, NULL, 0, NULL); + } + + XFREE(MTYPE_TMP, temp); } return BGP_NLRI_PARSE_OK; } diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h index 58fc1775ab2b..bc2f00511e0d 100644 --- a/bgpd/bgp_flowspec.h +++ b/bgpd/bgp_flowspec.h @@ -15,7 +15,7 @@ #define BGP_FLOWSPEC_NLRI_STRING_MAX 512 extern int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int withdraw); + struct bgp_nlri *packet, bool withdraw); extern void bgp_flowspec_vty_init(void); diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c index 326d7f2efb2b..66426ab32f02 100644 --- a/bgpd/bgp_flowspec_util.c +++ b/bgpd/bgp_flowspec_util.c @@ -185,16 +185,23 @@ int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type, offset++; } /* Prefix length check. */ - if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) + if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8) { *error = -1; + return offset; + } /* When packet overflow occur return immediately. */ - if (psize + offset > max_len) + if (psize + offset > max_len) { *error = -1; + return offset; + } /* Defensive coding, double-check * the psize fits in a struct prefix */ - if (psize > (ssize_t)sizeof(prefix_local.u)) + if (psize > (ssize_t)sizeof(prefix_local.u)) { *error = -1; + return offset; + } + memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize); offset += psize; switch (type) { @@ -352,8 +359,10 @@ int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type, *error = 0; do { - if (loop > BGP_PBR_MATCH_VAL_MAX) + if (loop > BGP_PBR_MATCH_VAL_MAX) { *error = -2; + return offset; + } hex2bin(&nlri_ptr[offset], op); /* if first element, AND bit can not be set */ if (op[1] == 1 && loop == 0) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 59a8fcb92a07..2cea9971e6e3 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -9,7 +9,7 @@ #include "linklist.h" #include "prefix.h" #include "sockunion.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "stream.h" #include "ringbuf.h" @@ -46,13 +46,6 @@ DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer)); DEFINE_HOOK(peer_status_changed, (struct peer * peer), (peer)); -enum bgp_fsm_state_progress { - BGP_FSM_FAILURE_AND_DELETE = -2, - BGP_FSM_FAILURE = -1, - BGP_FSM_SUCCESS = 0, - BGP_FSM_SUCCESS_STATE_TRANSFER = 1, -}; - /* Definition of display strings corresponding to FSM events. This should be * kept consistent with the events defined in bgpd.h */ @@ -82,13 +75,13 @@ static const char *const bgp_event_str[] = { function. */ /* BGP event function. */ -void bgp_event(struct thread *); +void bgp_event(struct event *event); /* BGP thread functions. */ -static void bgp_start_timer(struct thread *); -static void bgp_connect_timer(struct thread *); -static void bgp_holdtime_timer(struct thread *); -static void bgp_delayopen_timer(struct thread *); +static void bgp_start_timer(struct event *event); +static void bgp_connect_timer(struct event *event); +static void bgp_holdtime_timer(struct event *event); +static void bgp_delayopen_timer(struct event *event); /* BGP FSM functions. */ static enum bgp_fsm_state_progress bgp_start(struct peer *); @@ -169,17 +162,17 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) */ bgp_keepalives_off(from_peer); - THREAD_OFF(peer->t_routeadv); - THREAD_OFF(peer->t_connect); - THREAD_OFF(peer->t_delayopen); - THREAD_OFF(peer->t_connect_check_r); - THREAD_OFF(peer->t_connect_check_w); - THREAD_OFF(from_peer->t_routeadv); - THREAD_OFF(from_peer->t_connect); - THREAD_OFF(from_peer->t_delayopen); - THREAD_OFF(from_peer->t_connect_check_r); - THREAD_OFF(from_peer->t_connect_check_w); - THREAD_OFF(from_peer->t_process_packet); + EVENT_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_connect); + EVENT_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_connect_check_r); + EVENT_OFF(peer->t_connect_check_w); + EVENT_OFF(from_peer->t_routeadv); + EVENT_OFF(from_peer->t_connect); + EVENT_OFF(from_peer->t_delayopen); + EVENT_OFF(from_peer->t_connect_check_r); + EVENT_OFF(from_peer->t_connect_check_w); + EVENT_OFF(from_peer->t_process_packet); /* * At this point in time, it is possible that there are packets pending @@ -343,8 +336,8 @@ static struct peer *peer_xfer_conn(struct peer *from_peer) bgp_reads_on(peer); bgp_writes_on(peer); - thread_add_event(bm->master, bgp_process_packet, peer, 0, - &peer->t_process_packet); + event_add_event(bm->master, bgp_process_packet, peer, 0, + &peer->t_process_packet); return (peer); } @@ -364,23 +357,23 @@ void bgp_timer_set(struct peer *peer) inactive. All other timer must be turned off */ if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer) || peer->bgp->vrf_id == VRF_UNKNOWN) { - THREAD_OFF(peer->t_start); + EVENT_OFF(peer->t_start); } else { BGP_TIMER_ON(peer->t_start, bgp_start_timer, peer->v_start); } - THREAD_OFF(peer->t_connect); - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_connect); + EVENT_OFF(peer->t_holdtime); bgp_keepalives_off(peer); - THREAD_OFF(peer->t_routeadv); - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_delayopen); break; case Connect: /* After start timer is expired, the peer moves to Connect status. Make sure start timer is off and connect timer is on. */ - THREAD_OFF(peer->t_start); + EVENT_OFF(peer->t_start); if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, (peer->v_delayopen + peer->v_connect)); @@ -388,19 +381,19 @@ void bgp_timer_set(struct peer *peer) BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, peer->v_connect); - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_holdtime); bgp_keepalives_off(peer); - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); break; case Active: /* Active is waiting connection from remote peer. And if connect timer is expired, change status to Connect. */ - THREAD_OFF(peer->t_start); + EVENT_OFF(peer->t_start); /* If peer is passive mode, do not set connect timer. */ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSIVE) || CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) { - THREAD_OFF(peer->t_connect); + EVENT_OFF(peer->t_connect); } else { if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN)) BGP_TIMER_ON( @@ -410,80 +403,88 @@ void bgp_timer_set(struct peer *peer) BGP_TIMER_ON(peer->t_connect, bgp_connect_timer, peer->v_connect); } - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_holdtime); bgp_keepalives_off(peer); - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); break; case OpenSent: /* OpenSent status. */ - THREAD_OFF(peer->t_start); - THREAD_OFF(peer->t_connect); + EVENT_OFF(peer->t_start); + EVENT_OFF(peer->t_connect); if (peer->v_holdtime != 0) { BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); } else { - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_holdtime); } bgp_keepalives_off(peer); - THREAD_OFF(peer->t_routeadv); - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_delayopen); break; case OpenConfirm: /* OpenConfirm status. */ - THREAD_OFF(peer->t_start); - THREAD_OFF(peer->t_connect); + EVENT_OFF(peer->t_start); + EVENT_OFF(peer->t_connect); - /* If the negotiated Hold Time value is zero, then the Hold Time - timer and KeepAlive timers are not started. */ - if (peer->v_holdtime == 0) { - THREAD_OFF(peer->t_holdtime); + /* + * If the negotiated Hold Time value is zero, then the Hold Time + * timer and KeepAlive timers are not started. + * Additionally if a different hold timer has been negotiated + * than we must stop then start the timer again + */ + EVENT_OFF(peer->t_holdtime); + if (peer->v_holdtime == 0) bgp_keepalives_off(peer); - } else { + else { BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); bgp_keepalives_on(peer); } - THREAD_OFF(peer->t_routeadv); - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_delayopen); break; case Established: /* In Established status start and connect timer is turned off. */ - THREAD_OFF(peer->t_start); - THREAD_OFF(peer->t_connect); - THREAD_OFF(peer->t_delayopen); - - /* Same as OpenConfirm, if holdtime is zero then both holdtime - and keepalive must be turned off. */ - if (peer->v_holdtime == 0) { - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_start); + EVENT_OFF(peer->t_connect); + EVENT_OFF(peer->t_delayopen); + + /* + * Same as OpenConfirm, if holdtime is zero then both holdtime + * and keepalive must be turned off. + * Additionally if a different hold timer has been negotiated + * then we must stop then start the timer again + */ + EVENT_OFF(peer->t_holdtime); + if (peer->v_holdtime == 0) bgp_keepalives_off(peer); - } else { + else { BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); bgp_keepalives_on(peer); } break; case Deleted: - THREAD_OFF(peer->t_gr_restart); - THREAD_OFF(peer->t_gr_stale); + EVENT_OFF(peer->t_gr_restart); + EVENT_OFF(peer->t_gr_stale); FOREACH_AFI_SAFI (afi, safi) - THREAD_OFF(peer->t_llgr_stale[afi][safi]); + EVENT_OFF(peer->t_llgr_stale[afi][safi]); - THREAD_OFF(peer->t_pmax_restart); - THREAD_OFF(peer->t_refresh_stalepath); + EVENT_OFF(peer->t_pmax_restart); + EVENT_OFF(peer->t_refresh_stalepath); /* fallthru */ case Clearing: - THREAD_OFF(peer->t_start); - THREAD_OFF(peer->t_connect); - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_start); + EVENT_OFF(peer->t_connect); + EVENT_OFF(peer->t_holdtime); bgp_keepalives_off(peer); - THREAD_OFF(peer->t_routeadv); - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_delayopen); break; case BGP_STATUS_MAX: flog_err(EC_LIB_DEVELOPMENT, @@ -494,28 +495,28 @@ void bgp_timer_set(struct peer *peer) /* BGP start timer. This function set BGP_Start event to thread value and process event. */ -static void bgp_start_timer(struct thread *thread) +static void bgp_start_timer(struct event *thread) { struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (start timer expire).", peer->host); - THREAD_VAL(thread) = BGP_Start; + EVENT_VAL(thread) = BGP_Start; bgp_event(thread); /* bgp_event unlocks peer */ } /* BGP connect retry timer. */ -static void bgp_connect_timer(struct thread *thread) +static void bgp_connect_timer(struct event *thread) { struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); /* stop the DelayOpenTimer if it is running */ - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_delayopen); assert(!peer->t_write); assert(!peer->t_read); @@ -526,18 +527,18 @@ static void bgp_connect_timer(struct thread *thread) if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) bgp_stop(peer); else { - THREAD_VAL(thread) = ConnectRetry_timer_expired; + EVENT_VAL(thread) = ConnectRetry_timer_expired; bgp_event(thread); /* bgp_event unlocks peer */ } } /* BGP holdtime timer. */ -static void bgp_holdtime_timer(struct thread *thread) +static void bgp_holdtime_timer(struct event *thread) { atomic_size_t inq_count; struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (holdtime timer expire)", @@ -559,15 +560,15 @@ static void bgp_holdtime_timer(struct thread *thread) BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer, peer->v_holdtime); - THREAD_VAL(thread) = Hold_Timer_expired; + EVENT_VAL(thread) = Hold_Timer_expired; bgp_event(thread); /* bgp_event unlocks peer */ } -void bgp_routeadv_timer(struct thread *thread) +void bgp_routeadv_timer(struct event *thread) { struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (routeadv timer expire)", @@ -575,8 +576,8 @@ void bgp_routeadv_timer(struct thread *thread) peer->synctime = monotime(NULL); - thread_add_timer_msec(bm->master, bgp_generate_updgrp_packets, peer, 0, - &peer->t_generate_updgrp_packets); + event_add_timer_msec(bm->master, bgp_generate_updgrp_packets, peer, 0, + &peer->t_generate_updgrp_packets); /* MRAI timer will be started again when FIFO is built, no need to * do it here. @@ -584,17 +585,17 @@ void bgp_routeadv_timer(struct thread *thread) } /* RFC 4271 DelayOpenTimer */ -void bgp_delayopen_timer(struct thread *thread) +void bgp_delayopen_timer(struct event *thread) { struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [FSM] Timer (DelayOpentimer expire)", peer->host); - THREAD_VAL(thread) = DelayOpen_timer_expired; + EVENT_VAL(thread) = DelayOpen_timer_expired; bgp_event(thread); /* bgp_event unlocks peer */ } @@ -647,7 +648,7 @@ static void bgp_graceful_restart_timer_off(struct peer *peer) return; UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT); - THREAD_OFF(peer->t_gr_stale); + EVENT_OFF(peer->t_gr_stale); if (peer_dynamic_neighbor(peer) && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) { @@ -660,14 +661,14 @@ static void bgp_graceful_restart_timer_off(struct peer *peer) bgp_timer_set(peer); } -static void bgp_llgr_stale_timer_expire(struct thread *thread) +static void bgp_llgr_stale_timer_expire(struct event *thread) { struct peer_af *paf; struct peer *peer; afi_t afi; safi_t safi; - paf = THREAD_ARG(thread); + paf = EVENT_ARG(thread); peer = paf->peer; afi = paf->afi; @@ -762,7 +763,7 @@ static void bgp_set_llgr_stale(struct peer *peer, afi_t afi, safi_t safi) } } -static void bgp_graceful_restart_timer_expire(struct thread *thread) +static void bgp_graceful_restart_timer_expire(struct event *thread) { struct peer *peer, *tmp_peer; struct listnode *node, *nnode; @@ -770,7 +771,7 @@ static void bgp_graceful_restart_timer_expire(struct thread *thread) afi_t afi; safi_t safi; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); if (bgp_debug_neighbor_events(peer)) { zlog_debug("%pBP graceful restart timer expired", peer); @@ -812,10 +813,9 @@ static void bgp_graceful_restart_timer_expire(struct thread *thread) bgp_set_llgr_stale(peer, afi, safi); bgp_clear_stale_route(peer, afi, safi); - thread_add_timer(bm->master, - bgp_llgr_stale_timer_expire, paf, - peer->llgr[afi][safi].stale_time, - &peer->t_llgr_stale[afi][safi]); + event_add_timer(bm->master, bgp_llgr_stale_timer_expire, + paf, peer->llgr[afi][safi].stale_time, + &peer->t_llgr_stale[afi][safi]); for (ALL_LIST_ELEMENTS(peer->bgp->peer, node, nnode, tmp_peer)) @@ -828,13 +828,13 @@ static void bgp_graceful_restart_timer_expire(struct thread *thread) bgp_graceful_restart_timer_off(peer); } -static void bgp_graceful_stale_timer_expire(struct thread *thread) +static void bgp_graceful_stale_timer_expire(struct event *thread) { struct peer *peer; afi_t afi; safi_t safi; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); if (bgp_debug_neighbor_events(peer)) zlog_debug("%pBP graceful restart stalepath timer expired", @@ -847,14 +847,14 @@ static void bgp_graceful_stale_timer_expire(struct thread *thread) } /* Selection deferral timer processing function */ -static void bgp_graceful_deferral_timer_expire(struct thread *thread) +static void bgp_graceful_deferral_timer_expire(struct event *thread) { struct afi_safi_info *info; afi_t afi; safi_t safi; struct bgp *bgp; - info = THREAD_ARG(thread); + info = EVENT_ARG(thread); afi = info->afi; safi = info->safi; bgp = info->bgp; @@ -901,8 +901,8 @@ bool bgp_update_delay_configured(struct bgp *bgp) on ending the update delay. */ void bgp_update_delay_end(struct bgp *bgp) { - THREAD_OFF(bgp->t_update_delay); - THREAD_OFF(bgp->t_establish_wait); + EVENT_OFF(bgp->t_update_delay); + EVENT_OFF(bgp->t_establish_wait); /* Reset update-delay related state */ bgp->update_delay_over = 1; @@ -965,7 +965,7 @@ void bgp_start_routeadv(struct bgp *bgp) for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { if (!peer_established(peer)) continue; - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0); } } @@ -985,7 +985,7 @@ void bgp_adjust_routeadv(struct peer *peer) * different * duration and schedule write thread immediately. */ - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); peer->synctime = monotime(NULL); /* If suppress fib pending is enabled, route is advertised to @@ -1017,7 +1017,7 @@ void bgp_adjust_routeadv(struct peer *peer) */ diff = difftime(nowtime, peer->last_update); if (diff > (double)peer->v_routeadv) { - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0); return; } @@ -1039,12 +1039,12 @@ void bgp_adjust_routeadv(struct peer *peer) * (MRAI - m) < r */ if (peer->t_routeadv) - remain = thread_timer_remain_second(peer->t_routeadv); + remain = event_timer_remain_second(peer->t_routeadv); else remain = peer->v_routeadv; diff = peer->v_routeadv - diff; if (diff <= (double)remain) { - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, diff); } } @@ -1117,14 +1117,14 @@ int bgp_fsm_error_subcode(int status) } /* The maxmed onstartup timer expiry callback. */ -static void bgp_maxmed_onstartup_timer(struct thread *thread) +static void bgp_maxmed_onstartup_timer(struct event *thread) { struct bgp *bgp; zlog_info("Max med on startup ended - timer expired."); - bgp = THREAD_ARG(thread); - THREAD_OFF(bgp->t_maxmed_onstartup); + bgp = EVENT_ARG(thread); + EVENT_OFF(bgp->t_maxmed_onstartup); bgp->maxmed_onstartup_over = 1; bgp_maxmed_update(bgp); @@ -1139,8 +1139,8 @@ static void bgp_maxmed_onstartup_begin(struct bgp *bgp) zlog_info("Begin maxmed onstartup mode - timer %d seconds", bgp->v_maxmed_onstartup); - thread_add_timer(bm->master, bgp_maxmed_onstartup_timer, bgp, - bgp->v_maxmed_onstartup, &bgp->t_maxmed_onstartup); + event_add_timer(bm->master, bgp_maxmed_onstartup_timer, bgp, + bgp->v_maxmed_onstartup, &bgp->t_maxmed_onstartup); if (!bgp->v_maxmed_admin) { bgp->maxmed_active = 1; @@ -1159,26 +1159,26 @@ static void bgp_maxmed_onstartup_process_status_change(struct peer *peer) } /* The update delay timer expiry callback. */ -static void bgp_update_delay_timer(struct thread *thread) +static void bgp_update_delay_timer(struct event *thread) { struct bgp *bgp; zlog_info("Update delay ended - timer expired."); - bgp = THREAD_ARG(thread); - THREAD_OFF(bgp->t_update_delay); + bgp = EVENT_ARG(thread); + EVENT_OFF(bgp->t_update_delay); bgp_update_delay_end(bgp); } /* The establish wait timer expiry callback. */ -static void bgp_establish_wait_timer(struct thread *thread) +static void bgp_establish_wait_timer(struct event *thread) { struct bgp *bgp; zlog_info("Establish wait - timer expired."); - bgp = THREAD_ARG(thread); - THREAD_OFF(bgp->t_establish_wait); + bgp = EVENT_ARG(thread); + EVENT_OFF(bgp->t_establish_wait); bgp_check_update_delay(bgp); } @@ -1198,12 +1198,12 @@ static void bgp_update_delay_begin(struct bgp *bgp) peer->update_delay_over = 0; /* Start the update-delay timer */ - thread_add_timer(bm->master, bgp_update_delay_timer, bgp, - bgp->v_update_delay, &bgp->t_update_delay); + event_add_timer(bm->master, bgp_update_delay_timer, bgp, + bgp->v_update_delay, &bgp->t_update_delay); if (bgp->v_establish_wait != bgp->v_update_delay) - thread_add_timer(bm->master, bgp_establish_wait_timer, bgp, - bgp->v_establish_wait, &bgp->t_establish_wait); + event_add_timer(bm->master, bgp_establish_wait_timer, bgp, + bgp->v_establish_wait, &bgp->t_establish_wait); frr_timestamp(3, bgp->update_delay_begin_time, sizeof(bgp->update_delay_begin_time)); @@ -1405,7 +1405,7 @@ enum bgp_fsm_state_progress bgp_stop(struct peer *peer) /* graceful restart */ if (peer->t_gr_stale) { - THREAD_OFF(peer->t_gr_stale); + EVENT_OFF(peer->t_gr_stale); if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP graceful restart stalepath timer stopped", @@ -1435,7 +1435,7 @@ enum bgp_fsm_state_progress bgp_stop(struct peer *peer) /* Stop route-refresh stalepath timer */ if (peer->t_refresh_stalepath) { - THREAD_OFF(peer->t_refresh_stalepath); + EVENT_OFF(peer->t_refresh_stalepath); if (bgp_debug_neighbor_events(peer)) zlog_debug( @@ -1469,11 +1469,11 @@ enum bgp_fsm_state_progress bgp_stop(struct peer *peer) /* There is no pending EOR message */ if (gr_info->eor_required == 0) { if (gr_info->t_select_deferral) { - void *info = THREAD_ARG( + void *info = EVENT_ARG( gr_info->t_select_deferral); XFREE(MTYPE_TMP, info); } - THREAD_OFF(gr_info->t_select_deferral); + EVENT_OFF(gr_info->t_select_deferral); gr_info->eor_received = 0; } } @@ -1498,15 +1498,15 @@ enum bgp_fsm_state_progress bgp_stop(struct peer *peer) bgp_writes_off(peer); bgp_reads_off(peer); - THREAD_OFF(peer->t_connect_check_r); - THREAD_OFF(peer->t_connect_check_w); + EVENT_OFF(peer->t_connect_check_r); + EVENT_OFF(peer->t_connect_check_w); /* Stop all timers. */ - THREAD_OFF(peer->t_start); - THREAD_OFF(peer->t_connect); - THREAD_OFF(peer->t_holdtime); - THREAD_OFF(peer->t_routeadv); - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_start); + EVENT_OFF(peer->t_connect); + EVENT_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_delayopen); /* Clear input and output buffer. */ frr_with_mutex (&peer->io_mtx) { @@ -1646,21 +1646,21 @@ bgp_stop_with_notify(struct peer *peer, uint8_t code, uint8_t sub_code) * when the connection is established. A read event is triggered when the * connection is closed. Thus we need to cancel whichever one did not occur. */ -static void bgp_connect_check(struct thread *thread) +static void bgp_connect_check(struct event *thread) { int status; socklen_t slen; int ret; struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON)); assert(!peer->t_read); assert(!peer->t_write); - THREAD_OFF(peer->t_connect_check_r); - THREAD_OFF(peer->t_connect_check_w); + EVENT_OFF(peer->t_connect_check_r); + EVENT_OFF(peer->t_connect_check_w); /* Check file descriptor. */ slen = sizeof(status); @@ -1933,10 +1933,10 @@ enum bgp_fsm_state_progress bgp_start(struct peer *peer) * bgp_connect_check() as the handler for each and cancel the * unused event in that function. */ - thread_add_read(bm->master, bgp_connect_check, peer, peer->fd, - &peer->t_connect_check_r); - thread_add_write(bm->master, bgp_connect_check, peer, peer->fd, - &peer->t_connect_check_w); + event_add_read(bm->master, bgp_connect_check, peer, peer->fd, + &peer->t_connect_check_r); + event_add_write(bm->master, bgp_connect_check, peer, peer->fd, + &peer->t_connect_check_w); break; } return BGP_FSM_SUCCESS; @@ -2005,7 +2005,7 @@ static enum bgp_fsm_state_progress bgp_fsm_delayopen_timer_expire(struct peer *peer) { /* Stop the DelayOpenTimer */ - THREAD_OFF(peer->t_delayopen); + EVENT_OFF(peer->t_delayopen); /* Send open message to peer */ bgp_open_send(peer); @@ -2038,16 +2038,16 @@ static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi, 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); + event_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, + bgp_zebra_update(bgp, afi, safi, ZEBRA_CLIENT_ROUTE_UPDATE_PENDING); } if (BGP_DEBUG(update, UPDATE_OUT)) @@ -2194,7 +2194,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) /* Send route processing complete message to RIB */ bgp_zebra_update( - afi, safi, peer->bgp->vrf_id, + peer->bgp, afi, safi, ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); } } else { @@ -2206,7 +2206,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) /* Send route processing complete message to RIB */ bgp_zebra_update( - afi, safi, peer->bgp->vrf_id, + peer->bgp, afi, safi, ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); } } @@ -2218,7 +2218,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) else { UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE); if (peer->t_gr_stale) { - THREAD_OFF(peer->t_gr_stale); + EVENT_OFF(peer->t_gr_stale); if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP graceful restart stalepath timer stopped", @@ -2227,7 +2227,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) } if (peer->t_gr_restart) { - THREAD_OFF(peer->t_gr_restart); + EVENT_OFF(peer->t_gr_restart); if (bgp_debug_neighbor_events(peer)) zlog_debug("%pBP graceful restart timer stopped", peer); } @@ -2243,7 +2243,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) */ FOREACH_AFI_SAFI (afi, safi) { if (peer->t_llgr_stale[afi][safi]) { - THREAD_OFF(peer->t_llgr_stale[afi][safi]); + EVENT_OFF(peer->t_llgr_stale[afi][safi]); if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP Long-lived stale timer stopped for afi/safi: %d/%d", @@ -2258,12 +2258,6 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) peer, afi, safi, ORF_TYPE_PREFIX, REFRESH_IMMEDIATE, 0, BGP_ROUTE_REFRESH_NORMAL); - else if (CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) - bgp_route_refresh_send( - peer, afi, safi, ORF_TYPE_PREFIX_OLD, - REFRESH_IMMEDIATE, 0, - BGP_ROUTE_REFRESH_NORMAL); } } @@ -2272,9 +2266,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV)) if (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_RCV)) SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH); } @@ -2288,7 +2280,7 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) * of read-only mode. */ if (!bgp_update_delay_active(peer->bgp)) { - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0); } @@ -2323,14 +2315,14 @@ static enum bgp_fsm_state_progress bgp_establish(struct peer *peer) /* Keepalive packet is received. */ static enum bgp_fsm_state_progress bgp_fsm_keepalive(struct peer *peer) { - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_holdtime); return BGP_FSM_SUCCESS; } /* Update packet is received. */ static enum bgp_fsm_state_progress bgp_fsm_update(struct peer *peer) { - THREAD_OFF(peer->t_holdtime); + EVENT_OFF(peer->t_holdtime); return BGP_FSM_SUCCESS; } @@ -2372,13 +2364,13 @@ void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops) break; case Connect: if (!has_valid_nexthops) { - THREAD_OFF(peer->t_connect); + EVENT_OFF(peer->t_connect); BGP_EVENT_ADD(peer, TCP_fatal_error); } break; case Active: if (has_valid_nexthops) { - THREAD_OFF(peer->t_connect); + EVENT_OFF(peer->t_connect); BGP_EVENT_ADD(peer, ConnectRetry_timer_expired); } break; @@ -2564,13 +2556,13 @@ static const struct { }; /* Execute event process. */ -void bgp_event(struct thread *thread) +void bgp_event(struct event *thread) { enum bgp_fsm_events event; struct peer *peer; - peer = THREAD_ARG(thread); - event = THREAD_VAL(thread); + peer = EVENT_ARG(thread); + event = EVENT_VAL(thread); peer_lock(peer); bgp_event_update(peer, event); @@ -2649,12 +2641,12 @@ int bgp_event_update(struct peer *peer, enum bgp_fsm_events event) ret != BGP_FSM_FAILURE_AND_DELETE) { 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, last reset: %s", peer->host, bgp_event_str[peer->cur_event], lookup_msg(bgp_status_msg, peer->status, NULL), bgp_event_str[peer->last_event], - bgp_event_str[peer->last_major_event], - peer->fd); + bgp_event_str[peer->last_major_event], peer->fd, + peer_down_str[peer->last_reset]); bgp_stop(peer); bgp_fsm_change_status(peer, Idle); bgp_timer_set(peer); @@ -2817,7 +2809,7 @@ 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 *global_gr_mode = NULL; + const char *global_gr_mode = "???"; switch (gl_mode) { case GLOBAL_HELPER: diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index bd1a6adfe874..daf31b266ecb 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -7,37 +7,45 @@ #ifndef _QUAGGA_BGP_FSM_H #define _QUAGGA_BGP_FSM_H +enum bgp_fsm_state_progress { + BGP_FSM_FAILURE_AND_DELETE = -2, + BGP_FSM_FAILURE = -1, + BGP_FSM_SUCCESS = 0, + BGP_FSM_SUCCESS_STATE_TRANSFER = 1, +}; + /* Macro for BGP read, write and timer thread. */ #define BGP_TIMER_ON(T, F, V) \ do { \ if ((peer->status != Deleted)) \ - thread_add_timer(bm->master, (F), peer, (V), &(T)); \ + event_add_timer(bm->master, (F), peer, (V), &(T)); \ } while (0) #define BGP_EVENT_ADD(P, E) \ do { \ if ((P)->status != Deleted) \ - thread_add_event(bm->master, bgp_event, (P), (E), \ - NULL); \ + event_add_event(bm->master, bgp_event, (P), (E), \ + NULL); \ } while (0) #define BGP_EVENT_FLUSH(P) \ do { \ assert(peer); \ - thread_cancel_event_ready(bm->master, (P)); \ + event_cancel_event_ready(bm->master, (P)); \ } while (0) -#define BGP_UPDATE_GROUP_TIMER_ON(T, F) \ - do { \ - if (BGP_SUPPRESS_FIB_ENABLED(peer->bgp) && \ - PEER_ROUTE_ADV_DELAY(peer)) \ - thread_add_timer_msec(bm->master, (F), peer, \ - (BGP_DEFAULT_UPDATE_ADVERTISEMENT_TIME * 1000),\ - (T)); \ - else \ - thread_add_timer_msec(bm->master, (F), peer, \ - 0, (T)); \ - } while (0) \ +#define BGP_UPDATE_GROUP_TIMER_ON(T, F) \ + do { \ + if (BGP_SUPPRESS_FIB_ENABLED(peer->bgp) && \ + PEER_ROUTE_ADV_DELAY(peer)) \ + event_add_timer_msec( \ + bm->master, (F), peer, \ + (BGP_DEFAULT_UPDATE_ADVERTISEMENT_TIME * \ + 1000), \ + (T)); \ + else \ + event_add_timer_msec(bm->master, (F), peer, 0, (T)); \ + } while (0) #define BGP_MSEC_JITTER 10 @@ -105,11 +113,11 @@ * Update FSM for peer based on whether we have valid nexthops or not. */ extern void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops); -extern void bgp_event(struct thread *); +extern void bgp_event(struct event *event); extern int bgp_event_update(struct peer *, enum bgp_fsm_events event); -extern int bgp_stop(struct peer *peer); +extern enum bgp_fsm_state_progress bgp_stop(struct peer *peer); extern void bgp_timer_set(struct peer *); -extern void bgp_routeadv_timer(struct thread *); +extern void bgp_routeadv_timer(struct event *event); extern void bgp_fsm_change_status(struct peer *peer, enum bgp_fsm_status status); extern const char *const peer_down_str[]; diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index 530b77987d0c..e9178fd8fccd 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -17,7 +17,7 @@ #include "network.h" // for ERRNO_IO_RETRY #include "stream.h" // for stream_get_endp, stream_getw_from, str... #include "ringbuf.h" // for ringbuf_remain, ringbuf_peek, ringbuf_... -#include "thread.h" // for THREAD_OFF, THREAD_ARG, thread... +#include "frrevent.h" // for EVENT_OFF, EVENT_ARG, thread... #include "bgpd/bgp_io.h" #include "bgpd/bgp_debug.h" // for bgp_debug_neighbor_events, bgp_type_str @@ -31,8 +31,8 @@ /* forward declarations */ static uint16_t bgp_write(struct peer *); static uint16_t bgp_read(struct peer *peer, int *code_p); -static void bgp_process_writes(struct thread *); -static void bgp_process_reads(struct thread *); +static void bgp_process_writes(struct event *event); +static void bgp_process_reads(struct event *event); static bool validate_header(struct peer *); /* generic i/o status codes */ @@ -55,8 +55,8 @@ void bgp_writes_on(struct peer *peer) assert(!peer->t_connect_check_w); assert(peer->fd); - thread_add_write(fpt->master, bgp_process_writes, peer, peer->fd, - &peer->t_write); + event_add_write(fpt->master, bgp_process_writes, peer, peer->fd, + &peer->t_write); SET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON); } @@ -65,8 +65,8 @@ void bgp_writes_off(struct peer *peer) struct frr_pthread *fpt = bgp_pth_io; assert(fpt->running); - thread_cancel_async(fpt->master, &peer->t_write, NULL); - THREAD_OFF(peer->t_generate_updgrp_packets); + event_cancel_async(fpt->master, &peer->t_write, NULL); + EVENT_OFF(peer->t_generate_updgrp_packets); UNSET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON); } @@ -85,8 +85,8 @@ void bgp_reads_on(struct peer *peer) assert(!peer->t_connect_check_w); assert(peer->fd); - thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd, - &peer->t_read); + event_add_read(fpt->master, bgp_process_reads, peer, peer->fd, + &peer->t_read); SET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON); } @@ -96,9 +96,9 @@ void bgp_reads_off(struct peer *peer) struct frr_pthread *fpt = bgp_pth_io; assert(fpt->running); - thread_cancel_async(fpt->master, &peer->t_read, NULL); - THREAD_OFF(peer->t_process_packet); - THREAD_OFF(peer->t_process_packet_error); + event_cancel_async(fpt->master, &peer->t_read, NULL); + EVENT_OFF(peer->t_process_packet); + EVENT_OFF(peer->t_process_packet_error); UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON); } @@ -108,10 +108,10 @@ void bgp_reads_off(struct peer *peer) /* * Called from I/O pthread when a file descriptor has become ready for writing. */ -static void bgp_process_writes(struct thread *thread) +static void bgp_process_writes(struct event *thread) { static struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); uint16_t status; bool reschedule; bool fatal = false; @@ -142,8 +142,8 @@ static void bgp_process_writes(struct thread *thread) * sent in the update message */ if (reschedule) { - thread_add_write(fpt->master, bgp_process_writes, peer, - peer->fd, &peer->t_write); + event_add_write(fpt->master, bgp_process_writes, peer, peer->fd, + &peer->t_write); } else if (!fatal) { BGP_UPDATE_GROUP_TIMER_ON(&peer->t_generate_updgrp_packets, bgp_generate_updgrp_packets); @@ -210,7 +210,7 @@ static int read_ibuf_work(struct peer *peer) * We read as much data as possible, process as many packets as we can and * place them on peer->ibuf for secondary processing by the main thread. */ -static void bgp_process_reads(struct thread *thread) +static void bgp_process_reads(struct event *thread) { /* clang-format off */ static struct peer *peer; /* peer to read from */ @@ -223,9 +223,9 @@ static void bgp_process_reads(struct thread *thread) int ret = 1; /* clang-format on */ - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); - if (peer->fd < 0 || bm->terminating) + if (bm->terminating || peer->fd < 0) return; struct frr_pthread *fpt = bgp_pth_io; @@ -247,8 +247,8 @@ static void bgp_process_reads(struct thread *thread) /* 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, &peer->t_process_packet_error); + event_add_event(bm->master, bgp_packet_process_error, peer, + code, &peer->t_process_packet_error); goto done; } @@ -292,11 +292,11 @@ static void bgp_process_reads(struct thread *thread) if (!ibuf_full) assert(ringbuf_space(peer->ibuf_work) >= peer->max_packet_size); - thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd, - &peer->t_read); + event_add_read(fpt->master, bgp_process_reads, peer, peer->fd, + &peer->t_read); if (added_pkt) - thread_add_event(bm->master, bgp_process_packet, peer, 0, - &peer->t_process_packet); + event_add_event(bm->master, bgp_process_packet, peer, 0, + &peer->t_process_packet); } /* diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c index b9e4d7c4c283..48bde1220d6c 100644 --- a/bgpd/bgp_keepalives.c +++ b/bgpd/bgp_keepalives.c @@ -15,7 +15,7 @@ #include "memory.h" // for MTYPE_TMP, XFREE, XCALLOC, XMALLOC #include "monotime.h" // for monotime, monotime_since -#include "bgpd/bgpd.h" // for peer, PEER_THREAD_KEEPALIVES_ON, peer... +#include "bgpd/bgpd.h" // for peer, PEER_EVENT_KEEPALIVES_ON, peer... #include "bgpd/bgp_debug.h" // for bgp_debug_neighbor_events #include "bgpd/bgp_packet.h" // for bgp_keepalive_send #include "bgpd/bgp_keepalives.h" @@ -136,12 +136,7 @@ static unsigned int peer_hash_key(const void *arg) /* Cleanup handler / deinitializer. */ static void bgp_keepalives_finish(void *arg) { - if (peerhash) { - hash_clean(peerhash, pkat_del); - hash_free(peerhash); - } - - peerhash = NULL; + hash_clean_and_free(&peerhash, pkat_del); pthread_mutex_unlock(peerhash_mtx); pthread_mutex_destroy(peerhash_mtx); @@ -167,7 +162,7 @@ void *bgp_keepalives_start(void *arg) /* * The RCU mechanism for each pthread is initialized in a "locked" * state. That's ok for pthreads using the frr_pthread, - * thread_fetch event loop, because that event loop unlocks regularly. + * event_fetch event loop, because that event loop unlocks regularly. * For foreign pthreads, the lock needs to be unlocked so that the * background rcu pthread can run. */ diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c index 0cad119af101..30090e0590b7 100644 --- a/bgpd/bgp_label.c +++ b/bgpd/bgp_label.c @@ -6,7 +6,7 @@ #include #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "zclient.h" #include "stream.h" @@ -297,6 +297,9 @@ static int bgp_nlri_get_labels(struct peer *peer, uint8_t *pnt, uint8_t plen, uint8_t llen = 0; uint8_t label_depth = 0; + if (plen < BGP_LABEL_BYTES) + return 0; + for (; data < lim; data += BGP_LABEL_BYTES) { memcpy(label, data, BGP_LABEL_BYTES); llen += BGP_LABEL_BYTES; @@ -359,6 +362,9 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN); addpath_id = ntohl(addpath_id); pnt += BGP_ADDPATH_ID_LEN; + + if (pnt >= lim) + return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW; } /* Fetch prefix length. */ @@ -377,6 +383,13 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, /* Fill in the labels */ llen = bgp_nlri_get_labels(peer, pnt, psize, &label); + if (llen == 0) { + flog_err( + EC_BGP_UPDATE_RCV, + "%s [Error] Update packet error (wrong label length 0)", + peer->host); + return BGP_NLRI_PARSE_ERROR_LABEL_LENGTH; + } p.prefixlen = prefixlen - BSIZE(llen); /* There needs to be at least one label */ @@ -384,8 +397,6 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, flog_err(EC_BGP_UPDATE_RCV, "%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); return BGP_NLRI_PARSE_ERROR_LABEL_LENGTH; } @@ -459,3 +470,20 @@ int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, return BGP_NLRI_PARSE_OK; } + +bool bgp_labels_same(const mpls_label_t *tbl_a, const uint32_t num_labels_a, + const mpls_label_t *tbl_b, const uint32_t num_labels_b) +{ + uint32_t i; + + if (num_labels_a != num_labels_b) + return false; + if (num_labels_a == 0) + return true; + + for (i = 0; i < num_labels_a; i++) { + if (tbl_a[i] != tbl_b[i]) + return false; + } + return true; +} diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h index ac7fbb27fbe9..b54403ee89b0 100644 --- a/bgpd/bgp_label.h +++ b/bgpd/bgp_label.h @@ -26,6 +26,10 @@ extern mpls_label_t bgp_adv_label(struct bgp_dest *dest, extern int bgp_nlri_parse_label(struct peer *peer, struct attr *attr, struct bgp_nlri *packet); +extern bool bgp_labels_same(const mpls_label_t *tbl_a, + const uint32_t num_labels_a, + const mpls_label_t *tbl_b, + const uint32_t num_labels_b); static inline int bgp_labeled_safi(safi_t safi) { diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c index f0f207442ea6..8ebb3339507a 100644 --- a/bgpd/bgp_labelpool.c +++ b/bgpd/bgp_labelpool.c @@ -15,7 +15,6 @@ #include "linklist.h" #include "skiplist.h" #include "workqueue.h" -#include "zclient.h" #include "mpls.h" #include "bgpd/bgpd.h" @@ -23,17 +22,15 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_rd.h" #define BGP_LABELPOOL_ENABLE_TESTS 0 #include "bgpd/bgp_labelpool_clippy.c" -/* - * Definitions and external declarations. - */ -extern struct zclient *zclient; - #if BGP_LABELPOOL_ENABLE_TESTS static void lptest_init(void); static void lptest_finish(void); @@ -177,7 +174,7 @@ static void lp_chunk_free(void *goner) XFREE(MTYPE_BGP_LABEL_CHUNK, goner); } -void bgp_lp_init(struct thread_master *master, struct labelpool *pool) +void bgp_lp_init(struct event_loop *master, struct labelpool *pool) { if (BGP_DEBUG(labelpool, LABELPOOL)) zlog_debug("%s: entry", __func__); @@ -220,6 +217,8 @@ void bgp_lp_finish(void) { struct lp_fifo *lf; struct work_queue_item *item, *titem; + struct listnode *node; + struct lp_chunk *chunk; #if BGP_LABELPOOL_ENABLE_TESTS lptest_finish(); @@ -233,6 +232,9 @@ void bgp_lp_finish(void) skiplist_free(lp->inuse); lp->inuse = NULL; + for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) + bgp_zebra_release_label_range(chunk->first, chunk->last); + list_delete(&lp->chunks); while ((lf = lp_fifo_pop(&lp->requests))) { @@ -445,15 +447,13 @@ void bgp_lp_get( lp_fifo_add_tail(&lp->requests, lf); if (lp_fifo_count(&lp->requests) > lp->pending_count) { - if (!zclient || zclient->sock < 0) + if (!bgp_zebra_request_label_range(MPLS_LABEL_BASE_ANY, + lp->next_chunksize)) return; - if (zclient_send_get_label_chunk(zclient, 0, lp->next_chunksize, - MPLS_LABEL_BASE_ANY) != - ZCLIENT_SEND_FAILURE) { - lp->pending_count += lp->next_chunksize; - if ((lp->next_chunksize << 1) <= LP_CHUNK_SIZE_MAX) - lp->next_chunksize <<= 1; - } + + lp->pending_count += lp->next_chunksize; + if ((lp->next_chunksize << 1) <= LP_CHUNK_SIZE_MAX) + lp->next_chunksize <<= 1; } } @@ -500,46 +500,12 @@ void bgp_lp_release( } } -/* - * zebra response giving us a chunk of labels - */ -void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) +static void bgp_sync_label_manager(struct event *e) { - struct lp_chunk *chunk; int debug = BGP_DEBUG(labelpool, LABELPOOL); struct lp_fifo *lf; - uint32_t labelcount; - - if (last < first) { - flog_err(EC_BGP_LABEL, - "%s: zebra label chunk invalid: first=%u, last=%u", - __func__, first, last); - return; - } - - chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk)); - - labelcount = last - first + 1; - - chunk->first = first; - chunk->last = last; - chunk->nfree = labelcount; - bf_init(chunk->allocated_map, labelcount); - - /* - * Optimize for allocation by adding the new (presumably larger) - * chunk at the head of the list so it is examined first. - */ - listnode_add_head(lp->chunks, chunk); - - lp->pending_count -= labelcount; - - if (debug) { - zlog_debug("%s: %zu pending requests", __func__, - lp_fifo_count(&lp->requests)); - } - while (labelcount && (lf = lp_fifo_first(&lp->requests))) { + while ((lf = lp_fifo_pop(&lp->requests))) { struct lp_lcb *lcb; void *labelid = lf->lcb.labelid; @@ -585,8 +551,6 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) break; } - labelcount -= 1; - /* * we filled the request from local pool. * Enqueue response work item with new label. @@ -607,9 +571,41 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last) work_queue_add(lp->callback_q, q); finishedrequest: - lp_fifo_del(&lp->requests, lf); XFREE(MTYPE_BGP_LABEL_FIFO, lf); +} + +event_add_timer(bm->master, bgp_sync_label_manager, NULL, 1, + &bm->t_bgp_sync_label_manager); +} + +void bgp_lp_event_chunk(uint32_t first, uint32_t last) +{ + struct lp_chunk *chunk; + uint32_t labelcount; + + if (last < first) { + flog_err(EC_BGP_LABEL, + "%s: zebra label chunk invalid: first=%u, last=%u", + __func__, first, last); + return; } + + chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk)); + + labelcount = last - first + 1; + + chunk->first = first; + chunk->last = last; + chunk->nfree = labelcount; + bf_init(chunk->allocated_map, labelcount); + + /* + * Optimize for allocation by adding the new (presumably larger) + * chunk at the head of the list so it is examined first. + */ + listnode_add_head(lp->chunks, chunk); + + lp->pending_count -= labelcount; } /* @@ -631,7 +627,6 @@ void bgp_lp_event_zebra_up(void) unsigned int chunks_needed; void *labelid; struct lp_lcb *lcb; - int lm_init_ok; lp->reconnect_count++; /* @@ -651,22 +646,16 @@ void bgp_lp_event_zebra_up(void) chunks_needed = (labels_needed / lp->next_chunksize) + 1; labels_needed = chunks_needed * lp->next_chunksize; - lm_init_ok = lm_label_manager_connect(zclient, 1) == 0; - - if (!lm_init_ok) { - zlog_err("%s: label manager connection error", __func__); - return; - } - - zclient_send_get_label_chunk(zclient, 0, labels_needed, - MPLS_LABEL_BASE_ANY); - lp->pending_count = labels_needed; - /* * Invalidate current list of chunks */ list_delete_all_node(lp->chunks); + if (!bgp_zebra_request_label_range(MPLS_LABEL_BASE_ANY, labels_needed)) + return; + + lp->pending_count = labels_needed; + /* * Invalidate any existing labels and requeue them as requests */ @@ -709,6 +698,9 @@ void bgp_lp_event_zebra_up(void) skiplist_delete_first(lp->inuse); } + + event_add_timer(bm->master, bgp_sync_label_manager, NULL, 1, + &bm->t_bgp_sync_label_manager); } DEFUN(show_bgp_labelpool_summary, show_bgp_labelpool_summary_cmd, @@ -830,6 +822,26 @@ DEFUN(show_bgp_labelpool_ledger, show_bgp_labelpool_ledger_cmd, lcb->label); break; + case LP_TYPE_NEXTHOP: + if (uj) { + json_object_string_add(json_elem, "prefix", + "nexthop"); + json_object_int_add(json_elem, "label", + lcb->label); + } else + vty_out(vty, "%-18s %u\n", "nexthop", + lcb->label); + break; + case LP_TYPE_BGP_L3VPN_BIND: + if (uj) { + json_object_string_add(json_elem, "prefix", + "l3vpn-bind"); + json_object_int_add(json_elem, "label", + lcb->label); + } else + vty_out(vty, "%-18s %u\n", "l3vpn-bind", + lcb->label); + break; } } if (uj) @@ -919,6 +931,24 @@ DEFUN(show_bgp_labelpool_inuse, show_bgp_labelpool_inuse_cmd, vty_out(vty, "%-18s %u\n", "VRF", label); break; + case LP_TYPE_NEXTHOP: + if (uj) { + json_object_string_add(json_elem, "prefix", + "nexthop"); + json_object_int_add(json_elem, "label", label); + } else + vty_out(vty, "%-18s %u\n", "nexthop", + label); + break; + case LP_TYPE_BGP_L3VPN_BIND: + if (uj) { + json_object_string_add(json_elem, "prefix", + "l3vpn-bind"); + json_object_int_add(json_elem, "label", label); + } else + vty_out(vty, "%-18s %u\n", "l3vpn-bind", + label); + break; } } if (uj) @@ -991,6 +1021,20 @@ DEFUN(show_bgp_labelpool_requests, show_bgp_labelpool_requests_cmd, else vty_out(vty, "VRF\n"); break; + case LP_TYPE_NEXTHOP: + if (uj) + json_object_string_add(json_elem, "prefix", + "nexthop"); + else + vty_out(vty, "Nexthop\n"); + break; + case LP_TYPE_BGP_L3VPN_BIND: + if (uj) + json_object_string_add(json_elem, "prefix", + "l3vpn-bind"); + else + vty_out(vty, "L3VPN-BIND\n"); + break; } } if (uj) @@ -1053,6 +1097,100 @@ DEFUN(show_bgp_labelpool_chunks, show_bgp_labelpool_chunks_cmd, return CMD_SUCCESS; } +static void show_bgp_nexthop_label_afi(struct vty *vty, afi_t afi, + struct bgp *bgp, bool detail) +{ + struct bgp_label_per_nexthop_cache_head *tree; + struct bgp_label_per_nexthop_cache *iter; + safi_t safi; + void *src; + char buf[PREFIX2STR_BUFFER]; + char labelstr[MPLS_LABEL_STRLEN]; + struct bgp_dest *dest; + struct bgp_path_info *path; + struct bgp *bgp_path; + struct bgp_table *table; + time_t tbuf; + + vty_out(vty, "Current BGP label nexthop cache for %s, VRF %s\n", + afi2str(afi), bgp->name_pretty); + + tree = &bgp->mpls_labels_per_nexthop[afi]; + frr_each (bgp_label_per_nexthop_cache, tree, iter) { + if (afi2family(afi) == AF_INET) + src = (void *)&iter->nexthop.u.prefix4; + else + src = (void *)&iter->nexthop.u.prefix6; + + vty_out(vty, " %s, label %s #paths %u\n", + inet_ntop(afi2family(afi), src, buf, sizeof(buf)), + mpls_label2str(1, &iter->label, labelstr, + sizeof(labelstr), 0, true), + iter->path_count); + if (iter->nh) + vty_out(vty, " if %s\n", + ifindex2ifname(iter->nh->ifindex, + iter->nh->vrf_id)); + tbuf = time(NULL) - (monotime(NULL) - iter->last_update); + vty_out(vty, " Last update: %s", ctime(&tbuf)); + if (!detail) + continue; + vty_out(vty, " Paths:\n"); + LIST_FOREACH (path, &(iter->paths), + mplsvpn.blnc.label_nh_thread) { + dest = path->net; + table = bgp_dest_table(dest); + assert(dest && table); + afi = family2afi(bgp_dest_get_prefix(dest)->family); + safi = table->safi; + bgp_path = table->bgp; + + if (dest->pdest) { + vty_out(vty, " %d/%d %pBD RD ", afi, safi, + dest); + + vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation), + (struct prefix_rd *)bgp_dest_get_prefix( + dest->pdest)); + vty_out(vty, " %s flags 0x%x\n", + bgp_path->name_pretty, path->flags); + } else + vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", + afi, safi, dest, bgp_path->name_pretty, + path->flags); + } + } +} + +DEFPY(show_bgp_nexthop_label, show_bgp_nexthop_label_cmd, + "show bgp [ VIEWVRFNAME] label-nexthop [detail]", + SHOW_STR BGP_STR BGP_INSTANCE_HELP_STR + "BGP label per-nexthop table\n" + "Show detailed information\n") +{ + int idx = 0; + char *vrf = NULL; + struct bgp *bgp; + bool detail = false; + int afi; + + if (argv_find(argv, argc, "vrf", &idx)) { + vrf = argv[++idx]->arg; + bgp = bgp_lookup_by_name(vrf); + } else + bgp = bgp_get_default(); + + if (!bgp) + return CMD_SUCCESS; + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + for (afi = AFI_IP; afi <= AFI_IP6; afi++) + show_bgp_nexthop_label_afi(vty, afi, bgp, detail); + return CMD_SUCCESS; +} + #if BGP_LABELPOOL_ENABLE_TESTS /*------------------------------------------------------------------------ * Testing code start @@ -1091,7 +1229,7 @@ struct lp_test { struct timeval starttime; struct skiplist *timestamps_alloc; struct skiplist *timestamps_dealloc; - struct thread *event_thread; + struct event *event_thread; unsigned int counter[LPT_STAT_MAX]; }; @@ -1150,7 +1288,7 @@ static int test_cb(mpls_label_t label, void *labelid, bool allocated) return 0; } -static void labelpool_test_event_handler(struct thread *thread) +static void labelpool_test_event_handler(struct event *thread) { struct lp_test *tcb; @@ -1202,7 +1340,7 @@ static void lptest_stop(void) } if (tcb->event_thread) - thread_cancel(&tcb->event_thread); + event_cancel(&tcb->event_thread); lpt_inprogress = false; } @@ -1491,7 +1629,7 @@ static void lptest_delete(void *val) } if (tcb->event_thread) - thread_cancel(&tcb->event_thread); + event_cancel(&tcb->event_thread); memset(tcb, 0, sizeof(*tcb)); @@ -1532,3 +1670,66 @@ void bgp_lp_vty_init(void) install_element(ENABLE_NODE, &clear_labelpool_perf_test_cmd); #endif /* BGP_LABELPOOL_ENABLE_TESTS */ } + +DEFINE_MTYPE_STATIC(BGPD, LABEL_PER_NEXTHOP_CACHE, + "BGP Label Per Nexthop entry"); + +/* The nexthops values are compared to + * find in the tree the appropriate cache entry + */ +int bgp_label_per_nexthop_cache_cmp(const struct bgp_label_per_nexthop_cache *a, + const struct bgp_label_per_nexthop_cache *b) +{ + return prefix_cmp(&a->nexthop, &b->nexthop); +} + +struct bgp_label_per_nexthop_cache * +bgp_label_per_nexthop_new(struct bgp_label_per_nexthop_cache_head *tree, + struct prefix *nexthop) +{ + struct bgp_label_per_nexthop_cache *blnc; + + blnc = XCALLOC(MTYPE_LABEL_PER_NEXTHOP_CACHE, + sizeof(struct bgp_label_per_nexthop_cache)); + blnc->tree = tree; + blnc->label = MPLS_INVALID_LABEL; + prefix_copy(&blnc->nexthop, nexthop); + LIST_INIT(&(blnc->paths)); + bgp_label_per_nexthop_cache_add(tree, blnc); + + return blnc; +} + +struct bgp_label_per_nexthop_cache * +bgp_label_per_nexthop_find(struct bgp_label_per_nexthop_cache_head *tree, + struct prefix *nexthop) +{ + struct bgp_label_per_nexthop_cache blnc = {}; + + if (!tree) + return NULL; + + memcpy(&blnc.nexthop, nexthop, sizeof(struct prefix)); + return bgp_label_per_nexthop_cache_find(tree, &blnc); +} + +void bgp_label_per_nexthop_free(struct bgp_label_per_nexthop_cache *blnc) +{ + if (blnc->label != MPLS_INVALID_LABEL) { + bgp_zebra_send_nexthop_label(ZEBRA_MPLS_LABELS_DELETE, + blnc->label, blnc->nh->ifindex, + blnc->nh->vrf_id, ZEBRA_LSP_BGP, + &blnc->nexthop, 0, NULL); + bgp_lp_release(LP_TYPE_NEXTHOP, blnc, blnc->label); + } + bgp_label_per_nexthop_cache_del(blnc->tree, blnc); + if (blnc->nh) + nexthop_free(blnc->nh); + blnc->nh = NULL; + XFREE(MTYPE_LABEL_PER_NEXTHOP_CACHE, blnc); +} + +void bgp_label_per_nexthop_init(void) +{ + install_element(VIEW_NODE, &show_bgp_nexthop_label_cmd); +} diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h index 955ab2d697c4..a17482d112e8 100644 --- a/bgpd/bgp_labelpool.h +++ b/bgpd/bgp_labelpool.h @@ -17,6 +17,8 @@ */ #define LP_TYPE_VRF 0x00000001 #define LP_TYPE_BGP_LU 0x00000002 +#define LP_TYPE_NEXTHOP 0x00000003 +#define LP_TYPE_BGP_L3VPN_BIND 0x00000004 PREDECL_LIST(lp_fifo); @@ -31,14 +33,65 @@ struct labelpool { uint32_t next_chunksize; /* request this many labels */ }; -extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool); +extern void bgp_lp_init(struct event_loop *master, struct labelpool *pool); extern void bgp_lp_finish(void); extern void bgp_lp_get(int type, void *labelid, int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated)); extern void bgp_lp_release(int type, void *labelid, mpls_label_t label); -extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last); +extern void bgp_lp_event_chunk(uint32_t first, uint32_t last); extern void bgp_lp_event_zebra_down(void); extern void bgp_lp_event_zebra_up(void); extern void bgp_lp_vty_init(void); +struct bgp_label_per_nexthop_cache; +PREDECL_RBTREE_UNIQ(bgp_label_per_nexthop_cache); + +extern int +bgp_label_per_nexthop_cache_cmp(const struct bgp_label_per_nexthop_cache *a, + const struct bgp_label_per_nexthop_cache *b); + +struct bgp_label_per_nexthop_cache { + + /* RB-tree entry. */ + struct bgp_label_per_nexthop_cache_item entry; + + /* the nexthop is the key of the list */ + struct prefix nexthop; + + /* calculated label */ + mpls_label_t label; + + /* number of path_vrfs */ + unsigned int path_count; + + /* back pointer to bgp instance */ + struct bgp *to_bgp; + + /* copy a nexthop resolution from bgp nexthop tracking + * used to extract the interface nexthop + */ + struct nexthop *nh; + + /* list of path_vrfs using it */ + LIST_HEAD(path_lists, bgp_path_info) paths; + + time_t last_update; + + /* Back pointer to the cache tree this entry belongs to. */ + struct bgp_label_per_nexthop_cache_head *tree; +}; + +DECLARE_RBTREE_UNIQ(bgp_label_per_nexthop_cache, + struct bgp_label_per_nexthop_cache, entry, + bgp_label_per_nexthop_cache_cmp); + +void bgp_label_per_nexthop_free(struct bgp_label_per_nexthop_cache *blnc); + +struct bgp_label_per_nexthop_cache * +bgp_label_per_nexthop_new(struct bgp_label_per_nexthop_cache_head *tree, + struct prefix *nexthop); +struct bgp_label_per_nexthop_cache * +bgp_label_per_nexthop_find(struct bgp_label_per_nexthop_cache_head *tree, + struct prefix *nexthop); +void bgp_label_per_nexthop_init(void); #endif /* _FRR_BGP_LABELPOOL_H */ diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c index 2329bcb6c648..1b8c22a5038c 100644 --- a/bgpd/bgp_lcommunity.c +++ b/bgpd/bgp_lcommunity.c @@ -197,12 +197,13 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, } /* 1 space + lcom->size lcom strings + null terminator */ - size_t str_buf_sz = BUFSIZ; + size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz); + len = 0; for (i = 0; i < lcom->size; i++) { if (i > 0) - strlcat(str_buf, " ", str_buf_sz); + len = strlcat(str_buf, " ", str_buf_sz); pnt = lcom->val + (i * LCOMMUNITY_SIZE); pnt = ptr_get_be32(pnt, &global); @@ -215,11 +216,22 @@ static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1, local2); + /* + * Aliases can cause havoc, if the alias length is greater + * than the LCOMMUNITY_STRLEN for a particular item + * then we need to realloc the memory associated + * with the string so that it can fit + */ const char *com2alias = translate_alias ? bgp_community2alias(lcsb) : lcsb; + size_t individual_len = strlen(com2alias); + if (individual_len + len > str_buf_sz) { + str_buf_sz = individual_len + len + 1; + str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, + str_buf_sz); + } len = strlcat(str_buf, com2alias, str_buf_sz); - assert((unsigned int)len < str_buf_sz); if (make_json) { json_string = json_object_new_string(com2alias); @@ -334,9 +346,7 @@ void lcommunity_init(void) void lcommunity_finish(void) { - hash_clean(lcomhash, (void (*)(void *))lcommunity_hash_free); - hash_free(lcomhash); - lcomhash = NULL; + hash_clean_and_free(&lcomhash, (void (*)(void *))lcommunity_hash_free); } /* Get next Large Communities token from the string. diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c index 5d49175e4339..0398e4e8c17c 100644 --- a/bgpd/bgp_mac.c +++ b/bgpd/bgp_mac.c @@ -64,8 +64,7 @@ static void bgp_mac_hash_free(void *data) void bgp_mac_finish(void) { - hash_clean(bm->self_mac_hash, bgp_mac_hash_free); - hash_free(bm->self_mac_hash); + hash_clean_and_free(&bm->self_mac_hash, bgp_mac_hash_free); } static void bgp_mac_hash_interface_string_del(void *val) @@ -280,15 +279,29 @@ static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname, } } +/* Add/Update entry of the 'bgp mac hash' table. + * A rescan of the EVPN tables is only needed if + * a new hash bucket is allocated. + * Learning an existing mac on a new interface (or + * having an existing mac move from one interface to + * another) does not result in changes to self mac + * state, so we shouldn't trigger a rescan. + */ void bgp_mac_add_mac_entry(struct interface *ifp) { struct bgp_self_mac lookup; struct bgp_self_mac *bsm; struct bgp_self_mac *old_bsm; char *ifname; + bool mac_added = false; memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN); - bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc); + bsm = hash_lookup(bm->self_mac_hash, &lookup); + if (!bsm) { + bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc); + /* mac is new, rescan needs to be triggered */ + mac_added = true; + } /* * Does this happen to be a move @@ -319,7 +332,8 @@ void bgp_mac_add_mac_entry(struct interface *ifp) listnode_add(bsm->ifp_list, ifname); } - bgp_mac_rescan_all_evpn_tables(&bsm->macaddr); + if (mac_added) + bgp_mac_rescan_all_evpn_tables(&bsm->macaddr); } void bgp_mac_del_mac_entry(struct interface *ifp) @@ -361,7 +375,7 @@ bool bgp_mac_exist(const struct ethaddr *mac) return true; } -/* This API checks EVPN type-2 prefix and comapares +/* This API checks EVPN type-2 prefix and compares * mac against any of local assigned (SVIs) MAC * address. */ @@ -376,8 +390,6 @@ bool bgp_mac_entry_exists(const struct prefix *p) return false; return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac); - - return true; } static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 85e49043725e..074059c1461d 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -9,7 +9,7 @@ #include "vector.h" #include "command.h" #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include #include "memory.h" #include "prefix.h" diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 551da692497c..da599688ea50 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -39,6 +39,9 @@ #include "bgpd/rfapi/rfapi_backend.h" #endif +DEFINE_MTYPE_STATIC(BGPD, MPLSVPN_NH_LABEL_BIND_CACHE, + "BGP MPLSVPN nexthop label bind cache"); + /* * Definitions and external declarations. */ @@ -593,7 +596,7 @@ static void sid_register(struct bgp *bgp, const struct in6_addr *sid, listnode_add(bgp->srv6_functions, func); } -static void sid_unregister(struct bgp *bgp, const struct in6_addr *sid) +void sid_unregister(struct bgp *bgp, const struct in6_addr *sid) { struct listnode *node, *nnode; struct bgp_srv6_function *func; @@ -952,8 +955,6 @@ void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset, static bool labels_same(struct bgp_path_info *bpi, mpls_label_t *label, uint32_t n) { - uint32_t i; - if (!bpi->extra) { if (!n) return true; @@ -961,14 +962,9 @@ static bool labels_same(struct bgp_path_info *bpi, mpls_label_t *label, return false; } - if (n != bpi->extra->num_labels) - return false; - - for (i = 0; i < n; ++i) { - if (label[i] != bpi->extra->label[i]) - return false; - } - return true; + return bgp_labels_same((const mpls_label_t *)bpi->extra->label, + bpi->extra->num_labels, + (const mpls_label_t *)label, n); } /* @@ -1103,7 +1099,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, struct bgp_path_info *new; struct bgp_path_info_extra *extra; uint32_t num_sids = 0; - void *parent = source_bpi; + struct bgp_path_info *parent = source_bpi; if (new_attr->srv6_l3vpn || new_attr->srv6_vpn) num_sids = 1; @@ -1116,12 +1112,14 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, /* * Routes that are redistributed into BGP from zebra do not get - * nexthop tracking. However, if those routes are subsequently - * imported to other RIBs within BGP, the leaked routes do not - * carry the original BGP_ROUTE_REDISTRIBUTE sub_type. Therefore, - * in order to determine if the route we are currently leaking - * should have nexthop tracking, we must find the ultimate - * parent so we can check its sub_type. + * nexthop tracking, unless MPLS allocation per nexthop is + * performed. In the default case nexthop tracking does not apply, + * if those routes are subsequently imported to other RIBs within + * BGP, the leaked routes do not carry the original + * BGP_ROUTE_REDISTRIBUTE sub_type. Therefore, in order to determine + * if the route we are currently leaking should have nexthop + * tracking, we must find the ultimate parent so we can check its + * sub_type. * * As of now, source_bpi may at most be a second-generation route * (only one hop back to ultimate parent for vrf-vpn-vrf scheme). @@ -1311,7 +1309,7 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, new->extra->parent = bgp_path_info_lock(parent); bgp_dest_lock_node( - (struct bgp_dest *)((struct bgp_path_info *)parent)->net); + (struct bgp_dest *)parent->net); if (bgp_orig) new->extra->bgp_orig = bgp_lock(bgp_orig); if (nexthop_orig) @@ -1336,6 +1334,272 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, return new; } +void bgp_mplsvpn_path_nh_label_unlink(struct bgp_path_info *pi) +{ + struct bgp_label_per_nexthop_cache *blnc; + + if (!pi) + return; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_LABEL_NH)) + return; + + blnc = pi->mplsvpn.blnc.label_nexthop_cache; + + if (!blnc) + return; + + LIST_REMOVE(pi, mplsvpn.blnc.label_nh_thread); + pi->mplsvpn.blnc.label_nexthop_cache->path_count--; + pi->mplsvpn.blnc.label_nexthop_cache = NULL; + UNSET_FLAG(pi->flags, BGP_PATH_MPLSVPN_LABEL_NH); + + if (LIST_EMPTY(&(blnc->paths))) + bgp_label_per_nexthop_free(blnc); +} + +/* Called upon reception of a ZAPI Message from zebra, about + * a new available label. + */ +static int bgp_mplsvpn_get_label_per_nexthop_cb(mpls_label_t label, + void *context, bool allocated) +{ + struct bgp_label_per_nexthop_cache *blnc = context; + mpls_label_t old_label; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + struct bgp_path_info *pi; + struct bgp_table *table; + + old_label = blnc->label; + + if (debug) + zlog_debug("%s: label=%u, allocated=%d, nexthop=%pFX", __func__, + label, allocated, &blnc->nexthop); + if (allocated) + /* update the entry with the new label */ + blnc->label = label; + else + /* + * previously-allocated label is now invalid + * eg: zebra deallocated the labels and notifies it + */ + blnc->label = MPLS_INVALID_LABEL; + + if (old_label == blnc->label) + return 0; /* no change */ + + /* update paths */ + if (blnc->label != MPLS_INVALID_LABEL) + bgp_zebra_send_nexthop_label(ZEBRA_MPLS_LABELS_ADD, blnc->label, + blnc->nh->ifindex, + blnc->nh->vrf_id, ZEBRA_LSP_BGP, + &blnc->nexthop, 0, NULL); + + LIST_FOREACH (pi, &(blnc->paths), mplsvpn.blnc.label_nh_thread) { + if (!pi->net) + continue; + table = bgp_dest_table(pi->net); + if (!table) + continue; + vpn_leak_from_vrf_update(blnc->to_bgp, table->bgp, pi); + } + + return 0; +} + +/* Get a per label nexthop value: + * - Find and return a per label nexthop from the cache + * - else allocate a new per label nexthop cache entry and request a + * label to zebra. Return MPLS_INVALID_LABEL + */ +static mpls_label_t +_vpn_leak_from_vrf_get_per_nexthop_label(struct bgp_path_info *pi, + struct bgp *to_bgp, + struct bgp *from_bgp, afi_t afi) +{ + struct bgp_nexthop_cache *bnc = pi->nexthop; + struct bgp_label_per_nexthop_cache *blnc; + struct bgp_label_per_nexthop_cache_head *tree; + struct prefix *nh_pfx = NULL; + struct prefix nh_gate = {0}; + + /* extract the nexthop from the BNC nexthop cache */ + switch (bnc->nexthop->type) { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + /* the nexthop is recursive */ + nh_gate.family = AF_INET; + nh_gate.prefixlen = IPV4_MAX_BITLEN; + IPV4_ADDR_COPY(&nh_gate.u.prefix4, &bnc->nexthop->gate.ipv4); + nh_pfx = &nh_gate; + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + /* the nexthop is recursive */ + nh_gate.family = AF_INET6; + nh_gate.prefixlen = IPV6_MAX_BITLEN; + IPV6_ADDR_COPY(&nh_gate.u.prefix6, &bnc->nexthop->gate.ipv6); + nh_pfx = &nh_gate; + break; + case NEXTHOP_TYPE_IFINDEX: + /* the nexthop is direcly connected */ + nh_pfx = &bnc->prefix; + break; + case NEXTHOP_TYPE_BLACKHOLE: + assert(!"Blackhole nexthop. Already checked by the caller."); + } + + /* find or allocate a nexthop label cache entry */ + tree = &from_bgp->mpls_labels_per_nexthop[family2afi(nh_pfx->family)]; + blnc = bgp_label_per_nexthop_find(tree, nh_pfx); + if (!blnc) { + blnc = bgp_label_per_nexthop_new(tree, nh_pfx); + blnc->to_bgp = to_bgp; + /* request a label to zebra for this nexthop + * the response from zebra will trigger the callback + */ + bgp_lp_get(LP_TYPE_NEXTHOP, blnc, + bgp_mplsvpn_get_label_per_nexthop_cb); + } + + if (pi->mplsvpn.blnc.label_nexthop_cache == blnc) + /* no change */ + return blnc->label; + + /* Unlink from any existing nexthop cache. Free the entry if unused. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + + /* updates NHT pi list reference */ + LIST_INSERT_HEAD(&(blnc->paths), pi, mplsvpn.blnc.label_nh_thread); + pi->mplsvpn.blnc.label_nexthop_cache = blnc; + pi->mplsvpn.blnc.label_nexthop_cache->path_count++; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_LABEL_NH); + blnc->last_update = monotime(NULL); + + /* then add or update the selected nexthop */ + if (!blnc->nh) + blnc->nh = nexthop_dup(bnc->nexthop, NULL); + else if (!nexthop_same(bnc->nexthop, blnc->nh)) { + nexthop_free(blnc->nh); + blnc->nh = nexthop_dup(bnc->nexthop, NULL); + if (blnc->label != MPLS_INVALID_LABEL) { + bgp_zebra_send_nexthop_label( + ZEBRA_MPLS_LABELS_REPLACE, blnc->label, + bnc->nexthop->ifindex, bnc->nexthop->vrf_id, + ZEBRA_LSP_BGP, &blnc->nexthop, 0, NULL); + } + } + + return blnc->label; +} + +/* Filter out all the cases where a per nexthop label is not possible: + * - return an invalid label when the nexthop is invalid + * - return the per VRF label when the per nexthop label is not supported + * Otherwise, find or request a per label nexthop. + */ +static mpls_label_t +vpn_leak_from_vrf_get_per_nexthop_label(afi_t afi, struct bgp_path_info *pi, + struct bgp *from_bgp, + struct bgp *to_bgp) +{ + struct bgp_path_info *bpi_ultimate = bgp_get_imported_bpi_ultimate(pi); + struct bgp *bgp_nexthop = NULL; + bool nh_valid; + afi_t nh_afi; + bool is_bgp_static_route; + + is_bgp_static_route = bpi_ultimate->sub_type == BGP_ROUTE_STATIC && + bpi_ultimate->type == ZEBRA_ROUTE_BGP; + + if (is_bgp_static_route == false && afi == AFI_IP && + CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) && + (pi->attr->nexthop.s_addr == INADDR_ANY || + !ipv4_unicast_valid(&pi->attr->nexthop))) { + /* IPv4 nexthop in standard BGP encoding format. + * Format of address is not valid (not any, not unicast). + * Fallback to the per VRF label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + if (is_bgp_static_route == false && afi == AFI_IP && + pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4 && + (pi->attr->mp_nexthop_global_in.s_addr == INADDR_ANY || + !ipv4_unicast_valid(&pi->attr->mp_nexthop_global_in))) { + /* IPv4 nexthop is in MP-BGP encoding format. + * Format of address is not valid (not any, not unicast). + * Fallback to the per VRF label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + if (is_bgp_static_route == false && afi == AFI_IP6 && + (pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL || + pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) && + (IN6_IS_ADDR_UNSPECIFIED(&pi->attr->mp_nexthop_global) || + IN6_IS_ADDR_LOOPBACK(&pi->attr->mp_nexthop_global) || + IN6_IS_ADDR_MULTICAST(&pi->attr->mp_nexthop_global))) { + /* IPv6 nexthop is in MP-BGP encoding format. + * Format of address is not valid + * Fallback to the per VRF label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + /* Check the next-hop reachability. + * Get the bgp instance where the bgp_path_info originates. + */ + if (pi->extra && pi->extra->bgp_orig) + bgp_nexthop = pi->extra->bgp_orig; + else + bgp_nexthop = from_bgp; + + nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr); + nh_valid = bgp_find_or_add_nexthop(from_bgp, bgp_nexthop, nh_afi, + SAFI_UNICAST, pi, NULL, 0, NULL); + + if (!nh_valid && is_bgp_static_route && + !CHECK_FLAG(from_bgp->flags, BGP_FLAG_IMPORT_CHECK)) { + /* "network" prefixes not routable, but since 'no bgp network + * import-check' is configured, they are always valid in the BGP + * table. Fallback to the per-vrf label + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + if (!nh_valid || !pi->nexthop || pi->nexthop->nexthop_num == 0 || + !pi->nexthop->nexthop) { + /* invalid next-hop: + * do not send the per-vrf label + * otherwise, when the next-hop becomes valid, + * we will have 2 BGP updates: + * - one with the per-vrf label + * - the second with the per-nexthop label + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return MPLS_INVALID_LABEL; + } + + if (pi->nexthop->nexthop_num > 1 || + pi->nexthop->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) { + /* Blackhole or ECMP routes + * is not compatible with per-nexthop label. + * Fallback to per-vrf label. + */ + bgp_mplsvpn_path_nh_label_unlink(pi); + return from_bgp->vpn_policy[afi].tovpn_label; + } + + return _vpn_leak_from_vrf_get_per_nexthop_label(pi, to_bgp, from_bgp, + afi); +} + /* cf vnc_import_bgp_add_route_mode_nvegroup() and add_vnc_route() */ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ struct bgp *from_bgp, /* from */ @@ -1528,12 +1792,32 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ nexthop_self_flag = 1; } - label_val = from_bgp->vpn_policy[afi].tovpn_label; - if (label_val == MPLS_LABEL_NONE) { + if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) + /* per nexthop label mode */ + label_val = vpn_leak_from_vrf_get_per_nexthop_label( + afi, path_vrf, from_bgp, to_bgp); + else + /* per VRF label mode */ + label_val = from_bgp->vpn_policy[afi].tovpn_label; + + if (label_val == MPLS_INVALID_LABEL && + CHECK_FLAG(from_bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) { + /* no valid label for the moment + * when the 'bgp_mplsvpn_get_label_per_nexthop_cb' callback gets + * a valid label value, it will call the current function again. + */ + if (debug) + zlog_debug( + "%s: %s skipping: waiting for a valid per-label nexthop.", + __func__, from_bgp->name_pretty); + return; + } + if (label_val == MPLS_LABEL_NONE) encode_label(MPLS_LABEL_IMPLICIT_NULL, &label); - } else { + else encode_label(label_val, &label); - } /* Set originator ID to "me" */ SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)); @@ -1647,6 +1931,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */ */ if (new_info) vpn_leak_to_vrf_update(from_bgp, new_info, NULL); + else + bgp_dest_unlock_node(bn); } void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */ @@ -1768,6 +2054,8 @@ void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp, struct bgp *from_bgp, bpi, afi, safi); bgp_path_info_delete(bn, bpi); bgp_process(to_bgp, bn, afi, safi); + bgp_mplsvpn_path_nh_label_unlink( + bpi->extra->parent); } } } @@ -1832,7 +2120,7 @@ static struct bgp *bgp_lookup_by_rd(struct bgp_path_info *bpi, return NULL; } -static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ +static void vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ struct bgp *from_bgp, /* from */ struct bgp_path_info *path_vpn, struct prefix_rd *prd) @@ -1862,7 +2150,7 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ "%s: from vpn (%s) to vrf (%s), skipping: %s", __func__, from_bgp->name_pretty, to_bgp->name_pretty, debugmsg); - return false; + return; } /* @@ -1889,7 +2177,7 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ zlog_debug( "from vpn (%s) to vrf (%s), skipping after no intersection of route targets", from_bgp->name_pretty, to_bgp->name_pretty); - return false; + return; } rd_buf[0] = '\0'; @@ -1906,7 +2194,7 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ zlog_debug( "%s: skipping import, match RD (%s) of src VRF (%s) and the prefix (%pFX)", __func__, rd_buf, to_bgp->name_pretty, p); - return false; + return; } if (debug) @@ -2017,7 +2305,7 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ to_bgp->vpn_policy[afi] .rmap[BGP_VPN_POLICY_DIR_FROMVPN] ->name); - return false; + return; } /* * if route-map changed nexthop, don't nexthop-self on output @@ -2075,19 +2363,64 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ zlog_debug("%s: pfx %pBD: num_labels %d", __func__, path_vpn->net, num_labels); - leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels, - num_labels, src_vrf, &nexthop_orig, nexthop_self_flag, - debug); + if (!leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels, + num_labels, src_vrf, &nexthop_orig, nexthop_self_flag, + debug)) + bgp_dest_unlock_node(bn); +} + +bool vpn_leak_to_vrf_no_retain_filter_check(struct bgp *from_bgp, + struct attr *attr, afi_t afi) +{ + struct ecommunity *ecom_route_target = bgp_attr_get_ecommunity(attr); + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + struct listnode *node; + const char *debugmsg; + struct bgp *to_bgp; + + /* Loop over BGP instances */ + for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, to_bgp)) { + if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) { + if (debug) + zlog_debug( + "%s: from vpn (%s) to vrf (%s) afi %s, skipping: %s", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + debugmsg); + continue; + } + + /* Check for intersection of route targets */ + if (!ecommunity_include( + to_bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + ecom_route_target)) { + if (debug) + zlog_debug( + "%s: from vpn (%s) to vrf (%s) afi %s %s, skipping after no intersection of route targets", + __func__, from_bgp->name_pretty, + to_bgp->name_pretty, afi2str(afi), + ecommunity_str(ecom_route_target)); + continue; + } + return false; + } + + if (debug) + zlog_debug( + "%s: from vpn (%s) afi %s %s, no import - must be filtered", + __func__, from_bgp->name_pretty, afi2str(afi), + ecommunity_str(ecom_route_target)); + return true; } -bool vpn_leak_to_vrf_update(struct bgp *from_bgp, +void vpn_leak_to_vrf_update(struct bgp *from_bgp, struct bgp_path_info *path_vpn, struct prefix_rd *prd) { struct listnode *mnode, *mnnode; struct bgp *bgp; - bool leak_success = false; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); @@ -2099,11 +2432,10 @@ bool vpn_leak_to_vrf_update(struct bgp *from_bgp, if (!path_vpn->extra || path_vpn->extra->bgp_orig != bgp) { /* no loop */ - leak_success |= vpn_leak_to_vrf_update_onevrf( - bgp, from_bgp, path_vpn, prd); + vpn_leak_to_vrf_update_onevrf(bgp, from_bgp, path_vpn, + prd); } } - return leak_success; } void vpn_leak_to_vrf_withdraw(struct bgp_path_info *path_vpn) @@ -2223,6 +2555,51 @@ void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi) } } +void vpn_leak_no_retain(struct bgp *to_bgp, struct bgp *vpn_from, afi_t afi) +{ + struct bgp_dest *pdest; + safi_t safi = SAFI_MPLS_VPN; + + assert(vpn_from); + + /* + * Walk vpn table + */ + for (pdest = bgp_table_top(vpn_from->rib[afi][safi]); pdest; + pdest = bgp_route_next(pdest)) { + struct bgp_table *table; + struct bgp_dest *bn; + struct bgp_path_info *bpi; + + /* This is the per-RD table of prefixes */ + 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_dest_get_bgp_path_info(bn); bpi; + bpi = bpi->next) { + + if (bpi->extra && + bpi->extra->bgp_orig == to_bgp) + continue; + + if (bpi->sub_type != BGP_ROUTE_NORMAL) + continue; + + if (!vpn_leak_to_vrf_no_retain_filter_check( + vpn_from, bpi->attr, afi)) + /* do not filter */ + continue; + + bgp_unlink_nexthop(bpi); + bgp_rib_remove(bn, bpi, bpi->peer, afi, safi); + } + } + } +} + void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from, afi_t afi) { @@ -3427,6 +3804,7 @@ void vpn_leak_postchange_all(void) */ void bgp_vpn_leak_unimport(struct bgp *from_bgp) { + struct bgp *bgp_default = bgp_get_default(); struct bgp *to_bgp; const char *tmp_name; char *vname; @@ -3505,6 +3883,17 @@ void bgp_vpn_leak_unimport(struct bgp *from_bgp) } } } + + if (bgp_default && + !CHECK_FLAG(bgp_default->af_flags[afi][SAFI_MPLS_VPN], + BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL)) { + /* 'from_bgp' instance will be deleted + * so force to unset importation to update VPN labels + */ + UNSET_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT); + vpn_leak_no_retain(from_bgp, bgp_default, afi); + } } return; } @@ -3579,3 +3968,386 @@ void bgp_vpn_leak_export(struct bgp *from_bgp) } } } + +/* The nexthops values are compared to + * find in the tree the appropriate cache entry + */ +int bgp_mplsvpn_nh_label_bind_cmp( + const struct bgp_mplsvpn_nh_label_bind_cache *a, + const struct bgp_mplsvpn_nh_label_bind_cache *b) +{ + if (prefix_cmp(&a->nexthop, &b->nexthop)) + return 1; + if (a->orig_label > b->orig_label) + return 1; + if (a->orig_label < b->orig_label) + return -1; + return 0; +} + +static void bgp_mplsvpn_nh_label_bind_send_nexthop_label( + struct bgp_mplsvpn_nh_label_bind_cache *bmnc, int cmd) +{ + struct prefix pfx_nh, *p = NULL; + uint32_t num_labels = 0, lsp_num_labels; + mpls_label_t label[MPLS_MAX_LABELS]; + struct nexthop *nh; + ifindex_t ifindex = IFINDEX_INTERNAL; + vrf_id_t vrf_id = VRF_DEFAULT; + uint32_t i; + + if (bmnc->nh == NULL) + return; + nh = bmnc->nh; + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + p = &bmnc->nexthop; + label[num_labels] = bmnc->orig_label; + num_labels += 1; + ifindex = nh->ifindex; + vrf_id = nh->vrf_id; + break; + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (nh->type == NEXTHOP_TYPE_IPV4 || + nh->type == NEXTHOP_TYPE_IPV4_IFINDEX) { + pfx_nh.family = AF_INET; + pfx_nh.prefixlen = IPV4_MAX_BITLEN; + IPV4_ADDR_COPY(&pfx_nh.u.prefix4, &nh->gate.ipv4); + } else { + pfx_nh.family = AF_INET6; + pfx_nh.prefixlen = IPV6_MAX_BITLEN; + IPV6_ADDR_COPY(&pfx_nh.u.prefix6, &nh->gate.ipv6); + } + p = &pfx_nh; + if (nh->nh_label) { + if (nh->nh_label->num_labels + 1 > MPLS_MAX_LABELS) { + /* label stack overflow. no label switching will be performed + */ + flog_err(EC_BGP_LABEL, + "%s [Error] BGP label %u->%u to %pFX, forged label stack too big: %u. Abort LSP installation", + bmnc->bgp_vpn->name_pretty, + bmnc->new_label, bmnc->orig_label, + &bmnc->nexthop, + nh->nh_label->num_labels + 1); + return; + } + lsp_num_labels = nh->nh_label->num_labels; + for (i = 0; i < lsp_num_labels; i++) + label[num_labels + i] = nh->nh_label->label[i]; + num_labels = lsp_num_labels; + } + label[num_labels] = bmnc->orig_label; + num_labels += 1; + if (nh->type == NEXTHOP_TYPE_IPV4_IFINDEX || + nh->type == NEXTHOP_TYPE_IPV6_IFINDEX) { + ifindex = nh->ifindex; + vrf_id = nh->vrf_id; + } + break; + case NEXTHOP_TYPE_BLACKHOLE: + return; + } + bgp_zebra_send_nexthop_label(cmd, bmnc->new_label, ifindex, vrf_id, + ZEBRA_LSP_BGP, p, num_labels, &label[0]); +} + +void bgp_mplsvpn_nh_label_bind_free( + struct bgp_mplsvpn_nh_label_bind_cache *bmnc) +{ + if (bmnc->allocation_in_progress) { + bmnc->allocation_in_progress = false; + bgp_mplsvpn_nh_label_bind_cache_del( + &bmnc->bgp_vpn->mplsvpn_nh_label_bind, bmnc); + return; + } + if (bmnc->new_label != MPLS_INVALID_LABEL) { + bgp_mplsvpn_nh_label_bind_send_nexthop_label( + bmnc, ZEBRA_MPLS_LABELS_DELETE); + bgp_lp_release(LP_TYPE_BGP_L3VPN_BIND, bmnc, bmnc->new_label); + } + bgp_mplsvpn_nh_label_bind_cache_del( + &bmnc->bgp_vpn->mplsvpn_nh_label_bind, bmnc); + + if (bmnc->nh) + nexthop_free(bmnc->nh); + + XFREE(MTYPE_MPLSVPN_NH_LABEL_BIND_CACHE, bmnc); +} + +struct bgp_mplsvpn_nh_label_bind_cache * +bgp_mplsvpn_nh_label_bind_new(struct bgp_mplsvpn_nh_label_bind_cache_head *tree, + struct prefix *p, mpls_label_t orig_label) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + bmnc = XCALLOC(MTYPE_MPLSVPN_NH_LABEL_BIND_CACHE, + sizeof(struct bgp_mplsvpn_nh_label_bind_cache)); + bmnc->new_label = MPLS_INVALID_LABEL; + prefix_copy(&bmnc->nexthop, p); + bmnc->orig_label = orig_label; + + LIST_INIT(&(bmnc->paths)); + bgp_mplsvpn_nh_label_bind_cache_add(tree, bmnc); + + return bmnc; +} + +struct bgp_mplsvpn_nh_label_bind_cache *bgp_mplsvpn_nh_label_bind_find( + struct bgp_mplsvpn_nh_label_bind_cache_head *tree, struct prefix *p, + mpls_label_t orig_label) +{ + struct bgp_mplsvpn_nh_label_bind_cache bmnc = {0}; + + if (!tree) + return NULL; + prefix_copy(&bmnc.nexthop, p); + bmnc.orig_label = orig_label; + + return bgp_mplsvpn_nh_label_bind_cache_find(tree, &bmnc); +} + +/* Called to check if the incoming l3vpn path entry + * has mpls label information + */ +bool bgp_mplsvpn_path_uses_valid_mpls_label(struct bgp_path_info *pi) +{ + if (pi->attr && pi->attr->srv6_l3vpn) + /* srv6 sid */ + return false; + + if (pi->attr && + CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) && + pi->attr->label_index != BGP_INVALID_LABEL_INDEX) + /* prefix_sid attribute */ + return false; + + if (!pi->extra || !bgp_is_valid_label(&pi->extra->label[0])) + /* invalid MPLS label */ + return false; + return true; +} + +mpls_label_t bgp_mplsvpn_nh_label_bind_get_label(struct bgp_path_info *pi) +{ + mpls_label_t label; + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + bmnc = pi->mplsvpn.bmnc.nh_label_bind_cache; + if (!bmnc || bmnc->new_label == MPLS_INVALID_LABEL) + /* allocation in progress + * or path not eligible for local label + */ + return MPLS_INVALID_LABEL; + + label = mpls_lse_encode(bmnc->new_label, 0, 0, 1); + bgp_set_valid_label(&label); + + return label; +} + +/* Called upon reception of a ZAPI Message from zebra, about + * a new available label. + */ +static int bgp_mplsvpn_nh_label_bind_get_local_label_cb(mpls_label_t label, + void *context, + bool allocated) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc = context; + struct bgp_table *table; + struct bgp_path_info *pi; + + if (BGP_DEBUG(labelpool, LABELPOOL)) + zlog_debug("%s: label=%u, allocated=%d, nexthop=%pFX, label %u", + __func__, label, allocated, &bmnc->nexthop, + bmnc->orig_label); + if (allocated) + /* update the entry with the new label */ + bmnc->new_label = label; + else + /* + * previously-allocated label is now invalid + * eg: zebra deallocated the labels and notifies it + */ + bmnc->new_label = MPLS_INVALID_LABEL; + + if (!bmnc->allocation_in_progress) { + bgp_mplsvpn_nh_label_bind_free(bmnc); + return 0; + } + bmnc->allocation_in_progress = false; + + if (bmnc->new_label != MPLS_INVALID_LABEL) + /* + * Create the LSP : bmnc->orig_label, + * via bmnc->prefix, interface bnc->nexthop->ifindex + */ + bgp_mplsvpn_nh_label_bind_send_nexthop_label( + bmnc, ZEBRA_MPLS_LABELS_ADD); + + LIST_FOREACH (pi, &(bmnc->paths), mplsvpn.bmnc.nh_label_bind_thread) { + /* we can advertise it */ + if (!pi->net) + continue; + table = bgp_dest_table(pi->net); + if (!table) + continue; + SET_FLAG(pi->net->flags, BGP_NODE_LABEL_CHANGED); + bgp_process(table->bgp, pi->net, table->afi, table->safi); + } + + return 0; +} + +void bgp_mplsvpn_path_nh_label_bind_unlink(struct bgp_path_info *pi) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + if (!pi) + return; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND)) + return; + + bmnc = pi->mplsvpn.bmnc.nh_label_bind_cache; + + if (!bmnc) + return; + + LIST_REMOVE(pi, mplsvpn.bmnc.nh_label_bind_thread); + pi->mplsvpn.bmnc.nh_label_bind_cache->path_count--; + pi->mplsvpn.bmnc.nh_label_bind_cache = NULL; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND); + + if (LIST_EMPTY(&(bmnc->paths))) + bgp_mplsvpn_nh_label_bind_free(bmnc); +} + +void bgp_mplsvpn_nh_label_bind_register_local_label(struct bgp *bgp, + struct bgp_dest *dest, + struct bgp_path_info *pi) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + struct bgp_mplsvpn_nh_label_bind_cache_head *tree; + + tree = &bgp->mplsvpn_nh_label_bind; + bmnc = bgp_mplsvpn_nh_label_bind_find( + tree, &pi->nexthop->prefix, decode_label(&pi->extra->label[0])); + if (!bmnc) { + bmnc = bgp_mplsvpn_nh_label_bind_new( + tree, &pi->nexthop->prefix, + decode_label(&pi->extra->label[0])); + bmnc->bgp_vpn = bgp; + bmnc->allocation_in_progress = true; + bgp_lp_get(LP_TYPE_BGP_L3VPN_BIND, bmnc, + bgp_mplsvpn_nh_label_bind_get_local_label_cb); + } + + if (pi->mplsvpn.bmnc.nh_label_bind_cache == bmnc) + /* no change */ + return; + + bgp_mplsvpn_path_nh_label_bind_unlink(pi); + + /* updates NHT pi list reference */ + LIST_INSERT_HEAD(&(bmnc->paths), pi, mplsvpn.bmnc.nh_label_bind_thread); + pi->mplsvpn.bmnc.nh_label_bind_cache = bmnc; + pi->mplsvpn.bmnc.nh_label_bind_cache->path_count++; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND); + bmnc->last_update = monotime(NULL); + + /* Add or update the selected nexthop */ + if (!bmnc->nh) + bmnc->nh = nexthop_dup(pi->nexthop->nexthop, NULL); + else if (!nexthop_same(pi->nexthop->nexthop, bmnc->nh)) { + nexthop_free(bmnc->nh); + bmnc->nh = nexthop_dup(pi->nexthop->nexthop, NULL); + if (bmnc->new_label != MPLS_INVALID_LABEL) + bgp_mplsvpn_nh_label_bind_send_nexthop_label( + bmnc, ZEBRA_MPLS_LABELS_REPLACE); + } +} + +static void show_bgp_mplsvpn_nh_label_bind_internal(struct vty *vty, + struct bgp *bgp, + bool detail) +{ + struct bgp_mplsvpn_nh_label_bind_cache_head *tree; + struct bgp_mplsvpn_nh_label_bind_cache *iter; + afi_t afi; + safi_t safi; + struct bgp_dest *dest; + struct bgp_path_info *path; + struct bgp *bgp_path; + struct bgp_table *table; + time_t tbuf; + + vty_out(vty, "Current BGP mpls-vpn nexthop label bind cache, %s\n", + bgp->name_pretty); + + tree = &bgp->mplsvpn_nh_label_bind; + frr_each (bgp_mplsvpn_nh_label_bind_cache, tree, iter) { + if (iter->nexthop.family == AF_INET) + vty_out(vty, " %pI4", &iter->nexthop.u.prefix4); + else + vty_out(vty, " %pI6", &iter->nexthop.u.prefix6); + vty_out(vty, ", label %u, local label %u #paths %u\n", + iter->orig_label, iter->new_label, iter->path_count); + if (iter->nh) + vty_out(vty, " interface %s\n", + ifindex2ifname(iter->nh->ifindex, + iter->nh->vrf_id)); + tbuf = time(NULL) - (monotime(NULL) - iter->last_update); + vty_out(vty, " Last update: %s", ctime(&tbuf)); + if (!detail) + continue; + vty_out(vty, " Paths:\n"); + LIST_FOREACH (path, &(iter->paths), + mplsvpn.bmnc.nh_label_bind_thread) { + dest = path->net; + table = bgp_dest_table(dest); + assert(dest && table); + afi = family2afi(bgp_dest_get_prefix(dest)->family); + safi = table->safi; + bgp_path = table->bgp; + + vty_out(vty, " %d/%d %pBD %s flags 0x%x\n", afi, + safi, dest, bgp_path->name_pretty, path->flags); + } + } +} + + +DEFUN(show_bgp_mplsvpn_nh_label_bind, show_bgp_mplsvpn_nh_label_bind_cmd, + "show bgp [ VIEWVRFNAME] mplsvpn-nh-label-bind [detail]", + SHOW_STR BGP_STR BGP_INSTANCE_HELP_STR + "BGP mplsvpn nexthop label binding entries\n" + "Show detailed information\n") +{ + int idx = 0; + char *vrf = NULL; + struct bgp *bgp; + bool detail = false; + + if (argv_find(argv, argc, "vrf", &idx)) { + vrf = argv[++idx]->arg; + bgp = bgp_lookup_by_name(vrf); + } else + bgp = bgp_get_default(); + + if (!bgp) + return CMD_SUCCESS; + + if (argv_find(argv, argc, "detail", &idx)) + detail = true; + + show_bgp_mplsvpn_nh_label_bind_internal(vty, bgp, detail); + return CMD_SUCCESS; +} + +void bgp_mplsvpn_nexthop_init(void) +{ + install_element(VIEW_NODE, &show_bgp_mplsvpn_nh_label_bind_cmd); +} diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index c832b4abd444..dcde42146c61 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -31,6 +31,7 @@ #define BGP_PREFIX_SID_SRV6_MAX_FUNCTION_LENGTH 20 extern void bgp_mplsvpn_init(void); +extern void bgp_mplsvpn_path_nh_label_unlink(struct bgp_path_info *pi); extern int bgp_nlri_parse_vpn(struct peer *, struct attr *, struct bgp_nlri *); extern uint32_t decode_label(mpls_label_t *); extern void encode_label(mpls_label_t, mpls_label_t *); @@ -55,10 +56,17 @@ extern void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, extern void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi); +extern void vpn_leak_no_retain(struct bgp *to_bgp, struct bgp *vpn_from, + afi_t afi); + extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp, afi_t afi); -extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp, +extern bool vpn_leak_to_vrf_no_retain_filter_check(struct bgp *from_bgp, + struct attr *attr, + afi_t afi); + +extern void vpn_leak_to_vrf_update(struct bgp *from_bgp, struct bgp_path_info *path_vpn, struct prefix_rd *prd); @@ -324,4 +332,72 @@ extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, extern void bgp_vpn_leak_unimport(struct bgp *from_bgp); extern void bgp_vpn_leak_export(struct bgp *from_bgp); +extern bool bgp_mplsvpn_path_uses_valid_mpls_label(struct bgp_path_info *pi); +extern int +bgp_mplsvpn_nh_label_bind_cmp(const struct bgp_mplsvpn_nh_label_bind_cache *a, + const struct bgp_mplsvpn_nh_label_bind_cache *b); +extern void bgp_mplsvpn_path_nh_label_bind_unlink(struct bgp_path_info *pi); +extern void bgp_mplsvpn_nh_label_bind_register_local_label( + struct bgp *bgp, struct bgp_dest *dest, struct bgp_path_info *pi); +mpls_label_t bgp_mplsvpn_nh_label_bind_get_label(struct bgp_path_info *pi); + +/* used to bind a local label to the (label, nexthop) values + * from an incoming BGP mplsvpn update + */ +struct bgp_mplsvpn_nh_label_bind_cache { + + /* RB-tree entry. */ + struct bgp_mplsvpn_nh_label_bind_cache_item entry; + + /* The nexthop and the vpn label are the key of the list. + * Only received BGP MPLSVPN updates may use that structure. + * orig_label is the original label received from the BGP Update. + */ + struct prefix nexthop; + mpls_label_t orig_label; + + /* resolved interface for the paths */ + struct nexthop *nh; + + /* number of mplsvpn path */ + unsigned int path_count; + + /* back pointer to bgp instance */ + struct bgp *bgp_vpn; + + /* MPLS label allocated value. + * When the next-hop is changed because of 'next-hop-self' or + * because it is an eBGP peer, the redistributed orig_label value + * is unmodified, unless the 'l3vpn-multi-domain-switching' + * is enabled: a new_label value is allocated: + * - The new_label value is sent in the advertised BGP update, + * instead of the label value. + * - An MPLS entry is set to swap with . + */ + mpls_label_t new_label; + + /* list of path_vrfs using it */ + LIST_HEAD(mplsvpn_nh_label_bind_path_lists, bgp_path_info) paths; + + time_t last_update; + + bool allocation_in_progress; +}; + +DECLARE_RBTREE_UNIQ(bgp_mplsvpn_nh_label_bind_cache, + struct bgp_mplsvpn_nh_label_bind_cache, entry, + bgp_mplsvpn_nh_label_bind_cmp); + +void bgp_mplsvpn_nh_label_bind_free( + struct bgp_mplsvpn_nh_label_bind_cache *bmnc); + +struct bgp_mplsvpn_nh_label_bind_cache * +bgp_mplsvpn_nh_label_bind_new(struct bgp_mplsvpn_nh_label_bind_cache_head *tree, + struct prefix *p, mpls_label_t orig_label); +struct bgp_mplsvpn_nh_label_bind_cache *bgp_mplsvpn_nh_label_bind_find( + struct bgp_mplsvpn_nh_label_bind_cache_head *tree, struct prefix *p, + mpls_label_t orig_label); +void bgp_mplsvpn_nexthop_init(void); +extern void sid_unregister(struct bgp *bgp, const struct in6_addr *sid); + #endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c index 9b2ab6680627..0208a6f5a549 100644 --- a/bgpd/bgp_mplsvpn_snmp.c +++ b/bgpd/bgp_mplsvpn_snmp.c @@ -12,7 +12,7 @@ #include "log.h" #include "prefix.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "smux.h" #include "filter.h" #include "hook.h" @@ -1124,7 +1124,8 @@ static uint8_t *mplsL3vpnVrfRtTable(struct variable *v, oid name[], struct bgp *l3vpn_bgp; uint32_t rt_index = 0; uint8_t rt_type = 0; - char *rt_b; + char *rt_b = NULL; + static char rt_b_str[BUFSIZ] = {}; if (smux_header_table(v, name, length, exact, var_len, write_method) == MATCH_FAILED) @@ -1156,14 +1157,16 @@ static uint8_t *mplsL3vpnVrfRtTable(struct variable *v, oid name[], ECOMMUNITY_ROUTE_TARGET); break; default: - rt_b = NULL; break; } - if (rt_b) + if (rt_b) { *var_len = strnlen(rt_b, ECOMMUNITY_STRLEN); - else + strlcpy(rt_b_str, rt_b, sizeof(rt_b_str)); + XFREE(MTYPE_ECOMMUNITY_STR, rt_b); + } else { *var_len = 0; - return (uint8_t *)rt_b; + } + return (uint8_t *)rt_b_str; case MPLSL3VPNVRFRTDESCR: /* since we dont have a description generate one */ memset(rt_description, 0, VRF_NAMSIZ + RT_PREAMBLE_SIZE); @@ -1624,6 +1627,7 @@ static uint8_t *mplsL3vpnRteTable(struct variable *v, oid name[], } } else return SNMP_INTEGER(MPLSL3VPNVRFRTECIDRTYPEOTHER); + break; case MPLSL3VPNVRFRTEINETCIDRPROTO: switch (pi->type) { case ZEBRA_ROUTE_CONNECT: diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 1c2e686e1c87..73fe00c7ab99 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -5,7 +5,7 @@ #include -#include "thread.h" +#include "frrevent.h" #include "sockunion.h" #include "sockopt.h" #include "memory.h" @@ -338,12 +338,12 @@ static void bgp_socket_set_buffer_size(const int fd) } /* Accept bgp connection. */ -static void bgp_accept(struct thread *thread) +static void bgp_accept(struct event *thread) { int bgp_sock; int accept_sock; union sockunion su; - struct bgp_listener *listener = THREAD_ARG(thread); + struct bgp_listener *listener = EVENT_ARG(thread); struct peer *peer; struct peer *peer1; char buf[SU_ADDRSTRLEN]; @@ -354,7 +354,7 @@ static void bgp_accept(struct thread *thread) bgp = bgp_lookup_by_name(listener->name); /* Register accept thread. */ - accept_sock = THREAD_FD(thread); + accept_sock = EVENT_FD(thread); if (accept_sock < 0) { flog_err_sys(EC_LIB_SOCKET, "[Error] BGP accept socket fd is negative: %d", @@ -362,8 +362,8 @@ static void bgp_accept(struct thread *thread) return; } - thread_add_read(bm->master, bgp_accept, listener, accept_sock, - &listener->thread); + event_add_read(bm->master, bgp_accept, listener, accept_sock, + &listener->thread); /* Accept client connection. */ bgp_sock = sockunion_accept(accept_sock, &su); @@ -391,7 +391,7 @@ static void bgp_accept(struct thread *thread) "[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); + EVENT_OFF(listener->thread); } else { flog_err_sys( EC_LIB_SOCKET, @@ -436,7 +436,7 @@ static void bgp_accept(struct thread *thread) sockopt_tcp_mss_set(bgp_sock, peer1->tcp_mss); bgp_fsm_change_status(peer1, Active); - THREAD_OFF( + EVENT_OFF( peer1->t_start); /* created in peer_create() */ if (peer_active(peer1)) { @@ -569,7 +569,7 @@ static void bgp_accept(struct thread *thread) } bgp_peer_reg_with_nht(peer); bgp_fsm_change_status(peer, Active); - THREAD_OFF(peer->t_start); /* created in peer_create() */ + EVENT_OFF(peer->t_start); /* created in peer_create() */ SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); /* Make dummy peer until read Open packet. */ @@ -759,6 +759,9 @@ int bgp_connect(struct peer *peer) ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN; + if (!BGP_PEER_SU_UNSPEC(peer)) + bgp_md5_set(peer); + bgp_md5_set_connect(peer->fd, &peer->su, prefixlen, peer->password); } @@ -805,9 +808,13 @@ int bgp_getsockname(struct peer *peer) if (!bgp_zebra_nexthop_set(peer->su_local, peer->su_remote, &peer->nexthop, peer)) { - flog_err(EC_BGP_NH_UPD, - "%s: nexthop_set failed, resetting connection - intf %p", - peer->host, peer->nexthop.ifp); + flog_err( + EC_BGP_NH_UPD, + "%s: nexthop_set failed, local: %pSUp remote: %pSUp update_if: %s resetting connection - intf %s", + peer->host, peer->su_local, peer->su_remote, + peer->update_if ? peer->update_if : "(None)", + peer->nexthop.ifp ? peer->nexthop.ifp->name + : "(Unknown)"); return -1; } return 0; @@ -858,8 +865,8 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, listener->bgp = bgp; memcpy(&listener->su, sa, salen); - thread_add_read(bm->master, bgp_accept, listener, sock, - &listener->thread); + event_add_read(bm->master, bgp_accept, listener, sock, + &listener->thread); listnode_add(bm->listen_sockets, listener); return 0; @@ -958,7 +965,7 @@ void bgp_close_vrf_socket(struct bgp *bgp) for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { if (listener->bgp == bgp) { - THREAD_OFF(listener->thread); + EVENT_OFF(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); XFREE(MTYPE_BGP_LISTENER, listener->name); @@ -980,7 +987,7 @@ void bgp_close(void) for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { if (listener->bgp) continue; - THREAD_OFF(listener->thread); + EVENT_OFF(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); XFREE(MTYPE_BGP_LISTENER, listener->name); diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index f21b219e2971..cf0b4362c533 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -11,7 +11,7 @@ struct bgp_listener { int fd; union sockunion su; - struct thread *thread; + struct event *thread; struct bgp *bgp; char *name; }; diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index 00a0bc84023e..a854ca0fe43b 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -6,7 +6,7 @@ #include #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "lib/json.h" #include "zclient.h" @@ -31,6 +31,7 @@ #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_rd.h" +#include "bgpd/bgp_mplsvpn.h" DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Addr Intf String"); @@ -119,6 +120,9 @@ static void bgp_nexthop_cache_reset(struct bgp_nexthop_cache_head *tree) while (!LIST_EMPTY(&(bnc->paths))) { struct bgp_path_info *path = LIST_FIRST(&(bnc->paths)); + bgp_mplsvpn_path_nh_label_unlink(path); + bgp_mplsvpn_path_nh_label_bind_unlink(path); + path_nh_map(path, bnc, false); } @@ -166,11 +170,7 @@ void bgp_tip_hash_init(struct bgp *bgp) void bgp_tip_hash_destroy(struct bgp *bgp) { - if (bgp->tip_hash == NULL) - return; - hash_clean(bgp->tip_hash, bgp_tip_hash_free); - hash_free(bgp->tip_hash); - bgp->tip_hash = NULL; + hash_clean_and_free(&bgp->tip_hash, bgp_tip_hash_free); } /* Add/Update Tunnel-IP entry of bgp martian next-hop table. @@ -305,11 +305,7 @@ void bgp_address_init(struct bgp *bgp) void bgp_address_destroy(struct bgp *bgp) { - if (bgp->address_hash == NULL) - return; - hash_clean(bgp->address_hash, bgp_address_hash_free); - hash_free(bgp->address_hash); - bgp->address_hash = NULL; + hash_clean_and_free(&bgp->address_hash, bgp_address_hash_free); } static void bgp_address_add(struct bgp *bgp, struct connected *ifc, @@ -916,17 +912,34 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, } switch (nexthop->type) { case NEXTHOP_TYPE_IPV6: - vty_out(vty, " gate %pI6\n", &nexthop->gate.ipv6); - break; case NEXTHOP_TYPE_IPV6_IFINDEX: - vty_out(vty, " gate %pI6, if %s\n", - &nexthop->gate.ipv6, - ifindex2ifname(bnc->ifindex ? bnc->ifindex - : nexthop->ifindex, - bgp->vrf_id)); + vty_out(vty, " gate %pI6", &nexthop->gate.ipv6); + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX && + bnc->ifindex) + vty_out(vty, ", if %s\n", + ifindex2ifname(bnc->ifindex, + bgp->vrf_id)); + else if (nexthop->ifindex) + vty_out(vty, ", if %s\n", + ifindex2ifname(nexthop->ifindex, + bgp->vrf_id)); + else + vty_out(vty, "\n"); break; case NEXTHOP_TYPE_IPV4: - vty_out(vty, " gate %pI4\n", &nexthop->gate.ipv4); + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, " gate %pI4", &nexthop->gate.ipv4); + if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX && + bnc->ifindex) + vty_out(vty, ", if %s\n", + ifindex2ifname(bnc->ifindex, + bgp->vrf_id)); + else if (nexthop->ifindex) + vty_out(vty, ", if %s\n", + ifindex2ifname(nexthop->ifindex, + bgp->vrf_id)); + else + vty_out(vty, "\n"); break; case NEXTHOP_TYPE_IFINDEX: vty_out(vty, " if %s\n", @@ -934,13 +947,6 @@ static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp, : nexthop->ifindex, bgp->vrf_id)); break; - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out(vty, " gate %pI4, if %s\n", - &nexthop->gate.ipv4, - ifindex2ifname(bnc->ifindex ? bnc->ifindex - : nexthop->ifindex, - bgp->vrf_id)); - break; case NEXTHOP_TYPE_BLACKHOLE: vty_out(vty, " blackhole\n"); break; diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 95e2f9165b11..47b646408521 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -104,11 +104,6 @@ struct tip_addr { int refcnt; }; -struct bgp_addrv6 { - struct in6_addr addrv6; - struct list *ifp_name_list; -}; - /* Forward declaration(s). */ struct peer; struct update_subgroup; diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 25b458a8e5e5..a46616803c10 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -6,7 +6,7 @@ #include #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "zclient.h" #include "stream.h" @@ -31,13 +31,15 @@ #include "bgpd/bgp_flowspec_util.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_rd.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_ecommunity.h" extern struct zclient *zclient; static void register_zebra_rnh(struct bgp_nexthop_cache *bnc); static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc); static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p); -static void bgp_nht_ifp_initial(struct thread *thread); +static void bgp_nht_ifp_initial(struct event *thread); static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc) { @@ -149,6 +151,9 @@ void bgp_unlink_nexthop(struct bgp_path_info *path) { struct bgp_nexthop_cache *bnc = path->nexthop; + bgp_mplsvpn_path_nh_label_unlink(path); + bgp_mplsvpn_path_nh_label_bind_unlink(path); + if (!bnc) return; @@ -318,13 +323,16 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, return 0; } - srte_color = pi->attr->srte_color; + if (CHECK_FLAG(pi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) + srte_color = bgp_attr_get_color(pi->attr); + } else if (peer) { /* * Gather the ifindex for if up/down events to be * tagged into this fun */ - if (afi == AFI_IP6 && + if (afi == AFI_IP6 && peer->conf_if && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr)) { ifindex = peer->su.sin6.sin6_scope_id; if (ifindex == 0) { @@ -464,7 +472,12 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) return bgp_isvalid_nexthop_for_mpls(bnc, pi); + else if (safi == SAFI_MPLS_VPN && pi && + pi->sub_type != BGP_ROUTE_IMPORTED) + /* avoid not redistributing mpls vpn routes */ + return 1; else + /* mpls-vpn routes with BGP_ROUTE_IMPORTED subtype */ return (bgp_isvalid_nexthop(bnc)); } @@ -544,7 +557,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc, char bnc_buf[BNC_FLAG_DUMP_SIZE]; zlog_debug( - "%s(%u): Rcvd NH update %pFX(%u)%u) - metric %d/%d #nhops %d/%d flags %s", + "%s(%u): Rcvd NH update %pFX(%u)(%u) - metric %d/%d #nhops %d/%d flags %s", bnc->bgp->name_pretty, bnc->bgp->vrf_id, &nhr->prefix, bnc->ifindex, bnc->srte_color, nhr->metric, bnc->metric, nhr->nexthop_num, bnc->nexthop_num, @@ -756,10 +769,10 @@ void bgp_nht_ifp_down(struct interface *ifp) bgp_nht_ifp_handle(ifp, false); } -static void bgp_nht_ifp_initial(struct thread *thread) +static void bgp_nht_ifp_initial(struct event *thread) { - ifindex_t ifindex = THREAD_VAL(thread); - struct bgp *bgp = THREAD_ARG(thread); + ifindex_t ifindex = EVENT_VAL(thread); + struct bgp *bgp = EVENT_ARG(thread); struct interface *ifp = if_lookup_by_index(ifindex, bgp->vrf_id); if (!ifp) @@ -811,8 +824,8 @@ void bgp_nht_interface_events(struct peer *peer) return; if (bnc->ifindex) - thread_add_event(bm->master, bgp_nht_ifp_initial, bnc->bgp, - bnc->ifindex, NULL); + event_add_event(bm->master, bgp_nht_ifp_initial, bnc->bgp, + bnc->ifindex, NULL); } void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) @@ -846,7 +859,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) if (!bnc_nhc) { if (BGP_DEBUG(nht, NHT)) zlog_debug( - "parse nexthop update(%pFX(%u)(%s)): bnc info not found for nexthop cache", + "parse nexthop update %pFX(%u)(%s): bnc info not found for nexthop cache", &nhr.prefix, nhr.srte_color, bgp->name_pretty); } else bgp_process_nexthop_update(bnc_nhc, &nhr, false); @@ -857,7 +870,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) if (!bnc_import) { if (BGP_DEBUG(nht, NHT)) zlog_debug( - "parse nexthop update(%pFX(%u)(%s)): bnc info not found for import check", + "parse nexthop update %pFX(%u)(%s): bnc info not found for import check", &nhr.prefix, nhr.srte_color, bgp->name_pretty); } else bgp_process_nexthop_update(bnc_import, &nhr, true); @@ -1134,10 +1147,21 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) } LIST_FOREACH (path, &(bnc->paths), nh_thread) { - if (!(path->type == ZEBRA_ROUTE_BGP - && ((path->sub_type == BGP_ROUTE_NORMAL) - || (path->sub_type == BGP_ROUTE_STATIC) - || (path->sub_type == BGP_ROUTE_IMPORTED)))) + if (path->type == ZEBRA_ROUTE_BGP && + (path->sub_type == BGP_ROUTE_NORMAL || + path->sub_type == BGP_ROUTE_STATIC || + path->sub_type == BGP_ROUTE_IMPORTED)) + /* evaluate the path */ + ; + else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE) { + /* evaluate the path for redistributed routes + * except those from VNC + */ + if ((path->type == ZEBRA_ROUTE_VNC) || + (path->type == ZEBRA_ROUTE_VNC_DIRECT)) + continue; + } else + /* don't evaluate the path */ continue; dest = path->net; @@ -1176,7 +1200,12 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) bnc_is_valid_nexthop = bgp_isvalid_nexthop_for_mpls(bnc, path) ? true : false; + } else if (safi == SAFI_MPLS_VPN && + path->sub_type != BGP_ROUTE_IMPORTED) { + /* avoid not redistributing mpls vpn routes */ + bnc_is_valid_nexthop = true; } else { + /* mpls-vpn routes with BGP_ROUTE_IMPORTED subtype */ if (bgp_update_martian_nexthop( bnc->bgp, afi, safi, path->type, path->sub_type, path->attr, dest)) { @@ -1224,13 +1253,32 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) else if (bpi_ultimate->extra) bpi_ultimate->extra->igpmetric = 0; - if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) - || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) - || path->attr->srte_color != 0) + if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED) || + CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED) || + bgp_attr_get_color(path->attr)) SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED); path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID); - if (path_valid != bnc_is_valid_nexthop) { + if (path->type == ZEBRA_ROUTE_BGP && + path->sub_type == BGP_ROUTE_STATIC && + !CHECK_FLAG(bgp_path->flags, BGP_FLAG_IMPORT_CHECK)) + /* static routes with 'no bgp network import-check' are + * always valid. if nht is called with static routes, + * the vpn exportation needs to be triggered + */ + vpn_leak_from_vrf_update(bgp_get_default(), bgp_path, + path); + else if (path->sub_type == BGP_ROUTE_REDISTRIBUTE && + safi == SAFI_UNICAST && + (bgp_path->inst_type == BGP_INSTANCE_TYPE_VRF || + bgp_path->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) + /* redistribute routes are always valid + * if nht is called with redistribute routes, the vpn + * exportation needs to be triggered + */ + vpn_leak_from_vrf_update(bgp_get_default(), bgp_path, + path); + else if (path_valid != bnc_is_valid_nexthop) { if (path_valid) { /* No longer valid, clear flag; also for EVPN * routes, unimport from VRFs if needed. @@ -1243,6 +1291,12 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) 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); + if (safi == SAFI_UNICAST && + (bgp_path->inst_type != + BGP_INSTANCE_TYPE_VIEW)) + vpn_leak_from_vrf_withdraw( + bgp_get_default(), bgp_path, + path); } else { /* Path becomes valid, set flag; also for EVPN * routes, import from VRFs if needed. @@ -1255,6 +1309,12 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) 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); + if (safi == SAFI_UNICAST && + (bgp_path->inst_type != + BGP_INSTANCE_TYPE_VIEW)) + vpn_leak_from_vrf_update( + bgp_get_default(), bgp_path, + path); } } diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 032767820f5c..1b266314fb6d 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -8,7 +8,7 @@ #include "linklist.h" #include "prefix.h" #include "stream.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "command.h" #include "memory.h" @@ -36,9 +36,6 @@ static const struct message capcode_str[] = { {CAPABILITY_CODE_ADDPATH, "AddPath"}, {CAPABILITY_CODE_DYNAMIC, "Dynamic"}, {CAPABILITY_CODE_ENHE, "Extended Next Hop Encoding"}, - {CAPABILITY_CODE_DYNAMIC_OLD, "Dynamic (Old)"}, - {CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)"}, - {CAPABILITY_CODE_ORF_OLD, "ORF (Old)"}, {CAPABILITY_CODE_FQDN, "FQDN"}, {CAPABILITY_CODE_ENHANCED_RR, "Enhanced Route Refresh"}, {CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"}, @@ -56,10 +53,7 @@ static const size_t cap_minsizes[] = { [CAPABILITY_CODE_AS4] = CAPABILITY_CODE_AS4_LEN, [CAPABILITY_CODE_ADDPATH] = CAPABILITY_CODE_ADDPATH_LEN, [CAPABILITY_CODE_DYNAMIC] = CAPABILITY_CODE_DYNAMIC_LEN, - [CAPABILITY_CODE_DYNAMIC_OLD] = CAPABILITY_CODE_DYNAMIC_LEN, [CAPABILITY_CODE_ENHE] = CAPABILITY_CODE_ENHE_LEN, - [CAPABILITY_CODE_REFRESH_OLD] = CAPABILITY_CODE_REFRESH_LEN, - [CAPABILITY_CODE_ORF_OLD] = CAPABILITY_CODE_ORF_LEN, [CAPABILITY_CODE_FQDN] = CAPABILITY_CODE_MIN_FQDN_LEN, [CAPABILITY_CODE_ENHANCED_RR] = CAPABILITY_CODE_ENHANCED_LEN, [CAPABILITY_CODE_EXT_MESSAGE] = CAPABILITY_CODE_EXT_MESSAGE_LEN, @@ -81,10 +75,7 @@ static const size_t cap_modsizes[] = { [CAPABILITY_CODE_AS4] = 4, [CAPABILITY_CODE_ADDPATH] = 4, [CAPABILITY_CODE_DYNAMIC] = 1, - [CAPABILITY_CODE_DYNAMIC_OLD] = 1, [CAPABILITY_CODE_ENHE] = 6, - [CAPABILITY_CODE_REFRESH_OLD] = 1, - [CAPABILITY_CODE_ORF_OLD] = 1, [CAPABILITY_CODE_FQDN] = 1, [CAPABILITY_CODE_ENHANCED_RR] = 1, [CAPABILITY_CODE_EXT_MESSAGE] = 1, @@ -351,7 +342,6 @@ static void bgp_capability_orf_not_support(struct peer *peer, iana_afi_t afi, static const struct message orf_type_str[] = { {ORF_TYPE_RESERVED, "Reserved"}, {ORF_TYPE_PREFIX, "Prefixlist"}, - {ORF_TYPE_PREFIX_OLD, "Prefixlist (old)"}, {0}}; static const struct message orf_mode_str[] = {{ORF_MODE_RECEIVE, "Receive"}, @@ -440,22 +430,6 @@ static int bgp_capability_orf_entry(struct peer *peer, continue; } break; - case CAPABILITY_CODE_ORF_OLD: - switch (type) { - case ORF_TYPE_RESERVED: - if (bgp_debug_neighbor_events(peer)) - zlog_debug( - "%s Addr-family %d/%d has reserved ORF type, ignoring", - peer->host, afi, safi); - break; - case ORF_TYPE_PREFIX_OLD: - break; - default: - bgp_capability_orf_not_support( - peer, pkt_afi, pkt_safi, type, mode); - continue; - } - break; default: bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi, type, mode); @@ -482,9 +456,6 @@ static int bgp_capability_orf_entry(struct peer *peer, if (hdr->code == CAPABILITY_CODE_ORF) { sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV; rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV; - } else if (hdr->code == CAPABILITY_CODE_ORF_OLD) { - sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV; - rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV; } else { bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi, type, mode); @@ -604,12 +575,24 @@ static int bgp_capability_restart(struct peer *peer, static int bgp_capability_llgr(struct peer *peer, struct capability_header *caphdr) { +/* + * +--------------------------------------------------+ + * | Address Family Identifier (16 bits) | + * +--------------------------------------------------+ + * | Subsequent Address Family Identifier (8 bits) | + * +--------------------------------------------------+ + * | Flags for Address Family (8 bits) | + * +--------------------------------------------------+ + * | Long-lived Stale Time (24 bits) | + * +--------------------------------------------------+ + */ +#define BGP_CAP_LLGR_MIN_PACKET_LEN 7 struct stream *s = BGP_INPUT(peer); size_t end = stream_get_getp(s) + caphdr->length; SET_FLAG(peer->cap, PEER_CAP_LLGR_RCV); - while (stream_get_getp(s) + 4 <= end) { + while (stream_get_getp(s) + BGP_CAP_LLGR_MIN_PACKET_LEN <= end) { afi_t afi; safi_t safi; iana_afi_t pkt_afi = stream_getw(s); @@ -999,14 +982,11 @@ static int bgp_capability_parse(struct peer *peer, size_t length, switch (caphdr.code) { case CAPABILITY_CODE_MP: case CAPABILITY_CODE_REFRESH: - case CAPABILITY_CODE_REFRESH_OLD: case CAPABILITY_CODE_ORF: - case CAPABILITY_CODE_ORF_OLD: case CAPABILITY_CODE_RESTART: case CAPABILITY_CODE_AS4: case CAPABILITY_CODE_ADDPATH: case CAPABILITY_CODE_DYNAMIC: - case CAPABILITY_CODE_DYNAMIC_OLD: case CAPABILITY_CODE_ENHE: case CAPABILITY_CODE_FQDN: case CAPABILITY_CODE_ENHANCED_RR: @@ -1064,18 +1044,14 @@ static int bgp_capability_parse(struct peer *peer, size_t length, } } break; case CAPABILITY_CODE_ENHANCED_RR: - case CAPABILITY_CODE_REFRESH: - case CAPABILITY_CODE_REFRESH_OLD: { + case CAPABILITY_CODE_REFRESH: { /* BGP refresh capability */ if (caphdr.code == CAPABILITY_CODE_ENHANCED_RR) SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_RCV); - else if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD) - SET_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV); else - SET_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV); + SET_FLAG(peer->cap, PEER_CAP_REFRESH_RCV); } break; case CAPABILITY_CODE_ORF: - case CAPABILITY_CODE_ORF_OLD: ret = bgp_capability_orf_entry(peer, &caphdr); break; case CAPABILITY_CODE_RESTART: @@ -1085,7 +1061,6 @@ static int bgp_capability_parse(struct peer *peer, size_t length, ret = bgp_capability_llgr(peer, &caphdr); break; case CAPABILITY_CODE_DYNAMIC: - case CAPABILITY_CODE_DYNAMIC_OLD: SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV); break; case CAPABILITY_CODE_AS4: @@ -1486,9 +1461,7 @@ static void bgp_open_capability_orf(struct stream *s, struct peer *peer, /* Address Prefix ORF */ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) { - stream_putc(s, (code == CAPABILITY_CODE_ORF - ? ORF_TYPE_PREFIX - : ORF_TYPE_PREFIX_OLD)); + stream_putc(s, ORF_TYPE_PREFIX); if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM) @@ -1757,11 +1730,6 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer, /* Route refresh. */ SET_FLAG(peer->cap, PEER_CAP_REFRESH_ADV); stream_putc(s, BGP_OPEN_OPT_CAP); - ext_opt_params ? stream_putw(s, CAPABILITY_CODE_REFRESH_LEN + 2) - : stream_putc(s, CAPABILITY_CODE_REFRESH_LEN + 2); - stream_putc(s, CAPABILITY_CODE_REFRESH_OLD); - stream_putc(s, CAPABILITY_CODE_REFRESH_LEN); - stream_putc(s, BGP_OPEN_OPT_CAP); ext_opt_params ? stream_putw(s, CAPABILITY_CODE_REFRESH_LEN + 2) : stream_putc(s, CAPABILITY_CODE_REFRESH_LEN + 2); stream_putc(s, CAPABILITY_CODE_REFRESH); @@ -1885,9 +1853,6 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer, PEER_FLAG_ORF_PREFIX_SM) || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) { - bgp_open_capability_orf(s, peer, afi, safi, - CAPABILITY_CODE_ORF_OLD, - ext_opt_params); bgp_open_capability_orf(s, peer, afi, safi, CAPABILITY_CODE_ORF, ext_opt_params); @@ -1898,12 +1863,6 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer, if (CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) { SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV); stream_putc(s, BGP_OPEN_OPT_CAP); - ext_opt_params - ? stream_putw(s, CAPABILITY_CODE_DYNAMIC_LEN + 2) - : stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN + 2); - stream_putc(s, CAPABILITY_CODE_DYNAMIC_OLD); - stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN); - stream_putc(s, BGP_OPEN_OPT_CAP); ext_opt_params ? stream_putw(s, CAPABILITY_CODE_DYNAMIC_LEN + 2) : stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN + 2); diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h index 7e3d07876ddb..b18dbaa04f08 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -31,7 +31,6 @@ struct graceful_restart_af { #define CAPABILITY_CODE_ORF 3 /* Cooperative Route Filtering Capability */ #define CAPABILITY_CODE_RESTART 64 /* Graceful Restart Capability */ #define CAPABILITY_CODE_AS4 65 /* 4-octet AS number Capability */ -#define CAPABILITY_CODE_DYNAMIC_OLD 66 /* Dynamic Capability, deprecated since 2003 */ #define CAPABILITY_CODE_DYNAMIC 67 /* Dynamic Capability */ #define CAPABILITY_CODE_ADDPATH 69 /* Addpath Capability */ #define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */ @@ -39,8 +38,6 @@ struct graceful_restart_af { #define CAPABILITY_CODE_FQDN 73 /* Advertise hostname capability */ #define CAPABILITY_CODE_SOFT_VERSION 75 /* Software Version capability */ #define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */ -#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */ -#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */ #define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */ #define CAPABILITY_CODE_ROLE 9 /* Role Capability */ @@ -65,7 +62,6 @@ struct graceful_restart_af { /* ORF Type */ #define ORF_TYPE_RESERVED 0 #define ORF_TYPE_PREFIX 64 -#define ORF_TYPE_PREFIX_OLD 128 /* ORF Mode */ #define ORF_MODE_RECEIVE 1 diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 93672d29f13d..ab9e94099797 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -8,7 +8,7 @@ #include #include -#include "thread.h" +#include "frrevent.h" #include "stream.h" #include "network.h" #include "prefix.h" @@ -331,7 +331,7 @@ static void bgp_update_explicit_eors(struct peer *peer) * calling safi function and for evpn, passed as parameter */ int bgp_nlri_parse(struct peer *peer, struct attr *attr, - struct bgp_nlri *packet, int mp_withdraw) + struct bgp_nlri *packet, bool mp_withdraw) { switch (packet->safi) { case SAFI_UNICAST: @@ -442,9 +442,9 @@ static void bgp_write_proceed_actions(struct peer *peer) * update group a peer belongs to, encode this information into packets, and * enqueue the packets onto the peer's output buffer. */ -void bgp_generate_updgrp_packets(struct thread *thread) +void bgp_generate_updgrp_packets(struct event *thread) { - struct peer *peer = THREAD_ARG(thread); + struct peer *peer = EVENT_ARG(thread); struct stream *s; struct peer_af *paf; @@ -1080,6 +1080,7 @@ void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code, * @param orf_type Outbound Route Filtering type * @param when_to_refresh Whether to refresh immediately or defer * @param remove Whether to remove ORF for specified AFI/SAFI + * @param subtype BGP enhanced route refresh optional subtypes */ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, uint8_t orf_type, uint8_t when_to_refresh, @@ -1102,7 +1103,7 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, s = stream_new(peer->max_packet_size); /* Make BGP update packet. */ - if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV)) bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_NEW); else bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_OLD); @@ -1115,7 +1116,7 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, stream_putc(s, 0); stream_putc(s, pkt_safi); - if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD) + if (orf_type == ORF_TYPE_PREFIX) if (remove || filter->plist[FILTER_IN].plist) { uint16_t orf_len; unsigned long orfp; @@ -1792,11 +1793,11 @@ static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size) return Receive_KEEPALIVE_message; } -static void bgp_refresh_stalepath_timer_expire(struct thread *thread) +static void bgp_refresh_stalepath_timer_expire(struct event *thread) { struct peer_af *paf; - paf = THREAD_ARG(thread); + paf = EVENT_ARG(thread); afi_t afi = paf->afi; safi_t safi = paf->safi; @@ -2106,11 +2107,11 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size) "EOR RCV", gr_info->eor_received); if (gr_info->t_select_deferral) { - void *info = THREAD_ARG( + void *info = EVENT_ARG( gr_info->t_select_deferral); XFREE(MTYPE_TMP, info); } - THREAD_OFF(gr_info->t_select_deferral); + EVENT_OFF(gr_info->t_select_deferral); gr_info->eor_required = 0; gr_info->eor_received = 0; /* Best path selection */ @@ -2374,8 +2375,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) /* orf_len in bounds? */ if ((stream_pnt(s) + orf_len) > end) break; /* XXX: Notify instead?? */ - if (orf_type == ORF_TYPE_PREFIX - || orf_type == ORF_TYPE_PREFIX_OLD) { + if (orf_type == ORF_TYPE_PREFIX) { uint8_t *p_pnt = stream_pnt(s); uint8_t *p_end = stream_pnt(s) + orf_len; struct orf_prefix orfp; @@ -2595,10 +2595,10 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) } if (peer_established(peer)) - thread_add_timer(bm->master, - bgp_refresh_stalepath_timer_expire, - paf, peer->bgp->stalepath_time, - &peer->t_refresh_stalepath); + event_add_timer(bm->master, + bgp_refresh_stalepath_timer_expire, paf, + peer->bgp->stalepath_time, + &peer->t_refresh_stalepath); if (bgp_debug_neighbor_events(peer)) zlog_debug( @@ -2613,7 +2613,7 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size) return BGP_PACKET_NOOP; } - THREAD_OFF(peer->t_refresh_stalepath); + EVENT_OFF(peer->t_refresh_stalepath); SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EORR_RECEIVED); UNSET_FLAG(peer->af_sflags[afi][safi], @@ -2863,11 +2863,11 @@ int bgp_capability_receive(struct peer *peer, bgp_size_t size) * would not, making event flow difficult to understand. Please think twice * before hacking this. * - * Thread type: THREAD_EVENT + * Thread type: EVENT_EVENT * @param thread * @return 0 */ -void bgp_process_packet(struct thread *thread) +void bgp_process_packet(struct event *thread) { /* Yes first of all get peer pointer. */ struct peer *peer; // peer @@ -2875,7 +2875,7 @@ void bgp_process_packet(struct thread *thread) int fsm_update_result; // return code of bgp_event_update() int mprc; // message processing return code - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); rpkt_quanta_old = atomic_load_explicit(&peer->bgp->rpkt_quanta, memory_order_relaxed); fsm_update_result = 0; @@ -3021,9 +3021,9 @@ void bgp_process_packet(struct thread *thread) frr_with_mutex (&peer->io_mtx) { // more work to do, come back later if (peer->ibuf->count > 0) - thread_add_event( - bm->master, bgp_process_packet, peer, 0, - &peer->t_process_packet); + event_add_event(bm->master, bgp_process_packet, + peer, 0, + &peer->t_process_packet); } } } @@ -3044,13 +3044,13 @@ void bgp_send_delayed_eor(struct bgp *bgp) * having the io pthread try to enqueue fsm events or mess with the peer * struct. */ -void bgp_packet_process_error(struct thread *thread) +void bgp_packet_process_error(struct event *thread) { struct peer *peer; int code; - peer = THREAD_ARG(thread); - code = THREAD_VAL(thread); + peer = EVENT_ARG(thread); + code = EVENT_VAL(thread); if (bgp_debug_neighbor_events(peer)) zlog_debug("%s [Event] BGP error %d on fd %d", diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h index 2eb5693ae235..04bdb81849bd 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -42,37 +42,39 @@ DECLARE_HOOK(bgp_packet_send, } while (0) /* Packet send and receive function prototypes. */ -extern void bgp_keepalive_send(struct peer *); -extern void bgp_open_send(struct peer *); -extern void bgp_notify_send(struct peer *, uint8_t, uint8_t); -extern void bgp_notify_send_with_data(struct peer *, uint8_t, uint8_t, - uint8_t *, size_t); +extern void bgp_keepalive_send(struct peer *peer); +extern void bgp_open_send(struct peer *peer); +extern void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code); +extern void bgp_notify_send_with_data(struct peer *peer, uint8_t code, + uint8_t sub_code, uint8_t *data, + size_t datalen); void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code, uint8_t *data, size_t datalen); extern void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi, uint8_t orf_type, uint8_t when_to_refresh, int remove, uint8_t subtype); -extern void bgp_capability_send(struct peer *, afi_t, safi_t, int, int); +extern void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi, + int capabilty_code, int action); -extern int bgp_capability_receive(struct peer *, bgp_size_t); +extern int bgp_capability_receive(struct peer *peer, bgp_size_t length); -extern int bgp_nlri_parse(struct peer *, struct attr *, struct bgp_nlri *, - int mp_withdraw); +extern int bgp_nlri_parse(struct peer *peer, struct attr *attr, + struct bgp_nlri *nlri, bool mp_withdraw); -extern void bgp_update_restarted_peers(struct peer *); -extern void bgp_update_implicit_eors(struct peer *); -extern void bgp_check_update_delay(struct bgp *); +extern void bgp_update_restarted_peers(struct peer *peer); +extern void bgp_update_implicit_eors(struct peer *peer); +extern void bgp_check_update_delay(struct bgp *peer); extern int bgp_packet_set_marker(struct stream *s, uint8_t type); extern void bgp_packet_set_size(struct stream *s); -extern void bgp_generate_updgrp_packets(struct thread *); -extern void bgp_process_packet(struct thread *); +extern void bgp_generate_updgrp_packets(struct event *event); +extern void bgp_process_packet(struct event *event); extern void bgp_send_delayed_eor(struct bgp *bgp); /* Task callback to handle socket error encountered in the io pthread */ -void bgp_packet_process_error(struct thread *thread); +void bgp_packet_process_error(struct event *thread); extern struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify); extern bool bgp_has_graceful_restart_notification(struct peer *peer); diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index 7bced6cd334f..bc9ecff7d909 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1014,7 +1014,7 @@ static void bgp_pbr_match_free(void *arg) bpm->action = NULL; } } - hash_free(bpm->entry_hash); + hash_clean_and_free(&bpm->entry_hash, NULL); XFREE(MTYPE_PBR_MATCH, bpm); } @@ -1386,23 +1386,13 @@ struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id, void bgp_pbr_cleanup(struct bgp *bgp) { - if (bgp->pbr_match_hash) { - hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free); - hash_free(bgp->pbr_match_hash); - bgp->pbr_match_hash = NULL; - } - if (bgp->pbr_rule_hash) { - hash_clean(bgp->pbr_rule_hash, bgp_pbr_rule_free); - hash_free(bgp->pbr_rule_hash); - bgp->pbr_rule_hash = NULL; - } - if (bgp->pbr_action_hash) { - hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free); - hash_free(bgp->pbr_action_hash); - bgp->pbr_action_hash = NULL; - } + hash_clean_and_free(&bgp->pbr_match_hash, bgp_pbr_match_free); + hash_clean_and_free(&bgp->pbr_rule_hash, bgp_pbr_rule_free); + hash_clean_and_free(&bgp->pbr_action_hash, bgp_pbr_action_free); + 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); diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 1e9f9429c5e8..c559be5c5212 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -20,7 +20,7 @@ #include "buffer.h" #include "sockunion.h" #include "plist.h" -#include "thread.h" +#include "frrevent.h" #include "workqueue.h" #include "queue.h" #include "memory.h" @@ -151,9 +151,9 @@ struct bgp_dest *bgp_afi_node_get(struct bgp_table *table, afi_t afi, return dest; } -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_dest *bgp_safi_node_lookup(struct bgp_table *table, safi_t safi, + const struct prefix *p, + struct prefix_rd *prd) { struct bgp_dest *dest; struct bgp_dest *pdest = NULL; @@ -298,6 +298,29 @@ struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path) return path; } +bool bgp_path_info_nexthop_changed(struct bgp_path_info *pi, struct peer *to, + afi_t afi) +{ + if (pi->peer->sort == BGP_PEER_IBGP && to->sort == BGP_PEER_IBGP && + !CHECK_FLAG(to->af_flags[afi][SAFI_MPLS_VPN], + PEER_FLAG_FORCE_NEXTHOP_SELF)) + /* IBGP RR with no nexthop self force configured */ + return false; + + if (to->sort == BGP_PEER_IBGP && + !CHECK_FLAG(to->af_flags[afi][SAFI_MPLS_VPN], + PEER_FLAG_NEXTHOP_SELF)) + /* IBGP RR with no nexthop self configured */ + return false; + + if (CHECK_FLAG(to->af_flags[afi][SAFI_MPLS_VPN], + PEER_FLAG_NEXTHOP_UNCHANGED)) + /* IBGP or EBGP with nexthop attribute unchanged */ + return false; + + return true; +} + /* This function sets flag BGP_NODE_SELECT_DEFER based on condition */ static int bgp_dest_set_defer_flag(struct bgp_dest *dest, bool delete) { @@ -316,10 +339,15 @@ static int bgp_dest_set_defer_flag(struct bgp_dest *dest, bool delete) return 0; if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED)) { - if (BGP_DEBUG(update, UPDATE_OUT)) + if (BGP_DEBUG(update, UPDATE_OUT)) { + table = bgp_dest_table(dest); + if (table) + bgp = table->bgp; + zlog_debug( - "Route %pBD is in workqueue and being processed, not deferred.", - dest); + "Route %pBD(%s) is in workqueue and being processed, not deferred.", + dest, bgp ? bgp->name_pretty : "(Unknown)"); + } return 0; } @@ -367,8 +395,8 @@ static int bgp_dest_set_defer_flag(struct bgp_dest *dest, bool delete) bgp->gr_info[afi][safi].gr_deferred++; SET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER); if (BGP_DEBUG(update, UPDATE_OUT)) - zlog_debug("DEFER route %pBD, dest %p", dest, - dest); + zlog_debug("DEFER route %pBD(%s), dest %p", + dest, bgp->name_pretty, dest); return 0; } } @@ -551,11 +579,11 @@ struct bgp_path_info *bgp_get_imported_bpi_ultimate(struct bgp_path_info *info) /* Compare two bgp route entity. If 'new' is preferable over 'exist' return 1. */ -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, - enum bgp_path_selection_reason *reason) +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, + enum bgp_path_selection_reason *reason) { const struct prefix *new_p; struct attr *newattr, *existattr; @@ -1461,7 +1489,7 @@ 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]; + char pfx_buf[PREFIX2STR_BUFFER] = {}; return bgp_path_info_cmp(bgp, new, exist, paths_eq, NULL, 0, pfx_buf, AFI_L2VPN, SAFI_EVPN, &reason); @@ -1991,6 +2019,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, int samepeer_safe = 0; /* for synthetic mplsvpns routes */ bool nh_reset = false; uint64_t cum_bw; + mpls_label_t label; if (DISABLE_BGP_ANNOUNCE) return false; @@ -2076,7 +2105,7 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, /* 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(dest, pi, peer, afi, safi); + 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 @@ -2085,6 +2114,29 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, p, &label); return false; } + } else if (safi == SAFI_MPLS_VPN && + CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND) && + pi->mplsvpn.bmnc.nh_label_bind_cache && peer && + pi->peer != peer && pi->sub_type != BGP_ROUTE_IMPORTED && + pi->sub_type != BGP_ROUTE_STATIC && + bgp_mplsvpn_path_uses_valid_mpls_label(pi) && + bgp_path_info_nexthop_changed(pi, peer, afi)) { + /* Redistributed mpls vpn route between distinct + * peers from 'pi->peer' to 'to', + * and an mpls label is used in this path, + * and there is a nh label bind entry, + * then get appropriate mpls local label + * and check its validity + */ + label = bgp_mplsvpn_nh_label_bind_get_label(pi); + if (!bgp_is_valid_label(&label)) { + if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) + zlog_debug("u%" PRIu64 ":s%" PRIu64 + " %pFX is filtered - no valid label", + subgrp->update_group->id, subgrp->id, + p); + return false; + } } /* Do not send back route to sender. */ @@ -2129,10 +2181,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, } /* ORF prefix-list filter check */ - if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) - && (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))) + if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) && + CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)) if (peer->orf_plist[afi][safi]) { if (prefix_list_apply(peer->orf_plist[afi][safi], p) == PREFIX_DENY) { @@ -2381,7 +2431,10 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) zlog_debug( "%pBP [Update:SEND] %pFX is filtered by route-map '%s'", - peer, p, ROUTE_MAP_OUT_NAME(filter)); + peer, p, + bgp_path_suppressed(pi) + ? UNSUPPRESS_MAP_NAME(filter) + : ROUTE_MAP_OUT_NAME(filter)); bgp_attr_flush(rmap_path.attr); return false; } @@ -2613,14 +2666,14 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi, return true; } -static void bgp_route_select_timer_expire(struct thread *thread) +static void bgp_route_select_timer_expire(struct event *thread) { struct afi_safi_info *info; afi_t afi; safi_t safi; struct bgp *bgp; - info = THREAD_ARG(thread); + info = EVENT_ARG(thread); afi = info->afi; safi = info->safi; bgp = info->bgp; @@ -2645,7 +2698,7 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, struct bgp_path_info *nextpi = NULL; int paths_eq, do_mpath, debug; struct list mp_list; - char pfx_buf[PREFIX2STR_BUFFER]; + char pfx_buf[PREFIX2STR_BUFFER] = {}; char path_buf[PATH_ADDPATH_STR_BUFFER]; bgp_mp_list_init(&mp_list); @@ -2755,8 +2808,10 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, bgp_path_info_reap(dest, pi); if (debug) - zlog_debug("%s: pi %p in holddown", __func__, - pi); + zlog_debug( + "%s: %pBD(%s) pi from %s in holddown", + __func__, dest, bgp->name_pretty, + pi->peer->host); continue; } @@ -2767,8 +2822,10 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, if (debug) zlog_debug( - "%s: pi %p non self peer %s not estab state", - __func__, pi, pi->peer->host); + "%s: %pBD(%s) non self peer %s not estab state", + __func__, dest, + bgp->name_pretty, + pi->peer->host); continue; } @@ -2777,7 +2834,9 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, && (!CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))) { bgp_path_info_unset_flag(dest, pi, BGP_PATH_DMED_CHECK); if (debug) - zlog_debug("%s: pi %p dmed", __func__, pi); + zlog_debug("%s: %pBD(%s) pi %s dmed", __func__, + dest, bgp->name_pretty, + pi->peer->host); continue; } @@ -2840,8 +2899,9 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, if (!bgp_path_info_nexthop_cmp(pi, new_select)) { if (debug) zlog_debug( - "%pBD: %s has the same nexthop as the bestpath, skip it", - dest, path_buf); + "%pBD(%s): %s has the same nexthop as the bestpath, skip it", + dest, bgp->name_pretty, + path_buf); continue; } @@ -2852,8 +2912,9 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, if (paths_eq) { if (debug) zlog_debug( - "%pBD: %s is equivalent to the bestpath, add to the multipath list", - dest, path_buf); + "%pBD(%s): %s is equivalent to the bestpath, add to the multipath list", + dest, bgp->name_pretty, + path_buf); bgp_mp_list_add(&mp_list, pi); } } @@ -2878,20 +2939,16 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest, */ void subgroup_process_announce_selected(struct update_subgroup *subgrp, struct bgp_path_info *selected, - struct bgp_dest *dest, - uint32_t addpath_tx_id) + struct bgp_dest *dest, afi_t afi, + safi_t safi, uint32_t addpath_tx_id) { const struct prefix *p; struct peer *onlypeer; struct attr attr; - afi_t afi; - safi_t safi; struct bgp *bgp; bool advertise; p = bgp_dest_get_prefix(dest); - afi = SUBGRP_AFI(subgrp); - safi = SUBGRP_SAFI(subgrp); bgp = SUBGRP_INST(subgrp); onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer : NULL); @@ -2912,7 +2969,7 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, * is pending (BGP_NODE_FIB_INSTALL_PENDING), do not advertise the * route */ - advertise = bgp_check_advertise(bgp, dest); + advertise = bgp_check_advertise(bgp, dest, safi); if (selected) { if (subgroup_announce_check(dest, selected, subgrp, p, &attr, @@ -2921,10 +2978,14 @@ void subgroup_process_announce_selected(struct update_subgroup *subgrp, * in FIB, then it is advertised */ if (advertise) { - if (!bgp_check_withdrawal(bgp, dest)) - bgp_adj_out_set_subgroup( - dest, subgrp, &attr, selected); - else + if (!bgp_check_withdrawal(bgp, dest, safi)) { + struct attr *adv_attr = + bgp_attr_intern(&attr); + + bgp_adj_out_set_subgroup(dest, subgrp, + adv_attr, + selected); + } else bgp_adj_out_unset_subgroup( dest, subgrp, 1, addpath_tx_id); } @@ -3050,7 +3111,9 @@ static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi, * the IMPLICIT_NULL label. This is pretty specialized: it's only called * in a path where we basically _know_ this is a BGP-LU route. */ -static bool bgp_lu_need_imp_null(const struct bgp_path_info *new_select) +static bool bgp_lu_need_null_label(struct bgp *bgp, + const struct bgp_path_info *new_select, + afi_t afi, mpls_label_t *label) { /* Certain types get imp null; so do paths where the nexthop is * not labeled. @@ -3058,13 +3121,131 @@ static bool bgp_lu_need_imp_null(const struct bgp_path_info *new_select) if (new_select->sub_type == BGP_ROUTE_STATIC || new_select->sub_type == BGP_ROUTE_AGGREGATE || new_select->sub_type == BGP_ROUTE_REDISTRIBUTE) + goto need_null_label; + else if (new_select->extra && + bgp_is_valid_label(&new_select->extra->label[0])) + return false; +need_null_label: + if (label == NULL) return true; - else if (new_select->extra == NULL || - !bgp_is_valid_label(&new_select->extra->label[0])) - /* TODO -- should be configurable? */ - return true; + /* Disable PHP : explicit-null */ + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) && + afi == AFI_IP) + *label = MPLS_LABEL_IPV4_EXPLICIT_NULL; + else if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL) && + afi == AFI_IP6) + *label = MPLS_LABEL_IPV6_EXPLICIT_NULL; else - return false; + /* Enforced PHP popping: implicit-null */ + *label = MPLS_LABEL_IMPLICIT_NULL; + + return true; +} + +/* Right now, since we only deal with per-prefix labels, it is not + * necessary to do this upon changes to best path. Exceptions: + * - label index has changed -> recalculate resulting label + * - path_info sub_type changed -> switch to/from null label value + * - no valid label (due to removed static label binding) -> get new one + */ +static void bgp_lu_handle_label_allocation(struct bgp *bgp, + struct bgp_dest *dest, + struct bgp_path_info *new_select, + struct bgp_path_info *old_select, + afi_t afi) +{ + mpls_label_t mpls_label_null; + + if (bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) { + if (new_select) { + if (!old_select || + bgp_label_index_differs(new_select, old_select) || + new_select->sub_type != old_select->sub_type || + !bgp_is_valid_label(&dest->local_label)) { + /* control label imposition for local + * routes, aggregate and redistributed + * routes + */ + mpls_label_null = MPLS_LABEL_IMPLICIT_NULL; + if (bgp_lu_need_null_label(bgp, new_select, afi, + &mpls_label_null)) { + if (CHECK_FLAG( + dest->flags, + BGP_NODE_REGISTERED_FOR_LABEL) || + CHECK_FLAG( + dest->flags, + BGP_NODE_LABEL_REQUESTED)) + bgp_unregister_for_label(dest); + dest->local_label = mpls_lse_encode( + mpls_label_null, 0, 0, 1); + bgp_set_valid_label(&dest->local_label); + } else + bgp_register_for_label(dest, + new_select); + } + } else if (CHECK_FLAG(dest->flags, + BGP_NODE_REGISTERED_FOR_LABEL) || + CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) { + bgp_unregister_for_label(dest); + } + } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL) || + CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) { + bgp_unregister_for_label(dest); + } +} + +static struct interface * +bgp_label_get_resolved_nh_iface(const struct bgp_path_info *pi) +{ + struct nexthop *nh; + + if (pi->nexthop == NULL || pi->nexthop->nexthop == NULL || + !CHECK_FLAG(pi->nexthop->flags, BGP_NEXTHOP_VALID)) + /* next-hop is not valid */ + return NULL; + + nh = pi->nexthop->nexthop; + if (nh->ifindex == IFINDEX_INTERNAL && + nh->type != NEXTHOP_TYPE_IPV4_IFINDEX && + nh->type != NEXTHOP_TYPE_IPV6_IFINDEX) + /* next-hop does not contain valid interface */ + return NULL; + + return if_lookup_by_index(nh->ifindex, nh->vrf_id); +} + +static void +bgp_mplsvpn_handle_label_allocation(struct bgp *bgp, struct bgp_dest *dest, + struct bgp_path_info *new_select, + struct bgp_path_info *old_select, afi_t afi) +{ + struct interface *ifp; + struct bgp_interface *bgp_ifp; + + if (bgp->allocate_mpls_labels[afi][SAFI_MPLS_VPN] && new_select) { + ifp = bgp_label_get_resolved_nh_iface(new_select); + if (ifp) + bgp_ifp = (struct bgp_interface *)(ifp->info); + else + bgp_ifp = NULL; + if (bgp_ifp && + CHECK_FLAG(bgp_ifp->flags, + BGP_INTERFACE_MPLS_L3VPN_SWITCHING) && + bgp_mplsvpn_path_uses_valid_mpls_label(new_select) && + new_select->sub_type != BGP_ROUTE_IMPORTED && + new_select->sub_type != BGP_ROUTE_STATIC) + bgp_mplsvpn_nh_label_bind_register_local_label( + bgp, dest, new_select); + else + bgp_mplsvpn_path_nh_label_bind_unlink(new_select); + } else { + if (new_select) + /* no mpls vpn allocation */ + bgp_mplsvpn_path_nh_label_bind_unlink(new_select); + else if (old_select) + /* unlink old selection if any */ + bgp_mplsvpn_path_nh_label_bind_unlink(old_select); + } } /* @@ -3099,8 +3280,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, debug = bgp_debug_bestpath(dest); if (debug) zlog_debug( - "%s: bgp delete in progress, ignoring event, p=%pBD", - __func__, dest); + "%s: bgp delete in progress, ignoring event, p=%pBD(%s)", + __func__, dest, bgp->name_pretty); return; } /* Is it end of initial update? (after startup) */ @@ -3123,7 +3304,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, debug = bgp_debug_bestpath(dest); if (debug) - zlog_debug("%s: p=%pBDi(%s) afi=%s, safi=%s start", __func__, + zlog_debug("%s: p=%pBD(%s) afi=%s, safi=%s start", __func__, dest, bgp->name_pretty, afi2str(afi), safi2str(safi)); @@ -3132,7 +3313,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, */ 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); + zlog_debug("SELECT_DEFER flag set for route %p(%s)", + dest, bgp->name_pretty); return; } @@ -3142,49 +3324,18 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, old_select = old_and_new.old; new_select = old_and_new.new; - /* Do we need to allocate or free labels? - * Right now, since we only deal with per-prefix labels, it is not - * necessary to do this upon changes to best path. Exceptions: - * - label index has changed -> recalculate resulting label - * - path_info sub_type changed -> switch to/from implicit-null - * - no valid label (due to removed static label binding) -> get new one - */ - if (bgp->allocate_mpls_labels[afi][safi]) { - if (new_select) { - if (!old_select - || bgp_label_index_differs(new_select, old_select) - || new_select->sub_type != old_select->sub_type - || !bgp_is_valid_label(&dest->local_label)) { - /* Enforced penultimate hop popping: - * implicit-null for local routes, aggregate - * and redistributed routes - */ - if (bgp_lu_need_imp_null(new_select)) { - if (CHECK_FLAG( - dest->flags, - BGP_NODE_REGISTERED_FOR_LABEL) - || CHECK_FLAG( - dest->flags, - BGP_NODE_LABEL_REQUESTED)) - bgp_unregister_for_label(dest); - dest->local_label = mpls_lse_encode( - MPLS_LABEL_IMPLICIT_NULL, 0, 0, - 1); - bgp_set_valid_label(&dest->local_label); - } else - bgp_register_for_label(dest, - new_select); - } - } else if (CHECK_FLAG(dest->flags, - BGP_NODE_REGISTERED_FOR_LABEL) - || CHECK_FLAG(dest->flags, - BGP_NODE_LABEL_REQUESTED)) { - bgp_unregister_for_label(dest); - } - } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL) - || CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) { - bgp_unregister_for_label(dest); - } + if (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST) + /* label unicast path : + * Do we need to allocate or free labels? + */ + bgp_lu_handle_label_allocation(bgp, dest, new_select, + old_select, afi); + else if (safi == SAFI_MPLS_VPN) + /* mpls vpn path: + * Do we need to allocate or free labels? + */ + bgp_mplsvpn_handle_label_allocation(bgp, dest, new_select, + old_select, afi); if (debug) zlog_debug( @@ -3195,10 +3346,11 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, /* 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(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 (old_select && old_select == new_select && + !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR) && + !CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_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)) { #ifdef ENABLE_BGP_VNC vnc_import_bgp_add_route(bgp, p, old_select); @@ -3207,11 +3359,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, if (bgp_fibupd_safi(safi) && !bgp_option_check(BGP_OPT_NO_FIB)) { - if (BGP_SUPPRESS_FIB_ENABLED(bgp) - && new_select->sub_type == BGP_ROUTE_NORMAL) - SET_FLAG(dest->flags, - BGP_NODE_FIB_INSTALL_PENDING); - if (new_select->type == ZEBRA_ROUTE_BGP && (new_select->sub_type == BGP_ROUTE_NORMAL || new_select->sub_type @@ -3257,18 +3404,20 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, */ UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR); + /* If the process wants to force deletion this flag will be set + */ + UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_CLEAR); + /* bestpath has changed; bump version */ if (old_select || new_select) { bgp_bump_version(dest); - if (!bgp->t_rmap_def_originate_eval) { - bgp_lock(bgp); - thread_add_timer( + if (!bgp->t_rmap_def_originate_eval) + event_add_timer( bm->master, update_group_refresh_default_originate_route_map, - bgp, RMAP_DEFAULT_ORIGINATE_EVAL_TIMER, + bgp, bgp->rmap_def_originate_eval_timer, &bgp->t_rmap_def_originate_eval); - } } if (old_select) @@ -3317,10 +3466,6 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, || new_select->sub_type == BGP_ROUTE_AGGREGATE || new_select->sub_type == BGP_ROUTE_IMPORTED)) { - if (BGP_SUPPRESS_FIB_ENABLED(bgp)) - SET_FLAG(dest->flags, - BGP_NODE_FIB_INSTALL_PENDING); - /* if this is an evpn imported type-5 prefix, * we need to withdraw the route first to clear * the nh neigh and the RMAC entry. @@ -3363,11 +3508,11 @@ void bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) struct afi_safi_info *thread_info; if (bgp->gr_info[afi][safi].t_route_select) { - struct thread *t = bgp->gr_info[afi][safi].t_route_select; + struct event *t = bgp->gr_info[afi][safi].t_route_select; - thread_info = THREAD_ARG(t); + thread_info = EVENT_ARG(t); XFREE(MTYPE_TMP, thread_info); - THREAD_OFF(bgp->gr_info[afi][safi].t_route_select); + EVENT_OFF(bgp->gr_info[afi][safi].t_route_select); } if (BGP_DEBUG(update, UPDATE_OUT)) { @@ -3401,7 +3546,7 @@ void bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) if (!bgp->gr_info[afi][safi].gr_deferred) { bgp_send_delayed_eor(bgp); /* Send route processing complete message to RIB */ - bgp_zebra_update(afi, safi, bgp->vrf_id, + bgp_zebra_update(bgp, afi, safi, ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE); return; } @@ -3415,7 +3560,7 @@ void bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi) /* If there are more routes to be processed, start the * selection timer */ - thread_add_timer(bm->master, bgp_route_select_timer_expire, thread_info, + event_add_timer(bm->master, bgp_route_select_timer_expire, thread_info, BGP_ROUTE_SELECT_DELAY, &bgp->gr_info[afi][safi].t_route_select); } @@ -3567,11 +3712,11 @@ void bgp_add_eoiu_mark(struct bgp *bgp) work_queue_add(bgp->process_queue, pqnode); } -static void bgp_maximum_prefix_restart_timer(struct thread *thread) +static void bgp_maximum_prefix_restart_timer(struct event *thread) { struct peer *peer; - peer = THREAD_ARG(thread); + peer = EVENT_ARG(thread); peer->t_pmax_restart = NULL; if (bgp_debug_neighbor_events(peer)) @@ -3826,6 +3971,12 @@ bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi, (type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true : false; + /* If `bgp allow-martian-nexthop` is turned on, return next-hop + * as good. + */ + if (bgp->allow_martian) + return false; + /* * Only validated for unicast and multicast currently. * Also valid for EVPN where the nexthop is an IP address. @@ -3999,7 +4150,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, afi_t nh_afi; bool force_evpn_import = false; safi_t orig_safi = safi; - bool leak_success = true; int allowas_in = 0; if (frrtrace_enabled(frr_bgp, process_update)) { @@ -4035,17 +4185,24 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (has_valid_label) assert(label != NULL); - /* Update overlay index of the attribute */ - if (afi == AFI_L2VPN && evpn) - memcpy(&attr->evpn_overlay, evpn, - sizeof(struct bgp_route_evpn)); /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ - if (!soft_reconfig - && CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) - && peer != bgp->peer_self) + if (!soft_reconfig && + CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) && + peer != bgp->peer_self) { + /* + * If the trigger is not from soft_reconfig and if + * PEER_FLAG_SOFT_RECONFIG is enabled for the peer, then attr + * will not be interned. In which case, it is ok to update the + * attr->evpn_overlay, so that, this can be stored in adj_in. + */ + if ((afi == AFI_L2VPN) && evpn) { + memcpy(&attr->evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); + } bgp_adj_in_set(dest, peer, attr, addpath_id); + } /* Update permitted loop count */ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN)) @@ -4136,6 +4293,31 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, goto filtered; } + if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_MPLS_VPN && + bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT && + !CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) && + vpn_leak_to_vrf_no_retain_filter_check(bgp, attr, afi)) { + reason = + "no import. Filtered by no bgp retain route-target all"; + goto filtered; + } + + /* If the route has Node Target Extended Communities, check + * if it's allowed to be installed locally. + */ + if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) { + struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); + + if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP, + ECOMMUNITY_NODE_TARGET) && + !ecommunity_node_target_match(ecomm, &peer->local_id)) { + reason = + "Node-Target Extended Communities do not contain own BGP Identifier;"; + goto filtered; + } + } + /* 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 @@ -4173,6 +4355,15 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } new_attr = *attr; + /* + * If bgp_update is called with soft_reconfig set then + * attr is interned. In this case, do not overwrite the + * attr->evpn_overlay with evpn directly. Instead memcpy + * evpn to new_atr.evpn_overlay before it is interned. + */ + if (soft_reconfig && (afi == AFI_L2VPN) && evpn) + memcpy(&new_attr.evpn_overlay, evpn, + sizeof(struct bgp_route_evpn)); /* Apply incoming route-map. * NB: new_attr may now contain newly allocated values from route-map @@ -4250,18 +4441,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_attr_flush(&new_attr); goto filtered; } - /* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following - * condition : - * Suppress fib is enabled - * BGP_OPT_NO_FIB is not enabled - * Route type is BGP_ROUTE_NORMAL (peer learnt routes) - * Route is being installed first time (BGP_NODE_FIB_INSTALLED not set) - */ - if (bgp_fibupd_safi(safi) && BGP_SUPPRESS_FIB_ENABLED(bgp) - && (sub_type == BGP_ROUTE_NORMAL) - && (!bgp_option_check(BGP_OPT_NO_FIB)) - && (!CHECK_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED))) - SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); /* If neighbor soo is configured, tag all incoming routes with * this SoO tag and then filter out advertisements in @@ -4575,10 +4754,12 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, /* Nexthop reachability check - for unicast and * labeled-unicast.. */ - 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 (((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST || + (safi == SAFI_MPLS_VPN && + pi->sub_type != BGP_ROUTE_IMPORTED))) || + (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, @@ -4599,10 +4780,14 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi, safi, pi, NULL, connected, bgp_nht_param_prefix) || - CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) + CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) { + if (accept_own) + bgp_path_info_set_flag( + dest, pi, BGP_PATH_ACCEPT_OWN); + bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); - else { + } else { if (BGP_DEBUG(nht, NHT)) { zlog_debug("%s(%pI4): NH unresolved", __func__, @@ -4612,10 +4797,13 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, BGP_PATH_VALID); } } else { + /* case mpls-vpn routes with accept-own community + * (which have the BGP_ROUTE_IMPORTED subtype) + * case other afi/safi not supporting nexthop tracking + */ if (accept_own) bgp_path_info_set_flag(dest, pi, BGP_PATH_ACCEPT_OWN); - bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID); } @@ -4667,7 +4855,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } if ((SAFI_MPLS_VPN == safi) && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { - leak_success = vpn_leak_to_vrf_update(bgp, pi, prd); + vpn_leak_to_vrf_update(bgp, pi, prd); } #ifdef ENABLE_BGP_VNC @@ -4682,13 +4870,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, type, sub_type, NULL); } #endif - if ((safi == SAFI_MPLS_VPN) && - !CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) && - !leak_success) { - bgp_unlink_nexthop(pi); - bgp_path_info_delete(dest, pi); - } return; } // End of implicit withdraw @@ -4745,9 +4926,11 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } /* Nexthop reachability check. */ - 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 (((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST || + (safi == SAFI_MPLS_VPN && + new->sub_type != BGP_ROUTE_IMPORTED))) || + (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, @@ -4762,18 +4945,25 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, if (bgp_find_or_add_nexthop(bgp, bgp, nh_afi, safi, new, NULL, connected, bgp_nht_param_prefix) || - CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) + CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) { + if (accept_own) + bgp_path_info_set_flag(dest, new, + BGP_PATH_ACCEPT_OWN); + bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); - else { + } else { if (BGP_DEBUG(nht, NHT)) zlog_debug("%s(%pI4): NH unresolved", __func__, &attr_new->nexthop); bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID); } } else { + /* case mpls-vpn routes with accept-own community + * (which have the BGP_ROUTE_IMPORTED subtype) + * case other afi/safi not supporting nexthop tracking + */ if (accept_own) bgp_path_info_set_flag(dest, new, BGP_PATH_ACCEPT_OWN); - bgp_path_info_set_flag(dest, new, BGP_PATH_VALID); } @@ -4830,7 +5020,7 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, } if ((SAFI_MPLS_VPN == safi) && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { - leak_success = vpn_leak_to_vrf_update(bgp, new, prd); + vpn_leak_to_vrf_update(bgp, new, prd); } #ifdef ENABLE_BGP_VNC if (SAFI_MPLS_VPN == safi) { @@ -4844,13 +5034,6 @@ void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, sub_type, NULL); } #endif - if ((safi == SAFI_MPLS_VPN) && - !CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) && - !leak_success) { - bgp_unlink_nexthop(new); - bgp_path_info_delete(dest, new); - } return; @@ -5028,7 +5211,7 @@ void bgp_stop_announce_route_timer(struct peer_af *paf) if (!paf->t_announce_route) return; - THREAD_OFF(paf->t_announce_route); + EVENT_OFF(paf->t_announce_route); } /* @@ -5037,12 +5220,12 @@ void bgp_stop_announce_route_timer(struct peer_af *paf) * Callback that is invoked when the route announcement timer for a * peer_af expires. */ -static void bgp_announce_route_timer_expired(struct thread *t) +static void bgp_announce_route_timer_expired(struct event *t) { struct peer_af *paf; struct peer *peer; - paf = THREAD_ARG(t); + paf = EVENT_ARG(t); peer = paf->peer; if (!peer_established(peer)) @@ -5091,11 +5274,11 @@ void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi, bool force) * multiple peers and the announcement doesn't happen in the * vty context. */ - thread_add_timer_msec(bm->master, bgp_announce_route_timer_expired, paf, - (subgrp->peer_count == 1) - ? BGP_ANNOUNCE_ROUTE_SHORT_DELAY_MS - : BGP_ANNOUNCE_ROUTE_DELAY_MS, - &paf->t_announce_route); + event_add_timer_msec(bm->master, bgp_announce_route_timer_expired, paf, + (subgrp->peer_count == 1) + ? BGP_ANNOUNCE_ROUTE_SHORT_DELAY_MS + : BGP_ANNOUNCE_ROUTE_DELAY_MS, + &paf->t_announce_route); } /* @@ -5197,7 +5380,7 @@ static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi, * Without splitting the full job into several part, * vtysh waits for the job to finish before responding to a BGP command */ -static void bgp_soft_reconfig_table_task(struct thread *thread) +static void bgp_soft_reconfig_table_task(struct event *thread) { uint32_t iter, max_iter; struct bgp_dest *dest; @@ -5207,7 +5390,7 @@ static void bgp_soft_reconfig_table_task(struct thread *thread) struct prefix_rd *prd; struct listnode *node, *nnode; - table = THREAD_ARG(thread); + table = EVENT_ARG(thread); prd = NULL; max_iter = SOFT_RECONFIG_TASK_MAX_PREFIX; @@ -5245,8 +5428,8 @@ static void bgp_soft_reconfig_table_task(struct thread *thread) */ if (dest || table->soft_reconfig_init) { table->soft_reconfig_init = false; - thread_add_event(bm->master, bgp_soft_reconfig_table_task, - table, 0, &table->soft_reconfig_thread); + event_add_event(bm->master, bgp_soft_reconfig_table_task, table, + 0, &table->soft_reconfig_thread); return; } /* we're done, clean up the background iteration context info and @@ -5301,7 +5484,7 @@ void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp, list_delete(&ntable->soft_reconfig_peers); bgp_soft_reconfig_table_flag(ntable, false); - THREAD_OFF(ntable->soft_reconfig_thread); + EVENT_OFF(ntable->soft_reconfig_thread); } } @@ -5347,9 +5530,9 @@ bool bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi) bgp_soft_reconfig_table_flag(table, true); if (!table->soft_reconfig_thread) - thread_add_event(bm->master, - bgp_soft_reconfig_table_task, table, 0, - &table->soft_reconfig_thread); + event_add_event(bm->master, + bgp_soft_reconfig_table_task, table, 0, + &table->soft_reconfig_thread); /* Cancel bgp_announce_route_timer_expired threads. * bgp_announce_route_timer_expired threads have been scheduled * to announce routes as soon as the soft_reconfigure process @@ -7004,8 +7187,8 @@ int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty, bgp_static->label = label; bgp_static->prd = prd; - if (rd_str) - bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str); + bgp_static->prd_pretty = XSTRDUP(MTYPE_BGP, rd_str); + if (rmap_str) { XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name); route_map_counter_decrement(bgp_static->rmap.map); @@ -7239,7 +7422,7 @@ static struct bgp_aggregate *bgp_aggregate_new(void) return XCALLOC(MTYPE_BGP_AGGREGATE, sizeof(struct bgp_aggregate)); } -static void bgp_aggregate_free(struct bgp_aggregate *aggregate) +void bgp_aggregate_free(struct bgp_aggregate *aggregate) { XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->suppress_map_name); route_map_counter_decrement(aggregate->suppress_map); @@ -7366,7 +7549,7 @@ static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin, asnotation = bgp_get_asnotation(NULL); - if (!ae) + if (!aspath) ae = aspath_empty(asnotation); if (!pi) @@ -7425,8 +7608,8 @@ static void bgp_aggregate_install( * If the aggregate information has not changed * no need to re-install it again. */ - if (bgp_aggregate_info_same(orig, origin, aspath, community, - ecommunity, lcommunity)) { + if (pi && bgp_aggregate_info_same(pi, origin, aspath, community, + ecommunity, lcommunity)) { bgp_dest_unlock_node(dest); if (aspath) @@ -7452,6 +7635,10 @@ static void bgp_aggregate_install( aggregate, atomic_aggregate, p); if (!attr) { + aspath_free(aspath); + community_free(&community); + ecommunity_free(&ecommunity); + lcommunity_free(&lcommunity); bgp_dest_unlock_node(dest); bgp_aggregate_delete(bgp, p, afi, safi, aggregate); if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) @@ -7642,7 +7829,7 @@ static void bgp_aggregate_med_update(struct bgp_aggregate *aggregate, } /* Update an aggregate as routes are added/removed from the BGP table */ -void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, +bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, safi_t safi, struct bgp_aggregate *aggregate) { struct bgp_table *table; @@ -7661,10 +7848,8 @@ void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, * then do not create aggregate route */ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS) || - (bgp->peer_self == NULL)) { - bgp_aggregate_free(aggregate); - return; - } + bgp->peer_self == NULL) + return false; /* Initialize and test routes for MED difference. */ if (aggregate->match_med) @@ -7704,7 +7889,7 @@ void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, /* If suppress fib is enabled and route not installed * in FIB, skip the route */ - if (!bgp_check_advertise(bgp, dest)) + if (!bgp_check_advertise(bgp, dest, safi)) continue; match = 0; @@ -7856,6 +8041,8 @@ void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi, bgp_aggregate_install(bgp, afi, safi, p, origin, aspath, community, ecommunity, lcommunity, atomic_aggregate, aggregate); + + return true; } void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi, @@ -8216,7 +8403,7 @@ void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p, /* If suppress fib is enabled and route not installed * in FIB, do not update the aggregate route */ - if (!bgp_check_advertise(bgp, pi->net)) + if (!bgp_check_advertise(bgp, pi->net, safi)) return; child = bgp_node_get(table, p); @@ -8334,59 +8521,7 @@ static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str, /* Unlock aggregate address configuration. */ bgp_dest_set_bgp_aggregate_info(dest, NULL); - if (aggregate->community) - community_free(&aggregate->community); - - if (aggregate->community_hash) { - /* Delete all communities in the hash. - */ - hash_clean(aggregate->community_hash, - bgp_aggr_community_remove); - /* Free up the community_hash. - */ - hash_free(aggregate->community_hash); - } - - if (aggregate->ecommunity) - ecommunity_free(&aggregate->ecommunity); - - if (aggregate->ecommunity_hash) { - /* Delete all ecommunities in the hash. - */ - hash_clean(aggregate->ecommunity_hash, - bgp_aggr_ecommunity_remove); - /* Free up the ecommunity_hash. - */ - hash_free(aggregate->ecommunity_hash); - } - - if (aggregate->lcommunity) - lcommunity_free(&aggregate->lcommunity); - - if (aggregate->lcommunity_hash) { - /* Delete all lcommunities in the hash. - */ - hash_clean(aggregate->lcommunity_hash, - bgp_aggr_lcommunity_remove); - /* Free up the lcommunity_hash. - */ - hash_free(aggregate->lcommunity_hash); - } - - if (aggregate->aspath) - aspath_free(aggregate->aspath); - - if (aggregate->aspath_hash) { - /* Delete all as-paths in the hash. - */ - hash_clean(aggregate->aspath_hash, - bgp_aggr_aspath_remove); - /* Free up the aspath_hash. - */ - hash_free(aggregate->aspath_hash); - } - - bgp_aggregate_free(aggregate); + bgp_free_aggregate_info(aggregate); bgp_dest_unlock_node(dest); bgp_dest_unlock_node(dest); @@ -8498,7 +8633,10 @@ static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi, bgp_dest_set_bgp_aggregate_info(dest, aggregate); /* Aggregate address insert into BGP routing table. */ - bgp_aggregate_route(bgp, &p, afi, safi, aggregate); + if (!bgp_aggregate_route(bgp, &p, afi, safi, aggregate)) { + bgp_aggregate_free(aggregate); + bgp_dest_unlock_node(dest); + } return CMD_SUCCESS; } @@ -8567,6 +8705,34 @@ DEFPY(aggregate_addressv4, aggregate_addressv4_cmd, match_med != NULL, suppress_map); } +void bgp_free_aggregate_info(struct bgp_aggregate *aggregate) +{ + if (aggregate->community) + community_free(&aggregate->community); + + hash_clean_and_free(&aggregate->community_hash, + bgp_aggr_community_remove); + + if (aggregate->ecommunity) + ecommunity_free(&aggregate->ecommunity); + + hash_clean_and_free(&aggregate->ecommunity_hash, + bgp_aggr_ecommunity_remove); + + if (aggregate->lcommunity) + lcommunity_free(&aggregate->lcommunity); + + hash_clean_and_free(&aggregate->lcommunity_hash, + bgp_aggr_lcommunity_remove); + + if (aggregate->aspath) + aspath_free(aggregate->aspath); + + hash_clean_and_free(&aggregate->aspath_hash, bgp_aggr_aspath_remove); + + bgp_aggregate_free(aggregate); +} + DEFPY(aggregate_addressv6, aggregate_addressv6_cmd, "[no] aggregate-address X:X::X:X/M$prefix [{" "as-set$as_set_s" @@ -8641,12 +8807,16 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, */ assert(attr.aspath); + if (p->family == AF_INET6) + UNSET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)); + switch (nhtype) { case NEXTHOP_TYPE_IFINDEX: switch (p->family) { case AF_INET: attr.nexthop.s_addr = INADDR_ANY; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + attr.mp_nexthop_global_in.s_addr = INADDR_ANY; break; case AF_INET6: memset(&attr.mp_nexthop_global, 0, @@ -8659,6 +8829,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, case NEXTHOP_TYPE_IPV4_IFINDEX: attr.nexthop = nexthop->ipv4; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + attr.mp_nexthop_global_in = nexthop->ipv4; break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -8670,6 +8841,7 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, case AF_INET: attr.nexthop.s_addr = INADDR_ANY; attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + attr.mp_nexthop_global_in.s_addr = INADDR_ANY; break; case AF_INET6: memset(&attr.mp_nexthop_global, 0, @@ -11109,7 +11281,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, if (path->peer->t_gr_restart && CHECK_FLAG(path->flags, BGP_PATH_STALE)) { unsigned long gr_remaining = - thread_timer_remain_second(path->peer->t_gr_restart); + event_timer_remain_second(path->peer->t_gr_restart); if (json_paths) { json_object_int_add(json_path, @@ -11125,7 +11297,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, bgp_attr_get_community(attr) && community_include(bgp_attr_get_community(attr), COMMUNITY_LLGR_STALE)) { - unsigned long llgr_remaining = thread_timer_remain_second( + unsigned long llgr_remaining = event_timer_remain_second( path->peer->t_llgr_stale[afi][safi]); if (json_paths) { @@ -11614,9 +11786,28 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, vty_out(vty, ",\"%pFX\": ", dest_p); } + /* This is used for 'json detail' vty keywords. + * + * In plain 'json' the per-prefix header is encoded + * as a standalone dictionary in the first json_paths + * array element: + * "": [{header}, {path-1}, {path-N}] + * (which is confusing and borderline broken) + * + * For 'json detail' this changes the value + * of each prefix-key to be a dictionary where each + * header item has its own key, and json_paths is + * tucked under the "paths" key: + * "": { + * "": , + * "": , + * "paths": [{path-1}, {path-N}] + * } + */ if (json_detail_header && json_paths != NULL) { const struct prefix_rd *prd; + /* Start per-prefix dictionary */ vty_out(vty, "{\n"); prd = bgp_rd_from_dest(dest, safi); @@ -11641,6 +11832,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi, */ vty_json_no_pretty(vty, json_paths); + /* End per-prefix dictionary */ if (json_detail_header_used) vty_out(vty, "} "); @@ -11733,7 +11925,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi, "\nDisplayed %ld routes and %ld total paths\n", output_cum, total_cum); } else { - if (use_json && output_cum == 0) + if (use_json && output_cum == 0 && json_header_depth == 0) vty_out(vty, "{}\n"); } return CMD_SUCCESS; @@ -11883,12 +12075,14 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp, vty_out(vty, "BGP routing table entry for %s%s%pFX, version %" PRIu64 "\n", - ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) + (((safi == SAFI_MPLS_VPN || + safi == SAFI_ENCAP) && + prd) ? prefix_rd2str(prd, buf1, sizeof(buf1), bgp->asnotation) : ""), - safi == SAFI_MPLS_VPN ? ":" : "", p, + safi == SAFI_MPLS_VPN && prd ? ":" : "", p, dest->version); } else { @@ -13005,6 +13199,15 @@ DEFPY(show_ip_bgp, show_ip_bgp_cmd, get_afi_safi_str(afi, safi, true)); + + /* Adding 'routes' key to make + * the json output format valid + * for evpn + */ + if (safi == SAFI_EVPN) + vty_out(vty, + "\"routes\":"); + } else vty_out(vty, "\nFor address family: %s\n", @@ -13346,11 +13549,11 @@ static void bgp_table_stats_rn(struct bgp_dest *dest, struct bgp_dest *top, } } -static void bgp_table_stats_walker(struct thread *t) +static void bgp_table_stats_walker(struct event *t) { struct bgp_dest *dest, *ndest; struct bgp_dest *top; - struct bgp_table_stats *ts = THREAD_ARG(t); + struct bgp_table_stats *ts = EVENT_ARG(t); unsigned int space = 0; if (!(top = bgp_table_top(ts->table))) @@ -13445,7 +13648,7 @@ static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, memset(&ts, 0, sizeof(ts)); ts.table = bgp->rib[afi][safi]; - thread_execute(bm->master, bgp_table_stats_walker, &ts, 0); + event_execute(bm->master, bgp_table_stats_walker, &ts, 0); for (i = 0; i < BGP_STATS_MAX; i++) { if ((!json && !table_stats_strs[i][TABLE_STATS_IDX_VTY]) @@ -13631,11 +13834,11 @@ static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi, json_bitlen = json_object_new_array(); for (i = 0; i <= bitlen; i++) { - struct json_object *ind_bit = json_object_new_object(); - if (!ts.prefix_len_count[i]) continue; + struct json_object *ind_bit = json_object_new_object(); + snprintf(temp_buf, sizeof(temp_buf), "%u", i); json_object_int_add(ind_bit, temp_buf, ts.prefix_len_count[i]); @@ -13743,11 +13946,11 @@ static void bgp_peer_count_proc(struct bgp_dest *rn, struct peer_pcounts *pc) } } -static void bgp_peer_count_walker(struct thread *t) +static void bgp_peer_count_walker(struct event *t) { struct bgp_dest *rn, *rm; const struct bgp_table *table; - struct peer_pcounts *pc = THREAD_ARG(t); + struct peer_pcounts *pc = EVENT_ARG(t); if (pc->safi == SAFI_MPLS_VPN || pc->safi == SAFI_ENCAP || pc->safi == SAFI_EVPN) { @@ -13802,7 +14005,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi, * stats for the thread-walk (i.e. ensure this can't be blamed on * on just vty_read()). */ - thread_execute(bm->master, bgp_peer_count_walker, &pcounts, 0); + event_execute(bm->master, bgp_peer_count_walker, &pcounts, 0); if (use_json) { json_object_string_add(json, "prefixCountsFor", peer->host); @@ -14223,7 +14426,6 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, for (ain = dest->adj_in; ain; ain = ain->next) { if (ain->peer != peer) continue; - show_adj_route_header(vty, peer, table, header1, header2, json, json_scode, json_ocode, wide, detail); @@ -14274,9 +14476,23 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, if (use_json) json_net = json_object_new_object(); + + struct bgp_path_info bpi; + struct bgp_dest buildit = *dest; + struct bgp_dest *pass_in; + + if (route_filtered || + ret == RMAP_DENY) { + bpi.attr = &attr; + bpi.peer = peer; + buildit.info = &bpi; + + pass_in = &buildit; + } else + pass_in = dest; bgp_show_path_info( - NULL /* prefix_rd */, dest, vty, - bgp, afi, safi, json_net, + NULL, pass_in, vty, bgp, afi, + safi, json_net, BGP_PATH_SHOW_ALL, &display, RPKI_NOT_BEING_USED); if (use_json) @@ -14871,35 +15087,42 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer, RPKI_NOT_BEING_USED); } -DEFUN (show_ip_bgp_flowspec_routes_detailed, - show_ip_bgp_flowspec_routes_detailed_cmd, - "show [ip] bgp [ VIEWVRFNAME] ["BGP_AFI_CMD_STR" flowspec] detail [json]", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - BGP_AFI_HELP_STR - "SAFI Flowspec\n" - "Detailed information on flowspec entries\n" - JSON_STR) +/* + * Used for "detailed" output for cmds like show bgp (or) + * show bgp (or) show bgp + */ +DEFPY(show_ip_bgp_vrf_afi_safi_routes_detailed, + show_ip_bgp_vrf_afi_safi_routes_detailed_cmd, + "show [ip] bgp [ VIEWVRFNAME$vrf_name] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] detail [json$uj]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Detailed information\n" + JSON_STR) { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; struct bgp *bgp = NULL; int idx = 0; - bool uj = use_json(argc, argv); uint16_t show_flags = BGP_SHOW_OPT_ROUTES_DETAIL; - if (uj) { - argc--; + if (uj) SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); - } bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi, &bgp, uj); if (!idx) return CMD_WARNING; + /* 'vrf all' case to iterate all vrfs & show output per vrf instance */ + if (vrf_name && strmatch(vrf_name, "all")) { + bgp_show_all_instances_routes_vty(vty, afi, safi, show_flags); + return CMD_SUCCESS; + } + /* All other cases except vrf all */ return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL, show_flags, RPKI_NOT_BEING_USED); } @@ -16015,8 +16238,9 @@ void bgp_route_init(void) install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd); install_element(VIEW_NODE, &show_ip_bgp_large_community_cmd); - /* show bgp ipv4 flowspec detailed */ - install_element(VIEW_NODE, &show_ip_bgp_flowspec_routes_detailed_cmd); + /* show bgp vrf detailed */ + install_element(VIEW_NODE, + &show_ip_bgp_vrf_afi_safi_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 66cc62ab090f..ccfd9d00d8a0 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -182,7 +182,7 @@ struct bgp_path_info_extra { } export; struct { - struct thread *timer; + struct event *timer; void *hme; /* encap monitor, if this is a VPN route */ struct prefix_rd rd; /* import: route's route-distinguisher */ @@ -246,6 +246,22 @@ struct bgp_path_info_extra { struct bgp_path_mh_info *mh_info; }; +struct bgp_mplsvpn_label_nh { + /* For nexthop per label linked list */ + LIST_ENTRY(bgp_path_info) label_nh_thread; + + /* Back pointer to the bgp label per nexthop structure */ + struct bgp_label_per_nexthop_cache *label_nexthop_cache; +}; + +struct bgp_mplsvpn_nh_label_bind { + /* For mplsvpn nexthop label bind linked list */ + LIST_ENTRY(bgp_path_info) nh_label_bind_thread; + + /* Back pointer to the bgp mplsvpn nexthop label bind structure */ + struct bgp_mplsvpn_nh_label_bind_cache *nh_label_bind_cache; +}; + struct bgp_path_info { /* For linked list. */ struct bgp_path_info *next; @@ -298,6 +314,8 @@ struct bgp_path_info { #define BGP_PATH_ANNC_NH_SELF (1 << 14) #define BGP_PATH_LINK_BW_CHG (1 << 15) #define BGP_PATH_ACCEPT_OWN (1 << 16) +#define BGP_PATH_MPLSVPN_LABEL_NH (1 << 17) +#define BGP_PATH_MPLSVPN_NH_LABEL_BIND (1 << 18) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ uint8_t type; @@ -319,6 +337,11 @@ struct bgp_path_info { /* Addpath identifiers */ uint32_t addpath_rx_id; struct bgp_addpath_info_data tx_addpath; + + union { + struct bgp_mplsvpn_label_nh blnc; + struct bgp_mplsvpn_nh_label_bind bmnc; + } mplsvpn; }; /* Structure used in BGP path selection */ @@ -586,8 +609,12 @@ static inline void prep_for_rmap_apply(struct bgp_path_info *dst_pi, } } -static inline bool bgp_check_advertise(struct bgp *bgp, struct bgp_dest *dest) +static inline bool bgp_check_advertise(struct bgp *bgp, struct bgp_dest *dest, + safi_t safi) { + if (!bgp_fibupd_safi(safi)) + return true; + return (!(BGP_SUPPRESS_FIB_ENABLED(bgp) && CHECK_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING) && (!bgp_option_check(BGP_OPT_NO_FIB)))); @@ -599,11 +626,12 @@ static inline bool bgp_check_advertise(struct bgp *bgp, struct bgp_dest *dest) * This function assumes that bgp_check_advertise was already returned * as good to go. */ -static inline bool bgp_check_withdrawal(struct bgp *bgp, struct bgp_dest *dest) +static inline bool bgp_check_withdrawal(struct bgp *bgp, struct bgp_dest *dest, + safi_t safi) { struct bgp_path_info *pi, *selected = NULL; - if (!BGP_SUPPRESS_FIB_ENABLED(bgp)) + if (!bgp_fibupd_safi(safi) || !BGP_SUPPRESS_FIB_ENABLED(bgp)) return false; for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { @@ -661,6 +689,7 @@ extern void bgp_process_queue_init(struct bgp *bgp); extern void bgp_route_init(void); extern void bgp_route_finish(void); extern void bgp_cleanup_routes(struct bgp *); +extern void bgp_free_aggregate_info(struct bgp_aggregate *aggregate); extern void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi, bool force); extern void bgp_stop_announce_route_timer(struct peer_af *paf); @@ -688,6 +717,8 @@ extern struct bgp_dest *bgp_afi_node_get(struct bgp_table *table, afi_t afi, 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 bool bgp_path_info_nexthop_changed(struct bgp_path_info *pi, + struct peer *to, afi_t afi); extern struct bgp_path_info * bgp_get_imported_bpi_ultimate(struct bgp_path_info *info); extern void bgp_path_info_add(struct bgp_dest *dest, struct bgp_path_info *pi); @@ -766,7 +797,7 @@ extern void bgp_config_write_distance(struct vty *, struct bgp *, afi_t, 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, +extern bool 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, @@ -806,7 +837,8 @@ extern void bgp_notify_conditional_adv_scanner(struct update_subgroup *subgrp); extern void subgroup_process_announce_selected(struct update_subgroup *subgrp, struct bgp_path_info *selected, - struct bgp_dest *dest, + struct bgp_dest *dest, afi_t afi, + safi_t safi, uint32_t addpath_tx_id); extern bool subgroup_announce_check(struct bgp_dest *dest, @@ -819,9 +851,10 @@ 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_dest *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi, - safi_t safi, const struct prefix *p, - struct prefix_rd *prd); +extern struct bgp_dest *bgp_safi_node_lookup(struct bgp_table *table, + safi_t safi, + const struct prefix *p, + struct prefix_rd *prd); extern void bgp_path_info_restore(struct bgp_dest *dest, struct bgp_path_info *path); @@ -876,6 +909,12 @@ extern void bgp_path_info_free_with_caller(const char *caller, extern void bgp_path_info_add_with_caller(const char *caller, struct bgp_dest *dest, struct bgp_path_info *pi); +extern void bgp_aggregate_free(struct bgp_aggregate *aggregate); +extern 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, + enum bgp_path_selection_reason *reason); #define bgp_path_info_add(A, B) \ bgp_path_info_add_with_caller(__func__, (A), (B)) #define bgp_path_info_free(B) bgp_path_info_free_with_caller(__func__, (B)) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 8fd46d00af77..c0365d8e3b83 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -461,7 +461,8 @@ route_match_ip_address(void *rule, const struct prefix *prefix, void *object) if (prefix->family == AF_INET) { alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -521,7 +522,8 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -581,7 +583,8 @@ route_match_ip_route_source(void *rule, const struct prefix *pfx, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -676,7 +679,7 @@ route_match_address_prefix_list(void *rule, afi_t afi, plist = prefix_list_lookup(afi, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -737,7 +740,8 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -786,7 +790,8 @@ route_match_ipv6_next_hop_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP6, (char *)rule); if (!plist) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -867,6 +872,46 @@ static const struct route_map_rule_cmd route_match_ip_next_hop_type_free }; +/* `match source-protocol` */ +static enum route_map_cmd_result_t +route_match_source_protocol(void *rule, const struct prefix *prefix, + void *object) +{ + struct bgp_path_info *path = object; + int *protocol = rule; + + if (!path) + return RMAP_NOMATCH; + + if (path->type == *protocol) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void *route_match_source_protocol_compile(const char *arg) +{ + int *protocol; + + protocol = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*protocol)); + *protocol = proto_name2num(arg); + + return protocol; +} + +static void route_match_source_protocol_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +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 +}; + + /* `match ip route-source prefix-list PREFIX_LIST' */ static enum route_map_cmd_result_t @@ -891,7 +936,8 @@ route_match_ip_route_source_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -956,7 +1002,7 @@ route_match_mac_address(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_L2VPN, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -964,7 +1010,7 @@ route_match_mac_address(void *rule, const struct prefix *prefix, void *object) return RMAP_NOMATCH; } if (prefix->u.prefix_evpn.route_type != BGP_EVPN_MAC_IP_ROUTE) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix %pFX is not a EVPN MAC IP ROUTE defaulting to NO_MATCH", __func__, prefix); @@ -2254,6 +2300,31 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = { }; /* `set as-path exclude ASn' */ +struct aspath_exclude { + struct aspath *aspath; + bool exclude_all; +}; + +static void *route_aspath_exclude_compile(const char *arg) +{ + struct aspath_exclude *ase; + const char *str = arg; + + ase = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct aspath_exclude)); + if (!strmatch(str, "all")) + ase->aspath = aspath_str2aspath(str, bgp_get_asnotation(NULL)); + else + ase->exclude_all = true; + return ase; +} + +static void route_aspath_exclude_free(void *rule) +{ + struct aspath_exclude *ase = rule; + + aspath_free(ase->aspath); + XFREE(MTYPE_ROUTE_MAP_COMPILED, ase); +} /* For ASN exclude mechanism. * Iterate over ASns requested and filter them from the given AS_PATH one by @@ -2263,16 +2334,28 @@ static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = { static enum route_map_cmd_result_t route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object) { - struct aspath *new_path, *exclude_path; + struct aspath *new_path; struct bgp_path_info *path; + struct aspath_exclude *ase = rule; - exclude_path = rule; path = object; + + if (path->peer->sort != BGP_PEER_EBGP) { + zlog_warn( + "`set as-path exclude` is supported only for EBGP peers"); + return RMAP_NOOP; + } + if (path->attr->aspath->refcnt) new_path = aspath_dup(path->attr->aspath); else new_path = path->attr->aspath; - path->attr->aspath = aspath_filter_exclude(new_path, exclude_path); + + if (ase->aspath) + path->attr->aspath = + aspath_filter_exclude(new_path, ase->aspath); + else if (ase->exclude_all) + path->attr->aspath = aspath_filter_exclude_all(new_path); return RMAP_OKAY; } @@ -2281,8 +2364,8 @@ route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object) 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, + route_aspath_exclude_compile, + route_aspath_exclude_free, }; /* `set as-path replace AS-PATH` */ @@ -2302,8 +2385,10 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object) struct aspath *aspath_new; const char *replace = rule; struct bgp_path_info *path = object; - as_t own_asn = path->peer->change_local_as ? path->peer->change_local_as - : path->peer->local_as; + as_t replace_asn = 0; + as_t configured_asn; + char *buf; + char src_asn[ASN_STRING_MAX_SIZE]; if (path->peer->sort != BGP_PEER_EBGP) { zlog_warn( @@ -2311,6 +2396,29 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object) return RMAP_NOOP; } + buf = strchr(replace, ' '); + if (!buf) { + configured_asn = path->peer->change_local_as + ? path->peer->change_local_as + : path->peer->local_as; + } else { + memcpy(src_asn, replace, (size_t)(buf - replace)); + src_asn[(size_t)(buf - replace)] = '\0'; + replace = src_asn; + buf++; + if (!asn_str2asn(buf, &configured_asn)) { + zlog_warn( + "`set as-path replace`, invalid configured AS %s", + buf); + return RMAP_NOOP; + } + } + + if (!strmatch(replace, "any") && !asn_str2asn(replace, &replace_asn)) { + zlog_warn("`set as-path replace`, invalid AS %s", replace); + return RMAP_NOOP; + } + if (path->attr->aspath->refcnt) aspath_new = aspath_dup(path->attr->aspath); else @@ -2318,13 +2426,10 @@ route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object) if (strmatch(replace, "any")) { path->attr->aspath = - aspath_replace_all_asn(aspath_new, own_asn); - } else { - as_t replace_asn = strtoul(replace, NULL, 10); - + aspath_replace_all_asn(aspath_new, configured_asn); + } else path->attr->aspath = aspath_replace_specific_asn( - aspath_new, replace_asn, own_asn); - } + aspath_new, replace_asn, configured_asn); aspath_free(aspath_new); @@ -2861,6 +2966,29 @@ static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = { route_set_ecommunity_free, }; +static void *route_set_ecommunity_nt_compile(const char *arg) +{ + struct rmap_ecom_set *rcs; + struct ecommunity *ecom; + + ecom = ecommunity_str2com(arg, ECOMMUNITY_NODE_TARGET, 0); + if (!ecom) + return NULL; + + rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set)); + rcs->ecom = ecommunity_intern(ecom); + rcs->none = false; + + return rcs; +} + +static const struct route_map_rule_cmd route_set_ecommunity_nt_cmd = { + "extcommunity nt", + route_set_ecommunity, + route_set_ecommunity_nt_compile, + route_set_ecommunity_free, +}; + /* `set extcommunity bandwidth' */ struct rmap_ecomm_lb_set { @@ -2986,6 +3114,44 @@ static void *route_set_ecommunity_lb_compile(const char *arg) return rels; } +static enum route_map_cmd_result_t +route_set_ecommunity_color(void *rule, const struct prefix *prefix, + void *object) +{ + struct bgp_path_info *path; + + path = object; + + route_set_ecommunity(rule, prefix, object); + + path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR); + return RMAP_OKAY; +} + +static void *route_set_ecommunity_color_compile(const char *arg) +{ + struct rmap_ecom_set *rcs; + struct ecommunity *ecom; + + ecom = ecommunity_str2com(arg, ECOMMUNITY_COLOR, 0); + if (!ecom) + return NULL; + + rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set)); + rcs->ecom = ecommunity_intern(ecom); + rcs->none = false; + + return rcs; +} + +static const struct route_map_rule_cmd route_set_ecommunity_color_cmd = { + "extcommunity color", + route_set_ecommunity_color, + route_set_ecommunity_color_compile, + route_set_ecommunity_free, +}; + + static void route_set_ecommunity_lb_free(void *rule) { XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); @@ -3242,7 +3408,8 @@ route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object) if (prefix->family == AF_INET6) { alist = access_list_lookup(AFI_IP6, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -3299,7 +3466,8 @@ route_match_ipv6_next_hop(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP6, (char *)rule); if (!alist) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -4024,10 +4192,7 @@ static void bgp_route_map_process_peer(const char *rmap_name, safi2str(safi), 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)) { + } else if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV)) { if (bgp_debug_update(peer, NULL, NULL, 1)) zlog_debug( "Processing route_map %s(%s:%s) update on peer %s (inbound, route-refresh)", @@ -4266,8 +4431,8 @@ static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name, inet_ntop(bn_p->family, &bn_p->u.prefix, buf, sizeof(buf))); - bgp_aggregate_route(bgp, bn_p, afi, safi, - aggregate); + (void)bgp_aggregate_route(bgp, bn_p, afi, safi, + aggregate); } } } @@ -4344,7 +4509,7 @@ static void bgp_route_map_process_update_cb(char *rmap_name) vpn_policy_routemap_event(rmap_name); } -void bgp_route_map_update_timer(struct thread *thread) +void bgp_route_map_update_timer(struct event *thread) { route_map_walk_update_list(bgp_route_map_process_update_cb); } @@ -4357,13 +4522,12 @@ static void bgp_route_map_mark_update(const char *rmap_name) /* If new update is received before the current timer timed out, * turn it off and start a new timer. */ - THREAD_OFF(bm->t_rmap_update); + EVENT_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); + event_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 */ @@ -5512,7 +5676,7 @@ DEFUN_YANG (set_ip_nexthop_unchanged, DEFUN_YANG (set_distance, set_distance_cmd, - "set distance (0-255)", + "set distance (1-255)", SET_STR "BGP Administrative Distance to use\n" "Distance value\n") @@ -5531,7 +5695,7 @@ DEFUN_YANG (set_distance, DEFUN_YANG (no_set_distance, no_set_distance_cmd, - "no set distance [(0-255)]", + "no set distance [(1-255)]", NO_STR SET_STR "BGP Administrative Distance to use\n" "Distance value\n") @@ -5730,41 +5894,48 @@ DEFUN_YANG (set_aspath_prepend_lastas, return nb_cli_apply_changes(vty, NULL); } -DEFPY_YANG (set_aspath_replace_asn, - set_aspath_replace_asn_cmd, - "set as-path replace $replace", - SET_STR - "Transform BGP AS_PATH attribute\n" - "Replace AS number to local AS number\n" - "Replace any AS number to local AS number\n" - "Replace a specific AS number in plain or dotted format to local AS number\n") +DEFPY_YANG(set_aspath_replace_asn, set_aspath_replace_asn_cmd, + "set as-path replace $replace [$configured_asn]", + SET_STR + "Transform BGP AS_PATH attribute\n" + "Replace AS number to local or configured AS number\n" + "Replace any AS number to local or configured AS number\n" + "Replace a specific AS number to local or configured AS number\n" + "Define the configured AS number\n") { const char *xpath = "./set-action[action='frr-bgp-route-map:as-path-replace']"; char xpath_value[XPATH_MAXLEN]; - as_t as_value; + as_t as_value, as_configured_value; + char replace_value[ASN_STRING_MAX_SIZE * 2]; if (!strmatch(replace, "any") && !asn_str2asn(replace, &as_value)) { vty_out(vty, "%% Invalid AS value %s\n", replace); return CMD_WARNING_CONFIG_FAILED; } - - nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + if (configured_asn_str && + !asn_str2asn(configured_asn_str, &as_configured_value)) { + vty_out(vty, "%% Invalid AS configured value %s\n", + configured_asn_str); + return CMD_WARNING_CONFIG_FAILED; + } snprintf(xpath_value, sizeof(xpath_value), "%s/rmap-set-action/frr-bgp-route-map:replace-as-path", xpath); - nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, replace); + snprintf(replace_value, sizeof(replace_value), "%s%s%s", replace, + configured_asn_str ? " " : "", + configured_asn_str ? configured_asn_str : ""); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, replace_value); return nb_cli_apply_changes(vty, NULL); } -DEFPY_YANG (no_set_aspath_replace_asn, - no_set_aspath_replace_asn_cmd, - "no set as-path replace []", - NO_STR - SET_STR - "Transform BGP AS_PATH attribute\n" - "Replace AS number to local AS number\n" - "Replace any AS number to local AS number\n" - "Replace a specific AS number in plain or dotted format to local AS number\n") +DEFPY_YANG(no_set_aspath_replace_asn, no_set_aspath_replace_asn_cmd, + "no set as-path replace [] [$configured_asn]", + NO_STR SET_STR + "Transform BGP AS_PATH attribute\n" + "Replace AS number to local or configured AS number\n" + "Replace any AS number to local or configured AS number\n" + "Replace a specific AS number to local or configured AS number\n" + "Define the configured AS number\n") { const char *xpath = "./set-action[action='frr-bgp-route-map:as-path-replace']"; @@ -5840,6 +6011,32 @@ DEFUN_YANG (set_aspath_exclude, return ret; } +DEFPY_YANG(set_aspath_exclude_all, set_aspath_exclude_all_cmd, + "[no$no] set as-path exclude all$all", + NO_STR SET_STR + "Transform BGP AS-path attribute\n" + "Exclude from the as-path\n" + "Exclude all AS numbers from the as-path\n") +{ + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:as-path-exclude']"; + char xpath_value[XPATH_MAXLEN]; + + if (no) + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + else { + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:exclude-as-path", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, all); + } + ret = nb_cli_apply_changes(vty, NULL); + + return ret; +} + DEFUN_YANG (no_set_aspath_exclude, no_set_aspath_exclude_cmd, "no set as-path exclude ASNUM...", @@ -6411,6 +6608,106 @@ ALIAS_YANG (no_set_ecommunity_lb, "BGP extended community attribute\n" "Link bandwidth extended community\n") +DEFPY_YANG (set_ecommunity_nt, + set_ecommunity_nt_cmd, + "set extcommunity nt RTLIST...", + SET_STR + "BGP extended community attribute\n" + "Node Target extended community\n" + "Node Target ID\n") +{ + int idx_nt = 3; + char *str; + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-nt']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-nt", xpath); + str = argv_concat(argv, argc, idx_nt); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, str); + return ret; +} + +DEFPY_YANG (no_set_ecommunity_nt, + no_set_ecommunity_nt_cmd, + "no set extcommunity nt RTLIST...", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Node Target extended community\n" + "Node Target ID\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-nt']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(set_ecommunity_color, set_ecommunity_color_cmd, + "set extcommunity color RTLIST...", + SET_STR + "BGP extended community attribute\n" + "Color extended community\n" + "Color ID\n") +{ + int idx_color = 3; + char *str; + int ret; + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-color']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/frr-bgp-route-map:extcommunity-color", + xpath); + str = argv_concat(argv, argc, idx_color); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str); + ret = nb_cli_apply_changes(vty, NULL); + XFREE(MTYPE_TMP, str); + return ret; +} + +DEFPY_YANG(no_set_ecommunity_color_all, no_set_ecommunity_color_all_cmd, + "no set extcommunity color", + NO_STR SET_STR + "BGP extended community attribute\n" + "Color extended community\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-color']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_set_ecommunity_color, no_set_ecommunity_color_cmd, + "no set extcommunity color RTLIST...", + NO_STR SET_STR + "BGP extended community attribute\n" + "Color extended community\n" + "Color ID\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-extcommunity-color']"; + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +ALIAS_YANG (no_set_ecommunity_nt, + no_set_ecommunity_nt_short_cmd, + "no set extcommunity nt", + NO_STR + SET_STR + "BGP extended community attribute\n" + "Node Target extended community\n") + DEFUN_YANG (set_origin, set_origin_cmd, "set origin ", @@ -7098,6 +7395,42 @@ DEFPY_YANG (match_rpki_extcommunity, return nb_cli_apply_changes(vty, NULL); } +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='frr-bgp-route-map:source-protocol']"; + char xpath_value[XPATH_MAXLEN]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-match-condition/frr-bgp-route-map:source-protocol", + xpath); + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, proto); + + return nb_cli_apply_changes(vty, NULL); +} + +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='frr-bgp-route-map:source-protocol']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} + /* Initialization of route map. */ void bgp_route_map_init(void) { @@ -7173,6 +7506,7 @@ void bgp_route_map_init(void) route_map_install_match(&route_match_ip_address_prefix_list_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_source_protocol_cmd); route_map_install_match(&route_match_ip_route_source_prefix_list_cmd); route_map_install_match(&route_match_aspath_cmd); route_map_install_match(&route_match_community_cmd); @@ -7216,8 +7550,10 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_vpnv6_nexthop_cmd); 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_nt_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_ecommunity_color_cmd); route_map_install_set(&route_set_ecommunity_none_cmd); route_map_install_set(&route_set_tag_cmd); route_map_install_set(&route_set_label_index_cmd); @@ -7279,6 +7615,7 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_aspath_prepend_asn_cmd); install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd); install_element(RMAP_NODE, &set_aspath_exclude_cmd); + install_element(RMAP_NODE, &set_aspath_exclude_all_cmd); install_element(RMAP_NODE, &set_aspath_replace_asn_cmd); install_element(RMAP_NODE, &no_set_aspath_prepend_cmd); install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_cmd); @@ -7318,6 +7655,12 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd); install_element(RMAP_NODE, &set_ecommunity_none_cmd); install_element(RMAP_NODE, &no_set_ecommunity_none_cmd); + install_element(RMAP_NODE, &set_ecommunity_nt_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_nt_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_nt_short_cmd); + install_element(RMAP_NODE, &set_ecommunity_color_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_color_cmd); + install_element(RMAP_NODE, &no_set_ecommunity_color_all_cmd); #ifdef KEEP_OLD_VPN_COMMANDS install_element(RMAP_NODE, &set_vpn_nexthop_cmd); install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd); @@ -7358,6 +7701,8 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); install_element(RMAP_NODE, &match_rpki_extcommunity_cmd); + install_element(RMAP_NODE, &match_source_protocol_cmd); + install_element(RMAP_NODE, &no_match_source_protocol_cmd); #ifdef HAVE_SCRIPTING install_element(RMAP_NODE, &match_script_cmd); #endif diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 9188a40cc94a..ae695a6f80a1 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -74,6 +74,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol", + .cbs = { + .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify, + .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address", .cbs = { @@ -185,6 +192,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo", .cbs = { @@ -386,6 +400,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-color", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific", .cbs = { diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index 07a1a6eb57fb..3ff58f71a733 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -30,6 +30,10 @@ int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_m struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy( struct nb_cb_destroy_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(struct nb_cb_modify_args *args); @@ -69,6 +73,10 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_modify(struct nb_cb_ int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args); @@ -145,6 +153,10 @@ int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy( + struct nb_cb_destroy_args *args); int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify( struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy( diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 142bded6c536..03b588a33b1d 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -367,6 +367,60 @@ lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy( return NB_OK; } +/* + * XPath: + * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-protocol + */ +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + enum rmap_compile_rets ret; + const char *proto; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + proto = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_mhook = bgp_route_match_delete; + rhc->rhc_rule = "source-protocol"; + rhc->rhc_event = RMAP_EVENT_MATCH_DELETED; + + ret = bgp_route_match_add(rhc->rhc_rmi, "source-protocol", + proto, RMAP_EVENT_MATCH_ADDED, + args->errmsg, args->errmsg_len); + + if (ret != RMAP_COMPILE_SUCCESS) { + rhc->rhc_mhook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_match_condition_rmap_match_condition_source_protocol_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: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + /* * XPath: * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity @@ -1413,6 +1467,58 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy( return NB_OK; } +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-nt + */ +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *str; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + str = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "extcommunity nt"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "extcommunity nt", str, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_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: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo @@ -2628,8 +2734,18 @@ int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify( struct nb_cb_modify_args *args) { + const char *asn; + enum match_type match; + switch (args->event) { case NB_EV_VALIDATE: + asn = yang_dnode_get_string(args->dnode, NULL); + if (!asn) + return NB_ERR_VALIDATION; + match = asn_str2asn_match(asn); + if (match == exact_match) + return NB_OK; + return NB_ERR_VALIDATION; case NB_EV_PREPARE: case NB_EV_ABORT: case NB_EV_APPLY: @@ -2836,6 +2952,58 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy return lib_route_map_entry_set_destroy(args); } +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-color + */ +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *str; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + str = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "extcommunity color"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, "extcommunity color", str, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_extcommunity_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: + return lib_route_map_entry_match_destroy(args); + } + + return NB_OK; +} + /* * XPath: * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 32ca909fe614..c6e3131e02fa 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -23,7 +23,7 @@ #include "command.h" #include "linklist.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -36,7 +36,6 @@ #include "northbound_cli.h" #include "lib/network.h" -#include "lib/thread.h" #include "rtrlib/rtrlib.h" #include "hook.h" #include "libfrr.h" @@ -54,7 +53,7 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_REVALIDATE, "BGP RPKI Revalidation"); #define RETRY_INTERVAL_DEFAULT 600 #define BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT 3 -static struct thread *t_rpki_sync; +static struct event *t_rpki_sync; #define RPKI_DEBUG(...) \ if (rpki_debug) { \ @@ -382,9 +381,9 @@ struct rpki_revalidate_prefix { safi_t safi; }; -static void rpki_revalidate_prefix(struct thread *thread) +static void rpki_revalidate_prefix(struct event *thread) { - struct rpki_revalidate_prefix *rrp = THREAD_ARG(thread); + struct rpki_revalidate_prefix *rrp = EVENT_ARG(thread); struct bgp_dest *match, *node; match = bgp_table_subtree_lookup(rrp->bgp->rib[rrp->afi][rrp->safi], @@ -403,15 +402,15 @@ static void rpki_revalidate_prefix(struct thread *thread) XFREE(MTYPE_BGP_RPKI_REVALIDATE, rrp); } -static void bgpd_sync_callback(struct thread *thread) +static void bgpd_sync_callback(struct event *thread) { struct bgp *bgp; struct listnode *node; struct prefix prefix; struct pfx_record rec; - thread_add_read(bm->master, bgpd_sync_callback, NULL, - rpki_sync_socket_bgpd, NULL); + event_add_read(bm->master, bgpd_sync_callback, NULL, + rpki_sync_socket_bgpd, NULL); if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) { while (read(rpki_sync_socket_bgpd, &rec, @@ -449,8 +448,8 @@ static void bgpd_sync_callback(struct thread *thread) rrp->prefix = prefix; rrp->afi = afi; rrp->safi = safi; - thread_add_event(bm->master, rpki_revalidate_prefix, - rrp, 0, &bgp->t_revalidate[afi][safi]); + event_add_event(bm->master, rpki_revalidate_prefix, rrp, + 0, &bgp->t_revalidate[afi][safi]); } } } @@ -490,9 +489,9 @@ struct rpki_revalidate_peer { struct peer *peer; }; -static void bgp_rpki_revalidate_peer(struct thread *thread) +static void bgp_rpki_revalidate_peer(struct event *thread) { - struct rpki_revalidate_peer *rvp = THREAD_ARG(thread); + struct rpki_revalidate_peer *rvp = EVENT_ARG(thread); /* * Here's the expensive bit of gnomish deviousness @@ -530,7 +529,7 @@ static void revalidate_all_routes(void) rvp->afi = afi; rvp->safi = safi; - thread_add_event( + event_add_event( bm->master, bgp_rpki_revalidate_peer, rvp, 0, &peer->t_revalidate_all[afi][safi]); @@ -581,8 +580,8 @@ static void rpki_init_sync_socket(void) } - thread_add_read(bm->master, bgpd_sync_callback, NULL, - rpki_sync_socket_bgpd, NULL); + event_add_read(bm->master, bgpd_sync_callback, NULL, + rpki_sync_socket_bgpd, NULL); return; @@ -592,7 +591,7 @@ static void rpki_init_sync_socket(void) } -static int bgp_rpki_init(struct thread_master *master) +static int bgp_rpki_init(struct event_loop *master) { rpki_debug = false; rtr_is_running = false; @@ -632,13 +631,13 @@ static int bgp_rpki_module_init(void) return 0; } -static void sync_expired(struct thread *thread) +static void sync_expired(struct event *thread) { if (!rtr_mgr_conf_in_sync(rtr_config)) { RPKI_DEBUG("rtr_mgr is not synced, retrying."); - thread_add_timer(bm->master, sync_expired, NULL, - BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT, - &t_rpki_sync); + event_add_timer(bm->master, sync_expired, NULL, + BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT, + &t_rpki_sync); return; } @@ -681,7 +680,7 @@ static int start(void) return ERROR; } - thread_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync); + event_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync); XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups); @@ -694,7 +693,7 @@ static void stop(void) { rtr_is_stopping = true; if (is_running()) { - THREAD_OFF(t_rpki_sync); + EVENT_OFF(t_rpki_sync); rtr_mgr_stop(rtr_config); rtr_mgr_free(rtr_config); rtr_is_running = false; diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 5aa5e142889c..ce9442c1e020 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -12,7 +12,7 @@ #include "log.h" #include "prefix.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "smux.h" #include "filter.h" #include "hook.h" @@ -30,7 +30,7 @@ #include "bgpd/bgp_snmp_bgp4v2.h" #include "bgpd/bgp_mplsvpn_snmp.h" -static int bgp_snmp_init(struct thread_master *tm) +static int bgp_snmp_init(struct event_loop *tm) { smux_init(tm); bgp_snmp_bgp4_init(tm); diff --git a/bgpd/bgp_snmp_bgp4.c b/bgpd/bgp_snmp_bgp4.c index 186c9e28463a..123d33bd1430 100644 --- a/bgpd/bgp_snmp_bgp4.c +++ b/bgpd/bgp_snmp_bgp4.c @@ -12,7 +12,7 @@ #include "log.h" #include "prefix.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "smux.h" #include "filter.h" #include "hook.h" @@ -791,7 +791,7 @@ int bgpTrapBackwardTransition(struct peer *peer) return 0; } -int bgp_snmp_bgp4_init(struct thread_master *tm) +int bgp_snmp_bgp4_init(struct event_loop *tm) { REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid); return 0; diff --git a/bgpd/bgp_snmp_bgp4.h b/bgpd/bgp_snmp_bgp4.h index dffbf5fecf21..ccf00d6b7c7e 100644 --- a/bgpd/bgp_snmp_bgp4.h +++ b/bgpd/bgp_snmp_bgp4.h @@ -71,6 +71,6 @@ extern int bgpTrapEstablished(struct peer *peer); extern int bgpTrapBackwardTransition(struct peer *peer); -extern int bgp_snmp_bgp4_init(struct thread_master *tm); +extern int bgp_snmp_bgp4_init(struct event_loop *tm); #endif /* _FRR_BGP_SNMP_BGP4_H_ */ diff --git a/bgpd/bgp_snmp_bgp4v2.c b/bgpd/bgp_snmp_bgp4v2.c index 9c2599d5f470..712b5d4af877 100644 --- a/bgpd/bgp_snmp_bgp4v2.c +++ b/bgpd/bgp_snmp_bgp4v2.c @@ -13,7 +13,7 @@ #include "log.h" #include "prefix.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "smux.h" #include "filter.h" #include "hook.h" @@ -1394,7 +1394,7 @@ static struct variable bgpv2_variables[] = { {1, 9, 1, BGP4V2_NLRI_PATH_ATTR_UNKNOWN, 2, 16}}, }; -int bgp_snmp_bgp4v2_init(struct thread_master *tm) +int bgp_snmp_bgp4v2_init(struct event_loop *tm) { REGISTER_MIB("mibII/bgpv2", bgpv2_variables, variable, bgpv2_oid); return 0; diff --git a/bgpd/bgp_snmp_bgp4v2.h b/bgpd/bgp_snmp_bgp4v2.h index 67f45a6173f8..7cdad586bc91 100644 --- a/bgpd/bgp_snmp_bgp4v2.h +++ b/bgpd/bgp_snmp_bgp4v2.h @@ -81,6 +81,6 @@ #define BGP4V2_ESTABLISHED_NOTIFICATION 1 #define BGP4V2_BACKWARD_TRANSITION_NOTIFICATION 2 -extern int bgp_snmp_bgp4v2_init(struct thread_master *tm); +extern int bgp_snmp_bgp4v2_init(struct event_loop *tm); #endif /* _FRR_BGP_SNMP_BGP4V2_H_ */ diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 997efee9f336..91941315f706 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -29,7 +29,7 @@ struct bgp_table { /* soft_reconfig_table in progress */ bool soft_reconfig_init; - struct thread *soft_reconfig_thread; + struct event *soft_reconfig_thread; /* list of peers on which soft_reconfig_table has to run */ struct list *soft_reconfig_peers; @@ -100,6 +100,7 @@ struct bgp_node { #define BGP_NODE_FIB_INSTALLED (1 << 6) #define BGP_NODE_LABEL_REQUESTED (1 << 7) #define BGP_NODE_SOFT_RECONFIG (1 << 8) +#define BGP_NODE_PROCESS_CLEAR (1 << 9) struct bgp_addpath_node_data tx_addpath; diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index 277492157fdf..849f6699329f 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -12,7 +12,7 @@ #include #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "stream.h" #include "command.h" @@ -101,12 +101,9 @@ static void sync_init(struct update_subgroup *subgrp, static void sync_delete(struct update_subgroup *subgrp) { XFREE(MTYPE_BGP_SYNCHRONISE, subgrp->sync); - if (subgrp->hash) { - hash_clean(subgrp->hash, - (void (*)(void *))bgp_advertise_attr_free); - hash_free(subgrp->hash); - } - subgrp->hash = NULL; + hash_clean_and_free(&subgrp->hash, + (void (*)(void *))bgp_advertise_attr_free); + if (subgrp->work) stream_free(subgrp->work); subgrp->work = NULL; @@ -145,6 +142,8 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi, dst->afc_nego[afi][safi] = src->afc_nego[afi][safi]; dst->orf_plist[afi][safi] = src->orf_plist[afi][safi]; dst->addpath_type[afi][safi] = src->addpath_type[afi][safi]; + dst->addpath_best_selected[afi][safi] = + src->addpath_best_selected[afi][safi]; dst->local_as = src->local_as; dst->change_local_as = src->change_local_as; dst->shared_network = src->shared_network; @@ -310,6 +309,7 @@ static void *updgrp_hash_alloc(void *p) * 16. Local-as should match, if configured. * 17. maximum-prefix-out * 18. Local-role should also match, if configured. + * 19. Add-Path best selected paths count should match as well * ) */ static unsigned int updgrp_hash_key_make(const void *p) @@ -322,6 +322,11 @@ static unsigned int updgrp_hash_key_make(const void *p) afi_t afi; safi_t safi; + /* + * IF YOU ADD AN ADDITION TO THE HASH KEY TO ENSURE + * THAT THE UPDATE GROUP CALCULATION IS CORRECT THEN + * PLEASE ADD IT TO THE DEBUG OUTPUT TOO AT THE BOTTOM + */ #define SEED1 999331 #define SEED2 2147483647 @@ -338,6 +343,7 @@ static unsigned int updgrp_hash_key_make(const void *p) key = jhash_1word((peer->flags & PEER_UPDGRP_FLAGS), key); key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key); key = jhash_1word((uint32_t)peer->addpath_type[afi][safi], key); + key = jhash_1word(peer->addpath_best_selected[afi][safi], key); key = jhash_1word((peer->cap & PEER_UPDGRP_CAP_FLAGS), key); key = jhash_1word((peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS), key); @@ -414,8 +420,6 @@ static unsigned int updgrp_hash_key_make(const void *p) */ 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) || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT)) key = jhash_1word(jhash(peer->host, strlen(peer->host), SEED2), key); @@ -436,6 +440,10 @@ static unsigned int updgrp_hash_key_make(const void *p) key = jhash_1word(jhash(soo_str, strlen(soo_str), SEED1), key); } + /* + * ANY NEW ITEMS THAT ARE ADDED TO THE key, ENSURE DEBUG + * STATEMENT STAYS UP TO DATE + */ if (bgp_debug_neighbor_events(peer)) { zlog_debug( "%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %ju", @@ -457,7 +465,7 @@ static unsigned int updgrp_hash_key_make(const void *p) ROUTE_MAP_OUT_NAME(filter) ? ROUTE_MAP_OUT_NAME(filter) : "(NONE)"); zlog_debug( - "%pBP Update Group Hash: dlist out: %s plist out: %s aslist out: %s usmap out: %s advmap: %s", + "%pBP Update Group Hash: dlist out: %s plist out: %s aslist out: %s usmap out: %s advmap: %s %d", peer, DISTRIBUTE_OUT_NAME(filter) ? DISTRIBUTE_OUT_NAME(filter) @@ -472,7 +480,8 @@ static unsigned int updgrp_hash_key_make(const void *p) ? UNSUPPRESS_MAP_NAME(filter) : "(NONE)", ADVERTISE_MAP_NAME(filter) ? ADVERTISE_MAP_NAME(filter) - : "(NONE)"); + : "(NONE)", + filter->advmap.update_type); zlog_debug( "%pBP Update Group Hash: default rmap: %s shared network and afi active network: %d", peer, @@ -481,15 +490,19 @@ static unsigned int updgrp_hash_key_make(const void *p) : "(NONE)", peer->shared_network && peer_afi_active_nego(peer, AFI_IP6)); + zlog_debug("%pBP Update Group Hash: Lonesoul: %d ORF prefix: %u max prefix out: %ju", + peer, !!CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL), + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_RCV), + (intmax_t)CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_OUT)); zlog_debug( - "%pBP Update Group Hash: Lonesoul: %d ORF prefix: %u ORF old: %u max prefix out: %ju", - peer, !!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), - (intmax_t)CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_OUT)); + "%pBP Update Group Hash: local role: %u AIGP: %d SOO: %s", + peer, peer->local_role, + !!CHECK_FLAG(peer->flags, PEER_FLAG_AIGP), + peer->soo[afi][safi] + ? ecommunity_str(peer->soo[afi][safi]) + : "(NONE)"); zlog_debug("%pBP Update Group Hash key: %u", peer, key); } return key; @@ -619,11 +632,9 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2) if ((afi == AFI_IP6) && (pe1->shared_network != pe2->shared_network)) return false; - if ((CHECK_FLAG(pe1->flags, PEER_FLAG_LONESOUL) - || CHECK_FLAG(pe1->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV) - || CHECK_FLAG(pe1->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_OLD_RCV)) - && !sockunion_same(&pe1->su, &pe2->su)) + if ((CHECK_FLAG(pe1->flags, PEER_FLAG_LONESOUL) || + CHECK_FLAG(pe1->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)) && + !sockunion_same(&pe1->su, &pe2->su)) return false; return true; @@ -1085,8 +1096,8 @@ static void update_subgroup_delete(struct update_subgroup *subgrp) if (subgrp->update_group) UPDGRP_INCR_STAT(subgrp->update_group, subgrps_deleted); - THREAD_OFF(subgrp->t_merge_check); - THREAD_OFF(subgrp->t_coalesce); + EVENT_OFF(subgrp->t_merge_check); + EVENT_OFF(subgrp->t_coalesce); bpacket_queue_cleanup(SUBGRP_PKTQ(subgrp)); subgroup_clear_table(subgrp); @@ -1404,11 +1415,11 @@ bool update_subgroup_check_merge(struct update_subgroup *subgrp, /* * update_subgroup_merge_check_thread_cb */ -static void update_subgroup_merge_check_thread_cb(struct thread *thread) +static void update_subgroup_merge_check_thread_cb(struct event *thread) { struct update_subgroup *subgrp; - subgrp = THREAD_ARG(thread); + subgrp = EVENT_ARG(thread); subgrp->t_merge_check = NULL; @@ -1435,8 +1446,8 @@ bool update_subgroup_trigger_merge_check(struct update_subgroup *subgrp, return false; subgrp->t_merge_check = NULL; - thread_add_timer_msec(bm->master, update_subgroup_merge_check_thread_cb, - subgrp, 0, &subgrp->t_merge_check); + event_add_timer_msec(bm->master, update_subgroup_merge_check_thread_cb, + subgrp, 0, &subgrp->t_merge_check); SUBGRP_INCR_STAT(subgrp, merge_checks_triggered); @@ -1947,15 +1958,8 @@ void update_group_adjust_peer(struct peer_af *paf) } updgrp = update_group_find(paf); - if (!updgrp) { + if (!updgrp) updgrp = update_group_create(paf); - if (!updgrp) { - flog_err(EC_BGP_UPDGRP_CREATE, - "couldn't create update group for peer %s", - paf->peer->host); - return; - } - } old_subgrp = paf->subgroup; @@ -1978,11 +1982,8 @@ void update_group_adjust_peer(struct peer_af *paf) } subgrp = update_subgroup_find(updgrp, paf); - if (!subgrp) { + if (!subgrp) subgrp = update_subgroup_create(updgrp); - if (!subgrp) - return; - } update_subgroup_add_peer(subgrp, paf, 1); if (BGP_DEBUG(update_groups, UPDATE_GROUPS)) @@ -2093,16 +2094,15 @@ update_group_default_originate_route_map_walkcb(struct update_group *updgrp, return UPDWALK_CONTINUE; } -void update_group_refresh_default_originate_route_map(struct thread *thread) +void update_group_refresh_default_originate_route_map(struct event *thread) { struct bgp *bgp; char reason[] = "refresh default-originate route-map"; - bgp = THREAD_ARG(thread); + bgp = EVENT_ARG(thread); update_group_walk(bgp, update_group_default_originate_route_map_walkcb, reason); - THREAD_OFF(bgp->t_rmap_def_originate_eval); - bgp_unlock(bgp); + EVENT_OFF(bgp->t_rmap_def_originate_eval); } /* @@ -2201,7 +2201,7 @@ void subgroup_trigger_write(struct update_subgroup *subgrp) */ SUBGRP_FOREACH_PEER (subgrp, paf) if (peer_established(paf->peer)) - thread_add_timer_msec( + event_add_timer_msec( bm->master, bgp_generate_updgrp_packets, paf->peer, 0, &paf->peer->t_generate_updgrp_packets); diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h index 2b2fadcdf402..70e7ac30b5ee 100644 --- a/bgpd/bgp_updgrp.h +++ b/bgpd/bgp_updgrp.h @@ -55,9 +55,8 @@ #define PEER_UPDGRP_CAP_FLAGS (PEER_CAP_AS4_RCV) #define PEER_UPDGRP_AF_CAP_FLAGS \ - (PEER_CAP_ORF_PREFIX_SM_RCV | PEER_CAP_ORF_PREFIX_SM_OLD_RCV \ - | PEER_CAP_ADDPATH_AF_TX_ADV | PEER_CAP_ADDPATH_AF_RX_RCV \ - | PEER_CAP_ENHE_AF_NEGO) + (PEER_CAP_ORF_PREFIX_SM_RCV | PEER_CAP_ADDPATH_AF_TX_ADV | \ + PEER_CAP_ADDPATH_AF_RX_RCV | PEER_CAP_ENHE_AF_NEGO) enum bpacket_attr_vec_type { BGP_ATTR_VEC_NH = 0, BGP_ATTR_VEC_MAX }; @@ -197,10 +196,10 @@ struct update_subgroup { /* announcement attribute hash */ struct hash *hash; - struct thread *t_coalesce; + struct event *t_coalesce; uint32_t v_coalesce; - struct thread *t_merge_check; + struct event *t_merge_check; /* table version that the subgroup has caught up to. */ uint64_t version; @@ -373,7 +372,7 @@ extern void update_group_af_walk(struct bgp *bgp, afi_t afi, safi_t safi, extern void update_group_walk(struct bgp *bgp, updgrp_walkcb cb, void *ctx); extern void update_group_periodic_merge(struct bgp *bgp); extern void -update_group_refresh_default_originate_route_map(struct thread *thread); +update_group_refresh_default_originate_route_map(struct event *thread); extern void update_group_start_advtimer(struct bgp *bgp); extern void update_subgroup_inherit_info(struct update_subgroup *to, diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index d6eb3ff20be6..68fd11a042a2 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -17,7 +17,7 @@ #include "memory.h" #include "prefix.h" #include "hash.h" -#include "thread.h" +#include "frrevent.h" #include "queue.h" #include "routemap.h" #include "filter.h" @@ -87,6 +87,67 @@ static void adj_free(struct bgp_adj_out *adj) XFREE(MTYPE_BGP_ADJ_OUT, adj); } +static void +subgrp_announce_addpath_best_selected(struct bgp_dest *dest, + struct update_subgroup *subgrp) +{ + afi_t afi = SUBGRP_AFI(subgrp); + safi_t safi = SUBGRP_SAFI(subgrp); + struct peer *peer = SUBGRP_PEER(subgrp); + enum bgp_path_selection_reason reason; + char pfx_buf[PREFIX2STR_BUFFER] = {}; + int paths_eq = 0; + int best_path_count = 0; + struct list *list = list_new(); + struct bgp_path_info *pi = NULL; + + if (peer->addpath_type[afi][safi] == BGP_ADDPATH_BEST_SELECTED) { + while (best_path_count++ < + peer->addpath_best_selected[afi][safi]) { + struct bgp_path_info *exist = NULL; + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; + pi = pi->next) { + if (listnode_lookup(list, pi)) + continue; + + if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) + continue; + + if (bgp_path_info_cmp(peer->bgp, pi, exist, + &paths_eq, NULL, 0, + pfx_buf, afi, safi, + &reason)) + exist = pi; + } + + if (exist) + listnode_add(list, exist); + } + } + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { + uint32_t id = bgp_addpath_id_for_peer(peer, afi, safi, + &pi->tx_addpath); + + if (peer->addpath_type[afi][safi] == + BGP_ADDPATH_BEST_SELECTED) { + if (listnode_lookup(list, pi)) + subgroup_process_announce_selected( + subgrp, pi, dest, afi, safi, id); + else + subgroup_process_announce_selected( + subgrp, NULL, dest, afi, safi, id); + } else { + subgroup_process_announce_selected(subgrp, pi, dest, + afi, safi, id); + } + } + + if (list) + list_delete(&list); +} + static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx, struct update_subgroup *subgrp) { @@ -114,8 +175,9 @@ static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx, } if (!pi) { - subgroup_process_announce_selected( - subgrp, NULL, ctx->dest, adj->addpath_tx_id); + subgroup_process_announce_selected(subgrp, NULL, + ctx->dest, afi, safi, + adj->addpath_tx_id); } } } @@ -124,7 +186,6 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) { struct updwalk_context *ctx = arg; struct update_subgroup *subgrp; - struct bgp_path_info *pi; afi_t afi; safi_t safi; struct peer *peer; @@ -142,7 +203,6 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) bgp_dest_to_rnode(ctx->dest)); UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { - /* * Skip the subgroups that have coalesce timer running. We will * walk the entire prefix table for those subgroups when the @@ -154,18 +214,8 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) if (addpath_capable) { subgrp_withdraw_stale_addpath(ctx, subgrp); - 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->dest, - bgp_addpath_id_for_peer( - peer, afi, safi, - &pi->tx_addpath)); - } + subgrp_announce_addpath_best_selected(ctx->dest, + subgrp); /* Process the bestpath last so the "show [ip] * bgp neighbor x.x.x.x advertised" @@ -173,7 +223,8 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) */ if (ctx->pi) subgroup_process_announce_selected( - subgrp, ctx->pi, ctx->dest, + subgrp, ctx->pi, ctx->dest, afi, + safi, bgp_addpath_id_for_peer( peer, afi, safi, &ctx->pi->tx_addpath)); @@ -182,7 +233,8 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) else { if (ctx->pi) { subgroup_process_announce_selected( - subgrp, ctx->pi, ctx->dest, + subgrp, ctx->pi, ctx->dest, afi, + safi, bgp_addpath_id_for_peer( peer, afi, safi, &ctx->pi->tx_addpath)); @@ -196,7 +248,8 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) if (adj->subgroup == subgrp) { subgroup_process_announce_selected( subgrp, NULL, - ctx->dest, + ctx->dest, afi, + safi, adj->addpath_tx_id); } } @@ -298,12 +351,13 @@ static void updgrp_show_adj(struct bgp *bgp, afi_t afi, safi_t safi, update_group_af_walk(bgp, afi, safi, updgrp_show_adj_walkcb, &ctx); } -static void subgroup_coalesce_timer(struct thread *thread) +static void subgroup_coalesce_timer(struct event *thread) { struct update_subgroup *subgrp; struct bgp *bgp; + safi_t safi; - subgrp = THREAD_ARG(thread); + subgrp = EVENT_ARG(thread); if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0)) zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes upon coalesce timer expiry(%u ms)", (SUBGRP_UPDGRP(subgrp))->id, subgrp->id, @@ -312,7 +366,7 @@ static void subgroup_coalesce_timer(struct thread *thread) subgrp->v_coalesce = 0; bgp = SUBGRP_INST(subgrp); subgroup_announce_route(subgrp); - + safi = SUBGRP_SAFI(subgrp); /* While the announce_route() may kick off the route advertisement timer * for @@ -323,13 +377,14 @@ static void subgroup_coalesce_timer(struct thread *thread) * announce, this is the method currently employed to trigger the EOR. */ if (!bgp_update_delay_active(SUBGRP_INST(subgrp)) && - !(BGP_SUPPRESS_FIB_ENABLED(bgp))) { + !(bgp_fibupd_safi(safi) && BGP_SUPPRESS_FIB_ENABLED(bgp))) { + struct peer_af *paf; struct peer *peer; SUBGRP_FOREACH_PEER (subgrp, paf) { peer = PAF_PEER(paf); - THREAD_OFF(peer->t_routeadv); + EVENT_OFF(peer->t_routeadv); BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0); } } @@ -497,6 +552,23 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest, zlog_debug("%s suppress UPDATE w/ attr: %s", peer->host, attr_str); } + + /* + * If BGP is skipping sending this value to it's peers + * the version number should be updated just like it + * would if it sent the data. Why? Because update + * groups will not be coalesced until such time that + * the version numbers are the same. + * + * Imagine a scenario with say 2 peers and they come + * up and are placed in the same update group. Then + * a new peer comes up a bit later. Then a prefix is + * flapped that we decide for the first 2 peers are + * mapped to and we decide not to send the data to + * it. Then unless more network changes happen we + * will never be able to coalesce the 3rd peer down + */ + subgrp->version = MAX(subgrp->version, dest->version); return; } @@ -527,7 +599,8 @@ void bgp_adj_out_set_subgroup(struct bgp_dest *dest, * the flag PEER_STATUS_ADV_DELAY which will allow * more routes to be sent in the update message */ - if (BGP_SUPPRESS_FIB_ENABLED(bgp)) { + if (bgp_fibupd_safi(safi) && + BGP_SUPPRESS_FIB_ENABLED(bgp)) { adv_peer = PAF_PEER(paf); if (!bgp_adv_fifo_count( &subgrp->sync->withdraw)) @@ -636,19 +709,15 @@ void subgroup_announce_table(struct update_subgroup *subgrp, { struct bgp_dest *dest; struct bgp_path_info *ri; - struct attr attr; struct peer *peer; afi_t afi; safi_t safi; safi_t safi_rib; bool addpath_capable; - struct bgp *bgp; - bool advertise; peer = SUBGRP_PEER(subgrp); afi = SUBGRP_AFI(subgrp); safi = SUBGRP_SAFI(subgrp); - bgp = SUBGRP_INST(subgrp); addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); if (safi == SAFI_LABELED_UNICAST) @@ -668,10 +737,9 @@ void subgroup_announce_table(struct update_subgroup *subgrp, SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING); for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { - const struct prefix *dest_p = bgp_dest_get_prefix(dest); - /* Check if the route can be advertised */ - advertise = bgp_check_advertise(bgp, dest); + if (addpath_capable) + subgrp_announce_addpath_best_selected(dest, subgrp); for (ri = bgp_dest_get_bgp_path_info(dest); ri; ri = ri->next) { @@ -679,44 +747,23 @@ void subgroup_announce_table(struct update_subgroup *subgrp, safi_rib)) continue; - if (subgroup_announce_check(dest, ri, subgrp, dest_p, - &attr, NULL)) { - /* Check if route can be advertised */ - if (advertise) { - if (!bgp_check_withdrawal(bgp, dest)) { - struct attr *adv_attr = - bgp_attr_intern(&attr); + /* 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_set_subgroup( - dest, subgrp, adv_attr, - ri); - } else - bgp_adj_out_unset_subgroup( - dest, subgrp, 1, - bgp_addpath_id_for_peer( - peer, afi, - safi_rib, - &ri->tx_addpath)); - } - } 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( - dest, subgrp, 1, + if (CHECK_FLAG(ri->flags, BGP_PATH_SELECTED)) + subgroup_process_announce_selected( + subgrp, ri, dest, afi, safi_rib, bgp_addpath_id_for_peer( peer, afi, safi_rib, &ri->tx_addpath)); - } } } UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING); @@ -901,8 +948,8 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw) memset(&p, 0, sizeof(p)); p.family = afi2family(afi); p.prefixlen = 0; - dest = bgp_afi_node_lookup(bgp->rib[afi][safi_rib], afi, safi_rib, &p, - NULL); + dest = bgp_safi_node_lookup(bgp->rib[afi][safi_rib], safi_rib, &p, + NULL); if (withdraw) { /* Withdraw the default route advertised using default @@ -1004,9 +1051,9 @@ void subgroup_announce_all(struct update_subgroup *subgrp) * We should wait for the coalesce timer. Arm the timer if not done. */ if (!subgrp->t_coalesce) { - thread_add_timer_msec(bm->master, subgroup_coalesce_timer, - subgrp, subgrp->v_coalesce, - &subgrp->t_coalesce); + event_add_timer_msec(bm->master, subgroup_coalesce_timer, + subgrp, subgrp->v_coalesce, + &subgrp->t_coalesce); } } @@ -1024,7 +1071,7 @@ void group_announce_route(struct bgp *bgp, afi_t afi, safi_t safi, /* If suppress fib is enabled, the route will be advertised when * FIB status is received */ - if (!bgp_check_advertise(bgp, dest)) + if (!bgp_check_advertise(bgp, dest, safi)) return; 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 5106dcf354cc..49b7f51286d4 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -12,7 +12,7 @@ #include #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "stream.h" #include "command.h" @@ -789,6 +789,29 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp) safi); label_pnt = &label; num_labels = 1; + } else if (safi == SAFI_MPLS_VPN && path && + CHECK_FLAG(path->flags, + BGP_PATH_MPLSVPN_NH_LABEL_BIND) && + path->mplsvpn.bmnc.nh_label_bind_cache && + path->peer && path->peer != peer && + path->sub_type != BGP_ROUTE_IMPORTED && + path->sub_type != BGP_ROUTE_STATIC && + bgp_mplsvpn_path_uses_valid_mpls_label( + path) && + bgp_path_info_nexthop_changed(path, peer, + afi)) { + /* Redistributed mpls vpn route between distinct + * peers from 'pi->peer' to 'to', + * and an mpls label is used in this path, + * and there is a nh label bind entry, + * then get appropriate mpls local label. When + * called here, 'get_label()' returns a valid + * label. + */ + label = bgp_mplsvpn_nh_label_bind_get_label( + path); + label_pnt = &label; + num_labels = 1; } else if (path && path->extra) { label_pnt = &path->extra->label[0]; num_labels = path->extra->num_labels; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index a1e9a9a8a94b..7942797528c1 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -16,7 +16,7 @@ #include "buffer.h" #include "linklist.h" #include "stream.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "memory.h" #include "lib_vty.h" @@ -1675,28 +1675,46 @@ DEFUN (no_router_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], + 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], + 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_ADV_IPV4_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) || - CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN], - BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) || - (hashcount(tmp_bgp->vnihash))) { + (CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) || + CHECK_FLAG( + tmp_bgp->af_flags[AFI_L2VPN] + [SAFI_EVPN], + BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) || + (tmp_bgp->l3vni)) { vty_out(vty, "%% Cannot delete default BGP instance. Dependent VRF instances exist\n"); return CMD_WARNING_CONFIG_FAILED; @@ -2194,7 +2212,7 @@ DEFUN (no_bgp_maxmed_onstartup, /* Cancel max-med onstartup if its on */ if (bgp->t_maxmed_onstartup) { - THREAD_OFF(bgp->t_maxmed_onstartup); + EVENT_OFF(bgp->t_maxmed_onstartup); bgp->maxmed_onstartup_over = 1; } @@ -2802,6 +2820,31 @@ DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd, return CMD_SUCCESS; } +DEFPY(bgp_lu_uses_explicit_null, bgp_lu_uses_explicit_null_cmd, + "[no] bgp labeled-unicast $value", + NO_STR BGP_STR + "BGP Labeled-unicast options\n" + "Use explicit-null label values for all local prefixes\n" + "Use the IPv4 explicit-null label value for IPv4 local prefixes\n" + "Use the IPv6 explicit-null label value for IPv6 local prefixes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + uint64_t label_mode; + + if (strmatch(value, "ipv4-explicit-null")) + label_mode = BGP_FLAG_LU_IPV4_EXPLICIT_NULL; + else if (strmatch(value, "ipv6-explicit-null")) + label_mode = BGP_FLAG_LU_IPV6_EXPLICIT_NULL; + else + label_mode = BGP_FLAG_LU_IPV4_EXPLICIT_NULL | + BGP_FLAG_LU_IPV6_EXPLICIT_NULL; + if (no) + UNSET_FLAG(bgp->flags, label_mode); + else + SET_FLAG(bgp->flags, label_mode); + return CMD_SUCCESS; +} + DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd, "bgp suppress-duplicates", BGP_STR @@ -7518,9 +7561,9 @@ DEFUN (bgp_set_route_map_delay_timer, * fired. */ if (!rmap_delay_timer && bm->t_rmap_update) { - THREAD_OFF(bm->t_rmap_update); - thread_execute(bm->master, bgp_route_map_update_timer, - NULL, 0); + EVENT_OFF(bm->t_rmap_update); + event_execute(bm->master, bgp_route_map_update_timer, + NULL, 0); } return CMD_SUCCESS; } else { @@ -7916,6 +7959,26 @@ DEFPY (bgp_condadv_period, return CMD_SUCCESS; } +DEFPY (bgp_def_originate_eval, + bgp_def_originate_eval_cmd, + "[no$no] bgp default-originate timer (0-3600)$timer", + NO_STR + BGP_STR + "Control default-originate\n" + "Set period to rescan BGP table to check if default-originate condition is met\n" + "Period between BGP table scans, in seconds; default 5\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp->rmap_def_originate_eval_timer = + no ? RMAP_DEFAULT_ORIGINATE_EVAL_TIMER : timer; + + if (bgp->t_rmap_def_originate_eval) + EVENT_OFF(bgp->t_rmap_def_originate_eval); + + return CMD_SUCCESS; +} + DEFPY (neighbor_advertise_map, neighbor_advertise_map_cmd, "[no$no] neighbor $neighbor advertise-map RMAP_NAME$advertise_str $exist RMAP_NAME$condition_str", @@ -8740,7 +8803,7 @@ DEFUN (neighbor_addpath_tx_all_paths, return CMD_WARNING_CONFIG_FAILED; bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), - BGP_ADDPATH_ALL); + BGP_ADDPATH_ALL, 0); return CMD_SUCCESS; } @@ -8773,7 +8836,7 @@ DEFUN (no_neighbor_addpath_tx_all_paths, } bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), - BGP_ADDPATH_NONE); + BGP_ADDPATH_NONE, 0); return CMD_SUCCESS; } @@ -8784,6 +8847,45 @@ ALIAS_HIDDEN(no_neighbor_addpath_tx_all_paths, NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Use addpath to advertise all paths to a neighbor\n") +DEFPY (neighbor_addpath_tx_best_selected_paths, + neighbor_addpath_tx_best_selected_paths_cmd, + "neighbor $neighbor addpath-tx-best-selected (1-6)$paths", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Use addpath to advertise best selected paths to a neighbor\n" + "The number of best paths\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), + BGP_ADDPATH_BEST_SELECTED, paths); + return CMD_SUCCESS; +} + +DEFPY (no_neighbor_addpath_tx_best_selected_paths, + no_neighbor_addpath_tx_best_selected_paths_cmd, + "no neighbor $neighbor addpath-tx-best-selected [(1-6)]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Use addpath to advertise best selected paths to a neighbor\n" + "The number of best paths\n") +{ + struct peer *peer; + + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + + bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), + BGP_ADDPATH_BEST_SELECTED, 0); + return CMD_SUCCESS; +} + DEFUN (neighbor_addpath_tx_bestpath_per_as, neighbor_addpath_tx_bestpath_per_as_cmd, "neighbor addpath-tx-bestpath-per-AS", @@ -8799,7 +8901,7 @@ DEFUN (neighbor_addpath_tx_bestpath_per_as, return CMD_WARNING_CONFIG_FAILED; bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), - BGP_ADDPATH_BEST_PER_AS); + BGP_ADDPATH_BEST_PER_AS, 0); return CMD_SUCCESS; } @@ -8833,7 +8935,7 @@ DEFUN (no_neighbor_addpath_tx_bestpath_per_as, } bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty), - BGP_ADDPATH_NONE); + BGP_ADDPATH_NONE, 0); return CMD_SUCCESS; } @@ -9140,6 +9242,65 @@ ALIAS (af_rd_vpn_export, "Between current address-family and vpn\n" "For routes leaked from current address-family to vpn\n") +DEFPY(af_label_vpn_export_allocation_mode, + af_label_vpn_export_allocation_mode_cmd, + "[no$no] label vpn export allocation-mode ", + NO_STR + "label value for VRF\n" + "Between current address-family and vpn\n" + "For routes leaked from current address-family to vpn\n" + "Label allocation mode\n" + "Allocate one label for all BGP updates of the VRF\n" + "Allocate a label per connected next-hop in the VRF\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + afi_t afi; + bool old_per_nexthop, new_per_nexthop; + + afi = vpn_policy_getafi(vty, bgp, false); + if (afi == AFI_MAX) + return CMD_WARNING_CONFIG_FAILED; + + old_per_nexthop = !!CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP); + if (no) { + if (old_per_nexthop == false && label_per_nh) + return CMD_ERR_NO_MATCH; + if (old_per_nexthop == true && label_per_vrf) + return CMD_ERR_NO_MATCH; + new_per_nexthop = false; + } else { + if (label_per_nh) + new_per_nexthop = true; + else + new_per_nexthop = false; + } + + /* no change */ + if (old_per_nexthop == new_per_nexthop) + return CMD_SUCCESS; + + /* + * pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_get_default(), + bgp); + + if (new_per_nexthop) + SET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP); + else + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP); + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, bgp_get_default(), + bgp); + + hook_call(bgp_snmp_update_last_changed, bgp); + return CMD_SUCCESS; +} + DEFPY (af_label_vpn_export, af_label_vpn_export_cmd, "[no] label vpn export <(0-1048575)$label_val|auto$label_auto>", @@ -9249,9 +9410,24 @@ DEFPY (af_sid_vpn_export, return CMD_WARNING_CONFIG_FAILED; if (!yes) { - /* implement me */ - vty_out(vty, "It's not implemented\n"); - return CMD_WARNING_CONFIG_FAILED; + /* when SID is not set, do nothing */ + if ((bgp->vpn_policy[afi].tovpn_sid_index == 0) && + !CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO)) + return CMD_SUCCESS; + + /* pre-change */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + bgp->vpn_policy[afi].tovpn_sid_index = 0; + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_SID_AUTO); + + /* post-change */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + return CMD_SUCCESS; } if (bgp->tovpn_sid_index != 0 || @@ -9290,7 +9466,7 @@ DEFPY (af_sid_vpn_export, zlog_debug("%s: auto sid alloc.", __func__); SET_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_SID_AUTO); - } else { + } else if (sid_idx != 0) { /* SID allocation index-mode */ if (debug) zlog_debug("%s: idx %ld sid alloc.", __func__, sid_idx); @@ -9817,6 +9993,7 @@ DEFPY (bgp_imexport_vpn, bool yes = true; int flag; enum vpn_policy_direction dir; + struct bgp *bgp_default = bgp_get_default(); if (argv_find(argv, argc, "no", &idx)) yes = false; @@ -9852,14 +10029,18 @@ DEFPY (bgp_imexport_vpn, SET_FLAG(bgp->af_flags[afi][safi], flag); if (!previous_state) { /* trigger export current vrf */ - vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); + vpn_leak_postchange(dir, afi, bgp_default, bgp); } } else { if (previous_state) { /* trigger un-export current vrf */ - vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + vpn_leak_prechange(dir, afi, bgp_default, bgp); } UNSET_FLAG(bgp->af_flags[afi][safi], flag); + if (previous_state && bgp_default && + !CHECK_FLAG(bgp_default->af_flags[afi][SAFI_MPLS_VPN], + BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL)) + vpn_leak_no_retain(bgp, bgp_default, afi); } hook_call(bgp_snmp_init_stats, bgp); @@ -11036,10 +11217,10 @@ static char *bgp_peer_description_stripped(char *desc, uint32_t size) { static char stripped[BUFSIZ]; uint32_t i = 0; - uint32_t last_space = 0; + uint32_t last_space = size; while (i < size) { - if (*(desc + i) == 0) { + if (*(desc + i) == '\0') { stripped[i] = '\0'; return stripped; } @@ -11049,10 +11230,7 @@ static char *bgp_peer_description_stripped(char *desc, uint32_t size) i++; } - if (last_space > size) - stripped[size + 1] = '\0'; - else - stripped[last_space] = '\0'; + stripped[last_space] = '\0'; return stripped; } @@ -11508,8 +11686,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, &peer->ibuf->count, memory_order_relaxed); - json_object_int_add(json_peer, "tableVersion", - peer->version[afi][safi]); + json_object_int_add( + json_peer, "tableVersion", + (paf && PAF_SUBGRP(paf)) + ? paf->subgroup->version + : 0); json_object_int_add(json_peer, "outq", outq_count); json_object_int_add(json_peer, "inq", @@ -11673,40 +11854,27 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, &peer->ibuf->count, memory_order_relaxed); - vty_out(vty, "4 "); + vty_out(vty, "4"); vty_out(vty, ASN_FORMAT_SPACE(bgp->asnotation), &peer->as); - if (show_wide) { + if (show_wide) vty_out(vty, ASN_FORMAT_SPACE( bgp->asnotation), peer->change_local_as ? &peer->change_local_as : &peer->local_as); - vty_out(vty, - " %9u %9u %8" PRIu64 - " %4zu %4zu %8s", - 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)); - } else { - vty_out(vty, - " %9u %9u %8" PRIu64 - " %4zu %4zu %8s", - 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)); - } + vty_out(vty, + " %9u %9u %8" PRIu64 " %4zu %4zu %8s", + PEER_TOTAL_RX(peer), + PEER_TOTAL_TX(peer), + (paf && PAF_SUBGRP(paf)) + ? paf->subgroup->version + : 0, + inq_count, outq_count, + peer_uptime(peer->uptime, timebuf, + BGP_UPTIME_LEN, 0, NULL)); + if (peer_established(peer)) { if (peer->afc_recv[afi][safi]) { if (CHECK_FLAG( @@ -11762,14 +11930,23 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, } /* Make sure `Desc` column is the lastest in * the output. + * If the description is not set, try + * to print the software version if the + * capability is enabled and received. */ if (peer->desc) vty_out(vty, " %s", bgp_peer_description_stripped( peer->desc, show_wide ? 64 : 20)); - else + else if (peer->soft_version) { + vty_out(vty, " %s", + bgp_peer_description_stripped( + peer->soft_version, + show_wide ? 64 : 20)); + } else { vty_out(vty, " N/A"); + } vty_out(vty, "\n"); } @@ -12345,7 +12522,7 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( if (peer->t_gr_stale != NULL) { json_object_int_add(json_timer, "stalePathTimerRemaining", - thread_timer_remain_second( + event_timer_remain_second( peer->t_gr_stale)); } @@ -12366,7 +12543,7 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( json_object_int_add( json_timer, "selectionDeferralTimerRemaining", - thread_timer_remain_second( + event_timer_remain_second( peer->bgp->gr_info[afi][safi] .t_select_deferral)); } @@ -12379,7 +12556,7 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( if (peer->t_gr_stale != NULL) vty_out(vty, " Stale Path Remaining(sec): %ld\n", - thread_timer_remain_second( + event_timer_remain_second( peer->t_gr_stale)); /* Display Configured Selection * Deferral only when when @@ -12394,7 +12571,7 @@ static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi( NULL) vty_out(vty, " Selection Deferral Time Remaining(sec): %ld\n", - thread_timer_remain_second( + event_timer_remain_second( peer->bgp->gr_info[afi][safi] .t_select_deferral)); } @@ -12428,7 +12605,7 @@ static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, if (p->t_gr_restart != NULL) json_object_int_add( json_timer, "restartTimerRemaining", - thread_timer_remain_second(p->t_gr_restart)); + event_timer_remain_second(p->t_gr_restart)); json_object_object_add(json, "timers", json_timer); } else { @@ -12441,10 +12618,10 @@ static void bgp_show_neighbor_graceful_restart_time(struct vty *vty, 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)); + event_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)); + event_timer_remain_second(p->t_gr_restart)); } } } @@ -12491,7 +12668,6 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, int orf_pfx_count; json_object *json_af = NULL; json_object *json_prefA = NULL; - json_object *json_prefB = NULL; json_object *json_addr = NULL; json_object *json_advmap = NULL; @@ -12534,37 +12710,13 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, json_prefA); } - if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_OLD_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) { - json_object_int_add(json_af, "orfOldType", - ORF_TYPE_PREFIX_OLD); - json_prefB = json_object_new_object(); - bgp_show_peer_afi_orf_cap( - vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV, - PEER_CAP_ORF_PREFIX_RM_ADV, - PEER_CAP_ORF_PREFIX_SM_OLD_RCV, - PEER_CAP_ORF_PREFIX_RM_OLD_RCV, use_json, - json_prefB); - json_object_object_add(json_af, "orfOldPrefixList", - json_prefB); - } - - if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_OLD_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_RCV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) json_object_object_add(json_addr, "afDependentCap", json_af); else @@ -12851,17 +13003,13 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, } else { vty_out(vty, " Not part of any update group\n"); } - if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_OLD_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) + if (CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_RCV) || + CHECK_FLAG(p->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_ADV) || + CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) vty_out(vty, " AF-dependant capabilities:\n"); if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) @@ -12880,22 +13028,6 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi, PEER_CAP_ORF_PREFIX_SM_RCV, PEER_CAP_ORF_PREFIX_RM_RCV, use_json, NULL); } - if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_OLD_RCV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_ADV) - || CHECK_FLAG(p->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) { - vty_out(vty, - " Outbound Route Filter (ORF) type (%d) Prefix-list:\n", - ORF_TYPE_PREFIX_OLD); - bgp_show_peer_afi_orf_cap( - vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV, - PEER_CAP_ORF_PREFIX_RM_ADV, - PEER_CAP_ORF_PREFIX_SM_OLD_RCV, - PEER_CAP_ORF_PREFIX_RM_OLD_RCV, use_json, NULL); - } snprintf(orf_pfx_name, sizeof(orf_pfx_name), "%s.%d.%d", p->host, afi, safi); @@ -13475,11 +13607,11 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_neigh, "bgpTimerConfiguredConditionalAdvertisementsSec", bgp->condition_check_period); - if (thread_is_scheduled(bgp->t_condition_check)) + if (event_is_scheduled(bgp->t_condition_check)) json_object_int_add( json_neigh, "bgpTimerUntilConditionalAdvertisementsSec", - thread_timer_remain_second( + event_timer_remain_second( bgp->t_condition_check)); } else { /* Administrative shutdown. */ @@ -13556,10 +13688,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, vty_out(vty, " Configured conditional advertisements interval is %d seconds\n", bgp->condition_check_period); - if (thread_is_scheduled(bgp->t_condition_check)) + if (event_is_scheduled(bgp->t_condition_check)) vty_out(vty, " Time until conditional advertisements begin is %lu seconds\n", - thread_timer_remain_second( + event_timer_remain_second( bgp->t_condition_check)); } /* Capability. */ @@ -13847,46 +13979,19 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, /* Route Refresh */ if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) || - CHECK_FLAG(p->cap, PEER_CAP_REFRESH_NEW_RCV) || - CHECK_FLAG(p->cap, PEER_CAP_REFRESH_OLD_RCV)) { + CHECK_FLAG(p->cap, PEER_CAP_REFRESH_RCV)) { if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) && - (CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_NEW_RCV) || - CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_OLD_RCV))) { - if (CHECK_FLAG( - p->cap, - PEER_CAP_REFRESH_OLD_RCV) && - CHECK_FLAG( - p->cap, - PEER_CAP_REFRESH_NEW_RCV)) - json_object_string_add( - json_cap, - "routeRefresh", - "advertisedAndReceivedOldNew"); - else { - if (CHECK_FLAG( - p->cap, - PEER_CAP_REFRESH_OLD_RCV)) - json_object_string_add( - json_cap, - "routeRefresh", - "advertisedAndReceivedOld"); - else - json_object_string_add( - json_cap, - "routeRefresh", - "advertisedAndReceivedNew"); - } - } else if (CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_ADV)) + CHECK_FLAG(p->cap, PEER_CAP_REFRESH_RCV)) + json_object_string_add(json_cap, + "routeRefresh", + "advertisedAndReceived"); + else if (CHECK_FLAG(p->cap, + PEER_CAP_REFRESH_ADV)) json_object_string_add(json_cap, "routeRefresh", "advertised"); else if (CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_NEW_RCV) || - CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_OLD_RCV)) + PEER_CAP_REFRESH_RCV)) json_object_string_add(json_cap, "routeRefresh", "received"); @@ -14288,33 +14393,16 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, /* Route Refresh */ if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) || - CHECK_FLAG(p->cap, PEER_CAP_REFRESH_NEW_RCV) || - CHECK_FLAG(p->cap, PEER_CAP_REFRESH_OLD_RCV)) { + CHECK_FLAG(p->cap, PEER_CAP_REFRESH_RCV)) { vty_out(vty, " Route refresh:"); if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV)) vty_out(vty, " advertised"); - if (CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_NEW_RCV) || - CHECK_FLAG(p->cap, - PEER_CAP_REFRESH_OLD_RCV)) - vty_out(vty, " %sreceived(%s)", + if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_RCV)) + vty_out(vty, " %sreceived", CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) ? "and " - : "", - (CHECK_FLAG( - p->cap, - PEER_CAP_REFRESH_OLD_RCV) && - CHECK_FLAG( - p->cap, - PEER_CAP_REFRESH_NEW_RCV)) - ? "old & new" - : CHECK_FLAG( - p->cap, - PEER_CAP_REFRESH_OLD_RCV) - ? "old" - : "new"); - + : ""); vty_out(vty, "\n"); } @@ -14496,13 +14584,13 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (p->t_gr_restart) json_object_int_add( json_grace, "gracefulRestartTimerMsecs", - thread_timer_remain_second(p->t_gr_restart) * + event_timer_remain_second(p->t_gr_restart) * 1000); if (p->t_gr_stale) json_object_int_add( json_grace, "gracefulStalepathTimerMsecs", - thread_timer_remain_second(p->t_gr_stale) * + event_timer_remain_second(p->t_gr_stale) * 1000); /* more gr info in new format */ BGP_SHOW_PEER_GR_CAPABILITY(vty, p, json_grace); @@ -14543,12 +14631,12 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (p->t_gr_restart) vty_out(vty, " The remaining time of restart timer is %ld\n", - thread_timer_remain_second(p->t_gr_restart)); + event_timer_remain_second(p->t_gr_restart)); if (p->t_gr_stale) vty_out(vty, " The remaining time of stalepath timer is %ld\n", - thread_timer_remain_second(p->t_gr_stale)); + event_timer_remain_second(p->t_gr_stale)); /* more gr info in new format */ BGP_SHOW_PEER_GR_CAPABILITY(vty, p, NULL); @@ -14782,14 +14870,15 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, json_neigh, "reducePrefixNumFrom"); json_object_int_add(json_neigh, "restartInTimerMsec", - thread_timer_remain_second( - p->t_pmax_restart) - * 1000); + event_timer_remain_second( + p->t_pmax_restart) * + 1000); } else vty_out(vty, " Reduce the no. of prefix from %s, will restart in %ld seconds\n", - p->host, thread_timer_remain_second( - p->t_pmax_restart)); + p->host, + event_timer_remain_second( + p->t_pmax_restart)); } else { if (use_json) json_object_boolean_true_add( @@ -14941,19 +15030,18 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, if (p->t_start) json_object_int_add( json_neigh, "nextStartTimerDueInMsecs", - thread_timer_remain_second(p->t_start) * 1000); + event_timer_remain_second(p->t_start) * 1000); if (p->t_connect) json_object_int_add( json_neigh, "nextConnectTimerDueInMsecs", - thread_timer_remain_second(p->t_connect) - * 1000); + event_timer_remain_second(p->t_connect) * 1000); if (p->t_routeadv) { json_object_int_add(json_neigh, "mraiInterval", p->v_routeadv); json_object_int_add( json_neigh, "mraiTimerExpireInMsecs", - thread_timer_remain_second(p->t_routeadv) - * 1000); + event_timer_remain_second(p->t_routeadv) * + 1000); } if (p->password) json_object_int_add(json_neigh, "authenticationEnabled", @@ -14982,15 +15070,15 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json, } if (p->t_start) vty_out(vty, "Next start timer due in %ld seconds\n", - thread_timer_remain_second(p->t_start)); + event_timer_remain_second(p->t_start)); if (p->t_connect) vty_out(vty, "Next connect timer due in %ld seconds\n", - thread_timer_remain_second(p->t_connect)); + event_timer_remain_second(p->t_connect)); if (p->t_routeadv) vty_out(vty, "MRAI (interval %u) timer expires in %ld seconds\n", p->v_routeadv, - thread_timer_remain_second(p->t_routeadv)); + event_timer_remain_second(p->t_routeadv)); if (p->password) vty_out(vty, "Peer Authentication Enabled\n"); @@ -17260,6 +17348,12 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, } } + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP)) + vty_out(vty, + "%*slabel vpn export allocation-mode per-nexthop\n", + indent, ""); + tovpn_sid_index = bgp->vpn_policy[afi].tovpn_sid_index; if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_SID_AUTO)) { @@ -17807,6 +17901,13 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp, " neighbor %s addpath-tx-bestpath-per-AS\n", addr); break; + case BGP_ADDPATH_BEST_SELECTED: + if (peer->addpath_best_selected[afi][safi]) + vty_out(vty, + " neighbor %s addpath-tx-best-selected %u\n", + addr, + peer->addpath_best_selected[afi][safi]); + break; case BGP_ADDPATH_MAX: case BGP_ADDPATH_NONE: break; @@ -18235,6 +18336,18 @@ int bgp_config_write(struct vty *vty) ? "" : "no "); + if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) && + !!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL)) + vty_out(vty, " bgp labeled-unicast explicit-null\n"); + else if (!!CHECK_FLAG(bgp->flags, + BGP_FLAG_LU_IPV4_EXPLICIT_NULL)) + vty_out(vty, + " bgp labeled-unicast ipv4-explicit-null\n"); + else if (!!CHECK_FLAG(bgp->flags, + BGP_FLAG_LU_IPV6_EXPLICIT_NULL)) + vty_out(vty, + " bgp labeled-unicast ipv6-explicit-null\n"); + /* draft-ietf-idr-deprecate-as-set-confed-set */ if (bgp->reject_as_sets) vty_out(vty, " bgp reject-as-sets\n"); @@ -18500,6 +18613,12 @@ int bgp_config_write(struct vty *vty) " bgp conditional-advertisement timer %u\n", bgp->condition_check_period); + /* default-originate timer configuration */ + if (bgp->rmap_def_originate_eval_timer != + RMAP_DEFAULT_ORIGINATE_EVAL_TIMER) + vty_out(vty, " bgp default-originate timer %u\n", + bgp->rmap_def_originate_eval_timer); + /* peer-group */ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { bgp_config_write_peer_global(vty, bgp, group->conf); @@ -18779,14 +18898,17 @@ static const struct cmd_variable_handler bgp_var_peergroup[] = { DEFINE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp)); -static struct thread *t_bgp_cfg; +static struct event *t_bgp_cfg; bool bgp_config_inprocess(void) { - return thread_is_scheduled(t_bgp_cfg); + return event_is_scheduled(t_bgp_cfg); } -static void bgp_config_finish(struct thread *t) +/* Max wait time for config to load before post-config processing */ +#define BGP_PRE_CONFIG_MAX_WAIT_SECONDS 600 + +static void bgp_config_finish(struct event *t) { struct listnode *node; struct bgp *bgp; @@ -18795,12 +18917,18 @@ static void bgp_config_finish(struct thread *t) hook_call(bgp_config_end, bgp); } +static void bgp_config_end_timeout(struct event *t) +{ + zlog_err("BGP configuration end timer expired after %d seconds.", + BGP_PRE_CONFIG_MAX_WAIT_SECONDS); + bgp_config_finish(t); +} + static void bgp_config_start(void) { -#define BGP_PRE_CONFIG_MAX_WAIT_SECONDS 600 - THREAD_OFF(t_bgp_cfg); - thread_add_timer(bm->master, bgp_config_finish, NULL, - BGP_PRE_CONFIG_MAX_WAIT_SECONDS, &t_bgp_cfg); + EVENT_OFF(t_bgp_cfg); + event_add_timer(bm->master, bgp_config_end_timeout, NULL, + BGP_PRE_CONFIG_MAX_WAIT_SECONDS, &t_bgp_cfg); } /* When we receive a hook the configuration is read, @@ -18812,8 +18940,8 @@ static void bgp_config_end(void) { #define BGP_POST_CONFIG_DELAY_SECONDS 1 uint32_t bgp_post_config_delay = - thread_is_scheduled(bm->t_rmap_update) - ? thread_timer_remain_second(bm->t_rmap_update) + event_is_scheduled(bm->t_rmap_update) + ? event_timer_remain_second(bm->t_rmap_update) : BGP_POST_CONFIG_DELAY_SECONDS; /* If BGP config processing thread isn't running, then @@ -18822,13 +18950,13 @@ static void bgp_config_end(void) if (!bgp_config_inprocess()) return; - THREAD_OFF(t_bgp_cfg); + EVENT_OFF(t_bgp_cfg); /* Start a new timer to make sure we don't send EoR * before route-maps are processed. */ - thread_add_timer(bm->master, bgp_config_finish, NULL, - bgp_post_config_delay, &t_bgp_cfg); + event_add_timer(bm->master, bgp_config_finish, NULL, + bgp_post_config_delay, &t_bgp_cfg); } static int config_write_interface_one(struct vty *vty, struct vrf *vrf) @@ -18849,6 +18977,12 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, " mpls bgp forwarding\n"); write++; } + if (CHECK_FLAG(iifp->flags, + BGP_INTERFACE_MPLS_L3VPN_SWITCHING)) { + vty_out(vty, + " mpls bgp l3vpn-multi-domain-switching\n"); + write++; + } if_vty_config_end(vty); } @@ -18899,6 +19033,35 @@ DEFPY(mpls_bgp_forwarding, mpls_bgp_forwarding_cmd, return CMD_SUCCESS; } +DEFPY(mpls_bgp_l3vpn_multi_domain_switching, + mpls_bgp_l3vpn_multi_domain_switching_cmd, + "[no$no] mpls bgp l3vpn-multi-domain-switching", + NO_STR MPLS_STR BGP_STR + "Bind a local MPLS label to incoming L3VPN updates\n") +{ + bool check; + struct bgp_interface *iifp; + + VTY_DECLVAR_CONTEXT(interface, ifp); + iifp = ifp->info; + if (!iifp) { + vty_out(vty, "Interface %s not available\n", ifp->name); + return CMD_WARNING_CONFIG_FAILED; + } + check = CHECK_FLAG(iifp->flags, BGP_INTERFACE_MPLS_L3VPN_SWITCHING); + if (check == !no) + return CMD_SUCCESS; + if (no) + UNSET_FLAG(iifp->flags, BGP_INTERFACE_MPLS_L3VPN_SWITCHING); + else + SET_FLAG(iifp->flags, BGP_INTERFACE_MPLS_L3VPN_SWITCHING); + /* trigger a nht update on eBGP sessions */ + if (if_is_operative(ifp)) + bgp_nht_ifp_up(ifp); + + return CMD_SUCCESS; +} + DEFPY (bgp_inq_limit, bgp_inq_limit_cmd, "bgp input-queue-limit (1-4294967295)$limit", @@ -18958,6 +19121,8 @@ static void bgp_vty_if_init(void) /* "mpls bgp forwarding" commands. */ install_element(INTERFACE_NODE, &mpls_bgp_forwarding_cmd); + install_element(INTERFACE_NODE, + &mpls_bgp_l3vpn_multi_domain_switching_cmd); } void bgp_vty_init(void) @@ -19154,6 +19319,9 @@ 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 labeled-unicast explicit-null */ + install_element(BGP_NODE, &bgp_lu_uses_explicit_null_cmd); + /* bgp suppress-duplicates */ install_element(BGP_NODE, &bgp_suppress_duplicates_cmd); install_element(BGP_NODE, &no_bgp_suppress_duplicates_cmd); @@ -19794,6 +19962,40 @@ void bgp_vty_init(void) install_element(BGP_VPNV6_NODE, &neighbor_addpath_tx_all_paths_cmd); install_element(BGP_VPNV6_NODE, &no_neighbor_addpath_tx_all_paths_cmd); + /* "neighbor addpath-tx-best-selected" commands.*/ + install_element(BGP_IPV4_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV4_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV4M_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV4M_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV4L_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV4L_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV6_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV6_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV6M_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV6M_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV6L_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_IPV6L_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_VPNV4_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_VPNV4_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_VPNV6_NODE, + &neighbor_addpath_tx_best_selected_paths_cmd); + install_element(BGP_VPNV6_NODE, + &no_neighbor_addpath_tx_best_selected_paths_cmd); + /* "neighbor addpath-tx-bestpath-per-AS" commands.*/ install_element(BGP_NODE, &neighbor_addpath_tx_bestpath_per_as_hidden_cmd); @@ -20125,6 +20327,9 @@ void bgp_vty_init(void) install_element(BGP_VPNV4_NODE, &neighbor_advertise_map_cmd); install_element(BGP_VPNV6_NODE, &neighbor_advertise_map_cmd); + /* bgp default-originate timer */ + install_element(BGP_NODE, &bgp_def_originate_eval_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); @@ -20418,6 +20623,10 @@ void bgp_vty_init(void) install_element(BGP_IPV6_NODE, &af_rd_vpn_export_cmd); install_element(BGP_IPV4_NODE, &af_label_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_label_vpn_export_cmd); + install_element(BGP_IPV4_NODE, + &af_label_vpn_export_allocation_mode_cmd); + install_element(BGP_IPV6_NODE, + &af_label_vpn_export_allocation_mode_cmd); install_element(BGP_IPV4_NODE, &af_nexthop_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_nexthop_vpn_export_cmd); install_element(BGP_IPV4_NODE, &af_rt_vpn_imexport_cmd); @@ -20536,6 +20745,7 @@ DEFUN (community_list_standard, argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); + assert(str); int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq, direct, style); @@ -20648,6 +20858,7 @@ DEFUN (community_list_expanded_all, argv_find(argv, argc, "AA:NN", &idx); char *str = argv_concat(argv, argc, idx); + assert(str); int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq, direct, style); @@ -20732,16 +20943,13 @@ static const char *community_list_config_str(struct community_entry *entry) { const char *str; - if (entry->any) - str = ""; - else { - if (entry->style == COMMUNITY_LIST_STANDARD) - str = community_str(entry->u.com, false, false); - else if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) - str = lcommunity_str(entry->u.lcom, false, false); - else - str = entry->config; - } + if (entry->style == COMMUNITY_LIST_STANDARD) + str = community_str(entry->u.com, false, false); + else if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) + str = lcommunity_str(entry->u.lcom, false, false); + else + str = entry->config; + return str; } @@ -20764,13 +20972,8 @@ static void community_list_show(struct vty *vty, struct community_list *list) : "expanded", list->name); } - if (entry->any) - vty_out(vty, " %s\n", - community_direct_str(entry->direct)); - else - vty_out(vty, " %s %s\n", - community_direct_str(entry->direct), - community_list_config_str(entry)); + vty_out(vty, " %s %s\n", community_direct_str(entry->direct), + community_list_config_str(entry)); } } @@ -21129,13 +21332,8 @@ static void lcommunity_list_show(struct vty *vty, struct community_list *list) : "expanded", list->name); } - if (entry->any) - vty_out(vty, " %s\n", - community_direct_str(entry->direct)); - else - vty_out(vty, " %s %s\n", - community_direct_str(entry->direct), - community_list_config_str(entry)); + vty_out(vty, " %s %s\n", community_direct_str(entry->direct), + community_list_config_str(entry)); } } @@ -21431,13 +21629,8 @@ static void extcommunity_list_show(struct vty *vty, struct community_list *list) : "expanded", list->name); } - if (entry->any) - vty_out(vty, " %s\n", - community_direct_str(entry->direct)); - else - vty_out(vty, " %s %s\n", - community_direct_str(entry->direct), - community_list_config_str(entry)); + vty_out(vty, " %s %s\n", community_direct_str(entry->direct), + community_list_config_str(entry)); } } diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 826723b92dd7..a105b6de3fa9 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -145,7 +145,7 @@ 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); extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp); extern void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp); -extern int bgp_vty_return(struct vty *vty, int ret); +extern int bgp_vty_return(struct vty *vty, enum bgp_create_error_code ret); extern bool bgp_config_inprocess(void); extern struct peer *peer_and_group_lookup_vty(struct vty *vty, const char *peer_str); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index d05768da0514..1320906339df 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -13,7 +13,7 @@ #include "sockunion.h" #include "zclient.h" #include "routemap.h" -#include "thread.h" +#include "frrevent.h" #include "queue.h" #include "memory.h" #include "lib/json.h" @@ -55,6 +55,8 @@ /* All information about zebra. */ struct zclient *zclient = NULL; +struct zclient *zclient_sync; +static bool bgp_zebra_label_manager_connect(void); /* hook to indicate vrf status change for SNMP */ DEFINE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp), @@ -834,6 +836,13 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, peer->bgp->vrf_id); } + /* Handle peerings via loopbacks. For instance, peer between + * 127.0.0.1 and 127.0.0.2. In short, allow peering with self + * via 127.0.0.0/8. + */ + if (!ifp && cmd_allow_reserved_ranges_get()) + ifp = if_get_vrf_loopback(peer->bgp->vrf_id); + if (!ifp) { /* * BGP views do not currently get proper data @@ -1046,19 +1055,19 @@ static bool bgp_table_map_apply(struct route_map *map, const struct prefix *p, return false; } -static struct thread *bgp_tm_thread_connect; +static struct event *bgp_tm_thread_connect; static bool bgp_tm_status_connected; static bool bgp_tm_chunk_obtained; #define BGP_FLOWSPEC_TABLE_CHUNK 100000 static uint32_t bgp_tm_min, bgp_tm_max, bgp_tm_chunk_size; struct bgp *bgp_tm_bgp; -static void bgp_zebra_tm_connect(struct thread *t) +static void bgp_zebra_tm_connect(struct event *t) { struct zclient *zclient; int delay = 10, ret = 0; - zclient = THREAD_ARG(t); + zclient = EVENT_ARG(t); if (bgp_tm_status_connected && zclient->sock > 0) delay = 60; else { @@ -1066,14 +1075,17 @@ static void bgp_zebra_tm_connect(struct thread *t) ret = tm_table_manager_connect(zclient); } if (ret < 0) { - zlog_info("Error connecting to table manager!"); + zlog_err("Error connecting to table manager!"); bgp_tm_status_connected = false; } else { - if (!bgp_tm_status_connected) - zlog_debug("Connecting to table manager. Success"); + if (!bgp_tm_status_connected) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug( + "Connecting to table manager. Success"); + } bgp_tm_status_connected = true; if (!bgp_tm_chunk_obtained) { - if (bgp_zebra_get_table_range(bgp_tm_chunk_size, + if (bgp_zebra_get_table_range(zclient, bgp_tm_chunk_size, &bgp_tm_min, &bgp_tm_max) >= 0) { bgp_tm_chunk_obtained = true; @@ -1082,8 +1094,8 @@ static void bgp_zebra_tm_connect(struct thread *t) } } } - thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, - &bgp_tm_thread_connect); + event_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, + &bgp_tm_thread_connect); } bool bgp_zebra_tm_chunk_obtained(void) @@ -1113,18 +1125,18 @@ void bgp_zebra_init_tm_connect(struct bgp *bgp) bgp_tm_min = bgp_tm_max = 0; bgp_tm_chunk_size = BGP_FLOWSPEC_TABLE_CHUNK; bgp_tm_bgp = bgp; - thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay, - &bgp_tm_thread_connect); + event_add_timer(bm->master, bgp_zebra_tm_connect, zclient_sync, delay, + &bgp_tm_thread_connect); } -int bgp_zebra_get_table_range(uint32_t chunk_size, +int bgp_zebra_get_table_range(struct zclient *zc, uint32_t chunk_size, uint32_t *start, uint32_t *end) { int ret; if (!bgp_tm_status_connected) return -1; - ret = tm_get_table_chunk(zclient, chunk_size, start, end); + ret = tm_get_table_chunk(zc, chunk_size, start, end); if (ret < 0) { flog_err(EC_BGP_TABLE_CHUNK, "BGP: Error getting table chunk %u", chunk_size); @@ -1309,6 +1321,14 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, uint32_t bos = 0; uint32_t exp = 0; + /* + * BGP is installing this route and bgp has been configured + * to suppress announcements until the route has been installed + * let's set the fact that we expect this route to be installed + */ + if (BGP_SUPPRESS_FIB_ENABLED(bgp)) + SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING); + /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. */ @@ -1425,7 +1445,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR))) - api_nh->srte_color = info->attr->srte_color; + api_nh->srte_color = bgp_attr_get_color(info->attr); if (bgp_debug_zebra(&api.prefix)) { if (mpinfo->extra) { @@ -1760,6 +1780,12 @@ void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info, struct zapi_route api; struct peer *peer; + /* + * If we are withdrawing the route, we don't need to have this + * flag set. So unset it. + */ + UNSET_FLAG(info->net->flags, BGP_NODE_FIB_INSTALL_PENDING); + /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. */ @@ -1888,7 +1914,7 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, redist_add_instance(&zclient->mi_redist[afi][type], instance); } else { - if (vrf_bitmap_check(zclient->redist[afi][type], bgp->vrf_id)) + if (vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; #ifdef ENABLE_BGP_VNC @@ -1898,7 +1924,7 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, } #endif - vrf_bitmap_set(zclient->redist[afi][type], bgp->vrf_id); + vrf_bitmap_set(&zclient->redist[afi][type], bgp->vrf_id); } /* @@ -2019,9 +2045,9 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type, return CMD_WARNING; redist_del_instance(&zclient->mi_redist[afi][type], instance); } else { - if (!vrf_bitmap_check(zclient->redist[afi][type], bgp->vrf_id)) + if (!vrf_bitmap_check(&zclient->redist[afi][type], bgp->vrf_id)) return CMD_WARNING; - vrf_bitmap_unset(zclient->redist[afi][type], bgp->vrf_id); + vrf_bitmap_unset(&zclient->redist[afi][type], bgp->vrf_id); } if (bgp_install_info_to_zebra(bgp)) { @@ -2596,8 +2622,8 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient, } /* Find the bgp route node */ - dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, &p, - &bgp->vrf_prd); + dest = bgp_safi_node_lookup(bgp->rib[afi][safi], safi, &p, + &bgp->vrf_prd); if (!dest) return -1; @@ -2833,9 +2859,6 @@ static void bgp_zebra_connected(struct zclient *zclient) bgp_zebra_instance_register(bgp); - /* tell label pool that zebra is connected */ - bgp_lp_event_zebra_up(); - /* TODO - What if we have peers and networks configured, do we have to * kick-start them? */ @@ -2963,9 +2986,9 @@ static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS) if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "Rx L3-VNI ADD VRF %s VNI %u RMAC svi-mac %pEA vrr-mac %pEA filter %s svi-if %u", - vrf_id_to_name(vrf_id), l3vni, &svi_rmac, - &vrr_rmac, + "Rx L3-VNI ADD VRF %s VNI %u Originator-IP %pI4 RMAC svi-mac %pEA vrr-mac %pEA filter %s svi-if %u", + vrf_id_to_name(vrf_id), l3vni, &originator_ip, + &svi_rmac, &vrr_rmac, filter ? "prefix-routes-only" : "none", svi_ifindex); @@ -3140,54 +3163,6 @@ static int bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS) return 0; } -static int bgp_zebra_process_label_chunk(ZAPI_CALLBACK_ARGS) -{ - struct stream *s = NULL; - uint8_t response_keep; - uint32_t first; - uint32_t last; - uint8_t proto; - unsigned short instance; - - s = zclient->ibuf; - STREAM_GETC(s, proto); - STREAM_GETW(s, instance); - STREAM_GETC(s, response_keep); - STREAM_GETL(s, first); - STREAM_GETL(s, last); - - if (zclient->redist_default != proto) { - flog_err(EC_BGP_LM_ERROR, "Got LM msg with wrong proto %u", - proto); - return 0; - } - if (zclient->instance != instance) { - flog_err(EC_BGP_LM_ERROR, "Got LM msg with wrong instance %u", - proto); - return 0; - } - - if (first > last || - first < MPLS_LABEL_UNRESERVED_MIN || - last > MPLS_LABEL_UNRESERVED_MAX) { - - flog_err(EC_BGP_LM_ERROR, "%s: Invalid Label chunk: %u - %u", - __func__, first, last); - return 0; - } - if (BGP_DEBUG(zebra, ZEBRA)) { - zlog_debug("Label Chunk assign: %u - %u (%u) ", - first, last, response_keep); - } - - bgp_lp_event_chunk(response_keep, first, last); - - return 0; - -stream_failure: /* for STREAM_GETX */ - return -1; -} - extern struct zebra_privs_t bgpd_privs; static int bgp_ifp_create(struct interface *ifp) @@ -3406,7 +3381,6 @@ static zclient_handler *const bgp_handlers[] = { [ZEBRA_L3VNI_DEL] = bgp_zebra_process_local_l3vni, [ZEBRA_IP_PREFIX_ROUTE_ADD] = bgp_zebra_process_local_ip_prefix, [ZEBRA_IP_PREFIX_ROUTE_DEL] = bgp_zebra_process_local_ip_prefix, - [ZEBRA_GET_LABEL_CHUNK] = bgp_zebra_process_label_chunk, [ZEBRA_RULE_NOTIFY_OWNER] = rule_notify_owner, [ZEBRA_IPSET_NOTIFY_OWNER] = ipset_notify_owner, [ZEBRA_IPSET_ENTRY_NOTIFY_OWNER] = ipset_entry_notify_owner, @@ -3443,8 +3417,57 @@ void bgp_if_init(void) hook_register_prio(if_del, 0, bgp_if_delete_hook); } -void bgp_zebra_init(struct thread_master *master, unsigned short instance) +static void bgp_start_label_manager(struct event *start) +{ + bgp_zebra_label_manager_connect(); +} + +static bool bgp_zebra_label_manager_ready(void) +{ + return (zclient_sync->sock > 0); +} + +static bool bgp_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 false; + } + /* 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) == ZCLIENT_SEND_FAILURE) { + zlog_warn("%s: failed sending hello for synchronous zclient!", + __func__); + close(zclient_sync->sock); + zclient_sync->sock = -1; + return false; + } + + /* 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 false; + } + + /* tell label pool that zebra is connected */ + bgp_lp_event_zebra_up(); + + return true; +} + +void bgp_zebra_init(struct event_loop *master, unsigned short instance) { + struct zclient_options options = zclient_options_default; + + options.synchronous = true; zclient_num_connects = 0; if_zapi_callbacks(bgp_ifp_create, bgp_ifp_up, @@ -3456,6 +3479,18 @@ void bgp_zebra_init(struct thread_master *master, unsigned short instance) zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs); zclient->zebra_connected = bgp_zebra_connected; zclient->instance = instance; + + /* Initialize special zclient for synchronous message exchanges. */ + zclient_sync = zclient_new(master, &options, NULL, 0); + zclient_sync->sock = -1; + zclient_sync->redist_default = ZEBRA_ROUTE_BGP; + zclient_sync->instance = instance; + zclient_sync->session_id = 1; + zclient_sync->privs = &bgpd_privs; + + if (!bgp_zebra_label_manager_ready()) + event_add_timer(master, bgp_start_label_manager, NULL, 1, + &bm->t_bgp_start_label_manager); } void bgp_zebra_destroy(void) @@ -3465,6 +3500,12 @@ void bgp_zebra_destroy(void) zclient_stop(zclient); zclient_free(zclient); zclient = NULL; + + if (zclient_sync == NULL) + return; + zclient_stop(zclient_sync); + zclient_free(zclient_sync); + zclient_sync = NULL; } int bgp_zebra_num_connects(void) @@ -3748,16 +3789,22 @@ int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) struct zapi_cap api; int ret = BGP_GR_SUCCESS; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: Sending %sable for %s", __func__, + disable ? "dis" : "en", bgp->name_pretty); + if (zclient == NULL) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("zclient invalid"); + zlog_debug("%s: %s zclient invalid", __func__, + bgp->name_pretty); 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"); + zlog_debug("%s: %s client not connected", __func__, + bgp->name_pretty); return BGP_GR_FAILURE; } @@ -3776,7 +3823,8 @@ int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) == ZCLIENT_SEND_FAILURE) { - zlog_err("error sending capability"); + zlog_err("%s: %s error sending capability", __func__, + bgp->name_pretty); ret = BGP_GR_FAILURE; } else { if (disable) @@ -3785,7 +3833,8 @@ int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) bgp->present_zebra_gr_state = ZEBRA_GR_ENABLE; if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("send capabilty success"); + zlog_debug("%s: %s send capabilty success", __func__, + bgp->name_pretty); ret = BGP_GR_SUCCESS; } return ret; @@ -3794,32 +3843,41 @@ int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable) /* 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) +int bgp_zebra_update(struct bgp *bgp, afi_t afi, safi_t safi, + enum zserv_client_capabilities type) { struct zapi_cap api = {0}; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: %s afi: %u safi: %u Command %s", __func__, + bgp->name_pretty, afi, safi, + zserv_gr_client_cap_string(type)); + if (zclient == NULL) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("zclient == NULL, invalid"); + zlog_debug("%s: %s zclient == NULL, invalid", __func__, + bgp->name_pretty); 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"); + zlog_debug("%s: %s client not connected", __func__, + bgp->name_pretty); return BGP_GR_FAILURE; } api.afi = afi; api.safi = safi; - api.vrf_id = vrf_id; + api.vrf_id = bgp->vrf_id; api.cap = type; if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) == ZCLIENT_SEND_FAILURE) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("error sending capability"); + zlog_debug("%s: %s error sending capability", __func__, + bgp->name_pretty); return BGP_GR_FAILURE; } return BGP_GR_SUCCESS; @@ -3831,6 +3889,10 @@ int bgp_zebra_stale_timer_update(struct bgp *bgp) { struct zapi_cap api; + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("%s: %s Timer Update to %u", __func__, + bgp->name_pretty, bgp->rib_stale_time); + if (zclient == NULL) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("zclient invalid"); @@ -3840,7 +3902,8 @@ int bgp_zebra_stale_timer_update(struct bgp *bgp) /* Check if the client is connected */ if ((zclient->sock < 0) || (zclient->t_connect)) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("client not connected"); + zlog_debug("%s: %s client not connected", __func__, + bgp->name_pretty); return BGP_GR_FAILURE; } @@ -3851,11 +3914,11 @@ int bgp_zebra_stale_timer_update(struct bgp *bgp) if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api) == ZCLIENT_SEND_FAILURE) { if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("error sending capability"); + zlog_debug("%s: %s error sending capability", __func__, + bgp->name_pretty); return BGP_GR_FAILURE; } - if (BGP_DEBUG(zebra, ZEBRA)) - zlog_debug("send capabilty success"); + return BGP_GR_SUCCESS; } @@ -3868,3 +3931,82 @@ int bgp_zebra_srv6_manager_release_locator_chunk(const char *name) { return srv6_manager_release_locator_chunk(zclient, name); } + +void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label, + ifindex_t ifindex, vrf_id_t vrf_id, + enum lsp_types_t ltype, struct prefix *p, + uint32_t num_labels, + mpls_label_t out_labels[]) +{ + struct zapi_labels zl = {}; + struct zapi_nexthop *znh; + int i = 0; + + zl.type = ltype; + zl.local_label = label; + zl.nexthop_num = 1; + znh = &zl.nexthops[0]; + if (p->family == AF_INET) + IPV4_ADDR_COPY(&znh->gate.ipv4, &p->u.prefix4); + else + IPV6_ADDR_COPY(&znh->gate.ipv6, &p->u.prefix6); + if (ifindex == IFINDEX_INTERNAL) + znh->type = (p->family == AF_INET) ? NEXTHOP_TYPE_IPV4 + : NEXTHOP_TYPE_IPV6; + else + znh->type = (p->family == AF_INET) ? NEXTHOP_TYPE_IPV4_IFINDEX + : NEXTHOP_TYPE_IPV6_IFINDEX; + znh->ifindex = ifindex; + znh->vrf_id = vrf_id; + if (num_labels == 0) + znh->label_num = 0; + else { + if (num_labels > MPLS_MAX_LABELS) + znh->label_num = MPLS_MAX_LABELS; + else + znh->label_num = num_labels; + for (i = 0; i < znh->label_num; i++) + znh->labels[i] = out_labels[i]; + } + /* vrf_id is DEFAULT_VRF */ + zebra_send_mpls_labels(zclient, cmd, &zl); +} + +bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size) +{ + int ret; + uint32_t start, end; + + if (!zclient_sync || !bgp_zebra_label_manager_ready()) + return false; + + 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 false; + } + + if (start > end || start < MPLS_LABEL_UNRESERVED_MIN || + end > MPLS_LABEL_UNRESERVED_MAX) { + flog_err(EC_BGP_LM_ERROR, "%s: Invalid Label chunk: %u - %u", + __func__, start, end); + return false; + } + + bgp_lp_event_chunk(start, end); + + return true; +} + +void bgp_zebra_release_label_range(uint32_t start, uint32_t end) +{ + int ret; + + if (!zclient_sync || !bgp_zebra_label_manager_ready()) + return; + + ret = lm_release_label_chunk(zclient_sync, start, end); + if (ret < 0) + zlog_warn("%s: error releasing label range!", __func__); +} diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 8a0203c32db1..a0f78194606d 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -18,14 +18,13 @@ /* 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(struct event_loop *master, unsigned short instance); extern void bgp_if_init(void); extern void bgp_zebra_init_tm_connect(struct bgp *bgp); extern uint32_t bgp_zebra_tm_get_id(void); extern bool bgp_zebra_tm_chunk_obtained(void); extern void bgp_zebra_destroy(void); -extern int bgp_zebra_get_table_range(uint32_t chunk_size, +extern int bgp_zebra_get_table_range(struct zclient *zc, uint32_t chunk_size, uint32_t *start, uint32_t *end); extern int bgp_if_update_all(void); extern void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, @@ -114,8 +113,16 @@ 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_update(struct bgp *bgp, afi_t afi, safi_t safi, + enum zserv_client_capabilities); extern int bgp_zebra_stale_timer_update(struct bgp *bgp); extern int bgp_zebra_srv6_manager_get_locator_chunk(const char *name); extern int bgp_zebra_srv6_manager_release_locator_chunk(const char *name); +extern void bgp_zebra_send_nexthop_label(int cmd, mpls_label_t label, + ifindex_t index, vrf_id_t vrfid, + enum lsp_types_t ltype, + struct prefix *p, uint32_t num_labels, + mpls_label_t out_labels[]); +extern bool bgp_zebra_request_label_range(uint32_t base, uint32_t chunk_size); +extern void bgp_zebra_release_label_range(uint32_t start, uint32_t end); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 96c6a111ce3b..5c97f90c5525 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -6,7 +6,7 @@ #include #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "stream.h" #include "ringbuf.h" @@ -80,7 +80,6 @@ #include "bgp_trace.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); -DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); DEFINE_QOBJ_TYPE(bgp_master); DEFINE_QOBJ_TYPE(bgp); DEFINE_QOBJ_TYPE(peer); @@ -1126,9 +1125,9 @@ static void peer_free(struct peer *peer) bgp_timer_set(peer); bgp_reads_off(peer); bgp_writes_off(peer); - thread_cancel_event_ready(bm->master, peer); + event_cancel_event_ready(bm->master, peer); FOREACH_AFI_SAFI (afi, safi) - THREAD_OFF(peer->t_revalidate_all[afi][safi]); + EVENT_OFF(peer->t_revalidate_all[afi][safi]); assert(!peer->t_write); assert(!peer->t_read); BGP_EVENT_FLUSH(peer); @@ -1183,7 +1182,7 @@ static void peer_free(struct peer *peer) bgp_peer_remove_bfd_config(peer); FOREACH_AFI_SAFI (afi, safi) - bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE); + bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE, 0); if (peer->change_local_as_pretty) XFREE(MTYPE_BGP, peer->change_local_as_pretty); @@ -1398,6 +1397,7 @@ struct peer *peer_new(struct bgp *bgp) SET_FLAG(peer->af_flags_invert[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE; + peer->addpath_best_selected[afi][safi] = 0; peer->soo[afi][safi] = NULL; } @@ -1496,9 +1496,11 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->v_delayopen = peer_src->v_delayopen; /* password apply */ - if (peer_src->password && !peer_dst->password) + if (peer_src->password) { + XFREE(MTYPE_PEER_PASSWORD, peer_dst->password); peer_dst->password = XSTRDUP(MTYPE_PEER_PASSWORD, peer_src->password); + } FOREACH_AFI_SAFI (afi, safi) { peer_dst->afc[afi][safi] = peer_src->afc[afi][safi]; @@ -1541,6 +1543,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src) peer_dst->ifname = XSTRDUP(MTYPE_BGP_PEER_IFNAME, peer_src->ifname); } + peer_dst->ttl = peer_src->ttl; } static int bgp_peer_conf_if_to_su_update_v4(struct peer *peer, @@ -2316,6 +2319,7 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) struct listnode *node, *nnode; struct peer *tmp_peer; struct bgp *bgp; + safi_t safi_check; /* Nothing to do if we've already activated this peer */ if (peer->afc[afi][safi]) @@ -2346,16 +2350,22 @@ 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 (ret != BGP_ERR_PEER_SAFI_CONFLICT && safi == SAFI_LABELED_UNICAST - && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) { + * afi/labeled-unicast or afi/mpls-vpn, recalc bestpaths to trigger + * label allocation */ + if (safi == SAFI_LABELED_UNICAST) + safi_check = SAFI_UNICAST; + else + safi_check = safi; + if (ret != BGP_ERR_PEER_SAFI_CONFLICT && + (safi == SAFI_LABELED_UNICAST || safi == SAFI_MPLS_VPN) && + !bgp->allocate_mpls_labels[afi][safi_check]) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "peer(s) are now active for labeled-unicast, allocate MPLS labels"); - - bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1; - bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); + "peer(s) are now active for %s, allocate MPLS labels", + safi2str(safi)); + bgp->allocate_mpls_labels[afi][safi_check] = 1; + bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi_check); } if (safi == SAFI_FLOWSPEC) { @@ -2421,6 +2431,7 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) struct peer *tmp_peer; struct listnode *node, *nnode; struct bgp *bgp; + safi_t safi_check; /* Nothing to do if we've already de-activated this peer */ if (!peer->afc[afi][safi]) @@ -2442,17 +2453,22 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) bgp = peer->bgp; /* If this is the last peer to be deactivated for this - * afi/labeled-unicast recalc bestpaths to trigger label deallocation */ - if (safi == SAFI_LABELED_UNICAST - && bgp->allocate_mpls_labels[afi][SAFI_UNICAST] - && !bgp_afi_safi_peer_exists(bgp, afi, safi)) { + * afi/labeled-unicast or afi/mpls-vpn, recalc bestpaths to trigger + * label deallocation */ + if (safi == SAFI_LABELED_UNICAST) + safi_check = SAFI_UNICAST; + else + safi_check = safi; + if ((safi == SAFI_LABELED_UNICAST || safi == SAFI_MPLS_VPN) && + bgp->allocate_mpls_labels[afi][safi_check] && + !bgp_afi_safi_peer_exists(bgp, afi, safi)) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "peer(s) are no longer active for labeled-unicast, deallocate MPLS labels"); - - bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0; - bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); + "peer(s) are no longer active for %s, deallocate MPLS labels", + safi2str(safi)); + bgp->allocate_mpls_labels[afi][safi_check] = 0; + bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi_check); } return ret; } @@ -2467,16 +2483,16 @@ void peer_nsf_stop(struct peer *peer) FOREACH_AFI_SAFI_NSF (afi, safi) { peer->nsf[afi][safi] = 0; - THREAD_OFF(peer->t_llgr_stale[afi][safi]); + EVENT_OFF(peer->t_llgr_stale[afi][safi]); } if (peer->t_gr_restart) { - THREAD_OFF(peer->t_gr_restart); + EVENT_OFF(peer->t_gr_restart); if (bgp_debug_neighbor_events(peer)) zlog_debug("%pBP graceful restart timer stopped", peer); } if (peer->t_gr_stale) { - THREAD_OFF(peer->t_gr_stale); + EVENT_OFF(peer->t_gr_stale); if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP graceful restart stalepath timer stopped", @@ -2516,9 +2532,9 @@ int peer_delete(struct peer *peer) bgp_keepalives_off(peer); bgp_reads_off(peer); bgp_writes_off(peer); - thread_cancel_event_ready(bm->master, peer); + event_cancel_event_ready(bm->master, peer); FOREACH_AFI_SAFI (afi, safi) - THREAD_OFF(peer->t_revalidate_all[afi][safi]); + EVENT_OFF(peer->t_revalidate_all[afi][safi]); 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)); @@ -3197,11 +3213,11 @@ int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer, return 0; } -static void bgp_startup_timer_expire(struct thread *thread) +static void bgp_startup_timer_expire(struct event *thread) { struct bgp *bgp; - bgp = THREAD_ARG(thread); + bgp = EVENT_ARG(thread); bgp->t_startup = NULL; } @@ -3327,6 +3343,7 @@ static struct bgp *bgp_create(as_t *as, const char *name, bgp_addpath_init_bgp_data(&bgp->tx_addpath); bgp->fast_convergence = false; bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME; + bgp->rmap_def_originate_eval_timer = RMAP_DEFAULT_ORIGINATE_EVAL_TIMER; #ifdef ENABLE_BGP_VNC if (inst_type != BGP_INSTANCE_TYPE_VRF) { @@ -3352,11 +3369,18 @@ static struct bgp *bgp_create(as_t *as, const char *name, SET_FLAG(bgp->af_flags[afi][SAFI_MPLS_VPN], BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL); } + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + bgp_label_per_nexthop_cache_init( + &bgp->mpls_labels_per_nexthop[afi]); + + bgp_mplsvpn_nh_label_bind_cache_init(&bgp->mplsvpn_nh_label_bind); + if (name) bgp->name = XSTRDUP(MTYPE_BGP, name); - thread_add_timer(bm->master, bgp_startup_timer_expire, bgp, - bgp->restart_time, &bgp->t_startup); + event_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) { @@ -3394,8 +3418,6 @@ static struct bgp *bgp_create(as_t *as, const char *name, /* assign a unique rd id for auto derivation of vrf's RD */ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id); - bgp->evpn_info = XCALLOC(MTYPE_BGP_EVPN_INFO, - sizeof(struct bgp_evpn_info)); bgp_evpn_init(bgp); bgp_evpn_vrf_es_init(bgp); bgp_pbr_init(bgp); @@ -3632,9 +3654,9 @@ static void bgp_zclient_set_redist(afi_t afi, int type, unsigned short instance, instance); } else { if (set) - vrf_bitmap_set(zclient->redist[afi][type], vrf_id); + vrf_bitmap_set(&zclient->redist[afi][type], vrf_id); else - vrf_bitmap_unset(zclient->redist[afi][type], vrf_id); + vrf_bitmap_unset(&zclient->redist[afi][type], vrf_id); } } @@ -3695,11 +3717,8 @@ void bgp_instance_down(struct bgp *bgp) struct listnode *next; /* Stop timers. */ - if (bgp->t_rmap_def_originate_eval) { - THREAD_OFF(bgp->t_rmap_def_originate_eval); - bgp_unlock(bgp); /* TODO - This timer is started with a lock - - why? */ - } + if (bgp->t_rmap_def_originate_eval) + EVENT_OFF(bgp->t_rmap_def_originate_eval); /* Bring down peers, so corresponding routes are purged. */ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) { @@ -3748,39 +3767,39 @@ int bgp_delete(struct bgp *bgp) hook_call(bgp_inst_delete, bgp); FOREACH_AFI_SAFI (afi, safi) - THREAD_OFF(bgp->t_revalidate[afi][safi]); + EVENT_OFF(bgp->t_revalidate[afi][safi]); - THREAD_OFF(bgp->t_condition_check); - THREAD_OFF(bgp->t_startup); - THREAD_OFF(bgp->t_maxmed_onstartup); - THREAD_OFF(bgp->t_update_delay); - THREAD_OFF(bgp->t_establish_wait); + EVENT_OFF(bgp->t_condition_check); + EVENT_OFF(bgp->t_startup); + EVENT_OFF(bgp->t_maxmed_onstartup); + EVENT_OFF(bgp->t_update_delay); + EVENT_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) { - struct thread *t; + struct event *t; gr_info = &bgp->gr_info[afi][safi]; if (!gr_info) continue; t = gr_info->t_select_deferral; if (t) { - void *info = THREAD_ARG(t); + void *info = EVENT_ARG(t); XFREE(MTYPE_TMP, info); } - THREAD_OFF(gr_info->t_select_deferral); + EVENT_OFF(gr_info->t_select_deferral); t = gr_info->t_route_select; if (t) { - void *info = THREAD_ARG(t); + void *info = EVENT_ARG(t); XFREE(MTYPE_TMP, info); } - THREAD_OFF(gr_info->t_route_select); + EVENT_OFF(gr_info->t_route_select); } if (BGP_DEBUG(zebra, ZEBRA)) { @@ -3802,11 +3821,8 @@ int bgp_delete(struct bgp *bgp) vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6); /* Stop timers. */ - if (bgp->t_rmap_def_originate_eval) { - THREAD_OFF(bgp->t_rmap_def_originate_eval); - bgp_unlock(bgp); /* TODO - This timer is started with a lock - - why? */ - } + if (bgp->t_rmap_def_originate_eval) + EVENT_OFF(bgp->t_rmap_def_originate_eval); /* Inform peers we're going down. */ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) @@ -3842,6 +3858,23 @@ int bgp_delete(struct bgp *bgp) #ifdef ENABLE_BGP_VNC rfapi_delete(bgp); #endif + + /* Free memory allocated with aggregate address configuration. */ + FOREACH_AFI_SAFI (afi, safi) { + struct bgp_aggregate *aggregate = NULL; + + for (struct bgp_dest *dest = + bgp_table_top(bgp->aggregate[afi][safi]); + dest; dest = bgp_route_next(dest)) { + aggregate = bgp_dest_get_bgp_aggregate_info(dest); + if (aggregate == NULL) + continue; + + bgp_dest_set_bgp_aggregate_info(dest, NULL); + bgp_free_aggregate_info(aggregate); + } + } + bgp_cleanup_routes(bgp); for (afi = 0; afi < AFI_MAX; ++afi) { @@ -3897,7 +3930,7 @@ int bgp_delete(struct bgp *bgp) if (bgp->process_queue) work_queue_free_and_null(&bgp->process_queue); - thread_master_free_unused(bm->master); + event_master_free_unused(bm->master); bgp_unlock(bgp); /* initial reference */ return 0; @@ -3950,8 +3983,6 @@ void bgp_free(struct bgp *bgp) bgp_evpn_cleanup(bgp); bgp_pbr_cleanup(bgp); - bgp_srv6_cleanup(bgp); - XFREE(MTYPE_BGP_EVPN_INFO, bgp->evpn_info); for (afi = AFI_IP; afi < AFI_MAX; afi++) { enum vpn_policy_direction dir; @@ -3969,8 +4000,20 @@ void bgp_free(struct bgp *bgp) ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]); if (bgp->vpn_policy[afi].tovpn_rd_pretty) XFREE(MTYPE_BGP, bgp->vpn_policy[afi].tovpn_rd_pretty); + if (bgp->vpn_policy[afi].tovpn_sid_locator != NULL) + srv6_locator_chunk_free( + &bgp->vpn_policy[afi].tovpn_sid_locator); + if (bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent != NULL) + XFREE(MTYPE_BGP_SRV6_SID, + bgp->vpn_policy[afi] + .tovpn_zebra_vrf_sid_last_sent); + if (bgp->vpn_policy[afi].tovpn_sid != NULL) { + sid_unregister(bgp, bgp->vpn_policy[afi].tovpn_sid); + XFREE(MTYPE_BGP_SRV6_SID, + bgp->vpn_policy[afi].tovpn_sid); + } } - + bgp_srv6_cleanup(bgp); bgp_confederation_id_unset(bgp); XFREE(MTYPE_BGP, bgp->as_pretty); @@ -4371,8 +4414,7 @@ void peer_change_action(struct peer *peer, afi_t afi, safi_t safi, bgp_notify_send(peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_CONFIG_CHANGE); } else if (type == peer_change_reset_in) { - if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV) - || CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV)) bgp_route_refresh_send(peer, afi, safi, 0, 0, 0, BGP_ROUTE_REFRESH_NORMAL); else { @@ -4528,7 +4570,7 @@ static void peer_flag_modify_action(struct peer *peer, uint64_t flag) UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); if (peer->t_pmax_restart) { - THREAD_OFF(peer->t_pmax_restart); + EVENT_OFF(peer->t_pmax_restart); if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP Maximum-prefix restart timer canceled", @@ -5675,21 +5717,9 @@ void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi, if (bgp_soft_reconfig_in(peer, afi, safi)) return; - if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV) || - CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) { - if (CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_ADV) && - (CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_RCV) || - CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV))) - peer_clear_soft(peer, afi, safi, - BGP_CLEAR_SOFT_IN_ORF_PREFIX); - else - bgp_route_refresh_send( - peer, afi, safi, 0, 0, 0, - BGP_ROUTE_REFRESH_NORMAL); - } + if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV)) + bgp_route_refresh_send(peer, afi, safi, 0, 0, 0, + BGP_ROUTE_REFRESH_NORMAL); } } @@ -6930,11 +6960,15 @@ static void peer_prefix_list_update(struct prefix_list *plist) /* If we touch prefix-list, we need to process * new updates. This is important for ORF to - * work correctly as well. + * work correctly. */ - if (peer->afc_nego[afi][safi]) - peer_on_policy_change(peer, afi, safi, - 0); + if (CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_SM_ADV) && + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_RCV)) + peer_clear_soft( + peer, afi, safi, + BGP_CLEAR_SOFT_IN_ORF_PREFIX); } } for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) { @@ -7398,7 +7432,7 @@ static bool peer_maximum_prefix_clear_overflow(struct peer *peer) UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW); if (peer->t_pmax_restart) { - THREAD_OFF(peer->t_pmax_restart); + EVENT_OFF(peer->t_pmax_restart); if (bgp_debug_neighbor_events(peer)) zlog_debug( "%pBP Maximum-prefix restart timer cancelled", @@ -7898,19 +7932,15 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi, if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) { if (CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_SM_ADV) - && (CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_RCV) - || CHECK_FLAG(peer->af_cap[afi][safi], - PEER_CAP_ORF_PREFIX_RM_OLD_RCV))) { + PEER_CAP_ORF_PREFIX_SM_ADV) && + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_CAP_ORF_PREFIX_RM_RCV)) { struct bgp_filter *filter = &peer->filter[afi][safi]; uint8_t prefix_type; if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_RCV)) prefix_type = ORF_TYPE_PREFIX; - else - prefix_type = ORF_TYPE_PREFIX_OLD; if (filter->plist[FILTER_IN].plist) { if (CHECK_FLAG(peer->af_sflags[afi][safi], @@ -7947,8 +7977,7 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi, /* If neighbor has route refresh capability, send route refresh message to the peer. */ - if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV) - || CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV)) + if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_RCV)) bgp_route_refresh_send( peer, afi, safi, 0, 0, 0, BGP_ROUTE_REFRESH_NORMAL); @@ -8010,7 +8039,7 @@ char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json, return buf; } -void bgp_master_init(struct thread_master *master, const int buffer_size, +void bgp_master_init(struct event_loop *master, const int buffer_size, struct list *addresses) { qobj_init(); @@ -8034,6 +8063,8 @@ void bgp_master_init(struct thread_master *master, const int buffer_size, bm->tcp_dscp = IPTOS_PREC_INTERNETCONTROL; bm->inq_limit = BM_DEFAULT_Q_LIMIT; bm->outq_limit = BM_DEFAULT_Q_LIMIT; + bm->t_bgp_sync_label_manager = NULL; + bm->t_bgp_start_label_manager = NULL; bgp_mac_init(); /* init the rd id space. @@ -8236,6 +8267,9 @@ void bgp_init(unsigned short instance) bgp_lp_vty_init(); + bgp_label_per_nexthop_init(); + bgp_mplsvpn_nexthop_init(); + cmd_variable_handler_register(bgp_viewvrf_var_handlers); } @@ -8276,7 +8310,9 @@ void bgp_terminate(void) if (bm->listen_sockets) list_delete(&bm->listen_sockets); - THREAD_OFF(bm->t_rmap_update); + EVENT_OFF(bm->t_rmap_update); + EVENT_OFF(bm->t_bgp_sync_label_manager); + EVENT_OFF(bm->t_bgp_start_label_manager); bgp_mac_finish(); } @@ -8287,6 +8323,7 @@ struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, int ret; struct peer *peer; union sockunion su; + struct peer_group *group; /* Get peer sockunion. */ ret = str2sockunion(ip_str, &su); @@ -8295,6 +8332,12 @@ struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, if (!peer) { peer = peer_lookup_by_hostname(bgp, ip_str); + if (!peer) { + group = peer_group_lookup(bgp, ip_str); + if (group) + peer = listnode_head(group->peer); + } + if (!peer) { if (use_json) { json_object *json_no = NULL; @@ -8372,3 +8415,16 @@ static ssize_t printfrr_bp(struct fbuf *buf, struct printfrr_eargs *ea, return bprintfrr(buf, "%s(%s)", peer->host, peer->hostname ? peer->hostname : "Unknown"); } + +const struct message bgp_martian_type_str[] = { + {BGP_MARTIAN_IF_IP, "Self Interface IP"}, + {BGP_MARTIAN_TUN_IP, "Self Tunnel IP"}, + {BGP_MARTIAN_IF_MAC, "Self Interface MAC"}, + {BGP_MARTIAN_RMAC, "Self RMAC"}, + {BGP_MARTIAN_SOO, "Self Site-of-Origin"}, + {0}}; + +const char *bgp_martian_type2str(enum bgp_martian_type mt) +{ + return lookup_msg(bgp_martian_type_str, mt, "Unknown Martian Type"); +} diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index c0dd8d5ef410..edf9234424f4 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -98,7 +98,7 @@ struct bgp_master { struct list *bgp; /* BGP thread master. */ - struct thread_master *master; + struct event_loop *master; /* Listening sockets */ struct list *listen_sockets; @@ -126,7 +126,7 @@ struct bgp_master { uint64_t subgrp_idspace; /* timer to dampen route map changes */ - struct thread *t_rmap_update; /* Handle route map updates */ + struct event *t_rmap_update; /* Handle route map updates */ uint32_t rmap_update_timer; /* Route map update timer */ #define RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */ @@ -165,6 +165,9 @@ struct bgp_master { uint32_t inq_limit; uint32_t outq_limit; + struct event *t_bgp_sync_label_manager; + struct event *t_bgp_start_label_manager; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(bgp_master); @@ -211,6 +214,7 @@ struct vpn_policy { #define BGP_VPN_POLICY_TOVPN_RD_SET (1 << 1) #define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2) #define BGP_VPN_POLICY_TOVPN_SID_AUTO (1 << 3) +#define BGP_VPN_POLICY_TOVPN_LABEL_PER_NEXTHOP (1 << 4) /* * If we are importing another vrf into us keep a list of @@ -266,11 +270,11 @@ struct graceful_restart_info { /* Count of EOR received */ uint32_t eor_received; /* Deferral Timer */ - struct thread *t_select_deferral; + struct event *t_select_deferral; /* Routes Deferred */ uint32_t gr_deferred; /* Best route select */ - struct thread *t_route_select; + struct event *t_route_select; /* AFI, SAFI enabled */ bool af_enabled[AFI_MAX][SAFI_MAX]; /* Route update completed */ @@ -330,6 +334,9 @@ struct as_confed { char *as_pretty; }; +struct bgp_mplsvpn_nh_label_bind_cache; +PREDECL_RBTREE_UNIQ(bgp_mplsvpn_nh_label_bind_cache); + /* BGP instance structure. */ struct bgp { /* AS number of this BGP instance. */ @@ -406,15 +413,16 @@ struct bgp { struct as_confed *confed_peers; int confed_peers_cnt; - struct thread - *t_startup; /* start-up timer on only once at the beginning */ + /* start-up timer on only once at the beginning */ + struct event *t_startup; uint32_t v_maxmed_onstartup; /* Duration of max-med on start-up */ #define BGP_MAXMED_ONSTARTUP_UNCONFIGURED 0 /* 0 means off, its the default */ uint32_t maxmed_onstartup_value; /* Max-med value when active on start-up */ - struct thread - *t_maxmed_onstartup; /* non-null when max-med onstartup is on */ + + /* non-null when max-med onstartup is on */ + struct event *t_maxmed_onstartup; uint8_t maxmed_onstartup_over; /* Flag to make it effective only once */ bool v_maxmed_admin; /* true/false if max-med administrative is on/off @@ -428,9 +436,9 @@ struct bgp { uint32_t maxmed_value; /* Max-med value when its active */ /* BGP update delay on startup */ - struct thread *t_update_delay; - struct thread *t_establish_wait; - struct thread *t_revalidate[AFI_MAX][SAFI_MAX]; + struct event *t_update_delay; + struct event *t_establish_wait; + struct event *t_revalidate[AFI_MAX][SAFI_MAX]; uint8_t update_delay_over; uint8_t main_zebra_update_hold; @@ -499,6 +507,10 @@ struct bgp { #define BGP_FLAG_HARD_ADMIN_RESET (1ULL << 31) /* Evaluate the AIGP attribute during the best path selection process */ #define BGP_FLAG_COMPARE_AIGP (1ULL << 32) +/* For BGP-LU, force IPv4 local prefixes to use ipv4-explicit-null label */ +#define BGP_FLAG_LU_IPV4_EXPLICIT_NULL (1ULL << 33) +/* For BGP-LU, force IPv6 local prefixes to use ipv6-explicit-null label */ +#define BGP_FLAG_LU_IPV6_EXPLICIT_NULL (1ULL << 34) /* BGP default address-families. * New peers inherit enabled afi/safis from bgp instance. @@ -568,6 +580,13 @@ struct bgp { /* Allocate MPLS labels */ uint8_t allocate_mpls_labels[AFI_MAX][SAFI_MAX]; + /* Tree for next-hop lookup cache. */ + struct bgp_label_per_nexthop_cache_head + mpls_labels_per_nexthop[AFI_MAX]; + + /* Tree for mplsvpn next-hop label bind cache */ + struct bgp_mplsvpn_nh_label_bind_cache_head mplsvpn_nh_label_bind; + /* Allocate hash entries to store policy routing information * The hash are used to host pbr rules somewhere. * Actually, pbr will only be used by flowspec @@ -590,7 +609,8 @@ struct bgp { struct hash *pbr_action_hash; /* timer to re-evaluate neighbor default-originate route-maps */ - struct thread *t_rmap_def_originate_eval; + struct event *t_rmap_def_originate_eval; + uint16_t rmap_def_originate_eval_timer; #define RMAP_DEFAULT_ORIGINATE_EVAL_TIMER 5 /* BGP distance configuration. */ @@ -769,7 +789,7 @@ struct bgp { /* BGP Conditional advertisement */ uint32_t condition_check_period; uint32_t condition_filter_count; - struct thread *t_condition_check; + struct event *t_condition_check; /* BGP VPN SRv6 backend */ bool srv6_enabled; @@ -800,6 +820,8 @@ DECLARE_QOBJ_TYPE(bgp); struct bgp_interface { #define BGP_INTERFACE_MPLS_BGP_FORWARDING (1 << 0) +/* L3VPN multi domain switching */ +#define BGP_INTERFACE_MPLS_L3VPN_SWITCHING (1 << 1) uint32_t flags; }; @@ -978,7 +1000,7 @@ struct peer_af { /* * Trigger timer for bgp_announce_route(). */ - struct thread *t_announce_route; + struct event *t_announce_route; afi_t afi; safi_t safi; @@ -1109,7 +1131,6 @@ struct peer { /* BGP peer group. */ struct peer_group *group; - uint64_t version[AFI_MAX][SAFI_MAX]; /* BGP peer_af structures, per configured AF on this peer */ struct peer_af *peer_af_array[BGP_AF_MAX]; @@ -1221,8 +1242,7 @@ struct peer { /* Capability flags (reset in bgp_stop) */ uint32_t cap; #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_REFRESH_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 */ @@ -1260,8 +1280,6 @@ struct peer { #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 */ @@ -1509,24 +1527,24 @@ struct peer { _Atomic uint32_t v_gr_restart; /* Threads. */ - struct thread *t_read; - struct thread *t_write; - struct thread *t_start; - struct thread *t_connect_check_r; - struct thread *t_connect_check_w; - struct thread *t_connect; - struct thread *t_holdtime; - struct thread *t_routeadv; - struct thread *t_delayopen; - struct thread *t_pmax_restart; - struct thread *t_gr_restart; - struct thread *t_gr_stale; - struct thread *t_llgr_stale[AFI_MAX][SAFI_MAX]; - struct thread *t_revalidate_all[AFI_MAX][SAFI_MAX]; - struct thread *t_generate_updgrp_packets; - struct thread *t_process_packet; - struct thread *t_process_packet_error; - struct thread *t_refresh_stalepath; + struct event *t_read; + struct event *t_write; + struct event *t_start; + struct event *t_connect_check_r; + struct event *t_connect_check_w; + struct event *t_connect; + struct event *t_holdtime; + struct event *t_routeadv; + struct event *t_delayopen; + struct event *t_pmax_restart; + struct event *t_gr_restart; + struct event *t_gr_stale; + struct event *t_llgr_stale[AFI_MAX][SAFI_MAX]; + struct event *t_revalidate_all[AFI_MAX][SAFI_MAX]; + struct event *t_generate_updgrp_packets; + struct event *t_process_packet; + struct event *t_process_packet_error; + struct event *t_refresh_stalepath; /* Thread flags. */ _Atomic uint32_t thread_flags; @@ -1781,6 +1799,9 @@ struct peer { #define BGP_MAX_SOFT_VERSION 64 char *soft_version; + /* Add-Path Best selected paths number to advertise */ + uint8_t addpath_best_selected[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(peer); @@ -2095,6 +2116,26 @@ enum peer_change_type { peer_change_reset_out, }; +/* Enumeration of martian ("self") entry types. + * Routes carrying fields that match a self entry are considered martians + * and should be handled accordingly, i.e. dropped or import-filtered. + * Note: + * These "martians" are separate from routes optionally allowed via + * 'bgp allow-martian-nexthop'. The optionally allowed martians are + * simply prefixes caught by ipv4_martian(), i.e. routes outside + * the non-reserved IPv4 Unicast address space. + */ +enum bgp_martian_type { + BGP_MARTIAN_IF_IP, /* bgp->address_hash */ + BGP_MARTIAN_TUN_IP, /* bgp->tip_hash */ + BGP_MARTIAN_IF_MAC, /* bgp->self_mac_hash */ + BGP_MARTIAN_RMAC, /* bgp->rmac */ + BGP_MARTIAN_SOO, /* bgp->evpn_info->macvrf_soo */ +}; + +extern const struct message bgp_martian_type_str[]; +extern const char *bgp_martian_type2str(enum bgp_martian_type mt); + extern struct bgp_master *bm; extern unsigned int multipath_num; @@ -2155,7 +2196,7 @@ 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, const int buffer_size, +extern void bgp_master_init(struct event_loop *master, const int buffer_size, struct list *addresses); extern void bgp_init(unsigned short instance); @@ -2363,7 +2404,7 @@ extern int peer_ttl_security_hops_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 void bgp_route_map_update_timer(struct thread *thread); +extern void bgp_route_map_update_timer(struct event *thread); extern const char *bgp_get_name_by_role(uint8_t role); extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp); diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index 8d6db9d7759d..67c70431bd19 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -51,6 +51,8 @@ #include #endif /* HAVE_GLIBC_BACKTRACE */ +#define DEBUG_CLEANUP 0 + struct ethaddr rfapi_ethaddr0 = {{0}}; #define DEBUG_RFAPI_STR "RF API debugging/testing command\n" @@ -3677,11 +3679,36 @@ void rfapi_delete(struct bgp *bgp) { extern void rfp_clear_vnc_nve_all(void); /* can't fix correctly yet */ +#if DEBUG_CLEANUP + zlog_debug("%s: bgp %p", __func__, bgp); +#endif + /* * This clears queries and registered routes, and closes nves */ if (bgp->rfapi) rfp_clear_vnc_nve_all(); + + /* + * close any remaining descriptors + */ + struct rfapi *h = bgp->rfapi; + + if (h && h->descriptors.count) { + struct listnode *node, *nnode; + struct rfapi_descriptor *rfd; +#if DEBUG_CLEANUP + zlog_debug("%s: descriptor count %u", __func__, + h->descriptors.count); +#endif + for (ALL_LIST_ELEMENTS(&h->descriptors, node, nnode, rfd)) { +#if DEBUG_CLEANUP + zlog_debug("%s: closing rfd %p", __func__, rfd); +#endif + (void)rfapi_close(rfd); + } + } + bgp_rfapi_cfg_destroy(bgp, bgp->rfapi_cfg); bgp->rfapi_cfg = NULL; bgp_rfapi_destroy(bgp, bgp->rfapi); diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h index 44f7507d55a6..3f61d5be19f4 100644 --- a/bgpd/rfapi/rfapi.h +++ b/bgpd/rfapi/rfapi.h @@ -335,8 +335,7 @@ struct rfapi_rfp_cfg { * return value: * rfp_start_val rfp returned value passed on rfp_stop and other rfapi calls --------------------------------------------*/ -extern void *rfp_start(struct thread_master *master, - struct rfapi_rfp_cfg **cfgp, +extern void *rfp_start(struct event_loop *master, struct rfapi_rfp_cfg **cfgp, struct rfapi_rfp_cb_methods **cbmp); /*------------------------------------------ diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h index 3870c31fca54..32ea0a28214f 100644 --- a/bgpd/rfapi/rfapi_backend.h +++ b/bgpd/rfapi/rfapi_backend.h @@ -14,7 +14,7 @@ #include "bgpd/bgp_nexthop.h" extern void rfapi_init(void); -extern void vnc_zebra_init(struct thread_master *master); +extern void vnc_zebra_init(struct event_loop *master); extern void vnc_zebra_destroy(void); extern void rfapi_delete(struct bgp *); diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c index 5c68545398f5..27f7c88d7be1 100644 --- a/bgpd/rfapi/rfapi_import.c +++ b/bgpd/rfapi/rfapi_import.c @@ -15,7 +15,7 @@ #include "lib/memory.h" #include "lib/log.h" #include "lib/skiplist.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/stream.h" #include "lib/lib_errors.h" @@ -127,7 +127,6 @@ void rfapiCheckRouteCount(void) struct agg_node *rn; int holddown_count = 0; - int local_count = 0; int imported_count = 0; int remote_count = 0; @@ -146,9 +145,7 @@ void rfapiCheckRouteCount(void) ++holddown_count; } else { - if (RFAPI_LOCAL_BI(bpi)) { - ++local_count; - } else { + if (!RFAPI_LOCAL_BI(bpi)) { if (RFAPI_DIRECT_IMPORT_BI( bpi)) { ++imported_count; @@ -848,10 +845,10 @@ static void rfapiBgpInfoChainFree(struct bgp_path_info *bpi) if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra->vnc.import.timer) { struct rfapi_withdraw *wcb = - THREAD_ARG(bpi->extra->vnc.import.timer); + EVENT_ARG(bpi->extra->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - THREAD_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc.import.timer); } next = bpi->next; @@ -874,33 +871,39 @@ static void rfapiImportTableFlush(struct rfapi_import_table *it) for (afi = AFI_IP; afi < AFI_MAX; ++afi) { struct agg_node *rn; + struct agg_table *at; - for (rn = agg_route_top(it->imported_vpn[afi]); rn; - rn = agg_route_next(rn)) { - /* - * Each route_node has: - * aggregate: points to rfapi_it_extra with monitor - * chain(s) - * info: points to chain of bgp_path_info - */ - /* free bgp_path_info and its children */ - rfapiBgpInfoChainFree(rn->info); - rn->info = NULL; + at = it->imported_vpn[afi]; + if (at) { + for (rn = agg_route_top(at); rn; + rn = agg_route_next(rn)) { + /* + * Each route_node has: + * aggregate: points to rfapi_it_extra with + * monitor chain(s) + * info: points to chain of bgp_path_info + */ + /* free bgp_path_info and its children */ + rfapiBgpInfoChainFree(rn->info); + rn->info = NULL; - rfapiMonitorExtraFlush(SAFI_MPLS_VPN, rn); + rfapiMonitorExtraFlush(SAFI_MPLS_VPN, rn); + } + agg_table_finish(at); } - for (rn = agg_route_top(it->imported_encap[afi]); rn; - rn = agg_route_next(rn)) { - /* free bgp_path_info and its children */ - rfapiBgpInfoChainFree(rn->info); - rn->info = NULL; + if (at) { + at = it->imported_encap[afi]; + for (rn = agg_route_top(at); rn; + rn = agg_route_next(rn)) { + /* free bgp_path_info and its children */ + rfapiBgpInfoChainFree(rn->info); + rn->info = NULL; - rfapiMonitorExtraFlush(SAFI_ENCAP, rn); + rfapiMonitorExtraFlush(SAFI_ENCAP, rn); + } + agg_table_finish(at); } - - agg_table_finish(it->imported_vpn[afi]); - agg_table_finish(it->imported_encap[afi]); } if (it->monitor_exterior_orphans) { skiplist_free(it->monitor_exterior_orphans); @@ -1936,7 +1939,7 @@ static void rfapiBgpInfoAttachSorted(struct agg_node *rn, struct bgp *bgp; struct bgp_path_info *prev; struct bgp_path_info *next; - char pfx_buf[PREFIX2STR_BUFFER]; + char pfx_buf[PREFIX2STR_BUFFER] = {}; bgp = bgp_get_default(); /* assume 1 instance for now */ @@ -2339,9 +2342,9 @@ static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi) /* * Timer callback for withdraw */ -static void rfapiWithdrawTimerVPN(struct thread *t) +static void rfapiWithdrawTimerVPN(struct event *t) { - struct rfapi_withdraw *wcb = THREAD_ARG(t); + struct rfapi_withdraw *wcb = EVENT_ARG(t); struct bgp_path_info *bpi = wcb->info; struct bgp *bgp = bgp_get_default(); const struct prefix *p; @@ -2648,9 +2651,9 @@ rfapiWithdrawEncapUpdateCachedUn(struct rfapi_import_table *import_table, return 0; } -static void rfapiWithdrawTimerEncap(struct thread *t) +static void rfapiWithdrawTimerEncap(struct event *t) { - struct rfapi_withdraw *wcb = THREAD_ARG(t); + struct rfapi_withdraw *wcb = EVENT_ARG(t); struct bgp_path_info *bpi = wcb->info; int was_first_route = 0; struct rfapi_monitor_encap *em; @@ -2733,7 +2736,7 @@ static void rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, struct agg_node *rn, struct bgp_path_info *bpi, afi_t afi, safi_t safi, - void (*timer_service_func)(struct thread *)) + void (*timer_service_func)(struct event *)) { uint32_t lifetime; struct rfapi_withdraw *wcb; @@ -2783,8 +2786,8 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, if (lifetime > UINT32_MAX / 1001) { /* sub-optimal case, but will probably never happen */ bpi->extra->vnc.import.timer = NULL; - thread_add_timer(bm->master, timer_service_func, wcb, lifetime, - &bpi->extra->vnc.import.timer); + event_add_timer(bm->master, timer_service_func, wcb, lifetime, + &bpi->extra->vnc.import.timer); } else { static uint32_t jitter; uint32_t lifetime_msec; @@ -2799,9 +2802,9 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, lifetime_msec = (lifetime * 1000) + jitter; bpi->extra->vnc.import.timer = NULL; - thread_add_timer_msec(bm->master, timer_service_func, wcb, - lifetime_msec, - &bpi->extra->vnc.import.timer); + event_add_timer_msec(bm->master, timer_service_func, wcb, + lifetime_msec, + &bpi->extra->vnc.import.timer); } /* re-sort route list (BGP_PATH_REMOVED routes are last) */ @@ -2825,7 +2828,7 @@ static void rfapiExpireEncapNow(struct rfapi_import_table *it, struct agg_node *rn, struct bgp_path_info *bpi) { struct rfapi_withdraw *wcb; - struct thread t; + struct event t; /* * pretend we're an expiring timer @@ -3070,12 +3073,11 @@ static void rfapiBgpInfoFilteredImportEncap( */ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra->vnc.import.timer) { - struct rfapi_withdraw *wcb = THREAD_ARG( + struct rfapi_withdraw *wcb = EVENT_ARG( bpi->extra->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - THREAD_OFF( - bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc.import.timer); } if (action == FIF_ACTION_UPDATE) { @@ -3088,7 +3090,7 @@ static void rfapiBgpInfoFilteredImportEncap( * bpi */ struct rfapi_withdraw *wcb; - struct thread t; + struct event t; /* * pretend we're an expiring timer @@ -3163,10 +3165,10 @@ static void rfapiBgpInfoFilteredImportEncap( __func__); if (bpi->extra->vnc.import.timer) { struct rfapi_withdraw *wcb = - THREAD_ARG(bpi->extra->vnc.import.timer); + EVENT_ARG(bpi->extra->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - THREAD_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc.import.timer); } rfapiExpireEncapNow(import_table, rn, bpi); } @@ -3299,7 +3301,7 @@ static void rfapiExpireVpnNow(struct rfapi_import_table *it, int lockoffset) { struct rfapi_withdraw *wcb; - struct thread t; + struct event t; /* * pretend we're an expiring timer @@ -3523,12 +3525,11 @@ void rfapiBgpInfoFilteredImportVPN( */ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra->vnc.import.timer) { - struct rfapi_withdraw *wcb = THREAD_ARG( + struct rfapi_withdraw *wcb = EVENT_ARG( bpi->extra->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - THREAD_OFF( - bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc.import.timer); import_table->holddown_count[afi] -= 1; RFAPI_UPDATE_ITABLE_COUNT( @@ -3742,10 +3743,10 @@ void rfapiBgpInfoFilteredImportVPN( __func__); if (bpi->extra->vnc.import.timer) { struct rfapi_withdraw *wcb = - THREAD_ARG(bpi->extra->vnc.import.timer); + EVENT_ARG(bpi->extra->vnc.import.timer); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - THREAD_OFF(bpi->extra->vnc.import.timer); + EVENT_OFF(bpi->extra->vnc.import.timer); } rfapiExpireVpnNow(import_table, rn, bpi, 0); } @@ -4040,7 +4041,7 @@ static void rfapiProcessPeerDownRt(struct peer *peer, struct agg_node *rn; struct bgp_path_info *bpi; struct agg_table *rt = NULL; - void (*timer_service_func)(struct thread *) = NULL; + void (*timer_service_func)(struct event *) = NULL; assert(afi == AFI_IP || afi == AFI_IP6); @@ -4260,10 +4261,7 @@ void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h) h->resolve_nve_nexthop = NULL; } - agg_table_finish(h->it_ce->imported_vpn[AFI_IP]); - agg_table_finish(h->it_ce->imported_vpn[AFI_IP6]); - agg_table_finish(h->it_ce->imported_encap[AFI_IP]); - agg_table_finish(h->it_ce->imported_encap[AFI_IP6]); + rfapiImportTableFlush(h->it_ce); if (h->import_mac) { struct rfapi_import_table *it; @@ -4482,7 +4480,7 @@ static void rfapiDeleteRemotePrefixesIt( continue; if (bpi->extra->vnc.import.timer) { struct rfapi_withdraw *wcb = - THREAD_ARG( + EVENT_ARG( bpi->extra->vnc .import .timer); @@ -4495,9 +4493,8 @@ static void rfapiDeleteRemotePrefixesIt( afi, 1); XFREE(MTYPE_RFAPI_WITHDRAW, wcb); - THREAD_OFF( - bpi->extra->vnc.import - .timer); + EVENT_OFF(bpi->extra->vnc.import + .timer); } } else { if (!delete_active) diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h index 3bec225f65ac..dd06afe7e26a 100644 --- a/bgpd/rfapi/rfapi_import.h +++ b/bgpd/rfapi/rfapi_import.h @@ -13,7 +13,7 @@ #ifndef QUAGGA_HGP_RFAPI_IMPORT_H #define QUAGGA_HGP_RFAPI_IMPORT_H -#include "lib/thread.h" +#include "frrevent.h" /* * These are per-rt-import-list diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c index f169a7808c5b..3fe957ba4de7 100644 --- a/bgpd/rfapi/rfapi_monitor.c +++ b/bgpd/rfapi/rfapi_monitor.c @@ -619,7 +619,7 @@ void rfapiMonitorDel(struct bgp *bgp, struct rfapi_descriptor *rfd, rfapiMonitorDetachImport(m); } - THREAD_OFF(m->timer); + EVENT_OFF(m->timer); /* * remove from rfd list @@ -656,7 +656,7 @@ int rfapiMonitorDelHd(struct rfapi_descriptor *rfd) rfapiMonitorDetachImport(m); } - THREAD_OFF(m->timer); + EVENT_OFF(m->timer); XFREE(MTYPE_RFAPI_MONITOR, m); rn->info = NULL; @@ -690,7 +690,7 @@ int rfapiMonitorDelHd(struct rfapi_descriptor *rfd) #endif } - THREAD_OFF(mon_eth->timer); + EVENT_OFF(mon_eth->timer); /* * remove from rfd list @@ -730,9 +730,9 @@ void rfapiMonitorResponseRemovalOn(struct bgp *bgp) bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; } -static void rfapiMonitorTimerExpire(struct thread *t) +static void rfapiMonitorTimerExpire(struct event *t) { - struct rfapi_monitor_vpn *m = THREAD_ARG(t); + struct rfapi_monitor_vpn *m = EVENT_ARG(t); /* forget reference to thread, it's gone */ m->timer = NULL; @@ -743,7 +743,7 @@ static void rfapiMonitorTimerExpire(struct thread *t) static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m) { - unsigned long remain = thread_timer_remain_second(m->timer); + unsigned long remain = event_timer_remain_second(m->timer); /* unexpected case, but avoid wraparound problems below */ if (remain > m->rfd->response_lifetime) @@ -753,7 +753,7 @@ static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m) if (m->rfd->response_lifetime - remain < 2) return; - THREAD_OFF(m->timer); + EVENT_OFF(m->timer); { char buf[BUFSIZ]; @@ -764,8 +764,8 @@ static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m) m->rfd->response_lifetime); } - thread_add_timer(bm->master, rfapiMonitorTimerExpire, m, - m->rfd->response_lifetime, &m->timer); + event_add_timer(bm->master, rfapiMonitorTimerExpire, m, + m->rfd->response_lifetime, &m->timer); } /* @@ -1036,9 +1036,9 @@ void rfapiMonitorMovedUp(struct rfapi_import_table *import_table, } } -static void rfapiMonitorEthTimerExpire(struct thread *t) +static void rfapiMonitorEthTimerExpire(struct event *t) { - struct rfapi_monitor_eth *m = THREAD_ARG(t); + struct rfapi_monitor_eth *m = EVENT_ARG(t); /* forget reference to thread, it's gone */ m->timer = NULL; @@ -1051,7 +1051,7 @@ static void rfapiMonitorEthTimerExpire(struct thread *t) static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m) { - unsigned long remain = thread_timer_remain_second(m->timer); + unsigned long remain = event_timer_remain_second(m->timer); /* unexpected case, but avoid wraparound problems below */ if (remain > m->rfd->response_lifetime) @@ -1061,7 +1061,7 @@ static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m) if (m->rfd->response_lifetime - remain < 2) return; - THREAD_OFF(m->timer); + EVENT_OFF(m->timer); { char buf[BUFSIZ]; @@ -1072,8 +1072,8 @@ static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m) m->rfd->response_lifetime); } - thread_add_timer(bm->master, rfapiMonitorEthTimerExpire, m, - m->rfd->response_lifetime, &m->timer); + event_add_timer(bm->master, rfapiMonitorEthTimerExpire, m, + m->rfd->response_lifetime, &m->timer); } static int mon_eth_cmp(const void *a, const void *b) @@ -1399,7 +1399,7 @@ void rfapiMonitorEthDel(struct bgp *bgp, struct rfapi_descriptor *rfd, rfapiMonitorEthDetachImport(bgp, val); } - THREAD_OFF(val->timer); + EVENT_OFF(val->timer); /* * remove from rfd list diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h index 68ba5a48d869..3200079b3907 100644 --- a/bgpd/rfapi/rfapi_monitor.h +++ b/bgpd/rfapi/rfapi_monitor.h @@ -25,7 +25,7 @@ struct rfapi_monitor_vpn { #define RFAPI_MON_FLAG_NEEDCALLBACK 0x00000001 /* deferred callback */ // int dcount; /* debugging counter */ - struct thread *timer; + struct event *timer; }; struct rfapi_monitor_encap { @@ -41,7 +41,7 @@ struct rfapi_monitor_eth { struct rfapi_descriptor *rfd; /* which NVE requested the route */ struct ethaddr macaddr; uint32_t logical_net_id; - struct thread *timer; + struct event *timer; }; /* diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c index f727f24f1d00..5784f95b2786 100644 --- a/bgpd/rfapi/rfapi_rib.c +++ b/bgpd/rfapi/rfapi_rib.c @@ -40,6 +40,7 @@ #define DEBUG_PENDING_DELETE_ROUTE 0 #define DEBUG_NHL 0 #define DEBUG_RIB_SL_RD 0 +#define DEBUG_CLEANUP 0 /* forward decl */ #if DEBUG_NHL @@ -116,7 +117,6 @@ void rfapiRibCheckCounts( struct bgp *bgp = bgp_get_default(); uint32_t t_pfx_active = 0; - uint32_t t_pfx_deleted = 0; uint32_t t_ri_active = 0; uint32_t t_ri_deleted = 0; @@ -131,7 +131,6 @@ void rfapiRibCheckCounts( afi_t afi; uint32_t pfx_active = 0; - uint32_t pfx_deleted = 0; for (afi = AFI_IP; afi < AFI_MAX; ++afi) { @@ -156,8 +155,6 @@ void rfapiRibCheckCounts( if (dsl) { ri_deleted = skiplist_count(dsl); t_ri_deleted += ri_deleted; - ++pfx_deleted; - ++t_pfx_deleted; } } for (rn = agg_route_top(rfd->rib_pending[afi]); rn; @@ -255,8 +252,8 @@ static void rfapi_info_free(struct rfapi_info *goner) if (goner->timer) { struct rfapi_rib_tcb *tcb; - tcb = THREAD_ARG(goner->timer); - THREAD_OFF(goner->timer); + tcb = EVENT_ARG(goner->timer); + EVENT_OFF(goner->timer); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); } XFREE(MTYPE_RFAPI_INFO, goner); @@ -278,9 +275,9 @@ struct rfapi_rib_tcb { /* * remove route from rib */ -static void rfapiRibExpireTimer(struct thread *t) +static void rfapiRibExpireTimer(struct event *t) { - struct rfapi_rib_tcb *tcb = THREAD_ARG(t); + struct rfapi_rib_tcb *tcb = EVENT_ARG(t); RFAPI_RIB_CHECK_COUNTS(1, 0); @@ -325,12 +322,17 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, struct rfapi_rib_tcb *tcb = NULL; if (ri->timer) { - tcb = THREAD_ARG(ri->timer); - THREAD_OFF(ri->timer); + tcb = EVENT_ARG(ri->timer); + EVENT_OFF(ri->timer); } else { tcb = XCALLOC(MTYPE_RFAPI_RECENT_DELETE, sizeof(struct rfapi_rib_tcb)); } +#if DEBUG_CLEANUP + zlog_debug("%s: rfd %p, rn %p, ri %p, tcb %p", __func__, rfd, rn, ri, + tcb); +#endif + tcb->rfd = rfd; tcb->ri = ri; tcb->rn = rn; @@ -345,8 +347,8 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, vnc_zlog_debug_verbose("%s: rfd %p pfx %pRN life %u", __func__, rfd, rn, ri->lifetime); - thread_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime, - &ri->timer); + event_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime, + &ri->timer); } extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */ @@ -510,6 +512,16 @@ void rfapiRibClear(struct rfapi_descriptor *rfd) NULL, (void **)&ri)) { + if (ri->timer) { + struct rfapi_rib_tcb + *tcb; + + tcb = EVENT_ARG( + ri->timer); + EVENT_OFF(ri->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, + tcb); + } rfapi_info_free(ri); skiplist_delete_first( (struct skiplist *) @@ -559,6 +571,9 @@ void rfapiRibFree(struct rfapi_descriptor *rfd) { afi_t afi; +#if DEBUG_CLEANUP + zlog_debug("%s: rfd %p", __func__, rfd); +#endif /* * NB rfd is typically detached from master list, so is not included @@ -900,8 +915,8 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, if (ri->timer) { struct rfapi_rib_tcb *tcb; - tcb = THREAD_ARG(ri->timer); - THREAD_OFF(ri->timer); + tcb = EVENT_ARG(ri->timer); + EVENT_OFF(ri->timer); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); } @@ -985,8 +1000,8 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, if (ori->timer) { struct rfapi_rib_tcb *tcb; - tcb = THREAD_ARG(ori->timer); - THREAD_OFF(ori->timer); + tcb = EVENT_ARG(ori->timer); + EVENT_OFF(ori->timer); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); } @@ -1319,8 +1334,8 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, if (ri->timer) { struct rfapi_rib_tcb *tcb; - tcb = THREAD_ARG(ri->timer); - THREAD_OFF(ri->timer); + tcb = EVENT_ARG(ri->timer); + EVENT_OFF(ri->timer); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); } RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); @@ -2303,10 +2318,6 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, int printedheader = 0; int routes_total = 0; int nhs_total = 0; - int prefixes_total = 0; - int prefixes_displayed = 0; - int nves_total = 0; - int nves_with_routes = 0; int nves_displayed = 0; int routes_displayed = 0; int nhs_displayed = 0; @@ -2326,10 +2337,6 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, int printednve = 0; afi_t afi; - ++nves_total; - if (rfd->rib_prefix_count) - ++nves_with_routes; - for (afi = AFI_IP; afi < AFI_MAX; ++afi) { struct agg_node *rn; @@ -2355,14 +2362,11 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, routes_total++; nhs_total += skiplist_count(sl); - ++prefixes_total; if (pfx_match && !prefix_match(pfx_match, p) && !prefix_match(p, pfx_match)) continue; - ++prefixes_displayed; - if (!printedheader) { ++printedheader; diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h index 1b626bd2e18b..5fa838bd34f7 100644 --- a/bgpd/rfapi/rfapi_rib.h +++ b/bgpd/rfapi/rfapi_rib.h @@ -61,7 +61,7 @@ struct rfapi_info { struct bgp_tea_options *tea_options; struct rfapi_un_option *un_options; struct rfapi_vn_option *vn_options; - struct thread *timer; + struct event *timer; }; /* diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 2b768b8f8d78..29698846c379 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -516,10 +516,10 @@ void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra && bpi->extra->vnc.import.timer) { - struct thread *t = - (struct thread *)bpi->extra->vnc.import.timer; + struct event *t = (struct event *)bpi->extra->vnc.import.timer; + r = snprintf(p, REMAIN, " [%4lu] ", - thread_timer_remain_second(t)); + event_timer_remain_second(t)); INCP; } else { @@ -822,11 +822,6 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) const char *vty_newline; int printedheader = 0; - - int nves_total = 0; - int nves_with_queries = 0; - int nves_displayed = 0; - int queries_total = 0; int queries_displayed = 0; @@ -850,15 +845,9 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) struct agg_node *rn; int printedquerier = 0; - - ++nves_total; - - if (rfd->mon - || (rfd->mon_eth && skiplist_count(rfd->mon_eth))) { - ++nves_with_queries; - } else { + if (!rfd->mon && + !(rfd->mon_eth && skiplist_count(rfd->mon_eth))) continue; - } /* * IP Queries @@ -904,13 +893,11 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) fp(out, "%-15s %-15s", buf_vn, buf_un); printedquerier = 1; - - ++nves_displayed; } else fp(out, "%-15s %-15s", "", ""); buf_remain[0] = 0; rfapiFormatSeconds( - thread_timer_remain_second(m->timer), + event_timer_remain_second(m->timer), buf_remain, BUFSIZ); fp(out, " %-15s %-10s\n", inet_ntop(m->p.family, &m->p.u.prefix, @@ -978,12 +965,10 @@ int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) fp(out, "%-15s %-15s", buf_vn, buf_un); printedquerier = 1; - - ++nves_displayed; } else fp(out, "%-15s %-15s", "", ""); buf_remain[0] = 0; - rfapiFormatSeconds(thread_timer_remain_second( + rfapiFormatSeconds(event_timer_remain_second( mon_eth->timer), buf_remain, BUFSIZ); fp(out, " %-17s %10d %-10s\n", @@ -1114,9 +1099,8 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, time_t age; char buf_age[BUFSIZ]; - struct thread *t = - (struct thread *)bpi->extra->vnc.import.timer; - remaining = thread_timer_remain_second(t); + struct event *t = (struct event *)bpi->extra->vnc.import.timer; + remaining = event_timer_remain_second(t); #ifdef RFAPI_REGISTRATIONS_REPORT_AGE /* @@ -1174,6 +1158,7 @@ static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, } if (tun_type != BGP_ENCAP_TYPE_MPLS && bpi->extra) { uint32_t l = decode_label(&bpi->extra->label[0]); + if (!MPLS_LABEL_IS_NULL(l)) { fp(out, " Label: %d", l); if (nlines == 1) @@ -4903,6 +4888,7 @@ static int vnc_clear_vrf(struct vty *vty, struct bgp *bgp, const char *arg_vrf, clear_vnc_prefix(&cda); vty_out(vty, "Cleared %u out of %d prefixes.\n", cda.pfx_count, start_count); + print_cleared_stats(&cda); /* frees lists in cda */ return CMD_SUCCESS; } diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index 544922044859..7decb75782dc 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -1692,7 +1692,7 @@ void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, * export expiration timer is already running on * this route: cancel it */ - THREAD_OFF(eti->timer); + EVENT_OFF(eti->timer); bgp_update(peer, prefix, /* prefix */ 0, /* addpath_id */ @@ -1704,9 +1704,9 @@ void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, bgp_attr_unintern(&iattr); } -static void vncExportWithdrawTimer(struct thread *t) +static void vncExportWithdrawTimer(struct event *t) { - struct vnc_export_info *eti = THREAD_ARG(t); + struct vnc_export_info *eti = EVENT_ARG(t); const struct prefix *p = agg_node_get_prefix(eti->node); /* @@ -1765,8 +1765,8 @@ void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, if (!eti->timer && eti->lifetime <= INT32_MAX) { eti->timer = NULL; - thread_add_timer(bm->master, vncExportWithdrawTimer, eti, - eti->lifetime, &eti->timer); + event_add_timer(bm->master, vncExportWithdrawTimer, eti, + eti->lifetime, &eti->timer); vnc_zlog_debug_verbose( "%s: set expiration timer for %u seconds", __func__, eti->lifetime); @@ -1922,7 +1922,7 @@ void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) * already running on * this route: cancel it */ - THREAD_OFF(eti->timer); + EVENT_OFF(eti->timer); vnc_zlog_debug_verbose( "%s: calling bgp_update", @@ -1991,7 +1991,7 @@ void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); if (eti) { - THREAD_OFF(eti->timer); + EVENT_OFF(eti->timer); vnc_eti_delete(eti); } diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h index 42c04f0c7956..f715de08578c 100644 --- a/bgpd/rfapi/vnc_export_table.h +++ b/bgpd/rfapi/vnc_export_table.h @@ -9,7 +9,7 @@ #define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ #include "lib/table.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/vty.h" #include "bgpd/bgpd.h" @@ -29,7 +29,7 @@ struct vnc_export_info { uint8_t type; uint8_t subtype; uint32_t lifetime; - struct thread *timer; + struct event *timer; }; extern struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type, diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c index 7c8e8f35c9e2..3fcc0e6f5f4b 100644 --- a/bgpd/rfapi/vnc_import_bgp.c +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -834,6 +834,8 @@ static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, if (ecom) ecommunity_free(&ecom); + if (iattr) + bgp_attr_unintern(&iattr); } static void vnc_import_bgp_add_route_mode_nvegroup( @@ -1030,6 +1032,8 @@ static void vnc_import_bgp_add_route_mode_nvegroup( if (ecom) ecommunity_free(&ecom); + if (iattr) + bgp_attr_unintern(&iattr); } static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp, @@ -1982,8 +1986,6 @@ void vnc_import_bgp_exterior_add_route_interior( if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) { - int count = 0; /* debugging */ - vnc_zlog_debug_verbose( "%s: has exterior monitor; ext src: %p", __func__, RFAPI_MONITOR_EXTERIOR(rn_interior)->source); @@ -2007,9 +2009,6 @@ void vnc_import_bgp_exterior_add_route_interior( struct attr new_attr; uint32_t label = 0; - - ++count; /* debugging */ - assert(bpi_exterior); assert(pfx_exterior); diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index 4dab0bc33305..9c971272e43c 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -560,9 +560,9 @@ static void vnc_zebra_add_del_prefix(struct bgp *bgp, return; } - if (!vrf_bitmap_check( - zclient_vnc->redist[family2afi(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) { @@ -622,7 +622,7 @@ static void vnc_zebra_add_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd, if (zclient_vnc->sock < 0) return; - if (!vrf_bitmap_check(zclient_vnc->redist[afi][ZEBRA_ROUTE_VNC], + if (!vrf_bitmap_check(&zclient_vnc->redist[afi][ZEBRA_ROUTE_VNC], VRF_DEFAULT)) return; @@ -819,12 +819,12 @@ int vnc_redistribute_set(struct bgp *bgp, afi_t afi, int type) // bgp->redist[afi][type] = 1; /* Return if already redistribute flag is set. */ - if (vrf_bitmap_check(zclient_vnc->redist[afi][type], VRF_DEFAULT)) + if (vrf_bitmap_check(&zclient_vnc->redist[afi][type], VRF_DEFAULT)) return CMD_WARNING_CONFIG_FAILED; - vrf_bitmap_set(zclient_vnc->redist[afi][type], VRF_DEFAULT); + vrf_bitmap_set(&zclient_vnc->redist[afi][type], VRF_DEFAULT); - // vrf_bitmap_set(zclient_vnc->redist[afi][type], VRF_DEFAULT); + // vrf_bitmap_set(&zclient_vnc->redist[afi][type], VRF_DEFAULT); /* Return if zebra connection is not established. */ if (zclient_vnc->sock < 0) @@ -855,9 +855,9 @@ int vnc_redistribute_unset(struct bgp *bgp, afi_t afi, int type) bgp->rfapi_cfg->redist[afi][type] = 0; /* Return if zebra connection is disabled. */ - if (!vrf_bitmap_check(zclient_vnc->redist[afi][type], VRF_DEFAULT)) + if (!vrf_bitmap_check(&zclient_vnc->redist[afi][type], VRF_DEFAULT)) return CMD_WARNING_CONFIG_FAILED; - vrf_bitmap_unset(zclient_vnc->redist[afi][type], VRF_DEFAULT); + vrf_bitmap_unset(&zclient_vnc->redist[afi][type], VRF_DEFAULT); if (bgp->rfapi_cfg->redist[AFI_IP][type] == 0 && bgp->rfapi_cfg->redist[AFI_IP6][type] == 0 @@ -890,7 +890,7 @@ static zclient_handler *const vnc_handlers[] = { * Modeled after bgp_zebra.c'bgp_zebra_init() * Charriere asks, "Is it possible to carry two?" */ -void vnc_zebra_init(struct thread_master *master) +void vnc_zebra_init(struct event_loop *master) { /* Set default values. */ zclient_vnc = zclient_new(master, &zclient_options_default, diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c index 3a512902e027..1ada36bbc00d 100644 --- a/bgpd/rfp-example/librfp/rfp_example.c +++ b/bgpd/rfp-example/librfp/rfp_example.c @@ -17,7 +17,7 @@ struct rfp_instance_t { struct rfapi_rfp_cfg rfapi_config; struct rfapi_rfp_cb_methods rfapi_callbacks; - struct thread_master *master; + struct event_loop *master; uint32_t config_var; }; @@ -271,7 +271,7 @@ static int rfp_cfg_write_cb(struct vty *vty, void *rfp_start_val) * rfp_start_val rfp returned value passed on rfp_stop and rfp_cfg_write * --------------------------------------------*/ -void *rfp_start(struct thread_master *master, struct rfapi_rfp_cfg **cfgp, +void *rfp_start(struct event_loop *master, struct rfapi_rfp_cfg **cfgp, struct rfapi_rfp_cb_methods **cbmp) { memset(&global_rfi, 0, sizeof(global_rfi)); diff --git a/configure.ac b/configure.ac index 495c89c0d610..6d0ffc7adfcc 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ ## AC_PREREQ([2.69]) -AC_INIT([frr], [9.0-dev], [https://github.com/frrouting/frr/issues]) +AC_INIT([frr], [9.1-dev], [https://github.com/frrouting/frr/issues]) PACKAGE_URL="https://frrouting.org/" AC_SUBST([PACKAGE_URL]) PACKAGE_FULLNAME="FRRouting" @@ -299,14 +299,20 @@ if test "$enable_scripting" = "yes"; then AX_LUA_HEADERS([], [ AC_MSG_ERROR([Lua 5.3 headers are required to build with Lua support. No other version is supported.]) ]) - AX_LUA_LIBS([ + PKG_CHECK_MODULES([LUA], [lua5.3], [ AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting]) - LIBS="$LIBS $LUA_LIB" + LIBS="$LIBS $LUA_LIBS" SCRIPTING=true ], [ - SCRIPTING=false - AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.]) - ]) + AX_LUA_LIBS([ + AC_DEFINE([HAVE_SCRIPTING], [1], [Have support for scripting]) + LIBS="$LIBS $LUA_LIB" + SCRIPTING=true + ], [ + SCRIPTING=false + AC_MSG_ERROR([Lua 5.3 libraries are required to build with Lua support. No other version is supported.]) + ]) + ]) fi dnl the following flags go in CFLAGS rather than AC_CFLAGS since they make @@ -616,6 +622,10 @@ AC_ARG_ENABLE([zebra], AS_HELP_STRING([--disable-zebra], [do not build zebra daemon])) AC_ARG_ENABLE([bgpd], AS_HELP_STRING([--disable-bgpd], [do not build bgpd])) +AC_ARG_ENABLE([mgmtd], + AS_HELP_STRING([--disable-mgmtd], [do not build mgmtd])) +AC_ARG_ENABLE([mgmtd_local_validations], + AS_HELP_STRING([--enable-mgmtd-local-validations], [dev: unimplemented local validation])) AC_ARG_ENABLE([ripd], AS_HELP_STRING([--disable-ripd], [do not build ripd])) AC_ARG_ENABLE([ripngd], @@ -681,6 +691,8 @@ AC_ARG_ENABLE([ospfapi], AC_ARG_ENABLE([ospfclient], AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI, (this is the default if --disable-ospfapi is set)])) +AC_ARG_WITH([log_timestamp_precision], + AS_HELP_STRING([--with-log-timestamp-precision=ARG], [set startup log timestamp precision, ARG must be 0-12])) AC_ARG_ENABLE([multipath], AS_HELP_STRING([--enable-multipath=ARG], [enable multipath function, ARG must be digit])) AC_ARG_WITH([service_timeout], @@ -782,6 +794,9 @@ if test "$ac_cv_lib_json_c_json_object_get" = "no"; then fi ]) +AC_ARG_ENABLE([ccls], +AS_HELP_STRING([--enable-ccls], [Write .ccls config for this build])) + AC_ARG_ENABLE([dev_build], AS_HELP_STRING([--enable-dev-build], [build for development])) @@ -890,10 +905,6 @@ if test "$enable_oldvpn_commands" = "yes"; then AC_DEFINE([KEEP_OLD_VPN_COMMANDS], [1], [Define for compiling with old vpn commands]) fi -# -# End of logic for protobuf support. -# - AC_MSG_CHECKING([if zebra should be configurable to send Route Advertisements]) if test "$enable_rtadv" != "no"; then AC_MSG_RESULT([yes]) @@ -956,8 +967,19 @@ esac AC_DEFINE_UNQUOTED([MULTIPATH_NUM], [$MPATH_NUM], [Maximum number of paths for a route]) -AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use]) +case "${with_log_timestamp_precision}" in +[[0-9]|1[012]]) +;; +"") +;; +*) +AC_MSG_FAILURE([Please specify a number from 0-12 for log precision ARG]) +;; +esac +with_log_timestamp_precision=${with_log_timestamp_precision:-0} +AC_DEFINE_UNQUOTED([LOG_TIMESTAMP_PRECISION], [${with_log_timestamp_precision}], [Startup zlog timestamp precision]) +AC_DEFINE_UNQUOTED([VTYSH_PAGER], ["$VTYSH_PAGER"], [What pager to use]) TIMEOUT_MIN=2 case "${with_service_timeout}" in @@ -1338,21 +1360,21 @@ 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]) +# Enable Protobuf by default at all times. +# 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 +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.]) - ]) +PKG_CHECK_MODULES([PROTOBUF_C], [libprotobuf-c >= 1.1.0],, [ + AC_MSG_FAILURE([minimum version (1.1.0) of libprotobuf-c not found. Install minimum required version of protobuf-c.]) +]) +if test "$enable_protobuf3" = "yes"; then PROTO3=true AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [AC_CHECK_DECLS(PROTOBUF_C_LABEL_NONE, @@ -1360,11 +1382,14 @@ if test "$enable_protobuf" = "yes"; then [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]) + [PROTO3=false && AC_MSG_FAILURE([protobuf3 requested but protobuf-c.h not found. Install protobuf-c.])]) fi +AC_DEFINE([HAVE_PROTOBUF], [1], [protobuf]) +# +# End of logic for protobuf support. +# + dnl --------------------- dnl Integrated VTY option @@ -1728,6 +1753,16 @@ AS_IF([test "$enable_bgpd" != "no"], [ AC_DEFINE([HAVE_BGPD], [1], [bgpd]) ]) +AS_IF([test "$enable_mgmtd" != "no"], [ + + AC_DEFINE([HAVE_MGMTD], [1], [mgmtd]) + + # Enable MGMTD local validations + AS_IF([test "$enable_mgmtd_local_validations" == "yes"], [ + AC_DEFINE([MGMTD_LOCAL_VALIDATIONS_ENABLED], [1], [Enable mgmtd local validations.]) + ]) +]) + AS_IF([test "$enable_ripd" != "no"], [ AC_DEFINE([HAVE_RIPD], [1], [ripd]) ]) @@ -2658,6 +2693,8 @@ AC_DEFINE_UNQUOTED([LDPD_SOCKET], ["$frr_statedir%s%s/ldpd.sock"], [ldpd control 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([OSPFD_GR_STATE], ["$frr_statedir%s/ospfd-gr.json"], [ospfd GR state information]) +AC_DEFINE_UNQUOTED([MGMTD_FE_SERVER_PATH], ["$frr_statedir/mgmtd_fe.sock"], [mgmtd frontend server socket]) +AC_DEFINE_UNQUOTED([MGMTD_BE_SERVER_PATH], ["$frr_statedir/mgmtd_be.sock"], [mgmtd backend server socket]) AC_DEFINE_UNQUOTED([OSPF6D_GR_STATE], ["$frr_statedir/ospf6d-gr.json"], [ospf6d GR state information]) AC_DEFINE_UNQUOTED([ISISD_RESTART], ["$frr_statedir%s/isid-restart.json"], [isisd restart information]) AC_DEFINE_UNQUOTED([OSPF6_AUTH_SEQ_NUM_FILE], ["$frr_statedir/ospf6d-at-seq-no.dat"], [ospf6d AT Sequence number information]) @@ -2678,6 +2715,7 @@ AC_SUBST([vtysh_bin]) CFG_SYSCONF="$sysconfdir" CFG_SBIN="$sbindir" +CFG_BIN="$bindir" CFG_STATE="$frr_statedir" CFG_MODULE="$moduledir" CFG_YANGMODELS="$yangmodelsdir" @@ -2685,6 +2723,7 @@ CFG_SCRIPT="$scriptdir" 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_BIN="\"$CFG_BIN\"" eval CFG_STATE="\"$CFG_STATE\"" eval CFG_MODULE="\"$CFG_MODULE\"" eval CFG_YANGMODELS="\"$CFG_YANGMODELS\"" @@ -2692,6 +2731,7 @@ for I in 1 2 3 4 5 6 7 8 9 10; do done AC_SUBST([CFG_SYSCONF]) AC_SUBST([CFG_SBIN]) +AC_SUBST([CFG_BIN]) AC_SUBST([CFG_STATE]) AC_SUBST([CFG_MODULE]) AC_SUBST([CFG_SCRIPT]) @@ -2716,7 +2756,7 @@ 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_PROTOBUF], [test "$enable_protobuf" != "no"]) AM_CONDITIONAL([HAVE_PROTOBUF3], [$PROTO3]) dnl PCEP plugin @@ -2733,6 +2773,7 @@ dnl daemons AM_CONDITIONAL([VTYSH], [test "$VTYSH" = "vtysh"]) AM_CONDITIONAL([ZEBRA], [test "$enable_zebra" != "no"]) AM_CONDITIONAL([BGPD], [test "$enable_bgpd" != "no"]) +AM_CONDITIONAL([MGMTD], [test "$enable_mgmtd" != "no"]) AM_CONDITIONAL([RIPD], [test "$enable_ripd" != "no"]) AM_CONDITIONAL([OSPFD], [test "$enable_ospfd" != "no"]) AM_CONDITIONAL([LDPD], [test "$enable_ldpd" != "no"]) @@ -2770,7 +2811,7 @@ AC_CONFIG_FILES([ alpine/APKBUILD snapcraft/snapcraft.yaml lib/version.h - tests/lib/cli/test_cli.refout + tests/lib/cli/test_cli.refout pkgsrc/mgmtd.sh pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh pkgsrc/eigrpd.sh]) @@ -2782,6 +2823,43 @@ AC_CONFIG_FILES([tools/frrcommon.sh]) AC_CONFIG_FILES([tools/frr.service]) AC_CONFIG_FILES([tools/frr@.service]) +# dnl write out a ccls file with our compile configuration +# dnl have to add -Wno-unused-function otherwise foobar_cmd_magic causes +# dnl all DEFPY(), et al., macros to flag as errors. +AS_IF([test "$enable_ccls" = "yes"], [ + AC_CONFIG_COMMANDS([gen-dot-ccls], [ + cat > "${srcdir}/.ccls" <> "${srcdir}/.ccls" + fi + if test -n "$FRR_ALL_CCLS_FLAGS"; then + echo ${FRR_ALL_CCLS_FLAGS} | tr ' ' '\n' >> "${srcdir}/.ccls" + fi + if test -n "$FRR_ALL_CCLS_CFLAGS"; then + cat >> "${srcdir}/.ccls" < Tue, 07 Feb 2023 16:00:00 +0500 + -- Jafar Al-Gharaibeh Tue, 06 Jun 2023 12:00:00 -0600 -frr (8.5-1) UNRELEASED; urgency=medium +frr (8.5-0) unstable; urgency=medium * New upstream release FRR 8.5 - -- Donatas Abraitis Tue, 07 Feb 2023 16:00:00 +0500 + -- Jafar Al-Gharaibeh Fri, 10 Mar 2023 02:00:00 -0600 frr (8.4.2-1) unstable; urgency=medium diff --git a/debian/frr.install b/debian/frr.install index 044b48498458..02912d448dee 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -8,6 +8,7 @@ usr/bin/vtysh usr/lib/*/frr/libfrr.* usr/lib/*/frr/libfrrcares.* usr/lib/*/frr/libfrrospfapiclient.* +usr/lib/*/frr/libmgmt_be_nb.* usr/lib/*/frr/modules/bgpd_bmp.so usr/lib/*/frr/modules/dplane_fpm_nl.so usr/lib/*/frr/modules/zebra_cumulus_mlag.so diff --git a/doc/developer/building-docker.rst b/doc/developer/building-docker.rst index 4cf356049e99..9d42784e35fd 100644 --- a/doc/developer/building-docker.rst +++ b/doc/developer/building-docker.rst @@ -16,8 +16,8 @@ The following platform images are used to support Travis CI and can also be used to reproduce topotest failures when the docker host is Ubuntu (tested on 18.04 and 20.04): -* Ubuntu 18.04 * Ubuntu 20.04 +* Ubuntu 22.04 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: @@ -130,57 +130,75 @@ No script, multi-arch (ex. amd64, arm64):: -Building Ubuntu 18.04 Image +Building Ubuntu 20.04 Image --------------------------- Build image (from project root directory):: - docker build -t frr-ubuntu18:latest -f docker/ubuntu18-ci/Dockerfile . + docker build -t frr-ubuntu20:latest --build-arg=UBUNTU_VERSION=20.04 -f docker/ubuntu-ci/Dockerfile . + +Running Full Topotest:: + + docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules \ + frr-ubuntu20:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' + +Extract results from the above run into `run-results` dir and analyze:: + + tests/topotest/analyze.py -C frr -Ar run-results Start the container:: - docker run -d --privileged --name frr-ubuntu18 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu18:latest + docker run -d --init --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest Running a topotest (when the docker host is Ubuntu):: - docker exec frr-ubuntu18 bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py' + docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py' Starting an interactive bash session:: - docker exec -it frr-ubuntu18 bash + docker exec -it frr-ubuntu20 bash Stopping an removing a container:: - docker stop frr-ubuntu18 ; docker rm frr-ubuntu18 + docker stop frr-ubuntu20 ; docker rm frr-ubuntu20 Removing the built image:: - docker rmi frr-ubuntu18:latest + docker rmi frr-ubuntu20:latest -Building Ubuntu 20.04 Image +Building Ubuntu 22.04 Image --------------------------- Build image (from project root directory):: - docker build -t frr-ubuntu20:latest -f docker/ubuntu20-ci/Dockerfile . + docker build -t frr-ubuntu22:latest -f docker/ubuntu-ci/Dockerfile . + +Running Full Topotest:: + + docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules \ + frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' + +Extract results from the above run into `run-results` dir and analyze:: + + tests/topotest/analyze.py -C frr -Ar run-results Start the container:: - docker run -d --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest + docker run -d --init --privileged --name frr-ubuntu22 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu22:latest Running a topotest (when the docker host is Ubuntu):: - docker exec frr-ubuntu20 bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py' + docker exec frr-ubuntu22 bash -c 'cd ~/frr/tests/topotests/ospf_topo1 ; sudo pytest test_ospf_topo1.py' Starting an interactive bash session:: - docker exec -it frr-ubuntu20 bash + docker exec -it frr-ubuntu22 bash Stopping an removing a container:: - docker stop frr-ubuntu20 ; docker rm frr-ubuntu20 + docker stop frr-ubuntu22 ; docker rm frr-ubuntu22 Removing the built image:: - docker rmi frr-ubuntu20:latest + docker rmi frr-ubuntu22:latest diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst index 7a7af42119cd..233d089f7957 100644 --- a/doc/developer/building-frr-for-centos6.rst +++ b/doc/developer/building-frr-for-centos6.rst @@ -46,7 +46,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 libcap-devel \ - elfutils-libelf-devel + elfutils-libelf-devel protobuf-c-devel Install newer version of bison (CentOS 6 package source is too old) from CentOS 7: diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst index c40b5de594df..e6da83019405 100644 --- a/doc/developer/building-frr-for-centos7.rst +++ b/doc/developer/building-frr-for-centos7.rst @@ -22,7 +22,7 @@ Add packages: readline-devel texinfo net-snmp-devel groff pkgconfig \ json-c-devel pam-devel bison flex pytest c-ares-devel \ python-devel python-sphinx libcap-devel \ - elfutils-libelf-devel libunwind-devel + elfutils-libelf-devel libunwind-devel protobuf-c-devel .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst index 659752f6df98..6d18e7be93a3 100644 --- a/doc/developer/building-frr-for-centos8.rst +++ b/doc/developer/building-frr-for-centos8.rst @@ -15,7 +15,8 @@ Add packages: 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 libcap-devel \ - elfutils-libelf-devel libunwind-devel + elfutils-libelf-devel libunwind-devel \ + protobuf-c-devel .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst index 5e58854ed7ab..7071cb660de8 100644 --- a/doc/developer/building-frr-for-debian8.rst +++ b/doc/developer/building-frr-for-debian8.rst @@ -18,7 +18,7 @@ Add packages: sudo apt-get install git autoconf automake libtool make \ libreadline-dev texinfo libjson-c-dev pkg-config bison flex python3-pip \ libc-ares-dev python3-dev python3-sphinx build-essential \ - libsnmp-dev libcap-dev libelf-dev + libsnmp-dev libcap-dev libelf-dev libprotobuf-c-dev protobuf-c-compiler Install newer pytest (>3.0) from pip diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst index b2fdef999041..1b2f1b933a42 100644 --- a/doc/developer/building-frr-for-debian9.rst +++ b/doc/developer/building-frr-for-debian9.rst @@ -11,7 +11,8 @@ Add packages: sudo apt-get install git autoconf automake libtool make \ libreadline-dev texinfo libjson-c-dev pkg-config bison flex \ libc-ares-dev python3-dev python3-pytest python3-sphinx build-essential \ - libsnmp-dev libcap-dev libelf-dev libunwind-dev + libsnmp-dev libcap-dev libelf-dev libunwind-dev \ + libprotobuf-c-dev protobuf-c-compiler .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-fedora.rst b/doc/developer/building-frr-for-fedora.rst index aa10f1118dab..35a24b2f4343 100644 --- a/doc/developer/building-frr-for-fedora.rst +++ b/doc/developer/building-frr-for-fedora.rst @@ -15,7 +15,7 @@ Installing Dependencies readline-devel texinfo net-snmp-devel groff pkgconfig json-c-devel \ pam-devel python3-pytest bison flex c-ares-devel python3-devel \ python3-sphinx perl-core patch libcap-devel \ - elfutils-libelf-devel libunwind-devel + elfutils-libelf-devel libunwind-devel protobuf-c-devel .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-freebsd10.rst b/doc/developer/building-frr-for-freebsd10.rst index 5e70b81d43d7..707f1e703364 100644 --- a/doc/developer/building-frr-for-freebsd10.rst +++ b/doc/developer/building-frr-for-freebsd10.rst @@ -17,7 +17,8 @@ is first package install and asked) :: pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py36-pytest c-ares python3.6 py36-sphinx libunwind + bison flex py36-pytest c-ares python3.6 py36-sphinx libunwind \ + protobuf-c .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-freebsd11.rst b/doc/developer/building-frr-for-freebsd11.rst index 808207b83148..af0b72b16def 100644 --- a/doc/developer/building-frr-for-freebsd11.rst +++ b/doc/developer/building-frr-for-freebsd11.rst @@ -17,7 +17,8 @@ is first package install and asked) .. code-block:: shell pkg install git autoconf automake libtool gmake json-c pkgconf \ - bison flex py36-pytest c-ares python3.6 py36-sphinx texinfo libunwind + bison flex py36-pytest c-ares python3.6 py36-sphinx texinfo libunwind \ + protobuf-c .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-freebsd9.rst b/doc/developer/building-frr-for-freebsd9.rst index 1e9774979525..30332875a016 100644 --- a/doc/developer/building-frr-for-freebsd9.rst +++ b/doc/developer/building-frr-for-freebsd9.rst @@ -18,7 +18,7 @@ is first package install and asked) pkg install -y git autoconf automake libtool gmake \ pkgconf texinfo json-c bison flex py36-pytest c-ares \ - python3 py36-sphinx libexecinfo + python3 py36-sphinx libexecinfo protobuf-c 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 a78f8b3c2f20..8958862fea7e 100644 --- a/doc/developer/building-frr-for-netbsd6.rst +++ b/doc/developer/building-frr-for-netbsd6.rst @@ -23,7 +23,8 @@ Add packages: :: sudo pkg_add git autoconf automake libtool gmake openssl \ - pkg-config json-c py36-test python36 py36-sphinx + pkg-config json-c py36-test python36 py36-sphinx \ + protobuf-c Install SSL Root Certificates (for git https access): diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst index a52ece19a18b..e751ba338c11 100644 --- a/doc/developer/building-frr-for-netbsd7.rst +++ b/doc/developer/building-frr-for-netbsd7.rst @@ -14,7 +14,8 @@ Install required packages :: sudo pkgin install git autoconf automake libtool gmake openssl \ - pkg-config json-c python36 py36-test py36-sphinx + pkg-config json-c python36 py36-test py36-sphinx \ + protobuf-c Install SSL Root Certificates (for git https access): diff --git a/doc/developer/building-frr-for-openbsd6.rst b/doc/developer/building-frr-for-openbsd6.rst index 88446685e067..00bc2e5f091d 100644 --- a/doc/developer/building-frr-for-openbsd6.rst +++ b/doc/developer/building-frr-for-openbsd6.rst @@ -16,7 +16,7 @@ Add packages: pkg_add clang libcares python3 pkg_add git autoconf-2.69p2 automake-1.15.1 libtool bison - pkg_add gmake json-c py-test py-sphinx libexecinfo + pkg_add gmake json-c py-test py-sphinx libexecinfo protobuf-c Select Python2.7 as default (required for pytest) diff --git a/doc/developer/building-frr-for-opensuse.rst b/doc/developer/building-frr-for-opensuse.rst index 38346fe88115..3ff445bcd062 100644 --- a/doc/developer/building-frr-for-opensuse.rst +++ b/doc/developer/building-frr-for-opensuse.rst @@ -14,7 +14,7 @@ Installing Dependencies readline-devel texinfo net-snmp-devel groff pkgconfig libjson-c-devel\ pam-devel python3-pytest bison flex c-ares-devel python3-devel\ python3-Sphinx perl patch libcap-devel libyang-devel \ - libelf-devel libunwind-devel + libelf-devel libunwind-devel protobuf-c .. include:: building-libunwind-note.rst diff --git a/doc/developer/building-frr-for-ubuntu1404.rst b/doc/developer/building-frr-for-ubuntu1404.rst index 2711e92b6ff1..cc6c3c03f383 100644 --- a/doc/developer/building-frr-for-ubuntu1404.rst +++ b/doc/developer/building-frr-for-ubuntu1404.rst @@ -18,6 +18,13 @@ Installing Dependencies .. include:: building-libyang.rst +Protobuf +^^^^^^^^ + +.. code-block:: console + + sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + Building & Installing FRR ------------------------- diff --git a/doc/developer/building-frr-for-ubuntu1604.rst b/doc/developer/building-frr-for-ubuntu1604.rst index d79545c859b0..e5c2389f399c 100644 --- a/doc/developer/building-frr-for-ubuntu1604.rst +++ b/doc/developer/building-frr-for-ubuntu1604.rst @@ -15,10 +15,17 @@ Installing Dependencies pkg-config libpam0g-dev libjson-c-dev bison flex python3-pytest \ libc-ares-dev python3-dev python-ipaddress python3-sphinx \ install-info build-essential libsnmp-dev perl libcap-dev \ - libelf-dev + libelf-dev libprotobuf-c-dev protobuf-c-compiler .. include:: building-libyang.rst +Protobuf +^^^^^^^^ + +.. code-block:: console + + sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + Building & Installing FRR ------------------------- diff --git a/doc/developer/building-frr-for-ubuntu2204.rst b/doc/developer/building-frr-for-ubuntu2204.rst new file mode 100644 index 000000000000..7e62d85763e7 --- /dev/null +++ b/doc/developer/building-frr-for-ubuntu2204.rst @@ -0,0 +1,182 @@ +Ubuntu 22.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 \ + libc-ares-dev python3-dev python3-sphinx \ + install-info build-essential libsnmp-dev perl \ + libcap-dev python2 libelf-dev libunwind-dev \ + libyang2 libyang2-dev + +.. include:: building-libunwind-note.rst + +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``. + +.. code-block:: shell + + sudo ln -s /usr/bin/python3 /usr/bin/python + python --version + +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/pip/2.7/get-pip.py --output get-pip.py + sudo python2 ./get-pip.py + + # And verify the installation + pip2 --version + + +Protobuf +^^^^^^^^ +This is optional + +.. code-block:: console + + sudo apt-get install protobuf-c-compiler libprotobuf-c-dev + + +Config Rollbacks +^^^^^^^^^^^^^^^^ + +If config rollbacks are enabled using ``--enable-config-rollbacks`` +the sqlite3 developer package also should be installed. + +.. code-block:: console + sudo apt install libsqlite3-dev + + +ZeroMQ +^^^^^^ +This is optional + +.. 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.rst b/doc/developer/building.rst index 2d8cc209b005..dac0f9a84d19 100644 --- a/doc/developer/building.rst +++ b/doc/developer/building.rst @@ -9,13 +9,13 @@ Building FRR static-linking building-frr-for-alpine + building-frr-for-archlinux building-frr-for-centos6 building-frr-for-centos7 building-frr-for-centos8 building-frr-for-debian8 building-frr-for-debian9 building-frr-for-fedora - building-frr-for-opensuse building-frr-for-freebsd9 building-frr-for-freebsd10 building-frr-for-freebsd11 @@ -23,11 +23,12 @@ Building FRR building-frr-for-netbsd6 building-frr-for-netbsd7 building-frr-for-openbsd6 + building-frr-for-opensuse building-frr-for-openwrt building-frr-for-ubuntu1404 building-frr-for-ubuntu1604 building-frr-for-ubuntu1804 building-frr-for-ubuntu2004 - building-frr-for-archlinux + building-frr-for-ubuntu2204 building-docker cross-compiling diff --git a/doc/developer/checkpatch.rst b/doc/developer/checkpatch.rst new file mode 100644 index 000000000000..b52452bc2963 --- /dev/null +++ b/doc/developer/checkpatch.rst @@ -0,0 +1,1249 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +========== +Checkpatch +========== + +Checkpatch (scripts/checkpatch.pl) is a perl script which checks for trivial +style violations in patches and optionally corrects them. Checkpatch can +also be run on file contexts and without the kernel tree. + +Checkpatch is not always right. Your judgement takes precedence over checkpatch +messages. If your code looks better with the violations, then its probably +best left alone. + + +Options +======= + +This section will describe the options checkpatch can be run with. + +Usage:: + + ./scripts/checkpatch.pl [OPTION]... [FILE]... + +Available options: + + - -q, --quiet + + Enable quiet mode. + + - -v, --verbose + Enable verbose mode. Additional verbose test descriptions are output + so as to provide information on why that particular message is shown. + + - --no-tree + + Run checkpatch without the kernel tree. + + - --no-signoff + + Disable the 'Signed-off-by' line check. The sign-off is a simple line at + the end of the explanation for the patch, which certifies that you wrote it + or otherwise have the right to pass it on as an open-source patch. + + Example:: + + Signed-off-by: Random J Developer + + Setting this flag effectively stops a message for a missing signed-off-by + line in a patch context. + + - --patch + + Treat FILE as a patch. This is the default option and need not be + explicitly specified. + + - --emacs + + Set output to emacs compile window format. This allows emacs users to jump + from the error in the compile window directly to the offending line in the + patch. + + - --terse + + Output only one line per report. + + - --showfile + + Show the diffed file position instead of the input file position. + + - -g, --git + + Treat FILE as a single commit or a git revision range. + + Single commit with: + + - + - ^ + - ~n + + Multiple commits with: + + - .. + - ... + - - + + - -f, --file + + Treat FILE as a regular source file. This option must be used when running + checkpatch on source files in the kernel. + + - --subjective, --strict + + Enable stricter tests in checkpatch. By default the tests emitted as CHECK + do not activate by default. Use this flag to activate the CHECK tests. + + - --list-types + + Every message emitted by checkpatch has an associated TYPE. Add this flag + to display all the types in checkpatch. + + Note that when this flag is active, checkpatch does not read the input FILE, + and no message is emitted. Only a list of types in checkpatch is output. + + - --types TYPE(,TYPE2...) + + Only display messages with the given types. + + Example:: + + ./scripts/checkpatch.pl mypatch.patch --types EMAIL_SUBJECT,BRACES + + - --ignore TYPE(,TYPE2...) + + Checkpatch will not emit messages for the specified types. + + Example:: + + ./scripts/checkpatch.pl mypatch.patch --ignore EMAIL_SUBJECT,BRACES + + - --show-types + + By default checkpatch doesn't display the type associated with the messages. + Set this flag to show the message type in the output. + + - --max-line-length=n + + Set the max line length (default 100). If a line exceeds the specified + length, a LONG_LINE message is emitted. + + + The message level is different for patch and file contexts. For patches, + a WARNING is emitted. While a milder CHECK is emitted for files. So for + file contexts, the --strict flag must also be enabled. + + - --min-conf-desc-length=n + + Set the Kconfig entry minimum description length, if shorter, warn. + + - --tab-size=n + + Set the number of spaces for tab (default 8). + + - --root=PATH + + PATH to the kernel tree root. + + This option must be specified when invoking checkpatch from outside + the kernel root. + + - --no-summary + + Suppress the per file summary. + + - --mailback + + Only produce a report in case of Warnings or Errors. Milder Checks are + excluded from this. + + - --summary-file + + Include the filename in summary. + + - --debug KEY=[0|1] + + Turn on/off debugging of KEY, where KEY is one of 'values', 'possible', + 'type', and 'attr' (default is all off). + + - --fix + + This is an EXPERIMENTAL feature. If correctable errors exists, a file + .EXPERIMENTAL-checkpatch-fixes is created which has the + automatically fixable errors corrected. + + - --fix-inplace + + EXPERIMENTAL - Similar to --fix but input file is overwritten with fixes. + + DO NOT USE this flag unless you are absolutely sure and you have a backup + in place. + + - --ignore-perl-version + + Override checking of perl version. Runtime errors maybe encountered after + enabling this flag if the perl version does not meet the minimum specified. + + - --codespell + + Use the codespell dictionary for checking spelling errors. + + - --codespellfile + + Use the specified codespell file. + Default is '/usr/share/codespell/dictionary.txt'. + + - --typedefsfile + + Read additional types from this file. + + - --color[=WHEN] + + Use colors 'always', 'never', or only when output is a terminal ('auto'). + Default is 'auto'. + + - --kconfig-prefix=WORD + + Use WORD as a prefix for Kconfig symbols (default is `CONFIG_`). + + - -h, --help, --version + + Display the help text. + +Message Levels +============== + +Messages in checkpatch are divided into three levels. The levels of messages +in checkpatch denote the severity of the error. They are: + + - ERROR + + This is the most strict level. Messages of type ERROR must be taken + seriously as they denote things that are very likely to be wrong. + + - WARNING + + This is the next stricter level. Messages of type WARNING requires a + more careful review. But it is milder than an ERROR. + + - CHECK + + This is the mildest level. These are things which may require some thought. + +Type Descriptions +================= + +This section contains a description of all the message types in checkpatch. + +.. Types in this section are also parsed by checkpatch. +.. The types are grouped into subsections based on use. + + +Allocation style +---------------- + + **ALLOC_ARRAY_ARGS** + The first argument for kcalloc or kmalloc_array should be the + number of elements. sizeof() as the first argument is generally + wrong. + + See: https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html + + **ALLOC_SIZEOF_STRUCT** + The allocation style is bad. In general for family of + allocation functions using sizeof() to get memory size, + constructs like:: + + p = alloc(sizeof(struct foo), ...) + + should be:: + + p = alloc(sizeof(*p), ...) + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#allocating-memory + + **ALLOC_WITH_MULTIPLY** + Prefer kmalloc_array/kcalloc over kmalloc/kzalloc with a + sizeof multiply. + + See: https://www.kernel.org/doc/html/latest/core-api/memory-allocation.html + + +API usage +--------- + + **ARCH_DEFINES** + Architecture specific defines should be avoided wherever + possible. + + **ARCH_INCLUDE_LINUX** + Whenever asm/file.h is included and linux/file.h exists, a + conversion can be made when linux/file.h includes asm/file.h. + However this is not always the case (See signal.h). + This message type is emitted only for includes from arch/. + + **AVOID_BUG** + BUG() or BUG_ON() should be avoided totally. + Use WARN() and WARN_ON() instead, and handle the "impossible" + error condition as gracefully as possible. + + See: https://www.kernel.org/doc/html/latest/process/deprecated.html#bug-and-bug-on + + **CONSIDER_KSTRTO** + The simple_strtol(), simple_strtoll(), simple_strtoul(), and + simple_strtoull() functions explicitly ignore overflows, which + may lead to unexpected results in callers. The respective kstrtol(), + kstrtoll(), kstrtoul(), and kstrtoull() functions tend to be the + correct replacements. + + See: https://www.kernel.org/doc/html/latest/process/deprecated.html#simple-strtol-simple-strtoll-simple-strtoul-simple-strtoull + + **CONSTANT_CONVERSION** + Use of __constant_ form is discouraged for the following functions:: + + __constant_cpu_to_be[x] + __constant_cpu_to_le[x] + __constant_be[x]_to_cpu + __constant_le[x]_to_cpu + __constant_htons + __constant_ntohs + + Using any of these outside of include/uapi/ is not preferred as using the + function without __constant_ is identical when the argument is a + constant. + + In big endian systems, the macros like __constant_cpu_to_be32(x) and + cpu_to_be32(x) expand to the same expression:: + + #define __constant_cpu_to_be32(x) ((__force __be32)(__u32)(x)) + #define __cpu_to_be32(x) ((__force __be32)(__u32)(x)) + + In little endian systems, the macros __constant_cpu_to_be32(x) and + cpu_to_be32(x) expand to __constant_swab32 and __swab32. __swab32 + has a __builtin_constant_p check:: + + #define __swab32(x) \ + (__builtin_constant_p((__u32)(x)) ? \ + ___constant_swab32(x) : \ + __fswab32(x)) + + So ultimately they have a special case for constants. + Similar is the case with all of the macros in the list. Thus + using the __constant_... forms are unnecessarily verbose and + not preferred outside of include/uapi. + + See: https://lore.kernel.org/lkml/1400106425.12666.6.camel@joe-AO725/ + + **DEPRECATED_API** + Usage of a deprecated RCU API is detected. It is recommended to replace + old flavourful RCU APIs by their new vanilla-RCU counterparts. + + The full list of available RCU APIs can be viewed from the kernel docs. + + See: https://www.kernel.org/doc/html/latest/RCU/whatisRCU.html#full-list-of-rcu-apis + + **DEPRECATED_VARIABLE** + EXTRA_{A,C,CPP,LD}FLAGS are deprecated and should be replaced by the new + flags added via commit f77bf01425b1 ("kbuild: introduce ccflags-y, + asflags-y and ldflags-y"). + + The following conversion scheme maybe used:: + + EXTRA_AFLAGS -> asflags-y + EXTRA_CFLAGS -> ccflags-y + EXTRA_CPPFLAGS -> cppflags-y + EXTRA_LDFLAGS -> ldflags-y + + See: + + 1. https://lore.kernel.org/lkml/20070930191054.GA15876@uranus.ravnborg.org/ + 2. https://lore.kernel.org/lkml/1313384834-24433-12-git-send-email-lacombar@gmail.com/ + 3. https://www.kernel.org/doc/html/latest/kbuild/makefiles.html#compilation-flags + + **DEVICE_ATTR_FUNCTIONS** + The function names used in DEVICE_ATTR is unusual. + Typically, the store and show functions are used with _store and + _show, where is a named attribute variable of the device. + + Consider the following examples:: + + static DEVICE_ATTR(type, 0444, type_show, NULL); + static DEVICE_ATTR(power, 0644, power_show, power_store); + + The function names should preferably follow the above pattern. + + See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes + + **DEVICE_ATTR_RO** + The DEVICE_ATTR_RO(name) helper macro can be used instead of + DEVICE_ATTR(name, 0444, name_show, NULL); + + Note that the macro automatically appends _show to the named + attribute variable of the device for the show method. + + See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes + + **DEVICE_ATTR_RW** + The DEVICE_ATTR_RW(name) helper macro can be used instead of + DEVICE_ATTR(name, 0644, name_show, name_store); + + Note that the macro automatically appends _show and _store to the + named attribute variable of the device for the show and store methods. + + See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes + + **DEVICE_ATTR_WO** + The DEVICE_AATR_WO(name) helper macro can be used instead of + DEVICE_ATTR(name, 0200, NULL, name_store); + + Note that the macro automatically appends _store to the + named attribute variable of the device for the store method. + + See: https://www.kernel.org/doc/html/latest/driver-api/driver-model/device.html#attributes + + **DUPLICATED_SYSCTL_CONST** + Commit d91bff3011cf ("proc/sysctl: add shared variables for range + check") added some shared const variables to be used instead of a local + copy in each source file. + + Consider replacing the sysctl range checking value with the shared + one in include/linux/sysctl.h. The following conversion scheme may + be used:: + + &zero -> SYSCTL_ZERO + &one -> SYSCTL_ONE + &int_max -> SYSCTL_INT_MAX + + See: + + 1. https://lore.kernel.org/lkml/20190430180111.10688-1-mcroce@redhat.com/ + 2. https://lore.kernel.org/lkml/20190531131422.14970-1-mcroce@redhat.com/ + + **ENOSYS** + ENOSYS means that a nonexistent system call was called. + Earlier, it was wrongly used for things like invalid operations on + otherwise valid syscalls. This should be avoided in new code. + + See: https://lore.kernel.org/lkml/5eb299021dec23c1a48fa7d9f2c8b794e967766d.1408730669.git.luto@amacapital.net/ + + **ENOTSUPP** + ENOTSUPP is not a standard error code and should be avoided in new patches. + EOPNOTSUPP should be used instead. + + See: https://lore.kernel.org/netdev/20200510182252.GA411829@lunn.ch/ + + **EXPORT_SYMBOL** + EXPORT_SYMBOL should immediately follow the symbol to be exported. + + **IN_ATOMIC** + in_atomic() is not for driver use so any such use is reported as an ERROR. + Also in_atomic() is often used to determine if sleeping is permitted, + but it is not reliable in this use model. Therefore its use is + strongly discouraged. + + However, in_atomic() is ok for core kernel use. + + See: https://lore.kernel.org/lkml/20080320201723.b87b3732.akpm@linux-foundation.org/ + + **LOCKDEP** + The lockdep_no_validate class was added as a temporary measure to + prevent warnings on conversion of device->sem to device->mutex. + It should not be used for any other purpose. + + See: https://lore.kernel.org/lkml/1268959062.9440.467.camel@laptop/ + + **MALFORMED_INCLUDE** + The #include statement has a malformed path. This has happened + because the author has included a double slash "//" in the pathname + accidentally. + + **USE_LOCKDEP** + lockdep_assert_held() annotations should be preferred over + assertions based on spin_is_locked() + + See: https://www.kernel.org/doc/html/latest/locking/lockdep-design.html#annotations + + **UAPI_INCLUDE** + No #include statements in include/uapi should use a uapi/ path. + + **USLEEP_RANGE** + usleep_range() should be preferred over udelay(). The proper way of + using usleep_range() is mentioned in the kernel docs. + + See: https://www.kernel.org/doc/html/latest/timers/timers-howto.html#delays-information-on-the-various-kernel-delay-sleep-mechanisms + + +Comments +-------- + + **BLOCK_COMMENT_STYLE** + The comment style is incorrect. The preferred style for multi- + line comments is:: + + /* + * This is the preferred style + * for multi line comments. + */ + + The networking comment style is a bit different, with the first line + not empty like the former:: + + /* This is the preferred comment style + * for files in net/ and drivers/net/ + */ + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#commenting + + **C99_COMMENTS** + C99 style single line comments (//) should not be used. + Prefer the block comment style instead. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#commenting + + **DATA_RACE** + Applications of data_race() should have a comment so as to document the + reasoning behind why it was deemed safe. + + See: https://lore.kernel.org/lkml/20200401101714.44781-1-elver@google.com/ + + **FSF_MAILING_ADDRESS** + Kernel maintainers reject new instances of the GPL boilerplate paragraph + directing people to write to the FSF for a copy of the GPL, since the + FSF has moved in the past and may do so again. + So do not write paragraphs about writing to the Free Software Foundation's + mailing address. + + See: https://lore.kernel.org/lkml/20131006222342.GT19510@leaf/ + + +Commit message +-------------- + + **BAD_SIGN_OFF** + The signed-off-by line does not fall in line with the standards + specified by the community. + + See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#developer-s-certificate-of-origin-1-1 + + **BAD_STABLE_ADDRESS_STYLE** + The email format for stable is incorrect. + Some valid options for stable address are:: + + 1. stable@vger.kernel.org + 2. stable@kernel.org + + For adding version info, the following comment style should be used:: + + stable@vger.kernel.org # version info + + **COMMIT_COMMENT_SYMBOL** + Commit log lines starting with a '#' are ignored by git as + comments. To solve this problem addition of a single space + infront of the log line is enough. + + **COMMIT_MESSAGE** + The patch is missing a commit description. A brief + description of the changes made by the patch should be added. + + See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes + + **EMAIL_SUBJECT** + Naming the tool that found the issue is not very useful in the + subject line. A good subject line summarizes the change that + the patch brings. + + See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes + + **FROM_SIGN_OFF_MISMATCH** + The author's email does not match with that in the Signed-off-by: + line(s). This can be sometimes caused due to an improperly configured + email client. + + This message is emitted due to any of the following reasons:: + + - The email names do not match. + - The email addresses do not match. + - The email subaddresses do not match. + - The email comments do not match. + + **MISSING_SIGN_OFF** + The patch is missing a Signed-off-by line. A signed-off-by + line should be added according to Developer's certificate of + Origin. + + See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin + + **NO_AUTHOR_SIGN_OFF** + The author of the patch has not signed off the patch. It is + required that a simple sign off line should be present at the + end of explanation of the patch to denote that the author has + written it or otherwise has the rights to pass it on as an open + source patch. + + See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin + + **DIFF_IN_COMMIT_MSG** + Avoid having diff content in commit message. + This causes problems when one tries to apply a file containing both + the changelog and the diff because patch(1) tries to apply the diff + which it found in the changelog. + + See: https://lore.kernel.org/lkml/20150611134006.9df79a893e3636019ad2759e@linux-foundation.org/ + + **GERRIT_CHANGE_ID** + To be picked up by gerrit, the footer of the commit message might + have a Change-Id like:: + + Change-Id: Ic8aaa0728a43936cd4c6e1ed590e01ba8f0fbf5b + Signed-off-by: A. U. Thor + + The Change-Id line must be removed before submitting. + + **GIT_COMMIT_ID** + The proper way to reference a commit id is: + commit <12+ chars of sha1> ("") + + An example may be:: + + Commit e21d2170f36602ae2708 ("video: remove unnecessary + platform_set_drvdata()") removed the unnecessary + platform_set_drvdata(), but left the variable "dev" unused, + delete it. + + See: https://www.kernel.org/doc/html/latest/process/submitting-patches.html#describe-your-changes + + +Comparison style +---------------- + + **ASSIGN_IN_IF** + Do not use assignments in if condition. + Example:: + + if ((foo = bar(...)) < BAZ) { + + should be written as:: + + foo = bar(...); + if (foo < BAZ) { + + **BOOL_COMPARISON** + Comparisons of A to true and false are better written + as A and !A. + + See: https://lore.kernel.org/lkml/1365563834.27174.12.camel@joe-AO722/ + + **COMPARISON_TO_NULL** + Comparisons to NULL in the form (foo == NULL) or (foo != NULL) + are better written as (!foo) and (foo). + + **CONSTANT_COMPARISON** + Comparisons with a constant or upper case identifier on the left + side of the test should be avoided. + + +Indentation and Line Breaks +--------------------------- + + **CODE_INDENT** + Code indent should use tabs instead of spaces. + Outside of comments, documentation and Kconfig, + spaces are never used for indentation. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#indentation + + **DEEP_INDENTATION** + Indentation with 6 or more tabs usually indicate overly indented + code. + + It is suggested to refactor excessive indentation of + if/else/for/do/while/switch statements. + + See: https://lore.kernel.org/lkml/1328311239.21255.24.camel@joe2Laptop/ + + **SWITCH_CASE_INDENT_LEVEL** + switch should be at the same indent as case. + Example:: + + switch (suffix) { + case 'G': + case 'g': + mem <<= 30; + break; + case 'M': + case 'm': + mem <<= 20; + break; + case 'K': + case 'k': + mem <<= 10; + fallthrough; + default: + break; + } + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#indentation + + **LONG_LINE** + The line has exceeded the specified maximum length. + To use a different maximum line length, the --max-line-length=n option + may be added while invoking checkpatch. + + Earlier, the default line length was 80 columns. Commit bdc48fa11e46 + ("checkpatch/coding-style: deprecate 80-column warning") increased the + limit to 100 columns. This is not a hard limit either and it's + preferable to stay within 80 columns whenever possible. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings + + **LONG_LINE_STRING** + A string starts before but extends beyond the maximum line length. + To use a different maximum line length, the --max-line-length=n option + may be added while invoking checkpatch. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings + + **LONG_LINE_COMMENT** + A comment starts before but extends beyond the maximum line length. + To use a different maximum line length, the --max-line-length=n option + may be added while invoking checkpatch. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#breaking-long-lines-and-strings + + **SPLIT_STRING** + Quoted strings that appear as messages in userspace and can be + grepped, should not be split across multiple lines. + + See: https://lore.kernel.org/lkml/20120203052727.GA15035@leaf/ + + **MULTILINE_DEREFERENCE** + A single dereferencing identifier spanned on multiple lines like:: + + struct_identifier->member[index]. + member = <foo>; + + is generally hard to follow. It can easily lead to typos and so makes + the code vulnerable to bugs. + + If fixing the multiple line dereferencing leads to an 80 column + violation, then either rewrite the code in a more simple way or if the + starting part of the dereferencing identifier is the same and used at + multiple places then store it in a temporary variable, and use that + temporary variable only at all the places. For example, if there are + two dereferencing identifiers:: + + member1->member2->member3.foo1; + member1->member2->member3.foo2; + + then store the member1->member2->member3 part in a temporary variable. + It not only helps to avoid the 80 column violation but also reduces + the program size by removing the unnecessary dereferences. + + But if none of the above methods work then ignore the 80 column + violation because it is much easier to read a dereferencing identifier + on a single line. + + **TRAILING_STATEMENTS** + Trailing statements (for example after any conditional) should be + on the next line. + Statements, such as:: + + if (x == y) break; + + should be:: + + if (x == y) + break; + + +Macros, Attributes and Symbols +------------------------------ + + **ARRAY_SIZE** + The ARRAY_SIZE(foo) macro should be preferred over + sizeof(foo)/sizeof(foo[0]) for finding number of elements in an + array. + + The macro is defined in include/linux/kernel.h:: + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + + **AVOID_EXTERNS** + Function prototypes don't need to be declared extern in .h + files. It's assumed by the compiler and is unnecessary. + + **AVOID_L_PREFIX** + Local symbol names that are prefixed with `.L` should be avoided, + as this has special meaning for the assembler; a symbol entry will + not be emitted into the symbol table. This can prevent `objtool` + from generating correct unwind info. + + Symbols with STB_LOCAL binding may still be used, and `.L` prefixed + local symbol names are still generally usable within a function, + but `.L` prefixed local symbol names should not be used to denote + the beginning or end of code regions via + `SYM_CODE_START_LOCAL`/`SYM_CODE_END` + + **BIT_MACRO** + Defines like: 1 << <digit> could be BIT(digit). + The BIT() macro is defined via include/linux/bits.h:: + + #define BIT(nr) (1UL << (nr)) + + **CONST_READ_MOSTLY** + When a variable is tagged with the __read_mostly annotation, it is a + signal to the compiler that accesses to the variable will be mostly + reads and rarely(but NOT never) a write. + + const __read_mostly does not make any sense as const data is already + read-only. The __read_mostly annotation thus should be removed. + + **DATE_TIME** + It is generally desirable that building the same source code with + the same set of tools is reproducible, i.e. the output is always + exactly the same. + + The kernel does *not* use the ``__DATE__`` and ``__TIME__`` macros, + and enables warnings if they are used as they can lead to + non-deterministic builds. + + See: https://www.kernel.org/doc/html/latest/kbuild/reproducible-builds.html#timestamps + + **DEFINE_ARCH_HAS** + The ARCH_HAS_xyz and ARCH_HAVE_xyz patterns are wrong. + + For big conceptual features use Kconfig symbols instead. And for + smaller things where we have compatibility fallback functions but + want architectures able to override them with optimized ones, we + should either use weak functions (appropriate for some cases), or + the symbol that protects them should be the same symbol we use. + + See: https://lore.kernel.org/lkml/CA+55aFycQ9XJvEOsiM3txHL5bjUc8CeKWJNR_H+MiicaddB42Q@mail.gmail.com/ + + **DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON** + do {} while(0) macros should not have a trailing semicolon. + + **INIT_ATTRIBUTE** + Const init definitions should use __initconst instead of + __initdata. + + Similarly init definitions without const require a separate + use of const. + + **INLINE_LOCATION** + The inline keyword should sit between storage class and type. + + For example, the following segment:: + + inline static int example_function(void) + { + ... + } + + should be:: + + static inline int example_function(void) + { + ... + } + + **MISPLACED_INIT** + It is possible to use section markers on variables in a way + which gcc doesn't understand (or at least not the way the + developer intended):: + + static struct __initdata samsung_pll_clock exynos4_plls[nr_plls] = { + + does not put exynos4_plls in the .initdata section. The __initdata + marker can be virtually anywhere on the line, except right after + "struct". The preferred location is before the "=" sign if there is + one, or before the trailing ";" otherwise. + + See: https://lore.kernel.org/lkml/1377655732.3619.19.camel@joe-AO722/ + + **MULTISTATEMENT_MACRO_USE_DO_WHILE** + Macros with multiple statements should be enclosed in a + do - while block. Same should also be the case for macros + starting with `if` to avoid logic defects:: + + #define macrofun(a, b, c) \ + do { \ + if (a == 5) \ + do_this(b, c); \ + } while (0) + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#macros-enums-and-rtl + + **PREFER_FALLTHROUGH** + Use the `fallthrough;` pseudo keyword instead of + `/* fallthrough */` like comments. + + **TRAILING_SEMICOLON** + Macro definition should not end with a semicolon. The macro + invocation style should be consistent with function calls. + This can prevent any unexpected code paths:: + + #define MAC do_something; + + If this macro is used within a if else statement, like:: + + if (some_condition) + MAC; + + else + do_something; + + Then there would be a compilation error, because when the macro is + expanded there are two trailing semicolons, so the else branch gets + orphaned. + + See: https://lore.kernel.org/lkml/1399671106.2912.21.camel@joe-AO725/ + + **SINGLE_STATEMENT_DO_WHILE_MACRO** + For the multi-statement macros, it is necessary to use the do-while + loop to avoid unpredictable code paths. The do-while loop helps to + group the multiple statements into a single one so that a + function-like macro can be used as a function only. + + But for the single statement macros, it is unnecessary to use the + do-while loop. Although the code is syntactically correct but using + the do-while loop is redundant. So remove the do-while loop for single + statement macros. + + **WEAK_DECLARATION** + Using weak declarations like __attribute__((weak)) or __weak + can have unintended link defects. Avoid using them. + + +Functions and Variables +----------------------- + + **CAMELCASE** + Avoid CamelCase Identifiers. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#naming + + **CONST_CONST** + Using `const <type> const *` is generally meant to be + written `const <type> * const`. + + **CONST_STRUCT** + Using const is generally a good idea. Checkpatch reads + a list of frequently used structs that are always or + almost always constant. + + The existing structs list can be viewed from + `scripts/const_structs.checkpatch`. + + See: https://lore.kernel.org/lkml/alpine.DEB.2.10.1608281509480.3321@hadrien/ + + **EMBEDDED_FUNCTION_NAME** + Embedded function names are less appropriate to use as + refactoring can cause function renaming. Prefer the use of + "%s", __func__ to embedded function names. + + Note that this does not work with -f (--file) checkpatch option + as it depends on patch context providing the function name. + + **FUNCTION_ARGUMENTS** + This warning is emitted due to any of the following reasons: + + 1. Arguments for the function declaration do not follow + the identifier name. Example:: + + void foo + (int bar, int baz) + + This should be corrected to:: + + void foo(int bar, int baz) + + 2. Some arguments for the function definition do not + have an identifier name. Example:: + + void foo(int) + + All arguments should have identifier names. + + **FUNCTION_WITHOUT_ARGS** + Function declarations without arguments like:: + + int foo() + + should be:: + + int foo(void) + + **GLOBAL_INITIALISERS** + Global variables should not be initialized explicitly to + 0 (or NULL, false, etc.). Your compiler (or rather your + loader, which is responsible for zeroing out the relevant + sections) automatically does it for you. + + **INITIALISED_STATIC** + Static variables should not be initialized explicitly to zero. + Your compiler (or rather your loader) automatically does + it for you. + + **MULTIPLE_ASSIGNMENTS** + Multiple assignments on a single line makes the code unnecessarily + complicated. So on a single line assign value to a single variable + only, this makes the code more readable and helps avoid typos. + + **RETURN_PARENTHESES** + return is not a function and as such doesn't need parentheses:: + + return (bar); + + can simply be:: + + return bar; + + +Permissions +----------- + + **DEVICE_ATTR_PERMS** + The permissions used in DEVICE_ATTR are unusual. + Typically only three permissions are used - 0644 (RW), 0444 (RO) + and 0200 (WO). + + See: https://www.kernel.org/doc/html/latest/filesystems/sysfs.html#attributes + + **EXECUTE_PERMISSIONS** + There is no reason for source files to be executable. The executable + bit can be removed safely. + + **EXPORTED_WORLD_WRITABLE** + Exporting world writable sysfs/debugfs files is usually a bad thing. + When done arbitrarily they can introduce serious security bugs. + In the past, some of the debugfs vulnerabilities would seemingly allow + any local user to write arbitrary values into device registers - a + situation from which little good can be expected to emerge. + + See: https://lore.kernel.org/linux-arm-kernel/cover.1296818921.git.segoon@openwall.com/ + + **NON_OCTAL_PERMISSIONS** + Permission bits should use 4 digit octal permissions (like 0700 or 0444). + Avoid using any other base like decimal. + + **SYMBOLIC_PERMS** + Permission bits in the octal form are more readable and easier to + understand than their symbolic counterparts because many command-line + tools use this notation. Experienced kernel developers have been using + these traditional Unix permission bits for decades and so they find it + easier to understand the octal notation than the symbolic macros. + For example, it is harder to read S_IWUSR|S_IRUGO than 0644, which + obscures the developer's intent rather than clarifying it. + + See: https://lore.kernel.org/lkml/CA+55aFw5v23T-zvDZp-MmD_EYxF8WbafwwB59934FV7g21uMGQ@mail.gmail.com/ + + +Spacing and Brackets +-------------------- + + **ASSIGNMENT_CONTINUATIONS** + Assignment operators should not be written at the start of a + line but should follow the operand at the previous line. + + **BRACES** + The placement of braces is stylistically incorrect. + The preferred way is to put the opening brace last on the line, + and put the closing brace first:: + + if (x is true) { + we do y + } + + This applies for all non-functional blocks. + However, there is one special case, namely functions: they have the + opening brace at the beginning of the next line, thus:: + + int function(int x) + { + body of function + } + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces + + **BRACKET_SPACE** + Whitespace before opening bracket '[' is prohibited. + There are some exceptions: + + 1. With a type on the left:: + + int [] a; + + 2. At the beginning of a line for slice initialisers:: + + [0...10] = 5, + + 3. Inside a curly brace:: + + = { [0...10] = 5 } + + **CONCATENATED_STRING** + Concatenated elements should have a space in between. + Example:: + + printk(KERN_INFO"bar"); + + should be:: + + printk(KERN_INFO "bar"); + + **ELSE_AFTER_BRACE** + `else {` should follow the closing block `}` on the same line. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces + + **LINE_SPACING** + Vertical space is wasted given the limited number of lines an + editor window can display when multiple blank lines are used. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces + + **OPEN_BRACE** + The opening brace should be following the function definitions on the + next line. For any non-functional block it should be on the same line + as the last construct. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces + + **POINTER_LOCATION** + When using pointer data or a function that returns a pointer type, + the preferred use of * is adjacent to the data name or function name + and not adjacent to the type name. + Examples:: + + char *linux_banner; + unsigned long long memparse(char *ptr, char **retptr); + char *match_strdup(substring_t *s); + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces + + **SPACING** + Whitespace style used in the kernel sources is described in kernel docs. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces + + **TRAILING_WHITESPACE** + Trailing whitespace should always be removed. + Some editors highlight the trailing whitespace and cause visual + distractions when editing files. + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#spaces + + **UNNECESSARY_PARENTHESES** + Parentheses are not required in the following cases: + + 1. Function pointer uses:: + + (foo->bar)(); + + could be:: + + foo->bar(); + + 2. Comparisons in if:: + + if ((foo->bar) && (foo->baz)) + if ((foo == bar)) + + could be:: + + if (foo->bar && foo->baz) + if (foo == bar) + + 3. addressof/dereference single Lvalues:: + + &(foo->bar) + *(foo->bar) + + could be:: + + &foo->bar + *foo->bar + + **WHILE_AFTER_BRACE** + while should follow the closing bracket on the same line:: + + do { + ... + } while(something); + + See: https://www.kernel.org/doc/html/latest/process/coding-style.html#placing-braces-and-spaces + + +Others +------ + + **CONFIG_DESCRIPTION** + Kconfig symbols should have a help text which fully describes + it. + + **CORRUPTED_PATCH** + The patch seems to be corrupted or lines are wrapped. + Please regenerate the patch file before sending it to the maintainer. + + **CVS_KEYWORD** + Since linux moved to git, the CVS markers are no longer used. + So, CVS style keywords ($Id$, $Revision$, $Log$) should not be + added. + + **DEFAULT_NO_BREAK** + switch default case is sometimes written as "default:;". This can + cause new cases added below default to be defective. + + A "break;" should be added after empty default statement to avoid + unwanted fallthrough. + + **DOS_LINE_ENDINGS** + For DOS-formatted patches, there are extra ^M symbols at the end of + the line. These should be removed. + + **DT_SCHEMA_BINDING_PATCH** + DT bindings moved to a json-schema based format instead of + freeform text. + + See: https://www.kernel.org/doc/html/latest/devicetree/bindings/writing-schema.html + + **DT_SPLIT_BINDING_PATCH** + Devicetree bindings should be their own patch. This is because + bindings are logically independent from a driver implementation, + they have a different maintainer (even though they often + are applied via the same tree), and it makes for a cleaner history in the + DT only tree created with git-filter-branch. + + See: https://www.kernel.org/doc/html/latest/devicetree/bindings/submitting-patches.html#i-for-patch-submitters + + **EMBEDDED_FILENAME** + Embedding the complete filename path inside the file isn't particularly + useful as often the path is moved around and becomes incorrect. + + **FILE_PATH_CHANGES** + Whenever files are added, moved, or deleted, the MAINTAINERS file + patterns can be out of sync or outdated. + + So MAINTAINERS might need updating in these cases. + + **MEMSET** + The memset use appears to be incorrect. This may be caused due to + badly ordered parameters. Please recheck the usage. + + **NOT_UNIFIED_DIFF** + The patch file does not appear to be in unified-diff format. Please + regenerate the patch file before sending it to the maintainer. + + **PRINTF_0XDECIMAL** + Prefixing 0x with decimal output is defective and should be corrected. + + **SPDX_LICENSE_TAG** + The source file is missing or has an improper SPDX identifier tag. + The Linux kernel requires the precise SPDX identifier in all source files, + and it is thoroughly documented in the kernel docs. + + See: https://www.kernel.org/doc/html/latest/process/license-rules.html + + **TYPO_SPELLING** + Some words may have been misspelled. Consider reviewing them. diff --git a/doc/developer/cli.rst b/doc/developer/cli.rst index 2a08531bd742..61b9cf6acb53 100644 --- a/doc/developer/cli.rst +++ b/doc/developer/cli.rst @@ -177,29 +177,29 @@ 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 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. | -+-----------------+-------------------------+-------------------------------------------------------+ -| ``ASNUM`` | ``<A.B|(1-4294967295>`` | Matches an AS in plain or dot format. | -+-----------------+-------------------------+-------------------------------------------------------+ ++-----------------+--------------------------+-------------------------------------------------------+ +| Token type | Syntax | Description | ++=================+==========================+=======================================================+ +| ``WORD`` | ``show ip bgp`` | Matches itself. In the 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. | ++-----------------+--------------------------+-------------------------------------------------------+ +| ``ASNUM`` | ``<A.B|(1-4294967295)>`` | Matches an AS in plain or dot format. | ++-----------------+--------------------------+-------------------------------------------------------+ 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 diff --git a/doc/developer/cspf.rst b/doc/developer/cspf.rst index 426553ff064b..9ff673a23944 100644 --- a/doc/developer/cspf.rst +++ b/doc/developer/cspf.rst @@ -162,6 +162,7 @@ various metrics. Link State provides such Traffic Engineering Database. To perform a Path Computation with given constraints, proceed as follow: .. code-block:: c + struct cspf *algo; struct ls_ted *ted; struct in_addr src; diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst index 6ba44b9c49f0..6088b52da44b 100644 --- a/doc/developer/frr-release-procedure.rst +++ b/doc/developer/frr-release-procedure.rst @@ -145,6 +145,30 @@ Stage 2 - Staging #. Kick off the Snapcraft build plan for the release. +#. Build Docker images + + 1. Log into the Netdef Docker build VM + 2. ``sudo -su builduser`` + 3. Suppose we are releasing 8.5.0, then ``X.Y.Z`` is ``8.5.0``. Run this: + + .. code-block:: console + + cd /home/builduser/frr + TAG=X.Y.Z + git fetch --all + git checkout frr-<version> + docker buildx build --platform linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/arm/v7,linux/arm/v6 -f docker/alpine/Dockerfile -t quay.io/frrouting/frr:$TAG --push . + git tag docker/$TAG + git push origin docker/$TAG + + This will build a multi-arch image and upload it to Quay, as well as + create a git tag corresponding to the commit that the image was built + from and upload that to Github. It's important that the git tag point to + the exact codebase that was used to build the docker image, so if any + changes need to be made on top of the ``frr-<version>`` release tag, make + sure these changes are committed and pointed at by the ``docker/X.Y.Z`` + tag. + Stage 3 - Publish ----------------- diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst index 513cac61797a..b98d237e68a8 100644 --- a/doc/developer/include-compile.rst +++ b/doc/developer/include-compile.rst @@ -17,7 +17,6 @@ obtained by running ``./configure -h``. The options shown below are examples. --localstatedir=/var/run/frr \ --sysconfdir=/etc/frr \ --with-moduledir=\${prefix}/lib/frr/modules \ - --with-libyang-pluginsdir=\${prefix}/lib/frr/libyang_plugins \ --enable-configfile-mask=0640 \ --enable-logfile-mask=0640 \ --enable-snmp=agentx \ diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 46fd8f612e89..5da7bc416849 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -12,6 +12,7 @@ FRRouting Developer's Guide fuzzing tracing testing + mgmtd-dev bgpd fpm grpc diff --git a/doc/developer/link-state.rst b/doc/developer/link-state.rst index 2072595e36da..aaa253de9432 100644 --- a/doc/developer/link-state.rst +++ b/doc/developer/link-state.rst @@ -9,7 +9,7 @@ build and manage a Traffic Engineering Database for the various FRR daemons. This API has been designed for several use cases: - BGP Link State (BGP-LS): where BGP protocol need to collect the link state - information from the routing daemons (IS-IS and/or OSPF) to implement RFC7572 + information from the routing daemons (IS-IS and/or OSPF) to implement RFC7752 - Path Computation Element (PCE): where path computation algorithms are based on Traffic Engineering Database - ReSerVation Protocol (RSVP): where signaling need to know the Traffic diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index e262f6af9421..52653d37686c 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -335,16 +335,16 @@ Time/interval formats FRR library helper formats ^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. frrfmt:: %pTH (struct thread *) +.. frrfmt:: %pTH (struct event *) - Print remaining time on timer thread. Interval-printing flag characters + Print remaining time on timer event. Interval-printing flag characters listed above for ``%pTV`` can be added, e.g. ``%pTHtx``. ``NULL`` pointers are printed as ``-``. -.. frrfmt:: %pTHD (struct thread *) +.. frrfmt:: %pTHD (struct event *) - Print debugging information for given thread. Sample output: + Print debugging information for given event. Sample output: .. code-block:: none @@ -502,6 +502,51 @@ General utility formats representation for a hexdump. Non-printable characters are replaced with a dot. +.. frrfmt:: %pIS (struct iso_address *) + + ([IS]o Network address) - Format ISO Network Address + + ``%pIS``: :frrfmtout:`01.0203.04O5` + ISO Network address is printed as separated byte. The number of byte of the + address is embeded in the `iso_net` structure. + + ``%pISl``: :frrfmtout:`01.0203.04O5.0607.0809.1011.1213.14` - long format to + print the long version of the ISO Network address which include the System + ID and the PSEUDO-ID of the IS-IS system + + Note that the `ISO_ADDR_STRLEN` define gives the total size of the string + that could be used in conjunction to snprintfrr. Use like:: + + char buf[ISO_ADDR_STRLEN]; + struct iso_address addr = {.addr_len = 4, .area_addr = {1, 2, 3, 4}}; + snprintfrr(buf, ISO_ADDR_STRLEN, "%pIS", &addr); + +.. frrfmt:: %pSY (uint8_t *) + + (IS-IS [SY]stem ID) - Format IS-IS System ID + + ``%pSY``: :frrfmtout:`0102.0304.0506` + +.. frrfmt:: %pPN (uint8_t *) + + (IS-IS [P]seudo [N]ode System ID) - Format IS-IS Pseudo Node System ID + + ``%pPN``: :frrfmtout:`0102.0304.0506.07` + +.. frrfmt:: %pLS (uint8_t *) + + (IS-IS [L]sp fragment [S]ystem ID) - Format IS-IS Pseudo System ID + + ``%pLS``: :frrfmtout:`0102.0304.0506.07-08` + + Note that the `ISO_SYSID_STRLEN` define gives the total size of the string + that could be used in conjunction to snprintfrr. Use like:: + + char buf[ISO_SYSID_STRLEN]; + uint8_t id[8] = {1, 2, 3, 4 , 5 , 6 , 7, 8}; + snprintfrr(buf, SYS_ID_SIZE, "%pSY", id); + + Integer formats ^^^^^^^^^^^^^^^ diff --git a/doc/developer/mgmtd-dev.rst b/doc/developer/mgmtd-dev.rst new file mode 100644 index 000000000000..9839aa8b6c82 --- /dev/null +++ b/doc/developer/mgmtd-dev.rst @@ -0,0 +1,222 @@ +.. +.. SPDX-License-Identifier: GPL-2.0-or-later +.. +.. June 19 2023, Christian Hopps <chopps@labn.net> +.. +.. Copyright (c) 2023, LabN Consulting, L.L.C. +.. + +.. _mgmtd_dev: + +MGMTD Development +================= + +Overview +^^^^^^^^ + +``mgmtd`` (Management Daemon) is a new centralized management daemon for FRR. + +Previously, ``vtysh`` was the only centralized management service provided. +Internally ``vtysh`` connects to each daemon and sends CLI commands (both +configuration and operational state queries) over a socket connection. This +service only supports CLI which is no longer sufficient. + +An important next step was made with the addition of YANG support. A YANG +infrastructure was added through a new development called *northbound*. This +*northbound* interface added the capability of daemons to be configured and +queried using YANG models. However, this interface was per daemon and not +centralized, which is not sufficient. + +``mgmtd`` harnesses this new *northbound* interface to provide a centralized +interface for all daemons. It utilizes the daemons YANG models to interact with +each daemon. ``mgmtd`` currently provides the CLI interface for each daemon that +has been converted to it, but in the future RESTCONF and NETCONF servers can +easily be added as *front-ends* to mgmtd to support those protocols as well. + + +Converting A Daemon to MGMTD +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A daemon must first be transitioned to the new *northbound* interface if that +has not already been done (see `this northbound conversion documentation +<https://github.com/opensourcerouting/frr/wiki/Retrofitting-Configuration-Commands>`_ +for how to do this). Once this is done a few simple steps are all that is +required move the daemon over to ``mgmtd`` control. + +Overview of Changes +------------------- + +Adding support for a *northbound* converted daemon involves very little work. It +requires enabling *frontend* (CLI and YANG) and *backend* (YANG) support. +``mgmtd`` was designed to keep this as simple as possible. + +Front-End Interface: + +1. Add YANG module file to ``mgmtd/subdir.am`` (e.g., ``yang/frr-staticd.c``) +2. Add YANG module description into array defined in ``mgmtd/mgmt_main.c`` +3. Add CLI handler file[s] to ``mgmtd/subdir.am`` (e.g., ``staticd/static_vty.c``) +4. [if needed] Exclude (#ifndef) non-configuration CLI handlers from CLI source + file (e.g., inside ``staticd/static_vty.c``) + +Back-End Interface: + +5. Add XPATHs mappings to a couple arrays to direct ``mgmtd`` at your daemon in + ``mgmtd/mgmt_be_adapter.c`` + + +Add YANG and CLI into MGMTD +--------------------------- + +As an example here is the addition made to ``mgmtd/subdir.am`` for adding +``staticd`` support. + +.. code-block:: make + + if STATICD + nodist_mgmtd_mgmtd_SOURCES += \ + yang/frr-staticd.yang.c \ + yang/frr-bfdd.yang.c \ + # end + nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c + endif + +An here is the addition to the modules array in ``mgmtd/mgmt_main.c``: + +.. code-block:: c + + static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + &frr_filter_info, + ... + #ifdef HAVE_STATICD + &(struct frr_yang_module_info){.name = "frr-staticd", + .ignore_cbs = true}, + #endif + } + + +CLI Handlers +------------ + +The daemon's CLI handlers for configuration (which having been converted to +*northbound* now simply generate YANG changes) will be linked directly into +``mgmtd``. + +If the operational and debug CLI commands are kept in files separate from the +daemon's configuration CLI commands then no extra work is required. Otherwise some +CPP #ifndef's will be required. + +Currently ``mgmtd`` supports configuration CLI but not operational +state so if both types of CLI handlers are present in a single file (e.g. a +``xxx_vty.c`` or ``xxx_cli.c`` file ) then #ifndef will be used to exclude these +non-configuration CLI handlers from ``mgmtd``. The same goes for *debug* CLI +handlers. For example: + +.. code-block:: c + + DEFPY(daemon_one_config, daemon_one_config_cmd, + "daemon one [optional-arg]" + ... + { + ... + } + + #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY + DEFPY(daemon_show_oepr, daemon_show_oepr_cmd, + "show daemon oper [all]" + ... + { + ... + } + #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ + + void daemon_vty_init(void) + { + install_element(CONFIG_NODE, &daemon_one_config_cmd); + ... + + #ifndef INCLUDE_MGMTD_CMDDEFS_ONLY + install_element(ENABLE_NODE, &daemon_show_oper_cmd); + #endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ + + } + + +Add Back-End XPATH mappings +--------------------------- + +In order for ``mgmtd`` to direct configuration to your daemon you need to add +some XPATH mappings to ``mgmtd/mgmt_be_adapter.c``. These XPATHs determine which +configuration changes get sent over the *back-end* interface to your daemon. + +Below are the strings added for staticd support: + +.. code-block:: c + + static const struct mgmt_be_xpath_map_init mgmt_xpath_map_init[] = { + { + .xpath_regexp = "/frr-vrf:lib/*", + .subscr_info = + { + #if HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = + MGMT_SUBSCR_VALIDATE_CFG | + MGMT_SUBSCR_NOTIFY_CFG, + #endif + }, + }, + ... + { + .xpath_regexp = + "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", + .subscr_info = + { + #if HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = + MGMT_SUBSCR_VALIDATE_CFG | + MGMT_SUBSCR_NOTIFY_CFG, + #endif + }, + }, + }; + + #if HAVE_STATICD + static struct mgmt_be_client_xpath staticd_xpaths[] = { + { + .xpath = "/frr-vrf:lib/*", + .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, + }, + ... + { + .xpath = + "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", + .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, + }, + }; + #endif + + static struct mgmt_be_client_xpath_map + mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { + #ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths, + array_size(staticd_xpaths)}, + #endif + }; + + +MGMTD Internals +^^^^^^^^^^^^^^^ + +This section will describe the internal functioning of ``mgmtd``, for now a +couple diagrams are included to aide in source code perusal. + + +The client side of a CLI change + +.. figure:: ../figures/cli-change-client.svg + :align: center + + +The server (mgmtd) side of a CLI change + +.. figure:: ../figures/cli-change-mgmtd.svg + :align: center diff --git a/doc/developer/modules.rst b/doc/developer/modules.rst index e95f8a1b4a72..0feac8e73874 100644 --- a/doc/developer/modules.rst +++ b/doc/developer/modules.rst @@ -56,9 +56,9 @@ Basic boilerplate: #include "hook.h" #include "module.h" #include "libfrr.h" - #include "thread.h" + #include "frrevent.h" - static int module_late_init(struct thread_master *master) + static int module_late_init(struct event_loop *master) { /* Do initialization stuff here */ return 0; diff --git a/doc/developer/path-internals-pcep.rst b/doc/developer/path-internals-pcep.rst index ca318314f1f3..a6b22204c2df 100644 --- a/doc/developer/path-internals-pcep.rst +++ b/doc/developer/path-internals-pcep.rst @@ -182,7 +182,7 @@ The controller is defined and implemented in `path_pcep_controller.[hc]`. Part of the controller code runs in FRR main thread and part runs in its own FRR pthread started to isolate the main thread from the PCCs' event loop. To communicate between the threads it uses FRR events, timers and -`thread_execute` calls. +`event_execute` calls. PCC diff --git a/doc/developer/process-architecture.rst b/doc/developer/process-architecture.rst index 37bd620f2463..06ee6a3c37ca 100644 --- a/doc/developer/process-architecture.rst +++ b/doc/developer/process-architecture.rst @@ -28,7 +28,7 @@ within the event system are variations on the term "thread". The primary datastructure that holds the state of an event loop in this system is called a "threadmaster". Events scheduled on the event loop - what would today be called an 'event' or 'task' in systems such as libevent - are called "threads" and the -datastructure for them is ``struct thread``. To add to the confusion, these +datastructure for them is ``struct event``. To add to the confusion, these "threads" have various types, one of which is "event". To hopefully avoid some of this confusion, this document refers to these "threads" as a 'task' except where the datastructures are explicitly named. When they are explicitly named, @@ -46,8 +46,9 @@ implemented in FRR. This doc should be expanded and broken off into its own section. For now it provides basic information necessary to understand the interplay between the event system and kernel threads. -The core event system is implemented in :file:`lib/thread.[ch]`. The primary -structure is ``struct thread_master``, hereafter referred to as a +The core event system is implemented in :file:`lib/event.c` and +:file:`lib/frrevent.h`. The primary +structure is ``struct event_loop``, hereafter referred to as a ``threadmaster``. A ``threadmaster`` is a global state object, or context, that holds all the tasks currently pending execution as well as statistics on tasks that have already executed. The event system is driven by adding tasks to this @@ -57,47 +58,47 @@ execute. At initialization, a daemon will typically create one fetch each task and execute it. These tasks have various types corresponding to their general action. The types -are given by integer macros in :file:`thread.h` and are: +are given by integer macros in :file:`frrevent.h` and are: -``THREAD_READ`` +``EVENT_READ`` Task which waits for a file descriptor to become ready for reading and then executes. -``THREAD_WRITE`` +``EVENT_WRITE`` Task which waits for a file descriptor to become ready for writing and then executes. -``THREAD_TIMER`` +``EVENT_TIMER`` Task which executes after a certain amount of time has passed since it was scheduled. -``THREAD_EVENT`` +``EVENT_EVENT`` Generic task that executes with high priority and carries an arbitrary integer indicating the event type to its handler. These are commonly used to implement the finite state machines typically found in routing protocols. -``THREAD_READY`` +``EVENT_READY`` Type used internally for tasks on the ready queue. -``THREAD_UNUSED`` - Type used internally for ``struct thread`` objects that aren't being used. - The event system pools ``struct thread`` to avoid heap allocations; this is +``EVENT_UNUSED`` + Type used internally for ``struct event`` objects that aren't being used. + The event system pools ``struct event`` to avoid heap allocations; this is the type they have when they're in the pool. -``THREAD_EXECUTE`` +``EVENT_EXECUTE`` Just before a task is run its type is changed to this. This is used to show ``X`` as the type in the output of :clicmd:`show thread cpu`. The programmer never has to work with these types explicitly. Each type of task is created and queued via special-purpose functions (actually macros, but irrelevant for the time being) for the specific type. For example, to add a -``THREAD_READ`` task, you would call +``EVENT_READ`` task, you would call :: - thread_add_read(struct thread_master *master, int (*handler)(struct thread *), void *arg, int fd, struct thread **ref); + event_add_read(struct event_loop *master, int (*handler)(struct event *), void *arg, int fd, struct event **ref); -The ``struct thread`` is then created and added to the appropriate internal +The ``struct event`` is then created and added to the appropriate internal datastructure within the ``threadmaster``. Note that the ``READ`` and ``WRITE`` tasks are independent - a ``READ`` task only tests for readability, for example. @@ -111,13 +112,13 @@ program. When no more tasks are available, the program dies. Typically at startup the first task added is an I/O task for VTYSH as well as any network sockets needed for peerings or IPC. -To retrieve the next task to run the program calls ``thread_fetch()``. -``thread_fetch()`` internally computes which task to execute next based on -rudimentary priority logic. Events (type ``THREAD_EVENT``) execute with the +To retrieve the next task to run the program calls ``event_fetch()``. +``event_fetch()`` internally computes which task to execute next based on +rudimentary priority logic. Events (type ``EVENT_EVENT``) execute with the highest priority, followed by expired timers and finally I/O tasks (type -``THREAD_READ`` and ``THREAD_WRITE``). When scheduling a task a function and an -arbitrary argument are provided. The task returned from ``thread_fetch()`` is -then executed with ``thread_call()``. +``EVENT_READ`` and ``EVENT_WRITE``). When scheduling a task a function and an +arbitrary argument are provided. The task returned from ``event_fetch()`` is +then executed with ``event_call()``. The following diagram illustrates a simplified version of this infrastructure. @@ -133,25 +134,25 @@ illustrated at the bottom. Mapping the general names used in the figure to specific FRR functions: -- ``task`` is ``struct thread *`` -- ``fetch`` is ``thread_fetch()`` -- ``exec()`` is ``thread_call`` -- ``cancel()`` is ``thread_cancel()`` -- ``schedule()`` is any of the various task-specific ``thread_add_*`` functions +- ``task`` is ``struct event *`` +- ``fetch`` is ``event_fetch()`` +- ``exec()`` is ``event_call()`` +- ``cancel()`` is ``event_cancel()`` +- ``schedule()`` is any of the various task-specific ``event_add_*`` functions Adding tasks is done with various task-specific function-like macros. These -macros wrap underlying functions in :file:`thread.c` to provide additional +macros wrap underlying functions in :file:`event.c` to provide additional information added at compile time, such as the line number the task was scheduled from, that can be accessed at runtime for debugging, logging and informational purposes. Each task type has its own specific scheduling function -that follow the naming convention ``thread_add_<type>``; see :file:`thread.h` +that follow the naming convention ``event_add_<type>``; see :file:`frrevent.h` for details. There are some gotchas to keep in mind: - I/O tasks are keyed off the file descriptor associated with the I/O operation. This means that for any given file descriptor, only one of each - type of I/O task (``THREAD_READ`` and ``THREAD_WRITE``) can be scheduled. For + type of I/O task (``EVENT_READ`` and ``EVENT_WRITE``) can be scheduled. For example, scheduling two write tasks one after the other will overwrite the first task with the second, resulting in total loss of the first task and difficult bugs. @@ -209,7 +210,8 @@ Kernel Thread Wrapper The basis for the integration of pthreads and the event system is a lightweight wrapper for both systems implemented in :file:`lib/frr_pthread.[ch]`. The header provides a core datastructure, ``struct frr_pthread``, that encapsulates -structures from both POSIX threads and :file:`thread.[ch]`. In particular, this +structures from both POSIX threads and :file:`event.c`, :file:`frrevent.h`. +In particular, this datastructure has a pointer to a ``threadmaster`` that runs within the pthread. It also has fields for a name as well as start and stop functions that have signatures similar to the POSIX arguments for ``pthread_create()``. @@ -217,18 +219,18 @@ signatures similar to the POSIX arguments for ``pthread_create()``. Calling ``frr_pthread_new()`` creates and registers a new ``frr_pthread``. The returned structure has a pre-initialized ``threadmaster``, and its ``start`` and ``stop`` functions are initialized to defaults that will run a basic event -loop with the given threadmaster. Calling ``frr_pthread_run`` starts the thread +loop with the given threadmaster. Calling ``frr_pthread_run()`` starts the thread with the ``start`` function. From there, the model is the same as the regular event model. To schedule tasks on a particular pthread, simply use the regular -:file:`thread.c` functions as usual and provide the ``threadmaster`` pointed to +:file:`event.c` functions as usual and provide the ``threadmaster`` pointed to from the ``frr_pthread``. As part of implementing the wrapper, the -:file:`thread.c` functions were made thread-safe. Consequently, it is safe to +:file:`event.c` functions were made thread-safe. Consequently, it is safe to schedule events on a ``threadmaster`` belonging both to the calling thread as well as *any other pthread*. This serves as the basis for inter-thread communication and boils down to a slightly more complicated method of message passing, where the messages are the regular task events as used in the event-driven model. The only difference is thread cancellation, which requires -calling ``thread_cancel_async()`` instead of ``thread_cancel`` to cancel a task +calling ``event_cancel_async()`` instead of ``event_cancel()`` to cancel a task currently scheduled on a ``threadmaster`` belonging to a different pthread. This is necessary to avoid race conditions in the specific case where one pthread wants to guarantee that a task on another pthread is cancelled before diff --git a/doc/developer/rcu.rst b/doc/developer/rcu.rst index ac4405121ea8..4fd56587ae85 100644 --- a/doc/developer/rcu.rst +++ b/doc/developer/rcu.rst @@ -120,7 +120,7 @@ atomic ops & datastructures with other types of locking, e.g. rwlocks. 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 + contract is that any ``struct event *`` 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. diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index b4c752a473e5..840afa9f74fc 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -6,12 +6,12 @@ dev_RSTFILES = \ doc/developer/bgp-typecodes.rst \ doc/developer/bgpd.rst \ doc/developer/building-frr-for-alpine.rst \ + doc/developer/building-frr-for-archlinux.rst \ doc/developer/building-frr-for-centos6.rst \ doc/developer/building-frr-for-centos7.rst \ doc/developer/building-frr-for-debian8.rst \ doc/developer/building-frr-for-debian9.rst \ doc/developer/building-frr-for-fedora.rst \ - doc/developer/building-frr-for-opensuse.rst \ doc/developer/building-frr-for-freebsd10.rst \ doc/developer/building-frr-for-freebsd11.rst \ doc/developer/building-frr-for-freebsd13.rst \ @@ -19,14 +19,17 @@ dev_RSTFILES = \ doc/developer/building-frr-for-netbsd6.rst \ doc/developer/building-frr-for-netbsd7.rst \ doc/developer/building-frr-for-openbsd6.rst \ + doc/developer/building-frr-for-opensuse.rst \ doc/developer/building-frr-for-openwrt.rst \ doc/developer/building-frr-for-ubuntu1404.rst \ doc/developer/building-frr-for-ubuntu1604.rst \ doc/developer/building-frr-for-ubuntu1804.rst \ doc/developer/building-frr-for-ubuntu2004.rst \ + doc/developer/building-frr-for-ubuntu2204.rst \ doc/developer/building-libunwind-note.rst \ doc/developer/building-libyang.rst \ doc/developer/building.rst \ + doc/developer/checkpatch.rst \ doc/developer/cli.rst \ doc/developer/conf.py \ doc/developer/cross-compiling.rst \ diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst index ada182d8479f..87edad40bb58 100644 --- a/doc/developer/topotests.rst +++ b/doc/developer/topotests.rst @@ -17,15 +17,23 @@ Tested with Ubuntu 20.04,Ubuntu 18.04, and Debian 11. Instructions are the same for all setups (i.e. ExaBGP is only used for BGP tests). +Tshark is only required if you enable any packet captures on test runs. + +Valgrind is only required if you enable valgrind on test runs. + Installing Topotest Requirements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code:: shell - apt-get install gdb - apt-get install iproute2 - apt-get install net-tools - apt-get install python3-pip + apt-get install \ + gdb \ + iproute2 \ + net-tools \ + python3-pip \ + iputils-ping \ + tshark \ + valgrind python3 -m pip install wheel python3 -m pip install 'pytest>=6.2.4' python3 -m pip install 'pytest-xdist>=2.3.0' @@ -38,12 +46,6 @@ Installing Topotest Requirements # To enable the gRPC topotest install: python3 -m pip install grpcio grpcio-tools - # Install Socat tool to run PIMv6 tests, - # Socat code can be taken from below url, - # which has latest changes done for PIMv6, - # join and traffic: - https://github.com/opensourcerouting/socat/ - Enable Coredumps """""""""""""""" @@ -79,9 +81,9 @@ following steps will get you there on Ubuntu 20.04. apt install snmpd snmp apt install snmp-mibs-downloader download-mibs - wget http://www.iana.org/assignments/ianaippmmetricsregistry-mib/ianaippmmetricsregistry-mib -O /usr/share/snmp/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB - wget http://pastebin.com/raw.php?i=p3QyuXzZ -O /usr/share/snmp/mibs/ietf/SNMPv2-PDU - wget http://pastebin.com/raw.php?i=gG7j8nyk -O /usr/share/snmp/mibs/ietf/IPATM-IPMC-MIB + wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/iana/IANA-IPPM-METRICS-REGISTRY-MIB -O /usr/share/snmp/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB + wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/SNMPv2-PDU -O /usr/share/snmp/mibs/ietf/SNMPv2-PDU + wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/IPATM-IPMC-MIB -O /usr/share/snmp/mibs/ietf/IPATM-IPMC-MIB edit /etc/snmp/snmp.conf to look like this # As the snmp packages come without MIB files due to license reasons, loading # of MIBs is disabled by default. If you added the MIBs you can reenable @@ -119,6 +121,7 @@ If you prefer to manually build FRR, then use the following suggested config: --sysconfdir=/etc/frr \ --enable-vtysh \ --enable-pimd \ + --enable-pim6d \ --enable-sharpd \ --enable-multipath=64 \ --enable-user=frr \ @@ -187,13 +190,15 @@ Analyze Test Results (``analyze.py``) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ By default router and execution logs are saved in ``/tmp/topotests`` and an XML -results file is saved in ``/tmp/topotests.xml``. An analysis tool ``analyze.py`` -is provided to archive and analyze these results after the run completes. +results file is saved in ``/tmp/topotests/topotests.xml``. An analysis tool +``analyze.py`` is provided to archive and analyze these results after the run +completes. After the test run completes one should pick an archive directory to store the results in and pass this value to ``analyze.py``. On first execution the results -are copied to that directory from ``/tmp``, and subsequent runs use that -directory for analyzing the results. Below is an example of this which also +are moved to that directory from ``/tmp/topotests``. Subsequent runs of +``analyze.py`` with the same args will use that directories contents for instead +of copying any new results from ``/tmp``. Below is an example of this which also shows the default behavior which is to display all failed and errored tests in the run. @@ -205,7 +210,7 @@ the run. bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py::test_BGP_GR_10_p2 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_routingTable -Here we see that 4 tests have failed. We an dig deeper by displaying the +Here we see that 4 tests have failed. We can dig deeper by displaying the captured logs and errors. First let's redisplay the results enumerated by adding the ``-E`` flag @@ -240,9 +245,11 @@ the number of the test we are interested in along with ``--errmsg`` option. assert False -Now to look at the full text of the error for a failed test we use ``-T N`` -where N is the number of the test we are interested in along with ``--errtext`` -option. +Now to look at the error text for a failed test we can use ``-T RANGES`` where +``RANGES`` can be a number (e.g., ``5``), a range (e.g., ``0-10``), or a comma +separated list numbers and ranges (e.g., ``5,10-20,30``) of the test cases we +are interested in along with ``--errtext`` option. In the example below we'll +select the first failed test case. .. code:: shell @@ -268,8 +275,8 @@ option. [...] To look at the full capture for a test including the stdout and stderr which -includes full debug logs, just use the ``-T N`` option without the ``--errmsg`` -or ``--errtext`` options. +includes full debug logs, use ``--full`` option, or specify a ``-T RANGES`` without +specifying ``--errmsg`` or ``--errtext``. .. code:: shell @@ -289,6 +296,46 @@ or ``--errtext`` options. --------------------------------- Captured Out --------------------------------- system-err: --------------------------------- Captured Err --------------------------------- +Filtered results +"""""""""""""""" + +There are 4 types of test results, [e]rrored, [f]ailed, [p]assed, and +[s]kipped. One can select the set of results to show with the ``-S`` or +``--select`` flags along with the letters for each type (i.e., ``-S efps`` +would select all results). By default ``analyze.py`` will use ``-S ef`` (i.e., +[e]rrors and [f]ailures) unless the ``--search`` filter is given in which case +the default is to search all results (i.e., ``-S efps``). + +One can find all results which contain a ``REGEXP``. To filter results using a +regular expression use the ``--search REGEXP`` option. In this case, by default, +all result types will be searched for a match against the given ``REGEXP``. If a +test result output contains a match it is selected into the set of results to show. + +An example of using ``--search`` would be to search all tests results for some +log message, perhaps a warning or error. + +Using XML Results File from CI +"""""""""""""""""""""""""""""" + +``analyze.py`` actually only needs the ``topotests.xml`` file to run. This is +very useful for analyzing a CI run failure where one only need download the +``topotests.xml`` artifact from the run and then pass that to ``analyze.py`` +with the ``-r`` or ``--results`` option. + +For local runs if you wish to simply copy the ``topotests.xml`` file (leaving +the log files where they are), you can pass the ``-a`` (or ``--save-xml``) +instead of the ``-A`` (or ``-save``) options. + +Analyze Results from a Container Run +"""""""""""""""""""""""""""""""""""" + +``analyze.py`` can also be used with ``docker`` or ``podman`` containers. +Everything works exactly as with a host run except that you specify the name of +the container, or the container-id, using the `-C` or ``--container`` option. +``analyze.py`` will then use the results inside that containers +``/tmp/topotests`` directory. It will extract and save those results when you +pass the ``-A`` or ``-a`` options just as withe host results. + Execute single test ^^^^^^^^^^^^^^^^^^^ @@ -296,14 +343,14 @@ Execute single test .. code:: shell cd test_to_be_run - ./test_to_be_run.py + sudo -E pytest ./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 + sudo -E pytest ./test_bgp_l3vpn_to_bgp_vrf.py For further options, refer to pytest documentation. @@ -311,32 +358,6 @@ Test will set exit code which can be used with ``git bisect``. For the simulated topology, see the description in the python file. -StdErr log from daemos after exit -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To enable the reporting of any messages seen on StdErr after the daemons exit, -the following env variable can be set:: - - export TOPOTESTS_CHECK_STDERR=Yes - -(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 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. - Running Topotests with AddressSanitizer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -402,6 +423,63 @@ environment. .. _screen: https://www.gnu.org/software/screen/ .. _tmux: https://github.com/tmux/tmux/wiki +Capturing Packets +""""""""""""""""" + +One can view and capture packets on any of the networks or interfaces defined by +the topotest by specifying the ``--pcap=NET|INTF|all[,NET|INTF,...]`` CLI option +as shown in the examples below. + +.. code:: shell + + # Capture on all networks in isis_topo1 test + sudo -E pytest isis_topo1 --pcap=all + + # Capture on `sw1` network + sudo -E pytest isis_topo1 --pcap=sw1 + + # Capture on `sw1` network and on interface `eth0` on router `r2` + sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0 + +For each capture a window is opened displaying a live summary of the captured +packets. Additionally, the entire packet stream is captured in a pcap file in +the tests log directory e.g.,: + +.. code:: console + + $ sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0 + ... + $ ls -l /tmp/topotests/isis_topo1.test_isis_topo1/ + -rw------- 1 root root 45172 Apr 19 05:30 capture-r2-r2-eth0.pcap + -rw------- 1 root root 48412 Apr 19 05:30 capture-sw1.pcap + ... + +Viewing Live Daemon Logs +"""""""""""""""""""""""" + +One can live view daemon or the frr logs in separate windows using the +``--logd`` CLI option as shown below. + +.. code:: shell + + # View `ripd` logs on all routers in test + sudo -E pytest rip_allow_ecmp --logd=ripd + + # View `ripd` logs on all routers and `mgmtd` log on `r1` + sudo -E pytest rip_allow_ecmp --logd=ripd --logd=mgmtd,r1 + +For each capture a window is opened displaying a live summary of the captured +packets. Additionally, the entire packet stream is captured in a pcap file in +the tests log directory e.g.,:: + +When using a unified log file `frr.log` one substitutes `frr` for the daemon +name in the ``--logd`` CLI option, e.g., + +.. code:: shell + + # View `frr` log on all routers in test + sudo -E pytest some_test_suite --logd=frr + Spawning Debugging CLI, ``vtysh`` or Shells on Routers on Test Failure """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" @@ -421,12 +499,30 @@ the help command from within a CLI launched on error: test_bgp_multiview_topo1/test_bgp_routingTable> help - Commands: - help :: this help - sh [hosts] <shell-command> :: execute <shell-command> on <host> - term [hosts] :: open shell terminals for hosts - vtysh [hosts] :: open vtysh terminals for hosts - [hosts] <vtysh-command> :: execute vtysh-command on hosts + Basic Commands: + cli :: open a secondary CLI window + help :: this help + hosts :: list hosts + quit :: quit the cli + + HOST can be a host or one of the following: + - '*' for all hosts + - '.' for the parent munet + - a regex specified between '/' (e.g., '/rtr.*/') + + New Window Commands: + logd HOST [HOST ...] DAEMON :: tail -f on the logfile of the given DAEMON for the given HOST[S] + pcap NETWORK :: capture packets from NETWORK into file capture-NETWORK.pcap the command is run within a new window which also shows packet summaries. NETWORK can also be an interface specified as HOST:INTF. To capture inside the host namespace. + stderr HOST [HOST ...] DAEMON :: tail -f on the stderr of the given DAEMON for the given HOST[S] + stdlog HOST [HOST ...] :: tail -f on the `frr.log` for the given HOST[S] + stdout HOST [HOST ...] DAEMON :: tail -f on the stdout of the given DAEMON for the given HOST[S] + term HOST [HOST ...] :: open terminal[s] (TMUX or XTerm) on HOST[S], * for all + vtysh ROUTER [ROUTER ...] :: + xterm HOST [HOST ...] :: open XTerm[s] on HOST[S], * for all + Inline Commands: + [ROUTER ...] COMMAND :: execute vtysh COMMAND on the router[s] + [HOST ...] sh <SHELL-COMMAND> :: execute <SHELL-COMMAND> on hosts + [HOST ...] shi <INTERACTIVE-COMMAND> :: execute <INTERACTIVE-COMMAND> on HOST[s] test_bgp_multiview_topo1/test_bgp_routingTable> r1 show int br ------ Host: r1 ------ @@ -487,6 +583,48 @@ Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router --gdb-breakpoints=nb_config_diff \ all-protocol-startup +Reporting Memleaks with FRR Memory Statistics +""""""""""""""""""""""""""""""""""""""""""""" + +FRR reports all allocated FRR memory objects on exit to standard error. +Topotest can be run to report such output as errors in order to check for +memleaks in FRR memory allocations. Specifying the CLI argument +``--memleaks`` will enable reporting FRR-based memory allocations at exit as errors. + +.. code:: shell + + sudo -E pytest --memleaks all-protocol-startup + + +StdErr log from daemos after exit +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When running with ``--memleaks``, to enable the reporting of other, +non-memory related, messages seen on StdErr after the daemons exit, +the following env variable can be set:: + + export TOPOTESTS_CHECK_STDERR=Yes + +(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 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When running with ``--memleaks``, FRR processes report unfreed memory +allocations upon exit. To enable also reporting of memory leaks to a specific +location, define an environment variable ``TOPOTESTS_CHECK_MEMLEAK`` with the +file prefix, i.e.: + + export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_" + +For tests that support the TOPOTESTS_CHECK_MEMLEAK environment variable, this +will enable output to the information to files with the given prefix (followed +by testname), e.g.,: +file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case +of a memory leak. + Detecting Memleaks with Valgrind """""""""""""""""""""""""""""""" @@ -501,6 +639,27 @@ memleak detection is enabled. sudo -E pytest --valgrind-memleaks all-protocol-startup +Collecting Performance Data using perf(1) +""""""""""""""""""""""""""""""""""""""""" + +Topotest can automatically launch any daemon under ``perf(1)`` to collect +performance data. The daemon is run in non-daemon mode with ``perf record -g``. +The ``perf.data`` file will be saved in the router specific directory under the +tests run directoy. + +Here's an example of collecting performance data from ``mgmtd`` on router ``r1`` +during the config_timing test. + +.. code:: console + + $ sudo -E pytest --perf=mgmtd,r1 config_timing + ... + $ find /tmp/topotests/ -name '*perf.data*' + /tmp/topotests/config_timing.test_config_timing/r1/perf.data + +To specify different arguments for ``perf record``, one can use the +``--perf-options`` this will replace the ``-g`` used by default. + .. _topotests_docker: Running Tests with Docker diff --git a/doc/developer/tracing.rst b/doc/developer/tracing.rst index 63b04585f18c..76f6004034e7 100644 --- a/doc/developer/tracing.rst +++ b/doc/developer/tracing.rst @@ -150,8 +150,8 @@ Example:: frr_libfrr:frr_pthread_stop (loglevel: TRACE_DEBUG_LINE (13)) (type: tracepoint) frr_libfrr:frr_pthread_run (loglevel: TRACE_DEBUG_LINE (13)) (type: tracepoint) frr_libfrr:thread_call (loglevel: TRACE_INFO (6)) (type: tracepoint) - frr_libfrr:thread_cancel_async (loglevel: TRACE_INFO (6)) (type: tracepoint) - frr_libfrr:thread_cancel (loglevel: TRACE_INFO (6)) (type: tracepoint) + frr_libfrr:event_cancel_async (loglevel: TRACE_INFO (6)) (type: tracepoint) + frr_libfrr:event_cancel (loglevel: TRACE_INFO (6)) (type: tracepoint) frr_libfrr:schedule_write (loglevel: TRACE_INFO (6)) (type: tracepoint) frr_libfrr:schedule_read (loglevel: TRACE_INFO (6)) (type: tracepoint) frr_libfrr:schedule_event (loglevel: TRACE_INFO (6)) (type: tracepoint) diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 320824ec1db5..6efe5f42f6a9 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -95,7 +95,7 @@ March/July/November. Walking backwards from this date: are considered lowest priority (regardless of when they were opened.) - 4 weeks earlier, the stable branch separates from master (named - ``dev/MAJOR.MINOR`` at this point) and tagged as ```base_X.Y``. + ``dev/MAJOR.MINOR`` at this point) and tagged as ``base_X.Y``. Master is unfrozen and new features may again proceed. Part of unfreezing master is editing the ``AC_INIT`` statement in @@ -166,15 +166,15 @@ as early as possible, i.e. the first 2-week window. For reference, the expected release schedule according to the above is: -+---------+------------+------------+------------+------------+------------+ -| Release | 2023-03-07 | 2023-07-04 | 2023-10-31 | 2024-02-27 | 2024-06-25 | -+---------+------------+------------+------------+------------+------------+ -| RC | 2023-02-21 | 2023-06-20 | 2023-10-17 | 2024-02-13 | 2024-06-11 | -+---------+------------+------------+------------+------------+------------+ -| dev/X.Y | 2023-02-07 | 2023-06-06 | 2023-10-03 | 2024-01-30 | 2024-05-28 | -+---------+------------+------------+------------+------------+------------+ -| freeze | 2023-01-24 | 2023-05-23 | 2023-09-19 | 2024-01-16 | 2024-05-14 | -+---------+------------+------------+------------+------------+------------+ ++---------+------------+------------+------------+ +| Release | 2023-07-04 | 2023-10-31 | 2024-02-27 | ++---------+------------+------------+------------+ +| RC | 2023-06-20 | 2023-10-17 | 2024-02-13 | ++---------+------------+------------+------------+ +| dev/X.Y | 2023-06-06 | 2023-10-03 | 2024-01-30 | ++---------+------------+------------+------------+ +| freeze | 2023-05-23 | 2023-09-19 | 2024-01-16 | ++---------+------------+------------+------------+ Here is the hint on how to get the dates easily: @@ -365,7 +365,6 @@ There is a built-in commit linter. Basic rules: `Check <https://github.com/FRRouting/frr/tree/master/.github/commitlint.config.js>`_ all the supported subsystems. -- Commit messages must start with a capital letter - Commit messages must not end with a period ``.`` Why was my pull request closed? @@ -538,7 +537,8 @@ Programming Languages, Tools and Libraries ========================================== The core of FRR is written in C (gcc or clang supported) and makes -use of GNU compiler extensions. A few non-essential scripts are +use of GNU compiler extensions. Additionally, the CLI generation +tool, `clippy`, requires Python. A few other non-essential scripts are implemented in Perl and Python. FRR requires the following tools to build distribution packages: automake, autoconf, texinfo, libtool and gawk and various libraries (i.e. libpam and libjson-c). @@ -868,14 +868,17 @@ clang-format https://clang.llvm.org/docs/ClangFormat.html checkpatch.sh +checkpatch.pl + In the Linux kernel source tree there is a Perl script used to check - incoming patches for style errors. FRR uses an adapted version of this - script for the same purpose. It can be found at - :file:`tools/checkpatch.sh`. This script takes a git-formatted diff or - patch file, applies it to a clean FRR tree, and inspects the result to catch - potential style errors. Running this script on your patches before - submission is highly recommended. The CI system runs this script as well and - will comment on the PR with the results if style errors are found. + incoming patches for style errors. FRR uses a shell script front end and an + adapted version of the perl script for the same purpose. These scripts can + be found at :file:`tools/checkpatch.sh` and :file:`tools/checkpatch.pl`. + This script takes a git-formatted diff or patch file, applies it to a clean + FRR tree, and inspects the result to catch potential style errors. Running + this script on your patches before submission is highly recommended. The CI + system runs this script as well and will comment on the PR with the results + if style errors are found. It is run like this:: @@ -916,6 +919,10 @@ checkpatch.sh If the script finds one or more WARNINGs it will exit with 1. If it finds one or more ERRORs it will exit with 2. + For convenience the Linux documentation for the :file:`tools/checkpatch.pl` + script has been included unmodified (i.e., it has not been updated to + reflect local changes) :doc:`here <checkpatch>` + Please remember that while FRR provides these tools for your convenience, responsibility for properly formatting your code ultimately lies on the @@ -1331,10 +1338,23 @@ frr-format plugin Using the plugin also changes the string for ``PRI[udx]64`` from the system value to ``%L[udx]`` (normally ``%ll[udx]`` or ``%l[udx]``.) -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. +Additionally, the FRR codebase is regularly scanned for static analysis +errors with Coverity and pull request changes are scanned as part of the +Continuous Integration (CI) process. Developers can scan their commits for +Coverity static analysis errors prior to submission using the +``scan-build`` command. To use this command, the ``clang-tools`` package must +be installed. For example, this can be accomplished on Ubuntu with the +``sudo apt-get install clang-tools`` command. Then, touch the files you want scanned and +invoke the ``scan-build`` command. For example:: + + cd ~/GitHub/frr + touch ospfd/ospf_flood.c ospfd/ospf_vty.c ospfd/ospf_opaque.c + cd build + scan-build make -j32 + +The results of the scan including any static analysis errors will appear inline. +Additionally, there will a directory in the /tmp containing the Coverity +reports (e.g., scan-build-2023-06-09-120100-473730-1). Executing non-installed dynamic binaries ---------------------------------------- diff --git a/doc/developer/zebra.rst b/doc/developer/zebra.rst index cef53f1cbeec..5f039758a5ee 100644 --- a/doc/developer/zebra.rst +++ b/doc/developer/zebra.rst @@ -218,8 +218,6 @@ Zebra Protocol Commands +------------------------------------+-------+ | ZEBRA_IMPORT_ROUTE_UNREGISTER | 27 | +------------------------------------+-------+ -| ZEBRA_IMPORT_CHECK_UPDATE | 28 | -+------------------------------------+-------+ | ZEBRA_BFD_DEST_REGISTER | 29 | +------------------------------------+-------+ | ZEBRA_BFD_DEST_DEREGISTER | 30 | diff --git a/doc/figures/cli-change-client.drawio b/doc/figures/cli-change-client.drawio new file mode 100644 index 000000000000..c7a68d40e4ae --- /dev/null +++ b/doc/figures/cli-change-client.drawio @@ -0,0 +1,522 @@ +<mxfile host="Electron" modified="2023-06-19T07:55:43.434Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.2.8 Chrome/112.0.5615.165 Electron/24.2.0 Safari/537.36" etag="hHcr6k13KyEFOw_PaIFY" version="21.2.8" type="device"> + <diagram name="Page-1" id="58cdce13-f638-feb5-8d6f-7d28b1aa9fa0"> + <mxGraphModel dx="2074" dy="1264" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="1"> + <root> + <mxCell id="0" /> + <mxCell id="1" parent="0" /> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-239" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;jumpStyle=gap;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-28" target="nUYlmBzm2YxJIW5L2hvB-238" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="265.02000000000004" y="307.47999999999996" /> + <mxPoint x="265.02000000000004" y="307.47999999999996" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-240" value="copy of vty-&gt;cfg_changes<br>to protobuf msg" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-239" vertex="1" connectable="0"> + <mxGeometry x="-0.1005" relative="1" as="geometry"> + <mxPoint x="56" y="-15" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-80" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;jumpStyle=gap;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-77" target="nUYlmBzm2YxJIW5L2hvB-78" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="120.01999999999998" y="672.48" /> + <mxPoint x="120.01999999999998" y="672.48" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-11" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;fontSize=12;startSize=8;endSize=8;strokeColor=#ff0000;labelBackgroundColor=none;endArrow=open;fontFamily=Verdana;align=left;entryX=0;entryY=0.5;entryDx=0;entryDy=0;jumpStyle=gap;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-1" target="nUYlmBzm2YxJIW5L2hvB-7" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="260" y="505" /> + <mxPoint x="260" y="505" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-34" value="N" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-11" vertex="1" connectable="0"> + <mxGeometry x="-0.3317" y="1" relative="1" as="geometry"> + <mxPoint x="60" y="-14" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-15" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-1" target="nUYlmBzm2YxJIW5L2hvB-13" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="170.0200000000002" y="492.47999999999996" as="sourcePoint" /> + <mxPoint x="200.0200000000002" y="567.48" as="targetPoint" /> + <Array as="points"> + <mxPoint x="190.01999999999998" y="522.48" /> + <mxPoint x="190.01999999999998" y="567.48" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-35" value="N+1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-15" vertex="1" connectable="0"> + <mxGeometry x="-0.5391" relative="1" as="geometry"> + <mxPoint x="20" y="2" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-1" value="user cmd:<br>&nbsp;"ip route 10.0.0.0/24 null0"<br>-------------------------------<br><br>" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999999" y="467.47999999999996" width="120" height="75" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-3" style="edgeStyle=orthogonalEdgeStyle;html=1;labelBackgroundColor=none;endArrow=open;endSize=8;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;align=left;entryX=0;entryY=0.5;entryDx=0;entryDy=0;jumpStyle=gap;exitX=1;exitY=0.25;exitDx=0;exitDy=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-1" target="nUYlmBzm2YxJIW5L2hvB-5" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="260.02000000000004" y="476.97999999999996" as="sourcePoint" /> + <mxPoint x="300.02000000000004" y="367.47999999999996" as="targetPoint" /> + <Array as="points"> + <mxPoint x="280.02000000000004" y="486.47999999999996" /> + <mxPoint x="280.02000000000004" y="397.47999999999996" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-33" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-3" vertex="1" connectable="0"> + <mxGeometry x="-0.3723" y="-1" relative="1" as="geometry"> + <mxPoint x="36" y="-76" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-5" value="nb_cli_enqueue_change" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=10;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="345.02000000000004" y="377.47999999999996" width="130" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-7" value="nb_cli_enqueue_change" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=10;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="345.02000000000004" y="484.97999999999996" width="130" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-29" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;jumpStyle=gap;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-13" target="nUYlmBzm2YxJIW5L2hvB-28" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="210" y="345" /> + <mxPoint x="210" y="345" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-31" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-13" target="nUYlmBzm2YxJIW5L2hvB-27" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-32" value="<font style="font-size: 7px;">file or !mgmtd</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=7;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-31" vertex="1" connectable="0"> + <mxGeometry x="-0.3307" y="1" relative="1" as="geometry"> + <mxPoint x="11" y="-9" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-13" value="nb_cli_apply_changes" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="210.01999999999998" y="547.48" width="100" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-14" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=4;rounded=1;labelBackgroundColor=none;strokeColor=#000000;fontFamily=Verdana;fontSize=12;fontColor=default;startSize=8;endSize=8;shape=connector;" parent="1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="409.62000000000006" y="467.47999999999996" as="sourcePoint" /> + <mxPoint x="409.62000000000006" y="427.47999999999996" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-17" value="" style="triangle;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;rotation=-90;" parent="1" vertex="1"> + <mxGeometry x="575" y="355" width="20" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-18" value="" style="triangle;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;rotation=-90;" parent="1" vertex="1"> + <mxGeometry x="575" y="385" width="20" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-19" value="" style="triangle;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;rotation=-90;" parent="1" vertex="1"> + <mxGeometry x="575" y="475" width="20" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-20" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=4;rounded=1;labelBackgroundColor=none;strokeColor=#000000;fontFamily=Verdana;fontSize=12;fontColor=default;startSize=8;endSize=8;shape=connector;" parent="1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="584.63" y="465" as="sourcePoint" /> + <mxPoint x="584.63" y="425" as="targetPoint" /> + <Array as="points"> + <mxPoint x="584.63" y="445" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-21" value="<font style="font-size: 10px;">candidate<br>ds</font>" style="shape=datastore;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="530" y="577.48" width="60" height="60" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-23" value="<font style="font-size: 10px;">candidate<br>ds</font>" style="shape=datastore;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="644.98" y="577.48" width="60" height="60" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-24" value="" style="shape=singleArrow;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="600" y="592.48" width="29.98" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-61" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-27" target="nUYlmBzm2YxJIW5L2hvB-59" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-27" value="nb_cli_apply_changes_internal" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="380.02000000000004" y="547.48" width="130" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-38" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#C73500;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;fillColor=#fa6800;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-238" target="nUYlmBzm2YxJIW5L2hvB-37" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="825.02" y="272.47999999999996" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-39" value="<font style="font-size: 10px;"><i>socket connection</i><br>FE client -&gt; adapter SETCFG_REQ<br><br></font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-38" vertex="1" connectable="0"> + <mxGeometry x="-0.0889" y="2" relative="1" as="geometry"> + <mxPoint x="-27" y="22" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-255" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-28" target="nUYlmBzm2YxJIW5L2hvB-246" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="230" y="200" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-256" value="implicit_commit<br style="font-size: 10px;">(legacy CLI)" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;horizontal=0;" parent="nUYlmBzm2YxJIW5L2hvB-255" vertex="1" connectable="0"> + <mxGeometry x="-0.5348" y="-1" relative="1" as="geometry"> + <mxPoint x="-11" y="9" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-28" value="vty_mgmt_send_config_data" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="200.01999999999998" y="327.47999999999996" width="130" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-238" value="mgmt_fe_send_setcfg_req" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="420.02000000000004" y="252.48000000000002" width="130" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-30" value="<font style="font-size: 7px;">mgmtd</font>" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=16;fontFamily=Verdana;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="235.01999999999998" y="517.48" width="50" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-36" value="" style="endArrow=none;dashed=1;html=1;dashPattern=1 3;strokeWidth=4;rounded=1;labelBackgroundColor=none;strokeColor=#000000;fontFamily=Verdana;fontSize=12;fontColor=default;startSize=8;endSize=8;shape=connector;" parent="1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="294.62" y="472.43" as="sourcePoint" /> + <mxPoint x="294.62" y="432.43" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-41" style="edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=doubleBlock;endFill=1;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-37" target="nUYlmBzm2YxJIW5L2hvB-40" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-42" value="<font style="font-size: 10px;">validates input and creates TXN (CONFIG)<br><i>can happen multiple times</i><br></font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-41" vertex="1" connectable="0"> + <mxGeometry x="0.197" y="1" relative="1" as="geometry"> + <mxPoint x="114" y="-4" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-37" value="mgmt_fe_session_handle_setcfg_req_msg" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="730.02" y="297.47999999999996" width="190" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-55" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;fillColor=#0050ef;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-40" target="nUYlmBzm2YxJIW5L2hvB-44" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-56" value="<font style="font-size: 10px;">copy protobuf -&gt; txn_req.set_cfg.cfg_changes<br style="border-color: var(--border-color); font-size: 10px;"></font><span style="font-size: 10px;"><font style="font-size: 10px;">TIMER: MGMTD_TXN_PROC_SETCFG</font><br style="font-size: 10px;"></span>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-55" vertex="1" connectable="0"> + <mxGeometry x="0.2852" y="-1" relative="1" as="geometry"> + <mxPoint x="126" y="-31" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-40" value="mgmt_txn_send_set_config_req" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="752.52" y="377.47999999999996" width="145" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-60" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-44" target="nUYlmBzm2YxJIW5L2hvB-59" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="675.02" y="547.48" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-128" value="1" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-60" vertex="1" connectable="0"> + <mxGeometry x="-0.3733" y="3" relative="1" as="geometry"> + <mxPoint x="21" y="-13" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-69" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.436;entryY=0.026;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-44" target="nUYlmBzm2YxJIW5L2hvB-68" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-70" value="implicit_commit" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-69" vertex="1" connectable="0"> + <mxGeometry x="-0.1764" y="-3" relative="1" as="geometry"> + <mxPoint x="48" y="-3" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-129" value="2" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-69" vertex="1" connectable="0"> + <mxGeometry x="-0.2682" y="-1" relative="1" as="geometry"> + <mxPoint x="-4" y="-11" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-72" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=baseDash;startSize=8;endSize=8;endFill=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-44" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="980.02" y="517.48" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-130" value="2" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-72" vertex="1" connectable="0"> + <mxGeometry x="-0.1117" y="-3" relative="1" as="geometry"> + <mxPoint x="-29" y="-13" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-44" value="<div>mgmt_txn_process_set_cfg</div>" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="752.52" y="497.47999999999996" width="145" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-49" value="" style="shape=singleArrow;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="480.02" y="375" width="70" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-50" value="" style="shape=singleArrow;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;rotation=-180;" parent="1" vertex="1"> + <mxGeometry x="615" y="375" width="70" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-59" value="<div>nb_candidate_edit</div>" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="542.5" y="524.98" width="105" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-64" value="struct<br>nb_cfg_change" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontSize=10;fontFamily=Verdana;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="535" y="315" width="100" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-68" value="<div>mgmt_txn_send_commit_config_req</div>" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffe6cc;strokeColor=#d79b00;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;" parent="1" vertex="1"> + <mxGeometry x="752.52" y="592.48" width="167.5" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-74" value="user cmd:<br>&nbsp;"ip route 10.0.1.0/24 null0"" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999999" y="550" width="100" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-75" value="user cmd:<br>&nbsp;"ip route 10.0.2.0/24 null0"" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="70.01999999999998" y="560" width="100" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-76" value="user cmd:<br>&nbsp;"ip route 10.0.3.0/24 null0"" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="80.01999999999998" y="570" width="100" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-77" value="user cmd:<br>"XFRR_end_configuration"<br>&nbsp;config or EOF" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;dashed=1;dashPattern=1 4;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999999" y="626.98" width="120" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-90" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-78" target="nUYlmBzm2YxJIW5L2hvB-84" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-78" value="vty_mgmt_send_commit_config" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="215.01999999999998" y="631.48" width="140" height="31" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-88" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#C73500;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;fillColor=#fa6800;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-84" target="nUYlmBzm2YxJIW5L2hvB-87" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="540" y="715" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-89" value="<i style="font-size: 10px;">socket connection<br style="font-size: 10px;"></i>FE client -&gt; adapter COMMCFG_REQ" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-88" vertex="1" connectable="0"> + <mxGeometry x="-0.0463" y="1" relative="1" as="geometry"> + <mxPoint x="-34" y="30" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-84" value="mgmt_fe_send_commitcfg_req" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="215.01999999999998" y="730" width="140" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-93" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;entryX=0.166;entryY=0.994;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-87" target="nUYlmBzm2YxJIW5L2hvB-68" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-87" value="mgmt_fe_session_handle_commit_config_req_msg<br>create txn if none yet<br>if running DS not locked, lock" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="490.00000000000006" y="700" width="220" height="90" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-95" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-68" target="nUYlmBzm2YxJIW5L2hvB-159" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="835.02" y="632.48" as="sourcePoint" /> + <mxPoint x="883.7977777777774" y="718.48" as="targetPoint" /> + <Array as="points" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-96" value="<span style="font-size: 10px;">curr_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG<br style="font-size: 10px;"></span>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-95" vertex="1" connectable="0"> + <mxGeometry x="0.2852" y="-1" relative="1" as="geometry"> + <mxPoint x="91" y="-21" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-131" value="<span style="font-size: 10px;">next_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG<br style="font-size: 10px;"></span>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0"> + <mxGeometry x="989.9977193457571" y="669.9979556509891" as="geometry"> + <mxPoint x="-46" y="1" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-159" value="TIMER:<br style="font-size: 7px;">MGMTD_TXN_PROC_COMCFG" style="ellipse;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=7;fillColor=#b1ddf0;strokeColor=#10739e;" parent="1" vertex="1"> + <mxGeometry x="800.02" y="717.26" width="120" height="80" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-241" value="<i style="border-color: var(--border-color);">does nothing more</i>:<span style="font-size: 9px;"><br>when</span><b style="font-size: 9px;"> not implicit_commit:</b><br style="font-size: 9px;">&nbsp;<font face="Courier New"><b>mgmt (set|delete)-config</b></font> CLI<br style="font-size: 9px;">(no_implicit_commit == true)<br style="font-size: 9px;">inside <font face="Courier New"><b>XFRR_{start,end}_config</b></font><br style="font-size: 9px;">(pending_allowed == true)" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=9;fillColor=#eeeeee;strokeColor=#36393d;" parent="1" vertex="1"> + <mxGeometry x="940.02" y="472.42999999999995" width="140" height="100.05" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-243" value="user cmd:<br>"XFRR_start_configuration"<br>&nbsp;config file read indicator" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;dashed=1;dashPattern=1 4;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999999" y="417.47999999999996" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-257" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-245" target="nUYlmBzm2YxJIW5L2hvB-246" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="150" y="243" /> + <mxPoint x="200" y="243" /> + <mxPoint x="200" y="180" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-258" value="NO implicit commit<br style="font-size: 10px;">(vtysh -f file)" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;horizontal=0;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-257" vertex="1" connectable="0"> + <mxGeometry x="-0.8771" y="-1" relative="1" as="geometry"> + <mxPoint x="9" y="-41" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-245" value="user cmd:<br>"configure terminal"" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=default;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;strokeWidth=1;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999999" y="367.47999999999996" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-248" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#C73500;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;strokeWidth=2;fillColor=#fa6800;startArrow=open;startFill=0;shadow=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-246" target="nUYlmBzm2YxJIW5L2hvB-247" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="625" y="190" as="targetPoint" /> + <Array as="points"> + <mxPoint x="585" y="193" /> + <mxPoint x="585" y="193" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-250" value="<i>socket connection<br style="font-size: 9px;"></i>FE client -&gt; adapter LOCKDS_REQ" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-248" vertex="1" connectable="0"> + <mxGeometry x="-0.0567" y="1" relative="1" as="geometry"> + <mxPoint x="5" y="-16" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-246" value="vty_mgmt_lock_cand_inline" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="380.02" y="172.48" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-247" value="LOCK CANDIDATE" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffe6cc;strokeColor=#d79b00;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;" parent="1" vertex="1"> + <mxGeometry x="680" y="175.00000000000003" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-252" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-251" target="nUYlmBzm2YxJIW5L2hvB-245" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-253" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;exitX=0.248;exitY=0.923;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-251" target="nUYlmBzm2YxJIW5L2hvB-243" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="90.01999999999998" y="322.47999999999996" /> + <mxPoint x="50.019999999999996" y="322.47999999999996" /> + <mxPoint x="50.019999999999996" y="443.47999999999996" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-254" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;exitX=0.088;exitY=0.793;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-251" target="nUYlmBzm2YxJIW5L2hvB-1" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="60" y="510" as="targetPoint" /> + <Array as="points"> + <mxPoint x="71" y="253" /> + <mxPoint x="40" y="253" /> + <mxPoint x="40" y="513" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-251" value="EVENT: VTYSH_READ" style="ellipse;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=7;fillColor=#b1ddf0;strokeColor=#10739e;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999999" y="142.48000000000002" width="120" height="80" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-260" value="UNLOCK CANDIDATE" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffe6cc;strokeColor=#d79b00;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;" parent="1" vertex="1"> + <mxGeometry x="680" y="120.00000000000001" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-265" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-261" target="nUYlmBzm2YxJIW5L2hvB-262" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="10" y="707" /> + <mxPoint x="10" y="130" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-266" value="NO implicit commit" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;horizontal=0;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-265" vertex="1" connectable="0"> + <mxGeometry x="-0.781" y="-1" relative="1" as="geometry"> + <mxPoint x="9" y="-12" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-261" value="user cmd:<br>"end/exit"" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=default;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;strokeWidth=1;" parent="1" vertex="1"> + <mxGeometry x="60.01999999999998" y="690" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-262" value="vty_mgmt_lock_cand_inline" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="380.02" y="120" width="120" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-275" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-270" target="nUYlmBzm2YxJIW5L2hvB-268" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="300" y="35" /> + <mxPoint x="300" y="35" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-270" value="vty_mgmt_set_config_result_notified" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="360" y="11.25" width="180" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-263" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#C73500;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;strokeWidth=2;fillColor=#fa6800;startArrow=open;startFill=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-262" target="nUYlmBzm2YxJIW5L2hvB-260" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="500.02" y="140" as="sourcePoint" /> + <mxPoint x="680.02" y="140" as="targetPoint" /> + <Array as="points" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-264" value="<i>socket connection<br style="font-size: 9px;"></i>FE client -&gt; adapter LOCKDS_REQ" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-263" vertex="1" connectable="0"> + <mxGeometry x="-0.0567" y="1" relative="1" as="geometry"> + <mxPoint x="5" y="-16" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-272" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-267" target="nUYlmBzm2YxJIW5L2hvB-271" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="630" y="76" /> + <mxPoint x="630" y="76" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-273" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-267" target="nUYlmBzm2YxJIW5L2hvB-270" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="630" y="40" /> + <mxPoint x="630" y="40" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-267" value="EVENT: REPLY NOTIFICATIONS" style="ellipse;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=7;fillColor=#b1ddf0;strokeColor=#10739e;" parent="1" vertex="1"> + <mxGeometry x="660" y="5" width="120" height="80" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-269" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;jumpStyle=gap;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-268" target="nUYlmBzm2YxJIW5L2hvB-251" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="80" y="80" /> + <mxPoint x="80" y="80" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-268" value="<div>VTYSH</div>" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#cdeb8b;strokeColor=#36393d;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;" parent="1" vertex="1"> + <mxGeometry x="30" y="15" width="77.48" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-274" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-271" target="nUYlmBzm2YxJIW5L2hvB-268" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="330" y="76" /> + <mxPoint x="330" y="50" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-271" value="vty_mgmt_commit_config_result_notified" style="rounded=1;whiteSpace=wrap;html=1;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=#000000;align=center;" parent="1" vertex="1"> + <mxGeometry x="360" y="58.75" width="180" height="35" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-292" value="" style="group" parent="1" vertex="1" connectable="0"> + <mxGeometry x="950" y="710" width="140" height="130" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-284" value="" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=Verdana;fontSize=12;fontColor=default;" parent="nUYlmBzm2YxJIW5L2hvB-292" vertex="1"> + <mxGeometry width="140" height="130" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-278" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#C73500;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;fillColor=#fa6800;" parent="nUYlmBzm2YxJIW5L2hvB-292" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="20" y="60" as="sourcePoint" /> + <mxPoint x="110.01999999999998" y="60" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-279" value="<i style="font-size: 10px;">socket&nbsp;</i>async" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-278" vertex="1" connectable="0"> + <mxGeometry x="-0.0463" y="1" relative="1" as="geometry"> + <mxPoint x="-8" y="-9" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-282" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;fillColor=#0050ef;" parent="nUYlmBzm2YxJIW5L2hvB-292" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="20" y="90" as="sourcePoint" /> + <mxPoint x="110" y="90" as="targetPoint" /> + <Array as="points"> + <mxPoint x="50" y="89.77000000000001" /> + <mxPoint x="50" y="89.77000000000001" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-283" value="<span style="font-size: 10px;"><font style="font-size: 10px;">timer/event&nbsp;</font>async<br style="font-size: 10px;"></span>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=10;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-282" vertex="1" connectable="0"> + <mxGeometry x="0.2852" y="-1" relative="1" as="geometry"> + <mxPoint x="-28" y="-11" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-285" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;labelBackgroundColor=none;strokeColor=#C73500;fontFamily=Verdana;fontSize=12;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;strokeWidth=2;fillColor=#fa6800;startArrow=open;startFill=0;" parent="nUYlmBzm2YxJIW5L2hvB-292" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="20" y="35" as="sourcePoint" /> + <mxPoint x="110" y="35" as="targetPoint" /> + <Array as="points"> + <mxPoint x="20" y="35" /> + <mxPoint x="20" y="35" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-286" value="<i>socket&nbsp; sync (short-circuit)<br></i>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-285" vertex="1" connectable="0"> + <mxGeometry x="-0.0567" y="1" relative="1" as="geometry"> + <mxPoint x="5" y="-16" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-287" value="" style="endArrow=open;html=1;rounded=1;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=12;fontColor=default;startSize=8;endSize=8;shape=connector;" parent="nUYlmBzm2YxJIW5L2hvB-292" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="20" y="120" as="sourcePoint" /> + <mxPoint x="105" y="119.19999999999999" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-288" value="function sync" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-287" vertex="1" connectable="0"> + <mxGeometry x="-0.26" y="2" relative="1" as="geometry"> + <mxPoint x="6" y="-8" as="offset" /> + </mxGeometry> + </mxCell> + </root> + </mxGraphModel> + </diagram> +</mxfile> diff --git a/doc/figures/cli-change-client.svg b/doc/figures/cli-change-client.svg new file mode 100644 index 000000000000..9194f24e67a7 --- /dev/null +++ b/doc/figures/cli-change-client.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1099" height="842" viewBox="-0.5 -0.5 1099 842" style="background-color: rgb(255, 255, 255);"><defs><filter id="dropShadow"><feGaussianBlur in="SourceAlpha" stdDeviation="1.7" result="blur"/><feOffset in="blur" dx="3" dy="3" result="offsetBlur"/><feFlood flood-color="#3D4574" flood-opacity="0.4" result="offsetColor"/><feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur"/><feBlend in="SourceGraphic" in2="offsetBlur"/></filter></defs><g filter="url(#dropShadow)"><path d="M 264 322.48 L 264 277.5 Q 264 267.5 274 267.5 L 416.78 267.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 408.9 272 L 417.9 267.5 L 408.9 263" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 253px; margin-left: 360px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">copy of vty->cfg_changes<br />to protobuf msg</div></div></div></foreignObject><text x="360" y="256" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">copy of vty->cfg_changes...</text></switch></g><path d="M 119 661.98 L 119 664.74 Q 119 667.5 119 657.5 L 119 649.75 Q 119 642 129 642 L 211.78 642" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 203.9 646.5 L 212.9 642 L 203.9 637.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 179.02 499.98 L 341.78 499.98" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 333.9 504.48 L 342.9 499.98 L 333.9 495.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 485px; margin-left: 295px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">N</div></div></div></foreignObject><text x="295" y="489" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">N</text></switch></g><path d="M 179.02 517.5 L 184.01 517.5 Q 189 517.5 189 527.5 L 189 552.5 Q 189 562.5 197.89 562.49 L 206.78 562.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 198.91 566.99 L 207.9 562.48 L 198.9 557.99" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 527px; margin-left: 210px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">N+1</div></div></div></foreignObject><text x="210" y="531" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">N+1</text></switch></g><rect x="59.02" y="462.48" width="120" height="75" rx="18" ry="18" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 500px; margin-left: 60px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br /> "ip route 10.0.0.0/24 null0"<br />-------------------------------<br /><br /></div></div></div></foreignObject><text x="119" y="502" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><path d="M 179.02 481.23 L 269 481.2 Q 279 481.2 279 471.2 L 279 402.5 Q 279 392.5 289 392.5 L 341.78 392.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 333.9 396.98 L 342.9 392.48 L 333.9 387.98" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 407px; margin-left: 296px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">1</div></div></div></foreignObject><text x="296" y="410" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">1</text></switch></g><rect x="344.02" y="372.48" width="130" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 392px; margin-left: 345px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">nb_cli_enqueue_change</div></div></div></foreignObject><text x="409" y="395" fill="#000000" font-family="Verdana" font-size="10px" text-anchor="middle">nb_cli_enqueue_change</text></switch></g><rect x="344.02" y="479.98" width="130" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 500px; margin-left: 345px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">nb_cli_enqueue_change</div></div></div></foreignObject><text x="409" y="503" fill="#000000" font-family="Verdana" font-size="10px" text-anchor="middle">nb_cli_enqueue_change</text></switch></g><path d="M 259.02 542.48 L 259.02 502.98 M 259.02 496.98 M 259.02 496.98 L 259.02 484.21 M 259.02 478.21 M 259.02 478.21 L 259.02 364.72" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 263.52 372.6 L 259.02 363.6 L 254.52 372.6" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 309.02 562.48 L 376.78 562.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 368.9 566.98 L 377.9 562.48 L 368.9 557.98" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 553px; margin-left: 344px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 7px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><font style="font-size: 7px;">file or !mgmtd</font></div></div></div></foreignObject><text x="344" y="555" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="7px" text-anchor="middle">file or !mgmtd</text></switch></g><rect x="209.02" y="542.48" width="100" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 562px; margin-left: 210px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">nb_cli_apply_changes</div></div></div></foreignObject><text x="259" y="565" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">nb_cli_apply_changes</text></switch></g><path d="M 408.62 462.48 L 408.62 422.48" fill="none" stroke="#000000" stroke-width="4" stroke-miterlimit="10" stroke-dasharray="4 12" pointer-events="stroke"/><path d="M 574 350 L 594 365 L 574 380 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(-90,584,365)" pointer-events="all"/><path d="M 574 380 L 594 395 L 574 410 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(-90,584,395)" pointer-events="all"/><path d="M 574 470 L 594 485 L 574 500 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(-90,584,485)" pointer-events="all"/><path d="M 583.63 460 L 583.63 450 Q 583.63 440 583.63 430 L 583.63 420" fill="none" stroke="#000000" stroke-width="4" stroke-miterlimit="10" stroke-dasharray="4 12" pointer-events="stroke"/><path d="M 529 580.48 C 529 569.81 589 569.81 589 580.48 L 589 624.48 C 589 635.15 529 635.15 529 624.48 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 529 580.48 C 529 588.48 589 588.48 589 580.48 M 529 584.48 C 529 592.48 589 592.48 589 584.48 M 529 588.48 C 529 596.48 589 596.48 589 588.48" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 612px; margin-left: 530px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><font style="font-size: 10px;">candidate<br />ds</font></div></div></div></foreignObject><text x="559" y="616" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">candidate...</text></switch></g><path d="M 643.98 580.48 C 643.98 569.81 703.98 569.81 703.98 580.48 L 703.98 624.48 C 703.98 635.15 643.98 635.15 643.98 624.48 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 643.98 580.48 C 643.98 588.48 703.98 588.48 703.98 580.48 M 643.98 584.48 C 643.98 592.48 703.98 592.48 703.98 584.48 M 643.98 588.48 C 643.98 596.48 703.98 596.48 703.98 588.48" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 612px; margin-left: 645px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><font style="font-size: 10px;">candidate<br />ds</font></div></div></div></foreignObject><text x="674" y="616" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">candidate...</text></switch></g><path d="M 599 597.98 L 622.98 597.98 L 622.98 587.48 L 628.98 602.48 L 622.98 617.48 L 622.98 606.98 L 599 606.98 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 509.02 562.5 L 519.01 562.5 Q 529 562.5 529 552.5 L 529 546.25 Q 529 540 534.13 539.99 L 539.26 539.98" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 531.39 544.5 L 540.38 539.98 L 531.37 535.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="379.02" y="542.48" width="130" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 562px; margin-left: 380px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">nb_cli_apply_changes_internal</div></div></div></foreignObject><text x="444" y="565" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">nb_cli_apply_changes_internal</text></switch></g><path d="M 549.02 267.5 L 814 267.5 Q 824 267.5 824.01 277.5 L 824.02 288.01" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 819.01 280.25 L 824.02 290.24 L 829.01 280.24" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 288px; margin-left: 660px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><font style="font-size: 10px;"><i>socket connection</i><br />FE client -> adapter SETCFG_REQ<br /><br /></font></div></div></div></foreignObject><text x="660" y="291" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">socket connection...</text></switch></g><path d="M 229 322.48 L 229 205 Q 229 195 239 195 L 376.78 195" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 368.9 199.5 L 377.9 195 L 368.9 190.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 219.5 265.98)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 266px; margin-left: 220px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">implicit_commit<br style="font-size: 10px;" />(legacy CLI)</div></div></div></foreignObject><text x="220" y="269" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">implicit_commit...</text></switch></g><rect x="199.02" y="322.48" width="130" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 342px; margin-left: 200px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">vty_mgmt_send_config_data</div></div></div></foreignObject><text x="264" y="345" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">vty_mgmt_send_config_data</text></switch></g><rect x="419.02" y="247.48" width="130" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 128px; height: 1px; padding-top: 267px; margin-left: 420px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_fe_send_setcfg_req</div></div></div></foreignObject><text x="484" y="270" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_fe_send_setcfg_req</text></switch></g><rect x="234.02" y="512.48" width="50" height="30" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 527px; margin-left: 259px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 16px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><font style="font-size: 7px;">mgmtd</font></div></div></div></foreignObject><text x="259" y="532" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="16px" text-anchor="middle">mgmtd</text></switch></g><path d="M 293.62 467.43 L 293.62 427.43" fill="none" stroke="#000000" stroke-width="4" stroke-miterlimit="10" stroke-dasharray="4 12" pointer-events="stroke"/><path d="M 824.02 332.48 L 824.02 357.36" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 824.02 371.36 L 820.52 364.36 L 827.52 364.36 Z M 824.02 364.36 L 820.52 357.36 L 827.52 357.36 Z" fill="#ff0000" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 353px; margin-left: 940px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><font style="font-size: 10px;">validates input and creates TXN (CONFIG)<br /><i>can happen multiple times</i><br /></font></div></div></div></foreignObject><text x="940" y="356" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">validates input and creates TXN (CONFIG)...</text></switch></g><rect x="729.02" y="292.48" width="190" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 188px; height: 1px; padding-top: 312px; margin-left: 730px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_fe_session_handle_setcfg_req_msg</div></div></div></foreignObject><text x="824" y="315" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_fe_session_handle_setcfg_req_msg</text></switch></g><path d="M 824.02 412.48 L 824.02 488.01" fill="none" stroke="#001dbc" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 819.02 480.24 L 824.02 490.24 L 829.02 480.24" fill="none" stroke="#001dbc" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 433px; margin-left: 950px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><font style="font-size: 10px;">copy protobuf -> txn_req.set_cfg.cfg_changes<br style="border-color: var(--border-color); font-size: 10px;" /></font><span style="font-size: 10px;"><font style="font-size: 10px;">TIMER: MGMTD_TXN_PROC_SETCFG</font><br style="font-size: 10px;" /></span></div></div></div></foreignObject><text x="950" y="436" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">copy protobuf -> txn_req.set_cfg.cfg_changes...</text></switch></g><rect x="751.52" y="372.48" width="145" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 392px; margin-left: 753px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_set_config_req</div></div></div></foreignObject><text x="824" y="395" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_set_config_req</text></switch></g><path d="M 751.52 512.5 L 709 512.5 Q 699 512.5 699 522.5 L 699 531.25 Q 699 540 689 540 L 648.74 540" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 656.62 535.5 L 647.62 540 L 656.62 544.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 503px; margin-left: 731px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">1</div></div></div></foreignObject><text x="731" y="507" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">1</text></switch></g><path d="M 824 532.48 L 824 550 Q 824 560 824.19 570 L 824.51 586.28" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 819.86 578.49 L 824.53 587.4 L 828.85 578.32" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 553px; margin-left: 870px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">implicit_commit</div></div></div></foreignObject><text x="870" y="556" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">implicit_commit</text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 543px; margin-left: 820px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">2</div></div></div></foreignObject><text x="820" y="547" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">2</text></switch></g><path d="M 896.52 512.5 L 927.8 512.5 Q 937.8 512.5 947.8 512.5 L 979.02 512.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 979.02 517.48 L 979.02 507.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 503px; margin-left: 905px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">2</div></div></div></foreignObject><text x="905" y="507" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="12px" text-anchor="middle">2</text></switch></g><rect x="751.52" y="492.48" width="145" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 512px; margin-left: 753px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>mgmt_txn_process_set_cfg</div></div></div></div></foreignObject><text x="824" y="515" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_process_set_cfg</text></switch></g><path d="M 479.02 384 L 535.02 384 L 535.02 370 L 549.02 390 L 535.02 410 L 535.02 396 L 479.02 396 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 614 384 L 670 384 L 670 370 L 684 390 L 670 410 L 670 396 L 614 396 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(-180,649,390)" pointer-events="all"/><rect x="541.5" y="519.98" width="105" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 103px; height: 1px; padding-top: 540px; margin-left: 543px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>nb_candidate_edit</div></div></div></div></foreignObject><text x="594" y="542" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">nb_candidate_edit</text></switch></g><rect x="534" y="310" width="100" height="40" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 330px; margin-left: 584px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">struct<br />nb_cfg_change</div></div></div></foreignObject><text x="584" y="333" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">struct...</text></switch></g><rect x="751.52" y="587.48" width="167.5" height="40" rx="9.6" ry="9.6" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 607px; margin-left: 753px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>mgmt_txn_send_commit_config_req</div></div></div></div></foreignObject><text x="835" y="610" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_commit_config_req</text></switch></g><rect x="59.02" y="545" width="100" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 565px; margin-left: 60px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br /> "ip route 10.0.1.0/24 null0"</div></div></div></foreignObject><text x="109" y="567" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><rect x="69.02" y="555" width="100" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 575px; margin-left: 70px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br /> "ip route 10.0.2.0/24 null0"</div></div></div></foreignObject><text x="119" y="577" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><rect x="79.02" y="565" width="100" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 98px; height: 1px; padding-top: 585px; margin-left: 80px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br /> "ip route 10.0.3.0/24 null0"</div></div></div></foreignObject><text x="129" y="587" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><rect x="59.02" y="621.98" width="120" height="40" rx="9.6" ry="9.6" fill="#ffffc0" stroke="#ff0000" stroke-dasharray="1 4" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 642px; margin-left: 60px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br />"XFRR_end_configuration"<br /> config or EOF</div></div></div></foreignObject><text x="119" y="644" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><path d="M 284.02 657.48 L 284.02 722.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 279.52 714.88 L 284.02 723.88 L 288.52 714.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="214.02" y="626.48" width="140" height="31" rx="7.44" ry="7.44" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 642px; margin-left: 215px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">vty_mgmt_send_commit_config</div></div></div></foreignObject><text x="284" y="644" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">vty_mgmt_send_commit_config</text></switch></g><path d="M 354.02 740 L 484.53 740" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 476.76 745 L 486.76 740 L 476.76 735" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 770px; margin-left: 385px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><i style="font-size: 10px;">socket connection<br style="font-size: 10px;" /></i>FE client -> adapter COMMCFG_REQ</div></div></div></foreignObject><text x="385" y="773" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">socket connection...</text></switch></g><rect x="214.02" y="725" width="140" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 740px; margin-left: 215px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_fe_send_commitcfg_req</div></div></div></foreignObject><text x="284" y="742" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_fe_send_commitcfg_req</text></switch></g><path d="M 709 740 L 769.3 740 Q 779.3 740 779.3 730 L 779.32 629.48" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 783.82 637.36 L 779.32 628.36 L 774.82 637.36" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="489" y="695" width="220" height="90" rx="21.6" ry="21.6" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 218px; height: 1px; padding-top: 740px; margin-left: 490px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_fe_session_handle_commit_config_req_msg<br />create txn if none yet<br />if running DS not locked, lock</div></div></div></foreignObject><text x="599" y="742" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_fe_session_handle_commit_config_req_msg...</text></switch></g><path d="M 835.3 627.48 L 835.3 659.9 Q 835.3 669.9 845.3 669.9 L 852.15 669.9 Q 859 669.9 859 679.9 L 859.02 710.02" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 854.52 702.14 L 859.02 711.14 L 863.52 702.14" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 653px; margin-left: 950px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><span style="font-size: 10px;">curr_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG<br style="font-size: 10px;" /></span></div></div></div></foreignObject><text x="950" y="656" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">curr_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG </text></switch></g><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 666px; margin-left: 943px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><span style="font-size: 10px;">next_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG<br style="font-size: 10px;" /></span></div></div></div></foreignObject><text x="943" y="669" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">next_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG </text></switch></g><ellipse cx="859.02" cy="752.26" rx="60" ry="40" fill="#b1ddf0" stroke="#10739e" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 752px; margin-left: 800px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 7px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">TIMER:<br style="font-size: 7px;" />MGMTD_TXN_PROC_COMCFG</div></div></div></foreignObject><text x="859" y="754" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="7px" text-anchor="middle">TIMER:...</text></switch></g><rect x="939.02" y="467.43" width="140" height="100.05" fill="#eeeeee" stroke="#36393d" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 138px; height: 1px; padding-top: 517px; margin-left: 940px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 9px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><i style="border-color: var(--border-color);">does nothing more</i>:<span style="font-size: 9px;"><br />when</span><b style="font-size: 9px;"> not implicit_commit:</b><br style="font-size: 9px;" /> <font face="Courier New"><b>mgmt (set|delete)-config</b></font> CLI<br style="font-size: 9px;" />(no_implicit_commit == true)<br style="font-size: 9px;" />inside <font face="Courier New"><b>XFRR_{start,end}_config</b></font><br style="font-size: 9px;" />(pending_allowed == true)</div></div></div></foreignObject><text x="1009" y="520" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="9px" text-anchor="middle">does nothing more:...</text></switch></g><rect x="59.02" y="412.48" width="120" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="#ff0000" stroke-dasharray="1 4" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 430px; margin-left: 60px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br />"XFRR_start_configuration"<br /> config file read indicator</div></div></div></foreignObject><text x="119" y="432" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><path d="M 149 362.48 L 149 248 Q 149 238 159 238 L 189 238 Q 199 238 199 228 L 199 185 Q 199 175 209 175 L 376.78 175" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 368.9 179.5 L 377.9 175 L 368.9 170.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 159.5 294.98)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 295px; margin-left: 160px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">NO implicit commit<br style="font-size: 10px;" />(vtysh -f file)</div></div></div></foreignObject><text x="160" y="298" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">NO implicit commit...</text></switch></g><rect x="59.02" y="362.48" width="120" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 380px; margin-left: 60px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br />"configure terminal"</div></div></div></foreignObject><text x="119" y="382" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><path d="M 503.49 188 L 574 188 Q 584 188 594 188 L 674.53 188" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="stroke"/><path d="M 511.26 183 L 501.26 188 L 511.26 193" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><path d="M 666.76 193 L 676.76 188 L 666.76 183" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 172px; margin-left: 590px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 9px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><i>socket connection<br style="font-size: 9px;" /></i>FE client -> adapter LOCKDS_REQ</div></div></div></foreignObject><text x="590" y="174" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="9px" text-anchor="middle">socket connection...</text></switch></g><rect x="379.02" y="167.48" width="120" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 185px; margin-left: 380px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">vty_mgmt_lock_cand_inline</div></div></div></foreignObject><text x="439" y="187" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">vty_mgmt_lock_cand_inline</text></switch></g><rect x="679" y="170" width="120" height="35" rx="8.4" ry="8.4" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 188px; margin-left: 680px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">LOCK CANDIDATE</div></div></div></foreignObject><text x="739" y="190" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">LOCK CANDIDATE</text></switch></g><path d="M 119.02 217.48 L 119.02 360.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 114.52 352.36 L 119.02 361.36 L 123.52 352.36" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 88.78 211.32 L 88.8 307.5 Q 88.8 317.5 78.8 317.5 L 59 317.5 Q 49 317.5 49 327.5 L 49 428.7 Q 49 438.7 52.89 438.71 L 56.78 438.72" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 48.89 443.2 L 57.9 438.73 L 48.92 434.2" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 69.58 200.92 L 69.6 238 Q 69.6 248 59.6 248 L 49 248 Q 39 248 39 258 L 39 498 Q 39 508 47.89 508 L 56.78 508" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 48.9 512.5 L 57.9 508 L 48.9 503.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><ellipse cx="119.02" cy="177.48" rx="60" ry="40" fill="#b1ddf0" stroke="#10739e" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 177px; margin-left: 60px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 7px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">EVENT: VTYSH_READ</div></div></div></foreignObject><text x="119" y="180" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="7px" text-anchor="middle">EVENT: VTYSH_READ</text></switch></g><rect x="679" y="115" width="120" height="35" rx="8.4" ry="8.4" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 133px; margin-left: 680px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">UNLOCK CANDIDATE</div></div></div></foreignObject><text x="739" y="135" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">UNLOCK CANDIDATE</text></switch></g><path d="M 59.02 702.5 L 19 702.5 Q 9 702.5 9 692.5 L 9 135 Q 9 125 19 125 L 376.78 125" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 368.9 129.5 L 377.9 125 L 368.9 120.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 19.5 631.02)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 631px; margin-left: 20px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">NO implicit commit</div></div></div></foreignObject><text x="20" y="634" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">NO implicit commit</text></switch></g><rect x="59.02" y="685" width="120" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="rgb(0, 0, 0)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 703px; margin-left: 60px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">user cmd:<br />"end/exit"</div></div></div></foreignObject><text x="119" y="705" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">user cmd:...</text></switch></g><rect x="379.02" y="115" width="120" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 133px; margin-left: 380px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">vty_mgmt_lock_cand_inline</div></div></div></foreignObject><text x="439" y="135" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">vty_mgmt_lock_cand_inline</text></switch></g><path d="M 359 30 L 309 30 Q 299 30 289 30 L 31.24 30" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 39.12 25.5 L 30.12 30 L 39.12 34.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="359" y="6.25" width="180" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 24px; margin-left: 360px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">vty_mgmt_set_config_result_notified</div></div></div></foreignObject><text x="449" y="26" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">vty_mgmt_set_config_result_notified</text></switch></g><path d="M 503.49 132.5 L 674.53 132.5" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="stroke"/><path d="M 511.26 127.5 L 501.26 132.5 L 511.26 137.5" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><path d="M 666.76 137.5 L 676.76 132.5 L 666.76 127.5" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 116px; margin-left: 590px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 9px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;"><i>socket connection<br style="font-size: 9px;" /></i>FE client -> adapter LOCKDS_REQ</div></div></div></foreignObject><text x="590" y="119" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="9px" text-anchor="middle">socket connection...</text></switch></g><path d="M 681.08 71 L 639 71 Q 629 71 619 71 L 541.24 71" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 549.12 66.5 L 540.12 71 L 549.12 75.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 659.47 35 L 639 35 Q 629 35 619 35 L 541.24 35" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 549.12 30.5 L 540.12 35 L 549.12 39.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><ellipse cx="719" cy="40" rx="60" ry="40" fill="#b1ddf0" stroke="#10739e" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 40px; margin-left: 660px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 7px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">EVENT: REPLY NOTIFICATIONS</div></div></div></foreignObject><text x="719" y="42" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="7px" text-anchor="middle">EVENT: REPLY NOTIFICATIONS</text></switch></g><path d="M 79 50 L 79 122 M 79 128 M 79 128 L 79 145.44" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 74.5 137.56 L 79 146.56 L 83.5 137.56" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="29" y="10" width="77.48" height="40" rx="9.6" ry="9.6" fill="#cdeb8b" stroke="#36393d" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 75px; height: 1px; padding-top: 30px; margin-left: 30px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><div>VTYSH</div></div></div></div></foreignObject><text x="68" y="32" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">VTYSH</text></switch></g><path d="M 359 71.3 L 339 71.3 Q 329 71.3 329 61.3 L 329 53.15 Q 329 45 319 45 L 108.72 45" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 116.6 40.5 L 107.6 45 L 116.6 49.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="359" y="53.75" width="180" height="35" rx="8.4" ry="8.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 71px; margin-left: 360px;"><div data-drawio-colors="color: #000000; " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">vty_mgmt_commit_config_result_notified</div></div></div></foreignObject><text x="449" y="74" fill="#000000" font-family="Verdana" font-size="8px" text-anchor="middle">vty_mgmt_commit_config_result_notified</text></switch></g><rect x="949" y="705" width="140" height="130" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="none"/><path d="M 969 765 L 1054.55 765" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="none"/><path d="M 1046.78 770 L 1056.78 765 L 1046.78 760" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 756px; margin-left: 1005px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"><i style="font-size: 10px;">socket </i>async</div></div></div></foreignObject><text x="1005" y="759" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">socket async</text></switch></g><path d="M 969 795 L 989 795 Q 999 795 1009 795 L 1054.53 795" fill="none" stroke="#001dbc" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="none"/><path d="M 1046.76 800 L 1056.76 795 L 1046.76 790" fill="none" stroke="#001dbc" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 786px; margin-left: 1000px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 10px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"><span style="font-size: 10px;"><font style="font-size: 10px;">timer/event </font>async<br style="font-size: 10px;" /></span></div></div></div></foreignObject><text x="1000" y="789" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="10px" text-anchor="middle">timer/event async </text></switch></g><path d="M 973.47 740 L 1054.53 740" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="none"/><path d="M 981.24 735 L 971.24 740 L 981.24 745" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><path d="M 1046.76 745 L 1056.76 740 L 1046.76 735" fill="none" stroke="#c73500" stroke-width="2" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 724px; margin-left: 1017px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 9px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;"><i>socket  sync (short-circuit)<br /></i></div></div></div></foreignObject><text x="1017" y="726" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="9px" text-anchor="middle">socket  sync (short-circuit) </text></switch></g><path d="M 969 825 L 1051.76 824.22" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><path d="M 1043.92 828.8 L 1052.88 824.21 L 1043.84 819.8" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="none"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 815px; margin-left: 1006px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 9px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: none; white-space: nowrap;">function sync</div></div></div></foreignObject><text x="1006" y="818" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="9px" text-anchor="middle">function sync</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/doc/figures/cli-change-mgmtd.drawio b/doc/figures/cli-change-mgmtd.drawio new file mode 100644 index 000000000000..e8beade742db --- /dev/null +++ b/doc/figures/cli-change-mgmtd.drawio @@ -0,0 +1,421 @@ +<mxfile host="Electron" modified="2023-06-19T08:43:10.542Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.4.0 Chrome/112.0.5615.204 Electron/24.5.1 Safari/537.36" etag="nT5OZWDjYXR5quOjpvZj" version="21.4.0" type="device"> + <diagram name="Page-1" id="58cdce13-f638-feb5-8d6f-7d28b1aa9fa0"> + <mxGraphModel dx="974" dy="1264" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="1"> + <root> + <mxCell id="0" /> + <mxCell id="1" parent="0" /> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-158" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;jumpStyle=gap;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-65" target="nUYlmBzm2YxJIW5L2hvB-157" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-150" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;startArrow=none;startFill=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-65" target="nUYlmBzm2YxJIW5L2hvB-148" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1350" y="320" /> + <mxPoint x="1350" y="320" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-65" value="mgmt_txn_prepare_cfg" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=default;align=center;" parent="1" vertex="1"> + <mxGeometry x="1280" y="279.78000000000003" width="145" height="20.44" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-160" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-217" target="nUYlmBzm2YxJIW5L2hvB-161" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="1920" y="380" as="targetPoint" /> + <Array as="points"> + <mxPoint x="1607" y="590" /> + <mxPoint x="1840" y="590" /> + <mxPoint x="1840" y="660" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-174" value="MESSAGE_TXN_REQ create" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-160" vertex="1" connectable="0"> + <mxGeometry x="-0.5683" relative="1" as="geometry"> + <mxPoint x="-17" y="-10" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-218" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-66" target="nUYlmBzm2YxJIW5L2hvB-217" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1490" y="590" /> + <mxPoint x="1490" y="590" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-66" value="mgmt_txn_send_be_txn_create" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=default;align=center;" parent="1" vertex="1"> + <mxGeometry x="1280" y="580" width="145" height="20" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-217" value="mgmt_be_send_txn_req" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=default;align=center;" parent="1" vertex="1"> + <mxGeometry x="1505" y="581" width="145" height="20" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-203" value="does nothing cfg_data replys will cause next transition " style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#eeeeee;strokeColor=#36393d;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1240" y="640" width="180" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-214" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-67" target="nUYlmBzm2YxJIW5L2hvB-213" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-216" value="next_phase =  PHASE_TXN_DELETE" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-214" vertex="1" connectable="0"> + <mxGeometry x="-0.2492" y="-1" relative="1" as="geometry"> + <mxPoint x="10" y="19" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-67" value="mgmt_txn_send_be_cfg_apply" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=default;align=center;" parent="1" vertex="1"> + <mxGeometry x="1200" y="709.9999999999999" width="145" height="20" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-213" value="mgmt_be_send_cfgapply_req" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=default;align=center;" parent="1" vertex="1"> + <mxGeometry x="1440" y="709.9999999999999" width="145" height="20" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-111" value="mgmt_txn_send_commit_cfg_reply" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;fontColor=default;align=center;" parent="1" vertex="1"> + <mxGeometry x="1161.25" y="769.9999999999999" width="145" height="20" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-140" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-94" target="nUYlmBzm2YxJIW5L2hvB-66" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1270" y="590" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-145" value="PHASE_TXN_CREATE" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-140" vertex="1" connectable="0"> + <mxGeometry x="0.2148" y="-2" relative="1" as="geometry"> + <mxPoint x="49" y="112" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-141" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-94" target="nUYlmBzm2YxJIW5L2hvB-65" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1300" y="260" /> + <mxPoint x="1353" y="260" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-144" value="PHASE_PREPARE_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-141" vertex="1" connectable="0"> + <mxGeometry x="-0.1955" y="3" relative="1" as="geometry"> + <mxPoint x="13" y="-7" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-142" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-94" target="nUYlmBzm2YxJIW5L2hvB-67" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1210" y="340" /> + <mxPoint x="1210" y="340" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-146" value="PHASE_CFG_APPLY" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-142" vertex="1" connectable="0"> + <mxGeometry x="0.6696" y="2" relative="1" as="geometry"> + <mxPoint x="48" y="68" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-143" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-94" target="nUYlmBzm2YxJIW5L2hvB-111" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1180" y="380" /> + <mxPoint x="1180" y="380" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-147" value="PHASE_TXN_DELETE" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-143" vertex="1" connectable="0"> + <mxGeometry x="0.7799" y="3" relative="1" as="geometry"> + <mxPoint x="51" y="48" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-204" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-94" target="nUYlmBzm2YxJIW5L2hvB-203" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1250" y="340" /> + <mxPoint x="1250" y="340" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-205" value="PHASE_SEND_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-204" vertex="1" connectable="0"> + <mxGeometry x="0.857" y="3" relative="1" as="geometry"> + <mxPoint x="37" y="18" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-94" value="mgmt_txn_process_commit_cfg" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1150" y="170" width="167.5" height="70" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-97" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-65" target="nUYlmBzm2YxJIW5L2hvB-138" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="1360" y="281" as="sourcePoint" /> + <mxPoint x="1318" y="225" as="targetPoint" /> + <Array as="points"> + <mxPoint x="1580" y="290" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-113" value="curr_phase = PHASE_TXN_CREATE" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-97" vertex="1" connectable="0"> + <mxGeometry x="0.2534" y="-1" relative="1" as="geometry"> + <mxPoint x="-101" y="35" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-119" value="mgmt_txn_send_commit_config_req" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffe6cc;strokeColor=#d79b00;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1150" y="50" width="167.5" height="40" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-122" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-119" target="nUYlmBzm2YxJIW5L2hvB-138" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="826" y="435" as="sourcePoint" /> + <mxPoint x="824" y="521" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-123" value="curr_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG " style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-122" vertex="1" connectable="0"> + <mxGeometry x="0.2852" y="-1" relative="1" as="geometry"> + <mxPoint x="-23" y="-21" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-132" value="next_phase == MGMTD_COMMIT_PHASE_PREPARE_CFG " style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0"> + <mxGeometry x="1425" y="89.99851851851847" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-139" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-138" target="nUYlmBzm2YxJIW5L2hvB-94" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1540" y="190" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-138" value="TIMER: MGMTD_TXN_PROC_COMCFG" style="ellipse;whiteSpace=wrap;fontFamily=Verdana;fontSize=8;fillColor=#b1ddf0;strokeColor=#10739e;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1520" y="30" width="120" height="80" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-156" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;startArrow=none;startFill=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-65" target="nUYlmBzm2YxJIW5L2hvB-154" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1353" y="310" /> + <mxPoint x="1513" y="310" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-148" value="GET nb_config_change's nb_config_diff(cand, run) or  txn->commit_cfg_req->req.commit_cfg.cfg_chgs " style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#e1d5e7;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;strokeColor=#9673a6;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1280" y="330" width="145" height="50" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-154" value="mgmt_txn_create_config_batches" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#e1d5e7;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;strokeColor=#9673a6;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1440" y="330" width="145" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-157" value="TIMER: MGMTD_TXN_ COMMITCFG_TIMEOUT" style="ellipse;whiteSpace=wrap;fontFamily=Verdana;fontSize=8;fillColor=#b1ddf0;strokeColor=#10739e;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1590" y="190" width="90" height="60" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-176" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=doubleBlock;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;endFill=1;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-161" target="nUYlmBzm2YxJIW5L2hvB-175" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1970" y="90" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-177" value="MESSAGE_CFG_DATA_REPLY" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;horizontal=0;" parent="nUYlmBzm2YxJIW5L2hvB-176" vertex="1" connectable="0"> + <mxGeometry x="0.177" relative="1" as="geometry"> + <mxPoint x="-10" y="255" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-224" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;endFill=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-161" target="nUYlmBzm2YxJIW5L2hvB-223" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="1890" y="460" as="targetPoint" /> + <Array as="points"> + <mxPoint x="1940" y="305" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-225" value="MESSAGE_CFG_APPLY_REPLY;" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;horizontal=0;" parent="nUYlmBzm2YxJIW5L2hvB-224" vertex="1" connectable="0"> + <mxGeometry x="-0.0859" y="3" relative="1" as="geometry"> + <mxPoint x="-7" y="64" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-161" value="Backend Client" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#cdeb8b;strokeColor=#36393d;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1935" y="610" width="205" height="120" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-169" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-163" target="nUYlmBzm2YxJIW5L2hvB-168" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-163" value="mgmt_txn_notify_be_txn_reply" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="2020" y="465" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-192" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=doubleBlock;startSize=8;endSize=8;endFill=1;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-168" target="nUYlmBzm2YxJIW5L2hvB-171" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-168" value="mgmt_txn_send_be_cfg_data" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="2020" y="415" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-188" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-171" target="nUYlmBzm2YxJIW5L2hvB-187" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-189" value="batch: PHASE_TXN_REQ -> PHASE_SEND_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-188" vertex="1" connectable="0"> + <mxGeometry x="-0.176" relative="1" as="geometry"> + <mxPoint x="-9" y="-55" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-171" value="mgmt_be_send_cfgdata_req" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="2025" y="310" width="157.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-200" value="txn: PHASE_TXN_REQ -> PHASE_SEND_CFG" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-187" target="nUYlmBzm2YxJIW5L2hvB-199" edge="1"> + <mxGeometry x="1" y="80" relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="2095" y="240" /> + <mxPoint x="2095" y="240" /> + </Array> + <mxPoint x="75" y="-80" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-187" value="mgmt_move_txn_cfg_batch_to_next" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="2025" y="255" width="157.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-201" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-199" target="nUYlmBzm2YxJIW5L2hvB-138" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="2095" y="30" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-202" value="curr_phase = PHASE_SEND_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-201" vertex="1" connectable="0"> + <mxGeometry x="0.4129" y="-3" relative="1" as="geometry"> + <mxPoint x="121" y="-7" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-199" value="mgmt_try_move_commit_to_next_phase if all backend clients have all been sent their batches move to next phase and post EVENT/TIMER" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="2002.5" y="160" width="185" height="60" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-172" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=doubleBlock;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;endFill=1;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-171" target="nUYlmBzm2YxJIW5L2hvB-161" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="1957.5" y="175" as="sourcePoint" /> + <Array as="points"> + <mxPoint x="2000" y="325" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-173" value="MESSAGE_CFG_DATA_REQ" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;horizontal=0;" parent="nUYlmBzm2YxJIW5L2hvB-172" vertex="1" connectable="0"> + <mxGeometry x="-0.1783" y="-1" relative="1" as="geometry"> + <mxPoint x="-9" y="-30" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-195" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-175" target="nUYlmBzm2YxJIW5L2hvB-179" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-197" value="batch: PHASE_SEND_CFG -> PHASE_APPLY_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-195" vertex="1" connectable="0"> + <mxGeometry x="0.463" y="-1" relative="1" as="geometry"> + <mxPoint x="1" y="-9" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-175" value="mgmt_txn_notify_be_cfgdata_reply" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1770" y="75" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-210" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-179" target="nUYlmBzm2YxJIW5L2hvB-180" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-179" value="mgmt_move_txn_cfg_batch_to_next" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1770" y="160" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-181" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-180" target="nUYlmBzm2YxJIW5L2hvB-138" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="1770" y="30" as="targetPoint" /> + <Array as="points"> + <mxPoint x="1720" y="235" /> + <mxPoint x="1720" y="50" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-182" value="curr_phase = PHASE_APPLY_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-181" vertex="1" connectable="0"> + <mxGeometry x="-0.4275" relative="1" as="geometry"> + <mxPoint x="50" y="72" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-180" value="mgmt_try_move_commit_to_next_phase" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1763.75" y="220" width="180" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-164" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-161" target="nUYlmBzm2YxJIW5L2hvB-163" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="2120" y="510" as="targetPoint" /> + <mxPoint x="2104" y="610" as="sourcePoint" /> + <Array as="points"> + <mxPoint x="2104" y="580" /> + <mxPoint x="2104" y="580" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-167" value="MESSAGE_TXN_REPLY" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;horizontal=0;" parent="nUYlmBzm2YxJIW5L2hvB-164" vertex="1" connectable="0"> + <mxGeometry x="-0.5021" y="1" relative="1" as="geometry"> + <mxPoint x="-13" y="-25" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-183" value="curr_phase = PHASE_TXN_CREATE" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0"> + <mxGeometry x="1470" y="569.9974074074072" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-184" value="next_phase = PHASE_SEND_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0"> + <mxGeometry x="1459.9985714285715" y="609.998574414664" as="geometry"> + <mxPoint x="5" y="-3" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-186" value="For each of the batches of cfgdata send msg and move" style="verticalLabelPosition=middle;verticalAlign=middle;strokeWidth=2;shape=mxgraph.lean_mapping.physical_pull;pointerEvents=1;fontFamily=Verdana;fontSize=8;fontColor=default;labelPosition=right;align=left;horizontal=1;" parent="1" vertex="1"> + <mxGeometry x="2025" y="370" width="30" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-191" value="For each of the  batches of cfgdata" style="verticalLabelPosition=middle;verticalAlign=middle;strokeWidth=2;shape=mxgraph.lean_mapping.physical_pull;pointerEvents=1;fontFamily=Verdana;fontSize=8;fontColor=default;labelPosition=right;align=left;horizontal=1;" parent="1" vertex="1"> + <mxGeometry x="1935" y="55" width="30" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-211" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-213" target="nUYlmBzm2YxJIW5L2hvB-161" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="1945" y="670" as="targetPoint" /> + <mxPoint x="1435" y="600" as="sourcePoint" /> + <Array as="points"> + <mxPoint x="1850" y="720" /> + <mxPoint x="1850" y="670" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-212" value="MESSAGE_CFG_APPLY" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="nUYlmBzm2YxJIW5L2hvB-211" vertex="1" connectable="0"> + <mxGeometry x="-0.5683" relative="1" as="geometry"> + <mxPoint x="-31" y="-10" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-219" value="batch: comm_phase = PHASE_TXN_CREATE" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0"> + <mxGeometry x="1470" y="539.9974074074072" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-220" value="batch: comm_phase = PHASE_APPLY_CFG" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0"> + <mxGeometry x="1390" y="699.9974074074072" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-228" value="batch: PHASE_APPLY_CFG -> PHASE_TXN_DELETE" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-223" target="nUYlmBzm2YxJIW5L2hvB-227" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-223" value="mgmt_txn_notify_be_cfg_apply_reply for each batch id in reply" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1750" y="290" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-226" value="mgmt_txn_send_be_txn_delete" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1750" y="420" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-229" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-227" target="nUYlmBzm2YxJIW5L2hvB-226" edge="1"> + <mxGeometry relative="1" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-233" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-227" target="nUYlmBzm2YxJIW5L2hvB-232" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1730" y="375" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-227" value="mgmt_move_txn_cfg_batch_to_next" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1750" y="360" width="167.5" height="30" as="geometry" /> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-230" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#ff0000;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;dashed=1;dashPattern=1 4;strokeWidth=2;entryX=-0.012;entryY=0.122;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-226" target="nUYlmBzm2YxJIW5L2hvB-161" edge="1"> + <mxGeometry relative="1" as="geometry"> + <mxPoint x="2055" y="699.96" as="targetPoint" /> + <mxPoint x="1700" y="470" as="sourcePoint" /> + <Array as="points"> + <mxPoint x="1870" y="625" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-231" value="MESSAGE_TXN_REQ delete" style="edgeLabel;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;fontFamily=Verdana;fontColor=default;labelBackgroundColor=none;horizontal=0;" parent="nUYlmBzm2YxJIW5L2hvB-230" vertex="1" connectable="0"> + <mxGeometry x="-0.5683" relative="1" as="geometry"> + <mxPoint x="-10" y="9" as="offset" /> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-234" value="" style="edgeStyle=orthogonalEdgeStyle;shape=connector;rounded=1;orthogonalLoop=1;jettySize=auto;labelBackgroundColor=none;strokeColor=#001DBC;fontFamily=Verdana;fontSize=8;fontColor=default;endArrow=open;startSize=8;endSize=8;strokeWidth=1;fillColor=#0050ef;" parent="1" source="nUYlmBzm2YxJIW5L2hvB-232" target="nUYlmBzm2YxJIW5L2hvB-138" edge="1"> + <mxGeometry relative="1" as="geometry"> + <Array as="points"> + <mxPoint x="1690" y="70" /> + </Array> + </mxGeometry> + </mxCell> + <mxCell id="nUYlmBzm2YxJIW5L2hvB-232" value="mgmt_try_move_commit_to_next_phase" style="rounded=1;whiteSpace=wrap;arcSize=24;fillColor=#ffffc0;strokeColor=#ff0000;shadow=0;comic=0;labelBackgroundColor=none;fontFamily=Verdana;fontSize=8;align=center;fontColor=default;" parent="1" vertex="1"> + <mxGeometry x="1560" y="420" width="180" height="30" as="geometry" /> + </mxCell> + </root> + </mxGraphModel> + </diagram> +</mxfile> diff --git a/doc/figures/cli-change-mgmtd.svg b/doc/figures/cli-change-mgmtd.svg new file mode 100644 index 000000000000..279f74a72628 --- /dev/null +++ b/doc/figures/cli-change-mgmtd.svg @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="1048" height="781" viewBox="-0.5 -0.5 1048 781" style="background-color: rgb(255, 255, 255);"><defs><filter id="dropShadow"><feGaussianBlur in="SourceAlpha" stdDeviation="1.7" result="blur"/><feOffset in="blur" dx="3" dy="3" result="offsetBlur"/><feFlood flood-color="#3D4574" flood-opacity="0.4" result="offsetColor"/><feComposite in="offsetColor" in2="offsetBlur" operator="in" result="offsetBlur"/><feBlend in="SourceGraphic" in2="offsetBlur"/></filter></defs><g filter="url(#dropShadow)"><path d="M 275 274 L 475 274 Q 485 274 485 264 L 485 236.24" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 489.5 244.12 L 485 235.12 L 480.5 244.12" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><path d="M 200 284.22 L 200 294.11 Q 200 304 200 307.88 L 200 311.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 195.5 303.88 L 200 312.88 L 204.5 303.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="130" y="263.78" width="145" height="20.44" rx="4.91" ry="4.91" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 274px; margin-left: 131px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_prepare_cfg</div></div></div></foreignObject><text x="203" y="276" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_prepare_cfg</text></switch></g><path d="M 500 574 L 680 574 Q 690 574 690 584 L 690 634 Q 690 644 700 644 L 780.53 644" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 772.76 649 L 782.76 644 L 772.76 639" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="560" y="562">MESSAGE_TXN_REQ</text><text x="560" y="572">create</text></g><path d="M 275 574 L 330 574 Q 340 574 346.38 574 L 352.76 574" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 344.88 578.5 L 353.88 574 L 344.88 569.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="130" y="564" width="145" height="20" rx="4.8" ry="4.8" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 574px; margin-left: 131px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_be_txn_create</div></div></div></foreignObject><text x="203" y="576" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_be_txn_create</text></switch></g><rect x="355" y="565" width="145" height="20" rx="4.8" ry="4.8" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 575px; margin-left: 356px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_be_send_txn_req</div></div></div></foreignObject><text x="428" y="577" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_be_send_txn_req</text></switch></g><rect x="90" y="624" width="180" height="30" rx="7.2" ry="7.2" fill="#eeeeee" stroke="#36393d" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 639px; margin-left: 91px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">does nothing<br />cfg_data replys will cause next transition<div><br /></div></div></div></div></foreignObject><text x="180" y="641" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">does nothing...</text></switch></g><path d="M 195 704 L 287.76 704" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 279.88 708.5 L 288.88 704 L 279.88 699.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="241" y="722">next_phase =</text><text x="241" y="732"> PHASE_TXN_DELETE</text></g><rect x="50" y="694" width="145" height="20" rx="4.8" ry="4.8" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 704px; margin-left: 51px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_be_cfg_apply</div></div></div></foreignObject><text x="123" y="706" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_be_cfg_apply</text></switch></g><rect x="290" y="694" width="145" height="20" rx="4.8" ry="4.8" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 704px; margin-left: 291px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_be_send_cfgapply_req</div></div></div></foreignObject><text x="363" y="706" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_be_send_cfgapply_req</text></switch></g><rect x="11.25" y="754" width="145" height="20" rx="4.8" ry="4.8" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 764px; margin-left: 12px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_commit_cfg_reply</div></div></div></foreignObject><text x="84" y="766" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_commit_cfg_reply</text></switch></g><path d="M 120 224 L 120 564 Q 120 574 123.88 574 L 127.76 574" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 119.88 578.5 L 128.88 574 L 119.88 569.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="167" y="558">PHASE_TXN_CREATE</text></g><path d="M 150 224 L 150 234 Q 150 244 160 244 L 193 244 Q 203 244 203 252.77 L 203 261.54" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 198.5 253.66 L 203 262.66 L 207.5 253.66" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="180" y="237">PHASE_PREPARE_CFG</text></g><path d="M 60 224 L 60 314 Q 60 324 60 334 L 60 691.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 55.5 683.88 L 60 692.88 L 64.5 683.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="110" y="687">PHASE_CFG_APPLY</text></g><path d="M 30 224 L 30 354 Q 30 364 30 374 L 30 751.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 25.5 743.88 L 30 752.88 L 34.5 743.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="84" y="747">PHASE_TXN_DELETE</text></g><path d="M 100 224 L 100 314 Q 100 324 100 334 L 100 621.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 95.5 613.88 L 100 622.88 L 104.5 613.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="140" y="616">PHASE_SEND_CFG</text></g><rect x="0" y="154" width="167.5" height="70" rx="16.8" ry="16.8" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 189px; margin-left: 1px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_process_commit_cfg</div></div></div></foreignObject><text x="84" y="191" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_process_commit_cfg</text></switch></g><path d="M 275 274 L 420 274 Q 430 274 430 264 L 430 96.24" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 434.5 104.12 L 430 95.12 L 425.5 104.12" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="330" y="252">curr_phase =</text><text x="330" y="262">PHASE_TXN_CREATE</text></g><rect x="0" y="34" width="167.5" height="40" rx="9.6" ry="9.6" fill="#ffe6cc" stroke="#d79b00" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 54px; margin-left: 1px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_commit_config_req</div></div></div></foreignObject><text x="84" y="56" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_commit_config_req</text></switch></g><path d="M 167.5 54 L 367.76 54" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 359.88 58.5 L 368.88 54 L 359.88 49.5" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="274.5" y="27">curr_phase ==</text><text x="274.5" y="37">MGMTD_COMMIT_PHASE_PREPARE_CFG</text></g><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="275" y="67">next_phase ==</text><text x="275" y="77">MGMTD_COMMIT_PHASE_PREPARE_CFG</text></g><path d="M 390 83.81 L 390 164 Q 390 174 380 174 L 169.74 174" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 177.62 169.5 L 168.62 174 L 177.62 178.5" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><ellipse cx="430" cy="54" rx="60" ry="40" fill="#b1ddf0" stroke="#10739e" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 54px; margin-left: 371px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">TIMER:<br />MGMTD_TXN_PROC_COMCFG</div></div></div></foreignObject><text x="430" y="56" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">TIMER:...</text></switch></g><path d="M 202.5 284.22 L 202.5 289.11 Q 202.5 294 212.5 294 L 353 294 Q 363 294 363 302.88 L 363 311.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 358.5 303.88 L 363 312.88 L 367.5 303.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="130" y="314" width="145" height="50" rx="12" ry="12" fill="#e1d5e7" stroke="#9673a6" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 339px; margin-left: 131px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">GET nb_config_change's<br />nb_config_diff(cand, run)<br />or <br />txn->commit_cfg_req->req.commit_cfg.cfg_chgs<div><br /></div></div></div></div></foreignObject><text x="203" y="341" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">GET nb_config_change's...</text></switch></g><rect x="290" y="314" width="145" height="30" rx="7.2" ry="7.2" fill="#e1d5e7" stroke="#9673a6" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 143px; height: 1px; padding-top: 329px; margin-left: 291px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_create_config_batches</div></div></div></foreignObject><text x="363" y="331" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_create_config_batches</text></switch></g><ellipse cx="485" cy="204" rx="45" ry="30" fill="#b1ddf0" stroke="#10739e" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 204px; margin-left: 441px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">TIMER:<br />MGMTD_TXN_<br />COMMITCFG_TIMEOUT</div></div></div></foreignObject><text x="485" y="206" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">TIMER:...</text></switch></g><path d="M 820 594 L 820 84 Q 820 74 814.87 74 L 809.74 74" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 789.74 74 L 799.74 69 L 799.74 79 Z M 799.74 74 L 809.74 69 L 809.74 79 Z" fill="#ff0000" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px" transform="rotate(-90,810.5,523.5)"><text x="810" y="526">MESSAGE_CFG_DATA_REPLY</text></g><path d="M 790 594 L 790 299 Q 790 289 780.99 289 L 771.97 289" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 779.74 284 L 769.74 289 L 779.74 294" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px" transform="rotate(-90,780.5,507.5)"><text x="780" y="510">MESSAGE_CFG_APPLY_REPLY;</text></g><rect x="785" y="594" width="205" height="120" rx="28.8" ry="28.8" fill="#cdeb8b" stroke="#36393d" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 203px; height: 1px; padding-top: 654px; margin-left: 786px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Backend Client</div></div></div></foreignObject><text x="888" y="656" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">Backend Client</text></switch></g><path d="M 953.8 449 L 953.8 439 Q 953.8 429 953.8 439 L 953.8 444 Q 953.8 449 953.8 440.12 L 953.8 431.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 958.3 439.12 L 953.8 430.12 L 949.3 439.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="870" y="449" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 464px; margin-left: 871px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_notify_be_txn_reply</div></div></div></foreignObject><text x="954" y="466" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_notify_be_txn_reply</text></switch></g><path d="M 953.75 399 L 953.75 343.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 953.75 325.12 L 958.25 334.12 L 949.25 334.12 Z M 953.75 334.12 L 958.25 343.12 L 949.25 343.12 Z" fill="#ff0000" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="870" y="399" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 414px; margin-left: 871px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_be_cfg_data</div></div></div></foreignObject><text x="954" y="416" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_be_cfg_data</text></switch></g><path d="M 953.8 294 L 953.8 284 Q 953.8 274 953.8 281.5 L 953.8 285.25 Q 953.8 289 953.8 280.12 L 953.8 271.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 958.3 279.12 L 953.8 270.12 L 949.3 279.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="944.8" y="225">batch: PHASE_TXN_REQ -> PHASE_SEND_CFG</text></g><rect x="875" y="294" width="157.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 156px; height: 1px; padding-top: 309px; margin-left: 876px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_be_send_cfgdata_req</div></div></div></foreignObject><text x="954" y="311" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_be_send_cfgdata_req</text></switch></g><path d="M 945 239 L 945 231.5 Q 945 224 945 215.12 L 945 206.24" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 949.5 214.12 L 945 205.12 L 940.5 214.12" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="939.5" y="126.5">txn: PHASE_TXN_REQ -> PHASE_SEND_CFG</text></g><rect x="875" y="239" width="157.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 156px; height: 1px; padding-top: 254px; margin-left: 876px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_move_txn_cfg_batch_to_next</div></div></div></foreignObject><text x="954" y="256" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_move_txn_cfg_batch_to_next</text></switch></g><path d="M 945 144 L 945 24 Q 945 14 935 14 L 432.24 14" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 440.12 9.5 L 431.12 14 L 440.12 18.5" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="740" y="7">curr_phase = PHASE_SEND_CFG</text></g><rect x="852.5" y="144" width="185" height="60" rx="14.4" ry="14.4" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 183px; height: 1px; padding-top: 174px; margin-left: 854px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_try_move_commit_to_next_phase<br />if all backend clients<br />have all been sent their batches<br />move to next phase and post EVENT/TIMER</div></div></div></foreignObject><text x="945" y="176" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_try_move_commit_to_next_phase...</text></switch></g><path d="M 875 309 L 860 309 Q 850 309 850 319 L 850 571.76" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 850 591.76 L 845 581.76 L 855 581.76 Z M 850 581.76 L 845 571.76 L 855 571.76 Z" fill="#ff0000" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px" transform="rotate(-90,840.5,380.5)"><text x="840" y="383">MESSAGE_CFG_DATA_REQ</text></g><path d="M 703.75 89 L 703.75 141.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 699.25 133.88 L 703.75 142.88 L 708.25 133.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="703.75" y="123">batch: PHASE_SEND_CFG -> PHASE_APPLY_CFG</text></g><rect x="620" y="59" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 74px; margin-left: 621px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_notify_be_cfgdata_reply</div></div></div></foreignObject><text x="704" y="76" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_notify_be_cfgdata_reply</text></switch></g><path d="M 703.8 174 L 703.8 184 Q 703.8 194 703.8 197.88 L 703.8 201.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 699.3 193.88 L 703.8 202.88 L 708.3 193.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="620" y="144" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 159px; margin-left: 621px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_move_txn_cfg_batch_to_next</div></div></div></foreignObject><text x="704" y="161" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_move_txn_cfg_batch_to_next</text></switch></g><path d="M 613.75 219 L 580 219 Q 570 219 570 209 L 570 44 Q 570 34 560 34 L 484.2 34" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 492.08 29.5 L 483.08 34 L 492.08 38.5" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="620" y="246.75">curr_phase = PHASE_APPLY_CFG</text></g><rect x="613.75" y="204" width="180" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 219px; margin-left: 615px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_try_move_commit_to_next_phase</div></div></div></foreignObject><text x="704" y="221" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_try_move_commit_to_next_phase</text></switch></g><path d="M 954 594 L 954 574 Q 954 564 954 554 L 954 483.47" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 959 491.24 L 954 481.24 L 949 491.24" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px" transform="rotate(-90,940.5,539.5)"><text x="940" y="542">MESSAGE_TXN_REPLY</text></g><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="320" y="552">curr_phase =</text><text x="320" y="562">PHASE_TXN_CREATE</text></g><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="315" y="589">next_phase =</text><text x="315" y="599">PHASE_SEND_CFG</text></g><path d="M 896.96 356.21 C 891.58 352.95 884.78 353.59 880.07 357.81 C 875.37 362.03 873.83 368.87 876.25 374.79 C 878.67 380.7 884.51 384.35 890.73 383.85 C 896.96 383.35 902.17 378.8 903.66 372.57" fill="none" stroke="rgb(0, 0, 0)" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><path d="M 902.21 372.57 L 904.38 368.85 L 905 373.31 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px"><text x="906.5" y="361.5">For each of the</text><text x="906.5" y="371.5">batches of cfgdata</text><text x="906.5" y="381.5">send msg and move</text></g><path d="M 806.96 41.21 C 801.58 37.95 794.78 38.59 790.07 42.81 C 785.37 47.03 783.83 53.87 786.25 59.79 C 788.67 65.7 794.51 69.35 800.73 68.85 C 806.96 68.35 812.17 63.8 813.66 57.57" fill="none" stroke="rgb(0, 0, 0)" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><path d="M 812.21 57.57 L 814.38 53.85 L 815 58.31 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px"><text x="816.5" y="51.5">For each of the</text><text x="816.5" y="61.5"> batches of cfgdata</text></g><path d="M 435 704 L 690 704 Q 700 704 700 694 L 700 664 Q 700 654 710 654 L 780.53 654" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 772.76 659 L 782.76 654 L 772.76 649" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="490" y="697">MESSAGE_CFG_APPLY</text></g><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="320" y="522">batch: comm_phase =</text><text x="320" y="532">PHASE_TXN_CREATE</text></g><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="240" y="682">batch: comm_phase =</text><text x="240" y="692">PHASE_APPLY_CFG</text></g><path d="M 683.75 304 L 683.75 341.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 679.25 333.88 L 683.75 342.88 L 688.25 333.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px"><text x="683.25" y="326.5">batch: PHASE_APPLY_CFG -> PHASE_TXN_DELETE</text></g><rect x="600" y="274" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 289px; margin-left: 601px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_notify_be_cfg_apply_reply<br />for each batch id in reply</div></div></div></foreignObject><text x="684" y="291" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_notify_be_cfg_apply_reply...</text></switch></g><rect x="600" y="404" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 419px; margin-left: 601px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_txn_send_be_txn_delete</div></div></div></foreignObject><text x="684" y="421" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_txn_send_be_txn_delete</text></switch></g><path d="M 683.8 374 L 683.8 384 Q 683.8 394 683.8 389 L 683.8 386.5 Q 683.8 384 683.8 392.88 L 683.8 401.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 679.3 393.88 L 683.8 402.88 L 688.3 393.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><path d="M 600 359 L 590 359 Q 580 359 580 369 L 580 401.76" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 575.5 393.88 L 580 402.88 L 584.5 393.88" fill="none" stroke="#ff0000" stroke-miterlimit="10" pointer-events="all"/><rect x="600" y="344" width="167.5" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 166px; height: 1px; padding-top: 359px; margin-left: 601px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_move_txn_cfg_batch_to_next</div></div></div></foreignObject><text x="684" y="361" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_move_txn_cfg_batch_to_next</text></switch></g><path d="M 720 434 L 720 598.6 Q 720 608.6 730 608.61 L 778.07 608.64" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="2 8" pointer-events="stroke"/><path d="M 770.3 613.63 L 780.3 608.64 L 770.31 603.63" fill="none" stroke="#ff0000" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/><g fill="rgb(0, 0, 0)" font-family="Verdana" text-anchor="middle" font-size="8px" transform="rotate(-90,710.5,493.5)"><text x="710" y="491">MESSAGE_TXN_REQ</text><text x="710" y="501">delete</text></g><path d="M 540 404 L 540 64 Q 540 54 530 54 L 492.24 54" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 500.12 49.5 L 491.12 54 L 500.12 58.5" fill="none" stroke="#001dbc" stroke-miterlimit="10" pointer-events="all"/><rect x="410" y="404" width="180" height="30" rx="7.2" ry="7.2" fill="#ffffc0" stroke="#ff0000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 178px; height: 1px; padding-top: 419px; margin-left: 411px;"><div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 8px; font-family: Verdana; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">mgmt_try_move_commit_to_next_phase</div></div></div></foreignObject><text x="500" y="421" fill="rgb(0, 0, 0)" font-family="Verdana" font-size="8px" text-anchor="middle">mgmt_try_move_commit_to_next_phase</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg> \ No newline at end of file diff --git a/doc/manpages/vtysh.rst b/doc/manpages/vtysh.rst index af527bea407c..396fcfc66e70 100644 --- a/doc/manpages/vtysh.rst +++ b/doc/manpages/vtysh.rst @@ -67,6 +67,15 @@ OPTIONS available for the vtysh command: Display a usage message on standard output and exit. +.. option:: -t, --timestamp + + Print a timestamp before going to shell or reading the configuration file. + +.. option:: --no-fork + + When used in conjunction with ``-b``, prevents vtysh from forking children to handle configuring each target daemon. + + ENVIRONMENT VARIABLES ===================== VTYSH_PAGER diff --git a/doc/subdir.am b/doc/subdir.am index a1297a4f810d..2795326d9bed 100644 --- a/doc/subdir.am +++ b/doc/subdir.am @@ -99,6 +99,10 @@ EXTRA_DIST += \ doc/mpls/ospfd.conf \ doc/mpls/cli_summary.txt \ doc/mpls/opaque_lsa.txt \ + doc/figures/cli-change-client.drawio \ + doc/figures/cli-change-client.svg \ + doc/figures/cli-change-mgmtd.drawio \ + doc/figures/cli-change-mgmtd.svg \ doc/figures/cligraph.png \ doc/figures/cligraph.svg \ doc/figures/fig-normal-processing.dia \ diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 254dad8303b1..337cfff9378f 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -349,6 +349,10 @@ Basic Config Commands Allow using IPv4 reserved (Class E) IP ranges for daemons. E.g.: setting IPv4 addresses for interfaces or allowing reserved ranges in BGP next-hops. + If you need multiple FRR instances (or FRR + any other daemon) running in a + single router and peering via 127.0.0.0/8, it's also possible to use this + knob if turned on. + Default: off. .. _sample-config-file: diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst index 1a42996771de..776726f193a6 100644 --- a/doc/user/bfd.rst +++ b/doc/user/bfd.rst @@ -10,6 +10,7 @@ the following RFCs: * :rfc:`5880` * :rfc:`5881` +* :rfc:`5882` * :rfc:`5883` Currently, there are two implementations of the BFD commands in FRR: @@ -353,6 +354,33 @@ The following commands are available inside the interface configuration node. that interface. +.. _bfd-rip-peer-config: + +RIP BFD configuration +--------------------- + +The following commands are available inside the interface configuration node: + +.. clicmd:: ip rip bfd + + Automatically create BFD session for each RIP peer discovered in this + interface. When the BFD session monitor signalize that the link is down + the RIP peer is removed and all the learned routes associated with that + peer are removed. + + +.. clicmd:: ip rip bfd profile BFD_PROFILE_NAME + + Selects a BFD profile for the BFD sessions created in this interface. + + +The following command is available in the RIP router configuration node: + +.. clicmd:: bfd default-profile BFD_PROFILE_NAME + + Selects a default BFD profile for all sessions without a profile specified. + + .. _bfd-static-peer-config: BFD Static Route Monitoring Configuration diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 1798f1ad2a07..f09512de3278 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -455,7 +455,7 @@ Administrative Distance Metrics .. _bgp-requires-policy: Require policy on EBGP -------------------------------- +---------------------- .. clicmd:: bgp ebgp-requires-policy @@ -1669,6 +1669,10 @@ Configuring Peers Configure BGP to send best known paths to neighbor in order to preserve multi path capabilities inside a network. +.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-best-selected (1-6) + + Configure BGP to calculate and send N best known paths to the neighbor. + .. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> disable-addpath-rx Do not accept additional paths from this neighbor. @@ -1729,6 +1733,12 @@ Configuring Peers and will not be displayed as part of a `show run`. The no form of the command turns off this ability. +.. clicmd:: bgp default-originate timer (0-3600) + + Set the period to rerun the default-originate route-map scanner process. The + default is 5 seconds. With a full routing table, it might be useful to increase + this setting to avoid scanning the whole BGP table aggressively. + .. clicmd:: bgp default ipv4-unicast This command allows the user to specify that the IPv4 Unicast address @@ -2032,6 +2042,13 @@ Capability Negotiation Disabled by default. +.. clicmd:: neighbor PEER aigp + + Send and receive AIGP attribute for this neighbor. This is valid only for + eBGP neighbors. + + Disabled by default. iBGP neighbors have this option enabled implicitly. + .. _bgp-as-path-access-lists: AS Path Access Lists @@ -2088,10 +2105,16 @@ Using AS Path in Route Map 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. -.. clicmd:: set as-path replace <any|ASN> +.. clicmd:: set as-path replace <any|ASN> [<ASN>] - Replace a specific AS number to local AS number. ``any`` replaces each - AS number in the AS-PATH with the local AS number. + Replace a specific AS number to local AS number or a configured AS number. + ``any`` replaces each AS number in the AS-PATH with either the local AS + number or the configured AS number. + +.. clicmd:: set as-path exclude all + + Remove all AS numbers from the AS_PATH of the BGP path's NLRI. The no form of + this command removes this set operation from the route-map. .. _bgp-communities-attribute: @@ -2290,7 +2313,7 @@ Numbered Community Lists ^^^^^^^^^^^^^^^^^^^^^^^^ When number is used for BGP community list name, the number has -special meanings. Community list number in the range from 1 and 99 is +special meanings. Community list number in the range from 1 to 99 is standard community list. Community list number in the range from 100 to 500 is expanded community list. These community lists are called as numbered community lists. On the other hand normal community lists @@ -2584,12 +2607,24 @@ BGP Extended Communities in Route Map .. clicmd:: set extcommunity rt EXTCOMMUNITY - This command set Route Target value. + This command sets Route Target value. + +.. clicmd:: set extcommunity nt EXTCOMMUNITY + + This command sets Node Target value. + + If the receiving BGP router supports Node Target Extended Communities, + it will install the route with the community that contains it's own + local BGP Identifier. Otherwise, it's not installed. .. clicmd:: set extcommunity soo EXTCOMMUNITY - This command set Site of Origin value. + This command sets Site of Origin value. +.. clicmd:: set extcomumnity color EXTCOMMUNITY + + This command sets colors values. + .. clicmd:: set extcommunity bandwidth <(1-25600) | cumulative | num-multipaths> [non-transitive] This command sets the BGP link-bandwidth extended community for the prefix @@ -2767,6 +2802,17 @@ happened automatically if local-role is set. value of his role (by setting local-role on his side). Otherwise, a Role Mismatch Notification will be sent. +Labeled unicast +--------------- + +*bgpd* supports labeled information, as per :rfc:`3107`. + +.. clicmd:: bgp labeled-unicast <explicit-null|ipv4-explicit-null|ipv6-explicit-null> + +By default, locally advertised prefixes use the `implicit-null` label to +encode in the outgoing NLRI. The following command uses the `explicit-null` +label value for all the BGP instances. + .. _bgp-l3vpn-vrfs: L3VPN VRFs @@ -2871,6 +2917,13 @@ address-family: extended community values as described in :ref:`bgp-extended-communities-attribute`. +.. clicmd:: label vpn export allocation-mode per-vrf|per-nexthop + + Select how labels are allocated in the given VRF. By default, the `per-vrf` + mode is selected, and one label is used for all prefixes from the VRF. The + `per-nexthop` will use a unique label for all prefixes that are reachable + via the same nexthop. + .. clicmd:: label vpn export (0..1048575)|auto Enables an MPLS label to be attached to a route exported from the current @@ -2934,6 +2987,14 @@ by issuing the following command under the interface configuration context. This configuration will install VPN prefixes originated from an e-bgp session, and with the next-hop directly connected. +.. clicmd:: mpls bgp l3vpn-multi-domain-switching + +Redistribute labeled L3VPN routes from AS to neighboring AS (RFC-4364 option +B, or within the same AS when the iBGP peer uses ``next-hop-self`` to rewrite +the next-hop attribute). The labeled L3VPN routes received on this interface are +re-advertised with local labels and an MPLS table swap entry is set to bind +the local label to the received label. + .. _bgp-l3vpn-srv6: L3VPN SRv6 @@ -3190,6 +3251,77 @@ Example configuration: exit-address-family ! +.. _bgp-evpn-mac-vrf-site-of-origin: + +EVPN MAC-VRF Site-of-Origin +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +In some EVPN deployments it is useful to associate a logical VTEP's Layer 2 +domain (MAC-VRF) with a Site-of-Origin "site" identifier. This provides a +BGP topology-independent means of marking and import-filtering EVPN routes +originated from a particular L2 domain. One situation where this is valuable +is when deploying EVPN using anycast VTEPs, i.e. Active/Active MLAG, as it +can be used to avoid ownership conflicts between the two control planes +(EVPN vs MLAG). + +Example Use Case (MLAG Anycast VTEPs): + +During normal operation, an MLAG VTEP will advertise EVPN routes for attached +hosts using a shared anycast IP as the BGP next-hop. It is expected for its +MLAG peer to drop routes originated by the MLAG Peer since they have a Martian +(self) next-hop. However, prior to the anycast IP being assigned to the local +system, the anycast BGP next-hop will not be considered a Martian (self) IP. +This results in a timing window where hosts that are locally attached to the +MLAG pair's L2 domain can be learned both as "local" (via MLAG) or "remote" +(via an EVPN route with a non-local next-hop). This can trigger erroneous MAC +Mobility events, as the host "moves" between one MLAG Peer's Unique VTEP-IP +and the shared anycast VTEP-IP, which causes unnecessary control plane and +data plane events to propagate throughout the EVPN domain. +By associating the MAC-VRF of both MLAG VTEPs with the same site identifier, +EVPN routes originated by one MLAG VTEP will ignored by its MLAG peer, ensuring +that only the MLAG control plane attempts to take ownership of local hosts. + +The EVPN MAC-VRF Site-of-Origin feature works by influencing two behaviors: + +1. All EVPN routes originating from the local MAC-VRF will have a + Site-of-Origin extended community added to the route, matching the + configured value. +2. EVPN routes will be subjected to a "self SoO" check during MAC-VRF + or IP-VRF import processing. If the EVPN route is found to carry a + Site-of-Origin extended community whose value matches the locally + configured MAC-VRF Site-of-Origin, the route will be maintained in + the global EVPN RIB ("show bgp l2vpn evpn route") but will not be + imported into the corresponding MAC-VRF ("show bgp vni") or IP-VRF + ("show bgp [vrf <vrfname>] [ipv4 | ipv6 [unicast]]"). + +The import filtering described in item (2) is constrained just to Type-2 +(MAC-IP) and Type-3 (IMET) EVPN routes. + +The EVPN MAC-VRF Site-of-Origin can be configured using a single CLI command +under ``address-family l2vpn evpn`` of the EVPN underlay BGP instance. + +.. clicmd:: [no] mac-vrf soo <site-of-origin-string> + +Example configuration: + +.. code-block:: frr + + router bgp 100 + neighbor 192.168.0.1 remote-as 101 + ! + address-family ipv4 l2vpn evpn + neighbor 192.168.0.1 activate + advertise-all-vni + mac-vrf soo 100.64.0.0:777 + exit-address-family + +This configuration ensures: + +1. EVPN routes originated from a local L2VNI will have a Site-of-Origin + extended community with the value ``100.64.0.0:777`` +2. Received EVPN routes carrying a Site-of-Origin extended community with the + value ``100.64.0.0:777`` will not be imported into a local MAC-VRF (L2VNI) + or IP-VRF (L3VNI). + .. _bgp-evpn-mh: EVPN Multihoming @@ -3849,6 +3981,12 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. Total number of neighbors 1 exit1# +If PfxRcd and/or PfxSnt is shown as ``(Policy)``, that means that the EBGP +default policy is turned on, but you don't have any filters applied for +incoming/outgoing directions. + +.. seealso:: :ref:`bgp-requires-policy` + .. clicmd:: show bgp [afi] [safi] [all] [wide|json] .. clicmd:: show bgp vrfs [<VRFNAME$vrf_name>] [json] @@ -4090,6 +4228,122 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. If ``afi`` is specified, with ``all`` option, routes will be displayed for each SAFI in the selected AFI. +.. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] [afi] [safi] detail [json] + + Display the detailed version of all routes from the specified bgp vrf table + for a given afi + safi. + + If no vrf is specified, then it is assumed as a default vrf and routes + are displayed from default vrf table. + + If ``all`` option is specified as vrf name, then all bgp vrf tables routes + from a given afi+safi are displayed in the detailed output of routes. + + If ``json`` option is specified, detailed output is displayed in JSON format. + + Following are sample output for few examples of how to use this command. + +.. code-block:: frr + + torm-23# sh bgp ipv4 unicast detail (OR) sh bgp vrf default ipv4 unicast detail + + !--- Output suppressed. + + BGP routing table entry for 172.16.16.1/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local, (Received from a RR-client) + 172.16.16.1 (metric 20) from torm-22(172.16.16.1) (192.168.0.10) + Origin IGP, metric 0, localpref 100, valid, internal + Last update: Fri May 8 12:54:05 2023 + BGP routing table entry for 172.16.16.2/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local + 0.0.0.0 from 0.0.0.0 (172.16.16.2) + Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received) + Last update: Wed May 8 12:54:41 2023 + + Displayed 2 routes and 2 total paths + +.. code-block:: frr + + torm-23# sh bgp vrf all detail + + Instance default: + + !--- Output suppressed. + + BGP routing table entry for 172.16.16.1/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local, (Received from a RR-client) + 172.16.16.1 (metric 20) from torm-22(172.16.16.1) (192.168.0.10) + Origin IGP, metric 0, localpref 100, valid, internal + Last update: Fri May 8 12:44:05 2023 + BGP routing table entry for 172.16.16.2/32 + Paths: (1 available, best #1, table default) + Not advertised to any peer + Local + 0.0.0.0 from 0.0.0.0 (172.16.16.2) + Origin incomplete, metric 0, weight 32768, valid, sourced, bestpath-from-AS Local, best (First path received) + Last update: Wed May 8 12:45:01 2023 + + Displayed 2 routes and 2 total paths + + Instance vrf3: + + !--- Output suppressed. + + BGP routing table entry for 192.168.0.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:12:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.0.2], VNI 1008/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1008 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:41:55 2023 + BGP routing table entry for 192.168.1.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:13:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.1.2], VNI 1009/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:41:55 2023 + + Displayed 2 routes and 2 total paths + + +.. code-block:: frr + + torm-23# sh bgp vrf vrf3 ipv4 unicast detail + + !--- Output suppressed. + + BGP routing table entry for 192.168.0.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:12:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.0.2], VNI 1008/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1008 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:23:35 2023 + BGP routing table entry for 192.168.1.2/32 + Paths: (1 available, best #1, vrf vrf3) + Not advertised to any peer + Imported from 172.16.16.1:13:[2]:[0]:[48]:[00:02:00:00:00:58]:[32]:[192.168.1.2], VNI 1009/4003 + Local + 172.16.16.1 from torm-22(172.16.16.1) (172.16.16.1) announce-nh-self + Origin IGP, localpref 100, valid, internal, bestpath-from-AS Local, best (First path received) + Extended Community: RT:65000:1009 ET:8 Rmac:00:02:00:00:00:58 + Last update: Fri May 8 02:23:55 2023 + + Displayed 2 routes and 2 total paths + .. _bgp-display-routes-by-community: Displaying Routes by Community Attribute diff --git a/doc/user/index.rst b/doc/user/index.rst index 8b9b7e5af7a4..4789677a9a04 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -27,6 +27,7 @@ Basics grpc filter routemap + affinitymap ipv6 kernel snmp @@ -66,6 +67,7 @@ Protocols vrrp bmp watchfrr + mgmtd ######## Appendix diff --git a/doc/user/installation.rst b/doc/user/installation.rst index 2310d397cd56..8e8fb246086b 100644 --- a/doc/user/installation.rst +++ b/doc/user/installation.rst @@ -390,6 +390,18 @@ and the configuration files in :file:`/usr/local/etc`. The :file:`/usr/local/` installation prefix and other directories may be changed using the following options to the configuration script. +.. option:: --enable-ccls + + Enable the creation of a :file:`.ccls` file in the top level source + directory. + + Some development environments (e.g., LSP server within emacs, et al.) can + utilize :clicmd:`ccls` to provide highly sophisticated IDE features (e.g., + semantically accurate jump-to definition/reference, and even code + refactoring). The `--enable-ccls` causes :file:`configure` to generate a + configuration for the :clicmd:`ccls` command, based on the configured + FRR build environment. + .. option:: --prefix <prefix> Install architecture-independent files in `prefix` [/usr/local]. diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 055841d230ae..570b8bd182d4 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -68,6 +68,10 @@ writing, *isisd* does not support multiple ISIS processes. Log changes in adjacency state. +.. clicmd:: log-pdu-drops + + Log any dropped PDUs. + .. clicmd:: metric-style [narrow | transition | wide] Set old-style (ISO 10589) or new-style packet formats: @@ -212,6 +216,10 @@ ISIS interface Add padding to IS-IS hello packets. +.. clicmd:: isis hello padding during-adjacency-formation + + Add padding to IS-IS hello packets during adjacency formation only. + .. clicmd:: isis hello-interval (1-600) [level-1 | level-2] Set Hello interval in seconds globally, for an area (level-1) or a domain @@ -308,12 +316,12 @@ Showing ISIS information Show the ISIS database globally, for a specific LSP id without or with details. -.. clicmd:: show isis topology [level-1|level-2] +.. clicmd:: show isis topology [level-1|level-2] [algorithm (128-255)] Show topology IS-IS paths to Intermediate Systems, globally, in area (level-1) or domain (level-2). -.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup] +.. clicmd:: show isis route [level-1|level-2] [prefix-sid|backup] [algorithm (128-255)] Show the ISIS routing table, as determined by the most recent SPF calculation. @@ -381,8 +389,7 @@ Traffic Engineering :ref:`ospf-traffic-engineering` - -.. _debugging-isis: +.. _isis-segment-routing: Segment Routing =============== @@ -415,7 +422,7 @@ Known limitations: MPLS dataplane. E.g. for Linux kernel, since version 4.13 the maximum value is 32. -.. clicmd:: segment-routing prefix <A.B.C.D/M|X:X::X:X/M> <absolute (16-1048575)|index (0-65535) [no-php-flag|explicit-null] [n-flag-clear] +.. clicmd:: segment-routing prefix <A.B.C.D/M|X:X::X:X/M> [algorithm (128-255)] <absolute (16-1048575)|index (0-65535) [no-php-flag|explicit-null] [n-flag-clear] prefix. The 'no-php-flag' means NO Penultimate Hop Popping that allows SR node to request to its neighbor to not pop the label. The 'explicit-null' @@ -424,10 +431,164 @@ Known limitations: clear the Node flag that is set by default for Prefix-SIDs associated to loopback addresses. This option is necessary to configure Anycast-SIDs. -.. clicmd:: show isis segment-routing node +.. clicmd:: show isis segment-routing node [algorithm (128-255)] Show detailed information about all learned Segment Routing Nodes. +.. _isis-flex-algo: + +Flex-Algos (Flex-Algo) +====================== + +*isisd* supports some features of +`RFC 9350 <https://tools.ietf.org/html/rfc9350>`_ on an MPLS Segment-Routing +dataplane. The compatibility has been tested against Cisco. + +IS-IS uses by default the `Shortest-Path-First` algorithm that basically +calculates paths based on the shortest total metric to the destinations. +Flex-Algo allows new algorithms to run in parallel to compute paths in different +manners, based on metrics (IGP metric or a new type of metrics such as Traffic +Engineering (TE) metric and minimum delay...) and constraints. New metric types +are not yet implemented but constraints are already operational. Constraints can +restrict paths to links with specific affinities or avoid links with specific +affinities. Combinations of these are also possible. + +The administrator can configure up to 128 Flex-Algos in an IS-IS area. +To do so, it defines a set of Flex-Algo Definitions (FAD) which +have the following characteristics: + +- a numeric identifier (ID) between 128 and 255 inclusive +- a set of constraints (basically, include or exclude a certain given set of + links, designated by a admin-group) +- the calculation type (only the `Shortest-Path-First` is currently supported) +- the metric type (only the IGP inherited metric type is currently supported) +- some additional flags (not supported for the moment). + +A subset of routers advertises the Flex-Algo Definitions (FAD) to the other +routers within an area. In order to use a common set of FADs, each router runs a +FAD election process for each locally configured algorithm, using the following +rules: + +- If a locally configured FAD is not advertised to the area, the router does not + participate in the particular flex algorithm. +- If a given flex algorithm is running, the participation in this particular + flex algorithm stops when its advertisements are over. +- A router includes its own FAD in the election process if and only if it is + advertised to the other routers. +- If only one router advertises the FAD, the FAD is elected. +- If several FADs are advertised with different priorities, the one with the + highest priority value is selected. +- If there are multiple advertisements of the FAD with the same highest + priority, the FAD of the router with the highest IS-IS system-ID is + selected. + +Routers only use the specifications of the elected FAD regardless of the locally +configured definitions. If a router does not support one of the FAD +characteristics, it stops participating in the Flex-Algo. + +For each running Flex-Algo, the Segment-Routing SIDs must be +configured with values unique to the algorithm. It allows routers to identify +which flex algorithm they must use for a given packet. + +The following commands configure Flex-Algo at the 'router isis' configuration +level. Segment-Routing prefixes must be configured for the Flex-Algo. + +.. clicmd:: flexible-algorithm (128-255) + + Add a Flex-Algo Definition (FAD) and enter the FAD configuration + level. The algorithm ID value is in the range of 128 to 255 inclusive. + +.. clicmd:: no flexible-algorithm (128-255) + + Unconfigure a Flex-Algo Definition. + +.. clicmd:: affinity-map NAME bit-position (0-255) + + Add the specified 'affinity-map'. Affinity-map definitions are used in + FADs and in interfaces admin-group definition. + + Affinity-maps format in advertisement TLVs use the extended admin-group + format defined in the RFC7308 section 2.2. The extended admin-group uses a + 256 bits field. If an affinity-map is set, the bit at the extended + admin-group 'bit-position' is set 1, else it is set to 0. + +The following commands configure Flex-Algo at the 'router isis' and +'flexible-algorithm (128-255)' configuration level. + +.. clicmd:: advertise-definition + + Advertise the current FAD to other IS-IS routers by using specific IS-IS + TLVs. By default, the definition is is not shared with other routers. + +   A router can advertise a FAD without participating in the Flex-Algo. + +.. clicmd:: priority (0-255) + + Set the specified 'priority' in the current FAD advertisements . + +.. clicmd:: metric-type [igp|te|delay] + + Set the 'metric-type' for the current FAD. 'igp' is + the default value and refers to the classic 'Shortest-Path-First' algorithm. + If the 'te' or the 'delay' metric is selected, the value is advertised but + the flex algorithm is disabled locally because these types are not currently + supported. + +.. clicmd:: no metric-type + + Reset the 'metric-type' to the default 'igp' metric. + +.. clicmd:: affinity exclude-any NAME + + Add the specified affinity to the list of exclude-any affinities. The + Flex-Algo will compute paths that exclude the segments with any of + the specified affinities. + +.. clicmd:: no affinity exclude-any NAME + + Remove the specified affinity to the list of exclude-any affinities. + +.. clicmd:: affinity include-all NAME + + Add the specified affinity to the list of include-all affinities. The + Flex-Algo will compute paths that include the segments with all + the specified affinities. + +.. clicmd:: no affinity include-all NAME + + Remove the specified affinity to the list of include-all affinities. + +.. clicmd:: affinity include-any NAME + + Add the specified affinity to the list of include-any affinities. The + Flex-Algo will compute paths that include the segments with any of + the specified affinities. + +.. clicmd:: no affinity include-any NAME + + Remove the specified affinity to the list of include-any affinities. + +The following commands configure Flex-Algo at the 'interface' configuration +level. + +.. clicmd:: isis affinity flex-algo NAME + + Add the specified affinity to the interface. + +.. clicmd:: no isis affinity flex-algo NAME + + Remove the specified affinity from the interface. + +The following command show Flex-Algo information: + +.. clicmd:: show isis flex-algo [(128-255)] + + Show information about the elected FADs + +'show isis route', 'show isis topology' and 'show isis segment-routing node' +includes an 'algorithm (128-255)' optional argument. See +:ref:`showing-isis-information` and :ref:`isis-segment-routing`. + Debugging ISIS ============== diff --git a/doc/user/mgmtd.rst b/doc/user/mgmtd.rst new file mode 100644 index 000000000000..737eb57c85bc --- /dev/null +++ b/doc/user/mgmtd.rst @@ -0,0 +1,409 @@ +.. _mgmtd: + +************************* +MGMTd (Management Daemon) +************************* + +The FRR Management Daemon (from now on referred to as MGMTd) is a new +centralized entity representing the FRR Management Plane which can take +management requests from any kind of UI/Frontend entity (e.g. CLI, Netconf, +Restconf, Grpc etc.) over a new unified and common Frontend interface and +can help maintain configurational data or retrieve operational data from +any number of FRR managed entities/components that have been integrated +with the new FRR Centralised Management Framework. + +For organizing the management data to be owned by the FRR Management plane, +management data is stored in YANG in compliance with a pre-defined set +of YANG based schema. Data shall also be stored/retrieved in YANG format only. + +The MGMTd also acts as a separate computational entity for offloading much +of the management related computational overload involved in maintaining of +management data and processing of management requests, from individual +component daemons (which can otherwise be a signficant burden on the individual +components, affecting performance of its other functionalities). + +Lastly, the MGMTd works in-tandem with one (or more) MGMT Frontend +Clients and a bunch of MGMT Backend Clients to realize the entirety +of the FRR Management plane. Some of the advanatages of this new framework +are: + + 1. Consolidation and management of all Management data by a single entity. + 2. Better control over configuration validation, commit and rollback. + 3. Faster collection of configuration data (without needing to involve + individual component daemons). + 4. Offload computational burden of YANG data parsing and validations + of new configuration data being provisoned away from individual + component daemons + 5. Improve performance of individual component daemons while loading + huge configuration or retrieving huge operational dataset. + +The new FRR Management Daemon consists of the following sub-components: + - MGMT Frontend Interface + - MGMT Backend Interface + - MGMT Transaction Engine + +.. _mgmt_fe: + +MGMT Frontend Interface +======================= + +The MGMT Frontend Interface is a bunch of message-based APIs that lets +any UI/Frontend client to interact with the MGMT daemon to requests a +set of management operations on a specific datastore/database. +Following is a list of databases/datastores supported by the MGMT +Frontend Interface and MGMTd: + + - Candidate Database: + + - Consists of configuration data items only. + - Data can be edited anytime using SET_CONFIG API. + - Data can be retrieved anytime using GET_CONFIG/GET_DATA API. + + - Running Database: + + - Consists of configuration data items only. + - Data cannot be edited using SET_CONFIG API. + - Data can only be modified using COMMIT_CONFIG API after which un-committed + data from Candidate database will be first validated and applied to + individualBackend component(s). Only on successful validation and apply on + all individual components will the new data be copied over to the Running + database. + - Data can be retrieved anytime using GET_CONFIG/GET_DATA API. + + - Operational Database: + + - Consists of non-configurational data items. + - Data is not stored on MGMT daemon. Rather it will be need to be fetched + in real-time from the corresponding Backend component (if present). + - Data can be retrieved anytime using GET_DATA API. + +Frontend Clients connected to MGMTd via Frontend Interface can themselves have +multiple connections from one (or more) of its own remote clients. The MGMT +Frontend Interface supports reresenting each of the remote clients for a given +Frontend client(e.g. Netconf clients on a single Netconf server) as individual +Frontend Client Sessions. So a single connection from a single Frontend Client +can create more than one Frontend Client sessions. + +Following are some of the management operations supported: + - INIT_SESSION/CLOSE_SESSION: Create/Destroy a session. Rest of all the + operations are supported only in the context of a specific session. + - LOCK_DB/UNLOCK_DB: Lock/Unlock Management datastores/databases. + - GET_CONFIG/GET_DATA: Retrieve configurational/operational data from a + specific datastore/database. + - SET_CONFIG/DELETE_CONFIG: Add/Modify/Delete specific data in a specific + datastore/database. + - COMMIT_CONFIG: Validate and/or apply the uncommited set of configurations + from one configuration database to another. + - Currently committing configurations from Candidate to Running database + is only allowed, and not vice versa. + +The exact set of message-based APIs are represented as Google Protobuf +messages and can be found in the following file distributed with FRR codebase. + +.. code-block:: frr + + lib/mgmt.proto + +The MGMT daemon implements a MGMT Frontend Server that opens a UNIX +socket-based IPC channel on the following path to listen for incoming +connections from all possible Frontend clients: + +.. code-block:: frr + + /var/run/frr/mgmtd_fe.sock + +Each connection received from a Frontend client is managed and tracked +as a MGMT Frontend adapter by the MGMT Frontend Adapter sub-component +implemented by MGMTd. + +To facilitate faster development/integration of Frontend clients with +MGMT Frontend Interface, a C-based library has been developed. The API +specification of this library can be found at: + +.. code-block:: frr + + lib/mgmt_fe_client.h + +Following is a list of message types supported on the MGMT Frontend Interface: + - SESSION_REQ<Client-Connection-Id, Destroy> + - SESSION_REPLY<Client-Connection-Id, Destroy, Session-Id> + - LOCK_DB_REQ <Session-Id, Database-Id> + - LOCK_DB_REPLY <Session-Id, Database-Id> + - UNLOCK_DB_REQ <Session-Id, Database-Id> + - UNLOCK_DB_REPLY <Session-Id, Database-Id> + - GET_CONFIG_REQ <Session-Id, Database-Id, Base-Yang-Xpath> + - GET_CONFIG_REPLY <Session-Id, Database-Id, Base-Yang-Xpath, Yang-Data-Set> + - SET_CONFIG_REQ <Session-Id, Database-Id, Base-Yang-Xpath, Delete, ...> + - SET_CONFIG_REPLY <Session-Id, Database-id, Base-Yang-Xpath, ..., Status> + - COMMIT_CONFIG_REQ <Session-Id, Source-Db-Id, Dest-Db-Id> + - COMMIT_CONFIG_REPLY <Session-Id, Source-Db-id, Dest-Db-Id, Status> + - GET_DATA_REQ <Session-Id, Database-Id, Base-Yang-Xpath> + - GET_DATA_REPLY <Session-Id, Database-id, Base-Yang-Xpath, Yang-Data-Set> + - REGISTER_NOTIFY_REQ <Session-Id, Database-Id, Base-Yang-Xpath> + - DATA_NOTIFY_REQ <Database-Id, Base-Yang-Xpath, Yang-Data-Set> + +Please refer to the MGMT Frontend Client Developers Reference and Guide +(coming soon) for more details. + +MGMTD Backend Interface +======================= +The MGMT Backend Interface is a bunch of message-based APIs that can be +used by individual component daemons like BGPd, Staticd, Zebra to connect +with MGMTd and utilize the new FRR Management Framework to let any Frontend +clients to retrieve any operational data or manipulate any configuration data +owned by the individual daemon component. + +Like the MGMT Frontend Interface, the MGMT Backend Interface is is also +comprised of the following: + + - MGMT Backend Server (running on MGMT daemon) + - MGMT Backend Adapter (running on MGMT daemon) + - MGMT Backend client (running on Backend component daemons) + +The MGMT Backend Client and MGMT Backend Adapter sub-component communicates +using a specific set of message-based APIs. + +The exact set of message-based APIs are represented as Google Protobuf +messages and can be found in the following file distributed with FRR codebase. + +.. code-block:: frr + + lib/mgmt.proto + +The MGMT daemon implements a MGMT Backend Server that opens a UNIX +socket-based IPC channel on the following path to listen for incoming +connections from all possible Backend clients: + +.. code-block:: frr + + /var/run/frr/mgmtd_be.sock + +Each connection received from a Backend client is managed and tracked +as a MGMT Backend adapter by the MGMT Backend Adapter sub-component +implemented by MGMTd. + +To facilitate faster development/integration of Backend clients with +MGMTd, a C-based library has been developed. The API specification +of this library can be found at: + +.. code-block:: frr + + lib/mgmt_be_client.h + +Following is a list of message types supported on the MGMT Backend Interface: + + - SUBSCRIBE_REQ <Req-Id, Base-Yang-Xpath, Filter-Type> + - SUBSCRIBE_REPLY <Req-Id, Status> + - TXN_REQ <Txn-Id, Create> + - TXN_REPLY <Txn-Id, Status> + - CREATE_CFGDATA_REQ <Txn-Id, Req-Id, Batch-Id, ConfigDataContents> + - CREATE_CFGDATA_ERROR <Txn-Id, Req-Id, Batch-Id, Status> + - VALIDATE_CFGDATA_REQ <Txn-Id, Batch-Id> + - VALIDATE_CFGDATA_REPLY <Txn-Id, Batch-Id, Status, ErrorInfo> + - APPLY_CFGDATA_REQ <Txn-Id, Batch-Id> + - APPLY_CFGDATA_REPLY <Txn-Id, Batch-Id, Status, ErrorInfo> + - GET_OPERDATA_REQ <Txn-Id, Base-Yang-Xpath, Filter-Type> + - GET_OPERDATA_REPLY <Txn-Id, OperDataContents> + +Please refer to the MGMT Backend Client Developers Reference and Guide +(coming soon) for more details. + +MGMTD Transaction Engine +======================== + +The MGMT Transaction sub-component is the main brain of the MGMT daemon that +takes management requests from one (or more) Frontend Client translates +them into transactions and drives them to completion in co-oridination with +one (or more) Backend client daemons involved in the request. + +A transaction can be seen as a set of management procedures executed over +the Backend Interface with one (or more) individual Backend component +daemons, as a result of some management request initiated from a specific +Frontend client session. These group of operations on the Backend Interface +with one (or more) individual components involved should be executed without +taking any further management requests from other Frontend client sessions. +To maintain this kind of atomic behavior a lock needs to be acquired +(sometimes implicitly if not explicitly) by the corresponding Frontend client +session, on the various datastores/databases involved in the management request +being executed. The same datastores/databases need to be unlocked when all +the procedures have been executed and the transaction is being closed. + +Following are some of the transaction types supported by MGMT: + + - Configuration Transactions + + - Used to execute management operations like SET_CONFIG and COMMIT_CONFIG + that involve writing/over-writing the contents of Candidate and Running + databases. + - One (and only) can be created and be in-progress at any given time. + - Once initiated by a specific Frontend Client session and is still + in-progress, all subsequent SET_CONFIG and COMMIT_CONFIG operations + from other Frontend Client sessions will be rejected and responded + with failure. + - Requires acquiring write-lock on Candidate (and later Running) databases. + + - Show Transactions + + - Used to execute management operations like GET_CONFIG and GET_DATA + that involve only reading the contents of Candidate and Running + databases (and sometimes real-time retrieval of operational data + from individual component daemons). + - Multiple instance of this transaction type can be created and be + in-progress at any given time. + - However, when a configuration transaction is currently in-progress + show transaction can be initiated by any Frontend Client session. + - Requires acquiring read-lock on Candidate and/or Running databases. + - NOTE: Currently GET_DATA on Operational database is NOT supported. To + be added in a future time soon. + +MGMTD Configuration Rollback and Commit History +=============================================== + +The MGMT daemon maintains upto 10 last configuration commit buffers +and can rollback the contents of the Running Database to any of the +commit-ids maintained in the commit buffers. + +Once the number of commit buffers exceeds 10, the oldest commit +buffer is deleted to make space for the latest commit. Also on +rollback to a specific commit-id, buffer of all the later commits +are deleted from commit record. + +Configuration rollback is only allowed via VTYSH shell as of today +and is not possible through the MGMT Frontend interface. + +MGMT Configuration commands +=========================== + +.. clicmd:: mgmt set-config XPATH VALUE + + This command uses a SET_CONFIG request over the MGMT Frontend Interface + for the specified xpath with specific value. This command is used for + testing purpose only. But can be used to set configuration data from CLI + using SET_CONFIG operations. + +.. clicmd:: mgmt delete-config XPATH + + This command uses a SET_CONFIG request (with delete option) over the + MGMT Frontend Interface o delete the YANG data node at the given + xpath unless it is a key-leaf node(in which case it is not deleted). + +.. clicmd:: mgmt load-config FILE <merge|replace> + + This command loads configuration in JSON format from the filepath specified, + and merges or replaces the Candidate DB as per the option specified. + +.. clicmd:: mgmt save-config <candidate|running> FILE + + This command dumps the DB specified in the db-name into the file in JSON + format. This command in not supported for the Operational DB. + +.. clicmd:: mgmt commit abort + + This command will abort any configuration present on the Candidate but not + been applied to the Running DB. + +.. clicmd:: mgmt commit apply + + This command commits any uncommited changes in the Candidate DB to the + Running DB. + +.. clicmd:: mgmt commit check + + This command validates the configuration but does not apply them to the + Running DB. + +.. clicmd:: mgmt rollback commit-id WORD + + This command rolls back the Running Database contents to the state + corresponding to the commit-id specified. + +.. clicmd:: mgmt rollback last WORD + + This command rolls back the last specified number of recent commits. + + +MGMT Show commands +================== + +.. clicmd:: show mgmt backend-adapter all + + This command shows the backend adapter information and the clients/daemons + connected to the adapters. + +.. clicmd:: show mgmt backend-yang-xpath-registry + + This command shows which Backend adapters are registered for which YANG + data subtree(s). + +.. clicmd:: show mgmt frontend-adapter all [detail] + + This command shows the frontend adapter information and the clients + connected to the adapters. + +.. clicmd:: show mgmt transaction all + + Shows the list of transaction and bunch of information about the transaction. + +.. clicmd:: show mgmt get-config [candidate|running] XPATH + + This command uses the GET_CONFIG operation over the MGMT Frontend interface and + returns the xpaths and values of the nodes of the subtree pointed by the <xpath>. + +.. clicmd:: show mgmt get-data [candidate|operation|running] XPATH + + This command uses the GET_DATA operation over the MGMT Frontend interface and + returns the xpaths and values of the nodes of the subtree pointed by the <xpath>. + Currenlty supported values for 'candidate' and 'running' only + ('operational' shall be supported in future soon). + +.. clicmd:: show mgmt database-contents [candidate|operation|running] [xpath WORD] [file WORD] json|xml + + This command dumps the subtree pointed by the xpath in JSON or XML format. If filepath is + not present then the tree will be printed on the shell. + +.. clicmd:: show mgmt commit-history + + This command dumps details of upto last 10 commits handled by MGMTd. + + +MGMT Daemon debug commands +========================== + +The following debug commands enable debugging within the management daemon: + +.. clicmd:: [no] debug mgmt backend + + Enable[/Disable] debugging messages related to backend operations within the + management daemon. + +.. clicmd:: [no] debug mgmt datastore + + Enable[/Disable] debugging messages related to YANG datastore operations + within the management daemon. + +.. clicmd:: [no] debug mgmt frontend + + Enable[/Disable] debugging messages related to frontend operations within the + management daemon. + +.. clicmd:: [no] debug mgmt transaction + + Enable[/Disable] debugging messages related to transactions within the + management daemon. + + +MGMT Client debug commands +========================== + +The following debug commands enable debugging within the management front and +backend clients: + +.. clicmd:: [no] debug mgmt client backend + + Enable[/Disable] debugging messages related to backend operations inside the + backend mgmtd clients. + +.. clicmd:: [no] debug mgmt client frontend + + Enable[/Disable] debugging messages related to frontend operations inside the + frontend mgmtd clients. diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 8dacb9c9dc25..2f4c956ffd90 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -287,6 +287,19 @@ OSPF6 interface Sets interface's Router Dead Interval. Default value is 40. +.. clicmd:: ipv6 ospf6 graceful-restart hello-delay HELLODELAYINTERVAL + + Set the length of time during which Grace-LSAs are sent at 1-second intervals + while coming back up after an unplanned outage. During this time, no hello + packets are sent. + + A higher hello delay will increase the chance that all neighbors are notified + about the ongoing graceful restart before receiving a hello packet (which is + crucial for the graceful restart to succeed). The hello delay shouldn't be set + too high, however, otherwise the adjacencies might time out. As a best practice, + it's recommended to set the hello delay and hello interval with the same values. + The default value is 10 seconds. + .. clicmd:: ipv6 ospf6 retransmit-interval RETRANSMITINTERVAL Sets interface's Rxmt Interval. Default value is 5. @@ -343,6 +356,10 @@ Graceful Restart To perform a graceful shutdown, the "graceful-restart prepare ipv6 ospf" EXEC-level command needs to be issued before restarting the ospf6d daemon. + When Graceful Restart is enabled and the ospf6d daemon crashes or is killed + abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once + it restarts. + .. clicmd:: graceful-restart helper enable [A.B.C.D] diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst index ce3648bf6df1..232b1c3934d5 100644 --- a/doc/user/ospfd.rst +++ b/doc/user/ospfd.rst @@ -310,6 +310,18 @@ To start OSPF process you have to specify the OSPF router. of packets to process before returning. The defult value of this parameter is 20. +.. clicmd:: socket buffer <send | recv | all> (1-4000000000) + + This command controls the ospf instance's socket buffer sizes. The + 'no' form resets one or both values to the default. + +.. clicmd:: no socket-per-interface + + Ordinarily, ospfd uses a socket per interface for sending + packets. This command disables those per-interface sockets, and + causes ospfd to use a single socket per ospf instance for sending + and receiving packets. + .. _ospf-area: Areas @@ -325,7 +337,6 @@ Areas announced to other areas. This command can be used only in ABR and ONLY router-LSAs (Type-1) and network-LSAs (Type-2) (i.e. LSAs with scope area) can be summarized. Type-5 AS-external-LSAs can't be summarized - their scope is AS. - Summarizing Type-7 AS-external-LSAs isn't supported yet by FRR. .. code-block:: frr @@ -431,6 +442,31 @@ Areas configured not to advertise forwarding addresses into the backbone to direct forwarded traffic to the NSSA ABR translator. +.. clicmd:: area A.B.C.D nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)] + +.. clicmd:: area (0-4294967295) nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)] + + NSSA ABRs and ASBRs can be configured with the `default-information-originate` + option to originate a Type-7 default route into the NSSA area. In the case + of NSSA ASBRs, the origination of the default route is conditioned to the + existence of a default route in the RIB that wasn't learned via the OSPF + protocol. + +.. clicmd:: area A.B.C.D nssa range A.B.C.D/M [<not-advertise|cost (0-16777215)>] + +.. clicmd:: area (0-4294967295) nssa range A.B.C.D/M [<not-advertise|cost (0-16777215)>] + + Summarize a group of external subnets into a single Type-7 LSA, which is + then translated to a Type-5 LSA and avertised to the backbone. + This command can only be used at the area boundary (NSSA ABR router). + + By default, the metric of the summary route is calculated as the highest + metric among the summarized routes. The `cost` option, however, can be used + to set an explicit metric. + + The `not-advertise` option, when present, prevents the summary route from + being advertised, effectively filtering the summarized routes. + .. clicmd:: area A.B.C.D default-cost (0-16777215) @@ -599,7 +635,20 @@ Interfaces :clicmd:`ip ospf dead-interval minimal hello-multiplier (2-20)` is also specified for the interface. -.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]) +.. clicmd:: ip ospf graceful-restart hello-delay (1-1800) + + Set the length of time during which Grace-LSAs are sent at 1-second intervals + while coming back up after an unplanned outage. During this time, no hello + packets are sent. + + A higher hello delay will increase the chance that all neighbors are notified + about the ongoing graceful restart before receiving a hello packet (which is + crucial for the graceful restart to succeed). The hello delay shouldn't be set + too high, however, otherwise the adjacencies might time out. As a best practice, + it's recommended to set the hello delay and hello interval with the same values. + The default value is 10 seconds. + +.. clicmd:: ip ospf network (broadcast|non-broadcast|point-to-multipoint [delay-reflood]|point-to-point [dmvpn]) 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 @@ -611,6 +660,13 @@ Interfaces point-to-point, but the HUB will be a point-to-multipoint. To make this topology work, specify the optional 'dmvpn' parameter at the spoke. + When the network is configured as point-to-multipoint and `delay-reflood` + is specified, LSAs received on the interface from neighbors on the + interface will not be flooded back out on the interface immediately. + Rather, they will be added to the neighbor's link state retransmission + list and only sent to the neighbor if the neighbor doesn't acknowledge + the LSA prior to the link state retransmission timer expiring. + Set explicitly network type for specified interface. .. clicmd:: ip ospf priority (0-255) @@ -734,6 +790,10 @@ Graceful Restart To perform a graceful shutdown, the "graceful-restart prepare ip ospf" EXEC-level command needs to be issued before restarting the ospfd daemon. + When Graceful Restart is enabled and the ospfd daemon crashes or is killed + abruptely (e.g. SIGKILL), it will attempt an unplanned Graceful Restart once + it restarts. + .. clicmd:: graceful-restart helper enable [A.B.C.D] @@ -806,25 +866,23 @@ Showing Information Json o/p of this command covers base route information i.e all LSAs except opaque lsa info. -.. clicmd:: show ip ospf [vrf <NAME|all>] database [json] - -.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database [self-originate] [json] -.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) LINK-STATE-ID [json] + Show the OSPF database summary. -.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) LINK-STATE-ID adv-router ADV-ROUTER [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database max-age [json] -.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) adv-router ADV-ROUTER [json] + Show all MaxAge LSAs present in the OSPF link-state database. -.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) LINK-STATE-ID self-originate [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database detail [LINK-STATE-ID] [adv-router A.B.C.D] [json] -.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary) self-originate [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database detail [LINK-STATE-ID] [self-originate] [json] -.. clicmd:: show ip ospf [vrf <NAME|all>] database max-age [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as) [LINK-STATE-ID] [adv-router A.B.C.D] [json] -.. clicmd:: show ip ospf [vrf <NAME|all>] database self-originate [json] +.. clicmd:: show ip ospf [vrf <NAME|all>] database (asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as) [LINK-STATE-ID] [self-originate] [json] - Show the OSPF database summary. + Show detailed information about the OSPF link-state database. .. clicmd:: show ip ospf route [json] @@ -854,7 +912,7 @@ Opaque LSA - *ospfd* supports Opaque LSA (:rfc:`2370`) as partial support for + *ospfd* supports Opaque LSA (:rfc:`5250`) 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 @@ -862,6 +920,18 @@ Opaque LSA extensions that are used with MPLS-TE; it does not support a complete RSVP-TE solution. +.. clicmd:: ip ospf capability opaque [A.B.C.D] + + Enable or disable OSPF LSA database exchange and flooding on an interface. + The default is that opaque capability is enabled as long as the opaque + capability is enabled with the :clicmd:`capability opaque` command at the + OSPF instance level (using the command above). Note that disabling opaque + LSA support on an interface will impact the applications using opaque LSAs + if the opaque LSAs are not received on other flooding paths by all the + OSPF routers using those applications. For example, OSPF Graceful Restart + uses opaque-link LSAs and disabling support on an interface will disable + graceful restart signaling on that interface. + .. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) .. clicmd:: show ip ospf [vrf <NAME|all>] database (opaque-link|opaque-area|opaque-external) LINK-STATE-ID diff --git a/doc/user/overview.rst b/doc/user/overview.rst index 4a24fa52be56..33a19346280b 100644 --- a/doc/user/overview.rst +++ b/doc/user/overview.rst @@ -424,6 +424,8 @@ BGP :t:`Extended Optional Parameters Length for BGP OPEN Message. E. Chen, J. Scudder. July 2021.` - :rfc:`9234` :t:`Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages. A. Azimov, E. Bogomazov, R. Bush, K. Patel, K. Sriram. May 2022.` +- :rfc:`9384` + :t:`A BGP Cease NOTIFICATION Subcode for Bidirectional Forwarding Detection (BFD). J. Haas. March 2023.` OSPF ---- @@ -463,6 +465,8 @@ BFD :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:`5882` + :t:`Generic Application of Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010` - :rfc:`5883` :t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010` diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 72471d7af0e2..d70c3c0e64e9 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -387,9 +387,14 @@ cause great confusion. .. clicmd:: show ip igmp [vrf NAME] join [json] Display IGMP static join information for a specific vrf. - If "vrf all" is provided, it displays information for all the vrfs present. + +.. index:: show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail] [json$json] +.. clicmd:: show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail] [json$json] -.. clicmd:: show ip igmp groups + Display IGMP static join information for all the vrfs present. + +.. index:: show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json] +.. clicmd:: show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json] Display IGMP groups information. diff --git a/doc/user/ripd.rst b/doc/user/ripd.rst index 67323e61f32d..f9c772430221 100644 --- a/doc/user/ripd.rst +++ b/doc/user/ripd.rst @@ -159,6 +159,11 @@ RIP Configuration If `poisoned-reverse` is also set, the router sends the poisoned routes with highest metric back to the sending router. +.. clicmd:: allow-ecmp [1-MULTIPATH_NUM] + + Control how many ECMP paths RIP can inject for the same prefix. If specified + without a number, a maximum is taken (compiled with ``--enable-multipath``). + .. _rip-version-control: RIP Version Control diff --git a/doc/user/ripngd.rst b/doc/user/ripngd.rst index df7a0e249ec7..1e78294f3226 100644 --- a/doc/user/ripngd.rst +++ b/doc/user/ripngd.rst @@ -38,6 +38,10 @@ Currently ripngd supports the following commands: Set RIPng static routing announcement of NETWORK. +.. clicmd:: allow-ecmp [1-MULTIPATH_NUM] + + Control how many ECMP paths RIPng can inject for the same prefix. If specified + without a number, a maximum is taken (compiled with ``--enable-multipath``). .. _ripngd-terminal-mode-commands: @@ -88,6 +92,53 @@ RIPng routes can be filtered by a distribute-list. `distribute-list` can be applied to both incoming and outgoing data. +.. _ripng-route-map: + +RIPng route-map +=============== + +Usage of *ripngd*'s route-map support. + +Route-map statement (:ref:`route-map`) is needed to use route-map +functionality. + +.. clicmd:: match interface WORD + + This command match to incoming interface. Notation of this match is + different from Cisco. Cisco uses a list of interfaces - NAME1 NAME2 ... + NAMEN. Ripngd allows only one name (maybe will change in the future). Next - + Cisco means interface which includes next-hop of routes (it is somewhat + similar to "ipv6 next-hop" statement). Ripngd means interface where this route + will be sent. This difference is because "next-hop" of same routes which + sends to different interfaces must be different. + +.. clicmd:: match ipv6 address WORD + +.. clicmd:: match ipv6 address prefix-list WORD + + Match if route destination is permitted by access-list/prefix-list. + +.. clicmd:: match metric (0-4294967295) + + This command match to the metric value of RIPng updates. For other protocol + compatibility metric range is shown as (0-4294967295). But for RIPng protocol + only the value range (0-16) make sense. + +.. clicmd:: set ipv6 next-hop local IPV6_ADDRESS + + Set the link-local IPv6 nexthop address. + +.. clicmd:: set metric (1-16) + + Set a metric for matched route when sending announcement. The metric value + range is very large for compatibility with other protocols. For RIPng, valid + metric values are from 1 to 16. + +.. clicmd:: set tag (1-4294967295) + + Set a tag on the matched route. + + Sample configuration ==================== diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index c205122b0b98..bd19ae88e230 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -212,7 +212,7 @@ Route Map Match Command .. clicmd:: match source-protocol PROTOCOL_NAME - This is a ZEBRA specific match command. Matches the + This is a ZEBRA and BGP specific match command. Matches the originating protocol specified. .. clicmd:: match source-instance NUMBER @@ -294,9 +294,9 @@ Route Map Set Command Subtract the BGP local preference from an existing `local_pref`. -.. clicmd:: set distance DISTANCE +.. clicmd:: set distance (1-255) - Set the Administrative distance to DISTANCE to use for the route. + Set the Administrative distance to use for the route. This is only locally significant and will not be dispersed to peers. .. clicmd:: set weight WEIGHT @@ -305,10 +305,18 @@ Route Map Set Command .. clicmd:: set metric <[+|-](1-4294967295)|rtt|+rtt|-rtt> - 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. + Set the route metric. When used with BGP, set the BGP attribute MED to a + specific value. Use `+`/`-` to add or subtract the specified value to/from + the existing/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. + +.. clicmd:: set min-metric <(0-4294967295)> + + Set the minimum meric for the route. + +.. clicmd:: set max-metric <(0-4294967295)> + + Set the maximum meric for the route. .. clicmd:: set aigp-metric <igp-metric|(1-4294967295)> diff --git a/doc/user/static.rst b/doc/user/static.rst index 05847ba39409..6d8aca97bbab 100644 --- a/doc/user/static.rst +++ b/doc/user/static.rst @@ -90,7 +90,7 @@ a static prefix and gateway, with several possible forms. Multiple nexthop static route ============================= -To create multiple nexthops to the same NETWORK, just reenter the same +To create multiple nexthops to the same NETWORK (also known as a multipath route), just reenter the same network statement with different nexthop information. .. code-block:: frr @@ -122,7 +122,7 @@ nexthops, if the platform supports this. ip route 10.0.0.0/8 null0 255 -This will install a multihop route via the specified next-hops if they are +This will install a multipath route via the specified next-hops if they are reachable, as well as a high-distance blackhole route, which can be useful to prevent traffic destined for a prefix to match less-specific routes (e.g. default) should the specified gateways not be reachable. E.g.: diff --git a/doc/user/subdir.am b/doc/user/subdir.am index b8c5c70d9c12..4879f7f7ef3c 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -54,6 +54,7 @@ user_RSTFILES = \ doc/user/flowspec.rst \ doc/user/watchfrr.rst \ doc/user/wecmp_linkbw.rst \ + doc/user/mgmtd.rst \ # end EXTRA_DIST += \ diff --git a/docker/alpine/Dockerfile b/docker/alpine/Dockerfile index afc6be2312a1..54621a49fdb0 100644 --- a/docker/alpine/Dockerfile +++ b/docker/alpine/Dockerfile @@ -25,6 +25,7 @@ RUN source /src/alpine/APKBUILD.in \ gzip \ py-pip \ rtrlib \ + protobuf-c-dev \ && pip install pytest RUN mkdir -p /pkgs/apk COPY . /src diff --git a/docker/centos-7/Dockerfile b/docker/centos-7/Dockerfile index 2d1ee9efa498..8739cee09baf 100644 --- a/docker/centos-7/Dockerfile +++ b/docker/centos-7/Dockerfile @@ -5,6 +5,7 @@ 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 libcap-devel systemd-devel \ + protobuf-c-devel \ https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \ https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-7-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el7.x86_64.rpm \ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \ diff --git a/docker/centos-8/Dockerfile b/docker/centos-8/Dockerfile index df772b83184e..88a7d6a0076c 100644 --- a/docker/centos-8/Dockerfile +++ b/docker/centos-8/Dockerfile @@ -9,6 +9,7 @@ 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 libcap-devel platform-python-devel \ + protobuf-c-devel \ https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \ diff --git a/docker/debian/Dockerfile b/docker/debian/Dockerfile index 7476e5fe3e7b..d136538c7d08 100644 --- a/docker/debian/Dockerfile +++ b/docker/debian/Dockerfile @@ -7,7 +7,7 @@ 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 \ - tini && rm -rf /var/lib/apt/lists/* + libprotobuf-c-dev protobuf-c-compiler tini && 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 diff --git a/docker/ubi-8/Dockerfile b/docker/ubi-8/Dockerfile deleted file mode 100644 index 1d1e8bdc6e23..000000000000 --- a/docker/ubi-8/Dockerfile +++ /dev/null @@ -1,83 +0,0 @@ -# This stage builds an rpm from the source -FROM registry.access.redhat.com/ubi8/ubi:8.5 as ubi-8-builder - -RUN dnf -y update-minimal --security --sec-severity=Important --sec-severity=Critical - -RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/PowerTools/x86_64/os - -RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ - && dnf install --enablerepo=* -qy rpm-build git autoconf pcre-devel \ - systemd-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 \ - libcap-devel platform-python-devel \ - https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ - https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-devel-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ - https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm \ - https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-devel-0.8.0-1.el7.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-snmp=agentx \ - --enable-numeric-version \ - --with-pkg-extra-version="_palmetto_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 registry.access.redhat.com/ubi8/ubi:8.5 -RUN dnf -y update-minimal --security --sec-severity=Important --sec-severity=Critical -ARG FRR_IMAGE_TAG -ARG FRR_RELEASE -ARG FRR_NAME -ARG FRR_VENDOR -LABEL name=$FRR_NAME \ - vendor=$FRR_VENDOR \ - version=$FRR_IMAGE_TAG \ - release=$FRR_RELEASE - -RUN rpm --import https://www.centos.org/keys/RPM-GPG-KEY-CentOS-Official \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/BaseOS/x86_64/os \ - && dnf config-manager --disableplugin subscription-manager --add-repo http://mirror.centos.org/centos/8-stream/AppStream/x86_64/os - -RUN dnf install -qy https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ - && mkdir -p /pkgs/rpm \ - && dnf install --enablerepo=* -qy https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-2/CentOS-8-x86_64-Packages/libyang2-2.0.0.10.g2eb910e4-1.el8.x86_64.rpm \ - https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-00146/CentOS-7-x86_64-Packages/librtr-0.8.0-1.el7.x86_64.rpm - -COPY --from=ubi-8-builder /rpmbuild/RPMS/ /pkgs/rpm/ - -RUN dnf install -qy /pkgs/rpm/*/*.rpm \ - && rm -rf /pkgs \ -# Own the config / PID files - && mkdir -p /var/run/frr \ - && chown -R frr:frr /etc/frr /var/run/frr - -# Add tini because no CentOS8 package -ENV TINI_VERSION v0.19.0 -ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini -RUN chmod +x /sbin/tini - -# Simple init manager for reaping processes and forwarding signals -ENTRYPOINT ["/sbin/tini", "--"] - -# Default CMD starts watchfrr -COPY docker/ubi-8/docker-start /usr/lib/frr/docker-start -CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/ubi8-minimal/Dockerfile b/docker/ubi8-minimal/Dockerfile new file mode 100644 index 000000000000..adb04219be7a --- /dev/null +++ b/docker/ubi8-minimal/Dockerfile @@ -0,0 +1,132 @@ +# This stage builds an rpm from the source +ARG UBI8_MINIMAL_VERSION +FROM registry.access.redhat.com/ubi8/ubi-minimal:${UBI8_MINIMAL_VERSION} as ubi8-minimal-builder + +RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8 + +ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo + +# ubi8-minimal comes with broken tzdata package installed, so we need to remove them +# and later reinstall it again: https://bugzilla.redhat.com/show_bug.cgi?id=1668185 +RUN rpm --quiet -e --nodeps tzdata >/dev/null 2>&1 + +RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \ + autoconf \ + automake \ + bison \ + c-ares-devel \ + flex \ + git \ + groff \ + json-c-devel \ + libcap-devel \ + libssh-devel \ + libtool \ + make \ + net-snmp-devel \ + openssl \ + pam-devel \ + pcre-devel \ + pkgconfig \ + platform-python-devel \ + python3-devel \ + python3-pytest \ + python3-sphinx \ + readline-devel \ + rpm-build \ + systemd-devel \ + texinfo \ + tzdata \ + && microdnf --disableplugin=subscription-manager clean all + +RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \ + && rpm -i /tmp/libyang2.rpm \ + && rm -f /tmp/libyang2.rpm + +RUN curl -sSL -o /tmp/libyang2-devel.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-devel-2.0.7-1.el8.x86_64.rpm \ + && rpm -i /tmp/libyang2-devel.rpm \ + && rm -f /tmp/libyang2-devel.rpm + +RUN curl -sSL -o /tmp/librtr.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-0.8.0-1.el8.x86_64.rpm \ + && rpm -i /tmp/librtr.rpm \ + && rm -f /tmp/librtr.rpm + +RUN curl -sSL -o /tmp/librtr-devel.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-devel-0.8.0-1.el8.x86_64.rpm \ + && rpm -i /tmp/librtr-devel.rpm \ + && rm -f /tmp/librtr-devel.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-snmp=agentx \ + --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 registry.access.redhat.com/ubi8/ubi-minimal:${UBI8_MINIMAL_VERSION} +ARG FRR_IMAGE_TAG +ARG FRR_RELEASE +ARG FRR_NAME +ARG FRR_VENDOR +LABEL name=$FRR_NAME \ + vendor=$FRR_VENDOR \ + version=$FRR_IMAGE_TAG \ + release=$FRR_RELEASE + +ADD docker/ubi8-minimal/almalinux.repo /etc/yum.repos.d/almalinux.repo + +RUN rpm --import https://repo.almalinux.org/almalinux/RPM-GPG-KEY-AlmaLinux-8 + +RUN microdnf --disableplugin=subscription-manager --setopt=install_weak_deps=0 install \ + c-ares \ + initscripts \ + net-snmp-agent-libs \ + net-snmp-libs \ + openssl \ + python3 \ + shadow-utils \ + systemd \ + && microdnf --disableplugin=subscription-manager clean all + +RUN curl -sSL -o /tmp/libyang2.rpm https://ci1.netdef.org/artifact/LIBYANG-LIBYANGV2/shared/build-12/RedHat-8-x86_64-Packages/libyang2-2.0.7-1.el8.x86_64.rpm \ + && rpm -i /tmp/libyang2.rpm \ + && rm -f /tmp/libyang2.rpm + +RUN curl -sSL -o /tmp/librtr.rpm https://ci1.netdef.org/artifact/RPKI-RTRLIB/shared/build-149/RedHat-8-x86_64-Packages/librtr-0.8.0-1.el8.x86_64.rpm \ + && rpm -i /tmp/librtr.rpm \ + && rm -f /tmp/librtr.rpm + +COPY --from=ubi8-minimal-builder /rpmbuild/RPMS/ /pkgs/rpm/ + +# Install packages and create FRR files and folders. Be sure to own the config / PID files +RUN rpm -i /pkgs/rpm/x86_64/*.rpm \ + && rm -rf /pkgs \ + && rm -rf /mnt/rootfs/var/cache/* /mnt/rootfs/var/log/dnf* /mnt/rootfs/var/log/yum.* \ + && mkdir -p /var/run/frr \ + && chown -R frr:frr /etc/frr /var/run/frr + +# There is no package for tini, add it manually +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini +RUN chmod +x /sbin/tini + +# Simple init manager for reaping processes and forwarding signals +ENTRYPOINT ["/sbin/tini", "--"] + +# Default CMD starts watchfrr +COPY docker/ubi8-minimal/docker-start /usr/lib/frr/docker-start +CMD ["/usr/lib/frr/docker-start"] diff --git a/docker/ubi8-minimal/almalinux.repo b/docker/ubi8-minimal/almalinux.repo new file mode 100644 index 000000000000..9b9877b18058 --- /dev/null +++ b/docker/ubi8-minimal/almalinux.repo @@ -0,0 +1,23 @@ +[AlmaLinux - baseos] +name=AlmaLinux $releasever - BaseOS +mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/baseos +# baseurl=https://repo.almalinux.org/almalinux/$releasever/BaseOS/$basearch/os/ +enabled=1 +gpgcheck=1 +countme=1 + +[AlmaLinux - appstream] +name=AlmaLinux $releasever - AppStream +mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/appstream +# baseurl=https://repo.almalinux.org/almalinux/$releasever/AppStream/$basearch/os/ +enabled=1 +gpgcheck=1 +countme=1 + +[AlmaLinux - powertools] +name=AlmaLinux $releasever - PowerTools +mirrorlist=https://mirrors.almalinux.org/mirrorlist/$releasever/powertools +# baseurl=https://repo.almalinux.org/almalinux/$releasever/PowerTools/$basearch/os/ +enabled=1 +gpgcheck=1 +countme=1 diff --git a/docker/ubi-8/build.sh b/docker/ubi8-minimal/build.sh similarity index 64% rename from docker/ubi-8/build.sh rename to docker/ubi8-minimal/build.sh index 0216636893af..2aa45c9bf51f 100755 --- a/docker/ubi-8/build.sh +++ b/docker/ubi8-minimal/build.sh @@ -5,37 +5,45 @@ set -e ## # Package version needs to be decimal ## -DISTRO=ubi-8 +DISTRO=ubi8-minimal + +UBI8_MINIMAL_VERSION=$1 +if [ -z "$UBI8_MINIMAL_VERSION" ]; then + UBI8_MINIMAL_VERSION="latest" +fi GITREV="$2" if [ -z "$GITREV" ];then GITREV="$(git rev-parse --short=10 HEAD)" fi -FRR_IMAGE_TAG="$1" +FRR_IMAGE_TAG="$3" if [ -z $FRR_IMAGE_TAG ];then - FRR_IMAGE_TAG="frr:ubi-8-$GITREV" + FRR_IMAGE_TAG="frr:ubi8-minimal-$GITREV" fi PKGVER="$(printf '%u\n' 0x$GITREV)" -FRR_RELEASE="$3" +FRR_RELEASE="$4" if [ -z $FRR_RELEASE ];then FRR_RELEASE=$(git describe --tags --abbrev=0) fi -FRR_NAME=$4 +FRR_NAME=$5 if [ -z $FRR_NAME ];then FRR_NAME=frr fi -FRR_VENDOR=$5 +FRR_VENDOR=$6 if [ -z $FRR_VENDOR ];then FRR_VENDOR=frr fi +DOCKERFILE_PATH="$(dirname $(realpath $0))/Dockerfile" + docker build \ --cache-from="frr:$DISTRO-builder-$GITREV" \ - --file=docker/$DISTRO/Dockerfile \ + --file="$DOCKERFILE_PATH" \ + --build-arg="UBI8_MINIMAL_VERSION=$UBI8_MINIMAL_VERSION" \ --build-arg="PKGVER=$PKGVER" \ --build-arg="FRR_IMAGE_TAG=$FRR_IMAGE_TAG" \ --build-arg="FRR_RELEASE=$FRR_RELEASE" \ diff --git a/docker/ubi-8/docker-start b/docker/ubi8-minimal/docker-start similarity index 100% rename from docker/ubi-8/docker-start rename to docker/ubi8-minimal/docker-start diff --git a/docker/ubuntu-ci/Dockerfile b/docker/ubuntu-ci/Dockerfile new file mode 100644 index 000000000000..939a43e4ea85 --- /dev/null +++ b/docker/ubuntu-ci/Dockerfile @@ -0,0 +1,127 @@ +ARG UBUNTU_VERSION=22.04 +FROM ubuntu:$UBUNTU_VERSION + +ARG DEBIAN_FRONTEND=noninteractive +ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn + +# Update and install build requirements. +RUN apt update && apt upgrade -y && \ + # Basic build requirements from documentation + apt-get install -y \ + autoconf \ + automake \ + bison \ + build-essential \ + flex \ + git \ + install-info \ + libc-ares-dev \ + libcap-dev \ + libelf-dev \ + libjson-c-dev \ + libpam0g-dev \ + libreadline-dev \ + libsnmp-dev \ + libsqlite3-dev \ + libtool \ + make \ + perl \ + pkg-config \ + python3-dev \ + python3-sphinx \ + texinfo \ + && \ + # Protobuf build requirements + apt-get install -y \ + libprotobuf-c-dev \ + protobuf-c-compiler \ + && \ + # Libyang2 extra build requirements + apt-get install -y \ + cmake \ + libpcre2-dev \ + && \ + # Runtime/triage/testing requirements + apt-get install -y \ + curl \ + gdb \ + iproute2 \ + iputils-ping \ + liblua5.3-dev \ + libssl-dev \ + lua5.3 \ + net-tools \ + python2 \ + python3-pip \ + snmp \ + snmp-mibs-downloader \ + snmpd \ + sudo \ + time \ + tshark \ + valgrind \ + yodl \ + && \ + download-mibs && \ + wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/iana/IANA-IPPM-METRICS-REGISTRY-MIB -O /usr/share/snmp/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB && \ + wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/SNMPv2-PDU -O /usr/share/snmp/mibs/ietf/SNMPv2-PDU && \ + wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/IPATM-IPMC-MIB -O /usr/share/snmp/mibs/ietf/IPATM-IPMC-MIB && \ + curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \ + python2 /tmp/get-pip.py && \ + rm -f /tmp/get-pip.py && \ + python3 -m pip install wheel && \ + python3 -m pip install pytest && \ + python3 -m pip install pytest-sugar && \ + python3 -m pip install pytest-xdist && \ + python3 -m pip install "scapy>=2.4.2" && \ + python3 -m pip install xmltodict && \ + python3 -m pip install grpcio grpcio-tools && \ + python2 -m pip install 'exabgp<4.0.0' + +RUN groupadd -r -g 92 frr && \ + groupadd -r -g 85 frrvty && \ + adduser --system --ingroup frr --home /home/frr \ + --gecos "FRR suite" --shell /bin/bash frr && \ + usermod -a -G frrvty frr && \ + useradd -d /var/run/exabgp/ -s /bin/false exabgp && \ + echo 'frr ALL = NOPASSWD: ALL' | tee /etc/sudoers.d/frr && \ + mkdir -p /home/frr && chown frr.frr /home/frr + +USER frr:frr + +# build and install libyang2 +RUN cd && pwd && ls -al && \ + git clone https://github.com/CESNET/libyang.git && \ + cd libyang && \ + git checkout v2.1.80 && \ + mkdir build; cd build && \ + cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ + -DCMAKE_BUILD_TYPE:String="Release" .. && \ + make -j $(nproc) && \ + sudo make install + +COPY --chown=frr:frr . /home/frr/frr/ + +RUN cd ~/frr && \ + ./bootstrap.sh && \ + ./configure \ + --prefix=/usr \ + --localstatedir=/var/run/frr \ + --sbindir=/usr/lib/frr \ + --sysconfdir=/etc/frr \ + --enable-sharpd \ + --enable-multipath=64 \ + --enable-user=frr \ + --enable-group=frr \ + --enable-config-rollbacks \ + --enable-vty-group=frrvty \ + --enable-snmp=agentx \ + --enable-scripting \ + --with-pkg-extra-version=-my-manual-build && \ + make -j $(nproc) && \ + sudo make install + +RUN cd ~/frr && make check || true + +COPY docker/ubuntu-ci/docker-start /usr/sbin/docker-start +CMD ["/usr/sbin/docker-start"] diff --git a/docker/ubuntu18-ci/docker-start b/docker/ubuntu-ci/docker-start similarity index 100% rename from docker/ubuntu18-ci/docker-start rename to docker/ubuntu-ci/docker-start diff --git a/docker/ubuntu18-ci/Dockerfile b/docker/ubuntu18-ci/Dockerfile deleted file mode 100644 index 07a5a2f7e0a5..000000000000 --- a/docker/ubuntu18-ci/Dockerfile +++ /dev/null @@ -1,73 +0,0 @@ -FROM ubuntu:18.04 - -ARG DEBIAN_FRONTEND=noninteractive -ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn -# Update Ubuntu Software repository -RUN apt update && \ - apt-get install -y \ - git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python3-pip \ - libc-ares-dev python3-dev python3-sphinx \ - install-info build-essential libsnmp-dev perl libcap-dev \ - libelf-dev \ - sudo gdb iputils-ping time \ - python-pip net-tools iproute2 && \ - python3 -m pip install wheel && \ - python3 -m pip install pytest && \ - python3 -m pip install pytest-xdist && \ - python3 -m pip install "scapy>=2.4.2" && \ - python3 -m pip install xmltodict && \ - python2 -m pip install 'exabgp<4.0.0' - -RUN groupadd -r -g 92 frr && \ - groupadd -r -g 85 frrvty && \ - adduser --system --ingroup frr --home /home/frr \ - --gecos "FRR suite" --shell /bin/bash frr && \ - usermod -a -G frrvty frr && \ - useradd -d /var/run/exabgp/ -s /bin/false exabgp && \ - echo 'frr ALL = NOPASSWD: ALL' | tee /etc/sudoers.d/frr && \ - mkdir -p /home/frr && chown frr.frr /home/frr - -#for libyang 2 -RUN apt-get install -y cmake libpcre2-dev - -USER frr:frr - -# build and install libyang2 -RUN cd && pwd && ls -al && \ - git clone https://github.com/CESNET/libyang.git && \ - cd libyang && \ - git checkout v2.0.0 && \ - mkdir build; cd build && \ - cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ - -DCMAKE_BUILD_TYPE:String="Release" .. && \ - make -j $(nproc) && \ - sudo make install - -COPY --chown=frr:frr . /home/frr/frr/ - -RUN cd && ls -al && ls -al frr - -RUN cd ~/frr && \ - ./bootstrap.sh && \ - ./configure \ - --prefix=/usr \ - --localstatedir=/var/run/frr \ - --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ - --enable-vtysh \ - --enable-pimd \ - --enable-sharpd \ - --enable-multipath=64 \ - --enable-user=frr \ - --enable-group=frr \ - --enable-vty-group=frrvty \ - --enable-snmp=agentx \ - --with-pkg-extra-version=-my-manual-build && \ - make -j $(nproc) && \ - sudo make install - -RUN cd ~/frr && make check || true - -COPY docker/ubuntu18-ci/docker-start /usr/sbin/docker-start -CMD ["/usr/sbin/docker-start"] diff --git a/docker/ubuntu18-ci/README.md b/docker/ubuntu18-ci/README.md deleted file mode 100644 index 4e8ab891e684..000000000000 --- a/docker/ubuntu18-ci/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Ubuntu 18.04 - -This builds an ubuntu 18.04 container for dev / test - -# Build - -``` -docker build -t frr-ubuntu18:latest -f docker/ubuntu18-ci/Dockerfile . -``` - -# Running - -``` -docker run -d --privileged --name frr-ubuntu18 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu18:latest -``` - -# make check - -``` -docker exec frr-ubuntu18 bash -c 'cd ~/frr ; make check' -``` - -# interactive bash -``` -docker exec -it frr-ubuntu18 bash -``` - -# topotest -- when Host O/S is Ubuntu only - -``` -docker exec frr-ubuntu18 bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py' -``` - -# stop & remove container - -``` -docker stop frr-ubuntu18 ; docker rm frr-ubuntu18 -``` - -# remove image - -``` -docker rmi frr-ubuntu18:latest -``` diff --git a/docker/ubuntu20-ci/Dockerfile b/docker/ubuntu20-ci/Dockerfile deleted file mode 100644 index b9fe385c3a9e..000000000000 --- a/docker/ubuntu20-ci/Dockerfile +++ /dev/null @@ -1,78 +0,0 @@ -FROM ubuntu:20.04 - -ARG DEBIAN_FRONTEND=noninteractive -ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn -# Update Ubuntu Software repository -RUN apt update && \ - apt-get install -y \ - git autoconf automake libtool make libreadline-dev texinfo \ - pkg-config libpam0g-dev libjson-c-dev bison flex python3-pip \ - libc-ares-dev python3-dev python3-sphinx \ - install-info build-essential libsnmp-dev perl \ - libcap-dev python2 libelf-dev \ - sudo gdb curl iputils-ping time \ - lua5.3 liblua5.3-dev \ - net-tools iproute2 && \ - curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output /tmp/get-pip.py && \ - python2 /tmp/get-pip.py && \ - rm -f /tmp/get-pip.py && \ - python3 -m pip install wheel && \ - python3 -m pip install pytest && \ - python3 -m pip install pytest-xdist && \ - python3 -m pip install "scapy>=2.4.2" && \ - python3 -m pip install xmltodict && \ - python2 -m pip install 'exabgp<4.0.0' - -RUN groupadd -r -g 92 frr && \ - groupadd -r -g 85 frrvty && \ - adduser --system --ingroup frr --home /home/frr \ - --gecos "FRR suite" --shell /bin/bash frr && \ - usermod -a -G frrvty frr && \ - useradd -d /var/run/exabgp/ -s /bin/false exabgp && \ - echo 'frr ALL = NOPASSWD: ALL' | tee /etc/sudoers.d/frr && \ - mkdir -p /home/frr && chown frr.frr /home/frr - -#for libyang 2 -RUN apt-get install -y cmake libpcre2-dev - -USER frr:frr - -# build and install libyang2 -RUN cd && pwd && ls -al && \ - git clone https://github.com/CESNET/libyang.git && \ - cd libyang && \ - git checkout v2.0.0 && \ - mkdir build; cd build && \ - cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr \ - -DCMAKE_BUILD_TYPE:String="Release" .. && \ - make -j $(nproc) && \ - sudo make install - -COPY --chown=frr:frr . /home/frr/frr/ - -RUN cd && ls -al && ls -al frr - -RUN cd ~/frr && \ - ./bootstrap.sh && \ - ./configure \ - --prefix=/usr \ - --localstatedir=/var/run/frr \ - --sbindir=/usr/lib/frr \ - --sysconfdir=/etc/frr \ - --enable-vtysh \ - --enable-pimd \ - --enable-sharpd \ - --enable-multipath=64 \ - --enable-user=frr \ - --enable-group=frr \ - --enable-vty-group=frrvty \ - --enable-snmp=agentx \ - --enable-scripting \ - --with-pkg-extra-version=-my-manual-build && \ - make -j $(nproc) && \ - sudo make install - -RUN cd ~/frr && make check || true - -COPY docker/ubuntu20-ci/docker-start /usr/sbin/docker-start -CMD ["/usr/sbin/docker-start"] diff --git a/docker/ubuntu20-ci/README.md b/docker/ubuntu20-ci/README.md index 11138c6507b6..536f8e2e3590 100644 --- a/docker/ubuntu20-ci/README.md +++ b/docker/ubuntu20-ci/README.md @@ -5,13 +5,25 @@ This builds an ubuntu 20.04 container for dev / test # Build ``` -docker build -t frr-ubuntu20:latest -f docker/ubuntu20-ci/Dockerfile . +docker build -t frr-ubuntu20:latest --build-arg=UBUNTU_VERSION=20.04 -f docker/ubuntu-ci/Dockerfile . +``` + +# Running Full Topotest + +``` +docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' +``` + +# Extract results from the above run into `run-results` dir and analyze + +``` +tests/topotest/analyze.py -C frr -Ar run-results ``` # Running ``` -docker run -d --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest +docker run -d --init --privileged --name frr-ubuntu20 --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu20:latest ``` # make check diff --git a/docker/ubuntu20-ci/docker-start b/docker/ubuntu20-ci/docker-start deleted file mode 100755 index 9a45c722f1b8..000000000000 --- a/docker/ubuntu20-ci/docker-start +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -if [ $(uname -a | grep -ci Ubuntu) -ge 1 ]; then - #for topotests under ubuntu host - sudo modprobe mpls-router mpls-iptunnel - sudo /etc/init.d/openvswitch-switch start -fi -while true ; do sleep 365d ; done diff --git a/docker/ubuntu22-ci/README.md b/docker/ubuntu22-ci/README.md new file mode 100644 index 000000000000..403abbf5bbe0 --- /dev/null +++ b/docker/ubuntu22-ci/README.md @@ -0,0 +1,57 @@ +# Ubuntu 22.04 + +This builds an ubuntu 22.04 container for dev / test + +# Build + +``` +docker build -t frr-ubuntu22:latest -f docker/ubuntu-ci/Dockerfile . +``` + +# Running Full Topotest + +``` +docker run --init -it --privileged --name frr -v /lib/modules:/lib/modules frr-ubuntu22:latest bash -c 'cd ~/frr/tests/topotests ; sudo pytest -nauto --dist=loadfile' +``` + +# Extract results from the above run into `run-results` dir and analyze + +``` +tests/topotest/analyze.py -C frr -Ar run-results +``` + +# Running + +``` +docker run -d --init --privileged --name frr --mount type=bind,source=/lib/modules,target=/lib/modules frr-ubuntu22:latest +``` + +# make check + +``` +docker exec frr bash -c 'cd ~/frr ; make check' +``` + +# interactive bash + +``` +docker exec -it frr bash +``` + +# topotest -- when Host O/S is Ubuntu only + +``` +docker exec frr bash -c 'cd ~/frr/tests/topotests/ospf-topo1 ; sudo pytest test_ospf_topo1.py' +``` + +# stop & remove container + +``` +docker stop frr ; docker rm frr +``` + +# remove image + +``` +docker rmi frr-ubuntu22:latest +``` diff --git a/eigrpd/eigrp_dump.c b/eigrpd/eigrp_dump.c index 489b3bd1c537..2ebabd0efa46 100644 --- a/eigrpd/eigrp_dump.c +++ b/eigrpd/eigrp_dump.c @@ -13,7 +13,7 @@ #include <zebra.h> #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "command.h" #include "stream.h" @@ -192,7 +192,7 @@ void show_ip_eigrp_neighbor_sub(struct vty *vty, struct eigrp_neighbor *nbr, vty_out(vty, "%-3u %-17pI4 %-21s", 0, &nbr->src, IF_NAME(nbr->ei)); if (nbr->t_holddown) vty_out(vty, "%-7lu", - thread_timer_remain_second(nbr->t_holddown)); + event_timer_remain_second(nbr->t_holddown)); else vty_out(vty, "- "); vty_out(vty, "%-8u %-6u %-5u", 0, 0, EIGRP_PACKET_RETRANS_TIME); diff --git a/eigrpd/eigrp_filter.c b/eigrpd/eigrp_filter.c index 09ae6be6dc7d..eceef6b8a793 100644 --- a/eigrpd/eigrp_filter.c +++ b/eigrpd/eigrp_filter.c @@ -21,7 +21,7 @@ #include "command.h" #include "prefix.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "log.h" #include "stream.h" @@ -111,11 +111,11 @@ void eigrp_distribute_update(struct distribute_ctx *ctx, // TODO: check Graceful restart after 10sec /* cancel GR scheduled */ - thread_cancel(&(e->t_distribute)); + event_cancel(&(e->t_distribute)); /* schedule Graceful restart for whole process in 10sec */ - thread_add_timer(master, eigrp_distribute_timer_process, e, - (10), &e->t_distribute); + event_add_timer(master, eigrp_distribute_timer_process, e, (10), + &e->t_distribute); return; } @@ -186,10 +186,10 @@ void eigrp_distribute_update(struct distribute_ctx *ctx, // TODO: check Graceful restart after 10sec /* Cancel GR scheduled */ - thread_cancel(&(ei->t_distribute)); + event_cancel(&(ei->t_distribute)); /* schedule Graceful restart for interface in 10sec */ - thread_add_timer(master, eigrp_distribute_timer_interface, ei, 10, - &ei->t_distribute); + event_add_timer(master, eigrp_distribute_timer_interface, ei, 10, + &ei->t_distribute); } /* @@ -242,11 +242,11 @@ void eigrp_distribute_update_all_wrapper(struct access_list *notused) * Called when 10sec waiting time expire and * executes Graceful restart for whole process */ -void eigrp_distribute_timer_process(struct thread *thread) +void eigrp_distribute_timer_process(struct event *thread) { struct eigrp *eigrp; - eigrp = THREAD_ARG(thread); + eigrp = EVENT_ARG(thread); /* execute GR for whole process */ eigrp_update_send_process_GR(eigrp, EIGRP_GR_FILTER, NULL); @@ -263,11 +263,11 @@ void eigrp_distribute_timer_process(struct thread *thread) * Called when 10sec waiting time expire and * executes Graceful restart for interface */ -void eigrp_distribute_timer_interface(struct thread *thread) +void eigrp_distribute_timer_interface(struct event *thread) { struct eigrp_interface *ei; - ei = THREAD_ARG(thread); + ei = EVENT_ARG(thread); ei->t_distribute = NULL; /* execute GR for interface */ diff --git a/eigrpd/eigrp_filter.h b/eigrpd/eigrp_filter.h index 493ba08f7d7e..dc8af8f53538 100644 --- a/eigrpd/eigrp_filter.h +++ b/eigrpd/eigrp_filter.h @@ -23,7 +23,7 @@ extern void eigrp_distribute_update(struct distribute_ctx *ctx, 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 void eigrp_distribute_timer_process(struct thread *thread); -extern void eigrp_distribute_timer_interface(struct thread *thread); +extern void eigrp_distribute_timer_process(struct event *thread); +extern void eigrp_distribute_timer_interface(struct event *thread); #endif /* EIGRPD_EIGRP_FILTER_H_ */ diff --git a/eigrpd/eigrp_fsm.c b/eigrpd/eigrp_fsm.c index d065af0cb919..6d8061e57232 100644 --- a/eigrpd/eigrp_fsm.c +++ b/eigrpd/eigrp_fsm.c @@ -53,8 +53,8 @@ */ #include <zebra.h> -#include <thread.h> +#include "frrevent.h" #include "prefix.h" #include "table.h" #include "memory.h" diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c index f62f54b68067..662c750e9639 100644 --- a/eigrpd/eigrp_hello.c +++ b/eigrpd/eigrp_hello.c @@ -16,7 +16,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" @@ -66,11 +66,11 @@ static const struct message eigrp_general_tlv_type_str[] = { * Sends hello packet via multicast for all interfaces eigrp * is configured for */ -void eigrp_hello_timer(struct thread *thread) +void eigrp_hello_timer(struct event *thread) { struct eigrp_interface *ei; - ei = THREAD_ARG(thread); + ei = EVENT_ARG(thread); if (IS_DEBUG_EIGRP(0, TIMERS)) zlog_debug("Start Hello Timer (%s) Expire [%u]", IF_NAME(ei), @@ -80,8 +80,8 @@ void eigrp_hello_timer(struct thread *thread) eigrp_hello_send(ei, EIGRP_HELLO_NORMAL, NULL); /* Hello timer set. */ - thread_add_timer(master, eigrp_hello_timer, ei, ei->params.v_hello, - &ei->t_hello); + event_add_timer(master, eigrp_hello_timer, ei, ei->params.v_hello, + &ei->t_hello); } /** @@ -726,8 +726,8 @@ void eigrp_hello_send_ack(struct eigrp_neighbor *nbr) listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); nbr->ei->on_write_q = 1; } - thread_add_write(master, eigrp_write, nbr->ei->eigrp, - nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); } } @@ -771,12 +771,12 @@ void eigrp_hello_send(struct eigrp_interface *ei, uint8_t flags, if (ei->eigrp->t_write == NULL) { if (flags & EIGRP_HELLO_GRACEFUL_SHUTDOWN) { - thread_execute(master, eigrp_write, ei->eigrp, - ei->eigrp->fd); + event_execute(master, eigrp_write, ei->eigrp, + ei->eigrp->fd); } else { - thread_add_write(master, eigrp_write, ei->eigrp, - ei->eigrp->fd, - &ei->eigrp->t_write); + event_add_write(master, eigrp_write, ei->eigrp, + ei->eigrp->fd, + &ei->eigrp->t_write); } } } diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index 8d98dcf5a18f..2381977dbaef 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -16,7 +16,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "prefix.h" #include "if.h" @@ -251,7 +251,7 @@ int eigrp_if_up(struct eigrp_interface *ei) /* Set multicast memberships appropriately for new state. */ eigrp_if_set_multicast(ei); - thread_add_event(master, eigrp_hello_timer, ei, (1), &ei->t_hello); + event_add_event(master, eigrp_hello_timer, ei, (1), &ei->t_hello); /*Prepare metrics*/ metric.bandwidth = eigrp_bandwidth_to_scaled(ei->params.bandwidth); @@ -333,7 +333,7 @@ int eigrp_if_down(struct eigrp_interface *ei) return 0; /* Shutdown packet reception and sending */ - THREAD_OFF(ei->t_hello); + EVENT_OFF(ei->t_hello); eigrp_if_stream_unset(ei); @@ -360,7 +360,7 @@ void eigrp_if_stream_unset(struct eigrp_interface *ei) if (ei->on_write_q) { listnode_delete(eigrp->oi_write_q, ei); if (list_isempty(eigrp->oi_write_q)) - thread_cancel(&(eigrp->t_write)); + event_cancel(&(eigrp->t_write)); ei->on_write_q = 0; } } @@ -422,7 +422,7 @@ void eigrp_if_free(struct eigrp_interface *ei, int source) struct eigrp *eigrp = ei->eigrp; if (source == INTERFACE_DOWN_BY_VTY) { - thread_cancel(&ei->t_hello); + event_cancel(&ei->t_hello); eigrp_hello_send(ei, EIGRP_HELLO_GRACEFUL_SHUTDOWN, NULL); } diff --git a/eigrpd/eigrp_macros.h b/eigrpd/eigrp_macros.h index b7cd22fee1df..75f1d7e133b4 100644 --- a/eigrpd/eigrp_macros.h +++ b/eigrpd/eigrp_macros.h @@ -27,6 +27,6 @@ /* FSM macros*/ #define EIGRP_FSM_EVENT_SCHEDULE(I, E) \ - thread_add_event(master, eigrp_fsm_event, (I), (E)) + event_add_event(master, eigrp_fsm_event, (I), (E)) #endif /* _ZEBRA_EIGRP_MACROS_H_ */ diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index 3c1e5da34004..6d7443b7914e 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -17,7 +17,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "linklist.h" #include "if.h" @@ -76,7 +76,7 @@ struct zebra_privs_t eigrpd_privs = { struct option longopts[] = {{0}}; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* Forward declaration of daemon info structure. */ static struct frr_daemon_info eigrpd_di; diff --git a/eigrpd/eigrp_neighbor.c b/eigrpd/eigrp_neighbor.c index 9288b0f1cd7c..25209c3bb3bb 100644 --- a/eigrpd/eigrp_neighbor.c +++ b/eigrpd/eigrp_neighbor.c @@ -20,7 +20,7 @@ #include "prefix.h" #include "memory.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "stream.h" #include "table.h" #include "log.h" @@ -164,19 +164,19 @@ void eigrp_nbr_delete(struct eigrp_neighbor *nbr) eigrp_topology_neighbor_down(nbr->ei->eigrp, nbr); /* Cancel all events. */ /* Thread lookup cost would be negligible. */ - thread_cancel_event(master, nbr); + event_cancel_event(master, nbr); eigrp_fifo_free(nbr->multicast_queue); eigrp_fifo_free(nbr->retrans_queue); - THREAD_OFF(nbr->t_holddown); + EVENT_OFF(nbr->t_holddown); if (nbr->ei) listnode_delete(nbr->ei->nbrs, nbr); XFREE(MTYPE_EIGRP_NEIGHBOR, nbr); } -void holddown_timer_expired(struct thread *thread) +void holddown_timer_expired(struct event *thread) { - struct eigrp_neighbor *nbr = THREAD_ARG(thread); + struct eigrp_neighbor *nbr = EVENT_ARG(thread); struct eigrp *eigrp = nbr->ei->eigrp; zlog_info("Neighbor %pI4 (%s) is down: holding time expired", &nbr->src, @@ -210,7 +210,7 @@ void eigrp_nbr_state_set(struct eigrp_neighbor *nbr, uint8_t state) // hold time.. nbr->v_holddown = EIGRP_HOLD_INTERVAL_DEFAULT; - THREAD_OFF(nbr->t_holddown); + EVENT_OFF(nbr->t_holddown); /* out with the old */ if (nbr->multicast_queue) @@ -252,24 +252,24 @@ void eigrp_nbr_state_update(struct eigrp_neighbor *nbr) switch (nbr->state) { case EIGRP_NEIGHBOR_DOWN: { /*Start Hold Down Timer for neighbor*/ - // THREAD_OFF(nbr->t_holddown); - // THREAD_TIMER_ON(master, nbr->t_holddown, + // EVENT_OFF(nbr->t_holddown); + // EVENT_TIMER_ON(master, nbr->t_holddown, // holddown_timer_expired, // nbr, nbr->v_holddown); break; } case EIGRP_NEIGHBOR_PENDING: { /*Reset Hold Down Timer for neighbor*/ - THREAD_OFF(nbr->t_holddown); - thread_add_timer(master, holddown_timer_expired, nbr, - nbr->v_holddown, &nbr->t_holddown); + EVENT_OFF(nbr->t_holddown); + event_add_timer(master, holddown_timer_expired, nbr, + nbr->v_holddown, &nbr->t_holddown); break; } case EIGRP_NEIGHBOR_UP: { /*Reset Hold Down Timer for neighbor*/ - THREAD_OFF(nbr->t_holddown); - thread_add_timer(master, holddown_timer_expired, nbr, - nbr->v_holddown, &nbr->t_holddown); + EVENT_OFF(nbr->t_holddown); + event_add_timer(master, holddown_timer_expired, nbr, + nbr->v_holddown, &nbr->t_holddown); break; } } diff --git a/eigrpd/eigrp_neighbor.h b/eigrpd/eigrp_neighbor.h index 5c9e44e2b96d..2ccc89cf3a7e 100644 --- a/eigrpd/eigrp_neighbor.h +++ b/eigrpd/eigrp_neighbor.h @@ -24,7 +24,7 @@ extern struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *ei, extern struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei); extern void eigrp_nbr_delete(struct eigrp_neighbor *neigh); -extern void holddown_timer_expired(struct thread *thread); +extern void holddown_timer_expired(struct event *thread); extern int eigrp_neighborship_check(struct eigrp_neighbor *neigh, struct TLV_Parameter_Type *tlv); diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c index 68edd0898a3a..ef567fe4ef63 100644 --- a/eigrpd/eigrp_network.c +++ b/eigrpd/eigrp_network.c @@ -12,7 +12,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "prefix.h" #include "if.h" diff --git a/eigrpd/eigrp_network.h b/eigrpd/eigrp_network.h index 33d91c2fbf7d..ac5c47f6f98b 100644 --- a/eigrpd/eigrp_network.h +++ b/eigrpd/eigrp_network.h @@ -20,7 +20,7 @@ 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); -extern void eigrp_hello_timer(struct thread *thread); +extern void eigrp_hello_timer(struct event *thread); extern void eigrp_if_update(struct interface *); extern int eigrp_if_add_allspfrouters(struct eigrp *, struct prefix *, unsigned int); diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c index 74a4dcb85854..f50abb7e48ca 100644 --- a/eigrpd/eigrp_northbound.c +++ b/eigrpd/eigrp_northbound.c @@ -724,7 +724,7 @@ static int eigrpd_instance_redistribute_create(struct nb_cb_create_args *args) else vrfid = VRF_DEFAULT; - if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], vrfid)) + if (vrf_bitmap_check(&zclient->redist[AFI_IP][proto], vrfid)) return NB_ERR_INCONSISTENCY; break; case NB_EV_PREPARE: diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index e00d62fb0869..963d229bc105 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -12,7 +12,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "vty.h" @@ -305,9 +305,9 @@ int eigrp_check_sha256_digest(struct stream *s, return 1; } -void eigrp_write(struct thread *thread) +void eigrp_write(struct event *thread) { - struct eigrp *eigrp = THREAD_ARG(thread); + struct eigrp *eigrp = EVENT_ARG(thread); struct eigrp_header *eigrph; struct eigrp_interface *ei; struct eigrp_packet *ep; @@ -453,13 +453,13 @@ void eigrp_write(struct thread *thread) /* If packets still remain in queue, call write thread. */ if (!list_isempty(eigrp->oi_write_q)) { - thread_add_write(master, eigrp_write, eigrp, eigrp->fd, - &eigrp->t_write); + event_add_write(master, eigrp_write, eigrp, eigrp->fd, + &eigrp->t_write); } } /* Starting point of packet process function. */ -void eigrp_read(struct thread *thread) +void eigrp_read(struct event *thread) { int ret; struct stream *ibuf; @@ -474,10 +474,10 @@ void eigrp_read(struct thread *thread) uint16_t length = 0; /* first of all get interface pointer. */ - eigrp = THREAD_ARG(thread); + eigrp = EVENT_ARG(thread); /* prepare for next packet. */ - thread_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); + event_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); stream_reset(eigrp->ibuf); if (!(ibuf = eigrp_recv_packet(eigrp, eigrp->fd, &ifp, eigrp->ibuf))) { @@ -828,9 +828,9 @@ void eigrp_send_packet_reliably(struct eigrp_neighbor *nbr) eigrp_fifo_push(nbr->ei->obuf, duplicate); /*Start retransmission timer*/ - thread_add_timer(master, eigrp_unack_packet_retrans, nbr, - EIGRP_PACKET_RETRANS_TIME, - &ep->t_retrans_timer); + event_add_timer(master, eigrp_unack_packet_retrans, nbr, + EIGRP_PACKET_RETRANS_TIME, + &ep->t_retrans_timer); /*Increment sequence number counter*/ nbr->ei->eigrp->sequence_number++; @@ -840,8 +840,8 @@ void eigrp_send_packet_reliably(struct eigrp_neighbor *nbr) listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); nbr->ei->on_write_q = 1; } - thread_add_write(master, eigrp_write, nbr->ei->eigrp, - nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); } } @@ -923,7 +923,7 @@ void eigrp_packet_free(struct eigrp_packet *ep) if (ep->s) stream_free(ep->s); - THREAD_OFF(ep->t_retrans_timer); + EVENT_OFF(ep->t_retrans_timer); XFREE(MTYPE_EIGRP_PACKET, ep); } @@ -970,10 +970,10 @@ static int eigrp_check_network_mask(struct eigrp_interface *ei, return 0; } -void eigrp_unack_packet_retrans(struct thread *thread) +void eigrp_unack_packet_retrans(struct event *thread) { struct eigrp_neighbor *nbr; - nbr = (struct eigrp_neighbor *)THREAD_ARG(thread); + nbr = (struct eigrp_neighbor *)EVENT_ARG(thread); struct eigrp_packet *ep; ep = eigrp_fifo_next(nbr->retrans_queue); @@ -992,24 +992,24 @@ void eigrp_unack_packet_retrans(struct thread *thread) } /*Start retransmission timer*/ - thread_add_timer(master, eigrp_unack_packet_retrans, nbr, - EIGRP_PACKET_RETRANS_TIME, - &ep->t_retrans_timer); + event_add_timer(master, eigrp_unack_packet_retrans, nbr, + EIGRP_PACKET_RETRANS_TIME, + &ep->t_retrans_timer); /* Hook thread to write packet. */ if (nbr->ei->on_write_q == 0) { listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); nbr->ei->on_write_q = 1; } - thread_add_write(master, eigrp_write, nbr->ei->eigrp, - nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); } } -void eigrp_unack_multicast_packet_retrans(struct thread *thread) +void eigrp_unack_multicast_packet_retrans(struct event *thread) { struct eigrp_neighbor *nbr; - nbr = (struct eigrp_neighbor *)THREAD_ARG(thread); + nbr = (struct eigrp_neighbor *)EVENT_ARG(thread); struct eigrp_packet *ep; ep = eigrp_fifo_next(nbr->multicast_queue); @@ -1027,17 +1027,17 @@ void eigrp_unack_multicast_packet_retrans(struct thread *thread) } /*Start retransmission timer*/ - thread_add_timer(master, eigrp_unack_multicast_packet_retrans, - nbr, EIGRP_PACKET_RETRANS_TIME, - &ep->t_retrans_timer); + event_add_timer(master, eigrp_unack_multicast_packet_retrans, + nbr, EIGRP_PACKET_RETRANS_TIME, + &ep->t_retrans_timer); /* Hook thread to write packet. */ if (nbr->ei->on_write_q == 0) { listnode_add(nbr->ei->eigrp->oi_write_q, nbr->ei); nbr->ei->on_write_q = 1; } - thread_add_write(master, eigrp_write, nbr->ei->eigrp, - nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); + event_add_write(master, eigrp_write, nbr->ei->eigrp, + nbr->ei->eigrp->fd, &nbr->ei->eigrp->t_write); } } diff --git a/eigrpd/eigrp_packet.h b/eigrpd/eigrp_packet.h index 4c1cfd48289b..6a0360ed80fb 100644 --- a/eigrpd/eigrp_packet.h +++ b/eigrpd/eigrp_packet.h @@ -18,8 +18,8 @@ #define _ZEBRA_EIGRP_PACKET_H /*Prototypes*/ -extern void eigrp_read(struct thread *thread); -extern void eigrp_write(struct thread *thread); +extern void eigrp_read(struct event *thread); +extern void eigrp_write(struct event *thread); extern struct eigrp_packet *eigrp_packet_new(size_t size, struct eigrp_neighbor *nbr); @@ -51,8 +51,8 @@ extern uint16_t eigrp_add_authTLV_MD5_to_stream(struct stream *s, extern uint16_t eigrp_add_authTLV_SHA256_to_stream(struct stream *s, struct eigrp_interface *ei); -extern void eigrp_unack_packet_retrans(struct thread *thread); -extern void eigrp_unack_multicast_packet_retrans(struct thread *thread); +extern void eigrp_unack_packet_retrans(struct event *thread); +extern void eigrp_unack_multicast_packet_retrans(struct event *thread); /* * untill there is reason to have their own header, these externs are found in @@ -65,7 +65,7 @@ extern void eigrp_hello_send_ack(struct eigrp_neighbor *nbr); extern void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph, struct eigrp_header *eigrph, struct stream *s, struct eigrp_interface *ei, int size); -extern void eigrp_hello_timer(struct thread *thread); +extern void eigrp_hello_timer(struct event *thread); /* * These externs are found in eigrp_update.c @@ -81,7 +81,7 @@ extern void eigrp_update_send_all(struct eigrp *eigrp, struct eigrp_interface *exception); extern void eigrp_update_send_init(struct eigrp_neighbor *nbr); extern void eigrp_update_send_EOT(struct eigrp_neighbor *nbr); -extern void eigrp_update_send_GR_thread(struct thread *thread); +extern void eigrp_update_send_GR_thread(struct event *thread); extern void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type, struct vty *vty); extern void eigrp_update_send_interface_GR(struct eigrp_interface *ei, diff --git a/eigrpd/eigrp_query.c b/eigrpd/eigrp_query.c index 56498ed67479..0e206cded653 100644 --- a/eigrpd/eigrp_query.c +++ b/eigrpd/eigrp_query.c @@ -12,7 +12,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" diff --git a/eigrpd/eigrp_reply.c b/eigrpd/eigrp_reply.c index a1413fe9c712..aae89e832b0a 100644 --- a/eigrpd/eigrp_reply.c +++ b/eigrpd/eigrp_reply.c @@ -16,7 +16,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" diff --git a/eigrpd/eigrp_siaquery.c b/eigrpd/eigrp_siaquery.c index 1c2ec3731f8c..71486a1f6f85 100644 --- a/eigrpd/eigrp_siaquery.c +++ b/eigrpd/eigrp_siaquery.c @@ -12,7 +12,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" diff --git a/eigrpd/eigrp_siareply.c b/eigrpd/eigrp_siareply.c index 37cca67373e2..6c8c1ef58de5 100644 --- a/eigrpd/eigrp_siareply.c +++ b/eigrpd/eigrp_siareply.c @@ -11,7 +11,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c index f159415ccb89..492ef3e71378 100644 --- a/eigrpd/eigrp_snmp.c +++ b/eigrpd/eigrp_snmp.c @@ -16,7 +16,7 @@ #include <net-snmp/net-snmp-config.h> #include <net-snmp/net-snmp-includes.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" diff --git a/eigrpd/eigrp_structs.h b/eigrpd/eigrp_structs.h index 9c88e34f6cab..8735c4866145 100644 --- a/eigrpd/eigrp_structs.h +++ b/eigrpd/eigrp_structs.h @@ -71,9 +71,9 @@ struct eigrp { struct list *oi_write_q; /*Threads*/ - struct thread *t_write; - struct thread *t_read; - struct thread *t_distribute; /* timer for distribute list */ + struct event *t_write; + struct event *t_read; + struct event *t_distribute; /* timer for distribute list */ struct route_table *networks; /* EIGRP config networks. */ @@ -165,8 +165,8 @@ struct eigrp_interface { struct list *nbrs; /* EIGRP Neighbor List */ /* Threads. */ - struct thread *t_hello; /* timer */ - struct thread *t_distribute; /* timer for distribute list */ + struct event *t_hello; /* timer */ + struct event *t_distribute; /* timer for distribute list */ int on_write_q; @@ -240,8 +240,8 @@ struct eigrp_neighbor { uint16_t v_holddown; /* Threads. */ - struct thread *t_holddown; - struct thread *t_nbr_send_gr; /* thread for sending multiple GR packet + struct event *t_holddown; + struct event *t_nbr_send_gr; /* thread for sending multiple GR packet chunks */ struct eigrp_fifo *retrans_queue; @@ -271,7 +271,7 @@ struct eigrp_packet { struct in_addr dst; /*Packet retransmission thread*/ - struct thread *t_retrans_timer; + struct event *t_retrans_timer; /*Packet retransmission counter*/ uint8_t retrans_counter; diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c index 49acf3069566..a056267bf70c 100644 --- a/eigrpd/eigrp_update.c +++ b/eigrpd/eigrp_update.c @@ -16,7 +16,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" @@ -842,9 +842,6 @@ static void eigrp_update_send_GR_part(struct eigrp_neighbor *nbr) eigrp_fsm_event(&fsm_msg); } - /* NULL the pointer */ - dest_addr = NULL; - /* delete processed prefix from list */ listnode_delete(prefixes, pe); @@ -895,19 +892,19 @@ static void eigrp_update_send_GR_part(struct eigrp_neighbor *nbr) * * Uses nbr_gr_packet_type and t_nbr_send_gr from neighbor. */ -void eigrp_update_send_GR_thread(struct thread *thread) +void eigrp_update_send_GR_thread(struct event *thread) { struct eigrp_neighbor *nbr; /* get argument from thread */ - nbr = THREAD_ARG(thread); + nbr = EVENT_ARG(thread); /* remove this thread pointer */ /* if there is packet waiting in queue, * schedule this thread again with small delay */ if (nbr->retrans_queue->count > 0) { - thread_add_timer_msec(master, eigrp_update_send_GR_thread, nbr, - 10, &nbr->t_nbr_send_gr); + event_add_timer_msec(master, eigrp_update_send_GR_thread, nbr, + 10, &nbr->t_nbr_send_gr); return; } @@ -916,7 +913,7 @@ void eigrp_update_send_GR_thread(struct thread *thread) /* if it wasn't last chunk, schedule this thread again */ if (nbr->nbr_gr_packet_type != EIGRP_PACKET_PART_LAST) { - thread_execute(master, eigrp_update_send_GR_thread, nbr, 0); + event_execute(master, eigrp_update_send_GR_thread, nbr, 0); } } @@ -982,7 +979,7 @@ void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type, /* indicate, that this is first GR Update packet chunk */ nbr->nbr_gr_packet_type = EIGRP_PACKET_PART_FIRST; /* execute packet sending in thread */ - thread_execute(master, eigrp_update_send_GR_thread, nbr, 0); + event_execute(master, eigrp_update_send_GR_thread, nbr, 0); } /** diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index 33f728ec4b92..88510e75afb5 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -17,7 +17,7 @@ #include <zebra.h> #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "table.h" #include "vty.h" diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index 179c2d0f2aa8..a5cecb9c1615 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -12,7 +12,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "network.h" #include "prefix.h" @@ -49,7 +49,7 @@ static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS); struct zclient *zclient = NULL; /* For registering threads. */ -extern struct thread_master *master; +extern struct event_loop *master; struct in_addr router_id_zebra; /* Router-id update message from zebra. */ @@ -249,9 +249,9 @@ void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *p) 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_id) - : vrf_bitmap_check(zclient->redist[AFI_IP][type], + ? vrf_bitmap_check( + &zclient->default_information[AFI_IP], vrf_id) + : vrf_bitmap_check(&zclient->redist[AFI_IP][type], vrf_id)); } diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c index a382862e3dc5..c7dd96bfff3d 100644 --- a/eigrpd/eigrpd.c +++ b/eigrpd/eigrpd.c @@ -12,7 +12,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "linklist.h" @@ -157,7 +157,7 @@ static struct eigrp *eigrp_new(uint16_t as, vrf_id_t vrf_id) eigrp->ibuf = stream_new(EIGRP_PACKET_MAX_LEN + 1); - thread_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); + event_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read); eigrp->oi_write_q = list_new(); eigrp->topology_table = route_table_init(); @@ -260,8 +260,8 @@ void eigrp_finish_final(struct eigrp *eigrp) eigrp_if_free(ei, INTERFACE_DOWN_BY_FINAL); } - THREAD_OFF(eigrp->t_write); - THREAD_OFF(eigrp->t_read); + EVENT_OFF(eigrp->t_write); + EVENT_OFF(eigrp->t_read); close(eigrp->fd); list_delete(&eigrp->eiflist); diff --git a/eigrpd/eigrpd.h b/eigrpd/eigrpd.h index 6e5d5d13dcb8..a6a4e39a591f 100644 --- a/eigrpd/eigrpd.h +++ b/eigrpd/eigrpd.h @@ -35,7 +35,7 @@ struct eigrp_master { struct list *eigrp; /* EIGRP thread master. */ - struct thread_master *master; + struct event_loop *master; /* Zebra interface list. */ struct list *iflist; @@ -51,7 +51,7 @@ struct eigrp_master { /* Extern variables. */ extern struct zclient *zclient; -extern struct thread_master *master; +extern struct event_loop *master; extern struct eigrp_master *eigrp_om; extern struct zebra_privs_t eigrpd_privs; diff --git a/gdb/lib.txt b/gdb/lib.txt index b44c23798558..5d22321b6291 100644 --- a/gdb/lib.txt +++ b/gdb/lib.txt @@ -157,7 +157,7 @@ document walk_route_table Walk through a routing table (or subset thereof) and dump all the non-null (struct route_node *)->info pointers. -Argument: A lib/thread.h::(struct route_node *) pointing to the route_node +Argument: A lib/hread.h::(struct route_node *) pointing to the route_node under which all data should be dumped end diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 64734f8628ef..b229aa6717bd 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -40,7 +40,7 @@ struct fabricd { enum fabricd_sync_state initial_sync_state; time_t initial_sync_start; struct isis_circuit *initial_sync_circuit; - struct thread *initial_sync_timeout; + struct event *initial_sync_timeout; struct isis_spftree *spftree; struct skiplist *neighbors; @@ -49,8 +49,8 @@ struct fabricd { uint8_t tier; uint8_t tier_config; uint8_t tier_pending; - struct thread *tier_calculation_timer; - struct thread *tier_set_timer; + struct event *tier_calculation_timer; + struct event *tier_set_timer; int csnp_delay; bool always_send_csnp; @@ -207,10 +207,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, &area->lspdb[IS_LEVEL_2 - 1], - area->isis->sysid, ISIS_LEVEL2, SPFTREE_IPV4, - SPF_TYPE_FORWARD, F_SPFTREE_HOPCOUNT_METRIC); + 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, SR_ALGORITHM_SPF); rv->neighbors = skiplist_new(0, neighbor_entry_list_cmp, neighbor_entry_del_void); rv->neighbors_neighbors = hash_create(neighbor_entry_hash_key, @@ -225,11 +225,11 @@ struct fabricd *fabricd_new(struct isis_area *area) void fabricd_finish(struct fabricd *f) { - THREAD_OFF(f->initial_sync_timeout); + EVENT_OFF(f->initial_sync_timeout); - THREAD_OFF(f->tier_calculation_timer); + EVENT_OFF(f->tier_calculation_timer); - THREAD_OFF(f->tier_set_timer); + EVENT_OFF(f->tier_set_timer); isis_spftree_del(f->spftree); neighbor_lists_clear(f); @@ -237,9 +237,9 @@ void fabricd_finish(struct fabricd *f) hash_free(f->neighbors_neighbors); } -static void fabricd_initial_sync_timeout(struct thread *thread) +static void fabricd_initial_sync_timeout(struct event *thread) { - struct fabricd *f = THREAD_ARG(thread); + struct fabricd *f = EVENT_ARG(thread); if (IS_DEBUG_ADJ_PACKETS) zlog_debug( @@ -267,14 +267,14 @@ void fabricd_initial_sync_hello(struct isis_circuit *circuit) if (f->initial_sync_timeout) return; - thread_add_timer(master, fabricd_initial_sync_timeout, f, - timeout, &f->initial_sync_timeout); + event_add_timer(master, fabricd_initial_sync_timeout, f, timeout, + &f->initial_sync_timeout); f->initial_sync_start = monotime(NULL); if (IS_DEBUG_ADJ_PACKETS) zlog_debug( - "OpenFabric: Started initial synchronization with %s on %s", - sysid_print(circuit->u.p2p.neighbor->sysid), + "OpenFabric: Started initial synchronization with %pSY on %s", + circuit->u.p2p.neighbor->sysid, circuit->interface->name); } @@ -325,7 +325,7 @@ void fabricd_initial_sync_finish(struct isis_area *area) f->initial_sync_circuit->interface->name); f->initial_sync_state = FABRICD_SYNC_COMPLETE; f->initial_sync_circuit = NULL; - THREAD_OFF(f->initial_sync_timeout); + EVENT_OFF(f->initial_sync_timeout); } static void fabricd_bump_tier_calculation_timer(struct fabricd *f); @@ -359,7 +359,9 @@ 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 == %u", rawlspid_print(furthest_t0->N.id), furthest_t0->d_N); + zlog_info( + "OpenFabric: Found %pLS as furthest t0 from local system, dist == %u", + furthest_t0->N.id, furthest_t0->d_N); struct isis_spftree *remote_tree = isis_run_hopcount_spf(area, furthest_t0->N.id, NULL); @@ -372,8 +374,9 @@ 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 == %u", rawlspid_print(furthest_from_remote->N.id), - furthest_from_remote->d_N); + zlog_info( + "OpenFabric: Found %pLS as furthest from remote dist == %u", + furthest_from_remote->N.id, furthest_from_remote->d_N); } int64_t tier = furthest_from_remote->d_N - furthest_t0->d_N; @@ -389,16 +392,16 @@ static uint8_t fabricd_calculate_fabric_tier(struct isis_area *area) return tier; } -static void fabricd_tier_set_timer(struct thread *thread) +static void fabricd_tier_set_timer(struct event *thread) { - struct fabricd *f = THREAD_ARG(thread); + struct fabricd *f = EVENT_ARG(thread); fabricd_set_tier(f, f->tier_pending); } -static void fabricd_tier_calculation_cb(struct thread *thread) +static void fabricd_tier_calculation_cb(struct event *thread) { - struct fabricd *f = THREAD_ARG(thread); + struct fabricd *f = EVENT_ARG(thread); uint8_t tier = ISIS_TIER_UNDEFINED; tier = fabricd_calculate_fabric_tier(f->area); @@ -408,28 +411,27 @@ static void fabricd_tier_calculation_cb(struct thread *thread) zlog_info("OpenFabric: Got tier %hhu from algorithm. Arming timer.", tier); f->tier_pending = tier; - thread_add_timer(master, fabricd_tier_set_timer, f, - f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], - &f->tier_set_timer); - + event_add_timer(master, fabricd_tier_set_timer, f, + f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], + &f->tier_set_timer); } static void fabricd_bump_tier_calculation_timer(struct fabricd *f) { /* Cancel timer if we already know our tier */ if (f->tier != ISIS_TIER_UNDEFINED || f->tier_set_timer) { - THREAD_OFF(f->tier_calculation_timer); + EVENT_OFF(f->tier_calculation_timer); return; } /* If we need to calculate the tier, wait some * time for the topology to settle before running * the calculation */ - THREAD_OFF(f->tier_calculation_timer); + EVENT_OFF(f->tier_calculation_timer); - thread_add_timer(master, fabricd_tier_calculation_cb, f, - 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], - &f->tier_calculation_timer); + event_add_timer(master, fabricd_tier_calculation_cb, f, + 2 * f->area->lsp_gen_interval[ISIS_LEVEL2 - 1], + &f->tier_calculation_timer); } static void fabricd_set_tier(struct fabricd *f, uint8_t tier) @@ -710,10 +712,10 @@ void fabricd_trigger_csnp(struct isis_area *area, bool circuit_scoped) if (!circuit->t_send_csnp[1]) continue; - THREAD_OFF(circuit->t_send_csnp[ISIS_LEVEL2 - 1]); - thread_add_timer_msec(master, send_l2_csnp, circuit, - isis_jitter(f->csnp_delay, CSNP_JITTER), - &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); + EVENT_OFF(circuit->t_send_csnp[ISIS_LEVEL2 - 1]); + event_add_timer_msec(master, send_l2_csnp, circuit, + isis_jitter(f->csnp_delay, CSNP_JITTER), + &circuit->t_send_csnp[ISIS_LEVEL2 - 1]); } } diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index cb2cf8f61190..30b71537e0e5 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -15,7 +15,7 @@ #include "hash.h" #include "vty.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "if.h" #include "stream.h" #include "bfd.h" @@ -148,7 +148,7 @@ void isis_delete_adj(void *arg) /* Remove self from snmp list without walking the list*/ list_delete_node(adj->circuit->snmp_adj_list, adj->snmp_list_node); - THREAD_OFF(adj->t_expire); + EVENT_OFF(adj->t_expire); if (adj->adj_state != ISIS_ADJ_DOWN) adj->adj_state = ISIS_ADJ_DOWN; @@ -283,6 +283,8 @@ void isis_adj_process_threeway(struct isis_adjacency *adj, } const char *isis_adj_name(const struct isis_adjacency *adj) { + static char buf[ISO_SYSID_STRLEN]; + if (!adj) return "NONE"; @@ -291,8 +293,9 @@ const char *isis_adj_name(const struct isis_adjacency *adj) dyn = dynhn_find_by_id(adj->circuit->isis, adj->sysid); if (dyn) return dyn->hostname; - else - return sysid_print(adj->sysid); + + snprintfrr(buf, sizeof(buf), "%pSY", adj->sysid); + return buf; } void isis_log_adj_change(struct isis_adjacency *adj, enum isis_adj_state old_state, @@ -396,13 +399,13 @@ void isis_adj_state_change(struct isis_adjacency **padj, adj->flaps++; if (level == IS_LEVEL_1) { - thread_add_timer(master, send_l1_csnp, - circuit, 0, - &circuit->t_send_csnp[0]); + event_add_timer( + master, send_l1_csnp, circuit, + 0, &circuit->t_send_csnp[0]); } else { - thread_add_timer(master, send_l2_csnp, - circuit, 0, - &circuit->t_send_csnp[1]); + event_add_timer( + master, send_l2_csnp, circuit, + 0, &circuit->t_send_csnp[1]); } } else if (old_state == ISIS_ADJ_UP) { circuit->upadjcount[level - 1]--; @@ -439,9 +442,8 @@ void isis_adj_print(struct isis_adjacency *adj) if (dyn) zlog_debug("%s", dyn->hostname); - zlog_debug("SystemId %20s SNPA %s, level %d; Holding Time %d", - sysid_print(adj->sysid), snpa_print(adj->snpa), adj->level, - adj->hold_time); + zlog_debug("SystemId %20pSY SNPA %pSY, level %d; Holding Time %d", + adj->sysid, adj->snpa, adj->level, adj->hold_time); if (adj->ipv4_address_count) { zlog_debug("IPv4 Address(es):"); for (unsigned int i = 0; i < adj->ipv4_address_count; i++) @@ -478,14 +480,14 @@ const char *isis_adj_yang_state(enum isis_adj_state state) assert(!"Reached end of function where we are not expecting to"); } -void isis_adj_expire(struct thread *thread) +void isis_adj_expire(struct event *thread) { struct isis_adjacency *adj; /* * Get the adjacency */ - adj = THREAD_ARG(thread); + adj = EVENT_ARG(thread); assert(adj); adj->t_expire = NULL; @@ -530,7 +532,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, time2string(adj->last_upd + adj->hold_time - now)); } - json_object_string_add(json, "snpa", snpa_print(adj->snpa)); + json_object_string_addf(json, "snpa", "%pSY", adj->snpa); } if (detail == ISIS_UI_LEVEL_DETAIL) { @@ -581,8 +583,7 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, isis_mtid2str(adj->mt_set[i])); } } - json_object_string_add(iface_json, "snpa", - snpa_print(adj->snpa)); + json_object_string_addf(iface_json, "snpa", "%pSY", adj->snpa); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); @@ -593,11 +594,8 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, json_object_string_add(iface_json, "lan-id", buf); } else { - snprintfrr(buf, sizeof(buf), "%s-%02x", - sysid_print(adj->lanid), - adj->lanid[ISIS_SYS_ID_LEN]); - json_object_string_add(iface_json, "lan-id", - buf); + json_object_string_addf(iface_json, "lan-id", + "%pSY", adj->lanid); } json_object_int_add(iface_json, "lan-prio", @@ -626,12 +624,9 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, area_addr_json); for (unsigned int i = 0; i < adj->area_address_count; i++) { - json_object_string_add( - area_addr_json, "isonet", - isonet_print(adj->area_addresses[i] - .area_addr, - adj->area_addresses[i] - .addr_len)); + json_object_string_addf( + area_addr_json, "isonet", "%pIS", + &adj->area_addresses[i]); } } if (adj->ipv4_address_count) { @@ -736,7 +731,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, + adj->hold_time - now); } else vty_out(vty, "- "); - vty_out(vty, "%-10s", snpa_print(adj->snpa)); + vty_out(vty, "%-10pSY", adj->snpa); vty_out(vty, "\n"); } @@ -780,7 +775,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, " %s\n", isis_mtid2str(adj->mt_set[i])); } - vty_out(vty, " SNPA: %s", snpa_print(adj->snpa)); + vty_out(vty, " SNPA: %pSY", adj->snpa); if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); @@ -788,9 +783,7 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, ", LAN id: %s.%02x", dyn->hostname, adj->lanid[ISIS_SYS_ID_LEN]); else - vty_out(vty, ", LAN id: %s.%02x", - sysid_print(adj->lanid), - adj->lanid[ISIS_SYS_ID_LEN]); + vty_out(vty, ", LAN id: %pPN", adj->lanid); vty_out(vty, "\n"); vty_out(vty, " LAN Priority: %u", @@ -811,11 +804,8 @@ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, vty_out(vty, " Area Address(es):\n"); for (unsigned int i = 0; i < adj->area_address_count; i++) { - vty_out(vty, " %s\n", - isonet_print(adj->area_addresses[i] - .area_addr, - adj->area_addresses[i] - .addr_len)); + vty_out(vty, " %pIS\n", + &adj->area_addresses[i]); } } if (adj->ipv4_address_count) { diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index c260ce78de0f..c0c8e68145d2 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -69,7 +69,7 @@ struct isis_adjacency { struct isis_dis_record dis_record[DIS_RECORDS * ISIS_LEVELS]; enum isis_adj_state adj_state; /* adjacencyState */ enum isis_adj_usage adj_usage; /* adjacencyUsage */ - struct area_addr *area_addresses; /* areaAdressesOfNeighbour */ + struct iso_address *area_addresses; /* areaAdressesOfNeighbour */ unsigned int area_address_count; struct nlpids nlpids; /* protocols spoken ... */ struct in_addr *ipv4_addresses; @@ -90,7 +90,7 @@ struct isis_adjacency { enum isis_threeway_state threeway_state; uint32_t ext_circuit_id; int flaps; /* number of adjacency flaps */ - struct thread *t_expire; /* expire after hold_time */ + struct event *t_expire; /* expire after hold_time */ struct isis_circuit *circuit; /* back pointer */ uint16_t *mt_set; /* Topologies this adjacency is valid for */ unsigned int mt_count; /* Number of entries in mt_set */ @@ -128,7 +128,7 @@ 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); -void isis_adj_expire(struct thread *thread); +void isis_adj_expire(struct event *thread); void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, char detail); void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, @@ -136,6 +136,6 @@ void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, void isis_adj_build_neigh_list(struct list *adjdb, struct list *list); void isis_adj_build_up_list(struct list *adjdb, struct list *list); int isis_adj_usage2levels(enum isis_adj_usage usage); -void isis_bfd_startup_timer(struct thread *thread); +void isis_bfd_startup_timer(struct event *thread); const char *isis_adj_name(const struct isis_adjacency *adj); #endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_affinitymap.c b/isisd/isis_affinitymap.c new file mode 100644 index 000000000000..41bad0a7d934 --- /dev/null +++ b/isisd/isis_affinitymap.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* IS-IS affinity-map + * Copyright 2023 6WIND S.A. + */ + +#include <zebra.h> +#include "lib/if.h" +#include "lib/vrf.h" +#include "isisd/isisd.h" +#include "isisd/isis_affinitymap.h" + +#ifndef FABRICD + +static bool isis_affinity_map_check_use(const char *affmap_name) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + struct isis_area *area; + struct listnode *area_node, *fa_node; + struct flex_algo *fa; + struct affinity_map *map; + uint16_t pos; + + if (!isis) + return false; + + map = affinity_map_get(affmap_name); + pos = map->bit_position; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node, + fa)) { + if (admin_group_get(&fa->admin_group_exclude_any, + pos) || + admin_group_get(&fa->admin_group_include_any, + pos) || + admin_group_get(&fa->admin_group_include_all, pos)) + return true; + } + } + return false; +} + +static void isis_affinity_map_update(const char *affmap_name, uint16_t old_pos, + uint16_t new_pos) +{ + struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); + struct listnode *area_node, *fa_node; + struct isis_area *area; + struct flex_algo *fa; + bool changed; + + if (!isis) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, area_node, area)) { + changed = false; + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, fa_node, + fa)) { + if (admin_group_get(&fa->admin_group_exclude_any, + old_pos)) { + admin_group_unset(&fa->admin_group_exclude_any, + old_pos); + admin_group_set(&fa->admin_group_exclude_any, + new_pos); + changed = true; + } + if (admin_group_get(&fa->admin_group_include_any, + old_pos)) { + admin_group_unset(&fa->admin_group_include_any, + old_pos); + admin_group_set(&fa->admin_group_include_any, + new_pos); + changed = true; + } + if (admin_group_get(&fa->admin_group_include_all, + old_pos)) { + admin_group_unset(&fa->admin_group_include_all, + old_pos); + admin_group_set(&fa->admin_group_include_all, + new_pos); + changed = true; + } + } + if (changed) + lsp_regenerate_schedule(area, area->is_type, 0); + } +} + +void isis_affinity_map_init(void) +{ + affinity_map_init(); + + affinity_map_set_check_use_hook(isis_affinity_map_check_use); + affinity_map_set_update_hook(isis_affinity_map_update); +} + +#endif /* ifndef FABRICD */ diff --git a/isisd/isis_affinitymap.h b/isisd/isis_affinitymap.h new file mode 100644 index 000000000000..c432e99f51eb --- /dev/null +++ b/isisd/isis_affinitymap.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* IS-IS affinity-map header + * Copyright 2023 6WIND S.A. + */ + +#ifndef __ISIS_AFFINITYMAP_H__ +#define __ISIS_AFFINITYMAP_H__ + +#include "lib/affinitymap.h" + +#ifndef FABRICD + +#ifdef __cplusplus +extern "C" { +#endif + +extern void isis_affinity_map_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ifndef FABRICD */ + +#endif /* __ISIS_AFFINITYMAP_H__ */ diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index b973dd493d1b..5e24e3521068 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -209,7 +209,7 @@ static int bfd_handle_circuit_add_addr(struct isis_circuit *circuit) return 0; } -void isis_bfd_init(struct thread_master *tm) +void isis_bfd_init(struct event_loop *tm) { bfd_protocol_integration_init(zclient, tm); diff --git a/isisd/isis_bfd.h b/isisd/isis_bfd.h index 30248ccecece..3cf0ed52195b 100644 --- a/isisd/isis_bfd.h +++ b/isisd/isis_bfd.h @@ -7,10 +7,10 @@ #define ISIS_BFD_H struct isis_circuit; -struct thread_master; +struct event_loop; void isis_bfd_circuit_cmd(struct isis_circuit *circuit); -void isis_bfd_init(struct thread_master *tm); +void isis_bfd_init(struct event_loop *tm); #endif diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 8644da2f0853..feab45123383 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -19,7 +19,7 @@ #include "if.h" #include "linklist.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "hash.h" #include "prefix.h" @@ -107,7 +107,7 @@ struct isis_circuit *isis_circuit_new(struct interface *ifp, const char *tag) "/frr-interface:lib/interface/frr-isisd:isis/circuit-type"); circuit->flags = 0; - circuit->pad_hellos = yang_get_default_bool( + circuit->pad_hellos = yang_get_default_enum( "/frr-interface:lib/interface/frr-isisd:isis/hello/padding"); circuit->hello_interval[0] = yang_get_default_uint32( "/frr-interface:lib/interface/frr-isisd:isis/hello/interval/level-1"); @@ -145,7 +145,7 @@ struct isis_circuit *isis_circuit_new(struct interface *ifp, const char *tag) #else circuit->is_type_config = IS_LEVEL_1_AND_2; circuit->flags = 0; - circuit->pad_hellos = 1; + circuit->pad_hellos = ISIS_HELLO_PADDING_ALWAYS; for (i = 0; i < 2; i++) { circuit->hello_interval[i] = DEFAULT_HELLO_INTERVAL; circuit->hello_multiplier[i] = DEFAULT_HELLO_MULTIPLIER; @@ -286,8 +286,7 @@ void isis_circuit_add_addr(struct isis_circuit *circuit, if (connected->address->family == AF_INET) { uint32_t addr = connected->address->u.prefix4.s_addr; addr = ntohl(addr); - if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr) - || IPV4_LINKLOCAL(addr)) + if (IPV4_NET0(addr) || IPV4_NET127(addr) || IN_CLASSD(addr)) return; for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ipv4)) @@ -627,12 +626,12 @@ void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream) void isis_circuit_prepare(struct isis_circuit *circuit) { #if ISIS_METHOD != ISIS_METHOD_DLPI - thread_add_read(master, isis_receive, circuit, circuit->fd, - &circuit->t_read); + event_add_read(master, isis_receive, circuit, circuit->fd, + &circuit->t_read); #else - thread_add_timer_msec(master, isis_receive, circuit, - listcount(circuit->area->circuit_list) * 100, - &circuit->t_read); + event_add_timer_msec(master, isis_receive, circuit, + listcount(circuit->area->circuit_list) * 100, + &circuit->t_read); #endif } @@ -696,10 +695,9 @@ int isis_circuit_up(struct isis_circuit *circuit) } #ifdef EXTREME_DEGUG if (IS_DEBUG_EVENTS) - zlog_debug("%s: if_id %d, isomtu %d snpa %s", __func__, - circuit->interface->ifindex, - ISO_MTU(circuit), - snpa_print(circuit->u.bc.snpa)); + zlog_debug("%s: if_id %d, isomtu %d snpa %pSY", + __func__, circuit->interface->ifindex, + ISO_MTU(circuit), circuit->u.bc.snpa); #endif /* EXTREME_DEBUG */ circuit->u.bc.adjdb[0] = list_new(); @@ -722,10 +720,10 @@ int isis_circuit_up(struct isis_circuit *circuit) send_hello_sched(circuit, level, TRIGGERED_IIH_DELAY); circuit->u.bc.lan_neighs[level - 1] = list_new(); - thread_add_timer(master, isis_run_dr, - &circuit->level_arg[level - 1], - 2 * circuit->hello_interval[level - 1], - &circuit->u.bc.t_run_dr[level - 1]); + event_add_timer(master, isis_run_dr, + &circuit->level_arg[level - 1], + 2 * circuit->hello_interval[level - 1], + &circuit->u.bc.t_run_dr[level - 1]); } /* 8.4.1 b) FIXME: solicit ES - 8.4.6 */ @@ -740,13 +738,13 @@ int isis_circuit_up(struct isis_circuit *circuit) /* initializing PSNP timers */ if (circuit->is_type & IS_LEVEL_1) - thread_add_timer( + event_add_timer( master, send_l1_psnp, circuit, isis_jitter(circuit->psnp_interval[0], PSNP_JITTER), &circuit->t_send_psnp[0]); if (circuit->is_type & IS_LEVEL_2) - thread_add_timer( + event_add_timer( master, send_l2_psnp, circuit, isis_jitter(circuit->psnp_interval[1], PSNP_JITTER), &circuit->t_send_psnp[1]); @@ -863,12 +861,12 @@ void isis_circuit_down(struct isis_circuit *circuit) memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); memset(circuit->u.bc.snpa, 0, ETH_ALEN); - THREAD_OFF(circuit->u.bc.t_send_lan_hello[0]); - THREAD_OFF(circuit->u.bc.t_send_lan_hello[1]); - THREAD_OFF(circuit->u.bc.t_run_dr[0]); - THREAD_OFF(circuit->u.bc.t_run_dr[1]); - THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[0]); - THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[1]); + EVENT_OFF(circuit->u.bc.t_send_lan_hello[0]); + EVENT_OFF(circuit->u.bc.t_send_lan_hello[1]); + EVENT_OFF(circuit->u.bc.t_run_dr[0]); + EVENT_OFF(circuit->u.bc.t_run_dr[1]); + EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[0]); + EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[1]); circuit->lsp_regenerate_pending[0] = 0; circuit->lsp_regenerate_pending[1] = 0; @@ -878,7 +876,7 @@ void isis_circuit_down(struct isis_circuit *circuit) } else if (circuit->circ_type == CIRCUIT_T_P2P) { isis_delete_adj(circuit->u.p2p.neighbor); circuit->u.p2p.neighbor = NULL; - THREAD_OFF(circuit->u.p2p.t_send_p2p_hello); + EVENT_OFF(circuit->u.p2p.t_send_p2p_hello); } /* @@ -891,11 +889,11 @@ void isis_circuit_down(struct isis_circuit *circuit) circuit->snmp_adj_idx_gen = 0; /* Cancel all active threads */ - THREAD_OFF(circuit->t_send_csnp[0]); - THREAD_OFF(circuit->t_send_csnp[1]); - THREAD_OFF(circuit->t_send_psnp[0]); - THREAD_OFF(circuit->t_send_psnp[1]); - THREAD_OFF(circuit->t_read); + EVENT_OFF(circuit->t_send_csnp[0]); + EVENT_OFF(circuit->t_send_csnp[1]); + EVENT_OFF(circuit->t_send_psnp[0]); + EVENT_OFF(circuit->t_send_psnp[1]); + EVENT_OFF(circuit->t_read); if (circuit->tx_queue) { isis_tx_queue_free(circuit->tx_queue); @@ -929,7 +927,7 @@ void isis_circuit_down(struct isis_circuit *circuit) circuit->snd_stream = NULL; } - thread_cancel_event(master, circuit); + event_cancel_event(master, circuit); return; } @@ -996,8 +994,8 @@ void isis_circuit_print_json(struct isis_circuit *circuit, json_object_string_add(iface_json, "level", circuit_t2string(circuit->is_type)); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - json_object_string_add(iface_json, "snpa", - snpa_print(circuit->u.bc.snpa)); + json_object_string_addf(iface_json, "snpa", "%pSY", + circuit->u.bc.snpa); levels_json = json_object_new_array(); @@ -1029,7 +1027,8 @@ void isis_circuit_print_json(struct isis_circuit *circuit, circuit->hello_multiplier[0]); json_object_string_add( hold_json, "pad", - (circuit->pad_hellos ? "yes" : "no")); + isis_hello_padding2string( + circuit->pad_hellos)); json_object_int_add(level_json, "cnsp-interval", circuit->csnp_interval[0]); json_object_int_add(level_json, "psnp-interval", @@ -1122,8 +1121,7 @@ void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, circuit_type2string(circuit->circ_type)); vty_out(vty, ", Level: %s", circuit_t2string(circuit->is_type)); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - vty_out(vty, ", SNPA: %-10s", - snpa_print(circuit->u.bc.snpa)); + vty_out(vty, ", SNPA: %-10pSY", circuit->u.bc.snpa); vty_out(vty, "\n"); if (circuit->is_type & IS_LEVEL_1) { vty_out(vty, " Level-1 Information:\n"); @@ -1137,11 +1135,11 @@ 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, Padding: %s\n", circuit->hello_interval[0], circuit->hello_multiplier[0], - (circuit->pad_hellos ? "(pad)" - : "(no-pad)")); + isis_hello_padding2string( + circuit->pad_hellos)); vty_out(vty, " CNSP interval: %u, PSNP interval: %u\n", circuit->csnp_interval[0], @@ -1169,11 +1167,11 @@ 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, Padding: %s\n", circuit->hello_interval[1], circuit->hello_multiplier[1], - (circuit->pad_hellos ? "(pad)" - : "(no-pad)")); + isis_hello_padding2string( + circuit->pad_hellos)); vty_out(vty, " CNSP interval: %u, PSNP interval: %u\n", circuit->csnp_interval[1], @@ -1319,11 +1317,20 @@ static int isis_interface_config_write(struct vty *vty) } } - /* ISIS - Hello padding - Defaults to true so only - * display if false */ - if (circuit->pad_hellos == 0) { + /* ISIS - Hello padding - Defaults to always so only + * display if not always */ + switch (circuit->pad_hellos) { + case ISIS_HELLO_PADDING_DISABLED: vty_out(vty, " no " PROTO_NAME " hello padding\n"); write++; + break; + case ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION: + vty_out(vty, PROTO_NAME + " hello padding during-adjacency-formation\n"); + write++; + break; + case ISIS_HELLO_PADDING_ALWAYS: + break; } if (circuit->disable_threeway_adj) { diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 494e96b6978c..7acde419fcfa 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -41,21 +41,21 @@ struct metric { struct isis_bcast_info { 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 event *t_run_dr[ISIS_LEVELS]; /* DR election thread */ + struct event *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[ISIS_LEVELS]; /* refresh pseudo-node + struct event *t_refresh_pseudo_lsp[ISIS_LEVELS]; /* refresh pseudo-node LSPs */ }; struct isis_p2p_info { struct isis_adjacency *neighbor; - struct thread *t_send_p2p_hello; /* send P2P IIHs in this thread */ + struct event *t_send_p2p_hello; /* send P2P IIHs in this thread */ }; struct isis_circuit_arg { @@ -63,6 +63,15 @@ struct isis_circuit_arg { struct isis_circuit *circuit; }; +/* + * Hello padding types + */ +enum isis_hello_padding { + ISIS_HELLO_PADDING_ALWAYS, + ISIS_HELLO_PADDING_DISABLED, + ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION +}; + struct isis_circuit { enum isis_circuit_state state; uint8_t circuit_id; /* l1/l2 bcast CircuitID */ @@ -76,9 +85,9 @@ struct isis_circuit { /* * Threads */ - struct thread *t_read; - struct thread *t_send_csnp[ISIS_LEVELS]; - struct thread *t_send_psnp[ISIS_LEVELS]; + struct event *t_read; + struct event *t_send_csnp[ISIS_LEVELS]; + struct event *t_send_psnp[ISIS_LEVELS]; struct isis_tx_queue *tx_queue; struct isis_circuit_arg level_arg[ISIS_LEVELS]; /* used as argument for threads */ @@ -100,7 +109,7 @@ struct isis_circuit { struct isis_p2p_info p2p; } u; uint8_t priority[ISIS_LEVELS]; /* l1/2 IS configured priority */ - int pad_hellos; /* add padding to Hello PDUs ? */ + enum isis_hello_padding pad_hellos; /* type of Hello PDUs padding */ char ext_domain; /* externalDomain (boolean) */ int lsp_regenerate_pending[ISIS_LEVELS]; uint64_t lsp_error_counter; diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c index 4a598aa8c9ed..7c7a8d238941 100644 --- a/isisd/isis_cli.c +++ b/isisd/isis_cli.c @@ -23,6 +23,7 @@ #include "isisd/isis_misc.h" #include "isisd/isis_circuit.h" #include "isisd/isis_csm.h" +#include "isisd/isis_flex_algo.h" #include "isisd/isis_cli_clippy.c" @@ -1126,6 +1127,53 @@ void cli_show_isis_purge_origin(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, " purge-originator\n"); } + +/* + * XPath: /frr-isisd:isis/instance/admin-group-send-zero + */ +DEFPY_YANG(isis_admin_group_send_zero, isis_admin_group_send_zero_cmd, + "[no] admin-group-send-zero", + NO_STR + "Allow sending the default admin-group value of 0x00000000.\n") +{ + nb_cli_enqueue_change(vty, "./admin-group-send-zero", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_admin_group_send_zero(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + vty_out(vty, " admin-group-send-zero\n"); +} + + +/* + * XPath: /frr-isisd:isis/instance/asla-legacy-flag + */ +DEFPY_HIDDEN(isis_asla_legacy_flag, isis_asla_legacy_flag_cmd, + "[no] asla-legacy-flag", + NO_STR "Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV.\n") +{ + nb_cli_enqueue_change(vty, "./asla-legacy-flag", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_asla_legacy_flag(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + vty_out(vty, " asla-legacy-flag\n"); +} + /* * XPath: /frr-isisd:isis/instance/mpls-te */ @@ -1769,6 +1817,122 @@ void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, "\n"); } +#ifndef FABRICD +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid + */ +DEFPY_YANG( + isis_sr_prefix_sid_algorithm, isis_sr_prefix_sid_algorithm_cmd, + "segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\ + algorithm (128-255)$algorithm\ + <absolute$sid_type (16-1048575)$sid_value|index$sid_type (0-65535)$sid_value>\ + [<no-php-flag|explicit-null>$lh_behavior] [n-flag-clear$n_flag_clear]", + SR_STR + "Prefix SID\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n" + "Algorithm Specific Prefix SID Configuration\n" + "Algorithm number\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" + "Not a node SID\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 if (strmatch(lh_behavior, "explicit-null")) + value = "explicit-null"; + else + value = "php"; + + 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); + nb_cli_enqueue_change(vty, "./n-flag-clear", NB_OP_MODIFY, + n_flag_clear ? "true" : "false"); + + return nb_cli_apply_changes( + vty, + "./segment-routing/algorithm-prefix-sids/algorithm-prefix-sid[prefix='%s'][algo='%s']", + prefix_str, algorithm_str); +} + +DEFPY_YANG( + no_isis_sr_prefix_algorithm_sid, no_isis_sr_prefix_sid_algorithm_cmd, + "no segment-routing prefix <A.B.C.D/M|X:X::X:X/M>$prefix\ + algorithm (128-255)$algorithm\ + [<absolute$sid_type (16-1048575)|index (0-65535)> [<no-php-flag|explicit-null>]]\ + [n-flag-clear]", + NO_STR SR_STR + "Prefix SID\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n" + "Algorithm Specific Prefix SID Configuration\n" + "Algorithm number\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" + "Not a node SID\n") +{ + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes( + vty, + "./segment-routing/algorithm-prefix-sids/algorithm-prefix-sid[prefix='%s'][algo='%s']", + prefix_str, algorithm_str); + return CMD_SUCCESS; +} +#endif /* ifndef FABRICD */ + +void cli_show_isis_prefix_sid_algorithm(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + const char *prefix; + const char *lh_behavior; + const char *sid_value_type; + const char *sid_value; + bool n_flag_clear; + uint32_t algorithm; + + prefix = yang_dnode_get_string(dnode, "./prefix"); + sid_value_type = yang_dnode_get_string(dnode, "./sid-value-type"); + sid_value = yang_dnode_get_string(dnode, "./sid-value"); + algorithm = yang_dnode_get_uint32(dnode, "./algo"); + lh_behavior = yang_dnode_get_string(dnode, "./last-hop-behavior"); + n_flag_clear = yang_dnode_get_bool(dnode, "./n-flag-clear"); + + vty_out(vty, " segment-routing prefix %s", prefix); + vty_out(vty, " algorithm %u", algorithm); + 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"); + if (n_flag_clear) + vty_out(vty, " n-flag-clear"); + vty_out(vty, "\n"); +} /* * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/lfa/priority-limit @@ -2262,15 +2426,22 @@ void cli_show_ip_isis_threeway_shake(struct vty *vty, /* * XPath: /frr-interface:lib/interface/frr-isisd: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" - "Pad hello packets\n") +DEFPY_YANG(isis_hello_padding, isis_hello_padding_cmd, + "[no] isis hello padding [during-adjacency-formation]$padding_type", + NO_STR + "IS-IS routing protocol\n" + "Type of padding for IS-IS hello packets\n" + "Pad hello packets\n" + "Add padding to hello packets during adjacency formation only.\n") { - nb_cli_enqueue_change(vty, "./frr-isisd:isis/hello/padding", - NB_OP_MODIFY, no ? "false" : "true"); - + if (no) { + nb_cli_enqueue_change(vty, "./frr-isisd:isis/hello/padding", + NB_OP_MODIFY, "disabled"); + } else { + nb_cli_enqueue_change(vty, "./frr-isisd:isis/hello/padding", + NB_OP_MODIFY, + padding_type ? padding_type : "always"); + } return nb_cli_apply_changes(vty, NULL); } @@ -2278,10 +2449,13 @@ void cli_show_ip_isis_hello_padding(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - if (!yang_dnode_get_bool(dnode, NULL)) + int hello_padding_type = yang_dnode_get_enum(dnode, NULL); + if (hello_padding_type == ISIS_HELLO_PADDING_DISABLED) vty_out(vty, " no"); - - vty_out(vty, " isis hello padding\n"); + vty_out(vty, " isis hello padding"); + if (hello_padding_type == ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION) + vty_out(vty, " during-adjacency-formation"); + vty_out(vty, "\n"); } /* @@ -2548,6 +2722,24 @@ void cli_show_ip_isis_circ_type(struct vty *vty, const struct lyd_node *dnode, } } +static int ag_change(struct vty *vty, int argc, struct cmd_token **argv, + const char *xpath, bool no, int start_idx) +{ + for (int i = start_idx; i < argc; i++) + nb_cli_enqueue_change(vty, xpath, + no ? NB_OP_DESTROY : NB_OP_CREATE, + argv[i]->arg); + return nb_cli_apply_changes(vty, NULL); +} + +static int ag_iter_cb(const struct lyd_node *dnode, void *arg) +{ + struct vty *vty = (struct vty *)arg; + + vty_out(vty, " %s", yang_dnode_get_string(dnode, ".")); + return YANG_ITER_CONTINUE; +} + /* * XPath: /frr-interface:lib/interface/frr-isisd:isis/network-type */ @@ -3015,6 +3207,26 @@ void cli_show_isis_log_adjacency(struct vty *vty, const struct lyd_node *dnode, vty_out(vty, " log-adjacency-changes\n"); } +/* + * XPath: /frr-isisd:isis/instance/log-pdu-drops + */ +DEFPY_YANG(log_pdu_drops, log_pdu_drops_cmd, "[no] log-pdu-drops", + NO_STR "Log any dropped PDUs\n") +{ + nb_cli_enqueue_change(vty, "./log-pdu-drops", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_log_pdu_drops(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + if (!yang_dnode_get_bool(dnode, NULL)) + vty_out(vty, " no"); + vty_out(vty, " log-pdu-drops\n"); +} + /* * XPath: /frr-isisd:isis/instance/mpls/ldp-sync */ @@ -3152,6 +3364,250 @@ void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, yang_dnode_get_string(dnode, NULL)); } +DEFPY_YANG_NOSH(flex_algo, flex_algo_cmd, "flex-algo (128-255)$algorithm", + "Flexible Algorithm\n" + "Flexible Algorithm Number\n") +{ + int ret; + char xpath[XPATH_MAXLEN + 37]; + + snprintf(xpath, sizeof(xpath), + "%s/flex-algos/flex-algo[flex-algo='%ld']", VTY_CURR_XPATH, + algorithm); + + nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL); + + ret = nb_cli_apply_changes( + vty, "./flex-algos/flex-algo[flex-algo='%ld']", algorithm); + if (ret == CMD_SUCCESS) + VTY_PUSH_XPATH(ISIS_FLEX_ALGO_NODE, xpath); + + return ret; +} + +DEFPY_YANG(no_flex_algo, no_flex_algo_cmd, "no flex-algo (128-255)$algorithm", + NO_STR + "Flexible Algorithm\n" + "Flexible Algorithm Number\n") +{ + char xpath[XPATH_MAXLEN + 37]; + + snprintf(xpath, sizeof(xpath), + "%s/flex-algos/flex-algo[flex-algo='%ld']", VTY_CURR_XPATH, + algorithm); + + if (!yang_dnode_exists(vty->candidate_config->dnode, xpath)) { + vty_out(vty, "ISIS flex-algo %ld isn't exist.\n", algorithm); + return CMD_ERR_NO_MATCH; + } + + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + return nb_cli_apply_changes_clear_pending( + vty, "./flex-algos/flex-algo[flex-algo='%ld']", algorithm); +} + +DEFPY_YANG(advertise_definition, advertise_definition_cmd, + "[no] advertise-definition", + NO_STR "Advertise Local Flexible Algorithm\n") +{ + nb_cli_enqueue_change(vty, "./advertise-definition", + no ? NB_OP_DESTROY : NB_OP_CREATE, + no ? NULL : "true"); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(affinity_include_any, affinity_include_any_cmd, + "[no] affinity include-any NAME...", + NO_STR + "Affinity configuration\n" + "Any Include with\n" + "Include NAME list\n") +{ + const char *xpath = "./affinity-include-anies/affinity-include-any"; + + return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2); +} + +DEFPY_YANG(affinity_include_all, affinity_include_all_cmd, + "[no] affinity include-all NAME...", + NO_STR + "Affinity configuration\n" + "All Include with\n" + "Include NAME list\n") +{ + const char *xpath = "./affinity-include-alls/affinity-include-all"; + + return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2); +} + +DEFPY_YANG(affinity_exclude_any, affinity_exclude_any_cmd, + "[no] affinity exclude-any NAME...", + NO_STR + "Affinity configuration\n" + "Any Exclude with\n" + "Exclude NAME list\n") +{ + const char *xpath = "./affinity-exclude-anies/affinity-exclude-any"; + + return ag_change(vty, argc, argv, xpath, no, no ? 3 : 2); +} + +DEFPY_YANG(prefix_metric, prefix_metric_cmd, "[no] prefix-metric", + NO_STR "Use Flex-Algo Prefix Metric\n") +{ + nb_cli_enqueue_change(vty, "./prefix-metric", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(dplane_sr_mpls, dplane_sr_mpls_cmd, "[no] dataplane sr-mpls", + NO_STR + "Advertise and participate in the specified Data-Planes\n" + "Advertise and participate in SR-MPLS data-plane\n") +{ + nb_cli_enqueue_change(vty, "./dplane-sr-mpls", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_HIDDEN(dplane_srv6, dplane_srv6_cmd, "[no] dataplane srv6", + NO_STR + "Advertise and participate in the specified Data-Planes\n" + "Advertise and participate in SRv6 data-plane\n") +{ + + nb_cli_enqueue_change(vty, "./dplane-srv6", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_HIDDEN(dplane_ip, dplane_ip_cmd, "[no] dataplane ip", + NO_STR + "Advertise and participate in the specified Data-Planes\n" + "Advertise and participate in IP data-plane\n") +{ + nb_cli_enqueue_change(vty, "./dplane-ip", + no ? NB_OP_DESTROY : NB_OP_CREATE, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(metric_type, metric_type_cmd, + "[no] metric-type [igp$igp|te$te|delay$delay]", + NO_STR + "Metric-type used by flex-algo calculation\n" + "Use IGP metric (default)\n" + "Use Delay as metric\n" + "Use Traffic Engineering metric\n") +{ + const char *type = NULL; + + if (igp) { + type = "igp"; + } else if (te) { + type = "te-default"; + } else if (delay) { + type = "min-uni-link-delay"; + } else { + vty_out(vty, "Error: unknown metric type\n"); + return CMD_SUCCESS; + } + + if (!igp) + vty_out(vty, + "Warning: this version can advertise a Flex-Algorithm Definition (FAD) with the %s metric.\n" + "However, participation in a Flex-Algorithm with such a metric is not yet supported.\n", + type); + + nb_cli_enqueue_change(vty, "./metric-type", + no ? NB_OP_DESTROY : NB_OP_MODIFY, + no ? NULL : type); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(priority, priority_cmd, "[no] priority (0-255)$priority", + NO_STR + "Flex-Algo definition priority\n" + "Priority value\n") +{ + nb_cli_enqueue_change(vty, "./priority", + no ? NB_OP_DESTROY : NB_OP_MODIFY, + no ? NULL : priority_str); + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + uint32_t algorithm; + enum flex_algo_metric_type metric_type; + uint32_t priority; + char type_str[10]; + + algorithm = yang_dnode_get_uint32(dnode, "./flex-algo"); + vty_out(vty, " flex-algo %u\n", algorithm); + + if (yang_dnode_exists(dnode, "./advertise-definition")) + vty_out(vty, " advertise-definition\n"); + + if (yang_dnode_exists(dnode, "./dplane-sr-mpls")) + vty_out(vty, " dataplane sr-mpls\n"); + if (yang_dnode_exists(dnode, "./dplane-srv6")) + vty_out(vty, " dataplane srv6\n"); + if (yang_dnode_exists(dnode, "./dplane-ip")) + vty_out(vty, " dataplane ip\n"); + + if (yang_dnode_exists(dnode, "./prefix-metric")) + vty_out(vty, " prefix-metric\n"); + + if (yang_dnode_exists(dnode, "./metric-type")) { + metric_type = yang_dnode_get_enum(dnode, "./metric-type"); + if (metric_type != MT_IGP) { + flex_algo_metric_type_print(type_str, sizeof(type_str), + metric_type); + vty_out(vty, " metric-type %s\n", type_str); + } + } + + if (yang_dnode_exists(dnode, "./priority")) { + priority = yang_dnode_get_uint32(dnode, "./priority"); + if (priority != FLEX_ALGO_PRIO_DEFAULT) + vty_out(vty, " priority %u\n", priority); + } + + if (yang_dnode_exists(dnode, + "./affinity-include-alls/affinity-include-all")) { + vty_out(vty, " affinity include-all"); + yang_dnode_iterate( + ag_iter_cb, vty, dnode, + "./affinity-include-alls/affinity-include-all"); + vty_out(vty, "\n"); + } + + if (yang_dnode_exists( + dnode, "./affinity-include-anies/affinity-include-any")) { + vty_out(vty, " affinity include-any"); + yang_dnode_iterate( + ag_iter_cb, vty, dnode, + "./affinity-include-anies/affinity-include-any"); + vty_out(vty, "\n"); + } + + if (yang_dnode_exists( + dnode, "./affinity-exclude-anies/affinity-exclude-any")) { + vty_out(vty, " affinity exclude-any"); + yang_dnode_iterate( + ag_iter_cb, vty, dnode, + "./affinity-exclude-anies/affinity-exclude-any"); + vty_out(vty, "\n"); + } +} + +void cli_show_isis_flex_algo_end(struct vty *vty, const struct lyd_node *dnode) +{ + vty_out(vty, " !\n"); +} + + void isis_cli_init(void) { install_element(CONFIG_NODE, &router_isis_cmd); @@ -3210,6 +3666,9 @@ void isis_cli_init(void) install_element(ISIS_NODE, &area_purge_originator_cmd); + install_element(ISIS_NODE, &isis_admin_group_send_zero_cmd); + install_element(ISIS_NODE, &isis_asla_legacy_flag_cmd); + install_element(ISIS_NODE, &isis_mpls_te_on_cmd); install_element(ISIS_NODE, &no_isis_mpls_te_on_cmd); install_element(ISIS_NODE, &isis_mpls_te_router_addr_cmd); @@ -3233,6 +3692,10 @@ void isis_cli_init(void) 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); +#ifndef FABRICD + install_element(ISIS_NODE, &isis_sr_prefix_sid_algorithm_cmd); + install_element(ISIS_NODE, &no_isis_sr_prefix_sid_algorithm_cmd); +#endif /* ifndef FABRICD */ install_element(ISIS_NODE, &isis_frr_lfa_priority_limit_cmd); install_element(ISIS_NODE, &isis_frr_lfa_tiebreaker_cmd); install_element(ISIS_NODE, &isis_frr_lfa_load_sharing_cmd); @@ -3280,6 +3743,7 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &isis_ti_lfa_cmd); install_element(ISIS_NODE, &log_adj_changes_cmd); + install_element(ISIS_NODE, &log_pdu_drops_cmd); install_element(ISIS_NODE, &isis_mpls_ldp_sync_cmd); install_element(ISIS_NODE, &no_isis_mpls_ldp_sync_cmd); @@ -3288,6 +3752,19 @@ void isis_cli_init(void) install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_cmd); install_element(INTERFACE_NODE, &isis_mpls_if_ldp_sync_holddown_cmd); install_element(INTERFACE_NODE, &no_isis_mpls_if_ldp_sync_holddown_cmd); + + install_element(ISIS_NODE, &flex_algo_cmd); + install_element(ISIS_NODE, &no_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &advertise_definition_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &affinity_include_any_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &affinity_include_all_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &affinity_exclude_any_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &dplane_sr_mpls_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &dplane_srv6_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &dplane_ip_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &prefix_metric_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &metric_type_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &priority_cmd); } #endif /* ifndef FABRICD */ diff --git a/isisd/isis_common.h b/isisd/isis_common.h index c908dc2e2463..2ded68f84d5b 100644 --- a/isisd/isis_common.h +++ b/isisd/isis_common.h @@ -11,14 +11,6 @@ #ifndef ISIS_COMMON_H #define ISIS_COMMON_H -/* - * Area Address - */ -struct area_addr { - uint8_t addr_len; - uint8_t area_addr[20]; -}; - struct isis_passwd { uint8_t len; #define ISIS_PASSWD_TYPE_UNUSED 0 diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 95bbc077a32c..9e278d47f81e 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -14,7 +14,7 @@ #include "if.h" #include "linklist.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "prefix.h" #include "stream.h" diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index b1a42158130b..3b9216080926 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -13,7 +13,7 @@ #include "log.h" #include "hash.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "stream.h" @@ -48,9 +48,9 @@ const char *isis_disflag2string(int disflag) return NULL; /* not reached */ } -void isis_run_dr(struct thread *thread) +void isis_run_dr(struct event *thread) { - struct isis_circuit_arg *arg = THREAD_ARG(thread); + struct isis_circuit_arg *arg = EVENT_ARG(thread); assert(arg); @@ -211,8 +211,8 @@ int isis_dr_resign(struct isis_circuit *circuit, int level) circuit->u.bc.is_dr[level - 1] = 0; circuit->u.bc.run_dr_elect[level - 1] = 0; - THREAD_OFF(circuit->u.bc.t_run_dr[level - 1]); - THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + EVENT_OFF(circuit->u.bc.t_run_dr[level - 1]); + EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); circuit->lsp_regenerate_pending[level - 1] = 0; memcpy(id, circuit->isis->sysid, ISIS_SYS_ID_LEN); @@ -223,29 +223,27 @@ int isis_dr_resign(struct isis_circuit *circuit, int level) if (level == 1) { memset(circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); - thread_add_timer(master, send_l1_psnp, circuit, - isis_jitter(circuit->psnp_interval[level - 1], - PSNP_JITTER), - &circuit->t_send_psnp[0]); + event_add_timer(master, send_l1_psnp, circuit, + isis_jitter(circuit->psnp_interval[level - 1], + PSNP_JITTER), + &circuit->t_send_psnp[0]); } else { memset(circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); - thread_add_timer(master, send_l2_psnp, circuit, - isis_jitter(circuit->psnp_interval[level - 1], - PSNP_JITTER), - &circuit->t_send_psnp[1]); + event_add_timer(master, send_l2_psnp, circuit, + isis_jitter(circuit->psnp_interval[level - 1], + PSNP_JITTER), + &circuit->t_send_psnp[1]); } - THREAD_OFF(circuit->t_send_csnp[level - 1]); + EVENT_OFF(circuit->t_send_csnp[level - 1]); - thread_add_timer(master, isis_run_dr, - &circuit->level_arg[level - 1], - 2 * circuit->hello_interval[level - 1], - &circuit->u.bc.t_run_dr[level - 1]); + event_add_timer(master, isis_run_dr, &circuit->level_arg[level - 1], + 2 * circuit->hello_interval[level - 1], + &circuit->u.bc.t_run_dr[level - 1]); - thread_add_event(master, isis_event_dis_status_change, circuit, 0, - NULL); + event_add_event(master, isis_event_dis_status_change, circuit, 0, NULL); return ISIS_OK; } @@ -276,10 +274,10 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) assert(circuit->circuit_id); /* must be non-zero */ lsp_generate_pseudo(circuit, 1); - thread_add_timer(master, send_l1_csnp, circuit, - isis_jitter(circuit->csnp_interval[level - 1], - CSNP_JITTER), - &circuit->t_send_csnp[0]); + event_add_timer(master, send_l1_csnp, circuit, + isis_jitter(circuit->csnp_interval[level - 1], + CSNP_JITTER), + &circuit->t_send_csnp[0]); } else { memcpy(old_dr, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); @@ -296,18 +294,16 @@ int isis_dr_commence(struct isis_circuit *circuit, int level) assert(circuit->circuit_id); /* must be non-zero */ lsp_generate_pseudo(circuit, 2); - thread_add_timer(master, send_l2_csnp, circuit, - isis_jitter(circuit->csnp_interval[level - 1], - CSNP_JITTER), - &circuit->t_send_csnp[1]); + event_add_timer(master, send_l2_csnp, circuit, + isis_jitter(circuit->csnp_interval[level - 1], + CSNP_JITTER), + &circuit->t_send_csnp[1]); } - thread_add_timer(master, isis_run_dr, - &circuit->level_arg[level - 1], - 2 * circuit->hello_interval[level - 1], - &circuit->u.bc.t_run_dr[level - 1]); - thread_add_event(master, isis_event_dis_status_change, circuit, 0, - NULL); + event_add_timer(master, isis_run_dr, &circuit->level_arg[level - 1], + 2 * circuit->hello_interval[level - 1], + &circuit->u.bc.t_run_dr[level - 1]); + event_add_event(master, isis_event_dis_status_change, circuit, 0, NULL); return ISIS_OK; } diff --git a/isisd/isis_dr.h b/isisd/isis_dr.h index 2b9bf60ffa79..135916a4cb07 100644 --- a/isisd/isis_dr.h +++ b/isisd/isis_dr.h @@ -11,7 +11,7 @@ #ifndef _ZEBRA_ISIS_DR_H #define _ZEBRA_ISIS_DR_H -void isis_run_dr(struct thread *thread); +void isis_run_dr(struct event *thread); int isis_dr_elect(struct isis_circuit *circuit, int level); int isis_dr_resign(struct isis_circuit *circuit, int level); int isis_dr_commence(struct isis_circuit *circuit, int level); diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index ef21de327eb6..61c49d08a742 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -16,7 +16,7 @@ #include "stream.h" #include "command.h" #include "if.h" -#include "thread.h" +#include "frrevent.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" @@ -29,14 +29,14 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_DYNHN, "ISIS dyn hostname"); -static void dyn_cache_cleanup(struct thread *); +static void dyn_cache_cleanup(struct event *); void dyn_cache_init(struct isis *isis) { isis->dyn_cache = list_new(); if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) - thread_add_timer(master, dyn_cache_cleanup, isis, 120, - &isis->t_dync_clean); + event_add_timer(master, dyn_cache_cleanup, isis, 120, + &isis->t_dync_clean); } void dyn_cache_finish(struct isis *isis) @@ -44,7 +44,7 @@ void dyn_cache_finish(struct isis *isis) struct listnode *node, *nnode; struct isis_dynhn *dyn; - THREAD_OFF(isis->t_dync_clean); + EVENT_OFF(isis->t_dync_clean); for (ALL_LIST_ELEMENTS(isis->dyn_cache, node, nnode, dyn)) { list_delete_node(isis->dyn_cache, node); @@ -54,14 +54,14 @@ void dyn_cache_finish(struct isis *isis) list_delete(&isis->dyn_cache); } -static void dyn_cache_cleanup(struct thread *thread) +static void dyn_cache_cleanup(struct event *thread) { struct listnode *node, *nnode; struct isis_dynhn *dyn; time_t now = time(NULL); struct isis *isis = NULL; - isis = THREAD_ARG(thread); + isis = EVENT_ARG(thread); isis->t_dync_clean = NULL; @@ -72,7 +72,7 @@ static void dyn_cache_cleanup(struct thread *thread) XFREE(MTYPE_ISIS_DYNHN, dyn); } - thread_add_timer(master, dyn_cache_cleanup, isis, 120, + event_add_timer(master, dyn_cache_cleanup, isis, 120, &isis->t_dync_clean); } @@ -145,12 +145,10 @@ void dynhn_print_all(struct vty *vty, struct isis *isis) vty_out(vty, "Level System ID Dynamic Hostname\n"); for (ALL_LIST_ELEMENTS_RO(isis->dyn_cache, node, dyn)) { vty_out(vty, "%-7d", dyn->level); - vty_out(vty, "%-15s%-15s\n", sysid_print(dyn->id), - dyn->hostname); + vty_out(vty, "%pSY %-15s\n", dyn->id, dyn->hostname); } - vty_out(vty, " * %s %s\n", sysid_print(isis->sysid), - cmd_hostname_get()); + vty_out(vty, " * %pSY %s\n", isis->sysid, cmd_hostname_get()); return; } diff --git a/isisd/isis_events.c b/isisd/isis_events.c index d7b396911408..32231a079f1a 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -13,7 +13,7 @@ #include "if.h" #include "linklist.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "prefix.h" #include "stream.h" @@ -63,23 +63,23 @@ static void circuit_commence_level(struct isis_circuit *circuit, int level) if (!circuit->is_passive) { if (level == 1) { - thread_add_timer(master, send_l1_psnp, circuit, - isis_jitter(circuit->psnp_interval[0], - PSNP_JITTER), - &circuit->t_send_psnp[0]); + event_add_timer(master, send_l1_psnp, circuit, + isis_jitter(circuit->psnp_interval[0], + PSNP_JITTER), + &circuit->t_send_psnp[0]); } else { - thread_add_timer(master, send_l2_psnp, circuit, - isis_jitter(circuit->psnp_interval[1], - PSNP_JITTER), - &circuit->t_send_psnp[1]); + event_add_timer(master, send_l2_psnp, circuit, + isis_jitter(circuit->psnp_interval[1], + PSNP_JITTER), + &circuit->t_send_psnp[1]); } } if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - thread_add_timer(master, isis_run_dr, - &circuit->level_arg[level - 1], - 2 * circuit->hello_interval[level - 1], - &circuit->u.bc.t_run_dr[level - 1]); + event_add_timer(master, isis_run_dr, + &circuit->level_arg[level - 1], + 2 * circuit->hello_interval[level - 1], + &circuit->u.bc.t_run_dr[level - 1]); send_hello_sched(circuit, level, TRIGGERED_IIH_DELAY); circuit->u.bc.lan_neighs[level - 1] = list_new(); @@ -96,13 +96,13 @@ static void circuit_resign_level(struct isis_circuit *circuit, int level) circuit->area->area_tag, circuit->circuit_id, circuit->interface->name, level); - THREAD_OFF(circuit->t_send_csnp[idx]); - THREAD_OFF(circuit->t_send_psnp[idx]); + EVENT_OFF(circuit->t_send_csnp[idx]); + EVENT_OFF(circuit->t_send_psnp[idx]); if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - THREAD_OFF(circuit->u.bc.t_send_lan_hello[idx]); - THREAD_OFF(circuit->u.bc.t_run_dr[idx]); - THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[idx]); + EVENT_OFF(circuit->u.bc.t_send_lan_hello[idx]); + EVENT_OFF(circuit->u.bc.t_run_dr[idx]); + EVENT_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; @@ -196,11 +196,11 @@ void isis_circuit_is_type_set(struct isis_circuit *circuit, int newtype) /* events supporting code */ -void isis_event_dis_status_change(struct thread *thread) +void isis_event_dis_status_change(struct event *thread) { struct isis_circuit *circuit; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); /* invalid arguments */ if (!circuit || !circuit->area) @@ -217,8 +217,8 @@ void isis_event_auth_failure(char *area_tag, const char *error_string, uint8_t *sysid) { if (IS_DEBUG_EVENTS) - zlog_debug("ISIS-Evt (%s) Authentication failure %s from %s", - area_tag, error_string, sysid_print(sysid)); + zlog_debug("ISIS-Evt (%s) Authentication failure %s from %pSY", + area_tag, error_string, sysid); return; } diff --git a/isisd/isis_events.h b/isisd/isis_events.h index e827f75a832f..a0ac964fab58 100644 --- a/isisd/isis_events.h +++ b/isisd/isis_events.h @@ -18,7 +18,7 @@ void isis_event_circuit_type_change(struct isis_circuit *circuit, int newtype); /* * Events related to adjacencies */ -void isis_event_dis_status_change(struct thread *thread); +void isis_event_dis_status_change(struct event *thread); /* * Error events diff --git a/isisd/isis_flex_algo.c b/isisd/isis_flex_algo.c new file mode 100644 index 000000000000..ef30987b8e7c --- /dev/null +++ b/isisd/isis_flex_algo.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * isis_flex_algo.c: IS-IS Flexible Algorithm + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#include <zebra.h> + +#include "memory.h" +#include "stream.h" +#include "sbuf.h" +#include "network.h" +#include "command.h" +#include "bitfield.h" + +#include "isisd/isisd.h" +#include "isisd/isis_tlvs.h" +#include "isisd/isis_common.h" +#include "isisd/isis_mt.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_te.h" +#include "isisd/isis_sr.h" +#include "isisd/isis_spf_private.h" +#include "isisd/isis_flex_algo.h" + +#ifndef FABRICD +DEFINE_MTYPE_STATIC(ISISD, FLEX_ALGO, "ISIS Flex Algo"); + +void *isis_flex_algo_data_alloc(void *voidarg) +{ + struct isis_flex_algo_alloc_arg *arg = voidarg; + struct isis_flex_algo_data *data; + + data = XCALLOC(MTYPE_FLEX_ALGO, sizeof(struct isis_flex_algo_data)); + + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { + if (!(arg->area->is_type & level)) + continue; + data->spftree[tree][level - 1] = isis_spftree_new( + arg->area, &arg->area->lspdb[level - 1], + arg->area->isis->sysid, level, tree, + SPF_TYPE_FORWARD, 0, arg->algorithm); + } + } + + return data; +} + +void isis_flex_algo_data_free(void *voiddata) +{ + struct isis_flex_algo_data *data = voiddata; + + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) + if (data->spftree[tree][level - 1]) + isis_spftree_del( + data->spftree[tree][level - 1]); + XFREE(MTYPE_FLEX_ALGO, data); +} + +static struct isis_router_cap_fad * +isis_flex_algo_definition_cmp(struct isis_router_cap_fad *elected, + struct isis_router_cap_fad *fa) +{ + if (!elected || fa->fad.priority > elected->fad.priority || + (fa->fad.priority == elected->fad.priority && + lsp_id_cmp(fa->sysid, elected->sysid) > 0)) + return fa; + + return elected; +} + +/** + * @brief Look up the flex-algo definition with the highest priority in the LSP + * Database (LSDB). If the value of priority is the same, the flex-algo + * definition with the highest sysid will be selected. + * @param algorithm flex-algo algorithm number + * @param area pointer + * @param local router capability Flex-Algo Definition (FAD) double pointer. + * - fad is NULL: use the local router capability FAD from LSDB for the + * election. + * - fad is not NULL and *fad is NULL: use no local router capability FAD for + * the election. + * - fad and *fad are not NULL: uses the *fad local definition instead of the + * local definition from LSDB for the election. + * @return elected flex-algo-definition object if exist, else NULL + */ +static struct isis_router_cap_fad * +_isis_flex_algo_elected(int algorithm, const struct isis_area *area, + struct isis_router_cap_fad **fad) +{ + struct flex_algo *flex_ago; + const struct isis_lsp *lsp; + struct isis_router_cap_fad *fa, *elected = NULL; + + if (!flex_algo_id_valid(algorithm)) + return NULL; + + /* No elected FAD if the algorithm is not locally configured */ + flex_ago = flex_algo_lookup(area->flex_algos, algorithm); + if (!flex_ago) + return NULL; + + /* No elected FAD if no data-plane is enabled + * Currently, only Segment-Routing MPLS is supported. + * Segment-Routing SRv6 and IP will be configured in the future. + */ + if (!CHECK_FLAG(flex_ago->dataplanes, FLEX_ALGO_SR_MPLS)) + return NULL; + + /* + * Perform FAD comparison. First, compare the priority, and if they are + * the same, compare the sys-id. + */ + /* clang-format off */ + frr_each_const(lspdb, &area->lspdb[ISIS_LEVEL1 - 1], lsp) { + /* clang-format on */ + + if (!lsp->tlvs || !lsp->tlvs->router_cap) + continue; + + if (lsp->own_lsp && fad) + continue; + + fa = lsp->tlvs->router_cap->fads[algorithm]; + + if (!fa) + continue; + + assert(algorithm == fa->fad.algorithm); + + memcpy(fa->sysid, lsp->hdr.lsp_id, ISIS_SYS_ID_LEN + 2); + + elected = isis_flex_algo_definition_cmp(elected, fa); + } + + if (fad && *fad) + elected = isis_flex_algo_definition_cmp(elected, *fad); + + return elected; +} + +struct isis_router_cap_fad *isis_flex_algo_elected(int algorithm, + const struct isis_area *area) +{ + return _isis_flex_algo_elected(algorithm, area, NULL); +} + +/** + * @brief Check the Flex-Algo Definition is supported by the current FRR version + * @param flex-algo + * @return true if supported else false + */ +bool isis_flex_algo_supported(struct flex_algo *fad) +{ + if (fad->calc_type != CALC_TYPE_SPF) + return false; + if (fad->metric_type != MT_IGP) + return false; + if (fad->flags != 0) + return false; + if (fad->exclude_srlg) + return false; + if (fad->unsupported_subtlv) + return false; + + return true; +} + +/** + * @brief Look for the elected Flex-Algo Definition and check that it is + * supported by the current FRR version + * @param algorithm flex-algo algorithm number + * @param area pointer + * @param local router capability Flex-Algo Definition (FAD) double pointer. + * @return elected flex-algo-definition object if exist and supported, else NULL + */ +static struct isis_router_cap_fad * +_isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area, + struct isis_router_cap_fad **fad) +{ + struct isis_router_cap_fad *elected_fad; + + elected_fad = _isis_flex_algo_elected(algorithm, area, fad); + if (!elected_fad) + return NULL; + + if (isis_flex_algo_supported(&elected_fad->fad)) + return elected_fad; + + return NULL; +} + +struct isis_router_cap_fad * +isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area) +{ + return _isis_flex_algo_elected_supported(algorithm, area, NULL); +} + +struct isis_router_cap_fad * +isis_flex_algo_elected_supported_local_fad(int algorithm, + const struct isis_area *area, + struct isis_router_cap_fad **fad) +{ + return _isis_flex_algo_elected_supported(algorithm, area, fad); +} + +/** + * Check LSP is participating specified SR Algorithm + * + * @param lsp IS-IS lsp + * @param algorithm SR Algorithm + * @return Return true if sr-algorithm tlv includes specified + * algorithm in router capability tlv + */ +bool sr_algorithm_participated(const struct isis_lsp *lsp, uint8_t algorithm) +{ + if (!lsp || !lsp->tlvs || !lsp->tlvs->router_cap) + return false; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (lsp->tlvs->router_cap->algo[i] == algorithm) + return true; + return false; +} + +bool isis_flex_algo_constraint_drop(struct isis_spftree *spftree, + struct isis_lsp *lsp, + struct isis_extended_reach *reach) +{ + bool ret; + struct isis_ext_subtlvs *subtlvs = reach->subtlvs; + struct isis_router_cap_fad *fad; + struct isis_asla_subtlvs *asla; + struct listnode *node; + uint32_t *link_admin_group = NULL; + uint32_t link_ext_admin_group_bitmap0; + struct admin_group *link_ext_admin_group = NULL; + + fad = isis_flex_algo_elected_supported(spftree->algorithm, + spftree->area); + if (!fad) + return true; + + for (ALL_LIST_ELEMENTS_RO(subtlvs->aslas, node, asla)) { + if (!CHECK_FLAG(asla->standard_apps, ISIS_SABM_FLAG_X)) + continue; + if (asla->legacy) { + if (IS_SUBTLV(subtlvs, EXT_ADM_GRP)) + link_admin_group = &subtlvs->adm_group; + + if (IS_SUBTLV(subtlvs, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&subtlvs->ext_admin_group) != + 0) + link_ext_admin_group = + &subtlvs->ext_admin_group; + } else { + if (IS_SUBTLV(asla, EXT_ADM_GRP)) + link_admin_group = &asla->admin_group; + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) + link_ext_admin_group = &asla->ext_admin_group; + } + break; + } + + /* RFC7308 section 2.3.1 + * A receiving node that notices that the AG differs from the first 32 + * bits of the EAG SHOULD report this mismatch to the operator. + */ + if (link_admin_group && link_ext_admin_group) { + link_ext_admin_group_bitmap0 = + admin_group_get_offset(link_ext_admin_group, 0); + if (*link_admin_group != link_ext_admin_group_bitmap0) + zlog_warn( + "ISIS-SPF: LSP from %pPN neighbor %pPN. Admin-group 0x%08x differs from ext admin-group 0x%08x.", + lsp->hdr.lsp_id, reach->id, *link_admin_group, + link_ext_admin_group_bitmap0); + } + + /* + * Exclude Any + */ + if (!admin_group_zero(&fad->fad.admin_group_exclude_any)) { + ret = admin_group_match_any(&fad->fad.admin_group_exclude_any, + link_admin_group, + link_ext_admin_group); + if (ret) + return true; + } + + /* + * Include Any + */ + if (!admin_group_zero(&fad->fad.admin_group_include_any)) { + ret = admin_group_match_any(&fad->fad.admin_group_include_any, + link_admin_group, + link_ext_admin_group); + if (!ret) + return true; + } + + /* + * Include All + */ + if (!admin_group_zero(&fad->fad.admin_group_include_all)) { + ret = admin_group_match_all(&fad->fad.admin_group_include_all, + link_admin_group, + link_ext_admin_group); + if (!ret) + return true; + } + + return false; +} + +#endif /* ifndef FABRICD */ diff --git a/isisd/isis_flex_algo.h b/isisd/isis_flex_algo.h new file mode 100644 index 000000000000..c475838243e2 --- /dev/null +++ b/isisd/isis_flex_algo.h @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * isis_flex_algo.h: IS-IS Flexible Algorithm + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#ifndef ISIS_FLEX_ALGO_H +#define ISIS_FLEX_ALGO_H + +#include "flex_algo.h" +#include "isisd/isis_constants.h" + +#ifndef FABRICD + +struct isis_flex_algo_data { + struct isis_spftree *spftree[SPFTREE_COUNT][ISIS_LEVELS]; + struct isis_area *area; +}; + +struct isis_flex_algo_alloc_arg { + uint8_t algorithm; + struct isis_area *area; +}; + +void *isis_flex_algo_data_alloc(void *arg); +void isis_flex_algo_data_free(void *data); + +struct isis_router_cap_fad * +isis_flex_algo_elected(int algorithm, const struct isis_area *area); +bool isis_flex_algo_supported(struct flex_algo *fad); +struct isis_router_cap_fad * +isis_flex_algo_elected_supported(int algorithm, const struct isis_area *area); +struct isis_router_cap_fad * +isis_flex_algo_elected_supported_local_fad(int algorithm, + const struct isis_area *area, + struct isis_router_cap_fad **fad); +struct isis_lsp; +bool sr_algorithm_participated(const struct isis_lsp *lsp, uint8_t algorithm); + +bool isis_flex_algo_constraint_drop(struct isis_spftree *spftree, + struct isis_lsp *lsp, + struct isis_extended_reach *reach); + +#endif /* ifndef FABRICD */ + +#endif /* ISIS_FLEX_ALGO_H */ diff --git a/isisd/isis_ldp_sync.c b/isisd/isis_ldp_sync.c index 817e2a201521..53676ffe3660 100644 --- a/isisd/isis_ldp_sync.c +++ b/isisd/isis_ldp_sync.c @@ -9,7 +9,7 @@ #include "monotime.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "table.h" #include "vty.h" @@ -171,7 +171,7 @@ void isis_ldp_sync_if_complete(struct isis_circuit *circuit) if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); isis_ldp_sync_set_if_metric(circuit, true); } @@ -191,7 +191,7 @@ void isis_ldp_sync_ldp_fail(struct isis_circuit *circuit) if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; isis_ldp_sync_set_if_metric(circuit, true); } @@ -331,7 +331,7 @@ void isis_ldp_sync_set_if_metric(struct isis_circuit *circuit, bool run_regen) /* * LDP-SYNC holddown timer routines */ -static void isis_ldp_sync_holddown_timer(struct thread *thread) +static void isis_ldp_sync_holddown_timer(struct event *thread) { struct isis_circuit *circuit; struct ldp_sync_info *ldp_sync_info; @@ -340,7 +340,7 @@ static void isis_ldp_sync_holddown_timer(struct thread *thread) * didn't receive msg from LDP indicating sync-complete * restore interface cost to original value */ - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); if (circuit->ldp_sync_info == NULL) return; @@ -373,9 +373,8 @@ void isis_ldp_sync_holddown_timer_add(struct isis_circuit *circuit) ils_debug("%s: start holddown timer for %s time %d", __func__, circuit->interface->name, ldp_sync_info->holddown); - thread_add_timer(master, isis_ldp_sync_holddown_timer, - circuit, ldp_sync_info->holddown, - &ldp_sync_info->t_holddown); + event_add_timer(master, isis_ldp_sync_holddown_timer, circuit, + ldp_sync_info->holddown, &ldp_sync_info->t_holddown); } /* @@ -517,7 +516,7 @@ void isis_if_ldp_sync_disable(struct isis_circuit *circuit) if (!CHECK_FLAG(area->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) return; - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; isis_ldp_sync_set_if_metric(circuit, true); } @@ -576,8 +575,8 @@ static void isis_circuit_ldp_sync_print_vty(struct isis_circuit *circuit, break; case LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP: if (ldp_sync_info->t_holddown != NULL) { - struct timeval remain = thread_timer_remain( - ldp_sync_info->t_holddown); + struct timeval remain = + event_timer_remain(ldp_sync_info->t_holddown); vty_out(vty, " Holddown timer is running %lld.%03lld remaining\n", (long long)remain.tv_sec, diff --git a/isisd/isis_lfa.c b/isisd/isis_lfa.c index 599dca12987e..6f21f4cea2cc 100644 --- a/isisd/isis_lfa.c +++ b/isisd/isis_lfa.c @@ -355,6 +355,7 @@ bool isis_lfa_excise_node_check(const struct isis_spftree *spftree, struct tilfa_find_pnode_prefix_sid_args { uint32_t sid_index; + int algorithm; }; static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix, @@ -368,15 +369,17 @@ static int tilfa_find_pnode_prefix_sid_cb(const struct prefix *prefix, if (!subtlvs || subtlvs->prefix_sids.count == 0) return LSP_ITER_CONTINUE; - psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; - - /* Require the node flag to be set. */ - if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE)) - return LSP_ITER_CONTINUE; - - args->sid_index = psid->value; - - return LSP_ITER_STOP; + for (psid = (struct isis_prefix_sid *)subtlvs->prefix_sids.head; psid; + psid = psid->next) { + /* Require the node flag to be set. */ + if (!CHECK_FLAG(psid->flags, ISIS_PREFIX_SID_NODE)) + continue; + if (psid->algorithm != args->algorithm) + continue; + args->sid_index = psid->value; + return LSP_ITER_STOP; + } + return LSP_ITER_CONTINUE; } /* Find Prefix-SID associated to a System ID. */ @@ -390,6 +393,8 @@ static uint32_t tilfa_find_pnode_prefix_sid(struct isis_spftree *spftree, if (!lsp) return UINT32_MAX; + args.algorithm = spftree->algorithm; + args.sid_index = UINT32_MAX; isis_lsp_iterate_ip_reach(lsp, spftree->family, spftree->mtid, tilfa_find_pnode_prefix_sid_cb, &args); @@ -1098,7 +1103,8 @@ struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree) spftree_reverse = isis_spftree_new( spftree->area, spftree->lspdb, spftree->sysid, spftree->level, spftree->tree_id, SPF_TYPE_REVERSE, - F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES); + F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES, + spftree->algorithm); isis_run_spf(spftree_reverse); return spftree_reverse; @@ -1194,7 +1200,8 @@ struct isis_spftree *isis_tilfa_compute(struct isis_area *area, /* Create post-convergence SPF tree. */ spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid, spftree->level, spftree->tree_id, - SPF_TYPE_TI_LFA, spftree->flags); + SPF_TYPE_TI_LFA, spftree->flags, + spftree->algorithm); spftree_pc->lfa.old.spftree = spftree; spftree_pc->lfa.old.spftree_reverse = spftree_reverse; spftree_pc->lfa.protected_resource = *resource; @@ -1242,7 +1249,8 @@ int isis_spf_run_neighbors(struct isis_spftree *spftree) adj_node->lfa.spftree = isis_spftree_new( spftree->area, spftree->lspdb, adj_node->sysid, spftree->level, spftree->tree_id, SPF_TYPE_FORWARD, - F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES); + F_SPFTREE_NO_ADJACENCIES | F_SPFTREE_NO_ROUTES, + spftree->algorithm); isis_run_spf(adj_node->lfa.spftree); } @@ -1388,9 +1396,9 @@ static struct rlfa *rlfa_lookup(struct isis_spftree *spftree, return rlfa_tree_find(&spftree->lfa.remote.rlfas, &s); } -static void isis_area_verify_routes_cb(struct thread *thread) +static void isis_area_verify_routes_cb(struct event *thread) { - struct isis_area *area = THREAD_ARG(thread); + struct isis_area *area = EVENT_ARG(thread); if (IS_DEBUG_LFA) zlog_debug("ISIS-LFA: updating RLFAs in the RIB"); @@ -1466,8 +1474,8 @@ int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa, if (ldp_label == MPLS_INVALID_LABEL) { if (IS_DEBUG_LFA) zlog_debug( - "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %s", - sysid_print(vadj->sadj->id)); + "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %pSY", + vadj->sadj->id); return -1; } @@ -1506,9 +1514,9 @@ int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa, spftree->route_table_backup); spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] += 1; - THREAD_OFF(area->t_rlfa_rib_update); - thread_add_timer(master, isis_area_verify_routes_cb, area, 2, - &area->t_rlfa_rib_update); + EVENT_OFF(area->t_rlfa_rib_update); + event_add_timer(master, isis_area_verify_routes_cb, area, 2, + &area->t_rlfa_rib_update); return 0; } @@ -1525,9 +1533,9 @@ void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa) isis_route_delete(area, rn, spftree->route_table_backup); spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] -= 1; - THREAD_OFF(area->t_rlfa_rib_update); - thread_add_timer(master, isis_area_verify_routes_cb, area, 2, - &area->t_rlfa_rib_update); + EVENT_OFF(area->t_rlfa_rib_update); + event_add_timer(master, isis_area_verify_routes_cb, area, 2, + &area->t_rlfa_rib_update); } void isis_rlfa_list_init(struct isis_spftree *spftree) @@ -1722,7 +1730,8 @@ struct isis_spftree *isis_rlfa_compute(struct isis_area *area, /* Create post-convergence SPF tree. */ spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid, spftree->level, spftree->tree_id, - SPF_TYPE_RLFA, spftree->flags); + SPF_TYPE_RLFA, spftree->flags, + spftree->algorithm); spftree_pc->lfa.old.spftree = spftree; spftree_pc->lfa.old.spftree_reverse = spftree_reverse; spftree_pc->lfa.remote.max_metric = max_metric; diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 4a332d0aed8e..950d5f359c08 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -12,7 +12,7 @@ #include <zebra.h> #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "stream.h" #include "memory.h" @@ -46,12 +46,13 @@ #include "isisd/fabricd.h" #include "isisd/isis_tx_queue.h" #include "isisd/isis_nb.h" +#include "isisd/isis_flex_algo.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_LSP, "ISIS LSP"); -static void lsp_refresh(struct thread *thread); -static void lsp_l1_refresh_pseudo(struct thread *thread); -static void lsp_l2_refresh_pseudo(struct thread *thread); +static void lsp_refresh(struct event *thread); +static void lsp_l1_refresh_pseudo(struct event *thread); +static void lsp_l2_refresh_pseudo(struct event *thread); static void lsp_destroy(struct isis_lsp *lsp); @@ -193,10 +194,9 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, || (lsp->hdr.rem_lifetime != 0 && rem_lifetime != 0))) { 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), - lsp->hdr.seqno, lsp->hdr.checksum, - lsp->hdr.rem_lifetime); + "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus", + areatag, 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%08x, cksum 0x%04hx, lifetime %hus", areatag, seqno, checksum, rem_lifetime); @@ -223,9 +223,9 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, && lsp->hdr.rem_lifetime)))) { 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); + "ISIS-Snp (%s): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus", + areatag, lsp->hdr.lsp_id, seqno, checksum, + rem_lifetime); zlog_debug( "ISIS-Snp (%s): is newer than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, lsp->hdr.seqno, lsp->hdr.checksum, @@ -234,9 +234,10 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, return LSP_NEWER; } 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): Compare LSP %pLS seq 0x%08x, cksum 0x%04hx, lifetime %hus", + areatag, lsp->hdr.lsp_id, seqno, checksum, + rem_lifetime); zlog_debug( "ISIS-Snp (%s): is older than ours seq 0x%08x, cksum 0x%04hx, lifetime %hus", areatag, lsp->hdr.seqno, lsp->hdr.checksum, @@ -429,9 +430,9 @@ bool isis_level2_adj_up(struct isis_area *area) /* * Unset the overload bit after the timer expires */ -void set_overload_on_start_timer(struct thread *thread) +void set_overload_on_start_timer(struct event *thread) { - struct isis_area *area = THREAD_ARG(thread); + struct isis_area *area = EVENT_ARG(thread); assert(area); area->t_overload_on_startup_timer = NULL; @@ -554,8 +555,8 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, if (lsp->own_lsp) { flog_err( EC_LIB_DEVELOPMENT, - "ISIS-Upd (%s): BUG updating LSP %s still marked as own LSP", - area->area_tag, rawlspid_print(lsp->hdr.lsp_id)); + "ISIS-Upd (%s): BUG updating LSP %pLS still marked as own LSP", + area->area_tag, lsp->hdr.lsp_id); lsp_clear_data(lsp); lsp->own_lsp = 0; } @@ -634,10 +635,8 @@ struct isis_lsp *lsp_new(struct isis_area *area, uint8_t *lsp_id, put_lsp_hdr(lsp, NULL, false); 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, - lsp->hdr.seqno); + zlog_debug("New LSP with ID %pLS len %d seqnum %08x", lsp_id, + lsp->hdr.pdu_len, lsp->hdr.seqno); return lsp; } @@ -704,7 +703,7 @@ void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, else if (!memcmp(isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) snprintf(id, sizeof(id), "%.14s", cmd_hostname_get()); else - memcpy(id, sysid_print(lsp_id), 15); + snprintf(id, sizeof(id), "%pSY", lsp_id); if (frag) snprintf(dest, dest_len, "%s.%02x-%02x", id, @@ -881,6 +880,65 @@ static uint16_t lsp_refresh_time(struct isis_lsp *lsp, uint16_t rem_lifetime) return refresh_time; } +static void lsp_build_internal_reach_ipv4(struct isis_lsp *lsp, + struct isis_area *area, + struct prefix_ipv4 *ipv4, + uint32_t metric) +{ + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL}; + + if (area->oldmetric) { + lsp_debug( + "ISIS (%s): Adding old-style IP reachability for %pFX", + area->area_tag, ipv4); + isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, metric); + } + + if (area->newmetric) { + lsp_debug("ISIS (%s): Adding te-style IP reachability for %pFX", + area->area_tag, ipv4); + + if (area->srdb.enabled) + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported(i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = + isis_sr_cfg_prefix_find(area, ipv4, i); + } + + isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric, false, + pcfgs); + } +} + +static void lsp_build_internal_reach_ipv6(struct isis_lsp *lsp, + struct isis_area *area, + struct prefix_ipv6 *ipv6, + uint32_t metric) +{ + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL}; + + lsp_debug("ISIS (%s): Adding IPv6 reachability for %pFX", + area->area_tag, ipv6); + + if (area->srdb.enabled) + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported(i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = isis_sr_cfg_prefix_find(area, ipv6, i); + } + + isis_tlvs_add_ipv6_reach(lsp->tlvs, isis_area_ipv6_topology(area), ipv6, + metric, false, pcfgs); +} + + static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area) { @@ -906,13 +964,23 @@ static void lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, isis_tlvs_add_oldstyle_ip_reach(lsp->tlvs, ipv4, metric); if (area->newmetric) { - struct sr_prefix_cfg *pcfg = NULL; + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = { + NULL}; if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find(area, ipv4); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported( + i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = isis_sr_cfg_prefix_find( + area, ipv4, i); + } isis_tlvs_add_extended_ip_reach(lsp->tlvs, ipv4, metric, - true, pcfg); + true, pcfgs); } } } @@ -940,14 +1008,24 @@ 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; + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = { + NULL}; if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find(area, p); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { +#ifndef FABRICD + if (flex_algo_id_valid(i) && + !isis_flex_algo_elected_supported( + i, area)) + continue; +#endif /* ifndef FABRICD */ + pcfgs[i] = isis_sr_cfg_prefix_find( + area, p, i); + } isis_tlvs_add_ipv6_reach(lsp->tlvs, isis_area_ipv6_topology(area), - p, metric, true, pcfg); + p, metric, true, pcfgs); } else if (isis_area_ipv6_dstsrc_enabled(area)) { isis_tlvs_add_ipv6_dstsrc_reach(lsp->tlvs, ISIS_MT_IPV6_DSTSRC, @@ -1064,9 +1142,30 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) /* Add Router Capability TLV. */ if (area->isis->router_id != 0) { - struct isis_router_cap cap = {}; + struct isis_router_cap *rcap; +#ifndef FABRICD + struct isis_router_cap_fad *rcap_fad; + struct listnode *node; + struct flex_algo *fa; +#endif /* ifndef FABRICD */ + + rcap = isis_tlvs_init_router_capability(lsp->tlvs); - cap.router_id.s_addr = area->isis->router_id; + rcap->router_id.s_addr = area->isis->router_id; + +#ifndef FABRICD + /* Set flex-algo definitions */ + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + if (!fa->advertise_definition) + continue; + lsp_debug("ISIS (%s): Flex-Algo Definition %u", + area->area_tag, fa->algorithm); + isis_tlvs_set_router_capability_fad(lsp->tlvs, fa, + fa->algorithm, + area->isis->sysid); + } +#endif /* ifndef FABRICD */ /* Add SR Sub-TLVs if SR is enabled. */ if (area->srdb.enabled) { @@ -1076,30 +1175,41 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) /* 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; + rcap->srgb.flags = ISIS_SUBTLV_SRGB_FLAG_I | + ISIS_SUBTLV_SRGB_FLAG_V; + rcap->srgb.range_size = range_size; + rcap->srgb.lower_bound = srdb->config.srgb_lower_bound; /* Then Algorithm */ - cap.algo[0] = SR_ALGORITHM_SPF; - cap.algo[1] = SR_ALGORITHM_UNSET; + rcap->algo[0] = SR_ALGORITHM_SPF; + rcap->algo[1] = SR_ALGORITHM_UNSET; +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, + node, fa)) { + if (fa->advertise_definition) + rcap_fad = rcap->fads[fa->algorithm]; + else + rcap_fad = NULL; + + if (!isis_flex_algo_elected_supported_local_fad( + fa->algorithm, area, &rcap_fad)) { + fa->state = false; + continue; + } + fa->state = true; + lsp_debug("ISIS (%s): SR Algorithm %u", + area->area_tag, fa->algorithm); + rcap->algo[fa->algorithm] = fa->algorithm; + } +#endif /* ifndef FABRICD */ /* SRLB */ - cap.srlb.flags = 0; + rcap->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; + rcap->srlb.range_size = range_size; + rcap->srlb.lower_bound = srdb->config.srlb_lower_bound; /* And finally MSD */ - cap.msd = srdb->config.msd; - } else { - /* Disable SR Algorithm */ - cap.algo[0] = SR_ALGORITHM_UNSET; - cap.algo[1] = SR_ALGORITHM_UNSET; + rcap->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. @@ -1189,31 +1299,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) struct listnode *ipnode; struct prefix_ipv4 *ipv4; for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, ipnode, - ipv4)) { - if (area->oldmetric) { - lsp_debug( - "ISIS (%s): Adding old-style IP reachability for %pFX", - area->area_tag, ipv4); - isis_tlvs_add_oldstyle_ip_reach( - lsp->tlvs, ipv4, metric); - } - - if (area->newmetric) { - struct sr_prefix_cfg *pcfg = NULL; - - lsp_debug( - "ISIS (%s): Adding te-style IP reachability for %pFX", - area->area_tag, ipv4); - - if (area->srdb.enabled) - pcfg = isis_sr_cfg_prefix_find( - area, ipv4); - - isis_tlvs_add_extended_ip_reach( - lsp->tlvs, ipv4, metric, false, - pcfg); - } - } + ipv4)) + lsp_build_internal_reach_ipv4(lsp, area, ipv4, + metric); } if (circuit->ipv6_router && circuit->ipv6_non_link->count > 0) { @@ -1221,22 +1309,9 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) 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 %pFX", - area->area_tag, ipv6); - - 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, false, pcfg); - } + ipnode, ipv6)) + lsp_build_internal_reach_ipv6(lsp, area, ipv6, + metric); } switch (circuit->circ_type) { @@ -1250,10 +1325,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (LSP_PSEUDO_ID(ne_id)) { if (area->oldmetric) { lsp_debug( - "ISIS (%s): Adding DIS %s.%02x as old-style neighbor", - area->area_tag, - sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + "ISIS (%s): Adding DIS %pPN as old-style neighbor", + area->area_tag, ne_id); isis_tlvs_add_oldstyle_reach( lsp->tlvs, ne_id, metric); @@ -1279,9 +1352,8 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (area->oldmetric) { lsp_debug( - "ISIS (%s): Adding old-style is reach for %s", - area->area_tag, - sysid_print(ne_id)); + "ISIS (%s): Adding old-style is reach for %pSY", + area->area_tag, ne_id); isis_tlvs_add_oldstyle_reach( lsp->tlvs, ne_id, metric); } @@ -1372,7 +1444,7 @@ int lsp_generate(struct isis_area *area, int level) return ISIS_ERROR; /* Check if config is still being processed */ - if (thread_is_scheduled(t_isis_cfg)) + if (event_is_scheduled(t_isis_cfg)) return ISIS_OK; memset(&lspid, 0, ISIS_SYS_ID_LEN + 2); @@ -1384,9 +1456,9 @@ int lsp_generate(struct isis_area *area, int level) overload_time = isis_restart_read_overload_time(area); if (overload_time > 0) { isis_area_overload_bit_set(area, true); - thread_add_timer(master, set_overload_on_start_timer, - area, overload_time, - &area->t_overload_on_startup_timer); + event_add_timer(master, set_overload_on_start_timer, + area, overload_time, + &area->t_overload_on_startup_timer); } device_startup = false; } @@ -1418,19 +1490,18 @@ int lsp_generate(struct isis_area *area, int level) refresh_time = lsp_refresh_time(newlsp, rem_lifetime); - THREAD_OFF(area->t_lsp_refresh[level - 1]); + EVENT_OFF(area->t_lsp_refresh[level - 1]); area->lsp_regenerate_pending[level - 1] = 0; - thread_add_timer(master, lsp_refresh, - &area->lsp_refresh_arg[level - 1], refresh_time, - &area->t_lsp_refresh[level - 1]); + event_add_timer(master, lsp_refresh, &area->lsp_refresh_arg[level - 1], + refresh_time, &area->t_lsp_refresh[level - 1]); 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, - newlsp->hdr.checksum, newlsp->hdr.rem_lifetime, - refresh_time); + zlog_debug( + "ISIS-Upd (%s): Building L%d LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", + area->area_tag, level, newlsp->hdr.lsp_id, + newlsp->hdr.pdu_len, newlsp->hdr.seqno, + newlsp->hdr.checksum, newlsp->hdr.rem_lifetime, + refresh_time); } sched_debug( "ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.", @@ -1501,15 +1572,14 @@ static int lsp_regenerate(struct isis_area *area, int level) lsp_seqno_update(lsp); refresh_time = lsp_refresh_time(lsp, rem_lifetime); - thread_add_timer(master, lsp_refresh, - &area->lsp_refresh_arg[level - 1], refresh_time, - &area->t_lsp_refresh[level - 1]); + event_add_timer(master, lsp_refresh, &area->lsp_refresh_arg[level - 1], + refresh_time, &area->t_lsp_refresh[level - 1]); area->lsp_regenerate_pending[level - 1] = 0; if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "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), + "ISIS-Upd (%s): Refreshed our L%d LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus refresh %hus", + area->area_tag, level, lsp->hdr.lsp_id, lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); } @@ -1523,9 +1593,9 @@ static int lsp_regenerate(struct isis_area *area, int level) /* * Something has changed or periodic refresh -> regenerate LSP */ -static void lsp_refresh(struct thread *thread) +static void lsp_refresh(struct event *thread) { - struct lsp_refresh_arg *arg = THREAD_ARG(thread); + struct lsp_refresh_arg *arg = EVENT_ARG(thread); assert(arg); @@ -1605,7 +1675,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, * Note: in case of a BFD 'down' message the refresh is * scheduled once again just to be sure */ - struct timeval remain = thread_timer_remain( + struct timeval remain = event_timer_remain( area->t_lsp_refresh[lvl - 1]); sched_debug( "ISIS (%s): Regeneration is already pending, nothing todo. (Due in %lld.%03lld seconds)", @@ -1629,7 +1699,7 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, "ISIS (%s): Will schedule regen timer. Last run was: %lld, Now is: %lld", area->area_tag, (long long)lsp->last_generated, (long long)now); - THREAD_OFF(area->t_lsp_refresh[lvl - 1]); + EVENT_OFF(area->t_lsp_refresh[lvl - 1]); diff = now - lsp->last_generated; if (diff < area->lsp_gen_interval[lvl - 1] && !(area->bfd_signalled_down)) { @@ -1668,10 +1738,9 @@ int _lsp_regenerate_schedule(struct isis_area *area, int level, } area->lsp_regenerate_pending[lvl - 1] = 1; - thread_add_timer_msec(master, lsp_refresh, - &area->lsp_refresh_arg[lvl - 1], - timeout, - &area->t_lsp_refresh[lvl - 1]); + event_add_timer_msec(master, lsp_refresh, + &area->lsp_refresh_arg[lvl - 1], timeout, + &area->t_lsp_refresh[lvl - 1]); } if (all_pseudo) { @@ -1701,9 +1770,9 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, lsp_clear_data(lsp); lsp->tlvs = isis_alloc_tlvs(); lsp_debug( - "ISIS (%s): Constructing pseudo LSP %s for interface %s level %d", - area->area_tag, rawlspid_print(lsp->hdr.lsp_id), - circuit->interface->name, level); + "ISIS (%s): Constructing pseudo LSP %pLS for interface %s level %d", + area->area_tag, lsp->hdr.lsp_id, circuit->interface->name, + level); lsp->level = level; /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ @@ -1720,10 +1789,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, if (circuit->area->oldmetric) { isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0); - lsp_debug( - "ISIS (%s): Adding %s.%02x as old-style neighbor (self)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + lsp_debug("ISIS (%s): Adding %pPN as old-style neighbor (self)", + area->area_tag, ne_id); } if (circuit->area->newmetric) { if (area_is_mt(circuit->area)) @@ -1731,10 +1798,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, else mtid = ISIS_MT_DISABLE; isis_tlvs_add_extended_reach(lsp->tlvs, mtid, ne_id, 0, NULL); - lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (self)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + lsp_debug("ISIS (%s): Adding %pPN as te-style neighbor (self)", + area->area_tag, ne_id); } adj_list = list_new(); @@ -1743,8 +1808,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, for (ALL_LIST_ELEMENTS_RO(adj_list, node, adj)) { if (!(adj->level & level)) { lsp_debug( - "ISIS (%s): Ignoring neighbor %s, level does not intersect", - area->area_tag, sysid_print(adj->sysid)); + "ISIS (%s): Ignoring neighbor %pSY, level does not intersect", + area->area_tag, adj->sysid); continue; } @@ -1756,8 +1821,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, && !(level == IS_LEVEL_2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) { lsp_debug( - "ISIS (%s): Ignoring neighbor %s, level does not match", - area->area_tag, sysid_print(adj->sysid)); + "ISIS (%s): Ignoring neighbor %pSY, level does not match", + area->area_tag, adj->sysid); continue; } @@ -1765,18 +1830,16 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit, if (circuit->area->oldmetric) { isis_tlvs_add_oldstyle_reach(lsp->tlvs, ne_id, 0); lsp_debug( - "ISIS (%s): Adding %s.%02x as old-style neighbor (peer)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + "ISIS (%s): Adding %pPN as old-style neighbor (peer)", + area->area_tag, ne_id); } if (circuit->area->newmetric) { isis_tlvs_add_extended_reach(lsp->tlvs, ISIS_MT_IPV4_UNICAST, ne_id, 0, NULL); lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", - area->area_tag, sysid_print(ne_id), - LSP_PSEUDO_ID(ne_id)); + "ISIS (%s): Adding %pPN as te-style neighbor (peer)", + area->area_tag, ne_id); } } list_delete(&adj_list); @@ -1822,23 +1885,22 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level) lsp_flood(lsp, NULL); refresh_time = lsp_refresh_time(lsp, rem_lifetime); - THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); circuit->lsp_regenerate_pending[level - 1] = 0; if (level == IS_LEVEL_1) - thread_add_timer( - master, lsp_l1_refresh_pseudo, circuit, refresh_time, - &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + event_add_timer(master, lsp_l1_refresh_pseudo, circuit, + refresh_time, + &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); else if (level == IS_LEVEL_2) - thread_add_timer( - master, lsp_l2_refresh_pseudo, circuit, refresh_time, - &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + event_add_timer(master, lsp_l2_refresh_pseudo, circuit, + refresh_time, + &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "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, + "ISIS-Upd (%s): Built L%d Pseudo LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", + circuit->area->area_tag, level, lsp->hdr.lsp_id, + lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); } @@ -1866,8 +1928,8 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) if (!lsp) { flog_err(EC_LIB_DEVELOPMENT, - "lsp_regenerate_pseudo: no l%d LSP %s found!", level, - rawlspid_print(lsp_id)); + "lsp_regenerate_pseudo: no l%d LSP %pLS found!", level, + lsp_id); return ISIS_ERROR; } @@ -1880,20 +1942,19 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) refresh_time = lsp_refresh_time(lsp, rem_lifetime); if (level == IS_LEVEL_1) - thread_add_timer( - master, lsp_l1_refresh_pseudo, circuit, refresh_time, - &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + event_add_timer(master, lsp_l1_refresh_pseudo, circuit, + refresh_time, + &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); else if (level == IS_LEVEL_2) - thread_add_timer( - master, lsp_l2_refresh_pseudo, circuit, refresh_time, - &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + event_add_timer(master, lsp_l2_refresh_pseudo, circuit, + refresh_time, + &circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "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, + "ISIS-Upd (%s): Refreshed L%d Pseudo LSP %pLS, len %hu, seq 0x%08x, cksum 0x%04hx, lifetime %hus, refresh %hus", + circuit->area->area_tag, level, lsp->hdr.lsp_id, + lsp->hdr.pdu_len, lsp->hdr.seqno, lsp->hdr.checksum, lsp->hdr.rem_lifetime, refresh_time); } @@ -1903,12 +1964,12 @@ static int lsp_regenerate_pseudo(struct isis_circuit *circuit, int level) /* * Something has changed or periodic refresh -> regenerate pseudo LSP */ -static void lsp_l1_refresh_pseudo(struct thread *thread) +static void lsp_l1_refresh_pseudo(struct event *thread) { struct isis_circuit *circuit; uint8_t id[ISIS_SYS_ID_LEN + 2]; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL; circuit->lsp_regenerate_pending[0] = 0; @@ -1925,12 +1986,12 @@ static void lsp_l1_refresh_pseudo(struct thread *thread) lsp_regenerate_pseudo(circuit, IS_LEVEL_1); } -static void lsp_l2_refresh_pseudo(struct thread *thread) +static void lsp_l2_refresh_pseudo(struct event *thread) { struct isis_circuit *circuit; uint8_t id[ISIS_SYS_ID_LEN + 2]; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL; circuit->lsp_regenerate_pending[1] = 0; @@ -1989,7 +2050,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) } if (circuit->lsp_regenerate_pending[lvl - 1]) { - struct timeval remain = thread_timer_remain( + struct timeval remain = event_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)", @@ -2013,7 +2074,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) "ISIS (%s): Will schedule PSN regen timer. Last run was: %lld, Now is: %lld", area->area_tag, (long long)lsp->last_generated, (long long)now); - THREAD_OFF(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); + EVENT_OFF(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); diff = now - lsp->last_generated; if (diff < circuit->area->lsp_gen_interval[lvl - 1]) { timeout = @@ -2032,11 +2093,11 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) circuit->lsp_regenerate_pending[lvl - 1] = 1; if (lvl == IS_LEVEL_1) { - thread_add_timer_msec( + event_add_timer_msec( master, lsp_l1_refresh_pseudo, circuit, timeout, &circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); } else if (lvl == IS_LEVEL_2) { - thread_add_timer_msec( + event_add_timer_msec( master, lsp_l2_refresh_pseudo, circuit, timeout, &circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); } @@ -2049,7 +2110,7 @@ int lsp_regenerate_schedule_pseudo(struct isis_circuit *circuit, int level) * Walk through LSPs for an area * - set remaining lifetime */ -void lsp_tick(struct thread *thread) +void lsp_tick(struct event *thread) { struct isis_area *area; struct isis_lsp *lsp; @@ -2057,10 +2118,10 @@ void lsp_tick(struct thread *thread) uint16_t rem_lifetime; bool fabricd_sync_incomplete = false; - area = THREAD_ARG(thread); + area = EVENT_ARG(thread); assert(area); area->t_tick = NULL; - thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); + event_add_timer(master, lsp_tick, area, 1, &area->t_tick); struct isis_circuit *fabricd_init_c = fabricd_initial_sync_circuit(area); @@ -2104,10 +2165,9 @@ void lsp_tick(struct thread *thread) if (lsp->age_out == 0) { zlog_debug( - "ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", + "ISIS-Upd (%s): L%u LSP %pLS seq 0x%08x aged out", area->area_tag, lsp->level, - rawlspid_print(lsp->hdr.lsp_id), - lsp->hdr.seqno); + lsp->hdr.lsp_id, lsp->hdr.seqno); /* if we're aging out fragment 0, lsp_destroy() * below will delete all other fragments too, @@ -2210,11 +2270,10 @@ void _lsp_flood(struct isis_lsp *lsp, struct isis_circuit *circuit, const char *func, const char *file, int line) { if (IS_DEBUG_FLOODING) { - zlog_debug("Flooding LSP %s%s%s (From %s %s:%d)", - rawlspid_print(lsp->hdr.lsp_id), - circuit ? " except on " : "", - circuit ? circuit->interface->name : "", - func, file, line); + zlog_debug("Flooding LSP %pLS%s%s (From %s %s:%d)", + lsp->hdr.lsp_id, circuit ? " except on " : "", + circuit ? circuit->interface->name : "", func, file, + line); } if (!fabricd) diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 5bb2b949c7a9..3839a9504c05 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -52,8 +52,8 @@ DECLARE_RBTREE_UNIQ(lspdb, struct isis_lsp, dbe, lspdb_compare); void lsp_db_init(struct lspdb_head *head); void lsp_db_fini(struct lspdb_head *head); -void lsp_tick(struct thread *thread); -void set_overload_on_start_timer(struct thread *thread); +void lsp_tick(struct event *thread); +void set_overload_on_start_timer(struct event *thread); int lsp_generate(struct isis_area *area, int level); #define lsp_regenerate_schedule(area, level, all_pseudo) \ diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 25ea187492ea..b495d3a9ddee 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -10,7 +10,7 @@ #include <zebra.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include <lib/version.h> #include "command.h" @@ -29,6 +29,7 @@ #include "routemap.h" #include "affinitymap.h" +#include "isisd/isis_affinitymap.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" #include "isisd/isis_flags.h" @@ -77,7 +78,7 @@ static const struct option longopts[] = { {0}}; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* * Prototypes. @@ -169,7 +170,10 @@ static const struct frr_yang_module_info *const isisd_yang_modules[] = { /* clang-format on */ -static void isis_config_finish(struct thread *t) +/* Max wait time for config to load before generating LSPs */ +#define ISIS_PRE_CONFIG_MAX_WAIT_SECONDS 600 + +static void isis_config_finish(struct event *t) { struct listnode *node, *inode; struct isis *isis; @@ -181,13 +185,18 @@ static void isis_config_finish(struct thread *t) } } +static void isis_config_end_timeout(struct event *t) +{ + zlog_err("IS-IS configuration end timer expired after %d seconds.", + ISIS_PRE_CONFIG_MAX_WAIT_SECONDS); + isis_config_finish(t); +} + static void isis_config_start(void) { - /* Max wait time for config to load before generating lsp */ -#define ISIS_PRE_CONFIG_MAX_WAIT_SECONDS 600 - THREAD_OFF(t_isis_cfg); - thread_add_timer(im->master, isis_config_finish, NULL, - ISIS_PRE_CONFIG_MAX_WAIT_SECONDS, &t_isis_cfg); + EVENT_OFF(t_isis_cfg); + event_add_timer(im->master, isis_config_end_timeout, NULL, + ISIS_PRE_CONFIG_MAX_WAIT_SECONDS, &t_isis_cfg); } static void isis_config_end(void) @@ -195,10 +204,10 @@ static void isis_config_end(void) /* If ISIS config processing thread isn't running, then * we can return and rely it's properly handled. */ - if (!thread_is_scheduled(t_isis_cfg)) + if (!event_is_scheduled(t_isis_cfg)) return; - THREAD_OFF(t_isis_cfg); + EVENT_OFF(t_isis_cfg); isis_config_finish(t_isis_cfg); } @@ -281,7 +290,7 @@ int main(int argc, char **argv, char **envp) #endif /* FABRICD */ #ifndef FABRICD isis_cli_init(); -#endif /* ifdef FABRICD */ +#endif /* ifndef FABRICD */ isis_spf_init(); isis_redist_init(); isis_route_map_init(); @@ -290,7 +299,9 @@ int main(int argc, char **argv, char **envp) lsp_init(); mt_init(); - affinity_map_init(); +#ifndef FABRICD + isis_affinity_map_init(); +#endif /* ifndef FABRICD */ isis_zebra_init(master, instance); isis_bfd_init(master); diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 4a490ab5ac21..e4ef6c8dfa3c 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -32,47 +32,12 @@ #include "isisd/isis_dynhn.h" /* staticly assigned vars for printing purposes */ +static char sys_hostname[ISO_SYSID_STRLEN]; struct in_addr new_prefix; -/* len of xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx */ -/* + place for #0 termination */ -char isonet[51]; /* len of xxYxxMxWxdxxhxxmxxs + place for #0 termination */ char datestring[20]; char nlpidstring[30]; -/* - * This converts the isonet to its printable format - */ -const char *isonet_print(const uint8_t *from, int len) -{ - int i = 0; - char tbuf[4]; - isonet[0] = '\0'; - - if (!from) - return "unknown"; - - while (i < len) { - if (i & 1) { - snprintf(tbuf, sizeof(tbuf), "%02x", *(from + i)); - strlcat(isonet, tbuf, sizeof(isonet)); - } else { - if (i == (len - 1)) { /* No dot at the end of address */ - snprintf(tbuf, sizeof(tbuf), "%02x", - *(from + i)); - strlcat(isonet, tbuf, sizeof(isonet)); - } else { - snprintf(tbuf, sizeof(tbuf), "%02x.", - *(from + i)); - strlcat(isonet, tbuf, sizeof(isonet)); - } - } - i++; - } - - return isonet; -} - /* * Returns 0 on error, length of buff on ok * extract dot from the dotted str, and insert all the number in a buff @@ -294,58 +259,17 @@ const char *syst2string(int type) return NULL; /* not reached */ } -/* - * Print functions - we print to static vars - */ -const char *snpa_print(const uint8_t *from) -{ - return isis_format_id(from, ISIS_SYS_ID_LEN); -} - -const char *sysid_print(const uint8_t *from) +const char *isis_hello_padding2string(int hello_padding_type) { - return isis_format_id(from, ISIS_SYS_ID_LEN); -} - -const char *rawlspid_print(const uint8_t *from) -{ - return isis_format_id(from, 8); -} - -#define FORMAT_ID_SIZE sizeof("0000.0000.0000.00-00") -const char *isis_format_id(const uint8_t *id, size_t len) -{ -#define FORMAT_BUF_COUNT 4 - static char buf_ring[FORMAT_BUF_COUNT][FORMAT_ID_SIZE]; - static size_t cur_buf = 0; - - char *rv; - - cur_buf++; - if (cur_buf >= FORMAT_BUF_COUNT) - cur_buf = 0; - - rv = buf_ring[cur_buf]; - - if (!id) { - snprintf(rv, FORMAT_ID_SIZE, "unknown"); - return rv; - } - - if (len < 6) { - snprintf(rv, FORMAT_ID_SIZE, "Short ID"); - return rv; + switch (hello_padding_type) { + case ISIS_HELLO_PADDING_DISABLED: + return "no"; + case ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION: + return "during-adjacency-formation"; + case ISIS_HELLO_PADDING_ALWAYS: + return "yes"; } - - snprintf(rv, FORMAT_ID_SIZE, "%02x%02x.%02x%02x.%02x%02x", id[0], id[1], - id[2], id[3], id[4], id[5]); - - if (len > 6) - snprintf(rv + 14, FORMAT_ID_SIZE - 14, ".%02x", id[6]); - if (len > 7) - snprintf(rv + 17, FORMAT_ID_SIZE - 17, "-%02x", id[7]); - - return rv; + return NULL; /* not reached */ } const char *time2string(uint32_t time) @@ -461,7 +385,8 @@ const char *print_sys_hostname(const uint8_t *sysid) return dyn->hostname; } - return sysid_print(sysid); + snprintfrr(sys_hostname, ISO_SYSID_STRLEN, "%pSY", sysid); + return sys_hostname; } /* @@ -495,11 +420,11 @@ void zlog_dump_data(void *data, int len) /* store hex str (for left side) */ snprintf(bytestr, sizeof(bytestr), "%02X ", *p); - strncat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); + strlcat(hexstr, bytestr, sizeof(hexstr) - strlen(hexstr) - 1); /* store char str (for right side) */ snprintf(bytestr, sizeof(bytestr), "%c", c); - strncat(charstr, bytestr, + strlcat(charstr, bytestr, sizeof(charstr) - strlen(charstr) - 1); if ((i % 16) == 0) { @@ -510,9 +435,9 @@ void zlog_dump_data(void *data, int len) charstr[0] = 0; } else if ((i % 8) == 0) { /* half line: add whitespaces */ - strncat(hexstr, " ", + strlcat(hexstr, " ", sizeof(hexstr) - strlen(hexstr) - 1); - strncat(charstr, " ", + strlcat(charstr, " ", sizeof(charstr) - strlen(charstr) - 1); } p++; /* next byte */ @@ -549,20 +474,20 @@ void log_multiline(int priority, const char *prefix, const char *format, ...) char *log_uptime(time_t uptime, char *buf, size_t nbuf) { - struct tm *tm; + struct tm tm; time_t difftime = time(NULL); difftime -= uptime; - tm = gmtime(&difftime); + gmtime_r(&difftime, &tm); if (difftime < ONE_DAY_SECOND) - snprintf(buf, nbuf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, - tm->tm_sec); + 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); + 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); + snprintf(buf, nbuf, "%02dw%dd%02dh", tm.tm_yday / 7, + tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour); return buf; } diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index b8b4ebd28097..3a1d136b1d92 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -16,6 +16,7 @@ const char *circuit_t2string(int); const char *circuit_state2string(int state); const char *circuit_type2string(int type); const char *syst2string(int); +const char *isis_hello_padding2string(int hello_padding_type); struct in_addr newprefix2inaddr(uint8_t *prefix_start, uint8_t prefix_masklen); /* * Converting input to memory stored format @@ -27,11 +28,6 @@ int sysid2buff(uint8_t *, const char *); /* * Printing functions */ -const char *isonet_print(const uint8_t *, int len); -const char *sysid_print(const uint8_t *); -const char *snpa_print(const uint8_t *); -const char *rawlspid_print(const uint8_t *); -const char *isis_format_id(const uint8_t *id, size_t len); const char *time2string(uint32_t); const char *nlpid2str(uint8_t nlpid); /* typedef struct nlpids nlpids; */ diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index fcc0f53815ed..d04a24dc4683 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -507,8 +507,8 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, /* Check if MT is enable for this area */ if (!area_is_mt(area)) { lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor (MT disable)", - area->area_tag, sysid_print(id), LSP_PSEUDO_ID(id)); + "ISIS (%s): Adding %pPN as te-style neighbor (MT disable)", + area->area_tag, id); isis_tlvs_add_extended_reach(tlvs, ISIS_MT_DISABLE, id, metric, ext); return; @@ -518,15 +518,12 @@ static void tlvs_add_mt_set(struct isis_area *area, struct isis_tlvs *tlvs, for (unsigned int i = 0; i < mt_count; i++) { uint16_t mtid = mt_set[i]; if (mt_set[i] == ISIS_MT_IPV4_UNICAST) { - lsp_debug( - "ISIS (%s): Adding %s.%02x as te-style neighbor", - area->area_tag, sysid_print(id), - LSP_PSEUDO_ID(id)); + lsp_debug("ISIS (%s): Adding %pPN as te-style neighbor", + area->area_tag, id); } else { lsp_debug( - "ISIS (%s): Adding %s.%02x as mt-style neighbor for %s", - area->area_tag, sysid_print(id), - LSP_PSEUDO_ID(id), isis_mtid2str(mtid)); + "ISIS (%s): Adding %pPN as mt-style neighbor for %s", + area->area_tag, id, isis_mtid2str(mtid)); } isis_tlvs_add_extended_reach(tlvs, mtid, id, metric, ext); } diff --git a/isisd/isis_nb.c b/isisd/isis_nb.c index 7dc3a0eb3da6..6da8fa2d28e2 100644 --- a/isisd/isis_nb.c +++ b/isisd/isis_nb.c @@ -102,6 +102,20 @@ const struct frr_yang_module_info frr_isisd_info = { .modify = isis_instance_purge_originator_modify, }, }, + { + .xpath = "/frr-isisd:isis/instance/admin-group-send-zero", + .cbs = { + .cli_show = cli_show_isis_admin_group_send_zero, + .modify = isis_instance_admin_group_send_zero_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/asla-legacy-flag", + .cbs = { + .cli_show = cli_show_isis_asla_legacy_flag, + .modify = isis_instance_asla_legacy_flag_modify, + }, + }, { .xpath = "/frr-isisd:isis/instance/lsp/mtu", .cbs = { @@ -558,6 +572,13 @@ const struct frr_yang_module_info frr_isisd_info = { .modify = isis_instance_log_adjacency_changes_modify, }, }, + { + .xpath = "/frr-isisd:isis/instance/log-pdu-drops", + .cbs = { + .cli_show = cli_show_isis_log_pdu_drops, + .modify = isis_instance_log_pdu_drops_modify, + }, + }, { .xpath = "/frr-isisd:isis/instance/mpls-te", .cbs = { @@ -681,6 +702,118 @@ const struct frr_yang_module_info frr_isisd_info = { .modify = isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify, } }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid", + .cbs = { + .create = isis_instance_segment_routing_algorithm_prefix_sid_create, + .destroy = isis_instance_segment_routing_algorithm_prefix_sid_destroy, + .pre_validate = isis_instance_segment_routing_algorithm_prefix_sid_pre_validate, + .apply_finish = isis_instance_segment_routing_algorithm_prefix_sid_apply_finish, + .cli_show = cli_show_isis_prefix_sid_algorithm, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value-type", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/sid-value", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/last-hop-behavior", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/n-flag-clear", + .cbs = { + .modify = isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo", + .cbs = { + .cli_show = cli_show_isis_flex_algo, + .cli_show_end = cli_show_isis_flex_algo_end, + .create = isis_instance_flex_algo_create, + .destroy = isis_instance_flex_algo_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/advertise-definition", + .cbs = { + .modify = isis_instance_flex_algo_advertise_definition_modify, + .destroy = isis_instance_flex_algo_advertise_definition_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-alls/affinity-include-all", + .cbs = { + .create = isis_instance_flex_algo_affinity_include_all_create, + .destroy = isis_instance_flex_algo_affinity_include_all_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-anies/affinity-include-any", + .cbs = { + .create = isis_instance_flex_algo_affinity_include_any_create, + .destroy = isis_instance_flex_algo_affinity_include_any_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/affinity-exclude-anies/affinity-exclude-any", + .cbs = { + .create = isis_instance_flex_algo_affinity_exclude_any_create, + .destroy = isis_instance_flex_algo_affinity_exclude_any_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/prefix-metric", + .cbs = { + .create = isis_instance_flex_algo_prefix_metric_create, + .destroy = isis_instance_flex_algo_prefix_metric_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/metric-type", + .cbs = { + .modify = isis_instance_flex_algo_metric_type_modify, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-sr-mpls", + .cbs = { + .create = isis_instance_flex_algo_dplane_sr_mpls_create, + .destroy = isis_instance_flex_algo_dplane_sr_mpls_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-srv6", + .cbs = { + .create = isis_instance_flex_algo_dplane_srv6_create, + .destroy = isis_instance_flex_algo_dplane_srv6_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/dplane-ip", + .cbs = { + .create = isis_instance_flex_algo_dplane_ip_create, + .destroy = isis_instance_flex_algo_dplane_ip_destroy, + }, + }, + { + .xpath = "/frr-isisd:isis/instance/flex-algos/flex-algo/priority", + .cbs = { + .modify = isis_instance_flex_algo_priority_modify, + .destroy = isis_instance_flex_algo_priority_destroy, + }, + }, { .xpath = "/frr-isisd:isis/instance/mpls/ldp-sync", .cbs = { diff --git a/isisd/isis_nb.h b/isisd/isis_nb.h index 480b2ce04132..13efa36d789e 100644 --- a/isisd/isis_nb.h +++ b/isisd/isis_nb.h @@ -29,6 +29,8 @@ int isis_instance_overload_on_startup_modify(struct nb_cb_modify_args *args); int isis_instance_advertise_high_metrics_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_admin_group_send_zero_modify(struct nb_cb_modify_args *args); +int isis_instance_asla_legacy_flag_modify(struct nb_cb_modify_args *args); int isis_instance_lsp_mtu_modify(struct nb_cb_modify_args *args); int isis_instance_advertise_passive_only_modify(struct nb_cb_modify_args *args); int isis_instance_lsp_refresh_interval_level_1_modify( @@ -196,6 +198,7 @@ int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify( int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy( struct nb_cb_destroy_args *args); int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args); +int isis_instance_log_pdu_drops_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); @@ -248,6 +251,67 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_last_hop_behavior_mo struct nb_cb_modify_args *args); int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify( struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_create( + struct nb_cb_create_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_pre_validate( + struct nb_cb_pre_validate_args *args); +void isis_instance_segment_routing_algorithm_prefix_sid_apply_finish( + struct nb_cb_apply_finish_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_type_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_sid_value_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_last_hop_behavior_modify( + struct nb_cb_modify_args *args); +int isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_modify( + struct nb_cb_modify_args *args); +int isis_instance_flex_algo_create(struct nb_cb_create_args *args); +int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_advertise_definition_modify( + struct nb_cb_modify_args *args); +int isis_instance_flex_algo_advertise_definition_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_include_any_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_include_any_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_include_all_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_include_all_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_exclude_any_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_exclude_any_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_prefix_metric_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_prefix_metric_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_dplane_sr_mpls_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_dplane_sr_mpls_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_dplane_srv6_create(struct nb_cb_create_args *args); +int isis_instance_flex_algo_dplane_srv6_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_dplane_ip_create(struct nb_cb_create_args *args); +int isis_instance_flex_algo_dplane_ip_destroy(struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args); +int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args); +int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_frr_disable_modify(struct nb_cb_modify_args *args); +int isis_instance_flex_algo_frr_disable_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_mapping_create( + struct nb_cb_create_args *args); +int isis_instance_flex_algo_affinity_mapping_destroy( + struct nb_cb_destroy_args *args); +int isis_instance_flex_algo_affinity_mapping_value_modify( + struct nb_cb_modify_args *args); +int isis_instance_flex_algo_affinity_mapping_value_destroy( + struct nb_cb_destroy_args *args); int isis_instance_mpls_ldp_sync_destroy(struct nb_cb_destroy_args *args); int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args); int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args); @@ -494,6 +558,12 @@ void cli_show_isis_purge_origin(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_mpls_te(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_admin_group_send_zero(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_asla_legacy_flag(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_mpls_te_router_addr(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -537,6 +607,9 @@ void cli_show_isis_node_msd(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_prefix_sid(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_prefix_sid_algorithm(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_frr_lfa_priority_limit(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -609,6 +682,8 @@ void cli_show_ip_isis_priority(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_log_adjacency(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_log_pdu_drops(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); void cli_show_isis_mpls_ldp_sync(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); void cli_show_isis_mpls_ldp_sync_holddown(struct vty *vty, @@ -620,6 +695,9 @@ void cli_show_isis_mpls_if_ldp_sync(struct vty *vty, void cli_show_isis_mpls_if_ldp_sync_holddown(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_isis_flex_algo(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); +void cli_show_isis_flex_algo_end(struct vty *vty, const struct lyd_node *dnode); /* Notifications. */ void isis_notif_db_overload(const struct isis_area *area, bool overload); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 2b3355bc9ffe..8a111b301da7 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -21,6 +21,7 @@ #include "vrf.h" #include "ldp_sync.h" #include "link_state.h" +#include "affinitymap.h" #include "isisd/isisd.h" #include "isisd/isis_nb.h" @@ -39,8 +40,14 @@ #include "isisd/isis_redist.h" #include "isisd/isis_ldp_sync.h" #include "isisd/isis_dr.h" +#include "isisd/isis_sr.h" +#include "isisd/isis_flex_algo.h" #include "isisd/isis_zebra.h" +#define AFFINITY_INCLUDE_ANY 0 +#define AFFINITY_INCLUDE_ALL 1 +#define AFFINITY_EXCLUDE_ANY 2 + /* * XPath: /frr-isisd:isis/instance */ @@ -103,14 +110,14 @@ int isis_instance_is_type_modify(struct nb_cb_modify_args *args) } struct sysid_iter { - struct area_addr *addr; + struct iso_address *addr; bool same; }; static int sysid_iter_cb(const struct lyd_node *dnode, void *arg) { struct sysid_iter *iter = arg; - struct area_addr addr; + struct iso_address addr; const char *net; net = yang_dnode_get_string(dnode, NULL); @@ -130,7 +137,7 @@ static int sysid_iter_cb(const struct lyd_node *dnode, void *arg) 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 iso_address addr, *addrr = NULL, *addrp = NULL; struct listnode *node; struct sysid_iter iter; uint8_t buff[255]; @@ -161,7 +168,8 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) } break; case NB_EV_PREPARE: - addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr)); + addrr = XMALLOC(MTYPE_ISIS_AREA_ADDR, + sizeof(struct iso_address)); addrr->addr_len = dotformat2buff(buff, net_title); memcpy(addrr->area_addr, buff, addrr->addr_len); args->resource->ptr = addrr; @@ -217,7 +225,7 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args) int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args) { - struct area_addr addr, *addrp = NULL; + struct iso_address addr, *addrp = NULL; struct listnode *node; uint8_t buff[255]; struct isis_area *area; @@ -415,6 +423,71 @@ int isis_instance_purge_originator_modify(struct nb_cb_modify_args *args) return NB_OK; } + +/* + * XPath: /frr-isisd:isis/instance/admin-group-send-zero + */ +int isis_instance_admin_group_send_zero_modify(struct nb_cb_modify_args *args) +{ + struct isis_circuit *circuit; + struct isis_area *area; + struct listnode *node; + struct flex_algo *fa; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + area = nb_running_get_entry(args->dnode, NULL, true); + area->admin_group_send_zero = yang_dnode_get_bool(args->dnode, NULL); + + if (area->admin_group_send_zero) { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + admin_group_allow_explicit_zero( + &fa->admin_group_exclude_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_all); + } + } else { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + admin_group_disallow_explicit_zero( + &fa->admin_group_exclude_any); + admin_group_disallow_explicit_zero( + &fa->admin_group_include_any); + admin_group_disallow_explicit_zero( + &fa->admin_group_include_all); + } + } + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + isis_link_params_update(circuit, circuit->interface); + + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return NB_OK; +} + + +/* + * XPath: /frr-isisd:isis/instance/asla-legacy-flag + */ +int isis_instance_asla_legacy_flag_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->asla_legacy_flag = yang_dnode_get_bool(args->dnode, NULL); + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); + + return NB_OK; +} + /* * XPath: /frr-isisd:isis/instance/lsp/mtu */ @@ -1829,6 +1902,23 @@ int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args) return NB_OK; } +/* + * XPath: /frr-isisd:isis/instance/log-pdu-drops + */ +int isis_instance_log_pdu_drops_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_pdu_drops = log ? 1 : 0; + + return NB_OK; +} + /* * XPath: /frr-isisd:isis/instance/mpls-te */ @@ -2258,7 +2348,7 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_create( 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); + pcfg = isis_sr_cfg_prefix_add(area, &prefix, SR_ALGORITHM_SPF); nb_running_set_entry(args->dnode, pcfg); return NB_OK; @@ -2448,6 +2538,833 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify( return NB_OK; } +/* + * XPath: + * /frr-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid + */ +int isis_instance_segment_routing_algorithm_prefix_sid_create( + struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct prefix prefix; + struct sr_prefix_cfg *pcfg; + uint32_t algorithm; + + 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"); + algorithm = yang_dnode_get_uint32(args->dnode, "./algo"); + + pcfg = isis_sr_cfg_prefix_add(area, &prefix, algorithm); + pcfg->algorithm = algorithm; + nb_running_set_entry(args->dnode, pcfg); + + return NB_OK; +} + +int isis_instance_segment_routing_algorithm_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_algorithm_prefix_sid_pre_validate( + struct nb_cb_pre_validate_args *args) +{ + const struct lyd_node *area_dnode; + struct isis_area *area; + struct prefix prefix; + uint32_t srgb_lbound; + uint32_t srgb_ubound; + uint32_t srgb_range; + uint32_t sid; + enum sr_sid_value_type sid_type; + struct isis_prefix_sid psid = {}; + + yang_dnode_get_prefix(&prefix, args->dnode, "./prefix"); + srgb_lbound = yang_dnode_get_uint32( + args->dnode, "../../label-blocks/srgb/lower-bound"); + srgb_ubound = yang_dnode_get_uint32( + args->dnode, "../../label-blocks/srgb/upper-bound"); + sid = yang_dnode_get_uint32(args->dnode, "./sid-value"); + sid_type = yang_dnode_get_enum(args->dnode, "./sid-value-type"); + + /* Check for invalid indexes/labels. */ + srgb_range = srgb_ubound - srgb_lbound + 1; + psid.value = sid; + switch (sid_type) { + case SR_SID_VALUE_TYPE_INDEX: + if (sid >= srgb_range) { + snprintf(args->errmsg, args->errmsg_len, + "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)) { + snprintf(args->errmsg, args->errmsg_len, + "Invalid absolute SID %u", sid); + return NB_ERR_VALIDATION; + } + SET_FLAG(psid.flags, ISIS_PREFIX_SID_VALUE); + SET_FLAG(psid.flags, ISIS_PREFIX_SID_LOCAL); + break; + } + + /* Check for Prefix-SID collisions. */ + area_dnode = yang_dnode_get_parent(args->dnode, "instance"); + area = nb_running_get_entry(area_dnode, NULL, false); + if (!area) + return NB_OK; + + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { + struct isis_spftree *spftree; + struct isis_vertex *vertex_psid; + + if (!(area->is_type & level)) + continue; + spftree = area->spftree[tree][level - 1]; + if (!spftree) + continue; + + vertex_psid = + isis_spf_prefix_sid_lookup(spftree, &psid); + if (vertex_psid && + !prefix_same(&vertex_psid->N.ip.p.dest, &prefix)) { + snprintfrr( + args->errmsg, args->errmsg_len, + "Prefix-SID collision detected, SID %s %u is already in use by prefix %pFX (L%u)", + CHECK_FLAG(psid.flags, + ISIS_PREFIX_SID_VALUE) + ? "label" + : "index", + psid.value, &vertex_psid->N.ip.p.dest, + level); + return NB_ERR_VALIDATION; + } + } + } + + return NB_OK; +} + +void isis_instance_segment_routing_algorithm_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/algorithm-prefix-sids/algorithm-prefix-sid/sid-value-type + */ +int isis_instance_segment_routing_algorithm_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/algorithm-prefix-sids/algorithm-prefix-sid/sid-value + */ +int isis_instance_segment_routing_algorithm_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/algorithm-prefix-sid-map/algorithm-prefix-sid/last-hop-behavior + */ +int isis_instance_segment_routing_algorithm_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-isisd:isis/instance/segment-routing/algorithm-prefix-sids/algorithm-prefix-sid/n-flag-clear + */ +int isis_instance_segment_routing_algorithm_prefix_sid_n_flag_clear_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->n_flag_clear = yang_dnode_get_bool(args->dnode, NULL); + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo + */ +int isis_instance_flex_algo_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + struct flex_algo *fa; + bool advertise; + uint32_t algorithm; + uint32_t priority = FLEX_ALGO_PRIO_DEFAULT; + struct isis_flex_algo_alloc_arg arg; + + algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo"); + advertise = yang_dnode_exists(args->dnode, "./advertise-definition"); + + switch (args->event) { + case NB_EV_APPLY: + area = nb_running_get_entry(args->dnode, NULL, true); + arg.algorithm = algorithm; + arg.area = area; + fa = flex_algo_alloc(area->flex_algos, algorithm, &arg); + fa->priority = priority; + fa->advertise_definition = advertise; + if (area->admin_group_send_zero) { + admin_group_allow_explicit_zero( + &fa->admin_group_exclude_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_any); + admin_group_allow_explicit_zero( + &fa->admin_group_include_all); + } + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + uint32_t algorithm; + + algorithm = yang_dnode_get_uint32(args->dnode, "./flex-algo"); + area = nb_running_get_entry(args->dnode, NULL, true); + + switch (args->event) { + case NB_EV_APPLY: + flex_algo_delete(area->flex_algos, algorithm); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/advertise-definition + */ +int isis_instance_flex_algo_advertise_definition_modify( + struct nb_cb_modify_args *args) +{ + struct isis_area *area; + struct flex_algo *fa; + bool advertise; + uint32_t algorithm; + + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + advertise = yang_dnode_exists(args->dnode, "./../advertise-definition"); + + switch (args->event) { + case NB_EV_APPLY: + area = nb_running_get_entry(args->dnode, NULL, true); + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->advertise_definition = advertise; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_advertise_definition_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + struct flex_algo *fa; + uint32_t algorithm; + + area = nb_running_get_entry(args->dnode, NULL, true); + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->advertise_definition = false; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +static int isis_instance_flex_algo_affinity_set(struct nb_cb_create_args *args, + int type) +{ + struct affinity_map *map; + struct isis_area *area; + struct admin_group *ag; + struct flex_algo *fa; + uint32_t algorithm; + const char *val; + + algorithm = yang_dnode_get_uint32(args->dnode, "../../flex-algo"); + area = nb_running_get_entry(args->dnode, NULL, true); + val = yang_dnode_get_string(args->dnode, "."); + + switch (args->event) { + case NB_EV_VALIDATE: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_RESOURCE; + } + if (type == AFFINITY_INCLUDE_ANY) + ag = &fa->admin_group_include_any; + else if (type == AFFINITY_INCLUDE_ALL) + ag = &fa->admin_group_include_all; + else if (type == AFFINITY_EXCLUDE_ANY) + ag = &fa->admin_group_exclude_any; + else + break; + + admin_group_set(ag, map->bit_position); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + } + + return NB_OK; +} + +static int +isis_instance_flex_algo_affinity_unset(struct nb_cb_destroy_args *args, + int type) +{ + struct affinity_map *map; + struct isis_area *area; + struct admin_group *ag; + struct flex_algo *fa; + uint32_t algorithm; + const char *val; + + algorithm = yang_dnode_get_uint32(args->dnode, "../../flex-algo"); + area = nb_running_get_entry(args->dnode, NULL, true); + val = yang_dnode_get_string(args->dnode, "."); + + switch (args->event) { + case NB_EV_VALIDATE: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + map = affinity_map_get(val); + if (!map) { + snprintf(args->errmsg, args->errmsg_len, + "affinity map %s isn't found", val); + return NB_ERR_RESOURCE; + } + if (type == AFFINITY_INCLUDE_ANY) + ag = &fa->admin_group_include_any; + else if (type == AFFINITY_INCLUDE_ALL) + ag = &fa->admin_group_include_all; + else if (type == AFFINITY_EXCLUDE_ANY) + ag = &fa->admin_group_exclude_any; + else + break; + + admin_group_unset(ag, map->bit_position); + if (area->admin_group_send_zero) + admin_group_allow_explicit_zero(ag); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + } + + return NB_OK; +} + +/* + * XPath: + * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-anies/affinity-include-any + */ +int isis_instance_flex_algo_affinity_include_any_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_affinity_set(args, AFFINITY_INCLUDE_ANY); +} + +int isis_instance_flex_algo_affinity_include_any_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_affinity_unset(args, + AFFINITY_INCLUDE_ANY); +} + +/* + * XPath: + * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-include-alls/affinity-include-all + */ +int isis_instance_flex_algo_affinity_include_all_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_affinity_set(args, AFFINITY_INCLUDE_ALL); +} + +int isis_instance_flex_algo_affinity_include_all_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_affinity_unset(args, + AFFINITY_INCLUDE_ALL); +} + +/* + * XPath: + * /frr-isisd:isis/instance/flex-algos/flex-algo/affinity-exclude-anies/affinity-exclude-any + */ +int isis_instance_flex_algo_affinity_exclude_any_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_affinity_set(args, AFFINITY_EXCLUDE_ANY); +} + +int isis_instance_flex_algo_affinity_exclude_any_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_affinity_unset(args, + AFFINITY_EXCLUDE_ANY); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/prefix-metric + */ + +int isis_instance_flex_algo_prefix_metric_create(struct nb_cb_create_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + SET_FLAG(fa->flags, FAD_FLAG_M); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_prefix_metric_destroy( + struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + UNSET_FLAG(fa->flags, FAD_FLAG_M); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +static int isis_instance_flex_algo_dplane_set(struct nb_cb_create_args *args, + int type) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + SET_FLAG(fa->dataplanes, type); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + if (type == FLEX_ALGO_SRV6 || type == FLEX_ALGO_IP) { + snprintf(args->errmsg, args->errmsg_len, + "%s Flex-algo dataplane is not yet supported.", + type == FLEX_ALGO_SRV6 ? "SRv6" : "IP"); + return NB_ERR_VALIDATION; + } + break; + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +static int isis_instance_flex_algo_dplane_unset(struct nb_cb_destroy_args *args, + int type) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + UNSET_FLAG(fa->dataplanes, type); + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-sr-mpls + */ + +int isis_instance_flex_algo_dplane_sr_mpls_create( + struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_SR_MPLS); +} + +int isis_instance_flex_algo_dplane_sr_mpls_destroy( + struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_SR_MPLS); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-srv6 + */ + +int isis_instance_flex_algo_dplane_srv6_create(struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_SRV6); +} + +int isis_instance_flex_algo_dplane_srv6_destroy(struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_SRV6); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/dplane-ip + */ + +int isis_instance_flex_algo_dplane_ip_create(struct nb_cb_create_args *args) +{ + return isis_instance_flex_algo_dplane_set(args, FLEX_ALGO_IP); +} + +int isis_instance_flex_algo_dplane_ip_destroy(struct nb_cb_destroy_args *args) +{ + return isis_instance_flex_algo_dplane_unset(args, FLEX_ALGO_IP); +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/metric-type + */ + +int isis_instance_flex_algo_metric_type_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + enum flex_algo_metric_type metric_type; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + metric_type = yang_dnode_get_enum(args->dnode, NULL); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->metric_type = metric_type; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-isisd:isis/instance/flex-algos/flex-algo/priority + */ + +int isis_instance_flex_algo_priority_modify(struct nb_cb_modify_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + uint32_t priority; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + priority = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->priority = priority; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + +int isis_instance_flex_algo_priority_destroy(struct nb_cb_destroy_args *args) +{ + struct isis_area *area; + const char *area_tag; + struct flex_algo *fa; + uint32_t algorithm; + uint32_t priority = FLEX_ALGO_PRIO_DEFAULT; + + area_tag = yang_dnode_get_string(args->dnode, "../../../area-tag"); + area = isis_area_lookup(area_tag, VRF_DEFAULT); + if (!area) + return NB_ERR_RESOURCE; + + algorithm = yang_dnode_get_uint32(args->dnode, "./../flex-algo"); + priority = yang_dnode_get_uint32(args->dnode, NULL); + + switch (args->event) { + case NB_EV_APPLY: + fa = flex_algo_lookup(area->flex_algos, algorithm); + if (!fa) { + snprintf(args->errmsg, args->errmsg_len, + "flex-algo object not found"); + return NB_ERR_RESOURCE; + } + fa->priority = priority; + lsp_regenerate_schedule(area, area->is_type, 0); + break; + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + } + + return NB_OK; +} + /* * XPath: /frr-isisd:isis/instance/mpls/ldp-sync */ @@ -2570,7 +3487,8 @@ int lib_interface_isis_area_tag_modify(struct nb_cb_modify_args *args) struct isis_circuit *circuit; if (args->event == NB_EV_VALIDATE) { - circuit = nb_running_get_entry_non_rec(lyd_parent(args->dnode), NULL, false); + circuit = nb_running_get_entry_non_rec(lyd_parent(args->dnode), + NULL, false); if (circuit) { snprintf(args->errmsg, args->errmsg_len, "Changing area tag is not allowed"); @@ -2783,7 +3701,7 @@ int lib_interface_isis_hello_padding_modify(struct nb_cb_modify_args *args) return NB_OK; circuit = nb_running_get_entry(args->dnode, NULL, true); - circuit->pad_hellos = yang_dnode_get_bool(args->dnode, NULL); + circuit->pad_hellos = yang_dnode_get_enum(args->dnode, NULL); return NB_OK; } diff --git a/isisd/isis_nb_notifications.c b/isisd/isis_nb_notifications.c index 94b1c47d3efe..5a1e312b4dbf 100644 --- a/isisd/isis_nb_notifications.c +++ b/isisd/isis_nb_notifications.c @@ -134,6 +134,7 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:lsp-too-large"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_area *area = circuit->area; @@ -143,7 +144,8 @@ void isis_notif_lsp_too_large(const struct isis_circuit *circuit, 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_lsp_too_large, circuit, pdu_size, lsp_id); @@ -180,11 +182,13 @@ void isis_notif_corrupted_lsp(const struct isis_area *area, const char *xpath = "/frr-isisd:corrupted-lsp-detected"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_corrupted_lsp, area); @@ -201,11 +205,13 @@ void isis_notif_lsp_exceed_max(const struct isis_area *area, const char *xpath = "/frr-isisd:attempt-to-exceed-max-sequence"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_lsp_exceed_max, area, lsp_id); @@ -299,6 +305,7 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, const char *xpath = "/frr-isisd:adjacency-state-change"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; struct yang_data *data; struct isis_circuit *circuit = adj->circuit; struct isis_area *area = circuit->area; @@ -312,7 +319,8 @@ void isis_notif_adj_state_change(const struct isis_adjacency *adj, 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)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->sysid); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/state", xpath); @@ -389,13 +397,15 @@ void isis_notif_lsp_received(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:lsp-received"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); data = yang_data_new_uint32(xpath_arg, seqno); @@ -419,11 +429,13 @@ void isis_notif_lsp_gen(const struct isis_area *area, const uint8_t *lsp_id, const char *xpath = "/frr-isisd:lsp-generation"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/sequence", xpath); data = yang_data_new_uint32(xpath_arg, seqno); @@ -503,13 +515,15 @@ void isis_notif_lsp_error(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:lsp-error-detected"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); snprintf(xpath_arg, sizeof(xpath_arg), "%s/raw-pdu", xpath); data = yang_data_new_binary(xpath_arg, raw_pdu, raw_pdu_len); @@ -530,13 +544,15 @@ void isis_notif_seqno_skipped(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:sequence-number-skipped"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_seqno_skipped, circuit, lsp_id); @@ -553,13 +569,15 @@ void isis_notif_own_lsp_purge(const struct isis_circuit *circuit, const char *xpath = "/frr-isisd:own-lsp-purge"; struct list *arguments = yang_data_list_new(); char xpath_arg[XPATH_MAXLEN]; + char xpath_value[ISO_SYSID_STRLEN]; 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, rawlspid_print(lsp_id)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pLS", lsp_id); + data = yang_data_new_string(xpath_arg, xpath_value); listnode_add(arguments, data); hook_call(isis_hook_own_lsp_purge, circuit, lsp_id); diff --git a/isisd/isis_nb_state.c b/isisd/isis_nb_state.c index 13fdddf555c9..b7c33ed27b8b 100644 --- a/isisd/isis_nb_state.c +++ b/isisd/isis_nb_state.c @@ -132,8 +132,11 @@ 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; + char xpath_value[ISO_SYSID_STRLEN]; - return yang_data_new_string(args->xpath, sysid_print(adj->sysid)); + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->sysid); + + return yang_data_new_string(args->xpath, xpath_value); } /* @@ -158,8 +161,11 @@ 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; + char xpath_value[ISO_SYSID_STRLEN]; + + snprintfrr(xpath_value, ISO_SYSID_STRLEN, "%pSY", adj->snpa); - return yang_data_new_string(args->xpath, snpa_print(adj->snpa)); + return yang_data_new_string(args->xpath, xpath_value); } /* diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 70ec2426d3ae..0cd43a7abc9e 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -11,7 +11,7 @@ #include <zebra.h> #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "log.h" #include "stream.h" @@ -192,9 +192,9 @@ static int process_p2p_hello(struct iih_info *iih) adj); /* lets take care of the expiry */ - THREAD_OFF(adj->t_expire); - thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, - &adj->t_expire); + EVENT_OFF(adj->t_expire); + event_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, + &adj->t_expire); /* While fabricds initial sync is in progress, ignore hellos from other * interfaces than the one we are performing the initial sync on. */ @@ -466,8 +466,8 @@ static int process_lan_hello(struct iih_info *iih) : iih->circuit->u.bc.l2_desig_is; if (memcmp(dis, iih->dis, ISIS_SYS_ID_LEN + 1)) { - thread_add_event(master, isis_event_dis_status_change, - iih->circuit, 0, NULL); + event_add_event(master, isis_event_dis_status_change, + iih->circuit, 0, NULL); memcpy(dis, iih->dis, ISIS_SYS_ID_LEN + 1); } } @@ -484,9 +484,9 @@ static int process_lan_hello(struct iih_info *iih) adj); /* lets take care of the expiry */ - THREAD_OFF(adj->t_expire); - thread_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, - &adj->t_expire); + EVENT_OFF(adj->t_expire); + event_add_timer(master, isis_adj_expire, adj, (long)adj->hold_time, + &adj->t_expire); /* * If the snpa for this circuit is found from LAN Neighbours TLV @@ -514,9 +514,9 @@ static int process_lan_hello(struct iih_info *iih) 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, - snpa_print(iih->ssnpa), iih->circuit->interface->name, + "ISIS-Adj (%s): Rcvd L%d LAN IIH from %pSY on %s, cirType %s, cirID %u, length %zd", + iih->circuit->area->area_tag, iih->level, iih->ssnpa, + iih->circuit->interface->name, circuit_t2string(iih->circuit->is_type), iih->circuit->circuit_id, stream_get_endp(iih->circuit->rcv_stream)); @@ -862,31 +862,32 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, #ifndef FABRICD /* send northbound notification */ + char buf[ISO_SYSID_STRLEN]; + + snprintfrr(buf, ISO_SYSID_STRLEN, "%pSY", hdr.lsp_id); isis_notif_lsp_received(circuit, hdr.lsp_id, hdr.seqno, time(NULL), - sysid_print(hdr.lsp_id)); + buf); #endif /* ifndef FABRICD */ if (pdu_len_validate(hdr.pdu_len, circuit)) { - zlog_debug("ISIS-Upd (%s): LSP %s invalid LSP length %hu", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.pdu_len); + zlog_debug("ISIS-Upd (%s): LSP %pLS invalid LSP length %hu", + circuit->area->area_tag, hdr.lsp_id, hdr.pdu_len); return ISIS_WARNING; } 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, - circuit->interface->name); + zlog_debug( + "ISIS-Upd (%s): Rcvd L%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus, len %hu, on %s", + circuit->area->area_tag, level, hdr.lsp_id, hdr.seqno, + hdr.checksum, hdr.rem_lifetime, hdr.pdu_len, + circuit->interface->name); } /* 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%x", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.lsp_bits & IS_LEVEL_1_AND_2); + zlog_debug("ISIS-Upd (%s): LSP %pLS invalid LSP is type 0x%x", + circuit->area->area_tag, hdr.lsp_id, + hdr.lsp_bits & IS_LEVEL_1_AND_2); /* continue as per RFC1122 Be liberal in what you accept, and * conservative in what you send */ } @@ -896,27 +897,25 @@ 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%04hx", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.checksum); + "ISIS-Upd (%s): LSP %pLS invalid LSP checksum 0x%04hx", + circuit->area->area_tag, hdr.lsp_id, hdr.checksum); return ISIS_WARNING; } /* 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", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - level); + "ISIS-Upd (%s): LSP %pLS received at level %d over circuit with externalDomain = true", + circuit->area->area_tag, hdr.lsp_id, level); return ISIS_WARNING; } /* 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", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - level, circuit_t2string(circuit->is_type)); + "ISIS-Upd (%s): LSP %pLS received at level %d over circuit of type %s", + circuit->area->area_tag, hdr.lsp_id, level, + circuit_t2string(circuit->is_type)); return ISIS_WARNING; } @@ -1016,11 +1015,11 @@ 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%08x, cksum 0x%04hx, lifetime %hus on %s", - circuit->area->area_tag, - rawlspid_print(hdr.lsp_id), hdr.seqno, - hdr.checksum, hdr.rem_lifetime, - circuit->interface->name); + zlog_debug( + "(%s): DS ======= LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", + circuit->area->area_tag, hdr.lsp_id, hdr.seqno, + hdr.checksum, hdr.rem_lifetime, + circuit->interface->name); goto out; /* Silently discard */ } } @@ -1057,9 +1056,9 @@ 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%08x with confused checksum received.", - circuit->area->area_tag, rawlspid_print(hdr.lsp_id), - hdr.seqno); + zlog_warn( + "ISIS-Upd (%s): LSP %pLS seq 0x%08x with confused checksum received.", + circuit->area->area_tag, hdr.lsp_id, hdr.seqno); hdr.rem_lifetime = 0; lsp_confusion = true; } else @@ -1153,10 +1152,9 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, } if (IS_DEBUG_UPDATE_PACKETS) zlog_debug( - "ISIS-Upd (%s): (1) re-originating LSP %s new seq 0x%08x", + "ISIS-Upd (%s): (1) re-originating LSP %pLS new seq 0x%08x", circuit->area->area_tag, - rawlspid_print(hdr.lsp_id), - lsp->hdr.seqno); + hdr.lsp_id, lsp->hdr.seqno); } else { /* our own LSP with 0 remaining life time */ #ifndef FABRICD @@ -1194,9 +1192,8 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, #endif /* ifndef FABRICD */ if (IS_DEBUG_UPDATE_PACKETS) { zlog_debug( - "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08x", - circuit->area->area_tag, - rawlspid_print(hdr.lsp_id), + "ISIS-Upd (%s): (2) re-originating LSP %pLS new seq 0x%08x", + circuit->area->area_tag, hdr.lsp_id, lsp->hdr.seqno); } lsp_flood(lsp, NULL); @@ -1361,9 +1358,9 @@ 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", - circuit->area->area_tag, level, typechar, - snpa_print(ssnpa), circuit->interface->name); + "ISIS-Snp (%s): Rcvd L%d %cSNP from %pSY on %s, skipping: we are not the DIS", + circuit->area->area_tag, level, typechar, ssnpa, + circuit->interface->name); return ISIS_OK; } @@ -1452,16 +1449,16 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, /* debug isis 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); + zlog_debug("ISIS-Snp (%s): Rcvd L%d %cSNP from %pSY on %s", + circuit->area->area_tag, level, typechar, 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%08x, cksum 0x%04hx, lifetime %hus", - circuit->area->area_tag, typechar, - rawlspid_print(entry->id), entry->seqno, - entry->checksum, entry->rem_lifetime); + "ISIS-Snp (%s): %cSNP entry %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus", + circuit->area->area_tag, typechar, entry->id, + entry->seqno, entry->checksum, + entry->rem_lifetime); } } @@ -1654,12 +1651,14 @@ 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=%hhx", idrp); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } if (idrp != ISO10589_ISIS) { flog_err(EC_ISIS_PACKET, "Not an IS-IS packet IDRP=%hhx", idrp); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1670,6 +1669,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_version_skew(circuit, version1, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } @@ -1693,12 +1693,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_id_len_mismatch(circuit, id_len, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } uint8_t expected_length; if (pdu_size(pdu_type, &expected_length)) { zlog_warn("Unsupported ISIS PDU %hhu", pdu_type); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } @@ -1706,6 +1708,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) flog_err(EC_ISIS_PACKET, "Expected fixed header length = %hhu but got %hhu", expected_length, length); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1713,6 +1716,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) flog_err( EC_ISIS_PACKET, "PDU is too short to contain fixed header of given PDU type."); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1723,12 +1727,14 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_version_skew(circuit, version2, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } if (circuit->is_passive) { zlog_warn("Received ISIS PDU on passive circuit %s", circuit->interface->name); + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_WARNING; } @@ -1747,6 +1753,7 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) isis_notif_max_area_addr_mismatch(circuit, max_area_addrs, raw_pdu, sizeof(raw_pdu)); #endif /* ifndef FABRICD */ + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } @@ -1754,17 +1761,22 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) case L1_LAN_HELLO: case L2_LAN_HELLO: case P2P_HELLO: - if (fabricd && pdu_type != P2P_HELLO) + if (fabricd && pdu_type != P2P_HELLO) { + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; + } + retval = process_hello(pdu_type, circuit, ssnpa); break; case L1_LINK_STATE: case L2_LINK_STATE: case FS_LINK_STATE: - if (fabricd - && pdu_type != L2_LINK_STATE - && pdu_type != FS_LINK_STATE) + if (fabricd && pdu_type != L2_LINK_STATE && + pdu_type != FS_LINK_STATE) { + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; + } + retval = process_lsp(pdu_type, circuit, ssnpa, max_area_addrs); break; case L1_COMPLETE_SEQ_NUM: @@ -1774,13 +1786,17 @@ int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa) retval = process_snp(pdu_type, circuit, ssnpa); break; default: + pdu_counter_count_drop(circuit->area, pdu_type); return ISIS_ERROR; } + if (retval != ISIS_OK) + pdu_counter_count_drop(circuit->area, pdu_type); + return retval; } -void isis_receive(struct thread *thread) +void isis_receive(struct event *thread) { struct isis_circuit *circuit; uint8_t ssnpa[ETH_ALEN]; @@ -1788,7 +1804,7 @@ void isis_receive(struct thread *thread) /* * Get the circuit */ - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); assert(circuit); circuit->t_read = NULL; @@ -1964,8 +1980,14 @@ int send_hello(struct isis_circuit *circuit, int level) isis_tlvs_add_global_ipv6_addresses(tlvs, circuit->ipv6_non_link); + bool should_pad_hello = + circuit->pad_hellos == ISIS_HELLO_PADDING_ALWAYS || + (circuit->pad_hellos == + ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION && + circuit->upadjcount[0] + circuit->upadjcount[1] == 0); + if (isis_pack_tlvs(tlvs, circuit->snd_stream, len_pointer, - circuit->pad_hellos, false)) { + should_pad_hello, false)) { isis_free_tlvs(tlvs); return ISIS_WARNING; /* XXX: Maybe Log TLV structure? */ } @@ -2003,9 +2025,9 @@ int send_hello(struct isis_circuit *circuit, int level) return retval; } -static void send_hello_cb(struct thread *thread) +static void send_hello_cb(struct event *thread) { - struct isis_circuit_arg *arg = THREAD_ARG(thread); + struct isis_circuit_arg *arg = EVENT_ARG(thread); assert(arg); struct isis_circuit *circuit = arg->circuit; @@ -2044,20 +2066,18 @@ static void send_hello_cb(struct thread *thread) } static void _send_hello_sched(struct isis_circuit *circuit, - struct thread **threadp, - int level, long delay) + struct event **threadp, int level, long delay) { if (*threadp) { - if (thread_timer_remain_msec(*threadp) < (unsigned long)delay) + if (event_timer_remain_msec(*threadp) < (unsigned long)delay) return; - THREAD_OFF(*threadp); + EVENT_OFF(*threadp); } - thread_add_timer_msec(master, send_hello_cb, - &circuit->level_arg[level - 1], - isis_jitter(delay, IIH_JITTER), - threadp); + event_add_timer_msec(master, send_hello_cb, + &circuit->level_arg[level - 1], + isis_jitter(delay, IIH_JITTER), threadp); } void send_hello_sched(struct isis_circuit *circuit, int level, long delay) @@ -2234,11 +2254,11 @@ int send_csnp(struct isis_circuit *circuit, int level) return ISIS_OK; } -void send_l1_csnp(struct thread *thread) +void send_l1_csnp(struct event *thread) { struct isis_circuit *circuit; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); assert(circuit); circuit->t_send_csnp[0] = NULL; @@ -2249,16 +2269,16 @@ void send_l1_csnp(struct thread *thread) send_csnp(circuit, 1); } /* set next timer thread */ - thread_add_timer(master, send_l1_csnp, circuit, - isis_jitter(circuit->csnp_interval[0], CSNP_JITTER), - &circuit->t_send_csnp[0]); + event_add_timer(master, send_l1_csnp, circuit, + isis_jitter(circuit->csnp_interval[0], CSNP_JITTER), + &circuit->t_send_csnp[0]); } -void send_l2_csnp(struct thread *thread) +void send_l2_csnp(struct event *thread) { struct isis_circuit *circuit; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); assert(circuit); circuit->t_send_csnp[1] = NULL; @@ -2269,9 +2289,9 @@ void send_l2_csnp(struct thread *thread) send_csnp(circuit, 2); } /* set next timer thread */ - thread_add_timer(master, send_l2_csnp, circuit, - isis_jitter(circuit->csnp_interval[1], CSNP_JITTER), - &circuit->t_send_csnp[1]); + event_add_timer(master, send_l2_csnp, circuit, + isis_jitter(circuit->csnp_interval[1], CSNP_JITTER), + &circuit->t_send_csnp[1]); } /* @@ -2388,32 +2408,32 @@ static int send_psnp(int level, struct isis_circuit *circuit) return ISIS_OK; } -void send_l1_psnp(struct thread *thread) +void send_l1_psnp(struct event *thread) { struct isis_circuit *circuit; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); assert(circuit); circuit->t_send_psnp[0] = NULL; send_psnp(1, circuit); /* set next timer thread */ - thread_add_timer(master, send_l1_psnp, circuit, - isis_jitter(circuit->psnp_interval[0], PSNP_JITTER), - &circuit->t_send_psnp[0]); + event_add_timer(master, send_l1_psnp, circuit, + isis_jitter(circuit->psnp_interval[0], PSNP_JITTER), + &circuit->t_send_psnp[0]); } /* * 7.3.15.4 action on expiration of partial SNP interval * level 2 */ -void send_l2_psnp(struct thread *thread) +void send_l2_psnp(struct event *thread) { struct isis_circuit *circuit; - circuit = THREAD_ARG(thread); + circuit = EVENT_ARG(thread); assert(circuit); circuit->t_send_psnp[1] = NULL; @@ -2421,9 +2441,9 @@ void send_l2_psnp(struct thread *thread) send_psnp(2, circuit); /* set next timer thread */ - thread_add_timer(master, send_l2_psnp, circuit, - isis_jitter(circuit->psnp_interval[1], PSNP_JITTER), - &circuit->t_send_psnp[1]); + event_add_timer(master, send_l2_psnp, circuit, + isis_jitter(circuit->psnp_interval[1], PSNP_JITTER), + &circuit->t_send_psnp[1]); } /* @@ -2456,11 +2476,11 @@ 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%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, - circuit->interface->name, stream_get_endp(lsp->pdu), + "ISIS-Upd (%s): Can't send L%d LSP %pLS, 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, lsp->hdr.lsp_id, + lsp->hdr.seqno, lsp->hdr.checksum, + lsp->hdr.rem_lifetime, circuit->interface->name, + stream_get_endp(lsp->pdu), stream_get_size(circuit->snd_stream)); #ifndef FABRICD /* send a northbound notification */ @@ -2484,14 +2504,14 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, } 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 " : "", - lsp->level, - rawlspid_print(lsp->hdr.lsp_id), lsp->hdr.seqno, - lsp->hdr.checksum, lsp->hdr.rem_lifetime, - circuit->interface->name); + zlog_debug( + "ISIS-Upd (%s): Sending %sL%d LSP %pLS, seq 0x%08x, cksum 0x%04hx, lifetime %hus on %s", + circuit->area->area_tag, + (tx_type == TX_LSP_CIRCUIT_SCOPED) ? "Circuit scoped " + : "", + lsp->level, lsp->hdr.lsp_id, lsp->hdr.seqno, + lsp->hdr.checksum, lsp->hdr.rem_lifetime, + circuit->interface->name); if (IS_DEBUG_PACKET_DUMP) zlog_dump_data(STREAM_DATA(circuit->snd_stream), stream_get_endp(circuit->snd_stream)); @@ -2529,3 +2549,37 @@ void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, isis_tx_queue_del(circuit->tx_queue, lsp); } } + +void isis_log_pdu_drops(struct isis_area *area, const char *pdu_type) +{ + uint64_t total_drops = 0; + + for (int i = 0; i < PDU_COUNTER_SIZE; i++) { + if (!area->pdu_drop_counters[i]) + continue; + total_drops += area->pdu_drop_counters[i]; + } + + zlog_info("PDU drop detected of type: %s. %" PRIu64 + " Total Drops; %" PRIu64 " L1 IIH drops; %" PRIu64 + " L2 IIH drops; %" PRIu64 " P2P IIH drops; %" PRIu64 + " L1 LSP drops; %" PRIu64 " L2 LSP drops; %" PRIu64 + " FS LSP drops; %" PRIu64 " L1 CSNP drops; %" PRIu64 + " L2 CSNP drops; %" PRIu64 " L1 PSNP drops; %" PRIu64 + " L2 PSNP drops.", + pdu_type, total_drops, + pdu_counter_get_count(area->pdu_drop_counters, L1_LAN_HELLO), + pdu_counter_get_count(area->pdu_drop_counters, L2_LAN_HELLO), + pdu_counter_get_count(area->pdu_drop_counters, P2P_HELLO), + pdu_counter_get_count(area->pdu_drop_counters, L1_LINK_STATE), + pdu_counter_get_count(area->pdu_drop_counters, L2_LINK_STATE), + pdu_counter_get_count(area->pdu_drop_counters, FS_LINK_STATE), + pdu_counter_get_count(area->pdu_drop_counters, + L1_COMPLETE_SEQ_NUM), + pdu_counter_get_count(area->pdu_drop_counters, + L2_COMPLETE_SEQ_NUM), + pdu_counter_get_count(area->pdu_drop_counters, + L1_PARTIAL_SEQ_NUM), + pdu_counter_get_count(area->pdu_drop_counters, + L2_PARTIAL_SEQ_NUM)); +} diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h index 512439400069..5303c61d38a2 100644 --- a/isisd/isis_pdu.h +++ b/isisd/isis_pdu.h @@ -182,7 +182,7 @@ struct isis_partial_seqnum_hdr { /* * Function for receiving IS-IS PDUs */ -void isis_receive(struct thread *thread); +void isis_receive(struct event *thread); /* * calling arguments for snp_process () @@ -197,13 +197,15 @@ void isis_receive(struct thread *thread); */ void send_hello_sched(struct isis_circuit *circuit, int level, long delay); int send_csnp(struct isis_circuit *circuit, int level); -void send_l1_csnp(struct thread *thread); -void send_l2_csnp(struct thread *thread); -void send_l1_psnp(struct thread *thread); -void send_l2_psnp(struct thread *thread); +void send_l1_csnp(struct event *thread); +void send_l2_csnp(struct event *thread); +void send_l1_psnp(struct event *thread); +void send_l2_psnp(struct event *thread); void send_lsp(struct isis_circuit *circuit, struct isis_lsp *lsp, enum isis_tx_type tx_type); void fill_fixed_hdr(uint8_t pdu_type, struct stream *stream); int send_hello(struct isis_circuit *circuit, int level); int isis_handle_pdu(struct isis_circuit *circuit, uint8_t *ssnpa); +void isis_log_pdu_drops(struct isis_area *area, const char *pdu_type); + #endif /* _ZEBRA_ISIS_PDU_H */ diff --git a/isisd/isis_pdu_counter.c b/isisd/isis_pdu_counter.c index 9d07b5e5989d..a3605a32a162 100644 --- a/isisd/isis_pdu_counter.c +++ b/isisd/isis_pdu_counter.c @@ -8,10 +8,10 @@ #include "vty.h" -#include "isisd/isis_pdu_counter.h" #include "isisd/isisd.h" #include "isisd/isis_circuit.h" #include "isisd/isis_pdu.h" +#include "isisd/isis_pdu_counter.h" static int pdu_type_to_counter_index(uint8_t pdu_type) { @@ -91,3 +91,23 @@ void pdu_counter_print(struct vty *vty, const char *prefix, pdu_counter_index_to_name(i), counter[i]); } } + +void pdu_counter_count_drop(struct isis_area *area, uint8_t pdu_type) +{ + pdu_counter_count(area->pdu_drop_counters, pdu_type); + + if (area->log_pdu_drops) { + isis_log_pdu_drops( + area, pdu_counter_index_to_name( + pdu_type_to_counter_index(pdu_type))); + } +} + +uint64_t pdu_counter_get_count(pdu_counter_t counter, uint8_t pdu_type) +{ + int index = pdu_type_to_counter_index(pdu_type); + + if (index < 0) + return -1; + return counter[index]; +} diff --git a/isisd/isis_pdu_counter.h b/isisd/isis_pdu_counter.h index c53c47368f46..5c35b4fb51da 100644 --- a/isisd/isis_pdu_counter.h +++ b/isisd/isis_pdu_counter.h @@ -24,5 +24,7 @@ typedef uint64_t pdu_counter_t[PDU_COUNTER_SIZE]; void pdu_counter_print(struct vty *vty, const char *prefix, pdu_counter_t counter); void pdu_counter_count(pdu_counter_t counter, uint8_t pdu_type); +void pdu_counter_count_drop(struct isis_area *area, uint8_t pdu_type); +uint64_t pdu_counter_get_count(pdu_counter_t counter, uint8_t pdu_type); #endif diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 4f8ea4a5e70b..af69fac1cd9a 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -35,14 +35,25 @@ /* tcpdump -i eth0 'isis' -dd */ 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 - * over GRE?) */ - /* { 0x28, 0, 0, 0x0000000c - 14 }, */ - /* { 0x25, 5, 0, 0x000005dc }, */ - {0x28, 0, 0, 0x0000000e - 14}, {0x15, 0, 3, 0x0000fefe}, - {0x30, 0, 0, 0x00000011 - 14}, {0x15, 0, 1, 0x00000083}, - {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}, + * off! */ + /* The following BPF filter accepts IS-IS over LLC and IS-IS over + * ethertype 0x00fe. + * BPF assembly: + * l0: ldh [0] + * l1: jeq #0xfefe, l2, l4 + * l2: ldb [3] + * l3: jmp l7 + * l4: ldh proto + * l5: jeq #0x00fe, l6, l9 + * l6: ldb [0] + * l7: jeq #0x83, l8, l9 + * l8: ret #0x40000 + * l9: ret #0 */ + {0x28, 0, 0, 0000000000}, {0x15, 0, 2, 0x0000fefe}, + {0x30, 0, 0, 0x00000003}, {0x05, 0, 0, 0x00000003}, + {0x28, 0, 0, 0xfffff000}, {0x15, 0, 3, 0x000000fe}, + {0x30, 0, 0, 0000000000}, {0x15, 0, 1, 0x00000083}, + {0x06, 0, 0, 0x00040000}, {0x06, 0, 0, 0000000000}, }; static const struct sock_fprog bpf = { diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 711d5cbed9f4..be92dcc22ef6 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -11,7 +11,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "log.h" @@ -36,9 +36,12 @@ #include "isis_spf_private.h" #include "isis_route.h" #include "isis_zebra.h" +#include "isis_flex_algo.h" DEFINE_MTYPE_STATIC(ISISD, ISIS_NEXTHOP, "ISIS nexthop"); DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_INFO, "ISIS route info"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_ROUTE_TABLE_INFO, "ISIS route table info"); + DEFINE_HOOK(isis_route_update_hook, (struct isis_area * area, struct prefix *prefix, @@ -51,8 +54,25 @@ 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) +static struct mpls_label_stack * +label_stack_dup(const struct mpls_label_stack *const orig) +{ + struct mpls_label_stack *copy; + int array_size; + + if (orig == NULL) + return NULL; + + array_size = orig->num_labels * sizeof(mpls_label_t); + copy = XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS, + sizeof(struct mpls_label_stack) + array_size); + copy->num_labels = orig->num_labels; + memcpy(copy->label, orig->label, array_size); + return copy; +} + +static struct isis_nexthop * +isis_nexthop_create(int family, const union g_addr *const ip, ifindex_t ifindex) { struct isis_nexthop *nexthop; @@ -65,12 +85,40 @@ static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip, return nexthop; } +static struct isis_nexthop * +isis_nexthop_dup(const struct isis_nexthop *const orig) +{ + struct isis_nexthop *nexthop; + + nexthop = isis_nexthop_create(orig->family, &orig->ip, orig->ifindex); + memcpy(nexthop->sysid, orig->sysid, ISIS_SYS_ID_LEN); + nexthop->sr = orig->sr; + nexthop->label_stack = label_stack_dup(orig->label_stack); + + return nexthop; +} + void isis_nexthop_delete(struct isis_nexthop *nexthop) { XFREE(MTYPE_ISIS_NEXTHOP_LABELS, nexthop->label_stack); XFREE(MTYPE_ISIS_NEXTHOP, nexthop); } +static struct list *isis_nexthop_list_dup(const struct list *orig) +{ + struct list *copy; + struct listnode *node; + struct isis_nexthop *nh; + struct isis_nexthop *nhcopy; + + copy = list_new(); + for (ALL_LIST_ELEMENTS_RO(orig, node, nh)) { + nhcopy = isis_nexthop_dup(nh); + listnode_add(copy, nhcopy); + } + return copy; +} + static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, union g_addr *ip, ifindex_t ifindex) { @@ -238,13 +286,28 @@ isis_route_info_new(struct prefix *prefix, struct prefix_ipv6 *src_p, rinfo->cost = cost; rinfo->depth = depth; - rinfo->sr = *sr; + rinfo->sr_algo[sr->algorithm] = *sr; + rinfo->sr_algo[sr->algorithm].nexthops = rinfo->nexthops; + rinfo->sr_algo[sr->algorithm].nexthops_backup = + rinfo->backup ? rinfo->backup->nexthops : NULL; return rinfo; } static void isis_route_info_delete(struct isis_route_info *route_info) { + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (!route_info->sr_algo[i].present) + continue; + + if (route_info->sr_algo[i].nexthops == route_info->nexthops) + continue; + + route_info->sr_algo[i].nexthops->del = + (void (*)(void *))isis_nexthop_delete; + list_delete(&route_info->sr_algo[i].nexthops); + } + if (route_info->nexthops) { route_info->nexthops->del = (void (*)(void *))isis_nexthop_delete; @@ -260,6 +323,27 @@ void isis_route_node_cleanup(struct route_table *table, struct route_node *node) isis_route_info_delete(node->info); } +struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm) +{ + struct isis_route_table_info *info; + + info = XCALLOC(MTYPE_ISIS_ROUTE_TABLE_INFO, sizeof(*info)); + info->algorithm = algorithm; + return info; +} + +void isis_route_table_info_free(void *info) +{ + XFREE(MTYPE_ISIS_ROUTE_TABLE_INFO, info); +} + +uint8_t isis_route_table_algorithm(const struct route_table *table) +{ + const struct isis_route_table_info *info = table->info; + + return info ? info->algorithm : 0; +} + static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new, struct isis_sr_psid_info *old) { @@ -273,6 +357,9 @@ static bool isis_sr_psid_info_same(struct isis_sr_psid_info *new, || new->sid.value != old->sid.value) return false; + if (new->sid.algorithm != old->sid.algorithm) + return false; + return true; } @@ -313,10 +400,22 @@ static int isis_route_info_same(struct isis_route_info *new, return 0; } - if (!isis_sr_psid_info_same(&new->sr, &old->sr)) { - if (buf) - snprintf(buf, buf_size, "SR input label"); - return 0; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_sr_psid_info new_sr_algo; + struct isis_sr_psid_info old_sr_algo; + + new_sr_algo = new->sr_algo[i]; + old_sr_algo = old->sr_algo[i]; + + if (!isis_sr_psid_info_same(&new_sr_algo, &old_sr_algo)) { + if (buf) + snprintf( + buf, buf_size, + "SR input label algo-%u (old: %s, new: %s)", + i, old_sr_algo.present ? "yes" : "no", + new_sr_algo.present ? "yes" : "no"); + return 0; + } } if (new->nexthops->count != old->nexthops->count) { @@ -405,7 +504,9 @@ isis_route_create(struct prefix *prefix, struct prefix_ipv6 *src_p, zlog_debug( "ISIS-Rte (%s): route changed: %pFX, change: %s", area->area_tag, prefix, change_buf); - rinfo_new->sr_previous = rinfo_old->sr; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + rinfo_new->sr_algo_previous[i] = + rinfo_old->sr_algo[i]; isis_route_info_delete(rinfo_old); route_info = rinfo_new; UNSET_FLAG(route_info->flag, @@ -461,11 +562,42 @@ static void isis_route_remove_previous_sid(struct isis_area *area, * Explicitly uninstall previous Prefix-SID label if it has * changed or was removed. */ - if (route_info->sr_previous.present && - (!route_info->sr.present || - route_info->sr_previous.label != route_info->sr.label)) - isis_zebra_prefix_sid_uninstall(area, prefix, route_info, - &route_info->sr_previous); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (route_info->sr_algo_previous[i].present && + (!route_info->sr_algo[i].present || + route_info->sr_algo_previous[i].label != + route_info->sr_algo[i].label)) + isis_zebra_prefix_sid_uninstall( + area, prefix, route_info, + &route_info->sr_algo_previous[i]); + } +} + +static void set_merge_route_info_sr_algo(struct isis_route_info *mrinfo, + struct isis_route_info *rinfo) +{ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (rinfo->sr_algo[i].present) { + assert(i == rinfo->sr_algo[i].algorithm); + assert(rinfo->nexthops); + assert(rinfo->backup ? rinfo->backup->nexthops != NULL + : true); + + if (mrinfo->sr_algo[i].nexthops != NULL && + mrinfo->sr_algo[i].nexthops != mrinfo->nexthops) { + mrinfo->sr_algo[i].nexthops->del = + (void (*)(void *))isis_nexthop_delete; + list_delete(&mrinfo->sr_algo[i].nexthops); + } + + mrinfo->sr_algo[i] = rinfo->sr_algo[i]; + mrinfo->sr_algo[i].nexthops = isis_nexthop_list_dup( + rinfo->sr_algo[i].nexthops); + } + } + + UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + UNSET_FLAG(mrinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } static void isis_route_update(struct isis_area *area, struct prefix *prefix, @@ -484,19 +616,35 @@ static void isis_route_update(struct isis_area *area, struct prefix *prefix, /* Install route. */ isis_zebra_route_add_route(area->isis, prefix, src_p, route_info); - /* Install/reinstall Prefix-SID label. */ - if (route_info->sr.present) - isis_zebra_prefix_sid_install(area, prefix, route_info, - &route_info->sr); + + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_sr_psid_info sr_algo; + + sr_algo = route_info->sr_algo[i]; + + /* + * Install/reinstall Prefix-SID label. + */ + if (sr_algo.present) + isis_zebra_prefix_sid_install(area, prefix, + &sr_algo); + + hook_call(isis_route_update_hook, area, prefix, + 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 { /* Uninstall Prefix-SID label. */ - if (route_info->sr.present) - isis_zebra_prefix_sid_uninstall( - area, prefix, route_info, &route_info->sr); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (route_info->sr_algo[i].present) + isis_zebra_prefix_sid_uninstall( + area, prefix, route_info, + &route_info->sr_algo[i]); + /* Uninstall route. */ isis_zebra_route_del_route(area->isis, prefix, src_p, route_info); @@ -516,6 +664,7 @@ static void _isis_route_verify_table(struct isis_area *area, #ifdef EXTREME_DEBUG char buff[SRCDEST2STR_BUFFER]; #endif /* EXTREME_DEBUG */ + uint8_t algorithm = isis_route_table_algorithm(table); for (rnode = route_top(table); rnode; rnode = srcdest_route_next(rnode)) { @@ -538,10 +687,14 @@ static void _isis_route_verify_table(struct isis_area *area, src_p); if (rnode_bck) { rinfo->backup = rnode_bck->info; + rinfo->sr_algo[algorithm].nexthops_backup = + rinfo->backup->nexthops; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else if (rinfo->backup) { rinfo->backup = NULL; + rinfo->sr_algo[algorithm].nexthops_backup = + NULL; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } @@ -573,7 +726,7 @@ static void _isis_route_verify_table(struct isis_area *area, if (CHECK_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) continue; - /* Area is either L1 or L2 => we use level route tables + /* In case the verify is not for a merge, we use a single table * directly for * validating => no problems with deleting routes. */ if (!tables) { @@ -581,13 +734,12 @@ static void _isis_route_verify_table(struct isis_area *area, continue; } - /* If area is L1L2, we work with merge table and - * therefore must - * delete node from level tables as well before deleting + /* If we work on a merged table, + * therefore we must + * delete node from each table as well before deleting * route info. */ - for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { - drnode = srcdest_rnode_lookup(tables[level - 1], - dst_p, src_p); + for (int i = 0; tables[i]; i++) { + drnode = srcdest_rnode_lookup(tables[i], dst_p, src_p); if (!drnode) continue; @@ -604,10 +756,36 @@ static void _isis_route_verify_table(struct isis_area *area, } } +static void _isis_route_verify_merge(struct isis_area *area, + struct route_table **tables, + struct route_table **tables_backup, + int tree); + void isis_route_verify_table(struct isis_area *area, struct route_table *table, - struct route_table *table_backup) + struct route_table *table_backup, int tree) { - _isis_route_verify_table(area, table, table_backup, NULL); + struct route_table *tables[SR_ALGORITHM_COUNT] = {table}; + struct route_table *tables_backup[SR_ALGORITHM_COUNT] = {table_backup}; +#ifndef FABRICD + int tables_next = 1; + int level = area->is_type == IS_LEVEL_1 ? ISIS_LEVEL1 : ISIS_LEVEL2; + struct listnode *node; + struct flex_algo *fa; + struct isis_flex_algo_data *data; + + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, fa)) { + data = fa->data; + tables[tables_next] = + data->spftree[tree][level - 1]->route_table; + tables_backup[tables_next] = + data->spftree[tree][level - 1]->route_table_backup; + _isis_route_verify_table(area, tables[tables_next], + tables_backup[tables_next], NULL); + tables_next++; + } +#endif /* ifndef FABRICD */ + + _isis_route_verify_merge(area, tables, tables_backup, tree); } /* Function to validate route tables for L1L2 areas. In this case we can't use @@ -624,18 +802,27 @@ void isis_route_verify_merge(struct isis_area *area, struct route_table *level1_table, struct route_table *level1_table_backup, struct route_table *level2_table, - struct route_table *level2_table_backup) + struct route_table *level2_table_backup, int tree) { - struct route_table *tables[] = {level1_table, level2_table}; + struct route_table *tables[] = {level1_table, level2_table, NULL}; struct route_table *tables_backup[] = {level1_table_backup, - level2_table_backup}; + level2_table_backup, NULL}; + _isis_route_verify_merge(area, tables, tables_backup, tree); +} + +static void _isis_route_verify_merge(struct isis_area *area, + struct route_table **tables, + struct route_table **tables_backup, + int tree) +{ struct route_table *merge; struct route_node *rnode, *mrnode; merge = srcdest_table_init(); - for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { - for (rnode = route_top(tables[level - 1]); rnode; + for (int i = 0; tables[i]; i++) { + uint8_t algorithm = isis_route_table_algorithm(tables[i]); + for (rnode = route_top(tables[i]); rnode; rnode = srcdest_route_next(rnode)) { struct isis_route_info *rinfo = rnode->info; struct route_node *rnode_bck; @@ -651,14 +838,18 @@ void isis_route_verify_merge(struct isis_area *area, (const struct prefix **)&src_p); /* Link primary route to backup route. */ - rnode_bck = srcdest_rnode_lookup( - tables_backup[level - 1], prefix, src_p); + rnode_bck = srcdest_rnode_lookup(tables_backup[i], + prefix, src_p); if (rnode_bck) { rinfo->backup = rnode_bck->info; + rinfo->sr_algo[algorithm].nexthops_backup = + rinfo->backup->nexthops; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else if (rinfo->backup) { rinfo->backup = NULL; + rinfo->sr_algo[algorithm].nexthops_backup = + NULL; UNSET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } @@ -667,6 +858,8 @@ void isis_route_verify_merge(struct isis_area *area, struct isis_route_info *mrinfo = mrnode->info; if (mrinfo) { route_unlock_node(mrnode); + set_merge_route_info_sr_algo(mrinfo, rinfo); + if (CHECK_FLAG(mrinfo->flag, ISIS_ROUTE_FLAG_ACTIVE)) { /* Clear the ZEBRA_SYNCED flag on the @@ -696,8 +889,9 @@ void isis_route_verify_merge(struct isis_area *area, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { continue; } + } else { + mrnode->info = rnode->info; } - mrnode->info = rnode->info; } } @@ -710,6 +904,7 @@ void isis_route_invalidate_table(struct isis_area *area, { struct route_node *rode; struct isis_route_info *rinfo; + uint8_t algorithm = isis_route_table_algorithm(table); for (rode = route_top(table); rode; rode = srcdest_route_next(rode)) { if (rode->info == NULL) continue; @@ -717,6 +912,7 @@ void isis_route_invalidate_table(struct isis_area *area, if (rinfo->backup) { rinfo->backup = NULL; + rinfo->sr_algo[algorithm].nexthops_backup = NULL; /* * For now, always force routes that have backup * nexthops to be reinstalled. diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 40e7462898d4..4d49a5ae9cf5 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -30,12 +30,16 @@ struct isis_route_info { uint8_t flag; uint32_t cost; uint32_t depth; - struct isis_sr_psid_info sr; - struct isis_sr_psid_info sr_previous; + struct isis_sr_psid_info sr_algo[SR_ALGORITHM_COUNT]; + struct isis_sr_psid_info sr_algo_previous[SR_ALGORITHM_COUNT]; struct list *nexthops; struct isis_route_info *backup; }; +struct isis_route_table_info { + uint8_t algorithm; +}; + DECLARE_HOOK(isis_route_update_hook, (struct isis_area * area, struct prefix *prefix, struct isis_route_info *route_info), @@ -56,14 +60,14 @@ void isis_route_delete(struct isis_area *area, struct route_node *rode, /* Walk the given table and install new routes to zebra and remove old ones. * route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */ void isis_route_verify_table(struct isis_area *area, struct route_table *table, - struct route_table *table_backup); + struct route_table *table_backup, int tree); /* Same as isis_route_verify_table, but merge L1 and L2 routes before */ void isis_route_verify_merge(struct isis_area *area, struct route_table *level1_table, struct route_table *level1_table_backup, struct route_table *level2_table, - struct route_table *level2_table_backup); + struct route_table *level2_table_backup, int tree); /* Unset ISIS_ROUTE_FLAG_ACTIVE on all routes. Used before running spf. */ void isis_route_invalidate_table(struct isis_area *area, @@ -73,9 +77,14 @@ void isis_route_invalidate_table(struct isis_area *area, void isis_route_node_cleanup(struct route_table *table, struct route_node *node); + void isis_route_switchover_nexthop(struct isis_area *area, struct route_table *table, int family, union g_addr *nexthop_addr, ifindex_t ifindex); +struct isis_route_table_info *isis_route_table_info_alloc(uint8_t algorithm); +void isis_route_table_info_free(void *info); +uint8_t isis_route_table_algorithm(const struct route_table *table); + #endif /* _ZEBRA_ISIS_ROUTE_H */ diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index 632b4ff95ec7..9be133c7880d 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -18,7 +18,7 @@ #include "plist.h" #include "routemap.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "isis_constants.h" diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c index ae570a0864d1..f9e3780e29e3 100644 --- a/isisd/isis_snmp.c +++ b/isisd/isis_snmp.c @@ -259,11 +259,6 @@ /* Declare static local variables for convenience. */ SNMP_LOCAL_VARIABLES -/* If ARRAY_SIZE is not available use a primitive substitution */ -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#endif - /* * Define time function, it serves two purposes * 1. Uses unint32_t for unix time and encapsulates @@ -425,7 +420,7 @@ static struct isis_func_to_prefix isis_func_to_prefix_arr[] = { {isis_snmp_find_isadj_ipaddr, {ISIS_ISADJIPADDR_ENTRY}, 4}, {isis_snmp_find_isadj_prot_supp, {ISIS_ISADJPROTSUPP_ENTRY}, 4}, }; -static size_t isis_func_to_prefix_count = ARRAY_SIZE(isis_func_to_prefix_arr); +static size_t isis_func_to_prefix_count = array_size(isis_func_to_prefix_arr); static struct variable isis_var_arr[] = { {ISIS_SYS_VERSION, INTEGER, RONLY, isis_snmp_find_sys_object}, @@ -554,7 +549,7 @@ static struct variable isis_var_arr[] = { isis_snmp_find_isadj_prot_supp}, }; -static const size_t isis_var_count = ARRAY_SIZE(isis_var_arr); +static const size_t isis_var_count = array_size(isis_var_arr); /* Minimal set of hard-coded data */ #define ISIS_VERSION (1) @@ -838,12 +833,12 @@ static int isis_snmp_conv_next(uint8_t *buf, size_t max_len, size_t *out_len, */ static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len, struct isis_area **ret_area, - struct area_addr **ret_addr) + struct iso_address **ret_addr) { uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX]; size_t addr_len; struct isis_area *area = NULL; - struct area_addr *addr = NULL; + struct iso_address *addr = NULL; struct listnode *addr_node; struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); @@ -885,15 +880,15 @@ static int isis_snmp_area_addr_lookup_exact(oid *oid_idx, size_t oid_idx_len, static int isis_snmp_area_addr_lookup_next(oid *oid_idx, size_t oid_idx_len, struct isis_area **ret_area, - struct area_addr **ret_addr) + struct iso_address **ret_addr) { uint8_t cmp_buf[ISIS_SNMP_OSI_ADDR_LEN_MAX]; size_t addr_len; int try_exact = 0; struct isis_area *found_area = NULL; struct isis_area *area = NULL; - struct area_addr *found_addr = NULL; - struct area_addr *addr = NULL; + struct iso_address *found_addr = NULL; + struct iso_address *addr = NULL; struct listnode *addr_node; struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT); @@ -1506,7 +1501,7 @@ static uint8_t *isis_snmp_find_man_area(struct variable *v, oid *name, WriteMethod **write_method) { int res; - struct area_addr *area_addr = NULL; + struct iso_address *area_addr = NULL; oid *oid_idx; size_t oid_idx_len; size_t off = 0; @@ -2161,7 +2156,10 @@ static uint8_t *isis_snmp_find_circ(struct variable *v, oid *name, /* * return false if lan hellos must be padded */ - if (circuit->pad_hellos) + if (circuit->pad_hellos == ISIS_HELLO_PADDING_ALWAYS || + (circuit->pad_hellos == + ISIS_HELLO_PADDING_DURING_ADJACENCY_FORMATION && + circuit->upadjcount[0] + circuit->upadjcount[1] == 0)) return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_FALSE); return SNMP_INTEGER(ISIS_SNMP_TRUTH_VALUE_TRUE); @@ -2487,6 +2485,11 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, uint32_t delta_ticks; time_t now_time; + /* Ring buffer to print SNPA */ +#define FORMAT_BUF_COUNT 4 + static char snpa[FORMAT_BUF_COUNT][ISO_SYSID_STRLEN]; + static size_t cur_buf = 0; + *write_method = NULL; if (*length <= v->namelen) { @@ -2533,9 +2536,10 @@ static uint8_t *isis_snmp_find_isadj(struct variable *v, oid *name, return SNMP_INTEGER(adj->threeway_state); case ISIS_ISADJ_NEIGHSNPAADDRESS: { - const char *snpa = (char *)snpa_print(adj->snpa); - *var_len = strlen(snpa); - return (uint8_t *)snpa; + cur_buf = (cur_buf + 1) % FORMAT_BUF_COUNT; + snprintfrr(snpa[cur_buf], ISO_SYSID_STRLEN, "%pSY", adj->snpa); + *var_len = strlen(snpa[cur_buf]); + return (uint8_t *)snpa[cur_buf]; } case ISIS_ISADJ_NEIGHSYSTYPE: @@ -2794,7 +2798,7 @@ static uint8_t *isis_snmp_find_isadj_prot_supp(struct variable *v, oid *name, /* Register ISIS-MIB. */ -static int isis_snmp_init(struct thread_master *tm) +static int isis_snmp_init(struct event_loop *tm) { struct isis_func_to_prefix *h2f = isis_func_to_prefix_arr; struct variable *v; @@ -2856,7 +2860,7 @@ static int isis_snmp_db_overload_update(const struct isis_area *area) /* Put in trap value */ snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, - ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + array_size(isis_snmp_trap_var), ASN_OBJECT_ID, (uint8_t *)&isis_snmp_trap_val_db_overload, sizeof(isis_snmp_trap_val_db_overload)); @@ -2865,11 +2869,11 @@ static int isis_snmp_db_overload_update(const struct isis_area *area) snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, - ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER, (uint8_t *)&val, sizeof(val)); /* Patch sys_level_state with proper index */ - off = ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state) - 1; + off = array_size(isis_snmp_trap_data_var_sys_level_state) - 1; isis_snmp_trap_data_var_sys_level_state[off] = val; /* Prepare data */ @@ -2880,7 +2884,7 @@ static int isis_snmp_db_overload_update(const struct isis_area *area) snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_sys_level_state, - ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_state), INTEGER, + array_size(isis_snmp_trap_data_var_sys_level_state), INTEGER, (uint8_t *)&val, sizeof(val)); send_v2trap(notification_vars); @@ -2902,7 +2906,7 @@ static int isis_snmp_lsp_exceed_max_update(const struct isis_area *area, /* Put in trap value */ snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, - ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + array_size(isis_snmp_trap_var), ASN_OBJECT_ID, (uint8_t *)&isis_snmp_trap_val_lsp_exceed_max, sizeof(isis_snmp_trap_val_lsp_exceed_max)); @@ -2911,12 +2915,12 @@ static int isis_snmp_lsp_exceed_max_update(const struct isis_area *area, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, - ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER, (uint8_t *)&val, sizeof(val)); snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_pdu_lsp_id, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, ISIS_SYS_ID_LEN + 2); send_v2trap(notification_vars); @@ -2959,18 +2963,18 @@ static void isis_snmp_update_worker_a(const struct isis_circuit *circuit, /* Put in trap value */ memcpy(var_name, isis_snmp_notifications, sizeof(isis_snmp_notifications)); - var_count = ARRAY_SIZE(isis_snmp_notifications); + var_count = array_size(isis_snmp_notifications); var_name[var_count++] = trap_id; /* Put in trap value */ snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, - ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + array_size(isis_snmp_trap_var), ASN_OBJECT_ID, (uint8_t *)var_name, var_count * sizeof(oid)); val = circuit->is_type; snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, - ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER, (uint8_t *)&val, sizeof(val)); if (oid_a_len != 0) { @@ -2989,7 +2993,7 @@ static void isis_snmp_update_worker_a(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, - ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, (uint8_t *)&val, sizeof(val)); @@ -3039,18 +3043,18 @@ static void isis_snmp_update_worker_b(const struct isis_circuit *circuit, /* Put in trap value */ memcpy(var_name, isis_snmp_notifications, sizeof(isis_snmp_notifications)); - var_count = ARRAY_SIZE(isis_snmp_notifications); + var_count = array_size(isis_snmp_notifications); var_name[var_count++] = trap_id; /* Put in trap value */ snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, - ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + array_size(isis_snmp_trap_var), ASN_OBJECT_ID, (uint8_t *)var_name, var_count * sizeof(oid)); val = circuit->is_type; snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, - ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER, (uint8_t *)&val, sizeof(val)); if (circuit->interface == NULL) @@ -3060,7 +3064,7 @@ static void isis_snmp_update_worker_b(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, - ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, (uint8_t *)&val, sizeof(val)); @@ -3105,9 +3109,9 @@ static int isis_snmp_id_len_mismatch_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_ID_LEN_MISMATCH, isis_snmp_trap_data_var_pdu_field_len, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32, + array_size(isis_snmp_trap_data_var_pdu_field_len), UNSIGNED32, &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); return 0; } @@ -3130,10 +3134,10 @@ isis_snmp_max_area_addr_mismatch_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_MAX_AREA_ADDR_MISMATCH, isis_snmp_trap_data_var_pdu_max_area_addr, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_max_area_addr), + array_size(isis_snmp_trap_data_var_pdu_max_area_addr), UNSIGNED32, &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); return 0; } @@ -3147,7 +3151,7 @@ static int isis_snmp_own_lsp_purge_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_OWN_LSP_PURGE, NULL, 0, STRING, NULL, 0, isis_snmp_trap_data_var_pdu_lsp_id, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, ISIS_SYS_ID_LEN + 2); return 0; } @@ -3161,7 +3165,7 @@ static int isis_snmp_seqno_skipped_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_SEQNO_SKIPPED, NULL, 0, STRING, NULL, 0, isis_snmp_trap_data_var_pdu_lsp_id, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, ISIS_SYS_ID_LEN + 2); return 0; } @@ -3180,7 +3184,7 @@ isis_snmp_authentication_type_failure_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_AUTHEN_TYPE_FAILURE, NULL, 0, STRING, NULL, 0, isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); return 0; } @@ -3198,7 +3202,7 @@ isis_snmp_authentication_failure_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_AUTHEN_FAILURE, NULL, 0, STRING, NULL, 0, isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); return 0; } @@ -3220,9 +3224,9 @@ static int isis_snmp_version_skew_update(const struct isis_circuit *circuit, isis_snmp_update_worker_b( circuit, ISIS_TRAP_VERSION_SKEW, isis_snmp_trap_data_var_pdu_proto_ver, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32, + array_size(isis_snmp_trap_data_var_pdu_proto_ver), UNSIGNED32, &val, sizeof(val), isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); return 0; } @@ -3245,7 +3249,7 @@ static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit, /* Put in trap value */ snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, - ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + array_size(isis_snmp_trap_var), ASN_OBJECT_ID, (uint8_t *)&isis_snmp_trap_val_area_mismatch, sizeof(isis_snmp_trap_val_area_mismatch)); @@ -3257,7 +3261,7 @@ static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, - ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, (uint8_t *)&val, sizeof(val)); @@ -3266,7 +3270,7 @@ static int isis_snmp_area_mismatch_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); send_v2trap(notification_vars); @@ -3289,7 +3293,7 @@ static int isis_snmp_reject_adjacency_update(const struct isis_circuit *circuit, isis_snmp_update_worker_a( circuit, ISIS_TRAP_REJ_ADJACENCY, NULL, 0, STRING, NULL, 0, isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); return 0; } @@ -3304,9 +3308,9 @@ static int isis_snmp_lsp_too_large_update(const struct isis_circuit *circuit, isis_snmp_update_worker_b( circuit, ISIS_TRAP_LSP_TOO_LARGE, isis_snmp_trap_data_var_pdu_lsp_size, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32, + array_size(isis_snmp_trap_data_var_pdu_lsp_size), UNSIGNED32, &pdu_size, sizeof(pdu_size), isis_snmp_trap_data_var_pdu_lsp_id, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, ISIS_SYS_ID_LEN + 2); return 0; } @@ -3331,9 +3335,9 @@ static int isis_snmp_adj_state_change_update(const struct isis_adjacency *adj) isis_snmp_update_worker_b( adj->circuit, ISIS_TRAP_ADJ_STATE_CHANGE, isis_snmp_trap_data_var_pdu_lsp_id, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, ISIS_SYS_ID_LEN + 2, isis_snmp_trap_data_var_adj_state, - ARRAY_SIZE(isis_snmp_trap_data_var_adj_state), INTEGER, &val, + array_size(isis_snmp_trap_data_var_adj_state), INTEGER, &val, sizeof(val)); return 0; } @@ -3356,7 +3360,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, /* Put in trap value */ snmp_varlist_add_variable(¬ification_vars, isis_snmp_trap_var, - ARRAY_SIZE(isis_snmp_trap_var), ASN_OBJECT_ID, + array_size(isis_snmp_trap_var), ASN_OBJECT_ID, (uint8_t *)&isis_snmp_trap_val_lsp_error, sizeof(isis_snmp_trap_val_lsp_error)); @@ -3365,13 +3369,13 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_sys_level_index, - ARRAY_SIZE(isis_snmp_trap_data_var_sys_level_index), INTEGER, + array_size(isis_snmp_trap_data_var_sys_level_index), INTEGER, (uint8_t *)&val, sizeof(val)); snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_pdu_lsp_id, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, + array_size(isis_snmp_trap_data_var_pdu_lsp_id), STRING, lsp_id, ISIS_SYS_ID_LEN + 2); /* Prepare data */ @@ -3382,7 +3386,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_circ_if_index, - ARRAY_SIZE(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, + array_size(isis_snmp_trap_data_var_circ_if_index), UNSIGNED32, (uint8_t *)&val, sizeof(val)); /* Prepare data */ @@ -3391,7 +3395,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_pdu_fragment, - ARRAY_SIZE(isis_snmp_trap_data_var_pdu_fragment), STRING, + array_size(isis_snmp_trap_data_var_pdu_fragment), STRING, raw_pdu, raw_pdu_len); /* Prepare data */ @@ -3399,7 +3403,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_error_offset, - ARRAY_SIZE(isis_snmp_trap_data_var_error_offset), UNSIGNED32, + array_size(isis_snmp_trap_data_var_error_offset), UNSIGNED32, (uint8_t *)&val, sizeof(val)); /* Prepare data */ @@ -3407,7 +3411,7 @@ static int isis_snmp_lsp_error_update(const struct isis_circuit *circuit, snmp_varlist_add_variable( ¬ification_vars, isis_snmp_trap_data_var_error_tlv_type, - ARRAY_SIZE(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32, + array_size(isis_snmp_trap_data_var_error_tlv_type), UNSIGNED32, (uint8_t *)&val, sizeof(val)); send_v2trap(notification_vars); diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index e1aec683d83c..e114467e07cc 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -11,7 +11,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "log.h" @@ -26,6 +26,7 @@ #include "spf_backoff.h" #include "srcdest_table.h" #include "vrf.h" +#include "lib/json.h" #include "isis_errors.h" #include "isis_constants.h" @@ -43,6 +44,7 @@ #include "isis_csm.h" #include "isis_mt.h" #include "isis_tlvs.h" +#include "isis_flex_algo.h" #include "isis_zebra.h" #include "fabricd.h" #include "isis_spf_private.h" @@ -241,11 +243,7 @@ void isis_vertex_del(struct isis_vertex *vertex) { list_delete(&vertex->Adj_N); list_delete(&vertex->parents); - if (vertex->firsthops) { - hash_clean(vertex->firsthops, NULL); - hash_free(vertex->firsthops); - vertex->firsthops = NULL; - } + hash_clean_and_free(&vertex->firsthops, NULL); memset(vertex, 0, sizeof(struct isis_vertex)); XFREE(MTYPE_ISIS_VERTEX, vertex); @@ -326,28 +324,41 @@ static void isis_spf_adj_free(void *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) +static void _isis_spftree_init(struct isis_spftree *tree) { - struct isis_spftree *tree; - - tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree)); - isis_vertex_queue_init(&tree->tents, "IS-IS SPF tents", true); isis_vertex_queue_init(&tree->paths, "IS-IS SPF paths", false); tree->route_table = srcdest_table_init(); tree->route_table->cleanup = isis_route_node_cleanup; + tree->route_table->info = isis_route_table_info_alloc(tree->algorithm); tree->route_table_backup = srcdest_table_init(); + tree->route_table_backup->info = + isis_route_table_info_alloc(tree->algorithm); tree->route_table_backup->cleanup = isis_route_node_cleanup; - tree->area = area; - tree->lspdb = lspdb; tree->prefix_sids = hash_create(prefix_sid_key_make, prefix_sid_cmp, "SR Prefix-SID Entries"); tree->sadj_list = list_new(); tree->sadj_list->del = isis_spf_adj_free; + isis_rlfa_list_init(tree); + tree->lfa.remote.pc_spftrees = list_new(); + tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del; + if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) { + isis_spf_node_list_init(&tree->lfa.p_space); + isis_spf_node_list_init(&tree->lfa.q_space); + } +} + +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, uint8_t algorithm) +{ + struct isis_spftree *tree; + + tree = XCALLOC(MTYPE_ISIS_SPFTREE, sizeof(struct isis_spftree)); + + tree->area = area; + tree->lspdb = lspdb; tree->last_run_timestamp = 0; tree->last_run_monotime = 0; tree->last_run_duration = 0; @@ -358,21 +369,16 @@ struct isis_spftree *isis_spftree_new(struct isis_area *area, tree->tree_id = tree_id; tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6; tree->flags = flags; - isis_rlfa_list_init(tree); - tree->lfa.remote.pc_spftrees = list_new(); - tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del; - if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) { - isis_spf_node_list_init(&tree->lfa.p_space); - isis_spf_node_list_init(&tree->lfa.q_space); - } + tree->algorithm = algorithm; + + _isis_spftree_init(tree); return tree; } -void isis_spftree_del(struct isis_spftree *spftree) +static void _isis_spftree_del(struct isis_spftree *spftree) { - hash_clean(spftree->prefix_sids, NULL); - hash_free(spftree->prefix_sids); + hash_clean_and_free(&spftree->prefix_sids, NULL); isis_zebra_rlfa_unregister_all(spftree); isis_rlfa_list_clear(spftree); list_delete(&spftree->lfa.remote.pc_spftrees); @@ -385,14 +391,30 @@ 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); + isis_route_table_info_free(spftree->route_table->info); + isis_route_table_info_free(spftree->route_table_backup->info); route_table_finish(spftree->route_table); route_table_finish(spftree->route_table_backup); +} + +void isis_spftree_del(struct isis_spftree *spftree) +{ + _isis_spftree_del(spftree); + spftree->route_table = NULL; XFREE(MTYPE_ISIS_SPFTREE, spftree); return; } +#ifndef FABRICD +static void isis_spftree_clear(struct isis_spftree *spftree) +{ + _isis_spftree_del(spftree); + _isis_spftree_init(spftree); +} +#endif /* ifndef FABRICD */ + static void isis_spftree_adj_del(struct isis_spftree *spftree, struct isis_adjacency *adj) { @@ -415,10 +437,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->lspdb[level - 1], - area->isis->sysid, level, tree, - SPF_TYPE_FORWARD, 0); + area->spftree[tree][level - 1] = isis_spftree_new( + area, &area->lspdb[level - 1], + area->isis->sysid, level, tree, + SPF_TYPE_FORWARD, 0, SR_ALGORITHM_SPF); } } } @@ -500,8 +522,8 @@ static struct isis_vertex *isis_spf_add_root(struct isis_spftree *spftree) #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: added this IS %s %s depth %d dist %d to PATHS", - vtype2string(vertex->type), + "ISIS-SPF: A:%hhu added this IS %s %s depth %d dist %d to PATHS", + spftree->algorithm, vtype2string(vertex->type), vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ @@ -590,9 +612,20 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id, vertex->N.ip.sr.sid = *psid; vertex->N.ip.sr.label = sr_prefix_in_label(area, psid, local); + vertex->N.ip.sr.algorithm = psid->algorithm; + if (vertex->N.ip.sr.label != MPLS_INVALID_LABEL) vertex->N.ip.sr.present = true; +#ifndef FABRICD + if (flex_algo_id_valid(spftree->algorithm) && + !isis_flex_algo_elected_supported( + spftree->algorithm, spftree->area)) { + vertex->N.ip.sr.present = false; + vertex->N.ip.sr.label = MPLS_INVALID_LABEL; + } +#endif /* ifndef FABRICD */ + (void)hash_get(spftree->prefix_sids, vertex, hash_alloc_intern); } @@ -620,8 +653,8 @@ isis_spf_add2tent(struct isis_spftree *spftree, enum vertextype vtype, void *id, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: add to TENT %s %s %s depth %d dist %d adjcount %d", - print_sys_hostname(vertex->N.id), + "ISIS-SPF: A:%hhu add to TENT %s %s %s depth %d dist %d adjcount %d", + spftree->algorithm, print_sys_hostname(vertex->N.id), vtype2string(vertex->type), vid2string(vertex, buff, sizeof(buff)), vertex->depth, vertex->d_N, listcount(vertex->Adj_N)); @@ -714,7 +747,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: process_N %s %s %s dist %d already found from PATH", + "ISIS-SPF: A:%hhu process_N %s %s %s dist %d already found from PATH", + spftree->algorithm, print_sys_hostname(vertex->N.id), vtype2string(vtype), vid2string(vertex, buff, sizeof(buff)), dist); @@ -730,7 +764,8 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: process_N %s %s %s dist %d parent %s adjcount %d", + "ISIS-SPF: A:%hhu process_N %s %s %s dist %d parent %s adjcount %d", + spftree->algorithm, print_sys_hostname(vertex->N.id), vtype2string(vtype), vid2string(vertex, buff, sizeof(buff)), dist, @@ -776,8 +811,9 @@ static void process_N(struct isis_spftree *spftree, enum vertextype vtype, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) zlog_debug( - "ISIS-SPF: process_N add2tent %s %s dist %d parent %s", - print_sys_hostname(id), vtype2string(vtype), dist, + "ISIS-SPF: A:%hhu process_N add2tent %s %s dist %d parent %s", + spftree->algorithm, print_sys_hostname(id), + vtype2string(vtype), dist, (parent ? print_sys_hostname(parent->N.id) : "null")); #endif /* EXTREME_DEBUG */ @@ -837,7 +873,8 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) - zlog_debug("ISIS-SPF: process_lsp %s", + zlog_debug("ISIS-SPF: A:%hhu process_lsp %s", + spftree->algorithm, print_sys_hostname(lsp->hdr.lsp_id)); #endif /* EXTREME_DEBUG */ @@ -894,6 +931,16 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN)) continue; +#ifndef FABRICD + + if (flex_algo_id_valid(spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + isis_flex_algo_constraint_drop(spftree, + lsp, er))) + continue; +#endif /* ifndef FABRICD */ + dist = cost + (CHECK_FLAG(spftree->flags, F_SPFTREE_HOPCOUNT_METRIC) @@ -970,9 +1017,21 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; - if (psid->algorithm != SR_ALGORITHM_SPF) + if (psid->algorithm != + spftree->algorithm) continue; +#ifndef FABRICD + if (flex_algo_id_valid( + spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + !isis_flex_algo_elected_supported( + spftree->algorithm, + spftree->area))) + continue; +#endif /* ifndef FABRICD */ + has_valid_psid = true; process_N(spftree, VTYPE_IPREACH_TE, &ip_info, dist, depth + 1, @@ -1038,9 +1097,21 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; - if (psid->algorithm != SR_ALGORITHM_SPF) + if (psid->algorithm != + spftree->algorithm) continue; +#ifndef FABRICD + if (flex_algo_id_valid( + spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + !isis_flex_algo_elected_supported( + spftree->algorithm, + spftree->area))) + continue; +#endif /* ifndef FABRICD */ + has_valid_psid = true; process_N(spftree, vtype, &ip_info, dist, depth + 1, psid, @@ -1072,8 +1143,8 @@ static int isis_spf_process_lsp(struct isis_spftree *spftree, && !isis_level2_adj_up(spftree->area)) { struct prefix_pair ip_info = { {0} }; if (IS_DEBUG_RTE_EVENTS) - zlog_debug("ISIS-Spf (%s): add default %s route", - rawlspid_print(lsp->hdr.lsp_id), + zlog_debug("ISIS-Spf (%pLS): add default %s route", + lsp->hdr.lsp_id, spftree->family == AF_INET ? "ipv4" : "ipv6"); @@ -1162,7 +1233,7 @@ static int isis_spf_preload_tent_ip_reach_cb(const struct prefix *prefix, struct isis_prefix_sid *psid = (struct isis_prefix_sid *)i; - if (psid->algorithm != SR_ALGORITHM_SPF) + if (psid->algorithm != spftree->algorithm) continue; has_valid_psid = true; @@ -1212,9 +1283,8 @@ static void isis_spf_preload_tent(struct isis_spftree *spftree, if (isis_lfa_excise_adj_check(spftree, adj_id)) { if (IS_DEBUG_LFA) - zlog_debug("ISIS-SPF: excising adjacency %s", - isis_format_id(sadj->id, - ISIS_SYS_ID_LEN + 1)); + zlog_debug("ISIS-SPF: excising adjacency %pPN", + sadj->id); continue; } @@ -1329,8 +1399,8 @@ static void spf_adj_list_parse_tlv(struct isis_spftree *spftree, LSP_FRAGMENT(lspid) = 0; lsp = lsp_search(spftree->lspdb, lspid); if (lsp == NULL || lsp->hdr.rem_lifetime == 0) { - zlog_warn("ISIS-SPF: No LSP found from root to L%d %s", - spftree->level, rawlspid_print(lspid)); + zlog_warn("ISIS-SPF: No LSP found from root to L%d %pLS", + spftree->level, lspid); return; } @@ -1426,6 +1496,19 @@ static void spf_adj_list_parse_lsp(struct isis_spftree *spftree, for (struct isis_extended_reach *reach = (struct isis_extended_reach *)head; reach; reach = reach->next) { +#ifndef FABRICD + /* + * cutting out adjacency by flex-algo link + * affinity attribute + */ + if (flex_algo_id_valid(spftree->algorithm) && + (!sr_algorithm_participated( + lsp, spftree->algorithm) || + isis_flex_algo_constraint_drop( + spftree, lsp, reach))) + continue; +#endif /* ifndef FABRICD */ + spf_adj_list_parse_tlv( spftree, adj_list, reach->id, pseudo_nodeid, pseudo_metric, @@ -1481,11 +1564,13 @@ static void add_to_paths(struct isis_spftree *spftree, #ifdef EXTREME_DEBUG if (IS_DEBUG_SPF_EVENTS) - zlog_debug("ISIS-SPF: added %s %s %s depth %d dist %d to PATHS", - print_sys_hostname(vertex->N.id), - vtype2string(vertex->type), - vid2string(vertex, buff, sizeof(buff)), - vertex->depth, vertex->d_N); + zlog_debug( + "ISIS-SPF: A:%hhu S:%p added %s %s %s depth %d dist %d to PATHS", + spftree->algorithm, spftree, + print_sys_hostname(vertex->N.id), + vtype2string(vertex->type), + vid2string(vertex, buff, sizeof(buff)), vertex->depth, + vertex->d_N); #endif /* EXTREME_DEBUG */ } @@ -1631,10 +1716,23 @@ static void spf_path_process(struct isis_spftree *spftree, break; } - isis_route_create( - &vertex->N.ip.p.dest, &vertex->N.ip.p.src, - vertex->d_N, vertex->depth, &vertex->N.ip.sr, - vertex->Adj_N, allow_ecmp, area, route_table); +#ifdef EXTREME_DEBUG + struct isis_route_info *ri = +#endif /* EXTREME_DEBUG */ + isis_route_create(&vertex->N.ip.p.dest, + &vertex->N.ip.p.src, + vertex->d_N, vertex->depth, + &vertex->N.ip.sr, + vertex->Adj_N, allow_ecmp, + area, route_table); + +#ifdef EXTREME_DEBUG + zlog_debug( + "ISIS-SPF: A:%hhu create route pfx %pFX dist %d, sr.algo %d, table %p, rv %p", + spftree->algorithm, &vertex->N.ip.p.dest, + vertex->d_N, vertex->N.ip.sr.algorithm, + route_table, ri); +#endif /* EXTREME_DEBUG */ } else if (IS_DEBUG_SPF_EVENTS) zlog_debug( "ISIS-SPF: no adjacencies, do not install route for %s depth %d dist %d", @@ -1654,12 +1752,10 @@ static void isis_spf_loop(struct isis_spftree *spftree, vertex = isis_vertex_queue_pop(&spftree->tents); #ifdef EXTREME_DEBUG - if (IS_DEBUG_SPF_EVENTS) - zlog_debug( - "ISIS-SPF: get TENT node %s %s depth %d dist %d to PATHS", - print_sys_hostname(vertex->N.id), - vtype2string(vertex->type), vertex->depth, - vertex->d_N); + zlog_debug( + "ISIS-SPF: A:%hhu get TENT node %s %s depth %d dist %d to PATHS", + spftree->algorithm, print_sys_hostname(vertex->N.id), + vtype2string(vertex->type), vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ add_to_paths(spftree, vertex); @@ -1668,9 +1764,8 @@ static void isis_spf_loop(struct isis_spftree *spftree, lsp = lsp_for_vertex(spftree, vertex); if (!lsp) { - zlog_warn("ISIS-SPF: No LSP found for %s", - isis_format_id(vertex->N.id, - sizeof(vertex->N.id))); + zlog_warn("ISIS-SPF: No LSP found for %pPN", + vertex->N.id); continue; } @@ -1708,10 +1803,10 @@ struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, struct isis_spftree *spftree) { if (!spftree) - spftree = isis_spftree_new(area, &area->lspdb[IS_LEVEL_2 - 1], - sysid, ISIS_LEVEL2, SPFTREE_IPV4, - SPF_TYPE_FORWARD, - F_SPFTREE_HOPCOUNT_METRIC); + spftree = isis_spftree_new( + area, &area->lspdb[IS_LEVEL_2 - 1], sysid, ISIS_LEVEL2, + SPFTREE_IPV4, SPF_TYPE_FORWARD, + F_SPFTREE_HOPCOUNT_METRIC, SR_ALGORITHM_SPF); init_spt(spftree, ISIS_MT_IPV4_UNICAST); if (!memcmp(sysid, area->isis->sysid, ISIS_SYS_ID_LEN)) { @@ -1748,6 +1843,9 @@ void isis_run_spf(struct isis_spftree *spftree) struct timeval time_end; struct isis_mt_router_info *mt_router_info; uint16_t mtid = 0; +#ifndef FABRICD + bool flex_algo_enabled; +#endif /* ifndef FABRICD */ /* Get time that can't roll backwards. */ monotime(&time_start); @@ -1782,6 +1880,38 @@ void isis_run_spf(struct isis_spftree *spftree) exit(1); } +#ifndef FABRICD + /* If a node is configured to participate in a particular Flexible- + * Algorithm, but there is no valid Flex-Algorithm definition available + * for it, or the selected Flex-Algorithm definition includes + * calculation-type, metric-type, constraint, flag, or Sub-TLV that is + * not supported by the node, it MUST stop participating in such + * Flexible-Algorithm. + */ + if (flex_algo_id_valid(spftree->algorithm)) { + flex_algo_enabled = isis_flex_algo_elected_supported( + spftree->algorithm, spftree->area); + if (flex_algo_enabled != + flex_algo_get_state(spftree->area->flex_algos, + spftree->algorithm)) { + /* actual state is inconsistent with local LSP */ + lsp_regenerate_schedule(spftree->area, + spftree->area->is_type, 0); + goto out; + } + if (!flex_algo_enabled) { + if (!CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) { + isis_spftree_clear(spftree); + SET_FLAG(spftree->flags, F_SPFTREE_DISABLED); + lsp_regenerate_schedule(spftree->area, + spftree->area->is_type, + 0); + } + goto out; + } + } +#endif /* ifndef FABRICD */ + /* * C.2.5 Step 0 */ @@ -1802,6 +1932,18 @@ void isis_run_spf(struct isis_spftree *spftree) } isis_spf_loop(spftree, spftree->sysid); + + +#ifndef FABRICD + /* flex-algo */ + if (CHECK_FLAG(spftree->flags, F_SPFTREE_DISABLED)) { + UNSET_FLAG(spftree->flags, F_SPFTREE_DISABLED); + lsp_regenerate_schedule(spftree->area, spftree->area->is_type, + 0); + } + +out: +#endif /* ifndef FABRICD */ spftree->runcount++; spftree->last_run_timestamp = time(NULL); spftree->last_run_monotime = monotime(&time_end); @@ -1823,29 +1965,37 @@ static void isis_run_spf_with_protection(struct isis_area *area, isis_spf_run_lfa(area, spftree); } -void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees) +void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees, + int tree) { if (area->is_type == IS_LEVEL_1) { isis_route_verify_table(area, trees[0]->route_table, - trees[0]->route_table_backup); + trees[0]->route_table_backup, tree); } else if (area->is_type == IS_LEVEL_2) { isis_route_verify_table(area, trees[1]->route_table, - trees[1]->route_table_backup); + trees[1]->route_table_backup, tree); } else { isis_route_verify_merge(area, trees[0]->route_table, trees[0]->route_table_backup, trees[1]->route_table, - trees[1]->route_table_backup); + trees[1]->route_table_backup, tree); } } void isis_spf_invalidate_routes(struct isis_spftree *tree) { + struct isis_route_table_info *backup_info; + isis_route_invalidate_table(tree->area, tree->route_table); /* Delete backup routes. */ + + backup_info = tree->route_table_backup->info; route_table_finish(tree->route_table_backup); + isis_route_table_info_free(backup_info); tree->route_table_backup = srcdest_table_init(); + tree->route_table_backup->info = + isis_route_table_info_alloc(tree->algorithm); tree->route_table_backup->cleanup = isis_route_node_cleanup; } @@ -1858,12 +2008,18 @@ void isis_spf_switchover_routes(struct isis_area *area, family, nexthop_ip, ifindex); } -static void isis_run_spf_cb(struct thread *thread) +static void isis_run_spf_cb(struct event *thread) { - struct isis_spf_run *run = THREAD_ARG(thread); + struct isis_spf_run *run = EVENT_ARG(thread); struct isis_area *area = run->area; int level = run->level; int have_run = 0; + struct listnode *node; + struct isis_circuit *circuit; +#ifndef FABRICD + struct flex_algo *fa; + struct isis_flex_algo_data *data; +#endif /* ifndef FABRICD */ XFREE(MTYPE_ISIS_SPF_RUN, run); @@ -1884,11 +2040,27 @@ static void isis_run_spf_cb(struct thread *thread) if (area->ip_circuits) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_IPV4][level - 1]); +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + data = fa->data; + isis_run_spf_with_protection( + area, data->spftree[SPFTREE_IPV4][level - 1]); + } +#endif /* ifndef FABRICD */ have_run = 1; } if (area->ipv6_circuits) { isis_run_spf_with_protection( area, area->spftree[SPFTREE_IPV6][level - 1]); +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + data = fa->data; + isis_run_spf_with_protection( + area, data->spftree[SPFTREE_IPV6][level - 1]); + } +#endif /* ifndef FABRICD */ have_run = 1; } if (area->ipv6_circuits && isis_area_ipv6_dstsrc_enabled(area)) { @@ -1903,8 +2075,6 @@ static void isis_run_spf_cb(struct thread *thread) isis_area_verify_routes(area); /* walk all circuits and reset any spf specific flags */ - struct listnode *node; - struct isis_circuit *circuit; for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); @@ -1955,7 +2125,7 @@ int _isis_spf_schedule(struct isis_area *area, int level, area->area_tag, level, diff, func, file, line); } - THREAD_OFF(area->t_rlfa_rib_update); + EVENT_OFF(area->t_rlfa_rib_update); if (area->spf_delay_ietf[level - 1]) { /* Need to call schedule function also if spf delay is running * to @@ -1966,9 +2136,9 @@ int _isis_spf_schedule(struct isis_area *area, int level, if (area->spf_timer[level - 1]) return ISIS_OK; - thread_add_timer_msec(master, isis_run_spf_cb, - isis_run_spf_arg(area, level), delay, - &area->spf_timer[level - 1]); + event_add_timer_msec(master, isis_run_spf_cb, + isis_run_spf_arg(area, level), delay, + &area->spf_timer[level - 1]); return ISIS_OK; } @@ -1995,8 +2165,8 @@ int _isis_spf_schedule(struct isis_area *area, int level, timer = area->min_spf_interval[level - 1] - diff; } - thread_add_timer(master, isis_run_spf_cb, isis_run_spf_arg(area, level), - timer, &area->spf_timer[level - 1]); + event_add_timer(master, isis_run_spf_cb, isis_run_spf_arg(area, level), + timer, &area->spf_timer[level - 1]); if (IS_DEBUG_SPF_EVENTS) zlog_debug("ISIS-SPF (%s) L%d SPF scheduled %ld sec from now", @@ -2110,8 +2280,13 @@ void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree) } static void show_isis_topology_common(struct vty *vty, int levels, - struct isis *isis) + struct isis *isis, uint8_t algo) { +#ifndef FABRICD + struct isis_flex_algo_data *fa_data; + struct flex_algo *fa; +#endif /* ifndef FABRICD */ + struct isis_spftree *spftree; struct listnode *node; struct isis_area *area; @@ -2119,27 +2294,68 @@ static void show_isis_topology_common(struct vty *vty, int levels, return; for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); + vty_out(vty, + "Area %s:", area->area_tag ? area->area_tag : "null"); + +#ifndef FABRICD + /* + * The shapes of the flex algo spftree 2-dimensional array + * and the area spftree 2-dimensional array are not guaranteed + * to be identical. + */ + fa = NULL; + if (flex_algo_id_valid(algo)) { + fa = flex_algo_lookup(area->flex_algos, algo); + if (!fa) + continue; + fa_data = (struct isis_flex_algo_data *)fa->data; + } else + fa_data = NULL; + + if (algo != SR_ALGORITHM_SPF) + vty_out(vty, " Algorithm %hhu\n", algo); + else +#endif /* ifndef FABRICD */ + vty_out(vty, "\n"); for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { if ((level & levels) == 0) continue; if (area->ip_circuits > 0) { - isis_print_spftree( - vty, - area->spftree[SPFTREE_IPV4][level - 1]); +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV4] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV4] + [level - 1]; + + isis_print_spftree(vty, spftree); } if (area->ipv6_circuits > 0) { - isis_print_spftree( - vty, - area->spftree[SPFTREE_IPV6][level - 1]); +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV6] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV6] + [level - 1]; + isis_print_spftree(vty, spftree); } if (isis_area_ipv6_dstsrc_enabled(area)) { - isis_print_spftree(vty, - area->spftree[SPFTREE_DSTSRC] - [level - 1]); +#ifndef FABRICD + if (fa_data) + spftree = + fa_data->spftree[SPFTREE_DSTSRC] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_DSTSRC] + [level - 1]; + isis_print_spftree(vty, spftree); } } @@ -2159,7 +2375,8 @@ DEFUN(show_isis_topology, show_isis_topology_cmd, " [vrf <NAME|all>] topology" #ifndef FABRICD " [<level-1|level-2>]" -#endif + " [algorithm (128-255)]" +#endif /* ifndef FABRICD */ , SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" @@ -2167,49 +2384,238 @@ DEFUN(show_isis_topology, show_isis_topology_cmd, #ifndef FABRICD "Paths to all level-1 routers in the area\n" "Paths to all level-2 routers in the domain\n" -#endif + "Show Flex-algo routes\n" + "Algorithm number\n" +#endif /* ifndef FABRICD */ ) { 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; + uint8_t algorithm = SR_ALGORITHM_SPF; +#ifndef FABRICD + int idx = 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; + levels = ISIS_LEVEL1 | ISIS_LEVEL2; + if (argv_find(argv, argc, "level-1", &idx)) + levels = ISIS_LEVEL1; + if (argv_find(argv, argc, "level-2", &idx)) + levels = ISIS_LEVEL2; + if (argv_find(argv, argc, "algorithm", &idx)) + algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); +#endif /* ifndef FABRICD */ + + 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, + algorithm); + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) + show_isis_topology_common(vty, levels, isis, algorithm); + } + + return CMD_SUCCESS; +} + +#ifndef FABRICD +static void show_isis_flex_algo_display_eag(struct vty *vty, char *buf, + int indent, + struct admin_group *admin_group) +{ + if (admin_group_zero(admin_group)) + vty_out(vty, "not-set\n"); + else { + vty_out(vty, "%s\n", + admin_group_string(buf, ADMIN_GROUP_PRINT_MAX_SIZE, + indent, admin_group)); + admin_group_print(buf, indent, admin_group); + if (buf[0] != '\0') + vty_out(vty, " Bit positions: %s\n", buf); + } +} + +static void show_isis_flex_algo_common(struct vty *vty, struct isis *isis, + uint8_t algorithm) +{ + struct isis_router_cap_fad *router_fad; + char buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + struct admin_group *admin_group; + struct isis_area *area; + struct listnode *node; + struct flex_algo *fa; + int indent, algo; + bool fad_identical, fad_supported; + + if (!isis->area_list || isis->area_list->count == 0) + return; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + /* + * The shapes of the flex algo spftree 2-dimensional array + * and the area spftree 2-dimensional array are not guaranteed + * to be identical. + */ + + for (algo = 0; algo < SR_ALGORITHM_COUNT; algo++) { + if (algorithm != SR_ALGORITHM_UNSET && + algorithm != algo) + continue; + + fa = flex_algo_lookup(area->flex_algos, algo); + if (!fa) + continue; + + vty_out(vty, "Area %s:", + area->area_tag ? area->area_tag : "null"); + + vty_out(vty, " Algorithm %d\n", algo); + vty_out(vty, "\n"); + + vty_out(vty, " Enabled Data-Planes:"); + if (fa->dataplanes == 0) { + vty_out(vty, " None\n\n"); + continue; + } + if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SR_MPLS)) + vty_out(vty, " SR-MPLS"); + if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_SRV6)) + vty_out(vty, " SRv6"); + if (CHECK_FLAG(fa->dataplanes, FLEX_ALGO_IP)) + vty_out(vty, " IP"); + vty_out(vty, "\n\n"); + + + router_fad = isis_flex_algo_elected(algo, area); + vty_out(vty, + " Elected and running Flexible-Algorithm Definition:\n"); + if (router_fad) + vty_out(vty, " Source: %pSY\n", + router_fad->sysid); + else + vty_out(vty, " Source: Not found\n"); + + if (!router_fad) { + vty_out(vty, "\n"); + continue; + } + + fad_identical = + flex_algo_definition_cmp(fa, &router_fad->fad); + fad_supported = + isis_flex_algo_supported(&router_fad->fad); + vty_out(vty, " Priority: %d\n", + router_fad->fad.priority); + vty_out(vty, " Equal to local: %s\n", + fad_identical ? "yes" : "no"); + vty_out(vty, " Local state: %s\n", + fad_supported + ? "enabled" + : "disabled (unsupported definition)"); + vty_out(vty, " Calculation type: "); + if (router_fad->fad.calc_type == 0) + vty_out(vty, "spf\n"); + else + vty_out(vty, "%d\n", router_fad->fad.calc_type); + vty_out(vty, " Metric type: %s\n", + flex_algo_metric_type_print( + buf, sizeof(buf), + router_fad->fad.metric_type)); + vty_out(vty, " Prefix-metric: %s\n", + CHECK_FLAG(router_fad->fad.flags, FAD_FLAG_M) + ? "enabled" + : "disabled"); + if (router_fad->fad.flags != 0 && + router_fad->fad.flags != FAD_FLAG_M) + vty_out(vty, " Flags: 0x%x\n", + router_fad->fad.flags); + vty_out(vty, " Exclude SRLG: %s\n", + router_fad->fad.exclude_srlg ? "enabled" + : "disabled"); + + admin_group = &router_fad->fad.admin_group_exclude_any; + indent = vty_out(vty, " Exclude-any admin-group: "); + show_isis_flex_algo_display_eag(vty, buf, indent, + admin_group); + + admin_group = &router_fad->fad.admin_group_include_all; + indent = vty_out(vty, " Include-all admin-group: "); + show_isis_flex_algo_display_eag(vty, buf, indent, + admin_group); + + admin_group = &router_fad->fad.admin_group_include_any; + indent = vty_out(vty, " Include-any admin-group: "); + show_isis_flex_algo_display_eag(vty, buf, indent, + admin_group); + + if (router_fad->fad.unsupported_subtlv) + vty_out(vty, + " Unsupported sub-TLV: Present (see logs)"); + + vty_out(vty, "\n"); + } } +} + +DEFUN(show_isis_flex_algo, show_isis_flex_algo_cmd, + "show " PROTO_NAME + " [vrf <NAME|all>] flex-algo" + " [(128-255)]", + SHOW_STR PROTO_HELP VRF_CMD_HELP_STR + "All VRFs\n" + "IS-IS Flex-algo information\n" + "Algorithm number\n") +{ + struct isis *isis; + struct listnode *node; + const char *vrf_name = VRF_DEFAULT_NAME; + bool all_vrf = false; + int idx = 0; + int idx_vrf = 0; + uint8_t flex_algo; if (!im) { vty_out(vty, "IS-IS Routing Process not enabled\n"); return CMD_SUCCESS; } + + if (argv_find(argv, argc, "flex-algo", &idx) && (idx + 1) < argc) + flex_algo = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); + else + flex_algo = SR_ALGORITHM_UNSET; + 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); + show_isis_flex_algo_common(vty, isis, + flex_algo); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis != NULL) - show_isis_topology_common(vty, levels, isis); + show_isis_flex_algo_common(vty, isis, flex_algo); } return CMD_SUCCESS; } +#endif /* ifndef FABRICD */ static void isis_print_route(struct ttable *tt, const struct prefix *prefix, struct isis_route_info *rinfo, bool prefix_sid, - bool no_adjacencies) + bool no_adjacencies, bool json) { struct isis_nexthop *nexthop; struct listnode *node; @@ -2217,96 +2623,116 @@ static void isis_print_route(struct ttable *tt, const struct prefix *prefix, char buf_prefix[BUFSIZ]; (void)prefix2str(prefix, 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]; - - if (!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 (prefix_sid) { - char buf_sid[BUFSIZ] = {}; - char buf_lblop[BUFSIZ] = {}; - - if (nexthop->sr.present) { - snprintf(buf_sid, sizeof(buf_sid), "%u", - nexthop->sr.sid.value); - sr_op2str(buf_lblop, sizeof(buf_lblop), - rinfo->sr.label, nexthop->sr.label); + for (int alg = 0; alg < SR_ALGORITHM_COUNT; alg++) { + for (ALL_LIST_ELEMENTS_RO(rinfo->sr_algo[alg].nexthops, node, + nexthop)) { + struct interface *ifp; + char buf_iface[BUFSIZ]; + char buf_nhop[BUFSIZ]; + + if (!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_sid, "-", sizeof(buf_sid)); - strlcpy(buf_lblop, "-", sizeof(buf_lblop)); + strlcpy(buf_nhop, + print_sys_hostname(nexthop->sysid), + sizeof(buf_nhop)); + strlcpy(buf_iface, "-", sizeof(buf_iface)); } - if (first) { - ttable_add_row(tt, "%s|%u|%s|%s|%s|%s", - buf_prefix, rinfo->cost, - buf_iface, buf_nhop, buf_sid, - buf_lblop); - first = false; - } else - ttable_add_row(tt, "||%s|%s|%s|%s", buf_iface, - buf_nhop, buf_sid, buf_lblop); - } else { - char buf_labels[BUFSIZ] = {}; - - if (nexthop->label_stack) { - for (int i = 0; - i < nexthop->label_stack->num_labels; - i++) { - char buf_label[BUFSIZ]; - - label2str( - nexthop->label_stack->label[i], - 0, buf_label, - sizeof(buf_label)); - if (i != 0) - strlcat(buf_labels, "/", + if (prefix_sid) { + char buf_sid[BUFSIZ] = {}; + char buf_lblop[BUFSIZ] = {}; + + if (rinfo->sr_algo[alg].present) { + snprintf(buf_sid, sizeof(buf_sid), "%u", + rinfo->sr_algo[alg].sid.value); + sr_op2str(buf_lblop, sizeof(buf_lblop), + rinfo->sr_algo[alg].label, + nexthop->sr.label); + } else if (alg == SR_ALGORITHM_SPF) { + strlcpy(buf_sid, "-", sizeof(buf_sid)); + strlcpy(buf_lblop, "-", + sizeof(buf_lblop)); + } else { + continue; + } + + if (first || json) { + ttable_add_row(tt, + "%s|%u|%s|%s|%s|%s|%d", + buf_prefix, rinfo->cost, + buf_iface, buf_nhop, + buf_sid, buf_lblop, alg); + first = false; + } else + ttable_add_row(tt, "||%s|%s|%s|%s|%d", + buf_iface, buf_nhop, + buf_sid, buf_lblop, alg); + } else { + char buf_labels[BUFSIZ] = {}; + + if (nexthop->label_stack) { + for (int i = 0; + i < + nexthop->label_stack->num_labels; + i++) { + char buf_label[BUFSIZ]; + + label2str(nexthop->label_stack + ->label[i], + 0, buf_label, + sizeof(buf_label)); + if (i != 0) + strlcat(buf_labels, "/", + sizeof(buf_labels)); + strlcat(buf_labels, buf_label, sizeof(buf_labels)); - strlcat(buf_labels, buf_label, + } + } else if (nexthop->sr.present) + label2str(nexthop->sr.label, 0, + buf_labels, + sizeof(buf_labels)); + else + strlcpy(buf_labels, "-", sizeof(buf_labels)); - } - } else if (nexthop->sr.present) - label2str(nexthop->sr.label, 0, 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); + + if (first || json) { + 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); + } } } + if (list_isempty(rinfo->nexthops)) { if (prefix_sid) { char buf_sid[BUFSIZ] = {}; char buf_lblop[BUFSIZ] = {}; - if (rinfo->sr.present) { + if (rinfo->sr_algo[SR_ALGORITHM_SPF].present) { snprintf(buf_sid, sizeof(buf_sid), "%u", - rinfo->sr.sid.value); - sr_op2str(buf_lblop, sizeof(buf_lblop), - rinfo->sr.label, - MPLS_LABEL_IMPLICIT_NULL); + rinfo->sr_algo[SR_ALGORITHM_SPF] + .sid.value); + sr_op2str( + buf_lblop, sizeof(buf_lblop), + rinfo->sr_algo[SR_ALGORITHM_SPF].label, + MPLS_LABEL_IMPLICIT_NULL); } else { strlcpy(buf_sid, "-", sizeof(buf_sid)); strlcpy(buf_lblop, "-", sizeof(buf_lblop)); @@ -2322,7 +2748,7 @@ static void isis_print_route(struct ttable *tt, const struct prefix *prefix, } void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, - bool prefix_sid, bool backup) + struct json_object **json, bool prefix_sid, bool backup) { struct route_table *route_table; struct ttable *tt; @@ -2348,13 +2774,16 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, return; } - vty_out(vty, "IS-IS %s %s routing table:\n\n", - circuit_t2string(spftree->level), tree_id_text); + if (json == NULL) + 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]); if (prefix_sid) - ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|SID|Label Op."); + ttable_add_row( + tt, + "Prefix|Metric|Interface|Nexthop|SID|Label Op.|Algo"); else ttable_add_row(tt, "Prefix|Metric|Interface|Nexthop|Label(s)"); tt->style.cell.rpad = 2; @@ -2374,55 +2803,160 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, if (!rinfo) continue; - isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies); + isis_print_route(tt, &rn->p, rinfo, prefix_sid, no_adjacencies, + json != NULL); } /* Dump the generated table. */ - if (tt->nrows > 1) { + if (json == NULL && tt->nrows > 1) { char *table; table = ttable_dump(tt, "\n"); vty_out(vty, "%s\n", table); XFREE(MTYPE_TMP, table); + } else if (json) { + *json = ttable_json(tt, prefix_sid ? "sdssdsdd" : "sdsss"); } ttable_del(tt); } static void show_isis_route_common(struct vty *vty, int levels, struct isis *isis, bool prefix_sid, - bool backup) + bool backup, uint8_t algo, + json_object **json) { + json_object *json_level = NULL, *jstr = NULL, *json_val; +#ifndef FABRICD + struct isis_flex_algo_data *fa_data; + struct flex_algo *fa; +#endif /* ifndef FABRICD */ + struct isis_spftree *spftree; struct listnode *node; struct isis_area *area; + char key[8]; if (!isis->area_list || isis->area_list->count == 0) return; + if (json) + *json = json_object_new_object(); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { - vty_out(vty, "Area %s:\n", - area->area_tag ? area->area_tag : "null"); +#ifndef FABRICD + /* + * The shapes of the flex algo spftree 2-dimensional array + * and the area spftree 2-dimensional array are not guaranteed + * to be identical. + */ + fa = NULL; + if (flex_algo_id_valid(algo)) { + fa = flex_algo_lookup(area->flex_algos, algo); + if (!fa) + continue; + fa_data = (struct isis_flex_algo_data *)fa->data; + } else { + fa_data = NULL; + } +#endif /* ifndef FABRICD */ + + if (json) { + jstr = json_object_new_string( + area->area_tag ? area->area_tag : "null"); + json_object_object_add(*json, "area", jstr); + } else { + vty_out(vty, "Area %s:", + area->area_tag ? area->area_tag : "null"); +#ifndef FABRICD + if (algo != SR_ALGORITHM_SPF) + vty_out(vty, " Algorithm %hhu\n", algo); + else +#endif /* ifndef FABRICD */ + vty_out(vty, "\n"); + } for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { if ((level & levels) == 0) continue; + if (json) { + json_level = json_object_new_object(); + jstr = json_object_new_string( + area->area_tag ? area->area_tag + : "null"); + json_object_object_add(json_level, "area", + jstr); + } + if (area->ip_circuits > 0) { - isis_print_routes( - vty, - area->spftree[SPFTREE_IPV4][level - 1], - prefix_sid, backup); + json_val = NULL; +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV4] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV4] + [level - 1]; + + if (!json) + isis_print_spftree(vty, spftree); + + isis_print_routes(vty, spftree, + json ? &json_val : NULL, + prefix_sid, backup); + if (json && json_val) { + json_object_object_add( + json_level, "ipv4", json_val); + } } if (area->ipv6_circuits > 0) { - isis_print_routes( - vty, - area->spftree[SPFTREE_IPV6][level - 1], - prefix_sid, backup); + json_val = NULL; +#ifndef FABRICD + if (fa_data) + spftree = fa_data->spftree[SPFTREE_IPV6] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_IPV6] + [level - 1]; + + if (!json) + isis_print_spftree(vty, spftree); + + isis_print_routes(vty, spftree, + json ? &json_val : NULL, + prefix_sid, backup); + if (json && json_val) { + json_object_object_add( + json_level, "ipv6", json_val); + } } if (isis_area_ipv6_dstsrc_enabled(area)) { - isis_print_routes(vty, - area->spftree[SPFTREE_DSTSRC] - [level - 1], + json_val = NULL; +#ifndef FABRICD + if (fa_data) + spftree = + fa_data->spftree[SPFTREE_DSTSRC] + [level - 1]; + else +#endif /* ifndef FABRICD */ + spftree = area->spftree[SPFTREE_DSTSRC] + [level - 1]; + + if (!json) + isis_print_spftree(vty, spftree); + isis_print_routes(vty, spftree, + json ? &json_val : NULL, prefix_sid, backup); + if (json && json_val) { + json_object_object_add(json_level, + "ipv6-dstsrc", + json_val); + } + } + if (json) { + snprintf(key, sizeof(key), "level-%d", level); + json_object_object_add(*json, key, json_level); } } } @@ -2433,16 +2967,25 @@ DEFUN(show_isis_route, show_isis_route_cmd, " [vrf <NAME|all>] route" #ifndef FABRICD " [<level-1|level-2>]" -#endif - " [<prefix-sid|backup>]", +#endif /* ifndef FABRICD */ + " [<prefix-sid|backup>]" +#ifndef FABRICD + " [algorithm (128-255)]" +#endif /* ifndef FABRICD */ + " [json$uj]", 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 +#endif /* ifndef FABRICD */ "Show Prefix-SID information\n" - "Show backup routes\n") + "Show backup routes\n" +#ifndef FABRICD + "Show Flex-algo routes\n" + "Algorithm number\n" +#endif /* ifndef FABRICD */ + JSON_STR) { int levels; struct isis *isis; @@ -2451,7 +2994,10 @@ DEFUN(show_isis_route, show_isis_route_cmd, bool all_vrf = false; bool prefix_sid = false; bool backup = false; + bool uj = use_json(argc, argv); int idx = 0; + json_object *json = NULL, *json_vrf = NULL; + uint8_t algorithm = SR_ALGORITHM_SPF; if (argv_find(argv, argc, "level-1", &idx)) levels = ISIS_LEVEL1; @@ -2471,17 +3017,50 @@ DEFUN(show_isis_route, show_isis_route_cmd, if (argv_find(argv, argc, "backup", &idx)) backup = true; +#ifndef FABRICD + if (argv_find(argv, argc, "algorithm", &idx)) + algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); +#endif /* ifndef FABRICD */ + + if (uj) + json = json_object_new_array(); + if (vrf_name) { if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - show_isis_route_common(vty, levels, isis, - prefix_sid, backup); - return CMD_SUCCESS; + for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) { + show_isis_route_common( + vty, levels, isis, prefix_sid, backup, + algorithm, uj ? &json_vrf : NULL); + if (uj) { + json_object_object_add( + json_vrf, "vrf_id", + json_object_new_int( + isis->vrf_id)); + json_object_array_add(json, json_vrf); + } + } + goto out; } isis = isis_lookup_by_vrfname(vrf_name); - if (isis != NULL) + if (isis != NULL) { show_isis_route_common(vty, levels, isis, prefix_sid, - backup); + backup, algorithm, + uj ? &json_vrf : NULL); + if (uj) { + json_object_object_add( + json_vrf, "vrf_id", + json_object_new_int(isis->vrf_id)); + json_object_array_add(json, json_vrf); + } + } + } + +out: + 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; @@ -2701,6 +3280,9 @@ DEFUN(show_isis_frr_summary, show_isis_frr_summary_cmd, void isis_spf_init(void) { +#ifndef FABRICD + install_element(VIEW_NODE, &show_isis_flex_algo_cmd); +#endif /* ifndef FABRICD */ install_element(VIEW_NODE, &show_isis_topology_cmd); install_element(VIEW_NODE, &show_isis_route_cmd); install_element(VIEW_NODE, &show_isis_frr_summary_cmd); diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 7f4ab707e767..7e9754d9bfa5 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -12,6 +12,7 @@ #define _ZEBRA_ISIS_SPF_H #include "isisd/isis_lfa.h" +#include "lib/json.h" struct isis_spftree; @@ -37,16 +38,15 @@ struct isis_spf_adj { #define F_ISIS_SPF_ADJ_METRIC_INFINITY 0x04 }; -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 * +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, uint8_t algorithm); struct isis_vertex *isis_spf_prefix_sid_lookup(struct isis_spftree *spftree, struct isis_prefix_sid *psid); void isis_spf_invalidate_routes(struct isis_spftree *tree); -void isis_spf_verify_routes(struct isis_area *area, - struct isis_spftree **trees); +void isis_spf_verify_routes(struct isis_area *area, struct isis_spftree **trees, + int tree); void isis_spf_switchover_routes(struct isis_area *area, struct isis_spftree **trees, int family, union g_addr *nexthop_ip, ifindex_t ifindex, @@ -63,7 +63,7 @@ int _isis_spf_schedule(struct isis_area *area, int level, const char *func, const char *file, int line); void isis_print_spftree(struct vty *vty, struct isis_spftree *spftree); void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, - bool prefix_sid, bool backup); + json_object **json, bool prefix_sid, bool backup); void isis_spf_init(void); void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); void isis_spf_print_json(struct isis_spftree *spftree, diff --git a/isisd/isis_spf_private.h b/isisd/isis_spf_private.h index d82937270209..763673063cfa 100644 --- a/isisd/isis_spf_private.h +++ b/isisd/isis_spf_private.h @@ -349,11 +349,16 @@ struct isis_spftree { uint32_t total[SPF_PREFIX_PRIO_MAX]; } protection_counters; } lfa; + uint8_t algorithm; uint8_t flags; }; #define F_SPFTREE_HOPCOUNT_METRIC 0x01 #define F_SPFTREE_NO_ROUTES 0x02 #define F_SPFTREE_NO_ADJACENCIES 0x04 +#ifndef FABRICD +/* flex-algo */ +#define F_SPFTREE_DISABLED 0x08 +#endif /* ifndef FABRICD */ __attribute__((__unused__)) static void isis_vertex_id_init(struct isis_vertex *vertex, const void *id, diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index 429399165487..f928185ffbc4 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -57,7 +57,17 @@ static void sr_adj_sid_del(struct sr_adjacency *sra); 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); + int ret; + + ret = prefix_cmp(&a->prefix, &b->prefix); + if (ret != 0) + return ret; + + ret = a->algorithm - b->algorithm; + if (ret != 0) + return ret; + + return 0; } DECLARE_RBTREE_UNIQ(srdb_prefix_cfg, struct sr_prefix_cfg, entry, sr_prefix_sid_cfg_compare); @@ -331,7 +341,8 @@ int isis_sr_cfg_srlb_update(struct isis_area *area, uint32_t lower_bound, * @return Newly added Prefix-SID configuration structure */ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, - const struct prefix *prefix) + const struct prefix *prefix, + uint8_t algorithm) { struct sr_prefix_cfg *pcfg; struct interface *ifp; @@ -341,6 +352,7 @@ struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, pcfg = XCALLOC(MTYPE_ISIS_SR_INFO, sizeof(*pcfg)); pcfg->prefix = *prefix; pcfg->area = area; + pcfg->algorithm = algorithm; /* Pull defaults from the YANG module. */ pcfg->sid_type = yang_get_default_enum( @@ -386,11 +398,13 @@ void isis_sr_cfg_prefix_del(struct sr_prefix_cfg *pcfg) * @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) + union prefixconstptr prefix, + uint8_t algorithm) { struct sr_prefix_cfg pcfg = {}; prefix_copy(&pcfg.prefix, prefix.p); + pcfg.algorithm = algorithm; return srdb_prefix_cfg_find(&area->srdb.config.prefix_sids, &pcfg); } @@ -405,7 +419,7 @@ 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; + psid->algorithm = pcfg->algorithm; /* Set SID flags. */ psid->flags = 0; @@ -917,10 +931,12 @@ static int sr_adj_ip_disabled(struct isis_adjacency *adj, int family, */ static int sr_if_new_hook(struct interface *ifp) { + struct sr_prefix_cfg *pcfgs[SR_ALGORITHM_COUNT] = {NULL}; struct isis_circuit *circuit; struct isis_area *area; struct connected *connected; struct listnode *node; + bool need_lsp_regenerate = false; /* Get corresponding circuit */ circuit = circuit_scan_by_ifp(ifp); @@ -937,18 +953,24 @@ static int sr_if_new_hook(struct interface *ifp) * 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; + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + pcfgs[i] = isis_sr_cfg_prefix_find( + area, connected->address, i); + + if (!pcfgs[i]) + continue; - if (sr_prefix_is_node_sid(ifp, &pcfg->prefix)) { - pcfg->node_sid = true; - lsp_regenerate_schedule(area, area->is_type, 0); + if (sr_prefix_is_node_sid(ifp, &pcfgs[i]->prefix)) { + pcfgs[i]->node_sid = true; + need_lsp_regenerate = true; + } } } + if (need_lsp_regenerate) + lsp_regenerate_schedule(area, area->is_type, 0); + return 0; } @@ -998,10 +1020,12 @@ char *sr_op2str(char *buf, size_t size, mpls_label_t label_in, * @param area IS-IS area * @param level IS-IS level */ -static void show_node(struct vty *vty, struct isis_area *area, int level) +static void show_node(struct vty *vty, struct isis_area *area, int level, + uint8_t algo) { struct isis_lsp *lsp; struct ttable *tt; + char buf[128]; vty_out(vty, " IS-IS %s SR-Nodes:\n\n", circuit_t2string(level)); @@ -1021,15 +1045,24 @@ static void show_node(struct vty *vty, struct isis_area *area, int level) cap = lsp->tlvs->router_cap; if (!cap) continue; + if (cap->algo[algo] == SR_ALGORITHM_UNSET) + continue; - ttable_add_row( - tt, "%s|%u - %u|%u - %u|%s|%u", - sysid_print(lsp->hdr.lsp_id), cap->srgb.lower_bound, - cap->srgb.lower_bound + cap->srgb.range_size - 1, - cap->srlb.lower_bound, - cap->srlb.lower_bound + cap->srlb.range_size - 1, - cap->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF", - cap->msd); + if (cap->algo[algo] == SR_ALGORITHM_SPF) + snprintf(buf, sizeof(buf), "SPF"); + else if (cap->algo[algo] == SR_ALGORITHM_STRICT_SPF) + snprintf(buf, sizeof(buf), "S-SPF"); +#ifndef FABRICD + else + snprintf(buf, sizeof(buf), "Flex-Algo %d", algo); +#endif /* ifndef FABRICD */ + + ttable_add_row(tt, "%pSY|%u - %u|%u - %u|%s|%u", + lsp->hdr.lsp_id, cap->srgb.lower_bound, + cap->srgb.lower_bound + cap->srgb.range_size - 1, + cap->srlb.lower_bound, + cap->srlb.lower_bound + cap->srlb.range_size - 1, + buf, cap->msd); } /* Dump the generated table. */ @@ -1044,15 +1077,31 @@ static void show_node(struct vty *vty, struct isis_area *area, int level) } DEFUN(show_sr_node, show_sr_node_cmd, - "show " PROTO_NAME " segment-routing node", - SHOW_STR - PROTO_HELP + "show " PROTO_NAME + " segment-routing node" +#ifndef FABRICD + " [algorithm (128-255)]" +#endif /* ifndef FABRICD */ + , + SHOW_STR PROTO_HELP "Segment-Routing\n" - "Segment-Routing node\n") + "Segment-Routing node\n" +#ifndef FABRICD + "Show Flex-algo nodes\n" + "Algorithm number\n" +#endif /* ifndef FABRICD */ +) { struct listnode *node, *inode; struct isis_area *area; + uint8_t algorithm = SR_ALGORITHM_SPF; struct isis *isis; +#ifndef FABRICD + int idx = 0; + + if (argv_find(argv, argc, "algorithm", &idx)) + algorithm = (uint8_t)strtoul(argv[idx + 1]->arg, NULL, 10); +#endif /* ifndef FABRICD */ for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { @@ -1064,7 +1113,7 @@ DEFUN(show_sr_node, show_sr_node_cmd, } for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) - show_node(vty, area, level); + show_node(vty, area, level, algorithm); } } @@ -1081,11 +1130,11 @@ DEFUN(show_sr_node, show_sr_node_cmd, * * @return 1 on success */ -static void sr_start_label_manager(struct thread *start) +static void sr_start_label_manager(struct event *start) { struct isis_area *area; - area = THREAD_ARG(start); + area = EVENT_ARG(start); /* re-attempt to start SR & Label Manager connection */ isis_sr_start(area); @@ -1108,8 +1157,8 @@ int isis_sr_start(struct isis_area *area) 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); + event_add_timer(master, sr_start_label_manager, area, 1, + &srdb->t_start_lm); return -1; } @@ -1168,7 +1217,7 @@ void isis_sr_stop(struct isis_area *area) area->area_tag); /* Disable any re-attempt to connect to Label Manager */ - THREAD_OFF(srdb->t_start_lm); + EVENT_OFF(srdb->t_start_lm); /* Uninstall all local Adjacency-SIDs. */ for (ALL_LIST_ELEMENTS(area->srdb.adj_sids, node, nnode, sra)) diff --git a/isisd/isis_sr.h b/isisd/isis_sr.h index 627f1fb74884..f5f0adf241e2 100644 --- a/isisd/isis_sr.h +++ b/isisd/isis_sr.h @@ -61,6 +61,11 @@ struct isis_sr_psid_info { /* Indicates whether the Prefix-SID is present or not. */ bool present; + + uint8_t algorithm; + + struct list *nexthops; + struct list *nexthops_backup; }; /* Segment Routing Local Block allocation */ @@ -147,6 +152,9 @@ struct sr_prefix_cfg { /* Backpointer to IS-IS area. */ struct isis_area *area; + + /* SR Algorithm number */ + uint8_t algorithm; }; /* Per-area IS-IS Segment Routing Data Base (SRDB). */ @@ -155,7 +163,7 @@ struct isis_sr_db { bool enabled; /* Thread timer to start Label Manager */ - struct thread *t_start_lm; + struct event *t_start_lm; /* List of local Adjacency-SIDs. */ struct list *adj_sids; @@ -198,11 +206,13 @@ 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 struct sr_prefix_cfg *isis_sr_cfg_prefix_add(struct isis_area *area, + const struct prefix *prefix, + uint8_t algorithm); 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); +isis_sr_cfg_prefix_find(struct isis_area *area, union prefixconstptr prefix, + uint8_t algorithm); extern void isis_sr_prefix_cfg2subtlv(const struct sr_prefix_cfg *pcfg, bool external, struct isis_prefix_sid *psid); diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 3659f4e07c67..90b53c540ef5 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -13,7 +13,7 @@ #include <math.h> #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "stream.h" #include "memory.h" @@ -164,6 +164,154 @@ void isis_mpls_te_term(struct isis_area *area) XFREE(MTYPE_ISIS_MPLS_TE, area->mta); } +static void isis_link_params_update_asla(struct isis_circuit *circuit, + struct interface *ifp) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node, *nnode; + struct isis_ext_subtlvs *ext = circuit->ext; + int i; + + if (!HAS_LINK_PARAMS(ifp)) { + list_delete_all_node(ext->aslas); + return; + } + +#ifndef FABRICD + /* RFC 8919 Application Specific Link-Attributes + * is required by flex-algo application ISIS_SABM_FLAG_X + */ + if (list_isempty(circuit->area->flex_algos->flex_algos)) + isis_tlvs_free_asla(ext, ISIS_SABM_FLAG_X); + else + isis_tlvs_find_alloc_asla(ext, ISIS_SABM_FLAG_X); +#endif /* ifndef FABRICD */ + + if (list_isempty(ext->aslas)) + return; + + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) { + asla->legacy = circuit->area->asla_legacy_flag; + RESET_SUBTLV(asla); + + if (asla->legacy) + continue; + + /* Fulfill ASLA subTLVs from interface link parameters */ + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) { + asla->admin_group = ifp->link_params->admin_grp; + SET_SUBTLV(asla, EXT_ADM_GRP); + } else + UNSET_SUBTLV(asla, EXT_ADM_GRP); + + if (IS_PARAM_SET(ifp->link_params, LP_EXTEND_ADM_GRP)) { + admin_group_copy(&asla->ext_admin_group, + &ifp->link_params->ext_admin_grp); + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + } else + UNSET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + + /* Send admin-group zero for better compatibility + * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2 + */ + if (circuit->area->admin_group_send_zero && + !IS_SUBTLV(asla, EXT_ADM_GRP) && + !IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP)) { + asla->admin_group = 0; + SET_SUBTLV(asla, EXT_ADM_GRP); + admin_group_clear(&asla->ext_admin_group); + admin_group_allow_explicit_zero(&asla->ext_admin_group); + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + } + + if (IS_PARAM_SET(ifp->link_params, LP_TE_METRIC)) { + asla->te_metric = ifp->link_params->te_metric; + SET_SUBTLV(asla, EXT_TE_METRIC); + } else + UNSET_SUBTLV(asla, EXT_TE_METRIC); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) { + asla->delay = ifp->link_params->av_delay; + SET_SUBTLV(asla, EXT_DELAY); + } else + UNSET_SUBTLV(asla, EXT_DELAY); + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) { + asla->min_delay = ifp->link_params->min_delay; + asla->max_delay = ifp->link_params->max_delay; + SET_SUBTLV(asla, EXT_MM_DELAY); + } else { + UNSET_SUBTLV(asla, EXT_MM_DELAY); + } + + if (asla->standard_apps == ISIS_SABM_FLAG_X) + /* Flex-Algo ASLA does not need the following TE + * sub-TLVs + */ + continue; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) { + asla->max_bw = ifp->link_params->max_bw; + SET_SUBTLV(asla, EXT_MAX_BW); + } else + UNSET_SUBTLV(asla, EXT_MAX_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) { + asla->max_rsv_bw = ifp->link_params->max_rsv_bw; + SET_SUBTLV(asla, EXT_MAX_RSV_BW); + } else + UNSET_SUBTLV(asla, EXT_MAX_RSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) { + for (i = 0; i < MAX_CLASS_TYPE; i++) + asla->unrsv_bw[i] = + ifp->link_params->unrsv_bw[i]; + SET_SUBTLV(asla, EXT_UNRSV_BW); + } else + UNSET_SUBTLV(asla, EXT_UNRSV_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) { + asla->delay_var = ifp->link_params->delay_var; + SET_SUBTLV(asla, EXT_DELAY_VAR); + } else + UNSET_SUBTLV(asla, EXT_DELAY_VAR); + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) { + asla->pkt_loss = ifp->link_params->pkt_loss; + SET_SUBTLV(asla, EXT_PKT_LOSS); + } else + UNSET_SUBTLV(asla, EXT_PKT_LOSS); + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) { + asla->res_bw = ifp->link_params->res_bw; + SET_SUBTLV(asla, EXT_RES_BW); + } else + UNSET_SUBTLV(asla, EXT_RES_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) { + asla->ava_bw = ifp->link_params->ava_bw; + SET_SUBTLV(asla, EXT_AVA_BW); + } else + UNSET_SUBTLV(asla, EXT_AVA_BW); + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) { + asla->use_bw = ifp->link_params->use_bw; + SET_SUBTLV(asla, EXT_USE_BW); + } else + UNSET_SUBTLV(asla, EXT_USE_BW); + } + + + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) { + if (!asla->legacy && NO_SUBTLV(asla) && + admin_group_nb_words(&asla->ext_admin_group) == 0) + /* remove ASLA without info from the list of ASLAs to + * not send void ASLA + */ + isis_tlvs_del_asla_flex_algo(ext, asla); + } +} + /* 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, @@ -210,6 +358,19 @@ void isis_link_params_update(struct isis_circuit *circuit, } else UNSET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); + /* Send admin-group zero for better compatibility + * https://www.rfc-editor.org/rfc/rfc7308#section-2.3.2 + */ + if (circuit->area->admin_group_send_zero && + !IS_SUBTLV(ext, EXT_ADM_GRP) && + !IS_SUBTLV(ext, EXT_EXTEND_ADM_GRP)) { + ext->adm_group = 0; + SET_SUBTLV(ext, EXT_ADM_GRP); + admin_group_clear(&ext->ext_admin_group); + admin_group_allow_explicit_zero(&ext->ext_admin_group); + SET_SUBTLV(ext, EXT_EXTEND_ADM_GRP); + } + /* If known, register local IPv4 addr from ip_addr list */ if (listcount(circuit->ip_addrs) != 0) { addr = (struct prefix_ipv4 *)listgetdata( @@ -331,6 +492,8 @@ void isis_link_params_update(struct isis_circuit *circuit, ext->status = 0; } + isis_link_params_update_asla(circuit, ifp); + return; } @@ -503,7 +666,12 @@ int isis_mpls_te_update(struct interface *ifp) isis_link_params_update(circuit, ifp); /* ... and LSP */ - if (circuit->area && IS_MPLS_TE(circuit->area->mta)) + if (circuit->area && + (IS_MPLS_TE(circuit->area->mta) +#ifndef FABRICD + || !list_isempty(circuit->area->flex_algos->flex_algos) +#endif /* ifndef FABRICD */ + )) lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); rc = 0; @@ -611,7 +779,7 @@ static struct ls_vertex *lsp_to_vertex(struct ls_ted *ted, struct isis_lsp *lsp) lnode.srgb.flag = cap->srgb.flags; lnode.srgb.lower_bound = cap->srgb.lower_bound; lnode.srgb.range_size = cap->srgb.range_size; - for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + for (int i = 0; i < LIB_LS_SR_ALGO_COUNT; i++) lnode.algo[i] = cap->algo[i]; } @@ -657,7 +825,7 @@ static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr) { struct ls_edge *edge; struct ls_standard *std; - uint64_t key = 0; + struct ls_edge_key key; /* Check parameters */ if (!ted || !attr) @@ -666,19 +834,22 @@ static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr) std = &attr->standard; /* Compute keys in function of local address (IPv4/v6) or identifier */ - if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) - key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff; - else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) - key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32 - | (uint64_t)ntohl(std->local6.s6_addr32[3])); - else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) - key = ((uint64_t)std->remote_id << 32) - | (((uint64_t)std->local_id) & 0xffffffff); - else - key = 0; + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) { + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &std->local); + } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) { + key.family = AF_INET6; + IPV6_ADDR_COPY(&key.k.addr6, &std->local6); + } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) { + key.family = AF_LOCAL; + key.k.link_id = (((uint64_t)std->local_id) & 0xffffffff) | + ((uint64_t)std->remote_id << 32); + } else { + key.family = AF_UNSPEC; + } /* Stop here if we don't got a valid key */ - if (key == 0) + if (key.family == AF_UNSPEC) return NULL; /* Get corresponding Edge by key from Link State Data Base */ @@ -697,18 +868,17 @@ static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_attributes *attr) } if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR)) - te_debug(" |- %s Edge (%" PRIu64 - ") from Extended Reach. %pI4", - edge->status == NEW ? "Create" : "Found", edge->key, - &attr->standard.local); + te_debug(" |- %s Edge (%pI4) from Extended Reach. %pI4", + edge->status == NEW ? "Create" : "Found", + &edge->key.k.addr, &attr->standard.local); else if (CHECK_FLAG(edge->attributes->flags, LS_ATTR_LOCAL_ADDR6)) - te_debug(" |- %s Edge (%" PRIu64 - ") from Extended Reach. %pI6", - edge->status == NEW ? "Create" : "Found", edge->key, - &attr->standard.local6); + te_debug(" |- %s Edge (%pI6) from Extended Reach. %pI6", + edge->status == NEW ? "Create" : "Found", + &edge->key.k.addr6, &attr->standard.local6); else te_debug(" |- %s Edge (%" PRIu64 ")", - edge->status == NEW ? "Create" : "Found", edge->key); + edge->status == NEW ? "Create" : "Found", + edge->key.k.link_id); return edge; } @@ -903,7 +1073,7 @@ static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric, struct ls_edge *edge, *dst; struct ls_attributes *attr; - te_debug(" |- Process Extended IS for %s", sysid_print(id)); + te_debug(" |- Process Extended IS for %pSY", id); /* Check parameters */ if (old_metric || !args || !tlvs) @@ -950,8 +1120,21 @@ static int lsp_to_edge_cb(const uint8_t *id, uint32_t metric, bool old_metric, } /* Try to update remote Link from remote address or reachability ID */ - te_debug(" |- Link Edge (%" PRIu64 ") to destination vertex (%s)", - edge->key, print_sys_hostname(id)); + if (edge->key.family == AF_INET) + te_debug(" |- Link Edge (%pI4) to destination vertex (%s)", + &edge->key.k.addr, print_sys_hostname(id)); + else if (edge->key.family == AF_INET6) + te_debug(" |- Link Edge (%pI6) to destination vertex (%s)", + &edge->key.k.addr6, print_sys_hostname(id)); + else if (edge->key.family == AF_LOCAL) + te_debug(" |- Link Edge (%" PRIu64 + ") to destination vertex (%s)", + edge->key.k.link_id, print_sys_hostname(id)); + else + te_debug( + " |- Link Edge (Unknown) to destination vertex (%s)", + print_sys_hostname(id)); + dst = ls_find_edge_by_destination(args->ted, edge->attributes); if (dst) { /* Attach remote link if not set */ @@ -1075,16 +1258,29 @@ static int lsp_to_subnet_cb(const struct prefix *prefix, uint32_t metric, } if (!std) prefix_copy(&p, prefix); - else + else { + /* Remove old subnet if any before prefix adjustment */ + subnet = ls_find_subnet(args->ted, prefix); + if (subnet) { + if (args->export) { + subnet->status = DELETE; + isis_te_export(LS_MSG_TYPE_PREFIX, subnet); + } + te_debug(" |- Remove subnet with prefix %pFX", + &subnet->key); + ls_subnet_del_all(args->ted, subnet); + } te_debug(" |- Adjust prefix %pFX with local address to: %pFX", prefix, &p); + } /* Search existing Subnet in TED ... */ - subnet = ls_find_subnet(args->ted, p); + subnet = ls_find_subnet(args->ted, &p); /* ... and create a new Subnet if not found */ if (!subnet) { - ls_pref = ls_prefix_new(vertex->node->adv, p); + ls_pref = ls_prefix_new(vertex->node->adv, &p); subnet = ls_subnet_add(args->ted, ls_pref); + /* Stop processing if we are unable to create a new subnet */ if (!subnet) return LSP_ITER_CONTINUE; } @@ -1167,14 +1363,14 @@ static void isis_te_parse_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) ted = mta->ted; - te_debug("ISIS-TE(%s): Parse LSP %s", lsp->area->area_tag, - sysid_print(lsp->hdr.lsp_id)); + te_debug("ISIS-TE(%s): Parse LSP %pSY", lsp->area->area_tag, + lsp->hdr.lsp_id); /* First parse LSP to obtain the corresponding Vertex */ vertex = lsp_to_vertex(ted, lsp); if (!vertex) { - zlog_warn("Unable to build Vertex from LSP %s. Abort!", - sysid_print(lsp->hdr.lsp_id)); + zlog_warn("Unable to build Vertex from LSP %pSY. Abort!", + lsp->hdr.lsp_id); return; } @@ -1238,8 +1434,8 @@ static void isis_te_delete_lsp(struct mpls_te_area *mta, struct isis_lsp *lsp) if (!IS_MPLS_TE(mta) || !mta->ted || !lsp) return; - te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %s", - lsp->area->area_tag, sysid_print(lsp->hdr.lsp_id)); + te_debug("ISIS-TE(%s): Delete Link State TED objects from LSP %pSY", + lsp->area->area_tag, lsp->hdr.lsp_id); /* Compute Link State Node ID from IS-IS sysID ... */ if (lsp->level == ISIS_LEVEL1) @@ -1728,7 +1924,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, struct ls_vertex *vertex; struct ls_edge *edge; struct ls_subnet *subnet; - uint64_t key; + struct ls_edge_key key; bool detail = false; bool uj = use_json(argc, argv); json_object *json = NULL; @@ -1782,7 +1978,8 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, return CMD_WARNING_CONFIG_FAILED; } /* Get the Edge from the Link State Database */ - key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &ip_addr); edge = ls_find_edge_by_key(ted, key); if (!edge) { vty_out(vty, "No edge found for ID %pI4\n", @@ -1797,8 +1994,8 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, return CMD_WARNING_CONFIG_FAILED; } /* Get the Edge from the Link State Database */ - key = (uint64_t)ntohl(ip6_addr.s6_addr32[3]) - | ((uint64_t)ntohl(ip6_addr.s6_addr32[2]) << 32); + key.family = AF_INET6; + IPV6_ADDR_COPY(&key.k.addr6, &ip6_addr); edge = ls_find_edge_by_key(ted, key); if (!edge) { vty_out(vty, "No edge found for ID %pI6\n", @@ -1822,7 +2019,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); @@ -1835,7 +2032,7 @@ static int show_ted(struct vty *vty, struct cmd_token *argv[], int argc, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index b52a38be7f87..e871ae8c4f99 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -29,9 +29,15 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_te.h" #include "isisd/isis_sr.h" +#include "isisd/isis_flex_algo.h" + +#define TLV_SIZE_MISMATCH(log, indent, target) \ + sbuf_push(log, indent, \ + "TLV size does not match expected size for " target "!\n") DEFINE_MTYPE_STATIC(ISISD, ISIS_TLV, "ISIS TLVs"); DEFINE_MTYPE(ISISD, ISIS_SUBTLV, "ISIS Sub-TLVs"); +DEFINE_MTYPE_STATIC(ISISD, ISIS_SUBSUBTLV, "ISIS Sub-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, @@ -119,6 +125,7 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) ext = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_ext_subtlvs)); init_item_list(&ext->adj_sid); init_item_list(&ext->lan_sid); + ext->aslas = list_new(); admin_group_init(&ext->ext_admin_group); @@ -128,6 +135,8 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) { struct isis_item *item, *next_item; + struct listnode *node, *nnode; + struct isis_asla_subtlvs *asla; if (!ext) return; @@ -142,6 +151,11 @@ void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) XFREE(MTYPE_ISIS_SUBTLV, item); } + for (ALL_LIST_ELEMENTS(ext->aslas, node, nnode, asla)) + isis_tlvs_del_asla_flex_algo(ext, asla); + + list_delete(&ext->aslas); + admin_group_term(&ext->ext_admin_group); XFREE(MTYPE_ISIS_SUBTLV, ext); @@ -158,6 +172,8 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) struct isis_ext_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); struct isis_adj_sid *adj; struct isis_lan_adj_sid *lan; + struct listnode *node, *nnode; + struct isis_asla_subtlvs *new_asla, *asla; /* Copy the Extended IS main part */ memcpy(rv, exts, sizeof(struct isis_ext_subtlvs)); @@ -222,12 +238,133 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) SET_SUBTLV(rv, EXT_LAN_ADJ_SID); } + rv->aslas = list_new(); + + for (ALL_LIST_ELEMENTS(exts->aslas, node, nnode, asla)) { + new_asla = XCALLOC(MTYPE_ISIS_SUBTLV, + sizeof(struct isis_asla_subtlvs)); + memcpy(new_asla, asla, sizeof(struct isis_asla_subtlvs)); + + new_asla->ext_admin_group.bitmap.data = NULL; + admin_group_copy(&new_asla->ext_admin_group, + &asla->ext_admin_group); + + listnode_add(rv->aslas, new_asla); + } + rv->ext_admin_group.bitmap.data = NULL; admin_group_copy(&rv->ext_admin_group, &exts->ext_admin_group); return rv; } +static void format_item_asla_subtlvs(struct isis_asla_subtlvs *asla, + struct sbuf *buf, int indent) +{ + char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + + sbuf_push(buf, indent, "Application Specific Link Attributes:\n"); + sbuf_push(buf, indent + 2, + "L flag: %u, SA-Length: %u, UDA-Length: %u\n", asla->legacy, + asla->standard_apps_length, asla->user_def_apps_length); + sbuf_push(buf, indent + 2, "Standard Applications: 0x%02x", + asla->standard_apps); + if (asla->standard_apps) { + uint8_t bit = asla->standard_apps; + if (bit & ISIS_SABM_FLAG_R) + sbuf_push(buf, 0, " RSVP-TE"); + if (bit & ISIS_SABM_FLAG_S) + sbuf_push(buf, 0, " SR-Policy"); + if (bit & ISIS_SABM_FLAG_L) + sbuf_push(buf, 0, " Loop-Free-Alternate"); + if (bit & ISIS_SABM_FLAG_X) + sbuf_push(buf, 0, " Flex-Algo"); + } + sbuf_push(buf, 0, "\n"); + sbuf_push(buf, indent + 2, "User Defined Applications: 0x%02x\n", + asla->user_def_apps); + + if (IS_SUBTLV(asla, EXT_ADM_GRP)) { + sbuf_push(buf, indent + 2, "Admin Group: 0x%08x\n", + asla->admin_group); + sbuf_push(buf, indent + 4, "Bit positions: %s\n", + admin_group_standard_print( + admin_group_buf, + indent + 2 + strlen("Admin Group: "), + asla->admin_group)); + } + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) { + sbuf_push(buf, indent + 2, "Ext Admin Group: %s\n", + admin_group_string( + admin_group_buf, ADMIN_GROUP_PRINT_MAX_SIZE, + indent + 2 + strlen("Ext Admin Group: "), + &asla->ext_admin_group)); + admin_group_print(admin_group_buf, + indent + 2 + strlen("Ext Admin Group: "), + &asla->ext_admin_group); + if (admin_group_buf[0] != '\0' && + (buf->pos + strlen(admin_group_buf) + + SBUF_DEFAULT_SIZE / 2) < buf->size) + sbuf_push(buf, indent + 4, "Bit positions: %s\n", + admin_group_buf); + } + if (IS_SUBTLV(asla, EXT_MAX_BW)) + sbuf_push(buf, indent + 2, + "Maximum Bandwidth: %g (Bytes/sec)\n", asla->max_bw); + if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) + sbuf_push(buf, indent + 2, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + asla->max_rsv_bw); + if (IS_SUBTLV(asla, EXT_UNRSV_BW)) { + sbuf_push(buf, indent + 2, "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, asla->unrsv_bw[j], j + 1, + asla->unrsv_bw[j + 1]); + } + } + if (IS_SUBTLV(asla, EXT_TE_METRIC)) + sbuf_push(buf, indent + 2, "Traffic Engineering Metric: %u\n", + asla->te_metric); + /* Extended metrics */ + if (IS_SUBTLV(asla, EXT_DELAY)) + sbuf_push(buf, indent + 2, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(asla->delay) ? "Anomalous" : "Normal", + asla->delay); + if (IS_SUBTLV(asla, EXT_MM_DELAY)) { + sbuf_push(buf, indent + 2, + "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(asla->min_delay) ? "Anomalous" : "Normal", + asla->min_delay & TE_EXT_MASK, + asla->max_delay & TE_EXT_MASK); + } + if (IS_SUBTLV(asla, EXT_DELAY_VAR)) { + sbuf_push(buf, indent + 2, "Delay Variation: %u (micro-sec)\n", + asla->delay_var & TE_EXT_MASK); + } + if (IS_SUBTLV(asla, EXT_PKT_LOSS)) + sbuf_push(buf, indent + 2, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(asla->pkt_loss) ? "Anomalous" : "Normal", + (float)((asla->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + if (IS_SUBTLV(asla, EXT_RES_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", + asla->res_bw); + if (IS_SUBTLV(asla, EXT_AVA_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Available Bandwidth: %g (Bytes/sec)\n", + asla->ava_bw); + if (IS_SUBTLV(asla, EXT_USE_BW)) + sbuf_push(buf, indent + 2, + "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", + asla->use_bw); +} + /* 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, struct json_object *json, @@ -236,6 +373,8 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; char aux_buf[255]; char cnt_buf[255]; + struct isis_asla_subtlvs *asla; + struct listnode *node; /* Standard metrics */ if (IS_SUBTLV(exts, EXT_ADM_GRP)) { @@ -244,7 +383,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, exts->adm_group); json_object_string_add(json, "adm-group", aux_buf); } else { - sbuf_push(buf, indent, "Administrative Group: 0x%x\n", + sbuf_push(buf, indent, "Admin Group: 0x%08x\n", exts->adm_group); sbuf_push(buf, indent + 2, "Bit positions: %s\n", admin_group_standard_print( @@ -661,7 +800,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, 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", + " Neighbor-ID: %pSY\n", lan->sid, lan->weight, lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1' @@ -681,9 +820,11 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG ? '1' : '0', - isis_format_id(lan->neighbor_id, 6)); + lan->neighbor_id); } } + for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) + format_item_asla_subtlvs(asla, buf, indent); } static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) @@ -691,10 +832,124 @@ static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) isis_del_ext_subtlvs(exts); } +static int pack_item_ext_subtlv_asla(struct isis_asla_subtlvs *asla, + struct stream *s, size_t *min_len) +{ + size_t subtlv_len; + size_t subtlv_len_pos; + + /* Sub TLV header */ + stream_putc(s, ISIS_SUBTLV_ASLA); + + subtlv_len_pos = stream_get_endp(s); + stream_putc(s, 0); /* length will be filled later */ + + /* SABM Flag/Length */ + if (asla->legacy) + stream_putc(s, ASLA_LEGACY_FLAG | asla->standard_apps_length); + else + stream_putc(s, asla->standard_apps_length); + stream_putc(s, asla->user_def_apps_length); /* UDABM Flag/Length */ + stream_putc(s, asla->standard_apps); + stream_putc(s, asla->user_def_apps); + + /* Administrative Group */ + if (IS_SUBTLV(asla, EXT_ADM_GRP)) { + stream_putc(s, ISIS_SUBTLV_ADMIN_GRP); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->admin_group); + } + + /* Extended Administrative Group */ + if (IS_SUBTLV(asla, EXT_EXTEND_ADM_GRP) && + admin_group_nb_words(&asla->ext_admin_group) != 0) { + size_t ag_length; + size_t ag_length_pos; + struct admin_group *ag; + + stream_putc(s, ISIS_SUBTLV_EXT_ADMIN_GRP); + ag_length_pos = stream_get_endp(s); + stream_putc(s, 0); /* length will be filled later*/ + + ag = &asla->ext_admin_group; + for (size_t i = 0; i < admin_group_nb_words(ag); i++) + stream_putl(s, ag->bitmap.data[i]); + + ag_length = stream_get_endp(s) - ag_length_pos - 1; + stream_putc_at(s, ag_length_pos, ag_length); + } + + if (IS_SUBTLV(asla, EXT_MAX_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->max_bw); + } + if (IS_SUBTLV(asla, EXT_MAX_RSV_BW)) { + stream_putc(s, ISIS_SUBTLV_MAX_RSV_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->max_rsv_bw); + } + if (IS_SUBTLV(asla, 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, asla->unrsv_bw[j]); + } + if (IS_SUBTLV(asla, EXT_TE_METRIC)) { + stream_putc(s, ISIS_SUBTLV_TE_METRIC); + stream_putc(s, ISIS_SUBTLV_TE_METRIC_SIZE); + stream_put3(s, asla->te_metric); + } + if (IS_SUBTLV(asla, EXT_DELAY)) { + stream_putc(s, ISIS_SUBTLV_AV_DELAY); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->delay); + } + if (IS_SUBTLV(asla, EXT_MM_DELAY)) { + stream_putc(s, ISIS_SUBTLV_MM_DELAY); + stream_putc(s, ISIS_SUBTLV_MM_DELAY_SIZE); + stream_putl(s, asla->min_delay); + stream_putl(s, asla->max_delay); + } + if (IS_SUBTLV(asla, EXT_DELAY_VAR)) { + stream_putc(s, ISIS_SUBTLV_DELAY_VAR); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->delay_var); + } + if (IS_SUBTLV(asla, EXT_PKT_LOSS)) { + stream_putc(s, ISIS_SUBTLV_PKT_LOSS); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putl(s, asla->pkt_loss); + } + if (IS_SUBTLV(asla, EXT_RES_BW)) { + stream_putc(s, ISIS_SUBTLV_RES_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->res_bw); + } + if (IS_SUBTLV(asla, EXT_AVA_BW)) { + stream_putc(s, ISIS_SUBTLV_AVA_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->ava_bw); + } + if (IS_SUBTLV(asla, EXT_USE_BW)) { + stream_putc(s, ISIS_SUBTLV_USE_BW); + stream_putc(s, ISIS_SUBTLV_DEF_SIZE); + stream_putf(s, asla->use_bw); + } + + subtlv_len = stream_get_endp(s) - subtlv_len_pos - 1; + stream_putc_at(s, subtlv_len_pos, subtlv_len); + + return 0; +} + static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, struct stream *s, size_t *min_len) { + struct isis_asla_subtlvs *asla; + struct listnode *node; uint8_t size; + int ret; if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) { *min_len = ISIS_SUBTLV_MAX_SIZE; @@ -858,6 +1113,227 @@ static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, } } + for (ALL_LIST_ELEMENTS_RO(exts->aslas, node, asla)) { + ret = pack_item_ext_subtlv_asla(asla, s, min_len); + if (ret < 0) + return ret; + } + + return 0; +} + +static int unpack_item_ext_subtlv_asla(uint16_t mtid, uint8_t subtlv_len, + struct stream *s, struct sbuf *log, + int indent, + struct isis_ext_subtlvs *exts) +{ + /* Standard App Identifier Bit Flags/Length */ + uint8_t sabm_flag_len; + /* User-defined App Identifier Bit Flags/Length */ + uint8_t uabm_flag_len; + uint8_t sabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0}; + uint8_t uabm[ASLA_APP_IDENTIFIER_BIT_LENGTH] = {0}; + uint8_t readable = subtlv_len; + uint8_t subsubtlv_type; + uint8_t subsubtlv_len; + size_t nb_groups; + struct isis_asla_subtlvs *asla; + + if (subtlv_len < ISIS_SUBSUBTLV_HDR_SIZE) { + TLV_SIZE_MISMATCH(log, indent, "ASLA"); + return -1; + } + + + asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*asla)); + + admin_group_init(&asla->ext_admin_group); + + + sabm_flag_len = stream_getc(s); + uabm_flag_len = stream_getc(s); + asla->legacy = CHECK_FLAG(sabm_flag_len, ASLA_LEGACY_FLAG); + asla->standard_apps_length = ASLA_APPS_LENGTH_MASK & sabm_flag_len; + asla->user_def_apps_length = ASLA_APPS_LENGTH_MASK & uabm_flag_len; + + readable -= ISIS_SUBSUBTLV_HDR_SIZE; + if (readable < + asla->standard_apps_length + asla->user_def_apps_length) { + TLV_SIZE_MISMATCH(log, indent, "ASLA"); + return -1; + } + + for (int i = 0; i < asla->standard_apps_length; i++) + sabm[i] = stream_getc(s); + for (int i = 0; i < asla->user_def_apps_length; i++) + uabm[i] = stream_getc(s); + + readable -= (asla->standard_apps_length + asla->user_def_apps_length); + + asla->standard_apps = sabm[0]; + asla->user_def_apps = uabm[0]; + + while (readable > 0) { + if (readable < ISIS_SUBSUBTLV_HDR_SIZE) { + TLV_SIZE_MISMATCH(log, indent, "ASLA Sub TLV"); + return -1; + } + + subsubtlv_type = stream_getc(s); + subsubtlv_len = stream_getc(s); + readable -= ISIS_SUBSUBTLV_HDR_SIZE; + + + switch (subsubtlv_type) { + case ISIS_SUBTLV_ADMIN_GRP: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "ASLA Adm Group"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->admin_group = stream_getl(s); + SET_SUBTLV(asla, EXT_ADM_GRP); + } + break; + + case ISIS_SUBTLV_EXT_ADMIN_GRP: + nb_groups = subsubtlv_len / sizeof(uint32_t); + for (size_t i = 0; i < nb_groups; i++) { + uint32_t val = stream_getl(s); + + admin_group_bulk_set(&asla->ext_admin_group, + val, i); + } + SET_SUBTLV(asla, EXT_EXTEND_ADM_GRP); + break; + case ISIS_SUBTLV_MAX_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Maximum Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->max_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_MAX_BW); + } + break; + case ISIS_SUBTLV_MAX_RSV_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Maximum Reservable Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->max_rsv_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_MAX_RSV_BW); + } + break; + case ISIS_SUBTLV_UNRSV_BW: + if (subsubtlv_len != ISIS_SUBTLV_UNRSV_BW_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Unreserved Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + for (int i = 0; i < MAX_CLASS_TYPE; i++) + asla->unrsv_bw[i] = stream_getf(s); + SET_SUBTLV(asla, EXT_UNRSV_BW); + } + break; + case ISIS_SUBTLV_TE_METRIC: + if (subsubtlv_len != ISIS_SUBTLV_TE_METRIC_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Traffic Engineering Metric"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->te_metric = stream_get3(s); + SET_SUBTLV(asla, EXT_TE_METRIC); + } + break; + /* Extended Metrics as defined in RFC 7810 */ + case ISIS_SUBTLV_AV_DELAY: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Average Link Delay"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->delay = stream_getl(s); + SET_SUBTLV(asla, EXT_DELAY); + } + break; + case ISIS_SUBTLV_MM_DELAY: + if (subsubtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Min/Max Link Delay"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->min_delay = stream_getl(s); + asla->max_delay = stream_getl(s); + SET_SUBTLV(asla, EXT_MM_DELAY); + } + break; + case ISIS_SUBTLV_DELAY_VAR: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Delay Variation"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->delay_var = stream_getl(s); + SET_SUBTLV(asla, EXT_DELAY_VAR); + } + break; + case ISIS_SUBTLV_PKT_LOSS: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH(log, indent, + "Link Packet Loss"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->pkt_loss = stream_getl(s); + SET_SUBTLV(asla, EXT_PKT_LOSS); + } + break; + case ISIS_SUBTLV_RES_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Residual Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->res_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_RES_BW); + } + break; + case ISIS_SUBTLV_AVA_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Available Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->ava_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_AVA_BW); + } + break; + case ISIS_SUBTLV_USE_BW: + if (subsubtlv_len != ISIS_SUBTLV_DEF_SIZE) { + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Utilized Bandwidth"); + stream_forward_getp(s, subsubtlv_len); + } else { + asla->use_bw = stream_getf(s); + SET_SUBTLV(asla, EXT_USE_BW); + } + break; + default: + zlog_debug("unknown (t,l)=(%u,%u)", subsubtlv_type, + subsubtlv_len); + stream_forward_getp(s, subsubtlv_len); + break; + } + readable -= subsubtlv_len; + } + + listnode_add(exts->aslas, asla); + return 0; } @@ -896,8 +1372,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, /* 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"); + TLV_SIZE_MISMATCH(log, indent, + "Administrative Group"); stream_forward_getp(s, subtlv_len); } else { exts->adm_group = stream_getl(s); @@ -915,8 +1391,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, "Link ID"); stream_forward_getp(s, subtlv_len); } else { exts->local_llri = stream_getl(s); @@ -926,8 +1401,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Local IP address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->local_addr.s_addr, s, 4); @@ -936,8 +1411,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Remote IP address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->neigh_addr.s_addr, s, 4); @@ -946,8 +1421,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Local IPv6 address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->local_addr6, s, 16); @@ -956,8 +1431,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Remote IPv6 address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->neigh_addr6, s, 16); @@ -966,8 +1441,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Maximum Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->max_bw = stream_getf(s); @@ -976,8 +1451,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH( + log, indent, + "Maximum Reservable Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->max_rsv_bw = stream_getf(s); @@ -986,8 +1462,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Unreserved Bandwidth"); stream_forward_getp(s, subtlv_len); } else { for (int i = 0; i < MAX_CLASS_TYPE; i++) @@ -997,8 +1473,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Traffic Engineering Metric"); stream_forward_getp(s, subtlv_len); } else { exts->te_metric = stream_get3(s); @@ -1007,8 +1483,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Remote AS number"); stream_forward_getp(s, subtlv_len); } else { exts->remote_as = stream_getl(s); @@ -1017,8 +1493,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Remote ASBR IP Address"); stream_forward_getp(s, subtlv_len); } else { stream_get(&exts->remote_ip.s_addr, s, 4); @@ -1028,8 +1504,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, /* 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"); + TLV_SIZE_MISMATCH(log, indent, + "Average Link Delay"); stream_forward_getp(s, subtlv_len); } else { exts->delay = stream_getl(s); @@ -1038,8 +1514,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, break; case ISIS_SUBTLV_MM_DELAY: if (subtlv_len != ISIS_SUBTLV_MM_DELAY_SIZE) { - sbuf_push(log, indent, - "TLV size does not match expected size for Min/Max Link Delay!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Min/Max Link Delay"); stream_forward_getp(s, subtlv_len); } else { exts->min_delay = stream_getl(s); @@ -1049,8 +1525,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Delay Variation"); stream_forward_getp(s, subtlv_len); } else { exts->delay_var = stream_getl(s); @@ -1059,8 +1535,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "Link Packet Loss"); stream_forward_getp(s, subtlv_len); } else { exts->pkt_loss = stream_getl(s); @@ -1069,8 +1545,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Residual Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->res_bw = stream_getf(s); @@ -1079,8 +1556,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Available Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->ava_bw = stream_getf(s); @@ -1089,8 +1567,9 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH( + log, indent, + "Unidirectional Utilized Bandwidth"); stream_forward_getp(s, subtlv_len); } else { exts->use_bw = stream_getf(s); @@ -1101,8 +1580,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, "Adjacency SID"); stream_forward_getp(s, subtlv_len); } else { struct isis_adj_sid *adj; @@ -1113,9 +1591,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, adj->weight = stream_getc(s); if (adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE) { - sbuf_push( - log, indent, - "TLV size does not match expected size for Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Adjacency SID"); stream_forward_getp(s, subtlv_len - 2); XFREE(MTYPE_ISIS_SUBTLV, adj); break; @@ -1125,9 +1602,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, && subtlv_len != ISIS_SUBTLV_ADJ_SID_SIZE + 1) { - sbuf_push( - log, indent, - "TLV size does not match expected size for Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "Adjacency SID"); stream_forward_getp(s, subtlv_len - 2); XFREE(MTYPE_ISIS_SUBTLV, adj); break; @@ -1152,8 +1628,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, 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"); + TLV_SIZE_MISMATCH(log, indent, + "LAN-Adjacency SID"); stream_forward_getp(s, subtlv_len); } else { struct isis_lan_adj_sid *lan; @@ -1168,9 +1644,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, if (lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG && subtlv_len != ISIS_SUBTLV_LAN_ADJ_SID_SIZE) { - sbuf_push( - log, indent, - "TLV size does not match expected size for LAN-Adjacency SID!\n"); + TLV_SIZE_MISMATCH(log, indent, + "LAN-Adjacency SID"); stream_forward_getp( s, subtlv_len - 2 - ISIS_SYS_ID_LEN); @@ -1182,9 +1657,8 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, && 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"); + TLV_SIZE_MISMATCH(log, indent, + "LAN-Adjacency SID"); stream_forward_getp( s, subtlv_len - 2 - ISIS_SYS_ID_LEN); @@ -1207,6 +1681,13 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, SET_SUBTLV(exts, EXT_LAN_ADJ_SID); } break; + case ISIS_SUBTLV_ASLA: + if (unpack_item_ext_subtlv_asla(mtid, subtlv_len, s, + log, indent, + exts) < 0) { + sbuf_push(log, indent, "TLV parse error"); + } + break; default: /* Skip unknown TLV */ stream_forward_getp(s, subtlv_len); @@ -1590,14 +2071,14 @@ static void format_item_area_address(uint16_t mtid, struct isis_item *i, int indent) { struct isis_area_address *addr = (struct isis_area_address *)i; + struct iso_address iso_addr; - if (json) { - json_object_string_add(json, "area-addr", - isonet_print(addr->addr, addr->len)); - } else { - sbuf_push(buf, indent, "Area Address: %s\n", - isonet_print(addr->addr, addr->len)); - } + memcpy(iso_addr.area_addr, addr->addr, ISO_ADDR_SIZE); + iso_addr.addr_len = addr->len; + if (json) + json_object_string_addf(json, "area-addr", "%pIS", &iso_addr); + else + sbuf_push(buf, indent, "Area Address: %pIS\n", &iso_addr); } static void free_item_area_address(struct isis_item *i) @@ -1678,17 +2159,18 @@ static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i, struct json_object *json, int indent) { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; + char sys_id[ISO_SYSID_STRLEN]; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id); if (json) { struct json_object *old_json; old_json = json_object_new_object(); json_object_object_add(json, "old-reach-style", old_json); - json_object_string_add(old_json, "is-reach", - isis_format_id(r->id, 7)); + json_object_string_add(old_json, "is-reach", sys_id); json_object_int_add(old_json, "metric", r->metric); } else sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n", - isis_format_id(r->id, 7), r->metric); + sys_id, r->metric); } static void free_item_oldstyle_reach(struct isis_item *i) @@ -1760,13 +2242,13 @@ static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i, int indent) { struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; + char sys_id[ISO_SYSID_STRLEN]; - if (json) { - json_object_string_add(json, "lan-neighbor", - isis_format_id(n->mac, 6)); - } else - sbuf_push(buf, indent, "LAN Neighbor: %s\n", - isis_format_id(n->mac, 6)); + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", n->mac); + if (json) + json_object_string_add(json, "lan-neighbor", sys_id); + else + sbuf_push(buf, indent, "LAN Neighbor: %s\n", sys_id); } static void free_item_lan_neighbor(struct isis_item *i) @@ -1831,23 +2313,25 @@ static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i, int indent) { struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + char sys_id[ISO_SYSID_STRLEN]; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pLS", e->id); if (json) { char buf[255]; struct json_object *lsp_json; lsp_json = json_object_new_object(); json_object_object_add(json, "lsp-entry", lsp_json); - json_object_string_add(lsp_json, "id", isis_format_id(e->id, 8)); + json_object_string_add(lsp_json, "id", sys_id); snprintfrr(buf,sizeof(buf),"0x%08x",e->seqno); json_object_string_add(lsp_json, "seq", buf); snprintfrr(buf,sizeof(buf),"0x%04hx",e->checksum); json_object_string_add(lsp_json, "chksum", buf); json_object_int_add(lsp_json, "lifetime", e->checksum); } else - sbuf_push(buf, indent, - "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); + sbuf_push( + buf, indent, + "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n", + sys_id, e->seqno, e->checksum, e->rem_lifetime); } static void free_item_lsp_entry(struct isis_item *i) @@ -1919,7 +2403,9 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, struct json_object *json, int indent) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; + char sys_id[ISO_SYSID_STRLEN]; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pPN", r->id); if (json) { struct json_object *reach_json; reach_json = json_object_new_object(); @@ -1927,8 +2413,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, json_object_string_add( reach_json, "mt-id", (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT"); - json_object_string_add(reach_json, "id", - isis_format_id(r->id, 7)); + json_object_string_add(reach_json, "id", sys_id); json_object_int_add(reach_json, "metric", r->metric); if (mtid != ISIS_MT_IPV4_UNICAST) json_object_string_add(reach_json, "mt-name", @@ -1940,7 +2425,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, } else { sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)", (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", - isis_format_id(r->id, 7), r->metric); + sys_id, r->metric); if (mtid != ISIS_MT_IPV4_UNICAST) sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); sbuf_push(buf, 0, "\n"); @@ -3125,9 +3610,12 @@ static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, struct sbuf *buf, struct json_object *json, int indent) { + char sys_id[ISO_SYSID_STRLEN]; + if (!threeway_adj) return; + snprintfrr(sys_id, ISO_SYSID_STRLEN, "%pSY", threeway_adj->neighbor_id); if (json) { struct json_object *three_json; three_json = json_object_new_object(); @@ -3140,9 +3628,7 @@ format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, threeway_adj->local_circuit_id); if (!threeway_adj->neighbor_set) return; - json_object_string_add( - three_json, "neigh-system-id", - isis_format_id(threeway_adj->neighbor_id, 6)); + json_object_string_add(three_json, "neigh-system-id", sys_id); json_object_int_add(three_json, "neigh-ext-circuit-id", threeway_adj->neighbor_circuit_id); } else { @@ -3155,8 +3641,7 @@ format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, 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 System ID: %s\n", sys_id); sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", threeway_adj->neighbor_circuit_id); } @@ -3437,6 +3922,39 @@ static struct isis_router_cap *copy_tlv_router_cap( memcpy(rv, router_cap, sizeof(*rv)); +#ifndef FABRICD + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_router_cap_fad *sc_fad; + struct isis_router_cap_fad *rv_fad; + + sc_fad = router_cap->fads[i]; + if (!sc_fad) + continue; + rv_fad = XMALLOC(MTYPE_ISIS_TLV, + sizeof(struct isis_router_cap_fad)); + *rv_fad = *sc_fad; + rv_fad->fad.admin_group_exclude_any.bitmap.data = NULL; + rv_fad->fad.admin_group_include_any.bitmap.data = NULL; + rv_fad->fad.admin_group_include_all.bitmap.data = NULL; + + assert(bf_is_inited( + sc_fad->fad.admin_group_exclude_any.bitmap)); + assert(bf_is_inited( + sc_fad->fad.admin_group_include_any.bitmap)); + assert(bf_is_inited( + sc_fad->fad.admin_group_include_all.bitmap)); + + admin_group_copy(&rv_fad->fad.admin_group_exclude_any, + &sc_fad->fad.admin_group_exclude_any); + admin_group_copy(&rv_fad->fad.admin_group_include_any, + &sc_fad->fad.admin_group_include_any); + admin_group_copy(&rv_fad->fad.admin_group_include_all, + &sc_fad->fad.admin_group_include_all); + + rv->fads[i] = rv_fad; + } +#endif /* ifndef FABRICD */ + return rv; } @@ -3548,46 +4066,186 @@ static void format_tlv_router_cap(const struct isis_router_cap *router_cap, 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"); + sr_algorithm_string( + router_cap->algo[i])); } /* 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); + +#ifndef FABRICD + /* Flex-Algo */ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + char admin_group_buf[ADMIN_GROUP_PRINT_MAX_SIZE]; + int indent2; + struct admin_group *admin_group; + struct isis_router_cap_fad *fad; + + fad = router_cap->fads[i]; + if (!fad) + continue; + + sbuf_push(buf, indent, " Flex-Algo Definition: %d\n", + fad->fad.algorithm); + sbuf_push(buf, indent, " Metric-Type: %d\n", + fad->fad.metric_type); + sbuf_push(buf, indent, " Calc-Type: %d\n", + fad->fad.calc_type); + sbuf_push(buf, indent, " Priority: %d\n", fad->fad.priority); + + indent2 = indent + strlen(" Exclude-Any: "); + admin_group = &fad->fad.admin_group_exclude_any; + sbuf_push(buf, indent, " Exclude-Any: "); + sbuf_push(buf, 0, "%s\n", + admin_group_string(admin_group_buf, + ADMIN_GROUP_PRINT_MAX_SIZE, + indent2, admin_group)); + + indent2 = indent + strlen(" Include-Any: "); + admin_group = &fad->fad.admin_group_include_any; + sbuf_push(buf, indent, " Include-Any: "); + sbuf_push(buf, 0, "%s\n", + admin_group_string(admin_group_buf, + ADMIN_GROUP_PRINT_MAX_SIZE, + indent2, admin_group)); + + indent2 = indent + strlen(" Include-All: "); + admin_group = &fad->fad.admin_group_include_all; + sbuf_push(buf, indent, " Include-All: "); + sbuf_push(buf, 0, "%s\n", + admin_group_string(admin_group_buf, + ADMIN_GROUP_PRINT_MAX_SIZE, + indent2, admin_group)); + + sbuf_push(buf, indent, " M-Flag: %c\n", + CHECK_FLAG(fad->fad.flags, FAD_FLAG_M) ? '1' : '0'); + + if (fad->fad.flags != 0 && fad->fad.flags != FAD_FLAG_M) + sbuf_push(buf, indent, " Flags: 0x%x\n", + fad->fad.flags); + if (fad->fad.exclude_srlg) + sbuf_push(buf, indent, " Exclude SRLG: Enabled\n"); + if (fad->fad.unsupported_subtlv) + sbuf_push(buf, indent, + " Got an unsupported sub-TLV: Yes\n"); + } +#endif /* ifndef FABRICD */ } static void free_tlv_router_cap(struct isis_router_cap *router_cap) { + if (!router_cap) + return; + +#ifndef FABRICD + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_router_cap_fad *fad; + + fad = router_cap->fads[i]; + if (!fad) + continue; + admin_group_term(&fad->fad.admin_group_exclude_any); + admin_group_term(&fad->fad.admin_group_include_any); + admin_group_term(&fad->fad.admin_group_include_all); + XFREE(MTYPE_ISIS_TLV, fad); + } +#endif /* ifndef FABRICD */ + XFREE(MTYPE_ISIS_TLV, router_cap); } +#ifndef FABRICD +static size_t +isis_router_cap_fad_sub_tlv_len(const struct isis_router_cap_fad *fad) +{ + size_t sz = ISIS_SUBTLV_FAD_MIN_SIZE; + uint32_t admin_group_length; + + admin_group_length = + admin_group_nb_words(&fad->fad.admin_group_exclude_any); + if (admin_group_length) + sz += sizeof(uint32_t) * admin_group_length + 2; + + admin_group_length = + admin_group_nb_words(&fad->fad.admin_group_include_any); + if (admin_group_length) + sz += sizeof(uint32_t) * admin_group_length + 2; + + admin_group_length = + admin_group_nb_words(&fad->fad.admin_group_include_all); + if (admin_group_length) + sz += sizeof(uint32_t) * admin_group_length + 2; + + if (fad->fad.flags != 0) + sz += ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE + 2; + + /* TODO: add exclude SRLG sub-sub-TLV length when supported */ + + return sz; +} +#endif /* ifndef FABRICD */ + +static size_t isis_router_cap_tlv_size(const struct isis_router_cap *router_cap) +{ + size_t sz = 2 + ISIS_ROUTER_CAP_SIZE; +#ifndef FABRICD + size_t fad_sz; +#endif /* ifndef FABRICD */ + int nb_algo; + + if ((router_cap->srgb.range_size != 0) && + (router_cap->srgb.lower_bound != 0)) { + sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE; + sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE; + + nb_algo = isis_tlvs_sr_algo_count(router_cap); + if (nb_algo != 0) + sz += 2 + nb_algo; + + if ((router_cap->srlb.range_size != 0) && + (router_cap->srlb.lower_bound != 0)) { + sz += 2 + ISIS_SUBTLV_SID_LABEL_RANGE_SIZE; + sz += 2 + ISIS_SUBTLV_SID_LABEL_SIZE; + } + + if (router_cap->msd != 0) + sz += 2 + ISIS_SUBTLV_NODE_MSD_SIZE; + } + +#ifndef FABRICD + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + if (!router_cap->fads[i]) + continue; + fad_sz = 2 + + isis_router_cap_fad_sub_tlv_len(router_cap->fads[i]); + if (((sz + fad_sz) % 256) < (sz % 256)) + sz += 2 + ISIS_ROUTER_CAP_SIZE + fad_sz; + else + sz += fad_sz; + } +#endif /* ifndef FABRICD */ + + return sz; +} + 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; + size_t tlv_len, 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)) + if (STREAM_WRITEABLE(s) < isis_router_cap_tlv_size(router_cap)) 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_putc(s, 0); /* Real length will be adjusted later */ stream_put_ipv4(s, router_cap->router_id.s_addr); stream_putc(s, router_cap->flags); @@ -3603,14 +4261,13 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, 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; + nb_algo = isis_tlvs_sr_algo_count(router_cap); 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]); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (router_cap->algo[i] != SR_ALGORITHM_UNSET) + stream_putc(s, router_cap->algo[i]); } /* Local Block if defined as per RFC8667 section #3.3 */ @@ -3635,6 +4292,80 @@ static int pack_tlv_router_cap(const struct isis_router_cap *router_cap, } } +#ifndef FABRICD + /* Flex Algo Definitions */ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_router_cap_fad *fad; + size_t subtlv_len; + struct admin_group *ag; + uint32_t admin_group_length; + + fad = router_cap->fads[i]; + if (!fad) + continue; + + subtlv_len = isis_router_cap_fad_sub_tlv_len(fad); + + if ((stream_get_endp(s) - len_pos - 1) > 250) { + /* 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); + + /* 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, 0); + stream_put_ipv4(s, router_cap->router_id.s_addr); + stream_putc(s, router_cap->flags); + } + + stream_putc(s, ISIS_SUBTLV_FAD); + stream_putc(s, subtlv_len); /* length will be filled later */ + + stream_putc(s, fad->fad.algorithm); + stream_putc(s, fad->fad.metric_type); + stream_putc(s, fad->fad.calc_type); + stream_putc(s, fad->fad.priority); + + ag = &fad->fad.admin_group_exclude_any; + admin_group_length = admin_group_nb_words(ag); + if (admin_group_length) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG); + stream_putc(s, sizeof(uint32_t) * admin_group_length); + for (size_t i = 0; i < admin_group_length; i++) + stream_putl(s, admin_group_get_offset(ag, i)); + } + + ag = &fad->fad.admin_group_include_any; + admin_group_length = admin_group_nb_words(ag); + if (admin_group_length) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG); + stream_putc(s, sizeof(uint32_t) * admin_group_length); + for (size_t i = 0; i < admin_group_length; i++) + stream_putl(s, admin_group_get_offset(ag, i)); + } + + ag = &fad->fad.admin_group_include_all; + admin_group_length = admin_group_nb_words(ag); + if (admin_group_length) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG); + stream_putc(s, sizeof(uint32_t) * admin_group_length); + for (size_t i = 0; i < admin_group_length; i++) + stream_putl(s, admin_group_get_offset(ag, i)); + } + + if (fad->fad.flags != 0) { + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS); + stream_putc(s, ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE); + stream_putc(s, fad->fad.flags); + } + } +#endif /* ifndef FABRICD */ + /* 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); @@ -3661,18 +4392,16 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, 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; + if (tlvs->router_cap) + /* Multiple Router Capability found */ + rcap = tlvs->router_cap; + else { + /* 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; } - /* 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); @@ -3680,6 +4409,10 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, /* Parse remaining part of the TLV if present */ subtlv_len = tlv_len - ISIS_ROUTER_CAP_SIZE; while (subtlv_len > 2) { +#ifndef FABRICD + struct isis_router_cap_fad *fad; + uint8_t subsubtlvs_len; +#endif /* ifndef FABRICD */ uint8_t msd_type; type = stream_getc(s); @@ -3752,14 +4485,16 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, case ISIS_SUBTLV_ALGORITHM: if (length == 0) break; - /* 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); + + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + rcap->algo[i] = SR_ALGORITHM_UNSET; + + for (int i = 0; i < length; i++) { + uint8_t algo; + + algo = stream_getc(s); + rcap->algo[algo] = algo; + } break; case ISIS_SUBTLV_SRLB: /* Check that SRLB is correctly formated */ @@ -3831,6 +4566,80 @@ static int unpack_tlv_router_cap(enum isis_tlv_context context, if (length > MSD_TLV_SIZE) stream_forward_getp(s, length - MSD_TLV_SIZE); break; +#ifndef FABRICD + case ISIS_SUBTLV_FAD: + fad = XCALLOC(MTYPE_ISIS_TLV, + sizeof(struct isis_router_cap_fad)); + fad->fad.algorithm = stream_getc(s); + fad->fad.metric_type = stream_getc(s); + fad->fad.calc_type = stream_getc(s); + fad->fad.priority = stream_getc(s); + rcap->fads[fad->fad.algorithm] = fad; + admin_group_init(&fad->fad.admin_group_exclude_any); + admin_group_init(&fad->fad.admin_group_include_any); + admin_group_init(&fad->fad.admin_group_include_all); + + subsubtlvs_len = length - 4; + while (subsubtlvs_len > 2) { + struct admin_group *ag; + uint8_t subsubtlv_type; + uint8_t subsubtlv_len; + uint32_t v; + int n_ag, i; + + subsubtlv_type = stream_getc(s); + subsubtlv_len = stream_getc(s); + + switch (subsubtlv_type) { + case ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG: + ag = &fad->fad.admin_group_exclude_any; + n_ag = subsubtlv_len / sizeof(uint32_t); + for (i = 0; i < n_ag; i++) { + v = stream_getl(s); + admin_group_bulk_set(ag, v, i); + } + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG: + ag = &fad->fad.admin_group_include_any; + n_ag = subsubtlv_len / sizeof(uint32_t); + for (i = 0; i < n_ag; i++) { + v = stream_getl(s); + admin_group_bulk_set(ag, v, i); + } + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG: + ag = &fad->fad.admin_group_include_all; + n_ag = subsubtlv_len / sizeof(uint32_t); + for (i = 0; i < n_ag; i++) { + v = stream_getl(s); + admin_group_bulk_set(ag, v, i); + } + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS: + if (subsubtlv_len == 0) + break; + + fad->fad.flags = stream_getc(s); + for (i = subsubtlv_len - 1; i > 0; --i) + stream_getc(s); + break; + case ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG: + fad->fad.exclude_srlg = true; + stream_forward_getp(s, subsubtlv_len); + break; + default: + sbuf_push( + log, indent, + "Received an unsupported Flex-Algo sub-TLV type %u\n", + subsubtlv_type); + fad->fad.unsupported_subtlv = true; + stream_forward_getp(s, subsubtlv_len); + break; + } + subsubtlvs_len -= 2 + subsubtlv_len; + } + break; +#endif /* ifndef FABRICD */ default: stream_forward_getp(s, length); break; @@ -3988,33 +4797,29 @@ static void format_tlv_purge_originator(struct isis_purge_originator *poi, struct sbuf *buf, struct json_object *json, int indent) { + char sen_id[ISO_SYSID_STRLEN]; + char gen_id[ISO_SYSID_STRLEN]; + if (!poi) return; + snprintfrr(gen_id, ISO_SYSID_STRLEN, "%pSY", poi->generator); + if (poi->sender_set) + snprintfrr(sen_id, ISO_SYSID_STRLEN, "%pSY", poi->sender); + if (json) { struct json_object *purge_json; purge_json = json_object_new_object(); json_object_object_add(json, "purge_originator", purge_json); - json_object_string_add( - purge_json, "id", - isis_format_id(poi->generator, sizeof(poi->generator))); - if (poi->sender_set) { - json_object_string_add( - purge_json, "rec-from", - isis_format_id(poi->sender, - sizeof(poi->sender))); - } + json_object_string_add(purge_json, "id", gen_id); + if (poi->sender_set) + json_object_string_add(purge_json, "rec-from", sen_id); } else { sbuf_push(buf, indent, "Purge Originator Identification:\n"); - sbuf_push( - buf, indent, " Generator: %s\n", - isis_format_id(poi->generator, sizeof(poi->generator))); - if (poi->sender_set) { - sbuf_push(buf, indent, " Received-From: %s\n", - isis_format_id(poi->sender, - sizeof(poi->sender))); - } + sbuf_push(buf, indent, " Generator: %s\n", gen_id); + if (poi->sender_set) + sbuf_push(buf, indent, " Received-From: %s\n", sen_id); } } @@ -5271,14 +6076,14 @@ void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs, struct list *addresses) { struct listnode *node; - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(addresses, node, area_addr)) { struct isis_area_address *a = XCALLOC(MTYPE_ISIS_TLV, sizeof(*a)); a->len = area_addr->addr_len; - memcpy(a->addr, area_addr->area_addr, 20); + memcpy(a->addr, area_addr->area_addr, ISO_ADDR_SIZE); append_item(&tlvs->area_addresses, (struct isis_item *)a); } } @@ -5475,7 +6280,7 @@ bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs, for (struct isis_area_address *addr = addr_head; addr; addr = addr->next) { struct listnode *node; - struct area_addr *a; + struct iso_address *a; for (ALL_LIST_ELEMENTS_RO(addresses, node, a)) { if (a->addr_len == addr->len @@ -5782,16 +6587,61 @@ 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) +/* Init Router Capability TLV parameters */ +struct isis_router_cap *isis_tlvs_init_router_capability(struct isis_tlvs *tlvs) { - 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; + + /* init SR algo list content to the default value */ + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + tlvs->router_cap->algo[i] = SR_ALGORITHM_UNSET; + + return tlvs->router_cap; +} + +#ifndef FABRICD +void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs, + struct flex_algo *fa, int algorithm, + uint8_t *sysid) +{ + struct isis_router_cap_fad *rcap_fad; + + assert(tlvs->router_cap); + + rcap_fad = tlvs->router_cap->fads[algorithm]; + + if (!rcap_fad) + rcap_fad = XCALLOC(MTYPE_ISIS_TLV, + sizeof(struct isis_router_cap_fad)); + + memset(rcap_fad->sysid, 0, ISIS_SYS_ID_LEN + 2); + memcpy(rcap_fad->sysid, sysid, ISIS_SYS_ID_LEN); + + memcpy(&rcap_fad->fad, fa, sizeof(struct flex_algo)); + + rcap_fad->fad.admin_group_exclude_any.bitmap.data = NULL; + rcap_fad->fad.admin_group_include_any.bitmap.data = NULL; + rcap_fad->fad.admin_group_include_all.bitmap.data = NULL; + + admin_group_copy(&rcap_fad->fad.admin_group_exclude_any, + &fa->admin_group_exclude_any); + admin_group_copy(&rcap_fad->fad.admin_group_include_any, + &fa->admin_group_include_any); + admin_group_copy(&rcap_fad->fad.admin_group_include_all, + &fa->admin_group_include_all); + + tlvs->router_cap->fads[algorithm] = rcap_fad; +} +#endif /* ifndef FABRICD */ + +int isis_tlvs_sr_algo_count(const struct isis_router_cap *cap) +{ + int count = 0; + + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (cap->algo[i] != SR_ALGORITHM_UNSET) + count++; + return count; } void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs, @@ -5861,42 +6711,107 @@ void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, UNSET_SUBTLV(exts, EXT_LAN_ADJ_SID); } +void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext, + struct isis_asla_subtlvs *asla) +{ + admin_group_term(&asla->ext_admin_group); + listnode_delete(ext->aslas, asla); + XFREE(MTYPE_ISIS_SUBTLV, asla); +} + +struct isis_asla_subtlvs * +isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node; + + if (!list_isempty(ext->aslas)) { + for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) { + if (CHECK_FLAG(asla->standard_apps, standard_apps)) + return asla; + } + } + + asla = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(struct isis_asla_subtlvs)); + admin_group_init(&asla->ext_admin_group); + SET_FLAG(asla->standard_apps, standard_apps); + SET_FLAG(asla->user_def_apps, standard_apps); + asla->standard_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH; + asla->user_def_apps_length = ASLA_APP_IDENTIFIER_BIT_LENGTH; + + listnode_add(ext->aslas, asla); + return asla; +} + +void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps) +{ + struct isis_asla_subtlvs *asla; + struct listnode *node; + + if (!ext) + return; + + for (ALL_LIST_ELEMENTS_RO(ext->aslas, node, asla)) { + if (!CHECK_FLAG(asla->standard_apps, standard_apps)) + continue; + isis_tlvs_del_asla_flex_algo(ext, asla); + break; + } +} + void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs, struct prefix_ipv4 *dest, uint32_t metric, - bool external, struct sr_prefix_cfg *pcfg) + bool external, + struct sr_prefix_cfg **pcfgs) { 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); + if (pcfgs) { r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); - append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_prefix_sid *psid; + struct sr_prefix_cfg *pcfg = pcfgs[i]; + + if (!pcfg) + continue; + + psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); + isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + 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, - bool external, struct sr_prefix_cfg *pcfg) + bool external, struct sr_prefix_cfg **pcfgs) { 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); + if (pcfgs) { r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); - append_item(&r->subtlvs->prefix_sids, (struct isis_item *)psid); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) { + struct isis_prefix_sid *psid; + struct sr_prefix_cfg *pcfg = pcfgs[i]; + + if (!pcfg) + continue; + + psid = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*psid)); + isis_sr_prefix_cfg2subtlv(pcfg, external, psid); + append_item(&r->subtlvs->prefix_sids, + (struct isis_item *)psid); + } } struct isis_item_list *l; diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 51058f1af1f4..03e2b2edccca 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -9,8 +9,12 @@ #ifndef ISIS_TLVS_H #define ISIS_TLVS_H +#include "segment_routing.h" #include "openbsd-tree.h" #include "prefix.h" +#include "flex_algo.h" +#include "affinitymap.h" + DECLARE_MTYPE(ISIS_SUBTLV); @@ -102,7 +106,7 @@ struct isis_spine_leaf { enum isis_threeway_state { ISIS_THREEWAY_DOWN = 2, ISIS_THREEWAY_INITIALIZING = 1, - ISIS_THREEWAY_UP = 0 + ISIS_THREEWAY_UP = 0, }; struct isis_threeway_adj { @@ -177,19 +181,18 @@ struct isis_lan_adj_sid { #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 +#ifndef FABRICD +struct isis_router_cap_fad; +struct isis_router_cap_fad { + uint8_t sysid[ISIS_SYS_ID_LEN + 2]; + + struct flex_algo fad; +}; +#endif /* ifndef FABRICD */ + struct isis_router_cap { struct in_addr router_id; uint8_t flags; @@ -200,6 +203,11 @@ struct isis_router_cap { uint8_t algo[SR_ALGORITHM_COUNT]; /* RFC 8491 */ uint8_t msd; + +#ifndef FABRICD + /* RFC9350 Flex-Algorithm */ + struct isis_router_cap_fad *fads[SR_ALGORITHM_COUNT]; +#endif /* ifndef FABRICD */ }; struct isis_item { @@ -309,7 +317,7 @@ enum isis_tlv_context { ISIS_CONTEXT_SUBTLV_NE_REACH, ISIS_CONTEXT_SUBTLV_IP_REACH, ISIS_CONTEXT_SUBTLV_IPV6_REACH, - ISIS_CONTEXT_MAX + ISIS_CONTEXT_MAX, }; struct isis_subtlvs { @@ -394,7 +402,22 @@ enum isis_tlv_type { ISIS_SUBTLV_AVA_BW = 38, ISIS_SUBTLV_USE_BW = 39, - ISIS_SUBTLV_MAX = 40 + /* RFC 7308 */ + ISIS_SUBTLV_EXT_ADMIN_GRP = 14, + + /* RFC 8919 */ + ISIS_SUBTLV_ASLA = 16, + + /* draft-ietf-lsr-isis-srv6-extensions */ + ISIS_SUBTLV_SID_END = 5, + ISIS_SUBTLV_SID_END_X = 43, + + ISIS_SUBTLV_MAX = 40, + + /* draft-ietf-lsr-isis-srv6-extensions */ + ISIS_SUBSUBTLV_SID_STRUCTURE = 1, + + ISIS_SUBSUBTLV_MAX = 256, }; /* subTLVs size for TE and SR */ @@ -422,19 +445,39 @@ enum ext_subtlv_size { /* RFC 7810 */ ISIS_SUBTLV_MM_DELAY_SIZE = 8, + /* RFC9350 - Flex-Algorithm */ + ISIS_SUBTLV_FAD = 26, + ISIS_SUBTLV_FAD_MIN_SIZE = 4, + ISIS_SUBTLV_HDR_SIZE = 2, ISIS_SUBTLV_DEF_SIZE = 4, - /* RFC 7308 */ - ISIS_SUBTLV_EXT_ADMIN_GRP = 14, + ISIS_SUBTLV_MAX_SIZE = 180, + + /* draft-ietf-lsr-isis-srv6-extensions */ + ISIS_SUBSUBTLV_SID_STRUCTURE_SIZE = 4, + + ISIS_SUBSUBTLV_HDR_SIZE = 2, + ISIS_SUBSUBTLV_MAX_SIZE = 180, + + /* RFC9350 - Flex-Algorithm */ + ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS_SIZE = 1, +}; - ISIS_SUBTLV_MAX_SIZE = 180 +enum ext_subsubtlv_types { + ISIS_SUBTLV_FAD_SUBSUBTLV_EXCAG = 1, + ISIS_SUBTLV_FAD_SUBSUBTLV_INCANYAG = 2, + ISIS_SUBTLV_FAD_SUBSUBTLV_INCALLAG = 3, + ISIS_SUBTLV_FAD_SUBSUBTLV_FLAGS = 4, + ISIS_SUBTLV_FAD_SUBSUBTLV_ESRLG = 5, }; /* 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 RESET_SUBTLV(s) (s->status = 0) +#define NO_SUBTLV(s) (s->status == 0) #define EXT_DISABLE 0x000000 #define EXT_ADM_GRP 0x000001 @@ -506,6 +549,45 @@ struct isis_ext_subtlvs { /* Segment Routing Adjacency & LAN Adjacency Segment ID */ struct isis_item_list adj_sid; struct isis_item_list lan_sid; + + struct list *aslas; +}; + +/* RFC 8919 */ +#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ +#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ +#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ +#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ + +#define ASLA_APP_IDENTIFIER_BIT_LENGTH 1 +#define ASLA_LEGACY_FLAG 0x80 +#define ASLA_APPS_LENGTH_MASK 0x7f + +struct isis_asla_subtlvs { + uint32_t status; + + /* Application Specific Link Attribute - RFC 8919 */ + bool legacy; /* L-Flag */ + uint8_t standard_apps_length; + uint8_t user_def_apps_length; + uint8_t standard_apps; + uint8_t user_def_apps; + + /* Sub-TLV list - rfc8919 section-3.1 */ + uint32_t admin_group; + struct admin_group ext_admin_group; /* Res. Class/Color - RFC 7308 */ + 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 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 */ }; #define IS_COMPAT_MT_TLV(tlv_type) \ @@ -536,6 +618,12 @@ struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); #define ISIS_MT_AT_MASK 0x4000 #endif +/* RFC 8919 */ +#define ISIS_SABM_FLAG_R 0x80 /* RSVP-TE */ +#define ISIS_SABM_FLAG_S 0x40 /* Segment Routing Policy */ +#define ISIS_SABM_FLAG_L 0x20 /* Loop-Free Alternate */ +#define ISIS_SABM_FLAG_X 0x10 /* Flex-Algorithm - RFC9350 */ + 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); @@ -567,8 +655,19 @@ 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); +struct isis_router_cap * +isis_tlvs_init_router_capability(struct isis_tlvs *tlvs); + +struct isis_area; +struct isis_flex_algo; +void isis_tlvs_set_router_capability_fad(struct isis_tlvs *tlvs, + struct flex_algo *fa, int algorithm, + uint8_t *sysid); + +struct isis_area; + +int isis_tlvs_sr_algo_count(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_set_te_router_id_ipv6(struct isis_tlvs *tlvs, @@ -577,10 +676,11 @@ 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, - bool external, struct sr_prefix_cfg *pcfg); + bool external, + struct sr_prefix_cfg **pcfgs); void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *dest, uint32_t metric, - bool external, struct sr_prefix_cfg *pcfg); + bool external, struct sr_prefix_cfg **pcfgs); void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *dest, struct prefix_ipv6 *src, @@ -596,6 +696,12 @@ void isis_tlvs_add_lan_adj_sid(struct isis_ext_subtlvs *exts, void isis_tlvs_del_lan_adj_sid(struct isis_ext_subtlvs *exts, struct isis_lan_adj_sid *lan); +void isis_tlvs_del_asla_flex_algo(struct isis_ext_subtlvs *ext, + struct isis_asla_subtlvs *asla); +struct isis_asla_subtlvs * +isis_tlvs_find_alloc_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps); +void isis_tlvs_free_asla(struct isis_ext_subtlvs *ext, uint8_t standard_apps); + 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, diff --git a/isisd/isis_tx_queue.c b/isisd/isis_tx_queue.c index ad91059766df..caf97f11749c 100644 --- a/isisd/isis_tx_queue.c +++ b/isisd/isis_tx_queue.c @@ -32,7 +32,7 @@ struct isis_tx_queue_entry { struct isis_lsp *lsp; enum isis_tx_type type; bool is_retry; - struct thread *retry; + struct event *retry; struct isis_tx_queue *queue; }; @@ -79,15 +79,14 @@ static void tx_queue_element_free(void *element) { struct isis_tx_queue_entry *e = element; - THREAD_OFF(e->retry); + EVENT_OFF(e->retry); XFREE(MTYPE_TX_QUEUE_ENTRY, e); } void isis_tx_queue_free(struct isis_tx_queue *queue) { - hash_clean(queue->hash, tx_queue_element_free); - hash_free(queue->hash); + hash_clean_and_free(&queue->hash, tx_queue_element_free); XFREE(MTYPE_TX_QUEUE, queue); } @@ -101,12 +100,12 @@ static struct isis_tx_queue_entry *tx_queue_find(struct isis_tx_queue *queue, return hash_lookup(queue->hash, &e); } -static void tx_queue_send_event(struct thread *thread) +static void tx_queue_send_event(struct event *thread) { - struct isis_tx_queue_entry *e = THREAD_ARG(thread); + struct isis_tx_queue_entry *e = EVENT_ARG(thread); struct isis_tx_queue *queue = e->queue; - thread_add_timer(master, tx_queue_send_event, e, 5, &e->retry); + event_add_timer(master, tx_queue_send_event, e, 5, &e->retry); if (e->is_retry) queue->circuit->area->lsp_rxmt_count++; @@ -127,12 +126,12 @@ void _isis_tx_queue_add(struct isis_tx_queue *queue, return; 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, - (type == TX_LSP_CIRCUIT_SCOPED) ? - "circuit scoped" : "regular", - func, file, line); + zlog_debug( + "Add LSP %pLS to %s queue as %s LSP. (From %s %s:%d)", + lsp->hdr.lsp_id, queue->circuit->interface->name, + (type == TX_LSP_CIRCUIT_SCOPED) ? "circuit scoped" + : "regular", + func, file, line); } struct isis_tx_queue_entry *e = tx_queue_find(queue, lsp); @@ -148,8 +147,8 @@ void _isis_tx_queue_add(struct isis_tx_queue *queue, e->type = type; - THREAD_OFF(e->retry); - thread_add_event(master, tx_queue_send_event, e, 0, &e->retry); + EVENT_OFF(e->retry); + event_add_event(master, tx_queue_send_event, e, 0, &e->retry); e->is_retry = false; } @@ -165,13 +164,12 @@ void _isis_tx_queue_del(struct isis_tx_queue *queue, struct isis_lsp *lsp, return; 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, + zlog_debug("Remove LSP %pLS from %s queue. (From %s %s:%d)", + lsp->hdr.lsp_id, queue->circuit->interface->name, func, file, line); } - THREAD_OFF(e->retry); + EVENT_OFF(e->retry); hash_release(queue->hash, e); XFREE(MTYPE_TX_QUEUE_ENTRY, e); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index 7e85576c7a3d..95bd37812f50 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -10,7 +10,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "memory.h" #include "log.h" @@ -327,14 +327,13 @@ void isis_zebra_route_del_route(struct isis *isis, */ void isis_zebra_prefix_sid_install(struct isis_area *area, struct prefix *prefix, - struct isis_route_info *rinfo, struct isis_sr_psid_info *psid) { struct zapi_labels zl; int count = 0; - sr_debug("ISIS-Sr (%s): update label %u for prefix %pFX", - area->area_tag, psid->label, prefix); + sr_debug("ISIS-Sr (%s): update label %u for prefix %pFX algorithm %u", + area->area_tag, psid->label, prefix, psid->algorithm); /* Prepare message. */ memset(&zl, 0, sizeof(zl)); @@ -342,7 +341,7 @@ void isis_zebra_prefix_sid_install(struct isis_area *area, zl.local_label = psid->label; /* Local routes don't have any nexthop and require special handling. */ - if (list_isempty(rinfo->nexthops)) { + if (list_isempty(psid->nexthops)) { struct zapi_nexthop *znh; struct interface *ifp; @@ -361,9 +360,9 @@ void isis_zebra_prefix_sid_install(struct isis_area *area, znh->labels[0] = MPLS_LABEL_IMPLICIT_NULL; } else { /* Add backup nexthops first. */ - if (rinfo->backup) { + if (psid->nexthops_backup) { count = isis_zebra_add_nexthops( - area->isis, rinfo->backup->nexthops, + area->isis, psid->nexthops_backup, zl.backup_nexthops, ISIS_NEXTHOP_BACKUP, true, 0); if (count > 0) { @@ -373,7 +372,7 @@ void isis_zebra_prefix_sid_install(struct isis_area *area, } /* Add primary nexthops. */ - count = isis_zebra_add_nexthops(area->isis, rinfo->nexthops, + count = isis_zebra_add_nexthops(area->isis, psid->nexthops, zl.nexthops, ISIS_NEXTHOP_MAIN, true, count); if (!count) @@ -400,8 +399,8 @@ void isis_zebra_prefix_sid_uninstall(struct isis_area *area, { struct zapi_labels zl; - sr_debug("ISIS-Sr (%s): delete label %u for prefix %pFX", - area->area_tag, psid->label, prefix); + sr_debug("ISIS-Sr (%s): delete label %u for prefix %pFX algorithm %u", + area->area_tag, psid->label, prefix, psid->algorithm); /* Prepare message. */ memset(&zl, 0, sizeof(zl)); @@ -773,9 +772,9 @@ static int isis_opaque_msg_handler(ZAPI_CALLBACK_ARGS) switch (info.type) { case LINK_STATE_SYNC: - STREAM_GETC(s, dst.proto); - STREAM_GETW(s, dst.instance); - STREAM_GETL(s, dst.session_id); + dst.proto = info.src_proto; + dst.instance = info.src_instance; + dst.session_id = info.src_session_id; dst.type = LINK_STATE_SYNC; ret = isis_te_sync_ted(dst); break; @@ -828,7 +827,7 @@ static zclient_handler *const isis_handlers[] = { [ZEBRA_CLIENT_CLOSE_NOTIFY] = isis_zebra_client_close_notify, }; -void isis_zebra_init(struct thread_master *master, int instance) +void isis_zebra_init(struct event_loop *master, int instance) { /* Initialize asynchronous zclient. */ zclient = zclient_new(master, &zclient_options_default, isis_handlers, diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index f34088f98ece..045c75874a26 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -20,7 +20,7 @@ struct label_chunk { }; #define CHUNK_SIZE 64 -void isis_zebra_init(struct thread_master *master, int instance); +void isis_zebra_init(struct event_loop *master, int instance); void isis_zebra_stop(void); struct isis_route_info; @@ -36,7 +36,6 @@ void isis_zebra_route_del_route(struct isis *isis, struct isis_route_info *route_info); void isis_zebra_prefix_sid_install(struct isis_area *area, struct prefix *prefix, - struct isis_route_info *rinfo, struct isis_sr_psid_info *psid); void isis_zebra_prefix_sid_uninstall(struct isis_area *area, struct prefix *prefix, diff --git a/isisd/isisd.c b/isisd/isisd.c index 586785b05f21..c6a7e803c157 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -9,7 +9,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "log.h" @@ -27,6 +27,7 @@ #include "zclient.h" #include "vrf.h" #include "spf_backoff.h" +#include "flex_algo.h" #include "lib/northbound_cli.h" #include "bfd.h" @@ -49,6 +50,7 @@ #include "isisd/isis_te.h" #include "isisd/isis_mt.h" #include "isisd/isis_sr.h" +#include "isisd/isis_flex_algo.h" #include "isisd/fabricd.h" #include "isisd/isis_nb.h" @@ -88,7 +90,7 @@ static struct isis_master isis_master; struct isis_master *im; /* ISIS config processing thread */ -struct thread *t_isis_cfg; +struct event *t_isis_cfg; #ifndef FABRICD DEFINE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area)); @@ -166,7 +168,7 @@ struct isis *isis_lookup_by_sysid(const uint8_t *sysid) return NULL; } -void isis_master_init(struct thread_master *master) +void isis_master_init(struct event_loop *master) { memset(&isis_master, 0, sizeof(isis_master)); im = &isis_master; @@ -272,7 +274,7 @@ void isis_area_del_circuit(struct isis_area *area, struct isis_circuit *circuit) static void delete_area_addr(void *arg) { - struct area_addr *addr = (struct area_addr *)arg; + struct iso_address *addr = (struct iso_address *)arg; XFREE(MTYPE_ISIS_AREA_ADDR, addr); } @@ -317,6 +319,12 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) if (area->is_type & IS_LEVEL_2) lsp_db_init(&area->lspdb[1]); +#ifndef FABRICD + /* Flex-Algo */ + area->flex_algos = flex_algos_alloc(isis_flex_algo_data_alloc, + isis_flex_algo_data_free); +#endif /* ifndef FABRICD */ + spftree_area_init(area); area->circuit_list = list_new(); @@ -325,7 +333,7 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) area->area_addrs->del = delete_area_addr; if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) - thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); + event_add_timer(master, lsp_tick, area, 1, &area->t_tick); flags_initialize(&area->flags); isis_sr_area_init(area); @@ -512,6 +520,10 @@ void isis_area_destroy(struct isis_area *area) isis_area_invalidate_routes(area, area->is_type); isis_area_verify_routes(area); +#ifndef FABRICD + flex_algos_free(area->flex_algos); +#endif /* ifndef FABRICD */ + isis_sr_area_term(area); isis_mpls_te_term(area); @@ -519,11 +531,11 @@ void isis_area_destroy(struct isis_area *area) spftree_area_del(area); if (area->spf_timer[0]) - isis_spf_timer_free(THREAD_ARG(area->spf_timer[0])); - THREAD_OFF(area->spf_timer[0]); + isis_spf_timer_free(EVENT_ARG(area->spf_timer[0])); + EVENT_OFF(area->spf_timer[0]); if (area->spf_timer[1]) - isis_spf_timer_free(THREAD_ARG(area->spf_timer[1])); - THREAD_OFF(area->spf_timer[1]); + isis_spf_timer_free(EVENT_ARG(area->spf_timer[1])); + EVENT_OFF(area->spf_timer[1]); spf_backoff_free(area->spf_delay_ietf[0]); spf_backoff_free(area->spf_delay_ietf[1]); @@ -543,12 +555,12 @@ void isis_area_destroy(struct isis_area *area) isis_lfa_tiebreakers_clear(area, ISIS_LEVEL1); isis_lfa_tiebreakers_clear(area, ISIS_LEVEL2); - THREAD_OFF(area->t_tick); - THREAD_OFF(area->t_lsp_refresh[0]); - THREAD_OFF(area->t_lsp_refresh[1]); - THREAD_OFF(area->t_rlfa_rib_update); + EVENT_OFF(area->t_tick); + EVENT_OFF(area->t_lsp_refresh[0]); + EVENT_OFF(area->t_lsp_refresh[1]); + EVENT_OFF(area->t_rlfa_rib_update); - thread_cancel_event(master, area); + event_cancel_event(master, area); listnode_delete(area->isis->area_list, area); @@ -626,22 +638,22 @@ static void isis_set_redist_vrf_bitmaps(struct isis *isis, bool set) if (type == DEFAULT_ROUTE) { if (set) vrf_bitmap_set( - zclient->default_information - [afi], + &zclient->default_information + [afi], isis->vrf_id); else vrf_bitmap_unset( - zclient->default_information - [afi], + &zclient->default_information + [afi], isis->vrf_id); } else { if (set) vrf_bitmap_set( - zclient->redist[afi][type], + &zclient->redist[afi][type], isis->vrf_id); else vrf_bitmap_unset( - zclient->redist[afi][type], + &zclient->redist[afi][type], isis->vrf_id); } } @@ -809,8 +821,8 @@ static void area_set_mt_overload(struct isis_area *area, uint16_t mtid, int area_net_title(struct vty *vty, const char *net_title) { VTY_DECLVAR_CONTEXT(isis_area, area); - struct area_addr *addr; - struct area_addr *addrp; + struct iso_address *addr; + struct iso_address *addrp; struct listnode *node; uint8_t buff[255]; @@ -823,14 +835,14 @@ int area_net_title(struct vty *vty, const char *net_title) return CMD_ERR_NOTHING_TODO; } - addr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct area_addr)); + addr = XMALLOC(MTYPE_ISIS_AREA_ADDR, sizeof(struct iso_address)); addr->addr_len = dotformat2buff(buff, net_title); memcpy(addr->area_addr, buff, addr->addr_len); #ifdef EXTREME_DEBUG zlog_debug("added area address %s for area %s (address length %d)", net_title, area->area_tag, addr->addr_len); #endif /* EXTREME_DEBUG */ - if (addr->addr_len < 8 || addr->addr_len > 20) { + if (addr->addr_len < ISO_ADDR_MIN || addr->addr_len > ISO_ADDR_SIZE) { vty_out(vty, "area address must be at least 8..20 octets long (%d)\n", addr->addr_len); @@ -852,8 +864,8 @@ int area_net_title(struct vty *vty, const char *net_title) 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(area->isis->sysid)); + zlog_debug("Router has SystemID %pSY", + area->isis->sysid); } else { /* * Check that the SystemID portions match @@ -899,12 +911,12 @@ int area_net_title(struct vty *vty, const char *net_title) int area_clear_net_title(struct vty *vty, const char *net_title) { VTY_DECLVAR_CONTEXT(isis_area, area); - struct area_addr addr, *addrp = NULL; + struct iso_address addr, *addrp = NULL; struct listnode *node; uint8_t buff[255]; addr.addr_len = dotformat2buff(buff, net_title); - if (addr.addr_len < 8 || addr.addr_len > 20) { + if (addr.addr_len < ISO_ADDR_MIN || addr.addr_len > ISO_ADDR_SIZE) { vty_out(vty, "Unsupported area address length %d, should be 8...20 \n", addr.addr_len); @@ -2250,7 +2262,7 @@ static void isis_spf_ietf_common(struct vty *vty, struct isis *isis) vty_out(vty, " Level-%d:\n", level); vty_out(vty, " SPF delay status: "); if (area->spf_timer[level - 1]) { - struct timeval remain = thread_timer_remain( + struct timeval remain = event_timer_remain( area->spf_timer[level - 1]); vty_out(vty, "Pending, due in %lld msec\n", (long long)remain.tv_sec * 1000 @@ -2348,11 +2360,11 @@ static void common_isis_summary_json(struct json_object *json, time_t cur; char uptime[MONOTIME_STRLEN]; char stier[5]; + json_object_string_add(json, "vrf", isis->name); json_object_int_add(json, "process-id", isis->process_id); if (isis->sysid_set) - json_object_string_add(json, "system-id", - sysid_print(isis->sysid)); + json_object_string_addf(json, "system-id", "%pSY", isis->sysid); cur = time(NULL); cur -= isis->uptime; @@ -2380,16 +2392,11 @@ static void common_isis_summary_json(struct json_object *json, } if (listcount(area->area_addrs) > 0) { - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, - area_addr)) { - json_object_string_add( - area_json, "net", - isonet_print(area_addr->area_addr, - area_addr->addr_len + - ISIS_SYS_ID_LEN + - 1)); - } + area_addr)) + json_object_string_addf(area_json, "net", + "%pISl", area_addr); } tx_pdu_json = json_object_new_object(); @@ -2462,8 +2469,7 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis) 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", - sysid_print(isis->sysid)); + vty_out(vty, "System Id : %pSY\n", isis->sysid); vty_out(vty, "Up time : "); vty_out_timestr(vty, isis->uptime); @@ -2485,15 +2491,10 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis) } if (listcount(area->area_addrs) > 0) { - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, - area_addr)) { - vty_out(vty, " Net: %s\n", - isonet_print(area_addr->area_addr, - area_addr->addr_len - + ISIS_SYS_ID_LEN - + 1)); - } + area_addr)) + vty_out(vty, " Net: %pISl\n", area_addr); } vty_out(vty, " TX counters per PDU type:\n"); @@ -2503,6 +2504,9 @@ static void common_isis_summary_vty(struct vty *vty, struct isis *isis) vty_out(vty, " RX counters per PDU type:\n"); pdu_counter_print(vty, " ", area->pdu_rx_counters); + vty_out(vty, " Drop counters per PDU type:\n"); + pdu_counter_print(vty, " ", area->pdu_drop_counters); + vty_out(vty, " Advertise high metrics: %s\n", area->advertise_high_metrics ? "Enabled" : "Disabled"); @@ -3069,12 +3073,27 @@ int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, void isis_area_invalidate_routes(struct isis_area *area, int levels) { +#ifndef FABRICD + struct flex_algo *fa; + struct listnode *node; + struct isis_flex_algo_data *data; +#endif /* ifndef FABRICD */ + for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { if (!(level & levels)) continue; for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { isis_spf_invalidate_routes( area->spftree[tree][level - 1]); + +#ifndef FABRICD + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, + node, fa)) { + data = fa->data; + isis_spf_invalidate_routes( + data->spftree[tree][level - 1]); + } +#endif /* ifndef FABRICD */ } } } @@ -3082,7 +3101,7 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels) void isis_area_verify_routes(struct isis_area *area) { for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) - isis_spf_verify_routes(area, area->spftree[tree]); + isis_spf_verify_routes(area, area->spftree[tree], tree); } void isis_area_switchover_routes(struct isis_area *area, int family, @@ -3106,6 +3125,12 @@ void isis_area_switchover_routes(struct isis_area *area, int family, static void area_resign_level(struct isis_area *area, int level) { +#ifndef FABRICD + struct flex_algo *fa; + struct listnode *node; + struct isis_flex_algo_data *data; +#endif /* ifndef FABRICD */ + isis_area_invalidate_routes(area, level); isis_area_verify_routes(area); @@ -3118,15 +3143,29 @@ static void area_resign_level(struct isis_area *area, int level) } } +#ifndef FABRICD + for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) { + for (ALL_LIST_ELEMENTS_RO(area->flex_algos->flex_algos, node, + fa)) { + data = fa->data; + if (data->spftree[tree][level - 1]) { + isis_spftree_del( + data->spftree[tree][level - 1]); + data->spftree[tree][level - 1] = NULL; + } + } + } +#endif /* ifndef FABRICD */ + if (area->spf_timer[level - 1]) - isis_spf_timer_free(THREAD_ARG(area->spf_timer[level - 1])); + isis_spf_timer_free(EVENT_ARG(area->spf_timer[level - 1])); - THREAD_OFF(area->spf_timer[level - 1]); + EVENT_OFF(area->spf_timer[level - 1]); sched_debug( "ISIS (%s): Resigned from L%d - canceling LSP regeneration timer.", area->area_tag, level); - THREAD_OFF(area->t_lsp_refresh[level - 1]); + EVENT_OFF(area->t_lsp_refresh[level - 1]); area->lsp_regenerate_pending[level - 1] = 0; } @@ -3215,7 +3254,7 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) } else { /* Cancel overload on startup timer if it's running */ if (area->t_overload_on_startup_timer) { - THREAD_OFF(area->t_overload_on_startup_timer); + EVENT_OFF(area->t_overload_on_startup_timer); area->t_overload_on_startup_timer = NULL; } } @@ -3494,15 +3533,10 @@ static int isis_config_write(struct vty *vty) write++; /* ISIS - Net */ if (listcount(area->area_addrs) > 0) { - struct area_addr *area_addr; + struct iso_address *area_addr; for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, area_addr)) { - vty_out(vty, " net %s\n", - isonet_print( - area_addr->area_addr, - area_addr->addr_len - + ISIS_SYS_ID_LEN - + 1)); + vty_out(vty, " net %pISl\n", area_addr); write++; } } @@ -3761,7 +3795,8 @@ struct cmd_node router_node = { .prompt = "%s(config-router)# ", .config_write = isis_config_write, }; -#else +#endif /* ifdef FABRICD */ +#ifndef FABRICD /* IS-IS configuration write function */ static int isis_config_write(struct vty *vty) { @@ -3784,7 +3819,14 @@ struct cmd_node router_node = { .prompt = "%s(config-router)# ", .config_write = isis_config_write, }; -#endif /* ifdef FABRICD */ + +struct cmd_node isis_flex_algo_node = { + .name = "isis-flex-algo", + .node = ISIS_FLEX_ALGO_NODE, + .parent_node = ISIS_NODE, + .prompt = "%s(config-router-flex-algo)# ", +}; +#endif /* ifdnef FABRICD */ void isis_init(void) { @@ -3893,6 +3935,10 @@ void isis_init(void) install_element(ROUTER_NODE, &log_adj_changes_cmd); install_element(ROUTER_NODE, &no_log_adj_changes_cmd); #endif /* ifdef FABRICD */ +#ifndef FABRICD + install_node(&isis_flex_algo_node); + install_default(ISIS_FLEX_ALGO_NODE); +#endif /* ifdnef FABRICD */ spf_backoff_cmd_init(); } diff --git a/isisd/isisd.h b/isisd/isisd.h index 37a36fd37ac8..f0d236b64376 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -24,6 +24,7 @@ #include "isis_lfa.h" #include "qobj.h" #include "ldp_sync.h" +#include "iso.h" DECLARE_MGROUP(ISISD); @@ -71,7 +72,7 @@ struct isis_master { /* ISIS instance. */ struct list *isis; /* ISIS thread master. */ - struct thread_master *master; + struct event_loop *master; uint8_t options; }; #define F_ISIS_UNIT_TEST 0x01 @@ -87,9 +88,9 @@ struct isis { uint32_t router_id; /* Router ID from zebra */ struct list *area_list; /* list of IS-IS areas */ uint8_t max_area_addrs; /* maximumAreaAdresses */ - struct area_addr *man_area_addrs; /* manualAreaAddresses */ + struct iso_address *man_area_addrs; /* manualAreaAddresses */ time_t uptime; /* when did we start */ - struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ + struct event *t_dync_clean; /* dynamic hostname cache cleanup thread */ uint32_t circuit_ids_used[8]; /* 256 bits to track circuit ids 1 through 255 */ int snmp_notifications; struct list *dyn_cache; @@ -99,7 +100,7 @@ struct isis { extern struct isis_master *im; -extern struct thread *t_isis_cfg; +extern struct event *t_isis_cfg; enum spf_tree_id { SPFTREE_IPV4 = 0, @@ -129,11 +130,11 @@ struct isis_area { 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]; - struct thread *t_overload_on_startup_timer; + struct event *t_tick; /* LSP walker */ + struct event *t_lsp_refresh[ISIS_LEVELS]; + struct event *t_overload_on_startup_timer; struct timeval last_lsp_refresh_event[ISIS_LEVELS]; - struct thread *t_rlfa_rib_update; + struct event *t_rlfa_rib_update; /* t_lsp_refresh is used in two ways: * a) regular refresh of LSPs * b) (possibly throttled) updates to LSPs @@ -162,6 +163,10 @@ struct isis_area { /* do we support new style metrics? */ char newmetric; char oldmetric; + /* Allow sending the default admin-group value of 0x00000000. */ + bool admin_group_send_zero; + /* Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV */ + bool asla_legacy_flag; /* identifies the routing instance */ char *area_tag; /* area addresses for this area */ @@ -195,6 +200,8 @@ struct isis_area { int ip_circuits; /* logging adjacency changes? */ uint8_t log_adj_changes; + /* logging pdu drops? */ + uint8_t log_pdu_drops; /* multi topology settings */ struct list *mt_settings; /* MPLS-TE settings */ @@ -217,6 +224,10 @@ struct isis_area { size_t tilfa_protected_links[ISIS_LEVELS]; /* MPLS LDP-IGP Sync */ struct ldp_sync_info_cmd ldp_sync_cmd; +#ifndef FABRICD + /* Flex-Algo */ + struct flex_algos *flex_algos; +#endif /* ifndef FABRICD */ /* Counters */ uint32_t circuit_state_changes; struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT] @@ -226,12 +237,13 @@ struct isis_area { struct spf_backoff *spf_delay_ietf[ISIS_LEVELS]; /*Structure with IETF SPF algo parameters*/ - struct thread *spf_timer[ISIS_LEVELS]; + struct event *spf_timer[ISIS_LEVELS]; struct lsp_refresh_arg lsp_refresh_arg[ISIS_LEVELS]; pdu_counter_t pdu_tx_counters; pdu_counter_t pdu_rx_counters; + pdu_counter_t pdu_drop_counters; uint64_t lsp_rxmt_count; /* Area counters */ @@ -252,7 +264,7 @@ DECLARE_MTYPE(ISIS_PLIST_NAME); DECLARE_HOOK(isis_area_overload_bit_update, (struct isis_area * area), (area)); void isis_terminate(void); -void isis_master_init(struct thread_master *master); +void isis_master_init(struct event_loop *master); void isis_vrf_link(struct isis *isis, struct vrf *vrf); void isis_vrf_unlink(struct isis *isis, struct vrf *vrf); struct isis *isis_lookup_by_vrfid(vrf_id_t vrf_id); @@ -329,7 +341,7 @@ void config_end_lsp_generate(struct isis_area *area); #define ISIS_SR "/frr-isisd:isis/instance/segment-routing" /* Master of threads. */ -extern struct thread_master *master; +extern struct event_loop *master; extern unsigned long debug_adj_pkt; extern unsigned long debug_snp_pkt; diff --git a/isisd/subdir.am b/isisd/subdir.am index dabf6a925ea0..6bd2477b198b 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -19,6 +19,7 @@ vtysh_daemons += fabricd endif noinst_HEADERS += \ + isisd/isis_affinitymap.h \ isisd/isis_adjacency.h \ isisd/isis_bfd.h \ isisd/isis_circuit.h \ @@ -45,6 +46,7 @@ noinst_HEADERS += \ isisd/isis_spf.h \ isisd/isis_spf_private.h \ isisd/isis_sr.h \ + isisd/isis_flex_algo.h \ isisd/isis_te.h \ isisd/isis_tlvs.h \ isisd/isis_tx_queue.h \ @@ -55,6 +57,7 @@ noinst_HEADERS += \ # end LIBISIS_SOURCES = \ + isisd/isis_affinitymap.c \ isisd/isis_adjacency.c \ isisd/isis_bfd.c \ isisd/isis_circuit.c \ @@ -76,6 +79,7 @@ LIBISIS_SOURCES = \ isisd/isis_routemap.c \ isisd/isis_spf.c \ isisd/isis_sr.c \ + isisd/isis_flex_algo.c \ isisd/isis_te.c \ isisd/isis_tlvs.c \ isisd/isis_tx_queue.c \ diff --git a/ldpd/accept.c b/ldpd/accept.c index 170c079b52cc..8e881e78aecd 100644 --- a/ldpd/accept.c +++ b/ldpd/accept.c @@ -13,21 +13,21 @@ struct accept_ev { LIST_ENTRY(accept_ev) entry; - struct thread *ev; - void (*accept_cb)(struct thread *); + struct event *ev; + void (*accept_cb)(struct event *); void *arg; int fd; }; struct { LIST_HEAD(, accept_ev) queue; - struct thread *evt; + struct event *evt; } accept_queue; static void accept_arm(void); static void accept_unarm(void); -static void accept_cb(struct thread *); -static void accept_timeout(struct thread *); +static void accept_cb(struct event *); +static void accept_timeout(struct event *); void accept_init(void) @@ -35,7 +35,7 @@ accept_init(void) LIST_INIT(&accept_queue.queue); } -int accept_add(int fd, void (*cb)(struct thread *), void *arg) +int accept_add(int fd, void (*cb)(struct event *), void *arg) { struct accept_ev *av; @@ -46,7 +46,7 @@ int accept_add(int fd, void (*cb)(struct thread *), void *arg) av->arg = arg; LIST_INSERT_HEAD(&accept_queue.queue, av, entry); - thread_add_read(master, accept_cb, av, av->fd, &av->ev); + event_add_read(master, accept_cb, av, av->fd, &av->ev); log_debug("%s: accepting on fd %d", __func__, fd); @@ -61,7 +61,7 @@ accept_del(int fd) LIST_FOREACH(av, &accept_queue.queue, entry) if (av->fd == fd) { log_debug("%s: %d removed from queue", __func__, fd); - THREAD_OFF(av->ev); + EVENT_OFF(av->ev); LIST_REMOVE(av, entry); free(av); return; @@ -73,7 +73,7 @@ accept_pause(void) { log_debug(__func__); accept_unarm(); - thread_add_timer(master, accept_timeout, NULL, 1, &accept_queue.evt); + event_add_timer(master, accept_timeout, NULL, 1, &accept_queue.evt); } void @@ -81,7 +81,7 @@ accept_unpause(void) { if (accept_queue.evt != NULL) { log_debug(__func__); - THREAD_OFF(accept_queue.evt); + EVENT_OFF(accept_queue.evt); accept_arm(); } } @@ -91,7 +91,7 @@ accept_arm(void) { struct accept_ev *av; LIST_FOREACH(av, &accept_queue.queue, entry) { - thread_add_read(master, accept_cb, av, av->fd, &av->ev); + event_add_read(master, accept_cb, av, av->fd, &av->ev); } } @@ -100,17 +100,17 @@ accept_unarm(void) { struct accept_ev *av; LIST_FOREACH(av, &accept_queue.queue, entry) - THREAD_OFF(av->ev); + EVENT_OFF(av->ev); } -static void accept_cb(struct thread *thread) +static void accept_cb(struct event *thread) { - struct accept_ev *av = THREAD_ARG(thread); - thread_add_read(master, accept_cb, av, av->fd, &av->ev); + struct accept_ev *av = EVENT_ARG(thread); + event_add_read(master, accept_cb, av, av->fd, &av->ev); av->accept_cb(thread); } -static void accept_timeout(struct thread *thread) +static void accept_timeout(struct event *thread) { accept_queue.evt = NULL; diff --git a/ldpd/adjacency.c b/ldpd/adjacency.c index 42c3ef05351a..0108af80444e 100644 --- a/ldpd/adjacency.c +++ b/ldpd/adjacency.c @@ -15,12 +15,12 @@ #include "log.h" static __inline int adj_compare(const struct adj *, const struct adj *); -static void adj_itimer(struct thread *); +static void adj_itimer(struct event *); static __inline int tnbr_compare(const struct tnbr *, const struct tnbr *); static void tnbr_del(struct ldpd_conf *, struct tnbr *); static void tnbr_start(struct tnbr *); static void tnbr_stop(struct tnbr *); -static void tnbr_hello_timer(struct thread *); +static void tnbr_hello_timer(struct event *); static void tnbr_start_hello_timer(struct tnbr *); static void tnbr_stop_hello_timer(struct tnbr *); @@ -161,9 +161,9 @@ adj_get_af(const struct adj *adj) /* adjacency timers */ /* ARGSUSED */ -static void adj_itimer(struct thread *thread) +static void adj_itimer(struct event *thread) { - struct adj *adj = THREAD_ARG(thread); + struct adj *adj = EVENT_ARG(thread); adj->inactivity_timer = NULL; @@ -185,16 +185,16 @@ static void adj_itimer(struct thread *thread) void adj_start_itimer(struct adj *adj) { - THREAD_OFF(adj->inactivity_timer); + EVENT_OFF(adj->inactivity_timer); adj->inactivity_timer = NULL; - thread_add_timer(master, adj_itimer, adj, adj->holdtime, - &adj->inactivity_timer); + event_add_timer(master, adj_itimer, adj, adj->holdtime, + &adj->inactivity_timer); } void adj_stop_itimer(struct adj *adj) { - THREAD_OFF(adj->inactivity_timer); + EVENT_OFF(adj->inactivity_timer); } /* targeted neighbors */ @@ -331,9 +331,9 @@ tnbr_get_hello_interval(struct tnbr *tnbr) /* target neighbors timers */ /* ARGSUSED */ -static void tnbr_hello_timer(struct thread *thread) +static void tnbr_hello_timer(struct event *thread) { - struct tnbr *tnbr = THREAD_ARG(thread); + struct tnbr *tnbr = EVENT_ARG(thread); tnbr->hello_timer = NULL; send_hello(HELLO_TARGETED, NULL, tnbr); @@ -343,16 +343,16 @@ static void tnbr_hello_timer(struct thread *thread) static void tnbr_start_hello_timer(struct tnbr *tnbr) { - THREAD_OFF(tnbr->hello_timer); + EVENT_OFF(tnbr->hello_timer); tnbr->hello_timer = NULL; - thread_add_timer(master, tnbr_hello_timer, tnbr, tnbr_get_hello_interval(tnbr), - &tnbr->hello_timer); + event_add_timer(master, tnbr_hello_timer, tnbr, + tnbr_get_hello_interval(tnbr), &tnbr->hello_timer); } static void tnbr_stop_hello_timer(struct tnbr *tnbr) { - THREAD_OFF(tnbr->hello_timer); + EVENT_OFF(tnbr->hello_timer); } struct ctl_adj * @@ -375,7 +375,7 @@ adj_to_ctl(struct adj *adj) } actl.holdtime = adj->holdtime; actl.holdtime_remaining = - thread_timer_remain_second(adj->inactivity_timer); + event_timer_remain_second(adj->inactivity_timer); actl.trans_addr = adj->trans_addr; actl.ds_tlv = adj->ds_tlv; diff --git a/ldpd/control.c b/ldpd/control.c index b1260feb68a4..db52d4632539 100644 --- a/ldpd/control.c +++ b/ldpd/control.c @@ -15,11 +15,11 @@ #define CONTROL_BACKLOG 5 -static void control_accept(struct thread *); +static void control_accept(struct event *); static struct ctl_conn *control_connbyfd(int); static struct ctl_conn *control_connbypid(pid_t); static void control_close(int); -static void control_dispatch_imsg(struct thread *); +static void control_dispatch_imsg(struct event *); struct ctl_conns ctl_conns; @@ -90,7 +90,7 @@ control_cleanup(char *path) } /* ARGSUSED */ -static void control_accept(struct thread *thread) +static void control_accept(struct event *thread) { int connfd; socklen_t len; @@ -98,16 +98,15 @@ static void control_accept(struct thread *thread) struct ctl_conn *c; len = sizeof(s_un); - if ((connfd = accept(THREAD_FD(thread), (struct sockaddr *)&s_un, - &len)) == -1) { + if ((connfd = accept(EVENT_FD(thread), (struct sockaddr *)&s_un, + &len)) == -1) { /* * Pause accept if we are out of file descriptors, or * libevent will haunt us here too. */ if (errno == ENFILE || errno == EMFILE) accept_pause(); - else if (errno != EWOULDBLOCK && errno != EINTR && - errno != ECONNABORTED) + else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) log_warn("%s: accept", __func__); return; } @@ -122,8 +121,8 @@ static void control_accept(struct thread *thread) imsg_init(&c->iev.ibuf, connfd); c->iev.handler_read = control_dispatch_imsg; c->iev.ev_read = NULL; - thread_add_read(master, c->iev.handler_read, &c->iev, c->iev.ibuf.fd, - &c->iev.ev_read); + event_add_read(master, c->iev.handler_read, &c->iev, c->iev.ibuf.fd, + &c->iev.ev_read); c->iev.handler_write = ldp_write_handler; c->iev.ev_write = NULL; @@ -169,17 +168,17 @@ control_close(int fd) msgbuf_clear(&c->iev.ibuf.w); TAILQ_REMOVE(&ctl_conns, c, entry); - THREAD_OFF(c->iev.ev_read); - THREAD_OFF(c->iev.ev_write); + EVENT_OFF(c->iev.ev_read); + EVENT_OFF(c->iev.ev_write); close(c->iev.ibuf.fd); accept_unpause(); free(c); } /* ARGSUSED */ -static void control_dispatch_imsg(struct thread *thread) +static void control_dispatch_imsg(struct event *thread) { - int fd = THREAD_FD(thread); + int fd = EVENT_FD(thread); struct ctl_conn *c; struct imsg imsg; ssize_t n; @@ -192,8 +191,7 @@ static void control_dispatch_imsg(struct thread *thread) c->iev.ev_read = NULL; - if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || - n == 0) { + if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) || n == 0) { control_close(fd); return; } @@ -217,12 +215,10 @@ static void control_dispatch_imsg(struct thread *thread) /* ignore */ break; case IMSG_CTL_SHOW_INTERFACE: - if (imsg.hdr.len == IMSG_HEADER_SIZE + - sizeof(ifidx)) { + if (imsg.hdr.len == IMSG_HEADER_SIZE + sizeof(ifidx)) { memcpy(&ifidx, imsg.data, sizeof(ifidx)); ldpe_iface_ctl(c, ifidx); - imsg_compose_event(&c->iev, IMSG_CTL_END, 0, - 0, -1, NULL, 0); + imsg_compose_event(&c->iev, IMSG_CTL_END, 0, 0, -1, NULL, 0); } break; case IMSG_CTL_SHOW_DISCOVERY: @@ -242,8 +238,7 @@ static void control_dispatch_imsg(struct thread *thread) ldpe_nbr_ctl(c); break; case IMSG_CTL_CLEAR_NBR: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct ctl_nbr)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ctl_nbr)) break; nbr_clear_ctl(imsg.data); @@ -255,8 +250,7 @@ static void control_dispatch_imsg(struct thread *thread) /* ignore */ break; default: - log_debug("%s: error handling imsg %d", __func__, - imsg.hdr.type); + log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); diff --git a/ldpd/hello.c b/ldpd/hello.c index 83c0b2f8ca00..0b07f24b4581 100644 --- a/ldpd/hello.c +++ b/ldpd/hello.c @@ -41,8 +41,8 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) /* multicast destination address */ switch (af) { case AF_INET: - if (!(leconf->ipv4.flags & F_LDPD_AF_NO_GTSM)) - flags |= F_HELLO_GTSM; + if (!CHECK_FLAG(leconf->ipv4.flags, F_LDPD_AF_NO_GTSM)) + SET_FLAG(flags, F_HELLO_GTSM); dst.v4 = global.mcast_addr_v4; break; case AF_INET6: @@ -56,9 +56,11 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) af = tnbr->af; holdtime = tnbr_get_hello_holdtime(tnbr); flags = F_HELLO_TARGETED; - if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count - || tnbr->rlfa_count) + if (CHECK_FLAG(tnbr->flags, F_TNBR_CONFIGURED) || + tnbr->pw_count || + tnbr->rlfa_count) flags |= F_HELLO_REQ_TARG; + fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket; /* unicast destination address */ @@ -88,10 +90,10 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) if ((buf = ibuf_open(size)) == NULL) fatal(__func__); - err |= gen_ldp_hdr(buf, size); + SET_FLAG(err, gen_ldp_hdr(buf, size)); size -= LDP_HDR_SIZE; - err |= gen_msg_hdr(buf, MSG_TYPE_HELLO, size); - err |= gen_hello_prms_tlv(buf, holdtime, flags); + SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_HELLO, size)); + SET_FLAG(err, gen_hello_prms_tlv(buf, holdtime, flags)); /* * RFC 7552 - Section 6.1: @@ -101,19 +103,19 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) */ switch (af) { case AF_INET: - err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR, - leconf->ipv4.trans_addr.v4.s_addr); + SET_FLAG(err, gen_opt4_hello_prms_tlv(buf, TLV_TYPE_IPV4TRANSADDR, + leconf->ipv4.trans_addr.v4.s_addr)); break; case AF_INET6: - err |= gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR, - leconf->ipv6.trans_addr.v6.s6_addr); + SET_FLAG(err, gen_opt16_hello_prms_tlv(buf, TLV_TYPE_IPV6TRANSADDR, + leconf->ipv6.trans_addr.v6.s6_addr)); break; default: fatalx("send_hello: unknown af"); } - err |= gen_opt4_hello_prms_tlv(buf, TLV_TYPE_CONFIG, - htonl(global.conf_seqnum)); + SET_FLAG(err, gen_opt4_hello_prms_tlv(buf, TLV_TYPE_CONFIG, + htonl(global.conf_seqnum))); /* * RFC 7552 - Section 6.1.1: @@ -121,7 +123,7 @@ send_hello(enum hello_type type, struct iface_af *ia, struct tnbr *tnbr) * MUST include the Dual-Stack capability TLV in all of its LDP Hellos". */ if (ldp_is_dual_stack(leconf)) - err |= gen_ds_hello_prms_tlv(buf, leconf->trans_pref); + SET_FLAG(err, gen_ds_hello_prms_tlv(buf, leconf->trans_pref)); if (err) { ibuf_free(buf); @@ -169,8 +171,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, r = tlv_decode_hello_prms(buf, len, &holdtime, &flags); if (r == -1) { - log_debug("%s: lsr-id %pI4: failed to decode params", __func__, - &lsr_id); + log_debug("%s: lsr-id %pI4: failed to decode params", __func__, &lsr_id); return; } /* safety checks */ @@ -179,14 +180,12 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, __func__, &lsr_id, holdtime); return; } - if (multicast && (flags & F_HELLO_TARGETED)) { - log_debug("%s: lsr-id %pI4: multicast targeted hello", __func__, - &lsr_id); + if (multicast && CHECK_FLAG(flags, F_HELLO_TARGETED)) { + log_debug("%s: lsr-id %pI4: multicast targeted hello", __func__, &lsr_id); return; } - if (!multicast && !((flags & F_HELLO_TARGETED))) { - log_debug("%s: lsr-id %pI4: unicast link hello", __func__, - &lsr_id); + if (!multicast && !CHECK_FLAG(flags, F_HELLO_TARGETED)) { + log_debug("%s: lsr-id %pI4: unicast link hello", __func__, &lsr_id); return; } buf += r; @@ -204,10 +203,10 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, __func__, &lsr_id); return; } - ds_tlv = (tlvs_rcvd & F_HELLO_TLV_RCVD_DS) ? 1 : 0; + ds_tlv = CHECK_FLAG(tlvs_rcvd, F_HELLO_TLV_RCVD_DS) ? 1 : 0; /* implicit transport address */ - if (!(tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR)) + if (!CHECK_FLAG(tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR)) trans_addr = *src; if (bad_addr(af, &trans_addr)) { log_debug("%s: lsr-id %pI4: invalid transport address %s", @@ -223,7 +222,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, * (i.e., MUST discard the targeted Hello if it failed the * check)". */ - if (flags & F_HELLO_TARGETED) { + if (CHECK_FLAG(flags, F_HELLO_TARGETED)) { log_debug("%s: lsr-id %pI4: invalid targeted hello transport address %s", __func__, &lsr_id, log_addr(af, &trans_addr)); return; @@ -232,7 +231,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, } memset(&source, 0, sizeof(source)); - if (flags & F_HELLO_TARGETED) { + if (CHECK_FLAG(flags, F_HELLO_TARGETED)) { /* * RFC 7552 - Section 5.2: * "The link-local IPv6 addresses MUST NOT be used as the @@ -247,26 +246,27 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, tnbr = tnbr_find(leconf, af, src); /* remove the dynamic tnbr if the 'R' bit was cleared */ - if (tnbr && (tnbr->flags & F_TNBR_DYNAMIC) && - !((flags & F_HELLO_REQ_TARG))) { - tnbr->flags &= ~F_TNBR_DYNAMIC; + if (tnbr && + CHECK_FLAG(tnbr->flags, F_TNBR_DYNAMIC) && + !CHECK_FLAG(flags, F_HELLO_REQ_TARG)) { + UNSET_FLAG(tnbr->flags, F_TNBR_DYNAMIC); tnbr = tnbr_check(leconf, tnbr); } if (!tnbr) { struct ldpd_af_conf *af_conf; - if (!(flags & F_HELLO_REQ_TARG)) + if (!CHECK_FLAG(flags, F_HELLO_REQ_TARG)) return; af_conf = ldp_af_conf_get(leconf, af); - if (!(af_conf->flags & F_LDPD_AF_THELLO_ACCEPT)) + if (!CHECK_FLAG(af_conf->flags, F_LDPD_AF_THELLO_ACCEPT)) return; if (ldpe_acl_check(af_conf->acl_thello_accept_from, af, src, (af == AF_INET) ? 32 : 128) != FILTER_PERMIT) return; tnbr = tnbr_new(af, src); - tnbr->flags |= F_TNBR_DYNAMIC; + SET_FLAG(tnbr->flags, F_TNBR_DYNAMIC); tnbr_update(tnbr); RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr); } @@ -308,8 +308,7 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, */ log_debug("%s: lsr-id %pI4: remote transport preference does not match the local preference", __func__, &lsr_id); if (nbr) - session_shutdown(nbr, S_TRANS_MISMTCH, msg->id, - msg->type); + session_shutdown(nbr, S_TRANS_MISMTCH, msg->id, msg->type); if (adj) adj_del(adj, S_SHUTDOWN); return; @@ -323,15 +322,13 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, switch (af) { case AF_INET: if (nbr_adj_count(nbr, AF_INET6) > 0) { - session_shutdown(nbr, S_DS_NONCMPLNCE, - msg->id, msg->type); + session_shutdown(nbr, S_DS_NONCMPLNCE, msg->id, msg->type); return; } break; case AF_INET6: if (nbr_adj_count(nbr, AF_INET) > 0) { - session_shutdown(nbr, S_DS_NONCMPLNCE, - msg->id, msg->type); + session_shutdown(nbr, S_DS_NONCMPLNCE, msg->id, msg->type); return; } break; @@ -384,16 +381,15 @@ recv_hello(struct in_addr lsr_id, struct ldp_msg *msg, int af, /* dynamic LDPv4 GTSM negotiation as per RFC 6720 */ if (nbr) { - if (flags & F_HELLO_GTSM) - nbr->flags |= F_NBR_GTSM_NEGOTIATED; + if (CHECK_FLAG(flags, F_HELLO_GTSM)) + SET_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED); else - nbr->flags &= ~F_NBR_GTSM_NEGOTIATED; + UNSET_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED); } /* update neighbor's configuration sequence number */ if (nbr && (tlvs_rcvd & F_HELLO_TLV_RCVD_CONF)) { - if (conf_seqnum > nbr->conf_seqnum && - nbr_pending_idtimer(nbr)) + if (conf_seqnum > nbr->conf_seqnum && nbr_pending_idtimer(nbr)) nbr_stop_idtimer(nbr); nbr->conf_seqnum = conf_seqnum; } @@ -465,7 +461,7 @@ gen_opt16_hello_prms_tlv(struct ibuf *buf, uint16_t type, uint8_t *value) static int gen_ds_hello_prms_tlv(struct ibuf *buf, uint32_t value) { - if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) + if (CHECK_FLAG(leconf->flags, F_LDPD_DS_CISCO_INTEROP)) value = htonl(value); else value = htonl(value << 28); @@ -533,26 +529,26 @@ tlv_decode_opt_hello_prms(char *buf, uint16_t len, int *tlvs_rcvd, int af, return (-1); if (af != AF_INET) return (-1); - if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR) + if (CHECK_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR)) break; memcpy(&addr->v4, buf, sizeof(addr->v4)); - *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR; + SET_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR); break; case TLV_TYPE_IPV6TRANSADDR: if (tlv_len != sizeof(addr->v6)) return (-1); if (af != AF_INET6) return (-1); - if (*tlvs_rcvd & F_HELLO_TLV_RCVD_ADDR) + if (CHECK_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR)) break; memcpy(&addr->v6, buf, sizeof(addr->v6)); - *tlvs_rcvd |= F_HELLO_TLV_RCVD_ADDR; + SET_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_ADDR); break; case TLV_TYPE_CONFIG: if (tlv_len != sizeof(uint32_t)) return (-1); memcpy(conf_number, buf, sizeof(uint32_t)); - *tlvs_rcvd |= F_HELLO_TLV_RCVD_CONF; + SET_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_CONF); break; case TLV_TYPE_DUALSTACK: if (tlv_len != sizeof(uint32_t)) @@ -566,19 +562,18 @@ tlv_decode_opt_hello_prms(char *buf, uint16_t len, int *tlvs_rcvd, int af, if (!ldp_is_dual_stack(leconf)) break; /* Shame on you, Cisco! */ - if (leconf->flags & F_LDPD_DS_CISCO_INTEROP) { - memcpy(trans_pref, buf + sizeof(uint16_t), - sizeof(uint16_t)); + if (CHECK_FLAG(leconf->flags, F_LDPD_DS_CISCO_INTEROP)) { + memcpy(trans_pref, buf + sizeof(uint16_t), sizeof(uint16_t)); *trans_pref = ntohs(*trans_pref); } else { memcpy(trans_pref, buf , sizeof(uint16_t)); *trans_pref = ntohs(*trans_pref) >> 12; } - *tlvs_rcvd |= F_HELLO_TLV_RCVD_DS; + SET_FLAG(*tlvs_rcvd, F_HELLO_TLV_RCVD_DS); break; default: /* if unknown flag set, ignore TLV */ - if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) return (-1); break; } diff --git a/ldpd/init.c b/ldpd/init.c index 15d653b747c0..f0cb98e5c03e 100644 --- a/ldpd/init.c +++ b/ldpd/init.c @@ -31,13 +31,13 @@ send_init(struct nbr *nbr) if ((buf = ibuf_open(size)) == NULL) fatal(__func__); - err |= gen_ldp_hdr(buf, size); + SET_FLAG(err, gen_ldp_hdr(buf, size)); size -= LDP_HDR_SIZE; - err |= gen_msg_hdr(buf, MSG_TYPE_INIT, size); - err |= gen_init_prms_tlv(buf, nbr); - err |= gen_cap_dynamic_tlv(buf); - err |= gen_cap_twcard_tlv(buf, 1); - err |= gen_cap_unotif_tlv(buf, 1); + SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_INIT, size)); + SET_FLAG(err, gen_init_prms_tlv(buf, nbr)); + SET_FLAG(err, gen_cap_dynamic_tlv(buf)); + SET_FLAG(err, gen_cap_twcard_tlv(buf, 1)); + SET_FLAG(err, gen_cap_unotif_tlv(buf, 1)); if (err) { ibuf_free(buf); return; @@ -121,62 +121,56 @@ recv_init(struct nbr *nbr, char *buf, uint16_t len) return (-1); case TLV_TYPE_DYNAMIC_CAP: if (tlv_len != CAP_TLV_DYNAMIC_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } - if (caps_rcvd & F_CAP_TLV_RCVD_DYNAMIC) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_DYNAMIC)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } - caps_rcvd |= F_CAP_TLV_RCVD_DYNAMIC; + SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_DYNAMIC); - nbr->flags |= F_NBR_CAP_DYNAMIC; + SET_FLAG(nbr->flags, F_NBR_CAP_DYNAMIC); log_debug("%s: lsr-id %pI4 announced the Dynamic Capability Announcement capability", __func__, &nbr->id); break; case TLV_TYPE_TWCARD_CAP: if (tlv_len != CAP_TLV_TWCARD_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } - if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } - caps_rcvd |= F_CAP_TLV_RCVD_TWCARD; + SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD); - nbr->flags |= F_NBR_CAP_TWCARD; + SET_FLAG(nbr->flags, F_NBR_CAP_TWCARD); log_debug("%s: lsr-id %pI4 announced the Typed Wildcard FEC capability", __func__, &nbr->id); break; case TLV_TYPE_UNOTIF_CAP: if (tlv_len != CAP_TLV_UNOTIF_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } - if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } - caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF; + SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF); - nbr->flags |= F_NBR_CAP_UNOTIF; + SET_FLAG(nbr->flags, F_NBR_CAP_UNOTIF); log_debug("%s: lsr-id %pI4 announced the Unrecognized Notification capability", __func__, &nbr->id); break; default: - if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) send_notification_rtlvs(nbr, S_UNSSUPORTDCAP, msg.id, msg.type, tlv_type, tlv_len, buf); /* ignore unknown tlv */ @@ -217,16 +211,16 @@ send_capability(struct nbr *nbr, uint16_t capability, int enable) if ((buf = ibuf_open(size)) == NULL) fatal(__func__); - err |= gen_ldp_hdr(buf, size); + SET_FLAG(err, gen_ldp_hdr(buf, size)); size -= LDP_HDR_SIZE; - err |= gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size); + SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_CAPABILITY, size)); switch (capability) { case TLV_TYPE_TWCARD_CAP: - err |= gen_cap_twcard_tlv(buf, enable); + SET_FLAG(err, gen_cap_twcard_tlv(buf, enable)); break; case TLV_TYPE_UNOTIF_CAP: - err |= gen_cap_unotif_tlv(buf, enable); + SET_FLAG(err, gen_cap_unotif_tlv(buf, enable)); break; case TLV_TYPE_DYNAMIC_CAP: /* @@ -288,52 +282,47 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) switch (tlv_type) { case TLV_TYPE_TWCARD_CAP: if (tlv_len != CAP_TLV_TWCARD_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } - if (caps_rcvd & F_CAP_TLV_RCVD_TWCARD) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } - caps_rcvd |= F_CAP_TLV_RCVD_TWCARD; + SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_TWCARD); memcpy(&reserved, buf, sizeof(reserved)); enable = reserved & STATE_BIT; if (enable) - nbr->flags |= F_NBR_CAP_TWCARD; + SET_FLAG(nbr->flags, F_NBR_CAP_TWCARD); else - nbr->flags &= ~F_NBR_CAP_TWCARD; + UNSET_FLAG(nbr->flags, F_NBR_CAP_TWCARD); log_debug("%s: lsr-id %pI4 %s the Typed Wildcard FEC capability", __func__, &nbr->id, (enable) ? "announced" : "withdrew"); break; case TLV_TYPE_UNOTIF_CAP: if (tlv_len != CAP_TLV_UNOTIF_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } - if (caps_rcvd & F_CAP_TLV_RCVD_UNOTIF) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + if (CHECK_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF)) { + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } - caps_rcvd |= F_CAP_TLV_RCVD_UNOTIF; + SET_FLAG(caps_rcvd, F_CAP_TLV_RCVD_UNOTIF); memcpy(&reserved, buf, sizeof(reserved)); enable = reserved & STATE_BIT; if (enable) - nbr->flags |= F_NBR_CAP_UNOTIF; + SET_FLAG(nbr->flags, F_NBR_CAP_UNOTIF); else - nbr->flags &= ~F_NBR_CAP_UNOTIF; + UNSET_FLAG(nbr->flags, F_NBR_CAP_UNOTIF); log_debug("%s: lsr-id %pI4 %s the Unrecognized Notification capability", __func__, - &nbr->id, (enable) ? "announced" : - "withdrew"); + &nbr->id, (enable) ? "announced" : "withdrew"); break; case TLV_TYPE_DYNAMIC_CAP: /* @@ -346,7 +335,7 @@ recv_capability(struct nbr *nbr, char *buf, uint16_t len) */ /* FALLTHROUGH */ default: - if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) send_notification_rtlvs(nbr, S_UNSSUPORTDCAP, msg.id, msg.type, tlv_type, tlv_len, buf); /* ignore unknown tlv */ diff --git a/ldpd/interface.c b/ldpd/interface.c index 5d1859298ea9..f0e70cbacce1 100644 --- a/ldpd/interface.c +++ b/ldpd/interface.c @@ -22,7 +22,7 @@ static struct if_addr *if_addr_lookup(struct if_addr_head *, struct kaddr *); static int if_start(struct iface *, int); static int if_reset(struct iface *, int); static void if_update_af(struct iface_af *); -static void if_hello_timer(struct thread *thread); +static void if_hello_timer(struct event *thread); static void if_start_hello_timer(struct iface_af *); static void if_stop_hello_timer(struct iface_af *); static int if_join_ipv4_group(struct iface *, struct in_addr *); @@ -32,7 +32,7 @@ static int if_leave_ipv6_group(struct iface *, struct in6_addr *); static int ldp_sync_fsm_init(struct iface *iface, int state); static int ldp_sync_act_iface_start_sync(struct iface *iface); -static void iface_wait_for_ldp_sync_timer(struct thread *thread); +static void iface_wait_for_ldp_sync_timer(struct event *thread); static void start_wait_for_ldp_sync_timer(struct iface *iface); static void stop_wait_for_ldp_sync_timer(struct iface *iface); static int ldp_sync_act_ldp_start_sync(struct iface *iface); @@ -138,14 +138,13 @@ void if_update_info(struct iface *iface, struct kif *kif) { /* get type */ - if (kif->flags & IFF_POINTOPOINT) + if (CHECK_FLAG(kif->flags, IFF_POINTOPOINT)) iface->type = IF_TYPE_POINTOPOINT; - if (kif->flags & IFF_BROADCAST && - kif->flags & IFF_MULTICAST) + if (CHECK_FLAG(kif->flags, IFF_BROADCAST) && + CHECK_FLAG(kif->flags, IFF_MULTICAST)) iface->type = IF_TYPE_BROADCAST; - if (ldpd_process == PROC_LDP_ENGINE && iface->operative && - !kif->operative) + if (ldpd_process == PROC_LDP_ENGINE && iface->operative && !kif->operative) ldp_sync_fsm(iface, LDP_SYNC_EVT_IFACE_SHUTDOWN); /* get index and flags */ @@ -276,8 +275,7 @@ if_start(struct iface *iface, int af) struct iface_af *ia; struct timeval now; - log_debug("%s: %s address-family %s", __func__, iface->name, - af_name(af)); + log_debug("%s: %s address-family %s", __func__, iface->name, af_name(af)); ia = iface_af_get(iface, af); @@ -444,9 +442,9 @@ if_get_wait_for_sync_interval(void) /* timers */ /* ARGSUSED */ -static void if_hello_timer(struct thread *thread) +static void if_hello_timer(struct event *thread) { - struct iface_af *ia = THREAD_ARG(thread); + struct iface_af *ia = EVENT_ARG(thread); ia->hello_timer = NULL; send_hello(HELLO_LINK, ia, NULL); @@ -456,15 +454,15 @@ static void if_hello_timer(struct thread *thread) static void if_start_hello_timer(struct iface_af *ia) { - THREAD_OFF(ia->hello_timer); - thread_add_timer(master, if_hello_timer, ia, if_get_hello_interval(ia), - &ia->hello_timer); + EVENT_OFF(ia->hello_timer); + event_add_timer(master, if_hello_timer, ia, if_get_hello_interval(ia), + &ia->hello_timer); } static void if_stop_hello_timer(struct iface_af *ia) { - THREAD_OFF(ia->hello_timer); + EVENT_OFF(ia->hello_timer); } struct ctl_iface * @@ -533,7 +531,7 @@ ldp_sync_to_ctl(struct iface *iface) ictl.timer_running = iface->ldp_sync.wait_for_sync_timer ? true : false; ictl.wait_time_remaining = - thread_timer_remain_second(iface->ldp_sync.wait_for_sync_timer); + event_timer_remain_second(iface->ldp_sync.wait_for_sync_timer); memset(&ictl.peer_ldp_id, 0, sizeof(ictl.peer_ldp_id)); @@ -560,8 +558,7 @@ if_join_ipv4_group(struct iface *iface, struct in_addr *addr) { struct in_addr if_addr; - log_debug("%s: interface %s addr %pI4", __func__, iface->name, - addr); + log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr); if_addr.s_addr = if_get_ipv4_addr(iface); @@ -579,8 +576,7 @@ if_leave_ipv4_group(struct iface *iface, struct in_addr *addr) { struct in_addr if_addr; - log_debug("%s: interface %s addr %pI4", __func__, iface->name, - addr); + log_debug("%s: interface %s addr %pI4", __func__, iface->name, addr); if_addr.s_addr = if_get_ipv4_addr(iface); @@ -720,9 +716,9 @@ ldp_sync_act_iface_start_sync(struct iface *iface) return (0); } -static void iface_wait_for_ldp_sync_timer(struct thread *thread) +static void iface_wait_for_ldp_sync_timer(struct event *thread) { - struct iface *iface = THREAD_ARG(thread); + struct iface *iface = EVENT_ARG(thread); ldp_sync_fsm(iface, LDP_SYNC_EVT_LDP_SYNC_COMPLETE); } @@ -732,15 +728,15 @@ static void start_wait_for_ldp_sync_timer(struct iface *iface) if (iface->ldp_sync.wait_for_sync_timer) return; - THREAD_OFF(iface->ldp_sync.wait_for_sync_timer); - thread_add_timer(master, iface_wait_for_ldp_sync_timer, iface, + EVENT_OFF(iface->ldp_sync.wait_for_sync_timer); + event_add_timer(master, iface_wait_for_ldp_sync_timer, iface, if_get_wait_for_sync_interval(), &iface->ldp_sync.wait_for_sync_timer); } static void stop_wait_for_ldp_sync_timer(struct iface *iface) { - THREAD_OFF(iface->ldp_sync.wait_for_sync_timer); + EVENT_OFF(iface->ldp_sync.wait_for_sync_timer); } static int diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c index 4664b1f894a8..ce038acdcb0a 100644 --- a/ldpd/l2vpn.c +++ b/ldpd/l2vpn.c @@ -161,7 +161,7 @@ l2vpn_if_update(struct l2vpn_if *lif) fec.type = MAP_TYPE_PWID; fec.fec.pwid.type = l2vpn->pw_type; fec.fec.pwid.group_id = 0; - fec.flags |= F_MAP_PW_ID; + SET_FLAG(fec.flags, F_MAP_PW_ID); fec.fec.pwid.pwid = pw->pwid; send_mac_withdrawal(nbr, &fec, lif->mac); @@ -274,17 +274,17 @@ l2vpn_pw_reset(struct l2vpn_pw *pw) pw->local_status = PW_FORWARDING; pw->remote_status = PW_NOT_FORWARDING; - if (pw->flags & F_PW_CWORD_CONF) - pw->flags |= F_PW_CWORD; + if (CHECK_FLAG(pw->flags, F_PW_CWORD_CONF)) + SET_FLAG(pw->flags, F_PW_CWORD); else - pw->flags &= ~F_PW_CWORD; + UNSET_FLAG(pw->flags, F_PW_CWORD); - if (pw->flags & F_PW_STATUSTLV_CONF) - pw->flags |= F_PW_STATUSTLV; + if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV_CONF)) + SET_FLAG(pw->flags, F_PW_STATUSTLV); else - pw->flags &= ~F_PW_STATUSTLV; + UNSET_FLAG(pw->flags, F_PW_STATUSTLV); - if (pw->flags & F_PW_STATUSTLV_CONF) { + if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV_CONF)) { struct fec_node *fn; struct fec fec; l2vpn_pw_fec(pw, &fec); @@ -300,8 +300,7 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) { /* check for a remote label */ if (fnh->remote_label == NO_LABEL) { - log_warnx("%s: pseudowire %s: no remote label", __func__, - pw->ifname); + log_warnx("%s: pseudowire %s: no remote label", __func__, pw->ifname); pw->reason = F_PW_NO_REMOTE_LABEL; return (0); } @@ -315,10 +314,9 @@ l2vpn_pw_ok(struct l2vpn_pw *pw, struct fec_nh *fnh) } /* check pw status if applicable */ - if ((pw->flags & F_PW_STATUSTLV) && + if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV) && pw->remote_status != PW_FORWARDING) { - log_warnx("%s: pseudowire %s: remote end is down", __func__, - pw->ifname); + log_warnx("%s: pseudowire %s: remote end is down", __func__, pw->ifname); pw->reason = F_PW_REMOTE_NOT_FWD; return (0); } @@ -345,34 +343,34 @@ l2vpn_pw_negotiate(struct lde_nbr *ln, struct fec_node *fn, struct map *map) /* RFC4447 - Section 6.2: control word negotiation */ if (fec_find(&ln->sent_map, &fn->fec)) { - if ((map->flags & F_MAP_PW_CWORD) && - !(pw->flags & F_PW_CWORD_CONF)) { + if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD) && + !CHECK_FLAG(pw->flags, F_PW_CWORD_CONF)) { /* ignore the received label mapping */ return (1); - } else if (!(map->flags & F_MAP_PW_CWORD) && - (pw->flags & F_PW_CWORD_CONF)) { + } else if (!CHECK_FLAG(map->flags, F_MAP_PW_CWORD) && + CHECK_FLAG(pw->flags, F_PW_CWORD_CONF)) { /* append a "Wrong C-bit" status code */ st.status_code = S_WRONG_CBIT; st.msg_id = map->msg_id; st.msg_type = htons(MSG_TYPE_LABELMAPPING); lde_send_labelwithdraw(ln, fn, NULL, &st); - pw->flags &= ~F_PW_CWORD; + UNSET_FLAG(pw->flags, F_PW_CWORD); lde_send_labelmapping(ln, fn, 1); } - } else if (map->flags & F_MAP_PW_CWORD) { - if (pw->flags & F_PW_CWORD_CONF) - pw->flags |= F_PW_CWORD; + } else if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD)) { + if (CHECK_FLAG(pw->flags, F_PW_CWORD_CONF)) + SET_FLAG(pw->flags, F_PW_CWORD); else /* act as if no label mapping had been received */ return (1); } else - pw->flags &= ~F_PW_CWORD; + UNSET_FLAG(pw->flags, F_PW_CWORD); /* RFC4447 - Section 5.4.3: pseudowire status negotiation */ if (fec_find(&ln->recv_map, &fn->fec) == NULL && - !(map->flags & F_MAP_PW_STATUS)) - pw->flags &= ~F_PW_STATUSTLV; + !CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) + UNSET_FLAG(pw->flags, F_PW_STATUSTLV); return (0); } @@ -385,12 +383,11 @@ l2vpn_send_pw_status(struct lde_nbr *ln, uint32_t status, struct fec *fec) memset(&nm, 0, sizeof(nm)); nm.status_code = S_PW_STATUS; nm.pw_status = status; - nm.flags |= F_NOTIF_PW_STATUS; + SET_FLAG(nm.flags, F_NOTIF_PW_STATUS); lde_fec2map(fec, &nm.fec); - nm.flags |= F_NOTIF_FEC; + SET_FLAG(nm.flags, F_NOTIF_FEC); - lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, - sizeof(nm)); + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, sizeof(nm)); } void @@ -402,14 +399,13 @@ l2vpn_send_pw_status_wcard(struct lde_nbr *ln, uint32_t status, memset(&nm, 0, sizeof(nm)); nm.status_code = S_PW_STATUS; nm.pw_status = status; - nm.flags |= F_NOTIF_PW_STATUS; + SET_FLAG(nm.flags, F_NOTIF_PW_STATUS); nm.fec.type = MAP_TYPE_PWID; nm.fec.fec.pwid.type = pw_type; nm.fec.fec.pwid.group_id = group_id; - nm.flags |= F_NOTIF_FEC; + SET_FLAG(nm.flags, F_NOTIF_FEC); - lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, - sizeof(nm)); + lde_imsg_compose_ldpe(IMSG_NOTIFICATION_SEND, ln->peerid, 0, &nm, sizeof(nm)); } void @@ -421,7 +417,7 @@ l2vpn_recv_pw_status(struct lde_nbr *ln, struct notify_msg *nm) struct l2vpn_pw *pw; if (nm->fec.type == MAP_TYPE_TYPED_WCARD || - !(nm->fec.flags & F_MAP_PW_ID)) { + !CHECK_FLAG(nm->fec.flags, F_MAP_PW_ID)) { l2vpn_recv_pw_status_wcard(ln, nm); return; } @@ -540,7 +536,7 @@ l2vpn_pw_status_update(struct zapi_pw_status *zpw) if (ln == NULL) return (0); l2vpn_pw_fec(pw, &fec); - if (pw->flags & F_PW_STATUSTLV) + if (CHECK_FLAG(pw->flags, F_PW_STATUSTLV)) l2vpn_send_pw_status(ln, local_status, &fec); else { struct fec_node *fn; @@ -611,8 +607,7 @@ l2vpn_binding_ctl(pid_t pid) pwctl.local_label = fn->local_label; pwctl.local_gid = 0; pwctl.local_ifmtu = pw->l2vpn->mtu; - pwctl.local_cword = (pw->flags & F_PW_CWORD_CONF) ? - 1 : 0; + pwctl.local_cword = CHECK_FLAG(pw->flags, F_PW_CWORD_CONF) ? 1 : 0; pwctl.reason = pw->reason; } else pwctl.local_label = NO_LABEL; @@ -624,11 +619,10 @@ l2vpn_binding_ctl(pid_t pid) if (me) { pwctl.remote_label = me->map.label; pwctl.remote_gid = me->map.fec.pwid.group_id; - if (me->map.flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(me->map.flags, F_MAP_PW_IFMTU)) pwctl.remote_ifmtu = me->map.fec.pwid.ifmtu; if (pw) - pwctl.remote_cword = (pw->flags & F_PW_CWORD) ? - 1 : 0; + pwctl.remote_cword = CHECK_FLAG(pw->flags, F_PW_CWORD) ? 1 : 0; lde_imsg_compose_ldpe(IMSG_CTL_SHOW_L2VPN_BINDING, 0, pid, &pwctl, sizeof(pwctl)); diff --git a/ldpd/labelmapping.c b/ldpd/labelmapping.c index dcaf1cc10b37..3c5a5d999160 100644 --- a/ldpd/labelmapping.c +++ b/ldpd/labelmapping.c @@ -47,12 +47,11 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) while ((me = TAILQ_FIRST(mh)) != NULL) { /* generate pdu */ if (first) { - if ((buf = ibuf_open(nbr->max_pdu_len + - LDP_HDR_DEAD_LEN)) == NULL) + if ((buf = ibuf_open(nbr->max_pdu_len + LDP_HDR_DEAD_LEN)) == NULL) fatal(__func__); /* real size will be set up later */ - err |= gen_ldp_hdr(buf, 0); + SET_FLAG(err, gen_ldp_hdr(buf, 0)); size = LDP_HDR_SIZE; first = 0; @@ -63,9 +62,9 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) msg_size += len_fec_tlv(&me->map); if (me->map.label != NO_LABEL) msg_size += LABEL_TLV_SIZE; - if (me->map.flags & F_MAP_REQ_ID) + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) msg_size += REQID_TLV_SIZE; - if (me->map.flags & F_MAP_STATUS) + if (CHECK_FLAG(me->map.flags, F_MAP_STATUS)) msg_size += STATUS_SIZE; /* maximum pdu length exceeded, we need a new ldp pdu */ @@ -78,17 +77,17 @@ send_labelmessage(struct nbr *nbr, uint16_t type, struct mapping_head *mh) size += msg_size; /* append message and tlvs */ - err |= gen_msg_hdr(buf, type, msg_size); - err |= gen_fec_tlv(buf, &me->map); + SET_FLAG(err, gen_msg_hdr(buf, type, msg_size)); + SET_FLAG(err, gen_fec_tlv(buf, &me->map)); if (me->map.label != NO_LABEL) - err |= gen_label_tlv(buf, me->map.label); - if (me->map.flags & F_MAP_REQ_ID) - err |= gen_reqid_tlv(buf, me->map.requestid); - if (me->map.flags & F_MAP_PW_STATUS) - err |= gen_pw_status_tlv(buf, me->map.pw_status); - if (me->map.flags & F_MAP_STATUS) - err |= gen_status_tlv(buf, me->map.st.status_code, - me->map.st.msg_id, me->map.st.msg_type); + SET_FLAG(err, gen_label_tlv(buf, me->map.label)); + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) + SET_FLAG(err, gen_reqid_tlv(buf, me->map.requestid)); + if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS)) + SET_FLAG(err, gen_pw_status_tlv(buf, me->map.pw_status)); + if (CHECK_FLAG(me->map.flags, F_MAP_STATUS)) + SET_FLAG(err, gen_status_tlv(buf, me->map.st.status_code, + me->map.st.msg_id, me->map.st.msg_type)); if (err) { ibuf_free(buf); mapping_list_clr(mh); @@ -172,15 +171,13 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) memset(&map, 0, sizeof(map)); map.msg_id = msg.id; - if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, - &map)) == -1) + if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, feclen, &map)) == -1) goto err; if (map.type == MAP_TYPE_PWID && - !(map.flags & F_MAP_PW_ID) && + !CHECK_FLAG(map.flags, F_MAP_PW_ID) && type != MSG_TYPE_LABELWITHDRAW && type != MSG_TYPE_LABELRELEASE) { - send_notification(nbr->tcp, S_MISS_MSG, msg.id, - msg.type); + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); goto err; } @@ -193,8 +190,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELMAPPING: case MSG_TYPE_LABELREQUEST: case MSG_TYPE_LABELABORTREQ: - session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, - msg.type); + session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type); goto err; default: break; @@ -211,8 +207,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) switch (type) { case MSG_TYPE_LABELMAPPING: case MSG_TYPE_LABELABORTREQ: - session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, - msg.type); + session_shutdown(nbr, S_UNKNOWN_FEC, msg.id, msg.type); goto err; default: break; @@ -223,8 +218,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) * LDP supports the use of multiple FEC Elements per * FEC for the Label Mapping message only. */ - if (type != MSG_TYPE_LABELMAPPING && - tlen != feclen) { + if (type != MSG_TYPE_LABELMAPPING && tlen != feclen) { session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } @@ -262,10 +256,10 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) * For Label Mapping messages the Label TLV is mandatory and * should appear right after the FEC TLV. */ - if (current_tlv == 1 && type == MSG_TYPE_LABELMAPPING && - !(tlv_type & TLV_TYPE_GENERICLABEL)) { - send_notification(nbr->tcp, S_MISS_MSG, msg.id, - msg.type); + if (current_tlv == 1 && + type == MSG_TYPE_LABELMAPPING && + !CHECK_FLAG(tlv_type, TLV_TYPE_GENERICLABEL)) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); goto err; } @@ -275,12 +269,11 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELMAPPING: case MSG_TYPE_LABELREQUEST: if (tlv_len != REQID_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } - flags |= F_MAP_REQ_ID; + SET_FLAG(flags, F_MAP_REQ_ID); memcpy(&reqbuf, buf, sizeof(reqbuf)); reqid = ntohl(reqbuf); break; @@ -299,8 +292,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELWITHDRAW: case MSG_TYPE_LABELRELEASE: if (tlv_len != LABEL_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } @@ -312,8 +304,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) label != MPLS_LABEL_IPV4_EXPLICIT_NULL && label != MPLS_LABEL_IPV6_EXPLICIT_NULL && label != MPLS_LABEL_IMPLICIT_NULL)) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } break; @@ -329,8 +320,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) case MSG_TYPE_LABELWITHDRAW: case MSG_TYPE_LABELRELEASE: /* unsupported */ - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; break; default: @@ -340,8 +330,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) break; case TLV_TYPE_STATUS: if (tlv_len != STATUS_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } /* ignore */ @@ -350,12 +339,11 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) switch (type) { case MSG_TYPE_LABELMAPPING: if (tlv_len != PW_STATUS_TLV_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); goto err; } - flags |= F_MAP_PW_STATUS; + SET_FLAG(flags, F_MAP_PW_STATUS); memcpy(&statusbuf, buf, sizeof(statusbuf)); pw_status = ntohl(statusbuf); break; @@ -365,7 +353,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) } break; default: - if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) + if (!CHECK_FLAG(ntohs(tlv.type), UNKNOWN_FLAG)) send_notification_rtlvs(nbr, S_UNKNOWN_TLV, msg.id, msg.type, tlv_type, tlv_len, buf); /* ignore unknown tlv */ @@ -380,14 +368,13 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) while ((me = TAILQ_FIRST(&mh)) != NULL) { int imsg_type = IMSG_NONE; - me->map.flags |= flags; + SET_FLAG(me->map.flags, flags); switch (me->map.type) { case MAP_TYPE_PREFIX: switch (me->map.fec.prefix.af) { case AF_INET: if (label == MPLS_LABEL_IPV6_EXPLICIT_NULL) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } if (!nbr->v4_enabled) @@ -395,8 +382,7 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) break; case AF_INET6: if (label == MPLS_LABEL_IPV4_EXPLICIT_NULL) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } if (!nbr->v6_enabled) @@ -408,18 +394,17 @@ recv_labelmessage(struct nbr *nbr, char *buf, uint16_t len, uint16_t type) break; case MAP_TYPE_PWID: if (label <= MPLS_LABEL_RESERVED_MAX) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, - msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); goto err; } - if (me->map.flags & F_MAP_PW_STATUS) + if (CHECK_FLAG(me->map.flags, F_MAP_PW_STATUS)) me->map.pw_status = pw_status; break; default: break; } me->map.label = label; - if (me->map.flags & F_MAP_REQ_ID) + if (CHECK_FLAG(me->map.flags, F_MAP_REQ_ID)) me->map.requestid = reqid; log_msg_mapping(0, type, nbr, &me->map); @@ -513,11 +498,11 @@ len_fec_tlv(struct map *map) break; case MAP_TYPE_PWID: len += FEC_PWID_ELM_MIN_LEN; - if (map->flags & F_MAP_PW_ID) + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) len += PW_STATUS_TLV_LEN; - if (map->flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) len += FEC_SUBTLV_IFMTU_SIZE; - if (map->flags & F_MAP_PW_STATUS) + if (CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) len += PW_STATUS_TLV_SIZE; break; case MAP_TYPE_TYPED_WCARD: @@ -552,15 +537,15 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) switch (map->type) { case MAP_TYPE_WILDCARD: ft.length = htons(sizeof(uint8_t)); - err |= ibuf_add(buf, &ft, sizeof(ft)); - err |= ibuf_add(buf, &map->type, sizeof(map->type)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type))); break; case MAP_TYPE_PREFIX: len = PREFIX_SIZE(map->fec.prefix.prefixlen); ft.length = htons(sizeof(map->type) + sizeof(family) + sizeof(map->fec.prefix.prefixlen) + len); - err |= ibuf_add(buf, &ft, sizeof(ft)); - err |= ibuf_add(buf, &map->type, sizeof(map->type)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(map->type))); switch (map->fec.prefix.af) { case AF_INET: family = htons(AF_IPV4); @@ -572,45 +557,45 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) fatalx("gen_fec_tlv: unknown af"); break; } - err |= ibuf_add(buf, &family, sizeof(family)); - err |= ibuf_add(buf, &map->fec.prefix.prefixlen, - sizeof(map->fec.prefix.prefixlen)); + SET_FLAG(err, ibuf_add(buf, &family, sizeof(family))); + SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefixlen, + sizeof(map->fec.prefix.prefixlen))); if (len) - err |= ibuf_add(buf, &map->fec.prefix.prefix, len); + SET_FLAG(err, ibuf_add(buf, &map->fec.prefix.prefix, len)); break; case MAP_TYPE_PWID: - if (map->flags & F_MAP_PW_ID) + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) pw_len += FEC_PWID_SIZE; - if (map->flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) pw_len += FEC_SUBTLV_IFMTU_SIZE; len = FEC_PWID_ELM_MIN_LEN + pw_len; ft.length = htons(len); - err |= ibuf_add(buf, &ft, sizeof(ft)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); - err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t))); pw_type = map->fec.pwid.type; - if (map->flags & F_MAP_PW_CWORD) - pw_type |= CONTROL_WORD_FLAG; + if (CHECK_FLAG(map->flags, F_MAP_PW_CWORD)) + SET_FLAG(pw_type, CONTROL_WORD_FLAG); pw_type = htons(pw_type); - err |= ibuf_add(buf, &pw_type, sizeof(uint16_t)); - err |= ibuf_add(buf, &pw_len, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t))); + SET_FLAG(err, ibuf_add(buf, &pw_len, sizeof(uint8_t))); group_id = htonl(map->fec.pwid.group_id); - err |= ibuf_add(buf, &group_id, sizeof(uint32_t)); - if (map->flags & F_MAP_PW_ID) { + SET_FLAG(err, ibuf_add(buf, &group_id, sizeof(uint32_t))); + if (CHECK_FLAG(map->flags, F_MAP_PW_ID)) { pwid = htonl(map->fec.pwid.pwid); - err |= ibuf_add(buf, &pwid, sizeof(uint32_t)); + SET_FLAG(err, ibuf_add(buf, &pwid, sizeof(uint32_t))); } - if (map->flags & F_MAP_PW_IFMTU) { + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) { struct subtlv stlv; stlv.type = SUBTLV_IFMTU; stlv.length = FEC_SUBTLV_IFMTU_SIZE; - err |= ibuf_add(buf, &stlv, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &stlv, sizeof(uint16_t))); ifmtu = htons(map->fec.pwid.ifmtu); - err |= ibuf_add(buf, &ifmtu, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &ifmtu, sizeof(uint16_t))); } break; case MAP_TYPE_TYPED_WCARD: @@ -624,14 +609,14 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) fatalx("gen_fec_tlv: unexpected fec type"); } ft.length = htons(len); - err |= ibuf_add(buf, &ft, sizeof(ft)); - err |= ibuf_add(buf, &map->type, sizeof(uint8_t)); - err |= ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &ft, sizeof(ft))); + SET_FLAG(err, ibuf_add(buf, &map->type, sizeof(uint8_t))); + SET_FLAG(err, ibuf_add(buf, &map->fec.twcard.type, sizeof(uint8_t))); switch (map->fec.twcard.type) { case MAP_TYPE_PREFIX: twcard_len = sizeof(uint16_t); - err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t))); switch (map->fec.twcard.u.prefix_af) { case AF_INET: @@ -645,13 +630,13 @@ gen_fec_tlv(struct ibuf *buf, struct map *map) break; } - err |= ibuf_add(buf, &family, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &family, sizeof(uint16_t))); break; case MAP_TYPE_PWID: twcard_len = sizeof(uint16_t); - err |= ibuf_add(buf, &twcard_len, sizeof(uint8_t)); + SET_FLAG(err, ibuf_add(buf, &twcard_len, sizeof(uint8_t))); pw_type = htons(map->fec.twcard.u.pw_type); - err |= ibuf_add(buf, &pw_type, sizeof(uint16_t)); + SET_FLAG(err, ibuf_add(buf, &pw_type, sizeof(uint16_t))); break; default: fatalx("gen_fec_tlv: unexpected fec type"); @@ -679,21 +664,18 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, if (len == FEC_ELM_WCARD_LEN) return (off); else { - session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); return (-1); } break; case MAP_TYPE_PREFIX: if (len < FEC_ELM_PREFIX_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } /* Address Family */ - memcpy(&map->fec.prefix.af, buf + off, - sizeof(map->fec.prefix.af)); + memcpy(&map->fec.prefix.af, buf + off, sizeof(map->fec.prefix.af)); off += sizeof(map->fec.prefix.af); map->fec.prefix.af = ntohs(map->fec.prefix.af); switch (map->fec.prefix.af) { @@ -704,8 +686,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, map->fec.prefix.af = AF_INET6; break; default: - send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id, - msg->type); + send_notification(nbr->tcp, S_UNSUP_ADDR, msg->id, msg->type); return (-1); } @@ -716,19 +697,16 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, && map->fec.prefix.prefixlen > IPV4_MAX_BITLEN) || (map->fec.prefix.af == AF_IPV6 && map->fec.prefix.prefixlen > IPV6_MAX_BITLEN)) { - session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, - msg->type); + 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); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } /* Prefix */ - memset(&map->fec.prefix.prefix, 0, - sizeof(map->fec.prefix.prefix)); + memset(&map->fec.prefix.prefix, 0, sizeof(map->fec.prefix.prefix)); memcpy(&map->fec.prefix.prefix, buf + off, PREFIX_SIZE(map->fec.prefix.prefixlen)); @@ -739,17 +717,16 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, return (off + PREFIX_SIZE(map->fec.prefix.prefixlen)); case MAP_TYPE_PWID: if (len < FEC_PWID_ELM_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } /* PW type */ memcpy(&map->fec.pwid.type, buf + off, sizeof(uint16_t)); map->fec.pwid.type = ntohs(map->fec.pwid.type); - if (map->fec.pwid.type & CONTROL_WORD_FLAG) { - map->flags |= F_MAP_PW_CWORD; - map->fec.pwid.type &= ~CONTROL_WORD_FLAG; + if (CHECK_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG)) { + SET_FLAG(map->flags, F_MAP_PW_CWORD); + UNSET_FLAG(map->fec.pwid.type, CONTROL_WORD_FLAG); } off += sizeof(uint16_t); @@ -758,8 +735,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, off += sizeof(uint8_t); if (len != FEC_PWID_ELM_MIN_LEN + pw_len) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } @@ -773,14 +749,13 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, return (off); if (pw_len < sizeof(uint32_t)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } memcpy(&map->fec.pwid.pwid, buf + off, sizeof(uint32_t)); map->fec.pwid.pwid = ntohl(map->fec.pwid.pwid); - map->flags |= F_MAP_PW_ID; + SET_FLAG(map->flags, F_MAP_PW_ID); off += sizeof(uint32_t); pw_len -= sizeof(uint32_t); @@ -789,29 +764,26 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, struct subtlv stlv; if (pw_len < sizeof(stlv)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } memcpy(&stlv, buf + off, sizeof(stlv)); if (stlv.length > pw_len) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } switch (stlv.type) { case SUBTLV_IFMTU: if (stlv.length != FEC_SUBTLV_IFMTU_SIZE) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg->id, msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } memcpy(&map->fec.pwid.ifmtu, buf + off + SUBTLV_HDR_SIZE, sizeof(uint16_t)); map->fec.pwid.ifmtu = ntohs(map->fec.pwid.ifmtu); - map->flags |= F_MAP_PW_IFMTU; + SET_FLAG(map->flags, F_MAP_PW_IFMTU); break; default: /* ignore */ @@ -824,8 +796,7 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, return (off); case MAP_TYPE_TYPED_WCARD: if (len < FEC_ELM_TWCARD_MIN_LEN) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } @@ -834,23 +805,19 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, memcpy(&twcard_len, buf + off, sizeof(uint8_t)); off += sizeof(uint8_t); if (len != FEC_ELM_TWCARD_MIN_LEN + twcard_len) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } switch (map->fec.twcard.type) { case MAP_TYPE_PREFIX: if (twcard_len != sizeof(uint16_t)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } - memcpy(&map->fec.twcard.u.prefix_af, buf + off, - sizeof(uint16_t)); - map->fec.twcard.u.prefix_af = - ntohs(map->fec.twcard.u.prefix_af); + memcpy(&map->fec.twcard.u.prefix_af, buf + off, sizeof(uint16_t)); + map->fec.twcard.u.prefix_af = ntohs(map->fec.twcard.u.prefix_af); off += sizeof(uint16_t); switch (map->fec.twcard.u.prefix_af) { @@ -861,29 +828,24 @@ tlv_decode_fec_elm(struct nbr *nbr, struct ldp_msg *msg, char *buf, map->fec.twcard.u.prefix_af = AF_INET6; break; default: - session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg->id, msg->type); return (-1); } break; case MAP_TYPE_PWID: if (twcard_len != sizeof(uint16_t)) { - session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, - msg->type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg->id, msg->type); return (-1); } - memcpy(&map->fec.twcard.u.pw_type, buf + off, - sizeof(uint16_t)); - map->fec.twcard.u.pw_type = - ntohs(map->fec.twcard.u.pw_type); + memcpy(&map->fec.twcard.u.pw_type, buf + off, sizeof(uint16_t)); + map->fec.twcard.u.pw_type = ntohs(map->fec.twcard.u.pw_type); /* ignore the reserved bit as per RFC 6667 */ map->fec.twcard.u.pw_type &= ~PW_TWCARD_RESERVED_BIT; off += sizeof(uint16_t); break; default: - send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, - msg->type); + send_notification(nbr->tcp, S_UNKNOWN_FEC, msg->id, msg->type); return (-1); } diff --git a/ldpd/lde.c b/ldpd/lde.c index 325b33d057c5..c7e915deb332 100644 --- a/ldpd/lde.c +++ b/ldpd/lde.c @@ -30,8 +30,8 @@ #include "libfrr.h" static void lde_shutdown(void); -static void lde_dispatch_imsg(struct thread *thread); -static void lde_dispatch_parent(struct thread *thread); +static void lde_dispatch_imsg(struct event *thread); +static void lde_dispatch_parent(struct event *thread); static __inline int lde_nbr_compare(const struct lde_nbr *, const struct lde_nbr *); static struct lde_nbr *lde_nbr_new(uint32_t, struct lde_nbr *); @@ -87,7 +87,7 @@ static struct list *label_chunk_list; static struct listnode *current_label_chunk; /* Synchronous zclient to request labels */ -static struct zclient *zclient_sync; +struct zclient *zclient_sync; /* SIGINT / SIGTERM handler. */ static void @@ -134,8 +134,8 @@ lde(void) fatal(NULL); imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC); iev_main->handler_read = lde_dispatch_parent; - thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd, - &iev_main->ev_read); + event_add_read(master, iev_main->handler_read, iev_main, + iev_main->ibuf.fd, &iev_main->ev_read); iev_main->handler_write = ldp_write_handler; memset(&iev_main_sync_data, 0, sizeof(iev_main_sync_data)); @@ -145,9 +145,9 @@ lde(void) /* create base configuration */ ldeconf = config_new_empty(); - struct thread thread; - while (thread_fetch(master, &thread)) - thread_call(&thread); + struct event thread; + while (event_fetch(master, &thread)) + event_call(&thread); /* NOTREACHED */ return; @@ -232,9 +232,9 @@ lde_imsg_compose_ldpe(int type, uint32_t peerid, pid_t pid, void *data, } /* ARGSUSED */ -static void lde_dispatch_imsg(struct thread *thread) +static void lde_dispatch_imsg(struct event *thread) { - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct lde_nbr *ln; @@ -395,14 +395,14 @@ static void lde_dispatch_imsg(struct thread *thread) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); lde_shutdown(); } } /* ARGSUSED */ -static void lde_dispatch_parent(struct thread *thread) +static void lde_dispatch_parent(struct event *thread) { static struct ldpd_conf *nconf; struct iface *iface, *niface; @@ -415,7 +415,7 @@ static void lde_dispatch_parent(struct thread *thread) struct kif *kif; struct kroute *kr; int fd; - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; ssize_t n; int shut = 0; @@ -523,8 +523,8 @@ static void lde_dispatch_parent(struct thread *thread) fatal(NULL); imsg_init(&iev_ldpe->ibuf, fd); iev_ldpe->handler_read = lde_dispatch_imsg; - thread_add_read(master, iev_ldpe->handler_read, iev_ldpe, iev_ldpe->ibuf.fd, - &iev_ldpe->ev_read); + event_add_read(master, iev_ldpe->handler_read, iev_ldpe, + iev_ldpe->ibuf.fd, &iev_ldpe->ev_read); iev_ldpe->handler_write = ldp_write_handler; iev_ldpe->ev_write = NULL; break; @@ -673,8 +673,8 @@ static void lde_dispatch_parent(struct thread *thread) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); lde_shutdown(); } } @@ -2123,7 +2123,7 @@ lde_address_list_free(struct lde_nbr *ln) /* * Event callback used to retry the label-manager sync zapi session. */ -static void zclient_sync_retry(struct thread *thread) +static void zclient_sync_retry(struct event *thread) { zclient_sync_init(); } @@ -2178,7 +2178,7 @@ static void zclient_sync_init(void) zclient_sync = NULL; /* Retry using a timer */ - thread_add_timer(master, zclient_sync_retry, NULL, 1, NULL); + event_add_timer(master, zclient_sync_retry, NULL, 1, NULL); } static void diff --git a/ldpd/lde.h b/ldpd/lde.h index 3a64f0d3b9dc..2688b6a86bcb 100644 --- a/ldpd/lde.h +++ b/ldpd/lde.h @@ -132,7 +132,7 @@ struct label_chunk { extern struct ldpd_conf *ldeconf; extern struct fec_tree ft; extern struct nbr_tree lde_nbrs; -extern struct thread *gc_timer; +extern struct event *gc_timer; /* lde.c */ void lde(void); @@ -216,7 +216,7 @@ void lde_check_withdraw(struct map *, struct lde_nbr *); void lde_check_withdraw_wcard(struct map *, struct lde_nbr *); int lde_wildcard_apply(struct map *, struct fec *, struct lde_map *); -void lde_gc_timer(struct thread *thread); +void lde_gc_timer(struct event *thread); void lde_gc_start_timer(void); void lde_gc_stop_timer(void); diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c index d737e9f4166a..04bff9015856 100644 --- a/ldpd/lde_lib.c +++ b/ldpd/lde_lib.c @@ -17,18 +17,17 @@ #include "mpls.h" static __inline int fec_compare(const struct fec *, const struct fec *); -static int lde_nbr_is_nexthop(struct fec_node *, - struct lde_nbr *); -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 *, +static int lde_nbr_is_nexthop(struct fec_node *, struct lde_nbr *); +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, unsigned short); -static void fec_nh_del(struct fec_nh *); +static void fec_nh_del(struct fec_nh *); RB_GENERATE(fec_tree, fec, entry, fec_compare) -struct fec_tree ft = RB_INITIALIZER(&ft); -struct thread *gc_timer; +struct fec_tree ft = RB_INITIALIZER(&ft); +struct event *gc_timer; /* FEC tree functions */ void @@ -47,11 +46,9 @@ fec_compare(const struct fec *a, const struct fec *b) switch (a->type) { case FEC_TYPE_IPV4: - if (ntohl(a->u.ipv4.prefix.s_addr) < - ntohl(b->u.ipv4.prefix.s_addr)) + if (ntohl(a->u.ipv4.prefix.s_addr) < ntohl(b->u.ipv4.prefix.s_addr)) return (-1); - if (ntohl(a->u.ipv4.prefix.s_addr) > - ntohl(b->u.ipv4.prefix.s_addr)) + if (ntohl(a->u.ipv4.prefix.s_addr) > ntohl(b->u.ipv4.prefix.s_addr)) return (1); if (a->u.ipv4.prefixlen < b->u.ipv4.prefixlen) return (-1); @@ -79,11 +76,9 @@ fec_compare(const struct fec *a, const struct fec *b) return (-1); if (a->u.pwid.pwid > b->u.pwid.pwid) return (1); - if (ntohl(a->u.pwid.lsr_id.s_addr) < - ntohl(b->u.pwid.lsr_id.s_addr)) + if (ntohl(a->u.pwid.lsr_id.s_addr) < ntohl(b->u.pwid.lsr_id.s_addr)) return (-1); - if (ntohl(a->u.pwid.lsr_id.s_addr) > - ntohl(b->u.pwid.lsr_id.s_addr)) + if (ntohl(a->u.pwid.lsr_id.s_addr) > ntohl(b->u.pwid.lsr_id.s_addr)) return (1); return (0); } @@ -261,8 +256,7 @@ fec_add(struct fec *fec) 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)); + log_warnx("failed to add %s to ft tree", log_fec(&fn->fec)); return (fn); } @@ -338,14 +332,14 @@ lde_kernel_insert(struct fec *fec, int af, union ldpd_addr *nexthop, * installing in kernel and sending to peer */ iface = if_lookup(ldeconf, ifindex); - if ((ldeconf->flags & F_LDPD_ORDERED_CONTROL) && + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL) && !connected && iface != NULL && fec->type != FEC_TYPE_PWID) - fnh->flags |= F_FEC_NH_DEFER; + SET_FLAG(fnh->flags, F_FEC_NH_DEFER); } - fnh->flags |= F_FEC_NH_NEW; + SET_FLAG(fnh->flags, F_FEC_NH_NEW); if (connected) - fnh->flags |= F_FEC_NH_CONNECTED; + SET_FLAG(fnh->flags, F_FEC_NH_CONNECTED); } void @@ -388,22 +382,22 @@ lde_kernel_update(struct fec *fec) return; LIST_FOREACH_SAFE(fnh, &fn->nexthops, entry, safe) { - if (fnh->flags & F_FEC_NH_NEW) { - fnh->flags &= ~F_FEC_NH_NEW; + if (CHECK_FLAG(fnh->flags, F_FEC_NH_NEW)) { + UNSET_FLAG(fnh->flags, F_FEC_NH_NEW); /* * if LDP configured on interface or a static route * clear flag else treat fec as a connected route */ - if (ldeconf->flags & F_LDPD_ENABLED) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ENABLED)) { iface = if_lookup(ldeconf,fnh->ifindex); - if (fnh->flags & F_FEC_NH_CONNECTED || + if (CHECK_FLAG(fnh->flags, F_FEC_NH_CONNECTED) || iface || fnh->route_type == ZEBRA_ROUTE_STATIC) - fnh->flags &=~F_FEC_NH_NO_LDP; + UNSET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); else - fnh->flags |= F_FEC_NH_NO_LDP; + SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); } else - fnh->flags |= F_FEC_NH_NO_LDP; + SET_FLAG(fnh->flags, F_FEC_NH_NO_LDP); } else { lde_send_delete_klabel(fn, fnh); fec_nh_del(fnh); @@ -510,7 +504,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) /* RFC 4447 control word and status tlv negotiation */ if (map->type == MAP_TYPE_PWID && l2vpn_pw_negotiate(ln, fn, map)) { - if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) + if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) fn->pw_remote_status = map->pw_status; return; @@ -534,8 +528,7 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) * the possibility of multipath. */ LIST_FOREACH(fnh, &fn->nexthops, entry) { - if (lde_address_find(ln, fnh->af, - &fnh->nexthop) == NULL) + if (lde_address_find(ln, fnh->af, &fnh->nexthop) == NULL) continue; lde_send_delete_klabel(fn, fnh); @@ -561,9 +554,9 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) * NH so clear flag and send labelmap msg to * peer */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { send_map = true; - fnh->flags &= ~F_FEC_NH_DEFER; + UNSET_FLAG(fnh->flags, F_FEC_NH_DEFER); } fnh->remote_label = map->label; if (fn->local_label != NO_LABEL) @@ -575,9 +568,9 @@ lde_check_mapping(struct map *map, struct lde_nbr *ln, int rcvd_label_mapping) continue; pw->remote_group = map->fec.pwid.group_id; - if (map->flags & F_MAP_PW_IFMTU) + if (CHECK_FLAG(map->flags, F_MAP_PW_IFMTU)) pw->remote_mtu = map->fec.pwid.ifmtu; - if (rcvd_label_mapping && map->flags & F_MAP_PW_STATUS) { + if (rcvd_label_mapping && CHECK_FLAG(map->flags, F_MAP_PW_STATUS)) { pw->remote_status = map->pw_status; fn->pw_remote_status = map->pw_status; } @@ -726,7 +719,7 @@ lde_check_release(struct map *map, struct lde_nbr *ln) /* wildcard label release */ if (map->type == MAP_TYPE_WILDCARD || map->type == MAP_TYPE_TYPED_WCARD || - (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) { + (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) { lde_check_release_wcard(map, ln); return; } @@ -818,7 +811,7 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) /* wildcard label withdraw */ if (map->type == MAP_TYPE_WILDCARD || map->type == MAP_TYPE_TYPED_WCARD || - (map->type == MAP_TYPE_PWID && !(map->flags & F_MAP_PW_ID))) { + (map->type == MAP_TYPE_PWID && !CHECK_FLAG(map->flags, F_MAP_PW_ID))) { lde_check_withdraw_wcard(map, ln); return; } @@ -868,15 +861,14 @@ lde_check_withdraw(struct map *map, struct lde_nbr *ln) return; /* Ordered Control: additional withdraw steps */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(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); + me = (struct lde_map *)fec_find(&lnbr->sent_map, &fn->fec); /* * LWd.10: does label sent to peer "map" to withdraw @@ -915,8 +907,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) switch (f->type) { case FEC_TYPE_IPV4: case FEC_TYPE_IPV6: - if (!lde_address_find(ln, fnh->af, - &fnh->nexthop)) + if (!lde_address_find(ln, fnh->af, &fnh->nexthop)) continue; break; case FEC_TYPE_PWID: @@ -929,8 +920,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) default: break; } - if (map->label != NO_LABEL && map->label != - fnh->remote_label) + if (map->label != NO_LABEL && map->label != fnh->remote_label) continue; lde_send_delete_klabel(fn, fnh); @@ -941,8 +931,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL); /* LWd.3: check previously received label mapping */ - if (me && (map->label == NO_LABEL || - map->label == me->map.label)) + if (me && (map->label == NO_LABEL || map->label == me->map.label)) /* * LWd.4: remove record of previously received * label mapping @@ -953,7 +942,7 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) continue; /* Ordered Control: additional withdraw steps */ - if (ldeconf->flags & F_LDPD_ORDERED_CONTROL) { + if (CHECK_FLAG(ldeconf->flags, F_LDPD_ORDERED_CONTROL)) { /* * LWd.8: for each neighbor other that src of * withdraw msg @@ -965,16 +954,14 @@ lde_check_withdraw_wcard(struct map *map, struct lde_nbr *ln) /* LWd.9: check if previously sent a label * mapping */ - me = (struct lde_map *)fec_find( - &lnbr->sent_map, &fn->fec); + 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); + lde_send_labelwithdraw(lnbr, fn, NULL, NULL); } } } @@ -1026,7 +1013,7 @@ lde_wildcard_apply(struct map *wcard, struct fec *fec, struct lde_map *me) /* gabage collector timer: timer to remove dead entries from the LIB */ /* ARGSUSED */ -void lde_gc_timer(struct thread *thread) +void lde_gc_timer(struct event *thread) { struct fec *fec, *safe; struct fec_node *fn; @@ -1057,13 +1044,12 @@ void lde_gc_timer(struct thread *thread) void lde_gc_start_timer(void) { - THREAD_OFF(gc_timer); - thread_add_timer(master, lde_gc_timer, NULL, LDE_GC_INTERVAL, - &gc_timer); + EVENT_OFF(gc_timer); + event_add_timer(master, lde_gc_timer, NULL, LDE_GC_INTERVAL, &gc_timer); } void lde_gc_stop_timer(void) { - THREAD_OFF(gc_timer); + EVENT_OFF(gc_timer); } diff --git a/ldpd/ldp_debug.c b/ldpd/ldp_debug.c index d2aeaba8b314..957fb8e55636 100644 --- a/ldpd/ldp_debug.c +++ b/ldpd/ldp_debug.c @@ -97,8 +97,7 @@ ldp_vty_debug(struct vty *vty, const char *negate, const char *type_str, DEBUG_ON(zebra, LDP_DEBUG_ZEBRA); } - main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, - sizeof(ldp_debug)); + main_imsg_compose_both(IMSG_DEBUG_UPDATE, &ldp_debug, sizeof(ldp_debug)); return (CMD_SUCCESS); } @@ -119,13 +118,11 @@ ldp_vty_show_debugging(struct vty *vty) if (LDP_DEBUG(labels, LDP_DEBUG_LABELS)) vty_out (vty, " LDP labels debugging is on\n"); if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV_ALL)) - vty_out (vty, - " LDP detailed messages debugging is on (inbound)\n"); + vty_out (vty, " LDP detailed messages debugging is on (inbound)\n"); else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_RECV)) vty_out (vty," LDP messages debugging is on (inbound)\n"); if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND_ALL)) - vty_out (vty, - " LDP detailed messages debugging is on (outbound)\n"); + vty_out (vty, " LDP detailed messages debugging is on (outbound)\n"); else if (LDP_DEBUG(msg, LDP_DEBUG_MSG_SEND)) vty_out (vty," LDP messages debugging is on (outbound)\n"); if (LDP_DEBUG(sync, LDP_DEBUG_SYNC)) diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c index 9a26a8d17be6..ed391ac6003b 100644 --- a/ldpd/ldp_snmp.c +++ b/ldpd/ldp_snmp.c @@ -1173,7 +1173,7 @@ static int ldp_snmp_nbr_state_change(struct nbr * nbr, int old_state) return 0; } -static int ldp_snmp_init(struct thread_master *tm) +static int ldp_snmp_init(struct event_loop *tm) { hook_register(agentx_enabled, ldp_snmp_agentx_enabled); @@ -1182,7 +1182,7 @@ static int ldp_snmp_init(struct thread_master *tm) return 0; } -static int ldp_snmp_register_mib(struct thread_master *tm) +static int ldp_snmp_register_mib(struct event_loop *tm) { static int registered = 0; diff --git a/ldpd/ldp_zebra.c b/ldpd/ldp_zebra.c index 6f0f990e4919..0fd5d4613c08 100644 --- a/ldpd/ldp_zebra.c +++ b/ldpd/ldp_zebra.c @@ -22,8 +22,7 @@ #include "ldp_debug.h" static void ifp2kif(struct interface *, struct kif *); -static void ifc2kaddr(struct interface *, struct connected *, - struct kaddr *); +static void ifc2kaddr(struct interface *, struct connected *, struct kaddr *); 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); @@ -40,6 +39,7 @@ static int ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS); static void ldp_sync_zebra_init(void); static struct zclient *zclient; +extern struct zclient *zclient_sync; static bool zebra_registered = false; static void @@ -89,7 +89,7 @@ pw2zpw(struct l2vpn_pw *pw, struct zapi_pw *zpw) zpw->nexthop.ipv6 = pw->addr.v6; zpw->local_label = NO_LABEL; zpw->remote_label = NO_LABEL; - if (pw->flags & F_PW_CWORD) + if (CHECK_FLAG(pw->flags, F_PW_CWORD)) zpw->flags = F_PSEUDOWIRE_CWORD; zpw->data.ldp.lsr_id = pw->lsr_id; zpw->data.ldp.pwid = pw->pwid; @@ -162,10 +162,10 @@ ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS) struct zapi_rlfa_igp igp; struct zapi_rlfa_request rlfa; - s = zclient->ibuf; + s = zclient->ibuf; - if (zclient_opaque_decode(s, &info) != 0) - return -1; + if(zclient_opaque_decode(s, &info) != 0) + return -1; switch (info.type) { case LDP_IGP_SYNC_IF_STATE_REQUEST: @@ -239,7 +239,7 @@ ldp_zebra_send_mpls_labels(int cmd, struct kroute *kr) * dropping them). */ if (kr->remote_label == NO_LABEL - && !(ldpd_conf->flags & F_LDPD_ALLOW_BROKEN_LSP) + && !CHECK_FLAG(ldpd_conf->flags, F_LDPD_ALLOW_BROKEN_LSP) && cmd == ZEBRA_MPLS_LABELS_ADD) return 0; @@ -295,8 +295,7 @@ kmpw_add(struct zapi_pw *zpw) debug_zebra_out("pseudowire %s nexthop %s (add)", zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); - return zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw) - == ZCLIENT_SEND_FAILURE; + return zebra_send_pw(zclient, ZEBRA_PW_ADD, zpw) == ZCLIENT_SEND_FAILURE; } int @@ -305,8 +304,7 @@ kmpw_del(struct zapi_pw *zpw) debug_zebra_out("pseudowire %s nexthop %s (del)", zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); - return zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw) - == ZCLIENT_SEND_FAILURE; + return zebra_send_pw(zclient, ZEBRA_PW_DELETE, zpw) == ZCLIENT_SEND_FAILURE; } int @@ -316,8 +314,7 @@ kmpw_set(struct zapi_pw *zpw) zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop), zpw->local_label, zpw->remote_label); - return zebra_send_pw(zclient, ZEBRA_PW_SET, zpw) - == ZCLIENT_SEND_FAILURE; + return zebra_send_pw(zclient, ZEBRA_PW_SET, zpw) == ZCLIENT_SEND_FAILURE; } int @@ -326,8 +323,7 @@ kmpw_unset(struct zapi_pw *zpw) debug_zebra_out("pseudowire %s nexthop %s (unset)", zpw->ifname, log_addr(zpw->af, (union ldpd_addr *)&zpw->nexthop)); - return zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw) - == ZCLIENT_SEND_FAILURE; + return zebra_send_pw(zclient, ZEBRA_PW_UNSET, zpw) == ZCLIENT_SEND_FAILURE; } void @@ -349,8 +345,7 @@ kif_redistribute(const char *ifname) for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { ifc2kaddr(ifp, ifc, &ka); - main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, - sizeof(ka)); + main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka)); } } } @@ -418,14 +413,12 @@ ldp_interface_status_change(struct interface *ifp) if (if_is_operative(ifp)) { for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { ifc2kaddr(ifp, ifc, &ka); - main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, - sizeof(ka)); + main_imsg_compose_ldpe(IMSG_NEWADDR, 0, &ka, sizeof(ka)); } } else { for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { ifc2kaddr(ifp, ifc, &ka); - main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, - sizeof(ka)); + main_imsg_compose_ldpe(IMSG_DELADDR, 0, &ka, sizeof(ka)); } } @@ -530,7 +523,7 @@ ldp_zebra_read_route(ZAPI_CALLBACK_ARGS) switch (api.type) { case ZEBRA_ROUTE_CONNECT: - kr.flags |= F_CONNECTED; + SET_FLAG(kr.flags, F_CONNECTED); break; case ZEBRA_ROUTE_BGP: /* LDP should follow the IGP and ignore BGP routes */ @@ -580,7 +573,7 @@ ldp_zebra_read_route(ZAPI_CALLBACK_ARGS) kr.ifindex = api_nh->ifindex; break; case NEXTHOP_TYPE_IFINDEX: - if (!(kr.flags & F_CONNECTED)) + if (!CHECK_FLAG(kr.flags, F_CONNECTED)) continue; break; case NEXTHOP_TYPE_BLACKHOLE: @@ -593,8 +586,7 @@ ldp_zebra_read_route(ZAPI_CALLBACK_ARGS) zebra_route_string(api.type)); if (add) - main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr, - sizeof(kr)); + main_imsg_compose_lde(IMSG_NETWORK_ADD, 0, &kr, sizeof(kr)); } main_imsg_compose_lde(IMSG_NETWORK_UPDATE, 0, &kr, sizeof(kr)); @@ -655,7 +647,7 @@ ldp_zebra_connected(struct zclient *zclient) /* if MPLS was already enabled and we are re-connecting, register again */ - if (vty_conf->flags & F_LDPD_ENABLED) + if (CHECK_FLAG(vty_conf->flags, F_LDPD_ENABLED)) ldp_zebra_regdereg_zebra_info(true); ldp_zebra_opaque_register(); @@ -670,11 +662,9 @@ ldp_zebra_filter_update(struct access_list *access) if (access && access->name[0] != '\0') { strlcpy(laccess.name, access->name, sizeof(laccess.name)); - debug_evt("%s ACL update filter name %s", __func__, - access->name); + debug_evt("%s ACL update filter name %s", __func__, access->name); - main_imsg_compose_both(IMSG_FILTER_UPDATE, &laccess, - sizeof(laccess)); + main_imsg_compose_both(IMSG_FILTER_UPDATE, &laccess, sizeof(laccess)); } } @@ -690,11 +680,9 @@ static zclient_handler *const ldp_handlers[] = { [ZEBRA_OPAQUE_MESSAGE] = ldp_zebra_opaque_msg_handler, }; -void -ldp_zebra_init(struct thread_master *master) +void ldp_zebra_init(struct event_loop *master) { - if_zapi_callbacks(ldp_ifp_create, ldp_ifp_up, - ldp_ifp_down, ldp_ifp_destroy); + 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, ldp_handlers, @@ -716,4 +704,10 @@ ldp_zebra_destroy(void) zclient_stop(zclient); zclient_free(zclient); zclient = NULL; + + if (zclient_sync == NULL) + return; + zclient_stop(zclient_sync); + zclient_free(zclient_sync); + zclient_sync = NULL; } diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c index 2026a7857c6c..3c616d4a8c8b 100644 --- a/ldpd/ldpd.c +++ b/ldpd/ldpd.c @@ -35,8 +35,8 @@ static void ldpd_shutdown(void); static pid_t start_child(enum ldpd_process, char *, int, int); -static void main_dispatch_ldpe(struct thread *thread); -static void main_dispatch_lde(struct thread *thread); +static void main_dispatch_ldpe(struct event *thread); +static void main_dispatch_lde(struct event *thread); static int main_imsg_send_ipc_sockets(struct imsgbuf *, struct imsgbuf *); static void main_imsg_send_net_sockets(int); @@ -77,7 +77,7 @@ static pid_t lde_pid; static struct frr_daemon_info ldpd_di; -DEFINE_HOOK(ldp_register_mib, (struct thread_master * tm), (tm)); +DEFINE_HOOK(ldp_register_mib, (struct event_loop * tm), (tm)); static void ldp_load_module(const char *name) { @@ -104,7 +104,7 @@ enum ldpd_process ldpd_process; #define LDP_VTY_PORT 2612 /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* ldpd privileges */ static zebra_capabilities_t _caps_p [] = @@ -208,7 +208,7 @@ FRR_DAEMON_INFO(ldpd, LDP, .n_yang_modules = array_size(ldpd_yang_modules), ); -static void ldp_config_fork_apply(struct thread *t) +static void ldp_config_fork_apply(struct event *t) { /* * So the frr_config_fork() function schedules @@ -383,7 +383,7 @@ main(int argc, char *argv[]) frr_config_fork(); /* apply configuration */ - thread_add_event(master, ldp_config_fork_apply, NULL, 0, NULL); + event_add_event(master, ldp_config_fork_apply, NULL, 0, NULL); /* setup pipes to children */ if ((iev_ldpe = calloc(1, sizeof(struct imsgev))) == NULL || @@ -394,26 +394,26 @@ main(int argc, char *argv[]) imsg_init(&iev_ldpe->ibuf, pipe_parent2ldpe[0]); iev_ldpe->handler_read = main_dispatch_ldpe; - thread_add_read(master, iev_ldpe->handler_read, iev_ldpe, iev_ldpe->ibuf.fd, - &iev_ldpe->ev_read); + event_add_read(master, iev_ldpe->handler_read, iev_ldpe, + iev_ldpe->ibuf.fd, &iev_ldpe->ev_read); iev_ldpe->handler_write = ldp_write_handler; imsg_init(&iev_ldpe_sync->ibuf, pipe_parent2ldpe_sync[0]); iev_ldpe_sync->handler_read = main_dispatch_ldpe; - thread_add_read(master, iev_ldpe_sync->handler_read, iev_ldpe_sync, iev_ldpe_sync->ibuf.fd, - &iev_ldpe_sync->ev_read); + event_add_read(master, iev_ldpe_sync->handler_read, iev_ldpe_sync, + iev_ldpe_sync->ibuf.fd, &iev_ldpe_sync->ev_read); iev_ldpe_sync->handler_write = ldp_write_handler; imsg_init(&iev_lde->ibuf, pipe_parent2lde[0]); iev_lde->handler_read = main_dispatch_lde; - thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd, - &iev_lde->ev_read); + event_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd, + &iev_lde->ev_read); iev_lde->handler_write = ldp_write_handler; imsg_init(&iev_lde_sync->ibuf, pipe_parent2lde_sync[0]); iev_lde_sync->handler_read = main_dispatch_lde; - thread_add_read(master, iev_lde_sync->handler_read, iev_lde_sync, iev_lde_sync->ibuf.fd, - &iev_lde_sync->ev_read); + event_add_read(master, iev_lde_sync->handler_read, iev_lde_sync, + iev_lde_sync->ibuf.fd, &iev_lde_sync->ev_read); iev_lde_sync->handler_write = ldp_write_handler; if (main_imsg_send_ipc_sockets(&iev_ldpe->ibuf, &iev_lde->ibuf)) @@ -557,9 +557,9 @@ start_child(enum ldpd_process p, char *argv0, int fd_async, int fd_sync) /* imsg handling */ /* ARGSUSED */ -static void main_dispatch_ldpe(struct thread *thread) +static void main_dispatch_ldpe(struct event *thread) { - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; int af; @@ -613,8 +613,8 @@ static void main_dispatch_ldpe(struct thread *thread) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); ldpe_pid = 0; if (lde_pid == 0) @@ -625,9 +625,9 @@ static void main_dispatch_ldpe(struct thread *thread) } /* ARGSUSED */ -static void main_dispatch_lde(struct thread *thread) +static void main_dispatch_lde(struct event *thread) { - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; ssize_t n; @@ -722,8 +722,8 @@ static void main_dispatch_lde(struct thread *thread) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); lde_pid = 0; if (ldpe_pid == 0) ldpd_shutdown(); @@ -733,9 +733,9 @@ static void main_dispatch_lde(struct thread *thread) } /* ARGSUSED */ -void ldp_write_handler(struct thread *thread) +void ldp_write_handler(struct event *thread) { - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; ssize_t n; @@ -745,8 +745,8 @@ void ldp_write_handler(struct thread *thread) fatal("msgbuf_write"); if (n == 0) { /* this pipe is dead, so remove the event handlers */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); return; } @@ -787,12 +787,12 @@ void imsg_event_add(struct imsgev *iev) { if (iev->handler_read) - thread_add_read(master, iev->handler_read, iev, iev->ibuf.fd, - &iev->ev_read); + event_add_read(master, iev->handler_read, iev, iev->ibuf.fd, + &iev->ev_read); if (iev->handler_write && iev->ibuf.w.queued) - thread_add_write(master, iev->handler_write, iev, - iev->ibuf.fd, &iev->ev_write); + event_add_write(master, iev->handler_write, iev, iev->ibuf.fd, + &iev->ev_write); } int @@ -819,11 +819,11 @@ void evbuf_event_add(struct evbuf *eb) { if (eb->wbuf.queued) - thread_add_write(master, eb->handler, eb->arg, eb->wbuf.fd, - &eb->ev); + event_add_write(master, eb->handler, eb->arg, eb->wbuf.fd, + &eb->ev); } -void evbuf_init(struct evbuf *eb, int fd, void (*handler)(struct thread *), +void evbuf_init(struct evbuf *eb, int fd, void (*handler)(struct event *), void *arg) { msgbuf_init(&eb->wbuf); @@ -835,7 +835,7 @@ void evbuf_init(struct evbuf *eb, int fd, void (*handler)(struct thread *), void evbuf_clear(struct evbuf *eb) { - THREAD_OFF(eb->ev); + EVENT_OFF(eb->ev); msgbuf_clear(&eb->wbuf); eb->wbuf.fd = -1; } diff --git a/ldpd/ldpd.h b/ldpd/ldpd.h index dc993e3d7cc6..1fec5beafc7a 100644 --- a/ldpd/ldpd.h +++ b/ldpd/ldpd.h @@ -14,7 +14,7 @@ #include "queue.h" #include "openbsd-tree.h" #include "imsg.h" -#include "thread.h" +#include "frrevent.h" #include "qobj.h" #include "prefix.h" #include "filter.h" @@ -51,17 +51,17 @@ struct evbuf { struct msgbuf wbuf; - struct thread *ev; - void (*handler)(struct thread *); + struct event *ev; + void (*handler)(struct event *); void *arg; }; struct imsgev { struct imsgbuf ibuf; - void (*handler_write)(struct thread *); - struct thread *ev_write; - void (*handler_read)(struct thread *); - struct thread *ev_read; + void (*handler_write)(struct event *); + struct event *ev_write; + void (*handler_read)(struct event *); + struct event *ev_read; }; enum imsg_type { @@ -329,14 +329,14 @@ struct iface_af { int state; struct ia_adj_head adj_tree; time_t uptime; - struct thread *hello_timer; + struct event *hello_timer; uint16_t hello_holdtime; uint16_t hello_interval; }; struct iface_ldp_sync { int state; - struct thread *wait_for_sync_timer; + struct event *wait_for_sync_timer; }; struct iface { @@ -359,7 +359,7 @@ DECLARE_QOBJ_TYPE(iface); /* source of targeted hellos */ struct tnbr { RB_ENTRY(tnbr) entry; - struct thread *hello_timer; + struct event *hello_timer; struct adj *adj; int af; union ldpd_addr addr; @@ -582,8 +582,8 @@ DECLARE_QOBJ_TYPE(ldpd_conf); #define F_LDPD_ALLOW_BROKEN_LSP 0x0010 struct ldpd_af_global { - struct thread *disc_ev; - struct thread *edisc_ev; + struct event *disc_ev; + struct event *edisc_ev; int ldp_disc_socket; int ldp_edisc_socket; int ldp_session_socket; @@ -781,7 +781,7 @@ void sa2addr(struct sockaddr *, int *, union ldpd_addr *, socklen_t sockaddr_len(struct sockaddr *); /* ldpd.c */ -void ldp_write_handler(struct thread *thread); +void ldp_write_handler(struct event *thread); void main_imsg_compose_ldpe(int, pid_t, void *, uint16_t); void main_imsg_compose_lde(int, pid_t, void *, uint16_t); int main_imsg_compose_both(enum imsg_type, void *, @@ -791,7 +791,7 @@ int imsg_compose_event(struct imsgev *, uint16_t, uint32_t, pid_t, int, void *, uint16_t); void evbuf_enqueue(struct evbuf *, struct ibuf *); void evbuf_event_add(struct evbuf *); -void evbuf_init(struct evbuf *, int, void (*)(struct thread *), void *); +void evbuf_init(struct evbuf *, int, void (*)(struct event *), void *); void evbuf_clear(struct evbuf *); int ldp_acl_request(struct imsgev *, char *, int, union ldpd_addr *, uint8_t); @@ -883,11 +883,11 @@ const char *pw_type_name(uint16_t); const char *pw_error_code(uint8_t); /* quagga */ -extern struct thread_master *master; +extern struct event_loop *master; extern char ctl_sock_path[MAXPATHLEN]; /* ldp_zebra.c */ -void ldp_zebra_init(struct thread_master *); +void ldp_zebra_init(struct event_loop *m); void ldp_zebra_destroy(void); int ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *); int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response * @@ -904,7 +904,7 @@ void ldp_zebra_regdereg_zebra_info(bool want_register); (__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_INTFACELOCAL)) #endif -DECLARE_HOOK(ldp_register_mib, (struct thread_master * tm), (tm)); +DECLARE_HOOK(ldp_register_mib, (struct event_loop * tm), (tm)); extern void ldp_agentx_enabled(void); diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c index f53dc7254065..e66b9e92dd43 100644 --- a/ldpd/ldpe.c +++ b/ldpd/ldpe.c @@ -25,10 +25,10 @@ #include "libfrr.h" static void ldpe_shutdown(void); -static void ldpe_dispatch_main(struct thread *thread); -static void ldpe_dispatch_lde(struct thread *thread); +static void ldpe_dispatch_main(struct event *thread); +static void ldpe_dispatch_lde(struct event *thread); #ifdef __OpenBSD__ -static void ldpe_dispatch_pfkey(struct thread *thread); +static void ldpe_dispatch_pfkey(struct event *thread); #endif static void ldpe_setup_sockets(int, int, int, int); static void ldpe_close_sockets(int); @@ -44,7 +44,7 @@ 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; +static struct event *pfkey_ev; #endif /* ldpe privileges */ @@ -111,8 +111,8 @@ ldpe(void) fatal(NULL); imsg_init(&iev_main->ibuf, LDPD_FD_ASYNC); iev_main->handler_read = ldpe_dispatch_main; - thread_add_read(master, iev_main->handler_read, iev_main, iev_main->ibuf.fd, - &iev_main->ev_read); + event_add_read(master, iev_main->handler_read, iev_main, + iev_main->ibuf.fd, &iev_main->ev_read); iev_main->handler_write = ldp_write_handler; memset(&iev_main_data, 0, sizeof(iev_main_data)); @@ -122,9 +122,9 @@ ldpe(void) /* create base configuration */ leconf = config_new_empty(); - struct thread thread; - while (thread_fetch(master, &thread)) - thread_call(&thread); + struct event thread; + while (event_fetch(master, &thread)) + event_call(&thread); /* NOTREACHED */ return; @@ -137,8 +137,8 @@ ldpe_init(struct ldpd_init *init) /* This socket must be open before dropping privileges. */ global.pfkeysock = pfkey_init(); if (sysdep.no_pfkey == 0) { - thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock, - &pfkey_ev); + event_add_read(master, ldpe_dispatch_pfkey, NULL, + global.pfkeysock, &pfkey_ev); } #endif @@ -200,7 +200,7 @@ ldpe_shutdown(void) #ifdef __OpenBSD__ if (sysdep.no_pfkey == 0) { - THREAD_OFF(pfkey_ev); + EVENT_OFF(pfkey_ev); close(global.pfkeysock); } #endif @@ -257,12 +257,11 @@ ldpe_imsg_compose_lde(int type, uint32_t peerid, pid_t pid, void *data, { if (iev_lde->ibuf.fd == -1) return (0); - return (imsg_compose_event(iev_lde, type, peerid, pid, -1, - data, datalen)); + return (imsg_compose_event(iev_lde, type, peerid, pid, -1, data, datalen)); } /* ARGSUSED */ -static void ldpe_dispatch_main(struct thread *thread) +static void ldpe_dispatch_main(struct event *thread) { static struct ldpd_conf *nconf; struct iface *niface; @@ -273,7 +272,7 @@ static void ldpe_dispatch_main(struct thread *thread) struct l2vpn_pw *pw, *npw; struct imsg imsg; int fd; - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct iface *iface = NULL; struct kif *kif; @@ -309,8 +308,7 @@ static void ldpe_dispatch_main(struct thread *thread) switch (imsg.hdr.type) { case IMSG_IFSTATUS: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kif)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kif)) fatalx("IFSTATUS imsg with wrong len"); kif = imsg.data; @@ -336,15 +334,13 @@ static void ldpe_dispatch_main(struct thread *thread) } break; case IMSG_NEWADDR: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kaddr)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr)) fatalx("NEWADDR imsg with wrong len"); if_addr_add(imsg.data); break; case IMSG_DELADDR: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct kaddr)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct kaddr)) fatalx("DELADDR imsg with wrong len"); if_addr_del(imsg.data); @@ -363,14 +359,13 @@ static void ldpe_dispatch_main(struct thread *thread) fatal(NULL); imsg_init(&iev_lde->ibuf, fd); iev_lde->handler_read = ldpe_dispatch_lde; - thread_add_read(master, iev_lde->handler_read, iev_lde, iev_lde->ibuf.fd, - &iev_lde->ev_read); + event_add_read(master, iev_lde->handler_read, iev_lde, + iev_lde->ibuf.fd, &iev_lde->ev_read); iev_lde->handler_write = ldp_write_handler; iev_lde->ev_write = NULL; break; case IMSG_INIT: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(struct ldpd_init)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(struct ldpd_init)) fatalx("INIT imsg with wrong len"); memcpy(&init, imsg.data, sizeof(init)); @@ -398,14 +393,11 @@ static void ldpe_dispatch_main(struct thread *thread) disc_socket = -1; edisc_socket = -1; session_socket = -1; - if ((ldp_af_conf_get(leconf, af))->flags & - F_LDPD_AF_ENABLED) - ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, - af, NULL, 0); + if (CHECK_FLAG((ldp_af_conf_get(leconf, af))->flags, F_LDPD_AF_ENABLED)) + ldpe_imsg_compose_parent(IMSG_REQUEST_SOCKETS, af, NULL, 0); break; case IMSG_SOCKET_NET: - if (imsg.hdr.len != IMSG_HEADER_SIZE + - sizeof(enum socket_type)) + if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(enum socket_type)) fatalx("SOCKET_NET imsg with wrong len"); socket_type = imsg.data; @@ -434,15 +426,13 @@ static void ldpe_dispatch_main(struct thread *thread) break; } - ldpe_setup_sockets(af, disc_socket, edisc_socket, - session_socket); + ldpe_setup_sockets(af, disc_socket, edisc_socket, session_socket); if_update_all(af); tnbr_update_all(af); RB_FOREACH(nbr, nbr_id_head, &nbrs_by_id) { if (nbr->af != af) continue; - nbr->laddr = (ldp_af_conf_get(leconf, - af))->trans_addr; + nbr->laddr = (ldp_af_conf_get(leconf, af))->trans_addr; #ifdef __OpenBSD__ nbrp = nbr_params_find(leconf, nbr->id); if (nbrp) { @@ -456,8 +446,7 @@ static void ldpe_dispatch_main(struct thread *thread) } break; case IMSG_RTRID_UPDATE: - memcpy(&global.rtr_id, imsg.data, - sizeof(global.rtr_id)); + memcpy(&global.rtr_id, imsg.data, sizeof(global.rtr_id)); if (leconf->rtr_id.s_addr == INADDR_ANY) { ldpe_reset_nbrs(AF_UNSPEC); } @@ -465,8 +454,7 @@ static void ldpe_dispatch_main(struct thread *thread) tnbr_update_all(AF_UNSPEC); break; case IMSG_RECONF_CONF: - if ((nconf = malloc(sizeof(struct ldpd_conf))) == - NULL) + if ((nconf = malloc(sizeof(struct ldpd_conf))) == NULL) fatal(NULL); memcpy(nconf, imsg.data, sizeof(struct ldpd_conf)); @@ -546,16 +534,13 @@ static void 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)) { + 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); + ldpe_check_filter_af(AF_INET, &leconf->ipv4, laccess->name); + ldpe_check_filter_af(AF_INET6, &leconf->ipv6, laccess->name); break; case IMSG_LDP_SYNC_IF_STATE_REQUEST: if (imsg.hdr.len != IMSG_HEADER_SIZE + @@ -605,8 +590,7 @@ static void ldpe_dispatch_main(struct thread *thread) } break; default: - log_debug("%s: error handling imsg %d", - __func__, imsg.hdr.type); + log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); @@ -615,16 +599,16 @@ static void ldpe_dispatch_main(struct thread *thread) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); ldpe_shutdown(); } } /* ARGSUSED */ -static void ldpe_dispatch_lde(struct thread *thread) +static void ldpe_dispatch_lde(struct event *thread) { - struct imsgev *iev = THREAD_ARG(thread); + struct imsgev *iev = EVENT_ARG(thread); struct imsgbuf *ibuf = &iev->ibuf; struct imsg imsg; struct map *map; @@ -650,8 +634,7 @@ static void ldpe_dispatch_lde(struct thread *thread) case IMSG_RELEASE_ADD: case IMSG_REQUEST_ADD: case IMSG_WITHDRAW_ADD: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct map)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct map)) fatalx("invalid size of map request"); map = imsg.data; @@ -706,8 +689,7 @@ static void ldpe_dispatch_lde(struct thread *thread) } break; case IMSG_NOTIFICATION_SEND: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct notify_msg)) + if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct notify_msg)) fatalx("invalid size of OE request"); nm = imsg.data; @@ -741,8 +723,7 @@ static void ldpe_dispatch_lde(struct thread *thread) session_shutdown(nbr,S_SHUTDOWN,0,0); break; default: - log_debug("%s: error handling imsg %d", - __func__, imsg.hdr.type); + log_debug("%s: error handling imsg %d", __func__, imsg.hdr.type); break; } imsg_free(&imsg); @@ -751,20 +732,20 @@ static void ldpe_dispatch_lde(struct thread *thread) imsg_event_add(iev); else { /* this pipe is dead, so remove the event handlers and exit */ - THREAD_OFF(iev->ev_read); - THREAD_OFF(iev->ev_write); + EVENT_OFF(iev->ev_read); + EVENT_OFF(iev->ev_write); ldpe_shutdown(); } } #ifdef __OpenBSD__ /* ARGSUSED */ -static void ldpe_dispatch_pfkey(struct thread *thread) +static void ldpe_dispatch_pfkey(struct event *thread) { - int fd = THREAD_FD(thread); + int fd = EVENT_FD(thread); - thread_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock, - &pfkey_ev); + event_add_read(master, ldpe_dispatch_pfkey, NULL, global.pfkeysock, + &pfkey_ev); if (pfkey_read(fd, NULL) == -1) fatal("pfkey_read failed, exiting..."); @@ -781,13 +762,13 @@ ldpe_setup_sockets(int af, int disc_socket, int edisc_socket, /* discovery socket */ af_global->ldp_disc_socket = disc_socket; - thread_add_read(master, disc_recv_packet, &af_global->disc_ev, af_global->ldp_disc_socket, - &af_global->disc_ev); + event_add_read(master, disc_recv_packet, &af_global->disc_ev, + af_global->ldp_disc_socket, &af_global->disc_ev); /* extended discovery socket */ af_global->ldp_edisc_socket = edisc_socket; - thread_add_read(master, disc_recv_packet, &af_global->edisc_ev, af_global->ldp_edisc_socket, - &af_global->edisc_ev); + event_add_read(master, disc_recv_packet, &af_global->edisc_ev, + af_global->ldp_edisc_socket, &af_global->edisc_ev); /* session socket */ af_global->ldp_session_socket = session_socket; @@ -802,14 +783,14 @@ ldpe_close_sockets(int af) af_global = ldp_af_global_get(&global, af); /* discovery socket */ - THREAD_OFF(af_global->disc_ev); + EVENT_OFF(af_global->disc_ev); if (af_global->ldp_disc_socket != -1) { close(af_global->ldp_disc_socket); af_global->ldp_disc_socket = -1; } /* extended discovery socket */ - THREAD_OFF(af_global->edisc_ev); + EVENT_OFF(af_global->edisc_ev); if (af_global->ldp_edisc_socket != -1) { close(af_global->ldp_edisc_socket); af_global->ldp_edisc_socket = -1; @@ -860,7 +841,7 @@ ldpe_remove_dynamic_tnbrs(int af) if (tnbr->af != af) continue; - tnbr->flags &= ~F_TNBR_DYNAMIC; + UNSET_FLAG(tnbr->flags, F_TNBR_DYNAMIC); tnbr_check(leconf, tnbr); } } diff --git a/ldpd/ldpe.h b/ldpd/ldpe.h index e799538b6282..f310ba5dd209 100644 --- a/ldpd/ldpe.h +++ b/ldpd/ldpe.h @@ -37,7 +37,7 @@ struct adj { struct nbr *nbr; int ds_tlv; struct hello_source source; - struct thread *inactivity_timer; + struct event *inactivity_timer; uint16_t holdtime; union ldpd_addr trans_addr; }; @@ -50,7 +50,7 @@ struct tcp_conn { int fd; struct ibuf_read *rbuf; struct evbuf wbuf; - struct thread *rev; + struct event *rev; in_port_t lport; in_port_t rport; }; @@ -59,11 +59,11 @@ struct nbr { RB_ENTRY(nbr) id_tree, addr_tree, pid_tree; struct tcp_conn *tcp; struct nbr_adj_head adj_tree; /* adjacencies */ - struct thread *ev_connect; - struct thread *keepalive_timer; - struct thread *keepalive_timeout; - struct thread *init_timeout; - struct thread *initdelay_timer; + struct event *ev_connect; + struct event *keepalive_timer; + struct event *keepalive_timeout; + struct event *init_timeout; + struct event *initdelay_timer; struct mapping_head mapping_list; struct mapping_head withdraw_list; @@ -115,7 +115,7 @@ struct pending_conn { int fd; int af; union ldpd_addr addr; - struct thread *ev_timeout; + struct event *ev_timeout; }; #define PENDING_CONN_TIMEOUT 5 @@ -137,7 +137,7 @@ extern struct nbr_pid_head nbrs_by_pid; /* accept.c */ void accept_init(void); -int accept_add(int, void (*)(struct thread *), void *); +int accept_add(int, void (*)(struct event *), void *); void accept_del(int); void accept_pause(void); void accept_unpause(void); @@ -281,8 +281,8 @@ int gen_ldp_hdr(struct ibuf *, uint16_t); int gen_msg_hdr(struct ibuf *, uint16_t, uint16_t); int send_packet(int, int, union ldpd_addr *, struct iface_af *, void *, size_t); -void disc_recv_packet(struct thread *thread); -void session_accept(struct thread *thread); +void disc_recv_packet(struct event *thread); +void session_accept(struct event *thread); void session_accept_nbr(struct nbr *, int); void session_shutdown(struct nbr *, uint32_t, uint32_t, uint32_t); diff --git a/ldpd/log.c b/ldpd/log.c index a9898a64f09f..7c4d782dcfaa 100644 --- a/ldpd/log.c +++ b/ldpd/log.c @@ -35,13 +35,11 @@ vlog(int pri, const char *fmt, va_list ap) switch (ldpd_process) { case PROC_LDE_ENGINE: vsnprintfrr(buf, sizeof(buf), fmt, ap); - lde_imsg_compose_parent_sync(IMSG_LOG, pri, buf, - strlen(buf) + 1); + lde_imsg_compose_parent_sync(IMSG_LOG, pri, buf, strlen(buf) + 1); break; case PROC_LDP_ENGINE: vsnprintfrr(buf, sizeof(buf), fmt, ap); - ldpe_imsg_compose_parent_sync(IMSG_LOG, pri, buf, - strlen(buf) + 1); + ldpe_imsg_compose_parent_sync(IMSG_LOG, pri, buf, strlen(buf) + 1); break; case PROC_MAIN: vzlog(pri, fmt, ap); @@ -121,15 +119,13 @@ void fatal(const char *emsg) { if (emsg == NULL) - logit(LOG_CRIT, "fatal in %s: %s", log_procname, - strerror(errno)); + logit(LOG_CRIT, "fatal in %s: %s", log_procname, strerror(errno)); else if (errno) logit(LOG_CRIT, "fatal in %s: %s: %s", log_procname, emsg, strerror(errno)); else - logit(LOG_CRIT, "fatal in %s: %s", - log_procname, emsg); + logit(LOG_CRIT, "fatal in %s: %s", log_procname, emsg); exit(1); } diff --git a/ldpd/logmsg.c b/ldpd/logmsg.c index 4f1d950bb3da..75f4293f0cda 100644 --- a/ldpd/logmsg.c +++ b/ldpd/logmsg.c @@ -74,8 +74,7 @@ log_addr(int af, const union ldpd_addr *addr) switch (af) { case AF_INET: round = (round + 1) % NUM_LOGS; - if (inet_ntop(AF_INET, &addr->v4, buf[round], - sizeof(buf[round])) == NULL) + if (inet_ntop(AF_INET, &addr->v4, buf[round], sizeof(buf[round])) == NULL) return ("???"); return (buf[round]); case AF_INET6: @@ -166,8 +165,7 @@ log_hello_src(const struct hello_source *src) switch (src->type) { case HELLO_LINK: - snprintf(buf, sizeof(buf), "iface %s", - src->link.ia->iface->name); + snprintf(buf, sizeof(buf), "iface %s", src->link.ia->iface->name); break; case HELLO_TARGETED: snprintf(buf, sizeof(buf), "source %s", diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c index 831f2a6aba89..5209c55bb854 100644 --- a/ldpd/neighbor.c +++ b/ldpd/neighbor.c @@ -19,18 +19,16 @@ DEFINE_HOOK(ldp_nbr_state_change, (struct nbr * nbr, int old_state), (nbr, old_state)); static __inline int nbr_id_compare(const struct nbr *, const struct nbr *); -static __inline int nbr_addr_compare(const struct nbr *, - const struct nbr *); -static __inline int nbr_pid_compare(const struct nbr *, - const struct nbr *); +static __inline int nbr_addr_compare(const struct nbr *, const struct nbr *); +static __inline int nbr_pid_compare(const struct nbr *, const struct nbr *); static void nbr_update_peerid(struct nbr *); -static void nbr_ktimer(struct thread *thread); +static void nbr_ktimer(struct event *thread); static void nbr_start_ktimer(struct nbr *); -static void nbr_ktimeout(struct thread *thread); +static void nbr_ktimeout(struct event *thread); static void nbr_start_ktimeout(struct nbr *); -static void nbr_itimeout(struct thread *thread); +static void nbr_itimeout(struct event *thread); static void nbr_start_itimeout(struct nbr *); -static void nbr_idtimer(struct thread *thread); +static void nbr_idtimer(struct event *thread); static int nbr_act_session_operational(struct nbr *); static void nbr_send_labelmappings(struct nbr *); static __inline int nbr_params_compare(const struct nbr_params *, @@ -127,7 +125,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) old_state = nbr->state; for (i = 0; nbr_fsm_tbl[i].state != -1; i++) - if ((nbr_fsm_tbl[i].state & old_state) && + if (CHECK_FLAG(nbr_fsm_tbl[i].state, old_state) && (nbr_fsm_tbl[i].event == event)) { new_state = nbr_fsm_tbl[i].new_state; break; @@ -196,8 +194,7 @@ nbr_fsm(struct nbr *nbr, enum nbr_event event) send_keepalive(nbr); break; case NBR_ACT_CLOSE_SESSION: - ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, - NULL, 0); + ldpe_imsg_compose_lde(IMSG_NEIGHBOR_DOWN, nbr->peerid, 0, NULL, 0); session_close(nbr); break; case NBR_ACT_NOTHING: @@ -296,7 +293,7 @@ nbr_del(struct nbr *nbr) nbr->auth.method = AUTH_NONE; if (nbr_pending_connect(nbr)) - THREAD_OFF(nbr->ev_connect); + EVENT_OFF(nbr->ev_connect); nbr_stop_ktimer(nbr); nbr_stop_ktimeout(nbr); nbr_stop_itimeout(nbr); @@ -407,9 +404,9 @@ nbr_session_active_role(struct nbr *nbr) /* Keepalive timer: timer to send keepalive message to neighbors */ -static void nbr_ktimer(struct thread *thread) +static void nbr_ktimer(struct event *thread) { - struct nbr *nbr = THREAD_ARG(thread); + struct nbr *nbr = EVENT_ARG(thread); nbr->keepalive_timer = NULL; send_keepalive(nbr); @@ -423,22 +420,22 @@ nbr_start_ktimer(struct nbr *nbr) /* send three keepalives per period */ secs = nbr->keepalive / KEEPALIVE_PER_PERIOD; - THREAD_OFF(nbr->keepalive_timer); + EVENT_OFF(nbr->keepalive_timer); nbr->keepalive_timer = NULL; - thread_add_timer(master, nbr_ktimer, nbr, secs, &nbr->keepalive_timer); + event_add_timer(master, nbr_ktimer, nbr, secs, &nbr->keepalive_timer); } void nbr_stop_ktimer(struct nbr *nbr) { - THREAD_OFF(nbr->keepalive_timer); + EVENT_OFF(nbr->keepalive_timer); } /* Keepalive timeout: if the nbr hasn't sent keepalive */ -static void nbr_ktimeout(struct thread *thread) +static void nbr_ktimeout(struct event *thread) { - struct nbr *nbr = THREAD_ARG(thread); + struct nbr *nbr = EVENT_ARG(thread); nbr->keepalive_timeout = NULL; @@ -450,23 +447,23 @@ static void nbr_ktimeout(struct thread *thread) static void nbr_start_ktimeout(struct nbr *nbr) { - THREAD_OFF(nbr->keepalive_timeout); + EVENT_OFF(nbr->keepalive_timeout); nbr->keepalive_timeout = NULL; - thread_add_timer(master, nbr_ktimeout, nbr, nbr->keepalive, - &nbr->keepalive_timeout); + event_add_timer(master, nbr_ktimeout, nbr, nbr->keepalive, + &nbr->keepalive_timeout); } void nbr_stop_ktimeout(struct nbr *nbr) { - THREAD_OFF(nbr->keepalive_timeout); + EVENT_OFF(nbr->keepalive_timeout); } /* Session initialization timeout: if nbr got stuck in the initialization FSM */ -static void nbr_itimeout(struct thread *thread) +static void nbr_itimeout(struct event *thread) { - struct nbr *nbr = THREAD_ARG(thread); + struct nbr *nbr = EVENT_ARG(thread); log_debug("%s: lsr-id %pI4", __func__, &nbr->id); @@ -479,22 +476,22 @@ nbr_start_itimeout(struct nbr *nbr) int secs; secs = INIT_FSM_TIMEOUT; - THREAD_OFF(nbr->init_timeout); + EVENT_OFF(nbr->init_timeout); nbr->init_timeout = NULL; - thread_add_timer(master, nbr_itimeout, nbr, secs, &nbr->init_timeout); + event_add_timer(master, nbr_itimeout, nbr, secs, &nbr->init_timeout); } void nbr_stop_itimeout(struct nbr *nbr) { - THREAD_OFF(nbr->init_timeout); + EVENT_OFF(nbr->init_timeout); } /* Init delay timer: timer to retry to iniziatize session */ -static void nbr_idtimer(struct thread *thread) +static void nbr_idtimer(struct event *thread) { - struct nbr *nbr = THREAD_ARG(thread); + struct nbr *nbr = EVENT_ARG(thread); nbr->initdelay_timer = NULL; @@ -525,16 +522,15 @@ nbr_start_idtimer(struct nbr *nbr) break; } - THREAD_OFF(nbr->initdelay_timer); + EVENT_OFF(nbr->initdelay_timer); nbr->initdelay_timer = NULL; - thread_add_timer(master, nbr_idtimer, nbr, secs, - &nbr->initdelay_timer); + event_add_timer(master, nbr_idtimer, nbr, secs, &nbr->initdelay_timer); } void nbr_stop_idtimer(struct nbr *nbr) { - THREAD_OFF(nbr->initdelay_timer); + EVENT_OFF(nbr->initdelay_timer); } int @@ -549,9 +545,9 @@ nbr_pending_connect(struct nbr *nbr) return (nbr->ev_connect != NULL); } -static void nbr_connect_cb(struct thread *thread) +static void nbr_connect_cb(struct event *thread) { - struct nbr *nbr = THREAD_ARG(thread); + struct nbr *nbr = EVENT_ARG(thread); int error; socklen_t len; @@ -607,8 +603,7 @@ nbr_establish_connection(struct nbr *nbr) return (-1); } #else - sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, - nbrp->auth.md5key); + sock_set_md5sig(nbr->fd, nbr->af, &nbr->raddr, nbrp->auth.md5key); #endif } @@ -647,11 +642,10 @@ nbr_establish_connection(struct nbr *nbr) send_hello(adj->source.type, adj->source.link.ia, adj->source.target); - if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) - == -1) { + if (connect(nbr->fd, &remote_su.sa, sockaddr_len(&remote_su.sa)) == -1) { if (errno == EINPROGRESS) { - thread_add_write(master, nbr_connect_cb, nbr, nbr->fd, - &nbr->ev_connect); + event_add_write(master, nbr_connect_cb, nbr, nbr->fd, + &nbr->ev_connect); return (0); } log_warn("%s: error while connecting to %s", __func__, @@ -675,14 +669,14 @@ nbr_gtsm_enabled(struct nbr *nbr, struct nbr_params *nbrp) * statically (e.g., via configuration) and/or dynamically override the * default behavior and enable/disable GTSM on a per-peer basis". */ - if (nbrp && (nbrp->flags & F_NBRP_GTSM)) + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM)) return (nbrp->gtsm_enabled); - if ((ldp_af_conf_get(leconf, nbr->af))->flags & F_LDPD_AF_NO_GTSM) + if (CHECK_FLAG((ldp_af_conf_get(leconf, nbr->af))->flags, F_LDPD_AF_NO_GTSM)) return (0); /* By default, GTSM support has to be negotiated for LDPv4 */ - if (nbr->af == AF_INET && !(nbr->flags & F_NBR_GTSM_NEGOTIATED)) + if (nbr->af == AF_INET && !CHECK_FLAG(nbr->flags, F_NBR_GTSM_NEGOTIATED)) return (0); return (1); @@ -693,7 +687,7 @@ nbr_gtsm_setup(int fd, int af, struct nbr_params *nbrp) { int ttl = 255; - if (nbrp && (nbrp->flags & F_NBRP_GTSM_HOPS)) + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_GTSM_HOPS)) ttl = 256 - nbrp->gtsm_hops; switch (af) { @@ -741,8 +735,7 @@ nbr_gtsm_check(int fd, struct nbr *nbr, struct nbr_params *nbrp) } if (nbr_gtsm_setup(fd, nbr->af, nbrp) == -1) { - log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__, - &nbr->id); + log_warnx("%s: error enabling GTSM for lsr-id %pI4", __func__, &nbr->id); return (-1); } @@ -773,8 +766,7 @@ nbr_act_session_operational(struct nbr *nbr) static void nbr_send_labelmappings(struct nbr *nbr) { - ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, - NULL, 0); + ldpe_imsg_compose_lde(IMSG_LABEL_MAPPING_FULL, nbr->peerid, 0, NULL, 0); } static __inline int @@ -811,7 +803,7 @@ nbr_get_keepalive(int af, struct in_addr lsr_id) struct nbr_params *nbrp; nbrp = nbr_params_find(leconf, lsr_id); - if (nbrp && (nbrp->flags & F_NBRP_KEEPALIVE)) + if (nbrp && CHECK_FLAG(nbrp->flags, F_NBRP_KEEPALIVE)) return (nbrp->keepalive); return ((ldp_af_conf_get(leconf, af))->keepalive); @@ -835,8 +827,7 @@ nbr_to_ctl(struct nbr *nbr) nctl.stats = nbr->stats; nctl.flags = nbr->flags; nctl.max_pdu_len = nbr->max_pdu_len; - nctl.hold_time_remaining = - thread_timer_remain_second(nbr->keepalive_timer); + nctl.hold_time_remaining = event_timer_remain_second(nbr->keepalive_timer); gettimeofday(&now, NULL); if (nbr->state == NBR_STA_OPER) { diff --git a/ldpd/notification.c b/ldpd/notification.c index af5bb267d7a8..1709098d0907 100644 --- a/ldpd/notification.c +++ b/ldpd/notification.c @@ -25,28 +25,28 @@ send_notification_full(struct tcp_conn *tcp, struct notify_msg *nm) /* calculate size */ size = LDP_HDR_SIZE + LDP_MSG_SIZE + STATUS_SIZE; - if (nm->flags & F_NOTIF_PW_STATUS) + if (CHECK_FLAG(nm->flags, F_NOTIF_PW_STATUS)) size += PW_STATUS_TLV_SIZE; - if (nm->flags & F_NOTIF_FEC) + if (CHECK_FLAG(nm->flags, F_NOTIF_FEC)) size += len_fec_tlv(&nm->fec); - if (nm->flags & F_NOTIF_RETURNED_TLVS) + if (CHECK_FLAG(nm->flags, F_NOTIF_RETURNED_TLVS)) size += TLV_HDR_SIZE * 2 + nm->rtlvs.length; if ((buf = ibuf_open(size)) == NULL) fatal(__func__); - err |= gen_ldp_hdr(buf, size); + SET_FLAG(err, gen_ldp_hdr(buf, size)); size -= LDP_HDR_SIZE; - err |= gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size); - err |= gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type); + SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_NOTIFICATION, size)); + SET_FLAG(err, gen_status_tlv(buf, nm->status_code, nm->msg_id, nm->msg_type)); /* optional tlvs */ - if (nm->flags & F_NOTIF_PW_STATUS) - err |= gen_pw_status_tlv(buf, nm->pw_status); - if (nm->flags & F_NOTIF_FEC) - err |= gen_fec_tlv(buf, &nm->fec); - if (nm->flags & F_NOTIF_RETURNED_TLVS) - err |= gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length, - nm->rtlvs.data); + if (CHECK_FLAG(nm->flags, F_NOTIF_PW_STATUS)) + SET_FLAG(err, gen_pw_status_tlv(buf, nm->pw_status)); + if (CHECK_FLAG(nm->flags, F_NOTIF_FEC)) + SET_FLAG(err, gen_fec_tlv(buf, &nm->fec)); + if (CHECK_FLAG(nm->flags, F_NOTIF_RETURNED_TLVS)) + SET_FLAG(err, gen_returned_tlvs(buf, nm->rtlvs.type, nm->rtlvs.length, + nm->rtlvs.data)); if (err) { ibuf_free(buf); return; @@ -121,7 +121,7 @@ send_notification_rtlvs(struct nbr *nbr, uint32_t status_code, uint32_t msg_id, nm.rtlvs.type = tlv_type; nm.rtlvs.length = tlv_len; nm.rtlvs.data = tlv_data; - nm.flags |= F_NOTIF_RETURNED_TLVS; + SET_FLAG(nm.flags, F_NOTIF_RETURNED_TLVS); } send_notification_full(nbr->tcp, &nm); @@ -189,13 +189,12 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) break; case TLV_TYPE_PW_STATUS: if (tlv_len != 4) { - session_shutdown(nbr, S_BAD_TLV_LEN, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); return (-1); } nm.pw_status = ntohl(*(uint32_t *)buf); - nm.flags |= F_NOTIF_PW_STATUS; + SET_FLAG(nm.flags, F_NOTIF_PW_STATUS); break; case TLV_TYPE_FEC: if ((tlen = tlv_decode_fec_elm(nbr, &msg, buf, @@ -203,12 +202,11 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) return (-1); /* allow only one fec element */ if (tlen != tlv_len) { - session_shutdown(nbr, S_BAD_TLV_VAL, - msg.id, msg.type); + session_shutdown(nbr, S_BAD_TLV_VAL, msg.id, msg.type); leconf->stats.bad_tlv_len++; return (-1); } - nm.flags |= F_NOTIF_FEC; + SET_FLAG(nm.flags, F_NOTIF_FEC); break; default: if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) { @@ -226,9 +224,8 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) /* sanity checks */ switch (nm.status_code) { case S_PW_STATUS: - if (!(nm.flags & (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) { - send_notification(nbr->tcp, S_MISS_MSG, - msg.id, msg.type); + if (!CHECK_FLAG(nm.flags, (F_NOTIF_PW_STATUS|F_NOTIF_FEC))) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); return (-1); } @@ -236,20 +233,17 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) case MAP_TYPE_PWID: break; default: - send_notification(nbr->tcp, S_BAD_TLV_VAL, - msg.id, msg.type); + send_notification(nbr->tcp, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } break; case S_ENDOFLIB: - if (!(nm.flags & F_NOTIF_FEC)) { - send_notification(nbr->tcp, S_MISS_MSG, - msg.id, msg.type); + if (!CHECK_FLAG(nm.flags, F_NOTIF_FEC)) { + send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); return (-1); } if (nm.fec.type != MAP_TYPE_TYPED_WCARD) { - send_notification(nbr->tcp, S_BAD_TLV_VAL, - msg.id, msg.type); + send_notification(nbr->tcp, S_BAD_TLV_VAL, msg.id, msg.type); return (-1); } break; @@ -259,7 +253,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) log_msg_notification(0, nbr, &nm); - if (st.status_code & htonl(STATUS_FATAL)) { + if (CHECK_FLAG(st.status_code, htonl(STATUS_FATAL))) { if (nbr->state == NBR_STA_OPENSENT) nbr_start_idtimer(nbr); @@ -269,11 +263,9 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) * initialization, it SHOULD transmit a Shutdown message and * then close the transport connection". */ - if (nbr->state != NBR_STA_OPER && - nm.status_code == S_SHUTDOWN) { + if (nbr->state != NBR_STA_OPER && nm.status_code == S_SHUTDOWN) { leconf->stats.session_attempts++; - send_notification(nbr->tcp, S_SHUTDOWN, - msg.id, msg.type); + send_notification(nbr->tcp, S_SHUTDOWN, msg.id, msg.type); } leconf->stats.shutdown_rcv_notify++; @@ -287,8 +279,7 @@ recv_notification(struct nbr *nbr, char *buf, uint16_t len) switch (nm.status_code) { case S_PW_STATUS: case S_ENDOFLIB: - ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, - &nm, sizeof(nm)); + ldpe_imsg_compose_lde(IMSG_NOTIFICATION, nbr->peerid, 0, &nm, sizeof(nm)); break; case S_NO_HELLO: leconf->stats.session_rejects_hello++; @@ -361,8 +352,8 @@ gen_returned_tlvs(struct ibuf *buf, uint16_t type, uint16_t length, tlv.length = htons(length); err = ibuf_add(buf, &rtlvs, sizeof(rtlvs)); - err |= ibuf_add(buf, &tlv, sizeof(tlv)); - err |= ibuf_add(buf, tlv_data, length); + SET_FLAG(err, ibuf_add(buf, &tlv, sizeof(tlv))); + SET_FLAG(err, ibuf_add(buf, tlv_data, length)); return (err); } @@ -378,9 +369,9 @@ log_msg_notification(int out, struct nbr *nbr, struct notify_msg *nm) debug_msg(out, "notification: lsr-id %pI4, status %s", &nbr->id, status_code_name(nm->status_code)); - if (nm->flags & F_NOTIF_FEC) + if (CHECK_FLAG(nm->flags, F_NOTIF_FEC)) debug_msg(out, "notification: fec %s", log_map(&nm->fec)); - if (nm->flags & F_NOTIF_PW_STATUS) + if (CHECK_FLAG(nm->flags, F_NOTIF_PW_STATUS)) debug_msg(out, "notification: pw-status %s", (nm->pw_status == PW_FORWARDING) ? "forwarding" : "not forwarding"); } diff --git a/ldpd/packet.c b/ldpd/packet.c index a253ef466087..d2d9305e714c 100644 --- a/ldpd/packet.c +++ b/ldpd/packet.c @@ -15,14 +15,13 @@ #include "sockopt.h" -static struct iface *disc_find_iface(unsigned int, int, - union ldpd_addr *); -static void session_read(struct thread *thread); -static void session_write(struct thread *thread); -static ssize_t session_get_pdu(struct ibuf_read *, char **); -static void tcp_close(struct tcp_conn *); -static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); -static void pending_conn_timeout(struct thread *thread); +static struct iface *disc_find_iface(unsigned int, int, union ldpd_addr *); +static void session_read(struct event *thread); +static void session_write(struct event *thread); +static ssize_t session_get_pdu(struct ibuf_read *, char **); +static void tcp_close(struct tcp_conn *); +static struct pending_conn *pending_conn_new(int, int, union ldpd_addr *); +static void pending_conn_timeout(struct event *thread); int gen_ldp_hdr(struct ibuf *buf, uint16_t size) @@ -95,10 +94,10 @@ send_packet(int fd, int af, union ldpd_addr *dst, struct iface_af *ia, } /* Discovery functions */ -void disc_recv_packet(struct thread *thread) +void disc_recv_packet(struct event *thread) { - int fd = THREAD_FD(thread); - struct thread **threadp = THREAD_ARG(thread); + int fd = EVENT_FD(thread); + struct event **threadp = EVENT_ARG(thread); union { struct cmsghdr hdr; @@ -129,7 +128,7 @@ void disc_recv_packet(struct thread *thread) struct in_addr lsr_id; /* reschedule read */ - thread_add_read(master, disc_recv_packet, threadp, fd, threadp); + event_add_read(master, disc_recv_packet, threadp, fd, threadp); /* setup buffer */ memset(&m, 0, sizeof(m)); @@ -144,8 +143,7 @@ void disc_recv_packet(struct thread *thread) if ((r = recvmsg(fd, &m, 0)) == -1) { if (errno != EAGAIN && errno != EINTR) - log_debug("%s: read error: %s", __func__, - strerror(errno)); + log_debug("%s: read error: %s", __func__, strerror(errno)); return; } @@ -154,10 +152,10 @@ void disc_recv_packet(struct thread *thread) multicast = (m.msg_flags & MSG_MCAST) ? 1 : 0; #else multicast = 0; - for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; - cmsg = CMSG_NXTHDR(&m, cmsg)) { + for (cmsg = CMSG_FIRSTHDR(&m); cmsg != NULL; cmsg = CMSG_NXTHDR(&m, cmsg)) { #if defined(HAVE_IP_PKTINFO) - if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + if (af == AF_INET && + cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) { struct in_pktinfo *pktinfo; @@ -167,7 +165,8 @@ void disc_recv_packet(struct thread *thread) break; } #elif defined(HAVE_IP_RECVDSTADDR) - if (af == AF_INET && cmsg->cmsg_level == IPPROTO_IP && + if (af == AF_INET && + cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { struct in_addr *addr; @@ -179,7 +178,8 @@ void disc_recv_packet(struct thread *thread) #else #error "Unsupported socket API" #endif - if (af == AF_INET6 && cmsg->cmsg_level == IPPROTO_IPV6 && + if (af == AF_INET6 && + cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { struct in6_pktinfo *pktinfo; @@ -191,8 +191,7 @@ void disc_recv_packet(struct thread *thread) } #endif /* MSG_MCAST */ if (bad_addr(af, &src)) { - log_debug("%s: invalid source address: %s", __func__, - log_addr(af, &src)); + log_debug("%s: invalid source address: %s", __func__, log_addr(af, &src)); return; } ifindex = getsockopt_ifindex(af, &m); @@ -207,8 +206,7 @@ void disc_recv_packet(struct thread *thread) /* check packet size */ len = (uint16_t)r; if (len < (LDP_HDR_SIZE + LDP_MSG_SIZE) || len > LDP_MAX_LEN) { - log_debug("%s: bad packet size, source %s", __func__, - log_addr(af, &src)); + log_debug("%s: bad packet size, source %s", __func__, log_addr(af, &src)); return; } @@ -290,9 +288,9 @@ disc_find_iface(unsigned int ifindex, int af, union ldpd_addr *src) return (iface); } -void session_accept(struct thread *thread) +void session_accept(struct event *thread) { - int fd = THREAD_FD(thread); + int fd = EVENT_FD(thread); struct sockaddr_storage src; socklen_t len = sizeof(src); int newfd; @@ -309,10 +307,8 @@ void session_accept(struct thread *thread) */ if (errno == ENFILE || errno == EMFILE) { accept_pause(); - } else if (errno != EWOULDBLOCK && errno != EINTR && - errno != ECONNABORTED) - log_debug("%s: accept error: %s", __func__, - strerror(errno)); + } else if (errno != EWOULDBLOCK && errno != EINTR && errno != ECONNABORTED) + log_debug("%s: accept error: %s", __func__, strerror(errno)); return; } sock_set_nonblock(newfd); @@ -394,10 +390,10 @@ session_accept_nbr(struct nbr *nbr, int fd) nbr_fsm(nbr, NBR_EVT_MATCH_ADJ); } -static void session_read(struct thread *thread) +static void session_read(struct event *thread) { - int fd = THREAD_FD(thread); - struct nbr *nbr = THREAD_ARG(thread); + int fd = EVENT_FD(thread); + struct nbr *nbr = EVENT_ARG(thread); struct tcp_conn *tcp = nbr->tcp; struct ldp_hdr *ldp_hdr; struct ldp_msg *msg; @@ -406,7 +402,7 @@ static void session_read(struct thread *thread) uint16_t pdu_len, msg_len, msg_size, max_pdu_len; int ret; - thread_add_read(master, session_read, nbr, fd, &tcp->rev); + event_add_read(master, session_read, nbr, fd, &tcp->rev); if ((n = read(fd, tcp->rbuf->buf + tcp->rbuf->wpos, sizeof(tcp->rbuf->buf) - tcp->rbuf->wpos)) == -1) { @@ -445,15 +441,13 @@ static void session_read(struct thread *thread) max_pdu_len = nbr->max_pdu_len; else max_pdu_len = LDP_MAX_LEN; - if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || - pdu_len > max_pdu_len) { + if (pdu_len < (LDP_HDR_PDU_LEN + LDP_MSG_SIZE) || pdu_len > max_pdu_len) { session_shutdown(nbr, S_BAD_PDU_LEN, 0, 0); free(buf); return; } pdu_len -= LDP_HDR_PDU_LEN; - if (ldp_hdr->lsr_id != nbr->id.s_addr || - ldp_hdr->lspace_id != 0) { + if (ldp_hdr->lsr_id != nbr->id.s_addr || ldp_hdr->lspace_id != 0) { session_shutdown(nbr, S_BAD_LDP_ID, 0, 0); free(buf); return; @@ -469,10 +463,8 @@ static void session_read(struct thread *thread) msg = (struct ldp_msg *)pdu; type = ntohs(msg->type); msg_len = ntohs(msg->length); - if (msg_len < LDP_MSG_LEN || - (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) { - session_shutdown(nbr, S_BAD_MSG_LEN, msg->id, - msg->type); + if (msg_len < LDP_MSG_LEN || (msg_len + LDP_MSG_DEAD_LEN) > pdu_len) { + session_shutdown(nbr, S_BAD_MSG_LEN, msg->id, msg->type); free(buf); return; } @@ -484,8 +476,7 @@ static void session_read(struct thread *thread) case MSG_TYPE_INIT: if ((nbr->state != NBR_STA_INITIAL) && (nbr->state != NBR_STA_OPENSENT)) { - session_shutdown(nbr, S_SHUTDOWN, - msg->id, msg->type); + session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); return; } @@ -493,8 +484,7 @@ static void session_read(struct thread *thread) case MSG_TYPE_KEEPALIVE: if ((nbr->state == NBR_STA_INITIAL) || (nbr->state == NBR_STA_OPENSENT)) { - session_shutdown(nbr, S_SHUTDOWN, - msg->id, msg->type); + session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); return; } @@ -503,8 +493,7 @@ static void session_read(struct thread *thread) break; default: if (nbr->state != NBR_STA_OPER) { - session_shutdown(nbr, S_SHUTDOWN, - msg->id, msg->type); + session_shutdown(nbr, S_SHUTDOWN, msg->id, msg->type); free(buf); return; } @@ -534,16 +523,14 @@ static void session_read(struct thread *thread) case MSG_TYPE_LABELWITHDRAW: case MSG_TYPE_LABELRELEASE: case MSG_TYPE_LABELABORTREQ: - ret = recv_labelmessage(nbr, pdu, msg_size, - type); + ret = recv_labelmessage(nbr, pdu, msg_size, type); break; default: log_debug("%s: unknown LDP message from nbr %pI4", __func__, &nbr->id); if (!(ntohs(msg->type) & UNKNOWN_FLAG)) { nbr->stats.unknown_msg++; - send_notification(nbr->tcp, - S_UNKNOWN_MSG, msg->id, msg->type); + send_notification(nbr->tcp, S_UNKNOWN_MSG, msg->id, msg->type); } /* ignore the message */ ret = 0; @@ -610,9 +597,9 @@ static void session_read(struct thread *thread) free(buf); } -static void session_write(struct thread *thread) +static void session_write(struct event *thread) { - struct tcp_conn *tcp = THREAD_ARG(thread); + struct tcp_conn *tcp = EVENT_ARG(thread); struct nbr *nbr = tcp->nbr; tcp->wbuf.ev = NULL; @@ -640,7 +627,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, switch (nbr->state) { case NBR_STA_PRESENT: if (nbr_pending_connect(nbr)) - THREAD_OFF(nbr->ev_connect); + EVENT_OFF(nbr->ev_connect); break; case NBR_STA_INITIAL: case NBR_STA_OPENREC: @@ -664,8 +651,7 @@ session_shutdown(struct nbr *nbr, uint32_t status, uint32_t msg_id, void session_close(struct nbr *nbr) { - log_debug("%s: closing session with lsr-id %pI4", __func__, - &nbr->id); + log_debug("%s: closing session with lsr-id %pI4", __func__, &nbr->id); ldp_sync_fsm_nbr_event(nbr, LDP_SYNC_EVT_SESSION_CLOSE); @@ -721,7 +707,7 @@ tcp_new(int fd, struct nbr *nbr) if ((tcp->rbuf = calloc(1, sizeof(struct ibuf_read))) == NULL) fatal(__func__); - thread_add_read(master, session_read, nbr, tcp->fd, &tcp->rev); + event_add_read(master, session_read, nbr, tcp->fd, &tcp->rev); tcp->nbr = nbr; } @@ -745,7 +731,7 @@ tcp_close(struct tcp_conn *tcp) evbuf_clear(&tcp->wbuf); if (tcp->nbr) { - THREAD_OFF(tcp->rev); + EVENT_OFF(tcp->rev); free(tcp->rbuf); tcp->nbr->tcp = NULL; } @@ -768,8 +754,8 @@ pending_conn_new(int fd, int af, union ldpd_addr *addr) pconn->addr = *addr; TAILQ_INSERT_TAIL(&global.pending_conns, pconn, entry); pconn->ev_timeout = NULL; - thread_add_timer(master, pending_conn_timeout, pconn, PENDING_CONN_TIMEOUT, - &pconn->ev_timeout); + event_add_timer(master, pending_conn_timeout, pconn, + PENDING_CONN_TIMEOUT, &pconn->ev_timeout); return (pconn); } @@ -777,7 +763,7 @@ pending_conn_new(int fd, int af, union ldpd_addr *addr) void pending_conn_del(struct pending_conn *pconn) { - THREAD_OFF(pconn->ev_timeout); + EVENT_OFF(pconn->ev_timeout); TAILQ_REMOVE(&global.pending_conns, pconn, entry); free(pconn); } @@ -788,16 +774,15 @@ pending_conn_find(int af, union ldpd_addr *addr) struct pending_conn *pconn; TAILQ_FOREACH(pconn, &global.pending_conns, entry) - if (af == pconn->af && - ldp_addrcmp(af, addr, &pconn->addr) == 0) + if (af == pconn->af && ldp_addrcmp(af, addr, &pconn->addr) == 0) return (pconn); return (NULL); } -static void pending_conn_timeout(struct thread *thread) +static void pending_conn_timeout(struct event *thread) { - struct pending_conn *pconn = THREAD_ARG(thread); + struct pending_conn *pconn = EVENT_ARG(thread); struct tcp_conn *tcp; pconn->ev_timeout = NULL; diff --git a/ldpd/pfkey.c b/ldpd/pfkey.c index 4bea2e190437..ae771cae194f 100644 --- a/ldpd/pfkey.c +++ b/ldpd/pfkey.c @@ -256,8 +256,7 @@ pfkey_read(int sd, struct sadb_msg *h) } /* XXX: Only one message can be outstanding. */ - if (hdr.sadb_msg_seq == sadb_msg_seq && - hdr.sadb_msg_pid == pid) { + if (hdr.sadb_msg_seq == sadb_msg_seq && hdr.sadb_msg_pid == pid) { if (h) *h = hdr; return (0); @@ -412,8 +411,7 @@ pfkey_establish(struct nbr *nbr, struct nbr_params *nbrp) { switch (nbr->auth.method) { case AUTH_MD5SIG: - strlcpy(nbr->auth.md5key, nbrp->auth.md5key, - sizeof(nbr->auth.md5key)); + strlcpy(nbr->auth.md5key, nbrp->auth.md5key, sizeof(nbr->auth.md5key)); return pfkey_md5sig_establish(nbr, nbrp); case AUTH_NONE: return 0; diff --git a/ldpd/socket.c b/ldpd/socket.c index ec6d8be3d5d6..6b7e475d7f62 100644 --- a/ldpd/socket.c +++ b/ldpd/socket.c @@ -89,8 +89,7 @@ ldp_create_socket(int af, enum socket_type type) return (-1); } if (type == LDP_SOCKET_DISC) { - if (sock_set_ipv4_mcast_ttl(fd, - IP_DEFAULT_MULTICAST_TTL) == -1) { + if (sock_set_ipv4_mcast_ttl(fd, IP_DEFAULT_MULTICAST_TTL) == -1) { close(fd); return (-1); } @@ -141,7 +140,7 @@ ldp_create_socket(int af, enum socket_type type) close(fd); return (-1); } - if (!(ldpd_conf->ipv6.flags & F_LDPD_AF_NO_GTSM)) { + if (!CHECK_FLAG(ldpd_conf->ipv6.flags, F_LDPD_AF_NO_GTSM)) { /* ignore any possible error */ sock_set_ipv6_minhopcount(fd, 255); } @@ -171,8 +170,7 @@ ldp_create_socket(int af, enum socket_type type) #ifdef __OpenBSD__ opt = 1; - if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, - sizeof(opt)) == -1) { + if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &opt, sizeof(opt)) == -1) { if (errno == ENOPROTOOPT) { /* system w/o md5sig */ log_warnx("md5sig not available, disabling"); sysdep.no_md5sig = 1; @@ -196,7 +194,7 @@ sock_set_nonblock(int fd) if ((flags = fcntl(fd, F_GETFL, 0)) == -1) fatal("fcntl F_GETFL"); - flags |= O_NONBLOCK; + SET_FLAG(flags, O_NONBLOCK); if (fcntl(fd, F_SETFL, flags) == -1) fatal("fcntl F_SETFL"); @@ -210,7 +208,7 @@ sock_set_cloexec(int fd) if ((flags = fcntl(fd, F_GETFD, 0)) == -1) fatal("fcntl F_GETFD"); - flags |= FD_CLOEXEC; + SET_FLAG(flags, FD_CLOEXEC); if (fcntl(fd, F_SETFD, flags) == -1) fatal("fcntl F_SETFD"); @@ -222,16 +220,14 @@ sock_set_recvbuf(int fd) int bsize; bsize = 65535; - while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, - sizeof(bsize)) == -1) + while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)) == -1) bsize /= 2; } int sock_set_reuse(int fd, int enable) { - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, - sizeof(int)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { log_warn("%s: error setting SO_REUSEADDR", __func__); return (-1); } @@ -244,8 +240,7 @@ sock_set_bindany(int fd, int enable) { #ifdef HAVE_SO_BINDANY frr_with_privs(&ldpd_privs) { - if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, - sizeof(int)) < 0) { + if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable, sizeof(int)) < 0) { log_warn("%s: error setting SO_BINDANY", __func__); return (-1); } @@ -259,8 +254,7 @@ sock_set_bindany(int fd, int enable) return (0); #elif defined(IP_BINDANY) frr_with_privs(&ldpd_privs) { - if (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(int)) - < 0) { + if (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(int)) < 0) { log_warn("%s: error setting IP_BINDANY", __func__); return (-1); } @@ -343,10 +337,8 @@ sock_set_ipv4_ucast_ttl(int fd, int ttl) int sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl) { - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, - (char *)&ttl, sizeof(ttl)) < 0) { - log_warn("%s: error setting IP_MULTICAST_TTL to %d", - __func__, ttl); + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) < 0) { + log_warn("%s: error setting IP_MULTICAST_TTL to %d", __func__, ttl); return (-1); } @@ -358,8 +350,7 @@ sock_set_ipv4_mcast_ttl(int fd, uint8_t ttl) int sock_set_ipv4_pktinfo(int fd, int enable) { - if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &enable, - sizeof(enable)) < 0) { + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable)) < 0) { log_warn("%s: error setting IP_PKTINFO", __func__); return (-1); } @@ -370,8 +361,7 @@ sock_set_ipv4_pktinfo(int fd, int enable) int sock_set_ipv4_recvdstaddr(int fd, int enable) { - if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &enable, - sizeof(enable)) < 0) { + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &enable, sizeof(enable)) < 0) { log_warn("%s: error setting IP_RECVDSTADDR", __func__); return (-1); } @@ -409,8 +399,7 @@ sock_set_ipv4_mcast_loop(int fd) int sock_set_ipv6_dscp(int fd, int dscp) { - if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp, - sizeof(dscp)) < 0) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof(dscp)) < 0) { log_warn("%s: error setting IPV6_TCLASS", __func__); return (-1); } @@ -421,8 +410,7 @@ sock_set_ipv6_dscp(int fd, int dscp) int sock_set_ipv6_pktinfo(int fd, int enable) { - if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable, - sizeof(enable)) < 0) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable, sizeof(enable)) < 0) { log_warn("%s: error setting IPV6_RECVPKTINFO", __func__); return (-1); } diff --git a/lib/agentx.c b/lib/agentx.c index 2f45ae83326e..45f14c270393 100644 --- a/lib/agentx.c +++ b/lib/agentx.c @@ -27,13 +27,13 @@ DEFINE_HOOK(agentx_enabled, (), ()); static bool agentx_enabled = false; -static struct thread_master *agentx_tm; -static struct thread *timeout_thr = NULL; +static struct event_loop *agentx_tm; +static struct event *timeout_thr = NULL; static struct list *events = NULL; static void agentx_events_update(void); -static void agentx_timeout(struct thread *t) +static void agentx_timeout(struct event *t) { snmp_timeout(); run_alarms(); @@ -41,18 +41,18 @@ static void agentx_timeout(struct thread *t) agentx_events_update(); } -static void agentx_read(struct thread *t) +static void agentx_read(struct event *t) { fd_set fds; int flags, new_flags = 0; int nonblock = false; - struct listnode *ln = THREAD_ARG(t); - struct thread **thr = listgetdata(ln); + struct listnode *ln = EVENT_ARG(t); + struct event **thr = listgetdata(ln); XFREE(MTYPE_TMP, thr); list_delete_node(events, ln); /* fix for non blocking socket */ - flags = fcntl(THREAD_FD(t), F_GETFL, 0); + flags = fcntl(EVENT_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); @@ -62,19 +62,19 @@ static void agentx_read(struct thread *t) if (flags & O_NONBLOCK) nonblock = true; else - new_flags = fcntl(THREAD_FD(t), F_SETFL, flags | O_NONBLOCK); + new_flags = fcntl(EVENT_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); + FD_SET(EVENT_FD(t), &fds); snmp_read(&fds); /* Reset the flag */ if (!nonblock) { - new_flags = fcntl(THREAD_FD(t), F_SETFL, flags); + new_flags = fcntl(EVENT_FD(t), F_SETFL, flags); if (new_flags == -1) flog_err( @@ -94,22 +94,22 @@ static void agentx_events_update(void) struct timeval timeout = {.tv_sec = 0, .tv_usec = 0}; fd_set fds; struct listnode *ln; - struct thread **thr; + struct event **thr; int fd, thr_fd; - thread_cancel(&timeout_thr); + event_cancel(&timeout_thr); FD_ZERO(&fds); snmp_select_info(&maxfd, &fds, &timeout, &block); if (!block) { - thread_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout, - &timeout_thr); + event_add_timer_tv(agentx_tm, agentx_timeout, NULL, &timeout, + &timeout_thr); } ln = listhead(events); thr = ln ? listgetdata(ln) : NULL; - thr_fd = thr ? THREAD_FD(*thr) : -1; + thr_fd = thr ? EVENT_FD(*thr) : -1; /* "two-pointer" / two-list simultaneous iteration * ln/thr/thr_fd point to the next existing event listener to hit while @@ -119,21 +119,21 @@ static void agentx_events_update(void) if (thr_fd == fd) { struct listnode *nextln = listnextnode(ln); if (!FD_ISSET(fd, &fds)) { - thread_cancel(thr); + event_cancel(thr); XFREE(MTYPE_TMP, thr); list_delete_node(events, ln); } ln = nextln; thr = ln ? listgetdata(ln) : NULL; - thr_fd = thr ? THREAD_FD(*thr) : -1; + thr_fd = thr ? EVENT_FD(*thr) : -1; } /* need listener, but haven't hit one where it would be */ else if (FD_ISSET(fd, &fds)) { struct listnode *newln; - thr = XCALLOC(MTYPE_TMP, sizeof(struct thread *)); + thr = XCALLOC(MTYPE_TMP, sizeof(struct event *)); newln = listnode_add_before(events, ln, thr); - thread_add_read(agentx_tm, agentx_read, newln, fd, thr); + event_add_read(agentx_tm, agentx_read, newln, fd, thr); } } @@ -142,7 +142,7 @@ static void agentx_events_update(void) while (ln) { struct listnode *nextln = listnextnode(ln); thr = listgetdata(ln); - thread_cancel(thr); + event_cancel(thr); XFREE(MTYPE_TMP, thr); list_delete_node(events, ln); ln = nextln; @@ -244,7 +244,7 @@ bool smux_enabled(void) return agentx_enabled; } -void smux_init(struct thread_master *tm) +void smux_init(struct event_loop *tm) { agentx_tm = tm; diff --git a/lib/asn.h b/lib/asn.h index 81a42c658d74..a7394fa52bc6 100644 --- a/lib/asn.h +++ b/lib/asn.h @@ -66,10 +66,10 @@ extern char *asn_asn2string(const as_t *as, char *buf, size_t len, ((mode == ASNOTATION_DOT) ? "%pASD" : \ ((mode == ASNOTATION_DOTPLUS) ? "%pASE" : \ "%pASP")) -#define ASN_FORMAT_SPACE(mode) \ - ((mode == ASNOTATION_DOT) ? "%10pASD" : \ - ((mode == ASNOTATION_DOTPLUS) ? "%10pASE" : \ - "%10pASP")) +#define ASN_FORMAT_SPACE(mode) \ + ((mode == ASNOTATION_DOT) \ + ? "%11pASD" \ + : ((mode == ASNOTATION_DOTPLUS) ? "%11pASE" : "%11pASP")) /* for test */ extern void asn_relax_as_zero(bool relax); diff --git a/lib/bfd.c b/lib/bfd.c index 0d77c5c822de..cc6d09a60f5a 100644 --- a/lib/bfd.c +++ b/lib/bfd.c @@ -10,7 +10,7 @@ #include "command.h" #include "memory.h" #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "stream.h" #include "vrf.h" #include "zclient.h" @@ -75,7 +75,7 @@ struct bfd_session_params { * Next event. * * This variable controls what action to execute when the command batch - * finishes. Normally we'd use `thread_add_event` value, however since + * finishes. Normally we'd use `event_add_event` value, however since * that function is going to be called multiple times and the value * might be different we'll use this variable to keep track of it. */ @@ -87,7 +87,7 @@ struct bfd_session_params { * configuration load or northbound batch), so we'll use this to * install/uninstall the BFD session parameters only once. */ - struct thread *installev; + struct event *installev; /** BFD session installation state. */ bool installed; @@ -111,7 +111,7 @@ struct bfd_sessions_global { struct bfd_source_list source_list; /** Pointer to FRR's event manager. */ - struct thread_master *tm; + struct event_loop *tm; /** Pointer to zebra client data structure. */ struct zclient *zc; @@ -485,9 +485,9 @@ static bool _bfd_sess_valid(const struct bfd_session_params *bsp) return true; } -static void _bfd_sess_send(struct thread *t) +static void _bfd_sess_send(struct event *t) { - struct bfd_session_params *bsp = THREAD_ARG(t); + struct bfd_session_params *bsp = EVENT_ARG(t); int rv; /* Validate configuration before trying to send bogus data. */ @@ -532,16 +532,16 @@ static void _bfd_sess_send(struct thread *t) static void _bfd_sess_remove(struct bfd_session_params *bsp) { + /* Cancel any pending installation request. */ + EVENT_OFF(bsp->installev); + /* Not installed, nothing to do. */ if (!bsp->installed) return; - /* Cancel any pending installation request. */ - THREAD_OFF(bsp->installev); - /* Send request to remove any session. */ bsp->lastev = BSE_UNINSTALL; - thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0); + event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0); } void bfd_sess_free(struct bfd_session_params **bsp) @@ -733,13 +733,13 @@ void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable) void bfd_sess_install(struct bfd_session_params *bsp) { bsp->lastev = BSE_INSTALL; - thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); + event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); } void bfd_sess_uninstall(struct bfd_session_params *bsp) { bsp->lastev = BSE_UNINSTALL; - thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); + event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev); } enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp) @@ -890,11 +890,11 @@ int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS) bsp->installed = false; /* Cancel any pending installation request. */ - THREAD_OFF(bsp->installev); + EVENT_OFF(bsp->installev); /* Ask for installation. */ bsp->lastev = BSE_INSTALL; - thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0); + event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0); } return 0; @@ -1039,7 +1039,7 @@ static int bfd_protocol_integration_finish(void) return 0; } -void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm) +void bfd_protocol_integration_init(struct zclient *zc, struct event_loop *tm) { /* Initialize data structure. */ TAILQ_INIT(&bsglobal.bsplist); diff --git a/lib/bfd.h b/lib/bfd.h index 66c08cde749d..bfa5287340f2 100644 --- a/lib/bfd.h +++ b/lib/bfd.h @@ -348,7 +348,7 @@ void bfd_sess_show(struct vty *vty, struct json_object *json, * Initializes the BFD integration library. This function executes the * following actions: * - * - Copy the `struct thread_master` pointer to use as "thread" to execute + * - Copy the `struct event_loop` pointer to use as "thread" to execute * the BFD session parameters installation. * - Copy the `struct zclient` pointer to install its callbacks. * - Initializes internal data structures. @@ -356,8 +356,7 @@ void bfd_sess_show(struct vty *vty, struct json_object *json, * \param tm normally the daemon main thread event manager. * \param zc the zebra client of the daemon. */ -void bfd_protocol_integration_init(struct zclient *zc, - struct thread_master *tm); +void bfd_protocol_integration_init(struct zclient *zc, struct event_loop *tm); /** * BFD session registration arguments. diff --git a/lib/clippy.c b/lib/clippy.c index c3c9f5c4016d..d414053c5538 100644 --- a/lib/clippy.c +++ b/lib/clippy.c @@ -14,6 +14,53 @@ #include "command_graph.h" #include "clippy.h" +#if PY_VERSION_HEX >= 0x03080000 +/* new python init/config API added in Python 3.8 */ +int main(int argc, char **argv) +{ + PyStatus status; + PyPreConfig preconfig[1]; + PyConfig config[1]; + + PyPreConfig_InitPythonConfig(preconfig); + preconfig->configure_locale = 0; + preconfig->coerce_c_locale = 1; + preconfig->coerce_c_locale_warn = 0; + preconfig->isolated = 0; + preconfig->utf8_mode = 1; + preconfig->parse_argv = 0; + + status = Py_PreInitializeFromBytesArgs(preconfig, argc, argv); + if (PyStatus_Exception(status)) + Py_ExitStatusException(status); + + PyConfig_InitPythonConfig(config); +#if PY_VERSION_HEX >= 0x030b0000 /* 3.11 */ + config->safe_path = 0; +#endif + + status = PyConfig_SetBytesArgv(config, argc, argv); + if (PyStatus_Exception(status)) + Py_ExitStatusException(status); + + PyConfig_SetBytesString(config, &config->program_name, + argc > 0 ? argv[0] : "clippy"); + if (argc > 1) + PyConfig_SetBytesString(config, &config->run_filename, argv[1]); + + PyImport_AppendInittab("_clippy", command_py_init); + + status = Py_InitializeFromConfig(config); + if (PyStatus_Exception(status)) + Py_ExitStatusException(status); + + PyConfig_Clear(config); + + return Py_RunMain(); +} + +#else /* Python < 3.8 */ +/* old python init/config API, deprecated in Python 3.11 */ #if PY_MAJOR_VERSION >= 3 #define pychar wchar_t static wchar_t *wconv(const char *s) @@ -89,6 +136,7 @@ int main(int argc, char **argv) free(wargv); return 0; } +#endif /* Python < 3.8 */ /* and now for the ugly part... provide simplified logging functions so we * don't need to link libzebra (which would be a circular build dep) */ diff --git a/lib/command.c b/lib/command.c index ee5a3889e8c0..8025ab534fcf 100644 --- a/lib/command.c +++ b/lib/command.c @@ -17,7 +17,7 @@ #include "memory.h" #include "log.h" #include "log_vty.h" -#include "thread.h" +#include "frrevent.h" #include "vector.h" #include "linklist.h" #include "vty.h" @@ -31,6 +31,8 @@ #include "jhash.h" #include "hook.h" #include "lib_errors.h" +#include "mgmt_be_client.h" +#include "mgmt_fe_client.h" #include "northbound_cli.h" #include "network.h" #include "routemap.h" @@ -733,9 +735,13 @@ char *cmd_variable_comp2str(vector comps, unsigned short cols) char *item = vector_slot(comps, j); itemlen = strlen(item); - if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz) - buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2)); + size_t next_sz = cs + itemlen + AUTOCOMP_INDENT + 3; + if (next_sz > bsz) { + /* Make sure the buf size is large enough */ + bsz = next_sz; + buf = XREALLOC(MTYPE_TMP, buf, bsz); + } if (lc + itemlen + 1 >= cols) { cs += snprintf(&buf[cs], bsz - cs, "\n%*s", AUTOCOMP_INDENT, ""); @@ -1281,6 +1287,7 @@ int command_config_read_one_line(struct vty *vty, memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ); ve->line_num = line_num; + ve->cmd_ret = ret; if (!vty->error) vty->error = list_new(); @@ -1301,6 +1308,14 @@ int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num) while (fgets(vty->buf, VTY_BUFSIZ, fp)) { ++(*line_num); + if (vty_log_commands) { + int len = strlen(vty->buf); + + /* now log the command */ + zlog_notice("config-from-file# %.*s", len ? len - 1 : 0, + vty->buf); + } + ret = command_config_read_one_line(vty, NULL, *line_num, 0); if (ret != CMD_SUCCESS && ret != CMD_WARNING @@ -1318,11 +1333,12 @@ 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 [file-lock]]", "Configuration from vty interface\n" + "Configuration with locked datastores\n" "Configuration terminal\n") { - return vty_config_enter(vty, false, false); + return vty_config_enter(vty, false, false, argc == 3); } /* Enable command */ @@ -2438,6 +2454,8 @@ const char *host_config_get(void) void cmd_show_lib_debugs(struct vty *vty) { route_map_show_debug(vty); + mgmt_debug_be_client_show_debug(vty); + mgmt_debug_fe_client_show_debug(vty); } void install_default(enum node_type node) @@ -2542,7 +2560,7 @@ void cmd_init(int terminal) install_default(CONFIG_NODE); - thread_cmd_init(); + event_cmd_init(); workqueue_cmd_init(); hash_cmd_init(); } @@ -2596,9 +2614,7 @@ void cmd_terminate(void) // well graph_delete_graph(cmd_node->cmdgraph); vector_free(cmd_node->cmd_vector); - hash_clean(cmd_node->cmd_hash, NULL); - hash_free(cmd_node->cmd_hash); - cmd_node->cmd_hash = NULL; + hash_clean_and_free(&cmd_node->cmd_hash, NULL); } vector_free(cmdvec); diff --git a/lib/command.h b/lib/command.h index 6538e5658809..39fbfa661a02 100644 --- a/lib/command.h +++ b/lib/command.h @@ -129,6 +129,7 @@ enum node_type { LDP_L2VPN_NODE, /* LDP L2VPN node */ LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ ISIS_NODE, /* ISIS protocol mode */ + ISIS_FLEX_ALGO_NODE, /* ISIS Flex Algo mode */ ACCESS_NODE, /* Access list node. */ PREFIX_NODE, /* Prefix list node. */ ACCESS_IPV6_NODE, /* Access list node. */ @@ -429,6 +430,11 @@ struct cmd_node { #define SHARP_STR "Sharp Routing Protocol\n" #define OSPF_GR_STR \ "OSPF non-stop forwarding (NSF) also known as OSPF Graceful Restart\n" +#define MGMTD_STR "Management Daemon (MGMTD) information\n" +#define MGMTD_BE_ADAPTER_STR "MGMTD Backend Adapter information\n" +#define MGMTD_FE_ADAPTER_STR "MGMTD Frontend Adapter information\n" +#define MGMTD_TXN_STR "MGMTD Transaction information\n" +#define MGMTD_DS_STR "MGMTD Datastore information\n" #define CMD_VNI_RANGE "(1-16777215)" #define CONF_BACKUP_EXT ".sav" diff --git a/lib/compiler.h b/lib/compiler.h index d12e2828326f..29fcfbefbf2d 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -439,6 +439,14 @@ _Static_assert(sizeof(_uint64_t) == 8 && sizeof(_int64_t) == 8, #pragma diag_suppress 167 #endif /* __INTELISENSE__ */ +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define likely(_x) __builtin_expect(!!(_x), 1) +#define unlikely(_x) __builtin_expect(!!(_x), 0) +#else +#define likely(_x) !!(_x) +#define unlikely(_x) !!(_x) +#endif + #ifdef __cplusplus } #endif diff --git a/lib/cspf.c b/lib/cspf.c index b92c9cb395de..6a0fb7f63cc2 100644 --- a/lib/cspf.c +++ b/lib/cspf.c @@ -88,7 +88,7 @@ static struct c_path *cpath_copy(struct c_path *dest, const struct c_path *src) * * @param path Constrained Path structure to be deleted */ -static void cpath_del(struct c_path *path) +void cpath_del(struct c_path *path) { if (!path) return; diff --git a/lib/cspf.h b/lib/cspf.h index 3eceaa04af05..bba685a6172d 100644 --- a/lib/cspf.h +++ b/lib/cspf.h @@ -191,6 +191,8 @@ extern void cspf_del(struct cspf *algo); */ extern struct c_path *compute_p2p_path(struct cspf *algo, struct ls_ted *ted); +extern void cpath_del(struct c_path *path); + #ifdef __cplusplus } #endif diff --git a/lib/darr.c b/lib/darr.c new file mode 100644 index 000000000000..2c8b7b8778a1 --- /dev/null +++ b/lib/darr.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * June 23 2023, Christian Hopps <chopps@labn.net> + * + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ +#include <zebra.h> +#include "darr.h" + +void __dar_resize(void **a, uint count, size_t esize); + +static uint _msb(uint count) +{ + uint bit = 0; + int msb = 0; + + while (count) { + if (count & 1) + msb = bit; + count >>= 1; + bit += 1; + } + return msb; +} + +static uint darr_next_count(uint count, size_t esize) +{ + uint ncount; + + if (esize > sizeof(long long) && count == 1) + /* treat like a pointer */ + ncount = 1; + else { + uint msb = _msb(count); + + ncount = 1ull << msb; + /* if the users count wasn't a pow2 make it the next pow2. */ + if (ncount != count) { + assert(ncount < count); + ncount <<= 1; + if (esize < sizeof(long long) && ncount < 8) + ncount = 8; + } + } + return ncount; +} + +static size_t darr_size(uint count, size_t esize) +{ + return count * esize + sizeof(struct darr_metadata); +} + +void *__darr_resize(void *a, uint count, size_t esize) +{ + uint ncount = darr_next_count(count, esize); + size_t osz = (a == NULL) ? 0 : darr_size(darr_cap(a), esize); + size_t sz = darr_size(ncount, esize); + struct darr_metadata *dm = realloc(a ? _darr_meta(a) : NULL, sz); + /* do *not* use a */ + + assert(dm); + if (sz > osz) + memset((char *)dm + osz, 0, sz - osz); + + dm->cap = ncount; + + return (void *)(dm + 1); +} + + +void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero) +{ + + struct darr_metadata *dm; + uint olen, nlen; + + if (!a) + a = __darr_resize(NULL, at + count, esize); + dm = (struct darr_metadata *)a - 1; + olen = dm->len; + + // at == 1 + // count == 100 + // olen == 2 + + /* see if the user is expanding first using `at` */ + if (at >= olen) + nlen = at + count; + else + nlen = olen + count; + + if (nlen > dm->cap) { + a = __darr_resize(a, nlen, esize); + dm = (struct darr_metadata *)a - 1; + } + +#define _a_at(i) ((char *)a + ((i)*esize)) + if (at < olen) + memmove(_a_at(at + count), _a_at(at), esize * (olen - at)); + + dm->len = nlen; + + if (zero) { + if (at >= olen) { + at -= olen; + count += olen; + } + memset(_a_at(at), 0, esize * count); + } + + return (void *)a; +#undef _a_at +} diff --git a/lib/darr.h b/lib/darr.h new file mode 100644 index 000000000000..ca46fb30543f --- /dev/null +++ b/lib/darr.h @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * June 23 2023, Christian Hopps <chopps@labn.net> + * + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + * API functions: + * ============== + * - darr_append + * - darr_append_n + * - darr_append_nz + * - darr_cap + * - darr_ensure_cap + * - darr_ensure_i + * - darr_foreach_i + * - darr_foreach_p + * - darr_free + * - darr_insert + * - darr_insertz + * - darr_insert_n + * - darr_insert_nz + * - darr_len + * - darr_maxi + * - darr_pop + * - darr_push + * - darr_pushz + * - darr_remove + * - darr_remove_n + * - darr_reset + * - darr_setlen + */ +/* + * A few assured items + * + * - DAs will never have capacity 0 unless they are NULL pointers. + */ +#include <zebra.h> + +struct darr_metadata { + uint len; + uint cap; +}; +void *__darr_insert_n(void *a, uint at, uint count, size_t esize, bool zero); +void *__darr_resize(void *a, uint count, size_t esize); + +#define _darr_esize(A) sizeof((A)[0]) +#define darr_esize(A) sizeof((A)[0]) +#define _darr_len(A) _darr_meta(A)->len +#define _darr_meta(A) (((struct darr_metadata *)(A)) - 1) +#define _darr_resize(A, C) ({ (A) = __darr_resize((A), C, _darr_esize(A)); }) + +/* Get the current capacity of the array */ +#define darr_cap(A) (((A) == NULL) ? 0 : _darr_meta(A)->cap) + +/* Get the largest possible index one can `darr_ensure_i` w/o resizing */ +#define darr_maxi(A) ((int)darr_cap(A) - 1) + +/** + * Get the current length of the array. + * + * As long as `A` is non-NULL, this macro may be used as an L-value to modify + * the length of the array. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * The current length of the array. + */ +#define darr_len(A) (((A) == NULL) ? 0 : _darr_meta(A)->len) + +/** + * Set the current length of the array `A` to 0. + * + * Args: + * A: The dynamic array, can be NULL. + */ +#define darr_reset(A) \ + do { \ + if ((A)) \ + _darr_len(A) = 0; \ + } while (0) + +/** + * Set the current length of the array `A` to `L`. + * + * This function does *not* guarantee the memory is valid to L, + * use `darr_ensure` or `darr_ensure_cap` for that. + * + * Args: + * A: The dynamic array, can only be NULL if (L) == 0. + * L: The new length of the array. + */ +#define darr_setlen(A, L) \ + do { \ + assert((A) || !(L)); \ + if ((A)) { \ + /* have to cast to avoid compiler warning for "0" */ \ + assert((long long)darr_cap(A) >= (L)); \ + _darr_len(A) = (L); \ + } \ + } while (0) + +/** + * Free memory allocated for the dynamic array `A` + * + * Args: + * A: The dynamic array, can be NULL. + */ + +#define darr_free(A) \ + do { \ + if ((A)) { \ + free(_darr_meta(A)); \ + (A) = NULL; \ + } \ + } while (0) + +/** + * Make sure that there is room in the dynamic array `A` for `C` elements. + * + * The value `A` may be changed as a result of this call in which case any + * pointers into the previous memory block are no longer valid. The `A` value + * is guaranteed not to change if there is sufficient capacity in the array. + * + * Args: + * A: (IN/OUT) the dynamic array, can be NULL. + * I: the index to guarantee memory exists for + * + * Return: + * A pointer to the (possibly moved) array. + */ +#define darr_ensure_cap(A, C) \ + ({ \ + if (darr_cap(A) < (C)) \ + _darr_resize((A), (C)); \ + (A); \ + }) + +/** + * Return a pointer to the (I)th element of array `A`, making sure there is + * room for the element. + * + * If the array length is less than `I + 1` then the length is set to `I + 1`. + * + * The value `A` may be changed as a result of this call in which case any + * pointers into the previous memory block are no longer valid. The `A` value + * is guaranteed not to change if there is sufficient capacity in the array. + * + * Args: + * + * A: (IN/OUT) the dynamic array, can be NULL. + * I: the index to guarantee memory exists for + * + * Return: + * A pointer to the (I)th element in `A` + */ +#define darr_ensure_i(A, I) \ + ({ \ + if ((int)(I) > darr_maxi(A)) \ + _darr_resize((A), (I) + 1); \ + if ((I) + 1 > _darr_len(A)) \ + _darr_len(A) = (I) + 1; \ + &(A)[I]; \ + }) + +#define _darr_insert_n(A, I, N, Z) \ + ({ \ + (A) = __darr_insert_n(A, I, N, _darr_esize(A), Z); \ + &(A)[I]; \ + }) +/** + * Insert N uninitialized elements in the array at index `I`. + * + * Previous elements from `I` are shifted right by `N`. Array length is + * increased by `N`. + * + * The value `A` may be changed as a result of this call in which case any + * pointers into the previous memory block are no longer valid. The `A` value + * is guaranteed not to change if there is sufficient capacity in the array. + * + * The `z` variant zeros new elements. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * A pointer to the first inserted element in the array. + */ +#define darr_insert_n(A, I, N) _darr_insert_n(A, I, N, false) +#define darr_insert_nz(A, I, N) _darr_insert_n(A, I, N, true) + +/** + * Insert an uninitialized element in the array at index `I`. + * + * Previous elements from `I` are shifted right by 1. Array length is + * increased by 1. + * + * The value `A` may be changed as a result of this call in which case any + * pointers into the previous memory block are no longer valid. The `A` value + * is guaranteed not to change if there is sufficient capacity in the array. + * + * The `z` variant zeros the new element. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * A pointer to the element in the array. + */ +#define darr_insert(A, I) _darr_insert_n(A, I, 1, false) +#define darr_insertz(A, I) _darr_insert_n(A, I, 1, true) + +/** + * Remove `N` elements from the array starting at index `I`. + * + * Elements from `I` + `N` are shifted left by `N`. Array length is reduced by + * `N`. + * + * Args: + * A: The dynamic array, can be NULL. + */ +#define darr_remove_n(A, I, N) \ + do { \ + uint __i = (I); \ + uint __n = (N); \ + uint __len = darr_len(A); \ + if (!__len) \ + break; \ + else if (__i + __n < __len) { \ + memmove(&(A)[__i], &(A)[__i + __n], \ + _darr_esize(A) * (__len - (__i + __n))); \ + _darr_len(A) = __len - __n; \ + } else \ + _darr_len(A) = __i; \ + } while (0) + +/** + * Remove the `I`th element from the array. + * + * Previous elements from `I` + 1 are shifted left by 1, Array length is reduced + * by 1. + * + * Args: + * A: The dynamic array, can be NULL. + */ +#define darr_remove(A, I) darr_remove_n(A, I, 1) + + +#define _darr_append_n(A, N, Z) \ + ({ \ + uint __len = darr_len(A); \ + darr_ensure_cap(A, __len + (N)); \ + _darr_len(A) = __len + (N); \ + if (Z) \ + memset(&(A)[__len], 0, (N)*_darr_esize(A)); \ + &(A)[__len]; \ + }) +/** + * Extending the array's length by N. + * + * Args: + * A: The dynamic array, can be NULL. + * + * The `z` variant zeros new elements. + * + * Return: + * A pointer to the first of the added elements at the end of the array. + */ +#define darr_append_n(A, N) _darr_append_n(A, N, false) +#define darr_append_nz(A, N) _darr_append_n(A, N, true) + +/** + * Extending the array's length by 1. + * + * Args: + * A: The dynamic array, can be NULL. + * + * The `z` variant zeros the new element. + * + * Return: + * A pointer to the new element at the end of the array. + */ +#define darr_append(A) _darr_append_n(A, 1, false) +#define darr_appendz(A) _darr_append_n(A, 1, true) + +/** + * Append an element `E` onto the array `A`, extending it's length by 1. + * + * The `z` variant zeros the new element. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * A pointer to the element in the array. + */ +#define darr_push(A, E) (*darr_append(A) = (E)) +#define darr_pushz(A) (darr_appendz(A)) + + +/** + * Pop the last `N` elements from the array decrementing the length by `N`. + * + * Args: + * A: The dynamic array, can be NULL. + */ +#define darr_pop_n(A, N) \ + do { \ + if ((A) && (N) >= _darr_len(A)) \ + darr_reset(A); \ + else \ + _darr_len(A) -= (N); \ + } while (0) + + +/** + * Pop the last element from the array decrementing the length by 1. + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * The element just popped. + */ +#define darr_pop(A) \ + ({ \ + uint __len = _darr_len(A); \ + assert(__len); \ + darr_remove(A, __len - 1); \ + /* count on fact that we don't resize */ \ + (A)[__len - 1]; \ + }) + +/** + * Return the address at the end of the array -- useful for iterating + * + * Args: + * A: The dynamic array, can be NULL. + * + * Return: + * The address of the end of the array (past the last elment) or NULL + * if `A` is NULL. + */ +#define darr_end(A) ((A) + darr_len(A)) + +/** + * Iterate over array `A` using a pointer to each element in `P`. + * + * Args: + * A: The dynamic array, can be NULL. + * P: A variable with the same type as A used as the iterator. + */ +#define darr_foreach_p(A, P) for ((P) = (A); (P) < darr_end(A); (P)++) + +/** + * Iterate over array `A`s indices. + * + * Args: + * A: The dynamic array, can be NULL. + * I: A uint variable to store the current element index in. + */ +#define darr_foreach_i(A, I) for ((I) = 0; (I) < darr_len(A); (I)++) diff --git a/lib/defun_lex.l b/lib/defun_lex.l index 505ea59b0b61..124f864166f0 100644 --- a/lib/defun_lex.l +++ b/lib/defun_lex.l @@ -53,6 +53,7 @@ int comment_link; char string_end; char *value; +static const char *yyfilename; static void extendbuf(char **what, const char *arg) { @@ -119,8 +120,17 @@ SPECIAL [(),] } } <rstring>\\\n /* ignore */ +<rstring>\n { + fprintf(stderr, + "%s:%d: string continues past the end of the line\n", + yyfilename, yylineno); + free(value); + value = NULL; + BEGIN(INITIAL); + return STRING; + } <rstring>\\. extend(yytext); -<rstring>[^\\\"\']+ extend(yytext); +<rstring>[^\\\"\'\n]+ extend(yytext); "DEFUN" value = strdup(yytext); return DEFUNNY; "DEFUN_NOSH" value = strdup(yytext); return DEFUNNY; @@ -207,6 +217,10 @@ static PyObject *get_args(const char *filename, int lineno) if (tval[0] == ')') depth--; } + if (!tval) + return PyErr_Format(PyExc_ValueError, + "%s:%d: invalid token in DEFPY parameters", + filename, lineno); if (!pyArg) pyArg = PyList_New(0); PyList_Append(pyArg, PyUnicode_FromString(tval)); @@ -231,6 +245,7 @@ PyObject *clippy_parse(PyObject *self, PyObject *args) int token; yyin = fd; value = NULL; + yyfilename = filename; PyObject *pyCont = PyDict_New(); PyObject *pyObj = PyList_New(0); @@ -248,6 +263,7 @@ PyObject *clippy_parse(PyObject *self, PyObject *args) if (!pyArgs) { free(tval); Py_DECREF(pyCont); + yyfilename = NULL; return NULL; } pyItem = PyDict_New(); @@ -276,5 +292,6 @@ PyObject *clippy_parse(PyObject *self, PyObject *args) } def_yylex_destroy(); fclose(fd); + yyfilename = NULL; return pyCont; } diff --git a/lib/distribute.c b/lib/distribute.c index 4cfdcc7840e0..65487676d6c6 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -445,14 +445,15 @@ int config_write_distribute(struct vty *vty, void distribute_list_delete(struct distribute_ctx **ctx) { - if ((*ctx)->disthash) { - hash_clean((*ctx)->disthash, (void (*)(void *))distribute_free); + hash_clean_and_free(&(*ctx)->disthash, + (void (*)(void *))distribute_free); + + if (dist_ctx_list) { + listnode_delete(dist_ctx_list, *ctx); + if (list_isempty(dist_ctx_list)) + list_delete(&dist_ctx_list); } - if (!dist_ctx_list) - dist_ctx_list = list_new(); - listnode_delete(dist_ctx_list, *ctx); - if (list_isempty(dist_ctx_list)) - list_delete(&dist_ctx_list); + XFREE(MTYPE_DISTRIBUTE_CTX, (*ctx)); } diff --git a/lib/elf_py.c b/lib/elf_py.c index 05f5aef766fb..d473dc10cbf0 100644 --- a/lib/elf_py.c +++ b/lib/elf_py.c @@ -1140,7 +1140,8 @@ static PyObject *elffile_load(PyTypeObject *type, PyObject *args, fd = open(filename, O_RDONLY | O_NOCTTY); if (fd < 0 || fstat(fd, &st)) { PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename); - close(fd); + if (fd > 0) + close(fd); goto out; } w->len = st.st_size; diff --git a/lib/thread.c b/lib/event.c similarity index 76% rename from lib/thread.c rename to lib/event.c index 8324783a7b51..a8eb89f48d6d 100644 --- a/lib/thread.c +++ b/lib/event.c @@ -8,7 +8,7 @@ #include <zebra.h> #include <sys/resource.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "frrcu.h" #include "log.h" @@ -24,23 +24,23 @@ #include "libfrr.h" DEFINE_MTYPE_STATIC(LIB, THREAD, "Thread"); -DEFINE_MTYPE_STATIC(LIB, THREAD_MASTER, "Thread master"); -DEFINE_MTYPE_STATIC(LIB, THREAD_POLL, "Thread Poll Info"); -DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats"); +DEFINE_MTYPE_STATIC(LIB, EVENT_MASTER, "Thread master"); +DEFINE_MTYPE_STATIC(LIB, EVENT_POLL, "Thread Poll Info"); +DEFINE_MTYPE_STATIC(LIB, EVENT_STATS, "Thread stats"); -DECLARE_LIST(thread_list, struct thread, threaditem); +DECLARE_LIST(event_list, struct event, eventitem); struct cancel_req { int flags; - struct thread *thread; + struct event *thread; void *eventobj; - struct thread **threadref; + struct event **threadref; }; /* Flags for task cancellation */ -#define THREAD_CANCEL_FLAG_READY 0x01 +#define EVENT_CANCEL_FLAG_READY 0x01 -static int thread_timer_cmp(const struct thread *a, const struct thread *b) +static int event_timer_cmp(const struct event *a, const struct event *b) { if (a->u.sands.tv_sec < b->u.sands.tv_sec) return -1; @@ -53,7 +53,7 @@ static int thread_timer_cmp(const struct thread *a, const struct thread *b) return 0; } -DECLARE_HEAP(thread_timer_list, struct thread, timeritem, thread_timer_cmp); +DECLARE_HEAP(event_timer_list, struct event, timeritem, event_timer_cmp); #if defined(__APPLE__) #include <mach/mach.h> @@ -64,7 +64,7 @@ DECLARE_HEAP(thread_timer_list, struct thread, timeritem, thread_timer_cmp); do { \ const unsigned char wakebyte = 0x01; \ write(m->io_pipe[1], &wakebyte, 1); \ - } while (0); + } while (0) /* control variable for initializer */ static pthread_once_t init_once = PTHREAD_ONCE_INIT; @@ -73,7 +73,7 @@ pthread_key_t thread_current; static pthread_mutex_t masters_mtx = PTHREAD_MUTEX_INITIALIZER; static struct list *masters; -static void thread_free(struct thread_master *master, struct thread *thread); +static void thread_free(struct event_loop *master, struct event *thread); #ifndef EXCLUDE_CPU_TIME #define EXCLUDE_CPU_TIME 0 @@ -87,25 +87,26 @@ unsigned long cputime_threshold = CONSUMED_TIME_CHECK; unsigned long walltime_threshold = CONSUMED_TIME_CHECK; /* CLI start ---------------------------------------------------------------- */ -#include "lib/thread_clippy.c" +#include "lib/event_clippy.c" -static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a) +static unsigned int cpu_record_hash_key(const struct cpu_event_history *a) { int size = sizeof(a->func); return jhash(&a->func, size, 0); } -static bool cpu_record_hash_cmp(const struct cpu_thread_history *a, - const struct cpu_thread_history *b) +static bool cpu_record_hash_cmp(const struct cpu_event_history *a, + const struct cpu_event_history *b) { return a->func == b->func; } -static void *cpu_record_hash_alloc(struct cpu_thread_history *a) +static void *cpu_record_hash_alloc(struct cpu_event_history *a) { - struct cpu_thread_history *new; - new = XCALLOC(MTYPE_THREAD_STATS, sizeof(struct cpu_thread_history)); + struct cpu_event_history *new; + + new = XCALLOC(MTYPE_EVENT_STATS, sizeof(struct cpu_event_history)); new->func = a->func; new->funcname = a->funcname; return new; @@ -113,13 +114,13 @@ static void *cpu_record_hash_alloc(struct cpu_thread_history *a) static void cpu_record_hash_free(void *a) { - struct cpu_thread_history *hist = a; + struct cpu_event_history *hist = a; - XFREE(MTYPE_THREAD_STATS, hist); + XFREE(MTYPE_EVENT_STATS, hist); } -static void vty_out_cpu_thread_history(struct vty *vty, - struct cpu_thread_history *a) +static void vty_out_cpu_event_history(struct vty *vty, + struct cpu_event_history *a) { vty_out(vty, "%5zu %10zu.%03zu %9zu %8zu %9zu %8zu %9zu %9zu %9zu %10zu", @@ -128,21 +129,21 @@ static void vty_out_cpu_thread_history(struct vty *vty, (a->real.total / a->total_calls), a->real.max, a->total_cpu_warn, a->total_wall_warn, a->total_starv_warn); vty_out(vty, " %c%c%c%c%c %s\n", - a->types & (1 << THREAD_READ) ? 'R' : ' ', - a->types & (1 << THREAD_WRITE) ? 'W' : ' ', - a->types & (1 << THREAD_TIMER) ? 'T' : ' ', - a->types & (1 << THREAD_EVENT) ? 'E' : ' ', - a->types & (1 << THREAD_EXECUTE) ? 'X' : ' ', a->funcname); + a->types & (1 << EVENT_READ) ? 'R' : ' ', + a->types & (1 << EVENT_WRITE) ? 'W' : ' ', + a->types & (1 << EVENT_TIMER) ? 'T' : ' ', + a->types & (1 << EVENT_EVENT) ? 'E' : ' ', + a->types & (1 << EVENT_EXECUTE) ? 'X' : ' ', a->funcname); } static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[]) { - struct cpu_thread_history *totals = args[0]; - struct cpu_thread_history copy; + struct cpu_event_history *totals = args[0]; + struct cpu_event_history copy; struct vty *vty = args[1]; uint8_t *filter = args[2]; - struct cpu_thread_history *a = bucket->data; + struct cpu_event_history *a = bucket->data; copy.total_active = atomic_load_explicit(&a->total_active, memory_order_seq_cst); @@ -167,7 +168,7 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[]) if (!(copy.types & *filter)) return; - vty_out_cpu_thread_history(vty, ©); + vty_out_cpu_event_history(vty, ©); totals->total_active += copy.total_active; totals->total_calls += copy.total_calls; totals->total_cpu_warn += copy.total_cpu_warn; @@ -183,9 +184,9 @@ static void cpu_record_hash_print(struct hash_bucket *bucket, void *args[]) static void cpu_record_print(struct vty *vty, uint8_t filter) { - struct cpu_thread_history tmp; + struct cpu_event_history tmp; void *args[3] = {&tmp, vty, &filter}; - struct thread_master *m; + struct event_loop *m; struct listnode *ln; if (!cputime_enabled) @@ -203,8 +204,8 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { const char *name = m->name ? m->name : "main"; - char underline[strlen(name) + 1]; + memset(underline, '-', sizeof(underline)); underline[sizeof(underline) - 1] = '\0'; @@ -244,7 +245,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter) vty_out(vty, " Type Thread\n"); if (tmp.total_calls > 0) - vty_out_cpu_thread_history(vty, &tmp); + vty_out_cpu_event_history(vty, &tmp); } static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) @@ -252,7 +253,7 @@ static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) uint8_t *filter = args[0]; struct hash *cpu_record = args[1]; - struct cpu_thread_history *a = bucket->data; + struct cpu_event_history *a = bucket->data; if (!(a->types & *filter)) return; @@ -263,13 +264,14 @@ static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[]) static void cpu_record_clear(uint8_t filter) { uint8_t *tmp = &filter; - struct thread_master *m; + struct event_loop *m; struct listnode *ln; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { frr_with_mutex (&m->mtx) { void *args[2] = {tmp, m->cpu_record}; + hash_iterate( m->cpu_record, (void (*)(struct hash_bucket *, @@ -289,23 +291,23 @@ static uint8_t parse_filter(const char *filterstr) switch (filterstr[i]) { case 'r': case 'R': - filter |= (1 << THREAD_READ); + filter |= (1 << EVENT_READ); break; case 'w': case 'W': - filter |= (1 << THREAD_WRITE); + filter |= (1 << EVENT_WRITE); break; case 't': case 'T': - filter |= (1 << THREAD_TIMER); + filter |= (1 << EVENT_TIMER); break; case 'e': case 'E': - filter |= (1 << THREAD_EVENT); + filter |= (1 << EVENT_EVENT); break; case 'x': case 'X': - filter |= (1 << THREAD_EXECUTE); + filter |= (1 << EVENT_EXECUTE); break; default: break; @@ -395,11 +397,11 @@ ALIAS (service_walltime_warning, "Set up miscellaneous service\n" "Warn for tasks exceeding total wallclock threshold\n") -static void show_thread_poll_helper(struct vty *vty, struct thread_master *m) +static void show_thread_poll_helper(struct vty *vty, struct event_loop *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; - struct thread *thread; + struct event *thread; uint32_t i; memset(underline, '-', sizeof(underline)); @@ -444,12 +446,11 @@ DEFUN_NOSH (show_thread_poll, "Show poll FD's and information\n") { struct listnode *node; - struct thread_master *m; + struct event_loop *m; frr_with_mutex (&masters_mtx) { - for (ALL_LIST_ELEMENTS_RO(masters, node, m)) { + for (ALL_LIST_ELEMENTS_RO(masters, node, m)) show_thread_poll_helper(vty, m); - } } return CMD_SUCCESS; @@ -481,11 +482,11 @@ DEFUN (clear_thread_cpu, return CMD_SUCCESS; } -static void show_thread_timers_helper(struct vty *vty, struct thread_master *m) +static void show_thread_timers_helper(struct vty *vty, struct event_loop *m) { const char *name = m->name ? m->name : "main"; char underline[strlen(name) + 1]; - struct thread *thread; + struct event *thread; memset(underline, '-', sizeof(underline)); underline[sizeof(underline) - 1] = '\0'; @@ -493,7 +494,7 @@ static void show_thread_timers_helper(struct vty *vty, struct thread_master *m) vty_out(vty, "\nShowing timers for %s\n", name); vty_out(vty, "-------------------%s\n", underline); - frr_each (thread_timer_list, &m->timer, thread) { + frr_each (event_timer_list, &m->timer, thread) { vty_out(vty, " %-50s%pTH\n", thread->hist->funcname, thread); } } @@ -506,7 +507,7 @@ DEFPY_NOSH (show_thread_timers, "Show all timers and how long they have in the system\n") { struct listnode *node; - struct thread_master *m; + struct event_loop *m; frr_with_mutex (&masters_mtx) { for (ALL_LIST_ELEMENTS_RO(masters, node, m)) @@ -516,7 +517,7 @@ DEFPY_NOSH (show_thread_timers, return CMD_SUCCESS; } -void thread_cmd_init(void) +void event_cmd_init(void) { install_element(VIEW_NODE, &show_thread_cpu_cmd); install_element(VIEW_NODE, &show_thread_poll_cmd); @@ -544,14 +545,14 @@ static void initializer(void) pthread_key_create(&thread_current, NULL); } -struct thread_master *thread_master_create(const char *name) +struct event_loop *event_master_create(const char *name) { - struct thread_master *rv; + struct event_loop *rv; struct rlimit limit; pthread_once(&init_once, &initializer); - rv = XCALLOC(MTYPE_THREAD_MASTER, sizeof(struct thread_master)); + rv = XCALLOC(MTYPE_EVENT_MASTER, sizeof(struct event_loop)); /* Initialize master mutex */ pthread_mutex_init(&rv->mtx, NULL); @@ -559,7 +560,7 @@ struct thread_master *thread_master_create(const char *name) /* Set name */ name = name ? name : "default"; - rv->name = XSTRDUP(MTYPE_THREAD_MASTER, name); + rv->name = XSTRDUP(MTYPE_EVENT_MASTER, name); /* Initialize I/O task data structures */ @@ -570,13 +571,14 @@ struct thread_master *thread_master_create(const char *name) rv->fd_limit = (int)limit.rlim_cur; } - rv->read = XCALLOC(MTYPE_THREAD_POLL, - sizeof(struct thread *) * rv->fd_limit); + rv->read = XCALLOC(MTYPE_EVENT_POLL, + sizeof(struct event *) * rv->fd_limit); - rv->write = XCALLOC(MTYPE_THREAD_POLL, - sizeof(struct thread *) * rv->fd_limit); + rv->write = XCALLOC(MTYPE_EVENT_POLL, + sizeof(struct event *) * rv->fd_limit); char tmhashname[strlen(name) + 32]; + snprintf(tmhashname, sizeof(tmhashname), "%s - threadmaster event hash", name); rv->cpu_record = hash_create_size( @@ -584,12 +586,12 @@ struct thread_master *thread_master_create(const char *name) (bool (*)(const void *, const void *))cpu_record_hash_cmp, tmhashname); - thread_list_init(&rv->event); - thread_list_init(&rv->ready); - thread_list_init(&rv->unuse); - thread_timer_list_init(&rv->timer); + event_list_init(&rv->event); + event_list_init(&rv->ready); + event_list_init(&rv->unuse); + event_timer_list_init(&rv->timer); - /* Initialize thread_fetch() settings */ + /* Initialize event_fetch() settings */ rv->spin = true; rv->handle_signals = true; @@ -607,9 +609,9 @@ struct thread_master *thread_master_create(const char *name) /* Initialize data structures for poll() */ rv->handler.pfdsize = rv->fd_limit; rv->handler.pfdcount = 0; - rv->handler.pfds = XCALLOC(MTYPE_THREAD_MASTER, + rv->handler.pfds = XCALLOC(MTYPE_EVENT_MASTER, sizeof(struct pollfd) * rv->handler.pfdsize); - rv->handler.copy = XCALLOC(MTYPE_THREAD_MASTER, + rv->handler.copy = XCALLOC(MTYPE_EVENT_MASTER, sizeof(struct pollfd) * rv->handler.pfdsize); /* add to list of threadmasters */ @@ -623,32 +625,32 @@ struct thread_master *thread_master_create(const char *name) return rv; } -void thread_master_set_name(struct thread_master *master, const char *name) +void event_master_set_name(struct event_loop *master, const char *name) { frr_with_mutex (&master->mtx) { - XFREE(MTYPE_THREAD_MASTER, master->name); - master->name = XSTRDUP(MTYPE_THREAD_MASTER, name); + XFREE(MTYPE_EVENT_MASTER, master->name); + master->name = XSTRDUP(MTYPE_EVENT_MASTER, name); } } -#define THREAD_UNUSED_DEPTH 10 +#define EVENT_UNUSED_DEPTH 10 /* Move thread to unuse list. */ -static void thread_add_unuse(struct thread_master *m, struct thread *thread) +static void thread_add_unuse(struct event_loop *m, struct event *thread) { pthread_mutex_t mtxc = thread->mtx; assert(m != NULL && thread != NULL); thread->hist->total_active--; - memset(thread, 0, sizeof(struct thread)); - thread->type = THREAD_UNUSED; + memset(thread, 0, sizeof(struct event)); + thread->type = EVENT_UNUSED; /* Restore the thread mutex context. */ thread->mtx = mtxc; - if (thread_list_count(&m->unuse) < THREAD_UNUSED_DEPTH) { - thread_list_add_tail(&m->unuse, thread); + if (event_list_count(&m->unuse) < EVENT_UNUSED_DEPTH) { + event_list_add_tail(&m->unuse, thread); return; } @@ -656,19 +658,17 @@ static void thread_add_unuse(struct thread_master *m, struct thread *thread) } /* Free all unused thread. */ -static void thread_list_free(struct thread_master *m, - struct thread_list_head *list) +static void thread_list_free(struct event_loop *m, struct event_list_head *list) { - struct thread *t; + struct event *t; - while ((t = thread_list_pop(list))) + while ((t = event_list_pop(list))) thread_free(m, t); } -static void thread_array_free(struct thread_master *m, - struct thread **thread_array) +static void thread_array_free(struct event_loop *m, struct event **thread_array) { - struct thread *t; + struct event *t; int index; for (index = 0; index < m->fd_limit; ++index) { @@ -678,41 +678,41 @@ static void thread_array_free(struct thread_master *m, thread_free(m, t); } } - XFREE(MTYPE_THREAD_POLL, thread_array); + XFREE(MTYPE_EVENT_POLL, thread_array); } /* - * thread_master_free_unused + * event_master_free_unused * * As threads are finished with they are put on the * unuse list for later reuse. * If we are shutting down, Free up unused threads * So we can see if we forget to shut anything off */ -void thread_master_free_unused(struct thread_master *m) +void event_master_free_unused(struct event_loop *m) { frr_with_mutex (&m->mtx) { - struct thread *t; - while ((t = thread_list_pop(&m->unuse))) + struct event *t; + + while ((t = event_list_pop(&m->unuse))) thread_free(m, t); } } /* Stop thread scheduler. */ -void thread_master_free(struct thread_master *m) +void event_master_free(struct event_loop *m) { - struct thread *t; + struct event *t; frr_with_mutex (&masters_mtx) { listnode_delete(masters, m); - if (masters->count == 0) { + if (masters->count == 0) list_delete(&masters); - } } thread_array_free(m, m->read); thread_array_free(m, m->write); - while ((t = thread_timer_list_pop(&m->timer))) + while ((t = event_timer_list_pop(&m->timer))) thread_free(m, t); thread_list_free(m, &m->event); thread_list_free(m, &m->ready); @@ -724,22 +724,20 @@ void thread_master_free(struct thread_master *m) list_delete(&m->cancel_req); m->cancel_req = NULL; - hash_clean(m->cpu_record, cpu_record_hash_free); - hash_free(m->cpu_record); - m->cpu_record = NULL; + hash_clean_and_free(&m->cpu_record, cpu_record_hash_free); - XFREE(MTYPE_THREAD_MASTER, m->name); - XFREE(MTYPE_THREAD_MASTER, m->handler.pfds); - XFREE(MTYPE_THREAD_MASTER, m->handler.copy); - XFREE(MTYPE_THREAD_MASTER, m); + XFREE(MTYPE_EVENT_MASTER, m->name); + XFREE(MTYPE_EVENT_MASTER, m->handler.pfds); + XFREE(MTYPE_EVENT_MASTER, m->handler.copy); + XFREE(MTYPE_EVENT_MASTER, m); } /* Return remain time in milliseconds. */ -unsigned long thread_timer_remain_msec(struct thread *thread) +unsigned long event_timer_remain_msec(struct event *thread) { int64_t remain; - if (!thread_is_scheduled(thread)) + if (!event_is_scheduled(thread)) return 0; frr_with_mutex (&thread->mtx) { @@ -750,14 +748,15 @@ unsigned long thread_timer_remain_msec(struct thread *thread) } /* Return remain time in seconds. */ -unsigned long thread_timer_remain_second(struct thread *thread) +unsigned long event_timer_remain_second(struct event *thread) { - return thread_timer_remain_msec(thread) / 1000LL; + return event_timer_remain_msec(thread) / 1000LL; } -struct timeval thread_timer_remain(struct thread *thread) +struct timeval event_timer_remain(struct event *thread) { struct timeval remain; + frr_with_mutex (&thread->mtx) { monotime_until(&thread->u.sands, &remain); } @@ -782,28 +781,26 @@ static int time_hhmmss(char *buf, int buf_size, long sec) return wr != 8; } -char *thread_timer_to_hhmmss(char *buf, int buf_size, - struct thread *t_timer) +char *event_timer_to_hhmmss(char *buf, int buf_size, struct event *t_timer) { - if (t_timer) { - time_hhmmss(buf, buf_size, - thread_timer_remain_second(t_timer)); - } else { + if (t_timer) + time_hhmmss(buf, buf_size, event_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, - void (*func)(struct thread *), void *arg, - const struct xref_threadsched *xref) +static struct event *thread_get(struct event_loop *m, uint8_t type, + void (*func)(struct event *), void *arg, + const struct xref_eventsched *xref) { - struct thread *thread = thread_list_pop(&m->unuse); - struct cpu_thread_history tmp; + struct event *thread = event_list_pop(&m->unuse); + struct cpu_event_history tmp; if (!thread) { - thread = XCALLOC(MTYPE_THREAD, sizeof(struct thread)); + thread = XCALLOC(MTYPE_THREAD, sizeof(struct event)); /* mutex only needs to be initialized at struct creation. */ pthread_mutex_init(&thread->mtx, NULL); m->alloc++; @@ -813,7 +810,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, thread->add_type = type; thread->master = m; thread->arg = arg; - thread->yield = THREAD_YIELD_TIME_SLOT; /* default */ + thread->yield = EVENT_YIELD_TIME_SLOT; /* default */ thread->ref = NULL; thread->ignore_timer_late = false; @@ -842,7 +839,7 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type, return thread; } -static void thread_free(struct thread_master *master, struct thread *thread) +static void thread_free(struct event_loop *master, struct event *thread) { /* Update statistics. */ assert(master->alloc > 0); @@ -853,7 +850,7 @@ static void thread_free(struct thread_master *master, struct thread *thread) XFREE(MTYPE_THREAD, thread); } -static int fd_poll(struct thread_master *m, const struct timeval *timer_wait, +static int fd_poll(struct event_loop *m, const struct timeval *timer_wait, bool *eintr_p) { sigset_t origsigs; @@ -862,7 +859,7 @@ static int fd_poll(struct thread_master *m, const struct timeval *timer_wait, /* * If timer_wait is null here, that means poll() should block - * indefinitely, unless the thread_master has overridden it by setting + * indefinitely, unless the event_master has overridden it by setting * ->selectpoll_timeout. * * If the value is positive, it specifies the maximum number of @@ -875,15 +872,17 @@ static int fd_poll(struct thread_master *m, const struct timeval *timer_wait, /* number of file descriptors with events */ int num; - if (timer_wait != NULL - && m->selectpoll_timeout == 0) // use the default value + if (timer_wait != NULL && m->selectpoll_timeout == 0) { + /* use the default value */ timeout = (timer_wait->tv_sec * 1000) + (timer_wait->tv_usec / 1000); - else if (m->selectpoll_timeout > 0) // use the user's timeout + } else if (m->selectpoll_timeout > 0) { + /* use the user's timeout */ timeout = m->selectpoll_timeout; - else if (m->selectpoll_timeout - < 0) // effect a poll (return immediately) + } else if (m->selectpoll_timeout < 0) { + /* effect a poll (return immediately) */ timeout = 0; + } zlog_tls_buffer_flush(); rcu_read_unlock(); @@ -951,16 +950,15 @@ static int fd_poll(struct thread_master *m, const struct timeval *timer_wait, } /* Add new read thread. */ -void _thread_add_read_write(const struct xref_threadsched *xref, - struct thread_master *m, - void (*func)(struct thread *), void *arg, int fd, - struct thread **t_ptr) +void _event_add_read_write(const struct xref_eventsched *xref, + struct event_loop *m, void (*func)(struct event *), + void *arg, int fd, struct event **t_ptr) { - int dir = xref->thread_type; - struct thread *thread = NULL; - struct thread **thread_array; + int dir = xref->event_type; + struct event *thread = NULL; + struct event **thread_array; - if (dir == THREAD_READ) + if (dir == EVENT_READ) frrtrace(9, frr_libfrr, schedule_read, m, xref->funcname, xref->xref.file, xref->xref.line, t_ptr, fd, 0, arg, 0); @@ -974,20 +972,22 @@ void _thread_add_read_write(const struct xref_threadsched *xref, assert(!"Number of FD's open is greater than FRR currently configured to handle, aborting"); frr_with_mutex (&m->mtx) { + /* Thread is already scheduled; don't reschedule */ 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) + if (dir == EVENT_READ) thread_array = m->read; else thread_array = m->write; - /* if we already have a pollfd for our file descriptor, find and - * use it */ + /* + * 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; @@ -1010,7 +1010,7 @@ void _thread_add_read_write(const struct xref_threadsched *xref, m->handler.pfds[queuepos].fd = fd; m->handler.pfds[queuepos].events |= - (dir == THREAD_READ ? POLLIN : POLLOUT); + (dir == EVENT_READ ? POLLIN : POLLOUT); if (queuepos == m->handler.pfdcount) m->handler.pfdcount++; @@ -1031,13 +1031,13 @@ void _thread_add_read_write(const struct xref_threadsched *xref, } } -static void _thread_add_timer_timeval(const struct xref_threadsched *xref, - struct thread_master *m, - void (*func)(struct thread *), void *arg, - struct timeval *time_relative, - struct thread **t_ptr) +static void _event_add_timer_timeval(const struct xref_eventsched *xref, + struct event_loop *m, + void (*func)(struct event *), void *arg, + struct timeval *time_relative, + struct event **t_ptr) { - struct thread *thread; + struct event *thread; struct timeval t; assert(m != NULL); @@ -1057,11 +1057,11 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref, /* thread is already scheduled; don't reschedule */ return; - thread = thread_get(m, THREAD_TIMER, func, arg, xref); + thread = thread_get(m, EVENT_TIMER, func, arg, xref); frr_with_mutex (&thread->mtx) { thread->u.sands = t; - thread_timer_list_add(&m->timer, thread); + event_timer_list_add(&m->timer, thread); if (t_ptr) { *t_ptr = thread; thread->ref = t_ptr; @@ -1072,7 +1072,7 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref, * might change the time we'll wait for, give the pthread * a chance to re-compute. */ - if (thread_timer_list_first(&m->timer) == thread) + if (event_timer_list_first(&m->timer) == thread) AWAKEN(m); } #define ONEYEAR2SEC (60 * 60 * 24 * 365) @@ -1085,9 +1085,9 @@ static void _thread_add_timer_timeval(const struct xref_threadsched *xref, /* Add timer event thread. */ -void _thread_add_timer(const struct xref_threadsched *xref, - struct thread_master *m, void (*func)(struct thread *), - void *arg, long timer, struct thread **t_ptr) +void _event_add_timer(const struct xref_eventsched *xref, struct event_loop *m, + void (*func)(struct event *), void *arg, long timer, + struct event **t_ptr) { struct timeval trel; @@ -1096,14 +1096,13 @@ void _thread_add_timer(const struct xref_threadsched *xref, trel.tv_sec = timer; trel.tv_usec = 0; - _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); + _event_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); } /* Add timer event thread with "millisecond" resolution */ -void _thread_add_timer_msec(const struct xref_threadsched *xref, - struct thread_master *m, - void (*func)(struct thread *), void *arg, - long timer, struct thread **t_ptr) +void _event_add_timer_msec(const struct xref_eventsched *xref, + struct event_loop *m, void (*func)(struct event *), + void *arg, long timer, struct event **t_ptr) { struct timeval trel; @@ -1112,24 +1111,23 @@ void _thread_add_timer_msec(const struct xref_threadsched *xref, trel.tv_sec = timer / 1000; trel.tv_usec = 1000 * (timer % 1000); - _thread_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); + _event_add_timer_timeval(xref, m, func, arg, &trel, t_ptr); } /* Add timer event thread with "timeval" resolution */ -void _thread_add_timer_tv(const struct xref_threadsched *xref, - struct thread_master *m, - void (*func)(struct thread *), void *arg, - struct timeval *tv, struct thread **t_ptr) +void _event_add_timer_tv(const struct xref_eventsched *xref, + struct event_loop *m, void (*func)(struct event *), + void *arg, struct timeval *tv, struct event **t_ptr) { - _thread_add_timer_timeval(xref, m, func, arg, tv, t_ptr); + _event_add_timer_timeval(xref, m, func, arg, tv, t_ptr); } /* Add simple event thread. */ -void _thread_add_event(const struct xref_threadsched *xref, - struct thread_master *m, void (*func)(struct thread *), - void *arg, int val, struct thread **t_ptr) +void _event_add_event(const struct xref_eventsched *xref, struct event_loop *m, + void (*func)(struct event *), void *arg, int val, + struct event **t_ptr) { - struct thread *thread = NULL; + struct event *thread = NULL; frrtrace(9, frr_libfrr, schedule_event, m, xref->funcname, xref->xref.file, xref->xref.line, @@ -1142,10 +1140,10 @@ void _thread_add_event(const struct xref_threadsched *xref, /* thread is already scheduled; don't reschedule */ break; - thread = thread_get(m, THREAD_EVENT, func, arg, xref); + thread = thread_get(m, EVENT_EVENT, func, arg, xref); frr_with_mutex (&thread->mtx) { thread->u.val = val; - thread_list_add_tail(&m->event, thread); + event_list_add_tail(&m->event, thread); } if (t_ptr) { @@ -1163,7 +1161,7 @@ void _thread_add_event(const struct xref_threadsched *xref, * NOT's out the .events field of pollfd corresponding to the given file * descriptor. The event to be NOT'd is passed in the 'state' parameter. * - * This needs to happen for both copies of pollfd's. See 'thread_fetch' + * This needs to happen for both copies of pollfd's. See 'event_fetch' * implementation for details. * * @param master @@ -1173,8 +1171,8 @@ void _thread_add_event(const struct xref_threadsched *xref, * - POLLIN * - POLLOUT */ -static void thread_cancel_rw(struct thread_master *master, int fd, short state, - int idx_hint) +static void event_cancel_rw(struct event_loop *master, int fd, short state, + int idx_hint) { bool found = false; @@ -1218,8 +1216,10 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state, master->handler.pfds[master->handler.pfdcount].events = 0; } - /* If we have the same pollfd in the copy, perform the same operations, - * otherwise return. */ + /* + * If we have the same pollfd in the copy, perform the same operations, + * otherwise return. + */ if (i >= master->handler.copycount) return; @@ -1231,7 +1231,7 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state, * sizeof(struct pollfd)); master->handler.copycount--; master->handler.copy[master->handler.copycount].fd = 0; - master->handler.copy[master->handler.copycount].events = 0; + master->handler.copy[master->handler.copycount].events = 0; } } @@ -1239,10 +1239,10 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state, * Process task cancellation given a task argument: iterate through the * various lists of tasks, looking for any that match the argument. */ -static void cancel_arg_helper(struct thread_master *master, +static void cancel_arg_helper(struct event_loop *master, const struct cancel_req *cr) { - struct thread *t; + struct event *t; nfds_t i; int fd; struct pollfd *pfd; @@ -1252,26 +1252,26 @@ static void cancel_arg_helper(struct thread_master *master, return; /* First process the ready lists. */ - frr_each_safe(thread_list, &master->event, t) { + frr_each_safe (event_list, &master->event, t) { if (t->arg != cr->eventobj) continue; - thread_list_del(&master->event, t); + event_list_del(&master->event, t); if (t->ref) *t->ref = NULL; thread_add_unuse(master, t); } - frr_each_safe(thread_list, &master->ready, t) { + frr_each_safe (event_list, &master->ready, t) { if (t->arg != cr->eventobj) continue; - thread_list_del(&master->ready, t); + event_list_del(&master->ready, t); if (t->ref) *t->ref = NULL; thread_add_unuse(master, t); } /* If requested, stop here and ignore io and timers */ - if (CHECK_FLAG(cr->flags, THREAD_CANCEL_FLAG_READY)) + if (CHECK_FLAG(cr->flags, EVENT_CANCEL_FLAG_READY)) return; /* Check the io tasks */ @@ -1287,7 +1287,7 @@ static void cancel_arg_helper(struct thread_master *master, fd = pfd->fd; /* Found a match to cancel: clean up fd arrays */ - thread_cancel_rw(master, pfd->fd, pfd->events, i); + event_cancel_rw(master, pfd->fd, pfd->events, i); /* Clean up thread arrays */ master->read[fd] = NULL; @@ -1307,14 +1307,14 @@ static void cancel_arg_helper(struct thread_master *master, } /* Check the timer tasks */ - t = thread_timer_list_first(&master->timer); + t = event_timer_list_first(&master->timer); while (t) { - struct thread *t_next; + struct event *t_next; - t_next = thread_timer_list_next(&master->timer, t); + t_next = event_timer_list_next(&master->timer, t); if (t->arg == cr->eventobj) { - thread_timer_list_del(&master->timer, t); + event_timer_list_del(&master->timer, t); if (t->ref) *t->ref = NULL; thread_add_unuse(master, t); @@ -1327,16 +1327,16 @@ static void cancel_arg_helper(struct thread_master *master, /** * Process cancellation requests. * - * This may only be run from the pthread which owns the thread_master. + * This may only be run from the pthread which owns the event_master. * * @param master the thread master to process * @REQUIRE master->mtx */ -static void do_thread_cancel(struct thread_master *master) +static void do_event_cancel(struct event_loop *master) { - struct thread_list_head *list = NULL; - struct thread **thread_array = NULL; - struct thread *thread; + struct event_list_head *list = NULL; + struct event **thread_array = NULL; + struct event *thread; struct cancel_req *cr; struct listnode *ln; @@ -1367,33 +1367,33 @@ static void do_thread_cancel(struct thread_master *master) /* Determine the appropriate queue to cancel the thread from */ switch (thread->type) { - case THREAD_READ: - thread_cancel_rw(master, thread->u.fd, POLLIN, -1); + case EVENT_READ: + event_cancel_rw(master, thread->u.fd, POLLIN, -1); thread_array = master->read; break; - case THREAD_WRITE: - thread_cancel_rw(master, thread->u.fd, POLLOUT, -1); + case EVENT_WRITE: + event_cancel_rw(master, thread->u.fd, POLLOUT, -1); thread_array = master->write; break; - case THREAD_TIMER: - thread_timer_list_del(&master->timer, thread); + case EVENT_TIMER: + event_timer_list_del(&master->timer, thread); break; - case THREAD_EVENT: + case EVENT_EVENT: list = &master->event; break; - case THREAD_READY: + case EVENT_READY: list = &master->ready; break; - default: + case EVENT_UNUSED: + case EVENT_EXECUTE: continue; break; } - if (list) { - thread_list_del(list, thread); - } else if (thread_array) { + if (list) + event_list_del(list, thread); + else if (thread_array) thread_array[thread->u.fd] = NULL; - } if (thread->ref) *thread->ref = NULL; @@ -1405,7 +1405,7 @@ static void do_thread_cancel(struct thread_master *master) if (master->cancel_req) list_delete_all_node(master->cancel_req); - /* Wake up any threads which may be blocked in thread_cancel_async() */ + /* Wake up any threads which may be blocked in event_cancel_async() */ master->canceled = true; pthread_cond_broadcast(&master->cancel_cond); } @@ -1413,7 +1413,7 @@ static void do_thread_cancel(struct thread_master *master) /* * Helper function used for multiple flavors of arg-based cancellation. */ -static void cancel_event_helper(struct thread_master *m, void *arg, int flags) +static void cancel_event_helper(struct event_loop *m, void *arg, int flags) { struct cancel_req *cr; @@ -1430,7 +1430,7 @@ static void cancel_event_helper(struct thread_master *m, void *arg, int flags) frr_with_mutex (&m->mtx) { cr->eventobj = arg; listnode_add(m->cancel_req, cr); - do_thread_cancel(m); + do_event_cancel(m); } } @@ -1439,10 +1439,10 @@ static void cancel_event_helper(struct thread_master *m, void *arg, int flags) * * MT-Unsafe * - * @param m the thread_master to cancel from + * @param m the event_master to cancel from * @param arg the argument passed when creating the event */ -void thread_cancel_event(struct thread_master *master, void *arg) +void event_cancel_event(struct event_loop *master, void *arg) { cancel_event_helper(master, arg, 0); } @@ -1452,14 +1452,14 @@ void thread_cancel_event(struct thread_master *master, void *arg) * * MT-Unsafe * - * @param m the thread_master to cancel from + * @param m the event_master to cancel from * @param arg the argument passed when creating the event */ -void thread_cancel_event_ready(struct thread_master *m, void *arg) +void event_cancel_event_ready(struct event_loop *m, void *arg) { /* Only cancel ready/event tasks */ - cancel_event_helper(m, arg, THREAD_CANCEL_FLAG_READY); + cancel_event_helper(m, arg, EVENT_CANCEL_FLAG_READY); } /** @@ -1469,19 +1469,19 @@ void thread_cancel_event_ready(struct thread_master *m, void *arg) * * @param thread task to cancel */ -void thread_cancel(struct thread **thread) +void event_cancel(struct event **thread) { - struct thread_master *master; + struct event_loop *master; if (thread == NULL || *thread == NULL) return; master = (*thread)->master; - frrtrace(9, frr_libfrr, thread_cancel, master, - (*thread)->xref->funcname, (*thread)->xref->xref.file, - (*thread)->xref->xref.line, NULL, (*thread)->u.fd, - (*thread)->u.val, (*thread)->arg, (*thread)->u.sands.tv_sec); + frrtrace(9, frr_libfrr, event_cancel, master, (*thread)->xref->funcname, + (*thread)->xref->xref.file, (*thread)->xref->xref.line, NULL, + (*thread)->u.fd, (*thread)->u.val, (*thread)->arg, + (*thread)->u.sands.tv_sec); assert(master->owner == pthread_self()); @@ -1490,7 +1490,7 @@ void thread_cancel(struct thread **thread) XCALLOC(MTYPE_TMP, sizeof(struct cancel_req)); cr->thread = *thread; listnode_add(master->cancel_req, cr); - do_thread_cancel(master); + do_event_cancel(master); } *thread = NULL; @@ -1499,7 +1499,7 @@ void thread_cancel(struct thread **thread) /** * Asynchronous cancellation. * - * Called with either a struct thread ** or void * to an event argument, + * Called with either a struct event ** or void * to an event argument, * this function posts the correct cancellation request and blocks until it is * serviced. * @@ -1508,7 +1508,7 @@ void thread_cancel(struct thread **thread) * The last two parameters are mutually exclusive, i.e. if you pass one the * other must be NULL. * - * When the cancellation procedure executes on the target thread_master, the + * When the cancellation procedure executes on the target event_master, the * thread * provided is checked for nullity. If it is null, the thread is * assumed to no longer exist and the cancellation request is a no-op. Thus * users of this API must pass a back-reference when scheduling the original @@ -1520,19 +1520,19 @@ void thread_cancel(struct thread **thread) * @param thread pointer to thread to cancel * @param eventobj the event */ -void thread_cancel_async(struct thread_master *master, struct thread **thread, - void *eventobj) +void event_cancel_async(struct event_loop *master, struct event **thread, + void *eventobj) { assert(!(thread && eventobj) && (thread || eventobj)); if (thread && *thread) - frrtrace(9, frr_libfrr, thread_cancel_async, master, + frrtrace(9, frr_libfrr, event_cancel_async, master, (*thread)->xref->funcname, (*thread)->xref->xref.file, (*thread)->xref->xref.line, NULL, (*thread)->u.fd, (*thread)->u.val, (*thread)->arg, (*thread)->u.sands.tv_sec); else - frrtrace(9, frr_libfrr, thread_cancel_async, master, NULL, NULL, + frrtrace(9, frr_libfrr, event_cancel_async, master, NULL, NULL, 0, NULL, 0, 0, eventobj, 0); assert(master->owner != pthread_self()); @@ -1562,30 +1562,30 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread, } /* ------------------------------------------------------------------------- */ -static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers, +static struct timeval *thread_timer_wait(struct event_timer_list_head *timers, struct timeval *timer_val) { - if (!thread_timer_list_count(timers)) + if (!event_timer_list_count(timers)) return NULL; - struct thread *next_timer = thread_timer_list_first(timers); + struct event *next_timer = event_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, - struct thread *fetch) +static struct event *thread_run(struct event_loop *m, struct event *thread, + struct event *fetch) { *fetch = *thread; thread_add_unuse(m, thread); return fetch; } -static int thread_process_io_helper(struct thread_master *m, - struct thread *thread, short state, - short actual_state, int pos) +static int thread_process_io_helper(struct event_loop *m, struct event *thread, + short state, short actual_state, int pos) { - struct thread **thread_array; + struct event **thread_array; /* * poll() clears the .events field, but the pollfd array we @@ -1608,14 +1608,14 @@ static int thread_process_io_helper(struct thread_master *m, return 0; } - if (thread->type == THREAD_READ) + if (thread->type == EVENT_READ) thread_array = m->read; else thread_array = m->write; thread_array[thread->u.fd] = NULL; - thread_list_add_tail(&m->ready, thread); - thread->type = THREAD_READY; + event_list_add_tail(&m->ready, thread); + thread->type = EVENT_READY; return 1; } @@ -1629,7 +1629,7 @@ static int thread_process_io_helper(struct thread_master *m, * @param m the thread master * @param num the number of active file descriptors (return value of poll()) */ -static void thread_process_io(struct thread_master *m, unsigned int num) +static void thread_process_io(struct event_loop *m, unsigned int num) { unsigned int ready = 0; struct pollfd *pfds = m->handler.copy; @@ -1642,10 +1642,10 @@ static void thread_process_io(struct thread_master *m, unsigned int num) ready++; /* - * Unless someone has called thread_cancel from another + * Unless someone has called event_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 + * field in a given pollfd. Barring event_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 @@ -1663,9 +1663,10 @@ static void thread_process_io(struct thread_master *m, unsigned int num) thread_process_io_helper(m, m->write[pfds[i].fd], POLLOUT, pfds[i].revents, i); - /* if one of our file descriptors is garbage, remove the same - * from - * both pfds + update sizes and index */ + /* + * if one of our file descriptors is garbage, remove the same + * from both pfds + update sizes and index + */ if (pfds[i].revents & POLLNVAL) { memmove(m->handler.pfds + i, m->handler.pfds + i + 1, (m->handler.pfdcount - i - 1) @@ -1687,15 +1688,15 @@ 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 thread_master *m, +static unsigned int thread_process_timers(struct event_loop *m, struct timeval *timenow) { struct timeval prev = *timenow; bool displayed = false; - struct thread *thread; + struct event *thread; unsigned int ready = 0; - while ((thread = thread_timer_list_first(&m->timer))) { + while ((thread = event_timer_list_first(&m->timer))) { if (timercmp(timenow, &thread->u.sands, <)) break; prev = thread->u.sands; @@ -1719,9 +1720,9 @@ static unsigned int thread_process_timers(struct thread_master *m, } } - thread_timer_list_pop(&m->timer); - thread->type = THREAD_READY; - thread_list_add_tail(&m->ready, thread); + event_timer_list_pop(&m->timer); + thread->type = EVENT_READY; + event_list_add_tail(&m->ready, thread); ready++; } @@ -1729,14 +1730,14 @@ static unsigned int thread_process_timers(struct thread_master *m, } /* process a list en masse, e.g. for event thread lists */ -static unsigned int thread_process(struct thread_list_head *list) +static unsigned int thread_process(struct event_list_head *list) { - struct thread *thread; + struct event *thread; unsigned int ready = 0; - while ((thread = thread_list_pop(list))) { - thread->type = THREAD_READY; - thread_list_add_tail(&thread->master->ready, thread); + while ((thread = event_list_pop(list))) { + thread->type = EVENT_READY; + event_list_add_tail(&thread->master->ready, thread); ready++; } return ready; @@ -1744,9 +1745,9 @@ static unsigned int thread_process(struct thread_list_head *list) /* Fetch next ready thread. */ -struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) +struct event *event_fetch(struct event_loop *m, struct event *fetch) { - struct thread *thread = NULL; + struct event *thread = NULL; struct timeval now; struct timeval zerotime = {0, 0}; struct timeval tv; @@ -1762,13 +1763,13 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) pthread_mutex_lock(&m->mtx); /* Process any pending cancellation requests */ - do_thread_cancel(m); + do_event_cancel(m); /* * Attempt to flush ready queue before going into poll(). * This is performance-critical. Think twice before modifying. */ - if ((thread = thread_list_pop(&m->ready))) { + if ((thread = event_list_pop(&m->ready))) { fetch = thread_run(m, thread, fetch); if (fetch->ref) *fetch->ref = NULL; @@ -1804,11 +1805,11 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch) * 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)) + if (!event_list_count(&m->ready)) tw = thread_timer_wait(&m->timer, &tv); - if (thread_list_count(&m->ready) || - (tw && !timercmp(tw, &zerotime, >))) + if (event_list_count(&m->ready) || + (tw && !timercmp(tw, &zerotime, >))) tw = &zerotime; if (!tw && m->handler.pfdcount == 0) { /* die */ @@ -1869,8 +1870,8 @@ static unsigned long timeval_elapsed(struct timeval a, struct timeval b) + (a.tv_usec - b.tv_usec)); } -unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start, - unsigned long *cputime) +unsigned long event_consumed_time(RUSAGE_T *now, RUSAGE_T *start, + unsigned long *cputime) { #ifdef HAVE_CLOCK_THREAD_CPUTIME_ID @@ -1904,19 +1905,22 @@ unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start, return timeval_elapsed(now->real, start->real); } -/* We should aim to yield after yield milliseconds, which defaults - to THREAD_YIELD_TIME_SLOT . - Note: we are using real (wall clock) time for this calculation. - It could be argued that CPU time may make more sense in certain - contexts. The things to consider are whether the thread may have - blocked (in which case wall time increases, but CPU time does not), - or whether the system is heavily loaded with other processes competing - for CPU time. On balance, wall clock time seems to make sense. - Plus it has the added benefit that gettimeofday should be faster - than calling getrusage. */ -int thread_should_yield(struct thread *thread) +/* + * We should aim to yield after yield milliseconds, which defaults + * to EVENT_YIELD_TIME_SLOT . + * Note: we are using real (wall clock) time for this calculation. + * It could be argued that CPU time may make more sense in certain + * contexts. The things to consider are whether the thread may have + * blocked (in which case wall time increases, but CPU time does not), + * or whether the system is heavily loaded with other processes competing + * for CPU time. On balance, wall clock time seems to make sense. + * Plus it has the added benefit that gettimeofday should be faster + * than calling getrusage. + */ +int event_should_yield(struct event *thread) { int result; + frr_with_mutex (&thread->mtx) { result = monotime_since(&thread->real, NULL) > (int64_t)thread->yield; @@ -1924,14 +1928,14 @@ int thread_should_yield(struct thread *thread) return result; } -void thread_set_yield_time(struct thread *thread, unsigned long yield_time) +void event_set_yield_time(struct event *thread, unsigned long yield_time) { frr_with_mutex (&thread->mtx) { thread->yield = yield_time; } } -void thread_getrusage(RUSAGE_T *r) +void event_getrusage(RUSAGE_T *r) { monotime(&r->real); if (!cputime_enabled) { @@ -1965,7 +1969,7 @@ void thread_getrusage(RUSAGE_T *r) * particular, the maximum real and cpu times must be monotonically increasing * or this code is not correct. */ -void thread_call(struct thread *thread) +void event_call(struct event *thread) { RUSAGE_T before, after; @@ -1982,10 +1986,10 @@ void thread_call(struct thread *thread) thread->real = before.real; - frrtrace(9, frr_libfrr, thread_call, thread->master, + frrtrace(9, frr_libfrr, event_call, thread->master, thread->xref->funcname, thread->xref->xref.file, - thread->xref->xref.line, NULL, thread->u.fd, - thread->u.val, thread->arg, thread->u.sands.tv_sec); + thread->xref->xref.line, NULL, thread->u.fd, thread->u.val, + thread->arg, thread->u.sands.tv_sec); pthread_setspecific(thread_current, thread); (*thread->func)(thread); @@ -1997,7 +2001,7 @@ void thread_call(struct thread *thread) unsigned long walltime, cputime; unsigned long exp; - walltime = thread_consumed_time(&after, &before, &cputime); + walltime = event_consumed_time(&after, &before, &cputime); /* update walltime */ atomic_fetch_add_explicit(&thread->hist->real.total, walltime, @@ -2061,26 +2065,25 @@ void thread_call(struct thread *thread) } /* Execute thread */ -void _thread_execute(const struct xref_threadsched *xref, - struct thread_master *m, void (*func)(struct thread *), - void *arg, int val) +void _event_execute(const struct xref_eventsched *xref, struct event_loop *m, + void (*func)(struct event *), void *arg, int val) { - struct thread *thread; + struct event *thread; /* Get or allocate new thread to execute. */ frr_with_mutex (&m->mtx) { - thread = thread_get(m, THREAD_EVENT, func, arg, xref); + thread = thread_get(m, EVENT_EVENT, func, arg, xref); /* Set its event value. */ frr_with_mutex (&thread->mtx) { - thread->add_type = THREAD_EXECUTE; + thread->add_type = EVENT_EXECUTE; thread->u.val = val; thread->ref = &thread; } } /* Execute thread doing all accounting. */ - thread_call(thread); + event_call(thread); /* Give back or free thread. */ thread_add_unuse(m, thread); @@ -2133,16 +2136,13 @@ void debug_signals(const sigset_t *sigs) } static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea, - const struct thread *thread) -{ - static const char * const types[] = { - [THREAD_READ] = "read", - [THREAD_WRITE] = "write", - [THREAD_TIMER] = "timer", - [THREAD_EVENT] = "event", - [THREAD_READY] = "ready", - [THREAD_UNUSED] = "unused", - [THREAD_EXECUTE] = "exec", + const struct event *thread) +{ + static const char *const types[] = { + [EVENT_READ] = "read", [EVENT_WRITE] = "write", + [EVENT_TIMER] = "timer", [EVENT_EVENT] = "event", + [EVENT_READY] = "ready", [EVENT_UNUSED] = "unused", + [EVENT_EXECUTE] = "exec", }; ssize_t rv = 0; char info[16] = ""; @@ -2158,14 +2158,19 @@ static ssize_t printfrr_thread_dbg(struct fbuf *buf, struct printfrr_eargs *ea, rv += bprintfrr(buf, " INVALID(%u)", thread->type); switch (thread->type) { - case THREAD_READ: - case THREAD_WRITE: + case EVENT_READ: + case EVENT_WRITE: snprintfrr(info, sizeof(info), "fd=%d", thread->u.fd); break; - case THREAD_TIMER: + case EVENT_TIMER: snprintfrr(info, sizeof(info), "r=%pTVMud", &thread->u.sands); break; + case EVENT_READY: + case EVENT_EVENT: + case EVENT_UNUSED: + case EVENT_EXECUTE: + break; } rv += bprintfrr(buf, " %-12s %s() %s from %s:%d}", info, @@ -2178,7 +2183,7 @@ printfrr_ext_autoreg_p("TH", printfrr_thread); static ssize_t printfrr_thread(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { - const struct thread *thread = ptr; + const struct event *thread = ptr; struct timespec remain = {}; if (ea->fmt[0] == 'D') { diff --git a/lib/ferr.c b/lib/ferr.c index 5998befec2e2..33bcb075fa5f 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -180,9 +180,7 @@ void log_ref_init(void) void log_ref_fini(void) { frr_with_mutex (&refs_mtx) { - hash_clean(refs, NULL); - hash_free(refs); - refs = NULL; + hash_clean_and_free(&refs, NULL); } } diff --git a/lib/filter_cli.c b/lib/filter_cli.c index 7ef0d47f67bc..5c3dc5e49d55 100644 --- a/lib/filter_cli.c +++ b/lib/filter_cli.c @@ -1272,9 +1272,6 @@ DEFPY_YANG( pda.any = true; } - 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). @@ -1326,6 +1323,7 @@ DEFPY_YANG( vty, "./ipv4-prefix-length-lesser-or-equal", NB_OP_DESTROY, NULL); } + nb_cli_enqueue_change(vty, "./any", NB_OP_DESTROY, NULL); } else { nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); } @@ -1476,9 +1474,6 @@ DEFPY_YANG( pda.any = true; } - 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). @@ -1530,6 +1525,7 @@ DEFPY_YANG( vty, "./ipv6-prefix-length-lesser-or-equal", NB_OP_DESTROY, NULL); } + nb_cli_enqueue_change(vty, "./any", NB_OP_DESTROY, NULL); } else { nb_cli_enqueue_change(vty, "./any", NB_OP_CREATE, NULL); } diff --git a/lib/filter_nb.c b/lib/filter_nb.c index a14f232339db..9511b8f5b5cd 100644 --- a/lib/filter_nb.c +++ b/lib/filter_nb.c @@ -456,24 +456,6 @@ bool plist_is_dup(const struct lyd_node *dnode, struct plist_dup_args *pda) 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 = {}; - - /* 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; - - plist_dnode_to_prefix(entry_dnode, &pda.any, &pda.prefix, &pda.ge, - &pda.le); - - return plist_is_dup(entry_dnode, &pda); -} - /* * XPath: /frr-filter:lib/access-list */ @@ -1331,13 +1313,6 @@ lib_prefix_list_entry_ipv4_prefix_modify(struct nb_cb_modify_args *args) const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - 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 prefix_list_nb_validate_v4_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1366,13 +1341,6 @@ lib_prefix_list_entry_ipv6_prefix_modify(struct nb_cb_modify_args *args) const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - 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 prefix_list_nb_validate_v6_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1404,13 +1372,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_greater_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - 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 prefix_list_nb_validate_v4_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1448,13 +1409,6 @@ static int lib_prefix_list_entry_ipv4_prefix_length_lesser_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - 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 prefix_list_nb_validate_v4_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1492,13 +1446,6 @@ static int lib_prefix_list_entry_ipv6_prefix_length_greater_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - 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 prefix_list_nb_validate_v6_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1536,13 +1483,6 @@ static int lib_prefix_list_entry_ipv6_prefix_length_lesser_or_equal_modify( const struct lyd_node *plist_dnode = yang_dnode_get_parent(args->dnode, "prefix-list"); - 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 prefix_list_nb_validate_v6_af_type( plist_dnode, args->errmsg, args->errmsg_len); } @@ -1574,16 +1514,11 @@ 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; - } - + /* + * If we have gotten to this point, it's legal + */ + if (args->event == NB_EV_VALIDATE) return NB_OK; - } if (args->event != NB_EV_APPLY) return NB_OK; @@ -1630,7 +1565,7 @@ static int lib_prefix_list_entry_any_destroy(struct nb_cb_destroy_args *args) /* Start prefix entry update procedure. */ prefix_list_entry_update_start(ple); - prefix_list_entry_set_empty(ple); + ple->any = false; /* Finish prefix entry update procedure. */ prefix_list_entry_update_finish(ple); diff --git a/lib/flex_algo.c b/lib/flex_algo.c new file mode 100644 index 000000000000..f48117ff1b15 --- /dev/null +++ b/lib/flex_algo.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * flex_algo.c: Flexible Algorithm library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#include "zebra.h" + +#include "flex_algo.h" + +DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO_DATABASE, "Flex-Algo database"); +DEFINE_MTYPE_STATIC(LIB, FLEX_ALGO, "Flex-Algo algorithm information"); + +static void _flex_algo_delete(struct flex_algos *flex_algos, + struct flex_algo *fa); + +struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator, + flex_algo_releaser_t releaser) +{ + struct flex_algos *flex_algos; + + flex_algos = + XCALLOC(MTYPE_FLEX_ALGO_DATABASE, sizeof(struct flex_algos)); + flex_algos->flex_algos = list_new(); + flex_algos->allocator = allocator; + flex_algos->releaser = releaser; + return flex_algos; +} + +void flex_algos_free(struct flex_algos *flex_algos) +{ + struct listnode *node, *nnode; + struct flex_algo *fa; + + for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) + _flex_algo_delete(flex_algos, fa); + list_delete(&flex_algos->flex_algos); + XFREE(MTYPE_FLEX_ALGO_DATABASE, flex_algos); +} + +struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos, + uint8_t algorithm, void *arg) +{ + struct flex_algo *fa; + + fa = XCALLOC(MTYPE_FLEX_ALGO, sizeof(struct flex_algo)); + fa->algorithm = algorithm; + if (flex_algos->allocator) + fa->data = flex_algos->allocator(arg); + admin_group_init(&fa->admin_group_exclude_any); + admin_group_init(&fa->admin_group_include_any); + admin_group_init(&fa->admin_group_include_all); + listnode_add(flex_algos->flex_algos, fa); + return fa; +} + +static void _flex_algo_delete(struct flex_algos *flex_algos, + struct flex_algo *fa) +{ + if (flex_algos->releaser) + flex_algos->releaser(fa->data); + admin_group_term(&fa->admin_group_exclude_any); + admin_group_term(&fa->admin_group_include_any); + admin_group_term(&fa->admin_group_include_all); + listnode_delete(flex_algos->flex_algos, fa); + XFREE(MTYPE_FLEX_ALGO, fa); +} + + +void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm) +{ + struct listnode *node, *nnode; + struct flex_algo *fa; + + for (ALL_LIST_ELEMENTS(flex_algos->flex_algos, node, nnode, fa)) { + if (fa->algorithm != algorithm) + continue; + _flex_algo_delete(flex_algos, fa); + } +} + +/** + * @brief Look up the local flex-algo object by its algorithm number. + * @param algorithm flex-algo algorithm number + * @param area area pointer of flex-algo + * @return local flex-algo object if exist, else NULL + */ +struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos, + uint8_t algorithm) +{ + struct listnode *node; + struct flex_algo *fa; + + for (ALL_LIST_ELEMENTS_RO(flex_algos->flex_algos, node, fa)) + if (fa->algorithm == algorithm) + return fa; + return NULL; +} + +/** + * @brief Compare two Flex-Algo Definitions (FAD) + * @param Flex algo 1 + * @param Flex algo 2 + * @return true if the definition is equal, else false + */ +bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2) +{ + if (fa1->algorithm != fa2->algorithm) + return false; + if (fa1->calc_type != fa2->calc_type) + return false; + if (fa1->metric_type != fa2->metric_type) + return false; + if (fa1->exclude_srlg != fa2->exclude_srlg) + return false; + if (fa1->flags != fa2->flags) + return false; + if (fa1->unsupported_subtlv != fa2->unsupported_subtlv) + return false; + + if (!admin_group_cmp(&fa1->admin_group_exclude_any, + &fa2->admin_group_exclude_any)) + return false; + if (!admin_group_cmp(&fa1->admin_group_include_all, + &fa2->admin_group_include_all)) + return false; + if (!admin_group_cmp(&fa1->admin_group_include_any, + &fa2->admin_group_include_any)) + return false; + + return true; +} + +/** + * Check SR Algorithm is Flex-Algo + * according to RFC9350 section 4 + * + * @param algorithm SR Algorithm + */ +bool flex_algo_id_valid(uint16_t algorithm) +{ + return algorithm >= SR_ALGORITHM_FLEX_MIN && + algorithm <= SR_ALGORITHM_FLEX_MAX; +} + +char *flex_algo_metric_type_print(char *type_str, size_t sz, + enum flex_algo_metric_type metric_type) +{ + switch (metric_type) { + case MT_IGP: + snprintf(type_str, sz, "igp"); + break; + case MT_MIN_UNI_LINK_DELAY: + snprintf(type_str, sz, "delay"); + break; + case MT_TE_DEFAULT: + snprintf(type_str, sz, "te"); + break; + } + return type_str; +} + +bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm) +{ + struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm); + + if (!fa) + return false; + + return fa->state; +} + +void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm, + bool state) +{ + struct flex_algo *fa = flex_algo_lookup(flex_algos, algorithm); + + if (!fa) + return; + + fa->state = state; +} diff --git a/lib/flex_algo.h b/lib/flex_algo.h new file mode 100644 index 000000000000..e617e7cae849 --- /dev/null +++ b/lib/flex_algo.h @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * flex_algo.h: Flexible Algorithm library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#ifndef _FRR_FLEX_ALGO_H +#define _FRR_FLEX_ALGO_H + +#include "admin_group.h" +#include "linklist.h" +#include "prefix.h" +#include "segment_routing.h" + +#define FLEX_ALGO_PRIO_DEFAULT 128 + +#define CALC_TYPE_SPF 0 + +/* flex-algo definition flags */ + +/* M-flag (aka. prefix-metric) + * Flex-Algorithm specific prefix and ASBR metric MUST be used + */ +#define FAD_FLAG_M 0x80 + +/* + * Metric Type values from RFC9350 section 5.1 + */ +enum flex_algo_metric_type { + MT_IGP = 0, + MT_MIN_UNI_LINK_DELAY = 1, + MT_TE_DEFAULT = 2, +}; + + +/* Flex-Algo data about a given algorithm. + * It includes the definition and some local data. + */ +struct flex_algo { + /* Flex-Algo definition */ + uint8_t algorithm; + enum flex_algo_metric_type metric_type; + uint8_t calc_type; + uint8_t priority; + uint8_t flags; + + /* extended admin-groups */ + struct admin_group admin_group_exclude_any; + struct admin_group admin_group_include_any; + struct admin_group admin_group_include_all; + + /* Exclude SRLG Sub-TLV is not yet supported by IS-IS + * True if a Exclude SRLG Sub-TLV has been found + */ + bool exclude_srlg; + + /* True if an unsupported sub-TLV other Exclude SRLG + * has been received. + * A router that receives an unsupported definition + * that is elected must not participate in the algorithm. + * This boolean prevents future sub-TLV from being considered + * as supported. + */ + bool unsupported_subtlv; + + /* Flex-Algo local data */ + + /* True if the local definition must be advertised */ + bool advertise_definition; + + /* which dataplane must be used for the algorithm */ +#define FLEX_ALGO_SR_MPLS 0x01 +#define FLEX_ALGO_SRV6 0x02 +#define FLEX_ALGO_IP 0x04 + uint8_t dataplanes; + + /* True if the Algorithm is locally enabled (ie. a definition has been + * found and is supported). + */ + bool state; + + /* + * This property can be freely extended among different routing + * protocols. Since Flex-Algo is an IGP protocol agnostic, both IS-IS + * and OSPF can implement Flex-Algo. The struct flex_algo thus provides + * the general data structure of Flex-Algo, and the value of extending + * it with the IGP protocol is provided by this property. + */ + void *data; +}; + +typedef void *(*flex_algo_allocator_t)(void *); +typedef void (*flex_algo_releaser_t)(void *); + +struct flex_algos { + flex_algo_allocator_t allocator; + flex_algo_releaser_t releaser; + struct list *flex_algos; +}; + +/* + * Flex-Algo Utilities + */ +struct flex_algos *flex_algos_alloc(flex_algo_allocator_t allocator, + flex_algo_releaser_t releaser); +void flex_algos_free(struct flex_algos *flex_algos); +struct flex_algo *flex_algo_alloc(struct flex_algos *flex_algos, + uint8_t algorithm, void *arg); +struct flex_algo *flex_algo_lookup(struct flex_algos *flex_algos, + uint8_t algorithm); +void flex_algos_free(struct flex_algos *flex_algos); +bool flex_algo_definition_cmp(struct flex_algo *fa1, struct flex_algo *fa2); +void flex_algo_delete(struct flex_algos *flex_algos, uint8_t algorithm); +bool flex_algo_id_valid(uint16_t algorithm); +char *flex_algo_metric_type_print(char *type_str, size_t sz, + enum flex_algo_metric_type metric_type); + +bool flex_algo_get_state(struct flex_algos *flex_algos, uint8_t algorithm); + +void flex_algo_set_state(struct flex_algos *flex_algos, uint8_t algorithm, + bool state); +#endif /* _FRR_FLEX_ALGO_H */ diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index 0c617238fa1f..c4ead01bf6ea 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -75,7 +75,7 @@ struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, /* initialize mutex */ pthread_mutex_init(&fpt->mtx, NULL); /* create new thread master */ - fpt->master = thread_master_create(name); + fpt->master = event_master_create(name); /* set attributes */ fpt->attr = *attr; name = (name ? name : "Anonymous thread"); @@ -101,7 +101,7 @@ struct frr_pthread *frr_pthread_new(const struct frr_pthread_attr *attr, static void frr_pthread_destroy_nolock(struct frr_pthread *fpt) { - thread_master_free(fpt->master); + event_master_free(fpt->master); pthread_mutex_destroy(&fpt->mtx); pthread_mutex_destroy(fpt->running_cond_mtx); pthread_cond_destroy(fpt->running_cond); @@ -224,14 +224,14 @@ void frr_pthread_stop_all(void) */ /* dummy task for sleeper pipe */ -static void fpt_dummy(struct thread *thread) +static void fpt_dummy(struct event *thread) { } /* poison pill task to end event loop */ -static void fpt_finish(struct thread *thread) +static void fpt_finish(struct event *thread) { - struct frr_pthread *fpt = THREAD_ARG(thread); + struct frr_pthread *fpt = EVENT_ARG(thread); atomic_store_explicit(&fpt->running, false, memory_order_relaxed); } @@ -239,7 +239,7 @@ static void fpt_finish(struct thread *thread) /* stop function, called from other threads to halt this one */ static int fpt_halt(struct frr_pthread *fpt, void **res) { - thread_add_event(fpt->master, &fpt_finish, fpt, 0, NULL); + event_add_event(fpt->master, &fpt_finish, fpt, 0, NULL); pthread_join(fpt->thread, res); return 0; @@ -281,7 +281,7 @@ static void *fpt_run(void *arg) int sleeper[2]; pipe(sleeper); - thread_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL); + event_add_read(fpt->master, &fpt_dummy, NULL, sleeper[0], NULL); fpt->master->handle_signals = false; @@ -289,11 +289,11 @@ static void *fpt_run(void *arg) frr_pthread_notify_running(fpt); - struct thread task; + struct event task; while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) { pthread_testcancel(); - if (thread_fetch(fpt->master, &task)) { - thread_call(&task); + if (event_fetch(fpt->master, &task)) { + event_call(&task); } } diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index b1d08717fbed..f91044dfaef2 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -11,7 +11,7 @@ #include "frratomic.h" #include "memory.h" #include "frrcu.h" -#include "thread.h" +#include "frrevent.h" #ifdef __cplusplus extern "C" { @@ -41,7 +41,7 @@ struct frr_pthread { struct rcu_thread *rcu_thread; /* thread master for this pthread's thread.c event loop */ - struct thread_master *master; + struct event_loop *master; /* caller-specified data; start & stop funcs, name, id */ struct frr_pthread_attr attr; diff --git a/lib/frr_zmq.c b/lib/frr_zmq.c index 2673d576052e..b28dd7f1bbc5 100644 --- a/lib/frr_zmq.c +++ b/lib/frr_zmq.c @@ -15,7 +15,7 @@ #include <zebra.h> #include <zmq.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "frr_zmq.h" #include "log.h" @@ -43,9 +43,9 @@ void frrzmq_finish(void) } } -static void frrzmq_read_msg(struct thread *t) +static void frrzmq_read_msg(struct event *t) { - struct frrzmq_cb **cbp = THREAD_ARG(t); + struct frrzmq_cb **cbp = EVENT_ARG(t); struct frrzmq_cb *cb; zmq_msg_t msg; unsigned partno; @@ -138,8 +138,8 @@ static void frrzmq_read_msg(struct thread *t) if (read) frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT); - thread_add_read(t->master, frrzmq_read_msg, cbp, - cb->fd, &cb->read.thread); + event_add_read(t->master, frrzmq_read_msg, cbp, cb->fd, + &cb->read.thread); return; out_err: @@ -149,14 +149,13 @@ static void frrzmq_read_msg(struct thread *t) cb->read.cb_error(cb->read.arg, cb->zmqsock); } -int _frrzmq_thread_add_read(const struct xref_threadsched *xref, - struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*partfunc)(void *arg, void *zmqsock, - zmq_msg_t *msg, unsigned partnum), - void (*errfunc)(void *arg, void *zmqsock), - void *arg, void *zmqsock, - struct frrzmq_cb **cbp) +int _frrzmq_event_add_read(const struct xref_eventsched *xref, + struct event_loop *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, + zmq_msg_t *msg, unsigned partnum), + void (*errfunc)(void *arg, void *zmqsock), void *arg, + void *zmqsock, struct frrzmq_cb **cbp) { int fd, events; size_t len; @@ -191,19 +190,19 @@ int _frrzmq_thread_add_read(const struct xref_threadsched *xref, cb->in_cb = false; if (events & ZMQ_POLLIN) { - thread_cancel(&cb->read.thread); + event_cancel(&cb->read.thread); - thread_add_event(master, frrzmq_read_msg, cbp, fd, - &cb->read.thread); - } else - thread_add_read(master, frrzmq_read_msg, cbp, fd, + event_add_event(master, frrzmq_read_msg, cbp, fd, &cb->read.thread); + } else + event_add_read(master, frrzmq_read_msg, cbp, fd, + &cb->read.thread); return 0; } -static void frrzmq_write_msg(struct thread *t) +static void frrzmq_write_msg(struct event *t) { - struct frrzmq_cb **cbp = THREAD_ARG(t); + struct frrzmq_cb **cbp = EVENT_ARG(t); struct frrzmq_cb *cb; unsigned char written = 0; int ret; @@ -247,8 +246,8 @@ static void frrzmq_write_msg(struct thread *t) if (written) frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN); - thread_add_write(t->master, frrzmq_write_msg, cbp, - cb->fd, &cb->write.thread); + event_add_write(t->master, frrzmq_write_msg, cbp, cb->fd, + &cb->write.thread); return; out_err: @@ -258,11 +257,11 @@ static void frrzmq_write_msg(struct thread *t) cb->write.cb_error(cb->write.arg, cb->zmqsock); } -int _frrzmq_thread_add_write(const struct xref_threadsched *xref, - struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*errfunc)(void *arg, void *zmqsock), - void *arg, void *zmqsock, struct frrzmq_cb **cbp) +int _frrzmq_event_add_write(const struct xref_eventsched *xref, + struct event_loop *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*errfunc)(void *arg, void *zmqsock), + void *arg, void *zmqsock, struct frrzmq_cb **cbp) { int fd, events; size_t len; @@ -297,13 +296,13 @@ int _frrzmq_thread_add_write(const struct xref_threadsched *xref, cb->in_cb = false; if (events & ZMQ_POLLOUT) { - thread_cancel(&cb->write.thread); + event_cancel(&cb->write.thread); - _thread_add_event(xref, master, frrzmq_write_msg, cbp, fd, - &cb->write.thread); - } else - thread_add_write(master, frrzmq_write_msg, cbp, fd, + _event_add_event(xref, master, frrzmq_write_msg, cbp, fd, &cb->write.thread); + } else + event_add_write(master, frrzmq_write_msg, cbp, fd, + &cb->write.thread); return 0; } @@ -312,7 +311,7 @@ void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core) if (!cb || !*cb) return; core->cancelled = true; - thread_cancel(&core->thread); + event_cancel(&core->thread); /* If cancelled from within a callback, don't try to free memory * in this path. @@ -343,15 +342,15 @@ void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core, if (zmq_getsockopt(cb->zmqsock, ZMQ_EVENTS, &events, &len)) return; if ((events & event) && core->thread && !core->cancelled) { - struct thread_master *tm = core->thread->master; + struct event_loop *tm = core->thread->master; - thread_cancel(&core->thread); + event_cancel(&core->thread); if (event == ZMQ_POLLIN) - thread_add_event(tm, frrzmq_read_msg, - cbp, cb->fd, &core->thread); + event_add_event(tm, frrzmq_read_msg, cbp, cb->fd, + &core->thread); else - thread_add_event(tm, frrzmq_write_msg, - cbp, cb->fd, &core->thread); + event_add_event(tm, frrzmq_write_msg, cbp, cb->fd, + &core->thread); } } diff --git a/lib/frr_zmq.h b/lib/frr_zmq.h index f12291d60210..73da3770f4b9 100644 --- a/lib/frr_zmq.h +++ b/lib/frr_zmq.h @@ -7,7 +7,7 @@ #ifndef _FRRZMQ_H #define _FRRZMQ_H -#include "thread.h" +#include "frrevent.h" #include <zmq.h> #ifdef __cplusplus @@ -26,7 +26,7 @@ extern "C" { /* callback integration */ struct cb_core { - struct thread *thread; + struct event *thread; void *arg; bool cancelled; @@ -59,30 +59,29 @@ extern void frrzmq_finish(void); #define _xref_zmq_a(type, f, d, call) \ ({ \ - static const struct xref_threadsched _xref \ - __attribute__((used)) = { \ - .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \ + static const struct xref_eventsched _xref __attribute__( \ + (used)) = { \ + .xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__), \ .funcname = #f, \ .dest = #d, \ - .thread_type = THREAD_ ## type, \ + .event_type = EVENT_##type, \ }; \ XREF_LINK(_xref.xref); \ call; \ - }) \ - /* end */ + }) /* end */ /* core event registration, one of these 2 macros should be used */ -#define frrzmq_thread_add_read_msg(m, f, e, a, z, d) \ +#define frrzmq_event_add_read_msg(m, f, e, a, z, d) \ _xref_zmq_a(READ, f, d, \ - _frrzmq_thread_add_read(&_xref, m, f, NULL, e, a, z, d)) + _frrzmq_event_add_read(&_xref, m, f, NULL, e, a, z, d)) -#define frrzmq_thread_add_read_part(m, f, e, a, z, d) \ +#define frrzmq_event_add_read_part(m, f, e, a, z, d) \ _xref_zmq_a(READ, f, d, \ - _frrzmq_thread_add_read(&_xref, m, NULL, f, e, a, z, d)) + _frrzmq_event_add_read(&_xref, m, NULL, f, e, a, z, d)) -#define frrzmq_thread_add_write_msg(m, f, e, a, z, d) \ +#define frrzmq_event_add_write_msg(m, f, e, a, z, d) \ _xref_zmq_a(WRITE, f, d, \ - _frrzmq_thread_add_write(&_xref, m, f, e, a, z, d)) + _frrzmq_event_add_write(&_xref, m, f, e, a, z, d)) struct cb_core; struct frrzmq_cb; @@ -108,18 +107,20 @@ struct frrzmq_cb; * may schedule the event to run as soon as libfrr is back in its main * loop. */ -extern int _frrzmq_thread_add_read( - const struct xref_threadsched *xref, struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*partfunc)(void *arg, void *zmqsock, zmq_msg_t *msg, - unsigned partnum), - void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock, - struct frrzmq_cb **cb); -extern int _frrzmq_thread_add_write( - const struct xref_threadsched *xref, struct thread_master *master, - void (*msgfunc)(void *arg, void *zmqsock), - void (*errfunc)(void *arg, void *zmqsock), void *arg, void *zmqsock, - struct frrzmq_cb **cb); +extern int +_frrzmq_event_add_read(const struct xref_eventsched *xref, + struct event_loop *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*partfunc)(void *arg, void *zmqsock, + zmq_msg_t *msg, unsigned partnum), + void (*errfunc)(void *arg, void *zmqsock), void *arg, + void *zmqsock, struct frrzmq_cb **cb); +extern int _frrzmq_event_add_write(const struct xref_eventsched *xref, + struct event_loop *master, + void (*msgfunc)(void *arg, void *zmqsock), + void (*errfunc)(void *arg, void *zmqsock), + void *arg, void *zmqsock, + struct frrzmq_cb **cb); extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core); diff --git a/lib/frrevent.h b/lib/frrevent.h new file mode 100644 index 000000000000..2b0c52bb51eb --- /dev/null +++ b/lib/frrevent.h @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Event management routine header. + * Copyright (C) 1998 Kunihiro Ishiguro + */ + +#ifndef _ZEBRA_THREAD_H +#define _ZEBRA_THREAD_H + +#include <zebra.h> +#include <pthread.h> +#include <poll.h> +#include "monotime.h" +#include "frratomic.h" +#include "typesafe.h" +#include "xref.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern bool cputime_enabled; +extern unsigned long cputime_threshold; +/* capturing wallclock time is always enabled since it is fast (reading + * hardware TSC w/o syscalls) + */ +extern unsigned long walltime_threshold; + +struct rusage_t { +#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID + struct timespec cpu; +#else + struct rusage cpu; +#endif + struct timeval real; +}; +#define RUSAGE_T struct rusage_t + +#define GETRUSAGE(X) event_getrusage(X) + +PREDECL_LIST(event_list); +PREDECL_HEAP(event_timer_list); + +struct fd_handler { + /* number of pfd that fit in the allocated space of pfds. This is a + * constant and is the same for both pfds and copy. + */ + nfds_t pfdsize; + + /* file descriptors to monitor for i/o */ + struct pollfd *pfds; + /* number of pollfds stored in pfds */ + nfds_t pfdcount; + + /* chunk used for temp copy of pollfds */ + struct pollfd *copy; + /* number of pollfds stored in copy */ + nfds_t copycount; +}; + +struct xref_eventsched { + struct xref xref; + + const char *funcname; + const char *dest; + uint32_t event_type; +}; + +/* Master of the theads. */ +struct event_loop { + char *name; + + struct event **read; + struct event **write; + struct event_timer_list_head timer; + struct event_list_head event, ready, unuse; + struct list *cancel_req; + bool canceled; + pthread_cond_t cancel_cond; + struct hash *cpu_record; + int io_pipe[2]; + int fd_limit; + struct fd_handler handler; + unsigned long alloc; + long selectpoll_timeout; + bool spin; + bool handle_signals; + pthread_mutex_t mtx; + pthread_t owner; + + bool ready_run_loop; + RUSAGE_T last_getrusage; +}; + +/* Event types. */ +enum event_types { + EVENT_READ, + EVENT_WRITE, + EVENT_TIMER, + EVENT_EVENT, + EVENT_READY, + EVENT_UNUSED, + EVENT_EXECUTE, +}; + +/* Event itself. */ +struct event { + enum event_types type; /* event type */ + enum event_types add_type; /* event type */ + struct event_list_item eventitem; + struct event_timer_list_item timeritem; + struct event **ref; /* external reference (if given) */ + struct event_loop *master; /* pointer to the struct event_loop */ + void (*func)(struct event *e); /* event function */ + void *arg; /* event argument */ + union { + int val; /* second argument of the event. */ + int fd; /* file descriptor in case of r/w */ + struct timeval sands; /* rest of time sands value. */ + } u; + struct timeval real; + struct cpu_event_history *hist; /* cache pointer to cpu_history */ + unsigned long yield; /* yield time in microseconds */ + const struct xref_eventsched *xref; /* origin location */ + pthread_mutex_t mtx; /* mutex for thread.c functions */ + bool ignore_timer_late; +}; + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pTH"(struct event *) +#endif + +struct cpu_event_history { + void (*func)(struct event *e); + atomic_size_t total_cpu_warn; + atomic_size_t total_wall_warn; + atomic_size_t total_starv_warn; + atomic_size_t total_calls; + atomic_size_t total_active; + struct time_stats { + atomic_size_t total, max; + } real; + struct time_stats cpu; + atomic_uint_fast32_t types; + const char *funcname; +}; + +/* Struct timeval's tv_usec one second value. */ +#define TIMER_SECOND_MICRO 1000000L + +/* Event yield time. */ +#define EVENT_YIELD_TIME_SLOT 10 * 1000L /* 10ms */ + +#define EVENT_TIMER_STRLEN 12 + +/* Macros. */ +#define EVENT_ARG(X) ((X)->arg) +#define EVENT_FD(X) ((X)->u.fd) +#define EVENT_VAL(X) ((X)->u.val) + +/* + * Please consider this macro deprecated, and do not use it in new code. + */ +#define EVENT_OFF(thread) \ + do { \ + if ((thread)) \ + event_cancel(&(thread)); \ + } while (0) + +/* + * Macro wrappers to generate xrefs for all thread add calls. Includes + * file/line/function info for debugging/tracing. + */ +#include "lib/xref.h" + +#define _xref_t_a(addfn, type, m, f, a, v, t) \ + ({ \ + static const struct xref_eventsched _xref __attribute__( \ + (used)) = { \ + .xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__), \ + .funcname = #f, \ + .dest = #t, \ + .event_type = EVENT_##type, \ + }; \ + XREF_LINK(_xref.xref); \ + _event_add_##addfn(&_xref, m, f, a, v, t); \ + }) /* end */ + +#define event_add_read(m, f, a, v, t) _xref_t_a(read_write, READ, m, f, a, v, t) +#define event_add_write(m, f, a, v, t) \ + _xref_t_a(read_write, WRITE, m, f, a, v, t) +#define event_add_timer(m, f, a, v, t) _xref_t_a(timer, TIMER, m, f, a, v, t) +#define event_add_timer_msec(m, f, a, v, t) \ + _xref_t_a(timer_msec, TIMER, m, f, a, v, t) +#define event_add_timer_tv(m, f, a, v, t) \ + _xref_t_a(timer_tv, TIMER, m, f, a, v, t) +#define event_add_event(m, f, a, v, t) _xref_t_a(event, EVENT, m, f, a, v, t) + +#define event_execute(m, f, a, v) \ + ({ \ + static const struct xref_eventsched _xref __attribute__( \ + (used)) = { \ + .xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__), \ + .funcname = #f, \ + .dest = NULL, \ + .event_type = EVENT_EXECUTE, \ + }; \ + XREF_LINK(_xref.xref); \ + _event_execute(&_xref, m, f, a, v); \ + }) /* end */ + +/* Prototypes. */ +extern struct event_loop *event_master_create(const char *name); +void event_master_set_name(struct event_loop *master, const char *name); +extern void event_master_free(struct event_loop *m); +extern void event_master_free_unused(struct event_loop *m); + +extern void _event_add_read_write(const struct xref_eventsched *xref, + struct event_loop *master, + void (*fn)(struct event *), void *arg, int fd, + struct event **tref); + +extern void _event_add_timer(const struct xref_eventsched *xref, + struct event_loop *master, + void (*fn)(struct event *), void *arg, long t, + struct event **tref); + +extern void _event_add_timer_msec(const struct xref_eventsched *xref, + struct event_loop *master, + void (*fn)(struct event *), void *arg, long t, + struct event **tref); + +extern void _event_add_timer_tv(const struct xref_eventsched *xref, + struct event_loop *master, + void (*fn)(struct event *), void *arg, + struct timeval *tv, struct event **tref); + +extern void _event_add_event(const struct xref_eventsched *xref, + struct event_loop *master, + void (*fn)(struct event *), void *arg, int val, + struct event **tref); + +extern void _event_execute(const struct xref_eventsched *xref, + struct event_loop *master, + void (*fn)(struct event *), void *arg, int val); + +extern void event_cancel(struct event **event); +extern void event_cancel_async(struct event_loop *m, struct event **eptr, + void *data); +/* Cancel ready tasks with an arg matching 'arg' */ +extern void event_cancel_event_ready(struct event_loop *m, void *arg); +/* Cancel all tasks with an arg matching 'arg', including timers and io */ +extern void event_cancel_event(struct event_loop *m, void *arg); +extern struct event *event_fetch(struct event_loop *m, struct event *event); +extern void event_call(struct event *event); +extern unsigned long event_timer_remain_second(struct event *event); +extern struct timeval event_timer_remain(struct event *event); +extern unsigned long event_timer_remain_msec(struct event *event); +extern int event_should_yield(struct event *event); +/* set yield time for thread */ +extern void event_set_yield_time(struct event *event, unsigned long ytime); + +/* Internal libfrr exports */ +extern void event_getrusage(RUSAGE_T *r); +extern void event_cmd_init(void); + +/* Returns elapsed real (wall clock) time. */ +extern unsigned long event_consumed_time(RUSAGE_T *after, RUSAGE_T *before, + unsigned long *cpu_time_elapsed); + +/* only for use in logging functions! */ +extern pthread_key_t thread_current; +extern char *event_timer_to_hhmmss(char *buf, int buf_size, + struct event *t_timer); + +static inline bool event_is_scheduled(struct event *thread) +{ + if (thread) + return true; + + return false; +} + +/* Debug signal mask */ +void debug_signals(const sigset_t *sigs); + +static inline void event_ignore_late_timer(struct event *event) +{ + event->ignore_timer_late = true; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_THREAD_H */ diff --git a/lib/frrscript.c b/lib/frrscript.c index 4248a4500292..1b99c7e2c6df 100644 --- a/lib/frrscript.c +++ b/lib/frrscript.c @@ -398,8 +398,7 @@ int frrscript_load(struct frrscript *fs, const char *function_name, void frrscript_delete(struct frrscript *fs) { - hash_clean(fs->lua_function_hash, lua_function_free); - hash_free(fs->lua_function_hash); + hash_clean_and_free(&fs->lua_function_hash, lua_function_free); XFREE(MTYPE_SCRIPT, fs->name); XFREE(MTYPE_SCRIPT, fs); } @@ -417,8 +416,7 @@ void frrscript_init(const char *sd) void frrscript_fini(void) { - hash_clean(codec_hash, codec_free); - hash_free(codec_hash); + hash_clean_and_free(&codec_hash, codec_free); frrscript_names_destroy(); } diff --git a/lib/getopt.c b/lib/getopt.c deleted file mode 100644 index 9d0a31131097..000000000000 --- a/lib/getopt.c +++ /dev/null @@ -1,1011 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Getopt for GNU. - * NOTE: getopt is now part of the C library, so if you don't know what - * "Keep this file name-space clean" means, talk to drepper@gnu.org - * before changing it! - * - * Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98 - * Free Software Foundation, Inc. - * - * NOTE: The canonical source of this file is maintained with the GNU C Library. - * Bugs can be reported to bug-glibc@gnu.org. - */ - -/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. - Ditto for AIX 3.2 and <stdlib.h>. */ -#ifndef _NO_PROTO -# define _NO_PROTO -#endif - -#include <zebra.h> - -#if !defined __STDC__ || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -#ifndef const -# define const -#endif -#endif - -#include <stdio.h> - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -#include <gnu-versions.h> -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -# define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -#include <stdlib.h> -#include <unistd.h> -#endif /* GNU C library. */ - -#ifdef VMS -#include <unixlib.h> -#if HAVE_STRING_H - 0 -#include <string.h> -#endif -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. - When compiling libc, the _ macro is predefined. */ -#ifdef HAVE_LIBINTL_H -#include <libintl.h> -# define _(msgid) gettext (msgid) -#else -# define _(msgid) (msgid) -#endif -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = NULL; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include <string.h> -# define my_index strchr -#else - -#if HAVE_STRING_H -#include <string.h> -#else -#include <strings.h> -#endif - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -#ifndef getenv -extern char *getenv(const char *); -#endif - -static char *my_index(const char *str, int chr) -{ - while (*str) { - if (*str == chr) - return (char *)str; - str++; - } - return 0; -} - -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -#if (!defined __STDC__ || !__STDC__) && !defined strlen -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ -extern int strlen(const char *); -#endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -#ifdef _LIBC -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; - -static int nonoption_flags_max_len; -static int nonoption_flags_len; - -static int original_argc; -static char *const *original_argv; - -/* Make sure the environment variable bash 2.0 puts in the environment - is valid for the getopt call we must make sure that the ARGV passed - to getopt is that one passed to the process. */ -static void __attribute__((unused)) -store_args_and_env(int argc, char *const *argv) -{ - /* XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ - original_argc = argc; - original_argv = argv; -} -#ifdef text_set_element -text_set_element(__libc_subinit, store_args_and_env); -#endif /* text_set_element */ - -#define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -#if defined __STDC__ && __STDC__ -static void exchange(char **); -#endif - -static void exchange(argv) char **argv; -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - -/* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - -#ifdef _LIBC - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc(top + 1); - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else { - memset(__mempcpy(new_str, __getopt_nonoption_flags, - nonoption_flags_max_len), - '\0', top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = - argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS(bottom + i, - top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further - * swapping. */ - top -= len; - } else { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. - */ - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS(bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. - */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -#if defined __STDC__ && __STDC__ -static const char *_getopt_initialize(int, char *const *, const char *); -#endif -static const char *_getopt_initialize(argc, argv, optstring) int argc; -char *const *argv; -const char *optstring; -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - posixly_correct = getenv("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') { - ordering = RETURN_IN_ORDER; - ++optstring; - } else if (optstring[0] == '+') { - ordering = REQUIRE_ORDER; - ++optstring; - } else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - -#ifdef _LIBC - if (posixly_correct == NULL && argc == original_argc - && argv == original_argv) { - if (nonoption_flags_max_len == 0) { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = - strlen(orig_str); - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *)malloc(nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - memset(__mempcpy( - __getopt_nonoption_flags, - orig_str, len), - '\0', - nonoption_flags_max_len - len); - } - } - nonoption_flags_len = nonoption_flags_max_len; - } else - nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int _getopt_internal(argc, argv, optstring, longopts, longind, - long_only) int argc; -char *const *argv; -const char *optstring; -const struct option *longopts; -int *longind; -int long_only; -{ - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize(argc, argv, optstring); - __getopt_initialized = 1; - } - -/* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#ifdef _LIBC -#define NONOPTION_P \ - (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has - been - moved back by the user (who may also have changed the - arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) { - /* If we have just processed some options following some - non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt - && last_nonopt != optind) - exchange((char **)argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options - and extend the range of non-options previously - skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an - option, - then skip everything else like a non-option. */ - - if (optind != argc && !strcmp(argv[optind], "--")) { - optind++; - - if (first_nonopt != last_nonopt - && last_nonopt != optind) - exchange((char **)argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. - */ - - if (optind == argc) { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest - them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it - by. */ - - if (NONOPTION_P) { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] - || !my_index(optstring, argv[optind][1]))))) { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; - p++, option_index++) - if (!strncmp(p->name, nextchar, nameend - nextchar)) { - if ((unsigned int)(nameend - nextchar) - == (unsigned int)strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } else - /* Second or later nonexact match found. - */ - ambig = 1; - } - - if (ambig && !exact) { - if (opterr) - fprintf(stderr, - _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen(nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) { - option_index = indfound; - optind++; - if (*nameend) { - /* Don't test has_arg with >, because some C - compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else { - if (opterr) { - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf(stderr, - _("%s: option `--%s' doesn't allow an argument\n"), - argv[0], - pfound->name); - else - /* +option or -option */ - fprintf(stderr, - _("%s: option `%c%s' doesn't allow an argument\n"), - argv[0], - argv[optind - 1] - [0], - pfound->name); - } - - nextchar += strlen(nextchar); - - optopt = pfound->val; - return '?'; - } - } else if (pfound->has_arg == 1) { - if (optind < argc) - optarg = argv[optind++]; - else { - if (opterr) - fprintf(stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], - argv[optind - 1]); - nextchar += strlen(nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen(nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not - getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index(optstring, *nextchar) == NULL) { - if (opterr) { - if (argv[optind][1] == '-') - /* --option */ - fprintf(stderr, - _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); - else - /* +option or -option */ - fprintf(stderr, - _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], - nextchar); - } - nextchar = (char *)""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index(optstring, c); - - /* Increment `optind' when we start to process its last - * character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') { - if (opterr) { - if (posixly_correct) - /* 1003.2 specifies the format of this - * message. */ - fprintf(stderr, - _("%s: illegal option -- %c\n"), - argv[0], c); - else - fprintf(stderr, - _("%s: invalid option -- %c\n"), - argv[0], c); - } - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') { - optarg = nextchar; - /* If we end this ARGV-element by taking the - rest as an arg, - we must advance to the next element now. */ - optind++; - } else if (optind == argc) { - if (opterr) { - /* 1003.2 specifies the format of this - * message. */ - fprintf(stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt - as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; - *nameend && *nameend != '='; nameend++) - /* Do nothing. */; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; - p++, option_index++) - if (!strncmp(p->name, nextchar, - nameend - nextchar)) { - if ((unsigned int)(nameend - nextchar) - == strlen(p->name)) { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } else if (pfound == NULL) { - /* First nonexact match found. - */ - pfound = p; - indfound = option_index; - } else - /* Second or later nonexact - * match found. */ - ambig = 1; - } - if (ambig && !exact) { - if (opterr) - fprintf(stderr, - _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen(nextchar); - optind++; - return '?'; - } - if (pfound != NULL) { - option_index = indfound; - if (*nameend) { - /* Don't test has_arg with >, because - some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else { - if (opterr) - fprintf(stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], - pfound->name); - - nextchar += strlen(nextchar); - return '?'; - } - } else if (pfound->has_arg == 1) { - if (optind < argc) - optarg = argv[optind++]; - else { - if (opterr) - fprintf(stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], - argv[optind - - 1]); - nextchar += strlen(nextchar); - return optstring[0] == ':' - ? ':' - : '?'; - } - } - nextchar += strlen(nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') { - if (temp[2] == ':') { - /* This is an option that accepts an argument - * optionally. */ - if (*nextchar != '\0') { - optarg = nextchar; - optind++; - } else - optarg = NULL; - nextchar = NULL; - } else { - /* This is an option that requires an argument. - */ - if (*nextchar != '\0') { - optarg = nextchar; - /* If we end this ARGV-element by taking - the rest as an arg, - we must advance to the next element - now. */ - optind++; - } else if (optind == argc) { - if (opterr) { - /* 1003.2 specifies the format - * of this message. */ - fprintf(stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } else - /* We already incremented `optind' once; - increment it again when taking next - ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -#ifdef REALLY_NEED_PLAIN_GETOPT - -int getopt(argc, argv, optstring) int argc; -char *const *argv; -const char *optstring; -{ - return _getopt_internal(argc, argv, optstring, (const struct option *)0, - (int *)0, 0); -} - -#endif /* REALLY_NEED_PLAIN_GETOPT */ - -#endif /* Not ELIDE_CODE. */ - -#ifdef TEST - -/* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ - -int main(argc, argv) int argc; -char **argv; -{ - int c; - int digit_optind = 0; - - while (1) { - int this_option_optind = optind ? optind : 1; - - c = getopt(argc, argv, "abc:d:0123456789"); - if (c == -1) - break; - - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 - && digit_optind != this_option_optind) - printf("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf("option %c\n", c); - break; - - case 'a': - printf("option a\n"); - break; - - case 'b': - printf("option b\n"); - break; - - case 'c': - printf("option c with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - } - - exit(0); -} - -#endif /* TEST */ diff --git a/lib/getopt.h b/lib/getopt.h deleted file mode 100644 index 7863bdb66ae7..000000000000 --- a/lib/getopt.h +++ /dev/null @@ -1,143 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Declarations for getopt. - * Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc. - * - * NOTE: The canonical source of this file is maintained with the GNU C Library. - * Bugs can be reported to bug-glibc@gnu.org. - */ - -#ifndef _GETOPT_H -#define _GETOPT_H 1 - -/* - * The operating system may or may not provide getopt_long(), and if - * so it may or may not be a version we are willing to use. Our - * strategy is to declare getopt here, and then provide code unless - * the supplied version is adequate. The difficult case is when a - * declaration for getopt is provided, as our declaration must match. - * - * XXX Arguably this version should be named differently, and the - * local names defined to refer to the system version when we choose - * to use the system version. - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option { -#if defined(__STDC__) && __STDC__ - const char *name; -#else - char *name; -#endif - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -#define no_argument 0 -#define required_argument 1 -#define optional_argument 2 - -#if defined(__STDC__) && __STDC__ - -#ifdef REALLY_NEED_PLAIN_GETOPT - -/* - * getopt is defined in POSIX.2. Assume that if the system defines - * getopt that it complies with POSIX.2. If not, an autoconf test - * should be written to define NONPOSIX_GETOPT_DEFINITION. - */ -#ifndef NONPOSIX_GETOPT_DEFINITION -extern int getopt(int argc, char *const *argv, const char *shortopts); -#else /* NONPOSIX_GETOPT_DEFINITION */ -extern int getopt(void); -#endif /* NONPOSIX_GETOPT_DEFINITION */ - -#endif - - -extern int getopt_long(int argc, char *const *argv, const char *shortopts, - const struct option *longopts, int *longind); -extern int getopt_long_only(int argc, char *const *argv, const char *shortopts, - const struct option *longopts, int *longind); - -/* Internal only. Users should not call this directly. */ -extern int _getopt_internal(int argc, char *const *argv, const char *shortopts, - const struct option *longopts, int *longind, - int long_only); -#else /* not __STDC__ */ - -#ifdef REALLY_NEED_PLAIN_GETOPT -extern int getopt(); -#endif /* REALLY_NEED_PLAIN_GETOPT */ - -extern int getopt_long(); -extern int getopt_long_only(); - -extern int _getopt_internal(); - -#endif /* __STDC__ */ - -#ifdef __cplusplus -} -#endif - -#endif /* getopt.h */ diff --git a/lib/getopt1.c b/lib/getopt1.c deleted file mode 100644 index cf21c3aab473..000000000000 --- a/lib/getopt1.c +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* getopt_long and getopt_long_only entry points for GNU getopt. - * Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 - * Free Software Foundation, Inc. - * - * NOTE: The canonical source of this file is maintained with the GNU C Library. - * Bugs can be reported to bug-glibc@gnu.org. - */ - -#include <zebra.h> -#include "getopt.h" - -#if !defined __STDC__ || !__STDC__ -/* This is a separate conditional since some stdc systems - reject `defined (const)'. */ -#ifndef const -#define const -#endif -#endif - -#include <stdio.h> - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -#include <gnu-versions.h> -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include <stdlib.h> -#endif - -#ifndef NULL -#define NULL 0 -#endif - -int getopt_long(argc, argv, options, long_options, opt_index) int argc; -char *const *argv; -const char *options; -const struct option *long_options; -int *opt_index; -{ - return _getopt_internal(argc, argv, options, long_options, opt_index, - 0); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int getopt_long_only(argc, argv, options, long_options, opt_index) int argc; -char *const *argv; -const char *options; -const struct option *long_options; -int *opt_index; -{ - return _getopt_internal(argc, argv, options, long_options, opt_index, - 1); -} - - -#endif /* Not ELIDE_CODE. */ - -#ifdef TEST - -#include <stdio.h> - -int main(argc, argv) int argc; -char **argv; -{ - int c; - int digit_optind = 0; - - while (1) { - int this_option_optind = optind ? optind : 1; - int option_index = 0; - static struct option long_options[] = { - {"add", 1, 0, 0}, {"append", 0, 0, 0}, - {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, - {"create", 0, 0, 0}, {"file", 1, 0, 0}, - {0, 0, 0, 0}}; - - c = getopt_long(argc, argv, "abc:d:0123456789", long_options, - &option_index); - if (c == -1) - break; - - switch (c) { - case 0: - printf("option %s", long_options[option_index].name); - if (optarg) - printf(" with arg %s", optarg); - printf("\n"); - break; - - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (digit_optind != 0 - && digit_optind != this_option_optind) - printf("digits occur in two different argv-elements.\n"); - digit_optind = this_option_optind; - printf("option %c\n", c); - break; - - case 'a': - printf("option a\n"); - break; - - case 'b': - printf("option b\n"); - break; - - case 'c': - printf("option c with value `%s'\n", optarg); - break; - - case 'd': - printf("option d with value `%s'\n", optarg); - break; - - case '?': - break; - - default: - printf("?? getopt returned character code 0%o ??\n", c); - } - } - - if (optind < argc) { - printf("non-option ARGV-elements: "); - while (optind < argc) - printf("%s ", argv[optind++]); - printf("\n"); - } - - exit(0); -} - -#endif /* TEST */ diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c index fc7ebebbe557..24833e08ec75 100644 --- a/lib/grammar_sandbox.c +++ b/lib/grammar_sandbox.c @@ -19,7 +19,7 @@ #define GRAMMAR_STR "CLI grammar sandbox\n" -DEFINE_MTYPE_STATIC(LIB, CMD_TOKENS, "Command desc"); +DEFINE_MTYPE_STATIC(LIB, CMD_DESCRIPTIONS, "Command desc"); /** headers **/ void grammar_sandbox_init(void); @@ -53,7 +53,7 @@ DEFUN (grammar_test, // create cmd_element for parser struct cmd_element *cmd = - XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element)); + XCALLOC(MTYPE_CMD_DESCRIPTIONS, sizeof(struct cmd_element)); cmd->string = command; cmd->doc = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n"; @@ -207,11 +207,11 @@ DEFUN (grammar_test_doc, // create cmd_element with docstring struct cmd_element *cmd = - XCALLOC(MTYPE_CMD_TOKENS, sizeof(struct cmd_element)); + XCALLOC(MTYPE_CMD_DESCRIPTIONS, sizeof(struct cmd_element)); cmd->string = XSTRDUP( - MTYPE_CMD_TOKENS, + MTYPE_CMD_DESCRIPTIONS, "test docstring <example|selector follow> (1-255) end VARIABLE [OPTION|set lol] . VARARG"); - cmd->doc = XSTRDUP(MTYPE_CMD_TOKENS, + cmd->doc = XSTRDUP(MTYPE_CMD_DESCRIPTIONS, "Test stuff\n" "docstring thing\n" "first example\n" diff --git a/lib/grammar_sandbox_main.c b/lib/grammar_sandbox_main.c index cdb1c3adb449..abd42f359f05 100644 --- a/lib/grammar_sandbox_main.c +++ b/lib/grammar_sandbox_main.c @@ -23,13 +23,13 @@ static void vty_do_exit(int isexit) exit(0); } -struct thread_master *master; +struct event_loop *master; int main(int argc, char **argv) { - struct thread thread; + struct event event; - master = thread_master_create(NULL); + master = event_master_create(NULL); zlog_aux_init("NONE: ", LOG_DEBUG); @@ -45,8 +45,8 @@ int main(int argc, char **argv) vty_stdio(vty_do_exit); /* Fetch next active thread. */ - while (thread_fetch(master, &thread)) - thread_call(&thread); + while (event_fetch(master, &event)) + event_call(&event); /* Not reached. */ exit(0); diff --git a/lib/hash.c b/lib/hash.c index 97a77a2b9a10..df5624398533 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -297,6 +297,16 @@ void hash_clean(struct hash *hash, void (*free_func)(void *)) hash->stats.empty = hash->size; } +void hash_clean_and_free(struct hash **hash, void (*free_func)(void *)) +{ + if (!*hash) + return; + + hash_clean(*hash, free_func); + hash_free(*hash); + *hash = NULL; +} + static void hash_to_list_iter(struct hash_bucket *hb, void *arg) { struct list *list = arg; diff --git a/lib/hash.h b/lib/hash.h index f32309d80476..810faf903070 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -277,6 +277,17 @@ extern void hash_walk(struct hash *hash, */ extern void hash_clean(struct hash *hash, void (*free_func)(void *)); +/* + * Remove all elements from a hash table and free the table, + * setting the pointer to NULL. + * + * hash + * hash table to operate on + * free_func + * function to call with each removed item, intended to free the data + */ +extern void hash_clean_and_free(struct hash **hash, void (*free_func)(void *)); + /* * Delete a hash table. * diff --git a/lib/if.c b/lib/if.c index 08d891874273..6f567861d19c 100644 --- a/lib/if.c +++ b/lib/if.c @@ -1028,6 +1028,7 @@ void if_terminate(struct vrf *vrf) if (ifp->node) { ifp->node->info = NULL; route_unlock_node(ifp->node); + ifp->node = NULL; } if_delete(&ifp); } diff --git a/lib/if_rmap.c b/lib/if_rmap.c index fa8899e9f87e..42e162072ff3 100644 --- a/lib/if_rmap.c +++ b/lib/if_rmap.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* route-map for interface. * Copyright (C) 1999 Kunihiro Ishiguro + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include <zebra.h> @@ -10,6 +11,9 @@ #include "memory.h" #include "if.h" #include "if_rmap.h" +#include "northbound_cli.h" + +#include "lib/if_rmap_clippy.c" DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX, "Interface route map container"); DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, @@ -17,8 +21,6 @@ DEFINE_MTYPE_STATIC(LIB, IF_RMAP_CTX_NAME, DEFINE_MTYPE_STATIC(LIB, IF_RMAP, "Interface route map"); DEFINE_MTYPE_STATIC(LIB, IF_RMAP_NAME, "I.f. route map name"); -static struct list *if_rmap_ctx_list; - static struct if_rmap *if_rmap_new(void) { struct if_rmap *new; @@ -30,7 +32,9 @@ static struct if_rmap *if_rmap_new(void) static void if_rmap_free(struct if_rmap *if_rmap) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->ifname); + char *no_const_ifname = (char *)if_rmap->ifname; + + XFREE(MTYPE_IF_RMAP_NAME, no_const_ifname); XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); @@ -40,22 +44,16 @@ static void if_rmap_free(struct if_rmap *if_rmap) struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname) { - struct if_rmap key; + struct if_rmap key = {.ifname = ifname}; struct if_rmap *if_rmap; - /* temporary copy */ - key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL; - if_rmap = hash_lookup(ctx->ifrmaphash, &key); - XFREE(MTYPE_IF_RMAP_NAME, key.ifname); - return if_rmap; } void if_rmap_hook_add(struct if_rmap_ctx *ctx, - void (*func)(struct if_rmap_ctx *ctx, - struct if_rmap *)) + void (*func)(struct if_rmap_ctx *ctx, struct if_rmap *)) { ctx->if_rmap_add_hook = func; } @@ -80,16 +78,11 @@ static void *if_rmap_hash_alloc(void *arg) static struct if_rmap *if_rmap_get(struct if_rmap_ctx *ctx, const char *ifname) { - struct if_rmap key; + struct if_rmap key = {.ifname = ifname}; struct if_rmap *ret; - /* temporary copy */ - key.ifname = (ifname) ? XSTRDUP(MTYPE_IF_RMAP_NAME, ifname) : NULL; - ret = hash_get(ctx->ifrmaphash, &key, if_rmap_hash_alloc); - XFREE(MTYPE_IF_RMAP_NAME, key.ifname); - return ret; } @@ -108,177 +101,184 @@ static bool if_rmap_hash_cmp(const void *arg1, const void *arg2) return strcmp(if_rmap1->ifname, if_rmap2->ifname) == 0; } -static struct if_rmap *if_rmap_set(struct if_rmap_ctx *ctx, - const char *ifname, enum if_rmap_type type, - const char *routemap_name) +static void if_rmap_set(struct if_rmap_ctx *ctx, const char *ifname, + enum if_rmap_type type, const char *routemap_name) { - struct if_rmap *if_rmap; - - if_rmap = if_rmap_get(ctx, ifname); + struct if_rmap *if_rmap = if_rmap_get(ctx, ifname); - if (type == IF_RMAP_IN) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - if_rmap->routemap[IF_RMAP_IN] = - XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); - } - if (type == IF_RMAP_OUT) { - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - if_rmap->routemap[IF_RMAP_OUT] = - XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); - } + assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); + XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); + if_rmap->routemap[type] = XSTRDUP(MTYPE_IF_RMAP_NAME, routemap_name); if (ctx->if_rmap_add_hook) (ctx->if_rmap_add_hook)(ctx, if_rmap); - - return if_rmap; } -static int if_rmap_unset(struct if_rmap_ctx *ctx, - const char *ifname, enum if_rmap_type type, - const char *routemap_name) +static void if_rmap_unset(struct if_rmap_ctx *ctx, const char *ifname, + enum if_rmap_type type) { - struct if_rmap *if_rmap; + struct if_rmap *if_rmap = if_rmap_lookup(ctx, ifname); - if_rmap = if_rmap_lookup(ctx, ifname); if (!if_rmap) - return 0; - - if (type == IF_RMAP_IN) { - if (!if_rmap->routemap[IF_RMAP_IN]) - return 0; - if (strcmp(if_rmap->routemap[IF_RMAP_IN], routemap_name) != 0) - return 0; - - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_IN]); - } + return; - if (type == IF_RMAP_OUT) { - if (!if_rmap->routemap[IF_RMAP_OUT]) - return 0; - if (strcmp(if_rmap->routemap[IF_RMAP_OUT], routemap_name) != 0) - return 0; + assert(type == IF_RMAP_IN || type == IF_RMAP_OUT); + if (!if_rmap->routemap[type]) + return; - XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[IF_RMAP_OUT]); - } + XFREE(MTYPE_IF_RMAP_NAME, if_rmap->routemap[type]); if (ctx->if_rmap_delete_hook) ctx->if_rmap_delete_hook(ctx, if_rmap); - if (if_rmap->routemap[IF_RMAP_IN] == NULL - && if_rmap->routemap[IF_RMAP_OUT] == NULL) { + if (if_rmap->routemap[IF_RMAP_IN] == NULL && + if_rmap->routemap[IF_RMAP_OUT] == NULL) { hash_release(ctx->ifrmaphash, if_rmap); if_rmap_free(if_rmap); } - - return 1; } -DEFUN (if_rmap, - if_rmap_cmd, - "route-map RMAP_NAME <in|out> IFNAME", - "Route map set\n" - "Route map name\n" - "Route map set for input filtering\n" - "Route map set for output filtering\n" - "Route map interface name\n") +static int if_route_map_handler(struct vty *vty, bool no, const char *dir, + const char *other_dir, const char *ifname, + const char *route_map) { - int idx_rmap_name = 1; - int idx_in_out = 2; - int idx_ifname = 3; - enum if_rmap_type type; - struct if_rmap_ctx *ctx = - (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list); - - if (strncmp(argv[idx_in_out]->text, "in", 1) == 0) - type = IF_RMAP_IN; - else if (strncmp(argv[idx_in_out]->text, "out", 1) == 0) - type = IF_RMAP_OUT; - else { - vty_out(vty, "route-map direction must be [in|out]\n"); - return CMD_WARNING_CONFIG_FAILED; + enum nb_operation op = no ? NB_OP_DESTROY : NB_OP_MODIFY; + const struct lyd_node *dnode; + char xpath[XPATH_MAXLEN]; + + if (!no) { + snprintf( + xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, dir); + } else { + /* + * If we are deleting the last policy for this interface, + * (i.e., no `in` or `out` policy). delete the interface list + * node instead. + */ + dnode = yang_dnode_get(vty->candidate_config->dnode, + VTY_CURR_XPATH); + if (yang_dnode_existsf( + dnode, + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, other_dir)) { + snprintf( + xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']/%s-route-map", + ifname, dir); + } else { + /* both dir will be empty so delete the list node */ + snprintf(xpath, sizeof(xpath), + "./if-route-maps/if-route-map[interface='%s']", + ifname); + } } + nb_cli_enqueue_change(vty, xpath, op, route_map); - if_rmap_set(ctx, argv[idx_ifname]->arg, - type, argv[idx_rmap_name]->arg); + return nb_cli_apply_changes(vty, NULL); +} - return CMD_SUCCESS; +DEFPY_YANG(if_ipv4_route_map, if_ipv4_route_map_cmd, + "route-map ROUTE-MAP <in$in|out> IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, false, dir, other_dir, ifname, + route_map); } -DEFUN (no_if_rmap, - no_if_rmap_cmd, - "no route-map ROUTEMAP_NAME <in|out> IFNAME", - NO_STR - "Route map unset\n" - "Route map name\n" - "Route map for input filtering\n" - "Route map for output filtering\n" - "Route map interface name\n") +DEFPY_YANG(no_if_ipv4_route_map, no_if_ipv4_route_map_cmd, + "no route-map [ROUTE-MAP] <in$in|out> IFNAME", + NO_STR + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) { - int idx_routemap_name = 2; - int idx_in_out = 3; - int idx_ifname = 4; - int ret; - enum if_rmap_type type; - struct if_rmap_ctx *ctx = - (struct if_rmap_ctx *)listnode_head(if_rmap_ctx_list); - - if (strncmp(argv[idx_in_out]->arg, "i", 1) == 0) - type = IF_RMAP_IN; - else if (strncmp(argv[idx_in_out]->arg, "o", 1) == 0) - type = IF_RMAP_OUT; - else { - vty_out(vty, "route-map direction must be [in|out]\n"); - return CMD_WARNING_CONFIG_FAILED; - } + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; - ret = if_rmap_unset(ctx, argv[idx_ifname]->arg, type, - argv[idx_routemap_name]->arg); - if (!ret) { - vty_out(vty, "route-map doesn't exist\n"); - return CMD_WARNING_CONFIG_FAILED; - } - return CMD_SUCCESS; + return if_route_map_handler(vty, true, dir, other_dir, ifname, + route_map); } +/* + * CLI infra requires new handlers for ripngd + */ +DEFPY_YANG(if_ipv6_route_map, if_ipv6_route_map_cmd, + "route-map ROUTE-MAP <in$in|out> IFNAME", + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) +{ + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; -/* Configuration write function. */ -int config_write_if_rmap(struct vty *vty, - struct if_rmap_ctx *ctx) + return if_route_map_handler(vty, false, dir, other_dir, ifname, + route_map); +} + +DEFPY_YANG(no_if_ipv6_route_map, no_if_ipv6_route_map_cmd, + "no route-map [ROUTE-MAP] <in$in|out> IFNAME", + NO_STR + "Route map set\n" + "Route map name\n" + "Route map set for input filtering\n" + "Route map set for output filtering\n" INTERFACE_STR) { - unsigned int i; - struct hash_bucket *mp; - int write = 0; - struct hash *ifrmaphash = ctx->ifrmaphash; - - for (i = 0; i < ifrmaphash->size; i++) - for (mp = ifrmaphash->index[i]; mp; mp = mp->next) { - struct if_rmap *if_rmap; - - if_rmap = mp->data; - - if (if_rmap->routemap[IF_RMAP_IN]) { - vty_out(vty, " route-map %s in %s\n", - if_rmap->routemap[IF_RMAP_IN], - if_rmap->ifname); - write++; - } - - if (if_rmap->routemap[IF_RMAP_OUT]) { - vty_out(vty, " route-map %s out %s\n", - if_rmap->routemap[IF_RMAP_OUT], - if_rmap->ifname); - write++; - } - } - return write; + const char *dir = in ? "in" : "out"; + const char *other_dir = in ? "out" : "in"; + + return if_route_map_handler(vty, true, dir, other_dir, ifname, + route_map); +} + +void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + if (yang_dnode_exists(dnode, "./in-route-map")) + vty_out(vty, " route-map %s in %s\n", + yang_dnode_get_string(dnode, "./in-route-map"), + yang_dnode_get_string(dnode, "./interface")); + if (yang_dnode_exists(dnode, "./out-route-map")) + vty_out(vty, " route-map %s out %s\n", + yang_dnode_get_string(dnode, "./out-route-map"), + yang_dnode_get_string(dnode, "./interface")); +} + +void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode, + enum if_rmap_type type, bool del) +{ + + const char *mapname = yang_dnode_get_string(dnode, NULL); + const char *ifname = yang_dnode_get_string(dnode, "../interface"); + + if (del) + if_rmap_unset(ctx, ifname, type); + else + if_rmap_set(ctx, ifname, type, mapname); +} + +void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode) +{ + const char *ifname = yang_dnode_get_string(dnode, "interface"); + if_rmap_unset(ctx, ifname, IF_RMAP_IN); + if_rmap_unset(ctx, ifname, IF_RMAP_OUT); } void if_rmap_ctx_delete(struct if_rmap_ctx *ctx) { - listnode_delete(if_rmap_ctx_list, ctx); - hash_clean(ctx->ifrmaphash, (void (*)(void *))if_rmap_free); - if (ctx->name) - XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx); + hash_clean_and_free(&ctx->ifrmaphash, (void (*)(void *))if_rmap_free); + XFREE(MTYPE_IF_RMAP_CTX_NAME, ctx->name); XFREE(MTYPE_IF_RMAP_CTX, ctx); } @@ -289,29 +289,24 @@ struct if_rmap_ctx *if_rmap_ctx_create(const char *name) ctx = XCALLOC(MTYPE_IF_RMAP_CTX, sizeof(struct if_rmap_ctx)); - if (ctx->name) - ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name); - ctx->ifrmaphash = hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp, - "Interface Route-Map Hash"); - if (!if_rmap_ctx_list) - if_rmap_ctx_list = list_new(); - listnode_add(if_rmap_ctx_list, ctx); + ctx->name = XSTRDUP(MTYPE_IF_RMAP_CTX_NAME, name); + ctx->ifrmaphash = + hash_create_size(4, if_rmap_hash_make, if_rmap_hash_cmp, + "Interface Route-Map Hash"); return ctx; } void if_rmap_init(int node) { - if (node == RIPNG_NODE) { - } else if (node == RIP_NODE) { - install_element(RIP_NODE, &if_rmap_cmd); - install_element(RIP_NODE, &no_if_rmap_cmd); + if (node == RIP_NODE) { + install_element(RIP_NODE, &if_ipv4_route_map_cmd); + install_element(RIP_NODE, &no_if_ipv4_route_map_cmd); + } else if (node == RIPNG_NODE) { + install_element(RIPNG_NODE, &if_ipv6_route_map_cmd); + install_element(RIPNG_NODE, &no_if_ipv6_route_map_cmd); } - if_rmap_ctx_list = list_new(); } void if_rmap_terminate(void) { - if (!if_rmap_ctx_list) - return; - list_delete(&if_rmap_ctx_list); } diff --git a/lib/if_rmap.h b/lib/if_rmap.h index 3bdbc2a3b2cd..a9f811e2210c 100644 --- a/lib/if_rmap.h +++ b/lib/if_rmap.h @@ -6,15 +6,20 @@ #ifndef _ZEBRA_IF_RMAP_H #define _ZEBRA_IF_RMAP_H +#include "typesafe.h" + #ifdef __cplusplus extern "C" { #endif +struct lyd_node; +struct vty; + enum if_rmap_type { IF_RMAP_IN, IF_RMAP_OUT, IF_RMAP_MAX }; struct if_rmap { /* Name of the interface. */ - char *ifname; + const char *ifname; char *routemap[IF_RMAP_MAX]; }; @@ -45,7 +50,14 @@ void if_rmap_hook_delete(struct if_rmap_ctx *ctx, struct if_rmap *)); extern struct if_rmap *if_rmap_lookup(struct if_rmap_ctx *ctx, const char *ifname); +extern void if_rmap_yang_modify_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode, + enum if_rmap_type type, bool del); +extern void if_rmap_yang_destroy_cb(struct if_rmap_ctx *ctx, + const struct lyd_node *dnode); extern int config_write_if_rmap(struct vty *, struct if_rmap_ctx *ctx); +void cli_show_if_route_map(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); #ifdef __cplusplus } diff --git a/lib/iso.c b/lib/iso.c new file mode 100644 index 000000000000..fe97776ade49 --- /dev/null +++ b/lib/iso.c @@ -0,0 +1,144 @@ +/* + * ISO Network functions - iso_net.c + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2023 Orange http://www.orange.com + * + * This file is part of Free Range Routing (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 "compiler.h" + +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "printfrr.h" +#include "iso.h" + +/** + * Print ISO System ID as 0000.0000.0000 + * + * @param Print buffer + * @param Print argument + * @param Pointer to the System ID to be printed + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("SY", printfrr_iso_sysid); +static ssize_t printfrr_iso_sysid(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const uint8_t *id = vptr; + + if (!id) + return bputs(buf, "(null)"); + + return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x", + id[0], id[1], id[2], id[3], id[4], id[5]); +} + +/** + * Print ISO Pseudo Node system ID as 0000.0000.0000.00 + * + * @param Print buffer + * @param Print argument + * @param Pointer to the System ID to be printed + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("PN", printfrr_iso_pseudo); +static ssize_t printfrr_iso_pseudo(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const uint8_t *id = vptr; + + if (!id) + return bputs(buf, "(null)"); + + return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x", + id[0], id[1], id[2], id[3], id[4], id[5], id[6]); +} + +/** + * Print ISO LSP Fragment System ID as 0000.0000.0000.00-00 + * + * @param Print buffer + * @param Print argument + * @param Pointer to the System ID to be printed + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("LS", printfrr_iso_frag_id); +static ssize_t printfrr_iso_frag_id(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const uint8_t *id = vptr; + + if (!id) + return bputs(buf, "(null)"); + + return bprintfrr(buf, "%02x%02x.%02x%02x.%02x%02x.%02x-%02x", + id[0], id[1], id[2], id[3], id[4], id[5], id[6], + id[7]); +} + +/** + * Print ISO Network address as 00.0000.0000.0000 ... with the System ID + * as 0000.0000.0000.00 when long 'l' option is added to '%pIS' + * + * @param Print buffer + * @param Print argument + * @param Pointer to the ISO Network address + * + * @return Number of printed characters + */ +printfrr_ext_autoreg_p("IS", printfrr_iso_addr); +static ssize_t printfrr_iso_addr(struct fbuf *buf, struct printfrr_eargs *ea, + const void *vptr) +{ + const struct iso_address *ia = vptr; + uint8_t len = 0; + int i = 0; + ssize_t ret = 0; + + if (ea->fmt[0] == 'l') { + len = 7; /* ISO SYSTEM ID + 1 */ + ea->fmt++; + } + + if (!ia) + return bputs(buf, "(null)"); + + len += ia->addr_len; + while (i < len) { + /* No dot for odd index and at the end of address */ + if ((i & 1) || (i == (len - 1))) + ret += bprintfrr(buf, "%02x", ia->area_addr[i]); + else + ret += bprintfrr(buf, "%02x.", ia->area_addr[i]); + i++; + } + + return ret; +} + diff --git a/lib/iso.h b/lib/iso.h new file mode 100644 index 000000000000..975d3c154b64 --- /dev/null +++ b/lib/iso.h @@ -0,0 +1,49 @@ +/* + * ISO Network definition - iso_net.h + * + * Author: Olivier Dugeon <olivier.dugeon@orange.com> + * + * Copyright (C) 2023 Orange http://www.orange.com + * + * This file is part of Free Range Routing (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 LIB_ISO_H_ +#define LIB_ISO_H_ + +#include "compiler.h" + +/* len of "xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx" + '\0' */ +#define ISO_ADDR_STRLEN 51 +#define ISO_ADDR_MIN 8 +#define ISO_ADDR_SIZE 20 +struct iso_address { + uint8_t addr_len; + uint8_t area_addr[ISO_ADDR_SIZE]; +}; + +/* len of "xxxx.xxxx.xxxx.xx-xx" + '\0' */ +#define ISO_SYSID_STRLEN 21 + +#ifdef _FRR_ATTRIBUTE_PRINTFRR +#pragma FRR printfrr_ext "%pSY" (uint8_t *) +#pragma FRR printfrr_ext "%pPN" (uint8_t *) +#pragma FRR printfrr_ext "%pLS" (uint8_t *) +#pragma FRR printfrr_ext "%pIS" (struct iso_address *) +#endif + +#endif /* LIB_ISO_H_ */ diff --git a/lib/ldp_sync.c b/lib/ldp_sync.c index b01cf87287d2..d55819dfaffa 100644 --- a/lib/ldp_sync.c +++ b/lib/ldp_sync.c @@ -10,7 +10,7 @@ #include "memory.h" #include "prefix.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "stream.h" #include "zclient.h" #include "table.h" @@ -66,7 +66,7 @@ bool ldp_sync_if_down(struct ldp_sync_info *ldp_sync_info) * update state */ if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_UP) ldp_sync_info->state = diff --git a/lib/ldp_sync.h b/lib/ldp_sync.h index 5b6ebbf887a6..f7601ebf9d2f 100644 --- a/lib/ldp_sync.h +++ b/lib/ldp_sync.h @@ -37,7 +37,7 @@ struct ldp_sync_info { uint8_t enabled; /* enabled */ uint8_t state; /* running state */ uint16_t holddown; /* timer value */ - struct thread *t_holddown; /* holddown timer*/ + struct event *t_holddown; /* holddown timer*/ uint32_t metric[2]; /* isis interface metric */ }; diff --git a/lib/lib_vty.c b/lib/lib_vty.c index 2c8f2e90473d..c13d88a1e821 100644 --- a/lib/lib_vty.c +++ b/lib/lib_vty.c @@ -242,6 +242,17 @@ DEFUN_NOSH(end_config, end_config_cmd, "XFRR_end_configuration", ret = nb_cli_pending_commit_check(vty); zlog_info("Configuration Read in Took: %s", readin_time_str); + zlog_debug("%s: VTY:%p, pending SET-CFG: %u", __func__, vty, + (uint32_t)vty->mgmt_num_pending_setcfg); + + /* + * If (and only if) we have sent any CLI config commands to MGMTd + * FE interface using vty_mgmt_send_config_data() without implicit + * commit before, should we need to send an explicit COMMIT-REQ now + * to apply all those commands at once. + */ + if (vty->mgmt_num_pending_setcfg && vty_mgmt_fe_enabled()) + vty_mgmt_send_commit_config(vty, false, false); if (callback.end_config) (*callback.end_config)(); diff --git a/lib/libfrr.c b/lib/libfrr.c index d1b7dd133e61..33237df5fca3 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -33,10 +33,10 @@ #include "frrscript.h" #include "systemd.h" -DEFINE_HOOK(frr_early_init, (struct thread_master * tm), (tm)); -DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)); -DEFINE_HOOK(frr_config_pre, (struct thread_master * tm), (tm)); -DEFINE_HOOK(frr_config_post, (struct thread_master * tm), (tm)); +DEFINE_HOOK(frr_early_init, (struct event_loop * tm), (tm)); +DEFINE_HOOK(frr_late_init, (struct event_loop * tm), (tm)); +DEFINE_HOOK(frr_config_pre, (struct event_loop * tm), (tm)); +DEFINE_HOOK(frr_config_post, (struct event_loop * tm), (tm)); DEFINE_KOOH(frr_early_fini, (), ()); DEFINE_KOOH(frr_fini, (), ()); @@ -130,6 +130,7 @@ static const struct optspec os_always = { " --limit-fds Limit number of fds supported\n", lo_always}; +static bool logging_to_stdout = false; /* set when --log stdout specified */ static const struct option lo_cfg[] = { {"config_file", required_argument, NULL, 'f'}, @@ -696,8 +697,8 @@ static void _err_print(const void *cookie, const char *errstr) fprintf(stderr, "%s: %s\n", prefix, errstr); } -static struct thread_master *master; -struct thread_master *frr_init(void) +static struct event_loop *master; +struct event_loop *frr_init(void) { struct option_chain *oc; struct log_arg *log_arg; @@ -738,6 +739,11 @@ struct thread_master *frr_init(void) while ((log_arg = log_args_pop(di->early_logging))) { command_setup_early_logging(log_arg->target, di->early_loglevel); + /* this is a bit of a hack, + but need to notice when + the target is stdout */ + if (strcmp(log_arg->target, "stdout") == 0) + logging_to_stdout = true; XFREE(MTYPE_TMP, log_arg); } @@ -769,7 +775,7 @@ struct thread_master *frr_init(void) zprivs_init(di->privs); - master = thread_master_create(NULL); + master = event_master_create(NULL); signal_init(master, di->n_signals, di->signals); hook_call(frr_early_init, master); @@ -964,7 +970,7 @@ static void frr_daemonize(void) * to read the config in after thread execution starts, so that * we can match this behavior. */ -static void frr_config_read_in(struct thread *t) +static void frr_config_read_in(struct event *t) { hook_call(frr_config_pre, master); @@ -1015,8 +1021,8 @@ void frr_config_fork(void) exit(0); } - thread_add_event(master, frr_config_read_in, NULL, 0, - &di->read_in); + event_add_event(master, frr_config_read_in, NULL, 0, + &di->read_in); } if (di->daemon_mode || di->terminal) @@ -1030,7 +1036,7 @@ void frr_config_fork(void) zlog_tls_buffer_init(); } -static void frr_vty_serv(void) +void frr_vty_serv_start(void) { /* allow explicit override of vty_path in the future * (not currently set anywhere) */ @@ -1052,7 +1058,15 @@ static void frr_vty_serv(void) di->vty_path = vtypath_default; } - vty_serv_sock(di->vty_addr, di->vty_port, di->vty_path); + vty_serv_start(di->vty_addr, di->vty_port, di->vty_path); +} + +void frr_vty_serv_stop(void) +{ + vty_serv_stop(); + + if (di->vty_path) + unlink(di->vty_path); } static void frr_check_detach(void) @@ -1088,16 +1102,22 @@ static void frr_terminal_close(int isexit) "%s: failed to open /dev/null: %s", __func__, safe_strerror(errno)); } else { - dup2(nullfd, 0); - dup2(nullfd, 1); - dup2(nullfd, 2); + int fd; + /* + * only redirect stdin, stdout, stderr to null when a tty also + * don't redirect when stdout is set with --log stdout + */ + for (fd = 2; fd >= 0; fd--) + if (isatty(fd) && + (fd != STDOUT_FILENO || !logging_to_stdout)) + dup2(nullfd, fd); close(nullfd); } } -static struct thread *daemon_ctl_thread = NULL; +static struct event *daemon_ctl_thread = NULL; -static void frr_daemon_ctl(struct thread *t) +static void frr_daemon_ctl(struct event *t) { char buf[1]; ssize_t nr; @@ -1129,8 +1149,8 @@ static void frr_daemon_ctl(struct thread *t) } out: - thread_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock, - &daemon_ctl_thread); + event_add_read(master, frr_daemon_ctl, NULL, daemon_ctl_sock, + &daemon_ctl_thread); } void frr_detach(void) @@ -1139,11 +1159,12 @@ void frr_detach(void) frr_check_detach(); } -void frr_run(struct thread_master *master) +void frr_run(struct event_loop *master) { char instanceinfo[64] = ""; - frr_vty_serv(); + if (!(di->flags & FRR_MANUAL_VTY_START)) + frr_vty_serv_start(); if (di->instance) snprintf(instanceinfo, sizeof(instanceinfo), "instance %u ", @@ -1158,8 +1179,8 @@ void frr_run(struct thread_master *master) vty_stdio(frr_terminal_close); if (daemon_ctl_sock != -1) { set_nonblocking(daemon_ctl_sock); - thread_add_read(master, frr_daemon_ctl, NULL, - daemon_ctl_sock, &daemon_ctl_thread); + event_add_read(master, frr_daemon_ctl, NULL, + daemon_ctl_sock, &daemon_ctl_thread); } } else if (di->daemon_mode) { int nullfd = open("/dev/null", O_RDONLY | O_NOCTTY); @@ -1168,9 +1189,16 @@ void frr_run(struct thread_master *master) "%s: failed to open /dev/null: %s", __func__, safe_strerror(errno)); } else { - dup2(nullfd, 0); - dup2(nullfd, 1); - dup2(nullfd, 2); + int fd; + /* + * only redirect stdin, stdout, stderr to null when a + * tty also don't redirect when stdout is set with --log + * stdout + */ + for (fd = 2; fd >= 0; fd--) + if (isatty(fd) && + (fd != STDOUT_FILENO || !logging_to_stdout)) + dup2(nullfd, fd); close(nullfd); } @@ -1180,9 +1208,9 @@ void frr_run(struct thread_master *master) /* end fixed stderr startup logging */ zlog_startup_end(); - struct thread thread; - while (thread_fetch(master, &thread)) - thread_call(&thread); + struct event thread; + while (event_fetch(master, &thread)) + event_call(&thread); } void frr_early_fini(void) @@ -1194,7 +1222,7 @@ void frr_fini(void) { FILE *fp; char filename[128]; - int have_leftovers; + int have_leftovers = 0; hook_call(frr_fini); @@ -1213,23 +1241,22 @@ void frr_fini(void) frr_pthread_finish(); zprivs_terminate(di->privs); /* signal_init -> nothing needed */ - thread_master_free(master); + event_master_free(master); master = NULL; zlog_tls_buffer_fini(); zlog_fini(); /* frrmod_init -> nothing needed / hooks */ rcu_shutdown(); - if (!debug_memstats_at_exit) - return; - - have_leftovers = log_memstats(stderr, di->name); + /* also log memstats to stderr when stderr goes to a file*/ + if (debug_memstats_at_exit || !isatty(STDERR_FILENO)) + have_leftovers = log_memstats(stderr, di->name); /* in case we decide at runtime that we want exit-memstats for - * a daemon, but it has no stderr because it's daemonized + * a daemon * (only do this if we actually have something to print though) */ - if (!have_leftovers) + if (!debug_memstats_at_exit || !have_leftovers) return; snprintf(filename, sizeof(filename), "/tmp/frr-memstats-%s-%llu-%llu", diff --git a/lib/libfrr.h b/lib/libfrr.h index 97e9b93c101e..b260a54dfeb1 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -11,7 +11,7 @@ #include "typesafe.h" #include "sigevent.h" #include "privs.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "getopt.h" #include "module.h" @@ -39,6 +39,11 @@ extern "C" { * Does nothing if -d isn't used. */ #define FRR_DETACH_LATER (1 << 6) +/* If FRR_MANUAL_VTY_START is used, frr_run() will not automatically start + * listening on for vty connection (either TCP or Unix socket based). The daemon + * is responsible for calling frr_vty_serv() itself. + */ +#define FRR_MANUAL_VTY_START (1 << 7) PREDECL_DLIST(log_args); struct log_arg { @@ -70,7 +75,7 @@ struct frr_daemon_info { bool terminal; enum frr_cli_mode cli_mode; - struct thread *read_in; + struct event *read_in; const char *config_file; const char *backup_config_file; const char *pid_file; @@ -133,23 +138,25 @@ extern int frr_getopt(int argc, char *const argv[], int *longindex); extern __attribute__((__noreturn__)) void frr_help_exit(int status); -extern struct thread_master *frr_init(void); +extern struct event_loop *frr_init(void); extern const char *frr_get_progname(void); extern enum frr_cli_mode frr_get_cli_mode(void); extern uint32_t frr_get_fd_limit(void); extern bool frr_is_startup_fd(int fd); /* call order of these hooks is as ordered here */ -DECLARE_HOOK(frr_early_init, (struct thread_master * tm), (tm)); -DECLARE_HOOK(frr_late_init, (struct thread_master * tm), (tm)); +DECLARE_HOOK(frr_early_init, (struct event_loop * tm), (tm)); +DECLARE_HOOK(frr_late_init, (struct event_loop * tm), (tm)); /* fork() happens between late_init and config_pre */ -DECLARE_HOOK(frr_config_pre, (struct thread_master * tm), (tm)); -DECLARE_HOOK(frr_config_post, (struct thread_master * tm), (tm)); +DECLARE_HOOK(frr_config_pre, (struct event_loop * tm), (tm)); +DECLARE_HOOK(frr_config_post, (struct event_loop * tm), (tm)); extern void frr_config_fork(void); -extern void frr_run(struct thread_master *master); +extern void frr_run(struct event_loop *master); extern void frr_detach(void); +extern void frr_vty_serv_start(void); +extern void frr_vty_serv_stop(void); extern bool frr_zclient_addr(struct sockaddr_storage *sa, socklen_t *sa_len, const char *path); diff --git a/lib/libfrr_trace.h b/lib/libfrr_trace.h index 92c469706a5c..05c958fa42cd 100644 --- a/lib/libfrr_trace.h +++ b/lib/libfrr_trace.h @@ -21,7 +21,7 @@ #include <lttng/tracepoint.h> #include "hash.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "table.h" @@ -73,8 +73,8 @@ TRACEPOINT_EVENT( TRACEPOINT_LOGLEVEL(frr_libfrr, hash_release, TRACE_INFO) #define THREAD_SCHEDULE_ARGS \ - TP_ARGS(struct thread_master *, master, const char *, funcname, \ - const char *, schedfrom, int, fromln, struct thread **, \ + TP_ARGS(struct event_loop *, master, const char *, funcname, \ + const char *, schedfrom, int, fromln, struct event **, \ thread_ptr, int, fd, int, val, void *, arg, long, time) TRACEPOINT_EVENT_CLASS( @@ -103,9 +103,9 @@ THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_timer) THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_event) THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_read) THREAD_OPERATION_TRACEPOINT_INSTANCE(schedule_write) -THREAD_OPERATION_TRACEPOINT_INSTANCE(thread_cancel) -THREAD_OPERATION_TRACEPOINT_INSTANCE(thread_cancel_async) -THREAD_OPERATION_TRACEPOINT_INSTANCE(thread_call) +THREAD_OPERATION_TRACEPOINT_INSTANCE(event_cancel) +THREAD_OPERATION_TRACEPOINT_INSTANCE(event_cancel_async) +THREAD_OPERATION_TRACEPOINT_INSTANCE(event_call) TRACEPOINT_EVENT( frr_libfrr, diff --git a/lib/libospf.h b/lib/libospf.h index 3262534de59e..e3c1adb810f2 100644 --- a/lib/libospf.h +++ b/lib/libospf.h @@ -55,6 +55,7 @@ extern "C" { #define OSPF_ROUTER_DEAD_INTERVAL_DEFAULT 40 #define OSPF_ROUTER_DEAD_INTERVAL_MINIMAL 1 #define OSPF_HELLO_INTERVAL_DEFAULT 10 +#define OSPF_HELLO_DELAY_DEFAULT 10 #define OSPF_ROUTER_PRIORITY_DEFAULT 1 #define OSPF_RETRANSMIT_INTERVAL_DEFAULT 5 #define OSPF_TRANSMIT_DELAY_DEFAULT 1 @@ -67,6 +68,8 @@ extern "C" { #define OSPF_MTU_IGNORE_DEFAULT 0 #define OSPF_FAST_HELLO_DEFAULT 0 +#define OSPF_P2MP_DELAY_REFLOOD_DEFAULT false +#define OSPF_OPAQUE_CAPABLE_DEFAULT true #define OSPF_AREA_BACKBONE 0x00000000 /* 0.0.0.0 */ #define OSPF_AREA_RANGE_COST_UNSPEC -1U diff --git a/lib/link_state.c b/lib/link_state.c index 589c0ae704c4..6537f881ce15 100644 --- a/lib/link_state.c +++ b/lib/link_state.c @@ -26,6 +26,7 @@ #include "printfrr.h" #include <lib/json.h> #include "link_state.h" +#include "iso.h" /* Link State Memory allocation */ DEFINE_MTYPE_STATIC(LIB, LS_DB, "Link State Database"); @@ -333,7 +334,7 @@ int ls_attributes_same(struct ls_attributes *l1, struct ls_attributes *l2) /** * Link State prefix management functions */ -struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) +struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p) { struct ls_prefix *new; @@ -342,7 +343,7 @@ struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p) new = XCALLOC(MTYPE_LS_DB, sizeof(struct ls_prefix)); new->adv = adv; - new->pref = p; + new->pref = *p; return new; } @@ -496,7 +497,6 @@ void ls_vertex_del(struct ls_ted *ted, struct ls_vertex *vertex) /* Then remove Vertex from Link State Data Base and free memory */ vertices_del(&ted->vertices, vertex); XFREE(MTYPE_LS_DB, vertex); - vertex = NULL; } void ls_vertex_del_all(struct ls_ted *ted, struct ls_vertex *vertex) @@ -673,9 +673,9 @@ static void ls_edge_connect_to(struct ls_ted *ted, struct ls_edge *edge) } } -static uint64_t get_edge_key(struct ls_attributes *attr, bool dst) +static struct ls_edge_key get_edge_key(struct ls_attributes *attr, bool dst) { - uint64_t key = 0; + struct ls_edge_key key = {.family = AF_UNSPEC}; struct ls_standard *std; if (!attr) @@ -684,30 +684,37 @@ static uint64_t get_edge_key(struct ls_attributes *attr, bool dst) std = &attr->standard; if (dst) { - /* Key is the IPv4 remote address */ - if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) - key = ((uint64_t)ntohl(std->remote.s_addr)) - & 0xffffffff; - /* or the 64 bits LSB of IPv6 remote address */ - else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) - key = ((uint64_t)ntohl(std->remote6.s6_addr32[2]) << 32 - | (uint64_t)ntohl(std->remote6.s6_addr32[3])); - /* of remote identifier if no IP addresses are defined */ - else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) - key = (((uint64_t)std->remote_id) & 0xffffffff) - | ((uint64_t)std->local_id << 32); + if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR)) { + /* Key is the IPv4 remote address */ + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &std->remote); + } else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ADDR6)) { + /* or the IPv6 remote address */ + key.family = AF_INET6; + IPV6_ADDR_COPY(&key.k.addr6, &std->remote6); + } else if (CHECK_FLAG(attr->flags, LS_ATTR_NEIGH_ID)) { + /* or Remote identifier if IP addr. are not defined */ + key.family = AF_LOCAL; + key.k.link_id = + (((uint64_t)std->remote_id) & 0xffffffff) | + ((uint64_t)std->local_id << 32); + } } else { - /* Key is the IPv4 local address */ - if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) - key = ((uint64_t)ntohl(std->local.s_addr)) & 0xffffffff; - /* or the 64 bits LSB of IPv6 local address */ - else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) - key = ((uint64_t)ntohl(std->local6.s6_addr32[2]) << 32 - | (uint64_t)ntohl(std->local6.s6_addr32[3])); - /* of local identifier if no IP addresses are defined */ - else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) - key = (((uint64_t)std->local_id) & 0xffffffff) - | ((uint64_t)std->remote_id << 32); + if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) { + /* Key is the IPv4 local address */ + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &std->local); + } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) { + /* or the 64 bits LSB of IPv6 local address */ + key.family = AF_INET6; + IPV6_ADDR_COPY(&key.k.addr6, &std->local6); + } else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ID)) { + /* or Remote identifier if IP addr. are not defined */ + key.family = AF_LOCAL; + key.k.link_id = + (((uint64_t)std->local_id) & 0xffffffff) | + ((uint64_t)std->remote_id << 32); + } } return key; @@ -717,13 +724,13 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted, struct ls_attributes *attributes) { struct ls_edge *new; - uint64_t key = 0; + struct ls_edge_key key; if (attributes == NULL) return NULL; key = get_edge_key(attributes, false); - if (key == 0) + if (key.family == AF_UNSPEC) return NULL; /* Create Edge and add it to the TED */ @@ -741,11 +748,12 @@ struct ls_edge *ls_edge_add(struct ls_ted *ted, return new; } -struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, const uint64_t key) +struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, + const struct ls_edge_key key) { struct ls_edge edge = {}; - if (key == 0) + if (key.family == AF_UNSPEC) return NULL; edge.key = key; @@ -761,7 +769,7 @@ struct ls_edge *ls_find_edge_by_source(struct ls_ted *ted, return NULL; edge.key = get_edge_key(attributes, false); - if (edge.key == 0) + if (edge.key.family == AF_UNSPEC) return NULL; return edges_find(&ted->edges, &edge); @@ -776,7 +784,7 @@ struct ls_edge *ls_find_edge_by_destination(struct ls_ted *ted, return NULL; edge.key = get_edge_key(attributes, true); - if (edge.key == 0) + if (edge.key.family == AF_UNSPEC) return NULL; return edges_find(&ted->edges, &edge); @@ -814,7 +822,7 @@ int ls_edge_same(struct ls_edge *e1, struct ls_edge *e2) if (!e1 && !e2) return 1; - if (e1->key != e2->key) + if (edge_cmp(e1, e2) != 0) return 0; if (e1->attributes == e2->attributes) @@ -889,7 +897,7 @@ struct ls_subnet *ls_subnet_update(struct ls_ted *ted, struct ls_prefix *pref) if (pref == NULL) return NULL; - old = ls_find_subnet(ted, pref->pref); + old = ls_find_subnet(ted, &pref->pref); if (old) { if (!ls_prefix_same(old->ls_pref, pref)) { ls_prefix_del(old->ls_pref); @@ -942,11 +950,15 @@ void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet) ls_subnet_del(ted, subnet); } -struct ls_subnet *ls_find_subnet(struct ls_ted *ted, const struct prefix prefix) +struct ls_subnet *ls_find_subnet(struct ls_ted *ted, + const struct prefix *prefix) { struct ls_subnet subnet = {}; - subnet.key = prefix; + if (!prefix) + return NULL; + + prefix_copy(&subnet.key, prefix); return subnets_find(&ted->subnets, &subnet); } @@ -1126,31 +1138,13 @@ int ls_unregister(struct zclient *zclient, bool server) int ls_request_sync(struct zclient *zclient) { - struct stream *s; - uint16_t flags = 0; - /* Check buffer size */ if (STREAM_SIZE(zclient->obuf) < (ZEBRA_HEADER_SIZE + 3 * sizeof(uint32_t))) return -1; - s = zclient->obuf; - stream_reset(s); - - zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); - - /* Set type and flags */ - stream_putl(s, LINK_STATE_SYNC); - stream_putw(s, flags); - /* Send destination client info */ - stream_putc(s, zclient->redist_default); - stream_putw(s, zclient->instance); - stream_putl(s, zclient->session_id); - - /* Put length into the header at the start of the stream. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - return zclient_send_message(zclient); + /* No data with this message */ + return zclient_send_opaque(zclient, LINK_STATE_SYNC, NULL, 0); } static struct ls_node *ls_parse_node(struct stream *s) @@ -1611,23 +1605,15 @@ int ls_send_msg(struct zclient *zclient, struct ls_message *msg, (ZEBRA_HEADER_SIZE + sizeof(uint32_t) + sizeof(msg))) return -1; + /* Init the message, then encode the data inline. */ + if (dst == NULL) + zapi_opaque_init(zclient, LINK_STATE_UPDATE, flags); + else + zapi_opaque_unicast_init(zclient, LINK_STATE_UPDATE, flags, + dst->proto, dst->instance, + dst->session_id); + s = zclient->obuf; - stream_reset(s); - - zclient_create_header(s, ZEBRA_OPAQUE_MESSAGE, VRF_DEFAULT); - - /* Set sub-type, flags and destination for unicast message */ - stream_putl(s, LINK_STATE_UPDATE); - if (dst != NULL) { - SET_FLAG(flags, ZAPI_OPAQUE_FLAG_UNICAST); - stream_putw(s, flags); - /* Send destination client info */ - stream_putc(s, dst->proto); - stream_putw(s, dst->instance); - stream_putl(s, dst->session_id); - } else { - stream_putw(s, flags); - } /* Format Link State message */ if (ls_format_msg(s, msg) < 0) { @@ -1769,9 +1755,10 @@ struct ls_vertex *ls_msg2vertex(struct ls_ted *ted, struct ls_message *msg, case LS_MSG_EVENT_DELETE: vertex = ls_find_vertex_by_id(ted, node->adv); if (vertex) { - if (delete) + if (delete) { ls_vertex_del_all(ted, vertex); - else + vertex = NULL; + } else vertex->status = DELETE; } break; @@ -1846,11 +1833,12 @@ struct ls_subnet *ls_msg2subnet(struct ls_ted *ted, struct ls_message *msg, subnet->status = UPDATE; break; case LS_MSG_EVENT_DELETE: - subnet = ls_find_subnet(ted, pref->pref); + subnet = ls_find_subnet(ted, &pref->pref); if (subnet) { - if (delete) + if (delete) { ls_subnet_del_all(ted, subnet); - else + subnet = NULL; + } else subnet->status = DELETE; } break; @@ -1905,6 +1893,20 @@ void ls_delete_msg(struct ls_message *msg) if (msg == NULL) return; + if (msg->event == LS_MSG_EVENT_DELETE) { + switch (msg->type) { + case LS_MSG_TYPE_NODE: + ls_node_del(msg->data.node); + break; + case LS_MSG_TYPE_ATTRIBUTES: + ls_attributes_del(msg->data.attr); + break; + case LS_MSG_TYPE_PREFIX: + ls_prefix_del(msg->data.prefix); + break; + } + } + XFREE(MTYPE_LS_DB, msg); } @@ -1965,13 +1967,9 @@ static const char *const status2txt[] = { static const char *ls_node_id_to_text(struct ls_node_id lnid, char *str, size_t size) { - if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) { - uint8_t *id; - - id = lnid.id.iso.sys_id; - snprintfrr(str, size, "%02x%02x.%02x%02x.%02x%02x", id[0], - id[1], id[2], id[3], id[4], id[5]); - } else + if (lnid.origin == ISIS_L1 || lnid.origin == ISIS_L2) + snprintfrr(str, size, "%pSY", lnid.id.iso.sys_id); + else snprintfrr(str, size, "%pI4", &lnid.id.ip.addr); return str; @@ -2177,6 +2175,34 @@ void ls_show_vertices(struct ls_ted *ted, struct vty *vty, } } +static const char *edge_key_to_text(struct ls_edge_key key) +{ +#define FORMAT_BUF_COUNT 4 + static char buf_ring[FORMAT_BUF_COUNT][INET6_BUFSIZ]; + static size_t cur_buf = 0; + char *rv; + + rv = buf_ring[cur_buf]; + cur_buf = (cur_buf + 1) % FORMAT_BUF_COUNT; + + switch (key.family) { + case AF_INET: + snprintfrr(rv, INET6_BUFSIZ, "%pI4", &key.k.addr); + break; + case AF_INET6: + snprintfrr(rv, INET6_BUFSIZ, "%pI6", &key.k.addr6); + break; + case AF_LOCAL: + snprintfrr(rv, INET6_BUFSIZ, "%" PRIu64, key.k.link_id); + break; + default: + snprintfrr(rv, INET6_BUFSIZ, "(Unknown)"); + break; + } + + return rv; +} + static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty, bool verbose) { @@ -2189,7 +2215,7 @@ static void ls_show_edge_vty(struct ls_edge *edge, struct vty *vty, attr = edge->attributes; sbuf_init(&sbuf, NULL, 0); - sbuf_push(&sbuf, 2, "Edge (%" PRIu64 "): ", edge->key); + sbuf_push(&sbuf, 2, "Edge (%s): ", edge_key_to_text(edge->key)); if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR)) sbuf_push(&sbuf, 0, "%pI4", &attr->standard.local); else if (CHECK_FLAG(attr->flags, LS_ATTR_LOCAL_ADDR6)) @@ -2348,7 +2374,7 @@ static void ls_show_edge_json(struct ls_edge *edge, struct json_object *json) attr = edge->attributes; - json_object_int_add(json, "edge-id", edge->key); + json_object_string_add(json, "edge-id", edge_key_to_text(edge->key)); json_object_string_add(json, "status", status2txt[edge->status]); json_object_string_add(json, "origin", origin2txt[attr->adv.origin]); ls_node_id_to_text(attr->adv, buf, INET6_BUFSIZ); @@ -2726,8 +2752,8 @@ void ls_dump_ted(struct ls_ted *ted) for (ALL_LIST_ELEMENTS_RO(vertex->incoming_edges, lst_node, vertex_edge)) { zlog_debug( - " inc edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)", - vertex_edge->key, + " inc edge key:%s attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + edge_key_to_text(vertex_edge->key), &vertex_edge->attributes->adv.id.ip.addr, &vertex_edge->attributes->standard.local, &vertex_edge->attributes->standard.remote); @@ -2735,15 +2761,16 @@ void ls_dump_ted(struct ls_ted *ted) for (ALL_LIST_ELEMENTS_RO(vertex->outgoing_edges, lst_node, vertex_edge)) { zlog_debug( - " out edge key:%" PRIu64 " attr key:%pI4 loc:(%pI4) rmt:(%pI4)", - vertex_edge->key, + " out edge key:%s attr key:%pI4 loc:(%pI4) rmt:(%pI4)", + edge_key_to_text(vertex_edge->key), &vertex_edge->attributes->adv.id.ip.addr, &vertex_edge->attributes->standard.local, &vertex_edge->attributes->standard.remote); } } frr_each (edges, &ted->edges, edge) { - zlog_debug(" Ted edge key:%" PRIu64 "src:%pI4 dst:%pI4", edge->key, + zlog_debug(" Ted edge key:%s src:%pI4 dst:%pI4", + edge_key_to_text(edge->key), edge->source ? &edge->source->node->router_id : &inaddr_any, edge->destination diff --git a/lib/link_state.h b/lib/link_state.h index e6a6388ba413..d3a0ce39da99 100644 --- a/lib/link_state.h +++ b/lib/link_state.h @@ -92,6 +92,9 @@ struct ls_node_id { */ extern int ls_node_id_same(struct ls_node_id i1, struct ls_node_id i2); +/* Supported number of algorithm by the link-state library */ +#define LIB_LS_SR_ALGO_COUNT 2 + /* Link State flags to indicate which Node parameters are valid */ #define LS_NODE_UNSET 0x0000 #define LS_NODE_NAME 0x0001 @@ -123,7 +126,7 @@ struct ls_node { uint32_t lower_bound; /* MPLS label lower bound */ uint32_t range_size; /* MPLS label range size */ } srlb; - uint8_t algo[2]; /* Segment Routing Algorithms */ + uint8_t algo[LIB_LS_SR_ALGO_COUNT]; /* Segment Routing Algorithms */ uint8_t msd; /* Maximum Stack Depth */ }; @@ -314,7 +317,7 @@ extern int ls_attributes_same(struct ls_attributes *a1, * * @return New Link State Prefix */ -extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix p); +extern struct ls_prefix *ls_prefix_new(struct ls_node_id adv, struct prefix *p); /** * Remove Link State Prefix. Data Structure is freed. @@ -382,13 +385,23 @@ struct ls_vertex { struct list *prefixes; /* List of advertised prefix */ }; +/* Link State Edge Key structure */ +struct ls_edge_key { + uint8_t family; + union { + struct in_addr addr; + struct in6_addr addr6; + uint64_t link_id; + } k; +}; + /* Link State Edge structure */ PREDECL_RBTREE_UNIQ(edges); struct ls_edge { enum ls_type type; /* Link State Type */ enum ls_status status; /* Status of the Edge in the TED */ struct edges_item entry; /* Entry in RB tree */ - uint64_t key; /* Unique Key identifier */ + struct ls_edge_key key; /* Unique Key identifier */ struct ls_attributes *attributes; /* Link State attributes */ struct ls_vertex *source; /* Pointer to the source Vertex */ struct ls_vertex *destination; /* Pointer to the destination Vertex */ @@ -416,13 +429,25 @@ DECLARE_RBTREE_UNIQ(vertices, struct ls_vertex, entry, vertex_cmp); macro_inline int edge_cmp(const struct ls_edge *edge1, const struct ls_edge *edge2) { - return numcmp(edge1->key, edge2->key); + if (edge1->key.family != edge2->key.family) + return numcmp(edge1->key.family, edge2->key.family); + + switch (edge1->key.family) { + case AF_INET: + return memcmp(&edge1->key.k.addr, &edge2->key.k.addr, 4); + case AF_INET6: + return memcmp(&edge1->key.k.addr6, &edge2->key.k.addr6, 16); + case AF_LOCAL: + return numcmp(edge1->key.k.link_id, edge2->key.k.link_id); + default: + return 0; + } } DECLARE_RBTREE_UNIQ(edges, struct ls_edge, entry, edge_cmp); /* * Prefix comparison are done to the host part so, 10.0.0.1/24 - * and 10.0.0.2/24 are considered come different + * and 10.0.0.2/24 are considered different */ macro_inline int subnet_cmp(const struct ls_subnet *a, const struct ls_subnet *b) @@ -619,7 +644,7 @@ extern void ls_edge_del_all(struct ls_ted *ted, struct ls_edge *edge); * @return Edge if found, NULL otherwise */ extern struct ls_edge *ls_find_edge_by_key(struct ls_ted *ted, - const uint64_t key); + const struct ls_edge_key key); /** * Find Edge in the Link State Data Base by the source (local IPv4 or IPv6 @@ -709,7 +734,7 @@ extern void ls_subnet_del_all(struct ls_ted *ted, struct ls_subnet *subnet); * @return Subnet if found, NULL otherwise */ extern struct ls_subnet *ls_find_subnet(struct ls_ted *ted, - const struct prefix prefix); + const struct prefix *prefix); /** * Create a new Link State Data Base. diff --git a/lib/log.c b/lib/log.c index b1a06bfb26bb..df9b6c717633 100644 --- a/lib/log.c +++ b/lib/log.c @@ -140,7 +140,7 @@ void zlog_signal(int signo, const char *action, void *siginfo_v, fb.pos = buf; - struct thread *tc; + struct event *tc; tc = pthread_getspecific(thread_current); if (!tc) @@ -284,7 +284,7 @@ void zlog_backtrace(int priority) void zlog_thread_info(int log_level) { - struct thread *tc; + struct event *tc; tc = pthread_getspecific(thread_current); if (tc) @@ -457,7 +457,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_TC_CLASS_ADD), DESC_ENTRY(ZEBRA_TC_CLASS_DELETE), DESC_ENTRY(ZEBRA_TC_FILTER_ADD), - DESC_ENTRY(ZEBRA_TC_FILTER_DELETE)}; + DESC_ENTRY(ZEBRA_TC_FILTER_DELETE), + DESC_ENTRY(ZEBRA_OPAQUE_NOTIFY) +}; #undef DESC_ENTRY static const struct zebra_desc_table unknown = {0, "unknown", '?'}; @@ -506,6 +508,26 @@ const char *zserv_command_string(unsigned int command) return command_types[command].string; } +#define DESC_ENTRY(T) [(T)] = {(T), (#T), '\0'} +static const struct zebra_desc_table gr_client_cap_types[] = { + DESC_ENTRY(ZEBRA_CLIENT_GR_CAPABILITIES), + DESC_ENTRY(ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE), + DESC_ENTRY(ZEBRA_CLIENT_ROUTE_UPDATE_PENDING), + DESC_ENTRY(ZEBRA_CLIENT_GR_DISABLE), + DESC_ENTRY(ZEBRA_CLIENT_RIB_STALE_TIME), +}; +#undef DESC_ENTRY + +const char *zserv_gr_client_cap_string(uint32_t zcc) +{ + if (zcc >= array_size(gr_client_cap_types)) { + flog_err(EC_LIB_DEVELOPMENT, "unknown zserv command type: %u", + zcc); + return unknown.string; + } + return gr_client_cap_types[zcc].string; +} + int proto_name2num(const char *s) { unsigned i; diff --git a/lib/log.h b/lib/log.h index b8452ac215a8..8a95b7a005fd 100644 --- a/lib/log.h +++ b/lib/log.h @@ -113,6 +113,7 @@ extern int proto_name2num(const char *s); extern int proto_redistnum(int afi, const char *s); extern const char *zserv_command_string(unsigned int command); +extern const char *zserv_gr_client_cap_string(unsigned int zcc); #define OSPF_LOG(level, cond, fmt, ...) \ do { \ diff --git a/lib/log_vty.c b/lib/log_vty.c index fc28ffc6faf4..26e608d16b9b 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -34,18 +34,23 @@ static int log_cmdline_syslog_lvl = ZLOG_DISABLED; static struct zlog_cfg_file zt_file_cmdline = { .prio_min = ZLOG_DISABLED, + .ts_subsec = LOG_TIMESTAMP_PRECISION, }; static struct zlog_cfg_file zt_file = { .prio_min = ZLOG_DISABLED, + .ts_subsec = LOG_TIMESTAMP_PRECISION, }; static struct zlog_cfg_filterfile zt_filterfile = { - .parent = { - .prio_min = ZLOG_DISABLED, - }, + .parent = + { + .prio_min = ZLOG_DISABLED, + .ts_subsec = LOG_TIMESTAMP_PRECISION, + }, }; static struct zlog_cfg_file zt_stdout_file = { .prio_min = ZLOG_DISABLED, + .ts_subsec = LOG_TIMESTAMP_PRECISION, }; static struct zlog_cfg_5424 zt_stdout_journald = { .prio_min = ZLOG_DISABLED, diff --git a/lib/mgmt.proto b/lib/mgmt.proto new file mode 100644 index 000000000000..9e4b39abe4d2 --- /dev/null +++ b/lib/mgmt.proto @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: ISC +// +// mgmt.proto +// +// @copyright Copyright (C) 2021 Vmware, Inc. +// +// @author Pushpasis Sarkar <spushpasis@vmware.com> +// + +syntax = "proto2"; + +// +// Protobuf definitions pertaining to the MGMTD component. +// + +package mgmtd; + +// +// Common Sub-Messages +// + +message YangDataXPath { + required string xpath = 1; +} + +message YangDataValue { + oneof value { + // + // NOTE: For now let's use stringized value ONLY. + // We will enhance it later to pass native-format + // if needed. + // + // bool bool_val = 2; + // double double_val = 3; + // float float_val = 4; + // string string_val = 5; + // bytes bytes_val = 6; + // int32 int32_val = 7; + // int64 int64_val = 8; + // uint32 uint32_val = 9; + // uint64 uint64_val = 10; + // int32 int8_val = 11; + // uint32 uint8_val = 12; + // int32 int16_val = 13; + // uint32 uint16_val = 14; + string encoded_str_val = 100; + } +} + +message YangData { + required string xpath = 1; + optional YangDataValue value = 2; +} + +enum CfgDataReqType { + REQ_TYPE_NONE = 0; + SET_DATA = 1; + DELETE_DATA = 2; +} + +message YangCfgDataReq { + required YangData data = 1; + required CfgDataReqType req_type = 2; +} + +message YangGetDataReq { + required YangData data = 1; + required int64 next_indx = 2; +} + +// +// Backend Interface Messages +// +message BeSubscribeReq { + required string client_name = 1; + required bool subscribe_xpaths = 2; + repeated string xpath_reg = 3; +} + +message BeSubscribeReply { + required bool success = 1; +} + +message BeTxnReq { + required uint64 txn_id = 1; + required bool create = 2; +} + +message BeTxnReply { + required uint64 txn_id = 1; + required bool create = 2; + required bool success = 3; +} + +message BeCfgDataCreateReq { + required uint64 txn_id = 1; + required uint64 batch_id = 2; + repeated YangCfgDataReq data_req = 3; + required bool end_of_data = 4; +} + +message BeCfgDataCreateReply { + required uint64 txn_id = 1; + required uint64 batch_id = 2; + required bool success = 3; + optional string error_if_any = 4; +} + +message BeCfgDataApplyReq { + required uint64 txn_id = 1; +} + +message BeCfgDataApplyReply { + required uint64 txn_id = 1; + repeated uint64 batch_ids = 2; + required bool success = 3; + optional string error_if_any = 4; +} + +message BeOperDataGetReq { + required uint64 txn_id = 1; + required uint64 batch_id = 2; + repeated YangGetDataReq data = 3; +} + +message YangDataReply { + repeated YangData data = 1; + required int64 next_indx = 2; +} + +message BeOperDataGetReply { + required uint64 txn_id = 1; + required uint64 batch_id = 2; + required bool success = 3; + optional string error = 4; + optional YangDataReply data = 5; +} + +// +// Any message on the MGMTD Backend Interface. +// +message BeMessage { + oneof message { + BeSubscribeReq subscr_req = 2; + BeSubscribeReply subscr_reply = 3; + BeTxnReq txn_req = 4; + BeTxnReply txn_reply = 5; + BeCfgDataCreateReq cfg_data_req = 6; + BeCfgDataCreateReply cfg_data_reply = 7; + BeCfgDataApplyReq cfg_apply_req = 8; + BeCfgDataApplyReply cfg_apply_reply = 9; + BeOperDataGetReq get_req = 10; + BeOperDataGetReply get_reply = 11; + } +} + + +// +// Frontend Interface Messages +// + +message FeRegisterReq { + required string client_name = 1; +} + +message FeSessionReq { + required bool create = 1; + oneof id { + uint64 client_conn_id = 2; // Applicable for create request only + uint64 session_id = 3; // Applicable for delete request only + } +} + +message FeSessionReply { + required bool create = 1; + required bool success = 2; + optional uint64 client_conn_id = 3; // Applicable for create request only + required uint64 session_id = 4; +} + +enum DatastoreId { + DS_NONE = 0; + RUNNING_DS = 1; + CANDIDATE_DS = 2; + OPERATIONAL_DS = 3; + STARTUP_DS = 4; +} + +message FeLockDsReq { + required uint64 session_id = 1; + required uint64 req_id = 2; + required DatastoreId ds_id = 3; + required bool lock = 4; +} + +message FeLockDsReply { + required uint64 session_id = 1; + required uint64 req_id = 2; + required DatastoreId ds_id = 3; + required bool lock = 4; + required bool success = 5; + optional string error_if_any = 6; +} + +message FeSetConfigReq { + required uint64 session_id = 1; + required DatastoreId ds_id = 2; + required uint64 req_id = 3; + repeated YangCfgDataReq data = 4; + required bool implicit_commit = 5; + required DatastoreId commit_ds_id = 6; +} + +message FeSetConfigReply { + required uint64 session_id = 1; + required DatastoreId ds_id = 2; + required uint64 req_id = 3; + required bool success = 4; + required bool implicit_commit = 5; + optional string error_if_any = 6; +} + +message FeCommitConfigReq { + required uint64 session_id = 1; + required DatastoreId src_ds_id = 2; + required DatastoreId dst_ds_id = 3; + required uint64 req_id = 4; + required bool validate_only = 5; + required bool abort = 6; +} + +message FeCommitConfigReply { + required uint64 session_id = 1; + required DatastoreId src_ds_id = 2; + required DatastoreId dst_ds_id = 3; + required uint64 req_id = 4; + required bool validate_only = 5; + required bool success = 6; + required bool abort = 7; + optional string error_if_any = 8; +} + +message FeGetReq { + required uint64 session_id = 1; + required bool config = 2; + required DatastoreId ds_id = 3; + required uint64 req_id = 4; + repeated YangGetDataReq data = 5; +} + +message FeGetReply { + required uint64 session_id = 1; + required bool config = 2; + required DatastoreId ds_id = 3; + required uint64 req_id = 4; + required bool success = 5; + optional string error_if_any = 6; + optional YangDataReply data = 7; +} + +message FeNotifyDataReq { + repeated YangData data = 1; +} + +message FeRegisterNotifyReq { + required uint64 session_id = 1; + required DatastoreId ds_id = 2; + required bool register_req = 3; + required uint64 req_id = 4; + repeated YangDataXPath data_xpath = 5; +} + +message FeMessage { + oneof message { + FeRegisterReq register_req = 2; + FeSessionReq session_req = 3; + FeSessionReply session_reply = 4; + FeLockDsReq lockds_req = 5; + FeLockDsReply lockds_reply = 6; + FeSetConfigReq setcfg_req = 7; + FeSetConfigReply setcfg_reply = 8; + FeCommitConfigReq commcfg_req = 9; + FeCommitConfigReply commcfg_reply = 10; + FeGetReq get_req = 11; + FeGetReply get_reply = 12; + FeNotifyDataReq notify_data_req = 15; + FeRegisterNotifyReq regnotify_req = 16; + } +} diff --git a/lib/mgmt_be_client.c b/lib/mgmt_be_client.c new file mode 100644 index 000000000000..7bd9980357d5 --- /dev/null +++ b/lib/mgmt_be_client.c @@ -0,0 +1,985 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Backend Client Library api interfaces + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#include "debug.h" +#include "compiler.h" +#include "libfrr.h" +#include "mgmtd/mgmt.h" +#include "mgmt_be_client.h" +#include "mgmt_msg.h" +#include "mgmt_pb.h" +#include "network.h" +#include "northbound.h" +#include "stream.h" +#include "sockopt.h" + +#include "lib/mgmt_be_client_clippy.c" + +DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT, "backend client"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_CLIENT_NAME, "backend client name"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_BATCH, "backend transaction batch data"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_BE_TXN, "backend transaction data"); + +enum mgmt_be_txn_event { + MGMTD_BE_TXN_PROC_SETCFG = 1, + MGMTD_BE_TXN_PROC_GETCFG, + MGMTD_BE_TXN_PROC_GETDATA +}; + +struct mgmt_be_set_cfg_req { + struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + uint16_t num_cfg_changes; +}; + +struct mgmt_be_get_data_req { + char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH]; + uint16_t num_xpaths; +}; + +struct mgmt_be_txn_req { + enum mgmt_be_txn_event event; + union { + struct mgmt_be_set_cfg_req set_cfg; + struct mgmt_be_get_data_req get_data; + } req; +}; + +PREDECL_LIST(mgmt_be_batches); +struct mgmt_be_batch_ctx { + /* Batch-Id as assigned by MGMTD */ + uint64_t batch_id; + + struct mgmt_be_txn_req txn_req; + + uint32_t flags; + + struct mgmt_be_batches_item list_linkage; +}; +#define MGMTD_BE_BATCH_FLAGS_CFG_PREPARED (1U << 0) +#define MGMTD_BE_TXN_FLAGS_CFG_APPLIED (1U << 1) +DECLARE_LIST(mgmt_be_batches, struct mgmt_be_batch_ctx, list_linkage); + +PREDECL_LIST(mgmt_be_txns); +struct mgmt_be_txn_ctx { + /* Txn-Id as assigned by MGMTD */ + uint64_t txn_id; + uint32_t flags; + + struct mgmt_be_client_txn_ctx client_data; + struct mgmt_be_client *client; + + /* List of batches belonging to this transaction */ + struct mgmt_be_batches_head cfg_batches; + struct mgmt_be_batches_head apply_cfgs; + + struct mgmt_be_txns_item list_linkage; + + struct nb_transaction *nb_txn; + uint32_t nb_txn_id; +}; +#define MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED (1U << 1) + +DECLARE_LIST(mgmt_be_txns, struct mgmt_be_txn_ctx, list_linkage); + +#define FOREACH_BE_TXN_BATCH_IN_LIST(txn, batch) \ + frr_each_safe (mgmt_be_batches, &(txn)->cfg_batches, (batch)) + +#define FOREACH_BE_APPLY_BATCH_IN_LIST(txn, batch) \ + frr_each_safe (mgmt_be_batches, &(txn)->apply_cfgs, (batch)) + +struct mgmt_be_client { + struct msg_client client; + + char *name; + + struct nb_config *candidate_config; + struct nb_config *running_config; + + unsigned long num_edit_nb_cfg; + unsigned long avg_edit_nb_cfg_tm; + unsigned long num_prep_nb_cfg; + unsigned long avg_prep_nb_cfg_tm; + unsigned long num_apply_nb_cfg; + unsigned long avg_apply_nb_cfg_tm; + + struct mgmt_be_txns_head txn_head; + + struct mgmt_be_client_cbs cbs; + uintptr_t user_data; +}; + +#define FOREACH_BE_TXN_IN_LIST(client_ctx, txn) \ + frr_each_safe (mgmt_be_txns, &(client_ctx)->txn_head, (txn)) + +struct debug mgmt_dbg_be_client = {0, "Management backend client operations"}; + +const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1] = { +#ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = "staticd", +#endif + [MGMTD_BE_CLIENT_ID_MAX] = "Unknown/Invalid", +}; + +static int mgmt_be_client_send_msg(struct mgmt_be_client *client_ctx, + Mgmtd__BeMessage *be_msg) +{ + return msg_conn_send_msg( + &client_ctx->client.conn, MGMT_MSG_VERSION_PROTOBUF, be_msg, + mgmtd__be_message__get_packed_size(be_msg), + (size_t(*)(void *, void *))mgmtd__be_message__pack, false); +} + +static struct mgmt_be_batch_ctx * +mgmt_be_find_batch_by_id(struct mgmt_be_txn_ctx *txn, + uint64_t batch_id) +{ + struct mgmt_be_batch_ctx *batch = NULL; + + FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { + if (batch->batch_id == batch_id) + return batch; + } + + return NULL; +} + +static struct mgmt_be_batch_ctx * +mgmt_be_batch_create(struct mgmt_be_txn_ctx *txn, uint64_t batch_id) +{ + struct mgmt_be_batch_ctx *batch = NULL; + + batch = mgmt_be_find_batch_by_id(txn, batch_id); + if (!batch) { + batch = XCALLOC(MTYPE_MGMTD_BE_BATCH, + sizeof(struct mgmt_be_batch_ctx)); + assert(batch); + + batch->batch_id = batch_id; + mgmt_be_batches_add_tail(&txn->cfg_batches, batch); + + MGMTD_BE_CLIENT_DBG("Added new batch-id: %" PRIu64 + " to transaction", + batch_id); + } + + return batch; +} + +static void mgmt_be_batch_delete(struct mgmt_be_txn_ctx *txn, + struct mgmt_be_batch_ctx **batch) +{ + uint16_t indx; + + if (!batch) + return; + + mgmt_be_batches_del(&txn->cfg_batches, *batch); + if ((*batch)->txn_req.event == MGMTD_BE_TXN_PROC_SETCFG) { + for (indx = 0; indx < MGMTD_MAX_CFG_CHANGES_IN_BATCH; indx++) { + if ((*batch)->txn_req.req.set_cfg.cfg_changes[indx] + .value) { + free((char *)(*batch) + ->txn_req.req.set_cfg + .cfg_changes[indx] + .value); + } + } + } + + XFREE(MTYPE_MGMTD_BE_BATCH, *batch); + *batch = NULL; +} + +static void mgmt_be_cleanup_all_batches(struct mgmt_be_txn_ctx *txn) +{ + struct mgmt_be_batch_ctx *batch = NULL; + + FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { + mgmt_be_batch_delete(txn, &batch); + } + + FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) { + mgmt_be_batch_delete(txn, &batch); + } +} + +static struct mgmt_be_txn_ctx * +mgmt_be_find_txn_by_id(struct mgmt_be_client *client_ctx, uint64_t txn_id, + bool warn) +{ + struct mgmt_be_txn_ctx *txn = NULL; + + FOREACH_BE_TXN_IN_LIST (client_ctx, txn) + if (txn->txn_id == txn_id) + return txn; + if (warn) + MGMTD_BE_CLIENT_ERR("Unknown txn-id: %" PRIu64, txn_id); + + return NULL; +} + +static struct mgmt_be_txn_ctx * +mgmt_be_txn_create(struct mgmt_be_client *client_ctx, uint64_t txn_id) +{ + struct mgmt_be_txn_ctx *txn = NULL; + + txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false); + if (txn) { + MGMTD_BE_CLIENT_ERR("Can't create existing txn-id: %" PRIu64, + txn_id); + return NULL; + } + + txn = XCALLOC(MTYPE_MGMTD_BE_TXN, sizeof(struct mgmt_be_txn_ctx)); + txn->txn_id = txn_id; + txn->client = client_ctx; + mgmt_be_batches_init(&txn->cfg_batches); + mgmt_be_batches_init(&txn->apply_cfgs); + mgmt_be_txns_add_tail(&client_ctx->txn_head, txn); + + MGMTD_BE_CLIENT_DBG("Created new txn-id: %" PRIu64, txn_id); + + return txn; +} + +static void mgmt_be_txn_delete(struct mgmt_be_client *client_ctx, + struct mgmt_be_txn_ctx **txn) +{ + char err_msg[] = "MGMT Transaction Delete"; + + if (!txn) + return; + + /* + * Remove the transaction from the list of transactions + * so that future lookups with the same transaction id + * does not return this one. + */ + mgmt_be_txns_del(&client_ctx->txn_head, *txn); + + /* + * Time to delete the transaction which should also + * take care of cleaning up all batches created via + * CFGDATA_CREATE_REQs. But first notify the client + * about the transaction delete. + */ + if (client_ctx->cbs.txn_notify) + (void)(*client_ctx->cbs.txn_notify)(client_ctx, + client_ctx->user_data, + &(*txn)->client_data, true); + + mgmt_be_cleanup_all_batches(*txn); + if ((*txn)->nb_txn) + nb_candidate_commit_abort((*txn)->nb_txn, err_msg, + sizeof(err_msg)); + XFREE(MTYPE_MGMTD_BE_TXN, *txn); + + *txn = NULL; +} + +static void mgmt_be_cleanup_all_txns(struct mgmt_be_client *client_ctx) +{ + struct mgmt_be_txn_ctx *txn = NULL; + + FOREACH_BE_TXN_IN_LIST (client_ctx, txn) { + mgmt_be_txn_delete(client_ctx, &txn); + } +} + +static int mgmt_be_send_txn_reply(struct mgmt_be_client *client_ctx, + uint64_t txn_id, bool create) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeTxnReply txn_reply; + + mgmtd__be_txn_reply__init(&txn_reply); + txn_reply.create = create; + txn_reply.txn_id = txn_id; + txn_reply.success = true; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY; + be_msg.txn_reply = &txn_reply; + + MGMTD_BE_CLIENT_DBG("Sending TXN_REPLY txn-id %" PRIu64, txn_id); + + return mgmt_be_client_send_msg(client_ctx, &be_msg); +} + +static int mgmt_be_process_txn_req(struct mgmt_be_client *client_ctx, + uint64_t txn_id, bool create) +{ + struct mgmt_be_txn_ctx *txn; + + if (create) { + MGMTD_BE_CLIENT_DBG("Creating new txn-id %" PRIu64, txn_id); + + txn = mgmt_be_txn_create(client_ctx, txn_id); + if (!txn) + goto failed; + + if (client_ctx->cbs.txn_notify) + (*client_ctx->cbs.txn_notify)(client_ctx, + client_ctx->user_data, + &txn->client_data, false); + } else { + MGMTD_BE_CLIENT_DBG("Deleting txn-id: %" PRIu64, txn_id); + txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, false); + if (txn) + mgmt_be_txn_delete(client_ctx, &txn); + } + + return mgmt_be_send_txn_reply(client_ctx, txn_id, create); + +failed: + msg_conn_disconnect(&client_ctx->client.conn, true); + return -1; +} + +static int mgmt_be_send_cfgdata_create_reply(struct mgmt_be_client *client_ctx, + uint64_t txn_id, uint64_t batch_id, + bool success, + const char *error_if_any) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeCfgDataCreateReply cfgdata_reply; + + mgmtd__be_cfg_data_create_reply__init(&cfgdata_reply); + cfgdata_reply.txn_id = (uint64_t)txn_id; + cfgdata_reply.batch_id = (uint64_t)batch_id; + cfgdata_reply.success = success; + if (error_if_any) + cfgdata_reply.error_if_any = (char *)error_if_any; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY; + be_msg.cfg_data_reply = &cfgdata_reply; + + MGMTD_BE_CLIENT_DBG("Sending CFGDATA_CREATE_REPLY txn-id: %" PRIu64 + " batch-id: %" PRIu64, + txn_id, batch_id); + + return mgmt_be_client_send_msg(client_ctx, &be_msg); +} + +static void mgmt_be_txn_cfg_abort(struct mgmt_be_txn_ctx *txn) +{ + char errmsg[BUFSIZ] = {0}; + + assert(txn && txn->client); + if (txn->nb_txn) { + MGMTD_BE_CLIENT_ERR( + "Aborting configs after prep for txn-id: %" PRIu64, + txn->txn_id); + nb_candidate_commit_abort(txn->nb_txn, errmsg, sizeof(errmsg)); + txn->nb_txn = 0; + } + + /* + * revert candidate back to running + * + * This is one txn ctx but the candidate_config is per client ctx, how + * does that work? + */ + MGMTD_BE_CLIENT_DBG( + "Reset candidate configurations after abort of txn-id: %" PRIu64, + txn->txn_id); + nb_config_replace(txn->client->candidate_config, + txn->client->running_config, true); +} + +static int mgmt_be_txn_cfg_prepare(struct mgmt_be_txn_ctx *txn) +{ + struct mgmt_be_client *client_ctx; + struct mgmt_be_txn_req *txn_req = NULL; + struct nb_context nb_ctx = {0}; + struct timeval edit_nb_cfg_start; + struct timeval edit_nb_cfg_end; + unsigned long edit_nb_cfg_tm; + struct timeval prep_nb_cfg_start; + struct timeval prep_nb_cfg_end; + unsigned long prep_nb_cfg_tm; + struct mgmt_be_batch_ctx *batch; + bool error; + char err_buf[BUFSIZ]; + size_t num_processed; + int err; + + assert(txn && txn->client); + client_ctx = txn->client; + + num_processed = 0; + FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { + txn_req = &batch->txn_req; + error = false; + nb_ctx.client = NB_CLIENT_CLI; + nb_ctx.user = (void *)client_ctx->user_data; + + if (!txn->nb_txn) { + /* + * This happens when the current backend client is only + * interested in consuming the config items but is not + * interested in validating it. + */ + error = false; + + gettimeofday(&edit_nb_cfg_start, NULL); + nb_candidate_edit_config_changes( + client_ctx->candidate_config, + txn_req->req.set_cfg.cfg_changes, + (size_t)txn_req->req.set_cfg.num_cfg_changes, + NULL, NULL, 0, err_buf, sizeof(err_buf), + &error); + if (error) { + err_buf[sizeof(err_buf) - 1] = 0; + MGMTD_BE_CLIENT_ERR( + "Failed to update configs for txn-id: %" PRIu64 + " batch-id: %" PRIu64 + " to candidate, err: '%s'", + txn->txn_id, batch->batch_id, err_buf); + return -1; + } + gettimeofday(&edit_nb_cfg_end, NULL); + edit_nb_cfg_tm = timeval_elapsed(edit_nb_cfg_end, + edit_nb_cfg_start); + client_ctx->avg_edit_nb_cfg_tm = + ((client_ctx->avg_edit_nb_cfg_tm * + client_ctx->num_edit_nb_cfg) + + edit_nb_cfg_tm) / + (client_ctx->num_edit_nb_cfg + 1); + client_ctx->num_edit_nb_cfg++; + } + + num_processed++; + } + + if (!num_processed) + return 0; + + /* + * Now prepare all the batches we have applied in one go. + */ + nb_ctx.client = NB_CLIENT_CLI; + nb_ctx.user = (void *)client_ctx->user_data; + + gettimeofday(&prep_nb_cfg_start, NULL); + err = nb_candidate_commit_prepare(nb_ctx, client_ctx->candidate_config, + "MGMTD Backend Txn", &txn->nb_txn, +#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED + true, true, +#else + false, true, +#endif + err_buf, sizeof(err_buf) - 1); + if (err != NB_OK) { + err_buf[sizeof(err_buf) - 1] = 0; + if (err == NB_ERR_VALIDATION) + MGMTD_BE_CLIENT_ERR( + "Failed to validate configs txn-id: %" PRIu64 + " %zu batches, err: '%s'", + txn->txn_id, num_processed, err_buf); + else + MGMTD_BE_CLIENT_ERR( + "Failed to prepare configs for txn-id: %" PRIu64 + " %zu batches, err: '%s'", + txn->txn_id, num_processed, err_buf); + error = true; + SET_FLAG(txn->flags, MGMTD_BE_TXN_FLAGS_CFGPREP_FAILED); + } else + MGMTD_BE_CLIENT_DBG("Prepared configs for txn-id: %" PRIu64 + " %zu batches", + txn->txn_id, num_processed); + + gettimeofday(&prep_nb_cfg_end, NULL); + prep_nb_cfg_tm = timeval_elapsed(prep_nb_cfg_end, prep_nb_cfg_start); + client_ctx->avg_prep_nb_cfg_tm = ((client_ctx->avg_prep_nb_cfg_tm * + client_ctx->num_prep_nb_cfg) + + prep_nb_cfg_tm) / + (client_ctx->num_prep_nb_cfg + 1); + client_ctx->num_prep_nb_cfg++; + + FOREACH_BE_TXN_BATCH_IN_LIST (txn, batch) { + mgmt_be_send_cfgdata_create_reply( + client_ctx, txn->txn_id, batch->batch_id, + error ? false : true, error ? err_buf : NULL); + if (!error) { + SET_FLAG(batch->flags, + MGMTD_BE_BATCH_FLAGS_CFG_PREPARED); + mgmt_be_batches_del(&txn->cfg_batches, batch); + mgmt_be_batches_add_tail(&txn->apply_cfgs, batch); + } + } + + MGMTD_BE_CLIENT_DBG( + "Avg-nb-edit-duration %lu uSec, nb-prep-duration %lu (avg: %lu) uSec, batch size %u", + client_ctx->avg_edit_nb_cfg_tm, prep_nb_cfg_tm, + client_ctx->avg_prep_nb_cfg_tm, (uint32_t)num_processed); + + if (error) + mgmt_be_txn_cfg_abort(txn); + + return 0; +} + +/* + * Process all CFG_DATA_REQs received so far and prepare them all in one go. + */ +static int mgmt_be_update_setcfg_in_batch(struct mgmt_be_client *client_ctx, + struct mgmt_be_txn_ctx *txn, + uint64_t batch_id, + Mgmtd__YangCfgDataReq *cfg_req[], + int num_req) +{ + struct mgmt_be_batch_ctx *batch = NULL; + struct mgmt_be_txn_req *txn_req = NULL; + int index; + struct nb_cfg_change *cfg_chg; + + batch = mgmt_be_batch_create(txn, batch_id); + if (!batch) { + MGMTD_BE_CLIENT_ERR("Batch create failed!"); + return -1; + } + + txn_req = &batch->txn_req; + txn_req->event = MGMTD_BE_TXN_PROC_SETCFG; + MGMTD_BE_CLIENT_DBG("Created SETCFG request for batch-id: %" PRIu64 + " txn-id: %" PRIu64 " cfg-items:%d", + batch_id, txn->txn_id, num_req); + + txn_req->req.set_cfg.num_cfg_changes = num_req; + for (index = 0; index < num_req; index++) { + cfg_chg = &txn_req->req.set_cfg.cfg_changes[index]; + + if (cfg_req[index]->req_type + == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA) + cfg_chg->operation = NB_OP_DESTROY; + else + cfg_chg->operation = NB_OP_CREATE; + + strlcpy(cfg_chg->xpath, cfg_req[index]->data->xpath, + sizeof(cfg_chg->xpath)); + cfg_chg->value = (cfg_req[index]->data->value + && cfg_req[index] + ->data->value + ->encoded_str_val + ? strdup(cfg_req[index] + ->data->value + ->encoded_str_val) + : NULL); + if (cfg_chg->value + && !strncmp(cfg_chg->value, MGMTD_BE_CONTAINER_NODE_VAL, + strlen(MGMTD_BE_CONTAINER_NODE_VAL))) { + free((char *)cfg_chg->value); + cfg_chg->value = NULL; + } + } + + return 0; +} + +static int mgmt_be_process_cfgdata_req(struct mgmt_be_client *client_ctx, + uint64_t txn_id, uint64_t batch_id, + Mgmtd__YangCfgDataReq *cfg_req[], + int num_req, bool end_of_data) +{ + struct mgmt_be_txn_ctx *txn; + + txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, true); + if (!txn) + goto failed; + + mgmt_be_update_setcfg_in_batch(client_ctx, txn, batch_id, cfg_req, + num_req); + + if (txn && end_of_data) { + MGMTD_BE_CLIENT_DBG("End of data; CFG_PREPARE_REQ processing"); + if (mgmt_be_txn_cfg_prepare(txn)) + goto failed; + } + + return 0; +failed: + msg_conn_disconnect(&client_ctx->client.conn, true); + return -1; +} + +static int mgmt_be_send_apply_reply(struct mgmt_be_client *client_ctx, + uint64_t txn_id, uint64_t batch_ids[], + size_t num_batch_ids, bool success, + const char *error_if_any) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeCfgDataApplyReply apply_reply; + + mgmtd__be_cfg_data_apply_reply__init(&apply_reply); + apply_reply.success = success; + apply_reply.txn_id = txn_id; + apply_reply.batch_ids = (uint64_t *)batch_ids; + apply_reply.n_batch_ids = num_batch_ids; + + if (error_if_any) + apply_reply.error_if_any = (char *)error_if_any; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY; + be_msg.cfg_apply_reply = &apply_reply; + + MGMTD_BE_CLIENT_DBG( + "Sending CFG_APPLY_REPLY txn-id %" PRIu64 + " %zu batch ids %" PRIu64 " - %" PRIu64, + txn_id, num_batch_ids, + success && num_batch_ids ? batch_ids[0] : 0, + success && num_batch_ids ? batch_ids[num_batch_ids - 1] : 0); + + return mgmt_be_client_send_msg(client_ctx, &be_msg); +} + +static int mgmt_be_txn_proc_cfgapply(struct mgmt_be_txn_ctx *txn) +{ + struct mgmt_be_client *client_ctx; + struct timeval apply_nb_cfg_start; + struct timeval apply_nb_cfg_end; + unsigned long apply_nb_cfg_tm; + struct mgmt_be_batch_ctx *batch; + char err_buf[BUFSIZ]; + size_t num_processed; + static uint64_t batch_ids[MGMTD_BE_MAX_BATCH_IDS_IN_REQ]; + + assert(txn && txn->client); + client_ctx = txn->client; + + assert(txn->nb_txn); + num_processed = 0; + + /* + * Now apply all the batches we have applied in one go. + */ + gettimeofday(&apply_nb_cfg_start, NULL); + (void)nb_candidate_commit_apply(txn->nb_txn, true, &txn->nb_txn_id, + err_buf, sizeof(err_buf) - 1); + gettimeofday(&apply_nb_cfg_end, NULL); + + apply_nb_cfg_tm = timeval_elapsed(apply_nb_cfg_end, apply_nb_cfg_start); + client_ctx->avg_apply_nb_cfg_tm = ((client_ctx->avg_apply_nb_cfg_tm * + client_ctx->num_apply_nb_cfg) + + apply_nb_cfg_tm) / + (client_ctx->num_apply_nb_cfg + 1); + client_ctx->num_apply_nb_cfg++; + txn->nb_txn = NULL; + + /* + * Send back CFG_APPLY_REPLY for all batches applied. + */ + FOREACH_BE_APPLY_BATCH_IN_LIST (txn, batch) { + /* + * No need to delete the batch yet. Will be deleted during + * transaction cleanup on receiving TXN_DELETE_REQ. + */ + SET_FLAG(batch->flags, MGMTD_BE_TXN_FLAGS_CFG_APPLIED); + mgmt_be_batches_del(&txn->apply_cfgs, batch); + mgmt_be_batches_add_tail(&txn->cfg_batches, batch); + + batch_ids[num_processed] = batch->batch_id; + num_processed++; + if (num_processed == MGMTD_BE_MAX_BATCH_IDS_IN_REQ) { + mgmt_be_send_apply_reply(client_ctx, txn->txn_id, + batch_ids, num_processed, + true, NULL); + num_processed = 0; + } + } + + mgmt_be_send_apply_reply(client_ctx, txn->txn_id, batch_ids, + num_processed, true, NULL); + + MGMTD_BE_CLIENT_DBG("Nb-apply-duration %lu (avg: %lu) uSec", + apply_nb_cfg_tm, client_ctx->avg_apply_nb_cfg_tm); + + return 0; +} + +static int mgmt_be_process_cfg_apply(struct mgmt_be_client *client_ctx, + uint64_t txn_id) +{ + struct mgmt_be_txn_ctx *txn; + + txn = mgmt_be_find_txn_by_id(client_ctx, txn_id, true); + if (!txn) + goto failed; + + MGMTD_BE_CLIENT_DBG("Trigger CFG_APPLY_REQ processing"); + if (mgmt_be_txn_proc_cfgapply(txn)) + goto failed; + + return 0; +failed: + msg_conn_disconnect(&client_ctx->client.conn, true); + return -1; +} + + +static int mgmt_be_client_handle_msg(struct mgmt_be_client *client_ctx, + Mgmtd__BeMessage *be_msg) +{ + /* + * On error we may have closed the connection so don't do anything with + * the client_ctx on return. + * + * protobuf-c adds a max size enum with an internal, and changing by + * version, name; cast to an int to avoid unhandled enum warnings + */ + switch ((int)be_msg->message_case) { + case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY: + MGMTD_BE_CLIENT_DBG("Got SUBSCR_REPLY success %u", + be_msg->subscr_reply->success); + break; + case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ: + MGMTD_BE_CLIENT_DBG("Got TXN_REQ %s txn-id: %" PRIu64, + be_msg->txn_req->create ? "Create" + : "Delete", + be_msg->txn_req->txn_id); + mgmt_be_process_txn_req(client_ctx, + be_msg->txn_req->txn_id, + be_msg->txn_req->create); + break; + case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ: + MGMTD_BE_CLIENT_DBG("Got CFG_DATA_REQ txn-id: %" PRIu64 + " batch-id: %" PRIu64 " end-of-data %u", + be_msg->cfg_data_req->txn_id, + be_msg->cfg_data_req->batch_id, + be_msg->cfg_data_req->end_of_data); + mgmt_be_process_cfgdata_req( + client_ctx, be_msg->cfg_data_req->txn_id, + be_msg->cfg_data_req->batch_id, + be_msg->cfg_data_req->data_req, + be_msg->cfg_data_req->n_data_req, + be_msg->cfg_data_req->end_of_data); + break; + case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ: + MGMTD_BE_CLIENT_DBG("Got CFG_APPLY_REQ txn-id: %" PRIu64, + be_msg->cfg_data_req->txn_id); + mgmt_be_process_cfg_apply( + client_ctx, (uint64_t)be_msg->cfg_apply_req->txn_id); + break; + case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ: + MGMTD_BE_CLIENT_ERR("Got unhandled message type %u", + be_msg->message_case); + /* + * TODO: Add handling code in future. + */ + break; + /* + * NOTE: The following messages are always sent from Backend + * clients to MGMTd only and/or need not be handled here. + */ + case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: + case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY: + case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY: + case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY: + case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY: + case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET: + default: + /* + * A 'default' case is being added contrary to the + * FRR code guidelines to take care of build + * failures on certain build systems (courtesy of + * the proto-c package). + */ + break; + } + + return 0; +} + +static void mgmt_be_client_process_msg(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn) +{ + struct mgmt_be_client *client_ctx; + struct msg_client *client; + Mgmtd__BeMessage *be_msg; + + client = container_of(conn, struct msg_client, conn); + client_ctx = container_of(client, struct mgmt_be_client, client); + + be_msg = mgmtd__be_message__unpack(NULL, len, data); + if (!be_msg) { + MGMTD_BE_CLIENT_DBG("Failed to decode %zu bytes from server", + len); + return; + } + MGMTD_BE_CLIENT_DBG( + "Decoded %zu bytes of message(msg: %u/%u) from server", len, + be_msg->message_case, be_msg->message_case); + (void)mgmt_be_client_handle_msg(client_ctx, be_msg); + mgmtd__be_message__free_unpacked(be_msg, NULL); +} + +int mgmt_be_send_subscr_req(struct mgmt_be_client *client_ctx, + bool subscr_xpaths, int num_xpaths, + char **reg_xpaths) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeSubscribeReq subscr_req; + + mgmtd__be_subscribe_req__init(&subscr_req); + subscr_req.client_name = client_ctx->name; + subscr_req.n_xpath_reg = num_xpaths; + if (num_xpaths) + subscr_req.xpath_reg = reg_xpaths; + else + subscr_req.xpath_reg = NULL; + subscr_req.subscribe_xpaths = subscr_xpaths; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ; + be_msg.subscr_req = &subscr_req; + + MGMTD_FE_CLIENT_DBG( + "Sending SUBSCR_REQ name: %s subscr_xpaths: %u num_xpaths: %zu", + subscr_req.client_name, subscr_req.subscribe_xpaths, + subscr_req.n_xpath_reg); + + return mgmt_be_client_send_msg(client_ctx, &be_msg); +} + +static int _notify_conenct_disconnect(struct msg_client *msg_client, + bool connected) +{ + struct mgmt_be_client *client = + container_of(msg_client, struct mgmt_be_client, client); + int ret; + + if (connected) { + assert(msg_client->conn.fd != -1); + ret = mgmt_be_send_subscr_req(client, false, 0, NULL); + if (ret) + return ret; + } + + /* Notify BE client through registered callback (if any) */ + if (client->cbs.client_connect_notify) + (void)(*client->cbs.client_connect_notify)( + client, client->user_data, connected); + + /* Cleanup any in-progress TXN on disconnect */ + if (!connected) + mgmt_be_cleanup_all_txns(client); + + return 0; +} + +static int mgmt_be_client_notify_conenct(struct msg_client *client) +{ + return _notify_conenct_disconnect(client, true); +} + +static int mgmt_be_client_notify_disconenct(struct msg_conn *conn) +{ + struct msg_client *client = container_of(conn, struct msg_client, conn); + + return _notify_conenct_disconnect(client, false); +} + +/* + * Debug Flags + */ + +DEFPY(debug_mgmt_client_be, debug_mgmt_client_be_cmd, + "[no] debug mgmt client backend", + NO_STR DEBUG_STR MGMTD_STR + "client\n" + "backend\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + + DEBUG_MODE_SET(&mgmt_dbg_be_client, mode, !no); + + return CMD_SUCCESS; +} + +static void mgmt_debug_client_be_set_all(uint32_t flags, bool set) +{ + DEBUG_FLAGS_SET(&mgmt_dbg_be_client, flags, set); +} + +static int mgmt_debug_be_client_config_write(struct vty *vty) +{ + if (DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_CONF)) + vty_out(vty, "debug mgmt client frontend\n"); + + return 1; +} + +void mgmt_debug_be_client_show_debug(struct vty *vty) +{ + if (MGMTD_DBG_BE_CLIENT_CHECK()) + vty_out(vty, "debug mgmt client backend\n"); +} + +static struct debug_callbacks mgmt_dbg_be_client_cbs = { + .debug_set_all = mgmt_debug_client_be_set_all}; + +static struct cmd_node mgmt_dbg_node = { + .name = "mgmt backend client", + .node = DEBUG_NODE, + .prompt = "", + .config_write = mgmt_debug_be_client_config_write, +}; + +struct mgmt_be_client *mgmt_be_client_create(const char *client_name, + struct mgmt_be_client_cbs *cbs, + uintptr_t user_data, + struct event_loop *event_loop) +{ + struct mgmt_be_client *client = + XCALLOC(MTYPE_MGMTD_BE_CLIENT, sizeof(*client)); + + /* Only call after frr_init() */ + assert(running_config); + + client->name = XSTRDUP(MTYPE_MGMTD_BE_CLIENT_NAME, client_name); + client->running_config = running_config; + client->candidate_config = nb_config_new(NULL); + if (cbs) + client->cbs = *cbs; + mgmt_be_txns_init(&client->txn_head); + msg_client_init(&client->client, event_loop, MGMTD_BE_SERVER_PATH, + mgmt_be_client_notify_conenct, + mgmt_be_client_notify_disconenct, + mgmt_be_client_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC, + MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, false, + "BE-client", MGMTD_DBG_BE_CLIENT_CHECK()); + + MGMTD_BE_CLIENT_DBG("Initialized client '%s'", client_name); + + return client; +} + + +void mgmt_be_client_lib_vty_init(void) +{ + debug_init(&mgmt_dbg_be_client_cbs); + install_node(&mgmt_dbg_node); + install_element(ENABLE_NODE, &debug_mgmt_client_be_cmd); + install_element(CONFIG_NODE, &debug_mgmt_client_be_cmd); +} + +void mgmt_be_client_destroy(struct mgmt_be_client *client) +{ + MGMTD_BE_CLIENT_DBG("Destroying MGMTD Backend Client '%s'", + client->name); + + msg_client_cleanup(&client->client); + mgmt_be_cleanup_all_txns(client); + mgmt_be_txns_fini(&client->txn_head); + nb_config_free(client->candidate_config); + + XFREE(MTYPE_MGMTD_BE_CLIENT_NAME, client->name); + XFREE(MTYPE_MGMTD_BE_CLIENT, client); +} diff --git a/lib/mgmt_be_client.h b/lib/mgmt_be_client.h new file mode 100644 index 000000000000..4ad5ca5957cd --- /dev/null +++ b/lib/mgmt_be_client.h @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Backend Client Library api interfaces + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_BE_CLIENT_H_ +#define _FRR_MGMTD_BE_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "northbound.h" +#include "mgmt_pb.h" +#include "mgmtd/mgmt_defines.h" + +/*************************************************************** + * Client IDs + ***************************************************************/ + +/* + * Add enum value for each supported component, wrap with + * #ifdef HAVE_COMPONENT + */ +enum mgmt_be_client_id { + MGMTD_BE_CLIENT_ID_MIN = 0, + MGMTD_BE_CLIENT_ID_INIT = -1, +#ifdef HAVE_STATICD + MGMTD_BE_CLIENT_ID_STATICD, +#endif + MGMTD_BE_CLIENT_ID_MAX +}; + +#define FOREACH_MGMTD_BE_CLIENT_ID(id) \ + for ((id) = MGMTD_BE_CLIENT_ID_MIN; \ + (id) < MGMTD_BE_CLIENT_ID_MAX; (id)++) + +/*************************************************************** + * Constants + ***************************************************************/ + +#define MGMTD_BE_CLIENT_ERROR_STRING_MAX_LEN 32 + +#define MGMTD_BE_DEFAULT_CONN_RETRY_INTVL_SEC 5 + +#define MGMTD_BE_MSG_PROC_DELAY_USEC 10 +#define MGMTD_BE_MAX_NUM_MSG_PROC 500 + +#define MGMTD_BE_MSG_WRITE_DELAY_MSEC 1 +#define MGMTD_BE_MAX_NUM_MSG_WRITE 1000 + +#define GMGD_BE_MAX_NUM_REQ_ITEMS 64 + +#define MGMTD_BE_MSG_MAX_LEN 16384 + +#define MGMTD_SOCKET_BE_SEND_BUF_SIZE 65535 +#define MGMTD_SOCKET_BE_RECV_BUF_SIZE MGMTD_SOCKET_BE_SEND_BUF_SIZE + +#define MGMTD_MAX_CFG_CHANGES_IN_BATCH \ + ((10 * MGMTD_BE_MSG_MAX_LEN) / \ + (MGMTD_MAX_XPATH_LEN + MGMTD_MAX_YANG_VALUE_LEN)) + +/* + * MGMTD_BE_MSG_MAX_LEN must be used 80% + * since there is overhead of google protobuf + * that gets added to sent message + */ +#define MGMTD_BE_CFGDATA_PACKING_EFFICIENCY 0.8 +#define MGMTD_BE_CFGDATA_MAX_MSG_LEN \ + (MGMTD_BE_MSG_MAX_LEN * MGMTD_BE_CFGDATA_PACKING_EFFICIENCY) + +#define MGMTD_BE_MAX_BATCH_IDS_IN_REQ \ + (MGMTD_BE_MSG_MAX_LEN - 128) / sizeof(uint64_t) + +#define MGMTD_BE_CONTAINER_NODE_VAL "<<container>>" + +/*************************************************************** + * Data-structures + ***************************************************************/ + +#define MGMTD_BE_MAX_CLIENTS_PER_XPATH_REG 32 + +struct mgmt_be_client; + +struct mgmt_be_client_txn_ctx { + uintptr_t *user_ctx; +}; + +/** + * Backend client callbacks. + * + * Callbacks: + * client_connect_notify: called when connection is made/lost to mgmtd. + * txn_notify: called when a txn has been created + */ +struct mgmt_be_client_cbs { + void (*client_connect_notify)(struct mgmt_be_client *client, + uintptr_t usr_data, bool connected); + + void (*txn_notify)(struct mgmt_be_client *client, uintptr_t usr_data, + struct mgmt_be_client_txn_ctx *txn_ctx, + bool destroyed); +}; + +/*************************************************************** + * Global data exported + ***************************************************************/ + +extern const char *mgmt_be_client_names[MGMTD_BE_CLIENT_ID_MAX + 1]; + +static inline const char *mgmt_be_client_id2name(enum mgmt_be_client_id id) +{ + if (id > MGMTD_BE_CLIENT_ID_MAX) + id = MGMTD_BE_CLIENT_ID_MAX; + return mgmt_be_client_names[id]; +} + +static inline enum mgmt_be_client_id +mgmt_be_client_name2id(const char *name) +{ + enum mgmt_be_client_id id; + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (!strncmp(mgmt_be_client_names[id], name, + MGMTD_CLIENT_NAME_MAX_LEN)) + return id; + } + + return MGMTD_BE_CLIENT_ID_MAX; +} + +extern struct debug mgmt_dbg_be_client; + +/*************************************************************** + * API prototypes + ***************************************************************/ + +#define MGMTD_BE_CLIENT_DBG(fmt, ...) \ + DEBUGD(&mgmt_dbg_be_client, "BE-CLIENT: %s: " fmt, __func__, \ + ##__VA_ARGS__) +#define MGMTD_BE_CLIENT_ERR(fmt, ...) \ + zlog_err("BE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_DBG_BE_CLIENT_CHECK() \ + DEBUG_MODE_CHECK(&mgmt_dbg_be_client, DEBUG_MODE_ALL) + +/** + * Create backend client and connect to MGMTD. + * + * Args: + * client_name: the name of the client + * cbs: callbacks for various events. + * event_loop: the main event loop. + * + * Returns: + * Backend client object. + */ +extern struct mgmt_be_client * +mgmt_be_client_create(const char *name, struct mgmt_be_client_cbs *cbs, + uintptr_t user_data, struct event_loop *event_loop); + +/* + * Initialize library vty (adds debug support). + * + * This call should be added to your component when enabling other vty code to + * enable mgmtd client debugs. When adding, one needs to also add a their + * component in `xref2vtysh.py` as well. + */ +extern void mgmt_be_client_lib_vty_init(void); + +/* + * Print enabled debugging commands. + */ +extern void mgmt_debug_be_client_show_debug(struct vty *vty); + +/* + * [Un]-subscribe with MGMTD for one or more YANG subtree(s). + * + * client + * The client object. + * + * reg_yang_xpaths + * Yang xpath(s) that needs to be [un]-subscribed from/to + * + * num_xpaths + * Number of xpaths + * + * Returns: + * MGMTD_SUCCESS on success, MGMTD_* otherwise. + */ +extern int mgmt_be_send_subscr_req(struct mgmt_be_client *client, + bool subscr_xpaths, int num_xpaths, + char **reg_xpaths); + +/* + * Destroy backend client and cleanup everything. + */ +extern void mgmt_be_client_destroy(struct mgmt_be_client *client); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_MGMTD_BE_CLIENT_H_ */ diff --git a/lib/mgmt_fe_client.c b/lib/mgmt_fe_client.c new file mode 100644 index 000000000000..da19db463f37 --- /dev/null +++ b/lib/mgmt_fe_client.c @@ -0,0 +1,700 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Frontend Client Library api interfaces + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#include "compiler.h" +#include "debug.h" +#include "memory.h" +#include "libfrr.h" +#include "mgmt_fe_client.h" +#include "mgmt_msg.h" +#include "mgmt_pb.h" +#include "network.h" +#include "stream.h" +#include "sockopt.h" + +#include "lib/mgmt_fe_client_clippy.c" + +PREDECL_LIST(mgmt_sessions); + +struct mgmt_fe_client_session { + uint64_t client_id; /* FE client identifies itself with this ID */ + uint64_t session_id; /* FE adapter identified session with this ID */ + struct mgmt_fe_client *client; + uintptr_t user_ctx; + + struct mgmt_sessions_item list_linkage; +}; + +DECLARE_LIST(mgmt_sessions, struct mgmt_fe_client_session, list_linkage); + +DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT, "frontend client"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_CLIENT_NAME, "frontend client name"); +DEFINE_MTYPE_STATIC(LIB, MGMTD_FE_SESSION, "frontend session"); + +struct mgmt_fe_client { + struct msg_client client; + char *name; + struct mgmt_fe_client_cbs cbs; + uintptr_t user_data; + struct mgmt_sessions_head sessions; +}; + +#define FOREACH_SESSION_IN_LIST(client, session) \ + frr_each_safe (mgmt_sessions, &(client)->sessions, (session)) + +struct debug mgmt_dbg_fe_client = {0, "Management frontend client operations"}; + + +static inline const char *dsid2name(Mgmtd__DatastoreId id) +{ + switch ((int)id) { + case MGMTD_DS_NONE: + return "none"; + case MGMTD_DS_RUNNING: + return "running"; + case MGMTD_DS_CANDIDATE: + return "candidate"; + case MGMTD_DS_OPERATIONAL: + return "operational"; + default: + return "unknown-datastore-id"; + } +} + +static struct mgmt_fe_client_session * +mgmt_fe_find_session_by_client_id(struct mgmt_fe_client *client, + uint64_t client_id) +{ + struct mgmt_fe_client_session *session; + + FOREACH_SESSION_IN_LIST (client, session) { + if (session->client_id == client_id) { + MGMTD_FE_CLIENT_DBG("Found session-id %" PRIu64 + " using client-id %" PRIu64, + session->session_id, client_id); + return session; + } + } + MGMTD_FE_CLIENT_DBG("Session not found using client-id %" PRIu64, + client_id); + return NULL; +} + +static struct mgmt_fe_client_session * +mgmt_fe_find_session_by_session_id(struct mgmt_fe_client *client, + uint64_t session_id) +{ + struct mgmt_fe_client_session *session; + + FOREACH_SESSION_IN_LIST (client, session) { + if (session->session_id == session_id) { + MGMTD_FE_CLIENT_DBG( + "Found session of client-id %" PRIu64 + " using session-id %" PRIu64, + session->client_id, session_id); + return session; + } + } + MGMTD_FE_CLIENT_DBG("Session not found using session-id %" PRIu64, + session_id); + return NULL; +} + +static int mgmt_fe_client_send_msg(struct mgmt_fe_client *client, + Mgmtd__FeMessage *fe_msg, + bool short_circuit_ok) +{ + return msg_conn_send_msg( + &client->client.conn, MGMT_MSG_VERSION_PROTOBUF, fe_msg, + mgmtd__fe_message__get_packed_size(fe_msg), + (size_t(*)(void *, void *))mgmtd__fe_message__pack, + short_circuit_ok); +} + +static int mgmt_fe_send_register_req(struct mgmt_fe_client *client) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeRegisterReq rgstr_req; + + mgmtd__fe_register_req__init(&rgstr_req); + rgstr_req.client_name = client->name; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ; + fe_msg.register_req = &rgstr_req; + + MGMTD_FE_CLIENT_DBG( + "Sending REGISTER_REQ message to MGMTD Frontend server"); + + return mgmt_fe_client_send_msg(client, &fe_msg, true); +} + +static int mgmt_fe_send_session_req(struct mgmt_fe_client *client, + struct mgmt_fe_client_session *session, + bool create) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeSessionReq sess_req; + + mgmtd__fe_session_req__init(&sess_req); + sess_req.create = create; + if (create) { + sess_req.id_case = MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID; + sess_req.client_conn_id = session->client_id; + } else { + sess_req.id_case = MGMTD__FE_SESSION_REQ__ID_SESSION_ID; + sess_req.session_id = session->session_id; + } + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ; + fe_msg.session_req = &sess_req; + + MGMTD_FE_CLIENT_DBG( + "Sending SESSION_REQ %s message for client-id %" PRIu64, + create ? "create" : "destroy", session->client_id); + + return mgmt_fe_client_send_msg(client, &fe_msg, true); +} + +int mgmt_fe_send_lockds_req(struct mgmt_fe_client *client, uint64_t session_id, + uint64_t req_id, Mgmtd__DatastoreId ds_id, + bool lock, bool scok) +{ + (void)req_id; + Mgmtd__FeMessage fe_msg; + Mgmtd__FeLockDsReq lockds_req; + + mgmtd__fe_lock_ds_req__init(&lockds_req); + lockds_req.session_id = session_id; + lockds_req.req_id = req_id; + lockds_req.ds_id = ds_id; + lockds_req.lock = lock; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ; + fe_msg.lockds_req = &lockds_req; + + MGMTD_FE_CLIENT_DBG( + "Sending LOCKDS_REQ (%sLOCK) message for DS:%s session-id %" PRIu64, + lock ? "" : "UN", dsid2name(ds_id), session_id); + + + return mgmt_fe_client_send_msg(client, &fe_msg, scok); +} + +int mgmt_fe_send_setcfg_req(struct mgmt_fe_client *client, uint64_t session_id, + uint64_t req_id, Mgmtd__DatastoreId ds_id, + Mgmtd__YangCfgDataReq **data_req, int num_data_reqs, + bool implicit_commit, Mgmtd__DatastoreId dst_ds_id) +{ + (void)req_id; + Mgmtd__FeMessage fe_msg; + Mgmtd__FeSetConfigReq setcfg_req; + + mgmtd__fe_set_config_req__init(&setcfg_req); + setcfg_req.session_id = session_id; + setcfg_req.ds_id = ds_id; + setcfg_req.req_id = req_id; + setcfg_req.data = data_req; + setcfg_req.n_data = (size_t)num_data_reqs; + setcfg_req.implicit_commit = implicit_commit; + setcfg_req.commit_ds_id = dst_ds_id; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ; + fe_msg.setcfg_req = &setcfg_req; + + MGMTD_FE_CLIENT_DBG( + "Sending SET_CONFIG_REQ message for DS:%s session-id %" PRIu64 + " (#xpaths:%d)", + dsid2name(ds_id), session_id, num_data_reqs); + + return mgmt_fe_client_send_msg(client, &fe_msg, false); +} + +int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dest_ds_id, + bool validate_only, bool abort) +{ + (void)req_id; + Mgmtd__FeMessage fe_msg; + Mgmtd__FeCommitConfigReq commitcfg_req; + + mgmtd__fe_commit_config_req__init(&commitcfg_req); + commitcfg_req.session_id = session_id; + commitcfg_req.src_ds_id = src_ds_id; + commitcfg_req.dst_ds_id = dest_ds_id; + commitcfg_req.req_id = req_id; + commitcfg_req.validate_only = validate_only; + commitcfg_req.abort = abort; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ; + fe_msg.commcfg_req = &commitcfg_req; + + MGMTD_FE_CLIENT_DBG( + "Sending COMMIT_CONFIG_REQ message for Src-DS:%s, Dst-DS:%s session-id %" PRIu64, + dsid2name(src_ds_id), dsid2name(dest_ds_id), session_id); + + return mgmt_fe_client_send_msg(client, &fe_msg, false); +} + +int mgmt_fe_send_get_req(struct mgmt_fe_client *client, uint64_t session_id, + uint64_t req_id, bool is_config, + Mgmtd__DatastoreId ds_id, + Mgmtd__YangGetDataReq *data_req[], int num_data_reqs) +{ + (void)req_id; + Mgmtd__FeMessage fe_msg; + Mgmtd__FeGetReq getcfg_req; + + mgmtd__fe_get_req__init(&getcfg_req); + getcfg_req.session_id = session_id; + getcfg_req.config = is_config; + getcfg_req.ds_id = ds_id; + getcfg_req.req_id = req_id; + getcfg_req.data = data_req; + getcfg_req.n_data = (size_t)num_data_reqs; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GET_REQ; + fe_msg.get_req = &getcfg_req; + + MGMTD_FE_CLIENT_DBG("Sending GET_REQ (iscfg %d) message for DS:%s session-id %" PRIu64 + " (#xpaths:%d)", + is_config, dsid2name(ds_id), session_id, + num_data_reqs); + + return mgmt_fe_client_send_msg(client, &fe_msg, false); +} + +int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, bool register_req, + Mgmtd__YangDataXPath *data_req[], + int num_data_reqs) +{ + (void)req_id; + Mgmtd__FeMessage fe_msg; + Mgmtd__FeRegisterNotifyReq regntfy_req; + + mgmtd__fe_register_notify_req__init(®ntfy_req); + regntfy_req.session_id = session_id; + regntfy_req.ds_id = ds_id; + regntfy_req.register_req = register_req; + regntfy_req.data_xpath = data_req; + regntfy_req.n_data_xpath = (size_t)num_data_reqs; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ; + fe_msg.regnotify_req = ®ntfy_req; + + return mgmt_fe_client_send_msg(client, &fe_msg, false); +} + +static int mgmt_fe_client_handle_msg(struct mgmt_fe_client *client, + Mgmtd__FeMessage *fe_msg) +{ + struct mgmt_fe_client_session *session = NULL; + + /* + * protobuf-c adds a max size enum with an internal, and changing by + * version, name; cast to an int to avoid unhandled enum warnings + */ + switch ((int)fe_msg->message_case) { + case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY: + if (fe_msg->session_reply->create && + fe_msg->session_reply->has_client_conn_id) { + MGMTD_FE_CLIENT_DBG( + "Got SESSION_REPLY (create) for client-id %" PRIu64 + " with session-id: %" PRIu64, + fe_msg->session_reply->client_conn_id, + fe_msg->session_reply->session_id); + + session = mgmt_fe_find_session_by_client_id( + client, fe_msg->session_reply->client_conn_id); + + if (session && fe_msg->session_reply->success) { + MGMTD_FE_CLIENT_DBG( + "Session Created for client-id %" PRIu64, + fe_msg->session_reply->client_conn_id); + session->session_id = + fe_msg->session_reply->session_id; + } else { + MGMTD_FE_CLIENT_ERR( + "Session Create failed for client-id %" PRIu64, + fe_msg->session_reply->client_conn_id); + } + } else if (!fe_msg->session_reply->create) { + MGMTD_FE_CLIENT_DBG( + "Got SESSION_REPLY (destroy) for session-id %" PRIu64, + fe_msg->session_reply->session_id); + + session = mgmt_fe_find_session_by_session_id( + client, fe_msg->session_req->session_id); + } + + /* The session state may be deleted by the callback */ + if (session && session->client && + session->client->cbs.client_session_notify) + (*session->client->cbs.client_session_notify)( + client, client->user_data, session->client_id, + fe_msg->session_reply->create, + fe_msg->session_reply->success, + fe_msg->session_reply->session_id, + session->user_ctx); + break; + case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY: + MGMTD_FE_CLIENT_DBG("Got LOCKDS_REPLY for session-id %" PRIu64, + fe_msg->lockds_reply->session_id); + session = mgmt_fe_find_session_by_session_id( + client, fe_msg->lockds_reply->session_id); + + if (session && session->client && + session->client->cbs.lock_ds_notify) + (*session->client->cbs.lock_ds_notify)( + client, client->user_data, session->client_id, + fe_msg->lockds_reply->session_id, + session->user_ctx, fe_msg->lockds_reply->req_id, + fe_msg->lockds_reply->lock, + fe_msg->lockds_reply->success, + fe_msg->lockds_reply->ds_id, + fe_msg->lockds_reply->error_if_any); + break; + case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY: + MGMTD_FE_CLIENT_DBG("Got SETCFG_REPLY for session-id %" PRIu64, + fe_msg->setcfg_reply->session_id); + + session = mgmt_fe_find_session_by_session_id( + client, fe_msg->setcfg_reply->session_id); + + if (session && session->client && + session->client->cbs.set_config_notify) + (*session->client->cbs.set_config_notify)( + client, client->user_data, session->client_id, + fe_msg->setcfg_reply->session_id, + session->user_ctx, fe_msg->setcfg_reply->req_id, + fe_msg->setcfg_reply->success, + fe_msg->setcfg_reply->ds_id, + fe_msg->setcfg_reply->implicit_commit, + fe_msg->setcfg_reply->error_if_any); + break; + case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY: + MGMTD_FE_CLIENT_DBG("Got COMMCFG_REPLY for session-id %" PRIu64, + fe_msg->commcfg_reply->session_id); + + session = mgmt_fe_find_session_by_session_id( + client, fe_msg->commcfg_reply->session_id); + + if (session && session->client && + session->client->cbs.commit_config_notify) + (*session->client->cbs.commit_config_notify)( + client, client->user_data, session->client_id, + fe_msg->commcfg_reply->session_id, + session->user_ctx, + fe_msg->commcfg_reply->req_id, + fe_msg->commcfg_reply->success, + fe_msg->commcfg_reply->src_ds_id, + fe_msg->commcfg_reply->dst_ds_id, + fe_msg->commcfg_reply->validate_only, + fe_msg->commcfg_reply->error_if_any); + break; + case MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY: + MGMTD_FE_CLIENT_DBG("Got GET_REPLY for session-id %" PRIu64, + fe_msg->get_reply->session_id); + + session = + mgmt_fe_find_session_by_session_id(client, + fe_msg->get_reply + ->session_id); + + if (session && session->client && + session->client->cbs.get_data_notify) + (*session->client->cbs.get_data_notify)( + client, client->user_data, session->client_id, + fe_msg->get_reply->session_id, + session->user_ctx, fe_msg->get_reply->req_id, + fe_msg->get_reply->success, + fe_msg->get_reply->ds_id, + fe_msg->get_reply->data + ? fe_msg->get_reply->data->data + : NULL, + fe_msg->get_reply->data + ? fe_msg->get_reply->data->n_data + : 0, + fe_msg->get_reply->data + ? fe_msg->get_reply->data->next_indx + : 0, + fe_msg->get_reply->error_if_any); + break; + case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ: + /* + * TODO: Add handling code in future. + */ + break; + /* + * NOTE: The following messages are always sent from Frontend + * clients to MGMTd only and/or need not be handled here. + */ + case MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_GET_REQ: + case MGMTD__FE_MESSAGE__MESSAGE__NOT_SET: + default: + /* + * A 'default' case is being added contrary to the + * FRR code guidelines to take care of build + * failures on certain build systems (courtesy of + * the proto-c package). + */ + break; + } + + return 0; +} + +static void mgmt_fe_client_process_msg(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn) +{ + struct mgmt_fe_client *client; + struct msg_client *msg_client; + Mgmtd__FeMessage *fe_msg; + + msg_client = container_of(conn, struct msg_client, conn); + client = container_of(msg_client, struct mgmt_fe_client, client); + + fe_msg = mgmtd__fe_message__unpack(NULL, len, data); + if (!fe_msg) { + MGMTD_FE_CLIENT_DBG("Failed to decode %zu bytes from server.", + len); + return; + } + MGMTD_FE_CLIENT_DBG( + "Decoded %zu bytes of message(msg: %u/%u) from server", len, + fe_msg->message_case, fe_msg->message_case); + (void)mgmt_fe_client_handle_msg(client, fe_msg); + mgmtd__fe_message__free_unpacked(fe_msg, NULL); +} + +static int _notify_connect_disconnect(struct msg_client *msg_client, + bool connected) +{ + struct mgmt_fe_client *client = + container_of(msg_client, struct mgmt_fe_client, client); + struct mgmt_fe_client_session *session; + int ret; + + /* Send REGISTER_REQ message */ + if (connected) { + if ((ret = mgmt_fe_send_register_req(client)) != 0) + return ret; + } + + /* Walk list of sessions for this FE client deleting them */ + if (!connected && mgmt_sessions_count(&client->sessions)) { + MGMTD_FE_CLIENT_DBG("Cleaning up existing sessions"); + + FOREACH_SESSION_IN_LIST (client, session) { + assert(session->client); + + /* unlink from list first this avoids double free */ + mgmt_sessions_del(&client->sessions, session); + + /* notify FE client the session is being deleted */ + if (session->client->cbs.client_session_notify) { + (*session->client->cbs.client_session_notify)( + client, client->user_data, + session->client_id, false, true, + session->session_id, session->user_ctx); + } + + XFREE(MTYPE_MGMTD_FE_SESSION, session); + } + } + + /* Notify FE client through registered callback (if any). */ + if (client->cbs.client_connect_notify) + (void)(*client->cbs.client_connect_notify)( + client, client->user_data, connected); + return 0; +} + +static int mgmt_fe_client_notify_connect(struct msg_client *client) +{ + return _notify_connect_disconnect(client, true); +} + +static int mgmt_fe_client_notify_disconnect(struct msg_conn *conn) +{ + struct msg_client *client = container_of(conn, struct msg_client, conn); + + return _notify_connect_disconnect(client, false); +} + + +DEFPY(debug_mgmt_client_fe, debug_mgmt_client_fe_cmd, + "[no] debug mgmt client frontend", + NO_STR DEBUG_STR MGMTD_STR + "client\n" + "frontend\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + + DEBUG_MODE_SET(&mgmt_dbg_fe_client, mode, !no); + + return CMD_SUCCESS; +} + +static void mgmt_debug_client_fe_set_all(uint32_t flags, bool set) +{ + DEBUG_FLAGS_SET(&mgmt_dbg_fe_client, flags, set); +} + +static int mgmt_debug_fe_client_config_write(struct vty *vty) +{ + if (DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_CONF)) + vty_out(vty, "debug mgmt client frontend\n"); + + return CMD_SUCCESS; +} + +void mgmt_debug_fe_client_show_debug(struct vty *vty) +{ + if (MGMTD_DBG_FE_CLIENT_CHECK()) + vty_out(vty, "debug mgmt client frontend\n"); +} + +static struct debug_callbacks mgmt_dbg_fe_client_cbs = { + .debug_set_all = mgmt_debug_client_fe_set_all}; + +static struct cmd_node mgmt_dbg_node = { + .name = "mgmt client frontend", + .node = DEBUG_NODE, + .prompt = "", + .config_write = mgmt_debug_fe_client_config_write, +}; + +/* + * Initialize library and try connecting with MGMTD. + */ +struct mgmt_fe_client *mgmt_fe_client_create(const char *client_name, + struct mgmt_fe_client_cbs *cbs, + uintptr_t user_data, + struct event_loop *event_loop) +{ + struct mgmt_fe_client *client = + XCALLOC(MTYPE_MGMTD_FE_CLIENT, sizeof(*client)); + + client->name = XSTRDUP(MTYPE_MGMTD_FE_CLIENT_NAME, client_name); + client->user_data = user_data; + if (cbs) + client->cbs = *cbs; + + mgmt_sessions_init(&client->sessions); + + msg_client_init(&client->client, event_loop, MGMTD_FE_SERVER_PATH, + mgmt_fe_client_notify_connect, + mgmt_fe_client_notify_disconnect, + mgmt_fe_client_process_msg, MGMTD_FE_MAX_NUM_MSG_PROC, + MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN, true, + "FE-client", MGMTD_DBG_FE_CLIENT_CHECK()); + + MGMTD_FE_CLIENT_DBG("Initialized client '%s'", client_name); + + return client; +} + +void mgmt_fe_client_lib_vty_init(void) +{ + debug_init(&mgmt_dbg_fe_client_cbs); + install_node(&mgmt_dbg_node); + install_element(ENABLE_NODE, &debug_mgmt_client_fe_cmd); + install_element(CONFIG_NODE, &debug_mgmt_client_fe_cmd); +} + +uint mgmt_fe_client_session_count(struct mgmt_fe_client *client) +{ + return mgmt_sessions_count(&client->sessions); +} + +/* + * Create a new Session for a Frontend Client connection. + */ +enum mgmt_result mgmt_fe_create_client_session(struct mgmt_fe_client *client, + uint64_t client_id, + uintptr_t user_ctx) +{ + struct mgmt_fe_client_session *session; + + session = XCALLOC(MTYPE_MGMTD_FE_SESSION, + sizeof(struct mgmt_fe_client_session)); + assert(session); + session->user_ctx = user_ctx; + session->client_id = client_id; + session->client = client; + session->session_id = 0; + + mgmt_sessions_add_tail(&client->sessions, session); + + if (mgmt_fe_send_session_req(client, session, true) != 0) { + XFREE(MTYPE_MGMTD_FE_SESSION, session); + return MGMTD_INTERNAL_ERROR; + } + + return MGMTD_SUCCESS; +} + +/* + * Delete an existing Session for a Frontend Client connection. + */ +enum mgmt_result mgmt_fe_destroy_client_session(struct mgmt_fe_client *client, + uint64_t client_id) +{ + struct mgmt_fe_client_session *session; + + session = mgmt_fe_find_session_by_client_id(client, client_id); + if (!session || session->client != client) + return MGMTD_INVALID_PARAM; + + if (session->session_id && + mgmt_fe_send_session_req(client, session, false) != 0) + MGMTD_FE_CLIENT_ERR( + "Failed to send session destroy request for the session-id %" PRIu64, + session->session_id); + + mgmt_sessions_del(&client->sessions, session); + XFREE(MTYPE_MGMTD_FE_SESSION, session); + + return MGMTD_SUCCESS; +} + +/* + * Destroy library and cleanup everything. + */ +void mgmt_fe_client_destroy(struct mgmt_fe_client *client) +{ + struct mgmt_fe_client_session *session; + + MGMTD_FE_CLIENT_DBG("Destroying MGMTD Frontend Client '%s'", + client->name); + + FOREACH_SESSION_IN_LIST (client, session) + mgmt_fe_destroy_client_session(client, session->client_id); + + msg_client_cleanup(&client->client); + + XFREE(MTYPE_MGMTD_FE_CLIENT_NAME, client->name); + XFREE(MTYPE_MGMTD_FE_CLIENT, client); +} diff --git a/lib/mgmt_fe_client.h b/lib/mgmt_fe_client.h new file mode 100644 index 000000000000..286141da443a --- /dev/null +++ b/lib/mgmt_fe_client.h @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Frontend Client Library api interfaces + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_FE_CLIENT_H_ +#define _FRR_MGMTD_FE_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mgmt_pb.h" +#include "frrevent.h" +#include "mgmtd/mgmt_defines.h" + +/*************************************************************** + * Macros + ***************************************************************/ + +/* + * The server port MGMTD daemon is listening for Backend Client + * connections. + */ + +#define MGMTD_FE_CLIENT_ERROR_STRING_MAX_LEN 32 + +#define MGMTD_FE_DEFAULT_CONN_RETRY_INTVL_SEC 5 + +#define MGMTD_FE_MSG_PROC_DELAY_USEC 10 +#define MGMTD_FE_MAX_NUM_MSG_PROC 500 + +#define MGMTD_FE_MSG_WRITE_DELAY_MSEC 1 +#define MGMTD_FE_MAX_NUM_MSG_WRITE 100 + +#define GMGD_FE_MAX_NUM_REQ_ITEMS 64 + +#define MGMTD_FE_MSG_MAX_LEN 9000 + +#define MGMTD_SOCKET_FE_SEND_BUF_SIZE 65535 +#define MGMTD_SOCKET_FE_RECV_BUF_SIZE MGMTD_SOCKET_FE_SEND_BUF_SIZE + +/*************************************************************** + * Data-structures + ***************************************************************/ + +#define MGMTD_SESSION_ID_NONE 0 + +#define MGMTD_CLIENT_ID_NONE 0 + +#define MGMTD_DS_NONE MGMTD__DATASTORE_ID__DS_NONE +#define MGMTD_DS_RUNNING MGMTD__DATASTORE_ID__RUNNING_DS +#define MGMTD_DS_CANDIDATE MGMTD__DATASTORE_ID__CANDIDATE_DS +#define MGMTD_DS_OPERATIONAL MGMTD__DATASTORE_ID__OPERATIONAL_DS +#define MGMTD_DS_MAX_ID MGMTD_DS_OPERATIONAL + 1 + +struct mgmt_fe_client; + + +/* + * All the client specific information this library needs to + * initialize itself, setup connection with MGMTD FrontEnd interface + * and carry on all required procedures appropriately. + * + * FrontEnd clients need to initialise a instance of this structure + * with appropriate data and pass it while calling the API + * to initialize the library (See mgmt_fe_client_lib_init for + * more details). + */ +struct mgmt_fe_client_cbs { + void (*client_connect_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, bool connected); + + void (*client_session_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + bool create, bool success, + uintptr_t session_id, + uintptr_t user_session_client); + + void (*lock_ds_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uintptr_t session_id, + uintptr_t user_session_client, uint64_t req_id, + bool lock_ds, bool success, + Mgmtd__DatastoreId ds_id, char *errmsg_if_any); + + void (*set_config_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uintptr_t session_id, + uintptr_t user_session_client, + uint64_t req_id, bool success, + Mgmtd__DatastoreId ds_id, bool implcit_commit, + char *errmsg_if_any); + + void (*commit_config_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uintptr_t session_id, + uintptr_t user_session_client, + uint64_t req_id, bool success, + Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dst_ds_id, + bool validate_only, char *errmsg_if_any); + + int (*get_data_notify)(struct mgmt_fe_client *client, + uintptr_t user_data, uint64_t client_id, + uintptr_t session_id, + uintptr_t user_session_client, uint64_t req_id, + bool success, Mgmtd__DatastoreId ds_id, + Mgmtd__YangData **yang_data, size_t num_data, + int next_key, char *errmsg_if_any); + + int (*data_notify)(uint64_t client_id, uint64_t session_id, + uintptr_t user_data, uint64_t req_id, + Mgmtd__DatastoreId ds_id, + Mgmtd__YangData **yang_data, size_t num_data); +}; + +extern struct debug mgmt_dbg_fe_client; + +/*************************************************************** + * API prototypes + ***************************************************************/ + +#define MGMTD_FE_CLIENT_DBG(fmt, ...) \ + DEBUGD(&mgmt_dbg_fe_client, "FE-CLIENT: %s: " fmt, __func__, \ + ##__VA_ARGS__) +#define MGMTD_FE_CLIENT_ERR(fmt, ...) \ + zlog_err("FE-CLIENT: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_DBG_FE_CLIENT_CHECK() \ + DEBUG_MODE_CHECK(&mgmt_dbg_fe_client, DEBUG_MODE_ALL) + +/* + * Initialize library and try connecting with MGMTD FrontEnd interface. + * + * params + * Frontend client parameters. + * + * master_thread + * Thread master. + * + * Returns: + * Frontend client lib handler (nothing but address of mgmt_fe_client) + */ +extern struct mgmt_fe_client * +mgmt_fe_client_create(const char *client_name, struct mgmt_fe_client_cbs *cbs, + uintptr_t user_data, struct event_loop *event_loop); + +/* + * Initialize library vty (adds debug support). + * + * This call should be added to your component when enabling other vty + * code to enable mgmtd client debugs. When adding, one needs to also + * add a their component in `xref2vtysh.py` as well. + */ +extern void mgmt_fe_client_lib_vty_init(void); + +/* + * Print enabled debugging commands. + */ +extern void mgmt_debug_fe_client_show_debug(struct vty *vty); + +/* + * Create a new Session for a Frontend Client connection. + * + * lib_hndl + * Client library handler. + * + * client_id + * Unique identifier of client. + * + * user_client + * Client context. + * + * Returns: + * MGMTD_SUCCESS on success, MGMTD_* otherwise. + */ +extern enum mgmt_result +mgmt_fe_create_client_session(struct mgmt_fe_client *client, uint64_t client_id, + uintptr_t user_client); + +/* + * Delete an existing Session for a Frontend Client connection. + * + * lib_hndl + * Client library handler. + * + * client_id + * Unique identifier of client. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern enum mgmt_result +mgmt_fe_destroy_client_session(struct mgmt_fe_client *client, + uint64_t client_id); + +/* + * Send UN/LOCK_DS_REQ to MGMTD for a specific Datastore DS. + * + * lib_hndl + * Client library handler. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * ds_id + * Datastore ID (Running/Candidate/Oper/Startup) + * + * lock_ds + * TRUE for lock request, FALSE for unlock request. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_lockds_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, bool lock_ds, + bool scok); + +/* + * Send SET_CONFIG_REQ to MGMTD for one or more config data(s). + * + * lib_hndl + * Client library handler. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * ds_id + * Datastore ID (Running/Candidate/Oper/Startup) + * + * conf_req + * Details regarding the SET_CONFIG_REQ. + * + * num_req + * Number of config requests. + * + * implcit commit + * TRUE for implicit commit, FALSE otherwise. + * + * dst_ds_id + * Destination Datastore ID where data needs to be set. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ + +extern int mgmt_fe_send_setcfg_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, + Mgmtd__YangCfgDataReq **config_req, + int num_req, bool implicit_commit, + Mgmtd__DatastoreId dst_ds_id); + +/* + * Send SET_COMMMIT_REQ to MGMTD for one or more config data(s). + * + * lib_hndl + * Client library handler. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * src_ds_id + * Source datastore ID from where data needs to be committed from. + * + * dst_ds_id + * Destination datastore ID where data needs to be committed to. + * + * validate_only + * TRUE if data needs to be validated only, FALSE otherwise. + * + * abort + * TRUE if need to restore Src DS back to Dest DS, FALSE otherwise. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_commitcfg_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dst_ds_id, + bool validate_only, bool abort); + +/* + * Send GET_REQ to MGMTD for one or more config data item(s). + * + * If is_config is true gets config from the MGMTD datastore, otherwise + * operational state is queried from the backend clients. + * + * lib_hndl + * Client library handler. + * + * session_id + * Client session ID. + * + * is_config + * True if get-config else get-data. + * + * req_id + * Client request ID. + * + * ds_id + * Datastore ID (Running/Candidate) + * + * data_req + * Get xpaths requested. + * + * num_req + * Number of get xpath requests. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_get_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + bool is_config, Mgmtd__DatastoreId ds_id, + Mgmtd__YangGetDataReq **data_req, int num_reqs); + + +/* + * Send NOTIFY_REGISTER_REQ to MGMTD daemon. + * + * lib_hndl + * Client library handler. + * + * session_id + * Client session ID. + * + * req_id + * Client request ID. + * + * ds_id + * Datastore ID. + * + * register_req + * TRUE if registering, FALSE otherwise. + * + * data_req + * Details of the YANG notification data. + * + * num_reqs + * Number of data requests. + * + * Returns: + * 0 on success, otherwise msg_conn_send_msg() return values. + */ +extern int mgmt_fe_send_regnotify_req(struct mgmt_fe_client *client, + uint64_t session_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, + bool register_req, + Mgmtd__YangDataXPath **data_req, + int num_reqs); + +/* + * Destroy library and cleanup everything. + */ +extern void mgmt_fe_client_destroy(struct mgmt_fe_client *client); + +/* + * Get count of open sessions. + */ +extern uint mgmt_fe_client_session_count(struct mgmt_fe_client *client); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_MGMTD_FE_CLIENT_H_ */ diff --git a/lib/mgmt_msg.c b/lib/mgmt_msg.c new file mode 100644 index 000000000000..ee5c1008bd0c --- /dev/null +++ b/lib/mgmt_msg.c @@ -0,0 +1,926 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * March 6 2023, Christian Hopps <chopps@labn.net> + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ +#include <zebra.h> +#include "debug.h" +#include "network.h" +#include "sockopt.h" +#include "stream.h" +#include "frrevent.h" +#include "mgmt_msg.h" + + +#define MGMT_MSG_DBG(dbgtag, fmt, ...) \ + do { \ + if (dbgtag) \ + zlog_debug("%s: %s: " fmt, dbgtag, __func__, \ + ##__VA_ARGS__); \ + } while (0) + +#define MGMT_MSG_ERR(ms, fmt, ...) \ + zlog_err("%s: %s: " fmt, (ms)->idtag, __func__, ##__VA_ARGS__) + +DEFINE_MTYPE(LIB, MSG_CONN, "msg connection state"); + +/** + * Read data from a socket into streams containing 1 or more full msgs headed by + * mgmt_msg_hdr which contain API messages (currently protobuf). + * + * Args: + * ms: mgmt_msg_state for this process. + * fd: socket/file to read data from. + * debug: true to enable debug logging. + * + * Returns: + * MPP_DISCONNECT - socket should be closed and connect retried. + * MSV_SCHED_STREAM - this call should be rescheduled to run. + * MPP_SCHED_BOTH - this call and the procmsg buf should be scheduled to + *run. + */ +enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd, + bool debug) +{ + const char *dbgtag = debug ? ms->idtag : NULL; + size_t avail = STREAM_WRITEABLE(ms->ins); + struct mgmt_msg_hdr *mhdr = NULL; + size_t total = 0; + size_t mcount = 0; + ssize_t n, left; + + assert(ms && fd != -1); + + /* + * Read as much as we can into the stream. + */ + while (avail > sizeof(struct mgmt_msg_hdr)) { + n = stream_read_try(ms->ins, fd, avail); + + /* -2 is normal nothing read, and to retry */ + if (n == -2) { + MGMT_MSG_DBG(dbgtag, "nothing more to read"); + break; + } + if (n <= 0) { + if (n == 0) + MGMT_MSG_ERR(ms, "got EOF/disconnect"); + else + MGMT_MSG_ERR(ms, + "got error while reading: '%s'", + safe_strerror(errno)); + return MSR_DISCONNECT; + } + MGMT_MSG_DBG(dbgtag, "read %zd bytes", n); + ms->nrxb += n; + avail -= n; + } + + /* + * Check if we have read a complete messages or not. + */ + assert(stream_get_getp(ms->ins) == 0); + left = stream_get_endp(ms->ins); + while (left > (long)sizeof(struct mgmt_msg_hdr)) { + mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total); + if (!MGMT_MSG_IS_MARKER(mhdr->marker)) { + MGMT_MSG_DBG(dbgtag, "recv corrupt buffer, disconnect"); + return MSR_DISCONNECT; + } + if ((ssize_t)mhdr->len > left) + break; + + MGMT_MSG_DBG(dbgtag, "read full message len %u", mhdr->len); + total += mhdr->len; + left -= mhdr->len; + mcount++; + } + + if (!mcount) + return MSR_SCHED_STREAM; + + /* + * We have read at least one message into the stream, queue it up. + */ + mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(ms->ins) + total); + stream_set_endp(ms->ins, total); + stream_fifo_push(&ms->inq, ms->ins); + ms->ins = stream_new(ms->max_msg_sz); + if (left) { + stream_put(ms->ins, mhdr, left); + stream_set_endp(ms->ins, left); + } + + return MSR_SCHED_BOTH; +} + +/** + * Process streams containing whole messages that have been pushed onto the + * FIFO. This should be called from an event/timer handler and should be + * reschedulable. + * + * Args: + * ms: mgmt_msg_state for this process. + * handle_mgs: function to call for each received message. + * user: opaque value passed through to handle_msg. + * debug: true to enable debug logging. + * + * Returns: + * true if more to process (so reschedule) else false + */ +bool mgmt_msg_procbufs(struct mgmt_msg_state *ms, + void (*handle_msg)(uint8_t version, uint8_t *msg, + size_t msglen, void *user), + void *user, bool debug) +{ + const char *dbgtag = debug ? ms->idtag : NULL; + struct mgmt_msg_hdr *mhdr; + struct stream *work; + uint8_t *data; + size_t left, nproc; + + MGMT_MSG_DBG(dbgtag, "Have %zu streams to process", ms->inq.count); + + nproc = 0; + while (nproc < ms->max_read_buf) { + work = stream_fifo_pop(&ms->inq); + if (!work) + break; + + data = STREAM_DATA(work); + left = stream_get_endp(work); + MGMT_MSG_DBG(dbgtag, "Processing stream of len %zu", left); + + for (; left > sizeof(struct mgmt_msg_hdr); + left -= mhdr->len, data += mhdr->len) { + mhdr = (struct mgmt_msg_hdr *)data; + + assert(MGMT_MSG_IS_MARKER(mhdr->marker)); + assert(left >= mhdr->len); + + handle_msg(MGMT_MSG_MARKER_VERSION(mhdr->marker), + (uint8_t *)(mhdr + 1), + mhdr->len - sizeof(struct mgmt_msg_hdr), + user); + ms->nrxm++; + nproc++; + } + + if (work != ms->ins) + stream_free(work); /* Free it up */ + else + stream_reset(work); /* Reset stream for next read */ + } + + /* return true if should reschedule b/c more to process. */ + return stream_fifo_head(&ms->inq) != NULL; +} + +/** + * Write data from a onto the socket, using streams that have been queued for + * sending by mgmt_msg_send_msg. This function should be reschedulable. + * + * Args: + * ms: mgmt_msg_state for this process. + * fd: socket/file to read data from. + * debug: true to enable debug logging. + * + * Returns: + * MSW_SCHED_NONE - do not reschedule anything. + * MSW_SCHED_STREAM - this call should be rescheduled to run again. + * MSW_SCHED_WRITES_OFF - writes should be disabled with a timer to + * re-enable them a short time later + * MSW_DISCONNECT - socket should be closed and reconnect retried. + *run. + */ +enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd, + bool debug) +{ + const char *dbgtag = debug ? ms->idtag : NULL; + struct stream *s; + size_t nproc = 0; + ssize_t left; + ssize_t n; + + if (ms->outs) { + MGMT_MSG_DBG(dbgtag, + "found unqueued stream with %zu bytes, queueing", + stream_get_endp(ms->outs)); + stream_fifo_push(&ms->outq, ms->outs); + ms->outs = NULL; + } + + for (s = stream_fifo_head(&ms->outq); s && nproc < ms->max_write_buf; + s = stream_fifo_head(&ms->outq)) { + left = STREAM_READABLE(s); + assert(left); + + n = stream_flush(s, fd); + if (n <= 0) { + if (n == 0) + MGMT_MSG_ERR(ms, + "connection closed while writing"); + else if (ERRNO_IO_RETRY(errno)) { + MGMT_MSG_DBG( + dbgtag, + "retry error while writing %zd bytes: %s (%d)", + left, safe_strerror(errno), errno); + return MSW_SCHED_STREAM; + } else + MGMT_MSG_ERR( + ms, + "error while writing %zd bytes: %s (%d)", + left, safe_strerror(errno), errno); + + n = mgmt_msg_reset_writes(ms); + MGMT_MSG_DBG(dbgtag, "drop and freed %zd streams", n); + + return MSW_DISCONNECT; + } + + ms->ntxb += n; + if (n != left) { + MGMT_MSG_DBG(dbgtag, "short stream write %zd of %zd", n, + left); + stream_forward_getp(s, n); + return MSW_SCHED_STREAM; + } + + stream_free(stream_fifo_pop(&ms->outq)); + MGMT_MSG_DBG(dbgtag, "wrote stream of %zd bytes", n); + nproc++; + } + if (s) { + MGMT_MSG_DBG( + dbgtag, + "reached %zu buffer writes, pausing with %zu streams left", + ms->max_write_buf, ms->outq.count); + return MSW_SCHED_STREAM; + } + MGMT_MSG_DBG(dbgtag, "flushed all streams from output q"); + return MSW_SCHED_NONE; +} + + +/** + * Send a message by enqueueing it to be written over the socket by + * mgmt_msg_write. + * + * Args: + * ms: mgmt_msg_state for this process. + * version: version of this message, will be given to receiving side. + * msg: the message to be sent. + * len: the length of the message. + * packf: a function to pack the message. + * debug: true to enable debug logging. + * + * Returns: + * 0 on success, otherwise -1 on failure. The only failure mode is if a + * the message exceeds the maximum message size configured on init. + */ +int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, void *msg, + size_t len, size_t (*packf)(void *msg, void *buf), + bool debug) +{ + const char *dbgtag = debug ? ms->idtag : NULL; + struct mgmt_msg_hdr *mhdr; + struct stream *s; + uint8_t *dstbuf; + size_t endp, n; + size_t mlen = len + sizeof(*mhdr); + + if (mlen > ms->max_msg_sz) { + MGMT_MSG_ERR(ms, "Message %zu > max size %zu, dropping", mlen, + ms->max_msg_sz); + return -1; + } + + if (!ms->outs) { + MGMT_MSG_DBG(dbgtag, "creating new stream for msg len %zu", + len); + ms->outs = stream_new(ms->max_msg_sz); + } else if (STREAM_WRITEABLE(ms->outs) < mlen) { + MGMT_MSG_DBG( + dbgtag, + "enq existing stream len %zu and creating new stream for msg len %zu", + STREAM_WRITEABLE(ms->outs), mlen); + stream_fifo_push(&ms->outq, ms->outs); + ms->outs = stream_new(ms->max_msg_sz); + } else { + MGMT_MSG_DBG( + dbgtag, + "using existing stream with avail %zu for msg len %zu", + STREAM_WRITEABLE(ms->outs), mlen); + } + s = ms->outs; + + /* We have a stream with space, pack the message into it. */ + mhdr = (struct mgmt_msg_hdr *)(STREAM_DATA(s) + s->endp); + mhdr->marker = MGMT_MSG_MARKER(version); + mhdr->len = mlen; + stream_forward_endp(s, sizeof(*mhdr)); + endp = stream_get_endp(s); + dstbuf = STREAM_DATA(s) + endp; + if (packf) + n = packf(msg, dstbuf); + else { + memcpy(dstbuf, msg, len); + n = len; + } + stream_set_endp(s, endp + n); + ms->ntxm++; + + return 0; +} + +/** + * Create and open a unix domain stream socket on the given path + * setting non-blocking and send and receive buffer sizes. + * + * Args: + * path: path of unix domain socket to connect to. + * sendbuf: size of socket send buffer. + * recvbuf: size of socket receive buffer. + * dbgtag: if non-NULL enable log debug, and use this tag. + * + * Returns: + * socket fd or -1 on error. + */ +int mgmt_msg_connect(const char *path, size_t sendbuf, size_t recvbuf, + const char *dbgtag) +{ + int ret, sock, len; + struct sockaddr_un addr; + + MGMT_MSG_DBG(dbgtag, "connecting to server on %s", path); + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + MGMT_MSG_DBG(dbgtag, "socket failed: %s", safe_strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); +#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN + len = addr.sun_len = SUN_LEN(&addr); +#else + len = sizeof(addr.sun_family) + strlen(addr.sun_path); +#endif /* HAVE_STRUCT_SOCKADDR_UN_SUN_LEN */ + ret = connect(sock, (struct sockaddr *)&addr, len); + if (ret < 0) { + MGMT_MSG_DBG(dbgtag, "failed to connect on %s: %s", path, + safe_strerror(errno)); + close(sock); + return -1; + } + + MGMT_MSG_DBG(dbgtag, "connected to server on %s", path); + set_nonblocking(sock); + setsockopt_so_sendbuf(sock, sendbuf); + setsockopt_so_recvbuf(sock, recvbuf); + return sock; +} + +/** + * Reset the sending queue, by dequeueing all streams and freeing them. Return + * the number of streams freed. + * + * Args: + * ms: mgmt_msg_state for this process. + * + * Returns: + * Number of streams that were freed. + * + */ +size_t mgmt_msg_reset_writes(struct mgmt_msg_state *ms) +{ + struct stream *s; + size_t nproc = 0; + + for (s = stream_fifo_pop(&ms->outq); s; + s = stream_fifo_pop(&ms->outq), nproc++) + stream_free(s); + + return nproc; +} + + +void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf, + size_t max_write_buf, size_t max_msg_sz, const char *idtag) +{ + memset(ms, 0, sizeof(*ms)); + ms->ins = stream_new(max_msg_sz); + stream_fifo_init(&ms->inq); + stream_fifo_init(&ms->outq); + ms->max_read_buf = max_write_buf; + ms->max_write_buf = max_read_buf; + ms->max_msg_sz = max_msg_sz; + ms->idtag = strdup(idtag); +} + +void mgmt_msg_destroy(struct mgmt_msg_state *ms) +{ + mgmt_msg_reset_writes(ms); + if (ms->ins) + stream_free(ms->ins); + free(ms->idtag); +} + +/* + * Connections + */ + +#define MSG_CONN_DEFAULT_CONN_RETRY_MSEC 250 +#define MSG_CONN_SEND_BUF_SIZE (1u << 16) +#define MSG_CONN_RECV_BUF_SIZE (1u << 16) + +static void msg_client_sched_connect(struct msg_client *client, + unsigned long msec); + +static void msg_conn_sched_proc_msgs(struct msg_conn *conn); +static void msg_conn_sched_read(struct msg_conn *conn); +static void msg_conn_sched_write(struct msg_conn *conn); + +static void msg_conn_write(struct event *thread) +{ + struct msg_conn *conn = EVENT_ARG(thread); + enum mgmt_msg_wsched rv; + + rv = mgmt_msg_write(&conn->mstate, conn->fd, conn->debug); + if (rv == MSW_SCHED_STREAM) + msg_conn_sched_write(conn); + else if (rv == MSW_DISCONNECT) + msg_conn_disconnect(conn, conn->is_client); + else + assert(rv == MSW_SCHED_NONE); +} + +static void msg_conn_read(struct event *thread) +{ + struct msg_conn *conn = EVENT_ARG(thread); + enum mgmt_msg_rsched rv; + + rv = mgmt_msg_read(&conn->mstate, conn->fd, conn->debug); + if (rv == MSR_DISCONNECT) { + msg_conn_disconnect(conn, conn->is_client); + return; + } + if (rv == MSR_SCHED_BOTH) + msg_conn_sched_proc_msgs(conn); + msg_conn_sched_read(conn); +} + +/* collapse this into mgmt_msg_procbufs */ +static void msg_conn_proc_msgs(struct event *thread) +{ + struct msg_conn *conn = EVENT_ARG(thread); + + if (mgmt_msg_procbufs(&conn->mstate, + (void (*)(uint8_t, uint8_t *, size_t, + void *))conn->handle_msg, + conn, conn->debug)) + /* there's more, schedule handling more */ + msg_conn_sched_proc_msgs(conn); +} + +static void msg_conn_sched_read(struct msg_conn *conn) +{ + event_add_read(conn->loop, msg_conn_read, conn, conn->fd, + &conn->read_ev); +} + +static void msg_conn_sched_write(struct msg_conn *conn) +{ + event_add_write(conn->loop, msg_conn_write, conn, conn->fd, + &conn->write_ev); +} + +static void msg_conn_sched_proc_msgs(struct msg_conn *conn) +{ + event_add_event(conn->loop, msg_conn_proc_msgs, conn, 0, + &conn->proc_msg_ev); +} + + +void msg_conn_disconnect(struct msg_conn *conn, bool reconnect) +{ + + /* disconnect short-circuit if present */ + if (conn->remote_conn) { + conn->remote_conn->remote_conn = NULL; + conn->remote_conn = NULL; + } + + if (conn->fd != -1) { + close(conn->fd); + conn->fd = -1; + + /* Notify client through registered callback (if any) */ + if (conn->notify_disconnect) + (void)(*conn->notify_disconnect)(conn); + } + + if (reconnect) { + assert(conn->is_client); + msg_client_sched_connect( + container_of(conn, struct msg_client, conn), + MSG_CONN_DEFAULT_CONN_RETRY_MSEC); + } +} + +int msg_conn_send_msg(struct msg_conn *conn, uint8_t version, void *msg, + size_t mlen, size_t (*packf)(void *, void *), + bool short_circuit_ok) +{ + const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL; + + if (conn->fd == -1) { + MGMT_MSG_ERR(&conn->mstate, + "can't send message on closed connection"); + return -1; + } + + /* immediately handle the message if short-circuit is present */ + if (conn->remote_conn && short_circuit_ok) { + uint8_t *buf = msg; + size_t n = mlen; + bool old; + + if (packf) { + buf = XMALLOC(MTYPE_TMP, mlen); + n = packf(msg, buf); + } + + ++conn->short_circuit_depth; + MGMT_MSG_DBG(dbgtag, "SC send: depth %u msg: %p", + conn->short_circuit_depth, msg); + + old = conn->remote_conn->is_short_circuit; + conn->remote_conn->is_short_circuit = true; + conn->remote_conn->handle_msg(version, buf, n, + conn->remote_conn); + conn->remote_conn->is_short_circuit = old; + + --conn->short_circuit_depth; + MGMT_MSG_DBG(dbgtag, "SC return from depth: %u msg: %p", + conn->short_circuit_depth, msg); + + if (packf) + XFREE(MTYPE_TMP, buf); + return 0; + } + + int rv = mgmt_msg_send_msg(&conn->mstate, version, msg, mlen, packf, + conn->debug); + + msg_conn_sched_write(conn); + + return rv; +} + +void msg_conn_cleanup(struct msg_conn *conn) +{ + struct mgmt_msg_state *ms = &conn->mstate; + + /* disconnect short-circuit if present */ + if (conn->remote_conn) { + conn->remote_conn->remote_conn = NULL; + conn->remote_conn = NULL; + } + + if (conn->fd != -1) { + close(conn->fd); + conn->fd = -1; + } + + EVENT_OFF(conn->read_ev); + EVENT_OFF(conn->write_ev); + EVENT_OFF(conn->proc_msg_ev); + + mgmt_msg_destroy(ms); +} + +/* + * Client Connections + */ + +DECLARE_LIST(msg_server_list, struct msg_server, link); + +static struct msg_server_list_head msg_servers; + +static void msg_client_connect(struct msg_client *conn); + +static void msg_client_connect_timer(struct event *thread) +{ + msg_client_connect(EVENT_ARG(thread)); +} + +static void msg_client_sched_connect(struct msg_client *client, + unsigned long msec) +{ + struct msg_conn *conn = &client->conn; + const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL; + + MGMT_MSG_DBG(dbgtag, "connection retry in %lu msec", msec); + if (msec) + event_add_timer_msec(conn->loop, msg_client_connect_timer, + client, msec, &client->conn_retry_tmr); + else + event_add_event(conn->loop, msg_client_connect_timer, client, 0, + &client->conn_retry_tmr); +} + +static bool msg_client_connect_short_circuit(struct msg_client *client) +{ + struct msg_conn *server_conn; + struct msg_server *server; + const char *dbgtag = + client->conn.debug ? client->conn.mstate.idtag : NULL; + union sockunion su = {0}; + int sockets[2]; + + frr_each (msg_server_list, &msg_servers, server) + if (!strcmp(server->sopath, client->sopath)) + break; + if (!server) { + MGMT_MSG_DBG(dbgtag, + "no short-circuit connection available for %s", + client->sopath); + + return false; + } + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets)) { + MGMT_MSG_ERR( + &client->conn.mstate, + "socketpair failed trying to short-circuit connection on %s: %s", + client->sopath, safe_strerror(errno)); + return false; + } + + /* client side */ + client->conn.fd = sockets[0]; + set_nonblocking(sockets[0]); + setsockopt_so_sendbuf(sockets[0], client->conn.mstate.max_write_buf); + setsockopt_so_recvbuf(sockets[0], client->conn.mstate.max_read_buf); + + /* server side */ + memset(&su, 0, sizeof(union sockunion)); + server_conn = server->create(sockets[1], &su); + + client->conn.remote_conn = server_conn; + server_conn->remote_conn = &client->conn; + + MGMT_MSG_DBG( + dbgtag, + "short-circuit connection on %s server %s:%d to client %s:%d", + client->sopath, server_conn->mstate.idtag, server_conn->fd, + client->conn.mstate.idtag, client->conn.fd); + + MGMT_MSG_DBG( + server_conn->debug ? server_conn->mstate.idtag : NULL, + "short-circuit connection on %s client %s:%d to server %s:%d", + client->sopath, client->conn.mstate.idtag, client->conn.fd, + server_conn->mstate.idtag, server_conn->fd); + + return true; +} + + +/* Connect and start reading from the socket */ +static void msg_client_connect(struct msg_client *client) +{ + struct msg_conn *conn = &client->conn; + const char *dbgtag = conn->debug ? conn->mstate.idtag : NULL; + + if (!client->short_circuit_ok || + !msg_client_connect_short_circuit(client)) + conn->fd = + mgmt_msg_connect(client->sopath, MSG_CONN_SEND_BUF_SIZE, + MSG_CONN_RECV_BUF_SIZE, dbgtag); + + if (conn->fd == -1) + /* retry the connection */ + msg_client_sched_connect(client, + MSG_CONN_DEFAULT_CONN_RETRY_MSEC); + else if (client->notify_connect && client->notify_connect(client)) + /* client connect notify failed */ + msg_conn_disconnect(conn, true); + else + /* start reading */ + msg_conn_sched_read(conn); +} + +void msg_client_init(struct msg_client *client, struct event_loop *tm, + const char *sopath, + int (*notify_connect)(struct msg_client *client), + int (*notify_disconnect)(struct msg_conn *client), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *client), + size_t max_read_buf, size_t max_write_buf, + size_t max_msg_sz, bool short_circuit_ok, + const char *idtag, bool debug) +{ + struct msg_conn *conn = &client->conn; + memset(client, 0, sizeof(*client)); + + conn->loop = tm; + conn->fd = -1; + conn->handle_msg = handle_msg; + conn->notify_disconnect = notify_disconnect; + conn->is_client = true; + conn->debug = debug; + client->short_circuit_ok = short_circuit_ok; + client->sopath = strdup(sopath); + client->notify_connect = notify_connect; + + mgmt_msg_init(&conn->mstate, max_read_buf, max_write_buf, max_msg_sz, + idtag); + + /* XXX maybe just have client kick this off */ + /* Start trying to connect to server */ + msg_client_sched_connect(client, 0); +} + +void msg_client_cleanup(struct msg_client *client) +{ + assert(client->conn.is_client); + + EVENT_OFF(client->conn_retry_tmr); + free(client->sopath); + + msg_conn_cleanup(&client->conn); +} + + +/* + * Server-side connections + */ + +static void msg_server_accept(struct event *event) +{ + struct msg_server *server = EVENT_ARG(event); + int fd; + union sockunion su; + + if (server->fd < 0) + return; + + /* We continue hearing server listen socket. */ + event_add_read(server->loop, msg_server_accept, server, server->fd, + &server->listen_ev); + + memset(&su, 0, sizeof(union sockunion)); + + /* We can handle IPv4 or IPv6 socket. */ + fd = sockunion_accept(server->fd, &su); + if (fd < 0) { + zlog_err("Failed to accept %s client connection: %s", + server->idtag, safe_strerror(errno)); + return; + } + set_nonblocking(fd); + set_cloexec(fd); + + DEBUGD(server->debug, "Accepted new %s connection", server->idtag); + + server->create(fd, &su); +} + +int msg_server_init(struct msg_server *server, const char *sopath, + struct event_loop *loop, + struct msg_conn *(*create)(int fd, union sockunion *su), + const char *idtag, struct debug *debug) +{ + int ret; + int sock; + struct sockaddr_un addr; + mode_t old_mask; + + memset(server, 0, sizeof(*server)); + server->fd = -1; + + sock = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); + if (sock < 0) { + zlog_err("Failed to create %s server socket: %s", server->idtag, + safe_strerror(errno)); + goto fail; + } + + addr.sun_family = AF_UNIX, + strlcpy(addr.sun_path, sopath, sizeof(addr.sun_path)); + unlink(addr.sun_path); + old_mask = umask(0077); + ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); + if (ret < 0) { + zlog_err("Failed to bind %s server socket to '%s': %s", + server->idtag, addr.sun_path, safe_strerror(errno)); + umask(old_mask); + goto fail; + } + umask(old_mask); + + ret = listen(sock, MGMTD_MAX_CONN); + if (ret < 0) { + zlog_err("Failed to listen on %s server socket: %s", + server->idtag, safe_strerror(errno)); + goto fail; + } + + server->fd = sock; + server->loop = loop; + server->sopath = strdup(sopath); + server->idtag = strdup(idtag); + server->create = create; + server->debug = debug; + + msg_server_list_add_head(&msg_servers, server); + + event_add_read(server->loop, msg_server_accept, server, server->fd, + &server->listen_ev); + + + DEBUGD(debug, "Started %s server, listening on %s", idtag, sopath); + return 0; + +fail: + if (sock >= 0) + close(sock); + server->fd = -1; + return -1; +} + +void msg_server_cleanup(struct msg_server *server) +{ + DEBUGD(server->debug, "Closing %s server", server->idtag); + + if (server->listen_ev) + EVENT_OFF(server->listen_ev); + + msg_server_list_del(&msg_servers, server); + + if (server->fd >= 0) + close(server->fd); + free((char *)server->sopath); + free((char *)server->idtag); + + memset(server, 0, sizeof(*server)); + server->fd = -1; +} + +/* + * Initialize and start reading from the accepted socket + * + * notify_connect - only called for disconnect i.e., connected == false + */ +void msg_conn_accept_init(struct msg_conn *conn, struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + const char *idtag) +{ + conn->loop = tm; + conn->fd = fd; + conn->notify_disconnect = notify_disconnect; + conn->handle_msg = handle_msg; + conn->is_client = false; + + mgmt_msg_init(&conn->mstate, max_read, max_write, max_size, idtag); + + /* start reading */ + msg_conn_sched_read(conn); + + /* Make socket non-blocking. */ + set_nonblocking(conn->fd); + setsockopt_so_sendbuf(conn->fd, MSG_CONN_SEND_BUF_SIZE); + setsockopt_so_recvbuf(conn->fd, MSG_CONN_RECV_BUF_SIZE); +} + +struct msg_conn * +msg_server_conn_create(struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + void *user, const char *idtag) +{ + struct msg_conn *conn = XMALLOC(MTYPE_MSG_CONN, sizeof(*conn)); + memset(conn, 0, sizeof(*conn)); + msg_conn_accept_init(conn, tm, fd, notify_disconnect, handle_msg, + max_read, max_write, max_size, idtag); + conn->user = user; + return conn; +} + +void msg_server_conn_delete(struct msg_conn *conn) +{ + if (!conn) + return; + msg_conn_cleanup(conn); + XFREE(MTYPE_MSG_CONN, conn); +} diff --git a/lib/mgmt_msg.h b/lib/mgmt_msg.h new file mode 100644 index 000000000000..dd7ae59f9162 --- /dev/null +++ b/lib/mgmt_msg.h @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * March 6 2023, Christian Hopps <chopps@labn.net> + * + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ +#ifndef _MGMT_MSG_H +#define _MGMT_MSG_H + +#include "memory.h" +#include "stream.h" +#include "frrevent.h" + +DECLARE_MTYPE(MSG_CONN); + +/* + * Messages on the stream start with a marker that encodes a version octet. + */ +#define MGMT_MSG_MARKER_PFX (0x23232300u) /* ASCII - "###\ooo"*/ +#define MGMT_MSG_IS_MARKER(x) (((x)&0xFFFFFF00u) == MGMT_MSG_MARKER_PFX) +#define MGMT_MSG_MARKER(version) (MGMT_MSG_MARKER_PFX | (version)) +#define MGMT_MSG_MARKER_VERSION(x) (0xFF & (x)) + +#define MGMT_MSG_VERSION_PROTOBUF 0 +#define MGMT_MSG_VERSION_NATIVE 1 + + +struct mgmt_msg_state { + struct stream *ins; + struct stream *outs; + struct stream_fifo inq; + struct stream_fifo outq; + uint64_t nrxm; /* number of received messages */ + uint64_t nrxb; /* number of received bytes */ + uint64_t ntxm; /* number of sent messages */ + uint64_t ntxb; /* number of sent bytes */ + size_t max_read_buf; /* should replace with max time value */ + size_t max_write_buf; /* should replace with max time value */ + size_t max_msg_sz; + char *idtag; /* identifying tag for messages */ +}; + +struct mgmt_msg_hdr { + uint32_t marker; + uint32_t len; +}; + +enum mgmt_msg_rsched { + MSR_SCHED_BOTH, /* schedule both queue and read */ + MSR_SCHED_STREAM, /* schedule read */ + MSR_DISCONNECT, /* disconnect and start reconnecting */ +}; + +enum mgmt_msg_wsched { + MSW_SCHED_NONE, /* no scheduling required */ + MSW_SCHED_STREAM, /* schedule writing */ + MSW_DISCONNECT, /* disconnect and start reconnecting */ +}; + +struct msg_conn; + + +extern int mgmt_msg_connect(const char *path, size_t sendbuf, size_t recvbuf, + const char *dbgtag); +extern bool mgmt_msg_procbufs(struct mgmt_msg_state *ms, + void (*handle_msg)(uint8_t version, uint8_t *msg, + size_t msglen, void *user), + void *user, bool debug); +extern enum mgmt_msg_rsched mgmt_msg_read(struct mgmt_msg_state *ms, int fd, + bool debug); +extern size_t mgmt_msg_reset_writes(struct mgmt_msg_state *ms); +extern int mgmt_msg_send_msg(struct mgmt_msg_state *ms, uint8_t version, + void *msg, size_t len, + size_t (*packf)(void *msg, void *buf), bool debug); +extern enum mgmt_msg_wsched mgmt_msg_write(struct mgmt_msg_state *ms, int fd, + bool debug); + +extern void mgmt_msg_destroy(struct mgmt_msg_state *state); + +extern void mgmt_msg_init(struct mgmt_msg_state *ms, size_t max_read_buf, + size_t max_write_buf, size_t max_msg_sz, + const char *idtag); + +/* + * Connections + */ + +struct msg_conn { + int fd; + struct mgmt_msg_state mstate; + struct event_loop *loop; + struct event *read_ev; + struct event *write_ev; + struct event *proc_msg_ev; + struct msg_conn *remote_conn; + int (*notify_disconnect)(struct msg_conn *conn); + void (*handle_msg)(uint8_t version, uint8_t *data, size_t len, + struct msg_conn *conn); + void *user; + uint short_circuit_depth; + bool is_short_circuit; /* true when the message being handled is SC */ + bool is_client; + bool debug; +}; + + +/* + * `notify_disconnect` is not called when `msg_conn_cleanup` is called for a + * msg_conn which is currently connected. The socket is closed but there is no + * notification. + */ +extern void msg_conn_cleanup(struct msg_conn *conn); +extern void msg_conn_disconnect(struct msg_conn *conn, bool reconnect); +extern int msg_conn_send_msg(struct msg_conn *client, uint8_t version, + void *msg, size_t mlen, + size_t (*packf)(void *, void *), + bool short_circuit_ok); + +/* + * Client-side Connections + */ + +struct msg_client { + struct msg_conn conn; + struct event *conn_retry_tmr; + char *sopath; + int (*notify_connect)(struct msg_client *client); + bool short_circuit_ok; +}; + +/* + * `notify_disconnect` is not called when `msg_client_cleanup` is called for a + * msg_client which is currently connected. The socket is closed but there is no + * notification. + */ +extern void msg_client_cleanup(struct msg_client *client); + +/* + * `notify_disconnect` is not called when the user `msg_client_cleanup` is + * called for a client which is currently connected. The socket is closed + * but there is no notification. + */ +extern void +msg_client_init(struct msg_client *client, struct event_loop *tm, + const char *sopath, + int (*notify_connect)(struct msg_client *client), + int (*notify_disconnect)(struct msg_conn *client), + void (*handle_msg)(uint8_t version, uint8_t *data, size_t len, + struct msg_conn *client), + size_t max_read_buf, size_t max_write_buf, size_t max_msg_sz, + bool short_circuit_ok, const char *idtag, bool debug); + +/* + * Server-side Connections + */ +#define MGMTD_MAX_CONN 32 + +PREDECL_LIST(msg_server_list); + +struct msg_server { + int fd; + struct msg_server_list_item link; + struct event_loop *loop; + struct event *listen_ev; + const char *sopath; + const char *idtag; + struct msg_conn *(*create)(int fd, union sockunion *su); + struct debug *debug; +}; + +extern int msg_server_init(struct msg_server *server, const char *sopath, + struct event_loop *loop, + struct msg_conn *(*create)(int fd, + union sockunion *su), + const char *idtag, struct debug *debug); +extern void msg_server_cleanup(struct msg_server *server); + +/* + * `notify_disconnect` is not called when the user `msg_conn_cleanup` is + * called for a client which is currently connected. The socket is closed + * but there is no notification. + */ +struct msg_conn * +msg_server_conn_create(struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + void *user, const char *idtag); + +extern void msg_server_conn_delete(struct msg_conn *conn); + +extern void +msg_conn_accept_init(struct msg_conn *conn, struct event_loop *tm, int fd, + int (*notify_disconnect)(struct msg_conn *conn), + void (*handle_msg)(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn), + size_t max_read, size_t max_write, size_t max_size, + const char *idtag); + +#endif /* _MGMT_MSG_H */ diff --git a/lib/mgmt_pb.h b/lib/mgmt_pb.h new file mode 100644 index 000000000000..08bb748233b7 --- /dev/null +++ b/lib/mgmt_pb.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD protobuf main header file + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_PB_H_ +#define _FRR_MGMTD_PB_H_ + +#include "lib/mgmt.pb-c.h" + +#define mgmt_yang_data_xpath_init(ptr) mgmtd__yang_data_xpath__init(ptr) + +#define mgmt_yang_data_value_init(ptr) mgmtd__yang_data_value__init(ptr) + +#define mgmt_yang_data_init(ptr) mgmtd__yang_data__init(ptr) + +#define mgmt_yang_data_reply_init(ptr) mgmtd__yang_data_reply__init(ptr) + +#define mgmt_yang_cfg_data_req_init(ptr) mgmtd__yang_cfg_data_req__init(ptr) + +#define mgmt_yang_get_data_req_init(ptr) mgmtd__yang_get_data_req__init(ptr) + +#endif /* _FRR_MGMTD_PB_H_ */ diff --git a/lib/nexthop.c b/lib/nexthop.c index b04c95c05e11..dcbb76b68e10 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -1076,3 +1076,12 @@ static ssize_t printfrr_nh(struct fbuf *buf, struct printfrr_eargs *ea, } return -1; } + +bool nexthop_is_ifindex_type(const struct nexthop *nh) +{ + if (nh->type == NEXTHOP_TYPE_IFINDEX || + nh->type == NEXTHOP_TYPE_IPV4_IFINDEX || + nh->type == NEXTHOP_TYPE_IPV6_IFINDEX) + return true; + return false; +} diff --git a/lib/nexthop.h b/lib/nexthop.h index 1d95a3eee9b8..43dd71e11231 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -234,6 +234,9 @@ extern struct nexthop *nexthop_dup(const struct nexthop *nexthop, extern struct nexthop *nexthop_dup_no_recurse(const struct nexthop *nexthop, struct nexthop *rparent); +/* Check nexthop of IFINDEX type */ +extern bool nexthop_is_ifindex_type(const struct nexthop *nh); + /* * 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 diff --git a/lib/northbound.c b/lib/northbound.c index 6f2c522a294f..ef2344ee110c 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -93,7 +93,9 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg) { struct nb_node *nb_node; struct lysc_node *sparent, *sparent_list; + struct frr_yang_module_info *module; + module = (struct frr_yang_module_info *)arg; nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node)); yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath, sizeof(nb_node->xpath)); @@ -128,6 +130,9 @@ static int nb_node_new_cb(const struct lysc_node *snode, void *arg) assert(snode->priv == NULL); ((struct lysc_node *)snode)->priv = nb_node; + if (module && module->ignore_cbs) + SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS); + return YANG_ITER_CONTINUE; } @@ -162,7 +167,7 @@ struct nb_node *nb_node_find(const char *path) * Use libyang to find the schema node associated to the path and get * the northbound node from there (snode private pointer). */ - snode = lys_find_path(ly_native_ctx, NULL, path, 0); + snode = yang_find_snode(ly_native_ctx, path, 0); if (!snode) return NULL; @@ -230,6 +235,9 @@ static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node) { unsigned int error = 0; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return error; + error += nb_node_validate_cb(nb_node, NB_OP_CREATE, !!nb_node->cbs.create, false); error += nb_node_validate_cb(nb_node, NB_OP_MODIFY, @@ -297,6 +305,8 @@ struct nb_config *nb_config_new(struct lyd_node *dnode) config->dnode = yang_dnode_new(ly_native_ctx, true); config->version = 0; + RB_INIT(nb_config_cbs, &config->cfg_chgs); + return config; } @@ -304,6 +314,7 @@ void nb_config_free(struct nb_config *config) { if (config->dnode) yang_dnode_free(config->dnode); + nb_config_diff_del_changes(&config->cfg_chgs); XFREE(MTYPE_NB_CONFIG, config); } @@ -315,6 +326,8 @@ struct nb_config *nb_config_dup(const struct nb_config *config) dup->dnode = yang_dnode_dup(config->dnode); dup->version = config->version; + RB_INIT(nb_config_cbs, &dup->cfg_chgs); + return dup; } @@ -405,7 +418,7 @@ static void nb_config_diff_add_change(struct nb_config_cbs *changes, RB_INSERT(nb_config_cbs, changes, &change->cb); } -static void nb_config_diff_del_changes(struct nb_config_cbs *changes) +void nb_config_diff_del_changes(struct nb_config_cbs *changes) { while (!RB_EMPTY(nb_config_cbs, changes)) { struct nb_config_change *change; @@ -422,8 +435,8 @@ 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, uint32_t *seq, - struct nb_config_cbs *changes) +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; @@ -525,10 +538,16 @@ static inline void nb_config_diff_dnode_log(const char *context, } #endif -/* Calculate the delta between two different configurations. */ -static void nb_config_diff(const struct nb_config *config1, - const struct nb_config *config2, - struct nb_config_cbs *changes) +/* + * Calculate the delta between two different configurations. + * + * NOTE: 'config1' is the reference DB, while 'config2' is + * the DB being compared against 'config1'. Typically 'config1' + * should be the Running DB and 'config2' is the Candidate DB. + */ +void nb_config_diff(const struct nb_config *config1, + const struct nb_config *config2, + struct nb_config_cbs *changes) { struct lyd_node *diff = NULL; const struct lyd_node *root, *dnode; @@ -734,6 +753,170 @@ int nb_candidate_edit(struct nb_config *candidate, return NB_OK; } +static void nb_update_candidate_changes(struct nb_config *candidate, + struct nb_cfg_change *change, + uint32_t *seq) +{ + enum nb_operation oper = change->operation; + char *xpath = change->xpath; + struct lyd_node *root = NULL; + struct lyd_node *dnode; + struct nb_config_cbs *cfg_chgs = &candidate->cfg_chgs; + int op; + + switch (oper) { + case NB_OP_CREATE: + case NB_OP_MODIFY: + root = yang_dnode_get(candidate->dnode, xpath); + break; + case NB_OP_DESTROY: + root = yang_dnode_get(running_config->dnode, xpath); + /* code */ + break; + case NB_OP_MOVE: + case NB_OP_PRE_VALIDATE: + case NB_OP_APPLY_FINISH: + case NB_OP_GET_ELEM: + case NB_OP_GET_NEXT: + case NB_OP_GET_KEYS: + case NB_OP_LOOKUP_ENTRY: + case NB_OP_RPC: + break; + default: + assert(!"non-enum value, invalid"); + } + + if (!root) + return; + + LYD_TREE_DFS_BEGIN (root, dnode) { + op = nb_lyd_diff_get_op(dnode); + switch (op) { + case 'c': /* create */ + nb_config_diff_created(dnode, seq, cfg_chgs); + LYD_TREE_DFS_continue = 1; + break; + case 'd': /* delete */ + nb_config_diff_deleted(dnode, seq, cfg_chgs); + LYD_TREE_DFS_continue = 1; + break; + case 'r': /* replace */ + nb_config_diff_add_change(cfg_chgs, NB_OP_MODIFY, seq, + dnode); + break; + case 'n': /* none */ + default: + break; + } + LYD_TREE_DFS_END(root, dnode); + } +} + +static bool nb_is_operation_allowed(struct nb_node *nb_node, + struct nb_cfg_change *change) +{ + enum nb_operation oper = change->operation; + + if (lysc_is_key(nb_node->snode)) { + if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY) + return false; + } + return true; +} + +void nb_candidate_edit_config_changes( + struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[], + size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath, + int xpath_index, char *err_buf, int err_bufsize, bool *error) +{ + uint32_t seq = 0; + + if (error) + *error = false; + + if (xpath_base == NULL) + xpath_base = ""; + + /* Edit candidate configuration. */ + for (size_t i = 0; i < num_cfg_changes; i++) { + struct nb_cfg_change *change = &cfg_changes[i]; + struct nb_node *nb_node; + char xpath[XPATH_MAXLEN]; + struct yang_data *data; + int ret; + + /* Handle relative XPaths. */ + memset(xpath, 0, sizeof(xpath)); + if (xpath_index > 0 && + (xpath_base[0] == '.' || change->xpath[0] == '.')) + strlcpy(xpath, curr_xpath, sizeof(xpath)); + if (xpath_base[0]) { + if (xpath_base[0] == '.') + strlcat(xpath, xpath_base + 1, sizeof(xpath)); + else + strlcat(xpath, xpath_base, sizeof(xpath)); + } + if (change->xpath[0] == '.') + strlcat(xpath, change->xpath + 1, sizeof(xpath)); + else + strlcpy(xpath, change->xpath, sizeof(xpath)); + + /* Find the northbound node associated to the data path. */ + nb_node = nb_node_find(xpath); + if (!nb_node) { + flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, + "%s: unknown data path: %s", __func__, xpath); + if (error) + *error = true; + continue; + } + /* Find if the node to be edited is not a key node */ + if (!nb_is_operation_allowed(nb_node, change)) { + zlog_err(" Xpath %s points to key node", xpath); + if (error) + *error = true; + break; + } + + /* If the value is not set, get the default if it exists. */ + if (change->value == NULL) + change->value = yang_snode_get_default(nb_node->snode); + data = yang_data_new(xpath, change->value); + + /* + * Ignore "not found" errors when editing the candidate + * configuration. + */ + ret = nb_candidate_edit(candidate_config, nb_node, + change->operation, xpath, NULL, data); + yang_data_free(data); + 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]", + __func__, nb_operation_name(change->operation), + xpath); + if (error) + *error = true; + continue; + } + nb_update_candidate_changes(candidate_config, change, &seq); + } + + if (error && *error) { + char buf[BUFSIZ]; + + /* + * 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. + */ + snprintf(err_buf, err_bufsize, + "%% Failed to edit configuration.\n\n%s", + yang_print_errors(ly_native_ctx, buf, sizeof(buf))); + } +} + bool nb_candidate_needs_update(const struct nb_config *candidate) { if (candidate->version < running_config->version) @@ -761,12 +944,13 @@ 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, char *errmsg, - size_t errmsg_len) +int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state, + char *errmsg, size_t errmsg_len) { if (lyd_validate_all(&candidate->dnode, ly_native_ctx, - LYD_VALIDATE_NO_STATE, NULL) - != 0) { + no_state ? LYD_VALIDATE_NO_STATE + : LYD_VALIDATE_PRESENT, + NULL) != 0) { yang_print_errors(ly_native_ctx, errmsg, errmsg_len); return NB_ERR_VALIDATION; } @@ -775,10 +959,10 @@ static int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg, } /* Perform code-level validation using the northbound callbacks. */ -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) +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, *child; @@ -816,6 +1000,21 @@ static int nb_candidate_validate_code(struct nb_context *context, return NB_OK; } +int nb_candidate_diff_and_validate_yang(struct nb_context *context, + struct nb_config *candidate, + struct nb_config_cbs *changes, + char *errmsg, size_t errmsg_len) +{ + if (nb_candidate_validate_yang(candidate, true, errmsg, + sizeof(errmsg_len)) != NB_OK) + return NB_ERR_VALIDATION; + + RB_INIT(nb_config_cbs, changes); + nb_config_diff(running_config, candidate, changes); + + return NB_OK; +} + int nb_candidate_validate(struct nb_context *context, struct nb_config *candidate, char *errmsg, size_t errmsg_len) @@ -823,11 +1022,11 @@ int nb_candidate_validate(struct nb_context *context, struct nb_config_cbs changes; int ret; - if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) != NB_OK) - return NB_ERR_VALIDATION; + ret = nb_candidate_diff_and_validate_yang(context, candidate, &changes, + errmsg, errmsg_len); + if (ret != NB_OK) + return ret; - RB_INIT(nb_config_cbs, &changes); - nb_config_diff(running_config, candidate, &changes); ret = nb_candidate_validate_code(context, candidate, &changes, errmsg, errmsg_len); nb_config_diff_del_changes(&changes); @@ -839,12 +1038,14 @@ int nb_candidate_commit_prepare(struct nb_context context, struct nb_config *candidate, const char *comment, struct nb_transaction **transaction, + bool skip_validate, bool ignore_zero_change, char *errmsg, size_t errmsg_len) { struct nb_config_cbs changes; - if (nb_candidate_validate_yang(candidate, errmsg, errmsg_len) - != NB_OK) { + if (!skip_validate && + nb_candidate_validate_yang(candidate, true, errmsg, errmsg_len) != + NB_OK) { flog_warn(EC_LIB_NB_CANDIDATE_INVALID, "%s: failed to validate candidate configuration", __func__); @@ -853,14 +1054,15 @@ int nb_candidate_commit_prepare(struct nb_context context, RB_INIT(nb_config_cbs, &changes); nb_config_diff(running_config, candidate, &changes); - if (RB_EMPTY(nb_config_cbs, &changes)) { + if (!ignore_zero_change && 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_code(&context, candidate, &changes, errmsg, + if (!skip_validate && + 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", @@ -869,8 +1071,12 @@ int nb_candidate_commit_prepare(struct nb_context context, return NB_ERR_VALIDATION; } - *transaction = nb_transaction_new(context, candidate, &changes, comment, - errmsg, errmsg_len); + /* + * Re-use an existing transaction if provided. Else allocate a new one. + */ + if (!*transaction) + *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: %s", __func__, @@ -921,7 +1127,8 @@ int nb_candidate_commit(struct nb_context context, struct nb_config *candidate, int ret; ret = nb_candidate_commit_prepare(context, candidate, comment, - &transaction, errmsg, errmsg_len); + &transaction, false, false, errmsg, + errmsg_len); /* * Apply the changes if the preparation phase succeeded. Otherwise abort * the transaction. @@ -1015,6 +1222,8 @@ static int nb_callback_create(struct nb_context *context, bool unexpected_error = false; int ret; + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + nb_log_config_callback(event, NB_OP_CREATE, dnode); args.context = context; @@ -1064,6 +1273,8 @@ static int nb_callback_modify(struct nb_context *context, bool unexpected_error = false; int ret; + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + nb_log_config_callback(event, NB_OP_MODIFY, dnode); args.context = context; @@ -1113,6 +1324,8 @@ static int nb_callback_destroy(struct nb_context *context, bool unexpected_error = false; int ret; + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + nb_log_config_callback(event, NB_OP_DESTROY, dnode); args.context = context; @@ -1156,6 +1369,8 @@ static int nb_callback_move(struct nb_context *context, bool unexpected_error = false; int ret; + assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)); + nb_log_config_callback(event, NB_OP_MOVE, dnode); args.context = context; @@ -1199,6 +1414,9 @@ static int nb_callback_pre_validate(struct nb_context *context, bool unexpected_error = false; int ret; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return 0; + nb_log_config_callback(NB_EV_VALIDATE, NB_OP_PRE_VALIDATE, dnode); args.dnode = dnode; @@ -1230,6 +1448,9 @@ static void nb_callback_apply_finish(struct nb_context *context, { struct nb_cb_apply_finish_args args = {}; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return; + nb_log_config_callback(NB_EV_APPLY, NB_OP_APPLY_FINISH, dnode); args.context = context; @@ -1245,6 +1466,9 @@ struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node, { struct nb_cb_get_elem_args args = {}; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return NULL; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get_elem): xpath [%s] list_entry [%p]", xpath, list_entry); @@ -1260,6 +1484,9 @@ const void *nb_callback_get_next(const struct nb_node *nb_node, { struct nb_cb_get_next_args args = {}; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return NULL; + 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); @@ -1274,6 +1501,9 @@ int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry, { struct nb_cb_get_keys_args args = {}; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return 0; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (get_keys): node [%s] list_entry [%p]", nb_node->xpath, list_entry); @@ -1289,6 +1519,9 @@ const void *nb_callback_lookup_entry(const struct nb_node *nb_node, { struct nb_cb_lookup_entry_args args = {}; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return NULL; + DEBUGD(&nb_dbg_cbs_state, "northbound callback (lookup_entry): node [%s] parent_list_entry [%p]", nb_node->xpath, parent_list_entry); @@ -1304,6 +1537,9 @@ int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath, { struct nb_cb_rpc_args args = {}; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return 0; + DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath); args.xpath = xpath; @@ -1330,6 +1566,9 @@ static int nb_callback_configuration(struct nb_context *context, union nb_resource *resource; int ret = NB_ERR; + if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CBS)) + return NB_OK; + if (event == NB_EV_VALIDATE) resource = NULL; else @@ -1733,7 +1972,7 @@ static int nb_oper_data_iter_list(const struct nb_node *nb_node, /* Iterate over all list entries. */ do { const struct lysc_node_leaf *skey; - struct yang_list_keys list_keys; + struct yang_list_keys list_keys = {}; char xpath[XPATH_MAXLEN * 2]; int ret; @@ -1891,8 +2130,8 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator, * all YANG lists (if any). */ - LY_ERR err = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, - LYD_NEW_PATH_UPDATE, &dnode); + LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, + LYD_NEW_PATH_UPDATE, NULL, &dnode); if (err || !dnode) { const char *errmsg = err ? ly_errmsg(ly_native_ctx) : "node not found"; @@ -2389,6 +2628,10 @@ const char *nb_client_name(enum nb_client client) return "gRPC"; case NB_CLIENT_PCEP: return "Pcep"; + case NB_CLIENT_MGMTD_SERVER: + return "MGMTD Server"; + case NB_CLIENT_MGMTD_BE: + return "MGMT Backend"; case NB_CLIENT_NONE: return "None"; } @@ -2398,6 +2641,10 @@ const char *nb_client_name(enum nb_client client) static void nb_load_callbacks(const struct frr_yang_module_info *module) { + + if (module->ignore_cbs) + return; + for (size_t i = 0; module->nodes[i].xpath; i++) { struct nb_node *nb_node; uint32_t priority; @@ -2439,7 +2686,7 @@ void nb_validate_callbacks(void) } -void nb_init(struct thread_master *tm, +void nb_init(struct event_loop *tm, const struct frr_yang_module_info *const modules[], size_t nmodules, bool db_enabled) { @@ -2471,7 +2718,8 @@ void nb_init(struct thread_master *tm, /* Initialize the compiled nodes with northbound data */ for (size_t i = 0; i < nmodules; i++) { - yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0, NULL); + yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0, + (void *)modules[i]); nb_load_callbacks(modules[i]); } @@ -2498,8 +2746,7 @@ void nb_terminate(void) nb_nodes_delete(); /* Delete the running configuration. */ - hash_clean(running_config_entries, running_config_entry_free); - hash_free(running_config_entries); + hash_clean_and_free(&running_config_entries, running_config_entry_free); nb_config_free(running_config); pthread_mutex_destroy(&running_config_mgmt_lock.mtx); } diff --git a/lib/northbound.h b/lib/northbound.h index 152810b3a982..1723a87e4e75 100644 --- a/lib/northbound.h +++ b/lib/northbound.h @@ -7,7 +7,7 @@ #ifndef _FRR_NORTHBOUND_H_ #define _FRR_NORTHBOUND_H_ -#include "thread.h" +#include "frrevent.h" #include "hook.h" #include "linklist.h" #include "openbsd-tree.h" @@ -22,6 +22,39 @@ extern "C" { struct vty; struct debug; +struct nb_yang_xpath_tag { + uint32_t ns; + uint32_t id; +}; + +struct nb_yang_value { + struct lyd_value value; + LY_DATA_TYPE value_type; + uint8_t value_flags; +}; + +struct nb_yang_xpath_elem { + struct nb_yang_xpath_tag tag; + struct nb_yang_value val; +}; + +#define NB_MAX_NUM_KEYS UINT8_MAX +#define NB_MAX_NUM_XPATH_TAGS UINT8_MAX + +struct nb_yang_xpath { + uint8_t length; + struct { + uint8_t num_keys; + struct nb_yang_xpath_elem keys[NB_MAX_NUM_KEYS]; + } tags[NB_MAX_NUM_XPATH_TAGS]; +}; + +#define NB_YANG_XPATH_KEY(__xpath, __indx1, __indx2) \ + ((__xpath->num_tags > __indx1) && \ + (__xpath->tags[__indx1].num_keys > __indx2) \ + ? &__xpath->tags[__indx1].keys[__indx2] \ + : NULL) + /* Northbound events. */ enum nb_event { /* @@ -68,6 +101,12 @@ enum nb_operation { NB_OP_RPC, }; +struct nb_cfg_change { + char xpath[XPATH_MAXLEN]; + enum nb_operation operation; + const char *value; +}; + union nb_resource { int fd; void *ptr; @@ -558,6 +597,8 @@ struct nb_node { #define F_NB_NODE_CONFIG_ONLY 0x01 /* The YANG list doesn't contain key leafs. */ #define F_NB_NODE_KEYLESS_LIST 0x02 +/* Ignore callbacks for this node */ +#define F_NB_NODE_IGNORE_CBS 0x04 /* * HACK: old gcc versions (< 5.x) have a bug that prevents C99 flexible arrays @@ -570,6 +611,12 @@ struct frr_yang_module_info { /* YANG module name. */ const char *name; + /* + * Ignore callbacks for this module. Set this to true to + * load module without any callbacks. + */ + bool ignore_cbs; + /* Northbound callbacks. */ const struct { /* Data path of this YANG node. */ @@ -613,6 +660,8 @@ enum nb_client { NB_CLIENT_SYSREPO, NB_CLIENT_GRPC, NB_CLIENT_PCEP, + NB_CLIENT_MGMTD_SERVER, + NB_CLIENT_MGMTD_BE, }; /* Northbound context. */ @@ -624,12 +673,6 @@ struct nb_context { const void *user; }; -/* Northbound configuration. */ -struct nb_config { - struct lyd_node *dnode; - uint32_t version; -}; - /* Northbound configuration callback. */ struct nb_config_cb { RB_ENTRY(nb_config_cb) entry; @@ -656,6 +699,13 @@ struct nb_transaction { struct nb_config_cbs changes; }; +/* Northbound configuration. */ +struct nb_config { + struct lyd_node *dnode; + uint32_t version; + struct nb_config_cbs cfg_chgs; +}; + /* Callback function used by nb_oper_data_iterate(). */ typedef int (*nb_oper_data_cb)(const struct lysc_node *snode, struct yang_translator *translator, @@ -825,6 +875,22 @@ extern int nb_candidate_edit(struct nb_config *candidate, const struct yang_data *previous, const struct yang_data *data); +/* + * Create diff for configuration. + * + * dnode + * Pointer to a libyang data node containing the configuration data. If NULL + * is given, an empty configuration will be created. + * + * seq + * Returns sequence number assigned to the specific change. + * + * changes + * Northbound config callback head. + */ +extern void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq, + struct nb_config_cbs *changes); + /* * Check if a candidate configuration is outdated and needs to be updated. * @@ -836,6 +902,140 @@ extern int nb_candidate_edit(struct nb_config *candidate, */ extern bool nb_candidate_needs_update(const struct nb_config *candidate); +/* + * Edit candidate configuration changes. + * + * candidate_config + * Candidate configuration to edit. + * + * cfg_changes + * Northbound config changes. + * + * num_cfg_changes + * Number of config changes. + * + * xpath_base + * Base xpath for config. + * + * curr_xpath + * Current xpath for config. + * + * xpath_index + * Index of xpath being processed. + * + * err_buf + * Buffer to store human-readable error message in case of error. + * + * err_bufsize + * Size of err_buf. + * + * error + * TRUE on error, FALSE on success + */ +extern void nb_candidate_edit_config_changes( + struct nb_config *candidate_config, struct nb_cfg_change cfg_changes[], + size_t num_cfg_changes, const char *xpath_base, const char *curr_xpath, + int xpath_index, char *err_buf, int err_bufsize, bool *error); + +/* + * Delete candidate configuration changes. + * + * changes + * Northbound config changes. + */ +extern void nb_config_diff_del_changes(struct nb_config_cbs *changes); + +/* + * Create candidate diff and validate on yang tree + * + * context + * Context of the northbound transaction. + * + * candidate + * Candidate DB configuration. + * + * changes + * Northbound config changes. + * + * 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_diff_and_validate_yang(struct nb_context *context, + struct nb_config *candidate, + struct nb_config_cbs *changes, + char *errmsg, size_t errmsg_len); + +/* + * Calculate the delta between two different configurations. + * + * reference + * Running DB config changes to be compared against. + * + * incremental + * Candidate DB config changes that will be compared against reference. + * + * changes + * Will hold the final diff generated. + * + */ +extern void nb_config_diff(const struct nb_config *reference, + const struct nb_config *incremental, + struct nb_config_cbs *changes); + +/* + * Perform YANG syntactic and semantic validation. + * + * WARNING: lyd_validate() can change the configuration as part of the + * validation process. + * + * candidate + * Candidate DB configuration. + * + * 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_yang(struct nb_config *candidate, + bool no_state, char *errmsg, + size_t errmsg_len); + +/* + * Perform code-level validation using the northbound callbacks. + * + * context + * Context of the northbound transaction. + * + * candidate + * Candidate DB configuration. + * + * changes + * Northbound config changes. + * + * 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_code(struct nb_context *context, + struct nb_config *candidate, + struct nb_config_cbs *changes, + char *errmsg, size_t errmsg_len); + /* * Update a candidate configuration by rebasing the changes on top of the latest * running configuration. Resolve conflicts automatically by giving preference @@ -895,6 +1095,12 @@ extern int nb_candidate_validate(struct nb_context *context, * nb_candidate_commit_abort() or committed using * nb_candidate_commit_apply(). * + * skip_validate + * TRUE to skip commit validation, FALSE otherwise. + * + * ignore_zero_change + * TRUE to ignore if zero changes, FALSE otherwise. + * * errmsg * Buffer to store human-readable error message in case of error. * @@ -915,7 +1121,9 @@ extern 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); + bool skip_validate, + bool ignore_zero_change, char *errmsg, + size_t errmsg_len); /* * Abort a previously created configuration transaction, releasing all resources @@ -1270,7 +1478,7 @@ void nb_validate_callbacks(void); * db_enabled * Set this to record the transactions in the transaction log. */ -extern void nb_init(struct thread_master *tm, +extern void nb_init(struct event_loop *tm, const struct frr_yang_module_info *const modules[], size_t nmodules, bool db_enabled); diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index fa5884fb7850..8003679ed57b 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -29,7 +29,7 @@ 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 struct event_loop *master; static void vty_show_nb_errors(struct vty *vty, int error, const char *errmsg) { @@ -120,7 +120,7 @@ static int nb_cli_schedule_command(struct vty *vty) void nb_cli_enqueue_change(struct vty *vty, const char *xpath, enum nb_operation operation, const char *value) { - struct vty_cfg_change *change; + struct nb_cfg_change *change; if (vty->num_cfg_changes == VTY_MAXCFGCHANGES) { /* Not expected to happen. */ @@ -141,79 +141,21 @@ static int nb_cli_apply_changes_internal(struct vty *vty, bool clear_pending) { bool error = false; - - if (xpath_base == NULL) - xpath_base = ""; + char buf[BUFSIZ]; VTY_CHECK_XPATH; - /* Edit candidate configuration. */ - for (size_t i = 0; i < vty->num_cfg_changes; i++) { - struct vty_cfg_change *change = &vty->cfg_changes[i]; - struct nb_node *nb_node; - char xpath[XPATH_MAXLEN]; - struct yang_data *data; - int ret; - - /* Handle relative XPaths. */ - memset(xpath, 0, sizeof(xpath)); - if (vty->xpath_index > 0 - && (xpath_base[0] == '.' || change->xpath[0] == '.')) - strlcpy(xpath, VTY_CURR_XPATH, sizeof(xpath)); - if (xpath_base[0]) { - if (xpath_base[0] == '.') - strlcat(xpath, xpath_base + 1, sizeof(xpath)); - else - strlcat(xpath, xpath_base, sizeof(xpath)); - } - if (change->xpath[0] == '.') - strlcat(xpath, change->xpath + 1, sizeof(xpath)); - else - strlcpy(xpath, change->xpath, sizeof(xpath)); - - /* Find the northbound node associated to the data path. */ - nb_node = nb_node_find(xpath); - if (!nb_node) { - flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH, - "%s: unknown data path: %s", __func__, xpath); - error = true; - continue; - } - - /* If the value is not set, get the default if it exists. */ - if (change->value == NULL) - change->value = yang_snode_get_default(nb_node->snode); - data = yang_data_new(xpath, change->value); - - /* - * Ignore "not found" errors when editing the candidate - * configuration. - */ - 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) { - flog_warn( - EC_LIB_NB_CANDIDATE_EDIT_ERROR, - "%s: failed to edit candidate configuration: operation [%s] xpath [%s]", - __func__, nb_operation_name(change->operation), - xpath); - error = true; - continue; - } - } - + nb_candidate_edit_config_changes( + vty->candidate_config, vty->cfg_changes, vty->num_cfg_changes, + xpath_base, VTY_CURR_XPATH, vty->xpath_index, buf, sizeof(buf), + &error); if (error) { - char buf[BUFSIZ]; - /* * 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))); + vty_out(vty, "%s", buf); } /* @@ -241,6 +183,8 @@ static int nb_cli_apply_changes_internal(struct vty *vty, int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) { char xpath_base[XPATH_MAXLEN] = {}; + bool implicit_commit; + int ret; /* Parse the base XPath format string. */ if (xpath_base_fmt) { @@ -250,6 +194,20 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...) vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap); va_end(ap); } + + if (vty_mgmt_should_process_cli_apply_changes(vty)) { + VTY_CHECK_XPATH; + + if (vty->type == VTY_FILE) + return CMD_SUCCESS; + + implicit_commit = vty_needs_implicit_commit(vty); + ret = vty_mgmt_send_config_data(vty, implicit_commit); + if (ret >= 0 && !implicit_commit) + vty->mgmt_num_pending_setcfg++; + return ret; + } + return nb_cli_apply_changes_internal(vty, xpath_base, false); } @@ -257,6 +215,8 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty, const char *xpath_base_fmt, ...) { char xpath_base[XPATH_MAXLEN] = {}; + bool implicit_commit; + int ret; /* Parse the base XPath format string. */ if (xpath_base_fmt) { @@ -266,6 +226,24 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty, vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap); va_end(ap); } + + if (vty_mgmt_should_process_cli_apply_changes(vty)) { + VTY_CHECK_XPATH; + /* + * The legacy user wanted to clear pending (i.e., perform a + * commit immediately) due to some non-yang compatible + * functionality. This new mgmtd code however, continues to send + * changes putting off the commit until XFRR_end is received + * (i.e., end-of-config-file). This should be fine b/c all + * conversions to mgmtd require full proper implementations. + */ + implicit_commit = vty_needs_implicit_commit(vty); + ret = vty_mgmt_send_config_data(vty, implicit_commit); + if (ret >= 0 && !implicit_commit) + vty->mgmt_num_pending_setcfg++; + return ret; + } + return nb_cli_apply_changes_internal(vty, xpath_base, true); } @@ -297,7 +275,7 @@ int nb_cli_rpc(struct vty *vty, const char *xpath, struct list *input, void nb_cli_confirmed_commit_clean(struct vty *vty) { - thread_cancel(&vty->t_confirmed_commit_timeout); + event_cancel(&vty->t_confirmed_commit_timeout); nb_config_free(vty->confirmed_commit_rollback); vty->confirmed_commit_rollback = NULL; } @@ -332,9 +310,9 @@ int nb_cli_confirmed_commit_rollback(struct vty *vty) return ret; } -static void nb_cli_confirmed_commit_timeout(struct thread *thread) +static void nb_cli_confirmed_commit_timeout(struct event *thread) { - struct vty *vty = THREAD_ARG(thread); + struct vty *vty = EVENT_ARG(thread); /* XXX: broadcast this message to all logged-in users? */ vty_out(vty, @@ -360,11 +338,10 @@ static int nb_cli_commit(struct vty *vty, bool force, "%% Resetting confirmed-commit timeout to %u minute(s)\n\n", confirmed_timeout); - thread_cancel(&vty->t_confirmed_commit_timeout); - thread_add_timer(master, - nb_cli_confirmed_commit_timeout, vty, - confirmed_timeout * 60, - &vty->t_confirmed_commit_timeout); + event_cancel(&vty->t_confirmed_commit_timeout); + event_add_timer(master, nb_cli_confirmed_commit_timeout, + vty, confirmed_timeout * 60, + &vty->t_confirmed_commit_timeout); } else { /* Accept commit confirmation. */ vty_out(vty, "%% Commit complete.\n\n"); @@ -387,9 +364,9 @@ static int nb_cli_commit(struct vty *vty, bool force, vty->confirmed_commit_rollback = nb_config_dup(running_config); vty->t_confirmed_commit_timeout = NULL; - thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty, - confirmed_timeout * 60, - &vty->t_confirmed_commit_timeout); + event_add_timer(master, nb_cli_confirmed_commit_timeout, vty, + confirmed_timeout * 60, + &vty->t_confirmed_commit_timeout); } context.client = NB_CLIENT_CLI; @@ -786,7 +763,7 @@ DEFUN (config_exclusive, "Configuration from vty interface\n" "Configure exclusively from this terminal\n") { - return vty_config_enter(vty, true, true); + return vty_config_enter(vty, true, true, false); } /* Configure using a private candidate configuration. */ @@ -796,7 +773,7 @@ DEFUN (config_private, "Configuration from vty interface\n" "Configure using a private candidate configuration\n") { - return vty_config_enter(vty, true, false); + return vty_config_enter(vty, true, false, false); } DEFPY (config_commit, @@ -1467,6 +1444,7 @@ DEFPY (show_yang_operational_data, struct lyd_node *dnode; char *strp; uint32_t print_options = LYD_PRINT_WITHSIBLINGS; + int ret; if (xml) format = LYD_XML; @@ -1487,10 +1465,15 @@ DEFPY (show_yang_operational_data, /* Obtain data. */ dnode = yang_dnode_new(ly_ctx, false); - if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb, - dnode) - != NB_OK) { - vty_out(vty, "%% Failed to fetch operational data.\n"); + ret = nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb, + dnode); + if (ret != NB_OK) { + if (format == LYD_JSON) + vty_out(vty, "{}\n"); + else { + /* embed ly_last_errmsg() when we get newer libyang */ + vty_out(vty, "<!-- Not found -->\n"); + } yang_dnode_free(dnode); return CMD_WARNING; } @@ -1909,7 +1892,7 @@ static const struct cmd_variable_handler yang_var_handlers[] = { .completions = yang_translator_autocomplete}, {.completions = NULL}}; -void nb_cli_init(struct thread_master *tm) +void nb_cli_init(struct event_loop *tm) { master = tm; diff --git a/lib/northbound_cli.h b/lib/northbound_cli.h index ef2ef44eb01e..c8f8a8481ab7 100644 --- a/lib/northbound_cli.h +++ b/lib/northbound_cli.h @@ -137,7 +137,7 @@ extern void nb_cli_show_config_prepare(struct nb_config *config, extern void nb_cli_confirmed_commit_clean(struct vty *vty); extern int nb_cli_confirmed_commit_rollback(struct vty *vty); extern void nb_cli_install_default(int node); -extern void nb_cli_init(struct thread_master *tm); +extern void nb_cli_init(struct event_loop *tm); extern void nb_cli_terminate(void); #ifdef __cplusplus diff --git a/lib/northbound_confd.c b/lib/northbound_confd.c index 2b57ff27070f..34406a110b9e 100644 --- a/lib/northbound_confd.c +++ b/lib/northbound_confd.c @@ -23,10 +23,10 @@ DEFINE_MTYPE_STATIC(LIB, CONFD, "ConfD module"); static struct debug nb_dbg_client_confd = {0, "Northbound client: ConfD"}; -static struct thread_master *master; +static struct event_loop *master; static struct sockaddr confd_addr; static int cdb_sub_sock, dp_ctl_sock, dp_worker_sock; -static struct thread *t_cdb_sub, *t_dp_ctl, *t_dp_worker; +static struct event *t_cdb_sub, *t_dp_ctl, *t_dp_worker; static struct confd_daemon_ctx *dctx; static struct confd_notification_ctx *live_ctx; static bool confd_connected; @@ -312,7 +312,8 @@ static void frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen) transaction = NULL; context.client = NB_CLIENT_CONFD; ret = nb_candidate_commit_prepare(context, candidate, NULL, - &transaction, errmsg, sizeof(errmsg)); + &transaction, false, false, errmsg, + sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) { enum confd_errcode errcode; @@ -400,15 +401,15 @@ static int frr_confd_cdb_read_cb_abort(int fd, int *subp, int reslen) return 0; } -static void frr_confd_cdb_read_cb(struct thread *thread) +static void frr_confd_cdb_read_cb(struct event *thread) { - int fd = THREAD_FD(thread); + int fd = EVENT_FD(thread); enum cdb_sub_notification cdb_ev; int flags; int *subp = NULL; int reslen = 0; - thread_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub); + event_add_read(master, frr_confd_cdb_read_cb, NULL, fd, &t_cdb_sub); if (cdb_read_subscription_socket2(fd, &cdb_ev, &flags, &subp, &reslen) != CONFD_OK) { @@ -573,8 +574,8 @@ static int frr_confd_init_cdb(void) } pthread_detach(cdb_trigger_thread); - thread_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock, - &t_cdb_sub); + event_add_read(master, frr_confd_cdb_read_cb, NULL, cdb_sub_sock, + &t_cdb_sub); return 0; @@ -587,7 +588,7 @@ static int frr_confd_init_cdb(void) static void frr_confd_finish_cdb(void) { if (cdb_sub_sock > 0) { - THREAD_OFF(t_cdb_sub); + EVENT_OFF(t_cdb_sub); cdb_close(cdb_sub_sock); } } @@ -1172,22 +1173,23 @@ static int frr_confd_dp_read(struct confd_daemon_ctx *dctx, int fd) return 0; } -static void frr_confd_dp_ctl_read(struct thread *thread) +static void frr_confd_dp_ctl_read(struct event *thread) { - struct confd_daemon_ctx *dctx = THREAD_ARG(thread); - int fd = THREAD_FD(thread); + struct confd_daemon_ctx *dctx = EVENT_ARG(thread); + int fd = EVENT_FD(thread); - thread_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl); + event_add_read(master, frr_confd_dp_ctl_read, dctx, fd, &t_dp_ctl); frr_confd_dp_read(dctx, fd); } -static void frr_confd_dp_worker_read(struct thread *thread) +static void frr_confd_dp_worker_read(struct event *thread) { - struct confd_daemon_ctx *dctx = THREAD_ARG(thread); - int fd = THREAD_FD(thread); + struct confd_daemon_ctx *dctx = EVENT_ARG(thread); + int fd = EVENT_FD(thread); - thread_add_read(master, frr_confd_dp_worker_read, dctx, fd, &t_dp_worker); + event_add_read(master, frr_confd_dp_worker_read, dctx, fd, + &t_dp_worker); frr_confd_dp_read(dctx, fd); } @@ -1319,10 +1321,10 @@ static int frr_confd_init_dp(const char *program_name) goto error; } - thread_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock, - &t_dp_ctl); - thread_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock, - &t_dp_worker); + event_add_read(master, frr_confd_dp_ctl_read, dctx, dp_ctl_sock, + &t_dp_ctl); + event_add_read(master, frr_confd_dp_worker_read, dctx, dp_worker_sock, + &t_dp_worker); return 0; @@ -1335,11 +1337,11 @@ static int frr_confd_init_dp(const char *program_name) static void frr_confd_finish_dp(void) { if (dp_worker_sock > 0) { - THREAD_OFF(t_dp_worker); + EVENT_OFF(t_dp_worker); close(dp_worker_sock); } if (dp_ctl_sock > 0) { - THREAD_OFF(t_dp_ctl); + EVENT_OFF(t_dp_ctl); close(dp_ctl_sock); } if (dctx != NULL) @@ -1463,7 +1465,7 @@ static int frr_confd_finish(void) return 0; } -static int frr_confd_module_late_init(struct thread_master *tm) +static int frr_confd_module_late_init(struct event_loop *tm) { master = tm; diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 1459146eab1c..6c33351cef04 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -12,7 +12,7 @@ #include "log.h" #include "libfrr.h" #include "lib/version.h" -#include "lib/thread.h" +#include "frrevent.h" #include "command.h" #include "lib_errors.h" #include "northbound.h" @@ -38,7 +38,7 @@ */ static bool nb_dbg_client_grpc = 0; -static struct thread_master *main_master; +static struct event_loop *main_master; static struct frr_pthread *fpt; @@ -157,8 +157,7 @@ class RpcStateBase * state will either be MORE or FINISH. It will always be FINISH * for Unary RPCs. */ - thread_add_event(main_master, c_callback, (void *)this, 0, - NULL); + event_add_event(main_master, c_callback, (void *)this, 0, NULL); pthread_mutex_lock(&this->cmux); while (this->state == PROCESS) @@ -181,11 +180,11 @@ class RpcStateBase } protected: - virtual CallState run_mainthread(struct thread *thread) = 0; + virtual CallState run_mainthread(struct event *thread) = 0; - static void c_callback(struct thread *thread) + static void c_callback(struct event *thread) { - auto _tag = static_cast<RpcStateBase *>(THREAD_ARG(thread)); + auto _tag = static_cast<RpcStateBase *>(EVENT_ARG(thread)); /* * We hold the lock until the callback finishes and has updated * _tag->state, then we signal done and release. @@ -250,7 +249,7 @@ template <typename Q, typename S> class UnaryRpcState : public RpcStateBase ©->responder, cq, cq, copy); } - CallState run_mainthread(struct thread *thread) override + CallState run_mainthread(struct event *thread) override { // Unary RPC are always finished, see "Unary" :) grpc::Status status = this->callback(this); @@ -302,7 +301,7 @@ class StreamRpcState : public RpcStateBase ©->async_responder, cq, cq, copy); } - CallState run_mainthread(struct thread *thread) override + CallState run_mainthread(struct event *thread) override { if (this->callback(this)) return MORE; @@ -825,7 +824,8 @@ HandleUnaryCommit(UnaryRpcState<frr::CommitRequest, frr::CommitResponse> *tag) grpc_debug("`-> Performing PREPARE"); ret = nb_candidate_commit_prepare( context, candidate->config, comment.c_str(), - &candidate->transaction, errmsg, sizeof(errmsg)); + &candidate->transaction, false, false, errmsg, + sizeof(errmsg)); break; case frr::CommitRequest::ABORT: grpc_debug("`-> Performing ABORT"); @@ -1274,7 +1274,7 @@ static int frr_grpc_finish(void) * fork. This is done by scheduling this init function as an event task, since * the event loop doesn't run until after fork. */ -static void frr_grpc_module_very_late_init(struct thread *thread) +static void frr_grpc_module_very_late_init(struct event *thread) { const char *args = THIS_MODULE->load_args; uint port = GRPC_DEFAULT_PORT; @@ -1298,11 +1298,11 @@ static void frr_grpc_module_very_late_init(struct thread *thread) flog_err(EC_LIB_GRPC_INIT, "failed to initialize the gRPC module"); } -static int frr_grpc_module_late_init(struct thread_master *tm) +static int frr_grpc_module_late_init(struct event_loop *tm) { main_master = tm; hook_register(frr_fini, frr_grpc_finish); - thread_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL); + event_add_event(tm, frr_grpc_module_very_late_init, NULL, 0, NULL); return 0; } diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 096414ff2474..7fd4af83562f 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -23,12 +23,12 @@ 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 event_loop *master; static sr_session_ctx_t *session; static sr_conn_ctx_t *connection; static struct nb_transaction *transaction; -static void frr_sr_read_cb(struct thread *thread); +static void frr_sr_read_cb(struct event *thread); static int frr_sr_finish(void); /* Convert FRR YANG data value to sysrepo YANG data value. */ @@ -269,7 +269,8 @@ static int frr_sr_config_change_cb_prepare(sr_session_ctx_t *session, * required to apply them. */ ret = nb_candidate_commit_prepare(context, candidate, NULL, - &transaction, errmsg, sizeof(errmsg)); + &transaction, false, false, errmsg, + sizeof(errmsg)); if (ret != NB_OK && ret != NB_ERR_NO_CHANGES) flog_warn( EC_LIB_LIBSYSREPO, @@ -356,7 +357,7 @@ static int frr_sr_state_data_iter_cb(const struct lysc_node *snode, ly_errno = 0; ly_errno = lyd_new_path(NULL, ly_native_ctx, data->xpath, data->value, 0, &dnode); - if (!dnode && ly_errno) { + if (ly_errno) { flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed", __func__); yang_data_free(data); @@ -513,10 +514,10 @@ static int frr_sr_notification_send(const char *xpath, struct list *arguments) return NB_OK; } -static void frr_sr_read_cb(struct thread *thread) +static void frr_sr_read_cb(struct event *thread) { - struct yang_module *module = THREAD_ARG(thread); - int fd = THREAD_FD(thread); + struct yang_module *module = EVENT_ARG(thread); + int fd = EVENT_FD(thread); int ret; ret = sr_subscription_process_events(module->sr_subscription, session, @@ -527,7 +528,7 @@ static void frr_sr_read_cb(struct thread *thread) return; } - thread_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread); + event_add_read(master, frr_sr_read_cb, module, fd, &module->sr_thread); } static void frr_sr_subscribe_config(struct yang_module *module) @@ -687,8 +688,8 @@ static int frr_sr_init(void) sr_strerror(ret)); goto cleanup; } - thread_add_read(master, frr_sr_read_cb, module, - event_pipe, &module->sr_thread); + event_add_read(master, frr_sr_read_cb, module, event_pipe, + &module->sr_thread); } hook_register(nb_notification_send, frr_sr_notification_send); @@ -709,7 +710,7 @@ static int frr_sr_finish(void) if (!module->sr_subscription) continue; sr_unsubscribe(module->sr_subscription); - THREAD_OFF(module->sr_thread); + EVENT_OFF(module->sr_thread); } if (session) @@ -720,7 +721,7 @@ static int frr_sr_finish(void) return 0; } -static int frr_sr_module_config_loaded(struct thread_master *tm) +static int frr_sr_module_config_loaded(struct event_loop *tm) { master = tm; @@ -735,7 +736,7 @@ static int frr_sr_module_config_loaded(struct thread_master *tm) return 0; } -static int frr_sr_module_late_init(struct thread_master *tm) +static int frr_sr_module_late_init(struct event_loop *tm) { frr_sr_cli_init(); diff --git a/lib/plist.c b/lib/plist.c index e286a32f9212..d8ce83d219ea 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -336,6 +336,22 @@ prefix_list_entry_lookup(struct prefix_list *plist, struct prefix *prefix, return NULL; } +static bool +prefix_list_entry_lookup_prefix(struct prefix_list *plist, + struct prefix_list_entry *plist_entry) +{ + struct prefix_list_entry *pentry = NULL; + + for (pentry = plist->head; pentry; pentry = pentry->next) { + if (pentry == plist_entry) + continue; + if (prefix_same(&pentry->prefix, &plist_entry->prefix)) + return true; + } + + return false; +} + static void trie_walk_affected(size_t validbits, struct pltrie_table *table, uint8_t byte, struct prefix_list_entry *object, void (*fn)(struct prefix_list_entry *object, @@ -404,12 +420,16 @@ static void prefix_list_trie_del(struct prefix_list *plist, void prefix_list_entry_delete(struct prefix_list *plist, - struct prefix_list_entry *pentry, - int update_list) + struct prefix_list_entry *pentry, int update_list) { + bool duplicate = false; + if (plist == NULL || pentry == NULL) return; + if (prefix_list_entry_lookup_prefix(plist, pentry)) + duplicate = true; + prefix_list_trie_del(plist, pentry); if (pentry->prev) @@ -421,8 +441,10 @@ 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); + if (!duplicate) + route_map_notify_pentry_dependencies(plist->name, pentry, + RMAP_EVENT_PLIST_DELETED); + prefix_list_entry_free(pentry); plist->count--; @@ -557,11 +579,15 @@ static void prefix_list_entry_add(struct prefix_list *plist, void prefix_list_entry_update_start(struct prefix_list_entry *ple) { struct prefix_list *pl = ple->pl; + bool duplicate = false; /* Not installed, nothing to do. */ if (!ple->installed) return; + if (prefix_list_entry_lookup_prefix(pl, ple)) + duplicate = true; + prefix_list_trie_del(pl, ple); /* List manipulation: shameless copy from `prefix_list_entry_delete`. */ @@ -574,8 +600,9 @@ void prefix_list_entry_update_start(struct prefix_list_entry *ple) else pl->tail = ple->prev; - route_map_notify_pentry_dependencies(pl->name, ple, - RMAP_EVENT_PLIST_DELETED); + if (!duplicate) + route_map_notify_pentry_dependencies(pl->name, ple, + RMAP_EVENT_PLIST_DELETED); pl->count--; route_map_notify_dependencies(pl->name, RMAP_EVENT_PLIST_DELETED); diff --git a/lib/prefix.c b/lib/prefix.c index a6aae08a6a56..b8cad910f423 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1399,7 +1399,7 @@ bool ipv4_unicast_valid(const struct in_addr *addr) if (IPV4_CLASS_D(ip)) return false; - if (IPV4_CLASS_E(ip)) { + if (IPV4_NET0(ip) || IPV4_NET127(ip) || IPV4_CLASS_E(ip)) { if (cmd_allow_reserved_ranges_get()) return true; else diff --git a/lib/prefix.h b/lib/prefix.h index 9c5728370615..90b792b33c61 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -499,11 +499,8 @@ extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p); /* NOTE: This routine expects the address argument in network byte order. */ static inline bool ipv4_martian(const struct in_addr *addr) { - in_addr_t ip = ntohl(addr->s_addr); - - if (IPV4_NET0(ip) || IPV4_NET127(ip) || !ipv4_unicast_valid(addr)) { + if (!ipv4_unicast_valid(addr)) return true; - } return false; } @@ -601,6 +598,14 @@ static inline bool ipv6_mcast_ssm(const struct in6_addr *addr) return (bits & 0xfff0ffff) == 0xff300000; } +static inline bool ipv6_mcast_reserved(const struct in6_addr *addr) +{ + uint32_t bits = ntohl(addr->s6_addr32[0]); + + /* ffx2::/16 */ + return (bits & 0xff0fffff) == 0xff020000; +} + static inline uint8_t ipv4_mcast_scope(const struct in_addr *addr) { uint32_t bits = ntohl(addr->s_addr); diff --git a/lib/printf/glue.c b/lib/printf/glue.c index 25e2ff37a3cb..f799378af398 100644 --- a/lib/printf/glue.c +++ b/lib/printf/glue.c @@ -273,6 +273,7 @@ static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea, { const struct va_format *vaf = ptr; va_list ap; + ssize_t s; if (!vaf || !vaf->fmt || !vaf->va) return bputs(buf, "NULL"); @@ -285,6 +286,9 @@ static ssize_t printfrr_va(struct fbuf *buf, struct printfrr_eargs *ea, #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" /* can't format check this */ - return vbprintfrr(buf, vaf->fmt, ap); + s = vbprintfrr(buf, vaf->fmt, ap); #pragma GCC diagnostic pop + va_end(ap); + + return s; } diff --git a/lib/pullwr.c b/lib/pullwr.c index 5bc566c285d9..3967eb587541 100644 --- a/lib/pullwr.c +++ b/lib/pullwr.c @@ -16,9 +16,9 @@ struct pullwr { int fd; - struct thread_master *tm; + struct event_loop *tm; /* writer == NULL <=> we're idle */ - struct thread *writer; + struct event *writer; void *arg; void (*fill)(void *, struct pullwr *); @@ -38,12 +38,11 @@ struct pullwr { DEFINE_MTYPE_STATIC(LIB, PULLWR_HEAD, "pull-driven write controller"); DEFINE_MTYPE_STATIC(LIB, PULLWR_BUF, "pull-driven write buffer"); -static void pullwr_run(struct thread *t); +static void pullwr_run(struct event *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_new(struct event_loop *tm, int fd, void *arg, + void (*fill)(void *, struct pullwr *), + void (*err)(void *, struct pullwr *, bool)) { struct pullwr *pullwr; @@ -62,7 +61,7 @@ struct pullwr *_pullwr_new(struct thread_master *tm, int fd, void pullwr_del(struct pullwr *pullwr) { - THREAD_OFF(pullwr->writer); + EVENT_OFF(pullwr->writer); XFREE(MTYPE_PULLWR_BUF, pullwr->buffer); XFREE(MTYPE_PULLWR_HEAD, pullwr); @@ -80,7 +79,7 @@ void pullwr_bump(struct pullwr *pullwr) if (pullwr->writer) return; - thread_add_timer(pullwr->tm, pullwr_run, pullwr, 0, &pullwr->writer); + event_add_timer(pullwr->tm, pullwr_run, pullwr, 0, &pullwr->writer); } static size_t pullwr_iov(struct pullwr *pullwr, struct iovec *iov) @@ -176,9 +175,9 @@ void pullwr_write(struct pullwr *pullwr, const void *data, size_t len) pullwr_bump(pullwr); } -static void pullwr_run(struct thread *t) +static void pullwr_run(struct event *t) { - struct pullwr *pullwr = THREAD_ARG(t); + struct pullwr *pullwr = EVENT_ARG(t); struct iovec iov[2]; size_t niov, lastvalid; ssize_t nwr; @@ -206,7 +205,7 @@ static void pullwr_run(struct thread *t) 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() + * into idle, i.e. no calling event_add_write() */ pullwr_resize(pullwr, 0); return; @@ -237,7 +236,7 @@ static void pullwr_run(struct thread *t) * is full and we go wait until it's available for writing again. */ - thread_add_write(pullwr->tm, pullwr_run, pullwr, pullwr->fd, + event_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 diff --git a/lib/pullwr.h b/lib/pullwr.h index 77ecf855b404..ef2e01c04ede 100644 --- a/lib/pullwr.h +++ b/lib/pullwr.h @@ -10,7 +10,7 @@ #include <stdbool.h> #include <stdint.h> -#include "thread.h" +#include "frrevent.h" #include "stream.h" #ifdef __cplusplus @@ -45,10 +45,10 @@ struct pullwr; * 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 struct pullwr *_pullwr_new(struct event_loop *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 diff --git a/lib/qobj.c b/lib/qobj.c index 09b156ba390d..b9630e754713 100644 --- a/lib/qobj.c +++ b/lib/qobj.c @@ -7,7 +7,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "hash.h" #include "log.h" diff --git a/lib/resolver.c b/lib/resolver.c index 2918576c03a7..99bf356eb3e6 100644 --- a/lib/resolver.c +++ b/lib/resolver.c @@ -12,7 +12,7 @@ #include "typesafe.h" #include "jhash.h" -#include "thread.h" +#include "frrevent.h" #include "lib_errors.h" #include "resolver.h" #include "command.h" @@ -23,8 +23,8 @@ XREF_SETUP(); struct resolver_state { ares_channel channel; - struct thread_master *master; - struct thread *timeout; + struct event_loop *master; + struct event *timeout; }; static struct resolver_state state; @@ -47,7 +47,7 @@ struct resolver_fd { int fd; struct resolver_state *state; - struct thread *t_read, *t_write; + struct event *t_read, *t_write; }; static int resolver_fd_cmp(const struct resolver_fd *a, @@ -100,38 +100,38 @@ static void resolver_fd_drop_maybe(struct resolver_fd *resfd) static void resolver_update_timeouts(struct resolver_state *r); -static void resolver_cb_timeout(struct thread *t) +static void resolver_cb_timeout(struct event *t) { - struct resolver_state *r = THREAD_ARG(t); + struct resolver_state *r = EVENT_ARG(t); ares_process(r->channel, NULL, NULL); resolver_update_timeouts(r); } -static void resolver_cb_socket_readable(struct thread *t) +static void resolver_cb_socket_readable(struct event *t) { - struct resolver_fd *resfd = THREAD_ARG(t); + struct resolver_fd *resfd = EVENT_ARG(t); struct resolver_state *r = resfd->state; - thread_add_read(r->master, resolver_cb_socket_readable, resfd, - resfd->fd, &resfd->t_read); + event_add_read(r->master, resolver_cb_socket_readable, resfd, resfd->fd, + &resfd->t_read); /* ^ ordering important: - * ares_process_fd may transitively call THREAD_OFF(resfd->t_read) + * ares_process_fd may transitively call EVENT_OFF(resfd->t_read) * combined with resolver_fd_drop_maybe, so resfd may be free'd after! */ ares_process_fd(r->channel, resfd->fd, ARES_SOCKET_BAD); resolver_update_timeouts(r); } -static void resolver_cb_socket_writable(struct thread *t) +static void resolver_cb_socket_writable(struct event *t) { - struct resolver_fd *resfd = THREAD_ARG(t); + struct resolver_fd *resfd = EVENT_ARG(t); struct resolver_state *r = resfd->state; - thread_add_write(r->master, resolver_cb_socket_writable, resfd, - resfd->fd, &resfd->t_write); + event_add_write(r->master, resolver_cb_socket_writable, resfd, + resfd->fd, &resfd->t_write); /* ^ ordering important: - * ares_process_fd may transitively call THREAD_OFF(resfd->t_write) + * ares_process_fd may transitively call EVENT_OFF(resfd->t_write) * combined with resolver_fd_drop_maybe, so resfd may be free'd after! */ ares_process_fd(r->channel, ARES_SOCKET_BAD, resfd->fd); @@ -142,13 +142,13 @@ static void resolver_update_timeouts(struct resolver_state *r) { struct timeval *tv, tvbuf; - THREAD_OFF(r->timeout); + EVENT_OFF(r->timeout); tv = ares_timeout(r->channel, NULL, &tvbuf); if (tv) { unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000; - thread_add_timer_msec(r->master, resolver_cb_timeout, r, - timeoutms, &r->timeout); + event_add_timer_msec(r->master, resolver_cb_timeout, r, + timeoutms, &r->timeout); } } @@ -165,16 +165,16 @@ static void ares_socket_cb(void *data, ares_socket_t fd, int readable, assert(resfd->state == r); if (!readable) - THREAD_OFF(resfd->t_read); + EVENT_OFF(resfd->t_read); else if (!resfd->t_read) - thread_add_read(r->master, resolver_cb_socket_readable, resfd, - fd, &resfd->t_read); + event_add_read(r->master, resolver_cb_socket_readable, resfd, + fd, &resfd->t_read); if (!writable) - THREAD_OFF(resfd->t_write); + EVENT_OFF(resfd->t_write); else if (!resfd->t_write) - thread_add_write(r->master, resolver_cb_socket_writable, resfd, - fd, &resfd->t_write); + event_add_write(r->master, resolver_cb_socket_writable, resfd, + fd, &resfd->t_write); resolver_fd_drop_maybe(resfd); } @@ -222,9 +222,9 @@ static void ares_address_cb(void *arg, int status, int timeouts, callback(query, NULL, i, &addr[0]); } -static void resolver_cb_literal(struct thread *t) +static void resolver_cb_literal(struct event *t) { - struct resolver_query *query = THREAD_ARG(t); + struct resolver_query *query = EVENT_ARG(t); void (*callback)(struct resolver_query *, const char *, int, union sockunion *); @@ -264,8 +264,8 @@ void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id, /* 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); + event_add_timer_msec(state.master, resolver_cb_literal, query, + 0, &query->literal_cb); return; } @@ -314,7 +314,7 @@ static int resolver_config_write_debug(struct vty *vty) } -void resolver_init(struct thread_master *tm) +void resolver_init(struct event_loop *tm) { struct ares_options ares_opts; diff --git a/lib/resolver.h b/lib/resolver.h index d3f38f742de9..87e8ecdc4ae6 100644 --- a/lib/resolver.h +++ b/lib/resolver.h @@ -6,7 +6,7 @@ #ifndef _FRR_RESOLVER_H #define _FRR_RESOLVER_H -#include "thread.h" +#include "frrevent.h" #include "sockunion.h" #ifdef __cplusplus @@ -19,10 +19,10 @@ struct resolver_query { /* used to immediate provide the result if IP literal is passed in */ union sockunion literal_addr; - struct thread *literal_cb; + struct event *literal_cb; }; -void resolver_init(struct thread_master *tm); +void resolver_init(struct event_loop *tm); void resolver_resolve(struct resolver_query *query, int af, vrf_id_t vrf_id, const char *hostname, void (*cb)(struct resolver_query *, const char *, int, diff --git a/lib/routemap.c b/lib/routemap.c index 9f5c9e693e36..e0b0eb7a3cc5 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -394,6 +394,40 @@ void route_map_no_set_metric_hook(int (*func)(struct route_map_index *index, { rmap_match_set_hook.no_set_metric = func; } +/* set min-metric */ +void route_map_set_min_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.set_min_metric = func; +} + +/* no set min-metric */ +void route_map_no_set_min_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.no_set_min_metric = func; +} +/* set max-metric */ +void route_map_set_max_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.set_max_metric = func; +} + +/* no set max-metric */ +void route_map_no_set_max_metric_hook(int (*func)(struct route_map_index *index, + const char *command, + const char *arg, char *errmsg, + size_t errmsg_len)) +{ + rmap_match_set_hook.no_set_max_metric = func; +} /* set tag */ void route_map_set_tag_hook(int (*func)(struct route_map_index *index, @@ -669,7 +703,7 @@ static struct route_map *route_map_add(const char *name) if (!map->ipv6_prefix_table) map->ipv6_prefix_table = route_table_init(); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Add route-map %s", name); return map; } @@ -689,7 +723,7 @@ static void route_map_free_map(struct route_map *map) while ((index = map->head) != NULL) route_map_index_delete(index, 0); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Deleting route-map %s", map->name); list = &route_map_master; @@ -706,6 +740,9 @@ static void route_map_free_map(struct route_map *map) else list->head = map->next; + route_table_finish(map->ipv4_prefix_table); + route_table_finish(map->ipv6_prefix_table); + hash_release(route_map_master_hash, map); XFREE(MTYPE_ROUTE_MAP_NAME, map->name); XFREE(MTYPE_ROUTE_MAP, map); @@ -1120,7 +1157,7 @@ void route_map_index_delete(struct route_map_index *index, int notify) QOBJ_UNREG(index); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Deleting route-map %s sequence %d", index->map->name, index->pref); @@ -1231,7 +1268,7 @@ route_map_index_add(struct route_map *map, enum route_map_type type, int pref) route_map_notify_dependencies(map->name, RMAP_EVENT_CALL_ADDED); } - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Route-map %s add sequence %d, type: %s", map->name, pref, route_map_type_str(type)); @@ -1811,10 +1848,8 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, * must be AF_INET or AF_INET6 in order for the lookup to succeed. So if * the AF doesn't line up with the LPM trees, skip the optimization. */ - if (map->optimization_disabled || - (prefix->family == AF_INET && !map->ipv4_prefix_table) || - (prefix->family == AF_INET6 && !map->ipv6_prefix_table)) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (map->optimization_disabled) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "Skipping route-map optimization for route-map: %s, pfx: %pFX, family: %d", map->name, prefix, prefix->family); @@ -1826,9 +1861,6 @@ route_map_get_index(struct route_map *map, const struct prefix *prefix, else table = map->ipv6_prefix_table; - if (!table) - return NULL; - do { candidate_rmap_list = route_map_get_index_list(&rn, prefix, table); @@ -1914,19 +1946,10 @@ static void route_map_pfx_table_add_default(afi_t afi, p.family = afi2family(afi); p.prefixlen = 0; - if (p.family == AF_INET) { - table = index->map->ipv4_prefix_table; - if (!table) - index->map->ipv4_prefix_table = route_table_init(); - + if (p.family == AF_INET) table = index->map->ipv4_prefix_table; - } else { - table = index->map->ipv6_prefix_table; - if (!table) - index->map->ipv6_prefix_table = route_table_init(); - + else table = index->map->ipv6_prefix_table; - } /* Add default route to table */ rn = route_node_get(table, &p); @@ -2317,8 +2340,6 @@ 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 *rmap = NULL; - if (!index) return; @@ -2332,19 +2353,6 @@ static void route_map_pfx_tbl_update(route_map_event_t event, route_map_pfx_table_del_default(AFI_IP, index); route_map_pfx_table_del_default(AFI_IP6, index); - 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; } @@ -2546,6 +2554,9 @@ route_map_result_t route_map_apply_ext(struct route_map *map, struct prefix conv; if (recursion > RMAP_RECURSION_LIMIT) { + if (map) + map->applied++; + flog_warn( EC_LIB_RMAP_RECURSION_LIMIT, "route-map recursion limit (%d) reached, discarding route", @@ -2555,6 +2566,8 @@ route_map_result_t route_map_apply_ext(struct route_map *map, } if (map == NULL || map->head == NULL) { + if (map) + map->applied++; ret = RMAP_DENYMATCH; goto route_map_apply_end; } @@ -2569,12 +2582,14 @@ route_map_result_t route_map_apply_ext(struct route_map *map, */ if (prefix->family == AF_EVPN) { if (evpn_prefix2prefix(prefix, &conv) != 0) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "Unable to convert EVPN prefix %pFX into IPv4/IPv6 prefix. Falling back to non-optimized route-map lookup", prefix); } else { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, + DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "Converted EVPN prefix %pFX into %pFX for optimized route-map lookup", prefix, &conv); @@ -2586,13 +2601,13 @@ route_map_result_t route_map_apply_ext(struct route_map *map, index = route_map_get_index(map, prefix, match_object, &match_ret); if (index) { index->applied++; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug( "Best match route-map: %s, sequence: %d for pfx: %pFX, result: %s", map->name, index->pref, prefix, route_map_cmd_result_str(match_ret)); } else { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug( "No best match sequence for pfx: %pFX in route-map: %s, result: %s", prefix, map->name, @@ -2615,7 +2630,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, /* Apply this index. */ match_ret = route_map_apply_match(&index->match_list, prefix, match_object); - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) { + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) { zlog_debug( "Route-map: %s, sequence: %d, prefix: %pFX, result: %s", map->name, index->pref, prefix, @@ -2728,7 +2743,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, } route_map_apply_end: - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Route-map: %s, prefix: %pFX, result: %s", (map ? map->name : "null"), prefix, route_map_result_str(ret)); @@ -2783,7 +2798,7 @@ static void route_map_clear_reference(struct hash_bucket *bucket, void *arg) tmp_dep_data.rname = arg; dep_data = hash_release(dep->dep_rmap_hash, &tmp_dep_data); if (dep_data) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Clearing reference for %s to %s count: %d", dep->dep_name, tmp_dep_data.rname, dep_data->refcnt); @@ -2803,7 +2818,7 @@ static void route_map_clear_all_references(char *rmap_name) { int i; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Clearing references for %s", rmap_name); for (i = 1; i < ROUTE_MAP_DEP_MAX; i++) { @@ -2879,7 +2894,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, case RMAP_EVENT_LLIST_ADDED: case RMAP_EVENT_CALL_ADDED: case RMAP_EVENT_FILTER_ADDED: - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Adding dependency for filter %s in route-map %s", dep_name, rmap_name); dep = (struct route_map_dep *)hash_get( @@ -2908,7 +2923,7 @@ static int route_map_dep_update(struct hash *dephash, const char *dep_name, case RMAP_EVENT_LLIST_DELETED: case RMAP_EVENT_CALL_DELETED: case RMAP_EVENT_FILTER_DELETED: - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) 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); @@ -3034,7 +3049,7 @@ static void route_map_process_dependency(struct hash_bucket *bucket, void *data) dep_data = bucket->data; rmap_name = dep_data->rname; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Notifying %s of dependency", rmap_name); if (route_map_master.event_hook) (*route_map_master.event_hook)(rmap_name); @@ -3082,7 +3097,7 @@ void route_map_notify_dependencies(const char *affected_name, if (!dep->this_hash) dep->this_hash = upd8_hash; - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP))) zlog_debug("Filter %s updated", dep->dep_name); hash_iterate(dep->dep_rmap_hash, route_map_process_dependency, (void *)event); @@ -3101,27 +3116,24 @@ static void clear_route_map_helper(struct route_map *map) index->applied_clear = index->applied; } -DEFUN (rmap_clear_counters, +DEFPY (rmap_clear_counters, rmap_clear_counters_cmd, - "clear route-map counters [WORD]", + "clear route-map counters [RMAP_NAME$rmapname]", CLEAR_STR "route-map information\n" "counters associated with the specified route-map\n" "route-map name\n") { - int idx_word = 2; struct route_map *map; - const char *name = (argc == 3 ) ? argv[idx_word]->arg : NULL; - - if (name) { - map = route_map_lookup_by_name(name); + if (rmapname) { + map = route_map_lookup_by_name(rmapname); if (map) clear_route_map_helper(map); else { vty_out(vty, "%s: 'route-map %s' not found\n", - frr_protonameinst, name); + frr_protonameinst, rmapname); return CMD_SUCCESS; } } else { diff --git a/lib/routemap.h b/lib/routemap.h index 2197a49e76c9..a83ef9c9672a 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -259,6 +259,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(C, "frr-zebra-route-map:ipv4-next-hop-prefix-length")) #define IS_MATCH_SRC_PROTO(C) \ (strmatch(C, "frr-zebra-route-map:source-protocol")) +#define IS_MATCH_BGP_SRC_PROTO(C) \ + (strmatch(C, "frr-bgp-route-map:source-protocol")) #define IS_MATCH_SRC_INSTANCE(C) \ (strmatch(C, "frr-zebra-route-map:source-instance")) /* BGP route-map match conditions */ @@ -312,6 +314,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-route-map:ipv6-next-hop")) #define IS_SET_METRIC(A) \ (strmatch(A, "frr-route-map:set-metric")) +#define IS_SET_MIN_METRIC(A) (strmatch(A, "frr-route-map:set-min-metric")) +#define IS_SET_MAX_METRIC(A) (strmatch(A, "frr-route-map:set-max-metric")) #define IS_SET_TAG(A) (strmatch(A, "frr-route-map:set-tag")) #define IS_SET_SR_TE_COLOR(A) \ (strmatch(A, "frr-route-map:set-sr-te-color")) @@ -352,10 +356,15 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:set-extcommunity-none")) #define IS_SET_EXTCOMMUNITY_RT(A) \ (strmatch(A, "frr-bgp-route-map:set-extcommunity-rt")) +#define IS_SET_EXTCOMMUNITY_NT(A) \ + (strmatch(A, "frr-bgp-route-map:set-extcommunity-nt")) #define IS_SET_EXTCOMMUNITY_SOO(A) \ (strmatch(A, "frr-bgp-route-map:set-extcommunity-soo")) #define IS_SET_EXTCOMMUNITY_LB(A) \ (strmatch(A, "frr-bgp-route-map:set-extcommunity-lb")) +#define IS_SET_EXTCOMMUNITY_COLOR(A) \ + (strmatch(A, "frr-bgp-route-map:set-extcommunity-color")) + #define IS_SET_AGGREGATOR(A) \ (strmatch(A, "frr-bgp-route-map:aggregator")) #define IS_SET_AS_PREPEND(A) \ @@ -684,6 +693,22 @@ extern void route_map_no_set_metric_hook( int (*func)(struct route_map_index *index, const char *command, const char *arg, char *errmsg, size_t errmsg_len)); +/* set metric */ +extern void route_map_set_max_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); +/* no set metric */ +extern void route_map_no_set_max_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); +/* set metric */ +extern void route_map_set_min_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); +/* no set metric */ +extern void route_map_no_set_min_metric_hook( + int (*func)(struct route_map_index *index, const char *command, + const char *arg, char *errmsg, size_t errmsg_len)); /* set tag */ extern void route_map_set_tag_hook(int (*func)(struct route_map_index *index, const char *command, @@ -920,6 +945,25 @@ struct route_map_match_set_hooks { int (*no_set_metric)(struct route_map_index *index, const char *command, const char *arg, char *errmsg, size_t errmsg_len); + /* set min-metric */ + int (*set_min_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); + + /* no set min-metric */ + int (*no_set_min_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); + + /* set max-metric */ + int (*set_max_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); + + /* no set max-metric */ + int (*no_set_max_metric)(struct route_map_index *index, + const char *command, const char *arg, + char *errmsg, size_t errmsg_len); /* set tag */ int (*set_tag)(struct route_map_index *index, diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index 4345b74bc0c1..c1bdd28eab9d 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -599,11 +599,14 @@ void route_map_condition_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-match-condition/frr-zebra-route-map:ipv4-prefix-length")); - } else if (IS_MATCH_SRC_PROTO(condition)) { + } else if (IS_MATCH_SRC_PROTO(condition) || + IS_MATCH_BGP_SRC_PROTO(condition)) { vty_out(vty, " match source-protocol %s\n", yang_dnode_get_string( dnode, - "./rmap-match-condition/frr-zebra-route-map:source-protocol")); + IS_MATCH_SRC_PROTO(condition) + ? "./rmap-match-condition/frr-zebra-route-map:source-protocol" + : "./rmap-match-condition/frr-bgp-route-map:source-protocol")); } else if (IS_MATCH_SRC_INSTANCE(condition)) { vty_out(vty, " match source-instance %s\n", yang_dnode_get_string( @@ -890,6 +893,76 @@ DEFPY_YANG( return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(set_min_metric, set_min_metric_cmd, + "set min-metric <(0-4294967295)$metric>", + SET_STR + "Minimum metric value for destination routing protocol\n" + "Minimum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-min-metric']"; + char xpath_value[XPATH_MAXLEN]; + char value[64]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/min-metric", 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_min_metric, no_set_min_metric_cmd, + "no set min-metric [(0-4294967295)]", + NO_STR SET_STR + "Minimum metric value for destination routing protocol\n" + "Minumum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-min-metric']"; + + nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL); + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(set_max_metric, set_max_metric_cmd, + "set max-metric <(0-4294967295)$metric>", + SET_STR + "Maximum metric value for destination routing protocol\n" + "Miximum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-max-metric']"; + char xpath_value[XPATH_MAXLEN]; + char value[64]; + + nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL); + + snprintf(xpath_value, sizeof(xpath_value), + "%s/rmap-set-action/max-metric", 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_max_metric, no_set_max_metric_cmd, + "no set max-metric [(0-4294967295)]", + NO_STR SET_STR + "Maximum Metric value for destination routing protocol\n" + "Maximum metric value\n") +{ + const char *xpath = + "./set-action[action='frr-route-map:set-max-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", @@ -1011,6 +1084,14 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/value")); } + } else if (IS_SET_MIN_METRIC(action)) { + vty_out(vty, " set min-metric %s\n", + yang_dnode_get_string(dnode, + "./rmap-set-action/min-metric")); + } else if (IS_SET_MAX_METRIC(action)) { + vty_out(vty, " set max-metric %s\n", + yang_dnode_get_string(dnode, + "./rmap-set-action/max-metric")); } else if (IS_SET_TAG(action)) { vty_out(vty, " set tag %s\n", yang_dnode_get_string(dnode, "./rmap-set-action/tag")); @@ -1140,6 +1221,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:extcommunity-rt")); + } else if (IS_SET_EXTCOMMUNITY_NT(action)) { + vty_out(vty, " set extcommunity nt %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-nt")); } else if (IS_SET_EXTCOMMUNITY_SOO(action)) { vty_out(vty, " set extcommunity soo %s\n", yang_dnode_get_string( @@ -1173,6 +1259,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, strlcat(str, " non-transitive", sizeof(str)); vty_out(vty, " set extcommunity bandwidth %s\n", str); + } else if (IS_SET_EXTCOMMUNITY_COLOR(action)) { + vty_out(vty, " set extcommunity color %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:extcommunity-color")); } else if (IS_SET_EXTCOMMUNITY_NONE(action)) { if (yang_dnode_get_bool( dnode, @@ -1551,6 +1642,12 @@ void route_map_cli_init(void) install_element(RMAP_NODE, &set_metric_cmd); install_element(RMAP_NODE, &no_set_metric_cmd); + install_element(RMAP_NODE, &set_min_metric_cmd); + install_element(RMAP_NODE, &no_set_min_metric_cmd); + + install_element(RMAP_NODE, &set_max_metric_cmd); + install_element(RMAP_NODE, &no_set_max_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 index ab127dd0fcbf..465985099429 100644 --- a/lib/routemap_northbound.c +++ b/lib/routemap_northbound.c @@ -1027,6 +1027,110 @@ 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/min-metric + */ +static int set_action_min_metric_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource, + const char *value, char *errmsg, + size_t errmsg_len) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_min_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_min_metric; + rhc->rhc_rule = "min-metric"; + + rv = rmap_match_set_hook.set_min_metric(rhc->rhc_rmi, "min-metric", + value, errmsg, errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_min_metric_modify(struct nb_cb_modify_args *args) +{ + const char *min_metric = yang_dnode_get_string(args->dnode, NULL); + + return set_action_min_metric_modify(args->event, args->dnode, + args->resource, min_metric, + args->errmsg, args->errmsg_len); +} + +static int lib_route_map_entry_set_action_min_metric_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/max-metric + */ +static int set_action_max_metric_modify(enum nb_event event, + const struct lyd_node *dnode, + union nb_resource *resource, + const char *value, char *errmsg, + size_t errmsg_len) +{ + struct routemap_hook_context *rhc; + int rv; + + if (event != NB_EV_APPLY) + return NB_OK; + + /* Check for hook function. */ + if (rmap_match_set_hook.set_max_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_max_metric; + rhc->rhc_rule = "max-metric"; + + rv = rmap_match_set_hook.set_max_metric(rhc->rhc_rmi, "max-metric", + value, errmsg, errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + + return NB_OK; +} + +static int +lib_route_map_entry_set_action_max_metric_modify(struct nb_cb_modify_args *args) +{ + const char *max_metric = yang_dnode_get_string(args->dnode, NULL); + + return set_action_max_metric_modify(args->event, args->dnode, + args->resource, max_metric, + args->errmsg, args->errmsg_len); +} + +static int lib_route_map_entry_set_action_max_metric_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 */ @@ -1368,6 +1472,20 @@ const struct frr_yang_module_info frr_route_map_info = { .destroy = lib_route_map_entry_set_action_value_destroy, } }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/min-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_min_metric_modify, + .destroy = lib_route_map_entry_set_action_min_metric_destroy, + } + }, + { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/max-metric", + .cbs = { + .modify = lib_route_map_entry_set_action_max_metric_modify, + .destroy = lib_route_map_entry_set_action_max_metric_destroy, + } + }, { .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/add-metric", .cbs = { diff --git a/lib/segment_routing.c b/lib/segment_routing.c new file mode 100644 index 000000000000..f45a64d29ad0 --- /dev/null +++ b/lib/segment_routing.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * segment_routing.c: Segment-Routing Library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#include "segment_routing.h" + +const char *sr_algorithm_string(uint8_t algo) +{ + switch (algo) { + case SR_ALGORITHM_SPF: + return "SPF"; + case SR_ALGORITHM_STRICT_SPF: + return "Strict SPF"; + case SR_ALGORITHM_UNSET: + return "Unset"; + default: + return algo >= SR_ALGORITHM_FLEX_MIN ? "Flex-Algo" : "Unknown"; + } +} diff --git a/lib/segment_routing.h b/lib/segment_routing.h new file mode 100644 index 000000000000..5e71466bf193 --- /dev/null +++ b/lib/segment_routing.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/********************************************************************* + * Copyright 2022 Hiroki Shirokura, LINE Corporation + * Copyright 2022 Masakazu Asama + * Copyright 2022 6WIND S.A. + * + * segment_routing.h: Segment-Routing Library + * + * Authors + * ------- + * Hiroki Shirokura + * Masakazu Asama + * Louis Scalbert + */ + +#ifndef _FRR_SR_H +#define _FRR_SR_H + +#include <zebra.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * IGP Algorithm Types + * https://www.iana.org/assignments/igp-parameters/igp-parameters.xhtml + */ +#define SR_ALGORITHM_SPF 0 /* RFC8665 */ +#define SR_ALGORITHM_STRICT_SPF 1 /* RFC8665 */ +#define SR_ALGORITHM_UNSET 127 /* FRRouting defined */ +#define SR_ALGORITHM_FLEX_MIN 128 /* RFC9350 Flex-Algorithm */ +#define SR_ALGORITHM_FLEX_MAX 255 /* RFC9350 Flex-Algorithm */ +#define SR_ALGORITHM_COUNT 256 + +const char *sr_algorithm_string(uint8_t algo); + +#ifdef __cplusplus +} +#endif + +#endif /* _FRR_SR_H */ diff --git a/lib/sigevent.c b/lib/sigevent.c index 3ed34894e505..3cd65eb80085 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -22,7 +22,7 @@ /* master signals descriptor struct */ static struct frr_sigevent_master_t { - struct thread *t; + struct event *t; struct frr_signal_t *signals; int sigc; @@ -127,14 +127,14 @@ int frr_sigevent_process(void) #ifdef SIGEVENT_SCHEDULE_THREAD /* timer thread to check signals. shouldn't be needed */ -void frr_signal_timer(struct thread *t) +void frr_signal_timer(struct event *t) { struct frr_sigevent_master_t *sigm; - sigm = THREAD_ARG(t); + sigm = EVENT_ARG(t); sigm->t = NULL; - thread_add_timer(sigm->t->master, frr_signal_timer, &sigmaster, - FRR_SIGNAL_TIMER_INTERVAL, &sigm->t); + event_add_timer(sigm->t->master, frr_signal_timer, &sigmaster, + FRR_SIGNAL_TIMER_INTERVAL, &sigm->t); frr_sigevent_process(); } #endif /* SIGEVENT_SCHEDULE_THREAD */ @@ -331,8 +331,7 @@ static void trap_default_signals(void) } } -void signal_init(struct thread_master *m, int sigc, - struct frr_signal_t signals[]) +void signal_init(struct event_loop *m, int sigc, struct frr_signal_t signals[]) { int i = 0; @@ -354,7 +353,7 @@ void signal_init(struct thread_master *m, int sigc, #ifdef SIGEVENT_SCHEDULE_THREAD sigmaster.t = NULL; - thread_add_timer(m, frr_signal_timer, &sigmaster, - FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t); + event_add_timer(m, frr_signal_timer, &sigmaster, + FRR_SIGNAL_TIMER_INTERVAL, &sigmaster.t); #endif /* SIGEVENT_SCHEDULE_THREAD */ } diff --git a/lib/sigevent.h b/lib/sigevent.h index e58b9a70c02b..0b07f594c1c4 100644 --- a/lib/sigevent.h +++ b/lib/sigevent.h @@ -8,7 +8,7 @@ #ifndef _FRR_SIGNAL_H #define _FRR_SIGNAL_H -#include <thread.h> +#include <frrevent.h> #ifdef __cplusplus extern "C" { @@ -25,12 +25,12 @@ struct frr_signal_t { /* initialise sigevent system * takes: - * - pointer to valid struct thread_master + * - pointer to valid struct event_loop * - number of elements in passed in signals array * - array of frr_signal_t's describing signals to handle * and handlers to use for each signal */ -extern void signal_init(struct thread_master *m, int sigc, +extern void signal_init(struct event_loop *m, int sigc, struct frr_signal_t *signals); diff --git a/lib/smux.h b/lib/smux.h index 28a303cf729c..cec4d2a1bf1e 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -9,7 +9,7 @@ #include <net-snmp/agent/net-snmp-agent-includes.h> #include <net-snmp/agent/snmp_vars.h> -#include "thread.h" +#include "frrevent.h" #include "hook.h" #ifdef __cplusplus @@ -99,7 +99,7 @@ struct index_oid { */ extern bool smux_enabled(void); -extern void smux_init(struct thread_master *tm); +extern void smux_init(struct event_loop *tm); extern void smux_agentx_enable(void); extern void smux_register_mib(const char *, struct variable *, size_t, int, oid[], size_t); diff --git a/lib/spf_backoff.c b/lib/spf_backoff.c index 1e80b5ec2deb..b05c44ddf735 100644 --- a/lib/spf_backoff.c +++ b/lib/spf_backoff.c @@ -17,7 +17,7 @@ #include "command.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" DEFINE_MTYPE_STATIC(LIB, SPF_BACKOFF, "SPF backoff"); @@ -37,7 +37,7 @@ enum spf_backoff_state { }; struct spf_backoff { - struct thread_master *m; + struct event_loop *m; /* Timers as per draft */ long init_delay; @@ -48,8 +48,8 @@ struct spf_backoff { /* State machine */ enum spf_backoff_state state; - struct thread *t_holddown; - struct thread *t_timetolearn; + struct event *t_holddown; + struct event *t_timetolearn; /* For debugging */ char *name; @@ -70,7 +70,7 @@ static const char *spf_backoff_state2str(enum spf_backoff_state state) return "???"; } -struct spf_backoff *spf_backoff_new(struct thread_master *m, const char *name, +struct spf_backoff *spf_backoff_new(struct event_loop *m, const char *name, long init_delay, long short_delay, long long_delay, long holddown, long timetolearn) @@ -97,27 +97,27 @@ void spf_backoff_free(struct spf_backoff *backoff) if (!backoff) return; - thread_cancel(&backoff->t_holddown); - thread_cancel(&backoff->t_timetolearn); + event_cancel(&backoff->t_holddown); + event_cancel(&backoff->t_timetolearn); XFREE(MTYPE_SPF_BACKOFF_NAME, backoff->name); XFREE(MTYPE_SPF_BACKOFF, backoff); } -static void spf_backoff_timetolearn_elapsed(struct thread *thread) +static void spf_backoff_timetolearn_elapsed(struct event *thread) { - struct spf_backoff *backoff = THREAD_ARG(thread); + struct spf_backoff *backoff = EVENT_ARG(thread); backoff->state = SPF_BACKOFF_LONG_WAIT; backoff_debug("SPF Back-off(%s) TIMETOLEARN elapsed, move to state %s", backoff->name, spf_backoff_state2str(backoff->state)); } -static void spf_backoff_holddown_elapsed(struct thread *thread) +static void spf_backoff_holddown_elapsed(struct event *thread) { - struct spf_backoff *backoff = THREAD_ARG(thread); + struct spf_backoff *backoff = EVENT_ARG(thread); - THREAD_OFF(backoff->t_timetolearn); + EVENT_OFF(backoff->t_timetolearn); timerclear(&backoff->first_event_time); backoff->state = SPF_BACKOFF_QUIET; backoff_debug("SPF Back-off(%s) HOLDDOWN elapsed, move to state %s", @@ -139,21 +139,21 @@ long spf_backoff_schedule(struct spf_backoff *backoff) switch (backoff->state) { case SPF_BACKOFF_QUIET: backoff->state = SPF_BACKOFF_SHORT_WAIT; - thread_add_timer_msec( + event_add_timer_msec( backoff->m, spf_backoff_timetolearn_elapsed, backoff, backoff->timetolearn, &backoff->t_timetolearn); - thread_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed, - backoff, backoff->holddown, - &backoff->t_holddown); + event_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed, + backoff, backoff->holddown, + &backoff->t_holddown); backoff->first_event_time = now; rv = backoff->init_delay; break; case SPF_BACKOFF_SHORT_WAIT: case SPF_BACKOFF_LONG_WAIT: - thread_cancel(&backoff->t_holddown); - thread_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed, - backoff, backoff->holddown, - &backoff->t_holddown); + event_cancel(&backoff->t_holddown); + event_add_timer_msec(backoff->m, spf_backoff_holddown_elapsed, + backoff, backoff->holddown, + &backoff->t_holddown); if (backoff->state == SPF_BACKOFF_SHORT_WAIT) rv = backoff->short_delay; else @@ -204,8 +204,8 @@ void spf_backoff_show(struct spf_backoff *backoff, struct vty *vty, vty_out(vty, "%sHolddown timer: %ld msec\n", prefix, backoff->holddown); if (backoff->t_holddown) { - struct timeval remain = - thread_timer_remain(backoff->t_holddown); + struct timeval remain = event_timer_remain(backoff->t_holddown); + vty_out(vty, "%s Still runs for %lld msec\n", prefix, (long long)remain.tv_sec * 1000 @@ -218,7 +218,7 @@ void spf_backoff_show(struct spf_backoff *backoff, struct vty *vty, backoff->timetolearn); if (backoff->t_timetolearn) { struct timeval remain = - thread_timer_remain(backoff->t_timetolearn); + event_timer_remain(backoff->t_timetolearn); vty_out(vty, "%s Still runs for %lld msec\n", prefix, (long long)remain.tv_sec * 1000 diff --git a/lib/spf_backoff.h b/lib/spf_backoff.h index 87aa4a0825d1..83f1b76adcb1 100644 --- a/lib/spf_backoff.h +++ b/lib/spf_backoff.h @@ -18,10 +18,10 @@ extern "C" { #endif struct spf_backoff; -struct thread_master; +struct event_loop; struct vty; -struct spf_backoff *spf_backoff_new(struct thread_master *m, const char *name, +struct spf_backoff *spf_backoff_new(struct event_loop *m, const char *name, long init_delay, long short_delay, long long_delay, long holddown, long timetolearn); diff --git a/lib/subdir.am b/lib/subdir.am index beef8675aaba..d7b28ffbd59e 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -25,6 +25,7 @@ lib_libfrr_la_SOURCES = \ lib/command_parse.y \ lib/cspf.c \ lib/csv.c \ + lib/darr.c \ lib/debug.c \ lib/defaults.c \ lib/distribute.c \ @@ -33,13 +34,12 @@ lib_libfrr_la_SOURCES = \ lib/filter.c \ lib/filter_cli.c \ lib/filter_nb.c \ + lib/flex_algo.c \ lib/frrcu.c \ lib/frrlua.c \ lib/frrscript.c \ lib/frr_pthread.c \ lib/frrstr.c \ - lib/getopt.c \ - lib/getopt1.c \ lib/grammar_sandbox.c \ lib/graph.c \ lib/hash.c \ @@ -49,6 +49,7 @@ lib_libfrr_la_SOURCES = \ lib/if_rmap.c \ lib/imsg-buffer.c \ lib/imsg.c \ + lib/iso.c \ lib/jhash.c \ lib/json.c \ lib/keychain.c \ @@ -64,6 +65,9 @@ lib_libfrr_la_SOURCES = \ lib/log_vty.c \ lib/md5.c \ lib/memory.c \ + lib/mgmt_be_client.c \ + lib/mgmt_fe_client.c \ + lib/mgmt_msg.c \ lib/mlag.c \ lib/module.c \ lib/mpls.c \ @@ -97,6 +101,7 @@ lib_libfrr_la_SOURCES = \ lib/sockopt.c \ lib/sockunion.c \ lib/spf_backoff.c \ + lib/segment_routing.c \ lib/srcdest_table.c \ lib/stream.c \ lib/strformat.c \ @@ -105,7 +110,7 @@ lib_libfrr_la_SOURCES = \ lib/systemd.c \ lib/table.c \ lib/termtable.c \ - lib/thread.c \ + lib/event.c \ lib/typerb.c \ lib/typesafe.c \ lib/vector.c \ @@ -134,6 +139,7 @@ lib_libfrr_la_SOURCES = \ nodist_lib_libfrr_la_SOURCES = \ yang/frr-affinity-map.yang.c \ yang/frr-filter.yang.c \ + yang/frr-if-rmap.yang.c \ yang/frr-interface.yang.c \ yang/frr-route-map.yang.c \ yang/frr-route-types.yang.c \ @@ -146,6 +152,23 @@ nodist_lib_libfrr_la_SOURCES = \ yang/frr-module-translator.yang.c \ # end +# Add logic to build mgmt.proto +lib_libfrr_la_LIBADD += $(PROTOBUF_C_LIBS) + +BUILT_SOURCES += \ + lib/mgmt.pb-c.c \ + lib/mgmt.pb-c.h \ + # end + +CLEANFILES += \ + lib/mgmt.pb-c.h \ + lib/mgmt.pb-c.c \ + # end + +lib_libfrr_la_SOURCES += \ + lib/mgmt.pb-c.c \ + #end + if SQLITE3 lib_libfrr_la_LIBADD += $(SQLITE3_LIBS) lib_libfrr_la_SOURCES += lib/db.c @@ -155,13 +178,16 @@ clippy_scan += \ lib/affinitymap_cli.c \ lib/if.c \ lib/filter_cli.c \ + lib/if_rmap.c \ lib/log_vty.c \ + lib/mgmt_be_client.c \ + lib/mgmt_fe_client.c \ lib/nexthop_group.c \ lib/northbound_cli.c \ lib/plist.c \ lib/routemap.c \ lib/routemap_cli.c \ - lib/thread.c \ + lib/event.c \ lib/vty.c \ lib/zlog_5424_cli.c \ # end @@ -184,12 +210,14 @@ pkginclude_HEADERS += \ lib/compiler.h \ lib/cspf.h \ lib/csv.h \ + lib/darr.h \ lib/db.h \ lib/debug.h \ lib/defaults.h \ lib/distribute.h \ lib/ferr.h \ lib/filter.h \ + lib/flex_algo.h \ lib/freebsd-queue.h \ lib/frrlua.h \ lib/frrscript.h \ @@ -197,7 +225,6 @@ pkginclude_HEADERS += \ lib/frratomic.h \ lib/frrcu.h \ lib/frrstr.h \ - lib/getopt.h \ lib/graph.h \ lib/hash.h \ lib/hook.h \ @@ -207,6 +234,7 @@ pkginclude_HEADERS += \ lib/if_rmap.h \ lib/imsg.h \ lib/ipaddr.h \ + lib/iso.h \ lib/jhash.h \ lib/json.h \ lib/keychain.h \ @@ -222,6 +250,11 @@ pkginclude_HEADERS += \ lib/log_vty.h \ lib/md5.h \ lib/memory.h \ + lib/mgmt.pb-c.h \ + lib/mgmt_be_client.h \ + lib/mgmt_fe_client.h \ + lib/mgmt_msg.h \ + lib/mgmt_pb.h \ lib/module.h \ lib/monotime.h \ lib/mpls.h \ @@ -257,13 +290,14 @@ pkginclude_HEADERS += \ lib/sockopt.h \ lib/sockunion.h \ lib/spf_backoff.h \ + lib/segment_routing.h \ lib/srcdest_table.h \ lib/srte.h \ lib/stream.h \ lib/systemd.h \ lib/table.h \ lib/termtable.h \ - lib/thread.h \ + lib/frrevent.h \ lib/trace.h \ lib/typerb.h \ lib/typesafe.h \ diff --git a/lib/systemd.c b/lib/systemd.c index 0106e88b93c0..56a53a6e781b 100644 --- a/lib/systemd.c +++ b/lib/systemd.c @@ -7,7 +7,7 @@ #include <zebra.h> #include <sys/un.h> -#include "thread.h" +#include "frrevent.h" #include "systemd.h" #include "lib_errors.h" @@ -63,18 +63,18 @@ void systemd_send_stopping(void) systemd_send_information("STOPPING=1"); } -static struct thread_master *systemd_master = NULL; +static struct event_loop *systemd_master = NULL; -static void systemd_send_watchdog(struct thread *t) +static void systemd_send_watchdog(struct event *t) { systemd_send_information("WATCHDOG=1"); assert(watchdog_msec > 0); - thread_add_timer_msec(systemd_master, systemd_send_watchdog, NULL, - watchdog_msec, NULL); + event_add_timer_msec(systemd_master, systemd_send_watchdog, NULL, + watchdog_msec, NULL); } -void systemd_send_started(struct thread_master *m) +void systemd_send_started(struct event_loop *m) { assert(m != NULL); diff --git a/lib/systemd.h b/lib/systemd.h index 09f27a70baad..ba2461b8a1d7 100644 --- a/lib/systemd.h +++ b/lib/systemd.h @@ -21,11 +21,11 @@ extern bool sd_stderr_is_journal; void systemd_send_stopping(void); /* - * master - The struct thread_master * to use to schedule ourself + * master - The struct event_loop * to use to schedule ourself * the_process - Should we send watchdog if we are not the requested * process? */ -void systemd_send_started(struct thread_master *master); +void systemd_send_started(struct event_loop *master); /* * status - A status string to send to systemd diff --git a/lib/termtable.c b/lib/termtable.c index 30f1ab74c6dd..9b36d5ebfa8b 100644 --- a/lib/termtable.c +++ b/lib/termtable.c @@ -7,6 +7,7 @@ #include <zebra.h> #include <stdio.h> +#include "lib/json.h" #include "printfrr.h" #include "memory.h" #include "termtable.h" @@ -485,3 +486,45 @@ char *ttable_dump(struct ttable *tt, const char *newline) return buf; } + +/* Crude conversion from ttable to json array. + * Assume that the first row has column headings. + * + * Formats are: + * d int32 + * f double + * l int64 + * s string (default) + */ +json_object *ttable_json(struct ttable *tt, const char *const formats) +{ + struct ttable_cell *row; /* iteration pointers */ + json_object *json = NULL; + + json = json_object_new_array(); + + for (int i = 1; i < tt->nrows; i++) { + json_object *jobj; + json_object *val; + + row = tt->table[i]; + jobj = json_object_new_object(); + json_object_array_add(json, jobj); + for (int j = 0; j < tt->ncols; j++) { + switch (formats[j]) { + case 'd': + case 'l': + val = json_object_new_int64(atol(row[j].text)); + break; + case 'f': + val = json_object_new_double(atof(row[j].text)); + break; + default: + val = json_object_new_string(row[j].text); + } + json_object_object_add(jobj, tt->table[0][j].text, val); + } + } + + return json; +} diff --git a/lib/termtable.h b/lib/termtable.h index 3aa8caee89bd..7258682bd80a 100644 --- a/lib/termtable.h +++ b/lib/termtable.h @@ -8,6 +8,7 @@ #define _TERMTABLE_H_ #include <zebra.h> +#include "lib/json.h" #ifdef __cplusplus extern "C" { @@ -277,6 +278,17 @@ void ttable_rowseps(struct ttable *tt, unsigned int row, */ char *ttable_dump(struct ttable *tt, const char *newline); +/** + * Convert a table to a JSON array of objects. + * + * Caller must free the returned json_object structure. + * + * @param tt the table to convert + * @param formats an array of characters indicating what JSON type should be + * used. + */ +json_object *ttable_json(struct ttable *tt, const char *const formats); + #ifdef __cplusplus } #endif diff --git a/lib/thread.h b/lib/thread.h deleted file mode 100644 index 128d11b6ebbd..000000000000 --- a/lib/thread.h +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Thread management routine header. - * Copyright (C) 1998 Kunihiro Ishiguro - */ - -#ifndef _ZEBRA_THREAD_H -#define _ZEBRA_THREAD_H - -#include <zebra.h> -#include <pthread.h> -#include <poll.h> -#include "monotime.h" -#include "frratomic.h" -#include "typesafe.h" -#include "xref.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern bool cputime_enabled; -extern unsigned long cputime_threshold; -/* capturing wallclock time is always enabled since it is fast (reading - * hardware TSC w/o syscalls) - */ -extern unsigned long walltime_threshold; - -struct rusage_t { -#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID - struct timespec cpu; -#else - struct rusage cpu; -#endif - struct timeval real; -}; -#define RUSAGE_T struct rusage_t - -#define GETRUSAGE(X) thread_getrusage(X) - -PREDECL_LIST(thread_list); -PREDECL_HEAP(thread_timer_list); - -struct fd_handler { - /* number of pfd that fit in the allocated space of pfds. This is a - * constant and is the same for both pfds and copy. - */ - nfds_t pfdsize; - - /* file descriptors to monitor for i/o */ - struct pollfd *pfds; - /* number of pollfds stored in pfds */ - nfds_t pfdcount; - - /* chunk used for temp copy of pollfds */ - struct pollfd *copy; - /* number of pollfds stored in copy */ - nfds_t copycount; -}; - -struct xref_threadsched { - struct xref xref; - - const char *funcname; - const char *dest; - uint32_t thread_type; -}; - -/* Master of the theads. */ -struct thread_master { - char *name; - - struct thread **read; - struct thread **write; - struct thread_timer_list_head timer; - struct thread_list_head event, ready, unuse; - struct list *cancel_req; - bool canceled; - pthread_cond_t cancel_cond; - struct hash *cpu_record; - int io_pipe[2]; - int fd_limit; - struct fd_handler handler; - unsigned long alloc; - long selectpoll_timeout; - bool spin; - bool handle_signals; - pthread_mutex_t mtx; - pthread_t owner; - - bool ready_run_loop; - RUSAGE_T last_getrusage; -}; - -/* Thread itself. */ -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 */ - void (*func)(struct thread *); /* event function */ - void *arg; /* event argument */ - union { - int val; /* second argument of the event. */ - int fd; /* file descriptor in case of r/w */ - struct timeval sands; /* rest of time sands value. */ - } u; - struct timeval real; - struct cpu_thread_history *hist; /* cache pointer to cpu_history */ - unsigned long yield; /* yield time in microseconds */ - const struct xref_threadsched *xref; /* origin location */ - pthread_mutex_t mtx; /* mutex for thread.c functions */ - bool ignore_timer_late; -}; - -#ifdef _FRR_ATTRIBUTE_PRINTFRR -#pragma FRR printfrr_ext "%pTH" (struct thread *) -#endif - -struct cpu_thread_history { - void (*func)(struct thread *); - atomic_size_t total_cpu_warn; - atomic_size_t total_wall_warn; - atomic_size_t total_starv_warn; - atomic_size_t total_calls; - atomic_size_t total_active; - struct time_stats { - atomic_size_t total, max; - } real; - struct time_stats cpu; - atomic_uint_fast32_t types; - const char *funcname; -}; - -/* Struct timeval's tv_usec one second value. */ -#define TIMER_SECOND_MICRO 1000000L - -/* Thread types. */ -#define THREAD_READ 0 -#define THREAD_WRITE 1 -#define THREAD_TIMER 2 -#define THREAD_EVENT 3 -#define THREAD_READY 4 -#define THREAD_UNUSED 5 -#define THREAD_EXECUTE 6 - -/* 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) -#define THREAD_VAL(X) ((X)->u.val) - -/* - * Please consider this macro deprecated, and do not use it in new code. - */ -#define THREAD_OFF(thread) \ - do { \ - if ((thread)) \ - thread_cancel(&(thread)); \ - } while (0) - -/* - * Macro wrappers to generate xrefs for all thread add calls. Includes - * file/line/function info for debugging/tracing. - */ -#include "lib/xref.h" - -#define _xref_t_a(addfn, type, m, f, a, v, t) \ - ({ \ - static const struct xref_threadsched _xref \ - __attribute__((used)) = { \ - .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \ - .funcname = #f, \ - .dest = #t, \ - .thread_type = THREAD_ ## type, \ - }; \ - XREF_LINK(_xref.xref); \ - _thread_add_ ## addfn(&_xref, m, f, a, v, t); \ - }) \ - /* end */ - -#define thread_add_read(m,f,a,v,t) _xref_t_a(read_write, READ, m,f,a,v,t) -#define thread_add_write(m,f,a,v,t) _xref_t_a(read_write, WRITE, m,f,a,v,t) -#define thread_add_timer(m,f,a,v,t) _xref_t_a(timer, TIMER, m,f,a,v,t) -#define thread_add_timer_msec(m,f,a,v,t) _xref_t_a(timer_msec, TIMER, m,f,a,v,t) -#define thread_add_timer_tv(m,f,a,v,t) _xref_t_a(timer_tv, TIMER, m,f,a,v,t) -#define thread_add_event(m,f,a,v,t) _xref_t_a(event, EVENT, m,f,a,v,t) - -#define thread_execute(m,f,a,v) \ - ({ \ - static const struct xref_threadsched _xref \ - __attribute__((used)) = { \ - .xref = XREF_INIT(XREFT_THREADSCHED, NULL, __func__), \ - .funcname = #f, \ - .dest = NULL, \ - .thread_type = THREAD_EXECUTE, \ - }; \ - XREF_LINK(_xref.xref); \ - _thread_execute(&_xref, m, f, a, v); \ - }) /* end */ - -/* Prototypes. */ -extern struct thread_master *thread_master_create(const char *); -void thread_master_set_name(struct thread_master *master, const char *name); -extern void thread_master_free(struct thread_master *); -extern void thread_master_free_unused(struct thread_master *); - -extern void _thread_add_read_write(const struct xref_threadsched *xref, - struct thread_master *master, - void (*fn)(struct thread *), void *arg, - int fd, struct thread **tref); - -extern void _thread_add_timer(const struct xref_threadsched *xref, - struct thread_master *master, - void (*fn)(struct thread *), void *arg, long t, - struct thread **tref); - -extern void _thread_add_timer_msec(const struct xref_threadsched *xref, - struct thread_master *master, - void (*fn)(struct thread *), void *arg, - long t, struct thread **tref); - -extern void _thread_add_timer_tv(const struct xref_threadsched *xref, - struct thread_master *master, - void (*fn)(struct thread *), void *arg, - struct timeval *tv, struct thread **tref); - -extern void _thread_add_event(const struct xref_threadsched *xref, - struct thread_master *master, - void (*fn)(struct thread *), void *arg, int val, - struct thread **tref); - -extern void _thread_execute(const struct xref_threadsched *xref, - struct thread_master *master, - void (*fn)(struct thread *), void *arg, int val); - -extern void thread_cancel(struct thread **event); -extern void thread_cancel_async(struct thread_master *, struct thread **, - void *); -/* Cancel ready tasks with an arg matching 'arg' */ -extern void thread_cancel_event_ready(struct thread_master *m, void *arg); -/* Cancel all tasks with an arg matching 'arg', including timers and io */ -extern void thread_cancel_event(struct thread_master *m, void *arg); -extern struct thread *thread_fetch(struct thread_master *, struct thread *); -extern void thread_call(struct thread *); -extern unsigned long thread_timer_remain_second(struct thread *); -extern struct timeval thread_timer_remain(struct thread *); -extern unsigned long thread_timer_remain_msec(struct thread *); -extern int thread_should_yield(struct thread *); -/* set yield time for thread */ -extern void thread_set_yield_time(struct thread *, unsigned long); - -/* Internal libfrr exports */ -extern void thread_getrusage(RUSAGE_T *); -extern void thread_cmd_init(void); - -/* Returns elapsed real (wall clock) time. */ -extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before, - unsigned long *cpu_time_elapsed); - -/* 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); - -static inline bool thread_is_scheduled(struct thread *thread) -{ - if (thread) - return true; - - return false; -} - -/* Debug signal mask */ -void debug_signals(const sigset_t *sigs); - -static inline void thread_ignore_late_timer(struct thread *thread) -{ - thread->ignore_timer_late = true; -} - -#ifdef __cplusplus -} -#endif - -#endif /* _ZEBRA_THREAD_H */ diff --git a/lib/typesafe.c b/lib/typesafe.c index 0da35d0f8c18..c0774479852d 100644 --- a/lib/typesafe.c +++ b/lib/typesafe.c @@ -85,6 +85,15 @@ void typesafe_hash_grow(struct thash_head *head) uint32_t newsize = head->count, i, j; uint8_t newshift, delta; + /* note hash_grow is called after head->count++, so newsize is + * guaranteed to be >= 1. So the minimum argument to builtin_ctz + * below is 2, which returns 1, and that makes newshift >= 2. + * + * Calling hash_grow with a zero head->count would result in a + * malformed hash table that has tabshift == 1. + */ + assert(head->count > 0); + hash_consistency_check(head); newsize |= newsize >> 1; diff --git a/lib/typesafe.h b/lib/typesafe.h index 1e3f93256562..8eb59c33b709 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -20,6 +20,9 @@ extern "C" { #define frr_each(prefix, head, item) \ for (item = prefix##_first(head); item; \ item = prefix##_next(head, item)) +#define frr_each_const(prefix, head, item) \ + for (item = prefix##_const_first(head); item; \ + item = prefix##_const_next(head, item)) #define frr_each_safe(prefix, head, item) \ for (typeof(prefix##_next_safe(head, NULL)) prefix##_safe = \ prefix##_next_safe(head, \ @@ -780,6 +783,12 @@ struct thash_head { struct thash_item **entries; uint32_t count; + /* tabshift can be 0 if the hash table is empty and entries is NULL. + * otherwise it will always be 2 or larger because it contains + * the shift value *plus 1*. This is a trick to make HASH_SIZE return + * the correct value (with the >> 1) for tabshift == 0, without needing + * a conditional branch. + */ uint8_t tabshift; uint8_t minshift, maxshift; }; @@ -788,8 +797,11 @@ struct thash_head { ((1U << (tabshift)) >> 1) #define HASH_SIZE(head) \ _HASH_SIZE((head).tabshift) -#define _HASH_KEY(tabshift, val) \ - ((val) >> (33 - (tabshift))) +#define _HASH_KEY(tabshift, val) \ + ({ \ + assume((tabshift) >= 2 && (tabshift) <= 33); \ + (val) >> (33 - (tabshift)); \ + }) #define HASH_KEY(head, val) \ _HASH_KEY((head).tabshift, val) #define HASH_GROW_THRESHOLD(head) \ @@ -936,6 +948,8 @@ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ macro_pure bool prefix ## _member(const struct prefix##_head *h, \ const type *item) \ { \ + if (!h->hh.tabshift) \ + return NULL; \ uint32_t hval = item->field.hi.hashval, hbits = HASH_KEY(h->hh, hval); \ const struct thash_item *hitem = h->hh.entries[hbits]; \ while (hitem && hitem->hashval < hval) \ diff --git a/lib/vrf.c b/lib/vrf.c index a60f532f41c3..b279e8b7bf48 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -384,58 +384,80 @@ static void vrf_hash_bitmap_free(void *data) XFREE(MTYPE_VRF_BITMAP, bit); } -vrf_bitmap_t vrf_bitmap_init(void) +void vrf_bitmap_init(vrf_bitmap_t *pbmap) { - return hash_create_size(32, vrf_hash_bitmap_key, vrf_hash_bitmap_cmp, - "VRF BIT HASH"); + *pbmap = NULL; } -void vrf_bitmap_free(vrf_bitmap_t bmap) +void vrf_bitmap_free(vrf_bitmap_t *pbmap) { - struct hash *vrf_hash = bmap; + struct hash *vrf_hash; - if (vrf_hash == NULL) + if (!*pbmap) return; - hash_clean(vrf_hash, vrf_hash_bitmap_free); - hash_free(vrf_hash); + vrf_hash = *pbmap; + + hash_clean_and_free(&vrf_hash, vrf_hash_bitmap_free); } -void vrf_bitmap_set(vrf_bitmap_t bmap, vrf_id_t vrf_id) +void vrf_bitmap_set(vrf_bitmap_t *pbmap, vrf_id_t vrf_id) { struct vrf_bit_set lookup = { .vrf_id = vrf_id }; - struct hash *vrf_hash = bmap; + struct hash *vrf_hash; struct vrf_bit_set *bit; - if (vrf_hash == NULL || vrf_id == VRF_UNKNOWN) + if (vrf_id == VRF_UNKNOWN) return; + if (!*pbmap) + *pbmap = vrf_hash = + hash_create_size(2, vrf_hash_bitmap_key, + vrf_hash_bitmap_cmp, "VRF BIT HASH"); + else + vrf_hash = *pbmap; + bit = hash_get(vrf_hash, &lookup, vrf_hash_bitmap_alloc); bit->set = true; } -void vrf_bitmap_unset(vrf_bitmap_t bmap, vrf_id_t vrf_id) +void vrf_bitmap_unset(vrf_bitmap_t *pbmap, vrf_id_t vrf_id) { struct vrf_bit_set lookup = { .vrf_id = vrf_id }; - struct hash *vrf_hash = bmap; + struct hash *vrf_hash; struct vrf_bit_set *bit; - if (vrf_hash == NULL || vrf_id == VRF_UNKNOWN) + if (vrf_id == VRF_UNKNOWN) + return; + + /* + * If the hash is not created then unsetting is unnecessary + */ + if (!*pbmap) + return; + + vrf_hash = *pbmap; + + /* + * If we can't look it up, no need to unset it! + */ + bit = hash_lookup(vrf_hash, &lookup); + if (!bit) return; - bit = hash_get(vrf_hash, &lookup, vrf_hash_bitmap_alloc); bit->set = false; } -int vrf_bitmap_check(vrf_bitmap_t bmap, vrf_id_t vrf_id) +int vrf_bitmap_check(vrf_bitmap_t *pbmap, vrf_id_t vrf_id) { struct vrf_bit_set lookup = { .vrf_id = vrf_id }; - struct hash *vrf_hash = bmap; + struct hash *vrf_hash; struct vrf_bit_set *bit; - if (vrf_hash == NULL || vrf_id == VRF_UNKNOWN) + if (!*pbmap || vrf_id == VRF_UNKNOWN) return 0; + vrf_hash = *pbmap; bit = hash_lookup(vrf_hash, &lookup); if (bit) return bit->set; diff --git a/lib/vrf.h b/lib/vrf.h index 956730bac6ad..f66a9e6b325e 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -167,15 +167,14 @@ extern void *vrf_info_lookup(vrf_id_t); /* * VRF bit-map: maintaining flags, one bit per VRF ID */ - typedef void *vrf_bitmap_t; #define VRF_BITMAP_NULL NULL -extern vrf_bitmap_t vrf_bitmap_init(void); -extern void vrf_bitmap_free(vrf_bitmap_t); -extern void vrf_bitmap_set(vrf_bitmap_t, vrf_id_t); -extern void vrf_bitmap_unset(vrf_bitmap_t, vrf_id_t); -extern int vrf_bitmap_check(vrf_bitmap_t, vrf_id_t); +extern void vrf_bitmap_init(vrf_bitmap_t *pbmap); +extern void vrf_bitmap_free(vrf_bitmap_t *pbmap); +extern void vrf_bitmap_set(vrf_bitmap_t *pbmap, vrf_id_t vrf_id); +extern void vrf_bitmap_unset(vrf_bitmap_t *pbmap, vrf_id_t vrf_id); +extern int vrf_bitmap_check(vrf_bitmap_t *pbmap, vrf_id_t vrf_id); /* * VRF initializer/destructor diff --git a/lib/vty.c b/lib/vty.c index 786271abe89c..c9de00a2713d 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -21,8 +21,9 @@ #endif /* HAVE_LIBPCRE2_POSIX */ #include <stdio.h> +#include "debug.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "command.h" #include "sockunion.h" @@ -65,6 +66,13 @@ enum vty_event { #endif /* VTYSH */ }; +struct nb_config *vty_mgmt_candidate_config; + +static struct mgmt_fe_client *mgmt_fe_client; +static bool mgmt_fe_connected; +static uint64_t mgmt_client_id_next; +static uint64_t mgmt_last_req_id = UINT64_MAX; + PREDECL_DLIST(vtyservs); struct vty_serv { @@ -73,13 +81,14 @@ struct vty_serv { int sock; bool vtysh; - struct thread *t_accept; + struct event *t_accept; }; DECLARE_DLIST(vtyservs, struct vty_serv, itm); static void vty_event_serv(enum vty_event event, struct vty_serv *); static void vty_event(enum vty_event, struct vty *); +static int vtysh_flush(struct vty *vty); /* Extern host structure from command.c */ extern struct host host; @@ -109,8 +118,85 @@ static int no_password_check = 0; /* Integrated configuration file path */ static char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; -static bool do_log_commands; -static bool do_log_commands_perm; +bool vty_log_commands; +static bool vty_log_commands_perm; + +char const *const mgmt_daemons[] = { +#ifdef HAVE_STATICD + "staticd", +#endif +}; +uint mgmt_daemons_count = array_size(mgmt_daemons); + + +static int vty_mgmt_lock_candidate_inline(struct vty *vty) +{ + assert(!vty->mgmt_locked_candidate_ds); + (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, true, true); + return vty->mgmt_locked_candidate_ds ? 0 : -1; +} + +static int vty_mgmt_unlock_candidate_inline(struct vty *vty) +{ + assert(vty->mgmt_locked_candidate_ds); + (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_CANDIDATE, false, true); + return vty->mgmt_locked_candidate_ds ? -1 : 0; +} + +static int vty_mgmt_lock_running_inline(struct vty *vty) +{ + assert(!vty->mgmt_locked_running_ds); + (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, true, true); + return vty->mgmt_locked_running_ds ? 0 : -1; +} + +static int vty_mgmt_unlock_running_inline(struct vty *vty) +{ + assert(vty->mgmt_locked_running_ds); + (void)vty_mgmt_send_lockds_req(vty, MGMTD_DS_RUNNING, false, true); + return vty->mgmt_locked_running_ds ? -1 : 0; +} + +void vty_mgmt_resume_response(struct vty *vty, bool success) +{ + uint8_t header[4] = {0, 0, 0, 0}; + int ret = CMD_SUCCESS; + + if (!vty->mgmt_req_pending_cmd) { + zlog_err( + "vty resume response called without mgmt_req_pending_cmd"); + return; + } + + if (!success) + ret = CMD_WARNING_CONFIG_FAILED; + + MGMTD_FE_CLIENT_DBG( + "resuming CLI cmd after %s on vty session-id: %" PRIu64 + " with '%s'", + vty->mgmt_req_pending_cmd, vty->mgmt_session_id, + success ? "succeeded" : "failed"); + + vty->mgmt_req_pending_cmd = NULL; + + if (vty->type != VTY_FILE) { + header[3] = ret; + buffer_put(vty->obuf, header, 4); + if (!vty->t_write && (vtysh_flush(vty) < 0)) { + zlog_err("failed to vtysh_flush"); + /* Try to flush results; exit if a write error occurs */ + return; + } + } + + if (vty->status == VTY_CLOSE) + vty_close(vty); + else if (vty->type != VTY_FILE) + vty_event(VTYSH_READ, vty); + else + /* should we assert here? */ + zlog_err("mgmtd: unexpected resume while reading config file"); +} void vty_frame(struct vty *vty, const char *format, ...) { @@ -462,7 +548,7 @@ static int vty_command(struct vty *vty, char *buf) /* * Log non empty command lines */ - if (do_log_commands && + if (vty_log_commands && strncmp(buf, "echo PING", strlen("echo PING")) != 0) cp = buf; if (cp != NULL) { @@ -506,7 +592,7 @@ static int vty_command(struct vty *vty, char *buf) GETRUSAGE(&after); - walltime = thread_consumed_time(&after, &before, &cputime); + walltime = event_consumed_time(&after, &before, &cputime); if (cputime_enabled_here && cputime_enabled && cputime_threshold && cputime > cputime_threshold) @@ -1321,13 +1407,13 @@ static void vty_buffer_reset(struct vty *vty) } /* Read data via vty socket. */ -static void vty_read(struct thread *thread) +static void vty_read(struct event *thread) { int i; int nbytes; unsigned char buf[VTY_READ_BUFSIZ]; - struct vty *vty = THREAD_ARG(thread); + struct vty *vty = EVENT_ARG(thread); /* Read raw data from socket */ if ((nbytes = read(vty->fd, buf, VTY_READ_BUFSIZ)) <= 0) { @@ -1524,15 +1610,15 @@ static void vty_read(struct thread *thread) } /* Flush buffer to the vty. */ -static void vty_flush(struct thread *thread) +static void vty_flush(struct event *thread) { int erase; buffer_status_t flushrc; - struct vty *vty = THREAD_ARG(thread); + struct vty *vty = EVENT_ARG(thread); /* Tempolary disable read thread. */ if (vty->lines == 0) - THREAD_OFF(vty->t_read); + EVENT_OFF(vty->t_read); /* Function execution continue. */ erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE)); @@ -1586,6 +1672,18 @@ struct vty *vty_new(void) new->max = VTY_BUFSIZ; new->pass_fd = -1; + if (mgmt_fe_client) { + if (!mgmt_client_id_next) + mgmt_client_id_next++; + new->mgmt_client_id = mgmt_client_id_next++; + new->mgmt_session_id = 0; + mgmt_fe_create_client_session( + mgmt_fe_client, new->mgmt_client_id, (uintptr_t) new); + /* we short-circuit create the session so it must be set now */ + assertf(new->mgmt_session_id != 0, + "Failed to create client session for VTY"); + } + return new; } @@ -1708,9 +1806,9 @@ void vty_stdio_suspend(void) if (!stdio_vty) return; - THREAD_OFF(stdio_vty->t_write); - THREAD_OFF(stdio_vty->t_read); - THREAD_OFF(stdio_vty->t_timeout); + EVENT_OFF(stdio_vty->t_write); + EVENT_OFF(stdio_vty->t_read); + EVENT_OFF(stdio_vty->t_timeout); if (stdio_termios) tcsetattr(0, TCSANOW, &stdio_orig_termios); @@ -1774,9 +1872,9 @@ struct vty *vty_stdio(void (*atclose)(int isexit)) } /* Accept connection from the network. */ -static void vty_accept(struct thread *thread) +static void vty_accept(struct event *thread) { - struct vty_serv *vtyserv = THREAD_ARG(thread); + struct vty_serv *vtyserv = EVENT_ARG(thread); int vty_sock; union sockunion su; int ret; @@ -1987,9 +2085,9 @@ static void vty_serv_un(const char *path) /* #define VTYSH_DEBUG 1 */ -static void vtysh_accept(struct thread *thread) +static void vtysh_accept(struct event *thread) { - struct vty_serv *vtyserv = THREAD_ARG(thread); + struct vty_serv *vtyserv = EVENT_ARG(thread); int accept_sock = vtyserv->sock; int sock; int client_len; @@ -2117,7 +2215,91 @@ void vty_pass_fd(struct vty *vty, int fd) vty->pass_fd = fd; } -static void vtysh_read(struct thread *thread) +bool mgmt_vty_read_configs(void) +{ + char path[PATH_MAX]; + struct vty *vty; + FILE *confp; + uint line_num = 0; + uint count = 0; + uint index; + + vty = vty_new(); + vty->wfd = STDERR_FILENO; + vty->type = VTY_FILE; + vty->node = CONFIG_NODE; + vty->config = true; + vty->pending_allowed = true; + + vty->candidate_config = vty_shared_candidate_config; + + vty_mgmt_lock_candidate_inline(vty); + vty_mgmt_lock_running_inline(vty); + + for (index = 0; index < array_size(mgmt_daemons); index++) { + snprintf(path, sizeof(path), "%s/%s.conf", frr_sysconfdir, + mgmt_daemons[index]); + + confp = vty_open_config(path, config_default); + if (!confp) + continue; + + zlog_info("mgmtd: reading config file: %s", path); + + /* Execute configuration file */ + line_num = 0; + (void)config_from_file(vty, confp, &line_num); + count++; + + fclose(confp); + } + + snprintf(path, sizeof(path), "%s/mgmtd.conf", frr_sysconfdir); + confp = vty_open_config(path, config_default); + if (!confp) { + char *orig; + + snprintf(path, sizeof(path), "%s/zebra.conf", frr_sysconfdir); + orig = XSTRDUP(MTYPE_TMP, host_config_get()); + + zlog_info("mgmtd: trying backup config file: %s", path); + confp = vty_open_config(path, config_default); + + host_config_set(path); + XFREE(MTYPE_TMP, orig); + } + + if (confp) { + zlog_info("mgmtd: reading config file: %s", path); + + line_num = 0; + (void)config_from_file(vty, confp, &line_num); + count++; + + fclose(confp); + } + + /* Conditionally unlock as the config file may have "exit"d early which + * would then have unlocked things. + */ + if (vty->mgmt_locked_running_ds) + vty_mgmt_unlock_running_inline(vty); + if (vty->mgmt_locked_candidate_ds) + vty_mgmt_unlock_candidate_inline(vty); + + vty->pending_allowed = false; + + if (!count) + vty_close(vty); + else + vty_read_file_finish(vty, NULL); + + zlog_info("mgmtd: finished reading config files"); + + return true; +} + +static void vtysh_read(struct event *thread) { int ret; int sock; @@ -2127,8 +2309,21 @@ static void vtysh_read(struct thread *thread) unsigned char *p; uint8_t header[4] = {0, 0, 0, 0}; - sock = THREAD_FD(thread); - vty = THREAD_ARG(thread); + sock = EVENT_FD(thread); + vty = EVENT_ARG(thread); + + /* + * This code looks like it can read multiple commands from the `buf` + * value returned by read(); however, it cannot in some cases. + * + * There are multiple paths out of the "copying to vty->buf" loop, which + * lose any content not yet copied from the stack `buf`, `passfd`, + * `CMD_SUSPEND` and finally if a front-end for mgmtd (generally this + * would be mgmtd itself). So these code paths are counting on vtysh not + * sending us more than 1 command line before waiting on the reply to + * that command. + */ + assert(vty->type == VTY_SHELL_SERV); if ((nbytes = read(sock, buf, VTY_READ_BUFSIZ)) <= 0) { if (nbytes < 0) { @@ -2201,6 +2396,17 @@ static void vtysh_read(struct thread *thread) if (ret == CMD_SUSPEND) break; + /* with new infra we need to stop response till + * we get response through callback. + */ + if (vty->mgmt_req_pending_cmd) { + MGMTD_FE_CLIENT_DBG( + "postpone CLI response pending mgmtd %s on vty session-id %" PRIu64, + vty->mgmt_req_pending_cmd, + vty->mgmt_session_id); + return; + } + /* warning: watchfrr hardcodes this result write */ header[3] = ret; @@ -2220,9 +2426,9 @@ static void vtysh_read(struct thread *thread) vty_event(VTYSH_READ, vty); } -static void vtysh_write(struct thread *thread) +static void vtysh_write(struct event *thread) { - struct vty *vty = THREAD_ARG(thread); + struct vty *vty = EVENT_ARG(thread); vtysh_flush(vty); } @@ -2230,7 +2436,7 @@ static void vtysh_write(struct thread *thread) #endif /* VTYSH */ /* Determine address family to bind. */ -void vty_serv_sock(const char *addr, unsigned short port, const char *path) +void vty_serv_start(const char *addr, unsigned short port, const char *path) { /* If port is set to 0, do not listen on TCP/IP at all! */ if (port) @@ -2241,6 +2447,20 @@ void vty_serv_sock(const char *addr, unsigned short port, const char *path) #endif /* VTYSH */ } +void vty_serv_stop(void) +{ + struct vty_serv *vtyserv; + + while ((vtyserv = vtyservs_pop(vty_servs))) { + EVENT_OFF(vtyserv->t_accept); + close(vtyserv->sock); + XFREE(MTYPE_VTY_SERV, vtyserv); + } + + vtyservs_fini(vty_servs); + vtyservs_init(vty_servs); +} + static void vty_error_delete(void *arg) { struct vty_error *ve = arg; @@ -2257,13 +2477,30 @@ void vty_close(struct vty *vty) int i; bool was_stdio = false; + vty->status = VTY_CLOSE; + + /* + * If we reach here with pending config to commit we will be losing it + * so warn the user. + */ + if (vty->mgmt_num_pending_setcfg) + MGMTD_FE_CLIENT_ERR( + "vty closed, uncommitted config will be lost."); + /* Drop out of configure / transaction if needed. */ vty_config_exit(vty); + if (mgmt_fe_client && vty->mgmt_session_id) { + MGMTD_FE_CLIENT_DBG("closing vty session"); + mgmt_fe_destroy_client_session(mgmt_fe_client, + vty->mgmt_client_id); + vty->mgmt_session_id = 0; + } + /* Cancel threads.*/ - THREAD_OFF(vty->t_read); - THREAD_OFF(vty->t_write); - THREAD_OFF(vty->t_timeout); + EVENT_OFF(vty->t_read); + EVENT_OFF(vty->t_write); + EVENT_OFF(vty->t_timeout); if (vty->pass_fd != -1) { close(vty->pass_fd); @@ -2287,7 +2524,7 @@ void vty_close(struct vty *vty) if (vty->fd != -1) { if (vty->type == VTY_SHELL_SERV) vtys_del(vtysh_sessions, vty); - else + else if (vty->type == VTY_TERM) vtys_del(vty_sessions, vty); } @@ -2306,6 +2543,7 @@ void vty_close(struct vty *vty) if (vty->fd == STDIN_FILENO) was_stdio = true; + XFREE(MTYPE_TMP, vty->pending_cmds_buf); XFREE(MTYPE_VTY, vty->buf); if (vty->error) { @@ -2321,11 +2559,11 @@ void vty_close(struct vty *vty) } /* When time out occur output message then close connection. */ -static void vty_timeout(struct thread *thread) +static void vty_timeout(struct event *thread) { struct vty *vty; - vty = THREAD_ARG(thread); + vty = EVENT_ARG(thread); vty->v_timeout = 0; /* Clear buffer*/ @@ -2339,12 +2577,9 @@ static void vty_timeout(struct thread *thread) } /* Read up configuration file from file_name. */ -static void vty_read_file(struct nb_config *config, FILE *confp) +void vty_read_file(struct nb_config *config, FILE *confp) { - int ret; struct vty *vty; - struct vty_error *ve; - struct listnode *node; unsigned int line_num = 0; vty = vty_new(); @@ -2367,16 +2602,30 @@ static void vty_read_file(struct nb_config *config, FILE *confp) } /* Execute configuration file */ - ret = config_from_file(vty, confp, &line_num); + (void)config_from_file(vty, confp, &line_num); + + vty_read_file_finish(vty, config); +} + +void vty_read_file_finish(struct vty *vty, struct nb_config *config) +{ + struct vty_error *ve; + struct listnode *node; /* Flush any previous errors before printing messages below */ buffer_flush_all(vty->obuf, vty->wfd); - if (!((ret == CMD_SUCCESS) || (ret == CMD_ERR_NOTHING_TODO))) { + for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) { const char *message = NULL; char *nl; - switch (ret) { + switch (ve->cmd_ret) { + case CMD_SUCCESS: + message = "Command succeeded"; + break; + case CMD_ERR_NOTHING_TODO: + message = "Nothing to do"; + break; case CMD_ERR_AMBIGUOUS: message = "Ambiguous command"; break; @@ -2401,13 +2650,11 @@ static void vty_read_file(struct nb_config *config, FILE *confp) break; } - for (ALL_LIST_ELEMENTS_RO(vty->error, node, ve)) { - nl = strchr(ve->error_buf, '\n'); - if (nl) - *nl = '\0'; - flog_err(EC_LIB_VTY, "%s on config line %u: %s", - message, ve->line_num, ve->error_buf); - } + nl = strchr(ve->error_buf, '\n'); + if (nl) + *nl = '\0'; + flog_err(EC_LIB_VTY, "%s on config line %u: %s", message, + ve->line_num, ve->error_buf); } /* @@ -2417,6 +2664,7 @@ static void vty_read_file(struct nb_config *config, FILE *confp) if (config == NULL) { struct nb_context context = {}; char errmsg[BUFSIZ] = {0}; + int ret; context.client = NB_CLIENT_CLI; context.user = vty; @@ -2487,15 +2735,12 @@ static FILE *vty_use_backup_config(const char *fullpath) return ret; } -/* Read up configuration file from file_name. */ -bool vty_read_config(struct nb_config *config, const char *config_file, - char *config_default_dir) +FILE *vty_open_config(const char *config_file, char *config_default_dir) { char cwd[MAXPATHLEN]; FILE *confp = NULL; const char *fullpath; char *tmp = NULL; - bool read_success = false; /* If -f flag specified. */ if (config_file != NULL) { @@ -2558,10 +2803,8 @@ bool vty_read_config(struct nb_config *config, const char *config_file, if (strstr(config_default_dir, "vtysh") == NULL) { ret = stat(integrate_default, &conf_stat); - if (ret >= 0) { - read_success = true; + if (ret >= 0) goto tmp_free_and_out; - } } #endif /* VTYSH */ confp = fopen(config_default_dir, "r"); @@ -2587,51 +2830,62 @@ bool vty_read_config(struct nb_config *config, const char *config_file, fullpath = config_default_dir; } - vty_read_file(config, confp); - read_success = true; - - fclose(confp); - host_config_set(fullpath); tmp_free_and_out: XFREE(MTYPE_TMP, tmp); - return read_success; + return confp; } -static void update_xpath(struct vty *vty, const char *oldpath, - const char *newpath) + +bool vty_read_config(struct nb_config *config, const char *config_file, + char *config_default_dir) { - int i; + FILE *confp; - for (i = 0; i < vty->xpath_index; i++) { - if (!frrstr_startswith(vty->xpath[i], oldpath)) - break; + confp = vty_open_config(config_file, config_default_dir); + if (!confp) + return false; - char *tmp = frrstr_replace(vty->xpath[i], oldpath, newpath); - strlcpy(vty->xpath[i], tmp, sizeof(vty->xpath[0])); - XFREE(MTYPE_TMP, tmp); - } -} + vty_read_file(config, confp); -void vty_update_xpath(const char *oldpath, const char *newpath) -{ - struct vty *vty; + fclose(confp); - frr_each (vtys, vtysh_sessions, vty) - update_xpath(vty, oldpath, newpath); - frr_each (vtys, vty_sessions, vty) - update_xpath(vty, oldpath, newpath); + return true; } -int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) +int vty_config_enter(struct vty *vty, bool private_config, bool exclusive, + bool file_lock) { - if (exclusive && nb_running_lock(NB_CLIENT_CLI, vty)) { + if (exclusive && !vty_mgmt_fe_enabled() && + nb_running_lock(NB_CLIENT_CLI, vty)) { vty_out(vty, "%% Configuration is locked by other client\n"); return CMD_WARNING; } + /* + * We only need to do a lock when reading a config file as we will be + * sending a batch of setcfg changes followed by a single commit + * message. For user interactive mode we are doing implicit commits + * those will obtain the lock (or not) when they try and commit. + */ + if (file_lock && vty_mgmt_fe_enabled() && !private_config) { + if (vty_mgmt_lock_candidate_inline(vty)) { + vty_out(vty, + "%% Can't enter config; candidate datastore locked by another session\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (vty_mgmt_lock_running_inline(vty)) { + vty_out(vty, + "%% Can't enter config; running datastore locked by another session\n"); + vty_mgmt_unlock_candidate_inline(vty); + return CMD_WARNING_CONFIG_FAILED; + } + assert(vty->mgmt_locked_candidate_ds); + assert(vty->mgmt_locked_running_ds); + } + vty->node = CONFIG_NODE; vty->config = true; vty->private_config = private_config; @@ -2642,16 +2896,24 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive) vty->candidate_config_base = nb_config_dup(running_config); vty_out(vty, "Warning: uncommitted changes will be discarded on exit.\n\n"); - } else { - vty->candidate_config = vty_shared_candidate_config; - if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) - vty->candidate_config_base = - nb_config_dup(running_config); + return CMD_SUCCESS; } + /* + * NOTE: On the MGMTD daemon we point the VTY candidate DS to + * the global MGMTD candidate DS. Else we point to the VTY + * Shared Candidate Config. + */ + vty->candidate_config = vty_mgmt_candidate_config + ? vty_mgmt_candidate_config + : vty_shared_candidate_config; + if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) + vty->candidate_config_base = nb_config_dup(running_config); + return CMD_SUCCESS; } + void vty_config_exit(struct vty *vty) { enum node_type node = vty->node; @@ -2676,6 +2938,14 @@ int vty_config_node_exit(struct vty *vty) { vty->xpath_index = 0; + /* TODO: could we check for un-commited changes here? */ + + if (vty->mgmt_locked_running_ds) + vty_mgmt_unlock_running_inline(vty); + + if (vty->mgmt_locked_candidate_ds) + vty_mgmt_unlock_candidate_inline(vty); + /* Perform any pending commits. */ (void)nb_cli_pending_commit_check(vty); @@ -2700,23 +2970,33 @@ int vty_config_node_exit(struct vty *vty) } vty->config = false; + + /* + * If this is a config file and we are dropping out of config end + * parsing. + */ + if (vty->type == VTY_FILE && vty->status != VTY_CLOSE) { + vty_out(vty, "exit from config node while reading config file"); + vty->status = VTY_CLOSE; + } + return 1; } /* Master of the threads. */ -static struct thread_master *vty_master; +static struct event_loop *vty_master; static void vty_event_serv(enum vty_event event, struct vty_serv *vty_serv) { switch (event) { case VTY_SERV: - thread_add_read(vty_master, vty_accept, vty_serv, - vty_serv->sock, &vty_serv->t_accept); + event_add_read(vty_master, vty_accept, vty_serv, vty_serv->sock, + &vty_serv->t_accept); break; #ifdef VTYSH case VTYSH_SERV: - thread_add_read(vty_master, vtysh_accept, vty_serv, - vty_serv->sock, &vty_serv->t_accept); + event_add_read(vty_master, vtysh_accept, vty_serv, + vty_serv->sock, &vty_serv->t_accept); break; #endif /* VTYSH */ case VTY_READ: @@ -2733,34 +3013,34 @@ static void vty_event(enum vty_event event, struct vty *vty) switch (event) { #ifdef VTYSH case VTYSH_READ: - thread_add_read(vty_master, vtysh_read, vty, vty->fd, - &vty->t_read); + event_add_read(vty_master, vtysh_read, vty, vty->fd, + &vty->t_read); break; case VTYSH_WRITE: - thread_add_write(vty_master, vtysh_write, vty, vty->wfd, - &vty->t_write); + event_add_write(vty_master, vtysh_write, vty, vty->wfd, + &vty->t_write); break; #endif /* VTYSH */ case VTY_READ: - thread_add_read(vty_master, vty_read, vty, vty->fd, - &vty->t_read); + event_add_read(vty_master, vty_read, vty, vty->fd, + &vty->t_read); /* Time out treatment. */ if (vty->v_timeout) { - THREAD_OFF(vty->t_timeout); - thread_add_timer(vty_master, vty_timeout, vty, - vty->v_timeout, &vty->t_timeout); + EVENT_OFF(vty->t_timeout); + event_add_timer(vty_master, vty_timeout, vty, + vty->v_timeout, &vty->t_timeout); } break; case VTY_WRITE: - thread_add_write(vty_master, vty_flush, vty, vty->wfd, - &vty->t_write); + event_add_write(vty_master, vty_flush, vty, vty->wfd, + &vty->t_write); break; case VTY_TIMEOUT_RESET: - THREAD_OFF(vty->t_timeout); + EVENT_OFF(vty->t_timeout); if (vty->v_timeout) - thread_add_timer(vty_master, vty_timeout, vty, - vty->v_timeout, &vty->t_timeout); + event_add_timer(vty_master, vty_timeout, vty, + vty->v_timeout, &vty->t_timeout); break; case VTY_SERV: case VTYSH_SERV: @@ -3056,15 +3336,15 @@ DEFPY (log_commands, "Log all commands\n") { if (no) { - if (do_log_commands_perm) { + if (vty_log_commands_perm) { vty_out(vty, "Daemon started with permanent logging turned on for commands, ignoring\n"); return CMD_WARNING; } - do_log_commands = false; + vty_log_commands = false; } else - do_log_commands = true; + vty_log_commands = true; return CMD_SUCCESS; } @@ -3092,7 +3372,7 @@ static int vty_config_write(struct vty *vty) vty_endframe(vty, "exit\n"); - if (do_log_commands) + if (vty_log_commands) vty_out(vty, "log commands\n"); vty_out(vty, "!\n"); @@ -3173,8 +3453,411 @@ void vty_init_vtysh(void) /* currently nothing to do, but likely to have future use */ } + +/* + * These functions allow for CLI handling to be placed inside daemons; however, + * currently they are only used by mgmtd, with mgmtd having each daemons CLI + * functionality linked into it. This design choice was taken for efficiency. + */ + +static void vty_mgmt_server_connected(struct mgmt_fe_client *client, + uintptr_t usr_data, bool connected) +{ + MGMTD_FE_CLIENT_DBG("Got %sconnected %s MGMTD Frontend Server", + !connected ? "dis: " : "", + !connected ? "from" : "to"); + + /* + * We should not have any sessions for connecting or disconnecting case. + * The fe client library will delete all session on disconnect before + * calling us. + */ + assert(mgmt_fe_client_session_count(client) == 0); + + mgmt_fe_connected = connected; + + /* Start or stop listening for vty connections */ + if (connected) + frr_vty_serv_start(); + else + frr_vty_serv_stop(); +} + +/* + * A session has successfully been created for a vty. + */ +static void vty_mgmt_session_notify(struct mgmt_fe_client *client, + uintptr_t usr_data, uint64_t client_id, + bool create, bool success, + uintptr_t session_id, uintptr_t session_ctx) +{ + struct vty *vty; + + vty = (struct vty *)session_ctx; + + if (!success) { + zlog_err("%s session for client %" PRIu64 " failed!", + create ? "Creating" : "Destroying", client_id); + return; + } + + MGMTD_FE_CLIENT_DBG("%s session for client %" PRIu64 " successfully", + create ? "Created" : "Destroyed", client_id); + + if (create) { + assert(session_id != 0); + vty->mgmt_session_id = session_id; + } else { + vty->mgmt_session_id = 0; + /* We may come here by way of vty_close() and short-circuits */ + if (vty->status != VTY_CLOSE) + vty_close(vty); + } +} + +static void vty_mgmt_ds_lock_notified(struct mgmt_fe_client *client, + uintptr_t usr_data, uint64_t client_id, + uintptr_t session_id, + uintptr_t session_ctx, uint64_t req_id, + bool lock_ds, bool success, + Mgmtd__DatastoreId ds_id, + char *errmsg_if_any) +{ + struct vty *vty; + + vty = (struct vty *)session_ctx; + + assert(ds_id == MGMTD_DS_CANDIDATE || ds_id == MGMTD_DS_RUNNING); + if (!success) + zlog_err("%socking for DS %u failed, Err: '%s' vty %p", + lock_ds ? "L" : "Unl", ds_id, errmsg_if_any, vty); + else { + MGMTD_FE_CLIENT_DBG("%socked DS %u successfully", + lock_ds ? "L" : "Unl", ds_id); + if (ds_id == MGMTD_DS_CANDIDATE) + vty->mgmt_locked_candidate_ds = lock_ds; + else + vty->mgmt_locked_running_ds = lock_ds; + } + + if (vty->mgmt_req_pending_cmd) + vty_mgmt_resume_response(vty, success); +} + +static void vty_mgmt_set_config_result_notified( + struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id, + uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id, + bool success, Mgmtd__DatastoreId ds_id, bool implicit_commit, + char *errmsg_if_any) +{ + struct vty *vty; + + vty = (struct vty *)session_ctx; + + if (!success) { + zlog_err("SET_CONFIG request for client 0x%" PRIx64 + " failed, Error: '%s'", + client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); + vty_out(vty, "ERROR: SET_CONFIG request failed, Error: %s\n", + errmsg_if_any ? errmsg_if_any : "Unknown"); + } else { + MGMTD_FE_CLIENT_DBG("SET_CONFIG request for client 0x%" PRIx64 + " req-id %" PRIu64 " was successfull", + client_id, req_id); + } + + if (implicit_commit) { + /* In this case the changes have been applied, we are done */ + vty_mgmt_unlock_candidate_inline(vty); + vty_mgmt_unlock_running_inline(vty); + } + + vty_mgmt_resume_response(vty, success); +} + +static void vty_mgmt_commit_config_result_notified( + struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id, + uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id, + bool success, Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dst_ds_id, bool validate_only, char *errmsg_if_any) +{ + struct vty *vty; + + vty = (struct vty *)session_ctx; + + if (!success) { + zlog_err("COMMIT_CONFIG request for client 0x%" PRIx64 + " failed, Error: '%s'", + client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); + vty_out(vty, "ERROR: COMMIT_CONFIG request failed, Error: %s\n", + errmsg_if_any ? errmsg_if_any : "Unknown"); + } else { + MGMTD_FE_CLIENT_DBG( + "COMMIT_CONFIG request for client 0x%" PRIx64 + " req-id %" PRIu64 " was successfull", + client_id, req_id); + if (errmsg_if_any) + vty_out(vty, "MGMTD: %s\n", errmsg_if_any); + } + + vty_mgmt_resume_response(vty, success); +} + +static int vty_mgmt_get_data_result_notified( + struct mgmt_fe_client *client, uintptr_t usr_data, uint64_t client_id, + uintptr_t session_id, uintptr_t session_ctx, uint64_t req_id, + bool success, Mgmtd__DatastoreId ds_id, Mgmtd__YangData **yang_data, + size_t num_data, int next_key, char *errmsg_if_any) +{ + struct vty *vty; + size_t indx; + + vty = (struct vty *)session_ctx; + + if (!success) { + zlog_err("GET_DATA request for client 0x%" PRIx64 + " failed, Error: '%s'", + client_id, errmsg_if_any ? errmsg_if_any : "Unknown"); + vty_out(vty, "ERROR: GET_DATA request failed, Error: %s\n", + errmsg_if_any ? errmsg_if_any : "Unknown"); + vty_mgmt_resume_response(vty, success); + return -1; + } + + MGMTD_FE_CLIENT_DBG("GET_DATA request succeeded, client 0x%" PRIx64 + " req-id %" PRIu64, + client_id, req_id); + + if (req_id != mgmt_last_req_id) { + mgmt_last_req_id = req_id; + vty_out(vty, "[\n"); + } + + for (indx = 0; indx < num_data; indx++) { + vty_out(vty, " \"%s\": \"%s\"\n", yang_data[indx]->xpath, + yang_data[indx]->value->encoded_str_val); + } + if (next_key < 0) { + vty_out(vty, "]\n"); + vty_mgmt_resume_response(vty, success); + } + + return 0; +} + +static struct mgmt_fe_client_cbs mgmt_cbs = { + .client_connect_notify = vty_mgmt_server_connected, + .client_session_notify = vty_mgmt_session_notify, + .lock_ds_notify = vty_mgmt_ds_lock_notified, + .set_config_notify = vty_mgmt_set_config_result_notified, + .commit_config_notify = vty_mgmt_commit_config_result_notified, + .get_data_notify = vty_mgmt_get_data_result_notified, +}; + +void vty_init_mgmt_fe(void) +{ + char name[40]; + + assert(vty_master); + assert(!mgmt_fe_client); + snprintf(name, sizeof(name), "vty-%s-%ld", frr_get_progname(), + (long)getpid()); + mgmt_fe_client = mgmt_fe_client_create(name, &mgmt_cbs, 0, vty_master); + assert(mgmt_fe_client); +} + +bool vty_mgmt_fe_enabled(void) +{ + return mgmt_fe_client && mgmt_fe_connected; +} + +bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty) +{ + return vty->type != VTY_FILE && vty_mgmt_fe_enabled(); +} + +int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, + bool lock, bool scok) +{ + assert(mgmt_fe_client); + assert(vty->mgmt_session_id); + + vty->mgmt_req_id++; + if (mgmt_fe_send_lockds_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, ds_id, lock, scok)) { + zlog_err("Failed sending %sLOCK-DS-REQ req-id %" PRIu64, + lock ? "" : "UN", vty->mgmt_req_id); + vty_out(vty, "Failed to send %sLOCK-DS-REQ to MGMTD!\n", + lock ? "" : "UN"); + return -1; + } + + if (!scok) + vty->mgmt_req_pending_cmd = "MESSAGE_LOCKDS_REQ"; + + return 0; +} + +int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit) +{ + Mgmtd__YangDataValue value[VTY_MAXCFGCHANGES]; + Mgmtd__YangData cfg_data[VTY_MAXCFGCHANGES]; + Mgmtd__YangCfgDataReq cfg_req[VTY_MAXCFGCHANGES]; + Mgmtd__YangCfgDataReq *cfgreq[VTY_MAXCFGCHANGES] = {0}; + size_t indx; + + if (vty->type == VTY_FILE) { + /* + * if this is a config file read we will not send any of the + * changes until we are done reading the file and have modified + * the local candidate DS. + */ + /* no-one else should be sending data right now */ + assert(!vty->mgmt_num_pending_setcfg); + return 0; + } + + /* If we are FE client and we have a vty then we have a session */ + assert(mgmt_fe_client && vty->mgmt_client_id && vty->mgmt_session_id); + + if (!vty->num_cfg_changes) + return 0; + + /* grab the candidate and running lock prior to sending implicit commit + * command + */ + if (implicit_commit) { + if (vty_mgmt_lock_candidate_inline(vty)) { + vty_out(vty, + "%% command failed, could not lock candidate DS\n"); + return -1; + } else if (vty_mgmt_lock_running_inline(vty)) { + vty_out(vty, + "%% command failed, could not lock running DS\n"); + return -1; + } + } + + for (indx = 0; indx < vty->num_cfg_changes; indx++) { + mgmt_yang_data_init(&cfg_data[indx]); + + if (vty->cfg_changes[indx].value) { + mgmt_yang_data_value_init(&value[indx]); + value[indx].encoded_str_val = + (char *)vty->cfg_changes[indx].value; + value[indx].value_case = + MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; + cfg_data[indx].value = &value[indx]; + } + + cfg_data[indx].xpath = vty->cfg_changes[indx].xpath; + + mgmt_yang_cfg_data_req_init(&cfg_req[indx]); + cfg_req[indx].data = &cfg_data[indx]; + switch (vty->cfg_changes[indx].operation) { + case NB_OP_DESTROY: + cfg_req[indx].req_type = + MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; + break; + + case NB_OP_CREATE: + case NB_OP_MODIFY: + case NB_OP_MOVE: + case NB_OP_PRE_VALIDATE: + case NB_OP_APPLY_FINISH: + cfg_req[indx].req_type = + MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; + break; + case NB_OP_GET_ELEM: + case NB_OP_GET_NEXT: + case NB_OP_GET_KEYS: + case NB_OP_LOOKUP_ENTRY: + case NB_OP_RPC: + default: + assertf(false, + "Invalid operation type for send config: %d", + vty->cfg_changes[indx].operation); + /*NOTREACHED*/ + abort(); + } + + cfgreq[indx] = &cfg_req[indx]; + } + if (!indx) + return 0; + + vty->mgmt_req_id++; + if (mgmt_fe_send_setcfg_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, MGMTD_DS_CANDIDATE, + cfgreq, indx, implicit_commit, + MGMTD_DS_RUNNING)) { + zlog_err("Failed to send %zu config xpaths to mgmtd", indx); + vty_out(vty, "%% Failed to send commands to mgmtd\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_SETCFG_REQ"; + + return 0; +} + +int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, bool abort) +{ + if (mgmt_fe_client && vty->mgmt_session_id) { + vty->mgmt_req_id++; + if (mgmt_fe_send_commitcfg_req( + mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, MGMTD_DS_CANDIDATE, + MGMTD_DS_RUNNING, validate_only, abort)) { + zlog_err("Failed sending COMMIT-REQ req-id %" PRIu64, + vty->mgmt_req_id); + vty_out(vty, "Failed to send COMMIT-REQ to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_COMMCFG_REQ"; + vty->mgmt_num_pending_setcfg = 0; + } + + return 0; +} + +int vty_mgmt_send_get_req(struct vty *vty, bool is_config, + Mgmtd__DatastoreId datastore, const char **xpath_list, + int num_req) +{ + Mgmtd__YangData yang_data[VTY_MAXCFGCHANGES]; + Mgmtd__YangGetDataReq get_req[VTY_MAXCFGCHANGES]; + Mgmtd__YangGetDataReq *getreq[VTY_MAXCFGCHANGES]; + int i; + + vty->mgmt_req_id++; + + for (i = 0; i < num_req; i++) { + mgmt_yang_get_data_req_init(&get_req[i]); + mgmt_yang_data_init(&yang_data[i]); + + yang_data->xpath = (char *)xpath_list[i]; + + get_req[i].data = &yang_data[i]; + getreq[i] = &get_req[i]; + } + if (mgmt_fe_send_get_req(mgmt_fe_client, vty->mgmt_session_id, + vty->mgmt_req_id, is_config, datastore, getreq, + num_req)) { + zlog_err("Failed to send GET- to MGMTD for req-id %" PRIu64 ".", + vty->mgmt_req_id); + vty_out(vty, "Failed to send GET-CONFIG to MGMTD!\n"); + return -1; + } + + vty->mgmt_req_pending_cmd = "MESSAGE_GETCFG_REQ"; + + return 0; +} + /* Install vty's own commands like `who' command. */ -void vty_init(struct thread_master *master_thread, bool do_command_logging) +void vty_init(struct event_loop *master_thread, bool do_command_logging) { /* For further configuration read, preserve current directory. */ vty_save_cwd(); @@ -3195,8 +3878,8 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) install_element(CONFIG_NODE, &log_commands_cmd); if (do_command_logging) { - do_log_commands = true; - do_log_commands_perm = true; + vty_log_commands = true; + vty_log_commands_perm = true; } install_element(ENABLE_NODE, &terminal_monitor_cmd); @@ -3218,7 +3901,11 @@ void vty_init(struct thread_master *master_thread, bool do_command_logging) void vty_terminate(void) { struct vty *vty; - struct vty_serv *vtyserv; + + if (mgmt_fe_client) { + mgmt_fe_client_destroy(mgmt_fe_client); + mgmt_fe_client = 0; + } memset(vty_cwd, 0x00, sizeof(vty_cwd)); @@ -3239,12 +3926,5 @@ void vty_terminate(void) vtys_fini(vtysh_sessions); vtys_init(vtysh_sessions); - while ((vtyserv = vtyservs_pop(vty_servs))) { - THREAD_OFF(vtyserv->t_accept); - close(vtyserv->sock); - XFREE(MTYPE_VTY_SERV, vtyserv); - } - - vtyservs_fini(vty_servs); - vtyservs_init(vty_servs); + vty_serv_stop(); } diff --git a/lib/vty.h b/lib/vty.h index 3cab9590f1f4..ac3d2e501959 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -18,13 +18,15 @@ #include <regex.h> #endif /* HAVE_LIBPCRE2_POSIX */ -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "sockunion.h" #include "qobj.h" #include "compiler.h" #include "northbound.h" #include "zlog_live.h" +#include "libfrr.h" +#include "mgmt_fe_client.h" #ifdef __cplusplus extern "C" { @@ -41,6 +43,7 @@ struct json_object; struct vty_error { char error_buf[VTY_BUFSIZ]; uint32_t line_num; + int cmd_ret; }; struct vty_cfg_change { @@ -69,7 +72,11 @@ struct vty { bool is_paged; /* Is this vty connect to file or not */ - enum { VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV } type; + enum { VTY_TERM, /* telnet conn or stdin/stdout UI */ + VTY_FILE, /* reading and writing config files */ + VTY_SHELL, /* vtysh client side UI */ + VTY_SHELL_SERV, /* server-side vtysh connection */ + } type; /* Node status of this vty */ int node; @@ -113,12 +120,18 @@ struct vty { /* Changes enqueued to be applied in the candidate configuration. */ size_t num_cfg_changes; - struct vty_cfg_change cfg_changes[VTY_MAXCFGCHANGES]; + struct nb_cfg_change cfg_changes[VTY_MAXCFGCHANGES]; /* XPath of the current node */ int xpath_index; char xpath[VTY_MAXDEPTH][XPATH_MAXLEN]; + /* + * Keep track of how many SET_CFG requests has been sent so far that + * has not been committed yet. + */ + size_t mgmt_num_pending_setcfg; + /* In configure mode. */ bool config; @@ -139,7 +152,7 @@ struct vty { size_t pending_cmds_bufpos; /* Confirmed-commit timeout and rollback configuration. */ - struct thread *t_confirmed_commit_timeout; + struct event *t_confirmed_commit_timeout; struct nb_config *confirmed_commit_rollback; /* qobj object ID (replacement for "index") */ @@ -193,12 +206,12 @@ struct vty { int lines; /* Read and write thread. */ - struct thread *t_read; - struct thread *t_write; + struct event *t_read; + struct event *t_write; /* Timeout seconds and thread. */ unsigned long v_timeout; - struct thread *t_timeout; + struct event *t_timeout; /* What address is this vty comming from. */ char address[SU_ADDRSTRLEN]; @@ -208,6 +221,16 @@ struct vty { * without any output. */ size_t frame_pos; char frame[1024]; + + uint64_t mgmt_session_id; /* FE adapter identifies session w/ this */ + uint64_t mgmt_client_id; /* FE vty client identifies w/ this ID */ + uint64_t mgmt_req_id; + /* set when we have sent mgmtd a *REQ command in response to some vty + * CLI command and we are waiting on the reply so we can respond to the + * vty user. */ + const char *mgmt_req_pending_cmd; + bool mgmt_locked_candidate_ds; + bool mgmt_locked_running_ds; }; static inline void vty_push_context(struct vty *vty, int node, uint64_t id) @@ -283,10 +306,10 @@ static inline void vty_push_context(struct vty *vty, int node, uint64_t id) #define VTY_CHECK_XPATH \ do { \ - if (vty->type != VTY_FILE && !vty->private_config \ - && vty->xpath_index > 0 \ - && !yang_dnode_exists(vty->candidate_config->dnode, \ - VTY_CURR_XPATH)) { \ + 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, \ "Current configuration object was deleted " \ "by another process.\n\n"); \ @@ -319,8 +342,14 @@ struct vty_arg { #define IS_DIRECTORY_SEP(c) ((c) == DIRECTORY_SEP) #endif +extern struct nb_config *vty_mgmt_candidate_config; +extern bool vty_log_commands; + +extern char const *const mgmt_daemons[]; +extern uint mgmt_daemons_count; + /* Prototypes. */ -extern void vty_init(struct thread_master *, bool do_command_logging); +extern void vty_init(struct event_loop *m, bool do_command_logging); extern void vty_init_vtysh(void); extern void vty_terminate(void); extern void vty_reset(void); @@ -350,15 +379,19 @@ extern void vty_json_empty(struct vty *vty); */ extern void vty_pass_fd(struct vty *vty, int fd); +extern FILE *vty_open_config(const char *config_file, char *config_default_dir); extern bool vty_read_config(struct nb_config *config, const char *config_file, char *config_default_dir); +extern void vty_read_file(struct nb_config *config, FILE *confp); +extern void vty_read_file_finish(struct vty *vty, struct nb_config *config); extern void vty_time_print(struct vty *, int); -extern void vty_serv_sock(const char *, unsigned short, const char *); +extern void vty_serv_start(const char *, unsigned short, const char *); +extern void vty_serv_stop(void); extern void vty_close(struct vty *); extern char *vty_get_cwd(void); extern void vty_update_xpath(const char *oldpath, const char *newpath); extern int vty_config_enter(struct vty *vty, bool private_config, - bool exclusive); + bool exclusive, bool file_lock); extern void vty_config_exit(struct vty *); extern int vty_config_node_exit(struct vty *); extern int vty_shell(struct vty *); @@ -370,6 +403,26 @@ extern void vty_stdio_suspend(void); extern void vty_stdio_resume(void); extern void vty_stdio_close(void); +extern void vty_init_mgmt_fe(void); +extern bool vty_mgmt_fe_enabled(void); +extern bool vty_mgmt_should_process_cli_apply_changes(struct vty *vty); + +extern bool mgmt_vty_read_configs(void); +extern int vty_mgmt_send_config_data(struct vty *vty, bool implicit_commit); +extern int vty_mgmt_send_commit_config(struct vty *vty, bool validate_only, + bool abort); +extern int vty_mgmt_send_get_req(struct vty *vty, bool is_config, + Mgmtd__DatastoreId datastore, + const char **xpath_list, int num_req); +extern int vty_mgmt_send_lockds_req(struct vty *vty, Mgmtd__DatastoreId ds_id, + bool lock, bool scok); +extern void vty_mgmt_resume_response(struct vty *vty, bool success); + +static inline bool vty_needs_implicit_commit(struct vty *vty) +{ + return frr_get_cli_mode() == FRR_CLI_CLASSIC && !vty->pending_allowed; +} + #ifdef __cplusplus } #endif diff --git a/lib/wheel.c b/lib/wheel.c index 4aca23481bee..e17995c64a3e 100644 --- a/lib/wheel.c +++ b/lib/wheel.c @@ -6,7 +6,7 @@ */ #include "zebra.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "wheel.h" #include "log.h" @@ -16,9 +16,9 @@ DEFINE_MTYPE_STATIC(LIB, TIMER_WHEEL_LIST, "Timer Wheel Slot List"); static int debug_timer_wheel = 0; -static void wheel_timer_thread(struct thread *t); +static void wheel_timer_thread(struct event *t); -static void wheel_timer_thread_helper(struct thread *t) +static void wheel_timer_thread_helper(struct event *t) { struct listnode *node, *nextnode; unsigned long long curr_slot; @@ -26,7 +26,7 @@ static void wheel_timer_thread_helper(struct thread *t) struct timer_wheel *wheel; void *data; - wheel = THREAD_ARG(t); + wheel = EVENT_ARG(t); wheel->curr_slot += wheel->slots_to_skip; @@ -47,23 +47,23 @@ static void wheel_timer_thread_helper(struct thread *t) slots_to_skip++; wheel->slots_to_skip = slots_to_skip; - thread_add_timer_msec(wheel->master, wheel_timer_thread, wheel, - wheel->nexttime * slots_to_skip, &wheel->timer); + event_add_timer_msec(wheel->master, wheel_timer_thread, wheel, + wheel->nexttime * slots_to_skip, &wheel->timer); } -static void wheel_timer_thread(struct thread *t) +static void wheel_timer_thread(struct event *t) { struct timer_wheel *wheel; - wheel = THREAD_ARG(t); + wheel = EVENT_ARG(t); - thread_execute(wheel->master, wheel_timer_thread_helper, wheel, 0); + event_execute(wheel->master, wheel_timer_thread_helper, wheel, 0); } -struct timer_wheel *wheel_init(struct thread_master *master, int period, - size_t slots, unsigned int (*slot_key)(const void *), - void (*slot_run)(void *), - const char *run_name) +struct timer_wheel *wheel_init(struct event_loop *master, int period, + size_t slots, + unsigned int (*slot_key)(const void *), + void (*slot_run)(void *), const char *run_name) { struct timer_wheel *wheel; size_t i; @@ -85,8 +85,8 @@ struct timer_wheel *wheel_init(struct thread_master *master, int period, for (i = 0; i < slots; i++) wheel->wheel_slot_lists[i] = list_new(); - thread_add_timer_msec(wheel->master, wheel_timer_thread, wheel, - wheel->nexttime, &wheel->timer); + event_add_timer_msec(wheel->master, wheel_timer_thread, wheel, + wheel->nexttime, &wheel->timer); return wheel; } @@ -99,7 +99,7 @@ void wheel_delete(struct timer_wheel *wheel) list_delete(&wheel->wheel_slot_lists[i]); } - THREAD_OFF(wheel->timer); + EVENT_OFF(wheel->timer); XFREE(MTYPE_TIMER_WHEEL_LIST, wheel->wheel_slot_lists); XFREE(MTYPE_TIMER_WHEEL, wheel->name); XFREE(MTYPE_TIMER_WHEEL, wheel); diff --git a/lib/wheel.h b/lib/wheel.h index 9aa808cdfdb7..0d9ac1002072 100644 --- a/lib/wheel.h +++ b/lib/wheel.h @@ -13,7 +13,7 @@ extern "C" { struct timer_wheel { char *name; - struct thread_master *master; + struct event_loop *master; int slots; long long curr_slot; unsigned int period; @@ -21,7 +21,7 @@ struct timer_wheel { unsigned int slots_to_skip; struct list **wheel_slot_lists; - struct thread *timer; + struct event *timer; /* * Key to determine what slot the item belongs in */ @@ -66,7 +66,7 @@ struct timer_wheel { * and cause significant amount of time handling thread events instead * of running your code. */ -struct timer_wheel *wheel_init(struct thread_master *master, int period, +struct timer_wheel *wheel_init(struct event_loop *master, int period, size_t slots, unsigned int (*slot_key)(const void *), void (*slot_run)(void *), const char *run_name); diff --git a/lib/workqueue.c b/lib/workqueue.c index 5477aadd6525..fa5d585360cc 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -6,7 +6,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "workqueue.h" #include "linklist.h" @@ -59,8 +59,7 @@ static void work_queue_item_remove(struct work_queue *wq, } /* create new work queue */ -struct work_queue *work_queue_new(struct thread_master *m, - const char *queue_name) +struct work_queue *work_queue_new(struct event_loop *m, const char *queue_name) { struct work_queue *new; @@ -78,7 +77,7 @@ struct work_queue *work_queue_new(struct thread_master *m, /* Default values, can be overridden by caller */ new->spec.hold = WORK_QUEUE_DEFAULT_HOLD; - new->spec.yield = THREAD_YIELD_TIME_SLOT; + new->spec.yield = EVENT_YIELD_TIME_SLOT; new->spec.retry = WORK_QUEUE_DEFAULT_RETRY; return new; @@ -88,7 +87,7 @@ void work_queue_free_and_null(struct work_queue **wqp) { struct work_queue *wq = *wqp; - THREAD_OFF(wq->thread); + EVENT_OFF(wq->thread); while (!work_queue_empty(wq)) { struct work_queue_item *item = work_queue_last_item(wq); @@ -106,29 +105,29 @@ void work_queue_free_and_null(struct work_queue **wqp) bool work_queue_is_scheduled(struct work_queue *wq) { - return thread_is_scheduled(wq->thread); + return event_is_scheduled(wq->thread); } static int work_queue_schedule(struct work_queue *wq, unsigned int delay) { /* if appropriate, schedule work queue thread */ if (CHECK_FLAG(wq->flags, WQ_UNPLUGGED) && - !thread_is_scheduled(wq->thread) && !work_queue_empty(wq)) { + !event_is_scheduled(wq->thread) && !work_queue_empty(wq)) { /* Schedule timer if there's a delay, otherwise just schedule * as an 'event' */ if (delay > 0) { - thread_add_timer_msec(wq->master, work_queue_run, wq, - delay, &wq->thread); - thread_ignore_late_timer(wq->thread); + event_add_timer_msec(wq->master, work_queue_run, wq, + delay, &wq->thread); + event_ignore_late_timer(wq->thread); } else - thread_add_event(wq->master, work_queue_run, wq, 0, - &wq->thread); + event_add_event(wq->master, work_queue_run, wq, 0, + &wq->thread); /* set thread yield time, if needed */ - if (thread_is_scheduled(wq->thread) && - wq->spec.yield != THREAD_YIELD_TIME_SLOT) - thread_set_yield_time(wq->thread, wq->spec.yield); + if (event_is_scheduled(wq->thread) && + wq->spec.yield != EVENT_YIELD_TIME_SLOT) + event_set_yield_time(wq->thread, wq->spec.yield); return 1; } else return 0; @@ -198,7 +197,7 @@ void workqueue_cmd_init(void) */ void work_queue_plug(struct work_queue *wq) { - THREAD_OFF(wq->thread); + EVENT_OFF(wq->thread); UNSET_FLAG(wq->flags, WQ_UNPLUGGED); } @@ -218,7 +217,7 @@ void work_queue_unplug(struct work_queue *wq) * will reschedule itself if required, * otherwise work_queue_item_add */ -void work_queue_run(struct thread *thread) +void work_queue_run(struct event *thread) { struct work_queue *wq; struct work_queue_item *item, *titem; @@ -226,7 +225,7 @@ void work_queue_run(struct thread *thread) unsigned int cycles = 0; char yielded = 0; - wq = THREAD_ARG(thread); + wq = EVENT_ARG(thread); assert(wq); @@ -311,8 +310,8 @@ void work_queue_run(struct thread *thread) cycles++; /* test if we should yield */ - if (!(cycles % wq->cycles.granularity) - && thread_should_yield(thread)) { + if (!(cycles % wq->cycles.granularity) && + event_should_yield(thread)) { yielded = 1; goto stats; } diff --git a/lib/workqueue.h b/lib/workqueue.h index c7ed14b0563e..5d84739d5c6b 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -47,8 +47,8 @@ struct work_queue { /* Everything but the specification struct is private * the following may be read */ - struct thread_master *master; /* thread master */ - struct thread *thread; /* thread, if one is active */ + struct event_loop *master; /* thread master */ + struct event *thread; /* thread, if one is active */ char *name; /* work queue name */ /* Specification for this work queue. @@ -137,7 +137,7 @@ static inline void work_queue_item_dequeue(struct work_queue *wq, * user must fill in the spec of the returned work queue before adding * anything to it */ -extern struct work_queue *work_queue_new(struct thread_master *m, +extern struct work_queue *work_queue_new(struct event_loop *m, const char *queue_name); /* destroy work queue */ @@ -158,7 +158,7 @@ extern void work_queue_unplug(struct work_queue *wq); bool work_queue_is_scheduled(struct work_queue *wq); /* Helpers, exported for thread.c and command.c */ -extern void work_queue_run(struct thread *thread); +extern void work_queue_run(struct event *thread); extern void workqueue_cmd_init(void); diff --git a/lib/xref.h b/lib/xref.h index b49c9eb2f247..f06d65b422e3 100644 --- a/lib/xref.h +++ b/lib/xref.h @@ -20,7 +20,7 @@ extern "C" { enum xref_type { XREFT_NONE = 0, - XREFT_THREADSCHED = 0x100, + XREFT_EVENTSCHED = 0x100, XREFT_LOGMSG = 0x200, XREFT_ASSERT = 0x280, diff --git a/lib/yang.c b/lib/yang.c index 78738f7d4d5d..4dd865421718 100644 --- a/lib/yang.c +++ b/lib/yang.c @@ -250,6 +250,23 @@ void yang_snode_get_path(const struct lysc_node *snode, } } +struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath, + uint32_t options) +{ + struct lysc_node *snode; + struct ly_set *set; + LY_ERR err; + + err = lys_find_xpath(ly_native_ctx, NULL, xpath, options, &set); + if (err || !set->count) + return NULL; + + snode = set->snodes[0]; + ly_set_free(set, NULL); + + return snode; +} + struct lysc_node *yang_snode_real_parent(const struct lysc_node *snode) { struct lysc_node *parent = snode->parent; @@ -395,7 +412,12 @@ struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath) xpath += 2; if (lyd_find_xpath(dnode, xpath, &set)) { - assert(0); /* XXX replicates old libyang1 base code */ + /* + * Commenting out the below assert failure as it crashes mgmtd + * when bad xpath is passed. + * + * assert(0); XXX replicates old libyang1 base code + */ goto exit; } if (set->count == 0) diff --git a/lib/yang.h b/lib/yang.h index 91cd641ce84a..37369c09bf81 100644 --- a/lib/yang.h +++ b/lib/yang.h @@ -50,7 +50,7 @@ struct yang_module { #endif #ifdef HAVE_SYSREPO sr_subscription_ctx_t *sr_subscription; - struct thread *sr_thread; + struct event *sr_thread; #endif }; RB_HEAD(yang_modules, yang_module); @@ -210,6 +210,27 @@ extern void yang_snode_get_path(const struct lysc_node *snode, enum yang_path_type type, char *xpath, size_t xpath_len); + +/* + * Find libyang schema node for the given xpath. Uses `lys_find_xpath`, + * returning only the first of a set of nodes -- normally there should only + * be one. + * + * ly_ctx + * libyang context to operate on. + * + * xpath + * XPath expression (absolute or relative) to find the schema node for. + * + * options + * Libyang findxpathoptions value (see lys_find_xpath). + * + * Returns: + * The libyang schema node if found, or NULL if not found. + */ +extern struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, + const char *xpath, uint32_t options); + /* * Find first parent schema node which is a presence-container or a list * (non-presence containers are ignored). diff --git a/lib/yang_translator.c b/lib/yang_translator.c index de668230abe6..eae7577a0d1c 100644 --- a/lib/yang_translator.c +++ b/lib/yang_translator.c @@ -235,8 +235,8 @@ struct yang_translator *yang_translator_load(const char *path) xpath_custom = yang_dnode_get_string(set->dnodes[i], "./custom"); - snode_custom = lys_find_path(translator->ly_ctx, NULL, - xpath_custom, 0); + snode_custom = + yang_find_snode(translator->ly_ctx, xpath_custom, 0); if (!snode_custom) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, "%s: unknown data path: %s", __func__, @@ -247,8 +247,7 @@ struct yang_translator *yang_translator_load(const char *path) xpath_native = yang_dnode_get_string(set->dnodes[i], "./native"); - snode_native = - lys_find_path(ly_native_ctx, NULL, xpath_native, 0); + snode_native = yang_find_snode(ly_native_ctx, xpath_native, 0); if (!snode_native) { flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD, "%s: unknown data path: %s", __func__, @@ -315,7 +314,7 @@ yang_translate_xpath(const struct yang_translator *translator, int dir, else ly_ctx = ly_native_ctx; - snode = lys_find_path(ly_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_ctx, xpath, 0); if (!snode) { flog_warn(EC_LIB_YANG_TRANSLATION_ERROR, "%s: unknown data path: %s", __func__, xpath); diff --git a/lib/yang_wrappers.c b/lib/yang_wrappers.c index 509c4dd8560c..dc049a374a9e 100644 --- a/lib/yang_wrappers.c +++ b/lib/yang_wrappers.c @@ -89,7 +89,7 @@ static const char *yang_get_default_value(const char *xpath) const struct lysc_node *snode; const char *value; - snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_native_ctx, xpath, 0); if (snode == NULL) { flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); @@ -206,7 +206,7 @@ int yang_str2enum(const char *xpath, const char *value) const struct lysc_type_enum *type; const struct lysc_type_bitenum_item *enums; - snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_native_ctx, xpath, 0); if (snode == NULL) { flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); @@ -241,7 +241,7 @@ struct yang_data *yang_data_new_enum(const char *xpath, int value) const struct lysc_type_enum *type; const struct lysc_type_bitenum_item *enums; - snode = lys_find_path(ly_native_ctx, NULL, xpath, 0); + snode = yang_find_snode(ly_native_ctx, xpath, 0); if (snode == NULL) { flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH, "%s: unknown data path: %s", __func__, xpath); diff --git a/lib/zclient.c b/lib/zclient.c index 0e49d655280d..929a18a95372 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -14,7 +14,7 @@ #include "vrf_int.h" #include "if.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "zclient.h" #include "memory.h" #include "table.h" @@ -51,7 +51,7 @@ socklen_t zclient_addr_len; static int zclient_debug; /* Allocate zclient structure. */ -struct zclient *zclient_new(struct thread_master *master, +struct zclient *zclient_new(struct event_loop *master, struct zclient_options *opt, zclient_handler *const *handlers, size_t n_handlers) { @@ -160,9 +160,9 @@ void zclient_stop(struct zclient *zclient) zlog_debug("zclient %p stopped", zclient); /* Stop threads. */ - THREAD_OFF(zclient->t_read); - THREAD_OFF(zclient->t_connect); - THREAD_OFF(zclient->t_write); + EVENT_OFF(zclient->t_read); + EVENT_OFF(zclient->t_connect); + EVENT_OFF(zclient->t_write); /* Reset streams. */ stream_reset(zclient->ibuf); @@ -180,14 +180,14 @@ void zclient_stop(struct zclient *zclient) for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - vrf_bitmap_free(zclient->redist[afi][i]); + vrf_bitmap_free(&zclient->redist[afi][i]); zclient->redist[afi][i] = VRF_BITMAP_NULL; } redist_del_instance( &zclient->mi_redist[afi][zclient->redist_default], zclient->instance); - vrf_bitmap_free(zclient->default_information[afi]); + vrf_bitmap_free(&zclient->default_information[afi]); zclient->default_information[afi] = VRF_BITMAP_NULL; } } @@ -249,9 +249,9 @@ static enum zclient_send_status zclient_failed(struct zclient *zclient) return ZCLIENT_SEND_FAILURE; } -static void zclient_flush_data(struct thread *thread) +static void zclient_flush_data(struct event *thread) { - struct zclient *zclient = THREAD_ARG(thread); + struct zclient *zclient = EVENT_ARG(thread); zclient->t_write = NULL; if (zclient->sock < 0) @@ -266,8 +266,8 @@ static void zclient_flush_data(struct thread *thread) return; case BUFFER_PENDING: zclient->t_write = NULL; - thread_add_write(zclient->master, zclient_flush_data, zclient, - zclient->sock, &zclient->t_write); + event_add_write(zclient->master, zclient_flush_data, zclient, + zclient->sock, &zclient->t_write); break; case BUFFER_EMPTY: if (zclient->zebra_buffer_write_ready) @@ -295,11 +295,11 @@ enum zclient_send_status zclient_send_message(struct zclient *zclient) __func__, zclient->sock); return zclient_failed(zclient); case BUFFER_EMPTY: - THREAD_OFF(zclient->t_write); + EVENT_OFF(zclient->t_write); return ZCLIENT_SEND_SUCCESS; case BUFFER_PENDING: - thread_add_write(zclient->master, zclient_flush_data, zclient, - zclient->sock, &zclient->t_write); + event_add_write(zclient->master, zclient_flush_data, zclient, + zclient->sock, &zclient->t_write); return ZCLIENT_SEND_BUFFERED; } @@ -427,13 +427,18 @@ enum zclient_send_status zclient_send_vrf_label(struct zclient *zclient, } enum zclient_send_status zclient_send_localsid(struct zclient *zclient, - const struct in6_addr *sid, ifindex_t oif, + const struct in6_addr *sid, vrf_id_t vrf_id, enum seg6local_action_t action, const struct seg6local_context *context) { struct prefix_ipv6 p = {}; struct zapi_route api = {}; struct zapi_nexthop *znh; + struct interface *ifp; + + ifp = if_get_vrf_loopback(vrf_id); + if (ifp == NULL) + return ZCLIENT_SEND_FAILURE; p.family = AF_INET6; p.prefixlen = IPV6_MAX_BITLEN; @@ -456,7 +461,7 @@ enum zclient_send_status zclient_send_localsid(struct zclient *zclient, memset(znh, 0, sizeof(*znh)); znh->type = NEXTHOP_TYPE_IFINDEX; - znh->ifindex = oif; + znh->ifindex = ifp->ifindex; SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6LOCAL); znh->seg6local_action = action; memcpy(&znh->seg6local_ctx, context, sizeof(struct seg6local_context)); @@ -489,7 +494,7 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) /* Set unwanted redistribute route. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) - vrf_bitmap_set(zclient->redist[afi][zclient->redist_default], + vrf_bitmap_set(&zclient->redist[afi][zclient->redist_default], vrf_id); /* Flush all redistribute request. */ @@ -519,15 +524,15 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) /* Resend all redistribute request. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default - && vrf_bitmap_check(zclient->redist[afi][i], - vrf_id)) + if (i != zclient->redist_default && + vrf_bitmap_check(&zclient->redist[afi][i], vrf_id)) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, afi, i, 0, vrf_id); /* If default information is needed. */ - if (vrf_bitmap_check(zclient->default_information[afi], vrf_id)) + if (vrf_bitmap_check(&zclient->default_information[afi], + vrf_id)) zebra_redistribute_default_send( ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, afi, vrf_id); @@ -556,7 +561,7 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) /* Set unwanted redistribute route. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) - vrf_bitmap_unset(zclient->redist[afi][zclient->redist_default], + vrf_bitmap_unset(&zclient->redist[afi][zclient->redist_default], vrf_id); /* Flush all redistribute request. */ @@ -586,15 +591,15 @@ void zclient_send_dereg_requests(struct zclient *zclient, vrf_id_t vrf_id) /* Flush all redistribute request. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default - && vrf_bitmap_check(zclient->redist[afi][i], - vrf_id)) + if (i != zclient->redist_default && + vrf_bitmap_check(&zclient->redist[afi][i], vrf_id)) zebra_redistribute_send( ZEBRA_REDISTRIBUTE_DELETE, zclient, afi, i, 0, vrf_id); /* If default information is needed. */ - if (vrf_bitmap_check(zclient->default_information[afi], vrf_id)) + if (vrf_bitmap_check(&zclient->default_information[afi], + vrf_id)) zebra_redistribute_default_send( ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, afi, vrf_id); @@ -721,7 +726,7 @@ void zclient_init(struct zclient *zclient, int redist_default, /* Clear redistribution flags. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - zclient->redist[afi][i] = vrf_bitmap_init(); + vrf_bitmap_init(&zclient->redist[afi][i]); /* Set unwanted redistribute route. bgpd does not need BGP route redistribution. */ @@ -733,7 +738,7 @@ void zclient_init(struct zclient *zclient, int redist_default, instance); /* Set default-information redistribute to zero. */ - zclient->default_information[afi] = vrf_bitmap_init(); + vrf_bitmap_init(&zclient->default_information[afi]); } if (zclient_debug) @@ -744,11 +749,11 @@ void zclient_init(struct zclient *zclient, int redist_default, /* This function is a wrapper function for calling zclient_start from timer or event thread. */ -static void zclient_connect(struct thread *t) +static void zclient_connect(struct event *t) { struct zclient *zclient; - zclient = THREAD_ARG(t); + zclient = EVENT_ARG(t); zclient->t_connect = NULL; if (zclient_debug) @@ -3818,6 +3823,53 @@ enum zclient_send_status zclient_send_mlag_data(struct zclient *client, return zclient_send_message(client); } +/* + * Init/header setup for opaque zapi messages + */ +enum zclient_send_status zapi_opaque_init(struct zclient *zclient, + uint32_t type, uint16_t flags) +{ + struct stream *s; + + 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); + + /* Source daemon identifiers */ + stream_putc(s, zclient->redist_default); + stream_putw(s, zclient->instance); + stream_putl(s, zclient->session_id); + + return ZCLIENT_SEND_SUCCESS; +} + +/* + * Init, header setup for opaque unicast messages. + */ +enum zclient_send_status +zapi_opaque_unicast_init(struct zclient *zclient, uint32_t type, uint16_t flags, + uint8_t proto, uint16_t instance, uint32_t session_id) +{ + struct stream *s; + + s = zclient->obuf; + + /* Common init */ + zapi_opaque_init(zclient, type, flags | ZAPI_OPAQUE_FLAG_UNICAST); + + /* Send destination client info */ + stream_putc(s, proto); + stream_putw(s, instance); + stream_putl(s, session_id); + + return ZCLIENT_SEND_SUCCESS; +} + /* * Send an OPAQUE message, contents opaque to zebra. The message header * is a message subtype. @@ -3835,16 +3887,12 @@ enum zclient_send_status zclient_send_opaque(struct zclient *zclient, return ZCLIENT_SEND_FAILURE; 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); + zapi_opaque_init(zclient, type, flags); /* Send opaque data */ - stream_write(s, data, datasize); + if (datasize > 0) + 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)); @@ -3871,22 +3919,14 @@ zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type, return ZCLIENT_SEND_FAILURE; 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); + /* Common init */ + zapi_opaque_unicast_init(zclient, type, flags, proto, instance, + session_id); /* Send opaque data */ - stream_write(s, data, datasize); + if (datasize > 0) + 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)); @@ -3905,11 +3945,16 @@ int zclient_opaque_decode(struct stream *s, struct zapi_opaque_msg *info) STREAM_GETL(s, info->type); STREAM_GETW(s, info->flags); - /* Decode unicast client info if present */ + /* Decode sending daemon info */ + STREAM_GETC(s, info->src_proto); + STREAM_GETW(s, info->src_instance); + STREAM_GETL(s, info->src_session_id); + + /* Decode unicast destination 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); + STREAM_GETC(s, info->dest_proto); + STREAM_GETW(s, info->dest_instance); + STREAM_GETL(s, info->dest_session_id); } info->len = STREAM_READABLE(s); @@ -4026,7 +4071,7 @@ static zclient_handler *const lib_handlers[] = { }; /* Zebra client message read function. */ -static void zclient_read(struct thread *thread) +static void zclient_read(struct event *thread) { size_t already; uint16_t length, command; @@ -4035,7 +4080,7 @@ static void zclient_read(struct thread *thread) struct zclient *zclient; /* Get socket to zebra. */ - zclient = THREAD_ARG(thread); + zclient = EVENT_ARG(thread); zclient->t_read = NULL; /* Read zebra header (if we don't have it already). */ @@ -4163,15 +4208,15 @@ void zclient_redistribute(int command, struct zclient *zclient, afi_t afi, } else { if (command == ZEBRA_REDISTRIBUTE_ADD) { - if (vrf_bitmap_check(zclient->redist[afi][type], + if (vrf_bitmap_check(&zclient->redist[afi][type], vrf_id)) return; - vrf_bitmap_set(zclient->redist[afi][type], vrf_id); + vrf_bitmap_set(&zclient->redist[afi][type], vrf_id); } else { - if (!vrf_bitmap_check(zclient->redist[afi][type], + if (!vrf_bitmap_check(&zclient->redist[afi][type], vrf_id)) return; - vrf_bitmap_unset(zclient->redist[afi][type], vrf_id); + vrf_bitmap_unset(&zclient->redist[afi][type], vrf_id); } } @@ -4186,14 +4231,15 @@ void zclient_redistribute_default(int command, struct zclient *zclient, { if (command == ZEBRA_REDISTRIBUTE_DEFAULT_ADD) { - if (vrf_bitmap_check(zclient->default_information[afi], vrf_id)) + if (vrf_bitmap_check(&zclient->default_information[afi], + vrf_id)) return; - vrf_bitmap_set(zclient->default_information[afi], vrf_id); + vrf_bitmap_set(&zclient->default_information[afi], vrf_id); } else { - if (!vrf_bitmap_check(zclient->default_information[afi], + if (!vrf_bitmap_check(&zclient->default_information[afi], vrf_id)) return; - vrf_bitmap_unset(zclient->default_information[afi], vrf_id); + vrf_bitmap_unset(&zclient->default_information[afi], vrf_id); } if (zclient->sock > 0) @@ -4204,22 +4250,22 @@ static void zclient_event(enum zclient_event event, struct zclient *zclient) { switch (event) { case ZCLIENT_SCHEDULE: - thread_add_event(zclient->master, zclient_connect, zclient, 0, - &zclient->t_connect); + event_add_event(zclient->master, zclient_connect, zclient, 0, + &zclient->t_connect); break; case ZCLIENT_CONNECT: if (zclient_debug) zlog_debug( "zclient connect failures: %d schedule interval is now %d", zclient->fail, zclient->fail < 3 ? 10 : 60); - thread_add_timer(zclient->master, zclient_connect, zclient, - zclient->fail < 3 ? 10 : 60, - &zclient->t_connect); + event_add_timer(zclient->master, zclient_connect, zclient, + zclient->fail < 3 ? 10 : 60, + &zclient->t_connect); break; case ZCLIENT_READ: zclient->t_read = NULL; - thread_add_read(zclient->master, zclient_read, zclient, - zclient->sock, &zclient->t_read); + event_add_read(zclient->master, zclient_read, zclient, + zclient->sock, &zclient->t_read); break; } } @@ -4294,6 +4340,8 @@ int32_t zapi_capabilities_decode(struct stream *s, struct zapi_cap *api) memset(api, 0, sizeof(*api)); + api->safi = SAFI_UNICAST; + STREAM_GETL(s, api->cap); switch (api->cap) { case ZEBRA_CLIENT_GR_CAPABILITIES: @@ -4465,3 +4513,125 @@ int zclient_send_zebra_gre_request(struct zclient *client, zclient_send_message(client); return 0; } + + +/* + * Opaque notification features + */ + +/* + * Common encode helper for opaque notifications, both registration + * and async notification messages. + */ +static int opaque_notif_encode_common(struct stream *s, uint32_t msg_type, + bool request, bool reg, uint8_t proto, + uint16_t instance, uint32_t session_id) +{ + int ret = 0; + uint8_t val = 0; + + stream_reset(s); + + zclient_create_header(s, ZEBRA_OPAQUE_NOTIFY, VRF_DEFAULT); + + /* Notification or request */ + if (request) + val = 1; + stream_putc(s, val); + + if (reg) + val = 1; + else + val = 0; + stream_putc(s, val); + + stream_putl(s, msg_type); + + stream_putc(s, proto); + stream_putw(s, instance); + stream_putl(s, session_id); + + /* And capture message length */ + stream_putw_at(s, 0, stream_get_endp(s)); + + return ret; +} + +/* + * Encode a zapi opaque message type notification into buffer 's' + */ +int zclient_opaque_notif_encode(struct stream *s, uint32_t msg_type, bool reg, + uint8_t proto, uint16_t instance, + uint32_t session_id) +{ + return opaque_notif_encode_common(s, msg_type, false /* !request */, + reg, proto, instance, session_id); +} + +/* + * Decode an incoming zapi opaque message type notification + */ +int zclient_opaque_notif_decode(struct stream *s, + struct zapi_opaque_notif_info *info) +{ + uint8_t val; + + memset(info, 0, sizeof(*info)); + + STREAM_GETC(s, val); /* Registration or notification */ + info->request = (val != 0); + + STREAM_GETC(s, val); + info->reg = (val != 0); + + STREAM_GETL(s, info->msg_type); + + STREAM_GETC(s, info->proto); + STREAM_GETW(s, info->instance); + STREAM_GETL(s, info->session_id); + + return 0; + +stream_failure: + return -1; +} + +/* + * Encode and send a zapi opaque message type notification request to zebra + */ +enum zclient_send_status zclient_opaque_request_notify(struct zclient *zclient, + uint32_t msgtype) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return ZCLIENT_SEND_FAILURE; + + s = zclient->obuf; + + opaque_notif_encode_common(s, msgtype, true /* request */, + true /* register */, zclient->redist_default, + zclient->instance, zclient->session_id); + + return zclient_send_message(zclient); +} + +/* + * Encode and send a request to drop notifications for an opaque message type. + */ +enum zclient_send_status zclient_opaque_drop_notify(struct zclient *zclient, + uint32_t msgtype) +{ + struct stream *s; + + if (!zclient || zclient->sock < 0) + return ZCLIENT_SEND_FAILURE; + + s = zclient->obuf; + + opaque_notif_encode_common(s, msgtype, true /* req */, + false /* unreg */, zclient->redist_default, + zclient->instance, zclient->session_id); + + return zclient_send_message(zclient); +} diff --git a/lib/zclient.h b/lib/zclient.h index 53c7038c8874..316dd4cd68b9 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -87,7 +87,9 @@ enum zserv_client_capabilities { extern struct sockaddr_storage zclient_addr; extern socklen_t zclient_addr_len; -/* Zebra message types. */ +/* Zebra message types. Please update the corresponding + * command_types array with any changes! + */ typedef enum { ZEBRA_INTERFACE_ADD, ZEBRA_INTERFACE_DELETE, @@ -232,7 +234,11 @@ typedef enum { ZEBRA_TC_CLASS_DELETE, ZEBRA_TC_FILTER_ADD, ZEBRA_TC_FILTER_DELETE, + ZEBRA_OPAQUE_NOTIFY, } zebra_message_types_t; +/* Zebra message types. Please update the corresponding + * command_types array with any changes! + */ enum zebra_error_types { ZEBRA_UNKNOWN_ERROR, /* Error of unknown type */ @@ -290,7 +296,7 @@ typedef int (zclient_handler)(ZAPI_CALLBACK_ARGS); /* Structure for the zebra client. */ struct zclient { /* The thread master we schedule ourselves on */ - struct thread_master *master; + struct event_loop *master; /* Privileges to change socket values */ struct zebra_privs_t *privs; @@ -323,11 +329,11 @@ struct zclient { struct buffer *wb; /* Read and connect thread. */ - struct thread *t_read; - struct thread *t_connect; + struct event *t_read; + struct event *t_connect; /* Thread to write buffered data to zebra. */ - struct thread *t_write; + struct event *t_write; /* Redistribute information. */ uint8_t redist_default; /* clients protocol */ @@ -862,7 +868,7 @@ int zclient_neigh_ip_encode(struct stream *s, uint16_t cmd, union sockunion *in, extern uint32_t zclient_get_nhg_start(uint32_t proto); -extern struct zclient *zclient_new(struct thread_master *m, +extern struct zclient *zclient_new(struct event_loop *m, struct zclient_options *opt, zclient_handler *const *handlers, size_t n_handlers); @@ -901,7 +907,7 @@ zclient_send_vrf_label(struct zclient *zclient, vrf_id_t vrf_id, afi_t afi, extern enum zclient_send_status zclient_send_localsid(struct zclient *zclient, const struct in6_addr *sid, - ifindex_t oif, enum seg6local_action_t action, + vrf_id_t vrf_id, enum seg6local_action_t action, const struct seg6local_context *context); extern void zclient_send_reg_requests(struct zclient *, vrf_id_t); @@ -1176,16 +1182,33 @@ zclient_send_opaque_unicast(struct zclient *zclient, uint32_t type, uint32_t session_id, const uint8_t *data, size_t datasize); +/* Init functions also provided for clients who want to encode their + * data inline into the zclient's stream buffer. Please use these instead + * of hand-encoding the header info, since that may change over time. + * Note that these will reset the zclient's outbound stream before encoding. + */ +enum zclient_send_status zapi_opaque_init(struct zclient *zclient, + uint32_t type, uint16_t flags); + +enum zclient_send_status +zapi_opaque_unicast_init(struct zclient *zclient, uint32_t type, uint16_t flags, + uint8_t proto, uint16_t instance, uint32_t session_id); + /* 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; + /* Sending client info */ + uint8_t src_proto; + uint16_t src_instance; + uint32_t src_session_id; + + /* Destination client info - *if* UNICAST flag is set */ + uint8_t dest_proto; + uint16_t dest_instance; + uint32_t dest_session_id; }; #define ZAPI_OPAQUE_FLAG_UNICAST 0x01 @@ -1201,6 +1224,34 @@ struct zapi_opaque_reg_info { uint32_t session_id; }; +/* Simple struct conveying information about opaque notifications. + * Daemons can request notifications about the status of registration for + * opaque message types. For example, a client daemon can request notification + * when a server registers to receive a certain message code. Or a server can + * request notification when a subscriber registers for its output. + */ +struct zapi_opaque_notif_info { + bool request; /* Request to register, or notification from zebra */ + bool reg; /* Register or unregister */ + uint32_t msg_type; /* Target message code */ + + /* For notif registration, zapi info for the client. + * For notifications, zapi info for the message's server/registrant. + * For notification that there is no server/registrant, not present. + */ + uint8_t proto; + uint16_t instance; + uint32_t session_id; +}; + +/* The same ZAPI message is used for daemon->zebra requests, and for + * zebra->daemon notifications. + * Daemons send 'request' true, and 'reg' true or false. + * Zebra sends 'request' false, 'reg' set if the notification is a + * server/receiver registration for the message type, and false if the event + * is the end of registrations. + */ + /* Decode incoming opaque */ int zclient_opaque_decode(struct stream *msg, struct zapi_opaque_msg *info); @@ -1211,6 +1262,19 @@ enum zclient_send_status zclient_unregister_opaque(struct zclient *zclient, int zapi_opaque_reg_decode(struct stream *msg, struct zapi_opaque_reg_info *info); +/* Opaque notification features */ +enum zclient_send_status zclient_opaque_request_notify(struct zclient *zclient, + uint32_t msgtype); +enum zclient_send_status zclient_opaque_drop_notify(struct zclient *zclient, + uint32_t msgtype); + +/* Encode, decode an incoming zapi opaque notification */ +int zclient_opaque_notif_encode(struct stream *s, uint32_t msg_type, + bool reg /* register or unreg*/, uint8_t proto, + uint16_t instance, uint32_t session_id); +int zclient_opaque_notif_decode(struct stream *s, + struct zapi_opaque_notif_info *info); + /* * Registry of opaque message types. Please do not reuse an in-use * type code; some daemons are likely relying on it. diff --git a/lib/zlog.c b/lib/zlog.c index e05720fd9e7a..309a955fa90a 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -48,7 +48,7 @@ #include "frrcu.h" #include "zlog.h" #include "libfrr_trace.h" -#include "thread.h" +#include "frrevent.h" DEFINE_MTYPE_STATIC(LIB, LOG_MESSAGE, "log message"); DEFINE_MTYPE_STATIC(LIB, LOG_TLSBUF, "log thread-local buffer"); @@ -506,7 +506,7 @@ static void vzlog_tls(struct zlog_tls *zlog_tls, const struct xref_logmsg *xref, static void zlog_backtrace_msg(const struct xref_logmsg *xref, int prio) { - struct thread *tc = pthread_getspecific(thread_current); + struct event *tc = pthread_getspecific(thread_current); const char *uid = xref->xref.xrefdata->uid; bool found_thread = false; diff --git a/lib/zlog_5424.c b/lib/zlog_5424.c index 5264dda0f8f0..c15bdece299e 100644 --- a/lib/zlog_5424.c +++ b/lib/zlog_5424.c @@ -26,7 +26,7 @@ #include "frr_pthread.h" #include "command.h" #include "monotime.h" -#include "thread.h" +#include "frrevent.h" #include "lib/version.h" #include "lib/lib_errors.h" @@ -789,10 +789,10 @@ static void zlog_5424_cycle(struct zlog_cfg_5424 *zcf, int fd) rcu_free(MTYPE_LOG_5424, oldt, zt.rcu_head); } -static void zlog_5424_reconnect(struct thread *t) +static void zlog_5424_reconnect(struct event *t) { - struct zlog_cfg_5424 *zcf = THREAD_ARG(t); - int fd = THREAD_FD(t); + struct zlog_cfg_5424 *zcf = EVENT_ARG(t); + int fd = EVENT_FD(t); char dummy[256]; ssize_t ret; @@ -800,8 +800,8 @@ static void zlog_5424_reconnect(struct thread *t) ret = read(fd, dummy, sizeof(dummy)); if (ret > 0) { /* logger is sending us something?!?! */ - thread_add_read(t->master, zlog_5424_reconnect, zcf, fd, - &zcf->t_reconnect); + event_add_read(t->master, zlog_5424_reconnect, zcf, fd, + &zcf->t_reconnect); return; } @@ -1030,14 +1030,14 @@ static int zlog_5424_open(struct zlog_cfg_5424 *zcf, int sock_type) assert(zcf->master); if (fd != -1) { - thread_add_read(zcf->master, zlog_5424_reconnect, zcf, - fd, &zcf->t_reconnect); + event_add_read(zcf->master, zlog_5424_reconnect, zcf, + fd, &zcf->t_reconnect); zcf->reconn_backoff_cur = zcf->reconn_backoff; } else { - thread_add_timer_msec(zcf->master, zlog_5424_reconnect, - zcf, zcf->reconn_backoff_cur, - &zcf->t_reconnect); + event_add_timer_msec(zcf->master, zlog_5424_reconnect, + zcf, zcf->reconn_backoff_cur, + &zcf->t_reconnect); zcf->reconn_backoff_cur += zcf->reconn_backoff_cur / 2; if (zcf->reconn_backoff_cur > zcf->reconn_backoff_max) @@ -1053,7 +1053,7 @@ bool zlog_5424_apply_dst(struct zlog_cfg_5424 *zcf) { int fd = -1; - thread_cancel(&zcf->t_reconnect); + event_cancel(&zcf->t_reconnect); if (zcf->prio_min != ZLOG_DISABLED) fd = zlog_5424_open(zcf, -1); @@ -1106,7 +1106,7 @@ bool zlog_5424_rotate(struct zlog_cfg_5424 *zcf) if (!zcf->active) return true; - thread_cancel(&zcf->t_reconnect); + event_cancel(&zcf->t_reconnect); /* need to retain the socket type because it also influences * other fields (packets) and we can't atomically swap these diff --git a/lib/zlog_5424.h b/lib/zlog_5424.h index 377e7be2207d..06525c00447a 100644 --- a/lib/zlog_5424.h +++ b/lib/zlog_5424.h @@ -13,8 +13,8 @@ #include "zlog_targets.h" #include "qobj.h" -struct thread; -struct thread_master; +struct event; +struct event_loop; enum zlog_5424_dst { /* can be used to disable a target temporarily */ @@ -78,8 +78,8 @@ struct zlog_cfg_5424 { */ /* sockets only - read handler to reconnect on errors */ - struct thread_master *master; - struct thread *t_reconnect; + struct event_loop *master; + struct event *t_reconnect; unsigned int reconn_backoff, reconn_backoff_cur, reconn_backoff_max; int sock_type; struct sockaddr_storage sa; diff --git a/lib/zlog_5424_cli.c b/lib/zlog_5424_cli.c index 7f070afbc575..3003df542f58 100644 --- a/lib/zlog_5424_cli.c +++ b/lib/zlog_5424_cli.c @@ -28,7 +28,7 @@ DECLARE_RBTREE_UNIQ(targets, struct zlog_cfg_5424_user, targets_item, DEFINE_QOBJ_TYPE(zlog_cfg_5424_user); static struct targets_head targets = INIT_RBTREE_UNIQ(targets); -static struct thread_master *log_5424_master; +static struct event_loop *log_5424_master; static void clear_dst(struct zlog_cfg_5424_user *cfg); @@ -948,7 +948,7 @@ void log_5424_cmd_init(void) /* hooks */ -static int log_5424_early_init(struct thread_master *master); +static int log_5424_early_init(struct event_loop *master); static int log_5424_rotate(void); static int log_5424_fini(void); @@ -959,7 +959,7 @@ __attribute__((_CONSTRUCTOR(475))) static void zlog_5424_startup_init(void) hook_register(frr_fini, log_5424_fini); } -static int log_5424_early_init(struct thread_master *master) +static int log_5424_early_init(struct event_loop *master) { log_5424_master = master; diff --git a/mgmtd/.gitignore b/mgmtd/.gitignore new file mode 100644 index 000000000000..7ce107e93bb6 --- /dev/null +++ b/mgmtd/.gitignore @@ -0,0 +1 @@ +mgmtd diff --git a/mgmtd/Makefile b/mgmtd/Makefile new file mode 100644 index 000000000000..d69ec5f65ab5 --- /dev/null +++ b/mgmtd/Makefile @@ -0,0 +1,10 @@ +all: ALWAYS + @$(MAKE) -s -C .. mgmtd/mgmtd +%: ALWAYS + @$(MAKE) -s -C .. mgmtd/$@ + +Makefile: + #nothing +ALWAYS: +.PHONY: ALWAYS makefiles +.SUFFIXES: diff --git a/mgmtd/mgmt.c b/mgmtd/mgmt.c new file mode 100644 index 000000000000..77c4473e49f1 --- /dev/null +++ b/mgmtd/mgmt.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * FRR Management Daemon (MGMTD) program + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar + */ + +#include <zebra.h> +#include "debug.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_be_adapter.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_fe_adapter.h" +#include "mgmtd/mgmt_history.h" +#include "mgmtd/mgmt_memory.h" + +struct debug mgmt_debug_be = {.desc = "Management backend adapater"}; +struct debug mgmt_debug_ds = {.desc = "Management datastore"}; +struct debug mgmt_debug_fe = {.desc = "Management frontend adapater"}; +struct debug mgmt_debug_txn = {.desc = "Management transaction"}; + +/* MGMTD process wide configuration. */ +static struct mgmt_master mgmt_master; + +/* MGMTD process wide configuration pointer to export. */ +struct mgmt_master *mm; + +void mgmt_master_init(struct event_loop *master, const int buffer_size) +{ + memset(&mgmt_master, 0, sizeof(struct mgmt_master)); + + mm = &mgmt_master; + mm->master = master; + mm->terminating = false; + mm->socket_buffer = buffer_size; + mm->perf_stats_en = true; +} + +void mgmt_init(void) +{ + + /* Initialize datastores */ + mgmt_ds_init(mm); + + /* Initialize history */ + mgmt_history_init(); + + /* Initialize MGMTD Transaction module */ + mgmt_txn_init(mm, mm->master); + + /* Initialize the MGMTD Frontend Adapter Module */ + mgmt_fe_adapter_init(mm->master); + + /* Initialize the CLI frontend client */ + vty_init_mgmt_fe(); + + /* MGMTD VTY commands installation. */ + mgmt_vty_init(); + + /* + * Initialize the MGMTD Backend Adapter Module + * + * We do this after the FE stuff so that we always read our config file + * prior to any BE connection. + */ + mgmt_be_adapter_init(mm->master); +} + +void mgmt_terminate(void) +{ + mgmt_fe_adapter_destroy(); + mgmt_be_adapter_destroy(); + mgmt_txn_destroy(); + mgmt_history_destroy(); + mgmt_ds_destroy(); +} diff --git a/mgmtd/mgmt.h b/mgmtd/mgmt.h new file mode 100644 index 000000000000..f52d478bc2c5 --- /dev/null +++ b/mgmtd/mgmt.h @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD message definition header. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_H +#define _FRR_MGMTD_H + +#include "debug.h" +#include "vrf.h" +#include "defaults.h" +#include "stream.h" + +#include "mgmtd/mgmt_memory.h" +#include "mgmtd/mgmt_defines.h" +#include "mgmtd/mgmt_history.h" +#include "mgmtd/mgmt_txn.h" +#include "mgmtd/mgmt_ds.h" + +#define MGMTD_VTY_PORT 2622 +#define MGMTD_SOCKET_BUF_SIZE 65535 +#define MGMTD_MAX_COMMIT_LIST 10 + +extern struct debug mgmt_debug_be; +extern struct debug mgmt_debug_ds; +extern struct debug mgmt_debug_fe; +extern struct debug mgmt_debug_txn; + +#define MGMT_DEBUG_BE_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL) +#define MGMT_DEBUG_DS_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_ds, DEBUG_MODE_ALL) +#define MGMT_DEBUG_FE_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_fe, DEBUG_MODE_ALL) +#define MGMT_DEBUG_TXN_CHECK() DEBUG_MODE_CHECK(&mgmt_debug_tx, DEBUG_MODE_ALL) + +struct mgmt_txn_ctx; + +/* + * MGMTD master for system wide configurations and variables. + */ +struct mgmt_master { + struct event_loop *master; + + /* How big should we set the socket buffer size */ + uint32_t socket_buffer; + + /* The single instance of config transaction allowed at any time */ + struct mgmt_txns_head txn_list; + + /* Map of Transactions and its ID */ + struct hash *txn_hash; + uint64_t next_txn_id; + + /* The single instance of config transaction allowed at any time */ + struct mgmt_txn_ctx *cfg_txn; + + /* Datastores */ + struct mgmt_ds_ctx *running_ds; + struct mgmt_ds_ctx *candidate_ds; + struct mgmt_ds_ctx *oper_ds; + + bool terminating; /* global flag that sigint terminate seen */ + bool perf_stats_en; /* to enable performance stats measurement */ + + /* List of commit infos */ + struct mgmt_cmt_infos_head cmts; /* List of last 10 commits executed. */ +}; + +extern struct mgmt_master *mm; + +/* Inline functions */ +static inline unsigned long timeval_elapsed(struct timeval a, struct timeval b) +{ + return (((a.tv_sec - b.tv_sec) * TIMER_SECOND_MICRO) + + (a.tv_usec - b.tv_usec)); +} + +/* + * Remove trailing separator from a string. + * + * str + * A null terminated string. + * + * sep + * Trailing character that needs to be removed. + */ +static inline void mgmt_remove_trailing_separator(char *str, char sep) +{ + size_t len; + + len = strlen(str); + if (len && str[len - 1] == sep) + str[len - 1] = '\0'; +} + +/* Prototypes. */ +extern void mgmt_terminate(void); +extern void mgmt_reset(void); +extern time_t mgmt_clock(void); + +extern int mgmt_config_write(struct vty *vty); +extern struct vty *mgmt_vty_read_config(const char *config_file, + char *config_default_dir); +extern void mgmt_master_init(struct event_loop *master, const int buffer_size); + +extern void mgmt_init(void); +extern void mgmt_vty_init(void); + +#endif /* _FRR_MGMTD_H */ diff --git a/mgmtd/mgmt_be_adapter.c b/mgmtd/mgmt_be_adapter.c new file mode 100644 index 000000000000..399fdafded1a --- /dev/null +++ b/mgmtd/mgmt_be_adapter.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Backend Client Connection Adapter + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ + +#include <zebra.h> +#include "darr.h" +#include "frrevent.h" +#include "sockopt.h" +#include "network.h" +#include "libfrr.h" +#include "mgmt_msg.h" +#include "mgmt_pb.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_memory.h" +#include "mgmt_be_client.h" +#include "mgmtd/mgmt_be_adapter.h" + +#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \ + DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_BE_ADAPTER_ERR(fmt, ...) \ + zlog_err("BE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) + +#define FOREACH_ADAPTER_IN_LIST(adapter) \ + frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter)) + +/* + * Mapping of YANG XPath regular expressions to + * their corresponding backend clients. + */ +struct mgmt_be_xpath_map { + char *xpath_regexp; + uint subscr_info[MGMTD_BE_CLIENT_ID_MAX]; +}; + +struct mgmt_be_client_xpath { + const char *xpath; + uint subscribed; +}; + +struct mgmt_be_client_xpath_map { + struct mgmt_be_client_xpath *xpaths; + uint nxpaths; +}; + +struct mgmt_be_get_adapter_config_params { + struct mgmt_be_client_adapter *adapter; + struct nb_config_cbs *cfg_chgs; + uint32_t seq; +}; + +/* + * Each client gets their own map, but also union all the strings into the + * above map as well. + */ +#if HAVE_STATICD +static struct mgmt_be_client_xpath staticd_xpaths[] = { + { + .xpath = "/frr-vrf:lib/*", + .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, + }, + { + .xpath = "/frr-interface:lib/*", + .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, + }, + { + .xpath = + "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*", + .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG, + }, +}; +#endif + +static struct mgmt_be_client_xpath_map + mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = { +#ifdef HAVE_STATICD + [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths, + array_size(staticd_xpaths)}, +#endif +}; + +/* + * We would like to have a better ADT than one with O(n) comparisons + * + * Perhaps it's possible to sort this array in a way that allows binary search + * to find the start, then walk until no possible match can follow? Intuition + * says this probably involves exact match/no-match on a stem in the map array + * or something like that. + */ +static struct mgmt_be_xpath_map *mgmt_xpath_map; + +static struct event_loop *mgmt_loop; +static struct msg_server mgmt_be_server = {.fd = -1}; + +static struct mgmt_be_adapters_head mgmt_be_adapters; + +static struct mgmt_be_client_adapter + *mgmt_be_adapters_by_id[MGMTD_BE_CLIENT_ID_MAX]; + +/* Forward declarations */ +static void +mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter); + +static uint mgmt_be_get_subscr_for_xpath_and_client( + const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask); + +static struct mgmt_be_client_adapter * +mgmt_be_find_adapter_by_fd(int conn_fd) +{ + struct mgmt_be_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) { + if (adapter->conn->fd == conn_fd) + return adapter; + } + + return NULL; +} + +static struct mgmt_be_client_adapter * +mgmt_be_find_adapter_by_name(const char *name) +{ + struct mgmt_be_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) { + if (!strncmp(adapter->name, name, sizeof(adapter->name))) + return adapter; + } + + return NULL; +} + +static void mgmt_register_client_xpath(enum mgmt_be_client_id id, + const char *xpath, uint subscribed) +{ + struct mgmt_be_xpath_map *map; + + darr_foreach_p (mgmt_xpath_map, map) + if (!strcmp(xpath, map->xpath_regexp)) { + map->subscr_info[id] = subscribed; + return; + } + /* we didn't find a matching entry */ + map = darr_append(mgmt_xpath_map); + map->xpath_regexp = XSTRDUP(MTYPE_MGMTD_XPATH, xpath); + map->subscr_info[id] = subscribed; +} + +/* + * Load the initial mapping from static init map + */ +static void mgmt_be_xpath_map_init(void) +{ + struct mgmt_be_client_xpath *init, *end; + enum mgmt_be_client_id id; + + MGMTD_BE_ADAPTER_DBG("Init XPath Maps"); + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + init = mgmt_client_xpaths[id].xpaths; + end = init + mgmt_client_xpaths[id].nxpaths; + for (; init < end; init++) { + MGMTD_BE_ADAPTER_DBG(" - XPATH: '%s'", init->xpath); + mgmt_register_client_xpath(id, init->xpath, + init->subscribed); + } + } + + MGMTD_BE_ADAPTER_DBG("Total XPath Maps: %u", darr_len(mgmt_xpath_map)); +} + +static void mgmt_be_xpath_map_cleanup(void) +{ + struct mgmt_be_xpath_map *map; + + darr_foreach_p (mgmt_xpath_map, map) + XFREE(MTYPE_MGMTD_XPATH, map->xpath_regexp); + darr_free(mgmt_xpath_map); +} + +static int mgmt_be_eval_regexp_match(const char *xpath_regexp, + const char *xpath) +{ + int match_len = 0, re_indx = 0, xp_indx = 0; + int rexp_len, xpath_len; + bool match = true, re_wild = false, xp_wild = false; + bool delim = false, enter_wild_match = false; + char wild_delim = 0; + + rexp_len = strlen(xpath_regexp); + xpath_len = strlen(xpath); + + /* + * Remove the trailing wildcard from the regexp and Xpath. + */ + if (rexp_len && xpath_regexp[rexp_len-1] == '*') + rexp_len--; + if (xpath_len && xpath[xpath_len-1] == '*') + xpath_len--; + + if (!rexp_len || !xpath_len) + return 0; + + for (re_indx = 0, xp_indx = 0; + match && re_indx < rexp_len && xp_indx < xpath_len;) { + match = (xpath_regexp[re_indx] == xpath[xp_indx]); + + /* + * Check if we need to enter wildcard matching. + */ + if (!enter_wild_match && !match && + (xpath_regexp[re_indx] == '*' + || xpath[xp_indx] == '*')) { + /* + * Found wildcard + */ + enter_wild_match = + (xpath_regexp[re_indx-1] == '/' + || xpath_regexp[re_indx-1] == '\'' + || xpath[xp_indx-1] == '/' + || xpath[xp_indx-1] == '\''); + if (enter_wild_match) { + if (xpath_regexp[re_indx] == '*') { + /* + * Begin RE wildcard match. + */ + re_wild = true; + wild_delim = xpath_regexp[re_indx-1]; + } else if (xpath[xp_indx] == '*') { + /* + * Begin XP wildcard match. + */ + xp_wild = true; + wild_delim = xpath[xp_indx-1]; + } + } + } + + /* + * Check if we need to exit wildcard matching. + */ + if (enter_wild_match) { + if (re_wild && xpath[xp_indx] == wild_delim) { + /* + * End RE wildcard matching. + */ + re_wild = false; + if (re_indx < rexp_len-1) + re_indx++; + enter_wild_match = false; + } else if (xp_wild + && xpath_regexp[re_indx] == wild_delim) { + /* + * End XP wildcard matching. + */ + xp_wild = false; + if (xp_indx < xpath_len-1) + xp_indx++; + enter_wild_match = false; + } + } + + match = (xp_wild || re_wild + || xpath_regexp[re_indx] == xpath[xp_indx]); + + /* + * Check if we found a delimiter in both the Xpaths + */ + if ((xpath_regexp[re_indx] == '/' + && xpath[xp_indx] == '/') + || (xpath_regexp[re_indx] == ']' + && xpath[xp_indx] == ']') + || (xpath_regexp[re_indx] == '[' + && xpath[xp_indx] == '[')) { + /* + * Increment the match count if we have a + * new delimiter. + */ + if (match && re_indx && xp_indx && !delim) + match_len++; + delim = true; + } else { + delim = false; + } + + /* + * Proceed to the next character in the RE/XP string as + * necessary. + */ + if (!re_wild) + re_indx++; + if (!xp_wild) + xp_indx++; + } + + /* + * If we finished matching and the last token was a full match + * increment the match count appropriately. + */ + if (match && !delim && + (xpath_regexp[re_indx] == '/' + || xpath_regexp[re_indx] == ']')) + match_len++; + + return match_len; +} + +static void mgmt_be_adapter_delete(struct mgmt_be_client_adapter *adapter) +{ + MGMTD_BE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + + /* + * Notify about disconnect for appropriate cleanup + */ + mgmt_txn_notify_be_adapter_conn(adapter, false); + if (adapter->id < MGMTD_BE_CLIENT_ID_MAX) { + mgmt_be_adapters_by_id[adapter->id] = NULL; + adapter->id = MGMTD_BE_CLIENT_ID_MAX; + } + + assert(adapter->refcount == 1); + mgmt_be_adapter_unlock(&adapter); +} + +static int mgmt_be_adapter_notify_disconnect(struct msg_conn *conn) +{ + struct mgmt_be_client_adapter *adapter = conn->user; + + MGMTD_BE_ADAPTER_DBG("notify disconnect for client adapter '%s'", + adapter->name); + + mgmt_be_adapter_delete(adapter); + + return 0; +} + +static void +mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_be_client_adapter *old; + + FOREACH_ADAPTER_IN_LIST (old) { + if (old != adapter && + !strncmp(adapter->name, old->name, sizeof(adapter->name))) { + /* + * We have a Zombie lingering around + */ + MGMTD_BE_ADAPTER_DBG( + "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!", + adapter->name, adapter->conn->fd, + old->conn->fd); + /* this will/should delete old */ + msg_conn_disconnect(old->conn, false); + } + } +} + + +static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter, + Mgmtd__BeMessage *be_msg) +{ + return msg_conn_send_msg( + adapter->conn, MGMT_MSG_VERSION_PROTOBUF, be_msg, + mgmtd__be_message__get_packed_size(be_msg), + (size_t(*)(void *, void *))mgmtd__be_message__pack, false); +} + +static int mgmt_be_send_subscr_reply(struct mgmt_be_client_adapter *adapter, + bool success) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeSubscribeReply reply; + + mgmtd__be_subscribe_reply__init(&reply); + reply.success = success; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY; + be_msg.subscr_reply = &reply; + + MGMTD_FE_CLIENT_DBG("Sending SUBSCR_REPLY client: %s sucess: %u", + adapter->name, success); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +static int +mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter, + Mgmtd__BeMessage *be_msg) +{ + /* + * protobuf-c adds a max size enum with an internal, and changing by + * version, name; cast to an int to avoid unhandled enum warnings + */ + switch ((int)be_msg->message_case) { + case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ: + MGMTD_BE_ADAPTER_DBG( + "Got SUBSCR_REQ from '%s' to %sregister %zu xpaths", + be_msg->subscr_req->client_name, + !be_msg->subscr_req->subscribe_xpaths && + be_msg->subscr_req->n_xpath_reg + ? "de" + : "", + be_msg->subscr_req->n_xpath_reg); + + if (strlen(be_msg->subscr_req->client_name)) { + strlcpy(adapter->name, be_msg->subscr_req->client_name, + sizeof(adapter->name)); + adapter->id = mgmt_be_client_name2id(adapter->name); + if (adapter->id >= MGMTD_BE_CLIENT_ID_MAX) { + MGMTD_BE_ADAPTER_ERR( + "Unable to resolve adapter '%s' to a valid ID. Disconnecting!", + adapter->name); + /* this will/should delete old */ + msg_conn_disconnect(adapter->conn, false); + zlog_err("XXX different from original code"); + break; + } + mgmt_be_adapters_by_id[adapter->id] = adapter; + mgmt_be_adapter_cleanup_old_conn(adapter); + + /* schedule INIT sequence now that it is registered */ + mgmt_be_adapter_sched_init_event(adapter); + } + + if (be_msg->subscr_req->n_xpath_reg) + /* we aren't handling dynamic xpaths yet */ + mgmt_be_send_subscr_reply(adapter, false); + else + mgmt_be_send_subscr_reply(adapter, true); + break; + case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY: + MGMTD_BE_ADAPTER_DBG( + "Got %s TXN_REPLY from '%s' txn-id %" PRIx64 + " with '%s'", + be_msg->txn_reply->create ? "Create" : "Delete", + adapter->name, be_msg->txn_reply->txn_id, + be_msg->txn_reply->success ? "success" : "failure"); + /* + * Forward the TXN_REPLY to txn module. + */ + mgmt_txn_notify_be_txn_reply( + be_msg->txn_reply->txn_id, + be_msg->txn_reply->create, + be_msg->txn_reply->success, adapter); + break; + case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY: + MGMTD_BE_ADAPTER_DBG( + "Got CFGDATA_REPLY from '%s' txn-id %" PRIx64 + " batch-id %" PRIu64 " err:'%s'", + adapter->name, be_msg->cfg_data_reply->txn_id, + be_msg->cfg_data_reply->batch_id, + be_msg->cfg_data_reply->error_if_any + ? be_msg->cfg_data_reply->error_if_any + : "None"); + /* + * Forward the CGFData-create reply to txn module. + */ + mgmt_txn_notify_be_cfgdata_reply( + be_msg->cfg_data_reply->txn_id, + be_msg->cfg_data_reply->batch_id, + be_msg->cfg_data_reply->success, + be_msg->cfg_data_reply->error_if_any, adapter); + break; + case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY: + MGMTD_BE_ADAPTER_DBG( + "Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64 + " for %zu batches id %" PRIu64 "-%" PRIu64 " err:'%s'", + be_msg->cfg_apply_reply->success ? "successful" + : "failed", + adapter->name, be_msg->cfg_apply_reply->txn_id, + be_msg->cfg_apply_reply->n_batch_ids, + be_msg->cfg_apply_reply->batch_ids[0], + be_msg->cfg_apply_reply->batch_ids + [be_msg->cfg_apply_reply->n_batch_ids - 1], + be_msg->cfg_apply_reply->error_if_any + ? be_msg->cfg_apply_reply->error_if_any + : "None"); + /* + * Forward the CGFData-apply reply to txn module. + */ + mgmt_txn_notify_be_cfg_apply_reply( + be_msg->cfg_apply_reply->txn_id, + be_msg->cfg_apply_reply->success, + (uint64_t *)be_msg->cfg_apply_reply->batch_ids, + be_msg->cfg_apply_reply->n_batch_ids, + be_msg->cfg_apply_reply->error_if_any, adapter); + break; + case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY: + /* + * TODO: Add handling code in future. + */ + break; + /* + * NOTE: The following messages are always sent from MGMTD to + * Backend clients only and/or need not be handled on MGMTd. + */ + case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY: + case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ: + case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ: + case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ: + case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ: + case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET: + default: + /* + * A 'default' case is being added contrary to the + * FRR code guidelines to take care of build + * failures on certain build systems (courtesy of + * the proto-c package). + */ + break; + } + + return 0; +} + +int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, bool create) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeTxnReq txn_req; + + mgmtd__be_txn_req__init(&txn_req); + txn_req.create = create; + txn_req.txn_id = txn_id; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ; + be_msg.txn_req = &txn_req; + + MGMTD_BE_ADAPTER_DBG("Sending TXN_REQ to '%s' txn-id: %" PRIu64, + adapter->name, txn_id); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, uint64_t batch_id, + Mgmtd__YangCfgDataReq **cfgdata_reqs, + size_t num_reqs, bool end_of_data) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeCfgDataCreateReq cfgdata_req; + + mgmtd__be_cfg_data_create_req__init(&cfgdata_req); + cfgdata_req.batch_id = batch_id; + cfgdata_req.txn_id = txn_id; + cfgdata_req.data_req = cfgdata_reqs; + cfgdata_req.n_data_req = num_reqs; + cfgdata_req.end_of_data = end_of_data; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ; + be_msg.cfg_data_req = &cfgdata_req; + + MGMTD_BE_ADAPTER_DBG( + "Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64 + " batch-id: %" PRIu64, + adapter->name, txn_id, batch_id); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id) +{ + Mgmtd__BeMessage be_msg; + Mgmtd__BeCfgDataApplyReq apply_req; + + mgmtd__be_cfg_data_apply_req__init(&apply_req); + apply_req.txn_id = txn_id; + + mgmtd__be_message__init(&be_msg); + be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ; + be_msg.cfg_apply_req = &apply_req; + + MGMTD_BE_ADAPTER_DBG("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64, + adapter->name, txn_id); + + return mgmt_be_adapter_send_msg(adapter, &be_msg); +} + +static void mgmt_be_adapter_process_msg(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn) +{ + struct mgmt_be_client_adapter *adapter = conn->user; + Mgmtd__BeMessage *be_msg = mgmtd__be_message__unpack(NULL, len, data); + + if (!be_msg) { + MGMTD_BE_ADAPTER_DBG( + "Failed to decode %zu bytes for adapter: %s", len, + adapter->name); + return; + } + MGMTD_BE_ADAPTER_DBG("Decoded %zu bytes of message: %u for adapter: %s", + len, be_msg->message_case, adapter->name); + (void)mgmt_be_adapter_handle_msg(adapter, be_msg); + mgmtd__be_message__free_unpacked(be_msg, NULL); +} + +static void mgmt_be_iter_and_get_cfg(const char *xpath, struct lyd_node *node, + struct nb_node *nb_node, void *ctx) +{ + struct mgmt_be_get_adapter_config_params *parms = ctx; + struct mgmt_be_client_adapter *adapter = parms->adapter; + uint subscr; + + subscr = mgmt_be_get_subscr_for_xpath_and_client( + xpath, adapter->id, MGMT_SUBSCR_NOTIFY_CFG); + if (subscr) + nb_config_diff_created(node, &parms->seq, parms->cfg_chgs); +} + +/* + * Initialize a BE client over a new connection + */ +static void mgmt_be_adapter_conn_init(struct event *thread) +{ + struct mgmt_be_client_adapter *adapter; + + adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(thread); + assert(adapter && adapter->conn->fd >= 0); + + /* + * Check first if the current session can run a CONFIG + * transaction or not. Reschedule if a CONFIG transaction + * from another session is already in progress. + */ + if (mgmt_config_txn_in_progress() != MGMTD_SESSION_ID_NONE) { + zlog_err("XXX txn in progress, retry init"); + mgmt_be_adapter_sched_init_event(adapter); + return; + } + + /* + * Notify TXN module to create a CONFIG transaction and + * download the CONFIGs identified for this new client. + * If the TXN module fails to initiate the CONFIG transaction + * disconnect from the client forcing a reconnect later. + * That should also take care of destroying the adapter. + */ + if (mgmt_txn_notify_be_adapter_conn(adapter, true) != 0) { + zlog_err("XXX notify be adapter conn fail"); + msg_conn_disconnect(adapter->conn, false); + adapter = NULL; + } +} + +/* + * Schedule the initialization of the BE client connection. + */ +static void +mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter) +{ + event_add_timer_msec(mgmt_loop, mgmt_be_adapter_conn_init, adapter, + MGMTD_BE_CONN_INIT_DELAY_MSEC, + &adapter->conn_init_ev); +} + +void mgmt_be_adapter_lock(struct mgmt_be_client_adapter *adapter) +{ + adapter->refcount++; +} + +extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter) +{ + struct mgmt_be_client_adapter *a = *adapter; + assert(a && a->refcount); + + if (!--a->refcount) { + mgmt_be_adapters_del(&mgmt_be_adapters, a); + EVENT_OFF(a->conn_init_ev); + msg_server_conn_delete(a->conn); + XFREE(MTYPE_MGMTD_BE_ADPATER, a); + } + + *adapter = NULL; +} + +/* + * Initialize the BE adapter module + */ +void mgmt_be_adapter_init(struct event_loop *tm) +{ + assert(!mgmt_loop); + mgmt_loop = tm; + + mgmt_be_adapters_init(&mgmt_be_adapters); + mgmt_be_xpath_map_init(); + + if (msg_server_init(&mgmt_be_server, MGMTD_BE_SERVER_PATH, tm, + mgmt_be_create_adapter, "backend", + &mgmt_debug_be)) { + zlog_err("cannot initialize backend server"); + exit(1); + } +} + +/* + * Destroy the BE adapter module + */ +void mgmt_be_adapter_destroy(void) +{ + struct mgmt_be_client_adapter *adapter; + + msg_server_cleanup(&mgmt_be_server); + FOREACH_ADAPTER_IN_LIST (adapter) { + mgmt_be_adapter_delete(adapter); + } + mgmt_be_xpath_map_cleanup(); +} + +/* + * The server accepted a new connection + */ +struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from) +{ + struct mgmt_be_client_adapter *adapter = NULL; + + assert(!mgmt_be_find_adapter_by_fd(conn_fd)); + + adapter = XCALLOC(MTYPE_MGMTD_BE_ADPATER, + sizeof(struct mgmt_be_client_adapter)); + adapter->id = MGMTD_BE_CLIENT_ID_MAX; + snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d", + conn_fd); + + mgmt_be_adapter_lock(adapter); + mgmt_be_adapters_add_tail(&mgmt_be_adapters, adapter); + RB_INIT(nb_config_cbs, &adapter->cfg_chgs); + + adapter->conn = msg_server_conn_create( + mgmt_loop, conn_fd, mgmt_be_adapter_notify_disconnect, + mgmt_be_adapter_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC, + MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, adapter, + "BE-adapter"); + + MGMTD_BE_ADAPTER_DBG("Added new MGMTD Backend adapter '%s'", + adapter->name); + + return adapter->conn; +} + +struct mgmt_be_client_adapter * +mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id) +{ + return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id] + : NULL); +} + +struct mgmt_be_client_adapter * +mgmt_be_get_adapter_by_name(const char *name) +{ + return mgmt_be_find_adapter_by_name(name); +} + +int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, + struct nb_config_cbs **cfg_chgs) +{ + struct mgmt_be_get_adapter_config_params parms; + struct nb_config *cfg_root = mgmt_ds_get_nb_config(mm->running_ds); + + assert(cfg_chgs); + + /* + * TODO: we should consider making this an assertable condition and + * guaranteeing it be true when this function is called. B/c what is + * going to happen if there are some changes being sent, and we don't + * gather a new snapshot, what new changes that came after the previous + * snapshot will then be lost? + */ + if (RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)) { + parms.adapter = adapter; + parms.cfg_chgs = &adapter->cfg_chgs; + parms.seq = 0; + + mgmt_ds_iter_data(MGMTD_DS_RUNNING, cfg_root, "", + mgmt_be_iter_and_get_cfg, (void *)&parms); + } + + *cfg_chgs = &adapter->cfg_chgs; + return 0; +} + +void mgmt_be_get_subscr_info_for_xpath( + const char *xpath, struct mgmt_be_client_subscr_info *subscr_info) +{ + struct mgmt_be_xpath_map *map; + enum mgmt_be_client_id id; + + memset(subscr_info, 0, sizeof(*subscr_info)); + + MGMTD_BE_ADAPTER_DBG("XPATH: '%s'", xpath); + darr_foreach_p (mgmt_xpath_map, map) { + if (!mgmt_be_eval_regexp_match(map->xpath_regexp, xpath)) + continue; + FOREACH_MGMTD_BE_CLIENT_ID (id) { + subscr_info->xpath_subscr[id] |= map->subscr_info[id]; + } + } + + if (DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)) { + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (!subscr_info->xpath_subscr[id]) + continue; + MGMTD_BE_ADAPTER_DBG("Cient: %s: subscribed: 0x%x", + mgmt_be_client_id2name(id), + subscr_info->xpath_subscr[id]); + } + } +} + +/** + * Return the subscription info bits for a given `xpath` for a given + * `client_id`. + * + * Args: + * xpath - the xpath to check for subscription information. + * client_id - the BE client being checked for. + * subscr_mask - The subscr bits the caller is interested in seeing + * if set. + * + * Returns: + * The subscription info bits. + */ +static uint mgmt_be_get_subscr_for_xpath_and_client( + const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask) +{ + struct mgmt_be_client_xpath_map *map; + uint subscr = 0; + uint i; + + assert(client_id < MGMTD_BE_CLIENT_ID_MAX); + + MGMTD_BE_ADAPTER_DBG("Checking client: %s for xpath: '%s'", + mgmt_be_client_id2name(client_id), xpath); + + map = &mgmt_client_xpaths[client_id]; + for (i = 0; i < map->nxpaths; i++) { + if (!mgmt_be_eval_regexp_match(map->xpaths[i].xpath, xpath)) + continue; + MGMTD_BE_ADAPTER_DBG("xpath: %s: matched: %s", + map->xpaths[i].xpath, xpath); + subscr |= map->xpaths[i].subscribed; + if ((subscr & subscr_mask) == subscr_mask) + break; + } + MGMTD_BE_ADAPTER_DBG("client: %s: subscribed: 0x%x", + mgmt_be_client_id2name(client_id), subscr); + return subscr; +} + +void mgmt_be_adapter_status_write(struct vty *vty) +{ + struct mgmt_be_client_adapter *adapter; + + vty_out(vty, "MGMTD Backend Adapters\n"); + + FOREACH_ADAPTER_IN_LIST (adapter) { + vty_out(vty, " Client: \t\t\t%s\n", adapter->name); + vty_out(vty, " Conn-FD: \t\t\t%d\n", adapter->conn->fd); + vty_out(vty, " Client-Id: \t\t\t%d\n", adapter->id); + vty_out(vty, " Ref-Count: \t\t\t%u\n", adapter->refcount); + vty_out(vty, " Msg-Recvd: \t\t\t%" PRIu64 "\n", + adapter->conn->mstate.nrxm); + vty_out(vty, " Bytes-Recvd: \t\t%" PRIu64 "\n", + adapter->conn->mstate.nrxb); + vty_out(vty, " Msg-Sent: \t\t\t%" PRIu64 "\n", + adapter->conn->mstate.ntxm); + vty_out(vty, " Bytes-Sent: \t\t%" PRIu64 "\n", + adapter->conn->mstate.ntxb); + } + vty_out(vty, " Total: %d\n", + (int)mgmt_be_adapters_count(&mgmt_be_adapters)); +} + +void mgmt_be_xpath_register_write(struct vty *vty) +{ + struct mgmt_be_xpath_map *map; + enum mgmt_be_client_id id; + struct mgmt_be_client_adapter *adapter; + uint info; + + vty_out(vty, "MGMTD Backend XPath Registry\n"); + + darr_foreach_p (mgmt_xpath_map, map) { + vty_out(vty, " - XPATH: '%s'\n", map->xpath_regexp); + FOREACH_MGMTD_BE_CLIENT_ID (id) { + info = map->subscr_info[id]; + if (!info) + continue; + vty_out(vty, + " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n", + mgmt_be_client_id2name(id), + (info & MGMT_SUBSCR_VALIDATE_CFG) != 0, + (info & MGMT_SUBSCR_NOTIFY_CFG) != 0, + (info & MGMT_SUBSCR_OPER_OWN) != 0); + adapter = mgmt_be_get_adapter_by_id(id); + if (adapter) + vty_out(vty, " -- Adapter: %p\n", adapter); + } + } + + vty_out(vty, "Total XPath Registries: %u\n", darr_len(mgmt_xpath_map)); +} + +void mgmt_be_xpath_subscr_info_write(struct vty *vty, const char *xpath) +{ + struct mgmt_be_client_subscr_info subscr; + enum mgmt_be_client_id id; + struct mgmt_be_client_adapter *adapter; + uint info; + + mgmt_be_get_subscr_info_for_xpath(xpath, &subscr); + + vty_out(vty, "XPath: '%s'\n", xpath); + FOREACH_MGMTD_BE_CLIENT_ID (id) { + info = subscr.xpath_subscr[id]; + if (!info) + continue; + vty_out(vty, + " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n", + mgmt_be_client_id2name(id), + (info & MGMT_SUBSCR_VALIDATE_CFG) != 0, + (info & MGMT_SUBSCR_NOTIFY_CFG) != 0, + (info & MGMT_SUBSCR_OPER_OWN) != 0); + adapter = mgmt_be_get_adapter_by_id(id); + if (adapter) + vty_out(vty, " -- Adapter: %p\n", adapter); + } +} diff --git a/mgmtd/mgmt_be_adapter.h b/mgmtd/mgmt_be_adapter.h new file mode 100644 index 000000000000..ca8f55c457ff --- /dev/null +++ b/mgmtd/mgmt_be_adapter.h @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Backend Client Connection Adapter + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ + +#ifndef _FRR_MGMTD_BE_ADAPTER_H_ +#define _FRR_MGMTD_BE_ADAPTER_H_ + +#include "mgmt_be_client.h" +#include "mgmt_msg.h" +#include "mgmtd/mgmt_defines.h" +#include "mgmtd/mgmt_ds.h" + +#define MGMTD_BE_CONN_INIT_DELAY_MSEC 50 + +#define MGMTD_FIND_ADAPTER_BY_INDEX(adapter_index) \ + mgmt_adaptr_ref[adapter_index] + +enum mgmt_be_req_type { + MGMTD_BE_REQ_NONE = 0, + MGMTD_BE_REQ_CFG_VALIDATE, + MGMTD_BE_REQ_CFG_APPLY, + MGMTD_BE_REQ_DATA_GET_ELEM, + MGMTD_BE_REQ_DATA_GET_NEXT +}; + +struct mgmt_be_cfgreq { + Mgmtd__YangCfgDataReq **cfgdata_reqs; + size_t num_reqs; +}; + +struct mgmt_be_datareq { + Mgmtd__YangGetDataReq **getdata_reqs; + size_t num_reqs; +}; + +PREDECL_LIST(mgmt_be_adapters); +PREDECL_LIST(mgmt_txn_badapters); + +struct mgmt_be_client_adapter { + struct msg_conn *conn; + + struct event *conn_init_ev; + + enum mgmt_be_client_id id; + uint32_t flags; + char name[MGMTD_CLIENT_NAME_MAX_LEN]; + uint8_t num_xpath_reg; + char xpath_reg[MGMTD_MAX_NUM_XPATH_REG][MGMTD_MAX_XPATH_LEN]; + + int refcount; + + /* + * List of config items that should be sent to the + * backend during re/connect. This is temporarily + * created and then freed-up as soon as the initial + * config items has been applied onto the backend. + */ + struct nb_config_cbs cfg_chgs; + + struct mgmt_be_adapters_item list_linkage; +}; + +#define MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED (1U << 0) + +DECLARE_LIST(mgmt_be_adapters, struct mgmt_be_client_adapter, list_linkage); + +/* + * MGMT_SUBSCR_xxx - flags for subscription types for xpaths registrations + * + * MGMT_SUBSCR_VALIDATE_CFG :: the client should be asked to validate config + * MGMT_SUBSCR_NOTIFY_CFG :: the client should be notified of config changes + * MGMT_SUBSCR_OPER_OWN :: the client owns the given oeprational state + */ +#define MGMT_SUBSCR_VALIDATE_CFG 0x1 +#define MGMT_SUBSCR_NOTIFY_CFG 0x2 +#define MGMT_SUBSCR_OPER_OWN 0x4 +#define MGMT_SUBSCR_ALL 0x7 + +struct mgmt_be_client_subscr_info { + uint xpath_subscr[MGMTD_BE_CLIENT_ID_MAX]; +}; + +/* Initialise backend adapter module. */ +extern void mgmt_be_adapter_init(struct event_loop *tm); + +/* Destroy the backend adapter module. */ +extern void mgmt_be_adapter_destroy(void); + +/* Acquire lock for backend adapter. */ +extern void mgmt_be_adapter_lock(struct mgmt_be_client_adapter *adapter); + +/* Remove lock from backend adapter. */ +extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter); + +/* Create backend adapter. */ +extern struct msg_conn *mgmt_be_create_adapter(int conn_fd, + union sockunion *su); + +/* Fetch backend adapter given an adapter name. */ +extern struct mgmt_be_client_adapter * +mgmt_be_get_adapter_by_name(const char *name); + +/* Fetch backend adapter given an client ID. */ +extern struct mgmt_be_client_adapter * +mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id); + +/* Fetch backend adapter config. */ +extern int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter, + struct nb_config_cbs **cfg_chgs); + +/* Create/destroy a transaction. */ +extern int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, bool create); + +/* + * Send config data create request to backend client. + * + * adaptr + * Backend adapter information. + * + * txn_id + * Unique transaction identifier. + * + * batch_id + * Request batch ID. + * + * cfgdata_reqs + * An array of pointer to Mgmtd__YangCfgDataReq. + * + * num_reqs + * Length of the cfgdata_reqs array. + * + * end_of_data + * TRUE if the data from last batch, FALSE otherwise. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_be_send_cfgdata_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id, uint64_t batch_id, + Mgmtd__YangCfgDataReq **cfgdata_reqs, + size_t num_reqs, bool end_of_data); + +/* + * Send config apply request to backend client. + * + * adapter + * Backend adapter information. + * + * txn_id + * Unique transaction identifier. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter, + uint64_t txn_id); + +/* + * Dump backend adapter status to vty. + */ +extern void mgmt_be_adapter_status_write(struct vty *vty); + +/* + * Dump xpath registry for each backend client to vty. + */ +extern void mgmt_be_xpath_register_write(struct vty *vty); + +/** + * Lookup the clients which are subscribed to a given `xpath` + * and the way they are subscribed. + * + * Args: + * xpath - the xpath to check for subscription information. + * subscr_info - An array of uint indexed by client id + * each eleemnt holds the subscription info + * for that client. + */ +extern void mgmt_be_get_subscr_info_for_xpath( + const char *xpath, struct mgmt_be_client_subscr_info *subscr_info); + +/* + * Dump backend client information for a given xpath to vty. + */ +extern void mgmt_be_xpath_subscr_info_write(struct vty *vty, + const char *xpath); + +#endif /* _FRR_MGMTD_BE_ADAPTER_H_ */ diff --git a/mgmtd/mgmt_defines.h b/mgmtd/mgmt_defines.h new file mode 100644 index 000000000000..40fa67075d0e --- /dev/null +++ b/mgmtd/mgmt_defines.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD public defines. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_DEFINES_H +#define _FRR_MGMTD_DEFINES_H + +#include "yang.h" + +#define MGMTD_CLIENT_NAME_MAX_LEN 32 + +#define MGMTD_MAX_XPATH_LEN XPATH_MAXLEN + +#define MGMTD_MAX_YANG_VALUE_LEN YANG_VALUE_MAXLEN + +#define MGMTD_MAX_NUM_XPATH_REG 128 + +#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32 +#define MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH 8 + +enum mgmt_result { + MGMTD_SUCCESS = 0, + MGMTD_INVALID_PARAM, + MGMTD_INTERNAL_ERROR, + MGMTD_NO_CFG_CHANGES, + MGMTD_DS_LOCK_FAILED, + MGMTD_DS_UNLOCK_FAILED, + MGMTD_UNKNOWN_FAILURE +}; + +enum mgmt_fe_event { + MGMTD_FE_SERVER = 1, + MGMTD_FE_CONN_READ, + MGMTD_FE_CONN_WRITE, + MGMTD_FE_PROC_MSG +}; + +enum mgmt_be_event { + MGMTD_BE_SERVER = 1, + MGMTD_BE_CONN_INIT, + MGMTD_BE_CONN_READ, + MGMTD_BE_CONN_WRITE, + MGMTD_BE_PROC_MSG, + MGMTD_BE_SCHED_CFG_PREPARE, + MGMTD_BE_RESCHED_CFG_PREPARE, + MGMTD_BE_SCHED_CFG_APPLY, + MGMTD_BE_RESCHED_CFG_APPLY, +}; + +#define MGMTD_TXN_ID_NONE 0 + +#define MGMTD_TXN_BATCH_ID_NONE 0 + +#endif /* _FRR_MGMTD_DEFINES_H */ diff --git a/mgmtd/mgmt_ds.c b/mgmtd/mgmt_ds.c new file mode 100644 index 000000000000..a0e610c7c745 --- /dev/null +++ b/mgmtd/mgmt_ds.c @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Datastores + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#include "md5.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_memory.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_history.h" +#include "mgmtd/mgmt_txn.h" +#include "libyang/libyang.h" + +#define MGMTD_DS_DBG(fmt, ...) \ + DEBUGD(&mgmt_debug_ds, "DS: %s: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_DS_ERR(fmt, ...) \ + zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) + +struct mgmt_ds_ctx { + Mgmtd__DatastoreId ds_id; + + bool locked; + uint64_t vty_session_id; /* Owner of the lock or 0 */ + + bool config_ds; + + union { + struct nb_config *cfg_root; + struct lyd_node *dnode_root; + } root; +}; + +const char *mgmt_ds_names[MGMTD_DS_MAX_ID + 1] = { + MGMTD_DS_NAME_NONE, /* MGMTD_DS_NONE */ + MGMTD_DS_NAME_RUNNING, /* MGMTD_DS_RUNNING */ + MGMTD_DS_NAME_CANDIDATE, /* MGMTD_DS_CANDIDATE */ + MGMTD_DS_NAME_OPERATIONAL, /* MGMTD_DS_OPERATIONAL */ + "Unknown/Invalid", /* MGMTD_DS_ID_MAX */ +}; + +static struct mgmt_master *mgmt_ds_mm; +static struct mgmt_ds_ctx running, candidate, oper; + +/* Dump the data tree of the specified format in the file pointed by the path */ +static int mgmt_ds_dump_in_memory(struct mgmt_ds_ctx *ds_ctx, + const char *base_xpath, LYD_FORMAT format, + struct ly_out *out) +{ + struct lyd_node *root; + uint32_t options = 0; + + if (base_xpath[0] == '\0') + root = ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root; + else + root = yang_dnode_get(ds_ctx->config_ds + ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + base_xpath); + if (!root) + return -1; + + options = ds_ctx->config_ds ? LYD_PRINT_WD_TRIM : + LYD_PRINT_WD_EXPLICIT; + + if (base_xpath[0] == '\0') + lyd_print_all(out, root, format, options); + else + lyd_print_tree(out, root, format, options); + + return 0; +} + +static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src, + struct mgmt_ds_ctx *dst) +{ + if (!src || !dst) + return -1; + + MGMTD_DS_DBG("Replacing %s with %s", mgmt_ds_id2name(dst->ds_id), + mgmt_ds_id2name(src->ds_id)); + + if (src->config_ds && dst->config_ds) + nb_config_replace(dst->root.cfg_root, src->root.cfg_root, true); + else { + assert(!src->config_ds && !dst->config_ds); + if (dst->root.dnode_root) + yang_dnode_free(dst->root.dnode_root); + dst->root.dnode_root = yang_dnode_dup(src->root.dnode_root); + } + + if (src->ds_id == MGMTD_DS_CANDIDATE) { + /* + * Drop the changes in scratch-buffer. + */ + MGMTD_DS_DBG("Emptying Candidate Scratch buffer!"); + nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); + } + + return 0; +} + +static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src, + struct mgmt_ds_ctx *dst) +{ + int ret; + + if (!src || !dst) + return -1; + + MGMTD_DS_DBG("Merging DS %d with %d", dst->ds_id, src->ds_id); + if (src->config_ds && dst->config_ds) + ret = nb_config_merge(dst->root.cfg_root, src->root.cfg_root, + true); + else { + assert(!src->config_ds && !dst->config_ds); + ret = lyd_merge_siblings(&dst->root.dnode_root, + src->root.dnode_root, 0); + } + if (ret != 0) { + MGMTD_DS_ERR("merge failed with err: %d", ret); + return ret; + } + + if (src->ds_id == MGMTD_DS_CANDIDATE) { + /* + * Drop the changes in scratch-buffer. + */ + MGMTD_DS_DBG("Emptying Candidate Scratch buffer!"); + nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs); + } + + return 0; +} + +static int mgmt_ds_load_cfg_from_file(const char *filepath, + struct lyd_node **dnode) +{ + LY_ERR ret; + + *dnode = NULL; + ret = lyd_parse_data_path(ly_native_ctx, filepath, LYD_JSON, + LYD_PARSE_STRICT, 0, dnode); + + if (ret != LY_SUCCESS) { + if (*dnode) + yang_dnode_free(*dnode); + return -1; + } + + return 0; +} + +void mgmt_ds_reset_candidate(void) +{ + struct lyd_node *dnode = mm->candidate_ds->root.cfg_root->dnode; + + if (dnode) + yang_dnode_free(dnode); + + dnode = yang_dnode_new(ly_native_ctx, true); + mm->candidate_ds->root.cfg_root->dnode = dnode; +} + + +int mgmt_ds_init(struct mgmt_master *mm) +{ + if (mgmt_ds_mm || mm->running_ds || mm->candidate_ds || mm->oper_ds) + assert(!"MGMTD: Call ds_init only once!"); + + /* Use Running DS from NB module??? */ + if (!running_config) + assert(!"MGMTD: Call ds_init after frr_init only!"); + + running.root.cfg_root = running_config; + running.config_ds = true; + running.ds_id = MGMTD_DS_RUNNING; + + candidate.root.cfg_root = nb_config_dup(running.root.cfg_root); + candidate.config_ds = true; + candidate.ds_id = MGMTD_DS_CANDIDATE; + + /* + * Redirect lib/vty candidate-config datastore to the global candidate + * config Ds on the MGMTD process. + */ + vty_mgmt_candidate_config = candidate.root.cfg_root; + + oper.root.dnode_root = yang_dnode_new(ly_native_ctx, true); + oper.config_ds = false; + oper.ds_id = MGMTD_DS_OPERATIONAL; + + mm->running_ds = &running; + mm->candidate_ds = &candidate; + mm->oper_ds = &oper; + mgmt_ds_mm = mm; + + return 0; +} + +void mgmt_ds_destroy(void) +{ + nb_config_free(candidate.root.cfg_root); + candidate.root.cfg_root = NULL; + + yang_dnode_free(oper.root.dnode_root); + oper.root.dnode_root = NULL; +} + +struct mgmt_ds_ctx *mgmt_ds_get_ctx_by_id(struct mgmt_master *mm, + Mgmtd__DatastoreId ds_id) +{ + switch (ds_id) { + case MGMTD_DS_CANDIDATE: + return (mm->candidate_ds); + case MGMTD_DS_RUNNING: + return (mm->running_ds); + case MGMTD_DS_OPERATIONAL: + return (mm->oper_ds); + case MGMTD_DS_NONE: + case MGMTD__DATASTORE_ID__STARTUP_DS: + case _MGMTD__DATASTORE_ID_IS_INT_SIZE: + return 0; + } + + return 0; +} + +bool mgmt_ds_is_config(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return false; + + return ds_ctx->config_ds; +} + +bool mgmt_ds_is_locked(struct mgmt_ds_ctx *ds_ctx, uint64_t session_id) +{ + assert(ds_ctx); + return (ds_ctx->locked && ds_ctx->vty_session_id == session_id); +} + +int mgmt_ds_lock(struct mgmt_ds_ctx *ds_ctx, uint64_t session_id) +{ + assert(ds_ctx); + + if (ds_ctx->locked) + return EBUSY; + + ds_ctx->locked = true; + ds_ctx->vty_session_id = session_id; + return 0; +} + +void mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx) +{ + assert(ds_ctx); + if (!ds_ctx->locked) + zlog_warn( + "%s: WARNING: unlock on unlocked in DS:%s last session-id %" PRIu64, + __func__, mgmt_ds_id2name(ds_ctx->ds_id), + ds_ctx->vty_session_id); + ds_ctx->locked = 0; +} + +int mgmt_ds_copy_dss(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx, bool updt_cmt_rec) +{ + if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx, dst_ds_ctx) != 0) + return -1; + + if (updt_cmt_rec && dst_ds_ctx->ds_id == MGMTD_DS_RUNNING) + mgmt_history_new_record(dst_ds_ctx); + + return 0; +} + +int mgmt_ds_dump_ds_to_file(char *file_name, struct mgmt_ds_ctx *ds_ctx) +{ + struct ly_out *out; + int ret = 0; + + if (ly_out_new_filepath(file_name, &out) == LY_SUCCESS) { + ret = mgmt_ds_dump_in_memory(ds_ctx, "", LYD_JSON, out); + ly_out_free(out, NULL, 0); + } + + return ret; +} + +struct nb_config *mgmt_ds_get_nb_config(struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) + return NULL; + + return ds_ctx->config_ds ? ds_ctx->root.cfg_root : NULL; +} + +static int mgmt_walk_ds_nodes( + struct nb_config *root, const char *base_xpath, + struct lyd_node *base_dnode, + void (*mgmt_ds_node_iter_fn)(const char *xpath, struct lyd_node *node, + struct nb_node *nb_node, void *ctx), + void *ctx) +{ + /* this is 1k per recursion... */ + char xpath[MGMTD_MAX_XPATH_LEN]; + struct lyd_node *dnode; + struct nb_node *nbnode; + int ret = 0; + + assert(mgmt_ds_node_iter_fn); + + MGMTD_DS_DBG(" -- START: base xpath: '%s'", base_xpath); + + if (!base_dnode) + /* + * This function only returns the first node of a possible set + * of matches issuing a warning if more than 1 matches + */ + base_dnode = yang_dnode_get(root->dnode, base_xpath); + if (!base_dnode) + return -1; + + MGMTD_DS_DBG(" search base schema: '%s'", + lysc_path(base_dnode->schema, LYSC_PATH_LOG, xpath, + sizeof(xpath))); + + nbnode = (struct nb_node *)base_dnode->schema->priv; + (*mgmt_ds_node_iter_fn)(base_xpath, base_dnode, nbnode, ctx); + + /* + * If the base_xpath points to a leaf node we can skip the tree walk. + */ + if (base_dnode->schema->nodetype & LYD_NODE_TERM) + return 0; + + /* + * at this point the xpath matched this container node (or some parent + * and we're wildcard descending now) so by walking it's children we + * continue to change the meaning of an xpath regex to rather be a + * prefix matching path + */ + + LY_LIST_FOR (lyd_child(base_dnode), dnode) { + assert(dnode->schema && dnode->schema->priv); + + (void)lyd_path(dnode, LYD_PATH_STD, xpath, sizeof(xpath)); + + MGMTD_DS_DBG(" -- Child xpath: %s", xpath); + + ret = mgmt_walk_ds_nodes(root, xpath, dnode, + mgmt_ds_node_iter_fn, ctx); + if (ret != 0) + break; + } + + MGMTD_DS_DBG(" -- END: base xpath: '%s'", base_xpath); + + return ret; +} + +struct lyd_node *mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx *ds_ctx, + const char *xpath) +{ + if (!ds_ctx) + return NULL; + + return yang_dnode_get(ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + xpath); +} + +int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, const char *xpath) +{ + struct nb_node *nb_node; + struct lyd_node *dnode, *dep_dnode; + char dep_xpath[XPATH_MAXLEN]; + + if (!ds_ctx) + return -1; + + nb_node = nb_node_find(xpath); + + dnode = yang_dnode_get(ds_ctx->config_ds + ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + xpath); + + if (!dnode) + /* + * Return a special error code so the caller can choose + * whether to ignore it or not. + */ + return NB_ERR_NOT_FOUND; + /* destroy dependant */ + if (nb_node && nb_node->dep_cbs.get_dependant_xpath) { + nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath); + + dep_dnode = yang_dnode_get( + ds_ctx->config_ds ? ds_ctx->root.cfg_root->dnode + : ds_ctx->root.dnode_root, + dep_xpath); + if (dep_dnode) + lyd_free_tree(dep_dnode); + } + lyd_free_tree(dnode); + + return 0; +} + +int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *dst, + const char *file_path, bool merge) +{ + struct lyd_node *iter; + struct mgmt_ds_ctx parsed; + + if (!dst) + return -1; + + if (mgmt_ds_load_cfg_from_file(file_path, &iter) != 0) { + MGMTD_DS_ERR("Failed to load config from the file %s", + file_path); + return -1; + } + + parsed.root.cfg_root = nb_config_new(iter); + parsed.config_ds = true; + parsed.ds_id = dst->ds_id; + + if (merge) + mgmt_ds_merge_src_with_dst_ds(&parsed, dst); + else + mgmt_ds_replace_dst_with_src_ds(&parsed, dst); + + nb_config_free(parsed.root.cfg_root); + + return 0; +} + +int mgmt_ds_iter_data(Mgmtd__DatastoreId ds_id, struct nb_config *root, + const char *base_xpath, + void (*mgmt_ds_node_iter_fn)(const char *xpath, + struct lyd_node *node, + struct nb_node *nb_node, + void *ctx), + void *ctx) +{ + int ret = 0; + char xpath[MGMTD_MAX_XPATH_LEN]; + struct lyd_node *base_dnode = NULL; + struct lyd_node *node; + + if (!root) + return -1; + + strlcpy(xpath, base_xpath, sizeof(xpath)); + mgmt_remove_trailing_separator(xpath, '/'); + + /* + * mgmt_ds_iter_data is the only user of mgmt_walk_ds_nodes other than + * mgmt_walk_ds_nodes itself, so we can modify the API if we would like. + * Oper-state should be kept in mind though for the prefix walk + */ + + MGMTD_DS_DBG(" -- START DS walk for DSid: %d", ds_id); + + /* If the base_xpath is empty then crawl the sibblings */ + if (xpath[0] == 0) { + base_dnode = root->dnode; + + /* get first top-level sibling */ + while (base_dnode->parent) + base_dnode = lyd_parent(base_dnode); + + while (base_dnode->prev->next) + base_dnode = base_dnode->prev; + + LY_LIST_FOR (base_dnode, node) { + ret = mgmt_walk_ds_nodes(root, xpath, node, + mgmt_ds_node_iter_fn, ctx); + } + } else + ret = mgmt_walk_ds_nodes(root, xpath, base_dnode, + mgmt_ds_node_iter_fn, ctx); + + return ret; +} + +void mgmt_ds_dump_tree(struct vty *vty, struct mgmt_ds_ctx *ds_ctx, + const char *xpath, FILE *f, LYD_FORMAT format) +{ + struct ly_out *out; + char *str; + char base_xpath[MGMTD_MAX_XPATH_LEN] = {0}; + + if (!ds_ctx) { + vty_out(vty, " >>>>> Datastore Not Initialized!\n"); + return; + } + + if (xpath) { + strlcpy(base_xpath, xpath, MGMTD_MAX_XPATH_LEN); + mgmt_remove_trailing_separator(base_xpath, '/'); + } + + if (f) + ly_out_new_file(f, &out); + else + ly_out_new_memory(&str, 0, &out); + + mgmt_ds_dump_in_memory(ds_ctx, base_xpath, format, out); + + if (!f) + vty_out(vty, "%s\n", str); + + ly_out_free(out, NULL, 0); +} + +void mgmt_ds_status_write_one(struct vty *vty, struct mgmt_ds_ctx *ds_ctx) +{ + if (!ds_ctx) { + vty_out(vty, " >>>>> Datastore Not Initialized!\n"); + return; + } + + vty_out(vty, " DS: %s\n", mgmt_ds_id2name(ds_ctx->ds_id)); + vty_out(vty, " DS-Hndl: \t\t\t%p\n", ds_ctx); + vty_out(vty, " Config: \t\t\t%s\n", + ds_ctx->config_ds ? "True" : "False"); +} + +void mgmt_ds_status_write(struct vty *vty) +{ + vty_out(vty, "MGMTD Datastores\n"); + + mgmt_ds_status_write_one(vty, mgmt_ds_mm->running_ds); + + mgmt_ds_status_write_one(vty, mgmt_ds_mm->candidate_ds); + + mgmt_ds_status_write_one(vty, mgmt_ds_mm->oper_ds); +} diff --git a/mgmtd/mgmt_ds.h b/mgmtd/mgmt_ds.h new file mode 100644 index 000000000000..1cf4816027c3 --- /dev/null +++ b/mgmtd/mgmt_ds.h @@ -0,0 +1,336 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Datastores + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_DS_H_ +#define _FRR_MGMTD_DS_H_ + +#include "mgmt_fe_client.h" +#include "northbound.h" + +#include "mgmtd/mgmt_defines.h" +#include "mgmtd/mgmt_be_adapter.h" +#include "mgmtd/mgmt_fe_adapter.h" + +#define MGMTD_MAX_NUM_DSNODES_PER_BATCH 128 + +#define MGMTD_DS_NAME_MAX_LEN 32 +#define MGMTD_DS_NAME_NONE "none" +#define MGMTD_DS_NAME_RUNNING "running" +#define MGMTD_DS_NAME_CANDIDATE "candidate" +#define MGMTD_DS_NAME_OPERATIONAL "operational" + +#define FOREACH_MGMTD_DS_ID(id) \ + for ((id) = MGMTD_DS_NONE; (id) < MGMTD_DS_MAX_ID; (id)++) + +#define MGMTD_MAX_COMMIT_LIST 10 + +#define MGMTD_COMMIT_FILE_PATH DAEMON_DB_DIR "/commit-%s.json" +#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat" + +extern struct nb_config *running_config; + +struct mgmt_ds_ctx; + +/*************************************************************** + * Global data exported + ***************************************************************/ + +extern const char *mgmt_ds_names[MGMTD_DS_MAX_ID + 1]; + +/* + * Convert datastore ID to datastore name. + * + * id + * Datastore ID. + * + * Returns: + * Datastore name. + */ +static inline const char *mgmt_ds_id2name(Mgmtd__DatastoreId id) +{ + if (id > MGMTD_DS_MAX_ID) + id = MGMTD_DS_MAX_ID; + return mgmt_ds_names[id]; +} + +/* + * Convert datastore name to datastore ID. + * + * id + * Datastore name. + * + * Returns: + * Datastore ID. + */ +static inline Mgmtd__DatastoreId mgmt_ds_name2id(const char *name) +{ + Mgmtd__DatastoreId id; + + FOREACH_MGMTD_DS_ID (id) { + if (!strncmp(mgmt_ds_names[id], name, MGMTD_DS_NAME_MAX_LEN)) + return id; + } + + return MGMTD_DS_NONE; +} + +/* + * Convert datastore ID to datastore name. + * + * similar to above funtion. + */ +static inline Mgmtd__DatastoreId mgmt_get_ds_id_by_name(const char *ds_name) +{ + if (!strncmp(ds_name, "candidate", sizeof("candidate"))) + return MGMTD_DS_CANDIDATE; + else if (!strncmp(ds_name, "running", sizeof("running"))) + return MGMTD_DS_RUNNING; + else if (!strncmp(ds_name, "operational", sizeof("operational"))) + return MGMTD_DS_OPERATIONAL; + return MGMTD_DS_NONE; +} + +/* + * Appends trail wildcard '/' '*' to a given xpath. + * + * xpath + * YANG xpath. + * + * path_len + * xpath length. + */ +static inline void mgmt_xpath_append_trail_wildcard(char *xpath, + size_t *xpath_len) +{ + if (!xpath || !xpath_len) + return; + + if (!*xpath_len) + *xpath_len = strlen(xpath); + + if (*xpath_len > 2 && *xpath_len < MGMTD_MAX_XPATH_LEN - 2) { + if (xpath[*xpath_len - 1] == '/') { + xpath[*xpath_len] = '*'; + xpath[*xpath_len + 1] = 0; + (*xpath_len)++; + } else if (xpath[*xpath_len - 1] != '*') { + xpath[*xpath_len] = '/'; + xpath[*xpath_len + 1] = '*'; + xpath[*xpath_len + 2] = 0; + (*xpath_len) += 2; + } + } +} + +/* + * Removes trail wildcard '/' '*' from a given xpath. + * + * xpath + * YANG xpath. + * + * path_len + * xpath length. + */ +static inline void mgmt_xpath_remove_trail_wildcard(char *xpath, + size_t *xpath_len) +{ + if (!xpath || !xpath_len) + return; + + if (!*xpath_len) + *xpath_len = strlen(xpath); + + if (*xpath_len > 2 && xpath[*xpath_len - 2] == '/' + && xpath[*xpath_len - 1] == '*') { + xpath[*xpath_len - 2] = 0; + (*xpath_len) -= 2; + } +} + +/* Initialise datastore */ +extern int mgmt_ds_init(struct mgmt_master *cm); + +/* Destroy datastore */ +extern void mgmt_ds_destroy(void); + +/* + * Get datastore handler by ID + * + * mm + * Management master structure. + * + * ds_id + * Datastore ID. + * + * Returns: + * Datastore context (Holds info about ID, lock, root node etc). + */ +extern struct mgmt_ds_ctx *mgmt_ds_get_ctx_by_id(struct mgmt_master *mm, + Mgmtd__DatastoreId ds_id); + +/* + * Check if a given datastore is config ds + */ +extern bool mgmt_ds_is_config(struct mgmt_ds_ctx *ds_ctx); + +/* + * Check if a given datastore is locked by a given session + */ +extern bool mgmt_ds_is_locked(struct mgmt_ds_ctx *ds_ctx, uint64_t session_id); + +/* + * Acquire write lock to a ds given a ds_handle + */ +extern int mgmt_ds_lock(struct mgmt_ds_ctx *ds_ctx, uint64_t session_id); + +/* + * Remove a lock from ds given a ds_handle + */ +extern void mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx); + +/* + * Copy from source to destination datastore. + * + * src_ds + * Source datastore handle (ds to be copied from). + * + * dst_ds + * Destination datastore handle (ds to be copied to). + * + * update_cmd_rec + * TRUE if need to update commit record, FALSE otherwise. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_copy_dss(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx, + bool update_cmt_rec); + +/* + * Fetch northbound configuration for a given datastore context. + */ +extern struct nb_config *mgmt_ds_get_nb_config(struct mgmt_ds_ctx *ds_ctx); + +/* + * Find YANG data node given a datastore handle YANG xpath. + */ +extern struct lyd_node * +mgmt_ds_find_data_node_by_xpath(struct mgmt_ds_ctx *ds_ctx, + const char *xpath); + +/* + * Delete YANG data node given a datastore handle and YANG xpath. + */ +extern int mgmt_ds_delete_data_nodes(struct mgmt_ds_ctx *ds_ctx, + const char *xpath); + +/* + * Iterate over datastore data. + * + * ds_id + * Datastore ID.. + * + * root + * The root of the tree to iterate over. + * + * base_xpath + * Base YANG xpath from where needs to be iterated. + * + * iter_fn + * function that will be called during each iteration. + * + * ctx + * User defined opaque value normally used to pass + * reference to some user private context that will + * be passed to the iterator function provided in + * 'iter_fn'. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_iter_data( + Mgmtd__DatastoreId ds_id, struct nb_config *root, + const char *base_xpath, + void (*mgmt_ds_node_iter_fn)(const char *xpath, struct lyd_node *node, + struct nb_node *nb_node, void *ctx), + void *ctx); + +/* + * Load config to datastore from a file. + * + * ds_ctx + * Datastore context. + * + * file_path + * File path of the configuration file. + * + * merge + * TRUE if you want to merge with existing config, + * FALSE if you want to replace with existing config + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_load_config_from_file(struct mgmt_ds_ctx *ds_ctx, + const char *file_path, bool merge); + +/* + * Dump the data tree to a file with JSON/XML format. + * + * vty + * VTY context. + * + * ds_ctx + * Datastore context. + * + * xpath + * Base YANG xpath from where data needs to be dumped. + * + * f + * File pointer to where data to be dumped. + * + * format + * JSON/XML + */ +extern void mgmt_ds_dump_tree(struct vty *vty, struct mgmt_ds_ctx *ds_ctx, + const char *xpath, FILE *f, LYD_FORMAT format); + +/* + * Dump the complete data tree to a file with JSON format. + * + * file_name + * File path to where data to be dumped. + * + * ds + * Datastore context. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_ds_dump_ds_to_file(char *file_name, + struct mgmt_ds_ctx *ds_ctx); + +/* + * Dump information about specific datastore. + */ +extern void mgmt_ds_status_write_one(struct vty *vty, + struct mgmt_ds_ctx *ds_ctx); + +/* + * Dump information about all the datastores. + */ +extern void mgmt_ds_status_write(struct vty *vty); + + +/* + * Reset the candidate DS to empty state + */ +void mgmt_ds_reset_candidate(void); + +#endif /* _FRR_MGMTD_DS_H_ */ diff --git a/mgmtd/mgmt_fe_adapter.c b/mgmtd/mgmt_fe_adapter.c new file mode 100644 index 000000000000..a4bb33d61be6 --- /dev/null +++ b/mgmtd/mgmt_fe_adapter.c @@ -0,0 +1,1403 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Frontend Client Connection Adapter + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ + +#include <zebra.h> +#include "sockopt.h" +#include "network.h" +#include "libfrr.h" +#include "mgmt_fe_client.h" +#include "mgmt_msg.h" +#include "mgmt_pb.h" +#include "hash.h" +#include "jhash.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_memory.h" +#include "mgmtd/mgmt_fe_adapter.h" + +#define MGMTD_FE_ADAPTER_DBG(fmt, ...) \ + DEBUGD(&mgmt_debug_fe, "FE-ADAPTER: %s: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_FE_ADAPTER_ERR(fmt, ...) \ + zlog_err("FE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__) + +#define FOREACH_ADAPTER_IN_LIST(adapter) \ + frr_each_safe (mgmt_fe_adapters, &mgmt_fe_adapters, (adapter)) + +enum mgmt_session_event { + MGMTD_FE_SESSION_CFG_TXN_CLNUP = 1, + MGMTD_FE_SESSION_SHOW_TXN_CLNUP, +}; + +struct mgmt_fe_session_ctx { + struct mgmt_fe_client_adapter *adapter; + uint64_t session_id; + uint64_t client_id; + uint64_t txn_id; + uint64_t cfg_txn_id; + uint8_t ds_locked[MGMTD_DS_MAX_ID]; + struct event *proc_cfg_txn_clnp; + struct event *proc_show_txn_clnp; + + struct mgmt_fe_sessions_item list_linkage; +}; + +DECLARE_LIST(mgmt_fe_sessions, struct mgmt_fe_session_ctx, list_linkage); + +#define FOREACH_SESSION_IN_LIST(adapter, session) \ + frr_each_safe (mgmt_fe_sessions, &(adapter)->fe_sessions, (session)) + +static struct event_loop *mgmt_loop; +static struct msg_server mgmt_fe_server = {.fd = -1}; + +static struct mgmt_fe_adapters_head mgmt_fe_adapters; + +static struct hash *mgmt_fe_sessions; +static uint64_t mgmt_fe_next_session_id; + +/* Forward declarations */ +static void +mgmt_fe_session_register_event(struct mgmt_fe_session_ctx *session, + enum mgmt_session_event event); + +static int +mgmt_fe_session_write_lock_ds(Mgmtd__DatastoreId ds_id, + struct mgmt_ds_ctx *ds_ctx, + struct mgmt_fe_session_ctx *session) +{ + if (session->ds_locked[ds_id]) + zlog_warn("multiple lock taken by session-id: %" PRIu64 + " on DS:%s", + session->session_id, mgmt_ds_id2name(ds_id)); + else { + if (mgmt_ds_lock(ds_ctx, session->session_id)) { + MGMTD_FE_ADAPTER_DBG( + "Failed to lock the DS:%s for session-id: %" PRIu64 + " from %s!", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); + return -1; + } + + session->ds_locked[ds_id] = true; + MGMTD_FE_ADAPTER_DBG( + "Write-Locked the DS:%s for session-id: %" PRIu64 + " from %s", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); + } + + return 0; +} + +static void mgmt_fe_session_unlock_ds(Mgmtd__DatastoreId ds_id, + struct mgmt_ds_ctx *ds_ctx, + struct mgmt_fe_session_ctx *session) +{ + if (!session->ds_locked[ds_id]) + zlog_warn("unlock unlocked by session-id: %" PRIu64 " on DS:%s", + session->session_id, mgmt_ds_id2name(ds_id)); + + session->ds_locked[ds_id] = false; + mgmt_ds_unlock(ds_ctx); + MGMTD_FE_ADAPTER_DBG( + "Unlocked DS:%s write-locked earlier by session-id: %" PRIu64 + " from %s", + mgmt_ds_id2name(ds_id), session->session_id, + session->adapter->name); +} + +static void +mgmt_fe_session_cfg_txn_cleanup(struct mgmt_fe_session_ctx *session) +{ + /* + * Ensure any uncommitted changes in Candidate DS + * is discarded. + */ + mgmt_ds_copy_dss(mm->running_ds, mm->candidate_ds, false); + + /* + * Destroy the actual transaction created earlier. + */ + if (session->cfg_txn_id != MGMTD_TXN_ID_NONE) + mgmt_destroy_txn(&session->cfg_txn_id); +} + +static void +mgmt_fe_session_show_txn_cleanup(struct mgmt_fe_session_ctx *session) +{ + /* + * Destroy the transaction created recently. + */ + if (session->txn_id != MGMTD_TXN_ID_NONE) + mgmt_destroy_txn(&session->txn_id); +} + +static void +mgmt_fe_adapter_compute_set_cfg_timers(struct mgmt_setcfg_stats *setcfg_stats) +{ + setcfg_stats->last_exec_tm = timeval_elapsed(setcfg_stats->last_end, + setcfg_stats->last_start); + if (setcfg_stats->last_exec_tm > setcfg_stats->max_tm) + setcfg_stats->max_tm = setcfg_stats->last_exec_tm; + + if (setcfg_stats->last_exec_tm < setcfg_stats->min_tm) + setcfg_stats->min_tm = setcfg_stats->last_exec_tm; + + setcfg_stats->avg_tm = + (((setcfg_stats->avg_tm * (setcfg_stats->set_cfg_count - 1)) + + setcfg_stats->last_exec_tm) + / setcfg_stats->set_cfg_count); +} + +static void +mgmt_fe_session_compute_commit_timers(struct mgmt_commit_stats *cmt_stats) +{ + cmt_stats->last_exec_tm = + timeval_elapsed(cmt_stats->last_end, cmt_stats->last_start); + if (cmt_stats->last_exec_tm > cmt_stats->max_tm) { + cmt_stats->max_tm = cmt_stats->last_exec_tm; + cmt_stats->max_batch_cnt = cmt_stats->last_batch_cnt; + } + + if (cmt_stats->last_exec_tm < cmt_stats->min_tm) { + cmt_stats->min_tm = cmt_stats->last_exec_tm; + cmt_stats->min_batch_cnt = cmt_stats->last_batch_cnt; + } +} + +static void mgmt_fe_cleanup_session(struct mgmt_fe_session_ctx **sessionp) +{ + Mgmtd__DatastoreId ds_id; + struct mgmt_ds_ctx *ds_ctx; + struct mgmt_fe_session_ctx *session = *sessionp; + + if (session->adapter) { + mgmt_fe_session_cfg_txn_cleanup(session); + mgmt_fe_session_show_txn_cleanup(session); + for (ds_id = 0; ds_id < MGMTD_DS_MAX_ID; ds_id++) { + ds_ctx = mgmt_ds_get_ctx_by_id(mm, ds_id); + if (ds_ctx && session->ds_locked[ds_id]) + mgmt_fe_session_unlock_ds(ds_id, ds_ctx, + session); + } + mgmt_fe_sessions_del(&session->adapter->fe_sessions, session); + assert(session->adapter->refcount > 1); + mgmt_fe_adapter_unlock(&session->adapter); + } + + hash_release(mgmt_fe_sessions, session); + XFREE(MTYPE_MGMTD_FE_SESSION, session); + *sessionp = NULL; +} + +static struct mgmt_fe_session_ctx * +mgmt_fe_find_session_by_client_id(struct mgmt_fe_client_adapter *adapter, + uint64_t client_id) +{ + struct mgmt_fe_session_ctx *session; + + FOREACH_SESSION_IN_LIST (adapter, session) { + if (session->client_id == client_id) { + MGMTD_FE_ADAPTER_DBG("Found session-id %" PRIu64 + " using client-id %" PRIu64, + session->session_id, client_id); + return session; + } + } + MGMTD_FE_ADAPTER_DBG("Session not found using client-id %" PRIu64, + client_id); + return NULL; +} + +static unsigned int mgmt_fe_session_hash_key(const void *data) +{ + const struct mgmt_fe_session_ctx *session = data; + + return jhash2((uint32_t *) &session->session_id, + sizeof(session->session_id) / sizeof(uint32_t), 0); +} + +static bool mgmt_fe_session_hash_cmp(const void *d1, const void *d2) +{ + const struct mgmt_fe_session_ctx *session1 = d1; + const struct mgmt_fe_session_ctx *session2 = d2; + + return (session1->session_id == session2->session_id); +} + +static inline struct mgmt_fe_session_ctx * +mgmt_session_id2ctx(uint64_t session_id) +{ + struct mgmt_fe_session_ctx key = {0}; + struct mgmt_fe_session_ctx *session; + + if (!mgmt_fe_sessions) + return NULL; + + key.session_id = session_id; + session = hash_lookup(mgmt_fe_sessions, &key); + + return session; +} + +static struct mgmt_fe_session_ctx * +mgmt_fe_create_session(struct mgmt_fe_client_adapter *adapter, + uint64_t client_id) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_fe_find_session_by_client_id(adapter, client_id); + if (session) + mgmt_fe_cleanup_session(&session); + + session = XCALLOC(MTYPE_MGMTD_FE_SESSION, + sizeof(struct mgmt_fe_session_ctx)); + assert(session); + session->client_id = client_id; + session->adapter = adapter; + session->txn_id = MGMTD_TXN_ID_NONE; + session->cfg_txn_id = MGMTD_TXN_ID_NONE; + mgmt_fe_adapter_lock(adapter); + mgmt_fe_sessions_add_tail(&adapter->fe_sessions, session); + if (!mgmt_fe_next_session_id) + mgmt_fe_next_session_id++; + session->session_id = mgmt_fe_next_session_id++; + hash_get(mgmt_fe_sessions, session, hash_alloc_intern); + + return session; +} + +static int fe_adapter_send_msg(struct mgmt_fe_client_adapter *adapter, + Mgmtd__FeMessage *fe_msg, bool short_circuit_ok) +{ + return msg_conn_send_msg( + adapter->conn, MGMT_MSG_VERSION_PROTOBUF, fe_msg, + mgmtd__fe_message__get_packed_size(fe_msg), + (size_t(*)(void *, void *))mgmtd__fe_message__pack, + short_circuit_ok); +} + +static int fe_adapter_send_session_reply(struct mgmt_fe_client_adapter *adapter, + struct mgmt_fe_session_ctx *session, + bool create, bool success) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeSessionReply session_reply; + + mgmtd__fe_session_reply__init(&session_reply); + session_reply.create = create; + if (create) { + session_reply.has_client_conn_id = 1; + session_reply.client_conn_id = session->client_id; + } + session_reply.session_id = session->session_id; + session_reply.success = success; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY; + fe_msg.session_reply = &session_reply; + + MGMTD_FE_ADAPTER_DBG( + "Sending SESSION_REPLY message to MGMTD Frontend client '%s'", + adapter->name); + + return fe_adapter_send_msg(adapter, &fe_msg, true); +} + +static int fe_adapter_send_lockds_reply(struct mgmt_fe_session_ctx *session, + Mgmtd__DatastoreId ds_id, + uint64_t req_id, bool lock_ds, + bool success, const char *error_if_any) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeLockDsReply lockds_reply; + bool scok = session->adapter->conn->is_short_circuit; + + assert(session->adapter); + + mgmtd__fe_lock_ds_reply__init(&lockds_reply); + lockds_reply.session_id = session->session_id; + lockds_reply.ds_id = ds_id; + lockds_reply.req_id = req_id; + lockds_reply.lock = lock_ds; + lockds_reply.success = success; + if (error_if_any) + lockds_reply.error_if_any = (char *)error_if_any; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY; + fe_msg.lockds_reply = &lockds_reply; + + MGMTD_FE_ADAPTER_DBG( + "Sending LOCK_DS_REPLY message to MGMTD Frontend client '%s' scok: %d", + session->adapter->name, scok); + + return fe_adapter_send_msg(session->adapter, &fe_msg, scok); +} + +static int fe_adapter_send_set_cfg_reply(struct mgmt_fe_session_ctx *session, + Mgmtd__DatastoreId ds_id, + uint64_t req_id, bool success, + const char *error_if_any, + bool implicit_commit) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeSetConfigReply setcfg_reply; + + assert(session->adapter); + + if (implicit_commit && session->cfg_txn_id) + mgmt_fe_session_register_event( + session, MGMTD_FE_SESSION_CFG_TXN_CLNUP); + + mgmtd__fe_set_config_reply__init(&setcfg_reply); + setcfg_reply.session_id = session->session_id; + setcfg_reply.ds_id = ds_id; + setcfg_reply.req_id = req_id; + setcfg_reply.success = success; + setcfg_reply.implicit_commit = implicit_commit; + if (error_if_any) + setcfg_reply.error_if_any = (char *)error_if_any; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY; + fe_msg.setcfg_reply = &setcfg_reply; + + MGMTD_FE_ADAPTER_DBG( + "Sending SETCFG_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); + + if (implicit_commit) { + if (mm->perf_stats_en) + gettimeofday(&session->adapter->cmt_stats.last_end, + NULL); + mgmt_fe_session_compute_commit_timers( + &session->adapter->cmt_stats); + } + + if (mm->perf_stats_en) + gettimeofday(&session->adapter->setcfg_stats.last_end, NULL); + mgmt_fe_adapter_compute_set_cfg_timers(&session->adapter->setcfg_stats); + + return fe_adapter_send_msg(session->adapter, &fe_msg, false); +} + +static int fe_adapter_send_commit_cfg_reply( + struct mgmt_fe_session_ctx *session, Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dst_ds_id, uint64_t req_id, enum mgmt_result result, + bool validate_only, const char *error_if_any) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeCommitConfigReply commcfg_reply; + + assert(session->adapter); + + mgmtd__fe_commit_config_reply__init(&commcfg_reply); + commcfg_reply.session_id = session->session_id; + commcfg_reply.src_ds_id = src_ds_id; + commcfg_reply.dst_ds_id = dst_ds_id; + commcfg_reply.req_id = req_id; + commcfg_reply.success = + (result == MGMTD_SUCCESS || result == MGMTD_NO_CFG_CHANGES) + ? true + : false; + commcfg_reply.validate_only = validate_only; + if (error_if_any) + commcfg_reply.error_if_any = (char *)error_if_any; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY; + fe_msg.commcfg_reply = &commcfg_reply; + + MGMTD_FE_ADAPTER_DBG( + "Sending COMMIT_CONFIG_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); + + /* + * Cleanup the CONFIG transaction associated with this session. + */ + if (session->cfg_txn_id + && ((result == MGMTD_SUCCESS && !validate_only) + || (result == MGMTD_NO_CFG_CHANGES))) + mgmt_fe_session_register_event( + session, MGMTD_FE_SESSION_CFG_TXN_CLNUP); + + if (mm->perf_stats_en) + gettimeofday(&session->adapter->cmt_stats.last_end, NULL); + mgmt_fe_session_compute_commit_timers(&session->adapter->cmt_stats); + return fe_adapter_send_msg(session->adapter, &fe_msg, false); +} + +static int fe_adapter_send_get_reply(struct mgmt_fe_session_ctx *session, + Mgmtd__DatastoreId ds_id, uint64_t req_id, + bool success, Mgmtd__YangDataReply *data, + const char *error_if_any) +{ + Mgmtd__FeMessage fe_msg; + Mgmtd__FeGetReply get_reply; + + assert(session->adapter); + + mgmtd__fe_get_reply__init(&get_reply); + get_reply.session_id = session->session_id; + get_reply.ds_id = ds_id; + get_reply.req_id = req_id; + get_reply.success = success; + get_reply.data = data; + if (error_if_any) + get_reply.error_if_any = (char *)error_if_any; + + mgmtd__fe_message__init(&fe_msg); + fe_msg.message_case = MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY; + fe_msg.get_reply = &get_reply; + + MGMTD_FE_ADAPTER_DBG("Sending GET_REPLY message to MGMTD Frontend client '%s'", + session->adapter->name); + + /* + * Cleanup the SHOW transaction associated with this session. + */ + if (session->txn_id && (!success || (data && data->next_indx < 0))) + mgmt_fe_session_register_event(session, + MGMTD_FE_SESSION_SHOW_TXN_CLNUP); + + return fe_adapter_send_msg(session->adapter, &fe_msg, false); +} + +static void mgmt_fe_session_cfg_txn_clnup(struct event *thread) +{ + struct mgmt_fe_session_ctx *session; + + session = (struct mgmt_fe_session_ctx *)EVENT_ARG(thread); + + mgmt_fe_session_cfg_txn_cleanup(session); +} + +static void mgmt_fe_session_show_txn_clnup(struct event *thread) +{ + struct mgmt_fe_session_ctx *session; + + session = (struct mgmt_fe_session_ctx *)EVENT_ARG(thread); + + mgmt_fe_session_show_txn_cleanup(session); +} + +static void +mgmt_fe_session_register_event(struct mgmt_fe_session_ctx *session, + enum mgmt_session_event event) +{ + struct timeval tv = {.tv_sec = 0, + .tv_usec = MGMTD_FE_MSG_PROC_DELAY_USEC}; + + switch (event) { + case MGMTD_FE_SESSION_CFG_TXN_CLNUP: + event_add_timer_tv(mgmt_loop, mgmt_fe_session_cfg_txn_clnup, + session, &tv, &session->proc_cfg_txn_clnp); + break; + case MGMTD_FE_SESSION_SHOW_TXN_CLNUP: + event_add_timer_tv(mgmt_loop, mgmt_fe_session_show_txn_clnup, + session, &tv, &session->proc_show_txn_clnp); + break; + } +} + +static struct mgmt_fe_client_adapter * +mgmt_fe_find_adapter_by_fd(int conn_fd) +{ + struct mgmt_fe_client_adapter *adapter; + + FOREACH_ADAPTER_IN_LIST (adapter) { + if (adapter->conn->fd == conn_fd) + return adapter; + } + + return NULL; +} + +static void mgmt_fe_adapter_delete(struct mgmt_fe_client_adapter *adapter) +{ + struct mgmt_fe_session_ctx *session; + MGMTD_FE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name); + + /* TODO: notify about client disconnect for appropriate cleanup */ + FOREACH_SESSION_IN_LIST (adapter, session) + mgmt_fe_cleanup_session(&session); + mgmt_fe_sessions_fini(&adapter->fe_sessions); + + assert(adapter->refcount == 1); + mgmt_fe_adapter_unlock(&adapter); +} + +static int mgmt_fe_adapter_notify_disconnect(struct msg_conn *conn) +{ + struct mgmt_fe_client_adapter *adapter = conn->user; + + MGMTD_FE_ADAPTER_DBG("notify disconnect for client adapter '%s'", + adapter->name); + + mgmt_fe_adapter_delete(adapter); + + return 0; +} + +/* + * Purge any old connections that share the same client name with `adapter` + */ +static void +mgmt_fe_adapter_cleanup_old_conn(struct mgmt_fe_client_adapter *adapter) +{ + struct mgmt_fe_client_adapter *old; + + FOREACH_ADAPTER_IN_LIST (old) { + if (old == adapter) + continue; + if (strncmp(adapter->name, old->name, sizeof(adapter->name))) + continue; + + MGMTD_FE_ADAPTER_DBG( + "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)", + adapter->name, adapter->conn->fd, + old->conn->fd); + msg_conn_disconnect(old->conn, false); + } +} + +static int +mgmt_fe_session_handle_lockds_req_msg(struct mgmt_fe_session_ctx *session, + Mgmtd__FeLockDsReq *lockds_req) +{ + struct mgmt_ds_ctx *ds_ctx; + + if (lockds_req->ds_id != MGMTD_DS_CANDIDATE && + lockds_req->ds_id != MGMTD_DS_RUNNING) { + fe_adapter_send_lockds_reply( + session, lockds_req->ds_id, lockds_req->req_id, + lockds_req->lock, false, + "Lock/Unlock on DS other than candidate or running DS not supported"); + return -1; + } + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, lockds_req->ds_id); + if (!ds_ctx) { + fe_adapter_send_lockds_reply(session, lockds_req->ds_id, + lockds_req->req_id, + lockds_req->lock, false, + "Failed to retrieve handle for DS!"); + return -1; + } + + if (lockds_req->lock) { + if (mgmt_fe_session_write_lock_ds(lockds_req->ds_id, + ds_ctx, session) + != 0) { + fe_adapter_send_lockds_reply( + session, lockds_req->ds_id, lockds_req->req_id, + lockds_req->lock, false, + "Lock already taken on DS by another session!"); + return -1; + } + } else { + if (!session->ds_locked[lockds_req->ds_id]) { + fe_adapter_send_lockds_reply( + session, lockds_req->ds_id, lockds_req->req_id, + lockds_req->lock, false, + "Lock on DS was not taken by this session!"); + return 0; + } + + mgmt_fe_session_unlock_ds(lockds_req->ds_id, ds_ctx, session); + } + + if (fe_adapter_send_lockds_reply(session, lockds_req->ds_id, + lockds_req->req_id, lockds_req->lock, + true, NULL) != 0) { + MGMTD_FE_ADAPTER_DBG( + "Failed to send LOCK_DS_REPLY for DS %u session-id: %" PRIu64 + " from %s", + lockds_req->ds_id, session->session_id, + session->adapter->name); + } + + return 0; +} + +/* + * TODO: this function has too many conditionals relating to complex error + * conditions. It needs to be simplified and these complex error conditions + * probably need to just disconnect the client with a suitably loud log message. + */ +static int +mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session, + Mgmtd__FeSetConfigReq *setcfg_req) +{ + struct mgmt_ds_ctx *ds_ctx, *dst_ds_ctx = NULL; + bool txn_created = false; + + if (mm->perf_stats_en) + gettimeofday(&session->adapter->setcfg_stats.last_start, NULL); + + /* MGMTD currently only supports editing the candidate DS. */ + if (setcfg_req->ds_id != MGMTD_DS_CANDIDATE) { + fe_adapter_send_set_cfg_reply( + session, setcfg_req->ds_id, setcfg_req->req_id, false, + "Set-Config on datastores other than Candidate DS not supported", + setcfg_req->implicit_commit); + return 0; + } + ds_ctx = mgmt_ds_get_ctx_by_id(mm, setcfg_req->ds_id); + assert(ds_ctx); + + /* MGMTD currently only supports targetting the running DS. */ + if (setcfg_req->implicit_commit && + setcfg_req->commit_ds_id != MGMTD_DS_RUNNING) { + fe_adapter_send_set_cfg_reply( + session, setcfg_req->ds_id, setcfg_req->req_id, false, + "Implicit commit on datastores other than running DS not supported", + setcfg_req->implicit_commit); + return 0; + } + dst_ds_ctx = mgmt_ds_get_ctx_by_id(mm, setcfg_req->commit_ds_id); + assert(dst_ds_ctx); + + /* User should have write lock to change the DS */ + if (!session->ds_locked[setcfg_req->ds_id]) { + fe_adapter_send_set_cfg_reply(session, setcfg_req->ds_id, + setcfg_req->req_id, false, + "Candidate DS is not locked", + setcfg_req->implicit_commit); + return 0; + } + + if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { + /* as we have the lock no-one else should have a config txn */ + assert(mgmt_config_txn_in_progress() == MGMTD_SESSION_ID_NONE); + + /* Start a CONFIG Transaction (if not started already) */ + session->cfg_txn_id = mgmt_create_txn(session->session_id, + MGMTD_TXN_TYPE_CONFIG); + if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) { + fe_adapter_send_set_cfg_reply( + session, setcfg_req->ds_id, setcfg_req->req_id, + false, + "Failed to create a Configuration session!", + setcfg_req->implicit_commit); + return 0; + } + txn_created = true; + + MGMTD_FE_ADAPTER_DBG("Created new Config txn-id: %" PRIu64 + " for session-id %" PRIu64, + session->cfg_txn_id, session->session_id); + } else { + MGMTD_FE_ADAPTER_DBG("Config txn-id: %" PRIu64 + " for session-id: %" PRIu64 + " already created", + session->cfg_txn_id, session->session_id); + + if (setcfg_req->implicit_commit) { + /* + * In this scenario need to skip cleanup of the txn, + * so setting implicit commit to false. + */ + fe_adapter_send_set_cfg_reply( + session, setcfg_req->ds_id, setcfg_req->req_id, + false, + "A Configuration transaction is already in progress!", + false); + return 0; + } + } + + /* Create the SETConfig request under the transaction. */ + if (mgmt_txn_send_set_config_req(session->cfg_txn_id, setcfg_req->req_id, + setcfg_req->ds_id, ds_ctx, + setcfg_req->data, setcfg_req->n_data, + setcfg_req->implicit_commit, + setcfg_req->commit_ds_id, + dst_ds_ctx) != 0) { + fe_adapter_send_set_cfg_reply(session, setcfg_req->ds_id, + setcfg_req->req_id, false, + "Request processing for SET-CONFIG failed!", + setcfg_req->implicit_commit); + + /* delete transaction if we just created it */ + if (txn_created) + mgmt_destroy_txn(&session->cfg_txn_id); + } + + return 0; +} + +static int mgmt_fe_session_handle_get_req_msg(struct mgmt_fe_session_ctx *session, + Mgmtd__FeGetReq *get_req) +{ + struct mgmt_ds_ctx *ds_ctx; + struct nb_config *cfg_root = NULL; + Mgmtd__DatastoreId ds_id = get_req->ds_id; + uint64_t req_id = get_req->req_id; + bool is_cfg = get_req->config; + bool ds_ok = true; + + if (is_cfg && ds_id != MGMTD_DS_CANDIDATE && ds_id != MGMTD_DS_RUNNING) + ds_ok = false; + else if (!is_cfg && ds_id != MGMTD_DS_OPERATIONAL) + ds_ok = false; + if (!ds_ok) { + fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, + "get-req on unsupported datastore"); + return 0; + } + ds_ctx = mgmt_ds_get_ctx_by_id(mm, ds_id); + assert(ds_ctx); + + if (session->txn_id == MGMTD_TXN_ID_NONE) { + /* + * Start a SHOW Transaction (if not started already) + */ + session->txn_id = mgmt_create_txn(session->session_id, + MGMTD_TXN_TYPE_SHOW); + if (session->txn_id == MGMTD_SESSION_ID_NONE) { + fe_adapter_send_get_reply(session, ds_id, req_id, false, + NULL, + "Failed to create a Show transaction!"); + return -1; + } + + MGMTD_FE_ADAPTER_DBG("Created new show txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); + } else { + fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, + "Request processing for GET failed!"); + MGMTD_FE_ADAPTER_DBG("Transaction in progress txn-id: %" PRIu64 + " for session-id: %" PRIu64, + session->txn_id, session->session_id); + return -1; + } + + /* + * Get a copy of the datastore config root, avoids locking. + */ + if (is_cfg) + cfg_root = nb_config_dup(mgmt_ds_get_nb_config(ds_ctx)); + + /* + * Create a GET request under the transaction. + */ + if (mgmt_txn_send_get_req(session->txn_id, req_id, ds_id, cfg_root, + get_req->data, get_req->n_data)) { + fe_adapter_send_get_reply(session, ds_id, req_id, false, NULL, + "Request processing for GET failed!"); + + goto failed; + } + + return 0; +failed: + if (cfg_root) + nb_config_free(cfg_root); + /* + * Destroy the transaction created recently. + */ + if (session->txn_id != MGMTD_TXN_ID_NONE) + mgmt_destroy_txn(&session->txn_id); + + return -1; +} + + +static int mgmt_fe_session_handle_commit_config_req_msg( + struct mgmt_fe_session_ctx *session, + Mgmtd__FeCommitConfigReq *commcfg_req) +{ + struct mgmt_ds_ctx *src_ds_ctx, *dst_ds_ctx; + + if (mm->perf_stats_en) + gettimeofday(&session->adapter->cmt_stats.last_start, NULL); + session->adapter->cmt_stats.commit_cnt++; + + /* Validate source and dest DS */ + if (commcfg_req->src_ds_id != MGMTD_DS_CANDIDATE || + commcfg_req->dst_ds_id != MGMTD_DS_RUNNING) { + fe_adapter_send_commit_cfg_reply( + session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, + commcfg_req->req_id, MGMTD_INTERNAL_ERROR, + commcfg_req->validate_only, + "Source/Dest for commit must be candidate/running DS"); + return 0; + } + src_ds_ctx = mgmt_ds_get_ctx_by_id(mm, commcfg_req->src_ds_id); + assert(src_ds_ctx); + dst_ds_ctx = mgmt_ds_get_ctx_by_id(mm, commcfg_req->dst_ds_id); + assert(dst_ds_ctx); + + /* User should have lock on both source and dest DS */ + if (!session->ds_locked[commcfg_req->dst_ds_id] || + !session->ds_locked[commcfg_req->src_ds_id]) { + fe_adapter_send_commit_cfg_reply( + session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, + commcfg_req->req_id, MGMTD_DS_LOCK_FAILED, + commcfg_req->validate_only, + "Commit requires lock on candidate and/or running DS"); + return 0; + } + + if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) { + /* + * Start a CONFIG Transaction (if not started already) + */ + session->cfg_txn_id = mgmt_create_txn(session->session_id, + MGMTD_TXN_TYPE_CONFIG); + if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) { + fe_adapter_send_commit_cfg_reply( + session, commcfg_req->src_ds_id, + commcfg_req->dst_ds_id, commcfg_req->req_id, + MGMTD_INTERNAL_ERROR, commcfg_req->validate_only, + "Failed to create a Configuration session!"); + return 0; + } + MGMTD_FE_ADAPTER_DBG("Created txn-id: %" PRIu64 + " for session-id %" PRIu64 + " for COMMIT-CFG-REQ", + session->cfg_txn_id, session->session_id); + } + + /* + * Create COMMITConfig request under the transaction + */ + if (mgmt_txn_send_commit_config_req( + session->cfg_txn_id, commcfg_req->req_id, + commcfg_req->src_ds_id, src_ds_ctx, commcfg_req->dst_ds_id, + dst_ds_ctx, commcfg_req->validate_only, commcfg_req->abort, + false) != 0) { + fe_adapter_send_commit_cfg_reply( + session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id, + commcfg_req->req_id, MGMTD_INTERNAL_ERROR, + commcfg_req->validate_only, + "Request processing for COMMIT-CONFIG failed!"); + return 0; + } + + return 0; +} + +static int +mgmt_fe_adapter_handle_msg(struct mgmt_fe_client_adapter *adapter, + Mgmtd__FeMessage *fe_msg) +{ + struct mgmt_fe_session_ctx *session; + + /* + * protobuf-c adds a max size enum with an internal, and changing by + * version, name; cast to an int to avoid unhandled enum warnings + */ + switch ((int)fe_msg->message_case) { + case MGMTD__FE_MESSAGE__MESSAGE_REGISTER_REQ: + MGMTD_FE_ADAPTER_DBG("Got REGISTER_REQ from '%s'", + fe_msg->register_req->client_name); + + if (strlen(fe_msg->register_req->client_name)) { + strlcpy(adapter->name, + fe_msg->register_req->client_name, + sizeof(adapter->name)); + mgmt_fe_adapter_cleanup_old_conn(adapter); + } + break; + case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REQ: + if (fe_msg->session_req->create + && fe_msg->session_req->id_case + == MGMTD__FE_SESSION_REQ__ID_CLIENT_CONN_ID) { + MGMTD_FE_ADAPTER_DBG( + "Got SESSION_REQ (create) for client-id %" PRIu64 + " from '%s'", + fe_msg->session_req->client_conn_id, + adapter->name); + + session = mgmt_fe_create_session( + adapter, fe_msg->session_req->client_conn_id); + fe_adapter_send_session_reply(adapter, session, true, + session ? true : false); + } else if ( + !fe_msg->session_req->create + && fe_msg->session_req->id_case + == MGMTD__FE_SESSION_REQ__ID_SESSION_ID) { + MGMTD_FE_ADAPTER_DBG( + "Got SESSION_REQ (destroy) for session-id %" PRIu64 + "from '%s'", + fe_msg->session_req->session_id, adapter->name); + + session = mgmt_session_id2ctx( + fe_msg->session_req->session_id); + fe_adapter_send_session_reply(adapter, session, false, + true); + mgmt_fe_cleanup_session(&session); + } + break; + case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REQ: + session = mgmt_session_id2ctx( + fe_msg->lockds_req->session_id); + MGMTD_FE_ADAPTER_DBG( + "Got LOCKDS_REQ (%sLOCK) for DS:%s for session-id %" PRIu64 + " from '%s'", + fe_msg->lockds_req->lock ? "" : "UN", + mgmt_ds_id2name(fe_msg->lockds_req->ds_id), + fe_msg->lockds_req->session_id, adapter->name); + mgmt_fe_session_handle_lockds_req_msg( + session, fe_msg->lockds_req); + break; + case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REQ: + session = mgmt_session_id2ctx( + fe_msg->setcfg_req->session_id); + session->adapter->setcfg_stats.set_cfg_count++; + MGMTD_FE_ADAPTER_DBG( + "Got SETCFG_REQ (%d Xpaths, Implicit:%c) on DS:%s for session-id %" PRIu64 + " from '%s'", + (int)fe_msg->setcfg_req->n_data, + fe_msg->setcfg_req->implicit_commit ? 'T' : 'F', + mgmt_ds_id2name(fe_msg->setcfg_req->ds_id), + fe_msg->setcfg_req->session_id, adapter->name); + + mgmt_fe_session_handle_setcfg_req_msg( + session, fe_msg->setcfg_req); + break; + case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REQ: + session = mgmt_session_id2ctx( + fe_msg->commcfg_req->session_id); + MGMTD_FE_ADAPTER_DBG( + "Got COMMCFG_REQ for src-DS:%s dst-DS:%s (Abort:%c) on session-id %" PRIu64 + " from '%s'", + mgmt_ds_id2name(fe_msg->commcfg_req->src_ds_id), + mgmt_ds_id2name(fe_msg->commcfg_req->dst_ds_id), + fe_msg->commcfg_req->abort ? 'T' : 'F', + fe_msg->commcfg_req->session_id, adapter->name); + mgmt_fe_session_handle_commit_config_req_msg( + session, fe_msg->commcfg_req); + break; + case MGMTD__FE_MESSAGE__MESSAGE_GET_REQ: + session = mgmt_session_id2ctx(fe_msg->get_req->session_id); + MGMTD_FE_ADAPTER_DBG("Got GET_REQ (iscfg %d) for DS:%s (xpaths: %d) on session-id %" PRIu64 + " from '%s'", + (int)fe_msg->get_req->config, + mgmt_ds_id2name(fe_msg->get_req->ds_id), + (int)fe_msg->get_req->n_data, + fe_msg->get_req->session_id, adapter->name); + mgmt_fe_session_handle_get_req_msg(session, fe_msg->get_req); + break; + case MGMTD__FE_MESSAGE__MESSAGE_NOTIFY_DATA_REQ: + case MGMTD__FE_MESSAGE__MESSAGE_REGNOTIFY_REQ: + MGMTD_FE_ADAPTER_ERR( + "Got unhandled message of type %u from '%s'", + fe_msg->message_case, adapter->name); + /* + * TODO: Add handling code in future. + */ + break; + /* + * NOTE: The following messages are always sent from MGMTD to + * Frontend clients only and/or need not be handled on MGMTd. + */ + case MGMTD__FE_MESSAGE__MESSAGE_SESSION_REPLY: + case MGMTD__FE_MESSAGE__MESSAGE_LOCKDS_REPLY: + case MGMTD__FE_MESSAGE__MESSAGE_SETCFG_REPLY: + case MGMTD__FE_MESSAGE__MESSAGE_COMMCFG_REPLY: + case MGMTD__FE_MESSAGE__MESSAGE_GET_REPLY: + case MGMTD__FE_MESSAGE__MESSAGE__NOT_SET: + default: + /* + * A 'default' case is being added contrary to the + * FRR code guidelines to take care of build + * failures on certain build systems (courtesy of + * the proto-c package). + */ + break; + } + + return 0; +} + +static void mgmt_fe_adapter_process_msg(uint8_t version, uint8_t *data, + size_t len, struct msg_conn *conn) +{ + struct mgmt_fe_client_adapter *adapter = conn->user; + Mgmtd__FeMessage *fe_msg = mgmtd__fe_message__unpack(NULL, len, data); + + if (!fe_msg) { + MGMTD_FE_ADAPTER_DBG( + "Failed to decode %zu bytes for adapter: %s", len, + adapter->name); + return; + } + MGMTD_FE_ADAPTER_DBG( + "Decoded %zu bytes of message: %u from adapter: %s", len, + fe_msg->message_case, adapter->name); + (void)mgmt_fe_adapter_handle_msg(adapter, fe_msg); + mgmtd__fe_message__free_unpacked(fe_msg, NULL); +} + +void mgmt_fe_adapter_lock(struct mgmt_fe_client_adapter *adapter) +{ + adapter->refcount++; +} + +extern void mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter) +{ + struct mgmt_fe_client_adapter *a = *adapter; + assert(a && a->refcount); + + if (!--a->refcount) { + mgmt_fe_adapters_del(&mgmt_fe_adapters, a); + msg_server_conn_delete(a->conn); + XFREE(MTYPE_MGMTD_FE_ADPATER, a); + } + *adapter = NULL; +} + +/* + * Initialize the FE adapter module + */ +void mgmt_fe_adapter_init(struct event_loop *tm) +{ + assert(!mgmt_loop); + mgmt_loop = tm; + + mgmt_fe_adapters_init(&mgmt_fe_adapters); + + assert(!mgmt_fe_sessions); + mgmt_fe_sessions = + hash_create(mgmt_fe_session_hash_key, mgmt_fe_session_hash_cmp, + "MGMT Frontend Sessions"); + + if (msg_server_init(&mgmt_fe_server, MGMTD_FE_SERVER_PATH, tm, + mgmt_fe_create_adapter, "frontend", + &mgmt_debug_fe)) { + zlog_err("cannot initialize frontend server"); + exit(1); + } +} + +static void mgmt_fe_abort_if_session(void *data) +{ + struct mgmt_fe_session_ctx *session = data; + + MGMTD_FE_ADAPTER_ERR("found orphaned session id %" PRIu64 + " client id %" PRIu64 " adapter %s", + session->session_id, session->client_id, + session->adapter ? session->adapter->name + : "NULL"); + abort(); +} + +/* + * Destroy the FE adapter module + */ +void mgmt_fe_adapter_destroy(void) +{ + struct mgmt_fe_client_adapter *adapter; + + msg_server_cleanup(&mgmt_fe_server); + + /* Deleting the adapters will delete all the sessions */ + FOREACH_ADAPTER_IN_LIST (adapter) + mgmt_fe_adapter_delete(adapter); + + hash_clean_and_free(&mgmt_fe_sessions, mgmt_fe_abort_if_session); +} + +/* + * The server accepted a new connection + */ +struct msg_conn *mgmt_fe_create_adapter(int conn_fd, union sockunion *from) +{ + struct mgmt_fe_client_adapter *adapter = NULL; + + adapter = mgmt_fe_find_adapter_by_fd(conn_fd); + if (!adapter) { + adapter = XCALLOC(MTYPE_MGMTD_FE_ADPATER, + sizeof(struct mgmt_fe_client_adapter)); + snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d", + conn_fd); + + mgmt_fe_sessions_init(&adapter->fe_sessions); + mgmt_fe_adapter_lock(adapter); + mgmt_fe_adapters_add_tail(&mgmt_fe_adapters, adapter); + + adapter->conn = msg_server_conn_create( + mgmt_loop, conn_fd, mgmt_fe_adapter_notify_disconnect, + mgmt_fe_adapter_process_msg, MGMTD_FE_MAX_NUM_MSG_PROC, + MGMTD_FE_MAX_NUM_MSG_WRITE, MGMTD_FE_MSG_MAX_LEN, + adapter, "FE-adapter"); + + adapter->setcfg_stats.min_tm = ULONG_MAX; + adapter->cmt_stats.min_tm = ULONG_MAX; + MGMTD_FE_ADAPTER_DBG("Added new MGMTD Frontend adapter '%s'", + adapter->name); + } + return adapter->conn; +} + +int mgmt_fe_send_set_cfg_reply(uint64_t session_id, uint64_t txn_id, + Mgmtd__DatastoreId ds_id, uint64_t req_id, + enum mgmt_result result, + const char *error_if_any, + bool implicit_commit) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(session_id); + if (!session || session->cfg_txn_id != txn_id) { + if (session) + MGMTD_FE_ADAPTER_ERR( + "txn-id doesn't match, session txn-id is %" PRIu64 + " current txnid: %" PRIu64, + session->cfg_txn_id, txn_id); + return -1; + } + + return fe_adapter_send_set_cfg_reply(session, ds_id, req_id, + result == MGMTD_SUCCESS, + error_if_any, implicit_commit); +} + +int mgmt_fe_send_commit_cfg_reply(uint64_t session_id, uint64_t txn_id, + Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dst_ds_id, + uint64_t req_id, bool validate_only, + enum mgmt_result result, + const char *error_if_any) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(session_id); + if (!session || session->cfg_txn_id != txn_id) + return -1; + + return fe_adapter_send_commit_cfg_reply(session, src_ds_id, dst_ds_id, + req_id, result, validate_only, + error_if_any); +} + +int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id, + Mgmtd__DatastoreId ds_id, uint64_t req_id, + enum mgmt_result result, + Mgmtd__YangDataReply *data_resp, + const char *error_if_any) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(session_id); + if (!session || session->txn_id != txn_id) + return -1; + + return fe_adapter_send_get_reply(session, ds_id, req_id, + result == MGMTD_SUCCESS, data_resp, + error_if_any); +} + +struct mgmt_setcfg_stats * +mgmt_fe_get_session_setcfg_stats(uint64_t session_id) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(session_id); + if (!session || !session->adapter) + return NULL; + + return &session->adapter->setcfg_stats; +} + +struct mgmt_commit_stats * +mgmt_fe_get_session_commit_stats(uint64_t session_id) +{ + struct mgmt_fe_session_ctx *session; + + session = mgmt_session_id2ctx(session_id); + if (!session || !session->adapter) + return NULL; + + return &session->adapter->cmt_stats; +} + +static void +mgmt_fe_adapter_cmt_stats_write(struct vty *vty, + struct mgmt_fe_client_adapter *adapter) +{ + char buf[MGMT_LONG_TIME_MAX_LEN]; + + if (!mm->perf_stats_en) + return; + + vty_out(vty, " Num-Commits: \t\t\t%lu\n", + adapter->cmt_stats.commit_cnt); + if (adapter->cmt_stats.commit_cnt > 0) { + if (mm->perf_stats_en) + vty_out(vty, " Max-Commit-Duration: \t\t%lu uSecs\n", + adapter->cmt_stats.max_tm); + vty_out(vty, " Max-Commit-Batch-Size: \t\t%lu\n", + adapter->cmt_stats.max_batch_cnt); + if (mm->perf_stats_en) + vty_out(vty, " Min-Commit-Duration: \t\t%lu uSecs\n", + adapter->cmt_stats.min_tm); + vty_out(vty, " Min-Commit-Batch-Size: \t\t%lu\n", + adapter->cmt_stats.min_batch_cnt); + if (mm->perf_stats_en) + vty_out(vty, + " Last-Commit-Duration: \t\t%lu uSecs\n", + adapter->cmt_stats.last_exec_tm); + vty_out(vty, " Last-Commit-Batch-Size: \t\t%lu\n", + adapter->cmt_stats.last_batch_cnt); + vty_out(vty, " Last-Commit-CfgData-Reqs: \t\t%lu\n", + adapter->cmt_stats.last_num_cfgdata_reqs); + vty_out(vty, " Last-Commit-CfgApply-Reqs: \t\t%lu\n", + adapter->cmt_stats.last_num_apply_reqs); + if (mm->perf_stats_en) { + vty_out(vty, " Last-Commit-Details:\n"); + vty_out(vty, " Commit Start: \t\t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.last_start, buf, + sizeof(buf))); +#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED + vty_out(vty, " Config-Validate Start: \t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.validate_start, buf, + sizeof(buf))); +#endif + vty_out(vty, " Prep-Config Start: \t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.prep_cfg_start, buf, + sizeof(buf))); + vty_out(vty, " Txn-Create Start: \t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.txn_create_start, + buf, sizeof(buf))); + vty_out(vty, +#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED + " Send-Config Start: \t\t%s\n", +#else + " Send-Config-Validate Start: \t%s\n", +#endif + mgmt_realtime_to_string( + &adapter->cmt_stats.send_cfg_start, buf, + sizeof(buf))); + vty_out(vty, " Apply-Config Start: \t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.apply_cfg_start, + buf, sizeof(buf))); + vty_out(vty, " Apply-Config End: \t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.apply_cfg_end, buf, + sizeof(buf))); + vty_out(vty, " Txn-Delete Start: \t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.txn_del_start, buf, + sizeof(buf))); + vty_out(vty, " Commit End: \t\t\t%s\n", + mgmt_realtime_to_string( + &adapter->cmt_stats.last_end, buf, + sizeof(buf))); + } + } +} + +static void +mgmt_fe_adapter_setcfg_stats_write(struct vty *vty, + struct mgmt_fe_client_adapter *adapter) +{ + char buf[MGMT_LONG_TIME_MAX_LEN]; + + if (!mm->perf_stats_en) + return; + + vty_out(vty, " Num-Set-Cfg: \t\t\t%lu\n", + adapter->setcfg_stats.set_cfg_count); + if (mm->perf_stats_en && adapter->setcfg_stats.set_cfg_count > 0) { + vty_out(vty, " Max-Set-Cfg-Duration: \t\t%lu uSec\n", + adapter->setcfg_stats.max_tm); + vty_out(vty, " Min-Set-Cfg-Duration: \t\t%lu uSec\n", + adapter->setcfg_stats.min_tm); + vty_out(vty, " Avg-Set-Cfg-Duration: \t\t%lu uSec\n", + adapter->setcfg_stats.avg_tm); + vty_out(vty, " Last-Set-Cfg-Details:\n"); + vty_out(vty, " Set-Cfg Start: \t\t\t%s\n", + mgmt_realtime_to_string( + &adapter->setcfg_stats.last_start, buf, + sizeof(buf))); + vty_out(vty, " Set-Cfg End: \t\t\t%s\n", + mgmt_realtime_to_string(&adapter->setcfg_stats.last_end, + buf, sizeof(buf))); + } +} + +void mgmt_fe_adapter_status_write(struct vty *vty, bool detail) +{ + struct mgmt_fe_client_adapter *adapter; + struct mgmt_fe_session_ctx *session; + Mgmtd__DatastoreId ds_id; + bool locked = false; + + vty_out(vty, "MGMTD Frontend Adpaters\n"); + + FOREACH_ADAPTER_IN_LIST (adapter) { + vty_out(vty, " Client: \t\t\t\t%s\n", adapter->name); + vty_out(vty, " Conn-FD: \t\t\t\t%d\n", adapter->conn->fd); + if (detail) { + mgmt_fe_adapter_setcfg_stats_write(vty, adapter); + mgmt_fe_adapter_cmt_stats_write(vty, adapter); + } + vty_out(vty, " Sessions\n"); + FOREACH_SESSION_IN_LIST (adapter, session) { + vty_out(vty, " Session: \t\t\t\t%p\n", session); + vty_out(vty, " Client-Id: \t\t\t%" PRIu64 "\n", + session->client_id); + vty_out(vty, " Session-Id: \t\t\t%" PRIu64 "\n", + session->session_id); + vty_out(vty, " DS-Locks:\n"); + FOREACH_MGMTD_DS_ID (ds_id) { + if (session->ds_locked[ds_id]) { + locked = true; + vty_out(vty, " %s\n", + mgmt_ds_id2name(ds_id)); + } + } + if (!locked) + vty_out(vty, " None\n"); + } + vty_out(vty, " Total-Sessions: \t\t\t%d\n", + (int)mgmt_fe_sessions_count(&adapter->fe_sessions)); + vty_out(vty, " Msg-Recvd: \t\t\t\t%" PRIu64 "\n", + adapter->conn->mstate.nrxm); + vty_out(vty, " Bytes-Recvd: \t\t\t%" PRIu64 "\n", + adapter->conn->mstate.nrxb); + vty_out(vty, " Msg-Sent: \t\t\t\t%" PRIu64 "\n", + adapter->conn->mstate.ntxm); + vty_out(vty, " Bytes-Sent: \t\t\t%" PRIu64 "\n", + adapter->conn->mstate.ntxb); + } + vty_out(vty, " Total: %d\n", + (int)mgmt_fe_adapters_count(&mgmt_fe_adapters)); +} + +void mgmt_fe_adapter_perf_measurement(struct vty *vty, bool config) +{ + mm->perf_stats_en = config; +} + +void mgmt_fe_adapter_reset_perf_stats(struct vty *vty) +{ + struct mgmt_fe_client_adapter *adapter; + struct mgmt_fe_session_ctx *session; + + FOREACH_ADAPTER_IN_LIST (adapter) { + memset(&adapter->setcfg_stats, 0, + sizeof(adapter->setcfg_stats)); + FOREACH_SESSION_IN_LIST (adapter, session) { + memset(&adapter->cmt_stats, 0, + sizeof(adapter->cmt_stats)); + } + } +} diff --git a/mgmtd/mgmt_fe_adapter.h b/mgmtd/mgmt_fe_adapter.h new file mode 100644 index 000000000000..d2991ec1dbd0 --- /dev/null +++ b/mgmtd/mgmt_fe_adapter.h @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Frontend Client Connection Adapter + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ + +#ifndef _FRR_MGMTD_FE_ADAPTER_H_ +#define _FRR_MGMTD_FE_ADAPTER_H_ + +#include "mgmt_fe_client.h" +#include "mgmt_msg.h" +#include "mgmtd/mgmt_defines.h" + +struct mgmt_fe_client_adapter; +struct mgmt_master; + +struct mgmt_commit_stats { + struct timeval last_start; +#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED + struct timeval validate_start; +#endif + struct timeval prep_cfg_start; + struct timeval txn_create_start; + struct timeval send_cfg_start; + struct timeval apply_cfg_start; + struct timeval apply_cfg_end; + struct timeval txn_del_start; + struct timeval last_end; + unsigned long last_exec_tm; + unsigned long max_tm; + unsigned long min_tm; + unsigned long last_batch_cnt; + unsigned long last_num_cfgdata_reqs; + unsigned long last_num_apply_reqs; + unsigned long max_batch_cnt; + unsigned long min_batch_cnt; + unsigned long commit_cnt; +}; + +struct mgmt_setcfg_stats { + struct timeval last_start; + struct timeval last_end; + unsigned long last_exec_tm; + unsigned long max_tm; + unsigned long min_tm; + unsigned long avg_tm; + unsigned long set_cfg_count; +}; + +PREDECL_LIST(mgmt_fe_sessions); + +PREDECL_LIST(mgmt_fe_adapters); + +struct mgmt_fe_client_adapter { + struct msg_conn *conn; + char name[MGMTD_CLIENT_NAME_MAX_LEN]; + + /* List of sessions created and being maintained for this client. */ + struct mgmt_fe_sessions_head fe_sessions; + + int refcount; + struct mgmt_commit_stats cmt_stats; + struct mgmt_setcfg_stats setcfg_stats; + + struct mgmt_fe_adapters_item list_linkage; +}; + +DECLARE_LIST(mgmt_fe_adapters, struct mgmt_fe_client_adapter, list_linkage); + +/* Initialise frontend adapter module */ +extern void mgmt_fe_adapter_init(struct event_loop *tm); + +/* Destroy frontend adapter module */ +extern void mgmt_fe_adapter_destroy(void); + +/* Acquire lock for frontend adapter */ +extern void mgmt_fe_adapter_lock(struct mgmt_fe_client_adapter *adapter); + +/* Remove lock from frontend adapter */ +extern void +mgmt_fe_adapter_unlock(struct mgmt_fe_client_adapter **adapter); + +/* Create frontend adapter */ +extern struct msg_conn *mgmt_fe_create_adapter(int conn_fd, + union sockunion *su); + +/* + * Send set-config reply to the frontend client. + * + * session + * Unique session identifier. + * + * txn_id + * Unique transaction identifier. + * + * ds_id + * Datastore ID. + * + * req_id + * Config request ID. + * + * result + * Config request result (MGMT_*). + * + * error_if_any + * Buffer to store human-readable error message in case of error. + * + * implicit_commit + * TRUE if the commit is implicit, FALSE otherwise. + * + * Returns: + * 0 on success, -1 on failures. + */ +extern int mgmt_fe_send_set_cfg_reply(uint64_t session_id, uint64_t txn_id, + Mgmtd__DatastoreId ds_id, + uint64_t req_id, + enum mgmt_result result, + const char *error_if_any, + bool implcit_commit); + +/* + * Send commit-config reply to the frontend client. + */ +extern int mgmt_fe_send_commit_cfg_reply( + uint64_t session_id, uint64_t txn_id, Mgmtd__DatastoreId src_ds_id, + Mgmtd__DatastoreId dst_ds_id, uint64_t req_id, bool validate_only, + enum mgmt_result result, const char *error_if_any); + +/* + * Send get-config/get-data reply to the frontend client. + */ +extern int mgmt_fe_send_get_reply(uint64_t session_id, uint64_t txn_id, + Mgmtd__DatastoreId ds_id, uint64_t req_id, + enum mgmt_result result, + Mgmtd__YangDataReply *data_resp, + const char *error_if_any); + +/* Fetch frontend client session set-config stats */ +extern struct mgmt_setcfg_stats * +mgmt_fe_get_session_setcfg_stats(uint64_t session_id); + +/* Fetch frontend client session commit stats */ +extern struct mgmt_commit_stats * +mgmt_fe_get_session_commit_stats(uint64_t session_id); + +extern void mgmt_fe_adapter_status_write(struct vty *vty, bool detail); +extern void mgmt_fe_adapter_perf_measurement(struct vty *vty, bool config); +extern void mgmt_fe_adapter_reset_perf_stats(struct vty *vty); +#endif /* _FRR_MGMTD_FE_ADAPTER_H_ */ diff --git a/mgmtd/mgmt_history.c b/mgmtd/mgmt_history.c new file mode 100644 index 000000000000..d4069325ca50 --- /dev/null +++ b/mgmtd/mgmt_history.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + */ + +#include <zebra.h> +#include "md5.h" +#include "frrevent.h" +#include "xref.h" + +#include "mgmt_fe_client.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_history.h" + +struct mgmt_cmt_info_t { + struct mgmt_cmt_infos_item cmts; + + char cmtid_str[MGMT_SHORT_TIME_MAX_LEN]; + char time_str[MGMT_LONG_TIME_MAX_LEN]; + char cmt_json_file[PATH_MAX]; +}; + + +DECLARE_DLIST(mgmt_cmt_infos, struct mgmt_cmt_info_t, cmts); + +#define FOREACH_CMT_REC(mm, cmt_info) \ + frr_each_safe (mgmt_cmt_infos, &mm->cmts, cmt_info) + +/* + * The only instance of VTY session that has triggered an ongoing + * config rollback operation. + */ +static struct vty *rollback_vty; + +static bool file_exists(const char *path) +{ + return !access(path, F_OK); +} + +static void remove_file(const char *path) +{ + if (!file_exists(path)) + return; + if (unlink(path)) + zlog_err("Failed to remove commit history file %s: %s", path, + safe_strerror(errno)); +} + +static struct mgmt_cmt_info_t *mgmt_history_new_cmt_info(void) +{ + struct mgmt_cmt_info_t *new; + struct timespec tv; + struct tm tm; + + new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t)); + + clock_gettime(CLOCK_REALTIME, &tv); + localtime_r(&tv.tv_sec, &tm); + + mgmt_time_to_string(&tv, true, new->time_str, sizeof(new->time_str)); + mgmt_time_to_string(&tv, false, new->cmtid_str, sizeof(new->cmtid_str)); + snprintf(new->cmt_json_file, sizeof(new->cmt_json_file), + MGMTD_COMMIT_FILE_PATH, new->cmtid_str); + + return new; +} + +static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void) +{ + struct mgmt_cmt_info_t *new = mgmt_history_new_cmt_info(); + struct mgmt_cmt_info_t *cmt_info; + struct mgmt_cmt_info_t *last_cmt_info = NULL; + + if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) { + FOREACH_CMT_REC (mm, cmt_info) + last_cmt_info = cmt_info; + + if (last_cmt_info) { + remove_file(last_cmt_info->cmt_json_file); + mgmt_cmt_infos_del(&mm->cmts, last_cmt_info); + XFREE(MTYPE_MGMTD_CMT_INFO, last_cmt_info); + } + } + + mgmt_cmt_infos_add_head(&mm->cmts, new); + return new; +} + +static struct mgmt_cmt_info_t * +mgmt_history_find_cmt_record(const char *cmtid_str) +{ + struct mgmt_cmt_info_t *cmt_info; + + FOREACH_CMT_REC (mm, cmt_info) { + if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) + return cmt_info; + } + + return NULL; +} + +static bool mgmt_history_read_cmt_record_index(void) +{ + FILE *fp; + struct mgmt_cmt_info_t cmt_info; + struct mgmt_cmt_info_t *new; + int cnt = 0; + + if (!file_exists(MGMTD_COMMIT_FILE_PATH)) + return false; + + fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "rb"); + if (!fp) { + zlog_err("Failed to open commit history %s for reading: %s", + MGMTD_COMMIT_INDEX_FILE_NAME, safe_strerror(errno)); + return false; + } + + while ((fread(&cmt_info, sizeof(cmt_info), 1, fp)) > 0) { + if (cnt < MGMTD_MAX_COMMIT_LIST) { + if (!file_exists(cmt_info.cmt_json_file)) { + zlog_err("Commit in index, but file %s missing", + cmt_info.cmt_json_file); + continue; + } + + new = XCALLOC(MTYPE_MGMTD_CMT_INFO, + sizeof(struct mgmt_cmt_info_t)); + memcpy(new, &cmt_info, sizeof(struct mgmt_cmt_info_t)); + mgmt_cmt_infos_add_tail(&mm->cmts, new); + } else { + zlog_warn( + "More records found in commit history file %s than expected", + MGMTD_COMMIT_INDEX_FILE_NAME); + fclose(fp); + return false; + } + + cnt++; + } + + fclose(fp); + return true; +} + +static bool mgmt_history_dump_cmt_record_index(void) +{ + FILE *fp; + int ret = 0; + struct mgmt_cmt_info_t *cmt_info; + struct mgmt_cmt_info_t cmt_info_set[10]; + int cnt = 0; + + fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "wb"); + if (!fp) { + zlog_err("Failed to open commit history %s for writing: %s", + MGMTD_COMMIT_INDEX_FILE_NAME, safe_strerror(errno)); + return false; + } + + FOREACH_CMT_REC (mm, cmt_info) { + memcpy(&cmt_info_set[cnt], cmt_info, + sizeof(struct mgmt_cmt_info_t)); + cnt++; + } + + if (!cnt) { + fclose(fp); + return false; + } + + ret = fwrite(&cmt_info_set, sizeof(struct mgmt_cmt_info_t), cnt, fp); + fclose(fp); + if (ret != cnt) { + zlog_err("Failed to write full commit history, removing file"); + remove_file(MGMTD_COMMIT_INDEX_FILE_NAME); + return false; + } + return true; +} + +static int mgmt_history_rollback_to_cmt(struct vty *vty, + struct mgmt_cmt_info_t *cmt_info, + bool skip_file_load) +{ + struct mgmt_ds_ctx *src_ds_ctx; + struct mgmt_ds_ctx *dst_ds_ctx; + int ret = 0; + + if (rollback_vty) { + vty_out(vty, "ERROR: Rollback already in progress!\n"); + return -1; + } + + src_ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE); + dst_ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_RUNNING); + assert(src_ds_ctx); + assert(dst_ds_ctx); + + ret = mgmt_ds_lock(src_ds_ctx, vty->mgmt_session_id); + if (ret != 0) { + vty_out(vty, + "Failed to lock the DS %u for rollback Reason: %s!\n", + MGMTD_DS_RUNNING, strerror(ret)); + return -1; + } + + ret = mgmt_ds_lock(dst_ds_ctx, vty->mgmt_session_id); + if (ret != 0) { + mgmt_ds_unlock(src_ds_ctx); + vty_out(vty, + "Failed to lock the DS %u for rollback Reason: %s!\n", + MGMTD_DS_RUNNING, strerror(ret)); + return -1; + } + + if (!skip_file_load) { + ret = mgmt_ds_load_config_from_file( + src_ds_ctx, cmt_info->cmt_json_file, false); + if (ret != 0) { + vty_out(vty, + "Error with parsing the file with error code %d\n", + ret); + goto failed_unlock; + } + } + + /* Internally trigger a commit-request. */ + ret = mgmt_txn_rollback_trigger_cfg_apply(src_ds_ctx, dst_ds_ctx); + if (ret != 0) { + vty_out(vty, + "Error with creating commit apply txn with error code %d\n", + ret); + goto failed_unlock; + } + + mgmt_history_dump_cmt_record_index(); + + /* + * TODO: Cleanup: the generic TXN code currently checks for rollback + * and does the unlock when it completes. + */ + + /* + * Block the rollback command from returning till the rollback + * is completed. On rollback completion mgmt_history_rollback_complete() + * shall be called to resume the rollback command return to VTYSH. + */ + vty->mgmt_req_pending_cmd = "ROLLBACK"; + rollback_vty = vty; + return 0; + +failed_unlock: + mgmt_ds_unlock(src_ds_ctx); + mgmt_ds_unlock(dst_ds_ctx); + return ret; +} + +void mgmt_history_rollback_complete(bool success) +{ + vty_mgmt_resume_response(rollback_vty, success); + rollback_vty = NULL; +} + +int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str) +{ + int ret = 0; + struct mgmt_cmt_info_t *cmt_info; + + if (!mgmt_cmt_infos_count(&mm->cmts) || + !mgmt_history_find_cmt_record(cmtid_str)) { + vty_out(vty, "Invalid commit Id\n"); + return -1; + } + + FOREACH_CMT_REC (mm, cmt_info) { + if (strcmp(cmt_info->cmtid_str, cmtid_str) == 0) { + ret = mgmt_history_rollback_to_cmt(vty, cmt_info, + false); + return ret; + } + + remove_file(cmt_info->cmt_json_file); + mgmt_cmt_infos_del(&mm->cmts, cmt_info); + XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info); + } + + return 0; +} + +int mgmt_history_rollback_n(struct vty *vty, int num_cmts) +{ + int ret = 0; + int cnt = 0; + struct mgmt_cmt_info_t *cmt_info; + size_t cmts; + + if (!num_cmts) + num_cmts = 1; + + cmts = mgmt_cmt_infos_count(&mm->cmts); + if ((int)cmts < num_cmts) { + vty_out(vty, + "Number of commits found (%d) less than required to rollback\n", + (int)cmts); + return -1; + } + + if ((int)cmts == 1 || (int)cmts == num_cmts) { + vty_out(vty, + "Number of commits found (%d), Rollback of last commit is not supported\n", + (int)cmts); + return -1; + } + + FOREACH_CMT_REC (mm, cmt_info) { + if (cnt == num_cmts) { + ret = mgmt_history_rollback_to_cmt(vty, cmt_info, + false); + return ret; + } + + cnt++; + remove_file(cmt_info->cmt_json_file); + mgmt_cmt_infos_del(&mm->cmts, cmt_info); + XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info); + } + + if (!mgmt_cmt_infos_count(&mm->cmts)) { + mgmt_ds_reset_candidate(); + ret = mgmt_history_rollback_to_cmt(vty, cmt_info, true); + } + + return ret; +} + +void show_mgmt_cmt_history(struct vty *vty) +{ + struct mgmt_cmt_info_t *cmt_info; + int slno = 0; + + vty_out(vty, "Last 10 commit history:\n"); + vty_out(vty, "Slot Commit-ID Commit-Record-Time\n"); + FOREACH_CMT_REC (mm, cmt_info) { + vty_out(vty, "%4d %23s %s\n", slno, cmt_info->cmtid_str, + cmt_info->time_str); + slno++; + } +} + +void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx) +{ + struct mgmt_cmt_info_t *cmt_info = mgmt_history_create_cmt_rec(); + + mgmt_ds_dump_ds_to_file(cmt_info->cmt_json_file, ds_ctx); + mgmt_history_dump_cmt_record_index(); +} + +void mgmt_history_init(void) +{ + /* Create commit record for previously stored commit-apply */ + mgmt_cmt_infos_init(&mm->cmts); + mgmt_history_read_cmt_record_index(); +} + +void mgmt_history_destroy(void) +{ + struct mgmt_cmt_info_t *cmt_info; + + FOREACH_CMT_REC(mm, cmt_info) { + mgmt_cmt_infos_del(&mm->cmts, cmt_info); + XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info); + } + + mgmt_cmt_infos_fini(&mm->cmts); +} diff --git a/mgmtd/mgmt_history.h b/mgmtd/mgmt_history.h new file mode 100644 index 000000000000..5d9b6626949d --- /dev/null +++ b/mgmtd/mgmt_history.h @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ +#ifndef _FRR_MGMTD_HISTORY_H_ +#define _FRR_MGMTD_HISTORY_H_ + +#include "vrf.h" + +PREDECL_DLIST(mgmt_cmt_infos); + +struct mgmt_ds_ctx; + +/* + * Rollback specific commit from commit history. + * + * vty + * VTY context. + * + * cmtid_str + * Specific commit id from commit history. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str); + +/* + * Rollback n commits from commit history. + * + * vty + * VTY context. + * + * num_cmts + * Number of commits to be rolled back. + * + * Returns: + * 0 on success, -1 on failure. + */ +extern int mgmt_history_rollback_n(struct vty *vty, int num_cmts); + +extern void mgmt_history_rollback_complete(bool success); + +/* + * Show mgmt commit history. + */ +extern void show_mgmt_cmt_history(struct vty *vty); + +extern void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx); + +extern void mgmt_history_destroy(void); +extern void mgmt_history_init(void); + +/* + * 012345678901234567890123456789 + * 2023-12-31T12:12:12,012345678 + * 20231231121212012345678 + */ +#define MGMT_LONG_TIME_FMT "%Y-%m-%dT%H:%M:%S" +#define MGMT_LONG_TIME_MAX_LEN 30 +#define MGMT_SHORT_TIME_FMT "%Y%m%d%H%M%S" +#define MGMT_SHORT_TIME_MAX_LEN 24 + +static inline const char * +mgmt_time_to_string(struct timespec *tv, bool long_fmt, char *buffer, size_t sz) +{ + struct tm tm; + size_t n; + + localtime_r(&tv->tv_sec, &tm); + + if (long_fmt) { + n = strftime(buffer, sz, MGMT_LONG_TIME_FMT, &tm); + assert(n < sz); + snprintf(&buffer[n], sz - n, ",%09lu", tv->tv_nsec); + } else { + n = strftime(buffer, sz, MGMT_SHORT_TIME_FMT, &tm); + assert(n < sz); + snprintf(&buffer[n], sz - n, "%09lu", tv->tv_nsec); + } + + return buffer; +} + +static inline const char *mgmt_realtime_to_string(struct timeval *tv, char *buf, + size_t sz) +{ + struct timespec ts = {.tv_sec = tv->tv_sec, + .tv_nsec = tv->tv_usec * 1000}; + + return mgmt_time_to_string(&ts, true, buf, sz); +} + +#endif /* _FRR_MGMTD_HISTORY_H_ */ diff --git a/mgmtd/mgmt_main.c b/mgmtd/mgmt_main.c new file mode 100644 index 000000000000..39362fa74a83 --- /dev/null +++ b/mgmtd/mgmt_main.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Main routine of mgmt. + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar + */ + +#include <zebra.h> +#include "lib/version.h" +#include "routemap.h" +#include "filter.h" +#include "libfrr.h" +#include "frr_pthread.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" +#include "routing_nb.h" + + +/* mgmt options, we use GNU getopt library. */ +static const struct option longopts[] = { + {"skip_runas", no_argument, NULL, 'S'}, + {"no_zebra", no_argument, NULL, 'Z'}, + {"socket_size", required_argument, NULL, 's'}, + {0}}; + +static void mgmt_exit(int); +static void mgmt_vrf_terminate(void); + +/* privileges */ +static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW, + ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN}; + +struct zebra_privs_t mgmt_privs = { +#if defined(FRR_USER) && defined(FRR_GROUP) + .user = FRR_USER, + .group = FRR_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = array_size(_caps_p), + .cap_num_i = 0, +}; + +static struct frr_daemon_info mgmtd_di; +char backup_config_file[256]; + +/* SIGHUP handler. */ +static void sighup(void) +{ + zlog_info("SIGHUP received, ignoring"); + + return; + + /* + * This is turned off for the moment. There is all + * sorts of config turned off by mgmt_terminate + * that is not setup properly again in mgmt_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. */ + mgmt_terminate(); + + /* + * mgmt_reset(); + */ + zlog_info("MGMTD restarting!"); + + /* + * Reload config file. + * vty_read_config(NULL, mgmtd_di.config_file, config_default); + */ + /* Try to return to normal operation. */ +} + +/* SIGINT handler. */ +static __attribute__((__noreturn__)) void sigint(void) +{ + zlog_notice("Terminating on signal"); + assert(mm->terminating == false); + mm->terminating = true; /* global flag that shutting down */ + + mgmt_terminate(); + + mgmt_exit(0); + + exit(0); +} + +/* SIGUSR1 handler. */ +static void sigusr1(void) +{ + zlog_rotate(); +} + +/* + * Try to free up allocations we know about so that diagnostic tools such as + * valgrind are able to better illuminate leaks. + * + * Zebra route removal and protocol teardown are not meant to be done here. + * For example, "retain_mode" may be set. + */ +static __attribute__((__noreturn__)) void mgmt_exit(int status) +{ + /* it only makes sense for this to be called on a clean exit */ + assert(status == 0); + + frr_early_fini(); + + /* stop pthreads (if any) */ + frr_pthread_stop_all(); + + mgmt_vrf_terminate(); + + frr_fini(); + exit(status); +} + +static struct frr_signal_t mgmt_signals[] = { + { + .signal = SIGHUP, + .handler = &sighup, + }, + { + .signal = SIGUSR1, + .handler = &sigusr1, + }, + { + .signal = SIGINT, + .handler = &sigint, + }, + { + .signal = SIGTERM, + .handler = &sigint, + }, +}; + +static int mgmt_vrf_new(struct vrf *vrf) +{ + zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int mgmt_vrf_delete(struct vrf *vrf) +{ + zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int mgmt_vrf_enable(struct vrf *vrf) +{ + zlog_debug("VRF Enable: %s(%u)", vrf->name, vrf->vrf_id); + + return 0; +} + +static int mgmt_vrf_disable(struct vrf *vrf) +{ + zlog_debug("VRF Disable: %s(%u)", vrf->name, vrf->vrf_id); + + /* Note: This is a callback, the VRF will be deleted by the caller. */ + return 0; +} + +static int mgmt_vrf_config_write(struct vty *vty) +{ + return 0; +} + +static void mgmt_vrf_init(void) +{ + vrf_init(mgmt_vrf_new, mgmt_vrf_enable, mgmt_vrf_disable, + mgmt_vrf_delete); + vrf_cmd_init(mgmt_vrf_config_write); +} + +static void mgmt_vrf_terminate(void) +{ + vrf_terminate(); +} + +/* + * List of YANG modules to be loaded in the process context of + * MGMTd. + * + * NOTE: In future this will also include the YANG modules of + * all individual Backend clients. + */ +static const struct frr_yang_module_info *const mgmt_yang_modules[] = { + &frr_filter_info, + &frr_interface_info, + &frr_route_map_info, + &frr_routing_info, + &frr_vrf_info, +/* + * YANG module info supported by backend clients get added here. + * NOTE: Always set .ignore_cbs true for to avoid validating + * backend northbound callbacks during loading. + */ +#ifdef HAVE_STATICD + &(struct frr_yang_module_info){.name = "frr-staticd", + .ignore_cbs = true}, +#endif +}; + +FRR_DAEMON_INFO(mgmtd, MGMTD, .vty_port = MGMTD_VTY_PORT, + + .proghelp = "FRR Management Daemon.", + + .signals = mgmt_signals, .n_signals = array_size(mgmt_signals), + + .privs = &mgmt_privs, .yang_modules = mgmt_yang_modules, + .n_yang_modules = array_size(mgmt_yang_modules), + + /* avoid libfrr trying to read our config file for us */ + .flags = FRR_MANUAL_VTY_START); + +#define DEPRECATED_OPTIONS "" + +struct frr_daemon_info *mgmt_daemon_info = &mgmtd_di; + +/* Main routine of mgmt. Treatment of argument and start mgmt finite + * state machine is handled at here. + */ +int main(int argc, char **argv) +{ + int opt; + int buffer_size = MGMTD_SOCKET_BUF_SIZE; + + frr_preinit(&mgmtd_di, argc, argv); + frr_opt_add( + "s:" DEPRECATED_OPTIONS, longopts, + " -s, --socket_size Set MGMTD peer socket send buffer size\n"); + + /* Command line argument treatment. */ + while (1) { + opt = frr_getopt(argc, argv, 0); + + if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) { + fprintf(stderr, + "The -%c option no longer exists.\nPlease refer to the manual.\n", + opt); + continue; + } + + if (opt == EOF) + break; + + switch (opt) { + case 0: + break; + case 's': + buffer_size = atoi(optarg); + break; + default: + frr_help_exit(1); + break; + } + } + + /* MGMTD master init. */ + mgmt_master_init(frr_init(), buffer_size); + + /* VRF Initializations. */ + mgmt_vrf_init(); + + /* MGMTD related initialization. */ + mgmt_init(); + + snprintf(backup_config_file, sizeof(backup_config_file), + "%s/zebra.conf", frr_sysconfdir); + mgmtd_di.backup_config_file = backup_config_file; + + /* this will queue a read configs event */ + frr_config_fork(); + + frr_run(mm->master); + + /* Not reached. */ + return 0; +} diff --git a/mgmtd/mgmt_memory.c b/mgmtd/mgmt_memory.c new file mode 100644 index 000000000000..b2a0f0e848c0 --- /dev/null +++ b/mgmtd/mgmt_memory.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mgmt memory type definitions + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mgmt_memory.h" + +/* this file is temporary in nature; definitions should be moved to the + * files they're used in + */ + +DEFINE_MGROUP(MGMTD, "mgmt"); +DEFINE_MTYPE(MGMTD, MGMTD, "instance"); +DEFINE_MTYPE(MGMTD, MGMTD_XPATH, "xpath regex"); +DEFINE_MTYPE(MGMTD, MGMTD_BE_ADPATER, "backend adapter"); +DEFINE_MTYPE(MGMTD, MGMTD_FE_ADPATER, "frontend adapter"); +DEFINE_MTYPE(MGMTD, MGMTD_FE_SESSION, "frontend session"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN, "txn"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_REQ, "txn request"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_SETCFG_REQ, "txn set-config requests"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_COMMCFG_REQ, "txn commit-config requests"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ, "txn get-data requests"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY, "txn get-data replies"); +DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "txn config batches"); +DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "commit info"); diff --git a/mgmtd/mgmt_memory.h b/mgmtd/mgmt_memory.h new file mode 100644 index 000000000000..06518e38384f --- /dev/null +++ b/mgmtd/mgmt_memory.h @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * mgmt memory type declarations + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_MEMORY_H +#define _FRR_MGMTD_MEMORY_H + +#include "memory.h" + +DECLARE_MGROUP(MGMTD); +DECLARE_MTYPE(MGMTD); +DECLARE_MTYPE(MGMTD_XPATH); +DECLARE_MTYPE(MGMTD_BE_ADPATER); +DECLARE_MTYPE(MGMTD_FE_ADPATER); +DECLARE_MTYPE(MGMTD_FE_SESSION); +DECLARE_MTYPE(MGMTD_TXN); +DECLARE_MTYPE(MGMTD_TXN_REQ); +DECLARE_MTYPE(MGMTD_TXN_SETCFG_REQ); +DECLARE_MTYPE(MGMTD_TXN_COMMCFG_REQ); +DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ); +DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY); +DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH); +DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF); +DECLARE_MTYPE(MGMTD_CMT_INFO); +#endif /* _FRR_MGMTD_MEMORY_H */ diff --git a/mgmtd/mgmt_txn.c b/mgmtd/mgmt_txn.c new file mode 100644 index 000000000000..53b9b7df46c2 --- /dev/null +++ b/mgmtd/mgmt_txn.c @@ -0,0 +1,2644 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Transactions + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> +#include "hash.h" +#include "jhash.h" +#include "libfrr.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_memory.h" +#include "mgmtd/mgmt_txn.h" + +#define MGMTD_TXN_DBG(fmt, ...) \ + DEBUGD(&mgmt_debug_txn, "TXN: %s: " fmt, __func__, ##__VA_ARGS__) +#define MGMTD_TXN_ERR(fmt, ...) \ + zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__) + +#define MGMTD_TXN_LOCK(txn) mgmt_txn_lock(txn, __FILE__, __LINE__) +#define MGMTD_TXN_UNLOCK(txn) mgmt_txn_unlock(txn, __FILE__, __LINE__) + +enum mgmt_txn_event { + MGMTD_TXN_PROC_SETCFG = 1, + MGMTD_TXN_PROC_COMMITCFG, + MGMTD_TXN_PROC_GETCFG, + MGMTD_TXN_PROC_GETDATA, + MGMTD_TXN_COMMITCFG_TIMEOUT, + MGMTD_TXN_CLEANUP +}; + +PREDECL_LIST(mgmt_txn_reqs); + +struct mgmt_set_cfg_req { + Mgmtd__DatastoreId ds_id; + struct mgmt_ds_ctx *ds_ctx; + struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + uint16_t num_cfg_changes; + bool implicit_commit; + Mgmtd__DatastoreId dst_ds_id; + struct mgmt_ds_ctx *dst_ds_ctx; + struct mgmt_setcfg_stats *setcfg_stats; +}; + +enum mgmt_commit_phase { + MGMTD_COMMIT_PHASE_PREPARE_CFG = 0, + MGMTD_COMMIT_PHASE_TXN_CREATE, + MGMTD_COMMIT_PHASE_SEND_CFG, + MGMTD_COMMIT_PHASE_APPLY_CFG, + MGMTD_COMMIT_PHASE_TXN_DELETE, + MGMTD_COMMIT_PHASE_MAX +}; + +static inline const char *mgmt_commit_phase2str(enum mgmt_commit_phase cmt_phase) +{ + switch (cmt_phase) { + case MGMTD_COMMIT_PHASE_PREPARE_CFG: + return "PREP-CFG"; + case MGMTD_COMMIT_PHASE_TXN_CREATE: + return "CREATE-TXN"; + case MGMTD_COMMIT_PHASE_SEND_CFG: + return "SEND-CFG"; + case MGMTD_COMMIT_PHASE_APPLY_CFG: + return "APPLY-CFG"; + case MGMTD_COMMIT_PHASE_TXN_DELETE: + return "DELETE-TXN"; + case MGMTD_COMMIT_PHASE_MAX: + return "Invalid/Unknown"; + } + + return "Invalid/Unknown"; +} + +PREDECL_LIST(mgmt_txn_batches); + +struct mgmt_txn_be_cfg_batch { + struct mgmt_txn_ctx *txn; + uint64_t batch_id; + enum mgmt_be_client_id be_id; + struct mgmt_be_client_adapter *be_adapter; + uint xp_subscr[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + Mgmtd__YangCfgDataReq cfg_data[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + Mgmtd__YangCfgDataReq *cfg_datap[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + Mgmtd__YangData data[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + Mgmtd__YangDataValue value[MGMTD_MAX_CFG_CHANGES_IN_BATCH]; + size_t num_cfg_data; + int buf_space_left; + enum mgmt_commit_phase comm_phase; + struct mgmt_txn_batches_item list_linkage; +}; + +DECLARE_LIST(mgmt_txn_batches, struct mgmt_txn_be_cfg_batch, list_linkage); + +#define FOREACH_TXN_CFG_BATCH_IN_LIST(list, batch) \ + frr_each_safe (mgmt_txn_batches, list, batch) + +struct mgmt_commit_cfg_req { + Mgmtd__DatastoreId src_ds_id; + struct mgmt_ds_ctx *src_ds_ctx; + Mgmtd__DatastoreId dst_ds_id; + struct mgmt_ds_ctx *dst_ds_ctx; + uint32_t nb_txn_id; + uint8_t validate_only : 1; + uint8_t abort : 1; + uint8_t implicit : 1; + uint8_t rollback : 1; + + /* Track commit phases */ + enum mgmt_commit_phase curr_phase; + enum mgmt_commit_phase next_phase; + + /* + * Set of config changes to commit. This is used only + * when changes are NOT to be determined by comparing + * candidate and running DSs. This is typically used + * for downloading all relevant configs for a new backend + * client that has recently come up and connected with + * MGMTD. + */ + struct nb_config_cbs *cfg_chgs; + + /* + * Details on all the Backend Clients associated with + * this commit. + */ + struct mgmt_be_client_subscr_info subscr_info; + + /* + * List of backend batches for this commit to be validated + * and applied at the backend. + * + * FIXME: Need to re-think this design for the case set of + * validators for a given YANG data item is different from + * the set of notifiers for the same. We may need to have + * separate list of batches for VALIDATE and APPLY. + */ + struct mgmt_txn_batches_head curr_batches[MGMTD_BE_CLIENT_ID_MAX]; + struct mgmt_txn_batches_head next_batches[MGMTD_BE_CLIENT_ID_MAX]; + /* + * The last batch added for any backend client. This is always on + * 'curr_batches' + */ + struct mgmt_txn_be_cfg_batch *last_be_cfg_batch[MGMTD_BE_CLIENT_ID_MAX]; + struct hash *batches; + uint64_t next_batch_id; + + struct mgmt_commit_stats *cmt_stats; +}; + +struct mgmt_get_data_reply { + /* Buffer space for preparing data reply */ + int num_reply; + int last_batch; + Mgmtd__YangDataReply data_reply; + Mgmtd__YangData reply_data[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH]; + Mgmtd__YangData *reply_datap[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH]; + Mgmtd__YangDataValue reply_value[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH]; + char *reply_xpathp[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH]; +}; + +struct mgmt_get_data_req { + Mgmtd__DatastoreId ds_id; + struct nb_config *cfg_root; + char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH]; + int num_xpaths; + + /* + * Buffer space for preparing reply. + * NOTE: Should only be malloc-ed on demand to reduce + * memory footprint. Freed up via mgmt_trx_req_free() + */ + struct mgmt_get_data_reply *reply; + + int total_reply; +}; + +struct mgmt_txn_req { + struct mgmt_txn_ctx *txn; + enum mgmt_txn_event req_event; + uint64_t req_id; + union { + struct mgmt_set_cfg_req *set_cfg; + struct mgmt_get_data_req *get_data; + struct mgmt_commit_cfg_req commit_cfg; + } req; + + bool pending_be_proc; + struct mgmt_txn_reqs_item list_linkage; +}; + +DECLARE_LIST(mgmt_txn_reqs, struct mgmt_txn_req, list_linkage); + +#define FOREACH_TXN_REQ_IN_LIST(list, req) \ + frr_each_safe (mgmt_txn_reqs, list, req) + +struct mgmt_txn_ctx { + uint64_t session_id; /* One transaction per client session */ + uint64_t txn_id; + enum mgmt_txn_type type; + + /* struct mgmt_master *mm; */ + + struct event *proc_set_cfg; + struct event *proc_comm_cfg; + struct event *proc_get_cfg; + struct event *proc_get_data; + struct event *comm_cfg_timeout; + struct event *clnup; + + /* List of backend adapters involved in this transaction */ + struct mgmt_txn_badapters_head be_adapters; + + int refcount; + + struct mgmt_txns_item list_linkage; + + /* + * List of pending set-config requests for a given + * transaction/session. Just one list for requests + * not processed at all. There's no backend interaction + * involved. + */ + struct mgmt_txn_reqs_head set_cfg_reqs; + /* + * List of pending get-config requests for a given + * transaction/session. Just one list for requests + * not processed at all. There's no backend interaction + * involved. + */ + struct mgmt_txn_reqs_head get_cfg_reqs; + /* + * List of pending get-data requests for a given + * transaction/session Two lists, one for requests + * not processed at all, and one for requests that + * has been sent to backend for processing. + */ + struct mgmt_txn_reqs_head get_data_reqs; + struct mgmt_txn_reqs_head pending_get_datas; + /* + * There will always be one commit-config allowed for a given + * transaction/session. No need to maintain lists for it. + */ + struct mgmt_txn_req *commit_cfg_req; +}; + +DECLARE_LIST(mgmt_txns, struct mgmt_txn_ctx, list_linkage); + +#define FOREACH_TXN_IN_LIST(mm, txn) \ + frr_each_safe (mgmt_txns, &(mm)->txn_list, (txn)) + +static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, + enum mgmt_result result, + const char *error_if_any); + +static inline const char *mgmt_txn_commit_phase_str(struct mgmt_txn_ctx *txn, + bool curr) +{ + if (!txn->commit_cfg_req) + return "None"; + + return (mgmt_commit_phase2str( + curr ? txn->commit_cfg_req->req.commit_cfg.curr_phase + : txn->commit_cfg_req->req.commit_cfg.next_phase)); +} + +static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line); +static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, + int line); +static int mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn, + struct mgmt_be_client_adapter *adapter); + +static struct event_loop *mgmt_txn_tm; +static struct mgmt_master *mgmt_txn_mm; + +static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, + enum mgmt_txn_event event); + +static int +mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, + struct mgmt_be_client_adapter *adapter); + +static struct mgmt_txn_be_cfg_batch * +mgmt_txn_cfg_batch_alloc(struct mgmt_txn_ctx *txn, enum mgmt_be_client_id id, + struct mgmt_be_client_adapter *be_adapter) +{ + struct mgmt_txn_be_cfg_batch *batch; + + batch = XCALLOC(MTYPE_MGMTD_TXN_CFG_BATCH, + sizeof(struct mgmt_txn_be_cfg_batch)); + assert(batch); + batch->be_id = id; + + batch->txn = txn; + MGMTD_TXN_LOCK(txn); + assert(txn->commit_cfg_req); + mgmt_txn_batches_add_tail(&txn->commit_cfg_req->req.commit_cfg + .curr_batches[id], + batch); + batch->be_adapter = be_adapter; + batch->buf_space_left = MGMTD_BE_CFGDATA_MAX_MSG_LEN; + if (be_adapter) + mgmt_be_adapter_lock(be_adapter); + + txn->commit_cfg_req->req.commit_cfg.last_be_cfg_batch[id] = batch; + if (!txn->commit_cfg_req->req.commit_cfg.next_batch_id) + txn->commit_cfg_req->req.commit_cfg.next_batch_id++; + batch->batch_id = txn->commit_cfg_req->req.commit_cfg.next_batch_id++; + hash_get(txn->commit_cfg_req->req.commit_cfg.batches, batch, + hash_alloc_intern); + + return batch; +} + +static void mgmt_txn_cfg_batch_free(struct mgmt_txn_be_cfg_batch **batch) +{ + size_t indx; + struct mgmt_commit_cfg_req *cmtcfg_req; + + MGMTD_TXN_DBG(" freeing batch-id: %" PRIu64 " txn-id %" PRIu64, + (*batch)->batch_id, (*batch)->txn->txn_id); + + assert((*batch)->txn && (*batch)->txn->type == MGMTD_TXN_TYPE_CONFIG); + + cmtcfg_req = &(*batch)->txn->commit_cfg_req->req.commit_cfg; + hash_release(cmtcfg_req->batches, *batch); + mgmt_txn_batches_del(&cmtcfg_req->curr_batches[(*batch)->be_id], *batch); + mgmt_txn_batches_del(&cmtcfg_req->next_batches[(*batch)->be_id], *batch); + + if ((*batch)->be_adapter) + mgmt_be_adapter_unlock(&(*batch)->be_adapter); + + for (indx = 0; indx < (*batch)->num_cfg_data; indx++) { + if ((*batch)->data[indx].xpath) { + free((*batch)->data[indx].xpath); + (*batch)->data[indx].xpath = NULL; + } + } + + MGMTD_TXN_UNLOCK(&(*batch)->txn); + + XFREE(MTYPE_MGMTD_TXN_CFG_BATCH, *batch); + *batch = NULL; +} + +static unsigned int mgmt_txn_cfgbatch_hash_key(const void *data) +{ + const struct mgmt_txn_be_cfg_batch *batch = data; + + return jhash2((uint32_t *)&batch->batch_id, + sizeof(batch->batch_id) / sizeof(uint32_t), 0); +} + +static bool mgmt_txn_cfgbatch_hash_cmp(const void *d1, const void *d2) +{ + const struct mgmt_txn_be_cfg_batch *batch1 = d1; + const struct mgmt_txn_be_cfg_batch *batch2 = d2; + + return (batch1->batch_id == batch2->batch_id); +} + +static void mgmt_txn_cfgbatch_hash_free(void *data) +{ + struct mgmt_txn_be_cfg_batch *batch = data; + + mgmt_txn_cfg_batch_free(&batch); +} + +static inline struct mgmt_txn_be_cfg_batch * +mgmt_txn_cfgbatch_id2ctx(struct mgmt_txn_ctx *txn, uint64_t batch_id) +{ + struct mgmt_txn_be_cfg_batch key = { 0 }; + struct mgmt_txn_be_cfg_batch *batch; + + if (!txn->commit_cfg_req) + return NULL; + + key.batch_id = batch_id; + batch = hash_lookup(txn->commit_cfg_req->req.commit_cfg.batches, &key); + + return batch; +} + +static void mgmt_txn_cleanup_be_cfg_batches(struct mgmt_txn_ctx *txn, + enum mgmt_be_client_id id) +{ + struct mgmt_txn_be_cfg_batch *batch; + struct mgmt_txn_batches_head *list; + + list = &txn->commit_cfg_req->req.commit_cfg.curr_batches[id]; + FOREACH_TXN_CFG_BATCH_IN_LIST (list, batch) + mgmt_txn_cfg_batch_free(&batch); + + mgmt_txn_batches_fini(list); + + list = &txn->commit_cfg_req->req.commit_cfg.next_batches[id]; + FOREACH_TXN_CFG_BATCH_IN_LIST (list, batch) + mgmt_txn_cfg_batch_free(&batch); + + mgmt_txn_batches_fini(list); + + txn->commit_cfg_req->req.commit_cfg.last_be_cfg_batch[id] = NULL; +} + +static struct mgmt_txn_req *mgmt_txn_req_alloc(struct mgmt_txn_ctx *txn, + uint64_t req_id, + enum mgmt_txn_event req_event) +{ + struct mgmt_txn_req *txn_req; + enum mgmt_be_client_id id; + + txn_req = XCALLOC(MTYPE_MGMTD_TXN_REQ, sizeof(struct mgmt_txn_req)); + assert(txn_req); + txn_req->txn = txn; + txn_req->req_id = req_id; + txn_req->req_event = req_event; + txn_req->pending_be_proc = false; + + switch (txn_req->req_event) { + case MGMTD_TXN_PROC_SETCFG: + txn_req->req.set_cfg = XCALLOC(MTYPE_MGMTD_TXN_SETCFG_REQ, + sizeof(struct mgmt_set_cfg_req)); + assert(txn_req->req.set_cfg); + mgmt_txn_reqs_add_tail(&txn->set_cfg_reqs, txn_req); + MGMTD_TXN_DBG("Added a new SETCFG req-id: %" PRIu64 + " txn-id: %" PRIu64 ", session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); + break; + case MGMTD_TXN_PROC_COMMITCFG: + txn->commit_cfg_req = txn_req; + MGMTD_TXN_DBG("Added a new COMMITCFG req-id: %" PRIu64 + " txn-id: %" PRIu64 " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + mgmt_txn_batches_init( + &txn_req->req.commit_cfg.curr_batches[id]); + mgmt_txn_batches_init( + &txn_req->req.commit_cfg.next_batches[id]); + } + + txn_req->req.commit_cfg.batches = + hash_create(mgmt_txn_cfgbatch_hash_key, + mgmt_txn_cfgbatch_hash_cmp, + "MGMT Config Batches"); + break; + case MGMTD_TXN_PROC_GETCFG: + txn_req->req.get_data = + XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ, + sizeof(struct mgmt_get_data_req)); + assert(txn_req->req.get_data); + mgmt_txn_reqs_add_tail(&txn->get_cfg_reqs, txn_req); + MGMTD_TXN_DBG("Added a new GETCFG req-id: %" PRIu64 + " txn-id: %" PRIu64 " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); + break; + case MGMTD_TXN_PROC_GETDATA: + txn_req->req.get_data = + XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REQ, + sizeof(struct mgmt_get_data_req)); + assert(txn_req->req.get_data); + mgmt_txn_reqs_add_tail(&txn->get_data_reqs, txn_req); + MGMTD_TXN_DBG("Added a new GETDATA req-id: %" PRIu64 + " txn-id: %" PRIu64 " session-id: %" PRIu64, + txn_req->req_id, txn->txn_id, txn->session_id); + break; + case MGMTD_TXN_COMMITCFG_TIMEOUT: + case MGMTD_TXN_CLEANUP: + break; + } + + MGMTD_TXN_LOCK(txn); + + return txn_req; +} + +static void mgmt_txn_req_free(struct mgmt_txn_req **txn_req) +{ + int indx; + struct mgmt_txn_reqs_head *req_list = NULL; + struct mgmt_txn_reqs_head *pending_list = NULL; + enum mgmt_be_client_id id; + struct mgmt_be_client_adapter *adapter; + struct mgmt_commit_cfg_req *ccreq; + bool cleanup; + + switch ((*txn_req)->req_event) { + case MGMTD_TXN_PROC_SETCFG: + for (indx = 0; indx < (*txn_req)->req.set_cfg->num_cfg_changes; + indx++) { + if ((*txn_req)->req.set_cfg->cfg_changes[indx].value) { + MGMTD_TXN_DBG("Freeing value for %s at %p ==> '%s'", + (*txn_req) + ->req.set_cfg + ->cfg_changes[indx] + .xpath, + (*txn_req) + ->req.set_cfg + ->cfg_changes[indx] + .value, + (*txn_req) + ->req.set_cfg + ->cfg_changes[indx] + .value); + free((void *)(*txn_req) + ->req.set_cfg->cfg_changes[indx] + .value); + } + } + req_list = &(*txn_req)->txn->set_cfg_reqs; + MGMTD_TXN_DBG("Deleting SETCFG req-id: %" PRIu64 + " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); + XFREE(MTYPE_MGMTD_TXN_SETCFG_REQ, (*txn_req)->req.set_cfg); + break; + case MGMTD_TXN_PROC_COMMITCFG: + MGMTD_TXN_DBG("Deleting COMMITCFG req-id: %" PRIu64 + " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); + + ccreq = &(*txn_req)->req.commit_cfg; + cleanup = (ccreq->curr_phase >= MGMTD_COMMIT_PHASE_TXN_CREATE && + ccreq->curr_phase < MGMTD_COMMIT_PHASE_TXN_DELETE); + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + /* + * Send TXN_DELETE to cleanup state for this + * transaction on backend + */ + + /* + * Get rid of the batches first so we don't end up doing + * anything more with them + */ + mgmt_txn_cleanup_be_cfg_batches((*txn_req)->txn, id); + if (ccreq->batches) { + hash_clean(ccreq->batches, + mgmt_txn_cfgbatch_hash_free); + hash_free(ccreq->batches); + ccreq->batches = NULL; + } + + /* + * If we were in the middle of the state machine then + * send a txn delete message + */ + adapter = mgmt_be_get_adapter_by_id(id); + if (adapter && cleanup && + ccreq->subscr_info.xpath_subscr[id]) + mgmt_txn_send_be_txn_delete((*txn_req)->txn, + adapter); + } + break; + case MGMTD_TXN_PROC_GETCFG: + for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths; + indx++) { + if ((*txn_req)->req.get_data->xpaths[indx]) + free((void *)(*txn_req) + ->req.get_data->xpaths[indx]); + } + req_list = &(*txn_req)->txn->get_cfg_reqs; + MGMTD_TXN_DBG("Deleting GETCFG req-id: %" PRIu64 + " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); + if ((*txn_req)->req.get_data->reply) + XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY, + (*txn_req)->req.get_data->reply); + + if ((*txn_req)->req.get_data->cfg_root) + nb_config_free((*txn_req)->req.get_data->cfg_root); + + XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data); + break; + case MGMTD_TXN_PROC_GETDATA: + for (indx = 0; indx < (*txn_req)->req.get_data->num_xpaths; + indx++) { + if ((*txn_req)->req.get_data->xpaths[indx]) + free((void *)(*txn_req) + ->req.get_data->xpaths[indx]); + } + pending_list = &(*txn_req)->txn->pending_get_datas; + req_list = &(*txn_req)->txn->get_data_reqs; + MGMTD_TXN_DBG("Deleting GETDATA req-id: %" PRIu64 + " txn-id: %" PRIu64, + (*txn_req)->req_id, (*txn_req)->txn->txn_id); + if ((*txn_req)->req.get_data->reply) + XFREE(MTYPE_MGMTD_TXN_GETDATA_REPLY, + (*txn_req)->req.get_data->reply); + XFREE(MTYPE_MGMTD_TXN_GETDATA_REQ, (*txn_req)->req.get_data); + break; + case MGMTD_TXN_COMMITCFG_TIMEOUT: + case MGMTD_TXN_CLEANUP: + break; + } + + if ((*txn_req)->pending_be_proc && pending_list) { + mgmt_txn_reqs_del(pending_list, *txn_req); + MGMTD_TXN_DBG("Removed req-id: %" PRIu64 + " from pending-list (left:%zu)", + (*txn_req)->req_id, + mgmt_txn_reqs_count(pending_list)); + } else if (req_list) { + mgmt_txn_reqs_del(req_list, *txn_req); + MGMTD_TXN_DBG("Removed req-id: %" PRIu64 + " from request-list (left:%zu)", + (*txn_req)->req_id, mgmt_txn_reqs_count(req_list)); + } + + (*txn_req)->pending_be_proc = false; + MGMTD_TXN_UNLOCK(&(*txn_req)->txn); + XFREE(MTYPE_MGMTD_TXN_REQ, (*txn_req)); + *txn_req = NULL; +} + +static void mgmt_txn_process_set_cfg(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + struct mgmt_ds_ctx *ds_ctx; + struct nb_config *nb_config; + char err_buf[1024]; + bool error; + int num_processed = 0; + size_t left; + struct mgmt_commit_stats *cmt_stats; + int ret = 0; + + txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); + assert(txn); + cmt_stats = mgmt_fe_get_session_commit_stats(txn->session_id); + + MGMTD_TXN_DBG("Processing %zu SET_CONFIG requests txn-id:%" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->set_cfg_reqs), txn->txn_id, + txn->session_id); + + FOREACH_TXN_REQ_IN_LIST (&txn->set_cfg_reqs, txn_req) { + assert(txn_req->req_event == MGMTD_TXN_PROC_SETCFG); + ds_ctx = txn_req->req.set_cfg->ds_ctx; + if (!ds_ctx) { + mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id, + txn_req->req.set_cfg->ds_id, + txn_req->req_id, + MGMTD_INTERNAL_ERROR, + "No such datastore!", + txn_req->req.set_cfg + ->implicit_commit); + goto mgmt_txn_process_set_cfg_done; + } + + nb_config = mgmt_ds_get_nb_config(ds_ctx); + if (!nb_config) { + mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id, + txn_req->req.set_cfg->ds_id, + txn_req->req_id, + MGMTD_INTERNAL_ERROR, + "Unable to retrieve DS Config Tree!", + txn_req->req.set_cfg + ->implicit_commit); + goto mgmt_txn_process_set_cfg_done; + } + + error = false; + nb_candidate_edit_config_changes(nb_config, + txn_req->req.set_cfg->cfg_changes, + (size_t)txn_req->req.set_cfg + ->num_cfg_changes, + NULL, NULL, 0, err_buf, + sizeof(err_buf), &error); + if (error) { + mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id, + txn_req->req.set_cfg->ds_id, + txn_req->req_id, + MGMTD_INTERNAL_ERROR, err_buf, + txn_req->req.set_cfg + ->implicit_commit); + goto mgmt_txn_process_set_cfg_done; + } + + if (txn_req->req.set_cfg->implicit_commit) { + assert(mgmt_txn_reqs_count(&txn->set_cfg_reqs) == 1); + assert(txn_req->req.set_cfg->dst_ds_ctx); + + /* We expect the user to have locked the DST DS */ + if (!mgmt_ds_is_locked(txn_req->req.set_cfg->dst_ds_ctx, + txn->session_id)) { + MGMTD_TXN_ERR("DS %u not locked for implicit commit txn-id: %" PRIu64 + " session-id: %" PRIu64 " err: %s", + txn_req->req.set_cfg->dst_ds_id, + txn->txn_id, txn->session_id, + strerror(ret)); + mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_DS_LOCK_FAILED, + "running DS not locked for implicit commit"); + goto mgmt_txn_process_set_cfg_done; + } + + mgmt_txn_send_commit_config_req(txn->txn_id, + txn_req->req_id, + txn_req->req.set_cfg + ->ds_id, + txn_req->req.set_cfg + ->ds_ctx, + txn_req->req.set_cfg + ->dst_ds_id, + txn_req->req.set_cfg + ->dst_ds_ctx, + false, false, true); + + if (mm->perf_stats_en) + gettimeofday(&cmt_stats->last_start, NULL); + cmt_stats->commit_cnt++; + } else if (mgmt_fe_send_set_cfg_reply(txn->session_id, + txn->txn_id, + txn_req->req.set_cfg->ds_id, + txn_req->req_id, + MGMTD_SUCCESS, NULL, + false) != 0) { + MGMTD_TXN_ERR("Failed to send SET_CONFIG_REPLY txn-id %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); + } + +mgmt_txn_process_set_cfg_done: + + /* + * Note: The following will remove it from the list as well. + */ + mgmt_txn_req_free(&txn_req); + + num_processed++; + if (num_processed == MGMTD_TXN_MAX_NUM_SETCFG_PROC) + break; + } + + left = mgmt_txn_reqs_count(&txn->set_cfg_reqs); + if (left) { + MGMTD_TXN_DBG("Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_SETCFG_PROC, + (int)left); + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_SETCFG); + } +} + +static int mgmt_txn_send_commit_cfg_reply(struct mgmt_txn_ctx *txn, + enum mgmt_result result, + const char *error_if_any) +{ + bool success, create_cmt_info_rec; + + if (!txn->commit_cfg_req) + return -1; + + success = (result == MGMTD_SUCCESS || result == MGMTD_NO_CFG_CHANGES); + + /* TODO: these replies should not be send if it's a rollback + * b/c right now that is special cased.. that special casing should be + * removed; however... + */ + if (!txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id && + !txn->commit_cfg_req->req.commit_cfg.rollback && + mgmt_fe_send_commit_cfg_reply(txn->session_id, txn->txn_id, + txn->commit_cfg_req->req.commit_cfg + .src_ds_id, + txn->commit_cfg_req->req.commit_cfg + .dst_ds_id, + txn->commit_cfg_req->req_id, + txn->commit_cfg_req->req.commit_cfg + .validate_only, + result, error_if_any) != 0) { + MGMTD_TXN_ERR("Failed to send COMMIT-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); + } + + if (txn->commit_cfg_req->req.commit_cfg.implicit && txn->session_id && + !txn->commit_cfg_req->req.commit_cfg.rollback && + mgmt_fe_send_set_cfg_reply(txn->session_id, txn->txn_id, + txn->commit_cfg_req->req.commit_cfg + .src_ds_id, + txn->commit_cfg_req->req_id, + success ? MGMTD_SUCCESS + : MGMTD_INTERNAL_ERROR, + error_if_any, true) != 0) { + MGMTD_TXN_ERR("Failed to send SET-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64, + txn->txn_id, txn->session_id); + } + + if (success) { + /* Stop the commit-timeout timer */ + /* XXX why only on success? */ + EVENT_OFF(txn->comm_cfg_timeout); + + create_cmt_info_rec = + (result != MGMTD_NO_CFG_CHANGES && + !txn->commit_cfg_req->req.commit_cfg.rollback); + + /* + * Successful commit: Merge Src DS into Dst DS if and only if + * this was not a validate-only or abort request. + */ + if ((txn->session_id && + !txn->commit_cfg_req->req.commit_cfg.validate_only && + !txn->commit_cfg_req->req.commit_cfg.abort) || + txn->commit_cfg_req->req.commit_cfg.rollback) { + mgmt_ds_copy_dss(txn->commit_cfg_req->req.commit_cfg + .src_ds_ctx, + txn->commit_cfg_req->req.commit_cfg + .dst_ds_ctx, + create_cmt_info_rec); + } + + /* + * Restore Src DS back to Dest DS only through a commit abort + * request. + */ + if (txn->session_id && txn->commit_cfg_req->req.commit_cfg.abort) + mgmt_ds_copy_dss(txn->commit_cfg_req->req.commit_cfg + .dst_ds_ctx, + txn->commit_cfg_req->req.commit_cfg + .src_ds_ctx, + false); + } else { + /* + * The commit has failied. For implicit commit requests restore + * back the contents of the candidate DS. + */ + if (txn->commit_cfg_req->req.commit_cfg.implicit) + mgmt_ds_copy_dss(txn->commit_cfg_req->req.commit_cfg + .dst_ds_ctx, + txn->commit_cfg_req->req.commit_cfg + .src_ds_ctx, + false); + } + + if (txn->commit_cfg_req->req.commit_cfg.rollback) { + mgmt_ds_unlock(txn->commit_cfg_req->req.commit_cfg.src_ds_ctx); + mgmt_ds_unlock(txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx); + /* + * Resume processing the rollback command. + * + * TODO: there's no good reason to special case rollback, the + * rollback boolean should be passed back to the FE client and it + * can do the right thing. + */ + mgmt_history_rollback_complete(success); + } + + txn->commit_cfg_req->req.commit_cfg.cmt_stats = NULL; + mgmt_txn_req_free(&txn->commit_cfg_req); + + /* + * The CONFIG Transaction should be destroyed from Frontend-adapter. + * But in case the transaction is not triggered from a front-end session + * we need to cleanup by itself. + */ + if (!txn->session_id) + mgmt_txn_register_event(txn, MGMTD_TXN_CLEANUP); + + return 0; +} + +static void +mgmt_move_txn_cfg_batch_to_next(struct mgmt_commit_cfg_req *cmtcfg_req, + struct mgmt_txn_be_cfg_batch *batch, + struct mgmt_txn_batches_head *src_list, + struct mgmt_txn_batches_head *dst_list, + bool update_commit_phase, + enum mgmt_commit_phase to_phase) +{ + mgmt_txn_batches_del(src_list, batch); + + if (update_commit_phase) { + MGMTD_TXN_DBG("Move txn-id %" PRIu64 " batch-id: %" PRIu64 + " from '%s' --> '%s'", + batch->txn->txn_id, batch->batch_id, + mgmt_commit_phase2str(batch->comm_phase), + mgmt_txn_commit_phase_str(batch->txn, false)); + batch->comm_phase = to_phase; + } + + mgmt_txn_batches_add_tail(dst_list, batch); +} + +static void mgmt_move_txn_cfg_batches(struct mgmt_txn_ctx *txn, + struct mgmt_commit_cfg_req *cmtcfg_req, + struct mgmt_txn_batches_head *src_list, + struct mgmt_txn_batches_head *dst_list, + bool update_commit_phase, + enum mgmt_commit_phase to_phase) +{ + struct mgmt_txn_be_cfg_batch *batch; + + FOREACH_TXN_CFG_BATCH_IN_LIST (src_list, batch) { + mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, batch, src_list, + dst_list, update_commit_phase, + to_phase); + } +} + +static int +mgmt_try_move_commit_to_next_phase(struct mgmt_txn_ctx *txn, + struct mgmt_commit_cfg_req *cmtcfg_req) +{ + struct mgmt_txn_batches_head *curr_list, *next_list; + enum mgmt_be_client_id id; + + MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')", + txn->txn_id, mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); + + /* + * Check if all clients has moved to next phase or not. + */ + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (cmtcfg_req->subscr_info.xpath_subscr[id] && + mgmt_txn_batches_count(&cmtcfg_req->curr_batches[id])) { + /* + * There's atleast once client who hasn't moved to + * next phase. + * + * TODO: Need to re-think this design for the case + * set of validators for a given YANG data item is + * different from the set of notifiers for the same. + */ + return -1; + } + } + + MGMTD_TXN_DBG("Move entire txn-id: %" PRIu64 " from '%s' to '%s'", + txn->txn_id, mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); + + /* + * If we are here, it means all the clients has moved to next phase. + * So we can move the whole commit to next phase. + */ + cmtcfg_req->curr_phase = cmtcfg_req->next_phase; + cmtcfg_req->next_phase++; + MGMTD_TXN_DBG("Move back all config batches for txn-id: %" PRIu64 + " from next to current branch", + txn->txn_id); + FOREACH_MGMTD_BE_CLIENT_ID (id) { + curr_list = &cmtcfg_req->curr_batches[id]; + next_list = &cmtcfg_req->next_batches[id]; + mgmt_move_txn_cfg_batches(txn, cmtcfg_req, next_list, curr_list, + false, 0); + } + + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); + + return 0; +} + +static int +mgmt_move_be_commit_to_next_phase(struct mgmt_txn_ctx *txn, + struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_commit_cfg_req *cmtcfg_req; + struct mgmt_txn_batches_head *curr_list, *next_list; + + if (txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req) + return -1; + + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + + MGMTD_TXN_DBG("Move txn-id: %" PRIu64 + " for '%s' Phase(current: '%s' next:'%s')", + txn->txn_id, adapter->name, + mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); + + MGMTD_TXN_DBG("Move all config batches for '%s' from current to next list", + adapter->name); + curr_list = &cmtcfg_req->curr_batches[adapter->id]; + next_list = &cmtcfg_req->next_batches[adapter->id]; + mgmt_move_txn_cfg_batches(txn, cmtcfg_req, curr_list, next_list, true, + cmtcfg_req->next_phase); + + MGMTD_TXN_DBG("txn-id: %" PRIu64 ", Phase(current:'%s' next:'%s')", + txn->txn_id, mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); + + /* + * Check if all clients has moved to next phase or not. + */ + mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); + + return 0; +} + +static int mgmt_txn_create_config_batches(struct mgmt_txn_req *txn_req, + struct nb_config_cbs *changes) +{ + struct nb_config_cb *cb, *nxt; + struct nb_config_change *chg; + struct mgmt_txn_be_cfg_batch *batch; + struct mgmt_be_client_subscr_info subscr_info; + char *xpath = NULL, *value = NULL; + char err_buf[1024]; + enum mgmt_be_client_id id; + struct mgmt_be_client_adapter *adapter; + struct mgmt_commit_cfg_req *cmtcfg_req; + bool found_validator; + int num_chgs = 0; + int xpath_len, value_len; + + cmtcfg_req = &txn_req->req.commit_cfg; + + RB_FOREACH_SAFE (cb, nb_config_cbs, changes, nxt) { + chg = (struct nb_config_change *)cb; + + /* + * Could have directly pointed to xpath in nb_node. + * But dont want to mess with it now. + * xpath = chg->cb.nb_node->xpath; + */ + xpath = lyd_path(chg->cb.dnode, LYD_PATH_STD, NULL, 0); + if (!xpath) { + (void)mgmt_txn_send_commit_cfg_reply( + txn_req->txn, MGMTD_INTERNAL_ERROR, + "Internal error! Could not get Xpath from Ds node!"); + return -1; + } + + value = (char *)lyd_get_value(chg->cb.dnode); + if (!value) + value = (char *)MGMTD_BE_CONTAINER_NODE_VAL; + + MGMTD_TXN_DBG("XPATH: %s, Value: '%s'", xpath, + value ? value : "NIL"); + + mgmt_be_get_subscr_info_for_xpath(xpath, &subscr_info); + + xpath_len = strlen(xpath) + 1; + value_len = strlen(value) + 1; + found_validator = false; + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (!(subscr_info.xpath_subscr[id] & + (MGMT_SUBSCR_VALIDATE_CFG | + MGMT_SUBSCR_NOTIFY_CFG))) + continue; + + adapter = mgmt_be_get_adapter_by_id(id); + if (!adapter) + continue; + + batch = cmtcfg_req->last_be_cfg_batch[id]; + if (!batch || + (batch->num_cfg_data == + MGMTD_MAX_CFG_CHANGES_IN_BATCH) || + (batch->buf_space_left < (xpath_len + value_len))) { + /* Allocate a new config batch */ + batch = mgmt_txn_cfg_batch_alloc(txn_req->txn, + id, adapter); + } + + batch->buf_space_left -= (xpath_len + value_len); + memcpy(&batch->xp_subscr[batch->num_cfg_data], + &subscr_info.xpath_subscr[id], + sizeof(batch->xp_subscr[0])); + + mgmt_yang_cfg_data_req_init( + &batch->cfg_data[batch->num_cfg_data]); + batch->cfg_datap[batch->num_cfg_data] = + &batch->cfg_data[batch->num_cfg_data]; + + if (chg->cb.operation == NB_OP_DESTROY) + batch->cfg_data[batch->num_cfg_data].req_type = + MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA; + else + batch->cfg_data[batch->num_cfg_data].req_type = + MGMTD__CFG_DATA_REQ_TYPE__SET_DATA; + + mgmt_yang_data_init(&batch->data[batch->num_cfg_data]); + batch->cfg_data[batch->num_cfg_data].data = + &batch->data[batch->num_cfg_data]; + batch->data[batch->num_cfg_data].xpath = strdup(xpath); + + mgmt_yang_data_value_init( + &batch->value[batch->num_cfg_data]); + batch->data[batch->num_cfg_data].value = + &batch->value[batch->num_cfg_data]; + batch->value[batch->num_cfg_data].value_case = + MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; + batch->value[batch->num_cfg_data].encoded_str_val = + value; + value = NULL; + + if (subscr_info.xpath_subscr[id] & + MGMT_SUBSCR_VALIDATE_CFG) + found_validator = true; + + cmtcfg_req->subscr_info.xpath_subscr[id] |= + subscr_info.xpath_subscr[id]; + MGMTD_TXN_DBG(" -- %s, batch-id: %" PRIu64 " item:%d", + adapter->name, batch->batch_id, + (int)batch->num_cfg_data); + + batch->num_cfg_data++; + num_chgs++; + } + + if (!found_validator) { + snprintf(err_buf, sizeof(err_buf), + "No validator module found for XPATH: '%s", + xpath); + MGMTD_TXN_ERR("***** %s", err_buf); + } + + free(xpath); + } + + cmtcfg_req->cmt_stats->last_batch_cnt = num_chgs; + if (!num_chgs) { + (void)mgmt_txn_send_commit_cfg_reply(txn_req->txn, + MGMTD_NO_CFG_CHANGES, + "No changes found to commit!"); + return -1; + } + + cmtcfg_req->next_phase = MGMTD_COMMIT_PHASE_TXN_CREATE; + return 0; +} + +static int mgmt_txn_prepare_config(struct mgmt_txn_ctx *txn) +{ + struct nb_context nb_ctx; + struct nb_config *nb_config; + struct nb_config_cbs changes; + struct nb_config_cbs *cfg_chgs = NULL; + int ret; + bool del_cfg_chgs = false; + + ret = 0; + memset(&nb_ctx, 0, sizeof(nb_ctx)); + memset(&changes, 0, sizeof(changes)); + if (txn->commit_cfg_req->req.commit_cfg.cfg_chgs) { + cfg_chgs = txn->commit_cfg_req->req.commit_cfg.cfg_chgs; + del_cfg_chgs = true; + goto mgmt_txn_prep_config_validation_done; + } + + if (txn->commit_cfg_req->req.commit_cfg.src_ds_id != MGMTD_DS_CANDIDATE) { + (void)mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INVALID_PARAM, + "Source DS cannot be any other than CANDIDATE!"); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + if (txn->commit_cfg_req->req.commit_cfg.dst_ds_id != MGMTD_DS_RUNNING) { + (void)mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INVALID_PARAM, + "Destination DS cannot be any other than RUNNING!"); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + if (!txn->commit_cfg_req->req.commit_cfg.src_ds_ctx) { + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, + "No such source datastore!"); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + if (!txn->commit_cfg_req->req.commit_cfg.dst_ds_ctx) { + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, + "No such destination datastore!"); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + if (txn->commit_cfg_req->req.commit_cfg.abort) { + /* + * This is a commit abort request. Return back success. + * That should trigger a restore of Candidate datastore to + * Running. + */ + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS, NULL); + goto mgmt_txn_prepare_config_done; + } + + nb_config = mgmt_ds_get_nb_config( + txn->commit_cfg_req->req.commit_cfg.src_ds_ctx); + if (!nb_config) { + (void)mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Unable to retrieve Commit DS Config Tree!"); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + /* + * Check for diffs from scratch buffer. If found empty + * get the diff from Candidate DS itself. + */ + cfg_chgs = &nb_config->cfg_chgs; + if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { + /* + * This could be the case when the config is directly + * loaded onto the candidate DS from a file. Get the + * diff from a full comparison of the candidate and + * running DSs. + */ + nb_config_diff(mgmt_ds_get_nb_config( + txn->commit_cfg_req->req.commit_cfg + .dst_ds_ctx), + nb_config, &changes); + cfg_chgs = &changes; + del_cfg_chgs = true; + } + + if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { + /* + * This means there's no changes to commit whatsoever + * is the source of the changes in config. + */ + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_NO_CFG_CHANGES, + "No changes found to be committed!"); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + +#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED + if (mm->perf_stats_en) + gettimeofday(&txn->commit_cfg_req->req.commit_cfg.cmt_stats + ->validate_start, + NULL); + /* + * Validate YANG contents of the source DS and get the diff + * between source and destination DS contents. + */ + char err_buf[1024] = { 0 }; + nb_ctx.client = NB_CLIENT_MGMTD_SERVER; + nb_ctx.user = (void *)txn; + + ret = nb_candidate_validate_yang(nb_config, false, err_buf, + sizeof(err_buf) - 1); + if (ret != NB_OK) { + if (strncmp(err_buf, " ", strlen(err_buf)) == 0) + strlcpy(err_buf, "Validation failed", sizeof(err_buf)); + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, + err_buf); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + /* + * Perform application level validations locally on the MGMTD + * process by calling application specific validation routines + * loaded onto MGMTD process using libraries. + */ + ret = nb_candidate_validate_code(&nb_ctx, nb_config, &changes, err_buf, + sizeof(err_buf) - 1); + if (ret != NB_OK) { + if (strncmp(err_buf, " ", strlen(err_buf)) == 0) + strlcpy(err_buf, "Validation failed", sizeof(err_buf)); + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_INVALID_PARAM, + err_buf); + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + if (txn->commit_cfg_req->req.commit_cfg.validate_only) { + /* + * This was a validate-only COMMIT request return success. + */ + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS, NULL); + goto mgmt_txn_prepare_config_done; + } +#endif /* ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED */ + +mgmt_txn_prep_config_validation_done: + + if (mm->perf_stats_en) + gettimeofday(&txn->commit_cfg_req->req.commit_cfg.cmt_stats + ->prep_cfg_start, + NULL); + + /* + * Iterate over the diffs and create ordered batches of config + * commands to be validated. + */ + ret = mgmt_txn_create_config_batches(txn->commit_cfg_req, cfg_chgs); + if (ret != 0) { + ret = -1; + goto mgmt_txn_prepare_config_done; + } + + /* Move to the Transaction Create Phase */ + txn->commit_cfg_req->req.commit_cfg.curr_phase = + MGMTD_COMMIT_PHASE_TXN_CREATE; + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); + + /* + * Start the COMMIT Timeout Timer to abort Txn if things get stuck at + * backend. + */ + mgmt_txn_register_event(txn, MGMTD_TXN_COMMITCFG_TIMEOUT); +mgmt_txn_prepare_config_done: + + if (cfg_chgs && del_cfg_chgs) + nb_config_diff_del_changes(cfg_chgs); + + return ret; +} + +static int mgmt_txn_send_be_txn_create(struct mgmt_txn_ctx *txn) +{ + enum mgmt_be_client_id id; + struct mgmt_be_client_adapter *adapter; + struct mgmt_commit_cfg_req *cmtcfg_req; + struct mgmt_txn_be_cfg_batch *batch; + + assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); + + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (cmtcfg_req->subscr_info.xpath_subscr[id]) { + adapter = mgmt_be_get_adapter_by_id(id); + if (mgmt_be_send_txn_req(adapter, txn->txn_id, true)) { + (void)mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Could not send TXN_CREATE to backend adapter"); + return -1; + } + + FOREACH_TXN_CFG_BATCH_IN_LIST (&txn->commit_cfg_req->req + .commit_cfg + .curr_batches[id], + batch) + batch->comm_phase = + MGMTD_COMMIT_PHASE_TXN_CREATE; + } + } + + txn->commit_cfg_req->req.commit_cfg.next_phase = + MGMTD_COMMIT_PHASE_SEND_CFG; + + /* + * Dont move the commit to next phase yet. Wait for the TXN_REPLY to + * come back. + */ + + MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 + " Phase(Current:'%s', Next: '%s')", + txn->txn_id, txn->session_id, + mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); + + return 0; +} + +static int mgmt_txn_send_be_cfg_data(struct mgmt_txn_ctx *txn, + struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_commit_cfg_req *cmtcfg_req; + struct mgmt_txn_be_cfg_batch *batch; + struct mgmt_be_cfgreq cfg_req = { 0 }; + size_t num_batches, indx; + + assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); + + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + assert(cmtcfg_req->subscr_info.xpath_subscr[adapter->id]); + + indx = 0; + num_batches = + mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id]); + FOREACH_TXN_CFG_BATCH_IN_LIST (&cmtcfg_req->curr_batches[adapter->id], + batch) { + assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_SEND_CFG); + + cfg_req.cfgdata_reqs = batch->cfg_datap; + cfg_req.num_reqs = batch->num_cfg_data; + indx++; + if (mgmt_be_send_cfgdata_req(adapter, txn->txn_id, + batch->batch_id, + cfg_req.cfgdata_reqs, + cfg_req.num_reqs, + indx == num_batches)) { + (void)mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Internal Error! Could not send config data to backend!"); + MGMTD_TXN_ERR("Could not send CFGDATA_CREATE txn-id: %" PRIu64 + " batch-id: %" PRIu64 " to client '%s", + txn->txn_id, batch->batch_id, + adapter->name); + return -1; + } + + cmtcfg_req->cmt_stats->last_num_cfgdata_reqs++; + mgmt_move_txn_cfg_batch_to_next( + cmtcfg_req, batch, + &cmtcfg_req->curr_batches[adapter->id], + &cmtcfg_req->next_batches[adapter->id], true, + MGMTD_COMMIT_PHASE_SEND_CFG); + } + + /* + * This could be the last Backend Client to send CFGDATA_CREATE_REQ to. + * Try moving the commit to next phase. + */ + mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); + + return 0; +} + +static int mgmt_txn_send_be_txn_delete(struct mgmt_txn_ctx *txn, + struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_commit_cfg_req *cmtcfg_req = + &txn->commit_cfg_req->req.commit_cfg; + + assert(txn->type == MGMTD_TXN_TYPE_CONFIG); + assert(!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])); + + if (!cmtcfg_req->subscr_info.xpath_subscr[adapter->id]) + return 0; + + return mgmt_be_send_txn_req(adapter, txn->txn_id, false); +} + +static void mgmt_txn_cfg_commit_timedout(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + + txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); + assert(txn); + + assert(txn->type == MGMTD_TXN_TYPE_CONFIG); + + if (!txn->commit_cfg_req) + return; + + MGMTD_TXN_ERR("Backend timeout txn-id: %" PRIu64 " aborting commit", + txn->txn_id); + + /* + * Send a COMMIT_CONFIG_REPLY with failure. + * NOTE: The transaction cleanup will be triggered from Front-end + * adapter. + */ + mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Operation on the backend timed-out. Aborting commit!"); +} + +/* + * Send CFG_APPLY_REQs to all the backend client. + * + * NOTE: This is always dispatched when all CFGDATA_CREATE_REQs + * for all backend clients has been generated. Please see + * mgmt_txn_register_event() and mgmt_txn_process_commit_cfg() + * for details. + */ +static int mgmt_txn_send_be_cfg_apply(struct mgmt_txn_ctx *txn) +{ + enum mgmt_be_client_id id; + struct mgmt_be_client_adapter *adapter; + struct mgmt_commit_cfg_req *cmtcfg_req; + struct mgmt_txn_batches_head *batch_list; + struct mgmt_txn_be_cfg_batch *batch; + + assert(txn->type == MGMTD_TXN_TYPE_CONFIG && txn->commit_cfg_req); + + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + if (cmtcfg_req->validate_only) { + /* + * If this was a validate-only COMMIT request return success. + */ + (void)mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS, NULL); + return 0; + } + + FOREACH_MGMTD_BE_CLIENT_ID (id) { + if (cmtcfg_req->subscr_info.xpath_subscr[id] & + MGMT_SUBSCR_NOTIFY_CFG) { + adapter = mgmt_be_get_adapter_by_id(id); + if (!adapter) + return -1; + + batch_list = &cmtcfg_req->curr_batches[id]; + if (mgmt_be_send_cfgapply_req(adapter, txn->txn_id)) { + (void)mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Could not send CFG_APPLY_REQ to backend adapter"); + return -1; + } + cmtcfg_req->cmt_stats->last_num_apply_reqs++; + + UNSET_FLAG(adapter->flags, + MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + + FOREACH_TXN_CFG_BATCH_IN_LIST (batch_list, batch) + batch->comm_phase = MGMTD_COMMIT_PHASE_APPLY_CFG; + } + } + + txn->commit_cfg_req->req.commit_cfg.next_phase = + MGMTD_COMMIT_PHASE_TXN_DELETE; + + /* + * Dont move the commit to next phase yet. Wait for all VALIDATE_REPLIES + * to come back. + */ + + return 0; +} + +static void mgmt_txn_process_commit_cfg(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_commit_cfg_req *cmtcfg_req; + + txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); + assert(txn); + + MGMTD_TXN_DBG("Processing COMMIT_CONFIG for txn-id: %" PRIu64 + " session-id: %" PRIu64 " Phase(Current:'%s', Next: '%s')", + txn->txn_id, txn->session_id, + mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); + + assert(txn->commit_cfg_req); + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + switch (cmtcfg_req->curr_phase) { + case MGMTD_COMMIT_PHASE_PREPARE_CFG: + mgmt_txn_prepare_config(txn); + break; + case MGMTD_COMMIT_PHASE_TXN_CREATE: + if (mm->perf_stats_en) + gettimeofday(&cmtcfg_req->cmt_stats->txn_create_start, + NULL); + /* + * Send TXN_CREATE_REQ to all Backend now. + */ + mgmt_txn_send_be_txn_create(txn); + break; + case MGMTD_COMMIT_PHASE_SEND_CFG: + if (mm->perf_stats_en) + gettimeofday(&cmtcfg_req->cmt_stats->send_cfg_start, + NULL); + /* + * All CFGDATA_CREATE_REQ should have been sent to + * Backend by now. + */ +#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED + assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG); + MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 + " trigger sending CFG_VALIDATE_REQ to all backend clients", + txn->txn_id, txn->session_id); +#else /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */ + assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG); + MGMTD_TXN_DBG("txn-id: %" PRIu64 " session-id: %" PRIu64 + " trigger sending CFG_APPLY_REQ to all backend clients", + txn->txn_id, txn->session_id); +#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */ + break; + case MGMTD_COMMIT_PHASE_APPLY_CFG: + if (mm->perf_stats_en) + gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_start, + NULL); + /* + * We should have received successful CFG_VALIDATE_REPLY from + * all concerned Backend Clients by now. Send out the + * CFG_APPLY_REQs now. + */ + mgmt_txn_send_be_cfg_apply(txn); + break; + case MGMTD_COMMIT_PHASE_TXN_DELETE: + if (mm->perf_stats_en) + gettimeofday(&cmtcfg_req->cmt_stats->txn_del_start, + NULL); + /* + * We would have sent TXN_DELETE_REQ to all backend by now. + * Send a successful CONFIG_COMMIT_REPLY back to front-end. + * NOTE: This should also trigger DS merge/unlock and Txn + * cleanup. Please see mgmt_fe_send_commit_cfg_reply() for + * more details. + */ + EVENT_OFF(txn->comm_cfg_timeout); + mgmt_txn_send_commit_cfg_reply(txn, MGMTD_SUCCESS, NULL); + break; + case MGMTD_COMMIT_PHASE_MAX: + break; + } + + MGMTD_TXN_DBG("txn-id:%" PRIu64 " session-id: %" PRIu64 + " phase updated to (current:'%s', next: '%s')", + txn->txn_id, txn->session_id, + mgmt_txn_commit_phase_str(txn, true), + mgmt_txn_commit_phase_str(txn, false)); +} + +static void mgmt_init_get_data_reply(struct mgmt_get_data_reply *get_reply) +{ + size_t indx; + + for (indx = 0; indx < array_size(get_reply->reply_data); indx++) + get_reply->reply_datap[indx] = &get_reply->reply_data[indx]; +} + +static void mgmt_reset_get_data_reply(struct mgmt_get_data_reply *get_reply) +{ + int indx; + + for (indx = 0; indx < get_reply->num_reply; indx++) { + if (get_reply->reply_xpathp[indx]) { + free(get_reply->reply_xpathp[indx]); + get_reply->reply_xpathp[indx] = 0; + } + if (get_reply->reply_data[indx].xpath) { + zlog_debug("%s free xpath %p", __func__, + get_reply->reply_data[indx].xpath); + free(get_reply->reply_data[indx].xpath); + get_reply->reply_data[indx].xpath = 0; + } + } + + get_reply->num_reply = 0; + memset(&get_reply->data_reply, 0, sizeof(get_reply->data_reply)); + memset(&get_reply->reply_data, 0, sizeof(get_reply->reply_data)); + memset(&get_reply->reply_datap, 0, sizeof(get_reply->reply_datap)); + + memset(&get_reply->reply_value, 0, sizeof(get_reply->reply_value)); + + mgmt_init_get_data_reply(get_reply); +} + +static void mgmt_reset_get_data_reply_buf(struct mgmt_get_data_req *get_data) +{ + if (get_data->reply) + mgmt_reset_get_data_reply(get_data->reply); +} + +static void mgmt_txn_send_getcfg_reply_data(struct mgmt_txn_req *txn_req, + struct mgmt_get_data_req *get_req) +{ + struct mgmt_get_data_reply *get_reply; + Mgmtd__YangDataReply *data_reply; + + get_reply = get_req->reply; + if (!get_reply) + return; + + data_reply = &get_reply->data_reply; + mgmt_yang_data_reply_init(data_reply); + data_reply->n_data = get_reply->num_reply; + data_reply->data = get_reply->reply_datap; + data_reply->next_indx = (!get_reply->last_batch ? get_req->total_reply + : -1); + + MGMTD_TXN_DBG("Sending %zu Get-Config/Data replies next-index:%" PRId64, + data_reply->n_data, data_reply->next_indx); + + switch (txn_req->req_event) { + case MGMTD_TXN_PROC_GETCFG: + if (mgmt_fe_send_get_reply(txn_req->txn->session_id, + txn_req->txn->txn_id, get_req->ds_id, + txn_req->req_id, MGMTD_SUCCESS, + data_reply, NULL) != 0) { + MGMTD_TXN_ERR("Failed to send GET-CONFIG-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64 + " req-id: %" PRIu64, + txn_req->txn->txn_id, + txn_req->txn->session_id, txn_req->req_id); + } + break; + case MGMTD_TXN_PROC_GETDATA: + if (mgmt_fe_send_get_reply(txn_req->txn->session_id, + txn_req->txn->txn_id, get_req->ds_id, + txn_req->req_id, MGMTD_SUCCESS, + data_reply, NULL) != 0) { + MGMTD_TXN_ERR("Failed to send GET-DATA-REPLY txn-id: %" PRIu64 + " session-id: %" PRIu64 + " req-id: %" PRIu64, + txn_req->txn->txn_id, + txn_req->txn->session_id, txn_req->req_id); + } + break; + case MGMTD_TXN_PROC_SETCFG: + case MGMTD_TXN_PROC_COMMITCFG: + case MGMTD_TXN_COMMITCFG_TIMEOUT: + case MGMTD_TXN_CLEANUP: + MGMTD_TXN_ERR("Invalid Txn-Req-Event %u", txn_req->req_event); + break; + } + + /* + * Reset reply buffer for next reply. + */ + mgmt_reset_get_data_reply_buf(get_req); +} + +static void mgmt_txn_iter_and_send_get_cfg_reply(const char *xpath, + struct lyd_node *node, + struct nb_node *nb_node, + void *ctx) +{ + struct mgmt_txn_req *txn_req; + struct mgmt_get_data_req *get_req; + struct mgmt_get_data_reply *get_reply; + Mgmtd__YangData *data; + Mgmtd__YangDataValue *data_value; + + txn_req = (struct mgmt_txn_req *)ctx; + if (!txn_req) + return; + + if (!(node->schema->nodetype & LYD_NODE_TERM)) + return; + + assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG || + txn_req->req_event == MGMTD_TXN_PROC_GETDATA); + + get_req = txn_req->req.get_data; + assert(get_req); + get_reply = get_req->reply; + data = &get_reply->reply_data[get_reply->num_reply]; + data_value = &get_reply->reply_value[get_reply->num_reply]; + + mgmt_yang_data_init(data); + data->xpath = strdup(xpath); + mgmt_yang_data_value_init(data_value); + data_value->value_case = MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL; + data_value->encoded_str_val = (char *)lyd_get_value(node); + data->value = data_value; + + get_reply->num_reply++; + get_req->total_reply++; + MGMTD_TXN_DBG(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply, + data->xpath, data_value->encoded_str_val); + + if (get_reply->num_reply == MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH) + mgmt_txn_send_getcfg_reply_data(txn_req, get_req); +} + +static int mgmt_txn_get_config(struct mgmt_txn_ctx *txn, + struct mgmt_txn_req *txn_req, + struct nb_config *root) +{ + int indx; + struct mgmt_get_data_req *get_data; + struct mgmt_get_data_reply *get_reply; + + get_data = txn_req->req.get_data; + + if (!get_data->reply) { + get_data->reply = XCALLOC(MTYPE_MGMTD_TXN_GETDATA_REPLY, + sizeof(struct mgmt_get_data_reply)); + if (!get_data->reply) { + mgmt_fe_send_get_reply( + txn->session_id, txn->txn_id, get_data->ds_id, + txn_req->req_id, MGMTD_INTERNAL_ERROR, NULL, + "Internal error: Unable to allocate reply buffers!"); + goto mgmt_txn_get_config_failed; + } + } + + /* + * Read data contents from the DS and respond back directly. + * No need to go to backend for getting data. + */ + get_reply = get_data->reply; + for (indx = 0; indx < get_data->num_xpaths; indx++) { + MGMTD_TXN_DBG("Trying to get all data under '%s'", + get_data->xpaths[indx]); + mgmt_init_get_data_reply(get_reply); + /* + * mgmt_ds_iter_data works on path prefixes, but the user may + * want to also use an xpath regexp we need to add this + * functionality. + */ + if (mgmt_ds_iter_data(get_data->ds_id, root, + get_data->xpaths[indx], + mgmt_txn_iter_and_send_get_cfg_reply, + (void *)txn_req) == -1) { + MGMTD_TXN_DBG("Invalid Xpath '%s", + get_data->xpaths[indx]); + mgmt_fe_send_get_reply(txn->session_id, txn->txn_id, + get_data->ds_id, txn_req->req_id, + MGMTD_INTERNAL_ERROR, NULL, + "Invalid xpath"); + goto mgmt_txn_get_config_failed; + } + MGMTD_TXN_DBG("Got %d remaining data-replies for xpath '%s'", + get_reply->num_reply, get_data->xpaths[indx]); + get_reply->last_batch = true; + mgmt_txn_send_getcfg_reply_data(txn_req, get_data); + } + +mgmt_txn_get_config_failed: + + /* + * Delete the txn request. It will also remove it from request + * list. + */ + mgmt_txn_req_free(&txn_req); + + return 0; +} + +static void mgmt_txn_process_get_cfg(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + struct nb_config *cfg_root; + int num_processed = 0; + bool error; + + txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); + assert(txn); + + MGMTD_TXN_DBG("Processing %zu GET_CONFIG requests txn-id: %" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->get_cfg_reqs), txn->txn_id, + txn->session_id); + + FOREACH_TXN_REQ_IN_LIST (&txn->get_cfg_reqs, txn_req) { + error = false; + assert(txn_req->req_event == MGMTD_TXN_PROC_GETCFG); + cfg_root = txn_req->req.get_data->cfg_root; + assert(cfg_root); + + if (mgmt_txn_get_config(txn, txn_req, cfg_root) != 0) { + MGMTD_TXN_ERR("Unable to retrieve config from DS %d txn-id: %" PRIu64 + " session-id: %" PRIu64 + " req-id: %" PRIu64, + txn_req->req.get_data->ds_id, txn->txn_id, + txn->session_id, txn_req->req_id); + error = true; + } + + if (error) { + /* + * Delete the txn request. + * Note: The following will remove it from the list + * as well. + */ + mgmt_txn_req_free(&txn_req); + } + + /* + * Else the transaction would have been already deleted or + * moved to corresponding pending list. No need to delete it. + */ + num_processed++; + if (num_processed == MGMTD_TXN_MAX_NUM_GETCFG_PROC) + break; + } + + if (mgmt_txn_reqs_count(&txn->get_cfg_reqs)) { + MGMTD_TXN_DBG("Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_GETCFG_PROC); + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETCFG); + } +} + +static void mgmt_txn_process_get_data(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + int num_processed = 0; + + txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); + assert(txn); + + MGMTD_TXN_DBG("Processing %zu GET_DATA requests txn-id: %" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_reqs_count(&txn->get_data_reqs), txn->txn_id, + txn->session_id); + + FOREACH_TXN_REQ_IN_LIST (&txn->get_data_reqs, txn_req) { + assert(txn_req->req_event == MGMTD_TXN_PROC_GETDATA); + + /* + * TODO: Trigger GET procedures for Backend + * For now return back error. + */ + mgmt_fe_send_get_reply(txn->session_id, txn->txn_id, + txn_req->req.get_data->ds_id, + txn_req->req_id, MGMTD_INTERNAL_ERROR, + NULL, "GET-DATA is not supported yet!"); + /* + * Delete the txn request. + * Note: The following will remove it from the list + * as well. + */ + mgmt_txn_req_free(&txn_req); + + /* + * Else the transaction would have been already deleted or + * moved to corresponding pending list. No need to delete it. + */ + num_processed++; + if (num_processed == MGMTD_TXN_MAX_NUM_GETDATA_PROC) + break; + } + + if (mgmt_txn_reqs_count(&txn->get_data_reqs)) { + MGMTD_TXN_DBG("Processed maximum number of Get-Data requests (%d/%d). Rescheduling for rest.", + num_processed, MGMTD_TXN_MAX_NUM_GETDATA_PROC); + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_GETDATA); + } +} + +static struct mgmt_txn_ctx * +mgmt_fe_find_txn_by_session_id(struct mgmt_master *cm, uint64_t session_id, + enum mgmt_txn_type type) +{ + struct mgmt_txn_ctx *txn; + + FOREACH_TXN_IN_LIST (cm, txn) { + if (txn->session_id == session_id && txn->type == type) + return txn; + } + + return NULL; +} + +static struct mgmt_txn_ctx *mgmt_txn_create_new(uint64_t session_id, + enum mgmt_txn_type type) +{ + struct mgmt_txn_ctx *txn = NULL; + + /* + * For 'CONFIG' transaction check if one is already created + * or not. + */ + if (type == MGMTD_TXN_TYPE_CONFIG && mgmt_txn_mm->cfg_txn) { + if (mgmt_config_txn_in_progress() == session_id) + txn = mgmt_txn_mm->cfg_txn; + goto mgmt_create_txn_done; + } + + txn = mgmt_fe_find_txn_by_session_id(mgmt_txn_mm, session_id, type); + if (!txn) { + txn = XCALLOC(MTYPE_MGMTD_TXN, sizeof(struct mgmt_txn_ctx)); + assert(txn); + + txn->session_id = session_id; + txn->type = type; + mgmt_txns_add_tail(&mgmt_txn_mm->txn_list, txn); + mgmt_txn_reqs_init(&txn->set_cfg_reqs); + mgmt_txn_reqs_init(&txn->get_cfg_reqs); + mgmt_txn_reqs_init(&txn->get_data_reqs); + mgmt_txn_reqs_init(&txn->pending_get_datas); + txn->commit_cfg_req = NULL; + txn->refcount = 0; + if (!mgmt_txn_mm->next_txn_id) + mgmt_txn_mm->next_txn_id++; + txn->txn_id = mgmt_txn_mm->next_txn_id++; + hash_get(mgmt_txn_mm->txn_hash, txn, hash_alloc_intern); + + MGMTD_TXN_DBG("Added new '%s' txn-id: %" PRIu64, + mgmt_txn_type2str(type), txn->txn_id); + + if (type == MGMTD_TXN_TYPE_CONFIG) + mgmt_txn_mm->cfg_txn = txn; + + MGMTD_TXN_LOCK(txn); + } + +mgmt_create_txn_done: + return txn; +} + +static void mgmt_txn_delete(struct mgmt_txn_ctx **txn) +{ + MGMTD_TXN_UNLOCK(txn); +} + +static unsigned int mgmt_txn_hash_key(const void *data) +{ + const struct mgmt_txn_ctx *txn = data; + + return jhash2((uint32_t *)&txn->txn_id, + sizeof(txn->txn_id) / sizeof(uint32_t), 0); +} + +static bool mgmt_txn_hash_cmp(const void *d1, const void *d2) +{ + const struct mgmt_txn_ctx *txn1 = d1; + const struct mgmt_txn_ctx *txn2 = d2; + + return (txn1->txn_id == txn2->txn_id); +} + +static void mgmt_txn_hash_free(void *data) +{ + struct mgmt_txn_ctx *txn = data; + + mgmt_txn_delete(&txn); +} + +static void mgmt_txn_hash_init(void) +{ + if (!mgmt_txn_mm || mgmt_txn_mm->txn_hash) + return; + + mgmt_txn_mm->txn_hash = hash_create(mgmt_txn_hash_key, mgmt_txn_hash_cmp, + "MGMT Transactions"); +} + +static void mgmt_txn_hash_destroy(void) +{ + if (!mgmt_txn_mm || !mgmt_txn_mm->txn_hash) + return; + + hash_clean(mgmt_txn_mm->txn_hash, mgmt_txn_hash_free); + hash_free(mgmt_txn_mm->txn_hash); + mgmt_txn_mm->txn_hash = NULL; +} + +static inline struct mgmt_txn_ctx *mgmt_txn_id2ctx(uint64_t txn_id) +{ + struct mgmt_txn_ctx key = { 0 }; + struct mgmt_txn_ctx *txn; + + if (!mgmt_txn_mm || !mgmt_txn_mm->txn_hash) + return NULL; + + key.txn_id = txn_id; + txn = hash_lookup(mgmt_txn_mm->txn_hash, &key); + + return txn; +} + +static void mgmt_txn_lock(struct mgmt_txn_ctx *txn, const char *file, int line) +{ + txn->refcount++; + MGMTD_TXN_DBG("%s:%d --> Lock %s txn-id: %" PRIu64 " refcnt: %d", file, + line, mgmt_txn_type2str(txn->type), txn->txn_id, + txn->refcount); +} + +static void mgmt_txn_unlock(struct mgmt_txn_ctx **txn, const char *file, + int line) +{ + assert(*txn && (*txn)->refcount); + + (*txn)->refcount--; + MGMTD_TXN_DBG("%s:%d --> Unlock %s txn-id: %" PRIu64 " refcnt: %d", + file, line, mgmt_txn_type2str((*txn)->type), + (*txn)->txn_id, (*txn)->refcount); + if (!(*txn)->refcount) { + if ((*txn)->type == MGMTD_TXN_TYPE_CONFIG) + if (mgmt_txn_mm->cfg_txn == *txn) + mgmt_txn_mm->cfg_txn = NULL; + EVENT_OFF((*txn)->proc_get_cfg); + EVENT_OFF((*txn)->proc_get_data); + EVENT_OFF((*txn)->proc_comm_cfg); + EVENT_OFF((*txn)->comm_cfg_timeout); + hash_release(mgmt_txn_mm->txn_hash, *txn); + mgmt_txns_del(&mgmt_txn_mm->txn_list, *txn); + + MGMTD_TXN_DBG("Deleted %s txn-id: %" PRIu64 + " session-id: %" PRIu64, + mgmt_txn_type2str((*txn)->type), (*txn)->txn_id, + (*txn)->session_id); + + XFREE(MTYPE_MGMTD_TXN, *txn); + } + + *txn = NULL; +} + +static void mgmt_txn_cleanup_txn(struct mgmt_txn_ctx **txn) +{ + /* TODO: Any other cleanup applicable */ + + mgmt_txn_delete(txn); +} + +static void mgmt_txn_cleanup_all_txns(void) +{ + struct mgmt_txn_ctx *txn; + + if (!mgmt_txn_mm || !mgmt_txn_mm->txn_hash) + return; + + FOREACH_TXN_IN_LIST (mgmt_txn_mm, txn) + mgmt_txn_cleanup_txn(&txn); +} + +static void mgmt_txn_cleanup(struct event *thread) +{ + struct mgmt_txn_ctx *txn; + + txn = (struct mgmt_txn_ctx *)EVENT_ARG(thread); + assert(txn); + + mgmt_txn_cleanup_txn(&txn); +} + +static void mgmt_txn_register_event(struct mgmt_txn_ctx *txn, + enum mgmt_txn_event event) +{ + struct timeval tv = { .tv_sec = 0, + .tv_usec = MGMTD_TXN_PROC_DELAY_USEC }; + + assert(mgmt_txn_mm && mgmt_txn_tm); + + switch (event) { + case MGMTD_TXN_PROC_SETCFG: + event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_set_cfg, txn, + &tv, &txn->proc_set_cfg); + break; + case MGMTD_TXN_PROC_COMMITCFG: + event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_commit_cfg, + txn, &tv, &txn->proc_comm_cfg); + break; + case MGMTD_TXN_PROC_GETCFG: + event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_cfg, txn, + &tv, &txn->proc_get_cfg); + break; + case MGMTD_TXN_PROC_GETDATA: + event_add_timer_tv(mgmt_txn_tm, mgmt_txn_process_get_data, txn, + &tv, &txn->proc_get_data); + break; + case MGMTD_TXN_COMMITCFG_TIMEOUT: + event_add_timer_msec(mgmt_txn_tm, mgmt_txn_cfg_commit_timedout, + txn, MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC, + &txn->comm_cfg_timeout); + break; + case MGMTD_TXN_CLEANUP: + tv.tv_usec = MGMTD_TXN_CLEANUP_DELAY_USEC; + event_add_timer_tv(mgmt_txn_tm, mgmt_txn_cleanup, txn, &tv, + &txn->clnup); + } +} + +int mgmt_txn_init(struct mgmt_master *mm, struct event_loop *tm) +{ + if (mgmt_txn_mm || mgmt_txn_tm) + assert(!"MGMTD TXN: Call txn_init() only once"); + + mgmt_txn_mm = mm; + mgmt_txn_tm = tm; + mgmt_txns_init(&mm->txn_list); + mgmt_txn_hash_init(); + assert(!mm->cfg_txn); + mm->cfg_txn = NULL; + + return 0; +} + +void mgmt_txn_destroy(void) +{ + mgmt_txn_cleanup_all_txns(); + mgmt_txn_hash_destroy(); +} + +uint64_t mgmt_config_txn_in_progress(void) +{ + if (mgmt_txn_mm && mgmt_txn_mm->cfg_txn) + return mgmt_txn_mm->cfg_txn->session_id; + + return MGMTD_SESSION_ID_NONE; +} + +uint64_t mgmt_create_txn(uint64_t session_id, enum mgmt_txn_type type) +{ + struct mgmt_txn_ctx *txn; + + txn = mgmt_txn_create_new(session_id, type); + return txn ? txn->txn_id : MGMTD_TXN_ID_NONE; +} + +void mgmt_destroy_txn(uint64_t *txn_id) +{ + struct mgmt_txn_ctx *txn; + + txn = mgmt_txn_id2ctx(*txn_id); + if (!txn) + return; + + mgmt_txn_delete(&txn); + *txn_id = MGMTD_TXN_ID_NONE; +} + +int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, + struct mgmt_ds_ctx *ds_ctx, + Mgmtd__YangCfgDataReq **cfg_req, + size_t num_req, bool implicit_commit, + Mgmtd__DatastoreId dst_ds_id, + struct mgmt_ds_ctx *dst_ds_ctx) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + size_t indx; + uint16_t *num_chgs; + struct nb_cfg_change *cfg_chg; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn) + return -1; + + if (implicit_commit && mgmt_txn_reqs_count(&txn->set_cfg_reqs)) { + MGMTD_TXN_ERR( + "For implicit commit config only one SETCFG-REQ can be allowed!"); + return -1; + } + + txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_SETCFG); + txn_req->req.set_cfg->ds_id = ds_id; + txn_req->req.set_cfg->ds_ctx = ds_ctx; + num_chgs = &txn_req->req.set_cfg->num_cfg_changes; + for (indx = 0; indx < num_req; indx++) { + cfg_chg = &txn_req->req.set_cfg->cfg_changes[*num_chgs]; + + if (cfg_req[indx]->req_type == + MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA) + cfg_chg->operation = NB_OP_DESTROY; + else if (cfg_req[indx]->req_type == + MGMTD__CFG_DATA_REQ_TYPE__SET_DATA) + cfg_chg->operation = + mgmt_ds_find_data_node_by_xpath(ds_ctx, + cfg_req[indx] + ->data + ->xpath) + ? NB_OP_MODIFY + : NB_OP_CREATE; + else + continue; + + MGMTD_TXN_DBG("XPath: '%s', Value: '%s'", + cfg_req[indx]->data->xpath, + (cfg_req[indx]->data->value && + cfg_req[indx]->data->value->encoded_str_val + ? cfg_req[indx]->data->value->encoded_str_val + : "NULL")); + strlcpy(cfg_chg->xpath, cfg_req[indx]->data->xpath, + sizeof(cfg_chg->xpath)); + cfg_chg->value = + (cfg_req[indx]->data->value && + cfg_req[indx]->data->value->encoded_str_val + ? strdup(cfg_req[indx] + ->data->value->encoded_str_val) + : NULL); + if (cfg_chg->value) + MGMTD_TXN_DBG("Allocated value at %p ==> '%s'", + cfg_chg->value, cfg_chg->value); + + (*num_chgs)++; + } + txn_req->req.set_cfg->implicit_commit = implicit_commit; + txn_req->req.set_cfg->dst_ds_id = dst_ds_id; + txn_req->req.set_cfg->dst_ds_ctx = dst_ds_ctx; + txn_req->req.set_cfg->setcfg_stats = + mgmt_fe_get_session_setcfg_stats(txn->session_id); + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_SETCFG); + + return 0; +} + +int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id, + Mgmtd__DatastoreId src_ds_id, + struct mgmt_ds_ctx *src_ds_ctx, + Mgmtd__DatastoreId dst_ds_id, + struct mgmt_ds_ctx *dst_ds_ctx, + bool validate_only, bool abort, + bool implicit) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn) + return -1; + + if (txn->commit_cfg_req) { + MGMTD_TXN_ERR("Commit already in-progress txn-id: %" PRIu64 + " session-id: %" PRIu64 ". Cannot start another", + txn->txn_id, txn->session_id); + return -1; + } + + txn_req = mgmt_txn_req_alloc(txn, req_id, MGMTD_TXN_PROC_COMMITCFG); + txn_req->req.commit_cfg.src_ds_id = src_ds_id; + txn_req->req.commit_cfg.src_ds_ctx = src_ds_ctx; + txn_req->req.commit_cfg.dst_ds_id = dst_ds_id; + txn_req->req.commit_cfg.dst_ds_ctx = dst_ds_ctx; + txn_req->req.commit_cfg.validate_only = validate_only; + txn_req->req.commit_cfg.abort = abort; + txn_req->req.commit_cfg.implicit = implicit; + txn_req->req.commit_cfg.cmt_stats = + mgmt_fe_get_session_commit_stats(txn->session_id); + + /* + * Trigger a COMMIT-CONFIG process. + */ + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); + return 0; +} + +int mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, + bool connect) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + struct mgmt_commit_cfg_req *cmtcfg_req; + static struct mgmt_commit_stats dummy_stats; + struct nb_config_cbs *adapter_cfgs = NULL; + + memset(&dummy_stats, 0, sizeof(dummy_stats)); + if (connect) { + /* Get config for this single backend client */ + + mgmt_be_get_adapter_config(adapter, &adapter_cfgs); + if (!adapter_cfgs || RB_EMPTY(nb_config_cbs, adapter_cfgs)) { + SET_FLAG(adapter->flags, + MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + return 0; + } + + /* + * Create a CONFIG transaction to push the config changes + * provided to the backend client. + */ + txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG); + if (!txn) { + MGMTD_TXN_ERR("Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'", + adapter->name); + return -1; + } + + MGMTD_TXN_DBG("Created initial txn-id: %" PRIu64 + " for BE client '%s'", + txn->txn_id, adapter->name); + /* + * Set the changeset for transaction to commit and trigger the + * commit request. + */ + txn_req = mgmt_txn_req_alloc(txn, 0, MGMTD_TXN_PROC_COMMITCFG); + txn_req->req.commit_cfg.src_ds_id = MGMTD_DS_NONE; + txn_req->req.commit_cfg.src_ds_ctx = 0; + txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_NONE; + txn_req->req.commit_cfg.dst_ds_ctx = 0; + txn_req->req.commit_cfg.validate_only = false; + txn_req->req.commit_cfg.abort = false; + txn_req->req.commit_cfg.cmt_stats = &dummy_stats; + txn_req->req.commit_cfg.cfg_chgs = adapter_cfgs; + + /* + * Trigger a COMMIT-CONFIG process. + */ + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); + + } else { + /* + * Check if any transaction is currently on-going that + * involves this backend client. If so, report the transaction + * has failed. + */ + FOREACH_TXN_IN_LIST (mgmt_txn_mm, txn) { + /* TODO: update with operational state when that is + * completed */ + if (txn->type == MGMTD_TXN_TYPE_CONFIG) { + cmtcfg_req = txn->commit_cfg_req + ? &txn->commit_cfg_req->req + .commit_cfg + : NULL; + if (cmtcfg_req && + cmtcfg_req->subscr_info + .xpath_subscr[adapter->id]) { + mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Backend daemon disconnected while processing commit!"); + } + } + } + } + + return 0; +} + +int mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, + struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_commit_cfg_req *cmtcfg_req = NULL; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG) + return -1; + + if (!create && !txn->commit_cfg_req) + return 0; + + assert(txn->commit_cfg_req); + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + if (create) { + if (success) { + /* + * Done with TXN_CREATE. Move the backend client to + * next phase. + */ + assert(cmtcfg_req->curr_phase == + MGMTD_COMMIT_PHASE_TXN_CREATE); + + /* + * Send CFGDATA_CREATE-REQs to the backend immediately. + */ + mgmt_txn_send_be_cfg_data(txn, adapter); + } else { + mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + "Internal error! Failed to initiate transaction at backend!"); + } + } else { + /* + * Done with TXN_DELETE. Move the backend client to next phase. + */ + if (false) + mgmt_move_be_commit_to_next_phase(txn, adapter); + } + + return 0; +} + +int mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, + bool success, char *error_if_any, + struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_be_cfg_batch *batch; + struct mgmt_commit_cfg_req *cmtcfg_req; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG) + return -1; + + if (!txn->commit_cfg_req) + return -1; + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + + batch = mgmt_txn_cfgbatch_id2ctx(txn, batch_id); + if (!batch || batch->txn != txn) + return -1; + + if (!success) { + MGMTD_TXN_ERR("CFGDATA_CREATE_REQ sent to '%s' failed txn-id: %" PRIu64 + " batch-id %" PRIu64 " err: %s", + adapter->name, txn->txn_id, batch->batch_id, + error_if_any ? error_if_any : "None"); + mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + error_if_any + ? error_if_any + : "Internal error! Failed to download config data to backend!"); + return 0; + } + + MGMTD_TXN_DBG("CFGDATA_CREATE_REQ sent to '%s' was successful txn-id: %" PRIu64 + " batch-id %" PRIu64 " err: %s", + adapter->name, txn->txn_id, batch->batch_id, + error_if_any ? error_if_any : "None"); + mgmt_move_txn_cfg_batch_to_next(cmtcfg_req, batch, + &cmtcfg_req->curr_batches[adapter->id], + &cmtcfg_req->next_batches[adapter->id], + true, MGMTD_COMMIT_PHASE_APPLY_CFG); + + mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); + + return 0; +} + +int mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, + uint64_t batch_ids[], + size_t num_batch_ids, char *error_if_any, + struct mgmt_be_client_adapter *adapter) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_be_cfg_batch *batch; + struct mgmt_commit_cfg_req *cmtcfg_req = NULL; + size_t indx; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn || txn->type != MGMTD_TXN_TYPE_CONFIG || !txn->commit_cfg_req) + return -1; + + cmtcfg_req = &txn->commit_cfg_req->req.commit_cfg; + + if (!success) { + MGMTD_TXN_ERR("CFGDATA_APPLY_REQ sent to '%s' failed txn-id: %" PRIu64 + " batch ids %" PRIu64 " - %" PRIu64 " err: %s", + adapter->name, txn->txn_id, batch_ids[0], + batch_ids[num_batch_ids - 1], + error_if_any ? error_if_any : "None"); + mgmt_txn_send_commit_cfg_reply( + txn, MGMTD_INTERNAL_ERROR, + error_if_any + ? error_if_any + : "Internal error! Failed to apply config data on backend!"); + return 0; + } + + for (indx = 0; indx < num_batch_ids; indx++) { + batch = mgmt_txn_cfgbatch_id2ctx(txn, batch_ids[indx]); + if (batch->txn != txn) + return -1; + mgmt_move_txn_cfg_batch_to_next( + cmtcfg_req, batch, + &cmtcfg_req->curr_batches[adapter->id], + &cmtcfg_req->next_batches[adapter->id], true, + MGMTD_COMMIT_PHASE_TXN_DELETE); + } + + if (!mgmt_txn_batches_count(&cmtcfg_req->curr_batches[adapter->id])) { + /* + * All configuration for the specific backend has been applied. + * Send TXN-DELETE to wrap up the transaction for this backend. + */ + SET_FLAG(adapter->flags, MGMTD_BE_ADAPTER_FLAGS_CFG_SYNCED); + mgmt_txn_send_be_txn_delete(txn, adapter); + } + + mgmt_try_move_commit_to_next_phase(txn, cmtcfg_req); + if (mm->perf_stats_en) + gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_end, NULL); + + return 0; +} + +int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, struct nb_config *cfg_root, + Mgmtd__YangGetDataReq **data_req, size_t num_reqs) +{ + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + enum mgmt_txn_event req_event; + size_t indx; + + txn = mgmt_txn_id2ctx(txn_id); + if (!txn) + return -1; + + req_event = cfg_root ? MGMTD_TXN_PROC_GETCFG : MGMTD_TXN_PROC_GETDATA; + + txn_req = mgmt_txn_req_alloc(txn, req_id, req_event); + txn_req->req.get_data->ds_id = ds_id; + txn_req->req.get_data->cfg_root = cfg_root; + for (indx = 0; + indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH; + indx++) { + MGMTD_TXN_DBG("XPath: '%s'", data_req[indx]->data->xpath); + txn_req->req.get_data->xpaths[indx] = + strdup(data_req[indx]->data->xpath); + txn_req->req.get_data->num_xpaths++; + } + + mgmt_txn_register_event(txn, req_event); + + return 0; +} + +void mgmt_txn_status_write(struct vty *vty) +{ + struct mgmt_txn_ctx *txn; + + vty_out(vty, "MGMTD Transactions\n"); + + FOREACH_TXN_IN_LIST (mgmt_txn_mm, txn) { + vty_out(vty, " Txn: \t\t\t0x%p\n", txn); + vty_out(vty, " Txn-Id: \t\t\t%" PRIu64 "\n", txn->txn_id); + vty_out(vty, " Session-Id: \t\t%" PRIu64 "\n", + txn->session_id); + vty_out(vty, " Type: \t\t\t%s\n", + mgmt_txn_type2str(txn->type)); + vty_out(vty, " Ref-Count: \t\t\t%d\n", txn->refcount); + } + vty_out(vty, " Total: %d\n", + (int)mgmt_txns_count(&mgmt_txn_mm->txn_list)); +} + +int mgmt_txn_rollback_trigger_cfg_apply(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx) +{ + static struct nb_config_cbs changes; + static struct mgmt_commit_stats dummy_stats; + + struct nb_config_cbs *cfg_chgs = NULL; + struct mgmt_txn_ctx *txn; + struct mgmt_txn_req *txn_req; + + memset(&changes, 0, sizeof(changes)); + memset(&dummy_stats, 0, sizeof(dummy_stats)); + /* + * This could be the case when the config is directly + * loaded onto the candidate DS from a file. Get the + * diff from a full comparison of the candidate and + * running DSs. + */ + nb_config_diff(mgmt_ds_get_nb_config(dst_ds_ctx), + mgmt_ds_get_nb_config(src_ds_ctx), &changes); + cfg_chgs = &changes; + + if (RB_EMPTY(nb_config_cbs, cfg_chgs)) { + /* + * This means there's no changes to commit whatsoever + * is the source of the changes in config. + */ + return -1; + } + + /* + * Create a CONFIG transaction to push the config changes + * provided to the backend client. + */ + txn = mgmt_txn_create_new(0, MGMTD_TXN_TYPE_CONFIG); + if (!txn) { + MGMTD_TXN_ERR( + "Failed to create CONFIG Transaction for downloading CONFIGs"); + return -1; + } + + MGMTD_TXN_DBG("Created rollback txn-id: %" PRIu64, txn->txn_id); + + /* + * Set the changeset for transaction to commit and trigger the commit + * request. + */ + txn_req = mgmt_txn_req_alloc(txn, 0, MGMTD_TXN_PROC_COMMITCFG); + txn_req->req.commit_cfg.src_ds_id = MGMTD_DS_CANDIDATE; + txn_req->req.commit_cfg.src_ds_ctx = src_ds_ctx; + txn_req->req.commit_cfg.dst_ds_id = MGMTD_DS_RUNNING; + txn_req->req.commit_cfg.dst_ds_ctx = dst_ds_ctx; + txn_req->req.commit_cfg.validate_only = false; + txn_req->req.commit_cfg.abort = false; + txn_req->req.commit_cfg.rollback = true; + txn_req->req.commit_cfg.cmt_stats = &dummy_stats; + txn_req->req.commit_cfg.cfg_chgs = cfg_chgs; + + /* + * Trigger a COMMIT-CONFIG process. + */ + mgmt_txn_register_event(txn, MGMTD_TXN_PROC_COMMITCFG); + return 0; +} diff --git a/mgmtd/mgmt_txn.h b/mgmtd/mgmt_txn.h new file mode 100644 index 000000000000..068f07a5ca8a --- /dev/null +++ b/mgmtd/mgmt_txn.h @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD Transactions + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#ifndef _FRR_MGMTD_TXN_H_ +#define _FRR_MGMTD_TXN_H_ + +#include "mgmtd/mgmt_be_adapter.h" +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_ds.h" + +#define MGMTD_TXN_PROC_DELAY_MSEC 5 +#define MGMTD_TXN_PROC_DELAY_USEC 10 +#define MGMTD_TXN_MAX_NUM_SETCFG_PROC 128 +#define MGMTD_TXN_MAX_NUM_GETCFG_PROC 128 +#define MGMTD_TXN_MAX_NUM_GETDATA_PROC 128 + +#define MGMTD_TXN_SEND_CFGVALIDATE_DELAY_MSEC 100 +#define MGMTD_TXN_SEND_CFGAPPLY_DELAY_MSEC 100 +#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC 30000 /* 30 seconds */ + +#define MGMTD_TXN_CLEANUP_DELAY_MSEC 100 +#define MGMTD_TXN_CLEANUP_DELAY_USEC 10 + +/* + * The following definition enables local validation of config + * on the MGMTD process by loading client-defined NB callbacks + * and calling them locally before sening CNFG_APPLY_REQ to + * backend for actual apply of configuration on internal state + * of the backend application. + * + * #define MGMTD_LOCAL_VALIDATIONS_ENABLED + * + * Note: Enabled by default in configure.ac, if this needs to be + * disabled then pass --enable-mgmtd-local-validations=no to + * the list of arguments passed to ./configure + */ + +PREDECL_LIST(mgmt_txns); + +struct mgmt_master; + +enum mgmt_txn_type { + MGMTD_TXN_TYPE_NONE = 0, + MGMTD_TXN_TYPE_CONFIG, + MGMTD_TXN_TYPE_SHOW +}; + +static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type) +{ + switch (type) { + case MGMTD_TXN_TYPE_NONE: + return "None"; + case MGMTD_TXN_TYPE_CONFIG: + return "CONFIG"; + case MGMTD_TXN_TYPE_SHOW: + return "SHOW"; + } + + return "Unknown"; +} + +/* Initialise transaction module. */ +extern int mgmt_txn_init(struct mgmt_master *cm, struct event_loop *tm); + +/* Destroy the transaction module. */ +extern void mgmt_txn_destroy(void); + +/* + * Check if transaction is in progress. + * + * Returns: + * session ID if in-progress, MGMTD_SESSION_ID_NONE otherwise. + */ +extern uint64_t mgmt_config_txn_in_progress(void); + +/* + * Create transaction. + * + * session_id + * Session ID. + * + * type + * Transaction type (CONFIG/SHOW/NONE) + * + * Returns: + * transaction ID. + */ +extern uint64_t mgmt_create_txn(uint64_t session_id, enum mgmt_txn_type type); + +/* + * Destroy transaction. + * + * txn_id + * Unique transaction identifier. + */ +extern void mgmt_destroy_txn(uint64_t *txn_id); + +/* + * Send set-config request to be processed later in transaction. + * + * txn_id + * Unique transaction identifier. + * + * req_id + * Unique transaction request identifier. + * + * ds_id + * Datastore ID. + * + * ds_hndl + * Datastore handle. + * + * cfg_req + * Config requests. + * + * num_req + * Number of config requests. + * + * implicit_commit + * TRUE if the commit is implicit, FALSE otherwise. + * + * dst_ds_id + * Destination datastore ID. + * + * dst_ds_handle + * Destination datastore handle. + * + * Returns: + * 0 on success, -1 on failures. + */ +extern int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, + struct mgmt_ds_ctx *ds_ctx, + Mgmtd__YangCfgDataReq **cfg_req, + size_t num_req, bool implicit_commit, + Mgmtd__DatastoreId dst_ds_id, + struct mgmt_ds_ctx *dst_ds_ctx); + +/* + * Send commit-config request to be processed later in transaction. + * + * txn_id + * Unique transaction identifier. + * + * req_id + * Unique transaction request identifier. + * + * src_ds_id + * Source datastore ID. + * + * src_ds_hndl + * Source Datastore handle. + * + * validate_only + * TRUE if commit request needs to be validated only, FALSE otherwise. + * + * abort + * TRUE if need to restore Src DS back to Dest DS, FALSE otherwise. + * + * implicit + * TRUE if the commit is implicit, FALSE otherwise. + * + * Returns: + * 0 on success, -1 on failures. + */ +extern int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id, + Mgmtd__DatastoreId src_ds_id, + struct mgmt_ds_ctx *dst_ds_ctx, + Mgmtd__DatastoreId dst_ds_id, + struct mgmt_ds_ctx *src_ds_ctx, + bool validate_only, bool abort, + bool implicit); + +/* + * Send get-{cfg,data} request to be processed later in transaction. + * + * Is get-config if cfg_root is provided and the config is gathered locally, + * otherwise it's get-data and data is fetched from backedn clients. + */ +extern int mgmt_txn_send_get_req(uint64_t txn_id, uint64_t req_id, + Mgmtd__DatastoreId ds_id, + struct nb_config *cfg_root, + Mgmtd__YangGetDataReq **data_req, + size_t num_reqs); + +/* + * Notifiy backend adapter on connection. + */ +extern int +mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter, + bool connect); + +/* + * Reply to backend adapter about transaction create/delete. + */ +extern int +mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success, + struct mgmt_be_client_adapter *adapter); + +/* + * Reply to backend adapater with config data create request. + */ +extern int +mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id, + bool success, char *error_if_any, + struct mgmt_be_client_adapter *adapter); + +/* + * Reply to backend adapater with config data validate request. + */ +extern int mgmt_txn_notify_be_cfg_validate_reply( + uint64_t txn_id, bool success, uint64_t batch_ids[], + size_t num_batch_ids, char *error_if_any, + struct mgmt_be_client_adapter *adapter); + +/* + * Reply to backend adapater with config data apply request. + */ +extern int +mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success, + uint64_t batch_ids[], + size_t num_batch_ids, char *error_if_any, + struct mgmt_be_client_adapter *adapter); + +/* + * Dump transaction status to vty. + */ +extern void mgmt_txn_status_write(struct vty *vty); + +/* + * Trigger rollback config apply. + * + * Creates a new transaction and commit request for rollback. + */ +extern int +mgmt_txn_rollback_trigger_cfg_apply(struct mgmt_ds_ctx *src_ds_ctx, + struct mgmt_ds_ctx *dst_ds_ctx); +#endif /* _FRR_MGMTD_TXN_H_ */ diff --git a/mgmtd/mgmt_vty.c b/mgmtd/mgmt_vty.c new file mode 100644 index 000000000000..44c6c0097ad6 --- /dev/null +++ b/mgmtd/mgmt_vty.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * MGMTD VTY Interface + * + * Copyright (C) 2021 Vmware, Inc. + * Pushpasis Sarkar <spushpasis@vmware.com> + */ + +#include <zebra.h> + +#include "command.h" +#include "json.h" +#include "network.h" +#include "northbound_cli.h" + +#include "mgmtd/mgmt.h" +#include "mgmtd/mgmt_be_adapter.h" +#include "mgmtd/mgmt_fe_adapter.h" +#include "mgmtd/mgmt_ds.h" +#include "mgmtd/mgmt_history.h" + +#include "mgmtd/mgmt_vty_clippy.c" + +extern struct frr_daemon_info *mgmt_daemon_info; + +DEFPY(show_mgmt_be_adapter, + show_mgmt_be_adapter_cmd, + "show mgmt backend-adapter all", + SHOW_STR + MGMTD_STR + MGMTD_BE_ADAPTER_STR + "Display all Backend Adapters\n") +{ + mgmt_be_adapter_status_write(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_be_xpath_reg, + show_mgmt_be_xpath_reg_cmd, + "show mgmt backend-yang-xpath-registry", + SHOW_STR + MGMTD_STR + "Backend Adapter YANG Xpath Registry\n") +{ + mgmt_be_xpath_register_write(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_fe_adapter, show_mgmt_fe_adapter_cmd, + "show mgmt frontend-adapter all [detail$detail]", + SHOW_STR + MGMTD_STR + MGMTD_FE_ADAPTER_STR + "Display all Frontend Adapters\n" + "Display more details\n") +{ + mgmt_fe_adapter_status_write(vty, !!detail); + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(mgmt_performance_measurement, + mgmt_performance_measurement_cmd, + "[no] mgmt performance-measurement", + NO_STR + MGMTD_STR + "Enable performance measurement\n") +{ + if (no) + mgmt_fe_adapter_perf_measurement(vty, false); + else + mgmt_fe_adapter_perf_measurement(vty, true); + + return CMD_SUCCESS; +} + +DEFPY(mgmt_reset_performance_stats, + mgmt_reset_performance_stats_cmd, + "mgmt reset-statistics", + MGMTD_STR + "Reset the Performance measurement statistics\n") +{ + mgmt_fe_adapter_reset_perf_stats(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_txn, + show_mgmt_txn_cmd, + "show mgmt transaction all", + SHOW_STR + MGMTD_STR + MGMTD_TXN_STR + "Display all Transactions\n") +{ + mgmt_txn_status_write(vty); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_ds, + show_mgmt_ds_cmd, + "show mgmt datastore [all|candidate|operational|running]$dsname", + SHOW_STR + MGMTD_STR + MGMTD_DS_STR + "All datastores (default)\n" + "Candidate datastore\n" + "Operational datastore\n" + "Running datastore\n") +{ + struct mgmt_ds_ctx *ds_ctx; + + if (!dsname || dsname[0] == 'a') { + mgmt_ds_status_write(vty); + return CMD_SUCCESS; + } + ds_ctx = mgmt_ds_get_ctx_by_id(mm, mgmt_ds_name2id(dsname)); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access %s datastore!\n", dsname); + return CMD_ERR_NO_MATCH; + } + mgmt_ds_status_write_one(vty, ds_ctx); + + return CMD_SUCCESS; +} + +DEFPY(mgmt_commit, + mgmt_commit_cmd, + "mgmt commit <check|apply|abort>$type", + MGMTD_STR + "Commit action\n" + "Validate the set of config commands\n" + "Validate and apply the set of config commands\n" + "Abort and drop the set of config commands recently added\n") +{ + bool validate_only = type[0] == 'c'; + bool abort = type[1] == 'b'; + + if (vty_mgmt_send_commit_config(vty, validate_only, abort) != 0) + return CMD_WARNING_CONFIG_FAILED; + return CMD_SUCCESS; +} + +DEFPY(mgmt_set_config_data, mgmt_set_config_data_cmd, + "mgmt set-config WORD$path VALUE", + MGMTD_STR + "Set configuration data\n" + "XPath expression specifying the YANG data path\n" + "Value of the data to set\n") +{ + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = value; + vty->cfg_changes[0].operation = NB_OP_CREATE; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, false); + return CMD_SUCCESS; +} + +DEFPY(mgmt_delete_config_data, mgmt_delete_config_data_cmd, + "mgmt delete-config WORD$path", + MGMTD_STR + "Delete configuration data\n" + "XPath expression specifying the YANG data path\n") +{ + + strlcpy(vty->cfg_changes[0].xpath, path, + sizeof(vty->cfg_changes[0].xpath)); + vty->cfg_changes[0].value = NULL; + vty->cfg_changes[0].operation = NB_OP_DESTROY; + vty->num_cfg_changes = 1; + + vty_mgmt_send_config_data(vty, false); + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_get_config, show_mgmt_get_config_cmd, + "show mgmt get-config [candidate|operational|running]$dsname WORD$path", + SHOW_STR MGMTD_STR + "Get configuration data from a specific configuration datastore\n" + "Candidate datastore (default)\n" + "Operational datastore\n" + "Running datastore\n" + "XPath expression specifying the YANG data path\n") +{ + const char *xpath_list[VTY_MAXCFGCHANGES] = {0}; + Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE; + + if (dsname) + datastore = mgmt_ds_name2id(dsname); + + xpath_list[0] = path; + vty_mgmt_send_get_req(vty, true, datastore, xpath_list, 1); + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_get_data, show_mgmt_get_data_cmd, + "show mgmt get-data [candidate|operational|running]$dsname WORD$path", + SHOW_STR MGMTD_STR + "Get data from a specific datastore\n" + "Candidate datastore\n" + "Operational datastore (default)\n" + "Running datastore\n" + "XPath expression specifying the YANG data path\n") +{ + const char *xpath_list[VTY_MAXCFGCHANGES] = {0}; + Mgmtd__DatastoreId datastore = MGMTD_DS_OPERATIONAL; + + if (dsname) + datastore = mgmt_ds_name2id(dsname); + + xpath_list[0] = path; + vty_mgmt_send_get_req(vty, false, datastore, xpath_list, 1); + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_dump_data, + show_mgmt_dump_data_cmd, + "show mgmt datastore-contents [candidate|operational|running]$dsname [xpath WORD$path] [file WORD$filepath] <json|xml>$fmt", + SHOW_STR + MGMTD_STR + "Get Datastore contents from a specific datastore\n" + "Candidate datastore (default)\n" + "Operational datastore\n" + "Running datastore\n" + "XPath expression specifying the YANG data path\n" + "XPath string\n" + "Dump the contents to a file\n" + "Full path of the file\n" + "json output\n" + "xml output\n") +{ + struct mgmt_ds_ctx *ds_ctx; + Mgmtd__DatastoreId datastore = MGMTD_DS_CANDIDATE; + LYD_FORMAT format = fmt[0] == 'j' ? LYD_JSON : LYD_XML; + FILE *f = NULL; + + if (datastore) + datastore = mgmt_ds_name2id(dsname); + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access datastore!\n"); + return CMD_ERR_NO_MATCH; + } + + if (filepath) { + f = fopen(filepath, "w"); + if (!f) { + vty_out(vty, + "Could not open file pointed by filepath %s\n", + filepath); + return CMD_SUCCESS; + } + } + + mgmt_ds_dump_tree(vty, ds_ctx, path, f, format); + + if (f) + fclose(f); + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_map_xpath, + show_mgmt_map_xpath_cmd, + "show mgmt yang-xpath-subscription WORD$path", + SHOW_STR + MGMTD_STR + "Get YANG Backend Subscription\n" + "XPath expression specifying the YANG data path\n") +{ + mgmt_be_xpath_subscr_info_write(vty, path); + return CMD_SUCCESS; +} + +DEFPY(mgmt_load_config, + mgmt_load_config_cmd, + "mgmt load-config WORD$filepath <merge|replace>$type", + MGMTD_STR + "Load configuration onto Candidate Datastore\n" + "Full path of the file\n" + "Merge configuration with contents of Candidate Datastore\n" + "Replace the existing contents of Candidate datastore\n") +{ + bool merge = type[0] == 'm' ? true : false; + struct mgmt_ds_ctx *ds_ctx; + int ret; + + if (access(filepath, F_OK) == -1) { + vty_out(vty, "ERROR: File %s : %s\n", filepath, + strerror(errno)); + return CMD_ERR_NO_FILE; + } + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access Candidate datastore!\n"); + return CMD_ERR_NO_MATCH; + } + + ret = mgmt_ds_load_config_from_file(ds_ctx, filepath, merge); + if (ret != 0) + vty_out(vty, "Error with parsing the file with error code %d\n", + ret); + return CMD_SUCCESS; +} + +DEFPY(mgmt_save_config, + mgmt_save_config_cmd, + "mgmt save-config <candidate|running>$dsname WORD$filepath", + MGMTD_STR + "Save configuration from datastore\n" + "Candidate datastore\n" + "Running datastore\n" + "Full path of the file\n") +{ + Mgmtd__DatastoreId datastore = mgmt_ds_name2id(dsname); + struct mgmt_ds_ctx *ds_ctx; + FILE *f; + + ds_ctx = mgmt_ds_get_ctx_by_id(mm, datastore); + if (!ds_ctx) { + vty_out(vty, "ERROR: Could not access the '%s' datastore!\n", + dsname); + return CMD_ERR_NO_MATCH; + } + + if (!filepath) { + vty_out(vty, "ERROR: No file path mentioned!\n"); + return CMD_ERR_NO_MATCH; + } + + f = fopen(filepath, "w"); + if (!f) { + vty_out(vty, "Could not open file pointed by filepath %s\n", + filepath); + return CMD_SUCCESS; + } + + mgmt_ds_dump_tree(vty, ds_ctx, "/", f, LYD_JSON); + + fclose(f); + + return CMD_SUCCESS; +} + +DEFPY(show_mgmt_cmt_hist, + show_mgmt_cmt_hist_cmd, + "show mgmt commit-history", + SHOW_STR + MGMTD_STR + "Show commit history\n") +{ + show_mgmt_cmt_history(vty); + return CMD_SUCCESS; +} + +DEFPY(mgmt_rollback, + mgmt_rollback_cmd, + "mgmt rollback <commit-id WORD$commit | last [(1-10)]$last>", + MGMTD_STR + "Rollback commits\n" + "Rollback to commit ID\n" + "Commit-ID\n" + "Rollbak n commits\n" + "Number of commits\n") +{ + if (commit) + mgmt_history_rollback_by_id(vty, commit); + else + mgmt_history_rollback_n(vty, last); + + return CMD_SUCCESS; +} + +int config_write_mgmt_debug(struct vty *vty); +static struct cmd_node debug_node = { + .name = "debug", + .node = DEBUG_NODE, + .prompt = "", + .config_write = config_write_mgmt_debug, +}; + +static int write_mgmt_debug_helper(struct vty *vty, bool config) +{ + uint32_t mode = config ? DEBUG_MODE_CONF : DEBUG_MODE_ALL; + bool be = DEBUG_MODE_CHECK(&mgmt_debug_be, mode); + bool ds = DEBUG_MODE_CHECK(&mgmt_debug_ds, mode); + bool fe = DEBUG_MODE_CHECK(&mgmt_debug_fe, mode); + bool txn = DEBUG_MODE_CHECK(&mgmt_debug_txn, mode); + + if (!(be || ds || fe || txn)) + return 0; + + vty_out(vty, "debug mgmt"); + if (be) + vty_out(vty, " backend"); + if (ds) + vty_out(vty, " datastore"); + if (fe) + vty_out(vty, " frontend"); + if (txn) + vty_out(vty, " transaction"); + + vty_out(vty, "\n"); + + return 0; +} + +int config_write_mgmt_debug(struct vty *vty) +{ + return write_mgmt_debug_helper(vty, true); +} + +DEFPY_NOSH(show_debugging_mgmt, show_debugging_mgmt_cmd, + "show debugging [mgmt]", SHOW_STR DEBUG_STR "MGMT Information\n") +{ + vty_out(vty, "MGMT debugging status:\n"); + + write_mgmt_debug_helper(vty, false); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; +} + +DEFPY(debug_mgmt, debug_mgmt_cmd, + "[no$no] debug mgmt {backend$be|datastore$ds|frontend$fe|transaction$txn}", + NO_STR DEBUG_STR MGMTD_STR + "Backend debug\n" + "Datastore debug\n" + "Frontend debug\n" + "Transaction debug\n") +{ + uint32_t mode = DEBUG_NODE2MODE(vty->node); + + if (be) + DEBUG_MODE_SET(&mgmt_debug_be, mode, !no); + if (ds) + DEBUG_MODE_SET(&mgmt_debug_ds, mode, !no); + if (fe) + DEBUG_MODE_SET(&mgmt_debug_fe, mode, !no); + if (txn) + DEBUG_MODE_SET(&mgmt_debug_txn, mode, !no); + + return CMD_SUCCESS; +} + +static void mgmt_config_read_in(struct event *event) +{ + mgmt_vty_read_configs(); +} + +void mgmt_vty_init(void) +{ + /* + * Initialize command handling from VTYSH connection. + * Call command initialization routines defined by + * backend components that are moved to new MGMTD infra + * here one by one. + */ +#if HAVE_STATICD + extern void static_vty_init(void); + static_vty_init(); +#endif + + event_add_event(mm->master, mgmt_config_read_in, NULL, 0, + &mgmt_daemon_info->read_in); + + install_node(&debug_node); + + install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd); + install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd); + install_element(VIEW_NODE, &show_mgmt_fe_adapter_cmd); + install_element(VIEW_NODE, &show_mgmt_txn_cmd); + install_element(VIEW_NODE, &show_mgmt_ds_cmd); + install_element(VIEW_NODE, &show_mgmt_get_config_cmd); + install_element(VIEW_NODE, &show_mgmt_get_data_cmd); + install_element(VIEW_NODE, &show_mgmt_dump_data_cmd); + install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd); + install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd); + + install_element(CONFIG_NODE, &mgmt_commit_cmd); + install_element(CONFIG_NODE, &mgmt_set_config_data_cmd); + install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd); + install_element(CONFIG_NODE, &mgmt_load_config_cmd); + install_element(CONFIG_NODE, &mgmt_save_config_cmd); + install_element(CONFIG_NODE, &mgmt_rollback_cmd); + + install_element(VIEW_NODE, &debug_mgmt_cmd); + install_element(CONFIG_NODE, &debug_mgmt_cmd); + + /* Enable view */ + install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd); + install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd); + + install_element(ENABLE_NODE, &show_debugging_mgmt_cmd); + + mgmt_fe_client_lib_vty_init(); + /* + * TODO: Register and handlers for auto-completion here. + */ +} diff --git a/mgmtd/subdir.am b/mgmtd/subdir.am new file mode 100644 index 000000000000..67b45d5bd9bb --- /dev/null +++ b/mgmtd/subdir.am @@ -0,0 +1,68 @@ +# +# mgmtd -- Mangagement Daemon +# + +# dist_examples_DATA += \ + # end + +vtysh_daemons += mgmtd + +# man8 += $(MANBUILD)/frr-mgmtd.8 +# endif + +clippy_scan += \ + mgmtd/mgmt_vty.c \ + # end + +lib_LTLIBRARIES += mgmtd/libmgmt_be_nb.la +nodist_mgmtd_libmgmt_be_nb_la_SOURCES = \ + # end +mgmtd_libmgmt_be_nb_la_CFLAGS = $(AM_CFLAGS) -DINCLUDE_MGMTD_CMDDEFS_ONLY +mgmtd_libmgmt_be_nb_la_CPPFLAGS = $(AM_CPPFLAGS) -DINCLUDE_MGMTD_CMDDEFS_ONLY +mgmtd_libmgmt_be_nb_la_LDFLAGS = -version-info 0:0:0 + +noinst_LIBRARIES += mgmtd/libmgmtd.a +mgmtd_libmgmtd_a_SOURCES = \ + mgmtd/mgmt.c \ + mgmtd/mgmt_ds.c \ + mgmtd/mgmt_be_adapter.c \ + mgmtd/mgmt_fe_adapter.c \ + mgmtd/mgmt_history.c \ + mgmtd/mgmt_memory.c \ + mgmtd/mgmt_txn.c \ + mgmtd/mgmt_vty.c \ + # end + +mgmtdheaderdir = $(pkgincludedir)/mgmtd +mgmtdheader_HEADERS = \ + mgmtd/mgmt_defines.h \ + # end + +noinst_HEADERS += \ + mgmtd/mgmt.h \ + mgmtd/mgmt_be_adapter.h \ + mgmtd/mgmt_ds.h \ + mgmtd/mgmt_fe_adapter.h \ + mgmtd/mgmt_history.h \ + mgmtd/mgmt_memory.h \ + mgmtd/mgmt_txn.h \ + # end + +sbin_PROGRAMS += mgmtd/mgmtd + +mgmtd_mgmtd_SOURCES = \ + mgmtd/mgmt_main.c \ + # end +nodist_mgmtd_mgmtd_SOURCES = \ + # nothing +mgmtd_mgmtd_CFLAGS = $(AM_CFLAGS) -I ./ +mgmtd_mgmtd_LDADD = mgmtd/libmgmtd.a lib/libfrr.la $(LIBCAP) $(LIBM) $(LIBYANG_LIBS) $(UST_LIBS) +mgmtd_mgmtd_LDADD += mgmtd/libmgmt_be_nb.la + +if STATICD +nodist_mgmtd_mgmtd_SOURCES += \ + yang/frr-staticd.yang.c \ + yang/frr-bfdd.yang.c \ + # end +nodist_mgmtd_libmgmt_be_nb_la_SOURCES += staticd/static_vty.c +endif diff --git a/nhrpd/debug.h b/nhrpd/debug.h index e9428fa90a0e..f2c7022ad409 100644 --- a/nhrpd/debug.h +++ b/nhrpd/debug.h @@ -1,13 +1,5 @@ #include "log.h" -#if defined(__GNUC__) && (__GNUC__ >= 3) -#define likely(_x) __builtin_expect(!!(_x), 1) -#define unlikely(_x) __builtin_expect(!!(_x), 0) -#else -#define likely(_x) !!(_x) -#define unlikely(_x) !!(_x) -#endif - #define NHRP_DEBUG_COMMON (1 << 0) #define NHRP_DEBUG_KERNEL (1 << 1) #define NHRP_DEBUG_IF (1 << 2) diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c index 877659b4c00f..2e22f8e247a7 100644 --- a/nhrpd/netlink_arp.c +++ b/nhrpd/netlink_arp.c @@ -14,7 +14,7 @@ #include <linux/neighbour.h> #include <linux/netfilter/nfnetlink_log.h> -#include "thread.h" +#include "frrevent.h" #include "stream.h" #include "prefix.h" #include "nhrpd.h" @@ -23,7 +23,7 @@ int netlink_nflog_group; static int netlink_log_fd = -1; -static struct thread *netlink_log_thread; +static struct event *netlink_log_thread; void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma) @@ -96,10 +96,10 @@ static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb) nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl); } -static void netlink_log_recv(struct thread *t) +static void netlink_log_recv(struct event *t) { uint8_t buf[ZNL_BUFFER_SIZE]; - int fd = THREAD_FD(t); + int fd = EVENT_FD(t); struct zbuf payload, zb; struct nlmsghdr *n; @@ -118,14 +118,14 @@ static void netlink_log_recv(struct thread *t) } } - thread_add_read(master, netlink_log_recv, 0, netlink_log_fd, - &netlink_log_thread); + event_add_read(master, netlink_log_recv, 0, netlink_log_fd, + &netlink_log_thread); } void netlink_set_nflog_group(int nlgroup) { if (netlink_log_fd >= 0) { - thread_cancel(&netlink_log_thread); + event_cancel(&netlink_log_thread); close(netlink_log_fd); netlink_log_fd = -1; } @@ -136,8 +136,8 @@ void netlink_set_nflog_group(int nlgroup) return; netlink_log_register(netlink_log_fd, nlgroup); - thread_add_read(master, netlink_log_recv, 0, netlink_log_fd, - &netlink_log_thread); + event_add_read(master, netlink_log_recv, 0, netlink_log_fd, + &netlink_log_thread); } } diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c index 31b370cbf839..1a11e0d98ba7 100644 --- a/nhrpd/nhrp_cache.c +++ b/nhrpd/nhrp_cache.c @@ -5,7 +5,7 @@ #include "zebra.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "nhrpd.h" @@ -70,8 +70,10 @@ static void nhrp_cache_free(struct nhrp_cache *c) notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE); assert(!notifier_active(&c->notifier_list)); hash_release(nifp->cache_hash, c); - THREAD_OFF(c->t_timeout); - THREAD_OFF(c->t_auth); + nhrp_peer_unref(c->cur.peer); + nhrp_peer_unref(c->new.peer); + EVENT_OFF(c->t_timeout); + EVENT_OFF(c->t_auth); XFREE(MTYPE_NHRP_CACHE, c); } @@ -193,17 +195,17 @@ struct nhrp_cache *nhrp_cache_get(struct interface *ifp, create ? nhrp_cache_alloc : NULL); } -static void nhrp_cache_do_free(struct thread *t) +static void nhrp_cache_do_free(struct event *t) { - struct nhrp_cache *c = THREAD_ARG(t); + struct nhrp_cache *c = EVENT_ARG(t); c->t_timeout = NULL; nhrp_cache_free(c); } -static void nhrp_cache_do_timeout(struct thread *t) +static void nhrp_cache_do_timeout(struct event *t) { - struct nhrp_cache *c = THREAD_ARG(t); + struct nhrp_cache *c = EVENT_ARG(t); c->t_timeout = NULL; if (c->cur.type != NHRP_CACHE_INVALID) @@ -308,7 +310,7 @@ static void nhrp_cache_peer_notifier(struct notifier_block *n, static void nhrp_cache_reset_new(struct nhrp_cache *c) { - THREAD_OFF(c->t_auth); + EVENT_OFF(c->t_auth); if (notifier_list_anywhere(&c->newpeer_notifier)) nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier); nhrp_peer_unref(c->new.peer); @@ -318,13 +320,13 @@ static void nhrp_cache_reset_new(struct nhrp_cache *c) static void nhrp_cache_update_timers(struct nhrp_cache *c) { - THREAD_OFF(c->t_timeout); + EVENT_OFF(c->t_timeout); switch (c->cur.type) { case NHRP_CACHE_INVALID: if (!c->t_auth) - thread_add_timer_msec(master, nhrp_cache_do_free, c, 10, - &c->t_timeout); + event_add_timer_msec(master, nhrp_cache_do_free, c, 10, + &c->t_timeout); break; case NHRP_CACHE_INCOMPLETE: case NHRP_CACHE_NEGATIVE: @@ -335,9 +337,9 @@ static void nhrp_cache_update_timers(struct nhrp_cache *c) case NHRP_CACHE_LOCAL: case NHRP_CACHE_NUM_TYPES: if (c->cur.expires) - thread_add_timer(master, nhrp_cache_do_timeout, c, - c->cur.expires - monotime(NULL), - &c->t_timeout); + event_add_timer(master, nhrp_cache_do_timeout, c, + c->cur.expires - monotime(NULL), + &c->t_timeout); break; } } @@ -393,9 +395,9 @@ static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg) nhrp_cache_update_timers(c); } -static void nhrp_cache_do_auth_timeout(struct thread *t) +static void nhrp_cache_do_auth_timeout(struct event *t) { - struct nhrp_cache *c = THREAD_ARG(t); + struct nhrp_cache *c = EVENT_ARG(t); c->t_auth = NULL; nhrp_cache_authorize_binding(&c->eventid, (void *)"timeout"); } @@ -411,8 +413,8 @@ static void nhrp_cache_newpeer_notifier(struct notifier_block *n, if (nhrp_peer_check(c->new.peer, 1)) { evmgr_notify("authorize-binding", c, nhrp_cache_authorize_binding); - thread_add_timer(master, nhrp_cache_do_auth_timeout, c, - 10, &c->t_auth); + event_add_timer(master, nhrp_cache_do_auth_timeout, c, + 10, &c->t_auth); } break; case NOTIFY_PEER_DOWN: @@ -504,8 +506,8 @@ int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, nhrp_cache_newpeer_notifier); nhrp_cache_newpeer_notifier(&c->newpeer_notifier, NOTIFY_PEER_UP); - thread_add_timer(master, nhrp_cache_do_auth_timeout, c, - 60, &c->t_auth); + event_add_timer(master, nhrp_cache_do_auth_timeout, c, + 60, &c->t_auth); } } nhrp_cache_update_timers(c); diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c index 9c1189083152..ba318581d572 100644 --- a/nhrpd/nhrp_event.c +++ b/nhrpd/nhrp_event.c @@ -11,7 +11,7 @@ #include <sys/socket.h> #include <sys/un.h> -#include "thread.h" +#include "frrevent.h" #include "zbuf.h" #include "log.h" #include "nhrpd.h" @@ -20,19 +20,19 @@ const char *nhrp_event_socket_path; struct nhrp_reqid_pool nhrp_event_reqid; struct event_manager { - struct thread *t_reconnect, *t_read, *t_write; + struct event *t_reconnect, *t_read, *t_write; struct zbuf ibuf; struct zbuf_queue obuf; int fd; uint8_t ibuf_data[4 * 1024]; }; -static void evmgr_reconnect(struct thread *t); +static void evmgr_reconnect(struct event *t); static void evmgr_connection_error(struct event_manager *evmgr) { - THREAD_OFF(evmgr->t_read); - THREAD_OFF(evmgr->t_write); + EVENT_OFF(evmgr->t_read); + EVENT_OFF(evmgr->t_write); zbuf_reset(&evmgr->ibuf); zbufq_reset(&evmgr->obuf); @@ -40,8 +40,8 @@ static void evmgr_connection_error(struct event_manager *evmgr) close(evmgr->fd); evmgr->fd = -1; if (nhrp_event_socket_path) - thread_add_timer_msec(master, evmgr_reconnect, evmgr, 10, - &evmgr->t_reconnect); + event_add_timer_msec(master, evmgr_reconnect, evmgr, 10, + &evmgr->t_reconnect); } static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb) @@ -74,9 +74,9 @@ static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb) } } -static void evmgr_read(struct thread *t) +static void evmgr_read(struct event *t) { - struct event_manager *evmgr = THREAD_ARG(t); + struct event_manager *evmgr = EVENT_ARG(t); struct zbuf *ibuf = &evmgr->ibuf; struct zbuf msg; @@ -89,18 +89,18 @@ static void evmgr_read(struct thread *t) while (zbuf_may_pull_until(ibuf, "\n\n", &msg)) evmgr_recv_message(evmgr, &msg); - thread_add_read(master, evmgr_read, evmgr, evmgr->fd, &evmgr->t_read); + event_add_read(master, evmgr_read, evmgr, evmgr->fd, &evmgr->t_read); } -static void evmgr_write(struct thread *t) +static void evmgr_write(struct event *t) { - struct event_manager *evmgr = THREAD_ARG(t); + struct event_manager *evmgr = EVENT_ARG(t); int r; r = zbufq_write(&evmgr->obuf, evmgr->fd); if (r > 0) { - thread_add_write(master, evmgr_write, evmgr, evmgr->fd, - &evmgr->t_write); + event_add_write(master, evmgr_write, evmgr, evmgr->fd, + &evmgr->t_write); } else if (r < 0) { evmgr_connection_error(evmgr); } @@ -175,13 +175,13 @@ static void evmgr_submit(struct event_manager *evmgr, struct zbuf *obuf) zbuf_put(obuf, "\n", 1); zbufq_queue(&evmgr->obuf, obuf); if (evmgr->fd >= 0) - thread_add_write(master, evmgr_write, evmgr, evmgr->fd, - &evmgr->t_write); + event_add_write(master, evmgr_write, evmgr, evmgr->fd, + &evmgr->t_write); } -static void evmgr_reconnect(struct thread *t) +static void evmgr_reconnect(struct event *t) { - struct event_manager *evmgr = THREAD_ARG(t); + struct event_manager *evmgr = EVENT_ARG(t); int fd; if (evmgr->fd >= 0 || !nhrp_event_socket_path) @@ -192,14 +192,14 @@ static void evmgr_reconnect(struct thread *t) zlog_warn("%s: failure connecting nhrp-event socket: %s", __func__, strerror(errno)); zbufq_reset(&evmgr->obuf); - thread_add_timer(master, evmgr_reconnect, evmgr, 10, - &evmgr->t_reconnect); + event_add_timer(master, evmgr_reconnect, evmgr, 10, + &evmgr->t_reconnect); return; } zlog_info("Connected to Event Manager"); evmgr->fd = fd; - thread_add_read(master, evmgr_read, evmgr, evmgr->fd, &evmgr->t_read); + event_add_read(master, evmgr_read, evmgr, evmgr->fd, &evmgr->t_read); } static struct event_manager evmgr_connection; @@ -211,8 +211,8 @@ void evmgr_init(void) evmgr->fd = -1; zbuf_init(&evmgr->ibuf, evmgr->ibuf_data, sizeof(evmgr->ibuf_data), 0); zbufq_init(&evmgr->obuf); - thread_add_timer_msec(master, evmgr_reconnect, evmgr, 10, - &evmgr->t_reconnect); + event_add_timer_msec(master, evmgr_reconnect, evmgr, 10, + &evmgr->t_reconnect); } void evmgr_set_socket(const char *socket) diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c index d396f510ed81..7c84fde3675b 100644 --- a/nhrpd/nhrp_interface.c +++ b/nhrpd/nhrp_interface.c @@ -11,7 +11,7 @@ #include "zebra.h" #include "linklist.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "nhrpd.h" #include "os.h" diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c index 10d3c8f82fc8..593498ca1347 100644 --- a/nhrpd/nhrp_main.c +++ b/nhrpd/nhrp_main.c @@ -12,7 +12,7 @@ #include "zebra.h" #include "privs.h" #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "sigevent.h" #include "lib/version.h" #include "log.h" @@ -28,7 +28,7 @@ DEFINE_MGROUP(NHRPD, "NHRP"); unsigned int debug_flags = 0; -struct thread_master *master; +struct event_loop *master; struct timeval current_time; /* nhrpd options. */ diff --git a/nhrpd/nhrp_multicast.c b/nhrpd/nhrp_multicast.c index cdd79e25f4a3..fdc1a31f2519 100644 --- a/nhrpd/nhrp_multicast.c +++ b/nhrpd/nhrp_multicast.c @@ -18,7 +18,7 @@ #include <sys/types.h> #include <sys/socket.h> -#include "thread.h" +#include "frrevent.h" #include "nhrpd.h" #include "netlink.h" #include "znl.h" @@ -28,7 +28,7 @@ DEFINE_MTYPE_STATIC(NHRPD, NHRP_MULTICAST, "NHRP Multicast"); int netlink_mcast_nflog_group; static int netlink_mcast_log_fd = -1; -static struct thread *netlink_mcast_log_thread; +static struct event *netlink_mcast_log_thread; struct mcast_ctx { struct interface *ifp; @@ -138,10 +138,10 @@ static void netlink_mcast_log_handler(struct nlmsghdr *msg, struct zbuf *zb) } } -static void netlink_mcast_log_recv(struct thread *t) +static void netlink_mcast_log_recv(struct event *t) { uint8_t buf[65535]; /* Max OSPF Packet size */ - int fd = THREAD_FD(t); + int fd = EVENT_FD(t); struct zbuf payload, zb; struct nlmsghdr *n; @@ -160,8 +160,8 @@ static void netlink_mcast_log_recv(struct thread *t) } } - thread_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd, - &netlink_mcast_log_thread); + event_add_read(master, netlink_mcast_log_recv, 0, netlink_mcast_log_fd, + &netlink_mcast_log_thread); } static void netlink_mcast_log_register(int fd, int group) @@ -190,7 +190,7 @@ static void netlink_mcast_log_register(int fd, int group) void netlink_mcast_set_nflog_group(int nlgroup) { if (netlink_mcast_log_fd >= 0) { - THREAD_OFF(netlink_mcast_log_thread); + EVENT_OFF(netlink_mcast_log_thread); close(netlink_mcast_log_fd); netlink_mcast_log_fd = -1; debugf(NHRP_DEBUG_COMMON, "De-register nflog group"); @@ -202,9 +202,8 @@ void netlink_mcast_set_nflog_group(int nlgroup) return; netlink_mcast_log_register(netlink_mcast_log_fd, nlgroup); - thread_add_read(master, netlink_mcast_log_recv, 0, - netlink_mcast_log_fd, - &netlink_mcast_log_thread); + event_add_read(master, netlink_mcast_log_recv, 0, + netlink_mcast_log_fd, &netlink_mcast_log_thread); debugf(NHRP_DEBUG_COMMON, "Register nflog group: %d", netlink_mcast_nflog_group); } diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c index 49f881df1f6a..acd3b7df97f6 100644 --- a/nhrpd/nhrp_nhs.c +++ b/nhrpd/nhrp_nhs.c @@ -6,15 +6,15 @@ #include "zebra.h" #include "zbuf.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "nhrpd.h" #include "nhrp_protocol.h" DEFINE_MTYPE_STATIC(NHRPD, NHRP_NHS, "NHRP next hop server"); DEFINE_MTYPE_STATIC(NHRPD, NHRP_REGISTRATION, "NHRP registration entries"); -static void nhrp_nhs_resolve(struct thread *t); -static void nhrp_reg_send_req(struct thread *t); +static void nhrp_nhs_resolve(struct event *t); +static void nhrp_reg_send_req(struct event *t); static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) { @@ -88,12 +88,12 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) /* Success - schedule next registration, and route NHS */ r->timeout = 2; holdtime = nifp->afi[nhs->afi].holdtime; - THREAD_OFF(r->t_register); + EVENT_OFF(r->t_register); /* RFC 2332 5.2.3 - Registration is recommend to be renewed * every one third of holdtime */ - thread_add_timer(master, nhrp_reg_send_req, r, holdtime / 3, - &r->t_register); + event_add_timer(master, nhrp_reg_send_req, r, holdtime / 3, + &r->t_register); r->proto_addr = p->dst_proto; c = nhrp_cache_get(ifp, &p->dst_proto, 1); @@ -103,9 +103,9 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) &cie_nbma_nhs); } -static void nhrp_reg_timeout(struct thread *t) +static void nhrp_reg_timeout(struct event *t) { - struct nhrp_registration *r = THREAD_ARG(t); + struct nhrp_registration *r = EVENT_ARG(t); struct nhrp_cache *c; @@ -133,7 +133,7 @@ static void nhrp_reg_timeout(struct thread *t) } r->timeout = 2; } - thread_add_timer_msec(master, nhrp_reg_send_req, r, 10, &r->t_register); + event_add_timer_msec(master, nhrp_reg_send_req, r, 10, &r->t_register); } static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd) @@ -148,16 +148,16 @@ 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 %pSU", &r->peer->vc->remote.nbma); - THREAD_OFF(r->t_register); - thread_add_timer_msec(master, nhrp_reg_send_req, r, 10, - &r->t_register); + EVENT_OFF(r->t_register); + event_add_timer_msec(master, nhrp_reg_send_req, r, 10, + &r->t_register); break; } } -static void nhrp_reg_send_req(struct thread *t) +static void nhrp_reg_send_req(struct event *t) { - struct nhrp_registration *r = THREAD_ARG(t); + struct nhrp_registration *r = EVENT_ARG(t); struct nhrp_nhs *nhs = r->nhs; struct interface *ifp = nhs->ifp; struct nhrp_interface *nifp = ifp->info; @@ -171,13 +171,13 @@ static void nhrp_reg_send_req(struct thread *t) if (!nhrp_peer_check(r->peer, 2)) { debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %pSU", &r->peer->vc->remote.nbma); - thread_add_timer(master, nhrp_reg_send_req, r, 120, - &r->t_register); + event_add_timer(master, nhrp_reg_send_req, r, 120, + &r->t_register); return; } - thread_add_timer(master, nhrp_reg_timeout, r, r->timeout, - &r->t_register); + event_add_timer(master, nhrp_reg_timeout, r, r->timeout, + &r->t_register); /* RFC2332 5.2.3 NHC uses it's own address as dst if NHS is unknown */ dst_proto = &nhs->proto_addr; @@ -244,7 +244,7 @@ static void nhrp_reg_delete(struct nhrp_registration *r) nhrp_peer_notify_del(r->peer, &r->peer_notifier); nhrp_peer_unref(r->peer); nhrp_reglist_del(&r->nhs->reglist_head, r); - THREAD_OFF(r->t_register); + EVENT_OFF(r->t_register); XFREE(MTYPE_NHRP_REGISTRATION, r); } @@ -269,13 +269,13 @@ static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr, if (n < 0) { /* Failed, retry in a moment */ - thread_add_timer(master, nhrp_nhs_resolve, nhs, 5, - &nhs->t_resolve); + event_add_timer(master, nhrp_nhs_resolve, nhs, 5, + &nhs->t_resolve); return; } - thread_add_timer(master, nhrp_nhs_resolve, nhs, 2 * 60 * 60, - &nhs->t_resolve); + event_add_timer(master, nhrp_nhs_resolve, nhs, 2 * 60 * 60, + &nhs->t_resolve); frr_each (nhrp_reglist, &nhs->reglist_head, reg) reg->mark = 1; @@ -300,8 +300,8 @@ static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr, nhrp_reglist_add_tail(&nhs->reglist_head, reg); nhrp_peer_notify_add(reg->peer, ®->peer_notifier, nhrp_reg_peer_notify); - thread_add_timer_msec(master, nhrp_reg_send_req, reg, 50, - ®->t_register); + event_add_timer_msec(master, nhrp_reg_send_req, reg, 50, + ®->t_register); } frr_each_safe (nhrp_reglist, &nhs->reglist_head, reg) @@ -309,9 +309,9 @@ static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr, nhrp_reg_delete(reg); } -static void nhrp_nhs_resolve(struct thread *t) +static void nhrp_nhs_resolve(struct event *t) { - struct nhrp_nhs *nhs = THREAD_ARG(t); + struct nhrp_nhs *nhs = EVENT_ARG(t); resolver_resolve(&nhs->dns_resolve, AF_INET, VRF_DEFAULT, nhs->nbma_fqdn, nhrp_nhs_resolve_cb); @@ -347,8 +347,8 @@ int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, .reglist_head = INIT_DLIST(nhs->reglist_head), }; nhrp_nhslist_add_tail(&nifp->afi[afi].nhslist_head, nhs); - thread_add_timer_msec(master, nhrp_nhs_resolve, nhs, 1000, - &nhs->t_resolve); + event_add_timer_msec(master, nhrp_nhs_resolve, nhs, 1000, + &nhs->t_resolve); return NHRP_OK; } @@ -383,7 +383,7 @@ int nhrp_nhs_free(struct nhrp_interface *nifp, afi_t afi, struct nhrp_nhs *nhs) frr_each_safe (nhrp_reglist, &nhs->reglist_head, r) nhrp_reg_delete(r); - THREAD_OFF(nhs->t_resolve); + EVENT_OFF(nhs->t_resolve); nhrp_nhslist_del(&nifp->afi[afi].nhslist_head, nhs); free((void *)nhs->nbma_fqdn); XFREE(MTYPE_NHRP_NHS, nhs); @@ -420,6 +420,7 @@ void nhrp_nhs_terminate(void) &nifp->afi[afi].nhslist_head, nhs) nhrp_nhs_free(nifp, afi, nhs); } + nhrp_peer_interface_del(ifp); } } diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c index ecf8aa0b1322..9d0b30cfeea2 100644 --- a/nhrpd/nhrp_packet.c +++ b/nhrpd/nhrp_packet.c @@ -10,7 +10,7 @@ #include <netinet/if_ether.h> #include "nhrpd.h" #include "zbuf.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "nhrp_protocol.h" @@ -286,9 +286,9 @@ int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, return -1; } -static void nhrp_packet_recvraw(struct thread *t) +static void nhrp_packet_recvraw(struct event *t) { - int fd = THREAD_FD(t), ifindex; + int fd = EVENT_FD(t), ifindex; struct zbuf *zb; struct interface *ifp; struct nhrp_peer *p; @@ -296,7 +296,7 @@ static void nhrp_packet_recvraw(struct thread *t) uint8_t addr[64]; size_t len, addrlen; - thread_add_read(master, nhrp_packet_recvraw, 0, fd, NULL); + event_add_read(master, nhrp_packet_recvraw, 0, fd, NULL); zb = zbuf_alloc(1500); if (!zb) @@ -336,6 +336,6 @@ static void nhrp_packet_recvraw(struct thread *t) int nhrp_packet_init(void) { - thread_add_read(master, nhrp_packet_recvraw, 0, os_socket(), NULL); + event_add_read(master, nhrp_packet_recvraw, 0, os_socket(), NULL); return 0; } diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c index 0c9d5ade3765..ffb6cf750643 100644 --- a/nhrpd/nhrp_peer.c +++ b/nhrpd/nhrp_peer.c @@ -11,7 +11,7 @@ #include "zebra.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "network.h" @@ -43,17 +43,17 @@ static void nhrp_peer_check_delete(struct nhrp_peer *p) debugf(NHRP_DEBUG_COMMON, "Deleting peer ref:%d remote:%pSU local:%pSU", p->ref, &p->vc->remote.nbma, &p->vc->local.nbma); - THREAD_OFF(p->t_fallback); - THREAD_OFF(p->t_timer); + EVENT_OFF(p->t_fallback); + EVENT_OFF(p->t_timer); hash_release(nifp->peer_hash, p); nhrp_interface_notify_del(p->ifp, &p->ifp_notifier); nhrp_vc_notify_del(p->vc, &p->vc_notifier); XFREE(MTYPE_NHRP_PEER, p); } -static void nhrp_peer_notify_up(struct thread *t) +static void nhrp_peer_notify_up(struct event *t) { - struct nhrp_peer *p = THREAD_ARG(t); + struct nhrp_peer *p = EVENT_ARG(t); struct nhrp_vc *vc = p->vc; struct interface *ifp = p->ifp; struct nhrp_interface *nifp = ifp->info; @@ -76,14 +76,14 @@ static void __nhrp_peer_check(struct nhrp_peer *p) online = nifp->enabled && (!nifp->ipsec_profile || vc->ipsec); if (p->online != online) { - THREAD_OFF(p->t_fallback); + EVENT_OFF(p->t_fallback); if (online && notifier_active(&p->notifier_list)) { /* If we requested the IPsec connection, delay * the up notification a bit to allow things * settle down. This allows IKE to install * SPDs and SAs. */ - thread_add_timer_msec(master, nhrp_peer_notify_up, p, - 50, &p->t_fallback); + event_add_timer_msec(master, nhrp_peer_notify_up, p, 50, + &p->t_fallback); } else { nhrp_peer_ref(p); p->online = online; @@ -201,12 +201,7 @@ void nhrp_peer_interface_del(struct interface *ifp) debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted peer entries (%lu)", nifp->peer_hash ? nifp->peer_hash->count : 0); - if (nifp->peer_hash) { - hash_clean(nifp->peer_hash, do_peer_hash_free); - assert(nifp->peer_hash->count == 0); - hash_free(nifp->peer_hash); - nifp->peer_hash = NULL; - } + hash_clean_and_free(&nifp->peer_hash, do_peer_hash_free); } struct nhrp_peer *nhrp_peer_get(struct interface *ifp, @@ -253,9 +248,9 @@ void nhrp_peer_unref(struct nhrp_peer *p) } } -static void nhrp_peer_request_timeout(struct thread *t) +static void nhrp_peer_request_timeout(struct event *t) { - struct nhrp_peer *p = THREAD_ARG(t); + struct nhrp_peer *p = EVENT_ARG(t); struct nhrp_vc *vc = p->vc; struct interface *ifp = p->ifp; struct nhrp_interface *nifp = ifp->info; @@ -269,21 +264,21 @@ static void nhrp_peer_request_timeout(struct thread *t) p->fallback_requested = 1; vici_request_vc(nifp->ipsec_fallback_profile, &vc->local.nbma, &vc->remote.nbma, p->prio); - thread_add_timer(master, nhrp_peer_request_timeout, p, 30, - &p->t_fallback); + event_add_timer(master, nhrp_peer_request_timeout, p, 30, + &p->t_fallback); } else { p->requested = p->fallback_requested = 0; } } -static void nhrp_peer_defer_vici_request(struct thread *t) +static void nhrp_peer_defer_vici_request(struct event *t) { - struct nhrp_peer *p = THREAD_ARG(t); + struct nhrp_peer *p = EVENT_ARG(t); struct nhrp_vc *vc = p->vc; struct interface *ifp = p->ifp; struct nhrp_interface *nifp = ifp->info; - THREAD_OFF(p->t_timer); + EVENT_OFF(p->t_timer); if (p->online) { debugf(NHRP_DEBUG_COMMON, @@ -292,10 +287,10 @@ static void nhrp_peer_defer_vici_request(struct thread *t) } else { vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, p->prio); - thread_add_timer( - master, nhrp_peer_request_timeout, p, - (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30, - &p->t_fallback); + event_add_timer(master, nhrp_peer_request_timeout, p, + (nifp->ipsec_fallback_profile && !p->prio) ? 15 + : 30, + &p->t_fallback); } } @@ -325,10 +320,10 @@ int nhrp_peer_check(struct nhrp_peer *p, int establish) if (p->prio) { vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, p->prio); - thread_add_timer( - master, nhrp_peer_request_timeout, p, - (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30, - &p->t_fallback); + event_add_timer(master, nhrp_peer_request_timeout, p, + (nifp->ipsec_fallback_profile && !p->prio) ? 15 + : 30, + &p->t_fallback); } else { /* Maximum timeout is 1 second */ int r_time_ms = frr_weak_random() % 1000; @@ -336,8 +331,8 @@ int nhrp_peer_check(struct nhrp_peer *p, int establish) debugf(NHRP_DEBUG_COMMON, "Initiating IPsec connection request to %pSU after %d ms:", &vc->remote.nbma, r_time_ms); - thread_add_timer_msec(master, nhrp_peer_defer_vici_request, - p, r_time_ms, &p->t_timer); + event_add_timer_msec(master, nhrp_peer_defer_vici_request, p, + r_time_ms, &p->t_timer); } return 0; diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index bf7ba5f650bb..060e60314d73 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -108,11 +108,10 @@ 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) + const union sockunion *nexthop_ref, uint32_t mtu) { struct zapi_route api; struct zapi_nexthop *api_nh; - union sockunion *nexthop_ref = (union sockunion *)nexthop; if (zclient->sock < 0) return; @@ -125,9 +124,10 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, switch (type) { case NHRP_CACHE_NEGATIVE: + /* Fill in a blackhole nexthop */ zapi_route_set_blackhole(&api, BLACKHOLE_REJECT); ifp = NULL; - nexthop = NULL; + nexthop_ref = NULL; break; case NHRP_CACHE_DYNAMIC: case NHRP_CACHE_NHS: diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c index 90fb1d67c285..04dad2aea627 100644 --- a/nhrpd/nhrp_shortcut.c +++ b/nhrpd/nhrp_shortcut.c @@ -10,7 +10,7 @@ #include "nhrpd.h" #include "table.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "nhrp_protocol.h" @@ -18,7 +18,7 @@ DEFINE_MTYPE_STATIC(NHRPD, NHRP_SHORTCUT, "NHRP shortcut"); static struct route_table *shortcut_rib[AFI_MAX]; -static void nhrp_shortcut_do_purge(struct thread *t); +static void nhrp_shortcut_do_purge(struct event *t); static void nhrp_shortcut_delete(struct nhrp_shortcut *s); static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s); @@ -31,12 +31,12 @@ static void nhrp_shortcut_check_use(struct nhrp_shortcut *s) } } -static void nhrp_shortcut_do_expire(struct thread *t) +static void nhrp_shortcut_do_expire(struct event *t) { - struct nhrp_shortcut *s = THREAD_ARG(t); + struct nhrp_shortcut *s = EVENT_ARG(t); - thread_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3, - &s->t_timer); + event_add_timer(master, nhrp_shortcut_do_purge, s, s->holding_time / 3, + &s->t_timer); s->expiring = 1; nhrp_shortcut_check_use(s); } @@ -123,12 +123,12 @@ static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, s->route_installed = 0; } - THREAD_OFF(s->t_timer); + EVENT_OFF(s->t_timer); if (holding_time) { s->expiring = 0; s->holding_time = holding_time; - thread_add_timer(master, nhrp_shortcut_do_expire, s, - 2 * holding_time / 3, &s->t_timer); + event_add_timer(master, nhrp_shortcut_do_expire, s, + 2 * holding_time / 3, &s->t_timer); } } @@ -137,7 +137,7 @@ static void nhrp_shortcut_delete(struct nhrp_shortcut *s) struct route_node *rn; afi_t afi = family2afi(PREFIX_FAMILY(s->p)); - THREAD_OFF(s->t_timer); + EVENT_OFF(s->t_timer); nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); debugf(NHRP_DEBUG_ROUTE, "Shortcut %pFX purged", s->p); @@ -154,9 +154,9 @@ static void nhrp_shortcut_delete(struct nhrp_shortcut *s) } } -static void nhrp_shortcut_do_purge(struct thread *t) +static void nhrp_shortcut_do_purge(struct event *t) { - struct nhrp_shortcut *s = THREAD_ARG(t); + struct nhrp_shortcut *s = EVENT_ARG(t); s->t_timer = NULL; nhrp_shortcut_delete(s); } @@ -204,8 +204,8 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, int holding_time = pp->if_ad->holdtime; nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); - THREAD_OFF(s->t_timer); - thread_add_timer(master, nhrp_shortcut_do_purge, s, 1, &s->t_timer); + EVENT_OFF(s->t_timer); + event_add_timer(master, nhrp_shortcut_do_purge, s, 1, &s->t_timer); if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) { if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION @@ -454,9 +454,9 @@ void nhrp_shortcut_initiate(union sockunion *addr) s = nhrp_shortcut_get(&p); if (s && s->type != NHRP_CACHE_INCOMPLETE) { s->addr = *addr; - THREAD_OFF(s->t_timer); - thread_add_timer(master, nhrp_shortcut_do_purge, s, 30, - &s->t_timer); + EVENT_OFF(s->t_timer); + event_add_timer(master, nhrp_shortcut_do_purge, s, 30, + &s->t_timer); nhrp_shortcut_send_resolution_req(s); } } @@ -499,13 +499,13 @@ struct purge_ctx { void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force) { - THREAD_OFF(s->t_timer); + EVENT_OFF(s->t_timer); nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); if (force) { /* Immediate purge on route with draw or pending shortcut */ - thread_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5, - &s->t_timer); + event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 5, + &s->t_timer); } else { /* Soft expire - force immediate renewal, but purge * in few seconds to make sure stale route is not @@ -514,8 +514,8 @@ void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force) * This allows to keep nhrp route up, and to not * cause temporary rerouting via hubs causing latency * jitter. */ - thread_add_timer_msec(master, nhrp_shortcut_do_purge, s, 3000, - &s->t_timer); + event_add_timer_msec(master, nhrp_shortcut_do_purge, s, 3000, + &s->t_timer); s->expiring = 1; nhrp_shortcut_check_use(s); } diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c index bffb2f6e948d..2c3201438b83 100644 --- a/nhrpd/nhrp_vc.c +++ b/nhrpd/nhrp_vc.c @@ -7,7 +7,7 @@ #include "memory.h" #include "stream.h" #include "hash.h" -#include "thread.h" +#include "frrevent.h" #include "jhash.h" #include "nhrpd.h" diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h index c01296bee5f1..1421f0fc387a 100644 --- a/nhrpd/nhrpd.h +++ b/nhrpd/nhrpd.h @@ -19,7 +19,7 @@ DECLARE_MGROUP(NHRPD); #define NHRP_VTY_PORT 2610 #define NHRP_DEFAULT_CONFIG "nhrpd.conf" -extern struct thread_master *master; +extern struct event_loop *master; enum { NHRP_OK = 0, NHRP_ERR_FAIL, @@ -166,9 +166,9 @@ struct nhrp_peer { struct notifier_list notifier_list; struct interface *ifp; struct nhrp_vc *vc; - struct thread *t_fallback; + struct event *t_fallback; struct notifier_block vc_notifier, ifp_notifier; - struct thread *t_timer; + struct event *t_timer; }; struct nhrp_packet_parser { @@ -232,8 +232,8 @@ struct nhrp_cache { struct notifier_block newpeer_notifier; struct notifier_list notifier_list; struct nhrp_reqid eventid; - struct thread *t_timeout; - struct thread *t_auth; + struct event *t_timeout; + struct event *t_auth; struct { enum nhrp_cache_type type; @@ -251,7 +251,7 @@ struct nhrp_shortcut { union sockunion addr; struct nhrp_reqid reqid; - struct thread *t_timer; + struct event *t_timer; enum nhrp_cache_type type; unsigned int holding_time; @@ -275,7 +275,7 @@ struct nhrp_nhs { union sockunion proto_addr; const char *nbma_fqdn; /* IP-address or FQDN */ - struct thread *t_resolve; + struct event *t_resolve; struct resolver_query dns_resolve; struct nhrp_reglist_head reglist_head; }; @@ -293,7 +293,7 @@ DECLARE_DLIST(nhrp_mcastlist, struct nhrp_multicast, mcastlist_entry); struct nhrp_registration { struct nhrp_reglist_item reglist_entry; - struct thread *t_register; + struct event *t_register; struct nhrp_nhs *nhs; struct nhrp_reqid reqid; unsigned int timeout; diff --git a/nhrpd/vici.c b/nhrpd/vici.c index 1dbb4e4f5eb2..2f76362603ce 100644 --- a/nhrpd/vici.c +++ b/nhrpd/vici.c @@ -11,7 +11,7 @@ #include <sys/socket.h> #include <sys/un.h> -#include "thread.h" +#include "frrevent.h" #include "zbuf.h" #include "log.h" #include "lib_errors.h" @@ -44,7 +44,7 @@ static int blob2buf(const struct blob *b, char *buf, size_t n) } struct vici_conn { - struct thread *t_reconnect, *t_read, *t_write; + struct event *t_reconnect, *t_read, *t_write; struct zbuf ibuf; struct zbuf_queue obuf; int fd; @@ -56,7 +56,7 @@ struct vici_message_ctx { int nsections; }; -static void vici_reconnect(struct thread *t); +static void vici_reconnect(struct event *t); static void vici_submit_request(struct vici_conn *vici, const char *name, ...); static void vici_zbuf_puts(struct zbuf *obuf, const char *str) @@ -70,14 +70,14 @@ static void vici_connection_error(struct vici_conn *vici) { nhrp_vc_reset(); - THREAD_OFF(vici->t_read); - THREAD_OFF(vici->t_write); + EVENT_OFF(vici->t_read); + EVENT_OFF(vici->t_write); zbuf_reset(&vici->ibuf); zbufq_reset(&vici->obuf); close(vici->fd); vici->fd = -1; - thread_add_timer(master, vici_reconnect, vici, 2, &vici->t_reconnect); + event_add_timer(master, vici_reconnect, vici, 2, &vici->t_reconnect); } static void vici_parse_message(struct vici_conn *vici, struct zbuf *msg, @@ -357,9 +357,9 @@ static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg) } } -static void vici_read(struct thread *t) +static void vici_read(struct event *t) { - struct vici_conn *vici = THREAD_ARG(t); + struct vici_conn *vici = EVENT_ARG(t); struct zbuf *ibuf = &vici->ibuf; struct zbuf pktbuf; @@ -384,18 +384,18 @@ static void vici_read(struct thread *t) vici_recv_message(vici, &pktbuf); } while (1); - thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read); + event_add_read(master, vici_read, vici, vici->fd, &vici->t_read); } -static void vici_write(struct thread *t) +static void vici_write(struct event *t) { - struct vici_conn *vici = THREAD_ARG(t); + struct vici_conn *vici = EVENT_ARG(t); int r; r = zbufq_write(&vici->obuf, vici->fd); if (r > 0) { - thread_add_write(master, vici_write, vici, vici->fd, - &vici->t_write); + event_add_write(master, vici_write, vici, vici->fd, + &vici->t_write); } else if (r < 0) { vici_connection_error(vici); } @@ -409,7 +409,7 @@ static void vici_submit(struct vici_conn *vici, struct zbuf *obuf) } zbufq_queue(&vici->obuf, obuf); - thread_add_write(master, vici_write, vici, vici->fd, &vici->t_write); + event_add_write(master, vici_write, vici, vici->fd, &vici->t_write); } static void vici_submit_request(struct vici_conn *vici, const char *name, ...) @@ -500,9 +500,9 @@ static char *vici_get_charon_filepath(void) return buff; } -static void vici_reconnect(struct thread *t) +static void vici_reconnect(struct event *t) { - struct vici_conn *vici = THREAD_ARG(t); + struct vici_conn *vici = EVENT_ARG(t); int fd; char *file_path; @@ -519,14 +519,14 @@ static void vici_reconnect(struct thread *t) debugf(NHRP_DEBUG_VICI, "%s: failure connecting VICI socket: %s", __func__, strerror(errno)); - thread_add_timer(master, vici_reconnect, vici, 2, - &vici->t_reconnect); + event_add_timer(master, vici_reconnect, vici, 2, + &vici->t_reconnect); return; } debugf(NHRP_DEBUG_COMMON, "VICI: Connected"); vici->fd = fd; - thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read); + event_add_read(master, vici_read, vici, vici->fd, &vici->t_read); /* Send event subscribtions */ // vici_register_event(vici, "child-updown"); @@ -547,8 +547,8 @@ void vici_init(void) vici->fd = -1; zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0); zbufq_init(&vici->obuf); - thread_add_timer_msec(master, vici_reconnect, vici, 10, - &vici->t_reconnect); + event_add_timer_msec(master, vici_reconnect, vici, 10, + &vici->t_reconnect); } void vici_terminate(void) diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 0575f5abe1c6..f4202a4a29e7 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -12,7 +12,7 @@ #include "vty.h" #include "linklist.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "plist.h" #include "filter.h" @@ -515,11 +515,21 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, summary->path.origin.id = ADV_ROUTER_IN_PREFIX(&route->prefix); } else { + struct ospf6_lsa *old; + summary->path.origin.type = htons(OSPF6_LSTYPE_INTER_PREFIX); - summary->path.origin.id = ospf6_new_ls_id( - summary->path.origin.type, - summary->path.origin.adv_router, area->lsdb); + + /* Try to reuse LS-ID from previous running instance. */ + old = ospf6_find_inter_prefix_lsa(area->ospf6, area, + &route->prefix); + if (old) + summary->path.origin.id = old->header->id; + else + summary->path.origin.id = ospf6_new_ls_id( + summary->path.origin.type, + summary->path.origin.adv_router, + area->lsdb); } summary = ospf6_route_add(summary, summary_table); } else { @@ -1136,11 +1146,9 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) } if (CHECK_FLAG(prefix_lsa->prefix.prefix_options, - OSPF6_PREFIX_OPTION_NU) - || CHECK_FLAG(prefix_lsa->prefix.prefix_options, - OSPF6_PREFIX_OPTION_LA)) { + OSPF6_PREFIX_OPTION_NU)) { if (is_debug) - zlog_debug("Prefix has NU/LA bit set, ignore"); + zlog_debug("Prefix has the NU bit set, ignore"); if (old) ospf6_route_remove(old, table); return; @@ -1153,7 +1161,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa) if (!OSPF6_OPT_ISSET(router_lsa->options, OSPF6_OPT_R) || !OSPF6_OPT_ISSET(router_lsa->options, OSPF6_OPT_V6)) { if (is_debug) - zlog_debug("Prefix has NU/LA bit set, ignore"); + zlog_debug( + "Router-LSA has the V6-bit or R-bit unset, ignore"); if (old) ospf6_route_remove(old, table); diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 5795690a781e..69c711bf06e0 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -8,7 +8,7 @@ #include "log.h" #include "memory.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "if.h" @@ -421,8 +421,8 @@ void ospf6_area_disable(struct ospf6_area *oa) ospf6_spf_table_finish(oa->spf_table); ospf6_route_remove_all(oa->route_table); - THREAD_OFF(oa->thread_router_lsa); - THREAD_OFF(oa->thread_intra_prefix_lsa); + EVENT_OFF(oa->thread_router_lsa); + EVENT_OFF(oa->thread_intra_prefix_lsa); } diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index 62b32d376226..d9afd65d8127 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -60,8 +60,8 @@ struct ospf6_area { uint32_t spf_calculation; /* SPF calculation count */ - struct thread *thread_router_lsa; - struct thread *thread_intra_prefix_lsa; + struct event *thread_router_lsa; + struct event *thread_intra_prefix_lsa; uint32_t router_lsa_size_limit; /* Area announce list */ diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 05275c52eaa9..713dd820751c 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -13,7 +13,7 @@ #include "routemap.h" #include "table.h" #include "plist.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "lib/northbound_cli.h" @@ -180,13 +180,13 @@ struct ospf6_lsa *ospf6_as_external_lsa_originate(struct ospf6_route *route, return lsa; } -void ospf6_orig_as_external_lsa(struct thread *thread) +void ospf6_orig_as_external_lsa(struct event *thread) { struct ospf6_interface *oi; struct ospf6_lsa *lsa; uint32_t type, adv_router; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); if (oi->state == OSPF6_INTERFACE_DOWN) return; @@ -1065,9 +1065,9 @@ static void ospf6_asbr_routemap_unset(struct ospf6_redist *red) ROUTEMAP(red) = NULL; } -static void ospf6_asbr_routemap_update_timer(struct thread *thread) +static void ospf6_asbr_routemap_update_timer(struct event *thread) { - struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6 *ospf6 = EVENT_ARG(thread); struct ospf6_redist *red; int type; @@ -1104,15 +1104,14 @@ void ospf6_asbr_distribute_list_update(struct ospf6 *ospf6, { SET_FLAG(red->flag, OSPF6_IS_RMAP_CHANGED); - if (thread_is_scheduled(ospf6->t_distribute_update)) + if (event_is_scheduled(ospf6->t_distribute_update)) return; if (IS_OSPF6_DEBUG_ASBR) zlog_debug("%s: trigger redistribute reset thread", __func__); - thread_add_timer_msec(master, ospf6_asbr_routemap_update_timer, ospf6, - OSPF_MIN_LS_INTERVAL, - &ospf6->t_distribute_update); + event_add_timer_msec(master, ospf6_asbr_routemap_update_timer, ospf6, + OSPF_MIN_LS_INTERVAL, &ospf6->t_distribute_update); } void ospf6_asbr_routemap_update(const char *mapname) @@ -1380,8 +1379,8 @@ ospf6_external_aggr_match(struct ospf6 *ospf6, struct prefix *p) void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct prefix *prefix, unsigned int nexthop_num, - struct in6_addr *nexthop, route_tag_t tag, - struct ospf6 *ospf6) + const struct in6_addr *nexthop, + route_tag_t tag, struct ospf6 *ospf6) { route_map_result_t ret; struct ospf6_route troute; @@ -1469,9 +1468,13 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, info->type = type; - if (nexthop_num && nexthop) + if (nexthop_num && nexthop) { ospf6_route_add_nexthop(match, ifindex, nexthop); - else + if (!IN6_IS_ADDR_UNSPECIFIED(nexthop) + && !IN6_IS_ADDR_LINKLOCAL(nexthop)) + memcpy(&info->forwarding, nexthop, + sizeof(struct in6_addr)); + } else ospf6_route_add_nexthop(match, ifindex, NULL); match->path.origin.id = htonl(info->id); @@ -1515,9 +1518,13 @@ void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, } info->type = type; - if (nexthop_num && nexthop) + if (nexthop_num && nexthop) { ospf6_route_add_nexthop(route, ifindex, nexthop); - else + if (!IN6_IS_ADDR_UNSPECIFIED(nexthop) + && !IN6_IS_ADDR_LINKLOCAL(nexthop)) + memcpy(&info->forwarding, nexthop, + sizeof(struct in6_addr)); + } else ospf6_route_add_nexthop(route, ifindex, NULL); route = ospf6_route_add(route, ospf6->external_table); @@ -3015,9 +3022,9 @@ static void ospf6_aggr_handle_external_info(void *data) if (IS_OSPF6_DEBUG_AGGR) zlog_debug("%s: LSA found, refresh it", __func__); - THREAD_OFF(lsa->refresh); - thread_add_event(master, ospf6_lsa_refresh, lsa, 0, - &lsa->refresh); + EVENT_OFF(lsa->refresh); + event_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); return; } } @@ -3125,11 +3132,9 @@ static void ospf6_handle_external_aggr_update(struct ospf6 *ospf6) aggr->action = OSPF6_ROUTE_AGGR_NONE; ospf6_asbr_summary_config_delete(ospf6, rn); - if (OSPF6_EXTERNAL_RT_COUNT(aggr)) - hash_clean(aggr->match_extnl_hash, - ospf6_aggr_handle_external_info); + hash_clean_and_free(&aggr->match_extnl_hash, + ospf6_aggr_handle_external_info); - hash_free(aggr->match_extnl_hash); XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr); } else if (aggr->action == OSPF6_ROUTE_AGGR_MODIFY) { @@ -3167,18 +3172,14 @@ static void ospf6_aggr_unlink_external_info(void *data) void ospf6_external_aggregator_free(struct ospf6_external_aggr_rt *aggr) { - if (OSPF6_EXTERNAL_RT_COUNT(aggr)) - hash_clean(aggr->match_extnl_hash, - ospf6_aggr_unlink_external_info); + hash_clean_and_free(&aggr->match_extnl_hash, + ospf6_aggr_unlink_external_info); if (IS_OSPF6_DEBUG_AGGR) zlog_debug("%s: Release the aggregator Address(%pFX)", __func__, &aggr->p); - hash_free(aggr->match_extnl_hash); - aggr->match_extnl_hash = NULL; - XFREE(MTYPE_OSPF6_EXTERNAL_RT_AGGR, aggr); } @@ -3224,9 +3225,9 @@ static void ospf6_handle_exnl_rt_after_aggr_del(struct ospf6 *ospf6, lsa = ospf6_find_external_lsa(ospf6, &rt->prefix); if (lsa) { - THREAD_OFF(lsa->refresh); - thread_add_event(master, ospf6_lsa_refresh, lsa, 0, - &lsa->refresh); + EVENT_OFF(lsa->refresh); + event_add_event(master, ospf6_lsa_refresh, lsa, 0, + &lsa->refresh); } else { if (IS_OSPF6_DEBUG_AGGR) zlog_debug("%s: Originate external route(%pFX)", @@ -3330,9 +3331,9 @@ ospf6_handle_external_aggr_add(struct ospf6 *ospf6) } } -static void ospf6_asbr_summary_process(struct thread *thread) +static void ospf6_asbr_summary_process(struct event *thread) { - struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6 *ospf6 = EVENT_ARG(thread); int operation = 0; operation = ospf6->aggr_action; @@ -3362,7 +3363,7 @@ ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6, { aggr->action = operation; - if (thread_is_scheduled(ospf6->t_external_aggr)) { + if (event_is_scheduled(ospf6->t_external_aggr)) { if (ospf6->aggr_action == OSPF6_ROUTE_AGGR_ADD) { if (IS_OSPF6_DEBUG_AGGR) @@ -3375,7 +3376,7 @@ ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6, if (IS_OSPF6_DEBUG_AGGR) zlog_debug("%s, Restarting Aggregator delay timer.", __func__); - THREAD_OFF(ospf6->t_external_aggr); + EVENT_OFF(ospf6->t_external_aggr); } } @@ -3384,10 +3385,8 @@ ospf6_start_asbr_summary_delay_timer(struct ospf6 *ospf6, __func__, ospf6->aggr_delay_interval); ospf6->aggr_action = operation; - thread_add_timer(master, - ospf6_asbr_summary_process, - ospf6, ospf6->aggr_delay_interval, - &ospf6->t_external_aggr); + event_add_timer(master, ospf6_asbr_summary_process, ospf6, + ospf6->aggr_delay_interval, &ospf6->t_external_aggr); } int ospf6_asbr_external_rt_advertise(struct ospf6 *ospf6, diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 15982ca64c9e..d63e46727890 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -114,7 +114,7 @@ extern int ospf6_asbr_is_asbr(struct ospf6 *o); extern void ospf6_asbr_redistribute_add(int type, ifindex_t ifindex, struct prefix *prefix, unsigned int nexthop_num, - struct in6_addr *nexthop, + const struct in6_addr *nexthop, route_tag_t tag, struct ospf6 *ospf6); extern void ospf6_asbr_redistribute_remove(int type, ifindex_t ifindex, struct prefix *prefix, diff --git a/ospf6d/ospf6_auth_trailer.c b/ospf6d/ospf6_auth_trailer.c index 68817b5d085a..f98f092edbb7 100644 --- a/ospf6d/ospf6_auth_trailer.c +++ b/ospf6d/ospf6_auth_trailer.c @@ -604,20 +604,19 @@ void ospf6_auth_digest_send(struct in6_addr *src, struct ospf6_interface *oi, else return; - ospf6->seqnum_l++; if (ospf6->seqnum_l == 0xFFFFFFFF) { - ospf6->seqnum_h++; - ospf6->seqnum_l = 0; + if (ospf6->seqnum_h == 0xFFFFFFFF) { + /* Key must be reset, which is not handled as of now. */ + zlog_err("Sequence number wrapped; key must be reset."); + ospf6->seqnum_h = 0; + } else { + ospf6->seqnum_h++; + } ospf6_auth_seqno_nvm_update(ospf6); - } - /* Key must be reset. which is not handled as of now. */ - if ((ospf6->seqnum_l == 0xFFFFFFFF) - && (ospf6->seqnum_h == 0xFFFFFFFF)) { ospf6->seqnum_l = 0; - ospf6->seqnum_h = 0; - zlog_err( - "Both Higher and Lower sequence number has wrapped. Need to reset the key"); + } else { + ospf6->seqnum_l++; } memset(apad, 0, sizeof(apad)); diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index 6cc9c3013b24..6379f9d992c5 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -11,7 +11,7 @@ #include "linklist.h" #include "memory.h" #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "stream.h" #include "zclient.h" @@ -105,8 +105,8 @@ static void ospf6_bfd_callback(struct bfd_session_params *bsp, if (bss->state == BFD_STATUS_DOWN && bss->previous_state == BFD_STATUS_UP) { - THREAD_OFF(on->inactivity_timer); - thread_add_event(master, inactivity_timer, on, 0, NULL); + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); } } diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 2d2069566c60..fd5a4a426e0f 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -6,7 +6,7 @@ #include <zebra.h> #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "command.h" @@ -103,9 +103,9 @@ void ospf6_lsa_originate(struct ospf6 *ospf6, struct ospf6_lsa *lsa) lsdb_self = ospf6_get_scoped_lsdb_self(lsa); ospf6_lsdb_add(ospf6_lsa_copy(lsa), lsdb_self); - THREAD_OFF(lsa->refresh); - thread_add_timer(master, ospf6_lsa_refresh, lsa, OSPF_LS_REFRESH_TIME, - &lsa->refresh); + EVENT_OFF(lsa->refresh); + event_add_timer(master, ospf6_lsa_refresh, lsa, OSPF_LS_REFRESH_TIME, + &lsa->refresh); if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type) || IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type)) { @@ -168,8 +168,8 @@ void ospf6_lsa_purge(struct ospf6_lsa *lsa) self = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id, lsa->header->adv_router, lsdb_self); if (self) { - THREAD_OFF(self->expire); - THREAD_OFF(self->refresh); + EVENT_OFF(self->expire); + EVENT_OFF(self->refresh); ospf6_lsdb_remove(self, lsdb_self); } @@ -250,17 +250,17 @@ void ospf6_install_lsa(struct ospf6_lsa *lsa) lsa->name); lsa->external_lsa_id = old->external_lsa_id; } - THREAD_OFF(old->expire); - THREAD_OFF(old->refresh); + EVENT_OFF(old->expire); + EVENT_OFF(old->refresh); ospf6_flood_clear(old); } monotime(&now); if (!OSPF6_LSA_IS_MAXAGE(lsa)) { - thread_add_timer(master, ospf6_lsa_expire, lsa, - OSPF_LSA_MAXAGE + lsa->birth.tv_sec - - now.tv_sec, - &lsa->expire); + event_add_timer(master, ospf6_lsa_expire, lsa, + OSPF_LSA_MAXAGE + lsa->birth.tv_sec - + now.tv_sec, + &lsa->expire); } else lsa->expire = NULL; @@ -389,7 +389,7 @@ 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); - req = ospf6_lsa_unlock(req); + ospf6_lsa_unlock(&req); on->last_ls_req = NULL; } if (req) @@ -406,7 +406,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) { - req = ospf6_lsa_unlock(req); + ospf6_lsa_unlock(&req); on->last_ls_req = NULL; } if (req) @@ -473,10 +473,10 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, ospf6_lsdb_add(ospf6_lsa_copy(lsa), on->retrans_list); - thread_add_timer( - master, ospf6_lsupdate_send_neighbor, - on, on->ospf6_if->rxmt_interval, - &on->thread_send_lsupdate); + event_add_timer(master, + ospf6_lsupdate_send_neighbor, + on, on->ospf6_if->rxmt_interval, + &on->thread_send_lsupdate); retrans_added++; } } @@ -520,14 +520,14 @@ void ospf6_flood_interface(struct ospf6_neighbor *from, struct ospf6_lsa *lsa, if ((oi->type == OSPF_IFTYPE_BROADCAST) || (oi->type == OSPF_IFTYPE_POINTOPOINT)) { ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsupdate_list); - thread_add_event(master, ospf6_lsupdate_send_interface, oi, 0, - &oi->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_interface, oi, 0, + &oi->thread_send_lsupdate); } else { /* reschedule retransmissions to all neighbors */ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { - THREAD_OFF(on->thread_send_lsupdate); - thread_add_event(master, ospf6_lsupdate_send_neighbor, - on, 0, &on->thread_send_lsupdate); + EVENT_OFF(on->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_neighbor, + on, 0, &on->thread_send_lsupdate); } } } @@ -697,8 +697,8 @@ static void ospf6_acknowledge_lsa_bdrouter(struct ospf6_lsa *lsa, "Delayed acknowledgement (BDR & MoreRecent & from DR)"); /* Delayed acknowledgement */ ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsack_list); - thread_add_timer(master, ospf6_lsack_send_interface, oi, - 3, &oi->thread_send_lsack); + event_add_timer(master, ospf6_lsack_send_interface, oi, + 3, &oi->thread_send_lsack); } else { if (is_debug) zlog_debug( @@ -718,8 +718,8 @@ static void ospf6_acknowledge_lsa_bdrouter(struct ospf6_lsa *lsa, "Delayed acknowledgement (BDR & Duplicate & ImpliedAck & from DR)"); /* Delayed acknowledgement */ ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsack_list); - thread_add_timer(master, ospf6_lsack_send_interface, oi, - 3, &oi->thread_send_lsack); + event_add_timer(master, ospf6_lsack_send_interface, oi, + 3, &oi->thread_send_lsack); } else { if (is_debug) zlog_debug( @@ -736,8 +736,8 @@ static void ospf6_acknowledge_lsa_bdrouter(struct ospf6_lsa *lsa, if (is_debug) zlog_debug("Direct acknowledgement (BDR & Duplicate)"); ospf6_lsdb_add(ospf6_lsa_copy(lsa), from->lsack_list); - thread_add_event(master, ospf6_lsack_send_neighbor, from, 0, - &from->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_neighbor, from, 0, + &from->thread_send_lsack); return; } @@ -778,8 +778,8 @@ static void ospf6_acknowledge_lsa_allother(struct ospf6_lsa *lsa, "Delayed acknowledgement (AllOther & MoreRecent)"); /* Delayed acknowledgement */ ospf6_lsdb_add(ospf6_lsa_copy(lsa), oi->lsack_list); - thread_add_timer(master, ospf6_lsack_send_interface, oi, 3, - &oi->thread_send_lsack); + event_add_timer(master, ospf6_lsack_send_interface, oi, 3, + &oi->thread_send_lsack); return; } @@ -802,8 +802,8 @@ static void ospf6_acknowledge_lsa_allother(struct ospf6_lsa *lsa, zlog_debug( "Direct acknowledgement (AllOther & Duplicate)"); ospf6_lsdb_add(ospf6_lsa_copy(lsa), from->lsack_list); - thread_add_event(master, ospf6_lsack_send_neighbor, from, 0, - &from->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_neighbor, from, 0, + &from->thread_send_lsack); return; } @@ -921,7 +921,11 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, /* (1) LSA Checksum */ if (!ospf6_lsa_checksum_valid(new->header)) { if (is_debug) - zlog_debug("Wrong LSA Checksum, discard"); + zlog_debug( + "Wrong LSA Checksum %s (Router-ID: %pI4) [Type:%s Checksum:%#06hx), discard", + from->name, &from->router_id, + ospf6_lstype_name(new->header->type), + ntohs(new->header->checksum)); ospf6_lsa_delete(new); return; } @@ -969,8 +973,8 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, /* a) Acknowledge back to neighbor (Direct acknowledgement, * 13.5) */ ospf6_lsdb_add(ospf6_lsa_copy(new), from->lsack_list); - thread_add_event(master, ospf6_lsack_send_neighbor, from, 0, - &from->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_neighbor, from, 0, + &from->thread_send_lsack); /* b) Discard */ ospf6_lsa_delete(new); @@ -1101,13 +1105,16 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, "Newer instance of the self-originated LSA"); zlog_debug("Schedule reorigination"); } - thread_add_event(master, ospf6_lsa_refresh, new, 0, - &new->refresh); + event_add_event(master, ospf6_lsa_refresh, new, 0, + &new->refresh); } + /* GR: check for network topology change. */ struct ospf6 *ospf6 = from->ospf6_if->area->ospf6; struct ospf6_area *area = from->ospf6_if->area; - if (ospf6->gr_info.restart_in_progress) + if (ospf6->gr_info.restart_in_progress && + (new->header->type == ntohs(OSPF6_LSTYPE_ROUTER) || + new->header->type == ntohs(OSPF6_LSTYPE_NETWORK))) ospf6_gr_check_lsdb_consistency(ospf6, area); return; @@ -1127,7 +1134,7 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, new->name); /* BadLSReq */ - thread_add_event(master, bad_lsreq, from, 0, NULL); + event_add_event(master, bad_lsreq, from, 0, NULL); ospf6_lsa_delete(new); return; @@ -1224,8 +1231,8 @@ void ospf6_receive_lsa(struct ospf6_neighbor *from, ospf6_lsdb_add(ospf6_lsa_copy(old), from->lsupdate_list); - thread_add_event(master, ospf6_lsupdate_send_neighbor, - from, 0, &from->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_neighbor, + from, 0, &from->thread_send_lsupdate); ospf6_lsa_delete(new); return; diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index c182e48f2e7a..69230e572b06 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -14,6 +14,7 @@ #include "log.h" #include "hook.h" #include "printfrr.h" +#include "lib_errors.h" #include "ospf6d/ospf6_lsa.h" #include "ospf6d/ospf6_lsdb.h" @@ -25,21 +26,25 @@ #include "ospf6d/ospf6_zebra.h" #include "ospf6d/ospf6_message.h" #include "ospf6d/ospf6_neighbor.h" +#include "ospf6d/ospf6_network.h" #include "ospf6d/ospf6_flood.h" #include "ospf6d/ospf6_intra.h" #include "ospf6d/ospf6_spf.h" #include "ospf6d/ospf6_gr.h" #include "ospf6d/ospf6_gr_clippy.c" -static void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +static void ospf6_gr_grace_period_expired(struct event *thread); /* Originate and install Grace-LSA for a given interface. */ -static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) +static int ospf6_gr_lsa_originate(struct ospf6_interface *oi, + enum ospf6_gr_restart_reason reason) { - struct ospf6_gr_info *gr_info = &oi->area->ospf6->gr_info; + struct ospf6 *ospf6 = oi->area->ospf6; + struct ospf6_gr_info *gr_info = &ospf6->gr_info; struct ospf6_lsa_header *lsa_header; struct ospf6_grace_lsa *grace_lsa; struct ospf6_lsa *lsa; + uint16_t lsa_length; char buffer[OSPF6_MAX_LSASIZE]; if (IS_OSPF6_DEBUG_ORIGINATE(LINK)) @@ -61,29 +66,59 @@ static int ospf6_gr_lsa_originate(struct ospf6_interface *oi) /* Put restart reason. */ grace_lsa->tlv_reason.header.type = htons(RESTART_REASON_TYPE); grace_lsa->tlv_reason.header.length = htons(RESTART_REASON_LENGTH); - if (gr_info->restart_support) - grace_lsa->tlv_reason.reason = OSPF6_GR_SW_RESTART; - else - grace_lsa->tlv_reason.reason = OSPF6_GR_UNKNOWN_RESTART; + grace_lsa->tlv_reason.reason = reason; /* Fill LSA Header */ + lsa_length = sizeof(*lsa_header) + sizeof(*grace_lsa); lsa_header->age = 0; lsa_header->type = htons(OSPF6_LSTYPE_GRACE_LSA); lsa_header->id = htonl(oi->interface->ifindex); - lsa_header->adv_router = oi->area->ospf6->router_id; + lsa_header->adv_router = ospf6->router_id; lsa_header->seqnum = ospf6_new_ls_seqnum(lsa_header->type, lsa_header->id, lsa_header->adv_router, oi->lsdb); - lsa_header->length = htons(sizeof(*lsa_header) + sizeof(*grace_lsa)); + lsa_header->length = htons(lsa_length); /* LSA checksum */ ospf6_lsa_checksum(lsa_header); - /* create LSA */ - lsa = ospf6_lsa_create(lsa_header); - - /* Originate */ - ospf6_lsa_originate_interface(lsa, oi); + if (reason == OSPF6_GR_UNKNOWN_RESTART) { + struct ospf6_header *oh; + uint32_t *uv32; + int n; + uint16_t length = OSPF6_HEADER_SIZE + 4 + lsa_length; + struct iovec iovector[2] = {}; + + /* Reserve space for OSPFv3 header. */ + memmove(&buffer[OSPF6_HEADER_SIZE + 4], buffer, lsa_length); + + /* Fill in the OSPFv3 header. */ + oh = (struct ospf6_header *)buffer; + oh->version = OSPFV3_VERSION; + oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE; + oh->router_id = oi->area->ospf6->router_id; + oh->area_id = oi->area->area_id; + oh->instance_id = oi->instance_id; + oh->reserved = 0; + oh->length = htons(length); + + /* Fill LSA header. */ + uv32 = (uint32_t *)&buffer[sizeof(*oh)]; + *uv32 = htonl(1); + + /* Send packet. */ + iovector[0].iov_base = lsa_header; + iovector[0].iov_len = length; + n = ospf6_sendmsg(oi->linklocal_addr, &allspfrouters6, + oi->interface->ifindex, iovector, ospf6->fd); + if (n != length) + flog_err(EC_LIB_DEVELOPMENT, + "%s: could not send entire message", __func__); + } else { + /* Create and install LSA. */ + lsa = ospf6_lsa_create(lsa_header); + ospf6_lsa_originate_interface(lsa, oi); + } return 0; } @@ -127,16 +162,16 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) { struct ospf6_area *area; struct listnode *onode, *anode; + struct ospf6_route *route; if (IS_DEBUG_OSPF6_GR) zlog_debug("GR: exiting graceful restart: %s", reason); ospf6->gr_info.restart_in_progress = false; ospf6->gr_info.finishing_restart = true; - THREAD_OFF(ospf6->gr_info.t_grace_period); - - /* Record in non-volatile memory that the restart is complete. */ - ospf6_gr_nvm_delete(ospf6); + XFREE(MTYPE_TMP, ospf6->gr_info.exit_reason); + ospf6->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); + EVENT_OFF(ospf6->gr_info.t_grace_period); for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, onode, area)) { struct ospf6_interface *oi; @@ -148,8 +183,24 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) */ OSPF6_ROUTER_LSA_EXECUTE(area); + /* + * Force reorigination of intra-area-prefix-LSAs to handle + * areas without any full adjacency. + */ + OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(area); + for (ALL_LIST_ELEMENTS_RO(area->if_list, anode, oi)) { - OSPF6_LINK_LSA_EXECUTE(oi); + /* Disable hello delay. */ + if (oi->gr.hello_delay.t_grace_send) { + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + event_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); + } + + /* Reoriginate Link-LSA. */ + if (oi->type != OSPF_IFTYPE_VIRTUALLINK) + OSPF6_LINK_LSA_EXECUTE(oi); /* * 2) The router should reoriginate network-LSAs on all @@ -160,6 +211,16 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) } } + /* + * While all self-originated NSSA and AS-external LSAs were already + * learned from the helping neighbors, we need to reoriginate them in + * order to ensure they will be refreshed periodically. + */ + for (route = ospf6_route_head(ospf6->external_table); route; + route = ospf6_route_next(route)) + ospf6_handle_external_lsa_origination(ospf6, route, + &route->prefix); + /* * 3) The router reruns its OSPF routing calculations, this time * installing the results into the system forwarding table, and @@ -176,6 +237,27 @@ static void ospf6_gr_restart_exit(struct ospf6 *ospf6, const char *reason) ospf6_gr_flush_grace_lsas(ospf6); } +/* Enter the Graceful Restart mode. */ +void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, + time_t timestamp) +{ + unsigned long remaining_time; + + ospf6->gr_info.restart_in_progress = true; + ospf6->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF6_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf6_gr_grace_period_expired, ospf6, + remaining_time, &ospf6->gr_info.t_grace_period); +} + #define RTR_LSA_MISSING 0 #define RTR_LSA_ADJ_FOUND 1 #define RTR_LSA_ADJ_NOT_FOUND 2 @@ -211,8 +293,10 @@ static int ospf6_router_lsa_contains_adj(struct ospf6_area *area, if (lsdesc->type != OSPF6_ROUTER_LSDESC_POINTTOPOINT) continue; - if (lsdesc->neighbor_router_id == neighbor_router_id) + if (lsdesc->neighbor_router_id == neighbor_router_id) { + ospf6_lsa_unlock(&lsa); return RTR_LSA_ADJ_FOUND; + } } } @@ -429,8 +513,10 @@ static bool ospf6_gr_check_adjs(struct ospf6 *ospf6) for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, router, lsa_self)) { found = true; - if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) + if (!ospf6_gr_check_adjs_lsa(area, lsa_self)) { + ospf6_lsa_unlock(&lsa_self); return false; + } } if (!found) return false; @@ -440,18 +526,33 @@ static bool ospf6_gr_check_adjs(struct ospf6 *ospf6) } /* Handling of grace period expiry. */ -static void ospf6_gr_grace_period_expired(struct thread *thread) +static void ospf6_gr_grace_period_expired(struct event *thread) { - struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6 *ospf6 = EVENT_ARG(thread); ospf6_gr_restart_exit(ospf6, "grace period has expired"); } +/* Send extra Grace-LSA out the interface (unplanned outages only). */ +void ospf6_gr_iface_send_grace_lsa(struct event *thread) +{ + struct ospf6_interface *oi = EVENT_ARG(thread); + + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); + + if (++oi->gr.hello_delay.elapsed_seconds < oi->gr.hello_delay.interval) + event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); + else + event_add_event(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); +} + /* * Record in non-volatile memory that the given OSPF instance is attempting to * perform a graceful restart. */ -static void ospf6_gr_nvm_update(struct ospf6 *ospf6) +static void ospf6_gr_nvm_update(struct ospf6 *ospf6, bool prepare) { const char *inst_name; json_object *json; @@ -477,16 +578,18 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6) json_instance); } + json_object_int_add(json_instance, "gracePeriod", + ospf6->gr_info.grace_period); + /* * Record not only the grace period, but also a UNIX timestamp * corresponding to the end of that period. That way, once ospf6d is * restarted, it will be possible to take into account the time that * passed while ospf6d wasn't running. */ - json_object_int_add(json_instance, "gracePeriod", - ospf6->gr_info.grace_period); - json_object_int_add(json_instance, "timestamp", - time(NULL) + ospf6->gr_info.grace_period); + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf6->gr_info.grace_period); json_object_to_file_ext((char *)OSPF6D_GR_STATE, json, JSON_C_TO_STRING_PRETTY); @@ -497,7 +600,7 @@ static void ospf6_gr_nvm_update(struct ospf6 *ospf6) * Delete GR status information about the given OSPF instance from non-volatile * memory. */ -static void ospf6_gr_nvm_delete(struct ospf6 *ospf6) +void ospf6_gr_nvm_delete(struct ospf6 *ospf6) { const char *inst_name; json_object *json; @@ -533,6 +636,7 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_object *json_instances; json_object *json_instance; json_object *json_timestamp; + json_object *json_grace_period; time_t timestamp = 0; inst_name = ospf6->name ? ospf6->name : VRF_DEFAULT_NAME; @@ -554,29 +658,33 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_instance); } + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); if (json_timestamp) { time_t now; - unsigned long remaining_time; - /* Check if the grace period has already expired. */ + /* Planned GR: check if the grace period has already expired. */ now = time(NULL); timestamp = json_object_get_int(json_timestamp); if (now > timestamp) { ospf6_gr_restart_exit( ospf6, "grace period has expired already"); - } else { - /* Schedule grace period timeout. */ - ospf6->gr_info.restart_in_progress = true; - remaining_time = timestamp - time(NULL); - if (IS_DEBUG_OSPF6_GR) - zlog_debug( - "GR: remaining time until grace period expires: %lu(s)", - remaining_time); - thread_add_timer(master, ospf6_gr_grace_period_expired, - ospf6, remaining_time, - &ospf6->gr_info.t_grace_period); - } + } else + ospf6_gr_restart_enter(ospf6, OSPF6_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf6->gr_info.grace_period = grace_period; + ospf6_gr_restart_enter(ospf6, OSPF6_GR_UNKNOWN_RESTART, + time(NULL) + + ospf6->gr_info.grace_period); } json_object_object_del(json_instances, inst_name); @@ -586,6 +694,24 @@ void ospf6_gr_nvm_read(struct ospf6 *ospf6) json_object_free(json); } +void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi) +{ + /* + * Can't check OSPF interface state as the OSPF instance might not be + * enabled yet. + */ + if (!if_is_operative(oi->interface) || if_is_loopback(oi->interface)) + return; + + /* Send Grace-LSA. */ + ospf6_gr_lsa_originate(oi, oi->area->ospf6->gr_info.reason); + + /* Start GR hello-delay interval. */ + oi->gr.hello_delay.elapsed_seconds = 0; + event_add_timer(master, ospf6_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); +} + /* Prepare to start a Graceful Restart. */ static void ospf6_gr_prepare(void) { @@ -606,25 +732,17 @@ static void ospf6_gr_prepare(void) ospf6->gr_info.grace_period, ospf6_vrf_id_to_name(ospf6->vrf_id)); - /* Freeze OSPF routes in the RIB. */ - if (ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period)) { - zlog_warn( - "%s: failed to activate graceful restart: not connected to zebra", - __func__); - continue; - } - /* Send a Grace-LSA to all neighbors. */ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, anode, area)) { for (ALL_LIST_ELEMENTS_RO(area->if_list, inode, oi)) { if (oi->state < OSPF6_INTERFACE_POINTTOPOINT) continue; - ospf6_gr_lsa_originate(oi); + ospf6_gr_lsa_originate(oi, OSPF6_GR_SW_RESTART); } } /* Record end of the grace period in non-volatile memory. */ - ospf6_gr_nvm_update(ospf6); + ospf6_gr_nvm_update(ospf6, true); /* * Mark that a Graceful Restart preparation is in progress, to @@ -695,6 +813,12 @@ DEFPY(ospf6_graceful_restart, ospf6_graceful_restart_cmd, ospf6->gr_info.restart_support = true; ospf6->gr_info.grace_period = grace_period; + /* Freeze OSPF routes in the RIB. */ + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf6_gr_nvm_update(ospf6, false); + return CMD_SUCCESS; } @@ -717,6 +841,8 @@ DEFPY(ospf6_no_graceful_restart, ospf6_no_graceful_restart_cmd, ospf6->gr_info.restart_support = false; ospf6->gr_info.grace_period = OSPF6_DFLT_GRACE_INTERVAL; + ospf6_gr_nvm_delete(ospf6); + ospf6_zebra_gr_disable(ospf6); return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_gr.h b/ospf6d/ospf6_gr.h index 2c7e8b341c0c..84ef3aeb8acd 100644 --- a/ospf6d/ospf6_gr.h +++ b/ospf6d/ospf6_gr.h @@ -155,9 +155,15 @@ extern int config_write_ospf6_gr(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_gr_helper(struct vty *vty, struct ospf6 *ospf6); extern int config_write_ospf6_debug_gr_helper(struct vty *vty); +extern void ospf6_gr_iface_send_grace_lsa(struct event *thread); +extern void ospf6_gr_restart_enter(struct ospf6 *ospf6, + enum ospf6_gr_restart_reason reason, + time_t timestamp); extern void ospf6_gr_check_lsdb_consistency(struct ospf6 *ospf, struct ospf6_area *area); extern void ospf6_gr_nvm_read(struct ospf6 *ospf); +extern void ospf6_gr_nvm_delete(struct ospf6 *ospf6); +extern void ospf6_gr_unplanned_start_interface(struct ospf6_interface *oi); extern void ospf6_gr_init(void); #endif /* OSPF6_GR_H */ diff --git a/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c index 65ce078fcbc5..8159ac0cd3d3 100644 --- a/ospf6d/ospf6_gr_helper.c +++ b/ospf6d/ospf6_gr_helper.c @@ -110,10 +110,8 @@ static void ospf6_enable_rtr_hash_destroy(struct ospf6 *ospf6) if (ospf6->ospf6_helper_cfg.enable_rtr_list == NULL) return; - hash_clean(ospf6->ospf6_helper_cfg.enable_rtr_list, - ospf6_disable_rtr_hash_free); - hash_free(ospf6->ospf6_helper_cfg.enable_rtr_list); - ospf6->ospf6_helper_cfg.enable_rtr_list = NULL; + hash_clean_and_free(&ospf6->ospf6_helper_cfg.enable_rtr_list, + ospf6_disable_rtr_hash_free); } /* @@ -197,9 +195,9 @@ static int ospf6_extract_grace_lsa_fields(struct ospf6_lsa *lsa, * Returns: * Nothing */ -static void ospf6_handle_grace_timer_expiry(struct thread *thread) +static void ospf6_handle_grace_timer_expiry(struct event *thread) { - struct ospf6_neighbor *nbr = THREAD_ARG(thread); + struct ospf6_neighbor *nbr = EVENT_ARG(thread); ospf6_gr_helper_exit(nbr, OSPF6_GR_HELPER_GRACE_TIMEOUT); } @@ -230,9 +228,9 @@ static bool ospf6_check_chg_in_rxmt_list(struct ospf6_neighbor *nbr) lsa->header->adv_router, lsa->lsdb); if (lsa_in_db && lsa_in_db->tobe_acknowledged) { - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); if (lsanext) - ospf6_lsa_unlock(lsanext); + ospf6_lsa_unlock(&lsanext); return OSPF6_TRUE; } @@ -280,8 +278,9 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, if (IS_DEBUG_OSPF6_GR) zlog_debug( - "%s, Grace LSA received from %pI4, grace interval:%u, restart reason :%s", - __func__, &restarter->router_id, grace_interval, + "%s, Grace LSA received from %s(%pI4), grace interval:%u, restart reason:%s", + __func__, restarter->name, &restarter->router_id, + grace_interval, ospf6_restart_reason_desc[restart_reason]); /* Verify Helper enabled globally */ @@ -383,7 +382,7 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, } if (OSPF6_GR_IS_ACTIVE_HELPER(restarter)) { - THREAD_OFF(restarter->gr_helper_info.t_grace_timer); + EVENT_OFF(restarter->gr_helper_info.t_grace_timer); if (ospf6->ospf6_helper_cfg.active_restarter_cnt > 0) ospf6->ospf6_helper_cfg.active_restarter_cnt--; @@ -416,9 +415,9 @@ int ospf6_process_grace_lsa(struct ospf6 *ospf6, struct ospf6_lsa *lsa, actual_grace_interval); /* Start the grace timer */ - thread_add_timer(master, ospf6_handle_grace_timer_expiry, restarter, - actual_grace_interval, - &restarter->gr_helper_info.t_grace_timer); + event_add_timer(master, ospf6_handle_grace_timer_expiry, restarter, + actual_grace_interval, + &restarter->gr_helper_info.t_grace_timer); return OSPF6_GR_ACTIVE_HELPER; } @@ -471,7 +470,7 @@ void ospf6_gr_helper_exit(struct ospf6_neighbor *nbr, * expiry, stop the grace timer. */ if (reason != OSPF6_GR_HELPER_GRACE_TIMEOUT) - THREAD_OFF(nbr->gr_helper_info.t_grace_timer); + EVENT_OFF(nbr->gr_helper_info.t_grace_timer); if (ospf6->ospf6_helper_cfg.active_restarter_cnt <= 0) { zlog_err( @@ -834,7 +833,7 @@ static void show_ospfv6_gr_helper_per_nbr(struct vty *vty, json_object *json, vty_out(vty, " Actual Grace period : %d(in seconds)\n", nbr->gr_helper_info.actual_grace_period); vty_out(vty, " Remaining GraceTime:%ld(in seconds).\n", - thread_timer_remain_second( + event_timer_remain_second( nbr->gr_helper_info.t_grace_timer)); vty_out(vty, " Graceful Restart reason: %s.\n\n", ospf6_restart_reason_desc[nbr->gr_helper_info @@ -851,8 +850,8 @@ static void show_ospfv6_gr_helper_per_nbr(struct vty *vty, json_object *json, json_object_int_add(json_neigh, "actualGraceInterval", nbr->gr_helper_info.actual_grace_period); json_object_int_add(json_neigh, "remainGracetime", - thread_timer_remain_second( - nbr->gr_helper_info.t_grace_timer)); + event_timer_remain_second( + nbr->gr_helper_info.t_grace_timer)); json_object_string_add(json_neigh, "restartReason", ospf6_restart_reason_desc[ nbr->gr_helper_info.gr_restart_reason]); @@ -938,12 +937,22 @@ static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6, (ospf6->ospf6_helper_cfg.strict_lsa_check) ? "Enabled" : "Disabled"); + +#if CONFDATE > 20240401 + CPP_NOTICE("Remove deprecated json key: restartSupoort") +#endif json_object_string_add( json, "restartSupoort", (ospf6->ospf6_helper_cfg.only_planned_restart) ? "Planned Restart only" : "Planned and Unplanned Restarts"); + json_object_string_add( + json, "restartSupport", + (ospf6->ospf6_helper_cfg.only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + json_object_int_add( json, "supportedGracePeriod", ospf6->ospf6_helper_cfg.supported_grace_time); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 7a22fdf0641f..0fb3d29e254f 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -9,7 +9,7 @@ #include "if.h" #include "log.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "plist.h" #include "zclient.h" @@ -35,6 +35,7 @@ #include "ospf6_proto.h" #include "lib/keychain.h" #include "ospf6_auth_trailer.h" +#include "ospf6d/ospf6_interface_clippy.c" DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_IF, "OSPF6 interface"); DEFINE_MTYPE(OSPF6D, OSPF6_AUTH_KEYCHAIN, "OSPF6 auth keychain"); @@ -202,6 +203,7 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp) oi->priority = OSPF6_INTERFACE_PRIORITY; oi->hello_interval = OSPF_HELLO_INTERVAL_DEFAULT; + oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT; oi->dead_interval = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; oi->rxmt_interval = OSPF_RETRANSMIT_INTERVAL_DEFAULT; oi->type = ospf6_default_iftype(ifp); @@ -260,11 +262,11 @@ void ospf6_interface_delete(struct ospf6_interface *oi) list_delete(&oi->neighbor_list); - THREAD_OFF(oi->thread_send_hello); - THREAD_OFF(oi->thread_send_lsupdate); - THREAD_OFF(oi->thread_send_lsack); - THREAD_OFF(oi->thread_sso); - THREAD_OFF(oi->thread_wait_timer); + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_send_lsupdate); + EVENT_OFF(oi->thread_send_lsack); + EVENT_OFF(oi->thread_sso); + EVENT_OFF(oi->thread_wait_timer); ospf6_lsdb_remove_all(oi->lsdb); ospf6_lsdb_remove_all(oi->lsupdate_list); @@ -307,23 +309,26 @@ void ospf6_interface_disable(struct ospf6_interface *oi) { SET_FLAG(oi->flag, OSPF6_INTERFACE_DISABLE); - thread_execute(master, interface_down, oi, 0); + event_execute(master, interface_down, oi, 0); ospf6_lsdb_remove_all(oi->lsdb); ospf6_lsdb_remove_all(oi->lsdb_self); ospf6_lsdb_remove_all(oi->lsupdate_list); ospf6_lsdb_remove_all(oi->lsack_list); - THREAD_OFF(oi->thread_send_hello); - THREAD_OFF(oi->thread_send_lsupdate); - THREAD_OFF(oi->thread_send_lsack); - THREAD_OFF(oi->thread_sso); + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_send_lsupdate); + EVENT_OFF(oi->thread_send_lsack); + EVENT_OFF(oi->thread_sso); - THREAD_OFF(oi->thread_network_lsa); - 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); + EVENT_OFF(oi->thread_network_lsa); + EVENT_OFF(oi->thread_link_lsa); + EVENT_OFF(oi->thread_intra_prefix_lsa); + EVENT_OFF(oi->thread_as_extern_lsa); + EVENT_OFF(oi->thread_wait_timer); + + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); } static struct in6_addr * @@ -382,9 +387,9 @@ void ospf6_interface_state_update(struct interface *ifp) if (if_is_operative(ifp) && (ospf6_interface_get_linklocal_address(oi->interface) || if_is_loopback(oi->interface))) - thread_execute(master, interface_up, oi, 0); + event_execute(master, interface_up, oi, 0); else - thread_execute(master, interface_down, oi, 0); + event_execute(master, interface_down, oi, 0); return; } @@ -511,7 +516,6 @@ static int ospf6_interface_state_change(uint8_t next_state, OSPF6_NETWORK_LSA_EXECUTE(oi); OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi); OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oi->area); - OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi); } else if (prev_state == OSPF6_INTERFACE_DR || next_state == OSPF6_INTERFACE_DR) { OSPF6_NETWORK_LSA_SCHEDULE(oi); @@ -592,6 +596,7 @@ static struct ospf6_neighbor *better_drouter(struct ospf6_neighbor *a, uint8_t dr_election(struct ospf6_interface *oi) { + struct ospf6 *ospf6 = oi->area->ospf6; struct listnode *node, *nnode; struct ospf6_neighbor *on, *drouter, *bdrouter, myself; struct ospf6_neighbor *best_drouter, *best_bdrouter; @@ -602,13 +607,12 @@ uint8_t dr_election(struct ospf6_interface *oi) /* pseudo neighbor myself, including noting current DR/BDR (1) */ memset(&myself, 0, sizeof(myself)); - inet_ntop(AF_INET, &oi->area->ospf6->router_id, myself.name, - sizeof(myself.name)); + inet_ntop(AF_INET, &ospf6->router_id, myself.name, sizeof(myself.name)); myself.state = OSPF6_NEIGHBOR_TWOWAY; myself.drouter = oi->drouter; myself.bdrouter = oi->bdrouter; myself.priority = oi->priority; - myself.router_id = oi->area->ospf6->router_id; + myself.router_id = ospf6->router_id; /* Electing BDR (2) */ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) @@ -657,8 +661,10 @@ uint8_t dr_election(struct ospf6_interface *oi) /* If DR or BDR change, invoke AdjOK? for each neighbor (7) */ /* RFC 2328 section 12.4. Originating LSAs (3) will be handled accordingly after AdjOK */ - if (oi->drouter != (drouter ? drouter->router_id : htonl(0)) - || oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl(0))) { + + if (oi->drouter != (drouter ? drouter->router_id : htonl(0)) || + oi->bdrouter != (bdrouter ? bdrouter->router_id : htonl(0)) || + ospf6->gr_info.restart_in_progress) { if (IS_OSPF6_DEBUG_INTERFACE) zlog_debug("DR Election on %s: DR: %s BDR: %s", oi->interface->name, @@ -669,8 +675,8 @@ uint8_t dr_election(struct ospf6_interface *oi) if (on->state < OSPF6_NEIGHBOR_TWOWAY) continue; /* Schedule AdjOK. */ - thread_add_event(master, adj_ok, on, 0, - &on->thread_adj_ok); + event_add_event(master, adj_ok, on, 0, + &on->thread_adj_ok); } } @@ -718,18 +724,18 @@ static bool ifmaddr_check(ifindex_t ifindex, struct in6_addr *addr) #endif /* __FreeBSD__ */ /* Interface State Machine */ -void interface_up(struct thread *thread) +void interface_up(struct event *thread) { struct ospf6_interface *oi; struct ospf6 *ospf6; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi && oi->interface); if (!oi->type_cfg) oi->type = ospf6_default_iftype(oi->interface); - thread_cancel(&oi->thread_sso); + event_cancel(&oi->thread_sso); if (IS_OSPF6_DEBUG_INTERFACE) zlog_debug("Interface Event %s: [InterfaceUp]", @@ -770,6 +776,17 @@ void interface_up(struct thread *thread) return; } + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->area->ospf6->gr_info.restart_in_progress && + oi->area->ospf6->gr_info.reason == OSPF6_GR_UNKNOWN_RESTART) + ospf6_gr_unplanned_start_interface(oi); + #ifdef __FreeBSD__ /* * There's a delay in FreeBSD between issuing a command to leave a @@ -783,9 +800,8 @@ void interface_up(struct thread *thread) zlog_info( "Interface %s is still in all routers group, rescheduling for SSO", oi->interface->name); - thread_add_timer(master, interface_up, oi, - OSPF6_INTERFACE_SSO_RETRY_INT, - &oi->thread_sso); + event_add_timer(master, interface_up, oi, + OSPF6_INTERFACE_SSO_RETRY_INT, &oi->thread_sso); return; } #endif /* __FreeBSD__ */ @@ -800,9 +816,9 @@ void interface_up(struct thread *thread) zlog_info( "Scheduling %s for sso retry, trial count: %d", oi->interface->name, oi->sso_try_cnt); - thread_add_timer(master, interface_up, oi, - OSPF6_INTERFACE_SSO_RETRY_INT, - &oi->thread_sso); + event_add_timer(master, interface_up, oi, + OSPF6_INTERFACE_SSO_RETRY_INT, + &oi->thread_sso); } return; } @@ -814,8 +830,8 @@ void interface_up(struct thread *thread) /* Schedule Hello */ if (!CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE) && !if_is_loopback(oi->interface)) { - thread_add_timer(master, ospf6_hello_send, oi, 0, - &oi->thread_send_hello); + event_add_timer(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); } /* decide next interface state */ @@ -827,16 +843,16 @@ void interface_up(struct thread *thread) 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, - &oi->thread_wait_timer); + event_add_timer(master, wait_timer, oi, oi->dead_interval, + &oi->thread_wait_timer); } } -void wait_timer(struct thread *thread) +void wait_timer(struct event *thread) { struct ospf6_interface *oi; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi && oi->interface); if (IS_OSPF6_DEBUG_INTERFACE) @@ -847,11 +863,11 @@ void wait_timer(struct thread *thread) ospf6_interface_state_change(dr_election(oi), oi); } -void backup_seen(struct thread *thread) +void backup_seen(struct event *thread) { struct ospf6_interface *oi; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi && oi->interface); if (IS_OSPF6_DEBUG_INTERFACE) @@ -862,11 +878,11 @@ void backup_seen(struct thread *thread) ospf6_interface_state_change(dr_election(oi), oi); } -void neighbor_change(struct thread *thread) +void neighbor_change(struct event *thread) { struct ospf6_interface *oi; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi && oi->interface); if (IS_OSPF6_DEBUG_INTERFACE) @@ -879,14 +895,14 @@ void neighbor_change(struct thread *thread) ospf6_interface_state_change(dr_election(oi), oi); } -void interface_down(struct thread *thread) +void interface_down(struct event *thread) { struct ospf6_interface *oi; struct listnode *node, *nnode; struct ospf6_neighbor *on; struct ospf6 *ospf6; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi && oi->interface); if (IS_OSPF6_DEBUG_INTERFACE) @@ -894,10 +910,10 @@ void interface_down(struct thread *thread) oi->interface->name); /* Stop Hellos */ - THREAD_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_send_hello); /* Stop trying to set socket options. */ - THREAD_OFF(oi->thread_sso); + EVENT_OFF(oi->thread_sso); /* Cease the HELPER role for all the neighbours * of this interface. @@ -934,7 +950,7 @@ void interface_down(struct thread *thread) if (oi->on_write_q) { listnode_delete(ospf6->oi_write_q, oi); if (list_isempty(ospf6->oi_write_q)) - thread_cancel(&ospf6->t_write); + event_cancel(&ospf6->t_write); oi->on_write_q = 0; } @@ -1139,7 +1155,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, if (use_json) { timerclear(&res); - if (thread_is_scheduled(oi->thread_send_lsupdate)) + if (event_is_scheduled(oi->thread_send_lsupdate)) timersub(&oi->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); @@ -1149,9 +1165,8 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, duration); json_object_string_add( json_obj, "lsUpdateSendThread", - (thread_is_scheduled(oi->thread_send_lsupdate) - ? "on" - : "off")); + (event_is_scheduled(oi->thread_send_lsupdate) ? "on" + : "off")); json_arr = json_object_new_array(); for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) @@ -1161,7 +1176,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, json_arr); timerclear(&res); - if (thread_is_scheduled(oi->thread_send_lsack)) + if (event_is_scheduled(oi->thread_send_lsack)) timersub(&oi->thread_send_lsack->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); @@ -1171,8 +1186,8 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, duration); json_object_string_add( json_obj, "lsAckSendThread", - (thread_is_scheduled(oi->thread_send_lsack) ? "on" - : "off")); + (event_is_scheduled(oi->thread_send_lsack) ? "on" + : "off")); json_arr = json_object_new_array(); for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) @@ -1180,32 +1195,38 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp, json_arr, json_object_new_string(lsa->name)); json_object_object_add(json_obj, "pendingLsaLsAck", json_arr); + if (oi->gr.hello_delay.interval != 0) + json_object_int_add(json_obj, "grHelloDelaySecs", + oi->gr.hello_delay.interval); } else { timerclear(&res); - if (thread_is_scheduled(oi->thread_send_lsupdate)) + if (event_is_scheduled(oi->thread_send_lsupdate)) timersub(&oi->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSUpdate in Time %s [thread %s]\n", oi->lsupdate_list->count, duration, - (thread_is_scheduled(oi->thread_send_lsupdate) - ? "on" - : "off")); + (event_is_scheduled(oi->thread_send_lsupdate) ? "on" + : "off")); for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); timerclear(&res); - if (thread_is_scheduled(oi->thread_send_lsack)) + if (event_is_scheduled(oi->thread_send_lsack)) timersub(&oi->thread_send_lsack->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSAck in Time %s [thread %s]\n", oi->lsack_list->count, duration, - (thread_is_scheduled(oi->thread_send_lsack) ? "on" - : "off")); + (event_is_scheduled(oi->thread_send_lsack) ? "on" + : "off")); for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); + + if (oi->gr.hello_delay.interval != 0) + vty_out(vty, " Graceful Restart hello delay: %us\n", + oi->gr.hello_delay.interval); } /* BFD specific. */ @@ -1877,8 +1898,8 @@ DEFUN (ipv6_ospf6_ifmtu, /* re-establish adjacencies */ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { - THREAD_OFF(on->inactivity_timer); - thread_add_event(master, inactivity_timer, on, 0, NULL); + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); } return CMD_SUCCESS; @@ -1923,8 +1944,8 @@ DEFUN (no_ipv6_ospf6_ifmtu, /* re-establish adjacencies */ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { - THREAD_OFF(on->inactivity_timer); - thread_add_event(master, inactivity_timer, on, 0, NULL); + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); } return CMD_SUCCESS; @@ -2106,11 +2127,11 @@ DEFUN (ipv6_ospf6_hellointerval, /* * If the thread is scheduled, send the new hello now. */ - if (thread_is_scheduled(oi->thread_send_hello)) { - THREAD_OFF(oi->thread_send_hello); + if (event_is_scheduled(oi->thread_send_hello)) { + EVENT_OFF(oi->thread_send_hello); - thread_add_timer(master, ospf6_hello_send, oi, 0, - &oi->thread_send_hello); + event_add_timer(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); } return CMD_SUCCESS; } @@ -2158,6 +2179,50 @@ ALIAS (ipv6_ospf6_deadinterval, "Interval time after which a neighbor is declared down\n" SECONDS_STR) +DEFPY(ipv6_ospf6_gr_hdelay, ipv6_ospf6_gr_hdelay_cmd, + "ipv6 ospf6 graceful-restart hello-delay (1-1800)", + IP6_STR + OSPF6_STR + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + /* Note: new or updated value won't affect ongoing graceful restart. */ + oi->gr.hello_delay.interval = hello_delay; + + return CMD_SUCCESS; +} + +DEFPY(no_ipv6_ospf6_gr_hdelay, no_ipv6_ospf6_gr_hdelay_cmd, + "no ipv6 ospf6 graceful-restart hello-delay [(1-1800)]", + NO_STR + IP6_STR + OSPF6_STR + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf6_interface *oi; + + oi = ifp->info; + if (oi == NULL) + oi = ospf6_interface_create(ifp); + + oi->gr.hello_delay.interval = OSPF_HELLO_DELAY_DEFAULT; + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + + return CMD_SUCCESS; +} + /* interface variable set command */ DEFUN (ipv6_ospf6_transmitdelay, ipv6_ospf6_transmitdelay_cmd, @@ -2323,12 +2388,12 @@ DEFUN (ipv6_ospf6_passive, assert(oi); SET_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE); - THREAD_OFF(oi->thread_send_hello); - THREAD_OFF(oi->thread_sso); + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_sso); for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) { - THREAD_OFF(on->inactivity_timer); - thread_add_event(master, inactivity_timer, on, 0, NULL); + EVENT_OFF(on->inactivity_timer); + event_add_event(master, inactivity_timer, on, 0, NULL); } return CMD_SUCCESS; @@ -2353,13 +2418,13 @@ DEFUN (no_ipv6_ospf6_passive, assert(oi); UNSET_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE); - THREAD_OFF(oi->thread_send_hello); - THREAD_OFF(oi->thread_sso); + EVENT_OFF(oi->thread_send_hello); + EVENT_OFF(oi->thread_sso); /* don't send hellos over loopback interface */ if (!if_is_loopback(oi->interface)) - thread_add_timer(master, ospf6_hello_send, oi, 0, - &oi->thread_send_hello); + event_add_timer(master, ospf6_hello_send, oi, 0, + &oi->thread_send_hello); return CMD_SUCCESS; } @@ -2519,8 +2584,8 @@ DEFUN (ipv6_ospf6_network, } /* Reset the interface */ - thread_execute(master, interface_down, oi, 0); - thread_execute(master, interface_up, oi, 0); + event_execute(master, interface_down, oi, 0); + event_execute(master, interface_up, oi, 0); return CMD_SUCCESS; } @@ -2555,8 +2620,8 @@ DEFUN (no_ipv6_ospf6_network, oi->type = type; /* Reset the interface */ - thread_execute(master, interface_down, oi, 0); - thread_execute(master, interface_up, oi, 0); + event_execute(master, interface_down, oi, 0); + event_execute(master, interface_up, oi, 0); return CMD_SUCCESS; } @@ -2625,6 +2690,11 @@ static int config_write_ospf6_interface(struct vty *vty, struct vrf *vrf) else if (oi->type_cfg && oi->type == OSPF_IFTYPE_BROADCAST) vty_out(vty, " ipv6 ospf6 network broadcast\n"); + if (oi->gr.hello_delay.interval != OSPF_HELLO_DELAY_DEFAULT) + vty_out(vty, + " ipv6 ospf6 graceful-restart hello-delay %u\n", + oi->gr.hello_delay.interval); + ospf6_bfd_write_config(vty, oi); ospf6_auth_write_config(vty, &oi->at_data); @@ -2723,12 +2793,14 @@ void ospf6_interface_init(void) install_element(INTERFACE_NODE, &ipv6_ospf6_deadinterval_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_hellointerval_cmd); + install_element(INTERFACE_NODE, &ipv6_ospf6_gr_hdelay_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_priority_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_retransmitinterval_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_transmitdelay_cmd); install_element(INTERFACE_NODE, &ipv6_ospf6_instance_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_deadinterval_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_hellointerval_cmd); + install_element(INTERFACE_NODE, &no_ipv6_ospf6_gr_hdelay_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_priority_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_retransmitinterval_cmd); install_element(INTERFACE_NODE, &no_ipv6_ospf6_transmitdelay_cmd); @@ -2772,8 +2844,8 @@ void ospf6_interface_clear(struct interface *ifp) zlog_debug("Interface %s: clear by reset", ifp->name); /* Reset the interface */ - thread_execute(master, interface_down, oi, 0); - thread_execute(master, interface_up, oi, 0); + event_execute(master, interface_down, oi, 0); + event_execute(master, interface_up, oi, 0); } /* Clear interface */ diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index e04842a7c8a8..5942df0ab5df 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -74,6 +74,15 @@ struct ospf6_interface { uint16_t dead_interval; uint32_t rxmt_interval; + /* Graceful-Restart data. */ + struct { + struct { + uint16_t interval; + uint16_t elapsed_seconds; + struct event *t_grace_send; + } hello_delay; + } gr; + uint32_t state_change; /* Cost */ @@ -90,7 +99,7 @@ struct ospf6_interface { /* Interface socket setting trial counter, resets on success */ uint8_t sso_try_cnt; - struct thread *thread_sso; + struct event *thread_sso; /* OSPF6 Interface flag */ char flag; @@ -115,15 +124,15 @@ struct ospf6_interface { struct ospf6_lsdb *lsack_list; /* Ongoing Tasks */ - struct thread *thread_send_hello; - struct thread *thread_send_lsupdate; - struct thread *thread_send_lsack; + struct event *thread_send_hello; + struct event *thread_send_lsupdate; + struct event *thread_send_lsack; - struct thread *thread_network_lsa; - struct thread *thread_link_lsa; - struct thread *thread_intra_prefix_lsa; - struct thread *thread_as_extern_lsa; - struct thread *thread_wait_timer; + struct event *thread_network_lsa; + struct event *thread_link_lsa; + struct event *thread_intra_prefix_lsa; + struct event *thread_as_extern_lsa; + struct event *thread_wait_timer; struct ospf6_route_table *route_connected; @@ -211,11 +220,11 @@ extern struct in6_addr * ospf6_interface_get_global_address(struct interface *ifp); /* interface event */ -extern void interface_up(struct thread *thread); -extern void interface_down(struct thread *thread); -extern void wait_timer(struct thread *thread); -extern void backup_seen(struct thread *thread); -extern void neighbor_change(struct thread *thread); +extern void interface_up(struct event *thread); +extern void interface_down(struct event *thread); +extern void wait_timer(struct event *thread); +extern void backup_seen(struct event *thread); +extern void neighbor_change(struct event *thread); extern void ospf6_interface_init(void); extern void ospf6_interface_clear(struct interface *ifp); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index e7ce2f5f723e..301fccecd77b 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -7,7 +7,7 @@ #include "log.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "if.h" #include "prefix.h" @@ -209,7 +209,7 @@ int ospf6_router_is_stub_router(struct ospf6_lsa *lsa) return OSPF6_NOT_STUB_ROUTER; } -void ospf6_router_lsa_originate(struct thread *thread) +void ospf6_router_lsa_originate(struct event *thread) { struct ospf6_area *oa; @@ -228,7 +228,7 @@ void ospf6_router_lsa_originate(struct thread *thread) uint32_t router; int count; - oa = (struct ospf6_area *)THREAD_ARG(thread); + oa = (struct ospf6_area *)EVENT_ARG(thread); if (oa->ospf6->gr_info.restart_in_progress) { if (IS_DEBUG_OSPF6_GR) @@ -494,7 +494,7 @@ static int ospf6_network_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, return 0; } -void ospf6_network_lsa_originate(struct thread *thread) +void ospf6_network_lsa_originate(struct event *thread) { struct ospf6_interface *oi; @@ -510,7 +510,7 @@ void ospf6_network_lsa_originate(struct thread *thread) struct listnode *i; uint16_t type; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); /* The interface must be enabled until here. A Network-LSA of a disabled interface (but was once enabled) should be flushed @@ -746,7 +746,7 @@ static int ospf6_link_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, return 0; } -void ospf6_link_lsa_originate(struct thread *thread) +void ospf6_link_lsa_originate(struct event *thread) { struct ospf6_interface *oi; @@ -758,7 +758,7 @@ void ospf6_link_lsa_originate(struct thread *thread) struct ospf6_route *route; struct ospf6_prefix *op; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi->area); @@ -982,7 +982,7 @@ static int ospf6_intra_prefix_lsa_show(struct vty *vty, struct ospf6_lsa *lsa, return 0; } -void ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) +void ospf6_intra_prefix_lsa_originate_stub(struct event *thread) { struct ospf6_area *oa; @@ -1001,7 +1001,7 @@ void ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) struct ospf6_route_table *route_advertise; int ls_id = 0; - oa = (struct ospf6_area *)THREAD_ARG(thread); + oa = (struct ospf6_area *)EVENT_ARG(thread); if (oa->ospf6->gr_info.restart_in_progress) { if (IS_DEBUG_OSPF6_GR) @@ -1217,7 +1217,7 @@ void ospf6_intra_prefix_lsa_originate_stub(struct thread *thread) } -void ospf6_intra_prefix_lsa_originate_transit(struct thread *thread) +void ospf6_intra_prefix_lsa_originate_transit(struct event *thread) { struct ospf6_interface *oi; @@ -1237,7 +1237,7 @@ void ospf6_intra_prefix_lsa_originate_transit(struct thread *thread) char *start, *end, *current; uint16_t type; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); assert(oi->area); diff --git a/ospf6d/ospf6_intra.h b/ospf6d/ospf6_intra.h index 79b5eb0ed9b0..0f286f0ab1a1 100644 --- a/ospf6d/ospf6_intra.h +++ b/ospf6d/ospf6_intra.h @@ -139,32 +139,32 @@ struct ospf6_intra_prefix_lsa { #define OSPF6_ROUTER_LSA_SCHEDULE(oa) \ do { \ if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ - thread_add_event(master, ospf6_router_lsa_originate, \ - oa, 0, &(oa)->thread_router_lsa); \ + event_add_event(master, ospf6_router_lsa_originate, \ + oa, 0, &(oa)->thread_router_lsa); \ } while (0) #define OSPF6_NETWORK_LSA_SCHEDULE(oi) \ do { \ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ - thread_add_event(master, ospf6_network_lsa_originate, \ - oi, 0, &(oi)->thread_network_lsa); \ + event_add_event(master, ospf6_network_lsa_originate, \ + oi, 0, &(oi)->thread_network_lsa); \ } while (0) #define OSPF6_LINK_LSA_SCHEDULE(oi) \ do { \ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ - thread_add_event(master, ospf6_link_lsa_originate, oi, \ - 0, &(oi)->thread_link_lsa); \ + event_add_event(master, ospf6_link_lsa_originate, oi, \ + 0, &(oi)->thread_link_lsa); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB(oa) \ do { \ if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ - thread_add_event( \ + event_add_event( \ master, ospf6_intra_prefix_lsa_originate_stub, \ oa, 0, &(oa)->thread_intra_prefix_lsa); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT(oi) \ do { \ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ - thread_add_event( \ + event_add_event( \ master, \ ospf6_intra_prefix_lsa_originate_transit, oi, \ 0, &(oi)->thread_intra_prefix_lsa); \ @@ -173,42 +173,42 @@ struct ospf6_intra_prefix_lsa { #define OSPF6_AS_EXTERN_LSA_SCHEDULE(oi) \ do { \ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ - thread_add_event(master, ospf6_orig_as_external_lsa, \ - oi, 0, &(oi)->thread_as_extern_lsa); \ + event_add_event(master, ospf6_orig_as_external_lsa, \ + oi, 0, &(oi)->thread_as_extern_lsa); \ } while (0) #define OSPF6_ROUTER_LSA_EXECUTE(oa) \ do { \ if (CHECK_FLAG((oa)->flag, OSPF6_AREA_ENABLE)) \ - thread_execute(master, ospf6_router_lsa_originate, oa, \ - 0); \ + event_execute(master, ospf6_router_lsa_originate, oa, \ + 0); \ } while (0) #define OSPF6_NETWORK_LSA_EXECUTE(oi) \ do { \ - THREAD_OFF((oi)->thread_network_lsa); \ - thread_execute(master, ospf6_network_lsa_originate, oi, 0); \ + EVENT_OFF((oi)->thread_network_lsa); \ + event_execute(master, ospf6_network_lsa_originate, oi, 0); \ } while (0) #define OSPF6_LINK_LSA_EXECUTE(oi) \ do { \ if (!CHECK_FLAG((oi)->flag, OSPF6_INTERFACE_DISABLE)) \ - thread_execute(master, ospf6_link_lsa_originate, oi, \ - 0); \ + event_execute(master, ospf6_link_lsa_originate, oi, \ + 0); \ } while (0) #define OSPF6_INTRA_PREFIX_LSA_EXECUTE_TRANSIT(oi) \ do { \ - THREAD_OFF((oi)->thread_intra_prefix_lsa); \ - thread_execute(master, \ - ospf6_intra_prefix_lsa_originate_transit, oi, \ - 0); \ + EVENT_OFF((oi)->thread_intra_prefix_lsa); \ + event_execute(master, \ + ospf6_intra_prefix_lsa_originate_transit, oi, \ + 0); \ } while (0) #define OSPF6_AS_EXTERN_LSA_EXECUTE(oi) \ do { \ - THREAD_OFF((oi)->thread_as_extern_lsa); \ - thread_execute(master, ospf6_orig_as_external_lsa, oi, 0); \ + EVENT_OFF((oi)->thread_as_extern_lsa); \ + event_execute(master, ospf6_orig_as_external_lsa, oi, 0); \ } while (0) /* Function Prototypes */ @@ -220,14 +220,14 @@ extern char *ospf6_network_lsdesc_lookup(uint32_t router_id, struct ospf6_lsa *lsa); extern int ospf6_router_is_stub_router(struct ospf6_lsa *lsa); -extern void ospf6_router_lsa_originate(struct thread *thread); -extern void ospf6_network_lsa_originate(struct thread *thread); -extern void ospf6_link_lsa_originate(struct thread *thread); -extern void ospf6_intra_prefix_lsa_originate_transit(struct thread *thread); -extern void ospf6_intra_prefix_lsa_originate_stub(struct thread *thread); +extern void ospf6_router_lsa_originate(struct event *thread); +extern void ospf6_network_lsa_originate(struct event *thread); +extern void ospf6_link_lsa_originate(struct event *thread); +extern void ospf6_intra_prefix_lsa_originate_transit(struct event *thread); +extern void ospf6_intra_prefix_lsa_originate_stub(struct event *thread); extern void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa); extern void ospf6_intra_prefix_lsa_remove(struct ospf6_lsa *lsa); -extern void ospf6_orig_as_external_lsa(struct thread *thread); +extern void ospf6_orig_as_external_lsa(struct event *thread); extern void ospf6_intra_route_calculation(struct ospf6_area *oa); extern void ospf6_intra_brouter_calculation(struct ospf6_area *oa); extern void ospf6_intra_prefix_route_ecmp_path(struct ospf6_area *oa, diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c index fe085b4cb4ed..e745c6c57703 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -12,7 +12,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "checksum.h" #include "frrstr.h" @@ -302,8 +302,8 @@ void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa) if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) zlog_debug("LSA: Premature aging: %s", lsa->name); - THREAD_OFF(lsa->expire); - THREAD_OFF(lsa->refresh); + EVENT_OFF(lsa->expire); + EVENT_OFF(lsa->refresh); /* * We clear the LSA from the neighbor retx lists now because it @@ -333,7 +333,7 @@ void ospf6_lsa_premature_aging(struct ospf6_lsa *lsa) ospf6_flood_clear(lsa); lsa->header->age = htons(OSPF_LSA_MAXAGE); - thread_execute(master, ospf6_lsa_expire, lsa, 0); + event_execute(master, ospf6_lsa_expire, lsa, 0); } /* check which is more recent. if a is more recent, return -1; @@ -760,8 +760,8 @@ void ospf6_lsa_delete(struct ospf6_lsa *lsa) assert(lsa->lock == 0); /* cancel threads */ - THREAD_OFF(lsa->expire); - THREAD_OFF(lsa->refresh); + EVENT_OFF(lsa->expire); + EVENT_OFF(lsa->refresh); /* do free */ XFREE(MTYPE_OSPF6_LSA_HEADER, lsa->header); @@ -797,33 +797,33 @@ struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa) } /* decrement reference counter of struct ospf6_lsa */ -struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa) +void ospf6_lsa_unlock(struct ospf6_lsa **lsa) { /* decrement reference counter */ - assert(lsa->lock > 0); - lsa->lock--; + assert((*lsa)->lock > 0); + (*lsa)->lock--; - if (lsa->lock != 0) - return lsa; + if ((*lsa)->lock != 0) + return; - ospf6_lsa_delete(lsa); - return NULL; + ospf6_lsa_delete(*lsa); + *lsa = NULL; } /* ospf6 lsa expiry */ -void ospf6_lsa_expire(struct thread *thread) +void ospf6_lsa_expire(struct event *thread) { struct ospf6_lsa *lsa; struct ospf6 *ospf6; - lsa = (struct ospf6_lsa *)THREAD_ARG(thread); + lsa = (struct ospf6_lsa *)EVENT_ARG(thread); assert(lsa && lsa->header); assert(OSPF6_LSA_IS_MAXAGE(lsa)); assert(!lsa->refresh); - lsa->expire = (struct thread *)NULL; + lsa->expire = (struct event *)NULL; if (IS_OSPF6_DEBUG_LSA_TYPE(lsa->header->type)) { zlog_debug("LSA Expire:"); @@ -845,15 +845,15 @@ void ospf6_lsa_expire(struct thread *thread) ospf6_maxage_remove(ospf6); } -void ospf6_lsa_refresh(struct thread *thread) +void ospf6_lsa_refresh(struct event *thread) { struct ospf6_lsa *old, *self, *new; struct ospf6_lsdb *lsdb_self; - old = (struct ospf6_lsa *)THREAD_ARG(thread); + old = (struct ospf6_lsa *)EVENT_ARG(thread); assert(old && old->header); - old->refresh = (struct thread *)NULL; + old->refresh = (struct event *)NULL; lsdb_self = ospf6_get_scoped_lsdb_self(old); self = ospf6_lsdb_lookup(old->header->type, old->header->id, @@ -875,8 +875,8 @@ void ospf6_lsa_refresh(struct thread *thread) new = ospf6_lsa_create(self->header); new->lsdb = old->lsdb; - thread_add_timer(master, ospf6_lsa_refresh, new, OSPF_LS_REFRESH_TIME, - &new->refresh); + event_add_timer(master, ospf6_lsa_refresh, new, OSPF_LS_REFRESH_TIME, + &new->refresh); /* store it in the LSDB for self-originated LSAs */ ospf6_lsdb_add(ospf6_lsa_copy(new), lsdb_self); diff --git a/ospf6d/ospf6_lsa.h b/ospf6d/ospf6_lsa.h index acb24d791d21..c9ac27df88e9 100644 --- a/ospf6d/ospf6_lsa.h +++ b/ospf6d/ospf6_lsa.h @@ -119,8 +119,8 @@ struct ospf6_lsa { struct timeval received; /* used by MinLSArrival check */ struct timeval installed; - struct thread *expire; - struct thread *refresh; /* For self-originated LSA */ + struct event *expire; + struct event *refresh; /* For self-originated LSA */ int retrans_count; @@ -239,10 +239,10 @@ extern void ospf6_lsa_delete(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_copy(struct ospf6_lsa *lsa); extern struct ospf6_lsa *ospf6_lsa_lock(struct ospf6_lsa *lsa); -extern struct ospf6_lsa *ospf6_lsa_unlock(struct ospf6_lsa *lsa); +extern void ospf6_lsa_unlock(struct ospf6_lsa **lsa); -extern void ospf6_lsa_expire(struct thread *thread); -extern void ospf6_lsa_refresh(struct thread *thread); +extern void ospf6_lsa_expire(struct event *thread); +extern void ospf6_lsa_refresh(struct event *thread); extern unsigned short ospf6_lsa_checksum(struct ospf6_lsa_header *lsah); extern int ospf6_lsa_checksum_valid(struct ospf6_lsa_header *lsah); diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 0221102b59f5..355860f6b123 100644 --- a/ospf6d/ospf6_lsdb.c +++ b/ospf6d/ospf6_lsdb.c @@ -13,8 +13,10 @@ #include "vty.h" #include "ospf6_proto.h" +#include "ospf6_area.h" #include "ospf6_lsa.h" #include "ospf6_lsdb.h" +#include "ospf6_abr.h" #include "ospf6_asbr.h" #include "ospf6_route.h" #include "ospf6d.h" @@ -137,7 +139,7 @@ void ospf6_lsdb_add(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) } /* to free the lookup lock in node get*/ route_unlock_node(current); - ospf6_lsa_unlock(old); + ospf6_lsa_unlock(&old); } ospf6_lsdb_count_assert(lsdb); @@ -166,7 +168,7 @@ void ospf6_lsdb_remove(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb) route_unlock_node(node); /* to free the lookup lock */ route_unlock_node(node); /* to free the original lock */ - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); ospf6_lsdb_count_assert(lsdb); } @@ -216,6 +218,33 @@ struct ospf6_lsa *ospf6_find_external_lsa(struct ospf6 *ospf6, struct prefix *p) return lsa; } +struct ospf6_lsa *ospf6_find_inter_prefix_lsa(struct ospf6 *ospf6, + struct ospf6_area *area, + struct prefix *p) +{ + struct ospf6_lsa *lsa; + uint16_t type = htons(OSPF6_LSTYPE_INTER_PREFIX); + + for (ALL_LSDB_TYPED_ADVRTR(area->lsdb, type, ospf6->router_id, lsa)) { + struct ospf6_inter_prefix_lsa *prefix_lsa; + struct prefix prefix; + + prefix_lsa = + (struct ospf6_inter_prefix_lsa *)OSPF6_LSA_HEADER_END( + lsa->header); + prefix.family = AF_INET6; + prefix.prefixlen = prefix_lsa->prefix.prefix_length; + ospf6_prefix_in6_addr(&prefix.u.prefix6, prefix_lsa, + &prefix_lsa->prefix); + if (prefix_same(p, &prefix)) { + ospf6_lsa_unlock(&lsa); + return lsa; + } + } + + return NULL; +} + struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, uint32_t adv_router, struct ospf6_lsdb *lsdb) @@ -299,7 +328,7 @@ struct ospf6_lsa *ospf6_lsdb_next(const struct route_node *iterend, { struct route_node *node = lsa->rn; - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); do node = route_next_until(node, iterend); @@ -332,7 +361,7 @@ void ospf6_lsdb_lsa_unlock(struct ospf6_lsa *lsa) if (lsa != NULL) { if (lsa->rn != NULL) route_unlock_node(lsa->rn); - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); } } @@ -368,8 +397,8 @@ int ospf6_lsdb_maxage_remover(struct ospf6_lsdb *lsdb) htonl(OSPF_MAX_SEQUENCE_NUMBER + 1); ospf6_lsa_checksum(lsa->header); - THREAD_OFF(lsa->refresh); - thread_execute(master, ospf6_lsa_refresh, lsa, 0); + EVENT_OFF(lsa->refresh); + event_execute(master, ospf6_lsa_refresh, lsa, 0); } else { zlog_debug("calling ospf6_lsdb_remove %s", lsa->name); ospf6_lsdb_remove(lsa, lsdb); diff --git a/ospf6d/ospf6_lsdb.h b/ospf6d/ospf6_lsdb.h index 7e20b05447d6..604406d75fe0 100644 --- a/ospf6d/ospf6_lsdb.h +++ b/ospf6d/ospf6_lsdb.h @@ -29,6 +29,9 @@ extern struct ospf6_lsa *ospf6_lsdb_lookup(uint16_t type, uint32_t id, extern struct ospf6_lsa *ospf6_lsdb_lookup_next(uint16_t type, uint32_t id, uint32_t adv_router, struct ospf6_lsdb *lsdb); +extern struct ospf6_lsa *ospf6_find_inter_prefix_lsa(struct ospf6 *ospf6, + struct ospf6_area *area, + struct prefix *p); extern void ospf6_lsdb_add(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); extern void ospf6_lsdb_remove(struct ospf6_lsa *lsa, struct ospf6_lsdb *lsdb); @@ -60,11 +63,11 @@ extern struct ospf6_lsa *ospf6_lsdb_next(const struct route_node *iterend, * it really early. */ #define ALL_LSDB(lsdb, lsa, lsanext) \ - const struct route_node *iterend = \ - ospf6_lsdb_head(lsdb, 0, 0, 0, &lsa); \ - (lsa) != NULL && ospf6_lsa_lock(lsa) \ - && ((lsanext) = ospf6_lsdb_next(iterend, (lsa)), 1); \ - ospf6_lsa_unlock(lsa), (lsa) = (lsanext) + const struct route_node *iterend = ospf6_lsdb_head(lsdb, 0, 0, 0, \ + &lsa); \ + (lsa) != NULL && ospf6_lsa_lock(lsa) && \ + ((lsanext) = ospf6_lsdb_next(iterend, (lsa)), 1); \ + ospf6_lsa_unlock(&lsa), (lsa) = (lsanext) extern void ospf6_lsdb_remove_all(struct ospf6_lsdb *lsdb); extern void ospf6_lsdb_lsa_unlock(struct ospf6_lsa *lsa); diff --git a/ospf6d/ospf6_main.c b/ospf6d/ospf6_main.c index abc460249d68..932304578af1 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -9,7 +9,7 @@ #include <stdlib.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "command.h" #include "vty.h" @@ -62,7 +62,7 @@ struct zebra_privs_t ospf6d_privs = { struct option longopts[] = {{0}}; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; static void __attribute__((noreturn)) ospf6_exit(int status) { @@ -173,6 +173,32 @@ FRR_DAEMON_INFO(ospf6d, OSPF6, .vty_port = OSPF6_VTY_PORT, .n_yang_modules = array_size(ospf6d_yang_modules), ); +/* Max wait time for config to load before accepting hellos */ +#define OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS 600 + +static void ospf6_config_finish(struct event *t) +{ + zlog_err("OSPF6 configuration end timer expired after %d seconds.", + OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS); +} + +static void ospf6_config_start(void) +{ + if (IS_OSPF6_DEBUG_EVENT) + zlog_debug("ospf6d config start received"); + EVENT_OFF(t_ospf6_cfg); + event_add_timer(master, ospf6_config_finish, NULL, + OSPF6_PRE_CONFIG_MAX_WAIT_SECONDS, &t_ospf6_cfg); +} + +static void ospf6_config_end(void) +{ + if (IS_OSPF6_DEBUG_EVENT) + zlog_debug("ospf6d config end received"); + + EVENT_OFF(t_ospf6_cfg); +} + /* Main routine of ospf6d. Treatment of argument and starting ospf finite state machine is handled here. */ int main(int argc, char *argv[], char *envp[]) @@ -217,6 +243,9 @@ int main(int argc, char *argv[], char *envp[]) /* initialize ospf6 */ ospf6_init(master); + /* Configuration processing callback initialization. */ + cmd_init_config_callbacks(ospf6_config_start, ospf6_config_end); + frr_config_fork(); frr_run(master); diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c index b7f261fcf995..e9b19ea0a61f 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -9,7 +9,7 @@ #include "log.h" #include "vty.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "lib_errors.h" #include "checksum.h" @@ -410,26 +410,29 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, /* HelloInterval check */ if (ntohs(hello->hello_interval) != oi->hello_interval) { zlog_warn( - "VRF %s: I/F %s HelloInterval mismatch: (my %d, rcvd %d)", - oi->interface->vrf->name, oi->interface->name, - oi->hello_interval, ntohs(hello->hello_interval)); + "VRF %s: I/F %s HelloInterval mismatch from %pI6 (%pI4): (my %d, rcvd %d)", + oi->interface->vrf->name, oi->interface->name, src, + &oh->router_id, oi->hello_interval, + ntohs(hello->hello_interval)); return; } /* RouterDeadInterval check */ if (ntohs(hello->dead_interval) != oi->dead_interval) { zlog_warn( - "VRF %s: I/F %s DeadInterval mismatch: (my %d, rcvd %d)", - oi->interface->vrf->name, oi->interface->name, - oi->dead_interval, ntohs(hello->dead_interval)); + "VRF %s: I/F %s DeadInterval mismatch from %pI6 (%pI4): (my %d, rcvd %d)", + oi->interface->vrf->name, oi->interface->name, src, + &oh->router_id, oi->dead_interval, + ntohs(hello->dead_interval)); return; } /* E-bit check */ - if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_E) - != OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_E)) { - zlog_warn("VRF %s: IF %s E-bit mismatch", - oi->interface->vrf->name, oi->interface->name); + if (OSPF6_OPT_ISSET(hello->options, OSPF6_OPT_E) != + OSPF6_OPT_ISSET(oi->area->options, OSPF6_OPT_E)) { + zlog_warn("VRF %s: IF %s E-bit mismatch from %pI6 (%pI4)", + oi->interface->vrf->name, oi->interface->name, src, + &oh->router_id); return; } @@ -532,9 +535,9 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, oi->hello_in++; /* Execute neighbor events */ - thread_execute(master, hello_received, on, 0); + event_execute(master, hello_received, on, 0); if (twoway) - thread_execute(master, twoway_received, on, 0); + event_execute(master, twoway_received, on, 0); else { if (OSPF6_GR_IS_ACTIVE_HELPER(on)) { if (IS_DEBUG_OSPF6_GR) @@ -550,7 +553,7 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, * receives one_way hellow when it acts as HELPER for * that specific neighbor. */ - thread_execute(master, oneway_received, on, 0); + event_execute(master, oneway_received, on, 0); } } @@ -591,9 +594,9 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst, /* Schedule interface events */ if (backupseen) - thread_add_event(master, backup_seen, oi, 0, NULL); + event_add_event(master, backup_seen, oi, 0, NULL); if (neighborchange) - thread_add_event(master, neighbor_change, oi, 0, NULL); + event_add_event(master, neighbor_change, oi, 0, NULL); if (neighbor_ifindex_change && on->state == OSPF6_NEIGHBOR_FULL) OSPF6_ROUTER_LSA_SCHEDULE(oi->area); @@ -621,7 +624,7 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, return; case OSPF6_NEIGHBOR_INIT: - thread_execute(master, twoway_received, on, 0); + event_execute(master, twoway_received, on, 0); if (on->state != OSPF6_NEIGHBOR_EXSTART) { if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) zlog_debug( @@ -637,7 +640,7 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, && !CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT) && ntohl(dbdesc->seqnum) == on->dbdesc_seqnum) { /* execute NegotiationDone */ - thread_execute(master, negotiation_done, on, 0); + event_execute(master, negotiation_done, on, 0); /* Record neighbor options */ memcpy(on->options, dbdesc->options, @@ -664,32 +667,33 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, zlog_warn( "DbDesc recv: Master/Slave bit mismatch Nbr %s", on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } if (CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_IBIT)) { zlog_warn("DbDesc recv: Initialize bit mismatch Nbr %s", on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } if (memcmp(on->options, dbdesc->options, sizeof(on->options))) { zlog_warn("DbDesc recv: Option field mismatch Nbr %s", on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum) { zlog_warn( - "DbDesc recv: Sequence number mismatch Nbr %s (%#lx expected)", - on->name, (unsigned long)on->dbdesc_seqnum); - thread_add_event(master, seqnumber_mismatch, on, 0, + "DbDesc recv: Sequence number mismatch Nbr %s (received %#lx, %#lx expected)", + on->name, (unsigned long)ntohl(dbdesc->seqnum), + (unsigned long)on->dbdesc_seqnum); + event_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } @@ -710,7 +714,7 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, zlog_warn( "DbDesc recv: Not duplicate dbdesc in state %s Nbr %s", ospf6_neighbor_state_str[on->state], on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, NULL); + event_add_event(master, seqnumber_mismatch, on, 0, NULL); return; default: @@ -754,8 +758,8 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, zlog_debug( "SeqNumMismatch (E-bit mismatch), discard"); ospf6_lsa_delete(his); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } @@ -783,19 +787,19 @@ static void ospf6_dbdesc_recv_master(struct ospf6_header *oh, /* schedule send lsreq */ if (on->request_list->count) - thread_add_event(master, ospf6_lsreq_send, on, 0, - &on->thread_send_lsreq); + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); - THREAD_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); /* More bit check */ if (!CHECK_FLAG(dbdesc->bits, OSPF6_DBDESC_MBIT) && !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT)) - thread_add_event(master, exchange_done, on, 0, - &on->thread_exchange_done); + event_add_event(master, exchange_done, on, 0, + &on->thread_exchange_done); else { - thread_add_event(master, ospf6_dbdesc_send_newone, on, 0, - &on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send_newone, on, 0, + &on->thread_send_dbdesc); } /* save last received dbdesc */ @@ -824,7 +828,7 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, return; case OSPF6_NEIGHBOR_INIT: - thread_execute(master, twoway_received, on, 0); + event_execute(master, twoway_received, on, 0); if (on->state != OSPF6_NEIGHBOR_EXSTART) { if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) zlog_debug( @@ -851,7 +855,7 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, on->dbdesc_seqnum = ntohl(dbdesc->seqnum); /* schedule NegotiationDone */ - thread_execute(master, negotiation_done, on, 0); + event_execute(master, negotiation_done, on, 0); /* Record neighbor options */ memcpy(on->options, dbdesc->options, @@ -871,9 +875,9 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) zlog_debug( "Duplicated dbdesc causes retransmit"); - THREAD_OFF(on->thread_send_dbdesc); - thread_add_event(master, ospf6_dbdesc_send, on, 0, - &on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); return; } @@ -881,8 +885,8 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, zlog_warn( "DbDesc slave recv: Master/Slave bit mismatch Nbr %s", on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } @@ -890,8 +894,8 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, zlog_warn( "DbDesc slave recv: Initialize bit mismatch Nbr %s", on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } @@ -899,16 +903,17 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, zlog_warn( "DbDesc slave recv: Option field mismatch Nbr %s", on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } if (ntohl(dbdesc->seqnum) != on->dbdesc_seqnum + 1) { zlog_warn( - "DbDesc slave recv: Sequence number mismatch Nbr %s (%#lx expected)", - on->name, (unsigned long)on->dbdesc_seqnum + 1); - thread_add_event(master, seqnumber_mismatch, on, 0, + "DbDesc slave recv: Sequence number mismatch Nbr %s (received %#lx, %#lx expected)", + on->name, (unsigned long)ntohl(dbdesc->seqnum), + (unsigned long)on->dbdesc_seqnum + 1); + event_add_event(master, seqnumber_mismatch, on, 0, NULL); return; } @@ -923,16 +928,16 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV_HDR)) zlog_debug( "Duplicated dbdesc causes retransmit"); - THREAD_OFF(on->thread_send_dbdesc); - thread_add_event(master, ospf6_dbdesc_send, on, 0, - &on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); return; } zlog_warn( "DbDesc slave recv: Not duplicate dbdesc in state %s Nbr %s", ospf6_neighbor_state_str[on->state], on->name); - thread_add_event(master, seqnumber_mismatch, on, 0, NULL); + event_add_event(master, seqnumber_mismatch, on, 0, NULL); return; default: @@ -972,8 +977,8 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, if (IS_OSPF6_DEBUG_MESSAGE(oh->type, RECV)) zlog_debug("E-bit mismatch with LSA Headers"); ospf6_lsa_delete(his); - thread_add_event(master, seqnumber_mismatch, on, 0, - NULL); + event_add_event(master, seqnumber_mismatch, on, 0, + NULL); return; } @@ -994,12 +999,12 @@ static void ospf6_dbdesc_recv_slave(struct ospf6_header *oh, /* schedule send lsreq */ if (on->request_list->count) - thread_add_event(master, ospf6_lsreq_send, on, 0, - &on->thread_send_lsreq); + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); - THREAD_OFF(on->thread_send_dbdesc); - thread_add_event(master, ospf6_dbdesc_send_newone, on, 0, - &on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send_newone, on, 0, + &on->thread_send_dbdesc); /* save last received dbdesc */ memcpy(&on->dbdesc_last, dbdesc, sizeof(struct ospf6_dbdesc)); @@ -1121,7 +1126,7 @@ static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst, "Can't find requested lsa [%s Id:%pI4 Adv:%pI4] send badLSReq", ospf6_lstype_name(e->type), &e->id, &e->adv_router); - thread_add_event(master, bad_lsreq, on, 0, NULL); + event_add_event(master, bad_lsreq, on, 0, NULL); return; } @@ -1131,9 +1136,9 @@ static void ospf6_lsreq_recv(struct in6_addr *src, struct in6_addr *dst, assert(p == OSPF6_MESSAGE_END(oh)); /* schedule send lsupdate */ - THREAD_OFF(on->thread_send_lsupdate); - thread_add_event(master, ospf6_lsupdate_send_neighbor, on, 0, - &on->thread_send_lsupdate); + EVENT_OFF(on->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_neighbor, on, 0, + &on->thread_send_lsupdate); } /* Verify, that the specified memory area contains exactly N valid IPv6 @@ -1537,20 +1542,25 @@ static int ospf6_rxpacket_examin(struct ospf6_interface *oi, struct ospf6_header *oh, const unsigned bytesonwire) { + struct ospf6_neighbor *on; if (MSG_OK != ospf6_packet_examin(oh, bytesonwire)) return MSG_NG; + on = ospf6_neighbor_lookup(oh->router_id, oi); + /* Area-ID check */ if (oh->area_id != oi->area->area_id) { if (oh->area_id == OSPF_AREA_BACKBONE) zlog_warn( - "VRF %s: I/F %s Message may be via Virtual Link: not supported", - oi->interface->vrf->name, oi->interface->name); + "VRF %s: I/F %s (%s, Router-ID: %pI4) Message may be via Virtual Link: not supported", + oi->interface->vrf->name, oi->interface->name, + on ? on->name : "null", &oh->router_id); else zlog_warn( - "VRF %s: I/F %s Area-ID mismatch (my %pI4, rcvd %pI4)", + "VRF %s: I/F %s (%s, Router-ID: %pI4) Area-ID mismatch (my %pI4, rcvd %pI4)", oi->interface->vrf->name, oi->interface->name, + on ? on->name : "null", &oh->router_id, &oi->area->area_id, &oh->area_id); return MSG_NG; } @@ -1558,9 +1568,10 @@ static int ospf6_rxpacket_examin(struct ospf6_interface *oi, /* Instance-ID check */ if (oh->instance_id != oi->instance_id) { zlog_warn( - "VRF %s: I/F %s Instance-ID mismatch (my %u, rcvd %u)", + "VRF %s: I/F %s (%s, Router-ID: %pI4) Instance-ID mismatch (my %u, rcvd %u)", oi->interface->vrf->name, oi->interface->name, - oi->instance_id, oh->instance_id); + on ? on->name : "null", &oh->router_id, oi->instance_id, + oh->instance_id); return MSG_NG; } @@ -1896,18 +1907,18 @@ static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6) return OSPF6_READ_CONTINUE; } -void ospf6_receive(struct thread *thread) +void ospf6_receive(struct event *thread) { int sockfd; struct ospf6 *ospf6; int count = 0; /* add next read thread */ - ospf6 = THREAD_ARG(thread); - sockfd = THREAD_FD(thread); + ospf6 = EVENT_ARG(thread); + sockfd = EVENT_FD(thread); - thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, - &ospf6->t_ospf6_receive); + event_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); while (count < ospf6->write_oi_count) { count++; @@ -2071,9 +2082,9 @@ static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s) return length; } -static void ospf6_write(struct thread *thread) +static void ospf6_write(struct event *thread) { - struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6 *ospf6 = EVENT_ARG(thread); struct ospf6_interface *oi; struct ospf6_header *oh; struct ospf6_packet *op; @@ -2221,17 +2232,32 @@ static void ospf6_write(struct thread *thread) /* If packets still remain in queue, call write thread. */ if (!list_isempty(ospf6->oi_write_q)) - thread_add_write(master, ospf6_write, ospf6, ospf6->fd, - &ospf6->t_write); + event_add_write(master, ospf6_write, ospf6, ospf6->fd, + &ospf6->t_write); } -void ospf6_hello_send(struct thread *thread) +void ospf6_hello_send(struct event *thread) { struct ospf6_interface *oi; struct ospf6_packet *op; uint16_t length = OSPF6_HEADER_SIZE; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); + + /* Check if the GR hello-delay is active. */ + if (oi->gr.hello_delay.t_grace_send) + return; + + /* Check if config is still being processed */ + if (event_is_scheduled(t_ospf6_cfg)) { + if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND)) + zlog_debug( + "Suppressing Hello on interface %s during config load", + oi->interface->name); + event_add_timer(master, ospf6_hello_send, oi, + oi->hello_interval, &oi->thread_send_hello); + return; + } if (oi->state <= OSPF6_INTERFACE_DOWN) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO, SEND_HDR)) @@ -2268,8 +2294,8 @@ void ospf6_hello_send(struct thread *thread) ospf6_packet_add_top(oi, op); /* set next thread */ - thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, - &oi->thread_send_hello); + event_add_timer(master, ospf6_hello_send, oi, oi->hello_interval, + &oi->thread_send_hello); OSPF6_MESSAGE_WRITE_ON(oi); } @@ -2309,9 +2335,9 @@ static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) if ((length + sizeof(struct ospf6_lsa_header) + OSPF6_HEADER_SIZE) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); if (lsanext) - ospf6_lsa_unlock(lsanext); + ospf6_lsa_unlock(&lsanext); break; } stream_put(s, lsa->header, @@ -2322,13 +2348,13 @@ static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s) return length; } -void ospf6_dbdesc_send(struct thread *thread) +void ospf6_dbdesc_send(struct event *thread) { struct ospf6_neighbor *on; uint16_t length = OSPF6_HEADER_SIZE; struct ospf6_packet *op; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); if (on->state < OSPF6_NEIGHBOR_EXSTART) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND)) @@ -2340,9 +2366,9 @@ void ospf6_dbdesc_send(struct thread *thread) /* set next thread if master */ if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) - thread_add_timer(master, ospf6_dbdesc_send, on, - on->ospf6_if->rxmt_interval, - &on->thread_send_dbdesc); + event_add_timer(master, ospf6_dbdesc_send, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_dbdesc); op = ospf6_packet_new(on->ospf6_if->ifmtu); ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s); @@ -2365,13 +2391,13 @@ void ospf6_dbdesc_send(struct thread *thread) OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); } -void ospf6_dbdesc_send_newone(struct thread *thread) +void ospf6_dbdesc_send_newone(struct event *thread) { struct ospf6_neighbor *on; struct ospf6_lsa *lsa, *lsanext; unsigned int size = 0; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); ospf6_lsdb_remove_all(on->dbdesc_list); /* move LSAs from summary_list to dbdesc_list (within neighbor @@ -2389,9 +2415,9 @@ void ospf6_dbdesc_send_newone(struct thread *thread) if (size + sizeof(struct ospf6_lsa_header) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); if (lsanext) - ospf6_lsa_unlock(lsanext); + ospf6_lsa_unlock(&lsanext); break; } @@ -2407,10 +2433,10 @@ void ospf6_dbdesc_send_newone(struct thread *thread) if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) && /* Slave */ !CHECK_FLAG(on->dbdesc_last.bits, OSPF6_DBDESC_MBIT) && !CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT)) - thread_add_event(master, exchange_done, on, 0, - &on->thread_exchange_done); + event_add_event(master, exchange_done, on, 0, + &on->thread_exchange_done); - thread_execute(master, ospf6_dbdesc_send, on, 0); + event_execute(master, ospf6_dbdesc_send, on, 0); } static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s) @@ -2421,9 +2447,9 @@ static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s) for (ALL_LSDB(on->request_list, lsa, lsanext)) { if ((length + OSPF6_HEADER_SIZE) > ospf6_packet_max(on->ospf6_if)) { - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); if (lsanext) - ospf6_lsa_unlock(lsanext); + ospf6_lsa_unlock(&lsanext); break; } stream_putw(s, 0); /* reserved */ @@ -2436,7 +2462,7 @@ static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s) if (last_req != NULL) { if (on->last_ls_req != NULL) - on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req); + ospf6_lsa_unlock(&on->last_ls_req); ospf6_lsa_lock(last_req); on->last_ls_req = last_req; @@ -2487,13 +2513,13 @@ static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on, return length; } -void ospf6_lsreq_send(struct thread *thread) +void ospf6_lsreq_send(struct event *thread) { struct ospf6_neighbor *on; struct ospf6_packet *op; uint16_t length = OSPF6_HEADER_SIZE; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); /* LSReq will be sent only in ExStart or Loading */ if (on->state != OSPF6_NEIGHBOR_EXCHANGE @@ -2507,7 +2533,8 @@ void ospf6_lsreq_send(struct thread *thread) /* schedule loading_done if request list is empty */ if (on->request_list->count == 0) { - thread_add_event(master, loading_done, on, 0, NULL); + event_add_event(master, loading_done, on, 0, + &on->event_loading_done); return; } @@ -2540,9 +2567,9 @@ void ospf6_lsreq_send(struct thread *thread) /* set next thread */ if (on->request_list->count != 0) { - thread_add_timer(master, ospf6_lsreq_send, on, - on->ospf6_if->rxmt_interval, - &on->thread_send_lsreq); + event_add_timer(master, ospf6_lsreq_send, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_lsreq); } } @@ -2550,9 +2577,7 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on, struct ospf6_interface *oi, struct ospf6_packet *op) { - if (on) { - if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT) || (on->ospf6_if->state == OSPF6_INTERFACE_DR) || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) @@ -2569,6 +2594,8 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on, op->dst = alldrouters6; } if (oi) { + struct ospf6 *ospf6; + ospf6_fill_hdr_checksum(oi, op); ospf6_packet_add(oi, op); /* If ospf instance is being deleted, send the packet @@ -2576,12 +2603,27 @@ static void ospf6_send_lsupdate(struct ospf6_neighbor *on, */ if ((oi->area == NULL) || (oi->area->ospf6 == NULL)) return; - if (oi->area->ospf6->inst_shutdown) { + + ospf6 = oi->area->ospf6; + if (ospf6->inst_shutdown) { if (oi->on_write_q == 0) { - listnode_add(oi->area->ospf6->oi_write_q, oi); + listnode_add(ospf6->oi_write_q, oi); oi->on_write_q = 1; } - thread_execute(master, ospf6_write, oi->area->ospf6, 0); + /* + * When ospf6d immediately calls event_execute + * for items in the oi_write_q. The event_execute + * will call ospf6_write and cause the oi_write_q + * to be emptied. *IF* there is already an event + * scheduled for the oi_write_q by something else + * then when it wakes up in the future and attempts + * to cycle through items in the queue it will + * assert. Let's stop the t_write event and + * if ospf6_write doesn't finish up the work + * it will schedule itself again. + */ + event_cancel(&ospf6->t_write); + event_execute(master, ospf6_write, ospf6, 0); } else OSPF6_MESSAGE_WRITE_ON(oi); } @@ -2665,14 +2707,14 @@ static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on, return length; } -void ospf6_lsupdate_send_neighbor(struct thread *thread) +void ospf6_lsupdate_send_neighbor(struct event *thread) { struct ospf6_neighbor *on; struct ospf6_packet *op; uint16_t length = OSPF6_HEADER_SIZE; int lsa_cnt = 0; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR)) zlog_debug("LSUpdate to neighbor %s", on->name); @@ -2723,12 +2765,12 @@ void ospf6_lsupdate_send_neighbor(struct thread *thread) ospf6_packet_free(op); if (on->lsupdate_list->count != 0) { - thread_add_event(master, ospf6_lsupdate_send_neighbor, on, 0, - &on->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_neighbor, on, 0, + &on->thread_send_lsupdate); } else if (on->retrans_list->count != 0) { - thread_add_timer(master, ospf6_lsupdate_send_neighbor, on, - on->ospf6_if->rxmt_interval, - &on->thread_send_lsupdate); + event_add_timer(master, ospf6_lsupdate_send_neighbor, on, + on->ospf6_if->rxmt_interval, + &on->thread_send_lsupdate); } } @@ -2800,14 +2842,14 @@ static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi, return length; } -void ospf6_lsupdate_send_interface(struct thread *thread) +void ospf6_lsupdate_send_interface(struct event *thread) { struct ospf6_interface *oi; struct ospf6_packet *op; uint16_t length = OSPF6_HEADER_SIZE; int lsa_cnt = 0; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); if (oi->state <= OSPF6_INTERFACE_WAITING) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, @@ -2836,18 +2878,18 @@ void ospf6_lsupdate_send_interface(struct thread *thread) ospf6_packet_free(op); if (oi->lsupdate_list->count > 0) { - thread_add_event(master, ospf6_lsupdate_send_interface, oi, 0, - &oi->thread_send_lsupdate); + event_add_event(master, ospf6_lsupdate_send_interface, oi, 0, + &oi->thread_send_lsupdate); } } -void ospf6_lsack_send_neighbor(struct thread *thread) +void ospf6_lsack_send_neighbor(struct event *thread) { struct ospf6_neighbor *on; struct ospf6_packet *op; uint16_t length = OSPF6_HEADER_SIZE; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); if (on->state < OSPF6_NEIGHBOR_EXCHANGE) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR)) @@ -2882,8 +2924,8 @@ void ospf6_lsack_send_neighbor(struct thread *thread) OSPF6_MESSAGE_WRITE_ON(on->ospf6_if); if (on->lsack_list->count > 0) - thread_add_event(master, ospf6_lsack_send_neighbor, on, 0, - &on->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_neighbor, on, 0, + &on->thread_send_lsack); } static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi, @@ -2898,13 +2940,13 @@ static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi, > ospf6_packet_max(oi)) { /* if we run out of packet size/space here, better to try again soon. */ - THREAD_OFF(oi->thread_send_lsack); - thread_add_event(master, ospf6_lsack_send_interface, oi, - 0, &oi->thread_send_lsack); + EVENT_OFF(oi->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_interface, oi, + 0, &oi->thread_send_lsack); - ospf6_lsa_unlock(lsa); + ospf6_lsa_unlock(&lsa); if (lsanext) - ospf6_lsa_unlock(lsanext); + ospf6_lsa_unlock(&lsanext); break; } ospf6_lsa_age_update_to_send(lsa, oi->transdelay); @@ -2917,13 +2959,13 @@ static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi, return length; } -void ospf6_lsack_send_interface(struct thread *thread) +void ospf6_lsack_send_interface(struct event *thread) { struct ospf6_interface *oi; struct ospf6_packet *op; uint16_t length = OSPF6_HEADER_SIZE; - oi = (struct ospf6_interface *)THREAD_ARG(thread); + oi = (struct ospf6_interface *)EVENT_ARG(thread); if (oi->state <= OSPF6_INTERFACE_WAITING) { if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSACK, SEND_HDR)) @@ -2964,8 +3006,8 @@ void ospf6_lsack_send_interface(struct thread *thread) OSPF6_MESSAGE_WRITE_ON(oi); if (oi->lsack_list->count > 0) - thread_add_event(master, ospf6_lsack_send_interface, oi, 0, - &oi->thread_send_lsack); + event_add_event(master, ospf6_lsack_send_interface, oi, 0, + &oi->thread_send_lsack); } /* Commands */ diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h index 1f6a7e86e037..2b25b074457b 100644 --- a/ospf6d/ospf6_message.h +++ b/ospf6d/ospf6_message.h @@ -158,16 +158,16 @@ extern void ospf6_fifo_free(struct ospf6_fifo *fifo); extern int ospf6_iobuf_size(unsigned int size); extern void ospf6_message_terminate(void); -extern void ospf6_receive(struct thread *thread); - -extern void ospf6_hello_send(struct thread *thread); -extern void ospf6_dbdesc_send(struct thread *thread); -extern void ospf6_dbdesc_send_newone(struct thread *thread); -extern void ospf6_lsreq_send(struct thread *thread); -extern void ospf6_lsupdate_send_interface(struct thread *thread); -extern void ospf6_lsupdate_send_neighbor(struct thread *thread); -extern void ospf6_lsack_send_interface(struct thread *thread); -extern void ospf6_lsack_send_neighbor(struct thread *thread); +extern void ospf6_receive(struct event *thread); + +extern void ospf6_hello_send(struct event *thread); +extern void ospf6_dbdesc_send(struct event *thread); +extern void ospf6_dbdesc_send_newone(struct event *thread); +extern void ospf6_lsreq_send(struct event *thread); +extern void ospf6_lsupdate_send_interface(struct event *thread); +extern void ospf6_lsupdate_send_neighbor(struct event *thread); +extern void ospf6_lsack_send_interface(struct event *thread); +extern void ospf6_lsack_send_neighbor(struct event *thread); extern int config_write_ospf6_debug_message(struct vty *); extern void install_element_ospf6_debug_message(void); diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 85002962ca9e..e1aec06f8e8b 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -7,7 +7,7 @@ #include "log.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "command.h" @@ -98,9 +98,10 @@ static void ospf6_neighbor_clear_ls_lists(struct ospf6_neighbor *on) ospf6_lsdb_remove_all(on->summary_list); if (on->last_ls_req) { - ospf6_lsa_unlock(on->last_ls_req); + ospf6_lsa_unlock(&on->last_ls_req); on->last_ls_req = NULL; } + ospf6_lsdb_remove_all(on->request_list); for (ALL_LSDB(on->retrans_list, lsa, lsanext)) { ospf6_decrement_retrans_count(lsa); @@ -163,18 +164,19 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on) ospf6_lsdb_delete(on->lsupdate_list); ospf6_lsdb_delete(on->lsack_list); - THREAD_OFF(on->inactivity_timer); + EVENT_OFF(on->inactivity_timer); - THREAD_OFF(on->last_dbdesc_release_timer); + EVENT_OFF(on->last_dbdesc_release_timer); - THREAD_OFF(on->thread_send_dbdesc); - THREAD_OFF(on->thread_send_lsreq); - THREAD_OFF(on->thread_send_lsupdate); - THREAD_OFF(on->thread_send_lsack); - THREAD_OFF(on->thread_exchange_done); - THREAD_OFF(on->thread_adj_ok); + EVENT_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_lsreq); + EVENT_OFF(on->thread_send_lsupdate); + EVENT_OFF(on->thread_send_lsack); + EVENT_OFF(on->thread_exchange_done); + EVENT_OFF(on->thread_adj_ok); + EVENT_OFF(on->event_loading_done); - THREAD_OFF(on->gr_helper_info.t_grace_timer); + EVENT_OFF(on->gr_helper_info.t_grace_timer); bfd_sess_free(&on->bfd_session); XFREE(MTYPE_OSPF6_NEIGHBOR, on); @@ -196,10 +198,12 @@ static void ospf6_neighbor_state_change(uint8_t next_state, /* log */ if (IS_OSPF6_DEBUG_NEIGHBOR(STATE)) { - zlog_debug("Neighbor state change %s: [%s]->[%s] (%s)", - on->name, ospf6_neighbor_state_str[prev_state], - ospf6_neighbor_state_str[next_state], - ospf6_neighbor_event_string(event)); + zlog_debug( + "Neighbor state change %s (Router-ID: %pI4): [%s]->[%s] (%s)", + on->name, &on->router_id, + ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); } /* Optionally notify about adjacency changes */ @@ -209,10 +213,13 @@ static void ospf6_neighbor_state_change(uint8_t next_state, OSPF6_LOG_ADJACENCY_DETAIL) || (next_state == OSPF6_NEIGHBOR_FULL) || (next_state < prev_state))) - zlog_notice("AdjChg: Nbr %s: %s -> %s (%s)", on->name, - ospf6_neighbor_state_str[prev_state], - ospf6_neighbor_state_str[next_state], - ospf6_neighbor_event_string(event)); + zlog_notice( + "AdjChg: Nbr %pI4(%s) on %s: %s -> %s (%s)", + &on->router_id, + vrf_id_to_name(on->ospf6_if->interface->vrf->vrf_id), + on->name, ospf6_neighbor_state_str[prev_state], + ospf6_neighbor_state_str[next_state], + ospf6_neighbor_event_string(event)); if (prev_state == OSPF6_NEIGHBOR_FULL || next_state == OSPF6_NEIGHBOR_FULL) { @@ -267,31 +274,31 @@ static int need_adjacency(struct ospf6_neighbor *on) return 0; } -void hello_received(struct thread *thread) +void hello_received(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) zlog_debug("Neighbor Event %s: *HelloReceived*", on->name); /* reset Inactivity Timer */ - THREAD_OFF(on->inactivity_timer); - thread_add_timer(master, inactivity_timer, on, - on->ospf6_if->dead_interval, &on->inactivity_timer); + EVENT_OFF(on->inactivity_timer); + event_add_timer(master, inactivity_timer, on, + on->ospf6_if->dead_interval, &on->inactivity_timer); if (on->state <= OSPF6_NEIGHBOR_DOWN) ospf6_neighbor_state_change(OSPF6_NEIGHBOR_INIT, on, OSPF6_NEIGHBOR_EVENT_HELLO_RCVD); } -void twoway_received(struct thread *thread) +void twoway_received(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state > OSPF6_NEIGHBOR_INIT) @@ -300,7 +307,7 @@ void twoway_received(struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) zlog_debug("Neighbor Event %s: *2Way-Received*", on->name); - thread_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); + event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); if (!need_adjacency(on)) { ospf6_neighbor_state_change(OSPF6_NEIGHBOR_TWOWAY, on, @@ -314,17 +321,17 @@ void twoway_received(struct thread *thread) SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); - THREAD_OFF(on->thread_send_dbdesc); - thread_add_event(master, ospf6_dbdesc_send, on, 0, - &on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); } -void negotiation_done(struct thread *thread) +void negotiation_done(struct event *thread) { struct ospf6_neighbor *on; struct ospf6_lsa *lsa, *lsanext; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state != OSPF6_NEIGHBOR_EXSTART) @@ -368,19 +375,19 @@ void negotiation_done(struct thread *thread) OSPF6_NEIGHBOR_EVENT_NEGOTIATION_DONE); } -static void ospf6_neighbor_last_dbdesc_release(struct thread *thread) +static void ospf6_neighbor_last_dbdesc_release(struct event *thread) { - struct ospf6_neighbor *on = THREAD_ARG(thread); + struct ospf6_neighbor *on = EVENT_ARG(thread); assert(on); memset(&on->dbdesc_last, 0, sizeof(struct ospf6_dbdesc)); } -void exchange_done(struct thread *thread) +void exchange_done(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state != OSPF6_NEIGHBOR_EXCHANGE) @@ -389,15 +396,15 @@ void exchange_done(struct thread *thread) if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) zlog_debug("Neighbor Event %s: *ExchangeDone*", on->name); - THREAD_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); ospf6_lsdb_remove_all(on->dbdesc_list); /* RFC 2328 (10.8): Release the last dbdesc after dead_interval */ if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) { - THREAD_OFF(on->last_dbdesc_release_timer); - thread_add_timer(master, ospf6_neighbor_last_dbdesc_release, on, - on->ospf6_if->dead_interval, - &on->last_dbdesc_release_timer); + EVENT_OFF(on->last_dbdesc_release_timer); + event_add_timer(master, ospf6_neighbor_last_dbdesc_release, on, + on->ospf6_if->dead_interval, + &on->last_dbdesc_release_timer); } if (on->request_list->count == 0) @@ -407,8 +414,8 @@ void exchange_done(struct thread *thread) ospf6_neighbor_state_change(OSPF6_NEIGHBOR_LOADING, on, OSPF6_NEIGHBOR_EVENT_EXCHANGE_DONE); - thread_add_event(master, ospf6_lsreq_send, on, 0, - &on->thread_send_lsreq); + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); } } @@ -423,20 +430,21 @@ void ospf6_check_nbr_loading(struct ospf6_neighbor *on) if ((on->state == OSPF6_NEIGHBOR_LOADING) || (on->state == OSPF6_NEIGHBOR_EXCHANGE)) { if (on->request_list->count == 0) - thread_add_event(master, loading_done, on, 0, NULL); + event_add_event(master, loading_done, on, 0, + &on->event_loading_done); else if (on->last_ls_req == NULL) { - THREAD_OFF(on->thread_send_lsreq); - thread_add_event(master, ospf6_lsreq_send, on, 0, - &on->thread_send_lsreq); + EVENT_OFF(on->thread_send_lsreq); + event_add_event(master, ospf6_lsreq_send, on, 0, + &on->thread_send_lsreq); } } } -void loading_done(struct thread *thread) +void loading_done(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state != OSPF6_NEIGHBOR_LOADING) @@ -451,11 +459,11 @@ void loading_done(struct thread *thread) OSPF6_NEIGHBOR_EVENT_LOADING_DONE); } -void adj_ok(struct thread *thread) +void adj_ok(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) @@ -468,9 +476,9 @@ void adj_ok(struct thread *thread) SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT); SET_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT); - THREAD_OFF(on->thread_send_dbdesc); - thread_add_event(master, ospf6_dbdesc_send, on, 0, - &on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); } else if (on->state >= OSPF6_NEIGHBOR_EXSTART && !need_adjacency(on)) { ospf6_neighbor_state_change(OSPF6_NEIGHBOR_TWOWAY, on, @@ -479,11 +487,11 @@ void adj_ok(struct thread *thread) } } -void seqnumber_mismatch(struct thread *thread) +void seqnumber_mismatch(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state < OSPF6_NEIGHBOR_EXCHANGE) @@ -500,18 +508,18 @@ void seqnumber_mismatch(struct thread *thread) ospf6_neighbor_clear_ls_lists(on); - THREAD_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ - thread_add_event(master, ospf6_dbdesc_send, on, 0, - &on->thread_send_dbdesc); + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); } -void bad_lsreq(struct thread *thread) +void bad_lsreq(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state < OSPF6_NEIGHBOR_EXCHANGE) @@ -528,19 +536,18 @@ void bad_lsreq(struct thread *thread) ospf6_neighbor_clear_ls_lists(on); - THREAD_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_dbdesc); on->dbdesc_seqnum++; /* Incr seqnum as per RFC2328, sec 10.3 */ - thread_add_event(master, ospf6_dbdesc_send, on, 0, - &on->thread_send_dbdesc); - + event_add_event(master, ospf6_dbdesc_send, on, 0, + &on->thread_send_dbdesc); } -void oneway_received(struct thread *thread) +void oneway_received(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (on->state < OSPF6_NEIGHBOR_TWOWAY) @@ -551,23 +558,23 @@ void oneway_received(struct thread *thread) ospf6_neighbor_state_change(OSPF6_NEIGHBOR_INIT, on, OSPF6_NEIGHBOR_EVENT_ONEWAY_RCVD); - thread_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); + event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); ospf6_neighbor_clear_ls_lists(on); - THREAD_OFF(on->thread_send_dbdesc); - THREAD_OFF(on->thread_send_lsreq); - THREAD_OFF(on->thread_send_lsupdate); - THREAD_OFF(on->thread_send_lsack); - THREAD_OFF(on->thread_exchange_done); - THREAD_OFF(on->thread_adj_ok); + EVENT_OFF(on->thread_send_dbdesc); + EVENT_OFF(on->thread_send_lsreq); + EVENT_OFF(on->thread_send_lsupdate); + EVENT_OFF(on->thread_send_lsack); + EVENT_OFF(on->thread_exchange_done); + EVENT_OFF(on->thread_adj_ok); } -void inactivity_timer(struct thread *thread) +void inactivity_timer(struct event *thread) { struct ospf6_neighbor *on; - on = (struct ospf6_neighbor *)THREAD_ARG(thread); + on = (struct ospf6_neighbor *)EVENT_ARG(thread); assert(on); if (IS_OSPF6_DEBUG_NEIGHBOR(EVENT)) @@ -583,8 +590,7 @@ void inactivity_timer(struct thread *thread) ospf6_neighbor_state_change( OSPF6_NEIGHBOR_DOWN, on, OSPF6_NEIGHBOR_EVENT_INACTIVITY_TIMER); - thread_add_event(master, neighbor_change, on->ospf6_if, 0, - NULL); + event_add_event(master, neighbor_change, on->ospf6_if, 0, NULL); listnode_delete(on->ospf6_if->neighbor_list, on); ospf6_neighbor_delete(on); @@ -595,9 +601,9 @@ void inactivity_timer(struct thread *thread) "%s, Acting as HELPER for this neighbour, So restart the dead timer.", __PRETTY_FUNCTION__); - thread_add_timer(master, inactivity_timer, on, - on->ospf6_if->dead_interval, - &on->inactivity_timer); + event_add_timer(master, inactivity_timer, on, + on->ospf6_if->dead_interval, + &on->inactivity_timer); } } @@ -812,7 +818,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty, timerclear(&res); - if (thread_is_scheduled(on->thread_send_dbdesc)) + if (event_is_scheduled(on->thread_send_dbdesc)) timersub(&on->thread_send_dbdesc->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); json_object_int_add(json_neighbor, "pendingLsaDbDescCount", @@ -821,8 +827,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, duration); json_object_string_add( json_neighbor, "dbDescSendThread", - (thread_is_scheduled(on->thread_send_dbdesc) ? "on" - : "off")); + (event_is_scheduled(on->thread_send_dbdesc) ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) json_object_array_add( @@ -831,7 +837,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty, json_array); timerclear(&res); - if (thread_is_scheduled(on->thread_send_lsreq)) + if (event_is_scheduled(on->thread_send_lsreq)) timersub(&on->thread_send_lsreq->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); json_object_int_add(json_neighbor, "pendingLsaLsReqCount", @@ -840,8 +846,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, duration); json_object_string_add( json_neighbor, "lsReqSendThread", - (thread_is_scheduled(on->thread_send_lsreq) ? "on" - : "off")); + (event_is_scheduled(on->thread_send_lsreq) ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->request_list, lsa, lsanext)) json_object_array_add( @@ -851,7 +857,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty, timerclear(&res); - if (thread_is_scheduled(on->thread_send_lsupdate)) + if (event_is_scheduled(on->thread_send_lsupdate)) timersub(&on->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); @@ -861,9 +867,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, duration); json_object_string_add( json_neighbor, "lsUpdateSendThread", - (thread_is_scheduled(on->thread_send_lsupdate) - ? "on" - : "off")); + (event_is_scheduled(on->thread_send_lsupdate) ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) json_object_array_add( @@ -872,7 +877,7 @@ static void ospf6_neighbor_show_detail(struct vty *vty, json_array); timerclear(&res); - if (thread_is_scheduled(on->thread_send_lsack)) + if (event_is_scheduled(on->thread_send_lsack)) timersub(&on->thread_send_lsack->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); json_object_int_add(json_neighbor, "pendingLsaLsAckCount", @@ -881,8 +886,8 @@ static void ospf6_neighbor_show_detail(struct vty *vty, duration); json_object_string_add( json_neighbor, "lsAckSendThread", - (thread_is_scheduled(on->thread_send_lsack) ? "on" - : "off")); + (event_is_scheduled(on->thread_send_lsack) ? "on" + : "off")); json_array = json_object_new_array(); for (ALL_LSDB(on->lsack_list, lsa, lsanext)) json_object_array_add( @@ -970,52 +975,51 @@ static void ospf6_neighbor_show_detail(struct vty *vty, vty_out(vty, " %s\n", lsa->name); timerclear(&res); - if (thread_is_scheduled(on->thread_send_dbdesc)) + if (event_is_scheduled(on->thread_send_dbdesc)) timersub(&on->thread_send_dbdesc->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for DbDesc in Time %s [thread %s]\n", on->dbdesc_list->count, duration, - (thread_is_scheduled(on->thread_send_dbdesc) ? "on" - : "off")); + (event_is_scheduled(on->thread_send_dbdesc) ? "on" + : "off")); for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); timerclear(&res); - if (thread_is_scheduled(on->thread_send_lsreq)) + if (event_is_scheduled(on->thread_send_lsreq)) timersub(&on->thread_send_lsreq->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSReq in Time %s [thread %s]\n", on->request_list->count, duration, - (thread_is_scheduled(on->thread_send_lsreq) ? "on" - : "off")); + (event_is_scheduled(on->thread_send_lsreq) ? "on" + : "off")); for (ALL_LSDB(on->request_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); timerclear(&res); - if (thread_is_scheduled(on->thread_send_lsupdate)) + if (event_is_scheduled(on->thread_send_lsupdate)) timersub(&on->thread_send_lsupdate->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSUpdate in Time %s [thread %s]\n", on->lsupdate_list->count, duration, - (thread_is_scheduled(on->thread_send_lsupdate) - ? "on" - : "off")); + (event_is_scheduled(on->thread_send_lsupdate) ? "on" + : "off")); for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); timerclear(&res); - if (thread_is_scheduled(on->thread_send_lsack)) + if (event_is_scheduled(on->thread_send_lsack)) timersub(&on->thread_send_lsack->u.sands, &now, &res); timerstring(&res, duration, sizeof(duration)); vty_out(vty, " %d Pending LSAs for LSAck in Time %s [thread %s]\n", on->lsack_list->count, duration, - (thread_is_scheduled(on->thread_send_lsack) ? "on" - : "off")); + (event_is_scheduled(on->thread_send_lsack) ? "on" + : "off")); for (ALL_LSDB(on->lsack_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index d9abd1ff94f2..226f4c1322b9 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -36,7 +36,7 @@ struct ospf6_helper_info { * helper until this timer until * this timer expires. */ - struct thread *t_grace_timer; + struct event *t_grace_timer; /* Helper status */ uint32_t gr_helper_status; @@ -111,18 +111,19 @@ struct ospf6_neighbor { struct ospf6_lsa *last_ls_req; /* Inactivity timer */ - struct thread *inactivity_timer; + struct event *inactivity_timer; /* Timer to release the last dbdesc packet */ - struct thread *last_dbdesc_release_timer; + struct event *last_dbdesc_release_timer; /* Thread for sending message */ - struct thread *thread_send_dbdesc; - struct thread *thread_send_lsreq; - struct thread *thread_send_lsupdate; - struct thread *thread_send_lsack; - struct thread *thread_exchange_done; - struct thread *thread_adj_ok; + struct event *thread_send_dbdesc; + struct event *thread_send_lsreq; + struct event *thread_send_lsupdate; + struct event *thread_send_lsack; + struct event *thread_exchange_done; + struct event *thread_adj_ok; + struct event *event_loading_done; /* BFD information */ struct bfd_session_params *bfd_session; @@ -190,16 +191,16 @@ struct ospf6_neighbor *ospf6_neighbor_create(uint32_t router_id, void ospf6_neighbor_delete(struct ospf6_neighbor *on); /* Neighbor event */ -extern void hello_received(struct thread *thread); -extern void twoway_received(struct thread *thread); -extern void negotiation_done(struct thread *thread); -extern void exchange_done(struct thread *thread); -extern void loading_done(struct thread *thread); -extern void adj_ok(struct thread *thread); -extern void seqnumber_mismatch(struct thread *thread); -extern void bad_lsreq(struct thread *thread); -extern void oneway_received(struct thread *thread); -extern void inactivity_timer(struct thread *thread); +extern void hello_received(struct event *thread); +extern void twoway_received(struct event *thread); +extern void negotiation_done(struct event *thread); +extern void exchange_done(struct event *thread); +extern void loading_done(struct event *thread); +extern void adj_ok(struct event *thread); +extern void seqnumber_mismatch(struct event *thread); +extern void bad_lsreq(struct event *thread); +extern void oneway_received(struct event *thread); +extern void inactivity_timer(struct event *thread); extern void ospf6_check_nbr_loading(struct ospf6_neighbor *on); extern void ospf6_neighbor_init(void); diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index c5627212f8c0..60b7cc59e9c7 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -29,11 +29,11 @@ extern int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst, listnode_add(oi->area->ospf6->oi_write_q, (oi)); \ (oi)->on_write_q = 1; \ } \ - if (list_was_empty \ - && !list_isempty(oi->area->ospf6->oi_write_q)) \ - thread_add_write(master, ospf6_write, oi->area->ospf6, \ - oi->area->ospf6->fd, \ - &oi->area->ospf6->t_write); \ + if (list_was_empty && \ + !list_isempty(oi->area->ospf6->oi_write_q)) \ + event_add_write(master, ospf6_write, oi->area->ospf6, \ + oi->area->ospf6->fd, \ + &oi->area->ospf6->t_write); \ } while (0) #endif /* OSPF6_NETWORK_H */ diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index d62a3a63223d..e7a10eba4152 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -13,7 +13,7 @@ #include "vty.h" #include "linklist.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "plist.h" #include "filter.h" @@ -976,9 +976,9 @@ int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route, } /* This function performs ABR related processing */ -static void ospf6_abr_task_timer(struct thread *thread) +static void ospf6_abr_task_timer(struct event *thread) { - struct ospf6 *ospf6 = THREAD_ARG(thread); + struct ospf6 *ospf6 = EVENT_ARG(thread); if (IS_OSPF6_DEBUG_ABR) zlog_debug("Running ABR task on timer"); @@ -992,7 +992,7 @@ static void ospf6_abr_task_timer(struct thread *thread) void ospf6_schedule_abr_task(struct ospf6 *ospf6) { - if (thread_is_scheduled(ospf6->t_abr_task)) { + if (event_is_scheduled(ospf6->t_abr_task)) { if (IS_OSPF6_DEBUG_ABR) zlog_debug("ABR task already scheduled"); return; @@ -1001,8 +1001,8 @@ void ospf6_schedule_abr_task(struct ospf6 *ospf6) if (IS_OSPF6_DEBUG_ABR) zlog_debug("Scheduling ABR task"); - thread_add_timer(master, ospf6_abr_task_timer, ospf6, - OSPF6_ABR_TASK_DELAY, &ospf6->t_abr_task); + event_add_timer(master, ospf6_abr_task_timer, ospf6, + OSPF6_ABR_TASK_DELAY, &ospf6->t_abr_task); } /* Flush the NSSA LSAs from the area */ @@ -1089,9 +1089,9 @@ static void ospf6_ase_lsa_refresh(struct ospf6 *o) route->path.origin.id, o->router_id, o->lsdb); if (old) { - THREAD_OFF(old->refresh); - thread_add_event(master, ospf6_lsa_refresh, old, 0, - &old->refresh); + EVENT_OFF(old->refresh); + event_add_event(master, ospf6_lsa_refresh, old, 0, + &old->refresh); } else { ospf6_as_external_lsa_originate(route, o); } @@ -1164,9 +1164,9 @@ void ospf6_area_nssa_update(struct ospf6_area *area) lsa)) { if (IS_OSPF6_DEBUG_NSSA) ospf6_lsa_header_print(lsa); - THREAD_OFF(lsa->refresh); - thread_add_event(master, ospf6_lsa_refresh, lsa, - 0, &lsa->refresh); + EVENT_OFF(lsa->refresh); + event_add_event(master, ospf6_lsa_refresh, lsa, + 0, &lsa->refresh); } } } @@ -1436,7 +1436,7 @@ DEFPY (no_area_nssa_range, SET_FLAG(range->flag, OSPF6_ROUTE_REMOVE); /* Redo summaries if required */ - thread_execute(master, ospf6_abr_task_timer, ospf6, 0); + event_execute(master, ospf6_abr_task_timer, ospf6, 0); } ospf6_route_remove(range, oa->nssa_range_table); diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 72dfa240afde..443032933d0f 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -267,7 +267,8 @@ int ospf6_num_nexthops(struct list *nh_list) return (listcount(nh_list)); } -void ospf6_add_nexthop(struct list *nh_list, int ifindex, struct in6_addr *addr) +void ospf6_add_nexthop(struct list *nh_list, int ifindex, + const struct in6_addr *addr) { struct ospf6_nexthop *nh; struct ospf6_nexthop nh_match; diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 2d4fcc930ece..c2125951ec52 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -312,7 +312,7 @@ extern int ospf6_num_nexthops(struct list *nh_list); extern void ospf6_copy_nexthops(struct list *dst, struct list *src); extern void ospf6_merge_nexthops(struct list *dst, struct list *src); extern void ospf6_add_nexthop(struct list *nh_list, int ifindex, - struct in6_addr *addr); + const struct in6_addr *addr); extern void ospf6_add_route_nexthop_blackhole(struct ospf6_route *route); extern int ospf6_num_nexthops(struct list *nh_list); extern bool ospf6_route_cmp_nexthops(struct ospf6_route *a, diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index 46fa27dd9e93..f88667bfd077 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -1383,7 +1383,7 @@ static int ospf6TrapIfStateChange(struct ospf6_interface *oi, int next_state, } /* Register OSPFv3-MIB. */ -static int ospf6_snmp_init(struct thread_master *master) +static int ospf6_snmp_init(struct event_loop *master) { smux_init(master); REGISTER_MIB("OSPFv3MIB", ospfv3_variables, variable, ospfv3_oid); diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index 112934bf1eb2..e39ae504a238 100644 --- a/ospf6d/ospf6_spf.c +++ b/ospf6d/ospf6_spf.c @@ -13,7 +13,7 @@ #include "vty.h" #include "prefix.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "lib_errors.h" #include "ospf6_lsa.h" @@ -310,7 +310,7 @@ static void ospf6_nexthop_calc(struct ospf6_vertex *w, struct ospf6_vertex *v, static int ospf6_spf_install(struct ospf6_vertex *v, struct ospf6_route_table *result_table) { - struct ospf6_route *route, *parent_route; + struct ospf6_route *route; struct ospf6_vertex *prev; if (IS_OSPF6_DEBUG_SPF(PROCESS)) @@ -330,7 +330,12 @@ static int ospf6_spf_install(struct ospf6_vertex *v, zlog_debug( " another path found to route %pFX lsa %s, merge", &route->prefix, v->lsa->name); - ospf6_spf_merge_nexthops_to_route(route, v); + + /* merging the parent's nexthop information to the child's + * if the parent is not the root of the tree. + */ + if (!ospf6_merge_parents_nh_to_child(v, route, result_table)) + ospf6_spf_merge_nexthops_to_route(route, v); prev = (struct ospf6_vertex *)route->route_option; assert(prev->hops <= v->hops); @@ -396,13 +401,7 @@ static int ospf6_spf_install(struct ospf6_vertex *v, * installed, * its parent's route's nexthops have already been installed. */ - if (v->parent && v->parent->hops) { - parent_route = - ospf6_route_lookup(&v->parent->vertex_id, result_table); - if (parent_route) { - ospf6_route_merge_nexthops(route, parent_route); - } - } + ospf6_merge_parents_nh_to_child(v, route, result_table); if (v->parent) listnode_add_sort(v->parent->child_list, v); @@ -590,7 +589,7 @@ static void ospf6_spf_log_database(struct ospf6_area *oa) zlog_debug("%s", buffer); } -static void ospf6_spf_calculation_thread(struct thread *t) +static void ospf6_spf_calculation_thread(struct event *t) { struct ospf6_area *oa; struct ospf6 *ospf6; @@ -599,7 +598,7 @@ static void ospf6_spf_calculation_thread(struct thread *t) int areas_processed = 0; char rbuf[32]; - ospf6 = (struct ospf6 *)THREAD_ARG(t); + ospf6 = (struct ospf6 *)EVENT_ARG(t); /* execute SPF calculation */ monotime(&start); @@ -687,7 +686,7 @@ void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason) } /* SPF calculation timer is already scheduled. */ - if (thread_is_scheduled(ospf6->t_spf_calc)) { + if (event_is_scheduled(ospf6->t_spf_calc)) { if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) zlog_debug( "SPF: calculation timer is already scheduled: %p", @@ -724,9 +723,9 @@ void ospf6_spf_schedule(struct ospf6 *ospf6, unsigned int reason) if (IS_OSPF6_DEBUG_SPF(PROCESS) || IS_OSPF6_DEBUG_SPF(TIME)) zlog_debug("SPF: Rescheduling in %ld msec", delay); - THREAD_OFF(ospf6->t_spf_calc); - thread_add_timer_msec(master, ospf6_spf_calculation_thread, ospf6, - delay, &ospf6->t_spf_calc); + EVENT_OFF(ospf6->t_spf_calc); + event_add_timer_msec(master, ospf6_spf_calculation_thread, ospf6, delay, + &ospf6->t_spf_calc); } void ospf6_spf_display_subtree(struct vty *vty, const char *prefix, int rest, @@ -1228,7 +1227,7 @@ int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, return 0; } -static void ospf6_ase_calculate_timer(struct thread *t) +static void ospf6_ase_calculate_timer(struct event *t) { struct ospf6 *ospf6; struct ospf6_lsa *lsa; @@ -1236,7 +1235,7 @@ static void ospf6_ase_calculate_timer(struct thread *t) struct ospf6_area *area; uint16_t type; - ospf6 = THREAD_ARG(t); + ospf6 = EVENT_ARG(t); /* Calculate external route for each AS-external-LSA */ type = htons(OSPF6_LSTYPE_AS_EXTERNAL); @@ -1263,6 +1262,7 @@ static void ospf6_ase_calculate_timer(struct thread *t) * no longer valid. */ ospf6_zebra_gr_disable(ospf6); + ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); ospf6->gr_info.finishing_restart = false; } } @@ -1272,6 +1272,23 @@ void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6) if (ospf6 == NULL) return; - thread_add_timer(master, ospf6_ase_calculate_timer, ospf6, - OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc); + event_add_timer(master, ospf6_ase_calculate_timer, ospf6, + OSPF6_ASE_CALC_INTERVAL, &ospf6->t_ase_calc); +} + +bool ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v, + struct ospf6_route *route, + struct ospf6_route_table *result_table) +{ + struct ospf6_route *parent_route; + + if (v->parent && v->parent->hops) { + parent_route = + ospf6_route_lookup(&v->parent->vertex_id, result_table); + if (parent_route) { + ospf6_route_merge_nexthops(route, parent_route); + return true; + } + } + return false; } diff --git a/ospf6d/ospf6_spf.h b/ospf6d/ospf6_spf.h index 55ca3ec4fe9e..c2fd5d9ef100 100644 --- a/ospf6d/ospf6_spf.h +++ b/ospf6d/ospf6_spf.h @@ -152,4 +152,8 @@ extern void ospf6_remove_temp_router_lsa(struct ospf6_area *area); extern void ospf6_ase_calculate_timer_add(struct ospf6 *ospf6); extern int ospf6_ase_calculate_route(struct ospf6 *ospf6, struct ospf6_lsa *lsa, struct ospf6_area *area); +extern bool +ospf6_merge_parents_nh_to_child(struct ospf6_vertex *v, + struct ospf6_route *route, + struct ospf6_route_table *result_table); #endif /* OSPF6_SPF_H */ diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index bd5aedb45efd..4c40298799e9 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -11,7 +11,7 @@ #include "linklist.h" #include "prefix.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "defaults.h" #include "lib/json.h" @@ -140,20 +140,20 @@ static void ospf6_set_redist_vrf_bitmaps(struct ospf6 *ospf6, bool set) "%s: setting redist vrf %d bitmap for type %d", __func__, ospf6->vrf_id, type); if (set) - vrf_bitmap_set(zclient->redist[AFI_IP6][type], + vrf_bitmap_set(&zclient->redist[AFI_IP6][type], ospf6->vrf_id); else - vrf_bitmap_unset(zclient->redist[AFI_IP6][type], + vrf_bitmap_unset(&zclient->redist[AFI_IP6][type], ospf6->vrf_id); } red_list = ospf6->redist[DEFAULT_ROUTE]; if (red_list) { if (set) - vrf_bitmap_set(zclient->default_information[AFI_IP6], + vrf_bitmap_set(&zclient->default_information[AFI_IP6], ospf6->vrf_id); else - vrf_bitmap_unset(zclient->default_information[AFI_IP6], + vrf_bitmap_unset(&zclient->default_information[AFI_IP6], ospf6->vrf_id); } } @@ -176,7 +176,7 @@ static int ospf6_vrf_disable(struct vrf *vrf) * from VRF and make it "down". */ ospf6_vrf_unlink(ospf6, vrf); - thread_cancel(&ospf6->t_ospf6_receive); + event_cancel(&ospf6->t_ospf6_receive); close(ospf6->fd); ospf6->fd = -1; } @@ -207,8 +207,8 @@ static int ospf6_vrf_enable(struct vrf *vrf) ret = ospf6_serv_sock(ospf6); if (ret < 0 || ospf6->fd <= 0) return 0; - thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, - &ospf6->t_ospf6_receive); + event_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); ospf6_router_id_update(ospf6, true); } @@ -455,6 +455,13 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->router_id == 0) ospf6_router_id_update(ospf6, true); ospf6_add(ospf6); + + /* + * Read from non-volatile memory whether this instance is performing a + * graceful restart or not. + */ + ospf6_gr_nvm_read(ospf6); + if (ospf6->vrf_id != VRF_UNKNOWN) { vrf = vrf_lookup_by_id(ospf6->vrf_id); FOR_ALL_INTERFACES (vrf, ifp) { @@ -465,14 +472,8 @@ struct ospf6 *ospf6_instance_create(const char *name) if (ospf6->fd < 0) return ospf6; - /* - * Read from non-volatile memory whether this instance is performing a - * graceful restart or not. - */ - ospf6_gr_nvm_read(ospf6); - - thread_add_read(master, ospf6_receive, ospf6, ospf6->fd, - &ospf6->t_ospf6_receive); + event_add_read(master, ospf6_receive, ospf6, ospf6->fd, + &ospf6->t_ospf6_receive); return ospf6; } @@ -490,6 +491,7 @@ void ospf6_delete(struct ospf6 *o) ospf6_gr_helper_deinit(o); if (!o->gr_info.prepare_in_progress) ospf6_flush_self_originated_lsas_now(o); + XFREE(MTYPE_TMP, o->gr_info.exit_reason); ospf6_disable(o); ospf6_del(o); @@ -552,19 +554,19 @@ static void ospf6_disable(struct ospf6 *o) ospf6_route_remove_all(o->route_table); ospf6_route_remove_all(o->brouter_table); - THREAD_OFF(o->maxage_remover); - THREAD_OFF(o->t_spf_calc); - THREAD_OFF(o->t_ase_calc); - THREAD_OFF(o->t_distribute_update); - THREAD_OFF(o->t_ospf6_receive); - THREAD_OFF(o->t_external_aggr); - THREAD_OFF(o->gr_info.t_grace_period); - THREAD_OFF(o->t_write); - THREAD_OFF(o->t_abr_task); + EVENT_OFF(o->maxage_remover); + EVENT_OFF(o->t_spf_calc); + EVENT_OFF(o->t_ase_calc); + EVENT_OFF(o->t_distribute_update); + EVENT_OFF(o->t_ospf6_receive); + EVENT_OFF(o->t_external_aggr); + EVENT_OFF(o->gr_info.t_grace_period); + EVENT_OFF(o->t_write); + EVENT_OFF(o->t_abr_task); } } -void ospf6_master_init(struct thread_master *master) +void ospf6_master_init(struct event_loop *master) { memset(&ospf6_master, 0, sizeof(ospf6_master)); @@ -573,9 +575,9 @@ void ospf6_master_init(struct thread_master *master) om6->master = master; } -static void ospf6_maxage_remover(struct thread *thread) +static void ospf6_maxage_remover(struct event *thread) { - struct ospf6 *o = (struct ospf6 *)THREAD_ARG(thread); + struct ospf6 *o = (struct ospf6 *)EVENT_ARG(thread); struct ospf6_area *oa; struct ospf6_interface *oi; struct ospf6_neighbor *on; @@ -619,9 +621,9 @@ static void ospf6_maxage_remover(struct thread *thread) void ospf6_maxage_remove(struct ospf6 *o) { if (o) - thread_add_timer(master, ospf6_maxage_remover, o, - OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT, - &o->maxage_remover); + event_add_timer(master, ospf6_maxage_remover, o, + OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT, + &o->maxage_remover); } bool ospf6_router_id_update(struct ospf6 *ospf6, bool init) @@ -693,6 +695,9 @@ DEFUN(no_router_ospf6, no_router_ospf6_cmd, "no router ospf6 [vrf NAME]", if (ospf6 == NULL) vty_out(vty, "OSPFv3 is not configured\n"); else { + if (ospf6->gr_info.restart_support) + ospf6_gr_nvm_delete(ospf6); + ospf6_delete(ospf6); ospf6 = NULL; } @@ -1359,7 +1364,7 @@ static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json, } else json_object_boolean_false_add(json, "spfHasRun"); - if (thread_is_scheduled(o->t_spf_calc)) { + if (event_is_scheduled(o->t_spf_calc)) { long time_store; json_object_boolean_true_add(json, "spfTimerActive"); @@ -1452,8 +1457,7 @@ static void ospf6_show(struct vty *vty, struct ospf6 *o, json_object *json, threadtimer_string(now, o->t_spf_calc, buf, sizeof(buf)); vty_out(vty, " SPF timer %s%s\n", - (thread_is_scheduled(o->t_spf_calc) ? "due in " - : "is "), + (event_is_scheduled(o->t_spf_calc) ? "due in " : "is "), buf); if (CHECK_FLAG(o->flag, OSPF6_STUB_ROUTER)) diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 08595a69d0c2..a38dad8fcedd 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -13,7 +13,7 @@ struct ospf6_master { /* OSPFv3 instance. */ struct list *ospf6; /* OSPFv3 thread master. */ - struct thread_master *master; + struct event_loop *master; }; /* ospf6->config_flags */ @@ -51,7 +51,9 @@ struct ospf6_gr_info { bool prepare_in_progress; bool finishing_restart; uint32_t grace_period; - struct thread *t_grace_period; + int reason; + char *exit_reason; + struct event *t_grace_period; }; struct ospf6_gr_helper { @@ -168,14 +170,14 @@ struct ospf6 { int fd; /* Threads */ - struct thread *t_spf_calc; /* SPF calculation timer. */ - 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 */ - struct thread *t_external_aggr; /* OSPF6 aggregation timer */ + struct event *t_spf_calc; /* SPF calculation timer. */ + struct event *t_ase_calc; /* ASE calculation timer. */ + struct event *maxage_remover; + struct event *t_distribute_update; /* Distirbute update timer. */ + struct event *t_ospf6_receive; /* OSPF6 receive timer */ + struct event *t_external_aggr; /* OSPF6 aggregation timer */ #define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20 - struct thread *t_write; + struct event *t_write; int write_oi_count; /* Num of packets sent per thread invocation */ uint32_t ref_bandwidth; @@ -205,7 +207,7 @@ struct ospf6 { /* Count of NSSA areas */ uint8_t anyNSSA; - struct thread *t_abr_task; /* ABR task timer. */ + struct event *t_abr_task; /* ABR task timer. */ struct list *oi_write_q; uint32_t redist_count; @@ -233,7 +235,7 @@ extern struct ospf6 *ospf6; extern struct ospf6_master *om6; /* prototypes */ -extern void ospf6_master_init(struct thread_master *master); +extern void ospf6_master_init(struct event_loop *master); extern void install_element_ospf6_clear_process(void); extern void ospf6_top_init(void); extern void ospf6_delete(struct ospf6 *o); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 6fe0a2492673..8bd0d8f0b58b 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -97,9 +97,9 @@ static int ospf6_router_id_update_zebra(ZAPI_CALLBACK_ARGS) /* redistribute function */ void ospf6_zebra_redistribute(int type, vrf_id_t vrf_id) { - if (vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)) + if (vrf_bitmap_check(&zclient->redist[AFI_IP6][type], vrf_id)) return; - vrf_bitmap_set(zclient->redist[AFI_IP6][type], vrf_id); + vrf_bitmap_set(&zclient->redist[AFI_IP6][type], vrf_id); if (zclient->sock > 0) zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, @@ -108,9 +108,9 @@ void ospf6_zebra_redistribute(int type, vrf_id_t vrf_id) void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id) { - if (!vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)) + if (!vrf_bitmap_check(&zclient->redist[AFI_IP6][type], vrf_id)) return; - vrf_bitmap_unset(zclient->redist[AFI_IP6][type], vrf_id); + 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_id); @@ -239,12 +239,18 @@ static int ospf6_zebra_gr_update(struct ospf6 *ospf6, int command, int ospf6_zebra_gr_enable(struct ospf6 *ospf6, uint32_t stale_time) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("Zebra enable GR [stale time %u]", stale_time); + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_CAPABILITIES, stale_time); } int ospf6_zebra_gr_disable(struct ospf6 *ospf6) { + if (IS_DEBUG_OSPF6_GR) + zlog_debug("Zebra disable GR"); + return ospf6_zebra_gr_update(ospf6, ZEBRA_CLIENT_GR_DISABLE, 0); } @@ -252,7 +258,7 @@ static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; unsigned long ifindex; - struct in6_addr *nexthop; + const struct in6_addr *nexthop = &in6addr_any; struct ospf6 *ospf6; struct prefix_ipv6 p; @@ -272,7 +278,9 @@ static int ospf6_zebra_read_route(ZAPI_CALLBACK_ARGS) return 0; ifindex = api.nexthops[0].ifindex; - nexthop = &api.nexthops[0].gate.ipv6; + if (api.nexthops[0].type == NEXTHOP_TYPE_IPV6 + || api.nexthops[0].type == NEXTHOP_TYPE_IPV6_IFINDEX) + nexthop = &api.nexthops[0].gate.ipv6; if (IS_OSPF6_DEBUG_ZEBRA(RECV)) zlog_debug( @@ -325,10 +333,10 @@ DEFUN(show_zebra, json_object_int_add(json_zebra, "fail", zclient->fail); json_object_int_add( json_zebra, "redistributeDefault", - vrf_bitmap_check(zclient->default_information[AFI_IP6], + vrf_bitmap_check(&zclient->default_information[AFI_IP6], VRF_DEFAULT)); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (vrf_bitmap_check(zclient->redist[AFI_IP6][i], + if (vrf_bitmap_check(&zclient->redist[AFI_IP6][i], VRF_DEFAULT)) json_object_array_add( json_array, @@ -343,11 +351,11 @@ DEFUN(show_zebra, 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], + vrf_bitmap_check(&zclient->default_information[AFI_IP6], VRF_DEFAULT)); vty_out(vty, " redistribute:"); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (vrf_bitmap_check(zclient->redist[AFI_IP6][i], + if (vrf_bitmap_check(&zclient->redist[AFI_IP6][i], VRF_DEFAULT)) vty_out(vty, " %s", zebra_route_string(i)); } @@ -733,10 +741,20 @@ uint8_t ospf6_distance_apply(struct prefix_ipv6 *p, struct ospf6_route * or, static void ospf6_zebra_connected(struct zclient *zclient) { + struct ospf6 *ospf6; + struct listnode *node; + /* Send the client registration */ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) { + if (!ospf6->gr_info.restart_support) + continue; + (void)ospf6_zebra_gr_enable(ospf6, ospf6->gr_info.grace_period); + } } static zclient_handler *const ospf6_handlers[] = { @@ -748,7 +766,7 @@ static zclient_handler *const ospf6_handlers[] = { [ZEBRA_NEXTHOP_UPDATE] = ospf6_zebra_import_check_update, }; -void ospf6_zebra_init(struct thread_master *master) +void ospf6_zebra_init(struct event_loop *master) { /* Allocate zebra structure. */ zclient = zclient_new(master, &zclient_options_default, ospf6_handlers, diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index eb6d919b3727..7669b5e2c0cd 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -38,8 +38,8 @@ extern void ospf6_zebra_route_update_remove(struct ospf6_route *request, 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 *tm); + vrf_bitmap_check(&zclient->redist[AFI_IP6][type], vrf_id) +extern void ospf6_zebra_init(struct event_loop *tm); extern void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg); extern void ospf6_zebra_add_discard(struct ospf6_route *request, struct ospf6 *ospf6); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 257a0f2b0eaf..d90a950d79f5 100644 --- a/ospf6d/ospf6d.c +++ b/ospf6d/ospf6d.c @@ -5,7 +5,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "command.h" @@ -34,9 +34,16 @@ #include "lib/json.h" #include "ospf6_nssa.h" #include "ospf6_auth_trailer.h" +#include "ospf6d/ospf6d_clippy.c" DEFINE_MGROUP(OSPF6D, "ospf6d"); +/* OSPF6 config processing timer thread */ +struct event *t_ospf6_cfg; + +/* OSPF6 debug event state */ +unsigned char conf_debug_ospf6_event; + struct route_node *route_prev(struct route_node *node) { struct route_node *end; @@ -62,6 +69,7 @@ struct route_node *route_prev(struct route_node *node) } static int config_write_ospf6_debug(struct vty *vty); +static int config_write_ospf6_debug_event(struct vty *vty); static struct cmd_node debug_node = { .name = "debug", .node = DEBUG_NODE, @@ -85,6 +93,7 @@ static int config_write_ospf6_debug(struct vty *vty) config_write_ospf6_debug_nssa(vty); config_write_ospf6_debug_gr_helper(vty); config_write_ospf6_debug_auth(vty); + config_write_ospf6_debug_event(vty); return 0; } @@ -1374,8 +1383,31 @@ DEFUN(show_ipv6_ospf6_linkstate_detail, show_ipv6_ospf6_linkstate_detail_cmd, return CMD_SUCCESS; } +DEFPY(debug_ospf6_event, debug_ospf6_event_cmd, "[no] debug ospf6 event", + NO_STR DEBUG_STR OSPF6_STR "Debug OSPFv3 event function\n") +{ + if (!no) + OSPF6_DEBUG_EVENT_ON(); + else + OSPF6_DEBUG_EVENT_OFF(); + return CMD_SUCCESS; +} + +static int config_write_ospf6_debug_event(struct vty *vty) +{ + if (IS_OSPF6_DEBUG_EVENT) + vty_out(vty, "debug ospf6 event\n"); + return 0; +} + +static void install_element_ospf6_debug_event(void) +{ + install_element(ENABLE_NODE, &debug_ospf6_event_cmd); + install_element(CONFIG_NODE, &debug_ospf6_event_cmd); +} + /* Install ospf related commands. */ -void ospf6_init(struct thread_master *master) +void ospf6_init(struct event_loop *master) { ospf6_top_init(); ospf6_area_init(); @@ -1447,6 +1479,7 @@ void ospf6_init(struct thread_master *master) VIEW_NODE, &show_ipv6_ospf6_database_type_self_originated_linkstate_id_cmd); install_element(VIEW_NODE, &show_ipv6_ospf6_database_aggr_router_cmd); + install_element_ospf6_debug_event(); install_element_ospf6_debug_auth(); ospf6_interface_auth_trailer_cmd_init(); install_element_ospf6_clear_intf_auth(); diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 234a0e881d9f..c927ee7566f1 100644 --- a/ospf6d/ospf6d.h +++ b/ospf6d/ospf6d.h @@ -7,13 +7,16 @@ #define OSPF6D_H #include "libospf.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" DECLARE_MGROUP(OSPF6D); /* global variables */ -extern struct thread_master *master; +extern struct event_loop *master; + +/* OSPF config processing timer thread */ +extern struct event *t_ospf6_cfg; /* Historical for KAME. */ #ifndef IPV6_JOIN_GROUP @@ -105,10 +108,16 @@ extern struct thread_master *master; extern struct zebra_privs_t ospf6d_privs; +/* Event Debug option */ +extern unsigned char conf_debug_ospf6_event; +#define OSPF6_DEBUG_EVENT_ON() (conf_debug_ospf6_event = 1) +#define OSPF6_DEBUG_EVENT_OFF() (conf_debug_ospf6_event = 0) +#define IS_OSPF6_DEBUG_EVENT (conf_debug_ospf6_event) + /* Function Prototypes */ extern struct route_node *route_prev(struct route_node *node); extern void ospf6_debug(void); -extern void ospf6_init(struct thread_master *master); +extern void ospf6_init(struct event_loop *master); #endif /* OSPF6D_H */ diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 3dff03956cea..f6d27c84cd5f 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -75,9 +75,11 @@ ospf6d_ospf6d_snmp_la_LDFLAGS = $(MODULE_LDFLAGS) ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la clippy_scan += \ + ospf6d/ospf6d.c \ ospf6d/ospf6_top.c \ ospf6d/ospf6_area.c \ ospf6d/ospf6_asbr.c \ + ospf6d/ospf6_interface.c \ ospf6d/ospf6_lsa.c \ ospf6d/ospf6_gr_helper.c \ ospf6d/ospf6_gr.c \ diff --git a/ospfclient/ospf_apiclient.c b/ospfclient/ospf_apiclient.c index c5bbc0150159..a1193001faea 100644 --- a/ospfclient/ospf_apiclient.c +++ b/ospfclient/ospf_apiclient.c @@ -8,7 +8,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "linklist.h" #include "if.h" diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c index cbc53ad3533f..24ff08561d7b 100644 --- a/ospfclient/ospfclient.c +++ b/ospfclient/ospfclient.c @@ -43,7 +43,7 @@ struct zebra_privs_t ospfd_privs = {.user = NULL, free to use any thread library (like pthreads). */ #include "ospfd/ospf_dump.h" /* for ospf_lsa_header_dump */ -#include "thread.h" +#include "frrevent.h" #include "log.h" /* Local portnumber for async channel. Note that OSPF API library will also @@ -51,7 +51,7 @@ struct zebra_privs_t ospfd_privs = {.user = NULL, #define ASYNCPORT 4000 /* Master thread */ -struct thread_master *master; +struct event_loop *master; /* Global variables */ struct ospf_apiclient *oclient; @@ -69,13 +69,13 @@ struct my_opaque_lsa { * --------------------------------------------------------- */ -static void lsa_delete(struct thread *t) +static void lsa_delete(struct event *t) { struct ospf_apiclient *oclient; struct in_addr area_id; int rc; - oclient = THREAD_ARG(t); + oclient = EVENT_ARG(t); rc = inet_aton(args[6], &area_id); if (rc <= 0) { @@ -92,7 +92,7 @@ static void lsa_delete(struct thread *t) printf("done, return code is = %d\n", rc); } -static void lsa_inject(struct thread *t) +static void lsa_inject(struct event *t) { struct ospf_apiclient *cl; struct in_addr ifaddr; @@ -106,7 +106,7 @@ static void lsa_inject(struct thread *t) static uint32_t counter = 1; /* Incremented each time invoked */ int rc; - cl = THREAD_ARG(t); + cl = EVENT_ARG(t); rc = inet_aton(args[5], &ifaddr); if (rc <= 0) { @@ -138,7 +138,7 @@ static void lsa_inject(struct thread *t) /* This thread handles asynchronous messages coming in from the OSPF API server */ -static void lsa_read(struct thread *thread) +static void lsa_read(struct event *thread) { struct ospf_apiclient *oclient; int fd; @@ -146,8 +146,8 @@ static void lsa_read(struct thread *thread) printf("lsa_read called\n"); - oclient = THREAD_ARG(thread); - fd = THREAD_FD(thread); + oclient = EVENT_ARG(thread); + fd = EVENT_FD(thread); /* Handle asynchronous message */ ret = ospf_apiclient_handle_async(oclient); @@ -157,7 +157,7 @@ static void lsa_read(struct thread *thread) } /* Reschedule read thread */ - thread_add_read(master, lsa_read, oclient, fd, NULL); + event_add_read(master, lsa_read, oclient, fd, NULL); } /* --------------------------------------------------------- @@ -209,13 +209,13 @@ static void ready_callback(uint8_t lsa_type, uint8_t opaque_type, lsa_type, opaque_type, &addr); /* Schedule opaque LSA originate in 5 secs */ - thread_add_timer(master, lsa_inject, oclient, 5, NULL); + event_add_timer(master, lsa_inject, oclient, 5, NULL); /* Schedule opaque LSA update with new value */ - thread_add_timer(master, lsa_inject, oclient, 10, NULL); + event_add_timer(master, lsa_inject, oclient, 10, NULL); /* Schedule delete */ - thread_add_timer(master, lsa_delete, oclient, 30, NULL); + event_add_timer(master, lsa_delete, oclient, 30, NULL); } static void new_if_callback(struct in_addr ifaddr, struct in_addr area_id) @@ -269,7 +269,7 @@ static int usage(void) int main(int argc, char *argv[]) { - struct thread thread; + struct event thread; args = argv; @@ -293,7 +293,7 @@ int main(int argc, char *argv[]) /* Initialization */ zprivs_preinit(&ospfd_privs); zprivs_init(&ospfd_privs); - master = thread_master_create(NULL); + master = event_master_create(NULL); /* Open connection to OSPF daemon */ oclient = ospf_apiclient_connect(args[1], ASYNCPORT); @@ -316,12 +316,12 @@ int main(int argc, char *argv[]) ospf_apiclient_sync_lsdb(oclient); /* Schedule thread that handles asynchronous messages */ - thread_add_read(master, lsa_read, oclient, oclient->fd_async, NULL); + event_add_read(master, lsa_read, oclient, oclient->fd_async, NULL); /* Now connection is established, run loop */ while (1) { - thread_fetch(master, &thread); - thread_call(&thread); + event_fetch(master, &thread); + event_call(&thread); } /* Never reached */ diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index 8f177cbce168..28d526870b80 100644 --- a/ospfd/ospf_abr.c +++ b/ospfd/ospf_abr.c @@ -7,7 +7,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" @@ -54,6 +54,7 @@ static void ospf_area_range_free(struct ospf_area_range *range) } static void ospf_area_range_add(struct ospf_area *area, + struct route_table *ranges, struct ospf_area_range *range) { struct route_node *rn; @@ -64,10 +65,12 @@ static void ospf_area_range_add(struct ospf_area *area, p.prefix = range->addr; apply_mask_ipv4(&p); - rn = route_node_get(area->ranges, (struct prefix *)&p); - if (rn->info) + rn = route_node_get(ranges, (struct prefix *)&p); + if (rn->info) { route_unlock_node(rn); - else + ospf_area_range_free(rn->info); + rn->info = range; + } else rn->info = range; } @@ -75,10 +78,12 @@ static void ospf_area_range_delete(struct ospf_area *area, struct route_node *rn) { struct ospf_area_range *range = rn->info; + bool nssa = CHECK_FLAG(range->flags, OSPF_AREA_RANGE_NSSA); - if (range->specifics != 0) + if (ospf_area_range_active(range) && + CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE)) ospf_delete_discard_route(area->ospf, area->ospf->new_table, - (struct prefix_ipv4 *)&rn->p); + (struct prefix_ipv4 *)&rn->p, nssa); ospf_area_range_free(range); rn->info = NULL; @@ -87,11 +92,12 @@ static void ospf_area_range_delete(struct ospf_area *area, } struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) { struct route_node *rn; - rn = route_node_lookup(area->ranges, (struct prefix *)p); + rn = route_node_lookup(ranges, (struct prefix *)p); if (rn) { route_unlock_node(rn); return rn->info; @@ -133,11 +139,12 @@ struct ospf_area_range *ospf_area_range_lookup_next(struct ospf_area *area, } static struct ospf_area_range *ospf_area_range_match(struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) { struct route_node *node; - node = route_node_match(area->ranges, (struct prefix *)p); + node = route_node_match(ranges, (struct prefix *)p); if (node) { route_unlock_node(node); return node->info; @@ -153,7 +160,7 @@ struct ospf_area_range *ospf_area_range_match_any(struct ospf *ospf, struct listnode *node; for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) - if ((range = ospf_area_range_match(area, p))) + if ((range = ospf_area_range_match(area, area->ranges, p))) return range; return NULL; @@ -169,17 +176,13 @@ static int ospf_area_actively_attached(struct ospf_area *area) return area->act_ints; } -int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id, - struct prefix_ipv4 *p, int advertise) +int ospf_area_range_set(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p, + int advertise, bool nssa) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_get(ospf, area_id); - if (area == NULL) - return 0; - - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, ranges, p); if (range != NULL) { if (!CHECK_FLAG(advertise, OSPF_AREA_RANGE_ADVERTISE)) range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; @@ -190,7 +193,7 @@ int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id, ospf_schedule_abr_task(ospf); } else { range = ospf_area_range_new(p); - ospf_area_range_add(area, range); + ospf_area_range_add(area, ranges, range); ospf_schedule_abr_task(ospf); } @@ -201,20 +204,19 @@ int ospf_area_range_set(struct ospf *ospf, struct in_addr area_id, range->cost_config = OSPF_AREA_RANGE_COST_UNSPEC; } + if (nssa) + SET_FLAG(range->flags, OSPF_AREA_RANGE_NSSA); + return 1; } -int ospf_area_range_cost_set(struct ospf *ospf, struct in_addr area_id, - struct prefix_ipv4 *p, uint32_t cost) +int ospf_area_range_cost_set(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p, + uint32_t cost) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_get(ospf, area_id); - if (area == NULL) - return 0; - - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, ranges, p); if (range == NULL) return 0; @@ -227,17 +229,12 @@ int ospf_area_range_cost_set(struct ospf *ospf, struct in_addr area_id, return 1; } -int ospf_area_range_unset(struct ospf *ospf, struct in_addr area_id, - struct prefix_ipv4 *p) +int ospf_area_range_unset(struct ospf *ospf, struct ospf_area *area, + struct route_table *ranges, struct prefix_ipv4 *p) { - struct ospf_area *area; struct route_node *rn; - area = ospf_area_lookup_by_area_id(ospf, area_id); - if (area == NULL) - return 0; - - rn = route_node_lookup(area->ranges, (struct prefix *)p); + rn = route_node_lookup(ranges, (struct prefix *)p); if (rn == NULL) return 0; @@ -249,14 +246,12 @@ int ospf_area_range_unset(struct ospf *ospf, struct in_addr area_id, return 1; } -int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id, +int ospf_area_range_substitute_set(struct ospf *ospf, struct ospf_area *area, struct prefix_ipv4 *p, struct prefix_ipv4 *s) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_get(ospf, area_id); - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, area->ranges, p); if (range != NULL) { if (!CHECK_FLAG(range->flags, OSPF_AREA_RANGE_ADVERTISE) @@ -264,7 +259,7 @@ int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id, ospf_schedule_abr_task(ospf); } else { range = ospf_area_range_new(p); - ospf_area_range_add(area, range); + ospf_area_range_add(area, area->ranges, range); ospf_schedule_abr_task(ospf); } @@ -276,17 +271,12 @@ int ospf_area_range_substitute_set(struct ospf *ospf, struct in_addr area_id, return 1; } -int ospf_area_range_substitute_unset(struct ospf *ospf, struct in_addr area_id, +int ospf_area_range_substitute_unset(struct ospf *ospf, struct ospf_area *area, struct prefix_ipv4 *p) { - struct ospf_area *area; struct ospf_area_range *range; - area = ospf_area_lookup_by_area_id(ospf, area_id); - if (area == NULL) - return 0; - - range = ospf_area_range_lookup(area, p); + range = ospf_area_range_lookup(area, area->ranges, p); if (range == NULL) return 0; @@ -538,8 +528,7 @@ void ospf_check_abr_status(struct ospf *ospf) } static void ospf_abr_update_aggregate(struct ospf_area_range *range, - struct ospf_route * or, - struct ospf_area *area) + uint32_t cost, struct ospf_area *area) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Start", __func__); @@ -557,20 +546,18 @@ static void ospf_abr_update_aggregate(struct ospf_area_range *range, range->cost = range->cost_config; } else { - if (range->specifics == 0) { + if (!ospf_area_range_active(range)) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: use or->cost %d", __func__, - or->cost); + zlog_debug("%s: use cost %d", __func__, cost); - range->cost = or->cost; /* 1st time get 1st cost */ + range->cost = cost; /* 1st time get 1st cost */ } - if (or->cost > range->cost) { + if (cost > range->cost) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug("%s: update to %d", __func__, - or->cost); + zlog_debug("%s: update to %d", __func__, cost); - range->cost = or->cost; + range->cost = cost; } } @@ -605,6 +592,7 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) struct ospf_lsa *old = NULL, *new = NULL; struct as_external_lsa *ext7; struct prefix_ipv4 p; + struct ospf_area_range *range; if (!CHECK_FLAG(lsa->data->options, OSPF_OPTION_NP)) { if (IS_DEBUG_OSPF_NSSA) @@ -646,6 +634,18 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) return 1; } + range = ospf_area_range_match(area, area->nssa_ranges, &p); + if (range) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug("Suppressed by range %pI4/%u of area %pI4", + &range->addr, range->masklen, + &area->area_id); + + ospf_abr_update_aggregate(range, GET_METRIC(ext7->e[0].metric), + area); + return 1; + } + if (old && CHECK_FLAG(old->flags, OSPF_LSA_APPROVED)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( @@ -675,17 +675,27 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa) } } - /* Area where Aggregate testing will be inserted, just like summary - advertisements */ - /* ospf_abr_check_nssa_range (p_arg, lsa-> cost, lsa -> area); */ - return 0; } -static void ospf_abr_translate_nssa_range(struct prefix_ipv4 *p, uint32_t cost) +static void ospf_abr_translate_nssa_range(struct ospf *ospf, + struct prefix_ipv4 *p, uint32_t cost) { - /* The Type-7 is created from the aggregated prefix and forwarded - for lsa installation and flooding... to be added... */ + struct external_info ei = {}; + struct ospf_lsa *lsa; + + prefix_copy(&ei.p, p); + ei.type = ZEBRA_ROUTE_OSPF; + ei.route_map_set.metric = cost; + ei.route_map_set.metric_type = -1; + + lsa = ospf_external_info_find_lsa(ospf, p); + if (lsa) + lsa = ospf_external_lsa_refresh(ospf, lsa, &ei, + LSA_REFRESH_FORCE, true); + else + lsa = ospf_external_lsa_originate(ospf, &ei); + SET_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT); } void ospf_abr_announce_network_to_area(struct prefix_ipv4 *p, uint32_t cost, @@ -892,9 +902,11 @@ static void ospf_abr_announce_network(struct ospf *ospf, struct prefix_ipv4 *p, zlog_debug( "%s: this is intra-area route to %pFX", __func__, p); - if ((range = ospf_area_range_match(or_area, p)) - && !ospf_area_is_transit(area)) - ospf_abr_update_aggregate(range, or, area); + if ((range = ospf_area_range_match( + or_area, or_area->ranges, p)) && + !ospf_area_is_transit(area)) + ospf_abr_update_aggregate(range, or->cost, + area); else ospf_abr_announce_network_to_area(p, or->cost, area); @@ -1345,7 +1357,7 @@ static void ospf_abr_unapprove_summaries(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } -static void ospf_abr_prepare_aggregates(struct ospf *ospf) +static void ospf_abr_prepare_aggregates(struct ospf *ospf, bool nssa) { struct listnode *node; struct route_node *rn; @@ -1356,7 +1368,14 @@ static void ospf_abr_prepare_aggregates(struct ospf *ospf) zlog_debug("%s: Start", __func__); for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { - for (rn = route_top(area->ranges); rn; rn = route_next(rn)) + struct route_table *ranges; + + if (nssa) + ranges = area->nssa_ranges; + else + ranges = area->ranges; + + for (rn = route_top(ranges); rn; rn = route_next(rn)) if ((range = rn->info) != NULL) { range->cost = 0; range->specifics = 0; @@ -1409,7 +1428,7 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) p.prefixlen = range->subst_masklen; } - if (range->specifics) { + if (ospf_area_range_active(range)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: active range", __func__); @@ -1452,13 +1471,11 @@ static void ospf_abr_announce_aggregates(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } -static void -ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ +static void ospf_abr_send_nssa_aggregates(struct ospf *ospf) { - struct listnode *node; /*, n; */ - struct ospf_area *area; /*, *ar; */ + struct listnode *node; + struct ospf_area *area; struct route_node *rn; - struct ospf_area_range *range; struct prefix_ipv4 p; if (IS_DEBUG_OSPF_NSSA) @@ -1472,20 +1489,13 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ zlog_debug("%s: looking at area %pI4", __func__, &area->area_id); - for (rn = route_top(area->ranges); rn; rn = route_next(rn)) { - if (rn->info == NULL) - continue; + for (rn = route_top(area->nssa_ranges); rn; + rn = route_next(rn)) { + struct ospf_area_range *range; range = rn->info; - - if (!CHECK_FLAG(range->flags, - OSPF_AREA_RANGE_ADVERTISE)) { - if (IS_DEBUG_OSPF_NSSA) - zlog_debug( - "%s: discarding suppress-ranges", - __func__); + if (!range) continue; - } p.family = AF_INET; p.prefix = range->addr; @@ -1495,14 +1505,9 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ zlog_debug("%s: this is range: %pFX", __func__, &p); - if (CHECK_FLAG(range->flags, - OSPF_AREA_RANGE_SUBSTITUTE)) { - p.family = AF_INET; - p.prefix = range->subst_addr; - p.prefixlen = range->subst_masklen; - } - - if (range->specifics) { + if (ospf_area_range_active(range) + && CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) { if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: active range", __func__); @@ -1512,7 +1517,8 @@ ospf_abr_send_nssa_aggregates(struct ospf *ospf) /* temporarily turned off */ * translate, Install (as Type-5), Approve, and * Flood */ - ospf_abr_translate_nssa_range(&p, range->cost); + ospf_abr_translate_nssa_range(ospf, &p, + range->cost); } } /* all area ranges*/ } /* all areas */ @@ -1727,13 +1733,13 @@ bool ospf_check_fr_enabled_all(struct ospf *ospf) * @param thread * @return 0. */ -static void ospf_abr_announce_non_dna_routers(struct thread *thread) +static void ospf_abr_announce_non_dna_routers(struct event *thread) { struct ospf_area *area; struct listnode *node; - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); - THREAD_OFF(ospf->t_abr_fr); + EVENT_OFF(ospf->t_abr_fr); if (!IS_OSPF_ABR(ospf)) return; @@ -1807,6 +1813,82 @@ static void ospf_abr_announce_non_dna_routers(struct thread *thread) OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__); } +static void ospf_abr_nssa_type7_default_create(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + struct external_info ei; + + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Announcing Type-7 default route into NSSA area %pI4", + &area->area_id); + + /* Prepare the extrenal_info for aggregator */ + memset(&ei, 0, sizeof(struct external_info)); + ei.p.family = AF_INET; + ei.p.prefixlen = 0; + ei.tag = 0; + ei.type = 0; + ei.instance = ospf->instance; + + /* Compute default route type and metric. */ + if (area->nssa_default_originate.metric_value != -1) + ei.route_map_set.metric = + area->nssa_default_originate.metric_value; + else + ei.route_map_set.metric = DEFAULT_DEFAULT_ALWAYS_METRIC; + if (area->nssa_default_originate.metric_type != -1) + ei.route_map_set.metric_type = + area->nssa_default_originate.metric_type; + else + ei.route_map_set.metric_type = DEFAULT_METRIC_TYPE; + + if (!lsa) + ospf_nssa_lsa_originate(area, &ei); + else + ospf_nssa_lsa_refresh(area, lsa, &ei); +} + +static void ospf_abr_nssa_type7_default_delete(struct ospf *ospf, + struct ospf_area *area, + struct ospf_lsa *lsa) +{ + if (lsa && !CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) { + if (IS_DEBUG_OSPF_NSSA) + zlog_debug( + "Withdrawing Type-7 default route from area %pI4", + &area->area_id); + + ospf_ls_retransmit_delete_nbr_area(area, lsa); + ospf_refresher_unregister_lsa(ospf, lsa); + ospf_lsa_flush_area(lsa, area); + } +} + +/* NSSA Type-7 default route. */ +void ospf_abr_nssa_type7_defaults(struct ospf *ospf) +{ + struct ospf_area *area; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { + struct in_addr id = {}; + struct ospf_lsa *lsa; + + lsa = ospf_lsdb_lookup_by_id(area->lsdb, OSPF_AS_NSSA_LSA, id, + area->ospf->router_id); + if (area->external_routing == OSPF_AREA_NSSA + && area->nssa_default_originate.enabled + && (IS_OSPF_ABR(ospf) + || (IS_OSPF_ASBR(ospf) + && ospf->nssa_default_import_check.status))) + ospf_abr_nssa_type7_default_create(ospf, area, lsa); + else + ospf_abr_nssa_type7_default_delete(ospf, area, lsa); + } +} + static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf, struct ospf_lsa *lsa) { @@ -1874,30 +1956,39 @@ static void ospf_abr_remove_unapproved_summaries(struct ospf *ospf) zlog_debug("%s: Stop", __func__); } -static void ospf_abr_manage_discard_routes(struct ospf *ospf) +static void ospf_abr_manage_discard_routes(struct ospf *ospf, bool nssa) { struct listnode *node, *nnode; struct route_node *rn; struct ospf_area *area; - struct ospf_area_range *range; - for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) - for (rn = route_top(area->ranges); rn; rn = route_next(rn)) - if ((range = rn->info) != NULL) - if (CHECK_FLAG(range->flags, - OSPF_AREA_RANGE_ADVERTISE)) { - if (range->specifics) - ospf_add_discard_route( - ospf, ospf->new_table, - area, - (struct prefix_ipv4 - *)&rn->p); - else - ospf_delete_discard_route( - ospf, ospf->new_table, - (struct prefix_ipv4 - *)&rn->p); - } + for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) { + struct route_table *ranges; + + if (nssa) + ranges = area->nssa_ranges; + else + ranges = area->ranges; + + for (rn = route_top(ranges); rn; rn = route_next(rn)) { + struct ospf_area_range *range; + + range = rn->info; + if (!range) + continue; + + if (ospf_area_range_active(range) + && CHECK_FLAG(range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + ospf_add_discard_route( + ospf, ospf->new_table, area, + (struct prefix_ipv4 *)&rn->p, nssa); + else + ospf_delete_discard_route( + ospf, ospf->new_table, + (struct prefix_ipv4 *)&rn->p, nssa); + } + } } /* This is the function taking care about ABR NSSA, i.e. NSSA @@ -1925,7 +2016,7 @@ static void ospf_abr_manage_discard_routes(struct ospf *ospf) For External Calculations, any NSSA areas use the Type-7 AREA-LSDB, any ABR-non-NSSA areas use the Type-5 GLOBAL-LSDB. */ -static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ +void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ { if (ospf->gr_info.restart_in_progress) return; @@ -1952,7 +2043,7 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ /* RESET all Ranges in every Area, same as summaries */ if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: NSSA initialize aggregates", __func__); - ospf_abr_prepare_aggregates(ospf); /*TURNED OFF just for now */ + ospf_abr_prepare_aggregates(ospf, true); /* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or * Aggregate as Type-7 @@ -1983,7 +2074,7 @@ static void ospf_abr_nssa_task(struct ospf *ospf) /* called only if any_nssa */ zlog_debug("%s: remove unapproved translates", __func__); ospf_abr_remove_unapproved_translates(ospf); - ospf_abr_manage_discard_routes(ospf); /* same as normal...discard */ + ospf_abr_manage_discard_routes(ospf, true); if (IS_DEBUG_OSPF_NSSA) zlog_debug("%s: Stop", __func__); @@ -2012,7 +2103,7 @@ void ospf_abr_task(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: prepare aggregates", __func__); - ospf_abr_prepare_aggregates(ospf); + ospf_abr_prepare_aggregates(ospf, false); if (IS_OSPF_ABR(ospf)) { if (IS_DEBUG_OSPF_EVENT) @@ -2031,6 +2122,11 @@ void ospf_abr_task(struct ospf *ospf) zlog_debug("%s: announce stub defaults", __func__); ospf_abr_announce_stub_defaults(ospf); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: announce NSSA Type-7 defaults", + __func__); + ospf_abr_nssa_type7_defaults(ospf); + if (ospf->fr_configured) { OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): announce non-DNArouters", @@ -2040,9 +2136,9 @@ void ospf_abr_task(struct ospf *ospf) * giving time for route synchronization in * all the routers. */ - thread_add_timer( - master, ospf_abr_announce_non_dna_routers, ospf, - OSPF_ABR_DNA_TIMER, &ospf->t_abr_fr); + event_add_timer(master, + ospf_abr_announce_non_dna_routers, ospf, + OSPF_ABR_DNA_TIMER, &ospf->t_abr_fr); } } @@ -2050,15 +2146,15 @@ void ospf_abr_task(struct ospf *ospf) zlog_debug("%s: remove unapproved summaries", __func__); ospf_abr_remove_unapproved_summaries(ospf); - ospf_abr_manage_discard_routes(ospf); + ospf_abr_manage_discard_routes(ospf, false); if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: Stop", __func__); } -static void ospf_abr_task_timer(struct thread *thread) +static void ospf_abr_task_timer(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); ospf->t_abr_task = 0; @@ -2077,6 +2173,6 @@ void ospf_schedule_abr_task(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("Scheduling ABR task"); - thread_add_timer(master, ospf_abr_task_timer, ospf, OSPF_ABR_TASK_DELAY, - &ospf->t_abr_task); + event_add_timer(master, ospf_abr_task_timer, ospf, OSPF_ABR_TASK_DELAY, + &ospf->t_abr_task); } diff --git a/ospfd/ospf_abr.h b/ospfd/ospf_abr.h index 19d444b125cf..cc2b2b05481f 100644 --- a/ospfd/ospf_abr.h +++ b/ospfd/ospf_abr.h @@ -16,6 +16,7 @@ #define OSPF_AREA_RANGE_ADVERTISE (1 << 0) #define OSPF_AREA_RANGE_SUBSTITUTE (1 << 1) +#define OSPF_AREA_RANGE_NSSA (1 << 2) /* Area range. */ struct ospf_area_range { @@ -44,23 +45,23 @@ struct ospf_area_range { /* Prototypes. */ extern struct ospf_area_range *ospf_area_range_lookup(struct ospf_area *, + struct route_table *, struct prefix_ipv4 *); - -extern struct ospf_area_range *ospf_some_area_range_match(struct prefix_ipv4 *); - extern struct ospf_area_range * ospf_area_range_lookup_next(struct ospf_area *, struct in_addr *, int); -extern int ospf_area_range_set(struct ospf *, struct in_addr, - struct prefix_ipv4 *, int); -extern int ospf_area_range_cost_set(struct ospf *, struct in_addr, - struct prefix_ipv4 *, uint32_t); -extern int ospf_area_range_unset(struct ospf *, struct in_addr, - struct prefix_ipv4 *); -extern int ospf_area_range_substitute_set(struct ospf *, struct in_addr, +extern int ospf_area_range_set(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *, int, + bool); +extern int ospf_area_range_cost_set(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *, + uint32_t); +extern int ospf_area_range_unset(struct ospf *, struct ospf_area *, + struct route_table *, struct prefix_ipv4 *); +extern int ospf_area_range_substitute_set(struct ospf *, struct ospf_area *, struct prefix_ipv4 *, struct prefix_ipv4 *); -extern int ospf_area_range_substitute_unset(struct ospf *, struct in_addr, +extern int ospf_area_range_substitute_unset(struct ospf *, struct ospf_area *, struct prefix_ipv4 *); extern struct ospf_area_range *ospf_area_range_match_any(struct ospf *, struct prefix_ipv4 *); @@ -69,10 +70,12 @@ extern int ospf_act_bb_connection(struct ospf *); extern void ospf_check_abr_status(struct ospf *); extern void ospf_abr_task(struct ospf *); +extern void ospf_abr_nssa_task(struct ospf *ospf); 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_type7_defaults(struct ospf *ospf); extern void ospf_abr_nssa_check_status(struct ospf *ospf); extern void ospf_abr_generate_indication_lsa(struct ospf *ospf, const struct ospf_area *area); diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index 3cb1287be5d0..213ee8c1fd16 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -18,7 +18,7 @@ #include "vty.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "buffer.h" diff --git a/ospfd/ospf_apiserver.c b/ospfd/ospf_apiserver.c index 5e4fc30a2880..34e59c545b7e 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -18,14 +18,14 @@ #include "vty.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "buffer.h" #include <sys/types.h> -#include "ospfd/ospfd.h" /* for "struct thread_master" */ +#include "ospfd/ospfd.h" /* for "struct event_loop" */ #include "ospfd/ospf_interface.h" #include "ospfd/ospf_ism.h" #include "ospfd/ospf_asbr.h" @@ -166,9 +166,14 @@ void ospf_apiserver_term(void) * Free all client instances. ospf_apiserver_free removes the node * from the list, so we examine the head of the list anew each time. */ - while (apiserver_list - && (apiserv = listgetdata(listhead(apiserver_list))) != NULL) + if (!apiserver_list) + return; + + while (listcount(apiserver_list)) { + apiserv = listgetdata(listhead(apiserver_list)); + ospf_apiserver_free(apiserv); + } /* Free client list itself */ if (apiserver_list) @@ -273,28 +278,28 @@ void ospf_apiserver_event(enum ospf_apiserver_event event, int fd, { switch (event) { case OSPF_APISERVER_ACCEPT: - (void)thread_add_read(master, ospf_apiserver_accept, apiserv, - fd, NULL); + (void)event_add_read(master, ospf_apiserver_accept, apiserv, fd, + NULL); break; case OSPF_APISERVER_SYNC_READ: apiserv->t_sync_read = NULL; - thread_add_read(master, ospf_apiserver_read, apiserv, fd, - &apiserv->t_sync_read); + event_add_read(master, ospf_apiserver_read, apiserv, fd, + &apiserv->t_sync_read); break; #ifdef USE_ASYNC_READ case OSPF_APISERVER_ASYNC_READ: apiserv->t_async_read = NULL; - thread_add_read(master, ospf_apiserver_read, apiserv, fd, - &apiserv->t_async_read); + event_add_read(master, ospf_apiserver_read, apiserv, fd, + &apiserv->t_async_read); break; #endif /* USE_ASYNC_READ */ case OSPF_APISERVER_SYNC_WRITE: - thread_add_write(master, ospf_apiserver_sync_write, apiserv, fd, - &apiserv->t_sync_write); + event_add_write(master, ospf_apiserver_sync_write, apiserv, fd, + &apiserv->t_sync_write); break; case OSPF_APISERVER_ASYNC_WRITE: - thread_add_write(master, ospf_apiserver_async_write, apiserv, - fd, &apiserv->t_async_write); + event_add_write(master, ospf_apiserver_async_write, apiserv, fd, + &apiserv->t_async_write); break; } } @@ -307,12 +312,12 @@ void ospf_apiserver_free(struct ospf_apiserver *apiserv) struct listnode *node; /* Cancel read and write threads. */ - THREAD_OFF(apiserv->t_sync_read); + EVENT_OFF(apiserv->t_sync_read); #ifdef USE_ASYNC_READ - THREAD_OFF(apiserv->t_async_read); + EVENT_OFF(apiserv->t_async_read); #endif /* USE_ASYNC_READ */ - THREAD_OFF(apiserv->t_sync_write); - THREAD_OFF(apiserv->t_async_write); + EVENT_OFF(apiserv->t_sync_write); + EVENT_OFF(apiserv->t_async_write); /* Unregister all opaque types that application registered and flush opaque LSAs if still in LSDB. */ @@ -323,6 +328,7 @@ void ospf_apiserver_free(struct ospf_apiserver *apiserv) ospf_apiserver_unregister_opaque_type( apiserv, regtype->lsa_type, regtype->opaque_type); } + list_delete(&apiserv->opaque_types); /* Close connections to OSPFd. */ if (apiserv->fd_sync > 0) { @@ -344,6 +350,8 @@ void ospf_apiserver_free(struct ospf_apiserver *apiserv) /* Remove from the list of active clients. */ listnode_delete(apiserver_list, apiserv); + XFREE(MTYPE_APISERVER_MSGFILTER, apiserv->filter); + if (IS_DEBUG_OSPF_EVENT) zlog_debug("API: Delete apiserv(%p), total#(%d)", (void *)apiserv, apiserver_list->count); @@ -352,15 +360,15 @@ void ospf_apiserver_free(struct ospf_apiserver *apiserv) XFREE(MTYPE_APISERVER, apiserv); } -void ospf_apiserver_read(struct thread *thread) +void ospf_apiserver_read(struct event *thread) { struct ospf_apiserver *apiserv; struct msg *msg; int fd; enum ospf_apiserver_event event; - apiserv = THREAD_ARG(thread); - fd = THREAD_FD(thread); + apiserv = EVENT_ARG(thread); + fd = EVENT_FD(thread); if (fd == apiserv->fd_sync) { event = OSPF_APISERVER_SYNC_READ; @@ -411,16 +419,16 @@ void ospf_apiserver_read(struct thread *thread) msg_free(msg); } -void ospf_apiserver_sync_write(struct thread *thread) +void ospf_apiserver_sync_write(struct event *thread) { struct ospf_apiserver *apiserv; struct msg *msg; int fd; int rc = -1; - apiserv = THREAD_ARG(thread); + apiserv = EVENT_ARG(thread); assert(apiserv); - fd = THREAD_FD(thread); + fd = EVENT_FD(thread); apiserv->t_sync_write = NULL; @@ -471,16 +479,16 @@ void ospf_apiserver_sync_write(struct thread *thread) } -void ospf_apiserver_async_write(struct thread *thread) +void ospf_apiserver_async_write(struct event *thread) { struct ospf_apiserver *apiserv; struct msg *msg; int fd; int rc = -1; - apiserv = THREAD_ARG(thread); + apiserv = EVENT_ARG(thread); assert(apiserv); - fd = THREAD_FD(thread); + fd = EVENT_FD(thread); apiserv->t_async_write = NULL; @@ -569,7 +577,7 @@ int ospf_apiserver_serv_sock_family(unsigned short port, int family) /* Accept connection request from external applications. For each accepted connection allocate own connection instance. */ -void ospf_apiserver_accept(struct thread *thread) +void ospf_apiserver_accept(struct event *thread) { int accept_sock; int new_sync_sock; @@ -581,8 +589,8 @@ void ospf_apiserver_accept(struct thread *thread) unsigned int peerlen; int ret; - /* THREAD_ARG (thread) is NULL */ - accept_sock = THREAD_FD(thread); + /* EVENT_ARG (thread) is NULL */ + accept_sock = EVENT_FD(thread); /* Keep hearing on socket for further connections. */ ospf_apiserver_event(OSPF_APISERVER_ACCEPT, accept_sock, NULL); @@ -889,6 +897,7 @@ int ospf_apiserver_unregister_opaque_type(struct ospf_apiserver *apiserv, /* Remove from list of registered opaque types */ listnode_delete(apiserv->opaque_types, regtype); + XFREE(MTYPE_APISERVER, regtype); if (IS_DEBUG_OSPF_EVENT) zlog_debug( "API: Del LSA-type(%d)/Opaque-type(%d) from apiserv(%p), total#(%d)", @@ -1755,6 +1764,12 @@ int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old) * session. Dump it, but increment past it's seqnum. */ assert(!ospf_opaque_is_owned(old)); + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug( + "LSA[Type%d:%pI4]: OSPF API Server Originate LSA Old Seq: 0x%x Age: %d", + old->data->type, &old->data->id, + ntohl(old->data->ls_seqnum), + ntohl(old->data->ls_age)); if (IS_LSA_MAX_SEQ(old)) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: old LSA at maxseq", __func__); @@ -1763,6 +1778,11 @@ int ospf_apiserver_originate1(struct ospf_lsa *lsa, struct ospf_lsa *old) lsa->data->ls_seqnum = lsa_seqnum_increment(old); ospf_discard_from_db(ospf, old->lsdb, old); } + if (IS_DEBUG_OSPF_CLIENT_API) + zlog_debug( + "LSA[Type%d:%pI4]: OSPF API Server Originate LSA New Seq: 0x%x Age: %d", + lsa->data->type, &lsa->data->id, + ntohl(lsa->data->ls_seqnum), ntohl(lsa->data->ls_age)); /* Install this LSA into LSDB. */ if (ospf_lsa_install(ospf, lsa->oi, lsa) == NULL) { @@ -1837,6 +1857,11 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); assert(ospf); + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: OSPF API Server LSA Refresher", + lsa->data->type, &lsa->data->id); + } + apiserv = lookup_apiserver_by_lsa(lsa); if (!apiserv) { zlog_warn("%s: LSA[%s]: No apiserver?", __func__, @@ -1846,14 +1871,14 @@ struct ospf_lsa *ospf_apiserver_lsa_refresher(struct ospf_lsa *lsa) goto out; } - if (IS_LSA_MAXAGE(lsa)) { - ospf_opaque_lsa_flush_schedule(lsa); - goto out; - } - /* Check if updated version of LSA instance has already prepared. */ new = ospf_lsdb_lookup(&apiserv->reserve, lsa); if (!new) { + if (IS_LSA_MAXAGE(lsa)) { + ospf_opaque_lsa_flush_schedule(lsa); + goto out; + } + /* This is a periodic refresh, driven by core OSPF mechanism. */ new = ospf_apiserver_opaque_lsa_new(lsa->area, lsa->oi, lsa->data); diff --git a/ospfd/ospf_apiserver.h b/ospfd/ospf_apiserver.h index a90d18573b8a..0aaf67c1f3cd 100644 --- a/ospfd/ospf_apiserver.h +++ b/ospfd/ospf_apiserver.h @@ -48,12 +48,12 @@ struct ospf_apiserver { struct msg_fifo *out_async_fifo; /* Read and write threads */ - struct thread *t_sync_read; + struct event *t_sync_read; #ifdef USE_ASYNC_READ - struct thread *t_async_read; + struct event *t_async_read; #endif /* USE_ASYNC_READ */ - struct thread *t_sync_write; - struct thread *t_async_write; + struct event *t_sync_write; + struct event *t_async_write; }; enum ospf_apiserver_event { @@ -79,10 +79,10 @@ extern void ospf_apiserver_free(struct ospf_apiserver *apiserv); extern void ospf_apiserver_event(enum ospf_apiserver_event event, int fd, struct ospf_apiserver *apiserv); extern int ospf_apiserver_serv_sock_family(unsigned short port, int family); -extern void ospf_apiserver_accept(struct thread *thread); -extern void ospf_apiserver_read(struct thread *thread); -extern void ospf_apiserver_sync_write(struct thread *thread); -extern void ospf_apiserver_async_write(struct thread *thread); +extern void ospf_apiserver_accept(struct event *thread); +extern void ospf_apiserver_read(struct event *thread); +extern void ospf_apiserver_sync_write(struct event *thread); +extern void ospf_apiserver_async_write(struct event *thread); extern int ospf_apiserver_send_reply(struct ospf_apiserver *apiserv, uint32_t seqnr, uint8_t rc); diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index 5b5367811be1..1b68f6e02236 100644 --- a/ospfd/ospf_asbr.c +++ b/ospfd/ospf_asbr.c @@ -6,7 +6,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" @@ -94,7 +94,7 @@ int ospf_route_map_set_compare(struct route_map_set_values *values1, struct external_info * ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, struct prefix_ipv4 p, ifindex_t ifindex, - struct in_addr nexthop, route_tag_t tag) + struct in_addr nexthop, route_tag_t tag, uint32_t metric) { struct external_info *new; struct route_node *rn; @@ -131,6 +131,9 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, new->tag = tag; new->orig_tag = tag; new->aggr_route = NULL; + new->metric = metric; + new->min_metric = 0; + new->max_metric = OSPF_LS_INFINITY; /* we don't unlock rn from the get() because we're attaching the info */ if (rn) @@ -138,9 +141,9 @@ ospf_external_info_add(struct ospf *ospf, uint8_t type, unsigned short instance, if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { zlog_debug( - "Redistribute[%s][%u]: %pFX external info created, with NH %pI4", + "Redistribute[%s][%u]: %pFX external info created, with NH %pI4, metric:%u", ospf_redist_string(type), ospf->vrf_id, &p, - &nexthop.s_addr); + &nexthop.s_addr, metric); } return new; } @@ -264,9 +267,9 @@ void ospf_asbr_status_update(struct ospf *ospf, uint8_t status) /* If there's redistribution configured, we need to refresh external * LSAs in order to install Type-7 and flood to all NSSA Areas */ -static void ospf_asbr_nssa_redist_update_timer(struct thread *thread) +static void ospf_asbr_nssa_redist_update_timer(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); int type; ospf->t_asbr_nssa_redist_update = NULL; @@ -297,9 +300,9 @@ void ospf_schedule_asbr_nssa_redist_update(struct ospf *ospf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("Scheduling ASBR NSSA redistribution update"); - thread_add_timer(master, ospf_asbr_nssa_redist_update_timer, ospf, - OSPF_ASBR_NSSA_REDIST_UPDATE_DELAY, - &ospf->t_asbr_nssa_redist_update); + event_add_timer(master, ospf_asbr_nssa_redist_update_timer, ospf, + OSPF_ASBR_NSSA_REDIST_UPDATE_DELAY, + &ospf->t_asbr_nssa_redist_update); } void ospf_redistribute_withdraw(struct ospf *ospf, uint8_t type, @@ -450,15 +453,12 @@ static void ospf_aggr_unlink_external_info(void *data) void ospf_external_aggregator_free(struct ospf_external_aggr_rt *aggr) { - if (OSPF_EXTERNAL_RT_COUNT(aggr)) - hash_clean(aggr->match_extnl_hash, - (void *)ospf_aggr_unlink_external_info); + hash_clean_and_free(&aggr->match_extnl_hash, + (void *)ospf_aggr_unlink_external_info); if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR)) zlog_debug("%s: Release the aggregator Address(%pI4/%d)", __func__, &aggr->p.prefix, aggr->p.prefixlen); - hash_free(aggr->match_extnl_hash); - aggr->match_extnl_hash = NULL; XFREE(MTYPE_OSPF_EXTERNAL_RT_AGGR, aggr); } @@ -983,13 +983,9 @@ static void ospf_handle_external_aggr_update(struct ospf *ospf) aggr->action = OSPF_ROUTE_AGGR_NONE; ospf_external_aggr_delete(ospf, rn); - if (OSPF_EXTERNAL_RT_COUNT(aggr)) - hash_clean( - aggr->match_extnl_hash, - (void *)ospf_aggr_handle_external_info); - - hash_free(aggr->match_extnl_hash); - XFREE(MTYPE_OSPF_EXTERNAL_RT_AGGR, aggr); + hash_clean_and_free( + &aggr->match_extnl_hash, + (void *)ospf_aggr_handle_external_info); } else if (aggr->action == OSPF_ROUTE_AGGR_MODIFY) { @@ -1047,9 +1043,9 @@ static void ospf_handle_external_aggr_update(struct ospf *ospf) } } -static void ospf_asbr_external_aggr_process(struct thread *thread) +static void ospf_asbr_external_aggr_process(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); int operation = 0; ospf->t_external_aggr = NULL; @@ -1091,7 +1087,7 @@ static void ospf_external_aggr_timer(struct ospf *ospf, zlog_debug( "%s, Restarting Aggregator delay timer.", __func__); - THREAD_OFF(ospf->t_external_aggr); + EVENT_OFF(ospf->t_external_aggr); } } @@ -1100,8 +1096,8 @@ static void ospf_external_aggr_timer(struct ospf *ospf, __func__, ospf->aggr_delay_interval); ospf->aggr_action = operation; - thread_add_timer(master, ospf_asbr_external_aggr_process, ospf, - ospf->aggr_delay_interval, &ospf->t_external_aggr); + event_add_timer(master, ospf_asbr_external_aggr_process, ospf, + ospf->aggr_delay_interval, &ospf->t_external_aggr); } int ospf_asbr_external_aggregator_set(struct ospf *ospf, struct prefix_ipv4 *p, diff --git a/ospfd/ospf_asbr.h b/ospfd/ospf_asbr.h index 6b427c5fd20e..dfb9d965c54b 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -36,6 +36,10 @@ struct external_info { /* Actual tag received from zebra*/ route_tag_t orig_tag; + uint32_t metric; + uint32_t min_metric; + uint32_t max_metric; + 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 @@ -99,11 +103,10 @@ extern struct external_info *ospf_external_info_new(struct ospf *, uint8_t, extern void ospf_reset_route_map_set_values(struct route_map_set_values *); extern int ospf_route_map_set_compare(struct route_map_set_values *, struct route_map_set_values *); -extern struct external_info *ospf_external_info_add(struct ospf *, uint8_t, - unsigned short, - struct prefix_ipv4, - ifindex_t, struct in_addr, - route_tag_t); +extern struct external_info * +ospf_external_info_add(struct ospf *, uint8_t, unsigned short, + struct prefix_ipv4, ifindex_t, struct in_addr, + route_tag_t, uint32_t metric); extern void ospf_external_info_delete(struct ospf *, uint8_t, unsigned short, struct prefix_ipv4); extern struct external_info *ospf_external_info_lookup(struct ospf *, uint8_t, diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index b43f0cb378c2..610b5fc08e92 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -6,7 +6,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "hash.h" #include "linklist.h" @@ -159,7 +159,9 @@ ospf_ase_calculate_new_route(struct ospf_lsa *lsa, if (!IS_EXTERNAL_METRIC(al->e[0].tos)) { if (IS_DEBUG_OSPF(lsa, LSA)) - zlog_debug("Route[External]: type-1 created."); + zlog_debug( + "Route[External]: type-1 created, asbr cost:%d metric:%d.", + asbr_route->cost, metric); new->path_type = OSPF_PATH_TYPE1_EXTERNAL; new->cost = asbr_route->cost + metric; /* X + Y */ } else { @@ -549,7 +551,7 @@ static int ospf_ase_compare_tables(struct ospf *ospf, return 0; } -static void ospf_ase_calculate_timer(struct thread *t) +static void ospf_ase_calculate_timer(struct event *t) { struct ospf *ospf; struct ospf_lsa *lsa; @@ -558,7 +560,7 @@ static void ospf_ase_calculate_timer(struct thread *t) struct ospf_area *area; struct timeval start_time, stop_time; - ospf = THREAD_ARG(t); + ospf = EVENT_ARG(t); ospf->t_ase_calc = NULL; if (ospf->ase_calc) { @@ -613,6 +615,7 @@ static void ospf_ase_calculate_timer(struct thread *t) */ if (ospf->gr_info.finishing_restart) { ospf_zebra_gr_disable(ospf); + ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); ospf->gr_info.finishing_restart = false; } } @@ -630,8 +633,8 @@ void ospf_ase_calculate_timer_add(struct ospf *ospf) if (ospf == NULL) return; - thread_add_timer(master, ospf_ase_calculate_timer, ospf, - OSPF_ASE_CALC_INTERVAL, &ospf->t_ase_calc); + event_add_timer(master, ospf_ase_calculate_timer, ospf, + OSPF_ASE_CALC_INTERVAL, &ospf->t_ase_calc); } void ospf_ase_register_external_lsa(struct ospf_lsa *lsa, struct ospf *top) diff --git a/ospfd/ospf_bfd.c b/ospfd/ospf_bfd.c index 0b0016745d42..7d4c7c06b81e 100644 --- a/ospfd/ospf_bfd.c +++ b/ospfd/ospf_bfd.c @@ -12,7 +12,7 @@ #include "linklist.h" #include "memory.h" #include "prefix.h" -#include "thread.h" +#include "frrevent.h" #include "buffer.h" #include "stream.h" #include "zclient.h" @@ -310,7 +310,7 @@ DEFUN (no_ip_ospf_bfd, return CMD_SUCCESS; } -void ospf_bfd_init(struct thread_master *tm) +void ospf_bfd_init(struct event_loop *tm) { bfd_protocol_integration_init(zclient, tm); diff --git a/ospfd/ospf_bfd.h b/ospfd/ospf_bfd.h index 3c4e1e6c5f43..d454f9c97bf5 100644 --- a/ospfd/ospf_bfd.h +++ b/ospfd/ospf_bfd.h @@ -11,7 +11,7 @@ #include "ospfd/ospf_interface.h" #include "json.h" -extern void ospf_bfd_init(struct thread_master *tm); +extern void ospf_bfd_init(struct event_loop *tm); extern void ospf_bfd_write_config(struct vty *vty, const struct ospf_if_params *params); diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index b74b84e37da2..dbe6dd97d0d3 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -9,7 +9,7 @@ #include "lib/bfd.h" #include "monotime.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "command.h" #include "stream.h" @@ -235,7 +235,7 @@ const char *ospf_timeval_dump(struct timeval *t, char *buf, size_t size) return buf; } -const char *ospf_timer_dump(struct thread *t, char *buf, size_t size) +const char *ospf_timer_dump(struct event *t, char *buf, size_t size) { struct timeval result; if (!t) diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index 0f217971eecd..0d47be256501 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -136,15 +136,15 @@ extern unsigned long term_debug_ospf_client_api; extern char *ospf_lsa_type_str[]; /* Prototypes. */ -extern const char *ospf_area_name_string(struct ospf_area *); -extern const char *ospf_area_desc_string(struct ospf_area *); -extern const char *ospf_if_name_string(struct ospf_interface *); +extern const char *ospf_area_name_string(struct ospf_area *area); +extern const char *ospf_area_desc_string(struct ospf_area *area); +extern const char *ospf_if_name_string(struct ospf_interface *oip); extern int ospf_nbr_ism_state(struct ospf_neighbor *nbr); extern void ospf_nbr_ism_state_message(struct ospf_neighbor *nbr, char *buf, size_t size); -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_packet_dump(struct stream *); +extern const char *ospf_timer_dump(struct event *e, char *buf, size_t size); +extern const char *ospf_timeval_dump(struct timeval *t, char *buf, size_t size); +extern void ospf_packet_dump(struct stream *s); extern void ospf_debug_init(void); /* Appropriate buffer size to use with ospf_timer_dump and ospf_timeval_dump: */ diff --git a/ospfd/ospf_ext.c b/ospfd/ospf_ext.c index 5faaed076b40..75b58035a3d5 100644 --- a/ospfd/ospf_ext.c +++ b/ospfd/ospf_ext.c @@ -25,7 +25,7 @@ #include "vty.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "network.h" diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index d0453bbc4ad5..dd8c9268f108 100644 --- a/ospfd/ospf_flood.c +++ b/ospfd/ospf_flood.c @@ -12,7 +12,7 @@ #include "if.h" #include "command.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "log.h" #include "zclient.h" @@ -154,11 +154,11 @@ struct external_info *ospf_external_info_check(struct ospf *ospf, redist_on = is_default_prefix4(&p) ? vrf_bitmap_check( - zclient->default_information[AFI_IP], - ospf->vrf_id) - : (zclient->mi_redist[AFI_IP][type].enabled - || vrf_bitmap_check( - zclient->redist[AFI_IP][type], + &zclient->default_information[AFI_IP], + ospf->vrf_id) + : (zclient->mi_redist[AFI_IP][type].enabled || + vrf_bitmap_check( + &zclient->redist[AFI_IP][type], ospf->vrf_id)); // Pending: check for MI above. if (redist_on) { @@ -568,6 +568,15 @@ int ospf_flood_through_interface(struct ospf_interface *oi, if (!ospf_if_is_enable(oi)) return 0; + if (IS_OPAQUE_LSA(lsa->data->type) && + !OSPF_IF_PARAM(oi, opaque_capable)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "%s: Skipping interface %s (%s) with opaque disabled.", + __func__, IF_NAME(oi), ospf_get_name(oi->ospf)); + return 0; + } + /* If flood reduction is configured, set the DC bit on the lsa. */ if (IS_LSA_SELF(lsa)) { if (OSPF_FR_CONFIG(oi->area->ospf, oi->area)) { @@ -770,15 +779,26 @@ int ospf_flood_through_interface(struct ospf_interface *oi, OSPF_SEND_PACKET_DIRECT); } } else - /* Optimization: for P2MP interfaces, - don't send back out the incoming interface immediately, - allow time to rx multicast ack to the rx'ed (multicast) - update */ - if (retx_flag != 1 || - oi->type != OSPF_IFTYPE_POINTOMULTIPOINT || inbr == NULL || - oi != inbr->oi) - ospf_ls_upd_send_lsa(oi->nbr_self, lsa, - OSPF_SEND_PACKET_INDIRECT); + /* If P2MP delayed reflooding is configured and the LSA was + received from a neighbor on the P2MP interface, do not flood + if back out on the interface. The LSA will be retransmitted + upon expiration of each neighbor's retransmission timer. This + will allow time to receive a multicast multicast link state + acknoweldgement and remove the LSA from each neighbor's link + state retransmission list. */ + if (oi->p2mp_delay_reflood && + (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) && + (inbr != NULL) && (oi == inbr->oi)) { + if (IS_DEBUG_OSPF(lsa, LSA_FLOODING)) + zlog_debug( + "Delay reflooding for LSA[%s] from NBR %pI4 on interface %s", + dump_lsa_key(lsa), + inbr ? &(inbr->router_id) + : &(oi->ospf->router_id), + IF_NAME(oi)); + } else + ospf_ls_upd_send_lsa(oi->nbr_self, lsa, + OSPF_SEND_PACKET_INDIRECT); return 0; } diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c index e83ff1dccf20..c23c42052fc7 100644 --- a/ospfd/ospf_gr.c +++ b/ospfd/ospf_gr.c @@ -33,7 +33,7 @@ #include "ospfd/ospf_dump.h" #include "ospfd/ospf_gr_clippy.c" -static void ospf_gr_nvm_delete(struct ospf *ospf); +static void ospf_gr_grace_period_expired(struct event *thread); /* Lookup self-originated Grace-LSA in the LSDB. */ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf, @@ -53,7 +53,9 @@ static struct ospf_lsa *ospf_gr_lsa_lookup(struct ospf *ospf, /* Fill in fields of the Grace-LSA that is being originated. */ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, - struct ospf_interface *oi, struct stream *s) + struct ospf_interface *oi, + enum ospf_gr_restart_reason reason, + struct stream *s) { struct grace_tlv_graceperiod tlv_period = {}; struct grace_tlv_restart_reason tlv_reason = {}; @@ -68,10 +70,7 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, /* Put restart reason. */ tlv_reason.header.type = htons(RESTART_REASON_TYPE); tlv_reason.header.length = htons(RESTART_REASON_LENGTH); - if (gr_info->restart_support) - tlv_reason.reason = OSPF_GR_SW_RESTART; - else - tlv_reason.reason = OSPF_GR_UNKNOWN_RESTART; + tlv_reason.reason = reason; stream_put(s, &tlv_reason, sizeof(tlv_reason)); /* Put IP address. */ @@ -85,7 +84,8 @@ static void ospf_gr_lsa_body_set(struct ospf_gr_info *gr_info, } /* Generate Grace-LSA for a given interface. */ -static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) +static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi, + enum ospf_gr_restart_reason reason) { struct stream *s; struct lsa_header *lsah; @@ -112,7 +112,7 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) lsa_header_set(s, options, lsa_type, lsa_id, oi->ospf->router_id); /* Set opaque-LSA body fields. */ - ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, s); + ospf_gr_lsa_body_set(&oi->ospf->gr_info, oi, reason, s); /* Set length. */ length = stream_get_endp(s); @@ -135,15 +135,20 @@ static struct ospf_lsa *ospf_gr_lsa_new(struct ospf_interface *oi) } /* Originate and install Grace-LSA for a given interface. */ -static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage) +static void ospf_gr_lsa_originate(struct ospf_interface *oi, + enum ospf_gr_restart_reason reason, + bool maxage) { struct ospf_lsa *lsa, *old; - if (ospf_interface_neighbor_count(oi) == 0) + /* Skip originating a Grace-LSA when not necessary. */ + if (!if_is_operative(oi->ifp) || if_is_loopback(oi->ifp) || + (reason != OSPF_GR_UNKNOWN_RESTART && + ospf_interface_neighbor_count(oi) == 0)) return; /* Create new Grace-LSA. */ - lsa = ospf_gr_lsa_new(oi); + lsa = ospf_gr_lsa_new(oi, reason); if (!lsa) { zlog_warn("%s: ospf_gr_lsa_new() failed", __func__); return; @@ -157,18 +162,36 @@ static void ospf_gr_lsa_originate(struct ospf_interface *oi, bool maxage) if (old) lsa->data->ls_seqnum = lsa_seqnum_increment(old); - /* Install this LSA into LSDB. */ - if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { - zlog_warn("%s: ospf_lsa_install() failed", __func__); - ospf_lsa_unlock(&lsa); - return; + if (!maxage && reason == OSPF_GR_UNKNOWN_RESTART) { + struct list *update; + struct in_addr addr; + + /* + * When performing an unplanned restart, send a handcrafted + * Grace-LSA since the interface isn't fully initialized yet. + */ + ospf_lsa_checksum(lsa->data); + ospf_lsa_lock(lsa); + update = list_new(); + listnode_add(update, lsa); + addr.s_addr = htonl(OSPF_ALLSPFROUTERS); + ospf_ls_upd_queue_send(oi, update, addr, true); + list_delete(&update); + ospf_lsa_discard(lsa); + } else { + /* Install this LSA into LSDB. */ + if (ospf_lsa_install(oi->ospf, oi, lsa) == NULL) { + zlog_warn("%s: ospf_lsa_install() failed", __func__); + ospf_lsa_unlock(&lsa); + return; + } + + /* Flood the LSA through out the interface */ + ospf_flood_through_interface(oi, NULL, lsa); } /* Update new LSA origination count. */ oi->ospf->lsa_originate_count++; - - /* Flood the LSA through out the interface */ - ospf_flood_through_interface(oi, NULL, lsa); } /* Flush all self-originated Grace-LSAs. */ @@ -181,13 +204,14 @@ static void ospf_gr_flush_grace_lsas(struct ospf *ospf) struct ospf_interface *oi; struct listnode *inode; - if (IS_DEBUG_OSPF_GR) - zlog_debug( - "GR: flushing self-originated Grace-LSAs [area %pI4]", - &area->area_id); + for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) { + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: flushing self-originated Grace-LSA [area %pI4] [interface %s]", + &area->area_id, oi->ifp->name); - for (ALL_LIST_ELEMENTS_RO(area->oiflist, inode, oi)) - ospf_gr_lsa_originate(oi, true); + ospf_gr_lsa_originate(oi, ospf->gr_info.reason, true); + } } } @@ -201,10 +225,7 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) zlog_debug("GR: exiting graceful restart: %s", reason); ospf->gr_info.restart_in_progress = false; - THREAD_OFF(ospf->gr_info.t_grace_period); - - /* Record in non-volatile memory that the restart is complete. */ - ospf_gr_nvm_delete(ospf); + EVENT_OFF(ospf->gr_info.t_grace_period); for (ALL_LIST_ELEMENTS_RO(ospf->areas, onode, area)) { struct ospf_interface *oi; @@ -216,13 +237,22 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) */ ospf_router_lsa_update_area(area); - /* - * 2) The router should reoriginate network-LSAs on all segments - * where it is the Designated Router. - */ - for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) + for (ALL_LIST_ELEMENTS_RO(area->oiflist, anode, oi)) { + /* Disable hello delay. */ + if (oi->gr.hello_delay.t_grace_send) { + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, + ospf_hello_timer, 1); + } + + /* + * 2) The router should reoriginate network-LSAs on all + * segments where it is the Designated Router. + */ if (oi->state == ISM_DR) ospf_network_lsa_update(oi); + } } /* @@ -242,12 +272,34 @@ static void ospf_gr_restart_exit(struct ospf *ospf, const char *reason) * should be removed. */ ospf->gr_info.finishing_restart = true; + XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); + ospf->gr_info.exit_reason = XSTRDUP(MTYPE_TMP, reason); ospf_spf_calculate_schedule(ospf, SPF_FLAG_GR_FINISH); /* 6) Any grace-LSAs that the router originated should be flushed. */ ospf_gr_flush_grace_lsas(ospf); } +/* Enter the Graceful Restart mode. */ +void ospf_gr_restart_enter(struct ospf *ospf, + enum ospf_gr_restart_reason reason, time_t timestamp) +{ + unsigned long remaining_time; + + ospf->gr_info.restart_in_progress = true; + ospf->gr_info.reason = reason; + + /* Schedule grace period timeout. */ + remaining_time = timestamp - time(NULL); + if (IS_DEBUG_OSPF_GR) + zlog_debug( + "GR: remaining time until grace period expires: %lu(s)", + remaining_time); + + event_add_timer(master, ospf_gr_grace_period_expired, ospf, + remaining_time, &ospf->gr_info.t_grace_period); +} + /* Check if a Router-LSA contains a given link. */ static bool ospf_router_lsa_contains_adj(struct ospf_lsa *lsa, struct in_addr *id) @@ -495,9 +547,9 @@ void ospf_gr_check_adjs(struct ospf *ospf) } /* Handling of grace period expiry. */ -static void ospf_gr_grace_period_expired(struct thread *thread) +static void ospf_gr_grace_period_expired(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); ospf->gr_info.t_grace_period = NULL; ospf_gr_restart_exit(ospf, "grace period has expired"); @@ -518,11 +570,26 @@ static char *ospf_gr_nvm_filepath(struct ospf *ospf) return filepath; } +/* Send extra Grace-LSA out the interface (unplanned outages only). */ +void ospf_gr_iface_send_grace_lsa(struct event *thread) +{ + struct ospf_interface *oi = EVENT_ARG(thread); + struct ospf_if_params *params = IF_DEF_PARAMS(oi->ifp); + + ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); + + if (++oi->gr.hello_delay.elapsed_seconds < params->v_gr_hello_delay) + event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); + else + OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); +} + /* * Record in non-volatile memory that the given OSPF instance is attempting to * perform a graceful restart. */ -static void ospf_gr_nvm_update(struct ospf *ospf) +static void ospf_gr_nvm_update(struct ospf *ospf, bool prepare) { char *filepath; const char *inst_name; @@ -550,16 +617,18 @@ static void ospf_gr_nvm_update(struct ospf *ospf) json_instance); } + json_object_int_add(json_instance, "gracePeriod", + ospf->gr_info.grace_period); + /* * Record not only the grace period, but also a UNIX timestamp * corresponding to the end of that period. That way, once ospfd is * restarted, it will be possible to take into account the time that * passed while ospfd wasn't running. */ - json_object_int_add(json_instance, "gracePeriod", - ospf->gr_info.grace_period); - json_object_int_add(json_instance, "timestamp", - time(NULL) + ospf->gr_info.grace_period); + if (prepare) + json_object_int_add(json_instance, "timestamp", + time(NULL) + ospf->gr_info.grace_period); json_object_to_file_ext(filepath, json, JSON_C_TO_STRING_PRETTY); json_object_free(json); @@ -569,7 +638,7 @@ static void ospf_gr_nvm_update(struct ospf *ospf) * Delete GR status information about the given OSPF instance from non-volatile * memory. */ -static void ospf_gr_nvm_delete(struct ospf *ospf) +void ospf_gr_nvm_delete(struct ospf *ospf) { char *filepath; const char *inst_name; @@ -607,6 +676,7 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_object *json_instances; json_object *json_instance; json_object *json_timestamp; + json_object *json_grace_period; time_t timestamp = 0; filepath = ospf_gr_nvm_filepath(ospf); @@ -629,29 +699,33 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_instance); } + json_object_object_get_ex(json_instance, "gracePeriod", + &json_grace_period); json_object_object_get_ex(json_instance, "timestamp", &json_timestamp); + if (json_timestamp) { time_t now; - unsigned long remaining_time; - /* Check if the grace period has already expired. */ + /* Planned GR: check if the grace period has already expired. */ now = time(NULL); timestamp = json_object_get_int(json_timestamp); if (now > timestamp) { ospf_gr_restart_exit( ospf, "grace period has expired already"); - } else { - /* Schedule grace period timeout. */ - ospf->gr_info.restart_in_progress = true; - remaining_time = timestamp - time(NULL); - if (IS_DEBUG_OSPF_GR) - zlog_debug( - "GR: remaining time until grace period expires: %lu(s)", - remaining_time); - thread_add_timer(master, ospf_gr_grace_period_expired, - ospf, remaining_time, - &ospf->gr_info.t_grace_period); - } + } else + ospf_gr_restart_enter(ospf, OSPF_GR_SW_RESTART, + timestamp); + } else if (json_grace_period) { + uint32_t grace_period; + + /* + * Unplanned GR: the Grace-LSAs will be sent later as soon as + * the interfaces are operational. + */ + grace_period = json_object_get_int(json_grace_period); + ospf->gr_info.grace_period = grace_period; + ospf_gr_restart_enter(ospf, OSPF_GR_UNKNOWN_RESTART, + time(NULL) + ospf->gr_info.grace_period); } json_object_object_del(json_instances, inst_name); @@ -660,6 +734,17 @@ void ospf_gr_nvm_read(struct ospf *ospf) json_object_free(json); } +void ospf_gr_unplanned_start_interface(struct ospf_interface *oi) +{ + /* Send Grace-LSA. */ + ospf_gr_lsa_originate(oi, oi->ospf->gr_info.reason, false); + + /* Start GR hello-delay interval. */ + oi->gr.hello_delay.elapsed_seconds = 0; + event_add_timer(master, ospf_gr_iface_send_grace_lsa, oi, 1, + &oi->gr.hello_delay.t_grace_send); +} + /* Prepare to start a Graceful Restart. */ static void ospf_gr_prepare(void) { @@ -687,20 +772,19 @@ static void ospf_gr_prepare(void) continue; } - /* Freeze OSPF routes in the RIB. */ - if (ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period)) { - zlog_warn( - "%s: failed to activate graceful restart: not connected to zebra", - __func__); - continue; - } - /* Send a Grace-LSA to all neighbors. */ - for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) - ospf_gr_lsa_originate(oi, false); + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, inode, oi)) { + if (OSPF_IF_PARAM(oi, opaque_capable)) + ospf_gr_lsa_originate(oi, OSPF_GR_SW_RESTART, + false); + else + zlog_debug( + "GR: skipping grace LSA on interface %s (%s) with opaque capability disabled", + IF_NAME(oi), ospf_get_name(oi->ospf)); + } /* Record end of the grace period in non-volatile memory. */ - ospf_gr_nvm_update(ospf); + ospf_gr_nvm_update(ospf, true); /* * Mark that a Graceful Restart preparation is in progress, to @@ -749,6 +833,12 @@ DEFPY(graceful_restart, graceful_restart_cmd, ospf->gr_info.restart_support = true; ospf->gr_info.grace_period = grace_period; + /* Freeze OSPF routes in the RIB. */ + (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + + /* Record that GR is enabled in non-volatile memory. */ + ospf_gr_nvm_update(ospf, false); + return CMD_SUCCESS; } @@ -771,6 +861,8 @@ DEFPY(no_graceful_restart, no_graceful_restart_cmd, ospf->gr_info.restart_support = false; ospf->gr_info.grace_period = OSPF_DFLT_GRACE_INTERVAL; + ospf_gr_nvm_delete(ospf); + ospf_zebra_gr_disable(ospf); return CMD_SUCCESS; } diff --git a/ospfd/ospf_gr.h b/ospfd/ospf_gr.h index 65aa91d4213f..22f9e1ef2253 100644 --- a/ospfd/ospf_gr.h +++ b/ospfd/ospf_gr.h @@ -90,7 +90,7 @@ struct ospf_helper_info { * helper until this timer until * this timer expires. */ - struct thread *t_grace_timer; + struct event *t_grace_timer; /* Helper status */ uint32_t gr_helper_status; @@ -166,11 +166,16 @@ extern void ospf_gr_helper_supported_gracetime_set(struct ospf *ospf, uint32_t interval); extern void ospf_gr_helper_set_supported_planned_only_restart(struct ospf *ospf, bool planned_only); - +extern void ospf_gr_iface_send_grace_lsa(struct event *thread); +extern void ospf_gr_restart_enter(struct ospf *ospf, + enum ospf_gr_restart_reason reason, + time_t timestamp); extern void ospf_gr_check_lsdb_consistency(struct ospf *ospf, struct ospf_area *area); extern void ospf_gr_check_adjs(struct ospf *ospf); extern void ospf_gr_nvm_read(struct ospf *ospf); +extern void ospf_gr_nvm_delete(struct ospf *ospf); +extern void ospf_gr_unplanned_start_interface(struct ospf_interface *oi); extern void ospf_gr_init(void); #endif /* _ZEBRA_OSPF_GR_H */ diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c index 0263df48c5c7..b97b6802b2fe 100644 --- a/ospfd/ospf_gr_helper.c +++ b/ospfd/ospf_gr_helper.c @@ -8,7 +8,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" @@ -99,9 +99,7 @@ static void ospf_enable_rtr_hash_destroy(struct ospf *ospf) if (ospf->enable_rtr_list == NULL) return; - hash_clean(ospf->enable_rtr_list, ospf_disable_rtr_hash_free); - hash_free(ospf->enable_rtr_list); - ospf->enable_rtr_list = NULL; + hash_clean_and_free(&ospf->enable_rtr_list, ospf_disable_rtr_hash_free); } /* @@ -331,9 +329,9 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, * Returns: * Nothing */ -static void ospf_handle_grace_timer_expiry(struct thread *thread) +static void ospf_handle_grace_timer_expiry(struct event *thread) { - struct ospf_neighbor *nbr = THREAD_ARG(thread); + struct ospf_neighbor *nbr = EVENT_ARG(thread); nbr->gr_helper_info.t_grace_timer = NULL; @@ -502,7 +500,7 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, if (OSPF_GR_IS_ACTIVE_HELPER(restarter)) { if (restarter->gr_helper_info.t_grace_timer) - THREAD_OFF(restarter->gr_helper_info.t_grace_timer); + EVENT_OFF(restarter->gr_helper_info.t_grace_timer); if (ospf->active_restarter_cnt > 0) ospf->active_restarter_cnt--; @@ -535,9 +533,9 @@ int ospf_process_grace_lsa(struct ospf *ospf, struct ospf_lsa *lsa, actual_grace_interval); /* Start the grace timer */ - thread_add_timer(master, ospf_handle_grace_timer_expiry, restarter, - actual_grace_interval, - &restarter->gr_helper_info.t_grace_timer); + event_add_timer(master, ospf_handle_grace_timer_expiry, restarter, + actual_grace_interval, + &restarter->gr_helper_info.t_grace_timer); return OSPF_GR_ACTIVE_HELPER; } @@ -701,7 +699,7 @@ void ospf_gr_helper_exit(struct ospf_neighbor *nbr, * expiry, stop the grace timer. */ if (reason != OSPF_GR_HELPER_GRACE_TIMEOUT) - THREAD_OFF(nbr->gr_helper_info.t_grace_timer); + EVENT_OFF(nbr->gr_helper_info.t_grace_timer); /* check exit triggered due to successful completion * of graceful restart. diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c index d3c9626d9ae8..59112b2cd2b7 100644 --- a/ospfd/ospf_ia.c +++ b/ospfd/ospf_ia.c @@ -7,7 +7,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "hash.h" #include "linklist.h" diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index 4ea367ecad36..72de198116d1 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -6,7 +6,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "prefix.h" #include "if.h" @@ -101,6 +101,9 @@ int ospf_if_get_output_cost(struct ospf_interface *oi) cost = 1; else if (cost > 65535) cost = 65535; + + if (if_is_loopback(oi->ifp)) + cost = 0; } return cost; @@ -268,6 +271,10 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp, QOBJ_REG(oi, ospf_interface); + /* If first oi, check per-intf write socket */ + if (ospf->oi_running && ospf->intf_socket_enabled) + ospf_ifp_sock_init(ifp); + if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf interface %s vrf %s id %u created", __func__, ifp->name, ospf_get_name(ospf), @@ -289,7 +296,7 @@ void ospf_if_cleanup(struct ospf_interface *oi) /* oi->nbrs and oi->nbr_nbma should be deleted on InterfaceDown event */ /* delete all static neighbors attached to this interface */ for (ALL_LIST_ELEMENTS(oi->nbr_nbma, node, nnode, nbr_nbma)) { - THREAD_OFF(nbr_nbma->t_poll); + EVENT_OFF(nbr_nbma->t_poll); if (nbr_nbma->nbr) { nbr_nbma->nbr->nbr_nbma = NULL; @@ -324,6 +331,8 @@ void ospf_if_cleanup(struct ospf_interface *oi) void ospf_if_free(struct ospf_interface *oi) { + struct interface *ifp = oi->ifp; + ospf_if_down(oi); ospf_fifo_free(oi->obuf); @@ -356,7 +365,11 @@ void ospf_if_free(struct ospf_interface *oi) listnode_delete(oi->ospf->oiflist, oi); listnode_delete(oi->area->oiflist, oi); - thread_cancel_event(master, oi); + event_cancel_event(master, oi); + + /* If last oi, close per-interface socket */ + if (ospf_oi_count(ifp) == 0) + ospf_ifp_sock_close(ifp); memset(oi, 0, sizeof(*oi)); XFREE(MTYPE_OSPF_IF, oi); @@ -492,7 +505,7 @@ void ospf_interface_fifo_flush(struct ospf_interface *oi) if (oi->on_write_q) { listnode_delete(ospf->oi_write_q, oi); if (list_isempty(ospf->oi_write_q)) - THREAD_OFF(ospf->t_write); + EVENT_OFF(ospf->t_write); oi->on_write_q = 0; } } @@ -527,6 +540,7 @@ static struct ospf_if_params *ospf_new_if_params(void) UNSET_IF_PARAM(oip, passive_interface); UNSET_IF_PARAM(oip, v_hello); UNSET_IF_PARAM(oip, fast_hello); + UNSET_IF_PARAM(oip, v_gr_hello_delay); UNSET_IF_PARAM(oip, v_wait); UNSET_IF_PARAM(oip, priority); UNSET_IF_PARAM(oip, type); @@ -534,6 +548,7 @@ static struct ospf_if_params *ospf_new_if_params(void) UNSET_IF_PARAM(oip, auth_crypt); UNSET_IF_PARAM(oip, auth_type); UNSET_IF_PARAM(oip, if_area); + UNSET_IF_PARAM(oip, opaque_capable); oip->auth_crypt = list_new(); @@ -541,6 +556,8 @@ static struct ospf_if_params *ospf_new_if_params(void) oip->is_v_wait_set = false; oip->ptp_dmvpn = 0; + oip->p2mp_delay_reflood = OSPF_P2MP_DELAY_REFLOOD_DEFAULT; + oip->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; return oip; } @@ -570,19 +587,20 @@ void ospf_free_if_params(struct interface *ifp, struct in_addr addr) oip = rn->info; route_unlock_node(rn); - if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) - && !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) - && !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) - && !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) - && !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) - && !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) - && !OSPF_IF_PARAM_CONFIGURED(oip, v_wait) - && !OSPF_IF_PARAM_CONFIGURED(oip, priority) - && !OSPF_IF_PARAM_CONFIGURED(oip, type) - && !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) - && !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) - && !OSPF_IF_PARAM_CONFIGURED(oip, if_area) - && listcount(oip->auth_crypt) == 0) { + if (!OSPF_IF_PARAM_CONFIGURED(oip, output_cost_cmd) && + !OSPF_IF_PARAM_CONFIGURED(oip, transmit_delay) && + !OSPF_IF_PARAM_CONFIGURED(oip, retransmit_interval) && + !OSPF_IF_PARAM_CONFIGURED(oip, passive_interface) && + !OSPF_IF_PARAM_CONFIGURED(oip, v_hello) && + !OSPF_IF_PARAM_CONFIGURED(oip, fast_hello) && + !OSPF_IF_PARAM_CONFIGURED(oip, v_wait) && + !OSPF_IF_PARAM_CONFIGURED(oip, priority) && + !OSPF_IF_PARAM_CONFIGURED(oip, type) && + !OSPF_IF_PARAM_CONFIGURED(oip, auth_simple) && + !OSPF_IF_PARAM_CONFIGURED(oip, auth_type) && + !OSPF_IF_PARAM_CONFIGURED(oip, if_area) && + !OSPF_IF_PARAM_CONFIGURED(oip, opaque_capable) && + listcount(oip->auth_crypt) == 0) { ospf_del_if_params(ifp, oip); rn->info = NULL; route_unlock_node(rn); @@ -651,6 +669,8 @@ int ospf_if_new_hook(struct interface *ifp) ifp->info = XCALLOC(MTYPE_OSPF_IF_INFO, sizeof(struct ospf_if_info)); + IF_OSPF_IF_INFO(ifp)->oii_fd = -1; + IF_OIFS(ifp) = route_table_init(); IF_OIFS_PARAMS(ifp) = route_table_init(); @@ -674,6 +694,9 @@ int ospf_if_new_hook(struct interface *ifp) SET_IF_PARAM(IF_DEF_PARAMS(ifp), fast_hello); IF_DEF_PARAMS(ifp)->fast_hello = OSPF_FAST_HELLO_DEFAULT; + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_gr_hello_delay); + IF_DEF_PARAMS(ifp)->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT; + SET_IF_PARAM(IF_DEF_PARAMS(ifp), v_wait); IF_DEF_PARAMS(ifp)->v_wait = OSPF_ROUTER_DEAD_INTERVAL_DEFAULT; @@ -683,6 +706,9 @@ int ospf_if_new_hook(struct interface *ifp) SET_IF_PARAM(IF_DEF_PARAMS(ifp), auth_type); IF_DEF_PARAMS(ifp)->auth_type = OSPF_AUTH_NOTSET; + SET_IF_PARAM(IF_DEF_PARAMS(ifp), opaque_capable); + IF_DEF_PARAMS(ifp)->opaque_capable = OSPF_OPAQUE_CAPABLE_DEFAULT; + rc = ospf_opaque_new_if(ifp); return rc; } @@ -691,6 +717,8 @@ static int ospf_if_delete_hook(struct interface *ifp) { int rc = 0; struct route_node *rn; + struct ospf_if_info *oii; + rc = ospf_opaque_del_if(ifp); /* @@ -707,6 +735,13 @@ static int ospf_if_delete_hook(struct interface *ifp) route_table_finish(IF_OIFS(ifp)); route_table_finish(IF_OIFS_PARAMS(ifp)); + /* Close per-interface socket */ + oii = ifp->info; + if (oii && oii->oii_fd > 0) { + close(oii->oii_fd); + oii->oii_fd = -1; + } + XFREE(MTYPE_OSPF_IF_INFO, ifp->info); return rc; @@ -977,7 +1012,6 @@ static void ospf_vl_if_delete(struct ospf_vl_data *vl_data) if_delete(&ifp); if (!vrf_is_enabled(vrf)) vrf_delete(vrf); - vlink_count--; } /* for a defined area, count the number of configured vl @@ -1328,18 +1362,28 @@ static int ospf_ifp_create(struct interface *ifp) 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", + "Zebra: interface add %s vrf %s[%u] index %d flags %llx metric %d mtu %d speed %u status 0x%x", ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, ifp->ifindex, (unsigned long long)ifp->flags, - ifp->metric, ifp->mtu, ifp->speed); + ifp->metric, ifp->mtu, ifp->speed, ifp->status); 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)) { + /* Change ospf type param based on following + * condition: + * ospf type params is not set (first creation), + * OR ospf param type is changed based on + * link event, currently only handle for + * loopback interface type, for other ospf interface, + * type can be set from user config which needs to be + * preserved. + */ + if (IF_DEF_PARAMS(ifp) && + (!OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS(ifp), type) || + if_is_loopback(ifp))) { SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); } @@ -1368,6 +1412,17 @@ static int ospf_ifp_up(struct interface *ifp) struct ospf_interface *oi; struct route_node *rn; struct ospf_if_info *oii = ifp->info; + struct ospf *ospf; + + if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) + zlog_debug("Zebra: Interface[%s] state change to up.", + ifp->name); + + /* Open per-intf write socket if configured */ + ospf = ifp->vrf->info; + + if (ospf && ospf->oi_running && ospf->intf_socket_enabled) + ospf_ifp_sock_init(ifp); ospf_if_recalculate_output_cost(ifp); @@ -1385,10 +1440,6 @@ static int ospf_ifp_up(struct interface *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; @@ -1417,6 +1468,9 @@ static int ospf_ifp_down(struct interface *ifp) ospf_if_down(oi); } + /* Close per-interface write socket if configured */ + ospf_ifp_sock_close(ifp); + return 0; } @@ -1471,7 +1525,7 @@ void ospf_reset_hello_timer(struct interface *ifp, struct in_addr addr, ospf_hello_send(oi); /* Restart hello timer for this interface */ - THREAD_OFF(oi->t_hello); + EVENT_OFF(oi->t_hello); OSPF_HELLO_TIMER_ON(oi); } @@ -1495,7 +1549,7 @@ void ospf_reset_hello_timer(struct interface *ifp, struct in_addr addr, ospf_hello_send(oi); /* Restart the hello timer. */ - THREAD_OFF(oi->t_hello); + EVENT_OFF(oi->t_hello); OSPF_HELLO_TIMER_ON(oi); } } diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 0ee0cd4a3c6f..38ec45c7578d 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -72,6 +72,9 @@ struct ospf_if_params { DECLARE_IF_PARAM(uint32_t, v_wait); /* Router Dead Interval */ bool is_v_wait_set; /* Check for Dead Interval set */ + /* GR Hello Delay Interval */ + DECLARE_IF_PARAM(uint16_t, v_gr_hello_delay); + /* MTU mismatch check (see RFC2328, chap 10.6) */ DECLARE_IF_PARAM(uint8_t, mtu_ignore); @@ -106,6 +109,12 @@ struct ospf_if_params { /* point-to-point DMVPN configuration */ uint8_t ptp_dmvpn; + + /* point-to-multipoint delayed reflooding configuration */ + bool p2mp_delay_reflood; + + /* Opaque LSA capability at interface level (see RFC5250) */ + DECLARE_IF_PARAM(bool, opaque_capable); }; enum { MEMBER_ALLROUTERS = 0, @@ -121,6 +130,9 @@ struct ospf_if_info { membership_counts[MEMBER_MAX]; /* multicast group refcnts */ uint32_t curr_mtu; + + /* Per-interface write socket, configured via 'ospf' object */ + int oii_fd; }; struct ospf_interface; @@ -171,6 +183,9 @@ struct ospf_interface { /* point-to-point DMVPN configuration */ uint8_t ptp_dmvpn; + /* point-to-multipoint delayed reflooding */ + bool p2mp_delay_reflood; + /* State of Interface State Machine. */ uint8_t state; @@ -211,6 +226,14 @@ struct ospf_interface { /* List of configured NBMA neighbor. */ struct list *nbr_nbma; + /* Graceful-Restart data. */ + struct { + struct { + uint16_t elapsed_seconds; + struct event *t_grace_send; + } hello_delay; + } gr; + /* self-originated LSAs. */ struct ospf_lsa *network_lsa_self; /* network-LSA. */ struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */ @@ -228,12 +251,12 @@ struct ospf_interface { uint32_t v_ls_ack; /* Delayed Link State Acknowledgment */ /* Threads. */ - struct thread *t_hello; /* timer */ - struct thread *t_wait; /* timer */ - struct thread *t_ls_ack; /* timer */ - struct thread *t_ls_ack_direct; /* event */ - struct thread *t_ls_upd_event; /* event */ - struct thread *t_opaque_lsa_self; /* Type-9 Opaque-LSAs */ + struct event *t_hello; /* timer */ + struct event *t_wait; /* timer */ + struct event *t_ls_ack; /* timer */ + struct event *t_ls_ack_direct; /* event */ + struct event *t_ls_upd_event; /* event */ + struct event *t_opaque_lsa_self; /* Type-9 Opaque-LSAs */ int on_write_q; diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index 173ebdf20731..2516fa75db43 100644 --- a/ospfd/ospf_ism.c +++ b/ospfd/ospf_ism.c @@ -7,7 +7,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "prefix.h" #include "if.h" @@ -237,13 +237,17 @@ int ospf_dr_election(struct ospf_interface *oi) } -void ospf_hello_timer(struct thread *thread) +void ospf_hello_timer(struct event *thread) { struct ospf_interface *oi; - oi = THREAD_ARG(thread); + oi = EVENT_ARG(thread); oi->t_hello = NULL; + /* Check if the GR hello-delay is active. */ + if (oi->gr.hello_delay.t_grace_send) + return; + if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) zlog_debug("ISM[%s]: Timer (Hello timer expire)", IF_NAME(oi)); @@ -254,11 +258,11 @@ void ospf_hello_timer(struct thread *thread) OSPF_HELLO_TIMER_ON(oi); } -static void ospf_wait_timer(struct thread *thread) +static void ospf_wait_timer(struct event *thread) { struct ospf_interface *oi; - oi = THREAD_ARG(thread); + oi = EVENT_ARG(thread); oi->t_wait = NULL; if (IS_DEBUG_OSPF(ism, ISM_TIMERS)) @@ -279,16 +283,18 @@ static void ism_timer_set(struct ospf_interface *oi) interface parameters must be set to initial values, and timers are reset also. */ - THREAD_OFF(oi->t_hello); - THREAD_OFF(oi->t_wait); - THREAD_OFF(oi->t_ls_ack); + EVENT_OFF(oi->t_hello); + EVENT_OFF(oi->t_wait); + EVENT_OFF(oi->t_ls_ack); + EVENT_OFF(oi->gr.hello_delay.t_grace_send); break; case ISM_Loopback: /* In this state, the interface may be looped back and will be unavailable for regular data traffic. */ - THREAD_OFF(oi->t_hello); - THREAD_OFF(oi->t_wait); - THREAD_OFF(oi->t_ls_ack); + EVENT_OFF(oi->t_hello); + EVENT_OFF(oi->t_wait); + EVENT_OFF(oi->t_ls_ack); + EVENT_OFF(oi->gr.hello_delay.t_grace_send); break; case ISM_Waiting: /* The router is trying to determine the identity of DRouter and @@ -298,7 +304,7 @@ static void ism_timer_set(struct ospf_interface *oi) OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); OSPF_ISM_TIMER_ON(oi->t_wait, ospf_wait_timer, OSPF_IF_PARAM(oi, v_wait)); - THREAD_OFF(oi->t_ls_ack); + EVENT_OFF(oi->t_ls_ack); break; case ISM_PointToPoint: /* The interface connects to a physical Point-to-point network @@ -307,7 +313,7 @@ static void ism_timer_set(struct ospf_interface *oi) neighboring router. Hello packets are also sent. */ /* send first hello immediately */ OSPF_ISM_TIMER_MSEC_ON(oi->t_hello, ospf_hello_timer, 1); - THREAD_OFF(oi->t_wait); + EVENT_OFF(oi->t_wait); OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); break; @@ -317,7 +323,7 @@ static void ism_timer_set(struct ospf_interface *oi) and the router itself is neither Designated Router nor Backup Designated Router. */ OSPF_HELLO_TIMER_ON(oi); - THREAD_OFF(oi->t_wait); + EVENT_OFF(oi->t_wait); OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); break; @@ -326,7 +332,7 @@ static void ism_timer_set(struct ospf_interface *oi) network, and the router is Backup Designated Router. */ OSPF_HELLO_TIMER_ON(oi); - THREAD_OFF(oi->t_wait); + EVENT_OFF(oi->t_wait); OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); break; @@ -335,7 +341,7 @@ static void ism_timer_set(struct ospf_interface *oi) network, and the router is Designated Router. */ OSPF_HELLO_TIMER_ON(oi); - THREAD_OFF(oi->t_wait); + EVENT_OFF(oi->t_wait); OSPF_ISM_TIMER_ON(oi->t_ls_ack, ospf_ls_ack_timer, oi->v_ls_ack); break; @@ -560,14 +566,14 @@ static void ism_change_state(struct ospf_interface *oi, int state) } /* Execute ISM event process. */ -void ospf_ism_event(struct thread *thread) +void ospf_ism_event(struct event *thread) { int event; int next_state; struct ospf_interface *oi; - oi = THREAD_ARG(thread); - event = THREAD_VAL(thread); + oi = EVENT_ARG(thread); + event = EVENT_VAL(thread); /* Call function. */ next_state = (*(ISM[oi->state][event].func))(oi); diff --git a/ospfd/ospf_ism.h b/ospfd/ospf_ism.h index d0516f61a139..426dda77336a 100644 --- a/ospfd/ospf_ism.h +++ b/ospfd/ospf_ism.h @@ -39,15 +39,15 @@ oi->on_write_q = 1; \ } \ if (!list_isempty((O)->oi_write_q)) \ - thread_add_write(master, ospf_write, (O), (O)->fd, \ - &(O)->t_write); \ + event_add_write(master, ospf_write, (O), (O)->fd, \ + &(O)->t_write); \ } while (0) /* Macro for OSPF ISM timer turn on. */ -#define OSPF_ISM_TIMER_ON(T, F, V) thread_add_timer(master, (F), oi, (V), &(T)) +#define OSPF_ISM_TIMER_ON(T, F, V) event_add_timer(master, (F), oi, (V), &(T)) #define OSPF_ISM_TIMER_MSEC_ON(T, F, V) \ - thread_add_timer_msec(master, (F), oi, (V), &(T)) + event_add_timer_msec(master, (F), oi, (V), &(T)) /* convenience macro to set hello timer correctly, according to * whether fast-hello is set or not @@ -65,16 +65,16 @@ /* Macro for OSPF schedule event. */ #define OSPF_ISM_EVENT_SCHEDULE(I, E) \ - thread_add_event(master, ospf_ism_event, (I), (E), NULL) + event_add_event(master, ospf_ism_event, (I), (E), NULL) /* Macro for OSPF execute event. */ #define OSPF_ISM_EVENT_EXECUTE(I, E) \ - thread_execute(master, ospf_ism_event, (I), (E)) + event_execute(master, ospf_ism_event, (I), (E)) /* Prototypes. */ -extern void ospf_ism_event(struct thread *thread); +extern void ospf_ism_event(struct event *thread); extern void ism_change_status(struct ospf_interface *, int); -extern void ospf_hello_timer(struct thread *thread); +extern void ospf_hello_timer(struct event *thread); extern int ospf_dr_election(struct ospf_interface *oi); DECLARE_HOOK(ospf_ism_change, diff --git a/ospfd/ospf_ldp_sync.c b/ospfd/ospf_ldp_sync.c index d3da5003aa93..4aab880d2280 100644 --- a/ospfd/ospf_ldp_sync.c +++ b/ospfd/ospf_ldp_sync.c @@ -9,7 +9,7 @@ #include "monotime.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "table.h" #include "vty.h" @@ -190,7 +190,7 @@ void ospf_ldp_sync_if_complete(struct interface *ifp) if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED) { if (ldp_sync_info->state == LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP) ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_UP; - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); ospf_if_recalculate_output_cost(ifp); } } @@ -241,7 +241,7 @@ void ospf_ldp_sync_ldp_fail(struct interface *ifp) if (ldp_sync_info && ldp_sync_info->enabled == LDP_IGP_SYNC_ENABLED && ldp_sync_info->state != LDP_IGP_SYNC_STATE_NOT_REQUIRED) { - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); ldp_sync_info->state = LDP_IGP_SYNC_STATE_REQUIRED_NOT_UP; ospf_if_recalculate_output_cost(ifp); } @@ -305,7 +305,7 @@ void ospf_ldp_sync_if_remove(struct interface *ifp, bool remove) */ ols_debug("%s: Removed from if %s", __func__, ifp->name); - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; ospf_if_recalculate_output_cost(ifp); @@ -339,7 +339,7 @@ static int ospf_ldp_sync_ism_change(struct ospf_interface *oi, int state, /* * LDP-SYNC holddown timer routines */ -static void ospf_ldp_sync_holddown_timer(struct thread *thread) +static void ospf_ldp_sync_holddown_timer(struct event *thread) { struct interface *ifp; struct ospf_if_params *params; @@ -349,7 +349,7 @@ static void ospf_ldp_sync_holddown_timer(struct thread *thread) * didn't receive msg from LDP indicating sync-complete * restore interface cost to original value */ - ifp = THREAD_ARG(thread); + ifp = EVENT_ARG(thread); params = IF_DEF_PARAMS(ifp); if (params->ldp_sync_info) { ldp_sync_info = params->ldp_sync_info; @@ -383,9 +383,8 @@ void ospf_ldp_sync_holddown_timer_add(struct interface *ifp) ols_debug("%s: start holddown timer for %s time %d", __func__, ifp->name, ldp_sync_info->holddown); - thread_add_timer(master, ospf_ldp_sync_holddown_timer, - ifp, ldp_sync_info->holddown, - &ldp_sync_info->t_holddown); + event_add_timer(master, ospf_ldp_sync_holddown_timer, ifp, + ldp_sync_info->holddown, &ldp_sync_info->t_holddown); } /* @@ -638,6 +637,9 @@ static int show_ip_ospf_mpls_ldp_interface_common(struct vty *vty, rn = route_next(rn)) { oi = rn->info; + if (oi == NULL) + continue; + if (use_json) { json_interface_sub = json_object_new_object(); @@ -673,6 +675,9 @@ static int show_ip_ospf_mpls_ldp_interface_common(struct vty *vty, rn = route_next(rn)) { oi = rn->info; + if (oi == NULL) + continue; + if (use_json) json_interface_sub = json_object_new_object(); @@ -899,7 +904,7 @@ DEFPY (no_mpls_ldp_sync, SET_FLAG(ldp_sync_info->flags, LDP_SYNC_FLAG_IF_CONFIG); ldp_sync_info->enabled = LDP_IGP_SYNC_DEFAULT; ldp_sync_info->state = LDP_IGP_SYNC_STATE_NOT_REQUIRED; - THREAD_OFF(ldp_sync_info->t_holddown); + EVENT_OFF(ldp_sync_info->t_holddown); ospf_if_recalculate_output_cost(ifp); return CMD_SUCCESS; diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 49e342d72b56..27e7e027597f 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -14,7 +14,7 @@ #include "memory.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "checksum.h" @@ -38,7 +38,7 @@ #include "ospfd/ospf_abr.h" #include "ospfd/ospf_errors.h" -static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf *ospf, +static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf_area *area, struct prefix_ipv4 *p, uint8_t type, uint32_t metric, @@ -608,7 +608,8 @@ static int lsa_link_loopback_set(struct stream **s, struct ospf_interface *oi) mask.s_addr = 0xffffffff; id.s_addr = oi->address->u.prefix4.s_addr; - return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); + return link_info_set(s, id, mask, LSA_LINK_TYPE_STUB, 0, + oi->output_cost); } /* Describe Virtual Link. */ @@ -742,9 +743,9 @@ void ospf_router_lsa_body_set(struct stream **s, struct ospf_area *area) stream_putw_at(*s, putp, cnt); } -static void ospf_stub_router_timer(struct thread *t) +static void ospf_stub_router_timer(struct event *t) { - struct ospf_area *area = THREAD_ARG(t); + struct ospf_area *area = EVENT_ARG(t); area->t_stub_router = NULL; @@ -1283,23 +1284,24 @@ ospf_summary_lsa_prepare_and_flood(struct prefix_ipv4 *p, uint32_t metric, return new; } -static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf *ospf, +static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf_area *area, struct prefix_ipv4 *p, uint8_t type, uint32_t metric, struct in_addr old_id) { struct ospf_lsa *lsa = NULL; - struct ospf_lsa *new = NULL; + struct ospf_lsa *summary_lsa = NULL; struct summary_lsa *sl = NULL; struct ospf_area *old_area = NULL; + struct ospf *ospf = area->ospf; struct prefix_ipv4 old_prefix; uint32_t old_metric; struct in_addr mask; uint32_t metric_val; char *metric_buf; - lsa = ospf_lsdb_lookup_by_id(ospf->lsdb, type, p->prefix, + lsa = ospf_lsdb_lookup_by_id(area->lsdb, type, p->prefix, ospf->router_id); if (!lsa) { @@ -1327,19 +1329,19 @@ static struct ospf_lsa *ospf_handle_summarylsa_lsId_chg(struct ospf *ospf, if (type == OSPF_SUMMARY_LSA) { /*Refresh the LSA with new LSA*/ - ospf_summary_lsa_refresh(ospf, lsa); + summary_lsa = ospf_summary_lsa_refresh(ospf, lsa); - new = ospf_summary_lsa_prepare_and_flood( - &old_prefix, old_metric, old_area, old_id); + ospf_summary_lsa_prepare_and_flood(&old_prefix, old_metric, + old_area, old_id); } else { /*Refresh the LSA with new LSA*/ - ospf_summary_asbr_lsa_refresh(ospf, lsa); + summary_lsa = ospf_summary_asbr_lsa_refresh(ospf, lsa); - new = ospf_asbr_summary_lsa_prepare_and_flood( - &old_prefix, old_metric, old_area, old_id); + ospf_asbr_summary_lsa_prepare_and_flood(&old_prefix, old_metric, + old_area, old_id); } - return new; + return summary_lsa; } /* Originate Summary-LSA. */ @@ -1358,8 +1360,8 @@ struct ospf_lsa *ospf_summary_lsa_originate(struct prefix_ipv4 *p, if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) zlog_debug("Link ID has to be changed."); - new = ospf_handle_summarylsa_lsId_chg( - area->ospf, p, OSPF_SUMMARY_LSA, metric, id); + new = ospf_handle_summarylsa_lsId_chg(area, p, OSPF_SUMMARY_LSA, + metric, id); return new; } else if (status == LSID_NOT_AVAILABLE) { /* Link State ID not available. */ @@ -1521,7 +1523,7 @@ struct ospf_lsa *ospf_summary_asbr_lsa_originate(struct prefix_ipv4 *p, zlog_debug("Link ID has to be changed."); new = ospf_handle_summarylsa_lsId_chg( - area->ospf, p, OSPF_ASBR_SUMMARY_LSA, metric, id); + area, p, OSPF_ASBR_SUMMARY_LSA, metric, id); return new; } else if (status == LSID_NOT_AVAILABLE) { /* Link State ID not available. */ @@ -1653,9 +1655,6 @@ struct in_addr ospf_get_nssa_ip(struct ospf_area *area) if (best_default.s_addr != INADDR_ANY) return best_default; - if (best_default.s_addr != INADDR_ANY) - return best_default; - return fwd; } @@ -1867,8 +1866,7 @@ static struct ospf_lsa *ospf_external_lsa_new(struct ospf *ospf, } /* As Type-7 */ -static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa, - struct external_info *ei) +static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa) { struct ospf_lsa *new; struct as_external_lsa *extlsa; @@ -1978,6 +1976,9 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, ei.nexthop = ext->header.adv_router; ei.route_map_set.metric = -1; ei.route_map_set.metric_type = -1; + ei.metric = DEFAULT_DEFAULT_METRIC; + ei.max_metric = OSPF_LS_INFINITY; + ei.min_metric = 0; ei.tag = 0; ei.instance = 0; @@ -2006,7 +2007,6 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf, /* add translated flag, checksum and lock new lsa */ SET_FLAG(new->flags, OSPF_LSA_LOCAL_XLT); /* Translated from 7 */ - new = ospf_lsa_lock(new); return new; } @@ -2016,7 +2016,7 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, struct ospf_lsa *type7, struct ospf_lsa *type5) { - struct ospf_lsa *new; + struct ospf_lsa *new, *translated_lsa; struct as_external_lsa *extnew; if (ospf->gr_info.restart_in_progress) { @@ -2030,7 +2030,8 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, * the OSPF_LSA_LOCAL_XLT flag, must originate by hand */ - if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) { + if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) == + NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( "%s: Could not translate Type-7, Id %pI4, to Type-5", @@ -2038,16 +2039,17 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf, return NULL; } - extnew = (struct as_external_lsa *)new->data; + extnew = (struct as_external_lsa *)translated_lsa->data; /* Update LSA sequence number from translated Type-5 LSA */ if (type5) - new->data->ls_seqnum = lsa_seqnum_increment(type5); + translated_lsa->data->ls_seqnum = lsa_seqnum_increment(type5); - if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) { + if ((new = ospf_lsa_install(ospf, NULL, translated_lsa)) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: Could not install LSA id %pI4", __func__, &type7->data->id); + ospf_lsa_free(translated_lsa); return NULL; } @@ -2070,7 +2072,7 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, struct ospf_lsa *type7, struct ospf_lsa *type5) { - struct ospf_lsa *new = NULL; + struct ospf_lsa *new = NULL, *translated_lsa = NULL; struct as_external_lsa *extold = NULL; uint32_t ls_seqnum = 0; @@ -2146,7 +2148,8 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, ospf_ls_retransmit_delete_nbr_as(ospf, type5); /* create new translated LSA */ - if ((new = ospf_lsa_translated_nssa_new(ospf, type7)) == NULL) { + if ((translated_lsa = ospf_lsa_translated_nssa_new(ospf, type7)) == + NULL) { if (IS_DEBUG_OSPF_NSSA) zlog_debug( "%s: Could not translate Type-7 for %pI4 to Type-5", @@ -2156,13 +2159,14 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf, if (type7->area->suppress_fa == 1) { if (extold->e[0].fwd_addr.s_addr == 0) - new->data->ls_seqnum = htonl(ls_seqnum + 1); + translated_lsa->data->ls_seqnum = htonl(ls_seqnum + 1); } - if (!(new = ospf_lsa_install(ospf, NULL, new))) { + if (!(new = ospf_lsa_install(ospf, NULL, translated_lsa))) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "%s: Could not install translated LSA, Id %pI4", __func__, &type7->data->id); + ospf_lsa_free(translated_lsa); return NULL; } @@ -2252,7 +2256,7 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, /* stay away from translated LSAs! */ !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT))) ospf_install_flood_nssa( - ospf, new, ei); /* Install/Flood Type-7 to all NSSAs */ + ospf, new); /* Install/Flood Type-7 to all NSSAs */ /* Debug logging. */ if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { @@ -2265,6 +2269,100 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf, return new; } +/* Originate an NSSA-LSA, install and flood. */ +struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, + struct external_info *ei) +{ + struct ospf *ospf = area->ospf; + struct ospf_lsa *new; + + if (ospf->gr_info.restart_in_progress) { + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) + zlog_debug( + "LSA[Type7]: Graceful Restart in progress, don't originate"); + return NULL; + } + + if (ospf->router_id.s_addr == INADDR_ANY) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: deferring NSSA-LSA origination, router ID is zero", + &ei->p.prefix); + return NULL; + } + + /* Create new NSSA-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: Could not originate NSSA-LSA", + &ei->p.prefix); + return NULL; + } + new->data->type = OSPF_AS_NSSA_LSA; + new->area = area; + + /* Install newly created LSA into Type-7 LSDB. */ + ospf_lsa_install(ospf, NULL, new); + + /* Update LSA origination count. */ + ospf->lsa_originate_count++; + + /* Flooding new LSA */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: Originate NSSA-LSA %p", + new->data->type, &new->data->id, (void *)new); + ospf_lsa_header_dump(new->data); + } + + return new; +} + +/* Refresh NSSA-LSA. */ +struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, + struct ospf_lsa *lsa, + struct external_info *ei) +{ + struct ospf *ospf = area->ospf; + struct ospf_lsa *new; + + /* Delete LSA from neighbor retransmit-list. */ + ospf_ls_retransmit_delete_nbr_as(ospf, lsa); + + /* Unregister AS-external-LSA from refresh-list. */ + ospf_refresher_unregister_lsa(ospf, lsa); + + /* Create new NSSA-LSA instance. */ + if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "LSA[Type7:%pI4]: Could not originate NSSA-LSA", + &ei->p.prefix); + return NULL; + } + new->data->type = OSPF_AS_NSSA_LSA; + new->data->ls_seqnum = lsa_seqnum_increment(lsa); + new->area = area; + + /* Install newly created LSA into Type-7 LSDB. */ + ospf_lsa_install(ospf, NULL, new); + + /* Flooding new LSA */ + ospf_flood_through_area(area, NULL, new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) { + zlog_debug("LSA[Type%d:%pI4]: NSSA-LSA refresh", + new->data->type, &new->data->id); + ospf_lsa_header_dump(new->data); + } + + return new; +} + static struct external_info *ospf_default_external_info(struct ospf *ospf) { int type; @@ -2610,8 +2708,8 @@ struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf, /* If any attached NSSA, install as Type-7, flood to all NSSA Areas */ if (ospf->anyNSSA && !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT))) - ospf_install_flood_nssa(ospf, new, - ei); /* Install/Flood per new rules */ + ospf_install_flood_nssa(ospf, + new); /* Install/Flood per new rules */ /* Register self-originated LSA to refresh queue. * Translated LSAs should not be registered, but refreshed upon @@ -3041,9 +3139,9 @@ int ospf_check_nbr_status(struct ospf *ospf) } -void ospf_maxage_lsa_remover(struct thread *thread) +void ospf_maxage_lsa_remover(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); struct ospf_lsa *lsa, *old; struct route_node *rn; int reschedule = 0; @@ -3073,7 +3171,7 @@ void ospf_maxage_lsa_remover(struct thread *thread) } /* TODO: maybe convert this function to a work-queue */ - if (thread_should_yield(thread)) { + if (event_should_yield(thread)) { OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, 0); route_unlock_node( @@ -3289,9 +3387,9 @@ static int ospf_lsa_maxage_walker_remover(struct ospf *ospf, } /* Periodical check of MaxAge LSA. */ -void ospf_lsa_maxage_walker(struct thread *thread) +void ospf_lsa_maxage_walker(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); struct route_node *rn; struct ospf_lsa *lsa; struct ospf_area *area; @@ -3651,8 +3749,8 @@ void ospf_flush_self_originated_lsas_now(struct ospf *ospf) * without conflicting to other threads. */ if (ospf->t_maxage != NULL) { - THREAD_OFF(ospf->t_maxage); - thread_execute(master, ospf_maxage_lsa_remover, ospf, 0); + EVENT_OFF(ospf->t_maxage); + event_execute(master, ospf_maxage_lsa_remover, ospf, 0); } return; @@ -3837,11 +3935,11 @@ struct lsa_action { struct ospf_lsa *lsa; }; -static void ospf_lsa_action(struct thread *t) +static void ospf_lsa_action(struct event *t) { struct lsa_action *data; - data = THREAD_ARG(t); + data = EVENT_ARG(t); if (IS_DEBUG_OSPF(lsa, LSA) == OSPF_DEBUG_LSA) zlog_debug("LSA[Action]: Performing scheduled LSA action: %d", @@ -3869,7 +3967,7 @@ void ospf_schedule_lsa_flood_area(struct ospf_area *area, struct ospf_lsa *lsa) data->area = area; data->lsa = ospf_lsa_lock(lsa); /* Message / Flood area */ - thread_add_event(master, ospf_lsa_action, data, 0, NULL); + event_add_event(master, ospf_lsa_action, data, 0, NULL); } void ospf_schedule_lsa_flush_area(struct ospf_area *area, struct ospf_lsa *lsa) @@ -3881,7 +3979,7 @@ void ospf_schedule_lsa_flush_area(struct ospf_area *area, struct ospf_lsa *lsa) data->area = area; data->lsa = ospf_lsa_lock(lsa); /* Message / Flush area */ - thread_add_event(master, ospf_lsa_action, data, 0, NULL); + event_add_event(master, ospf_lsa_action, data, 0, NULL); } @@ -4028,11 +4126,11 @@ void ospf_refresher_unregister_lsa(struct ospf *ospf, struct ospf_lsa *lsa) } } -void ospf_lsa_refresh_walker(struct thread *t) +void ospf_lsa_refresh_walker(struct event *t) { struct list *refresh_list; struct listnode *node, *nnode; - struct ospf *ospf = THREAD_ARG(t); + struct ospf *ospf = EVENT_ARG(t); struct ospf_lsa *lsa; int i; struct list *lsa_to_refresh = list_new(); @@ -4090,19 +4188,19 @@ void ospf_lsa_refresh_walker(struct thread *t) } ospf->t_lsa_refresher = NULL; - thread_add_timer(master, ospf_lsa_refresh_walker, ospf, - ospf->lsa_refresh_interval, &ospf->t_lsa_refresher); + event_add_timer(master, ospf_lsa_refresh_walker, ospf, + ospf->lsa_refresh_interval, &ospf->t_lsa_refresher); ospf->lsa_refresher_started = monotime(NULL); for (ALL_LIST_ELEMENTS(lsa_to_refresh, node, nnode, lsa)) { dna_lsa = ospf_check_dna_lsa(lsa); if (!dna_lsa) { /* refresh only non-DNA LSAs */ ospf_lsa_refresh(ospf, lsa); - assert(lsa->lock > 0); - ospf_lsa_unlock(&lsa); /* lsa_refresh_queue & temp for - * lsa_to_refresh. - */ } + assert(lsa->lock > 0); + ospf_lsa_unlock(&lsa); /* lsa_refresh_queue & temp for + * lsa_to_refresh. + */ } list_delete(&lsa_to_refresh); diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 3c7ea3fda50f..d5ca0694ccb2 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -278,6 +278,11 @@ 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 struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area, + struct external_info *ei); +extern struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area, + struct ospf_lsa *lsa, + struct external_info *ei); 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, @@ -300,7 +305,7 @@ extern struct ospf_lsa *ospf_lsa_lookup_by_prefix(struct ospf_lsdb *, uint8_t, extern void ospf_lsa_maxage(struct ospf *, struct ospf_lsa *); extern uint32_t get_metric(uint8_t *); -extern void ospf_lsa_maxage_walker(struct thread *thread); +extern void ospf_lsa_maxage_walker(struct event *thread); extern struct ospf_lsa *ospf_lsa_refresh(struct ospf *, struct ospf_lsa *); extern void ospf_external_lsa_refresh_default(struct ospf *); @@ -320,7 +325,7 @@ extern void ospf_schedule_lsa_flush_area(struct ospf_area *, struct ospf_lsa *); extern void ospf_refresher_register_lsa(struct ospf *, struct ospf_lsa *); extern void ospf_refresher_unregister_lsa(struct ospf *, struct ospf_lsa *); -extern void ospf_lsa_refresh_walker(struct thread *thread); +extern void ospf_lsa_refresh_walker(struct event *thread); extern void ospf_lsa_maxage_delete(struct ospf *, struct ospf_lsa *); @@ -346,7 +351,7 @@ extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, struct ospf_lsa *lsa); extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); -extern void ospf_maxage_lsa_remover(struct thread *thread); +extern void ospf_maxage_lsa_remover(struct event *thread); extern bool ospf_check_dna_lsa(const struct ospf_lsa *lsa); extern void ospf_refresh_area_self_lsas(struct ospf_area *area); diff --git a/ospfd/ospf_main.c b/ospfd/ospf_main.c index 05fc5c95d1ec..536bd592d238 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -9,7 +9,7 @@ #include <lib/version.h> #include "bfd.h" #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "linklist.h" #include "if.h" @@ -70,7 +70,7 @@ const struct option longopts[] = { /* OSPFd program name */ /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; #ifdef SUPPORT_OSPF_API extern int ospf_apiserver_enable; @@ -134,6 +134,32 @@ FRR_DAEMON_INFO(ospfd, OSPF, .vty_port = OSPF_VTY_PORT, .n_yang_modules = array_size(ospfd_yang_modules), ); +/** Max wait time for config to load before accepting hellos */ +#define OSPF_PRE_CONFIG_MAX_WAIT_SECONDS 600 + +static void ospf_config_finish(struct event *t) +{ + zlog_err("OSPF configuration end timer expired after %d seconds.", + OSPF_PRE_CONFIG_MAX_WAIT_SECONDS); +} + +static void ospf_config_start(void) +{ + EVENT_OFF(t_ospf_cfg); + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospfd config start callback received."); + event_add_timer(master, ospf_config_finish, NULL, + OSPF_PRE_CONFIG_MAX_WAIT_SECONDS, &t_ospf_cfg); +} + +static void ospf_config_end(void) +{ + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospfd config end callback received."); + + EVENT_OFF(t_ospf_cfg); +} + /* OSPFd main routine. */ int main(int argc, char **argv) { @@ -193,6 +219,9 @@ int main(int argc, char **argv) access_list_init(); prefix_list_init(); + /* Configuration processing callback initialization. */ + cmd_init_config_callbacks(ospf_config_start, ospf_config_end); + /* OSPFd inits. */ ospf_if_init(); ospf_zebra_init(master, ospf_instance); diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index 8338c430772f..c238f051dfeb 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -11,7 +11,7 @@ #include "prefix.h" #include "memory.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "stream.h" #include "table.h" #include "log.h" @@ -125,17 +125,17 @@ void ospf_nbr_free(struct ospf_neighbor *nbr) } /* Cancel all timers. */ - THREAD_OFF(nbr->t_inactivity); - THREAD_OFF(nbr->t_db_desc); - THREAD_OFF(nbr->t_ls_req); - THREAD_OFF(nbr->t_ls_upd); + EVENT_OFF(nbr->t_inactivity); + EVENT_OFF(nbr->t_db_desc); + EVENT_OFF(nbr->t_ls_req); + EVENT_OFF(nbr->t_ls_upd); /* Cancel all events. */ /* Thread lookup cost would be negligible. */ - thread_cancel_event(master, nbr); + event_cancel_event(master, nbr); bfd_sess_free(&nbr->bfd_session); - THREAD_OFF(nbr->gr_helper_info.t_grace_timer); + EVENT_OFF(nbr->gr_helper_info.t_grace_timer); nbr->oi = NULL; XFREE(MTYPE_OSPF_NEIGHBOR, nbr); @@ -441,7 +441,7 @@ static struct ospf_neighbor *ospf_nbr_add(struct ospf_interface *oi, nbr->nbr_nbma = nbr_nbma; if (nbr_nbma->t_poll) - THREAD_OFF(nbr_nbma->t_poll); + EVENT_OFF(nbr_nbma->t_poll); nbr->state_change = nbr_nbma->state_change + 1; } diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h index 7bd5b1209d31..07d095f03d1b 100644 --- a/ospfd/ospf_neighbor.h +++ b/ospfd/ospf_neighbor.h @@ -57,11 +57,11 @@ struct ospf_neighbor { uint32_t v_ls_upd; /* Threads. */ - struct thread *t_inactivity; - struct thread *t_db_desc; - struct thread *t_ls_req; - struct thread *t_ls_upd; - struct thread *t_hello_reply; + struct event *t_inactivity; + struct event *t_db_desc; + struct event *t_ls_req; + struct event *t_ls_upd; + struct event *t_hello_reply; /* NBMA configured neighbour */ struct ospf_nbr_nbma *nbr_nbma; diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index e89ad020b18d..801f75ad1853 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -6,7 +6,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "prefix.h" #include "if.h" @@ -15,6 +15,7 @@ #include "sockopt.h" #include "privs.h" #include "lib_errors.h" +#include "lib/table.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_network.h" @@ -111,7 +112,7 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, "can't setsockopt IP_DROP_MEMBERSHIP (fd %d, addr %pI4, ifindex %u, AllDRouters): %s", top->fd, &p->u.prefix4, ifindex, safe_strerror(errno)); - else + else if (IS_DEBUG_OSPF_EVENT) zlog_debug( "interface %pI4 [%u] leave AllDRouters Multicast group.", &p->u.prefix4, ifindex); @@ -119,62 +120,60 @@ int ospf_if_drop_alldrouters(struct ospf *top, struct prefix *p, return ret; } -int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex) +int ospf_if_ipmulticast(int fd, struct prefix *p, ifindex_t ifindex) { uint8_t val; int ret, len; /* Prevent receiving self-origined multicast packets. */ - ret = setsockopt_ipv4_multicast_loop(top->fd, 0); + ret = setsockopt_ipv4_multicast_loop(fd, 0); if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_LOOP(0) for fd %d: %s", - top->fd, safe_strerror(errno)); + fd, safe_strerror(errno)); /* Explicitly set multicast ttl to 1 -- endo. */ val = 1; len = sizeof(val); - ret = setsockopt(top->fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, - len); + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&val, len); if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_TTL(1) for fd %d: %s", - top->fd, safe_strerror(errno)); + fd, safe_strerror(errno)); #ifndef GNU_LINUX /* For GNU LINUX ospf_write uses IP_PKTINFO, in_pktinfo to send * packet out of ifindex. Below would be used Non Linux system. */ - ret = setsockopt_ipv4_multicast_if(top->fd, p->u.prefix4, ifindex); + ret = setsockopt_ipv4_multicast_if(fd, p->u.prefix4, ifindex); if (ret < 0) flog_err(EC_LIB_SOCKET, "can't setsockopt IP_MULTICAST_IF(fd %d, addr %pI4, ifindex %u): %s", - top->fd, &p->u.prefix4, ifindex, + fd, &p->u.prefix4, ifindex, safe_strerror(errno)); #endif return ret; } -int ospf_sock_init(struct ospf *ospf) +/* + * Helper to open and set up a socket; returns the new fd on success, + * -1 on error. + */ +static int sock_init_common(vrf_id_t vrf_id, const char *name, int proto, + int *pfd) { int ospf_sock; int ret, hincl = 1; - int bufsize = (8 * 1024 * 1024); - /* silently ignore. already done */ - if (ospf->fd > 0) - return -1; - - if (ospf->vrf_id == VRF_UNKNOWN) { + if (vrf_id == VRF_UNKNOWN) { /* silently return since VRF is not ready */ return -1; } + frr_with_privs(&ospfd_privs) { - ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, - ospf->vrf_id, ospf->name); + ospf_sock = vrf_socket(AF_INET, SOCK_RAW, proto, vrf_id, name); if (ospf_sock < 0) { - flog_err(EC_LIB_SOCKET, - "ospf_read_sock_init: socket: %s", + flog_err(EC_LIB_SOCKET, "%s: socket: %s", __func__, safe_strerror(errno)); return -1; } @@ -213,9 +212,112 @@ int ospf_sock_init(struct ospf *ospf) ospf_sock); } - setsockopt_so_sendbuf(ospf_sock, bufsize); - setsockopt_so_recvbuf(ospf_sock, bufsize); + *pfd = ospf_sock; - ospf->fd = ospf_sock; return ret; } + +/* + * Update a socket bufsize(s), based on its ospf instance + */ +void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, + enum ospf_sock_type_e type) +{ + int bufsize; + + if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_RECV) { + bufsize = ospf->recv_sock_bufsize; + setsockopt_so_recvbuf(sock, bufsize); + } + + if (type == OSPF_SOCK_BOTH || type == OSPF_SOCK_SEND) { + bufsize = ospf->send_sock_bufsize; + setsockopt_so_sendbuf(sock, bufsize); + } +} + +int ospf_sock_init(struct ospf *ospf) +{ + int ret; + + /* silently ignore. already done */ + if (ospf->fd > 0) + return -1; + + ret = sock_init_common(ospf->vrf_id, ospf->name, IPPROTO_OSPFIGP, + &(ospf->fd)); + + if (ret >= 0) /* Update socket buffer sizes */ + ospf_sock_bufsize_update(ospf, ospf->fd, OSPF_SOCK_BOTH); + + return ret; +} + +/* + * Open per-interface write socket + */ +int ospf_ifp_sock_init(struct interface *ifp) +{ + struct ospf_if_info *oii; + struct ospf_interface *oi = NULL; + struct ospf *ospf = NULL; + struct route_node *rn; + int ret; + + oii = IF_OSPF_IF_INFO(ifp); + if (oii == NULL) + return -1; + + if (oii->oii_fd > 0) + return 0; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + if (rn && rn->info) { + oi = rn->info; + ospf = oi->ospf; + break; + } + } + + if (ospf == NULL) + return -1; + + ret = sock_init_common(ifp->vrf->vrf_id, ifp->name, IPPROTO_OSPFIGP, + &oii->oii_fd); + + if (ret >= 0) { /* Update socket buffer sizes */ + /* Write-only, so no recv buf */ + setsockopt_so_recvbuf(oii->oii_fd, 0); + + ospf_sock_bufsize_update(ospf, oii->oii_fd, OSPF_SOCK_SEND); + } + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, ifp->name, + oii, oii->oii_fd); + + return ret; +} + +/* + * Close per-interface write socket + */ +int ospf_ifp_sock_close(struct interface *ifp) +{ + struct ospf_if_info *oii; + + oii = IF_OSPF_IF_INFO(ifp); + if (oii == NULL) + return 0; + + if (oii->oii_fd > 0) { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("%s: ifp %s, oii %p, fd %d", __func__, + ifp->name, oii, oii->oii_fd); + + close(oii->oii_fd); + oii->oii_fd = -1; + } + + return 0; +} diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index 33fd8980bff2..b810bad50bef 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -13,7 +13,20 @@ extern int ospf_if_drop_allspfrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t); -extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t); +extern int ospf_if_ipmulticast(int fd, struct prefix *, ifindex_t); extern int ospf_sock_init(struct ospf *ospf); +/* Open, close per-interface write socket */ +int ospf_ifp_sock_init(struct interface *ifp); +int ospf_ifp_sock_close(struct interface *ifp); + +enum ospf_sock_type_e { + OSPF_SOCK_NONE = 0, + OSPF_SOCK_RECV, + OSPF_SOCK_SEND, + OSPF_SOCK_BOTH +}; + +void ospf_sock_bufsize_update(const struct ospf *ospf, int sock, + enum ospf_sock_type_e type); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospf_nsm.c b/ospfd/ospf_nsm.c index 65c7720481ba..bcbe02879575 100644 --- a/ospfd/ospf_nsm.c +++ b/ospfd/ospf_nsm.c @@ -7,7 +7,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "hash.h" #include "linklist.h" @@ -44,11 +44,11 @@ DEFINE_HOOK(ospf_nsm_change, static void nsm_clear_adj(struct ospf_neighbor *); /* OSPF NSM Timer functions. */ -static void ospf_inactivity_timer(struct thread *thread) +static void ospf_inactivity_timer(struct event *thread) { struct ospf_neighbor *nbr; - nbr = THREAD_ARG(thread); + nbr = EVENT_ARG(thread); nbr->t_inactivity = NULL; if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) @@ -71,11 +71,11 @@ static void ospf_inactivity_timer(struct thread *thread) } } -static void ospf_db_desc_timer(struct thread *thread) +static void ospf_db_desc_timer(struct event *thread) { struct ospf_neighbor *nbr; - nbr = THREAD_ARG(thread); + nbr = EVENT_ARG(thread); nbr->t_db_desc = NULL; if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) @@ -105,32 +105,32 @@ static void nsm_timer_set(struct ospf_neighbor *nbr) switch (nbr->state) { case NSM_Deleted: case NSM_Down: - THREAD_OFF(nbr->t_inactivity); - THREAD_OFF(nbr->t_hello_reply); + EVENT_OFF(nbr->t_inactivity); + EVENT_OFF(nbr->t_hello_reply); /* fallthru */ case NSM_Attempt: case NSM_Init: case NSM_TwoWay: - THREAD_OFF(nbr->t_db_desc); - THREAD_OFF(nbr->t_ls_upd); - THREAD_OFF(nbr->t_ls_req); + EVENT_OFF(nbr->t_db_desc); + EVENT_OFF(nbr->t_ls_upd); + EVENT_OFF(nbr->t_ls_req); break; case NSM_ExStart: OSPF_NSM_TIMER_ON(nbr->t_db_desc, ospf_db_desc_timer, nbr->v_db_desc); - THREAD_OFF(nbr->t_ls_upd); - THREAD_OFF(nbr->t_ls_req); + EVENT_OFF(nbr->t_ls_upd); + EVENT_OFF(nbr->t_ls_req); break; case NSM_Exchange: OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); if (!IS_SET_DD_MS(nbr->dd_flags)) - THREAD_OFF(nbr->t_db_desc); + EVENT_OFF(nbr->t_db_desc); break; case NSM_Loading: case NSM_Full: default: - THREAD_OFF(nbr->t_db_desc); + EVENT_OFF(nbr->t_db_desc); break; } } @@ -161,13 +161,13 @@ int nsm_should_adj(struct ospf_neighbor *nbr) static int nsm_hello_received(struct ospf_neighbor *nbr) { /* Start or Restart Inactivity Timer. */ - THREAD_OFF(nbr->t_inactivity); + EVENT_OFF(nbr->t_inactivity); OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, nbr->v_inactivity); if (nbr->oi->type == OSPF_IFTYPE_NBMA && nbr->nbr_nbma) - THREAD_OFF(nbr->nbr_nbma->t_poll); + EVENT_OFF(nbr->nbr_nbma->t_poll); /* Send proactive ARP requests */ if (nbr->state < NSM_Exchange) @@ -179,9 +179,9 @@ static int nsm_hello_received(struct ospf_neighbor *nbr) static int nsm_start(struct ospf_neighbor *nbr) { if (nbr->nbr_nbma) - THREAD_OFF(nbr->nbr_nbma->t_poll); + EVENT_OFF(nbr->nbr_nbma->t_poll); - THREAD_OFF(nbr->t_inactivity); + EVENT_OFF(nbr->t_inactivity); OSPF_NSM_TIMER_ON(nbr->t_inactivity, ospf_inactivity_timer, nbr->v_inactivity); @@ -629,12 +629,13 @@ 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 %pI4(%s) on %s: %s -> %s (%s)", - &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]); + zlog_notice( + "AdjChg: Nbr %pI4, NbrIP %pI4 (%s) on %s: %s -> %s (%s)", + &nbr->router_id, &nbr->src, + 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]); /* Advance in NSM */ if (next_state > nbr->state) @@ -790,14 +791,14 @@ static void nsm_change_state(struct ospf_neighbor *nbr, int state) } /* Execute NSM event process. */ -void ospf_nsm_event(struct thread *thread) +void ospf_nsm_event(struct event *thread) { int event; int next_state; struct ospf_neighbor *nbr; - nbr = THREAD_ARG(thread); - event = THREAD_VAL(thread); + nbr = EVENT_ARG(thread); + event = EVENT_VAL(thread); if (IS_DEBUG_OSPF(nsm, NSM_EVENTS)) zlog_debug("NSM[%s:%pI4:%s]: %s (%s)", IF_NAME(nbr->oi), diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h index b53a05914fe5..9973b4870d33 100644 --- a/ospfd/ospf_nsm.h +++ b/ospfd/ospf_nsm.h @@ -41,22 +41,22 @@ #define OSPF_NSM_EVENT_MAX 14 /* Macro for OSPF NSM timer turn on. */ -#define OSPF_NSM_TIMER_ON(T,F,V) thread_add_timer (master, (F), nbr, (V), &(T)) +#define OSPF_NSM_TIMER_ON(T, F, V) event_add_timer(master, (F), nbr, (V), &(T)) /* Macro for OSPF NSM schedule event. */ #define OSPF_NSM_EVENT_SCHEDULE(N, E) \ - thread_add_event(master, ospf_nsm_event, (N), (E), NULL) + event_add_event(master, ospf_nsm_event, (N), (E), NULL) /* Macro for OSPF NSM execute event. */ #define OSPF_NSM_EVENT_EXECUTE(N, E) \ - thread_execute(master, ospf_nsm_event, (N), (E)) + event_execute(master, ospf_nsm_event, (N), (E)) /* Prototypes. */ -extern void ospf_nsm_event(struct thread *); -extern void ospf_check_nbr_loading(struct ospf_neighbor *); -extern int ospf_db_summary_isempty(struct ospf_neighbor *); -extern int ospf_db_summary_count(struct ospf_neighbor *); -extern void ospf_db_summary_clear(struct ospf_neighbor *); +extern void ospf_nsm_event(struct event *e); +extern void ospf_check_nbr_loading(struct ospf_neighbor *nbr); +extern int ospf_db_summary_isempty(struct ospf_neighbor *nbr); +extern int ospf_db_summary_count(struct ospf_neighbor *nbr); +extern void ospf_db_summary_clear(struct ospf_neighbor *nbr); extern int nsm_should_adj(struct ospf_neighbor *nbr); DECLARE_HOOK(ospf_nsm_change, (struct ospf_neighbor * on, int state, int oldstate), diff --git a/ospfd/ospf_opaque.c b/ospfd/ospf_opaque.c index 2e8e48bb5a63..27f47a6d79a3 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -16,7 +16,7 @@ #include "vty.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "printfrr.h" @@ -117,6 +117,10 @@ void ospf_opaque_finish(void) ospf_ext_finish(); +#ifdef SUPPORT_OSPF_API + ospf_apiserver_term(); +#endif + ospf_sr_finish(); } @@ -133,7 +137,7 @@ int ospf_opaque_type9_lsa_init(struct ospf_interface *oi) void ospf_opaque_type9_lsa_term(struct ospf_interface *oi) { - THREAD_OFF(oi->t_opaque_lsa_self); + EVENT_OFF(oi->t_opaque_lsa_self); if (oi->opaque_lsa_self != NULL) list_delete(&oi->opaque_lsa_self); oi->opaque_lsa_self = NULL; @@ -162,7 +166,7 @@ void ospf_opaque_type10_lsa_term(struct ospf_area *area) area->lsdb->new_lsa_hook = area->lsdb->del_lsa_hook = NULL; #endif /* MONITOR_LSDB_CHANGE */ - THREAD_OFF(area->t_opaque_lsa_self); + EVENT_OFF(area->t_opaque_lsa_self); if (area->opaque_lsa_self != NULL) list_delete(&area->opaque_lsa_self); return; @@ -190,7 +194,7 @@ void ospf_opaque_type11_lsa_term(struct ospf *top) top->lsdb->new_lsa_hook = top->lsdb->del_lsa_hook = NULL; #endif /* MONITOR_LSDB_CHANGE */ - THREAD_OFF(top->t_opaque_lsa_self); + EVENT_OFF(top->t_opaque_lsa_self); if (top->opaque_lsa_self != NULL) list_delete(&top->opaque_lsa_self); return; @@ -475,7 +479,7 @@ struct opaque_info_per_type { * to (re-)originate their own Opaque-LSAs out-of-sync with others. * This thread is prepared for that specific purpose. */ - struct thread *t_opaque_lsa_self; + struct event *t_opaque_lsa_self; /* * Backpointer to an "owner" which is LSA-type dependent. @@ -497,7 +501,7 @@ struct opaque_info_per_id { uint32_t opaque_id; /* Thread for refresh/flush scheduling for this opaque-type/id. */ - struct thread *t_opaque_lsa_self; + struct event *t_opaque_lsa_self; /* Backpointer to Opaque-LSA control information per opaque-type. */ struct opaque_info_per_type *opqctl_type; @@ -540,7 +544,7 @@ register_opaque_info_per_type(struct ospf_opaque_functab *functab, listnode_add(new->area->opaque_lsa_self, oipt); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(new->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (new->area != NULL && (top = new->area->ospf) == NULL) { free_opaque_info_per_type(oipt, true); oipt = NULL; @@ -586,7 +590,7 @@ static void free_opaque_info_per_type(struct opaque_info_per_type *oipt, ospf_opaque_lsa_flush_schedule(lsa); } - THREAD_OFF(oipt->t_opaque_lsa_self); + EVENT_OFF(oipt->t_opaque_lsa_self); list_delete(&oipt->id_list); if (cleanup_owner) { /* Remove from its owner's self-originated LSA list. */ @@ -648,7 +652,7 @@ lookup_opaque_info_by_type(struct ospf_lsa *lsa) "Type-10 Opaque-LSA: Reference to AREA is missing?"); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((area = lsa->area) != NULL && (top = area->ospf) == NULL) { flog_warn( EC_OSPF_LSA, @@ -693,7 +697,7 @@ static void free_opaque_info_per_id(void *val) { struct opaque_info_per_id *oipi = (struct opaque_info_per_id *)val; - THREAD_OFF(oipi->t_opaque_lsa_self); + EVENT_OFF(oipi->t_opaque_lsa_self); if (oipi->lsa != NULL) ospf_lsa_unlock(&oipi->lsa); XFREE(MTYPE_OPAQUE_INFO_PER_ID, oipi); @@ -754,6 +758,13 @@ DEFUN (capability_opaque, { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + /* Check that OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "OSPF Opaque LSA is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Turn on the "master switch" of opaque-lsa capability. */ if (!CHECK_FLAG(ospf->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) @@ -1280,9 +1291,9 @@ static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa) * Following are Opaque-LSA origination/refresh management functions. *------------------------------------------------------------------------*/ -static void ospf_opaque_type9_lsa_originate(struct thread *t); -static void ospf_opaque_type10_lsa_originate(struct thread *t); -static void ospf_opaque_type11_lsa_originate(struct thread *t); +static void ospf_opaque_type9_lsa_originate(struct event *t); +static void ospf_opaque_type10_lsa_originate(struct event *t); +static void ospf_opaque_type11_lsa_originate(struct event *t); static void ospf_opaque_lsa_reoriginate_resume(struct list *listtop, void *arg); void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) @@ -1328,8 +1339,8 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) "Schedule Type-9 Opaque-LSA origination in %d ms later.", delay); oi->t_opaque_lsa_self = NULL; - thread_add_timer_msec(master, ospf_opaque_type9_lsa_originate, - oi, delay, &oi->t_opaque_lsa_self); + event_add_timer_msec(master, ospf_opaque_type9_lsa_originate, + oi, delay, &oi->t_opaque_lsa_self); delay += top->min_ls_interval; } @@ -1346,8 +1357,8 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) "Schedule Type-10 Opaque-LSA origination in %d ms later.", delay); area->t_opaque_lsa_self = NULL; - thread_add_timer_msec(master, ospf_opaque_type10_lsa_originate, - area, delay, &area->t_opaque_lsa_self); + event_add_timer_msec(master, ospf_opaque_type10_lsa_originate, + area, delay, &area->t_opaque_lsa_self); delay += top->min_ls_interval; } @@ -1364,8 +1375,8 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) "Schedule Type-11 Opaque-LSA origination in %d ms later.", delay); top->t_opaque_lsa_self = NULL; - thread_add_timer_msec(master, ospf_opaque_type11_lsa_originate, - top, delay, &top->t_opaque_lsa_self); + event_add_timer_msec(master, ospf_opaque_type11_lsa_originate, + top, delay, &top->t_opaque_lsa_self); delay += top->min_ls_interval; } @@ -1452,11 +1463,11 @@ void ospf_opaque_lsa_originate_schedule(struct ospf_interface *oi, int *delay0) *delay0 = delay; } -static void ospf_opaque_type9_lsa_originate(struct thread *t) +static void ospf_opaque_type9_lsa_originate(struct event *t) { struct ospf_interface *oi; - oi = THREAD_ARG(t); + oi = EVENT_ARG(t); oi->t_opaque_lsa_self = NULL; if (IS_DEBUG_OSPF_EVENT) @@ -1466,11 +1477,11 @@ static void ospf_opaque_type9_lsa_originate(struct thread *t) opaque_lsa_originate_callback(ospf_opaque_type9_funclist, oi); } -static void ospf_opaque_type10_lsa_originate(struct thread *t) +static void ospf_opaque_type10_lsa_originate(struct event *t) { struct ospf_area *area; - area = THREAD_ARG(t); + area = EVENT_ARG(t); area->t_opaque_lsa_self = NULL; if (IS_DEBUG_OSPF_EVENT) @@ -1481,11 +1492,11 @@ static void ospf_opaque_type10_lsa_originate(struct thread *t) opaque_lsa_originate_callback(ospf_opaque_type10_funclist, area); } -static void ospf_opaque_type11_lsa_originate(struct thread *t) +static void ospf_opaque_type11_lsa_originate(struct event *t) { struct ospf *top; - top = THREAD_ARG(t); + top = EVENT_ARG(t); top->t_opaque_lsa_self = NULL; if (IS_DEBUG_OSPF_EVENT) @@ -1584,7 +1595,7 @@ struct ospf_lsa *ospf_opaque_lsa_install(struct ospf_lsa *lsa, int rt_recalc) } break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (lsa->area != NULL && (top = lsa->area->ospf) == NULL) { /* Above conditions must have passed. */ flog_warn(EC_OSPF_LSA, "%s: Something wrong?", @@ -1611,7 +1622,7 @@ struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa) struct ospf_opaque_functab *functab; struct ospf_lsa *new = NULL; - ospf = ospf_lookup_by_vrf_id(lsa->vrf_id); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((functab = ospf_opaque_functab_lookup(lsa)) == NULL || functab->lsa_refresher == NULL) { @@ -1639,15 +1650,16 @@ struct ospf_lsa *ospf_opaque_lsa_refresh(struct ospf_lsa *lsa) * triggered by external interventions (vty session, signaling, etc). *------------------------------------------------------------------------*/ -#define OSPF_OPAQUE_TIMER_ON(T,F,L,V) thread_add_timer_msec (master, (F), (L), (V), &(T)) +#define OSPF_OPAQUE_TIMER_ON(T, F, L, V) \ + event_add_timer_msec(master, (F), (L), (V), &(T)) static struct ospf_lsa *pseudo_lsa(struct ospf_interface *oi, struct ospf_area *area, uint8_t lsa_type, uint8_t opaque_type); -static void ospf_opaque_type9_lsa_reoriginate_timer(struct thread *t); -static void ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t); -static void ospf_opaque_type11_lsa_reoriginate_timer(struct thread *t); -static void ospf_opaque_lsa_refresh_timer(struct thread *t); +static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t); +static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t); +static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t); +static void ospf_opaque_lsa_refresh_timer(struct event *t); void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, uint8_t lsa_type, uint8_t opaque_type) @@ -1658,7 +1670,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, struct ospf_lsa *lsa; struct opaque_info_per_type *oipt; - void (*func)(struct thread * t) = NULL; + void (*func)(struct event * t) = NULL; int delay; switch (lsa_type) { @@ -1747,7 +1759,7 @@ void ospf_opaque_lsa_reoriginate_schedule(void *lsa_type_dependent, /* Generate a dummy lsa to be passed for a lookup function. */ lsa = pseudo_lsa(oi, area, lsa_type, opaque_type); - lsa->vrf_id = top->vrf_id; + lsa->vrf_id = VRF_DEFAULT; if ((oipt = lookup_opaque_info_by_type(lsa)) == NULL) { struct ospf_opaque_functab *functab; @@ -1818,14 +1830,14 @@ static struct ospf_lsa *pseudo_lsa(struct ospf_interface *oi, return &lsa; } -static void ospf_opaque_type9_lsa_reoriginate_timer(struct thread *t) +static void ospf_opaque_type9_lsa_reoriginate_timer(struct event *t) { struct opaque_info_per_type *oipt; struct ospf_opaque_functab *functab; struct ospf *top; struct ospf_interface *oi; - oipt = THREAD_ARG(t); + oipt = EVENT_ARG(t); if ((functab = oipt->functab) == NULL || functab->lsa_originator == NULL) { @@ -1839,9 +1851,9 @@ static void ospf_opaque_type9_lsa_reoriginate_timer(struct thread *t) return; } - if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) - || !ospf_if_is_enable(oi) - || ospf_nbr_count_opaque_capable(oi) == 0) { + if (!CHECK_FLAG(top->config, OSPF_OPAQUE_CAPABLE) || + !OSPF_IF_PARAM(oi, opaque_capable) || !ospf_if_is_enable(oi) || + ospf_nbr_count_opaque_capable(oi) == 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Suspend re-origination of Type-9 Opaque-LSAs (opaque-type=%u) for a while...", @@ -1859,7 +1871,7 @@ static void ospf_opaque_type9_lsa_reoriginate_timer(struct thread *t) (*functab->lsa_originator)(oi); } -static void ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t) +static void ospf_opaque_type10_lsa_reoriginate_timer(struct event *t) { struct opaque_info_per_type *oipt; struct ospf_opaque_functab *functab; @@ -1869,7 +1881,7 @@ static void ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t) struct ospf_interface *oi; int n; - oipt = THREAD_ARG(t); + oipt = EVENT_ARG(t); if ((functab = oipt->functab) == NULL || functab->lsa_originator == NULL) { @@ -1908,13 +1920,13 @@ static void ospf_opaque_type10_lsa_reoriginate_timer(struct thread *t) (*functab->lsa_originator)(area); } -static void ospf_opaque_type11_lsa_reoriginate_timer(struct thread *t) +static void ospf_opaque_type11_lsa_reoriginate_timer(struct event *t) { struct opaque_info_per_type *oipt; struct ospf_opaque_functab *functab; struct ospf *top; - oipt = THREAD_ARG(t); + oipt = EVENT_ARG(t); if ((functab = oipt->functab) == NULL || functab->lsa_originator == NULL) { @@ -1982,7 +1994,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) ospf_ls_retransmit_delete_nbr_area(lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: - top = ospf_lookup_by_vrf_id(lsa0->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) top = lsa0->area->ospf; ospf_ls_retransmit_delete_nbr_as(top, lsa); @@ -2008,7 +2020,7 @@ void ospf_opaque_lsa_refresh_schedule(struct ospf_lsa *lsa0) return; } -static void ospf_opaque_lsa_refresh_timer(struct thread *t) +static void ospf_opaque_lsa_refresh_timer(struct event *t) { struct opaque_info_per_id *oipi; struct ospf_opaque_functab *functab; @@ -2017,7 +2029,7 @@ static void ospf_opaque_lsa_refresh_timer(struct thread *t) if (IS_DEBUG_OSPF_EVENT) zlog_debug("Timer[Opaque-LSA]: (Opaque-LSA Refresh expire)"); - oipi = THREAD_ARG(t); + oipi = EVENT_ARG(t); if ((lsa = oipi->lsa) != NULL) if ((functab = oipi->opqctl_type->functab) != NULL) @@ -2032,7 +2044,7 @@ void ospf_opaque_lsa_flush_schedule(struct ospf_lsa *lsa0) struct ospf_lsa *lsa; struct ospf *top; - top = ospf_lookup_by_vrf_id(lsa0->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if ((oipt = lookup_opaque_info_by_type(lsa0)) == NULL || (oipi = lookup_opaque_info_by_id(oipt, lsa0)) == NULL) { @@ -2108,14 +2120,21 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, lsa->data->type, &lsa->data->id); /* - * Since these LSA entries are not yet installed into corresponding - * LSDB, just flush them without calling ospf_ls_maxage() afterward. + * Install the stale LSA into the Link State Database, add it to the + * MaxAge list, and flush it from the OSPF routing domain. For other + * LSA types, the installation is done in the refresh function. It is + * done inline here since the opaque refresh function is dynamically + * registered when opaque LSAs are originated (which is not the case + * for stale LSAs). */ lsa->data->ls_age = htons(OSPF_LSA_MAXAGE); + ospf_lsa_install( + top, (lsa->data->type == OSPF_OPAQUE_LINK_LSA) ? nbr->oi : NULL, + lsa); + ospf_lsa_maxage(top, lsa); + switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: - ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa); - break; case OSPF_OPAQUE_AREA_LSA: ospf_flood_through_area(nbr->oi->area, NULL /*inbr*/, lsa); break; @@ -2127,7 +2146,6 @@ void ospf_opaque_self_originated_lsa_received(struct ospf_neighbor *nbr, __func__, lsa->data->type); return; } - ospf_lsa_discard(lsa); /* List "lsas" will be deleted by caller. */ } /*------------------------------------------------------------------------* diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 5268c9896b47..cfa0d5d57444 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -7,7 +7,7 @@ #include <zebra.h> #include "monotime.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "linklist.h" #include "prefix.h" @@ -308,8 +308,10 @@ static int ospf_check_md5_digest(struct ospf_interface *oi, ck = ospf_crypt_key_lookup(OSPF_IF_PARAM(oi, auth_crypt), ospfh->u.crypt.key_id); if (ck == NULL) { - flog_warn(EC_OSPF_MD5, "interface %s: ospf_check_md5 no key %d", - IF_NAME(oi), ospfh->u.crypt.key_id); + flog_warn( + EC_OSPF_MD5, + "interface %s: ospf_check_md5 no key %d, Router-ID: %pI4", + IF_NAME(oi), ospfh->u.crypt.key_id, &ospfh->router_id); return 0; } @@ -320,9 +322,9 @@ static int ospf_check_md5_digest(struct ospf_interface *oi, && ntohl(nbr->crypt_seqnum) > ntohl(ospfh->u.crypt.crypt_seqnum)) { flog_warn( EC_OSPF_MD5, - "interface %s: ospf_check_md5 bad sequence %d (expect %d)", + "interface %s: ospf_check_md5 bad sequence %d (expect %d), Router-ID: %pI4", IF_NAME(oi), ntohl(ospfh->u.crypt.crypt_seqnum), - ntohl(nbr->crypt_seqnum)); + ntohl(nbr->crypt_seqnum), &ospfh->router_id); return 0; } @@ -345,9 +347,10 @@ static int ospf_check_md5_digest(struct ospf_interface *oi, /* compare the two */ if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) { - flog_warn(EC_OSPF_MD5, - "interface %s: ospf_check_md5 checksum mismatch", - IF_NAME(oi)); + flog_warn( + EC_OSPF_MD5, + "interface %s: ospf_check_md5 checksum mismatch, Router-ID: %pI4", + IF_NAME(oi), &ospfh->router_id); return 0; } @@ -426,20 +429,21 @@ static int ospf_make_md5_digest(struct ospf_interface *oi, if (stream_get_endp(op->s) != op->length) /* XXX size_t */ - flog_warn(EC_OSPF_MD5, - "%s: length mismatch stream %lu ospf_packet %u", - __func__, (unsigned long)stream_get_endp(op->s), - op->length); + flog_warn( + EC_OSPF_MD5, + "%s: length mismatch stream %lu ospf_packet %u, Router-ID %pI4", + __func__, (unsigned long)stream_get_endp(op->s), + op->length, &ospfh->router_id); return OSPF_AUTH_MD5_SIZE; } -static void ospf_ls_req_timer(struct thread *thread) +static void ospf_ls_req_timer(struct event *thread) { struct ospf_neighbor *nbr; - nbr = THREAD_ARG(thread); + nbr = EVENT_ARG(thread); nbr->t_ls_req = NULL; /* Send Link State Request. */ @@ -452,17 +456,17 @@ static void ospf_ls_req_timer(struct thread *thread) void ospf_ls_req_event(struct ospf_neighbor *nbr) { - THREAD_OFF(nbr->t_ls_req); - thread_add_event(master, ospf_ls_req_timer, nbr, 0, &nbr->t_ls_req); + EVENT_OFF(nbr->t_ls_req); + event_add_event(master, ospf_ls_req_timer, nbr, 0, &nbr->t_ls_req); } /* Cyclic timer function. Fist registered in ospf_nbr_new () in ospf_neighbor.c */ -void ospf_ls_upd_timer(struct thread *thread) +void ospf_ls_upd_timer(struct event *thread) { struct ospf_neighbor *nbr; - nbr = THREAD_ARG(thread); + nbr = EVENT_ARG(thread); nbr->t_ls_upd = NULL; /* Send Link State Update. */ @@ -516,11 +520,11 @@ void ospf_ls_upd_timer(struct thread *thread) OSPF_NSM_TIMER_ON(nbr->t_ls_upd, ospf_ls_upd_timer, nbr->v_ls_upd); } -void ospf_ls_ack_timer(struct thread *thread) +void ospf_ls_ack_timer(struct event *thread) { struct ospf_interface *oi; - oi = THREAD_ARG(thread); + oi = EVENT_ARG(thread); oi->t_ls_ack = NULL; /* Send Link State Acknowledgment. */ @@ -604,9 +608,9 @@ static void ospf_write_frags(int fd, struct ospf_packet *op, struct ip *iph, } #endif /* WANT_OSPF_WRITE_FRAGMENT */ -static void ospf_write(struct thread *thread) +static void ospf_write(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); struct ospf_interface *oi; struct ospf_packet *op; struct sockaddr_in sa_dst; @@ -614,7 +618,7 @@ static void ospf_write(struct thread *thread) struct msghdr msg; struct iovec iov[2]; uint8_t type; - int ret; + int ret, fd; int flags = 0; struct listnode *node; #ifdef WANT_OSPF_WRITE_FRAGMENT @@ -629,11 +633,12 @@ static void ospf_write(struct thread *thread) struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf; struct in_pktinfo *pi; #endif + fd = ospf->fd; - if (ospf->fd < 0 || ospf->oi_running == 0) { + if (fd < 0 || ospf->oi_running == 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s failed to send, fd %d, instance %u", - __func__, ospf->fd, ospf->oi_running); + __func__, fd, ospf->oi_running); return; } @@ -653,6 +658,15 @@ static void ospf_write(struct thread *thread) /* convenience - max OSPF data per packet */ maxdatasize = oi->ifp->mtu - sizeof(struct ip); #endif /* WANT_OSPF_WRITE_FRAGMENT */ + + /* Reset socket fd to use. */ + fd = ospf->fd; + + /* Check for per-interface socket */ + if (ospf->intf_socket_enabled && + (IF_OSPF_IF_INFO(oi->ifp))->oii_fd > 0) + fd = (IF_OSPF_IF_INFO(oi->ifp))->oii_fd; + /* Get one packet from queue. */ op = ospf_fifo_head(oi->obuf); assert(op); @@ -660,8 +674,7 @@ static void ospf_write(struct thread *thread) if (op->dst.s_addr == htonl(OSPF_ALLSPFROUTERS) || op->dst.s_addr == htonl(OSPF_ALLDROUTERS)) - ospf_if_ipmulticast(ospf, oi->address, - oi->ifp->ifindex); + ospf_if_ipmulticast(fd, oi->address, oi->ifp->ifindex); /* Rewrite the md5 signature & update the seq */ ospf_make_md5_digest(oi, op); @@ -756,13 +769,13 @@ static void ospf_write(struct thread *thread) #ifdef WANT_OSPF_WRITE_FRAGMENT if (op->length > maxdatasize) - ospf_write_frags(ospf->fd, op, &iph, &msg, maxdatasize, + ospf_write_frags(fd, op, &iph, &msg, maxdatasize, oi->ifp->mtu, flags, type); #endif /* WANT_OSPF_WRITE_FRAGMENT */ /* send final fragment (could be first) */ sockopt_iphdrincl_swab_htosys(&iph); - ret = sendmsg(ospf->fd, &msg, flags); + ret = sendmsg(fd, &msg, flags); sockopt_iphdrincl_swab_systoh(&iph); if (IS_DEBUG_OSPF_EVENT) zlog_debug( @@ -843,8 +856,8 @@ static void ospf_write(struct thread *thread) /* If packets still remain in queue, call write thread. */ if (!list_isempty(ospf->oi_write_q)) - thread_add_write(master, ospf_write, ospf, ospf->fd, - &ospf->t_write); + event_add_write(master, ospf_write, ospf, ospf->fd, + &ospf->t_write); } /* OSPF Hello message read -- RFC2328 Section 10.5. */ @@ -896,11 +909,11 @@ 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 %pI4 [Hello:RECV]: RouterDeadInterval mismatch (expected %u, but received %u).", - &ospfh->router_id, - OSPF_IF_PARAM(oi, v_wait), - ntohl(hello->dead_interval)); + flog_warn( + EC_OSPF_PACKET, + "Packet %pI4 [Hello:RECV]: RouterDeadInterval mismatch on %s (expected %u, but received %u).", + &ospfh->router_id, IF_NAME(oi), + OSPF_IF_PARAM(oi, v_wait), ntohl(hello->dead_interval)); return; } @@ -910,8 +923,8 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, != ntohs(hello->hello_interval)) { flog_warn( EC_OSPF_PACKET, - "Packet %pI4 [Hello:RECV]: HelloInterval mismatch (expected %u, but received %u).", - &ospfh->router_id, + "Packet %pI4 [Hello:RECV]: HelloInterval mismatch on %s (expected %u, but received %u).", + &ospfh->router_id, IF_NAME(oi), OSPF_IF_PARAM(oi, v_hello), ntohs(hello->hello_interval)); return; @@ -919,8 +932,8 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, } if (IS_DEBUG_OSPF_EVENT) - zlog_debug("Packet %pI4 [Hello:RECV]: Options %s vrf %s", - &ospfh->router_id, + zlog_debug("Packet %pI4 [Hello:RECV]: Options on %s %s vrf %s", + &ospfh->router_id, IF_NAME(oi), ospf_options_dump(hello->options), ospf_vrf_id_to_name(oi->ospf->vrf_id)); @@ -934,21 +947,22 @@ static void ospf_hello(struct ip *iph, struct ospf_header *ospfh, * relationship. */ flog_warn(EC_OSPF_PACKET, - "Packet %pI4 [Hello:RECV]: T-bit on, drop it.", - &ospfh->router_id); + "Packet %pI4 [Hello:RECV]: T-bit ON on %s, drop it.", + &ospfh->router_id, IF_NAME(oi)); return; } #endif /* REJECT_IF_TBIT_ON */ - if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) - && CHECK_FLAG(hello->options, OSPF_OPTION_O)) { + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable) && + CHECK_FLAG(hello->options, OSPF_OPTION_O)) { /* * This router does know the correct usage of O-bit * the bit should be set in DD packet only. */ flog_warn(EC_OSPF_PACKET, - "Packet %pI4 [Hello:RECV]: O-bit abuse?", - &ospfh->router_id); + "Packet %pI4 [Hello:RECV]: O-bit abuse? on %s", + &ospfh->router_id, IF_NAME(oi)); #ifdef STRICT_OBIT_USAGE_CHECK return; /* Reject this packet. */ #else /* STRICT_OBIT_USAGE_CHECK */ @@ -1349,8 +1363,9 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, } #endif /* REJECT_IF_TBIT_ON */ - if (CHECK_FLAG(dd->options, OSPF_OPTION_O) - && !CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) { + if (CHECK_FLAG(dd->options, OSPF_OPTION_O) && + (!CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) || + !OSPF_IF_PARAM(oi, opaque_capable))) { /* * This node is not configured to handle O-bit, for now. * Clear it to ignore unsupported capability proposed by @@ -1435,7 +1450,8 @@ static void ospf_db_desc(struct ip *iph, struct ospf_header *ospfh, /* This is where the real Options are saved */ nbr->options = dd->options; - if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) { + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable)) { if (IS_DEBUG_OSPF_EVENT) zlog_debug( "Neighbor[%pI4] is %sOpaque-capable.", @@ -2018,7 +2034,7 @@ 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); @@ -2102,6 +2118,14 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, if (ospf_flood(oi->ospf, nbr, current, lsa) < 0) /* Trap NSSA later. */ DISCARD_LSA(lsa, 5); + + /* GR: check for network topology change. */ + if (ospf->gr_info.restart_in_progress && + ((lsa->data->type == OSPF_ROUTER_LSA || + lsa->data->type == OSPF_NETWORK_LSA))) + ospf_gr_check_lsdb_consistency(oi->ospf, + oi->area); + continue; } @@ -2214,9 +2238,6 @@ static void ospf_ls_upd(struct ospf *ospf, struct ip *iph, assert(listcount(lsas) == 0); list_delete(&lsas); - - if (ospf->gr_info.restart_in_progress) - ospf_gr_check_lsdb_consistency(oi->ospf, oi->area); } /* OSPF Link State Acknowledgment message read -- RFC2328 Section 13.7. */ @@ -2470,10 +2491,11 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) flog_warn( EC_OSPF_PACKET, - "interface %s: auth-type mismatch, local %s, rcvd Null", + "interface %s: auth-type mismatch, local %s, rcvd Null, Router-ID %pI4", IF_NAME(oi), lookup_msg(ospf_auth_type_str, - iface_auth_type, NULL)); + iface_auth_type, NULL), + &ospfh->router_id); return 0; } if (!ospf_check_sum(ospfh)) { @@ -2492,18 +2514,20 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) flog_warn( EC_OSPF_PACKET, - "interface %s: auth-type mismatch, local %s, rcvd Simple", + "interface %s: auth-type mismatch, local %s, rcvd Simple, Router-ID %pI4", IF_NAME(oi), lookup_msg(ospf_auth_type_str, - iface_auth_type, NULL)); + iface_auth_type, NULL), + &ospfh->router_id); return 0; } if (memcmp(OSPF_IF_PARAM(oi, auth_simple), ospfh->u.auth_data, OSPF_AUTH_SIMPLE_SIZE)) { if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn(EC_OSPF_PACKET, - "interface %s: Simple auth failed", - IF_NAME(oi)); + flog_warn( + EC_OSPF_PACKET, + "interface %s: Simple auth failed, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); return 0; } if (!ospf_check_sum(ospfh)) { @@ -2522,18 +2546,19 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) flog_warn( EC_OSPF_PACKET, - "interface %s: auth-type mismatch, local %s, rcvd Cryptographic", + "interface %s: auth-type mismatch, local %s, rcvd Cryptographic, Router-ID %pI4", IF_NAME(oi), lookup_msg(ospf_auth_type_str, - iface_auth_type, NULL)); + iface_auth_type, NULL), + &ospfh->router_id); return 0; } if (ospfh->checksum) { if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) flog_warn( EC_OSPF_PACKET, - "interface %s: OSPF header checksum is not 0", - IF_NAME(oi)); + "interface %s: OSPF header checksum is not 0, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); return 0; } /* only MD5 crypto method can pass ospf_packet_examin() */ @@ -2546,9 +2571,10 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) bug? */ !ospf_check_md5_digest(oi, ospfh)) { if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) - flog_warn(EC_OSPF_MD5, - "interface %s: MD5 auth failed", - IF_NAME(oi)); + flog_warn( + EC_OSPF_MD5, + "interface %s: MD5 auth failed, Router-ID %pI4", + IF_NAME(oi), &ospfh->router_id); return 0; } return 1; @@ -2556,8 +2582,8 @@ static int ospf_check_auth(struct ospf_interface *oi, struct ospf_header *ospfh) if (IS_DEBUG_OSPF_PACKET(ospfh->type - 1, RECV)) flog_warn( EC_OSPF_PACKET, - "interface %s: invalid packet auth-type (%02x)", - IF_NAME(oi), pkt_auth_type); + "interface %s: invalid packet auth-type (%02x), Router-ID %pI4", + IF_NAME(oi), pkt_auth_type, &ospfh->router_id); return 0; } } @@ -3189,17 +3215,17 @@ static enum ospf_read_return_enum ospf_read_helper(struct ospf *ospf) } /* Starting point of packet process function. */ -void ospf_read(struct thread *thread) +void ospf_read(struct event *thread) { struct ospf *ospf; int32_t count = 0; enum ospf_read_return_enum ret; /* first of all get interface pointer. */ - ospf = THREAD_ARG(thread); + ospf = EVENT_ARG(thread); /* prepare for next packet. */ - thread_add_read(master, ospf_read, ospf, ospf->fd, &ospf->t_read); + event_add_read(master, ospf_read, ospf, ospf->fd, &ospf->t_read); while (count < ospf->write_oi_count) { count++; @@ -3412,7 +3438,8 @@ static int ospf_make_db_desc(struct ospf_interface *oi, /* Set Options. */ options = OPTIONS(oi); - if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE)) + if (CHECK_FLAG(oi->ospf->config, OSPF_OPAQUE_CAPABLE) && + OSPF_IF_PARAM(oi, opaque_capable)) SET_FLAG(options, OSPF_OPTION_O); if (OSPF_FR_CONFIG(oi->ospf, oi->area)) SET_FLAG(options, OSPF_OPTION_DC); @@ -3659,6 +3686,16 @@ static void ospf_hello_send_sub(struct ospf_interface *oi, in_addr_t addr) struct ospf_packet *op; uint16_t length = OSPF_HEADER_SIZE; + /* Check if config is still being processed */ + if (event_is_scheduled(t_ospf_cfg)) { + if (IS_DEBUG_OSPF_PACKET(0, SEND)) + zlog_debug( + "Suppressing hello to %pI4 on %s during config load", + &(addr), IF_NAME(oi)); + + return; + } + op = ospf_packet_new(oi->ifp->mtu); /* Prepare OSPF common header. */ @@ -3724,11 +3761,11 @@ static void ospf_poll_send(struct ospf_nbr_nbma *nbr_nbma) ospf_hello_send_sub(oi, nbr_nbma->addr.s_addr); } -void ospf_poll_timer(struct thread *thread) +void ospf_poll_timer(struct event *thread) { struct ospf_nbr_nbma *nbr_nbma; - nbr_nbma = THREAD_ARG(thread); + nbr_nbma = EVENT_ARG(thread); nbr_nbma->t_poll = NULL; if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) @@ -3743,11 +3780,11 @@ void ospf_poll_timer(struct thread *thread) } -void ospf_hello_reply_timer(struct thread *thread) +void ospf_hello_reply_timer(struct event *thread) { struct ospf_neighbor *nbr; - nbr = THREAD_ARG(thread); + nbr = EVENT_ARG(thread); nbr->t_hello_reply = NULL; if (IS_DEBUG_OSPF(nsm, NSM_TIMERS)) @@ -4017,9 +4054,8 @@ static struct ospf_packet *ospf_ls_upd_packet_new(struct list *update, return ospf_packet_new(size - sizeof(struct ip)); } -static void ospf_ls_upd_queue_send(struct ospf_interface *oi, - struct list *update, struct in_addr addr, - int send_lsupd_now) +void ospf_ls_upd_queue_send(struct ospf_interface *oi, struct list *update, + struct in_addr addr, int send_lsupd_now) { struct ospf_packet *op; uint16_t length = OSPF_HEADER_SIZE; @@ -4058,7 +4094,7 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi, ospf_packet_add(oi, op); /* Call ospf_write() right away to send ospf packets to neighbors */ if (send_lsupd_now) { - struct thread os_packet_thd; + struct event os_packet_thd; os_packet_thd.arg = (void *)oi->ospf; if (oi->on_write_q == 0) { @@ -4082,16 +4118,16 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi, * is actually turned off. */ if (list_isempty(oi->ospf->oi_write_q)) - THREAD_OFF(oi->ospf->t_write); + EVENT_OFF(oi->ospf->t_write); } else { /* Hook thread to write packet. */ OSPF_ISM_WRITE_ON(oi->ospf); } } -static void ospf_ls_upd_send_queue_event(struct thread *thread) +static void ospf_ls_upd_send_queue_event(struct event *thread) { - struct ospf_interface *oi = THREAD_ARG(thread); + struct ospf_interface *oi = EVENT_ARG(thread); struct route_node *rn; struct route_node *rnext; struct list *update; @@ -4126,8 +4162,8 @@ static void ospf_ls_upd_send_queue_event(struct thread *thread) "%s: update lists not cleared, %d nodes to try again, raising new event", __func__, again); oi->t_ls_upd_event = NULL; - thread_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, - &oi->t_ls_upd_event); + event_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, + &oi->t_ls_upd_event); } if (IS_DEBUG_OSPF_EVENT) @@ -4198,8 +4234,8 @@ void ospf_ls_upd_send(struct ospf_neighbor *nbr, struct list *update, int flag, rn->p.u.prefix4, 1); } } else - thread_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, - &oi->t_ls_upd_event); + event_add_event(master, ospf_ls_upd_send_queue_event, oi, 0, + &oi->t_ls_upd_event); } static void ospf_ls_ack_send_list(struct ospf_interface *oi, struct list *ack, @@ -4236,9 +4272,9 @@ static void ospf_ls_ack_send_list(struct ospf_interface *oi, struct list *ack, OSPF_ISM_WRITE_ON(oi->ospf); } -static void ospf_ls_ack_send_event(struct thread *thread) +static void ospf_ls_ack_send_event(struct event *thread) { - struct ospf_interface *oi = THREAD_ARG(thread); + struct ospf_interface *oi = EVENT_ARG(thread); oi->t_ls_ack_direct = NULL; @@ -4262,8 +4298,8 @@ void ospf_ls_ack_send(struct ospf_neighbor *nbr, struct ospf_lsa *lsa) listnode_add(oi->ls_ack_direct.ls_ack, ospf_lsa_lock(lsa)); - thread_add_event(master, ospf_ls_ack_send_event, oi, 0, - &oi->t_ls_ack_direct); + event_add_event(master, ospf_ls_ack_send_event, oi, 0, + &oi->t_ls_ack_direct); } /* Send Link State Acknowledgment delayed. */ diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h index e8b279b9d895..234738979e95 100644 --- a/ospfd/ospf_packet.h +++ b/ospfd/ospf_packet.h @@ -124,7 +124,7 @@ 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_read(struct thread *thread); +extern void ospf_read(struct event *thread); extern void ospf_hello_send(struct ospf_interface *); extern void ospf_db_desc_send(struct ospf_neighbor *); extern void ospf_db_desc_resend(struct ospf_neighbor *); @@ -132,15 +132,18 @@ extern void ospf_ls_req_send(struct ospf_neighbor *); extern void ospf_ls_upd_send_lsa(struct ospf_neighbor *, struct ospf_lsa *, int); extern void ospf_ls_upd_send(struct ospf_neighbor *, struct list *, int, int); +extern void ospf_ls_upd_queue_send(struct ospf_interface *oi, + struct list *update, struct in_addr addr, + int send_lsupd_now); extern void ospf_ls_ack_send(struct ospf_neighbor *, struct ospf_lsa *); extern void ospf_ls_ack_send_delayed(struct ospf_interface *); extern void ospf_ls_retransmit(struct ospf_interface *, struct ospf_lsa *); extern void ospf_ls_req_event(struct ospf_neighbor *); -extern void ospf_ls_upd_timer(struct thread *thread); -extern void ospf_ls_ack_timer(struct thread *thread); -extern void ospf_poll_timer(struct thread *thread); -extern void ospf_hello_reply_timer(struct thread *thread); +extern void ospf_ls_upd_timer(struct event *thread); +extern void ospf_ls_ack_timer(struct event *thread); +extern void ospf_poll_timer(struct event *thread); +extern void ospf_hello_reply_timer(struct event *thread); extern const struct message ospf_packet_type_str[]; extern const size_t ospf_packet_type_str_max; diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c index a808ddc9f62a..725443f49078 100644 --- a/ospfd/ospf_ri.c +++ b/ospfd/ospf_ri.c @@ -20,7 +20,7 @@ #include "vty.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "mpls.h" @@ -798,13 +798,10 @@ static struct ospf_lsa *ospf_router_info_lsa_new(struct ospf_area *area) /* Now, create an OSPF LSA instance. */ new = ospf_lsa_new_and_data(length); + /* Routing Information is only supported for default VRF */ + new->vrf_id = VRF_DEFAULT; new->area = area; - if (new->area && new->area->ospf) - new->vrf_id = new->area->ospf->vrf_id; - else - new->vrf_id = VRF_DEFAULT; - SET_FLAG(new->flags, OSPF_LSA_SELF); memcpy(new->data, lsah, length); stream_free(s); @@ -817,7 +814,6 @@ static int ospf_router_info_lsa_originate_as(void *arg) struct ospf_lsa *new; struct ospf *top; int rc = -1; - vrf_id_t vrf_id = VRF_DEFAULT; /* Sanity Check */ if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) { @@ -830,13 +826,12 @@ static int ospf_router_info_lsa_originate_as(void *arg) /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ new = ospf_router_info_lsa_new(NULL); - new->vrf_id = VRF_DEFAULT; top = (struct ospf *)arg; /* Check ospf info */ if (top == NULL) { zlog_debug("RI (%s): ospf instance not found for vrf id %u", - __func__, vrf_id); + __func__, VRF_DEFAULT); ospf_lsa_unlock(&new); return rc; } @@ -874,7 +869,6 @@ static int ospf_router_info_lsa_originate_area(void *arg) struct ospf *top; struct ospf_ri_area_info *ai = NULL; int rc = -1; - vrf_id_t vrf_id = VRF_DEFAULT; /* Sanity Check */ if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) { @@ -893,19 +887,18 @@ static int ospf_router_info_lsa_originate_area(void *arg) __func__); return rc; } - if (ai->area->ospf) { - vrf_id = ai->area->ospf->vrf_id; + + if (ai->area->ospf) top = ai->area->ospf; - } else { - top = ospf_lookup_by_vrf_id(vrf_id); - } + else + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); + new = ospf_router_info_lsa_new(ai->area); - new->vrf_id = vrf_id; /* Check ospf info */ if (top == NULL) { zlog_debug("RI (%s): ospf instance not found for vrf id %u", - __func__, vrf_id); + __func__, VRF_DEFAULT); ospf_lsa_unlock(&new); return rc; } @@ -1039,10 +1032,9 @@ static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa) /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ new = ospf_router_info_lsa_new(ai->area); new->data->ls_seqnum = lsa_seqnum_increment(lsa); - new->vrf_id = lsa->vrf_id; /* Install this LSA into LSDB. */ /* Given "lsa" will be freed in the next function. */ - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "RI (%s): ospf_lsa_install() ?", __func__); @@ -1062,10 +1054,9 @@ static struct ospf_lsa *ospf_router_info_lsa_refresh(struct ospf_lsa *lsa) /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ new = ospf_router_info_lsa_new(NULL); new->data->ls_seqnum = lsa_seqnum_increment(lsa); - new->vrf_id = lsa->vrf_id; /* Install this LSA into LSDB. */ /* Given "lsa" will be freed in the next function. */ - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { flog_warn(EC_OSPF_LSA_INSTALL_FAILURE, "RI (%s): ospf_lsa_install() ?", __func__); @@ -1676,10 +1667,18 @@ DEFUN (router_info, { int idx_mode = 1; uint8_t scope; + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); if (OspfRI.enabled) return CMD_SUCCESS; + /* Check that the OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, + "Router Information is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + /* Check and get Area value if present */ if (strncmp(argv[idx_mode]->arg, "as", 2) == 0) scope = OSPF_OPAQUE_AS_LSA; diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index 5f18bff1cf79..cdb1eb009543 100644 --- a/ospfd/ospf_route.c +++ b/ospfd/ospf_route.c @@ -684,6 +684,8 @@ void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link, __func__); } } + if (rn->info) + ospf_route_free(rn->info); rn->info = or ; @@ -1008,7 +1010,8 @@ void ospf_prune_unreachable_routers(struct route_table *rtrs) } int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, - struct ospf_area *area, struct prefix_ipv4 *p) + struct ospf_area *area, struct prefix_ipv4 *p, + bool nssa) { struct route_node *rn; struct ospf_route * or, *new_or; @@ -1027,7 +1030,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, or = rn->info; - if (or->path_type == OSPF_PATH_INTRA_AREA) { + if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: an intra-area route exists", __func__); @@ -1054,7 +1057,10 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, new_or->cost = 0; new_or->u.std.area_id = area->area_id; new_or->u.std.external_routing = area->external_routing; - new_or->path_type = OSPF_PATH_INTER_AREA; + if (nssa) + new_or->path_type = OSPF_PATH_TYPE2_EXTERNAL; + else + new_or->path_type = OSPF_PATH_INTER_AREA; rn->info = new_or; ospf_zebra_add_discard(ospf, p); @@ -1063,7 +1069,7 @@ int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt, } void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, - struct prefix_ipv4 *p) + struct prefix_ipv4 *p, bool nssa) { struct route_node *rn; struct ospf_route * or ; @@ -1081,7 +1087,7 @@ void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt, or = rn->info; - if (or->path_type == OSPF_PATH_INTRA_AREA) { + if (!nssa && or->path_type == OSPF_PATH_INTRA_AREA) { if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: an intra-area route exists", __func__); return; diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 2582067aece7..7639a0049e12 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -152,9 +152,10 @@ extern void ospf_route_subst_nexthops(struct ospf_route *, struct list *); extern void ospf_prune_unreachable_networks(struct route_table *); extern void ospf_prune_unreachable_routers(struct route_table *); extern int ospf_add_discard_route(struct ospf *, struct route_table *, - struct ospf_area *, struct prefix_ipv4 *); + struct ospf_area *, struct prefix_ipv4 *, + bool); extern void ospf_delete_discard_route(struct ospf *, struct route_table *, - struct prefix_ipv4 *); + struct prefix_ipv4 *, bool); extern int ospf_route_match_same(struct route_table *, struct prefix_ipv4 *, struct ospf_route *); diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index 308700881927..d2f639031be6 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -120,7 +120,7 @@ route_match_ip_nexthop(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -168,7 +168,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -245,7 +245,7 @@ route_match_ip_address(void *rule, const struct prefix *prefix, void *object) alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -286,7 +286,7 @@ route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -398,17 +398,19 @@ route_set_metric(void *rule, const struct prefix *prefix, void *object) if (!metric->used) return RMAP_OKAY; - ei->route_map_set.metric = DEFAULT_DEFAULT_METRIC; + ROUTEMAP_METRIC(ei) = ei->metric; if (metric->type == metric_increment) - ei->route_map_set.metric += metric->metric; + ROUTEMAP_METRIC(ei) += metric->metric; else if (metric->type == metric_decrement) - ei->route_map_set.metric -= metric->metric; + ROUTEMAP_METRIC(ei) -= metric->metric; else if (metric->type == metric_absolute) - ei->route_map_set.metric = metric->metric; + ROUTEMAP_METRIC(ei) = metric->metric; - if (ei->route_map_set.metric > OSPF_LS_INFINITY) - ei->route_map_set.metric = OSPF_LS_INFINITY; + if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric) + ROUTEMAP_METRIC(ei) = ei->min_metric; + if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric) + ROUTEMAP_METRIC(ei) = ei->max_metric; return RMAP_OKAY; } @@ -462,6 +464,115 @@ static const struct route_map_rule_cmd route_set_metric_cmd = { route_set_metric_free, }; +/* `set min-metric METRIC' */ +/* Set min-metric to attribute. */ +static enum route_map_cmd_result_t +route_set_min_metric(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *min_metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + min_metric = rule; + ei = object; + + ei->min_metric = *min_metric; + + if (ei->min_metric > OSPF_LS_INFINITY) + ei->min_metric = OSPF_LS_INFINITY; + + if ((uint32_t)ROUTEMAP_METRIC(ei) < ei->min_metric) + ROUTEMAP_METRIC(ei) = ei->min_metric; + + return RMAP_OKAY; +} + +/* set min-metric compilation. */ +static void *route_set_min_metric_compile(const char *arg) +{ + + uint32_t *min_metric; + + min_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + + *min_metric = strtoul(arg, NULL, 10); + + if (*min_metric) + return min_metric; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, min_metric); + return NULL; +} + +/* Free route map's compiled `set min-metric' value. */ +static void route_set_min_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_min_metric_cmd = { + "min-metric", + route_set_min_metric, + route_set_min_metric_compile, + route_set_min_metric_free, +}; + + +/* `set max-metric METRIC' */ +/* Set max-metric to attribute. */ +static enum route_map_cmd_result_t +route_set_max_metric(void *rule, const struct prefix *prefix, void *object) +{ + uint32_t *max_metric; + struct external_info *ei; + + /* Fetch routemap's rule information. */ + max_metric = rule; + ei = object; + + ei->max_metric = *max_metric; + + if (ei->max_metric > OSPF_LS_INFINITY) + ei->max_metric = OSPF_LS_INFINITY; + + if ((uint32_t)ROUTEMAP_METRIC(ei) > ei->max_metric) + ROUTEMAP_METRIC(ei) = ei->max_metric; + + return RMAP_OKAY; +} + +/* set max-metric compilation. */ +static void *route_set_max_metric_compile(const char *arg) +{ + + uint32_t *max_metric; + + max_metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t)); + + *max_metric = strtoul(arg, NULL, 10); + + if (*max_metric) + return max_metric; + + XFREE(MTYPE_ROUTE_MAP_COMPILED, max_metric); + return NULL; +} + +/* Free route map's compiled `set max-metric' value. */ +static void route_set_max_metric_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set metric rule structure. */ +static const struct route_map_rule_cmd route_set_max_metric_cmd = { + "max-metric", + route_set_max_metric, + route_set_max_metric_compile, + route_set_max_metric_free, +}; + /* `set metric-type TYPE' */ /* Set metric-type to attribute. */ static enum route_map_cmd_result_t @@ -475,7 +586,7 @@ route_set_metric_type(void *rule, const struct prefix *prefix, void *object) ei = object; /* Set metric out value. */ - ei->route_map_set.metric_type = *metric_type; + ROUTEMAP_METRIC_TYPE(ei) = *metric_type; return RMAP_OKAY; } @@ -582,9 +693,6 @@ void ospf_route_map_init(void) route_map_delete_hook(ospf_route_map_update); route_map_event_hook(ospf_route_map_event); - route_map_set_metric_hook(generic_set_add); - route_map_no_set_metric_hook(generic_set_delete); - route_map_match_ip_next_hop_hook(generic_match_add); route_map_no_match_ip_next_hop_hook(generic_match_delete); @@ -609,6 +717,12 @@ void ospf_route_map_init(void) route_map_set_metric_hook(generic_set_add); route_map_no_set_metric_hook(generic_set_delete); + route_map_set_min_metric_hook(generic_set_add); + route_map_no_set_min_metric_hook(generic_set_delete); + + route_map_set_max_metric_hook(generic_set_add); + route_map_no_set_max_metric_hook(generic_set_delete); + route_map_set_tag_hook(generic_set_add); route_map_no_set_tag_hook(generic_set_delete); @@ -621,6 +735,8 @@ void ospf_route_map_init(void) route_map_install_match(&route_match_tag_cmd); route_map_install_set(&route_set_metric_cmd); + route_map_install_set(&route_set_min_metric_cmd); + route_map_install_set(&route_set_max_metric_cmd); route_map_install_set(&route_set_metric_type_cmd); route_map_install_set(&route_set_tag_cmd); diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index fb9f36349a12..fcc43e7311c2 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -1112,7 +1112,7 @@ static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v, oid2in_addr(offset, IN_ADDR_SIZE, range_net); p.prefix = *range_net; - return ospf_area_range_lookup(area, &p); + return ospf_area_range_lookup(area, area->ranges, &p); } else { /* Set OID offset for Area ID. */ offset = name + v->namelen; @@ -2525,7 +2525,7 @@ static int ospf_snmp_ism_change(struct ospf_interface *oi, int state, } /* Register OSPF2-MIB. */ -static int ospf_snmp_init(struct thread_master *tm) +static int ospf_snmp_init(struct event_loop *tm) { ospf_snmp_iflist = list_new(); ospf_snmp_vl_table = route_table_init(); diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index 7c1f9f17ac3c..274d5bc9af7b 100644 --- a/ospfd/ospf_spf.c +++ b/ospfd/ospf_spf.c @@ -6,7 +6,7 @@ #include <zebra.h> #include "monotime.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "hash.h" #include "linklist.h" @@ -1764,7 +1764,8 @@ void ospf_spf_calculate(struct ospf_area *area, struct ospf_lsa *root_lsa, if (v->type != OSPF_VERTEX_ROUTER) ospf_intra_add_transit(new_table, v, area); else { - ospf_intra_add_router(new_rtrs, v, area, false); + if (new_rtrs) + ospf_intra_add_router(new_rtrs, v, area, false); if (all_rtrs) ospf_intra_add_router(all_rtrs, v, area, true); } @@ -1841,9 +1842,9 @@ void ospf_spf_calculate_areas(struct ospf *ospf, struct route_table *new_table, } /* Worker for SPF calculation scheduler. */ -static void ospf_spf_calculate_schedule_worker(struct thread *thread) +static void ospf_spf_calculate_schedule_worker(struct event *thread) { - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); struct route_table *new_table, *new_rtrs; struct route_table *all_rtrs = NULL; struct timeval start_time, spf_start_time; @@ -2043,8 +2044,8 @@ 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_schedule_worker, ospf, - delay, &ospf->t_spf_calc); + event_add_timer_msec(master, ospf_spf_calculate_schedule_worker, ospf, + delay, &ospf->t_spf_calc); } /* Restart OSPF SPF algorithm*/ diff --git a/ospfd/ospf_sr.c b/ospfd/ospf_sr.c index adb14ec6b147..467cb0504d95 100644 --- a/ospfd/ospf_sr.c +++ b/ospfd/ospf_sr.c @@ -37,7 +37,7 @@ #include "sockunion.h" /* for inet_aton() */ #include "stream.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "zclient.h" #include "sbuf.h" @@ -457,11 +457,11 @@ int ospf_sr_local_block_release_label(mpls_label_t label) * * @return 1 on success */ -static void sr_start_label_manager(struct thread *start) +static void sr_start_label_manager(struct event *start) { struct ospf *ospf; - ospf = THREAD_ARG(start); + ospf = EVENT_ARG(start); /* re-attempt to start SR & Label Manager connection */ ospf_sr_start(ospf); @@ -496,8 +496,8 @@ static int ospf_sr_start(struct ospf *ospf) 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); + event_add_timer(master, sr_start_label_manager, ospf, 1, + &OspfSR.t_start_lm); osr_debug(" |- Failed to start the Label Manager"); return -1; } @@ -565,7 +565,7 @@ static void ospf_sr_stop(void) osr_debug("SR (%s): Stop Segment Routing", __func__); /* Disable any re-attempt to connect to Label Manager */ - THREAD_OFF(OspfSR.t_start_lm); + EVENT_OFF(OspfSR.t_start_lm); /* Release SRGB if active */ sr_global_block_delete(); @@ -639,10 +639,7 @@ void ospf_sr_term(void) /* Stop Segment Routing */ ospf_sr_stop(); - /* Clear SR Node Table */ - if (OspfSR.neighbors) - hash_free(OspfSR.neighbors); - + hash_clean_and_free(&OspfSR.neighbors, (void *)sr_node_del); } /* diff --git a/ospfd/ospf_sr.h b/ospfd/ospf_sr.h index 2c189083be0b..fa61f669a715 100644 --- a/ospfd/ospf_sr.h +++ b/ospfd/ospf_sr.h @@ -243,7 +243,7 @@ struct ospf_sr_db { uint8_t msd; /* Thread timer to start Label Manager */ - struct thread *t_start_lm; + struct event *t_start_lm; }; /* Structure aggregating all received SR info from LSAs by node */ diff --git a/ospfd/ospf_te.c b/ospfd/ospf_te.c index e3869abe5910..3cf39e5fb542 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -24,7 +24,7 @@ #include "vty.h" #include "stream.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ #include "network.h" @@ -1207,10 +1207,9 @@ static struct ospf_lsa *ospf_mpls_te_lsa_new(struct ospf *ospf, /* Now, create an OSPF LSA instance. */ new = ospf_lsa_new_and_data(length); - new->vrf_id = ospf->vrf_id; - if (area && area->ospf) - new->vrf_id = area->ospf->vrf_id; new->area = area; + new->vrf_id = VRF_DEFAULT; + SET_FLAG(new->flags, OSPF_LSA_SELF); memcpy(new->data, lsah, length); stream_free(s); @@ -1329,7 +1328,6 @@ static int ospf_mpls_te_lsa_originate2(struct ospf *top, __func__); return rc; } - new->vrf_id = top->vrf_id; /* Install this LSA into LSDB. */ if (ospf_lsa_install(top, NULL /*oi */, new) == NULL) { @@ -1482,7 +1480,7 @@ static struct ospf_lsa *ospf_mpls_te_lsa_refresh(struct ospf_lsa *lsa) ospf_opaque_lsa_flush_schedule(lsa); return NULL; } - top = ospf_lookup_by_vrf_id(lsa->vrf_id); + top = ospf_lookup_by_vrf_id(VRF_DEFAULT); /* Create new Opaque-LSA/MPLS-TE instance. */ new = ospf_mpls_te_lsa_new(top, area, lp); if (new == NULL) { @@ -1667,12 +1665,13 @@ static struct ls_vertex *get_vertex(struct ls_ted *ted, struct ospf_lsa *lsa) static struct ls_edge *get_edge(struct ls_ted *ted, struct ls_node_id adv, struct in_addr link_id) { - uint64_t key; + struct ls_edge_key key; struct ls_edge *edge; struct ls_attributes *attr; /* Search Edge that corresponds to the Link ID */ - key = ((uint64_t)ntohl(link_id.s_addr)) & 0xffffffff; + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &link_id); edge = ls_find_edge_by_key(ted, key); /* Create new one if not exist */ @@ -1781,7 +1780,7 @@ static void ospf_te_update_link(struct ls_ted *ted, struct ls_vertex *vertex, * @param metric Standard metric attached to this Edge */ static void ospf_te_update_subnet(struct ls_ted *ted, struct ls_vertex *vertex, - struct prefix p, uint8_t metric) + struct prefix *p, uint8_t metric) { struct ls_subnet *subnet; struct ls_prefix *ls_pref; @@ -1840,7 +1839,7 @@ static void ospf_te_delete_subnet(struct ls_ted *ted, struct in_addr addr) p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = addr; - subnet = ls_find_subnet(ted, p); + subnet = ls_find_subnet(ted, &p); /* Remove subnet if found */ if (subnet) { @@ -1933,7 +1932,7 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = rl->link[i].link_data; metric = ntohs(rl->link[i].metric); - ospf_te_update_subnet(ted, vertex, p, metric); + ospf_te_update_subnet(ted, vertex, &p, metric); break; case LSA_LINK_TYPE_STUB: /* Keep only /32 prefix */ @@ -1942,7 +1941,7 @@ static int ospf_te_parse_router_lsa(struct ls_ted *ted, struct ospf_lsa *lsa) p.family = AF_INET; p.u.prefix4 = rl->link[i].link_id; metric = ntohs(rl->link[i].metric); - ospf_te_update_subnet(ted, vertex, p, metric); + ospf_te_update_subnet(ted, vertex, &p, metric); } break; default: @@ -2074,12 +2073,12 @@ static void ospf_te_update_remote_asbr(struct ls_ted *ted, struct ls_edge *edge) p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = attr->standard.local; - ospf_te_update_subnet(ted, edge->source, p, attr->standard.te_metric); + ospf_te_update_subnet(ted, edge->source, &p, attr->standard.te_metric); p.family = AF_INET; p.prefixlen = IPV4_MAX_BITLEN; p.u.prefix4 = attr->standard.remote_addr; - ospf_te_update_subnet(ted, vertex, p, attr->standard.te_metric); + ospf_te_update_subnet(ted, vertex, &p, attr->standard.te_metric); /* Connect Edge to the remote Vertex */ if (edge->destination == NULL) { @@ -2352,7 +2351,7 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) struct ls_attributes *attr; struct tlv_header *tlvh; struct in_addr addr; - uint64_t key = 0; + struct ls_edge_key key = {.family = AF_UNSPEC}; uint16_t len, sum; uint8_t lsa_id; @@ -2368,12 +2367,13 @@ static int ospf_te_delete_te(struct ls_ted *ted, struct ospf_lsa *lsa) for (tlvh = TLV_DATA(tlvh); sum < len; tlvh = TLV_HDR_NEXT(tlvh)) { if (ntohs(tlvh->type) == TE_LINK_SUBTLV_LCLIF_IPADDR) { memcpy(&addr, TLV_DATA(tlvh), TE_LINK_SUBTLV_DEF_SIZE); - key = ((uint64_t)ntohl(addr.s_addr)) & 0xffffffff; + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &addr); break; } sum += TLV_SIZE(tlvh); } - if (key == 0) + if (key.family == AF_UNSPEC) return 0; /* Search Edge that corresponds to the Link ID */ @@ -2625,14 +2625,14 @@ static int ospf_te_parse_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) pref.family = AF_INET; pref.prefixlen = ext->pref_length; pref.u.prefix4 = ext->address; - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); /* Create new Link State Prefix if not found */ if (!subnet) { lnid.origin = OSPFv2; lnid.id.ip.addr = lsa->data->adv_router; lnid.id.ip.area_id = lsa->area->area_id; - ls_pref = ls_prefix_new(lnid, pref); + ls_pref = ls_prefix_new(lnid, &pref); /* and add it to the TED */ subnet = ls_subnet_add(ted, ls_pref); } @@ -2698,7 +2698,7 @@ static int ospf_te_delete_ext_pref(struct ls_ted *ted, struct ospf_lsa *lsa) pref.family = AF_INET; pref.prefixlen = ext->pref_length; pref.u.prefix4 = ext->address; - subnet = ls_find_subnet(ted, pref); + subnet = ls_find_subnet(ted, &pref); /* Check if there is a corresponding subnet */ if (!subnet) @@ -2864,11 +2864,12 @@ static int ospf_te_delete_ext_link(struct ls_ted *ted, struct ospf_lsa *lsa) struct ls_edge *edge; struct ls_attributes *atr; struct ext_tlv_link *ext; - uint64_t key; + struct ls_edge_key key; /* Search for corresponding Edge from Link State Data Base */ ext = (struct ext_tlv_link *)TLV_HDR_TOP(lsa->data); - key = ((uint64_t)ntohl(ext->link_data.s_addr)) & 0xffffffff; + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &ext->link_data); edge = ls_find_edge_by_key(ted, key); /* Check if there is a corresponding Edge */ @@ -3847,6 +3848,12 @@ DEFUN (ospf_mpls_te_on, if (OspfMplsTE.enabled) return CMD_SUCCESS; + /* Check that the OSPF is using default VRF */ + if (ospf->vrf_id != VRF_DEFAULT) { + vty_out(vty, "MPLS TE is only supported in default VRF\n"); + return CMD_WARNING_CONFIG_FAILED; + } + ote_debug("MPLS-TE: OFF -> ON"); OspfMplsTE.enabled = true; @@ -4229,12 +4236,10 @@ static void show_mpls_te_link_sub(struct vty *vty, struct interface *ifp) DEFUN (show_ip_ospf_mpls_te_link, show_ip_ospf_mpls_te_link_cmd, - "show ip ospf [vrf <NAME|all>] mpls-te interface [INTERFACE]", + "show ip ospf mpls-te interface [INTERFACE]", SHOW_STR IP_STR OSPF_STR - VRF_CMD_HELP_STR - "All VRFs\n" "MPLS-TE information\n" "Interface information\n" "Interface name\n") @@ -4242,43 +4247,18 @@ DEFUN (show_ip_ospf_mpls_te_link, struct vrf *vrf; int idx_interface = 0; struct interface *ifp = NULL; - struct listnode *node; - char *vrf_name = NULL; - bool all_vrf = false; - int inst = 0; - int idx_vrf = 0; struct ospf *ospf = NULL; - if (argv_find(argv, argc, "vrf", &idx_vrf)) { - vrf_name = argv[idx_vrf + 1]->arg; - all_vrf = strmatch(vrf_name, "all"); - } argv_find(argv, argc, "INTERFACE", &idx_interface); - /* vrf input is provided could be all or specific vrf*/ - if (vrf_name) { - if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { - if (!ospf->oi_running) - continue; - vrf = vrf_lookup_by_id(ospf->vrf_id); - FOR_ALL_INTERFACES (vrf, ifp) - show_mpls_te_link_sub(vty, ifp); - } - return CMD_SUCCESS; - } - ospf = ospf_lookup_by_inst_name(inst, vrf_name); - } else - ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); if (ospf == NULL || !ospf->oi_running) return CMD_SUCCESS; - vrf = vrf_lookup_by_id(ospf->vrf_id); + vrf = vrf_lookup_by_id(VRF_DEFAULT); if (!vrf) return CMD_SUCCESS; if (idx_interface) { - ifp = if_lookup_by_name( - argv[idx_interface]->arg, - ospf->vrf_id); + ifp = if_lookup_by_name(argv[idx_interface]->arg, VRF_DEFAULT); if (ifp == NULL) { vty_out(vty, "No such interface name in vrf %s\n", vrf->name); @@ -4321,6 +4301,7 @@ DEFUN (show_ip_ospf_mpls_te_db, struct ls_edge *edge; struct ls_subnet *subnet; uint64_t key; + struct ls_edge_key ekey; bool verbose = false; bool uj = use_json(argc, argv); json_object *json = NULL; @@ -4374,8 +4355,9 @@ DEFUN (show_ip_ospf_mpls_te_db, return CMD_WARNING_CONFIG_FAILED; } /* Get the Edge from the Link State Database */ - key = ((uint64_t)ntohl(ip_addr.s_addr)) & 0xffffffff; - edge = ls_find_edge_by_key(OspfMplsTE.ted, key); + ekey.family = AF_INET; + IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr); + edge = ls_find_edge_by_key(OspfMplsTE.ted, ekey); if (!edge) { vty_out(vty, "No edge found for ID %pI4\n", &ip_addr); @@ -4398,7 +4380,7 @@ DEFUN (show_ip_ospf_mpls_te_db, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(OspfMplsTE.ted, pref); + subnet = ls_find_subnet(OspfMplsTE.ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); diff --git a/ospfd/ospf_ti_lfa.c b/ospfd/ospf_ti_lfa.c index b14200868ee1..07fc50394782 100644 --- a/ospfd/ospf_ti_lfa.c +++ b/ospfd/ospf_ti_lfa.c @@ -237,7 +237,7 @@ static void ospf_ti_lfa_generate_inner_label_stack( struct q_space *q_space, struct ospf_ti_lfa_inner_backup_path_info *inner_backup_path_info) { - struct route_table *new_table, *new_rtrs; + struct route_table *new_table; struct vertex *q_node; struct vertex *start_vertex, *end_vertex; struct vertex_parent *vertex_parent; @@ -300,7 +300,6 @@ static void ospf_ti_lfa_generate_inner_label_stack( start_vertex, end_vertex); new_table = route_table_init(); - new_rtrs = route_table_init(); /* Copy the current state ... */ spf_orig = area->spf; @@ -311,7 +310,7 @@ static void ospf_ti_lfa_generate_inner_label_stack( XCALLOC(MTYPE_OSPF_P_SPACE, sizeof(struct p_spaces_head)); /* dry run true, root node false */ - ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, new_rtrs, + ospf_spf_calculate(area, start_vertex->lsa_p, new_table, NULL, NULL, true, false); q_node = ospf_spf_vertex_find(end_vertex->id, area->spf_vertex_list); @@ -622,7 +621,7 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area, { struct listnode *node; struct vertex *child; - struct route_table *new_table, *new_rtrs; + struct route_table *new_table; struct q_space *q_space, q_space_search; char label_buf[MPLS_LABEL_STRLEN]; char res_buf[PROTECTED_RESOURCE_STRLEN]; @@ -661,15 +660,13 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area, sizeof(struct ospf_ti_lfa_node_info)); new_table = route_table_init(); - /* XXX do these get freed?? */ - new_rtrs = route_table_init(); /* * Generate a new (reversed!) SPF tree for this vertex, * dry run true, root node false */ area->spf_reversed = true; - ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, new_rtrs, true, + ospf_spf_calculate(area, dest->lsa_p, new_table, NULL, NULL, true, false); /* Reset the flag for reverse SPF */ @@ -692,6 +689,11 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area, "%s: NO backup path found for root %pI4 and destination %pI4 for %s, aborting ...", __func__, &p_space->root->id, &q_space->root->id, res_buf); + + XFREE(MTYPE_OSPF_Q_SPACE, q_space->p_node_info); + XFREE(MTYPE_OSPF_Q_SPACE, q_space->q_node_info); + XFREE(MTYPE_OSPF_Q_SPACE, q_space); + return; } @@ -734,11 +736,9 @@ static void ospf_ti_lfa_generate_q_spaces(struct ospf_area *area, static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area, struct p_space *p_space) { - struct route_table *new_table, *new_rtrs; + struct route_table *new_table; new_table = route_table_init(); - /* XXX do these get freed?? */ - new_rtrs = route_table_init(); area->spf_protected_resource = p_space->protected_resource; @@ -757,8 +757,8 @@ static void ospf_ti_lfa_generate_post_convergence_spf(struct ospf_area *area, * endeavour (because LSAs are stored as a 'raw' stream), so we go with * this rather hacky way for now. */ - ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL, - new_rtrs, true, false); + ospf_spf_calculate(area, area->router_lsa_self, new_table, NULL, NULL, + true, false); p_space->pc_spf = area->spf; p_space->pc_vertex_list = area->spf_vertex_list; @@ -1078,6 +1078,7 @@ void ospf_ti_lfa_free_p_spaces(struct ospf_area *area) q_spaces_fini(p_space->q_spaces); XFREE(MTYPE_OSPF_Q_SPACE, p_space->q_spaces); + XFREE(MTYPE_OSPF_P_SPACE, p_space); } p_spaces_fini(area->p_spaces); diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index eb03a9c3a737..54fd60af23e3 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -10,7 +10,7 @@ #include "printfrr.h" #include "monotime.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "table.h" #include "vty.h" @@ -35,12 +35,11 @@ #include "ospfd/ospf_spf.h" #include "ospfd/ospf_route.h" #include "ospfd/ospf_zebra.h" -/*#include "ospfd/ospf_routemap.h" */ #include "ospfd/ospf_vty.h" #include "ospfd/ospf_dump.h" #include "ospfd/ospf_bfd.h" #include "ospfd/ospf_ldp_sync.h" - +#include "ospfd/ospf_network.h" FRR_CFG_DEFAULT_BOOL(OSPF_LOG_ADJACENCY_CHANGES, { .val_bool = true, .match_profile = "datacenter", }, @@ -233,9 +232,12 @@ DEFUN (no_router_ospf, return CMD_NOT_MY_INSTANCE; ospf = ospf_lookup(instance, vrf_name); - if (ospf) + if (ospf) { + if (ospf->gr_info.restart_support) + ospf_gr_nvm_delete(ospf); + ospf_finish(ospf); - else + } else ret = CMD_WARNING_CONFIG_FAILED; return ret; @@ -611,6 +613,7 @@ DEFUN (ospf_area_range, "Advertised metric for this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 1; int idx_ipv4_prefixlen = 3; int idx_cost = 6; @@ -622,12 +625,14 @@ DEFUN (ospf_area_range, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); 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); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, + OSPF_AREA_RANGE_ADVERTISE, false); if (argc > 5) { cost = strtoul(argv[idx_cost]->arg, NULL, 10); - ospf_area_range_cost_set(ospf, area_id, &p, cost); + ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost); } return CMD_SUCCESS; @@ -647,6 +652,7 @@ DEFUN (ospf_area_range_cost, "Network prefix to be announced instead of range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 1; int idx_ipv4_prefixlen = 3; int idx = 4; @@ -658,19 +664,20 @@ DEFUN (ospf_area_range_cost, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); 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); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + ospf_area_range_set(ospf, area, area->ranges, &p, + OSPF_AREA_RANGE_ADVERTISE, false); if (argv_find(argv, argc, "cost", &idx)) { cost = strtoul(argv[idx + 1]->arg, NULL, 10); - ospf_area_range_cost_set(ospf, area_id, &p, cost); + ospf_area_range_cost_set(ospf, area, area->ranges, &p, cost); } idx = 4; if (argv_find(argv, argc, "substitute", &idx)) { str2prefix_ipv4(argv[idx + 1]->arg, &s); - ospf_area_range_substitute_set(ospf, area_id, &p, &s); + ospf_area_range_substitute_set(ospf, area, &p, &s); } return CMD_SUCCESS; @@ -687,6 +694,7 @@ DEFUN (ospf_area_range_not_advertise, "DoNotAdvertise this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 1; int idx_ipv4_prefixlen = 3; struct prefix_ipv4 p; @@ -696,10 +704,11 @@ DEFUN (ospf_area_range_not_advertise, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); - ospf_area_range_set(ospf, area_id, &p, 0); - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_range_substitute_unset(ospf, area_id, &p); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_set(ospf, area, area->ranges, &p, 0, false); + ospf_area_range_substitute_unset(ospf, area, &p); return CMD_SUCCESS; } @@ -721,6 +730,7 @@ DEFUN (no_ospf_area_range, "DoNotAdvertise this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 2; int idx_ipv4_prefixlen = 4; struct prefix_ipv4 p; @@ -730,7 +740,10 @@ DEFUN (no_ospf_area_range, VTY_GET_OSPF_AREA_ID(area_id, format, argv[idx_ipv4_number]->arg); str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); - ospf_area_range_unset(ospf, area_id, &p); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_unset(ospf, area, area->ranges, &p); return CMD_SUCCESS; } @@ -748,6 +761,7 @@ DEFUN (no_ospf_area_range_substitute, "Network prefix to be announced instead of range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct ospf_area *area; int idx_ipv4_number = 2; int idx_ipv4_prefixlen = 4; int idx_ipv4_prefixlen_2 = 6; @@ -759,7 +773,10 @@ DEFUN (no_ospf_area_range_substitute, str2prefix_ipv4(argv[idx_ipv4_prefixlen]->arg, &p); str2prefix_ipv4(argv[idx_ipv4_prefixlen_2]->arg, &s); - ospf_area_range_substitute_unset(ospf, area_id, &p); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); + + ospf_area_range_substitute_unset(ospf, area, &p); return CMD_SUCCESS; } @@ -1433,15 +1450,35 @@ DEFUN (no_ospf_area_stub_no_summary, return CMD_SUCCESS; } -static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, - struct cmd_token **argv, int cfg_nosum, - int nosum) +DEFPY (ospf_area_nssa, + ospf_area_nssa_cmd, + "area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + <translate-candidate|translate-never|translate-always>$translator_role\ + |default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\ + |no-summary$no_summary\ + |suppress-fa$suppress_fa\ + }]", + "OSPF area parameters\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Configure OSPF area as nssa\n" + "Configure NSSA-ABR for translate election (default)\n" + "Configure NSSA-ABR to never translate\n" + "Configure NSSA-ABR to always translate\n" + "Originate Type 7 default into NSSA area\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Do not inject inter-area routes into nssa\n" + "Suppress forwarding address\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); struct in_addr area_id; int ret, format; - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, argv[1]->arg); + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str); ret = ospf_area_nssa_set(ospf, area_id); ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), @@ -1452,14 +1489,14 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, return CMD_WARNING_CONFIG_FAILED; } - if (argc > 3) { - if (strncmp(argv[3]->text, "translate-c", 11) == 0) + if (translator_role) { + if (strncmp(translator_role, "translate-c", 11) == 0) ospf_area_nssa_translator_role_set( ospf, area_id, OSPF_NSSA_ROLE_CANDIDATE); - else if (strncmp(argv[3]->text, "translate-n", 11) == 0) + else if (strncmp(translator_role, "translate-n", 11) == 0) ospf_area_nssa_translator_role_set( ospf, area_id, OSPF_NSSA_ROLE_NEVER); - else if (strncmp(argv[3]->text, "translate-a", 11) == 0) + else if (strncmp(translator_role, "translate-a", 11) == 0) ospf_area_nssa_translator_role_set( ospf, area_id, OSPF_NSSA_ROLE_ALWAYS); } else { @@ -1467,12 +1504,27 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, OSPF_NSSA_ROLE_CANDIDATE); } - if (cfg_nosum) { - if (nosum) - ospf_area_no_summary_set(ospf, area_id); - else - ospf_area_no_summary_unset(ospf, area_id); - } + if (dflt_originate) { + int metric_type = DEFAULT_METRIC_TYPE; + + if (mval_str == NULL) + mval = -1; + if (mtype_str) + (void)str2metric_type(mtype_str, &metric_type); + ospf_area_nssa_default_originate_set(ospf, area_id, mval, + metric_type); + } else + ospf_area_nssa_default_originate_unset(ospf, area_id); + + if (no_summary) + ospf_area_nssa_no_summary_set(ospf, area_id); + else + ospf_area_no_summary_unset(ospf, area_id); + + if (suppress_fa) + ospf_area_nssa_suppress_fa_set(ospf, area_id); + else + ospf_area_nssa_suppress_fa_unset(ospf, area_id); /* Flush the external LSA for the specified area */ ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_EXTERNAL_LSA); @@ -1482,168 +1534,125 @@ static int ospf_area_nssa_cmd_handler(struct vty *vty, int argc, return CMD_SUCCESS; } - -DEFUN (ospf_area_nssa_translate, - ospf_area_nssa_translate_cmd, - "area <A.B.C.D|(0-4294967295)> nssa <translate-candidate|translate-never|translate-always>", +DEFPY (no_ospf_area_nssa, + no_ospf_area_nssa_cmd, + "no area <A.B.C.D|(0-4294967295)>$area_str nssa\ + [{\ + <translate-candidate|translate-never|translate-always>\ + |default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\ + |no-summary\ + |suppress-fa\ + }]", + NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Configure OSPF area as nssa\n" "Configure NSSA-ABR for translate election (default)\n" "Configure NSSA-ABR to never translate\n" - "Configure NSSA-ABR to always translate\n") -{ - return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0); -} - -DEFUN (ospf_area_nssa, - ospf_area_nssa_cmd, - "area <A.B.C.D|(0-4294967295)> nssa", - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n") -{ - return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0); -} - -DEFUN(ospf_area_nssa_suppress_fa, ospf_area_nssa_suppress_fa_cmd, - "area <A.B.C.D|(0-4294967295)> nssa suppress-fa", - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n" - "Suppress forwarding address\n") + "Configure NSSA-ABR to always translate\n" + "Originate Type 7 default into NSSA area\n" + "OSPF default metric\n" + "OSPF metric\n" + "OSPF metric type for default routes\n" + "Set OSPF External Type 1/2 metrics\n" + "Do not inject inter-area routes into nssa\n" + "Suppress forwarding address\n") { - int idx_ipv4_number = 1; - struct in_addr area_id; - int format; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, - argv[idx_ipv4_number]->arg); - - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_nssa_suppress_fa_set(ospf, area_id); - - ospf_schedule_abr_task(ospf); - - return CMD_SUCCESS; -} - -DEFUN(no_ospf_area_nssa_suppress_fa, no_ospf_area_nssa_suppress_fa_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa suppress-fa", - NO_STR - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n" - "Suppress forwarding address\n") -{ - int idx_ipv4_number = 2; struct in_addr area_id; int format; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - - VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format, - argv[idx_ipv4_number]->arg); + VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, area_str); - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); + /* Flush the NSSA LSA for the specified area */ + ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA); + ospf_area_no_summary_unset(ospf, area_id); + ospf_area_nssa_default_originate_unset(ospf, area_id); ospf_area_nssa_suppress_fa_unset(ospf, area_id); + ospf_area_nssa_unset(ospf, area_id); ospf_schedule_abr_task(ospf); return CMD_SUCCESS; } -DEFUN (ospf_area_nssa_no_summary, - ospf_area_nssa_no_summary_cmd, - "area <A.B.C.D|(0-4294967295)> nssa no-summary", +DEFPY (ospf_area_nssa_range, + ospf_area_nssa_range_cmd, + "area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise$not_adv|cost (0-16777215)$cost>]", "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Configure OSPF area as nssa\n" - "Do not inject inter-area routes into nssa\n") + "Configured address range\n" + "Specify IPv4 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") { - int idx_ipv4_number = 1; - struct in_addr area_id; - int format; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, - argv[idx_ipv4_number]->arg); - - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_nssa_no_summary_set(ospf, area_id); - - ospf_schedule_abr_task(ospf); - - return CMD_SUCCESS; -} - -DEFUN (no_ospf_area_nssa_no_summary, - no_ospf_area_nssa_no_summary_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa no-summary", - NO_STR - "OSPF area parameters\n" - "OSPF area ID in IP address format\n" - "OSPF area ID as a decimal value\n" - "Configure OSPF area as nssa\n" - "Do not inject inter-area routes into nssa\n") -{ - int idx_ipv4_number = 2; + struct ospf_area *area; struct in_addr area_id; int format; + int advertise = 0; - VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + VTY_GET_OSPF_AREA_ID(area_id, format, area_str); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); - VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format, - argv[idx_ipv4_number]->arg); + if (area->external_routing != OSPF_AREA_NSSA) { + vty_out(vty, "%% First configure %s as an NSSA area\n", + area_str); + return CMD_WARNING; + } - ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id), - format); - ospf_area_no_summary_unset(ospf, area_id); + if (!not_adv) + advertise = OSPF_AREA_RANGE_ADVERTISE; - ospf_schedule_abr_task(ospf); + ospf_area_range_set(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix, advertise, true); + if (cost_str) + ospf_area_range_cost_set(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix, cost); return CMD_SUCCESS; } -DEFUN (no_ospf_area_nssa, - no_ospf_area_nssa_cmd, - "no area <A.B.C.D|(0-4294967295)> nssa [<translate-candidate|translate-never|translate-always>]", +DEFPY (no_ospf_area_nssa_range, + no_ospf_area_nssa_range_cmd, + "no area <A.B.C.D|(0-4294967295)>$area_str nssa range A.B.C.D/M$prefix [<not-advertise|cost (0-16777215)>]", NO_STR "OSPF area parameters\n" "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n" "Configure OSPF area as nssa\n" - "Configure NSSA-ABR for translate election (default)\n" - "Configure NSSA-ABR to never translate\n" - "Configure NSSA-ABR to always translate\n") + "Configured address range\n" + "Specify IPv4 prefix\n" + "Do not advertise\n" + "User specified metric for this range\n" + "Advertised metric for this range\n") { VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); - int idx_ipv4_number = 2; + struct ospf_area *area; struct in_addr area_id; int format; - VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format, - argv[idx_ipv4_number]->arg); + VTY_GET_OSPF_AREA_ID(area_id, format, area_str); + area = ospf_area_get(ospf, area_id); + ospf_area_display_format_set(ospf, area, format); - /* 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); + if (area->external_routing != OSPF_AREA_NSSA) { + vty_out(vty, "%% First configure %s as an NSSA area\n", + area_str); + return CMD_WARNING; + } - ospf_schedule_abr_task(ospf); + ospf_area_range_unset(ospf, area, area->nssa_ranges, + (struct prefix_ipv4 *)prefix); return CMD_SUCCESS; } - DEFUN (ospf_area_default_cost, ospf_area_default_cost_cmd, "area <A.B.C.D|(0-4294967295)> default-cost (0-16777215)", @@ -3396,6 +3405,23 @@ static int show_ip_ospf_common(struct vty *vty, struct ospf *ospf, /* show LDP-Sync status */ ospf_ldp_sync_show_info(vty, ospf, json_vrf, json ? 1 : 0); + /* Socket buffer sizes */ + if (json) { + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + json_object_int_add(json_vrf, "recvSockBufsize", + ospf->recv_sock_bufsize); + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + json_object_int_add(json_vrf, "sendSockBufsize", + ospf->send_sock_bufsize); + } else { + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + vty_out(vty, " Receive socket bufsize: %u\n", + ospf->recv_sock_bufsize); + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) + vty_out(vty, " Send socket bufsize: %u\n", + ospf->send_sock_bufsize); + } + /* Show each area status. */ for (ALL_LIST_ELEMENTS(ospf->areas, node, nnode, area)) show_ip_ospf_area(vty, area, json_areas, json ? 1 : 0); @@ -3594,6 +3620,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, struct ospf_neighbor *nbr; struct route_node *rn; uint32_t bandwidth = ifp->bandwidth ? ifp->bandwidth : ifp->speed; + struct ospf_if_params *params; + json_object *json_ois = NULL; + json_object *json_oi = NULL; /* Is interface up? */ if (use_json) { @@ -3644,17 +3673,33 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, } } + if (use_json) { + json_ois = json_object_new_object(); + json_object_object_add(json_interface_sub, "interfaceIp", + json_ois); + } + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { struct ospf_interface *oi = rn->info; if (oi == NULL) continue; +#if CONFDATE > 20240601 + CPP_NOTICE( + "Use all fields following ospfEnabled from interfaceIp hierarchy") +#endif + + if (use_json) + json_oi = json_object_new_object(); + if (CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED)) { - if (use_json) + if (use_json) { json_object_boolean_true_add(json_interface_sub, "ifUnnumbered"); - else + json_object_boolean_true_add(json_oi, + "ifUnnumbered"); + } else vty_out(vty, " This interface is UNNUMBERED,"); } else { struct in_addr dest; @@ -3668,6 +3713,13 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, json_object_int_add(json_interface_sub, "ipAddressPrefixlen", oi->address->prefixlen); + + json_object_string_addf( + json_oi, "ipAddress", "%pI4", + &oi->address->u.prefix4); + json_object_int_add(json_oi, + "ipAddressPrefixlen", + oi->address->prefixlen); } else vty_out(vty, " Internet Address %pFX,", oi->address); @@ -3690,17 +3742,29 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, } 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, + "ospfIfType", dstr); + + json_object_string_add(json_oi, "ospfIfType", + dstr); + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) { json_object_string_addf( json_interface_sub, "vlinkPeer", "%pI4", &dest); - else + + json_object_string_addf(json_oi, + "vlinkPeer", + "%pI4", &dest); + } else { json_object_string_addf( json_interface_sub, "localIfUsed", "%pI4", &dest); + + json_object_string_addf(json_oi, + "localIfUsed", + "%pI4", &dest); + } } else vty_out(vty, " %s %pI4,", dstr, &dest); @@ -3708,10 +3772,18 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, if (use_json) { json_object_string_add(json_interface_sub, "area", ospf_area_desc_string(oi->area)); - if (OSPF_IF_PARAM(oi, mtu_ignore)) + + json_object_string_add(json_oi, "area", + ospf_area_desc_string(oi->area)); + + if (OSPF_IF_PARAM(oi, mtu_ignore)) { + json_object_boolean_true_add( + json_oi, "mtuMismatchDetect"); json_object_boolean_true_add( json_interface_sub, "mtuMismatchDetect"); + } + json_object_string_addf(json_interface_sub, "routerId", "%pI4", &ospf->router_id); json_object_string_add(json_interface_sub, @@ -3719,14 +3791,29 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, ospf_network_type_str[oi->type]); json_object_int_add(json_interface_sub, "cost", oi->output_cost); - json_object_int_add( - json_interface_sub, "transmitDelaySecs", - OSPF_IF_PARAM(oi, transmit_delay)); + json_object_int_add(json_interface_sub, + "transmitDelaySecs", + OSPF_IF_PARAM(oi, transmit_delay)); json_object_string_add(json_interface_sub, "state", lookup_msg(ospf_ism_state_msg, oi->state, NULL)); json_object_int_add(json_interface_sub, "priority", PRIORITY(oi)); + + json_object_string_addf(json_oi, "routerId", "%pI4", + &ospf->router_id); + json_object_string_add(json_oi, "networkType", + ospf_network_type_str[oi->type]); + json_object_int_add(json_oi, "cost", oi->output_cost); + json_object_int_add(json_oi, "transmitDelaySecs", + OSPF_IF_PARAM(oi, transmit_delay)); + json_object_string_add(json_oi, "state", + lookup_msg(ospf_ism_state_msg, + oi->state, NULL)); + json_object_int_add(json_oi, "priority", PRIORITY(oi)); + json_object_boolean_add( + json_interface_sub, "opaqueCapable", + OSPF_IF_PARAM(oi, opaque_capable)); } else { vty_out(vty, " Area %s\n", ospf_area_desc_string(oi->area)); @@ -3746,6 +3833,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, OSPF_IF_PARAM(oi, transmit_delay), lookup_msg(ospf_ism_state_msg, oi->state, NULL), PRIORITY(oi)); + if (!OSPF_IF_PARAM(oi, opaque_capable)) + vty_out(vty, + " Opaque LSA capability disabled on interface\n"); } /* Show DR information. */ @@ -3764,6 +3854,13 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, json_interface_sub, "drAddress", "%pI4", &nbr->address.u.prefix4); + + json_object_string_addf( + json_oi, "drId", "%pI4", + &nbr->router_id); + json_object_string_addf( + json_oi, "drAddress", "%pI4", + &nbr->address.u.prefix4); } else { vty_out(vty, " Designated Router (ID) %pI4", @@ -3789,6 +3886,13 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, json_interface_sub, "bdrAddress", "%pI4", &nbr->address.u.prefix4); + + json_object_string_addf( + json_oi, "bdrId", "%pI4", + &nbr->router_id); + json_object_string_addf( + json_oi, "bdrAddress", "%pI4", + &nbr->address.u.prefix4); } else { vty_out(vty, " Backup Designated Router (ID) %pI4,", @@ -3804,28 +3908,43 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, if (oi->params && ntohl(oi->params->network_lsa_seqnum) != OSPF_INITIAL_SEQUENCE_NUMBER) { - if (use_json) + if (use_json) { json_object_int_add( json_interface_sub, "networkLsaSequence", ntohl(oi->params->network_lsa_seqnum)); - else + + json_object_int_add( + json_oi, "networkLsaSequence", + ntohl(oi->params->network_lsa_seqnum)); + } else { vty_out(vty, " Saved Network-LSA sequence number 0x%x\n", ntohl(oi->params->network_lsa_seqnum)); + } } if (use_json) { if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS) || OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { - if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) + if (OI_MEMBER_CHECK(oi, MEMBER_ALLROUTERS)) { json_object_boolean_true_add( json_interface_sub, "mcastMemberOspfAllRouters"); - if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) + + json_object_boolean_true_add( + json_oi, + "mcastMemberOspfAllRouters"); + } + if (OI_MEMBER_CHECK(oi, MEMBER_DROUTERS)) { json_object_boolean_true_add( json_interface_sub, "mcastMemberOspfDesignatedRouters"); + + json_object_boolean_true_add( + json_oi, + "mcastMemberOspfDesignatedRouters"); + } } } else { vty_out(vty, " Multicast group memberships:"); @@ -3841,23 +3960,38 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, } if (use_json) { - if (OSPF_IF_PARAM(oi, fast_hello) == 0) + if (OSPF_IF_PARAM(oi, fast_hello) == 0) { json_object_int_add( json_interface_sub, "timerMsecs", OSPF_IF_PARAM(oi, v_hello) * 1000); - else + + json_object_int_add(json_oi, "timerMsecs", + OSPF_IF_PARAM(oi, v_hello) * + 1000); + } else { json_object_int_add( json_interface_sub, "timerMsecs", 1000 / OSPF_IF_PARAM(oi, fast_hello)); - json_object_int_add(json_interface_sub, - "timerDeadSecs", + + json_object_int_add( + json_oi, "timerMsecs", + 1000 / OSPF_IF_PARAM(oi, fast_hello)); + } + json_object_int_add(json_interface_sub, "timerDeadSecs", OSPF_IF_PARAM(oi, v_wait)); - json_object_int_add(json_interface_sub, - "timerWaitSecs", + json_object_int_add(json_interface_sub, "timerWaitSecs", OSPF_IF_PARAM(oi, v_wait)); json_object_int_add( json_interface_sub, "timerRetransmitSecs", OSPF_IF_PARAM(oi, retransmit_interval)); + + json_object_int_add(json_oi, "timerDeadSecs", + OSPF_IF_PARAM(oi, v_wait)); + json_object_int_add(json_oi, "timerWaitSecs", + OSPF_IF_PARAM(oi, v_wait)); + json_object_int_add( + json_oi, "timerRetransmitSecs", + OSPF_IF_PARAM(oi, retransmit_interval)); } else { vty_out(vty, " Timer intervals configured,"); vty_out(vty, " Hello "); @@ -3886,17 +4020,23 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, json_object_int_add(json_interface_sub, "timerHelloInMsecs", time_store); + json_object_int_add(json_oi, + "timerHelloInMsecs", + time_store); } else vty_out(vty, " Hello due in %s\n", ospf_timer_dump(oi->t_hello, timebuf, sizeof(timebuf))); } else /* passive-interface is set */ { - if (use_json) + if (use_json) { json_object_boolean_true_add( json_interface_sub, "timerPassiveIface"); - else + + json_object_boolean_true_add( + json_oi, "timerPassiveIface"); + } else vty_out(vty, " No Hellos (Passive interface)\n"); } @@ -3907,17 +4047,61 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf, json_object_int_add(json_interface_sub, "nbrAdjacentCount", ospf_nbr_count(oi, NSM_Full)); + + json_object_int_add(json_oi, "nbrCount", + ospf_nbr_count(oi, 0)); + json_object_int_add(json_oi, "nbrAdjacentCount", + ospf_nbr_count(oi, NSM_Full)); } else vty_out(vty, " Neighbor Count is %d, Adjacent neighbor count is %d\n", ospf_nbr_count(oi, 0), ospf_nbr_count(oi, NSM_Full)); + params = IF_DEF_PARAMS(ifp); + if (params && + OSPF_IF_PARAM_CONFIGURED(params, v_gr_hello_delay)) { + if (use_json) { + json_object_int_add(json_interface_sub, + "grHelloDelaySecs", + params->v_gr_hello_delay); + + json_object_int_add(json_oi, "grHelloDelaySecs", + params->v_gr_hello_delay); + } else + vty_out(vty, + " Graceful Restart hello delay: %us\n", + params->v_gr_hello_delay); + } + ospf_interface_bfd_show(vty, ifp, json_interface_sub); /* OSPF Authentication information */ ospf_interface_auth_show(vty, oi, json_interface_sub, use_json); - } + + ospf_interface_auth_show(vty, oi, json_oi, use_json); + if (oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) { + if (use_json) { + json_object_boolean_add(json_interface_sub, + "p2mpDelayReflood", + oi->p2mp_delay_reflood); + + json_object_boolean_add(json_oi, + "p2mpDelayReflood", + oi->p2mp_delay_reflood); + } else { + vty_out(vty, + " %sDelay reflooding LSAs received on P2MP interface\n", + oi->p2mp_delay_reflood ? "" : "Don't "); + } + } + + /* Add ospf_interface object to main json blob using SIP as key + */ + if (use_json) + json_object_object_addf(json_ois, json_oi, "%pI4", + &oi->address->u.prefix4); + } } static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, @@ -3973,16 +4157,15 @@ static int show_ip_ospf_interface_common(struct vty *vty, struct ospf *ospf, /* Interface name is specified. */ ifp = if_lookup_by_name(intf_name, ospf->vrf_id); if (ifp == NULL) { - if (use_json) + if (use_json) { json_object_boolean_true_add(json_vrf, "noSuchIface"); - else + json_object_free(json_interface); + } else vty_out(vty, "No such interface name\n"); } else { - if (use_json) { + if (use_json) json_interface_sub = json_object_new_object(); - json_interface = json_object_new_object(); - } show_ip_ospf_interface_sub( vty, ospf, ifp, json_interface_sub, use_json); @@ -4125,6 +4308,9 @@ static int show_ip_ospf_interface_traffic_common( rn = route_next(rn)) { oi = rn->info; + if (oi == NULL) + continue; + if (use_json) { json_interface_sub = json_object_new_object(); @@ -4407,15 +4593,10 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty, json_neighbor = json_object_new_object(); ospf_nbr_ism_state_message(nbr, msgbuf, sizeof(msgbuf)); -#if CONFDATE > 20230321 - CPP_NOTICE( - "Remove show_ip_ospf_neighbor_sub() JSON keys: priority, state, deadTimeMsecs, address, retransmitCounter, requestCounter, dbSummaryCounter") -#endif - json_object_int_add(json_neighbor, "priority", nbr->priority); - json_object_string_add(json_neighbor, "state", msgbuf); + json_object_string_add(json_neighbor, "nbrState", msgbuf); + json_object_int_add(json_neighbor, "nbrPriority", nbr->priority); - json_object_string_add(json_neighbor, "nbrState", msgbuf); json_object_string_add( json_neighbor, "converged", @@ -4432,8 +4613,6 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty, 1000LL; json_object_int_add(json_neighbor, "upTimeInMsec", time_val); - json_object_int_add(json_neighbor, "deadTimeMsecs", - time_store); json_object_int_add(json_neighbor, "routerDeadIntervalTimerDueMsec", time_store); @@ -4452,24 +4631,16 @@ static void show_ip_ospf_neighbour_brief(struct vty *vty, "routerDeadIntervalTimerDueMsec", "inactive"); } - json_object_string_addf(json_neighbor, "address", "%pI4", - &nbr->src); json_object_string_addf(json_neighbor, "ifaceAddress", "%pI4", &nbr->src); json_object_string_add(json_neighbor, "ifaceName", IF_NAME(nbr->oi)); - json_object_int_add(json_neighbor, "retransmitCounter", - ospf_ls_retransmit_count(nbr)); json_object_int_add(json_neighbor, "linkStateRetransmissionListCounter", ospf_ls_retransmit_count(nbr)); - json_object_int_add(json_neighbor, "requestCounter", - ospf_ls_request_count(nbr)); json_object_int_add(json_neighbor, "linkStateRequestListCounter", ospf_ls_request_count(nbr)); - json_object_int_add(json_neighbor, "dbSummaryCounter", - ospf_db_summary_count(nbr)); json_object_int_add(json_neighbor, "databaseSummaryListCounter", ospf_db_summary_count(nbr)); @@ -5065,6 +5236,7 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, json_object *json_neigh = NULL, *json_neigh_array = NULL; char neigh_str[INET_ADDRSTRLEN] = {0}; char neigh_state[16] = {0}; + struct ospf_neighbor *nbr_dr, *nbr_bdr; if (use_json) { if (prev_nbr && @@ -5192,19 +5364,38 @@ static void show_ip_ospf_neighbor_detail_sub(struct vty *vty, } } - /* Show Designated Rotuer ID. */ - if (use_json) - json_object_string_addf(json_neigh, "routerDesignatedId", - "%pI4", &nbr->d_router); - else - vty_out(vty, " DR is %pI4,", &nbr->d_router); + /* Show Designated Router ID. */ + if (DR(oi).s_addr == INADDR_ANY) { + if (!use_json) + vty_out(vty, + " No designated router on this network\n"); + } else { + nbr_dr = ospf_nbr_lookup_by_addr(oi->nbrs, &DR(oi)); + if (nbr_dr) { + if (use_json) + json_object_string_addf( + json_neigh, "routerDesignatedId", + "%pI4", &nbr_dr->router_id); + else + vty_out(vty, " DR is %pI4,", + &nbr_dr->router_id); + } + } - /* Show Backup Designated Rotuer ID. */ - if (use_json) - json_object_string_addf(json_neigh, "routerDesignatedBackupId", - "%pI4", &nbr->bd_router); - else - vty_out(vty, " BDR is %pI4\n", &nbr->bd_router); + /* Show Backup Designated Router ID. */ + nbr_bdr = ospf_nbr_lookup_by_addr(oi->nbrs, &BDR(oi)); + if (nbr_bdr == NULL) { + if (!use_json) + vty_out(vty, + " No backup designated router on this network\n"); + } else { + if (use_json) + json_object_string_addf(json_neigh, + "routerDesignatedBackupId", + "%pI4", &nbr_bdr->router_id); + else + vty_out(vty, " BDR is %pI4\n", &nbr_bdr->router_id); + } /* Show options. */ if (use_json) { @@ -6800,31 +6991,24 @@ static void show_lsa_detail_adv_router_proc(struct vty *vty, struct in_addr *adv_router, json_object *json) { - char buf[PREFIX_STRLEN]; struct route_node *rn; struct ospf_lsa *lsa; + json_object *json_lsa = NULL; for (rn = route_top(rt); rn; rn = route_next(rn)) if ((lsa = rn->info)) { - json_object *json_lsa = NULL; - if (IPV4_ADDR_SAME(adv_router, &lsa->data->adv_router)) { if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) continue; - if (json) + if (json) { json_lsa = json_object_new_object(); + json_object_array_add(json, json_lsa); + } if (show_function[lsa->data->type] != NULL) show_function[lsa->data->type]( vty, lsa, json_lsa); - if (json) - json_object_object_add( - json, - inet_ntop(AF_INET, - &lsa->data->id, - buf, sizeof(buf)), - json_lsa); } } } @@ -6837,11 +7021,12 @@ static void show_lsa_detail_adv_router(struct vty *vty, struct ospf *ospf, struct listnode *node; struct ospf_area *area; char buf[PREFIX_STRLEN]; - json_object *json_lstype = NULL; - json_object *json_area = NULL; + json_object *json_lsa_type = NULL; + json_object *json_areas = NULL; + json_object *json_lsa_array = NULL; if (json) - json_lstype = json_object_new_object(); + json_lsa_type = json_object_new_object(); switch (type) { case OSPF_AS_EXTERNAL_LSA: @@ -6849,38 +7034,49 @@ static void show_lsa_detail_adv_router(struct vty *vty, struct ospf *ospf, if (!json) vty_out(vty, " %s \n\n", show_database_desc[type]); + else + json_lsa_array = json_object_new_array(); show_lsa_detail_adv_router_proc(vty, AS_LSDB(ospf, type), - adv_router, json_lstype); + adv_router, json_lsa_array); + if (json) + json_object_object_add(json, + show_database_desc_json[type], + json_lsa_array); break; default: + if (json) + json_areas = json_object_new_object(); for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { - if (json) - json_area = json_object_new_object(); - else + if (!json) { vty_out(vty, "\n %s (Area %s)\n\n", show_database_desc[type], ospf_area_desc_string(area)); - show_lsa_detail_adv_router_proc(vty, - AREA_LSDB(area, type), - adv_router, json_area); + } else { + json_lsa_array = json_object_new_array(); + json_object_object_add( + json_areas, + inet_ntop(AF_INET, &area->area_id, buf, + sizeof(buf)), + json_lsa_array); + } - if (json) - json_object_object_add(json_lstype, - inet_ntop(AF_INET, - &area->area_id, - buf, - sizeof(buf)), - json_area); + show_lsa_detail_adv_router_proc( + vty, AREA_LSDB(area, type), adv_router, + json_lsa_array); + } + + if (json) { + json_object_object_add(json_lsa_type, "areas", + json_areas); + json_object_object_add(json, + show_database_desc_json[type], + json_lsa_type); } break; } - - if (json) - json_object_object_add(json, show_database_desc[type], - json_lstype); } void show_ip_ospf_database_summary(struct vty *vty, struct ospf *ospf, int self, @@ -7084,15 +7280,13 @@ static void show_ip_ospf_database_maxage(struct vty *vty, struct ospf *ospf, OSPF_LSA_TYPE_OPAQUE_LINK_DESC OSPF_LSA_TYPE_OPAQUE_AREA_DESC \ OSPF_LSA_TYPE_OPAQUE_AS_DESC -static int show_ip_ospf_database_common(struct vty *vty, struct ospf *ospf, - int arg_base, int argc, - struct cmd_token **argv, - uint8_t use_vrf, json_object *json, - bool uj) +static int +show_ip_ospf_database_common(struct vty *vty, struct ospf *ospf, bool maxage, + bool self, bool detail, const char *type_name, + struct in_addr *lsid, struct in_addr *adv_router, + bool use_vrf, json_object *json, bool uj) { - int idx_type = 4; - int type, ret; - struct in_addr id, adv_router; + int type; json_object *json_vrf = NULL; if (uj) { @@ -7121,298 +7315,79 @@ static int show_ip_ospf_database_common(struct vty *vty, struct ospf *ospf, &ospf->router_id); } - /* Show all LSA. */ - if ((argc == arg_base + 4) || (uj && (argc == arg_base + 5))) { - show_ip_ospf_database_summary(vty, ospf, 0, json_vrf); - if (json) { - if (use_vrf) - json_object_object_add( - json, ospf_get_name(ospf), json_vrf); - } - return CMD_SUCCESS; - } - - /* Set database type to show. */ - if (strncmp(argv[arg_base + idx_type]->text, "r", 1) == 0) - type = OSPF_ROUTER_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "ne", 2) == 0) - type = OSPF_NETWORK_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "ns", 2) == 0) - type = OSPF_AS_NSSA_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "su", 2) == 0) - type = OSPF_SUMMARY_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "a", 1) == 0) - type = OSPF_ASBR_SUMMARY_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "e", 1) == 0) - type = OSPF_AS_EXTERNAL_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "se", 2) == 0) { - show_ip_ospf_database_summary(vty, ospf, 1, json_vrf); - if (json) { - if (use_vrf) - json_object_object_add( - json, ospf_get_name(ospf), json_vrf); - } - return CMD_SUCCESS; - } else if (strncmp(argv[arg_base + idx_type]->text, "m", 1) == 0) { + /* Show MaxAge LSAs */ + if (maxage) { show_ip_ospf_database_maxage(vty, ospf, json_vrf); if (json) { - if (use_vrf) - json_object_object_add( - json, ospf_get_name(ospf), json_vrf); - } - return CMD_SUCCESS; - } else if (strncmp(argv[arg_base + idx_type]->text, "opaque-l", 8) == 0) - type = OSPF_OPAQUE_LINK_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "opaque-ar", 9) == 0) - type = OSPF_OPAQUE_AREA_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "opaque-as", 9) == 0) - type = OSPF_OPAQUE_AS_LSA; - else - return CMD_WARNING; - - /* `show ip ospf database LSA'. */ - if ((argc == arg_base + 5) || (uj && (argc == arg_base + 6))) - show_lsa_detail(vty, ospf, type, NULL, NULL, json_vrf); - else if (argc >= arg_base + 6) { - ret = inet_aton(argv[arg_base + 5]->arg, &id); - if (!ret) - return CMD_WARNING; - - /* `show ip ospf database LSA ID'. */ - if ((argc == arg_base + 6) || (uj && (argc == arg_base + 7))) - show_lsa_detail(vty, ospf, type, &id, NULL, json_vrf); - /* `show ip ospf database LSA ID adv-router ADV_ROUTER'. */ - else if ((argc == arg_base + 7) - || (uj && (argc == arg_base + 8))) { - if (strncmp(argv[arg_base + 6]->text, "s", 1) == 0) - adv_router = ospf->router_id; - else { - ret = inet_aton(argv[arg_base + 7]->arg, - &adv_router); - if (!ret) - return CMD_WARNING; - } - show_lsa_detail(vty, ospf, type, &id, &adv_router, - json_vrf); - } - } - - if (json) { - if (use_vrf) - json_object_object_add(json, ospf_get_name(ospf), - json_vrf); - } - - return CMD_SUCCESS; -} - -DEFUN (show_ip_ospf_database_max, - show_ip_ospf_database_max_cmd, - "show ip ospf [vrf <NAME|all>] database <max-age|self-originate> [json]", - SHOW_STR - IP_STR - "OSPF information\n" - VRF_CMD_HELP_STR - "All VRFs\n" - "Database summary\n" - "LSAs in MaxAge list\n" - "Self-originated link states\n" - JSON_STR) -{ - struct ospf *ospf = NULL; - struct listnode *node = NULL; - char *vrf_name = NULL; - bool all_vrf = false; - int ret = CMD_SUCCESS; - int inst = 0; - int idx_vrf = 0; - uint8_t use_vrf = 0; - bool uj = use_json(argc, argv); - json_object *json = NULL; - - if (uj) - json = json_object_new_object(); - - OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - - if (vrf_name) { - bool ospf_output = false; - - use_vrf = 1; - - if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { - if (!ospf->oi_running) - continue; - ospf_output = true; - ret = show_ip_ospf_database_common( - vty, ospf, idx_vrf ? 2 : 0, argc, argv, - use_vrf, json, uj); - } - - if (!ospf_output) - vty_out(vty, "%% OSPF is not enabled\n"); - } else { - ospf = ospf_lookup_by_inst_name(inst, vrf_name); - if (ospf == NULL || !ospf->oi_running) { - vty_out(vty, - "%% OSPF is not enabled in vrf %s\n", - vrf_name); - if (uj) - json_object_free(json); - - return CMD_SUCCESS; + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); } - ret = (show_ip_ospf_database_common( - vty, ospf, idx_vrf ? 2 : 0, argc, argv, use_vrf, - json, uj)); } - } else { - /* Display default ospf (instance 0) info */ - ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); - if (ospf == NULL || !ospf->oi_running) { - vty_out(vty, "%% OSPF is not enabled in vrf default\n"); - if (uj) - json_object_free(json); - - return CMD_SUCCESS; - } - - ret = show_ip_ospf_database_common(vty, ospf, 0, argc, argv, - use_vrf, json, uj); - } - - if (uj) - vty_json(vty, json); - - return ret; -} - -ALIAS (show_ip_ospf_database_max, - show_ip_ospf_database_cmd, - "show ip ospf [vrf <NAME|all>] database [<asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> [A.B.C.D [<self-originate|adv-router A.B.C.D>]]] [json]", - SHOW_STR - IP_STR - "OSPF information\n" - VRF_CMD_HELP_STR - "All VRFs\n" - "Database summary\n" - OSPF_LSA_TYPES_DESC - "Link State ID (as an IP address)\n" - "Self-originated link states\n" - "Advertising Router link states\n" - "Advertising Router (as an IP address)\n" - JSON_STR) - -DEFUN (show_ip_ospf_instance_database_max, - show_ip_ospf_instance_database_max_cmd, - "show ip ospf (1-65535) database <max-age|self-originate> [json]", - SHOW_STR - IP_STR - "OSPF information\n" - "Instance ID\n" - "Database summary\n" - "LSAs in MaxAge list\n" - "Self-originated link states\n" - JSON_STR) -{ - int idx_number = 3; - struct ospf *ospf; - unsigned short instance = 0; - bool uj = use_json(argc, argv); - json_object *json = NULL; - - if (uj) - json = json_object_new_object(); - - instance = strtoul(argv[idx_number]->arg, NULL, 10); - if (instance != ospf_instance) - return CMD_NOT_MY_INSTANCE; - - ospf = ospf_lookup_instance(instance); - if (!ospf || !ospf->oi_running) return CMD_SUCCESS; - - show_ip_ospf_database_common(vty, ospf, 1, argc, argv, 0, json, uj); - - if (uj) - vty_json(vty, json); - - return CMD_SUCCESS; -} - -ALIAS (show_ip_ospf_instance_database_max, - show_ip_ospf_instance_database_cmd, - "show ip ospf (1-65535) database [<asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> [A.B.C.D [<self-originate|adv-router A.B.C.D>]]] [json]", - SHOW_STR - IP_STR - "OSPF information\n" - "Instance ID\n" - "Database summary\n" - OSPF_LSA_TYPES_DESC - "Link State ID (as an IP address)\n" - "Self-originated link states\n" - "Advertising Router link states\n" - "Advertising Router (as an IP address)\n" - JSON_STR) - -static int show_ip_ospf_database_type_adv_router_common(struct vty *vty, - struct ospf *ospf, - int arg_base, int argc, - struct cmd_token **argv, - uint8_t use_vrf, - json_object *json, - bool uj) -{ - int idx_type = 4; - int type, ret; - struct in_addr adv_router; - json_object *json_vrf = NULL; - - if (uj) { - if (use_vrf) - json_vrf = json_object_new_object(); - else - json_vrf = json; } - if (ospf->instance) { - if (uj) - json_object_int_add(json, "ospfInstance", - ospf->instance); - else - vty_out(vty, "\nOSPF Instance: %d\n\n", ospf->instance); - } + /* Show all LSAs. */ + if (!type_name) { + if (detail) { + for (int i = OSPF_ROUTER_LSA; i <= OSPF_OPAQUE_AS_LSA; + i++) { + switch (i) { + case OSPF_GROUP_MEMBER_LSA: + case OSPF_EXTERNAL_ATTRIBUTES_LSA: + /* ignore deprecated LSA types */ + continue; + default: + break; + } - ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf); + if (adv_router && !lsid) + show_lsa_detail_adv_router(vty, ospf, i, + adv_router, + json_vrf); + else + show_lsa_detail(vty, ospf, i, lsid, + adv_router, json_vrf); + } + } else + show_ip_ospf_database_summary(vty, ospf, self, + json_vrf); - /* Show Router ID. */ - if (uj) { - json_object_string_addf(json_vrf, "routerId", "%pI4", - &ospf->router_id); - } else { - vty_out(vty, "\n OSPF Router with ID (%pI4)\n\n", - &ospf->router_id); + if (json) { + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); + } + } + return CMD_SUCCESS; } /* Set database type to show. */ - if (strncmp(argv[arg_base + idx_type]->text, "r", 1) == 0) + if (strncmp(type_name, "r", 1) == 0) type = OSPF_ROUTER_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "ne", 2) == 0) + else if (strncmp(type_name, "ne", 2) == 0) type = OSPF_NETWORK_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "ns", 2) == 0) + else if (strncmp(type_name, "ns", 2) == 0) type = OSPF_AS_NSSA_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "s", 1) == 0) + else if (strncmp(type_name, "su", 2) == 0) type = OSPF_SUMMARY_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "a", 1) == 0) + else if (strncmp(type_name, "a", 1) == 0) type = OSPF_ASBR_SUMMARY_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "e", 1) == 0) + else if (strncmp(type_name, "e", 1) == 0) type = OSPF_AS_EXTERNAL_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "opaque-l", 8) == 0) + else if (strncmp(type_name, "opaque-l", 8) == 0) type = OSPF_OPAQUE_LINK_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "opaque-ar", 9) == 0) + else if (strncmp(type_name, "opaque-ar", 9) == 0) type = OSPF_OPAQUE_AREA_LSA; - else if (strncmp(argv[arg_base + idx_type]->text, "opaque-as", 9) == 0) + else if (strncmp(type_name, "opaque-as", 9) == 0) type = OSPF_OPAQUE_AS_LSA; else { if (uj) { @@ -7422,155 +7397,119 @@ static int show_ip_ospf_database_type_adv_router_common(struct vty *vty, return CMD_WARNING; } - /* `show ip ospf database LSA adv-router ADV_ROUTER'. */ - if (strncmp(argv[arg_base + 5]->text, "s", 1) == 0) - adv_router = ospf->router_id; - else { - ret = inet_aton(argv[arg_base + 6]->arg, &adv_router); - if (!ret) { - if (uj) { - if (use_vrf) - json_object_free(json_vrf); - } - return CMD_WARNING; - } - } - - show_lsa_detail_adv_router(vty, ospf, type, &adv_router, json_vrf); + if (adv_router && !lsid) + show_lsa_detail_adv_router(vty, ospf, type, adv_router, + json_vrf); + else + show_lsa_detail(vty, ospf, type, lsid, adv_router, json_vrf); if (json) { - if (use_vrf) - json_object_object_add(json, ospf_get_name(ospf), - json_vrf); + if (use_vrf) { + if (ospf->vrf_id == VRF_DEFAULT) + json_object_object_add(json, "default", + json_vrf); + else + json_object_object_add(json, ospf->name, + json_vrf); + } } return CMD_SUCCESS; } -DEFUN (show_ip_ospf_database_type_adv_router, - show_ip_ospf_database_type_adv_router_cmd, - "show ip ospf [vrf <NAME|all>] database <asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> <adv-router A.B.C.D|self-originate> [json]", +DEFPY (show_ip_ospf_database, + show_ip_ospf_database_cmd, + "show ip ospf [(1-65535)$instance_id] [vrf <NAME|all>$vrf_name] database\ + [<\ + max-age$maxage\ + |self-originate$selforig\ + |<\ + detail$detail\ + |<asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as>$type_name\ + >\ + [{\ + A.B.C.D$lsid\ + |<adv-router A.B.C.D$adv_router|self-originate$adv_router_self>\ + }]\ + >]\ + [json]", SHOW_STR IP_STR "OSPF information\n" + "Instance ID\n" VRF_CMD_HELP_STR "All VRFs\n" "Database summary\n" + "LSAs in MaxAge list\n" + "Self-originated link states\n" + "Show detailed information\n" OSPF_LSA_TYPES_DESC + "Link State ID (as an IP address)\n" "Advertising Router link states\n" "Advertising Router (as an IP address)\n" "Self-originated link states\n" JSON_STR) { - struct ospf *ospf = NULL; - struct listnode *node = NULL; - char *vrf_name = NULL; - bool all_vrf = false; + struct ospf *ospf; int ret = CMD_SUCCESS; - int inst = 0; - int idx_vrf = 0; - uint8_t use_vrf = 0; + bool use_vrf = !!vrf_name; bool uj = use_json(argc, argv); + struct in_addr *lsid_p = NULL; + struct in_addr *adv_router_p = NULL; json_object *json = NULL; if (uj) json = json_object_new_object(); + if (lsid_str) + lsid_p = &lsid; + if (adv_router_str) + adv_router_p = &adv_router; - OSPF_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - - if (vrf_name) { + if (vrf_name && strmatch(vrf_name, "all")) { + struct listnode *node; bool ospf_output = false; - use_vrf = 1; + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->oi_running) + continue; + if (ospf->instance != instance_id) + continue; - if (all_vrf) { - for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { - if (!ospf->oi_running) - continue; - ospf_output = true; - ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, 2, argc, argv, use_vrf, json, - uj); - } - if (!ospf_output) - vty_out(vty, "%% OSPF is not enabled\n"); - } else { - ospf = ospf_lookup_by_inst_name(inst, vrf_name); - if ((ospf == NULL) || !ospf->oi_running) { - if (uj) - vty_json(vty, json); - else - vty_out(vty, - "%% OSPF is not enabled in vrf %s\n", - vrf_name); - return CMD_SUCCESS; - } + if (adv_router_self) + adv_router_p = &ospf->router_id; - ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, 2, argc, argv, use_vrf, json, uj); + ospf_output = true; + ret = show_ip_ospf_database_common( + vty, ospf, !!maxage, !!selforig, !!detail, + type_name, lsid_p, adv_router_p, use_vrf, json, + uj); } + + if (!ospf_output && !uj) + vty_out(vty, "%% OSPF is not enabled\n"); } else { - /* Display default ospf (instance 0) info */ - ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT); + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + ospf = ospf_lookup_by_inst_name(instance_id, vrf_name); if (ospf == NULL || !ospf->oi_running) { if (uj) vty_json(vty, json); else - vty_out(vty, - "%% OSPF is not enabled on vrf default\n"); + vty_out(vty, "%% OSPF instance not found\n"); return CMD_SUCCESS; } + if (adv_router_self) + adv_router_p = &ospf->router_id; - ret = show_ip_ospf_database_type_adv_router_common( - vty, ospf, 0, argc, argv, use_vrf, json, uj); + ret = (show_ip_ospf_database_common( + vty, ospf, !!maxage, !!selforig, !!detail, type_name, + lsid_p, adv_router_p, use_vrf, json, uj)); } - if (uj) { - vty_out(vty, "%s\n", json_object_to_json_string(json)); - json_object_free(json); - } - - return ret; -} - -DEFUN (show_ip_ospf_instance_database_type_adv_router, - show_ip_ospf_instance_database_type_adv_router_cmd, - "show ip ospf (1-65535) database <asbr-summary|external|network|router|summary|nssa-external|opaque-link|opaque-area|opaque-as> <adv-router A.B.C.D|self-originate> [json]", - SHOW_STR - IP_STR - "OSPF information\n" - "Instance ID\n" - "Database summary\n" - OSPF_LSA_TYPES_DESC - "Advertising Router link states\n" - "Advertising Router (as an IP address)\n" - "Self-originated link states\n" - JSON_STR) -{ - int idx_number = 3; - struct ospf *ospf; - unsigned short instance = 0; - bool uj = use_json(argc, argv); - json_object *json = NULL; - - if (uj) - json = json_object_new_object(); - - instance = strtoul(argv[idx_number]->arg, NULL, 10); - if (instance != ospf_instance) - return CMD_NOT_MY_INSTANCE; - - ospf = ospf_lookup_instance(instance); - if (!ospf || !ospf->oi_running) - return CMD_SUCCESS; - - show_ip_ospf_database_type_adv_router_common(vty, ospf, 1, argc, argv, - 0, json, uj); - if (uj) vty_json(vty, json); - return CMD_SUCCESS; + return ret; } DEFUN (ip_ospf_authentication_args, @@ -8515,13 +8454,17 @@ DEFUN_HIDDEN (no_ospf_hello_interval, } DEFUN(ip_ospf_network, ip_ospf_network_cmd, - "ip ospf network <broadcast|non-broadcast|point-to-multipoint|point-to-point [dmvpn]>", + "ip ospf network <broadcast|" + "non-broadcast|" + "point-to-multipoint [delay-reflood]|" + "point-to-point [dmvpn]>", "IP Information\n" "OSPF interface commands\n" "Network type\n" "Specify OSPF broadcast multi-access network\n" "Specify OSPF NBMA network\n" "Specify OSPF point-to-multipoint network\n" + "Specify OSPF delayed reflooding of LSAs received on P2MP interface\n" "Specify OSPF point-to-point network\n" "Specify OSPF point-to-point DMVPN network\n") { @@ -8529,6 +8472,7 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd, int idx = 0; int old_type = IF_DEF_PARAMS(ifp)->type; uint8_t old_ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + uint8_t old_p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood; struct route_node *rn; if (old_type == OSPF_IFTYPE_LOOPBACK) { @@ -8538,21 +8482,26 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd, } IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0; + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = + OSPF_P2MP_DELAY_REFLOOD_DEFAULT; if (argv_find(argv, argc, "broadcast", &idx)) IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_BROADCAST; else if (argv_find(argv, argc, "non-broadcast", &idx)) IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_NBMA; - else if (argv_find(argv, argc, "point-to-multipoint", &idx)) + else if (argv_find(argv, argc, "point-to-multipoint", &idx)) { IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOMULTIPOINT; - else if (argv_find(argv, argc, "point-to-point", &idx)) { + if (argv_find(argv, argc, "delay-reflood", &idx)) + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = true; + } else if (argv_find(argv, argc, "point-to-point", &idx)) { IF_DEF_PARAMS(ifp)->type = OSPF_IFTYPE_POINTOPOINT; if (argv_find(argv, argc, "dmvpn", &idx)) IF_DEF_PARAMS(ifp)->ptp_dmvpn = 1; } - if (IF_DEF_PARAMS(ifp)->type == old_type - && IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn) + if (IF_DEF_PARAMS(ifp)->type == old_type && + IF_DEF_PARAMS(ifp)->ptp_dmvpn == old_ptp_dmvpn && + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood == old_p2mp_delay_reflood) return CMD_SUCCESS; SET_IF_PARAM(IF_DEF_PARAMS(ifp), type); @@ -8564,10 +8513,19 @@ DEFUN(ip_ospf_network, ip_ospf_network_cmd, continue; oi->type = IF_DEF_PARAMS(ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood; - if (oi->state > ISM_Down) { - OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); - OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + /* + * The OSPF interface only needs to be flapped if the network + * type or DMVPN parameter changes. + */ + if (IF_DEF_PARAMS(ifp)->type != old_type || + IF_DEF_PARAMS(ifp)->ptp_dmvpn != old_ptp_dmvpn) { + if (oi->state > ISM_Down) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } } } @@ -8605,6 +8563,8 @@ DEFUN (no_ip_ospf_network, IF_DEF_PARAMS(ifp)->type = ospf_default_iftype(ifp); IF_DEF_PARAMS(ifp)->ptp_dmvpn = 0; + IF_DEF_PARAMS(ifp)->p2mp_delay_reflood = + OSPF_P2MP_DELAY_REFLOOD_DEFAULT; if (IF_DEF_PARAMS(ifp)->type == old_type) return CMD_SUCCESS; @@ -8616,6 +8576,8 @@ DEFUN (no_ip_ospf_network, continue; oi->type = IF_DEF_PARAMS(ifp)->type; + oi->ptp_dmvpn = IF_DEF_PARAMS(ifp)->ptp_dmvpn; + oi->p2mp_delay_reflood = IF_DEF_PARAMS(ifp)->p2mp_delay_reflood; if (oi->state > ISM_Down) { OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); @@ -8864,6 +8826,59 @@ DEFUN_HIDDEN (no_ospf_retransmit_interval, return no_ip_ospf_retransmit_interval(self, vty, argc, argv); } +DEFPY (ip_ospf_gr_hdelay, + ip_ospf_gr_hdelay_cmd, + "ip ospf graceful-restart hello-delay (1-1800)", + IP_STR + "OSPF interface commands\n" + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + + params = IF_DEF_PARAMS(ifp); + + /* Note: new or updated value won't affect ongoing graceful restart. */ + SET_IF_PARAM(params, v_gr_hello_delay); + params->v_gr_hello_delay = hello_delay; + + return CMD_SUCCESS; +} + +DEFPY (no_ip_ospf_gr_hdelay, + no_ip_ospf_gr_hdelay_cmd, + "no ip ospf graceful-restart hello-delay [(1-1800)]", + NO_STR + IP_STR + "OSPF interface commands\n" + "Graceful Restart parameters\n" + "Delay the sending of the first hello packets.\n" + "Delay in seconds\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct ospf_if_params *params; + struct route_node *rn; + + params = IF_DEF_PARAMS(ifp); + UNSET_IF_PARAM(params, v_gr_hello_delay); + params->v_gr_hello_delay = OSPF_HELLO_DELAY_DEFAULT; + + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi; + + oi = rn->info; + if (!oi) + continue; + + oi->gr.hello_delay.elapsed_seconds = 0; + EVENT_OFF(oi->gr.hello_delay.t_grace_send); + } + + return CMD_SUCCESS; +} + DEFUN (ip_ospf_transmit_delay, ip_ospf_transmit_delay_addr_cmd, "ip ospf transmit-delay (1-65535) [A.B.C.D]", @@ -9794,6 +9809,61 @@ DEFUN (no_ip_ospf_mtu_ignore, return CMD_SUCCESS; } +DEFPY(ip_ospf_capability_opaque, ip_ospf_capability_opaque_addr_cmd, + "[no] ip ospf capability opaque [A.B.C.D]$ip_addr", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Disable OSPF capability on this interface\n" + "Disable OSPF opaque LSA capability on this interface\n" + "Address of interface\n") +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct route_node *rn; + bool old_opaque_capable; + bool opaque_capable_change; + + struct ospf_if_params *params; + params = IF_DEF_PARAMS(ifp); + + if (ip_addr.s_addr != INADDR_ANY) { + params = ospf_get_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + + old_opaque_capable = params->opaque_capable; + params->opaque_capable = (no) ? false : true; + opaque_capable_change = (old_opaque_capable != params->opaque_capable); + if (params->opaque_capable != OSPF_OPAQUE_CAPABLE_DEFAULT) + SET_IF_PARAM(params, opaque_capable); + else { + UNSET_IF_PARAM(params, opaque_capable); + if (params != IF_DEF_PARAMS(ifp)) { + ospf_free_if_params(ifp, ip_addr); + ospf_if_update_params(ifp, ip_addr); + } + } + + /* + * If there is a change to the opaque capability, flap the interface + * to reset all the neighbor adjacencies. + */ + if (opaque_capable_change) { + for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) { + struct ospf_interface *oi = rn->info; + + if (oi && (oi->state > ISM_Down) && + (ip_addr.s_addr == INADDR_ANY || + IPV4_ADDR_SAME(&oi->address->u.prefix4, + &ip_addr))) { + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceDown); + OSPF_ISM_EVENT_EXECUTE(oi, ISM_InterfaceUp); + } + } + } + return CMD_SUCCESS; +} + DEFUN (ospf_max_metric_router_lsa_admin, ospf_max_metric_router_lsa_admin_cmd, @@ -9891,7 +9961,7 @@ DEFUN (no_ospf_max_metric_router_lsa_startup, for (ALL_LIST_ELEMENTS_RO(ospf->areas, ln, area)) { SET_FLAG(area->stub_router_state, OSPF_AREA_WAS_START_STUB_ROUTED); - THREAD_OFF(area->t_stub_router); + EVENT_OFF(area->t_stub_router); /* Don't trample on admin stub routed */ if (!CHECK_FLAG(area->stub_router_state, @@ -10305,12 +10375,21 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf, json_object_string_add(json_vrf, "strictLsaCheck", (ospf->strict_lsa_check) ? "Enabled" : "Disabled"); +#if CONFDATE > 20240401 + CPP_NOTICE("Remove deprecated json key: restartSupoort") +#endif json_object_string_add( json_vrf, "restartSupoort", (ospf->only_planned_restart) ? "Planned Restart only" : "Planned and Unplanned Restarts"); + json_object_string_add( + json_vrf, "restartSupport", + (ospf->only_planned_restart) + ? "Planned Restart only" + : "Planned and Unplanned Restarts"); + json_object_int_add(json_vrf, "supportedGracePeriod", ospf->supported_grace_time); @@ -10388,9 +10467,9 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf, .actual_grace_period); vty_out(vty, " Remaining GraceTime:%ld(in seconds).\n", - thread_timer_remain_second( + event_timer_remain_second( nbr->gr_helper_info - .t_grace_timer)); + .t_grace_timer)); vty_out(vty, " Graceful Restart reason: %s.\n\n", ospf_restart_reason2str( @@ -10421,9 +10500,9 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf, .actual_grace_period); json_object_int_add( json_neigh, "remainGracetime", - thread_timer_remain_second( + event_timer_remain_second( nbr->gr_helper_info - .t_grace_timer)); + .t_grace_timer)); json_object_string_add( json_neigh, "restartReason", ospf_restart_reason2str( @@ -11962,6 +12041,10 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) == OSPF_IFTYPE_POINTOPOINT && params->ptp_dmvpn) vty_out(vty, " dmvpn"); + if (params->type == + OSPF_IFTYPE_POINTOMULTIPOINT && + params->p2mp_delay_reflood) + vty_out(vty, " delay-reflood"); if (params != IF_DEF_PARAMS(ifp) && rn) vty_out(vty, " %pI4", &rn->p.u.prefix4); @@ -12047,6 +12130,15 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) vty_out(vty, "\n"); } + /* Hello Graceful-Restart Delay print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, + v_gr_hello_delay) && + params->v_gr_hello_delay != + OSPF_HELLO_DELAY_DEFAULT) + vty_out(vty, + " ip ospf graceful-restart hello-delay %u\n", + params->v_gr_hello_delay); + /* Router Priority print. */ if (OSPF_IF_PARAM_CONFIGURED(params, priority) && params->priority @@ -12136,6 +12228,21 @@ static int config_write_interface_one(struct vty *vty, struct vrf *vrf) if (params && params->ldp_sync_info) ospf_ldp_sync_if_write_config(vty, params); + /* Capability opaque print. */ + if (OSPF_IF_PARAM_CONFIGURED(params, opaque_capable) && + params->opaque_capable != + OSPF_OPAQUE_CAPABLE_DEFAULT) { + if (params->opaque_capable == false) + vty_out(vty, + " no ip ospf capability opaque"); + else + vty_out(vty, + " ip ospf capability opaque"); + if (params != IF_DEF_PARAMS(ifp) && rn) + vty_out(vty, " %pI4", &rn->p.u.prefix4); + vty_out(vty, "\n"); + } + while (1) { if (rn == NULL) rn = route_top(IF_OIFS_PARAMS(ifp)); @@ -12233,29 +12340,62 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf) vty_out(vty, " no-summary\n"); vty_out(vty, "\n"); } else if (area->external_routing == OSPF_AREA_NSSA) { + vty_out(vty, " area %s nssa", buf); + switch (area->NSSATranslatorRole) { case OSPF_NSSA_ROLE_NEVER: - vty_out(vty, - " area %s nssa translate-never\n", - buf); + vty_out(vty, " translate-never"); break; case OSPF_NSSA_ROLE_ALWAYS: - vty_out(vty, - " area %s nssa translate-always\n", - buf); + vty_out(vty, " translate-always"); break; case OSPF_NSSA_ROLE_CANDIDATE: - vty_out(vty, " area %s nssa \n", buf); break; } - if (area->no_summary) + + if (area->nssa_default_originate.enabled) { vty_out(vty, - " area %s nssa no-summary\n", - buf); + " default-information-originate"); + if (area->nssa_default_originate + .metric_value != -1) + vty_out(vty, " metric %d", + area->nssa_default_originate + .metric_value); + if (area->nssa_default_originate + .metric_type != + DEFAULT_METRIC_TYPE) + vty_out(vty, " metric-type 1"); + } + + if (area->no_summary) + vty_out(vty, " no-summary"); if (area->suppress_fa) - vty_out(vty, - " area %s nssa suppress-fa\n", - buf); + vty_out(vty, " suppress-fa"); + vty_out(vty, "\n"); + + for (rn1 = route_top(area->nssa_ranges); rn1; + rn1 = route_next(rn1)) { + struct ospf_area_range *range; + + range = rn1->info; + if (!range) + continue; + + vty_out(vty, " area %s nssa range %pFX", + buf, &rn1->p); + + if (range->cost_config != + OSPF_AREA_RANGE_COST_UNSPEC) + vty_out(vty, " cost %u", + range->cost_config); + + if (!CHECK_FLAG( + range->flags, + OSPF_AREA_RANGE_ADVERTISE)) + vty_out(vty, " not-advertise"); + + vty_out(vty, "\n"); + } } if (area->default_cost != 1) @@ -12686,6 +12826,9 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) if (ospf->fr_configured) vty_out(vty, " flood-reduction\n"); + if (!ospf->intf_socket_enabled) + vty_out(vty, " no socket-per-interface\n"); + /* Redistribute information print. */ config_write_ospf_redistribute(vty, ospf); @@ -12742,6 +12885,22 @@ static int ospf_config_write_one(struct vty *vty, struct ospf *ospf) /* LDP-Sync print */ ospf_ldp_sync_write_config(vty, ospf); + /* Socket buffer sizes */ + if (ospf->recv_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE) { + if (ospf->send_sock_bufsize == ospf->recv_sock_bufsize) + vty_out(vty, " socket buffer all %u\n", + ospf->recv_sock_bufsize); + else + vty_out(vty, " socket buffer recv %u\n", + ospf->recv_sock_bufsize); + } + + if (ospf->send_sock_bufsize != OSPF_DEFAULT_SOCK_BUFSIZE && + ospf->send_sock_bufsize != ospf->recv_sock_bufsize) + vty_out(vty, " socket buffer send %u\n", + ospf->send_sock_bufsize); + + vty_out(vty, "exit\n"); write++; @@ -12781,13 +12940,6 @@ void ospf_vty_show_init(void) /* "show ip ospf database" commands. */ install_element(VIEW_NODE, &show_ip_ospf_database_cmd); - install_element(VIEW_NODE, &show_ip_ospf_database_max_cmd); - install_element(VIEW_NODE, - &show_ip_ospf_database_type_adv_router_cmd); - install_element(VIEW_NODE, - &show_ip_ospf_instance_database_type_adv_router_cmd); - install_element(VIEW_NODE, &show_ip_ospf_instance_database_cmd); - install_element(VIEW_NODE, &show_ip_ospf_instance_database_max_cmd); /* "show ip ospf interface" commands. */ install_element(VIEW_NODE, &show_ip_ospf_interface_cmd); @@ -12900,6 +13052,9 @@ static void ospf_vty_if_init(void) install_element(INTERFACE_NODE, &ip_ospf_passive_cmd); install_element(INTERFACE_NODE, &no_ip_ospf_passive_cmd); + /* "ip ospf capability opaque" commands. */ + install_element(INTERFACE_NODE, &ip_ospf_capability_opaque_addr_cmd); + /* These commands are compatibitliy for previous version. */ install_element(INTERFACE_NODE, &ospf_authentication_key_cmd); install_element(INTERFACE_NODE, &ospf_message_digest_key_cmd); @@ -13061,7 +13216,7 @@ DEFPY_HIDDEN(ospf_maxage_delay_timer, ospf_maxage_delay_timer_cmd, else ospf->maxage_delay = value; - THREAD_OFF(ospf->t_maxage); + EVENT_OFF(ospf->t_maxage); OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, ospf->maxage_delay); @@ -13205,6 +13360,71 @@ DEFPY(no_flood_reduction_area, no_flood_reduction_area_cmd, return CMD_SUCCESS; } +DEFPY(ospf_socket_bufsizes, + ospf_socket_bufsizes_cmd, + "[no] socket buffer <send$send_val | recv$recv_val | all$all_val> \ + ![(1-4000000000)$bufsize]", + NO_STR + "Socket parameters\n" + "Buffer size configuration\n" + "Send buffer size\n" + "Receive buffer size\n" + "Both send and receive buffer sizes\n" + "Buffer size, in bytes\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + uint32_t recvsz, sendsz; + + if (no) + bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + + if (all_val) { + recvsz = bufsize; + sendsz = bufsize; + } else if (send_val) { + sendsz = bufsize; + recvsz = ospf->recv_sock_bufsize; + } else if (recv_val) { + recvsz = bufsize; + sendsz = ospf->send_sock_bufsize; + } else + return CMD_SUCCESS; + + /* React to a change by modifying existing sockets */ + ospf_update_bufsize(ospf, recvsz, sendsz); + + return CMD_SUCCESS; +} + +DEFPY (per_intf_socket, + per_intf_socket_cmd, + "[no] socket-per-interface", + NO_STR + "Use write socket per interface\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf); + struct listnode *node; + struct ospf_interface *oi; + + if (no) { + if (ospf->intf_socket_enabled) { + ospf->intf_socket_enabled = false; + + /* Iterate and close any sockets */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + ospf_ifp_sock_close(oi->ifp); + } + } else if (!ospf->intf_socket_enabled) { + ospf->intf_socket_enabled = true; + + /* Iterate and open sockets */ + for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) + ospf_ifp_sock_init(oi->ifp); + } + + return CMD_SUCCESS; +} + void ospf_vty_clear_init(void) { install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd); @@ -13288,12 +13508,9 @@ void ospf_vty_init(void) /* "area nssa" commands. */ install_element(OSPF_NODE, &ospf_area_nssa_cmd); - install_element(OSPF_NODE, &ospf_area_nssa_translate_cmd); - install_element(OSPF_NODE, &ospf_area_nssa_no_summary_cmd); - install_element(OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd); - install_element(OSPF_NODE, &ospf_area_nssa_suppress_fa_cmd); - install_element(OSPF_NODE, &no_ospf_area_nssa_suppress_fa_cmd); install_element(OSPF_NODE, &no_ospf_area_nssa_cmd); + install_element(OSPF_NODE, &ospf_area_nssa_range_cmd); + install_element(OSPF_NODE, &no_ospf_area_nssa_range_cmd); install_element(OSPF_NODE, &ospf_area_default_cost_cmd); install_element(OSPF_NODE, &no_ospf_area_default_cost_cmd); @@ -13371,6 +13588,9 @@ void ospf_vty_init(void) install_element(OSPF_NODE, &flood_reduction_area_cmd); install_element(OSPF_NODE, &no_flood_reduction_area_cmd); + install_element(OSPF_NODE, &ospf_socket_bufsizes_cmd); + install_element(OSPF_NODE, &per_intf_socket_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 46fb653f3adf..abc580b13ef4 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -6,7 +6,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "network.h" #include "prefix.h" @@ -20,6 +20,7 @@ #include "log.h" #include "route_opaque.h" #include "lib/bfd.h" +#include "lib/lib_errors.h" #include "nexthop.h" #include "ospfd/ospfd.h" @@ -46,10 +47,10 @@ DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute"); /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; /* and for the Synchronous connection to the Label Manager */ -static struct zclient *zclient_sync; +struct zclient *zclient_sync; /* For registering threads. */ -extern struct thread_master *master; +extern struct event_loop *master; /* Router-id update message from zebra. */ static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS) @@ -509,10 +510,10 @@ bool ospf_external_default_routemap_apply_walk(struct ospf *ospf, * Function to originate or flush default after applying * route-map on all ei. */ -static void ospf_external_lsa_default_routemap_timer(struct thread *thread) +static void ospf_external_lsa_default_routemap_timer(struct event *thread) { struct list *ext_list; - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); struct prefix_ipv4 p; int type; int ret = 0; @@ -579,8 +580,8 @@ void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance) /* * 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); + event_add_event(master, ospf_external_lsa_default_routemap_timer, ospf, + 0, &ospf->t_default_routemap_timer); } /* Update NHLFE for Prefix SID */ @@ -813,16 +814,16 @@ int ospf_is_type_redistributed(struct ospf *ospf, int type, unsigned short instance) { return (DEFAULT_ROUTE_TYPE(type) - ? vrf_bitmap_check(zclient->default_information[AFI_IP], - ospf->vrf_id) - : ((instance - && redist_check_instance( + ? vrf_bitmap_check( + &zclient->default_information[AFI_IP], + ospf->vrf_id) + : ((instance && + redist_check_instance( &zclient->mi_redist[AFI_IP][type], - instance)) - || (!instance - && vrf_bitmap_check( - zclient->redist[AFI_IP][type], - ospf->vrf_id)))); + instance)) || + (!instance && + vrf_bitmap_check(&zclient->redist[AFI_IP][type], + ospf->vrf_id)))); } int ospf_redistribute_update(struct ospf *ospf, struct ospf_redist *red, @@ -955,8 +956,8 @@ int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, type_str = "always"; ospf->redistribute++; ospf_external_add(ospf, DEFAULT_ROUTE, 0); - ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, - 0); + ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, 0, + DEFAULT_DEFAULT_METRIC); break; } @@ -1124,9 +1125,9 @@ static bool ospf_external_lsa_default_routemap_apply(struct ospf *ospf, * 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); + event_add_event(master, + ospf_external_lsa_default_routemap_timer, ospf, + 0, &ospf->t_default_routemap_timer); } return true; @@ -1251,12 +1252,18 @@ static int ospf_zebra_gr_update(struct ospf *ospf, int command, int ospf_zebra_gr_enable(struct ospf *ospf, uint32_t stale_time) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("Zebra enable GR [stale time %u]", stale_time); + return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_CAPABILITIES, stale_time); } int ospf_zebra_gr_disable(struct ospf *ospf) { + if (IS_DEBUG_OSPF_GR) + zlog_debug("Zebra disable GR"); + return ospf_zebra_gr_update(ospf, ZEBRA_CLIENT_GR_DISABLE, 0); } @@ -1302,9 +1309,11 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) rt_type = DEFAULT_ROUTE; if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) - zlog_debug("%s: cmd %s from client %s: vrf_id %d, p %pFX", - __func__, zserv_command_string(cmd), - zebra_route_string(api.type), vrf_id, &api.prefix); + zlog_debug( + "%s: cmd %s from client %s: vrf_id %d, p %pFX, metric %d", + __func__, zserv_command_string(cmd), + zebra_route_string(api.type), vrf_id, &api.prefix, + api.metric); if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: @@ -1331,7 +1340,8 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) p); ei = ospf_external_info_add(ospf, rt_type, api.instance, p, - ifindex, nexthop, api.tag); + ifindex, nexthop, api.tag, + api.metric); if (ei == NULL) { /* Nothing has changed, so nothing to do; return */ return 0; @@ -1470,6 +1480,61 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) return 0; } +void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg) +{ + struct prefix prefix = {}; + int command; + + if (zclient->sock < 0) { + if (IS_DEBUG_OSPF(zebra, ZEBRA)) + zlog_debug(" Not connected to Zebra"); + return; + } + + prefix.family = AF_INET; + prefix.prefixlen = 0; + + if (unreg) + command = ZEBRA_NEXTHOP_UNREGISTER; + else + command = ZEBRA_NEXTHOP_REGISTER; + + if (IS_DEBUG_OSPF(zebra, ZEBRA)) + zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__, + zserv_command_string(command), &prefix, + ospf->vrf_id); + + if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false, + true, ospf->vrf_id) == ZCLIENT_SEND_FAILURE) + flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed", + __func__); +} + +static int ospf_zebra_import_check_update(ZAPI_CALLBACK_ARGS) +{ + struct ospf *ospf; + struct zapi_route nhr; + struct prefix matched; + + ospf = ospf_lookup_by_vrf_id(vrf_id); + if (ospf == NULL || !IS_OSPF_ASBR(ospf)) + return 0; + + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { + zlog_err("%s[%u]: Failure to decode route", __func__, + ospf->vrf_id); + return -1; + } + + if (matched.family != AF_INET || matched.prefixlen != 0 || + nhr.type == ZEBRA_ROUTE_OSPF) + return 0; + + ospf->nssa_default_import_check.status = !!nhr.nexthop_num; + ospf_abr_nssa_type7_defaults(ospf); + + return 0; +} int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name) { @@ -1510,14 +1575,14 @@ int ospf_distribute_list_out_unset(struct ospf *ospf, int type, } /* distribute-list update timer. */ -static void ospf_distribute_list_update_timer(struct thread *thread) +static void ospf_distribute_list_update_timer(struct event *thread) { struct route_node *rn; struct external_info *ei; struct route_table *rt; struct ospf_lsa *lsa; int type, default_refresh = 0; - struct ospf *ospf = THREAD_ARG(thread); + struct ospf *ospf = EVENT_ARG(thread); if (ospf == NULL) return; @@ -1641,9 +1706,8 @@ void ospf_distribute_list_update(struct ospf *ospf, int type, return; /* Set timer. If timer is already started, this call does nothing. */ - thread_add_timer_msec(master, ospf_distribute_list_update_timer, ospf, - ospf->min_ls_interval, - &ospf->t_distribute_update); + event_add_timer_msec(master, ospf_distribute_list_update_timer, ospf, + ospf->min_ls_interval, &ospf->t_distribute_update); } /* If access-list is updated, apply some check. */ @@ -1787,7 +1851,7 @@ static void ospf_prefix_list_update(struct prefix_list *plist) && strcmp(PREFIX_NAME_OUT(area), prefix_list_name(plist)) == 0) { - PREFIX_LIST_IN(area) = prefix_list_lookup( + PREFIX_LIST_OUT(area) = prefix_list_lookup( AFI_IP, PREFIX_NAME_OUT(area)); abr_inv++; } @@ -2062,10 +2126,20 @@ int ospf_zebra_label_manager_connect(void) static void ospf_zebra_connected(struct zclient *zclient) { + struct ospf *ospf; + struct listnode *node; + /* Send the client registration */ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); zclient_send_reg_requests(zclient, VRF_DEFAULT); + + /* Activate graceful restart if configured. */ + for (ALL_LIST_ELEMENTS_RO(om->ospf, node, ospf)) { + if (!ospf->gr_info.restart_support) + continue; + (void)ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period); + } } /* @@ -2087,9 +2161,9 @@ static int ospf_opaque_msg_handler(ZAPI_CALLBACK_ARGS) switch (info.type) { case LINK_STATE_SYNC: - STREAM_GETC(s, dst.proto); - STREAM_GETW(s, dst.instance); - STREAM_GETL(s, dst.session_id); + dst.proto = info.src_proto; + dst.instance = info.src_instance; + dst.session_id = info.src_session_id; dst.type = LINK_STATE_SYNC; ret = ospf_te_sync_ted(dst); break; @@ -2133,13 +2207,14 @@ static zclient_handler *const ospf_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf_zebra_read_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf_zebra_read_route, + [ZEBRA_NEXTHOP_UPDATE] = ospf_zebra_import_check_update, [ZEBRA_OPAQUE_MESSAGE] = ospf_opaque_msg_handler, [ZEBRA_CLIENT_CLOSE_NOTIFY] = ospf_zebra_client_close_notify, }; -void ospf_zebra_init(struct thread_master *master, unsigned short instance) +void ospf_zebra_init(struct event_loop *master, unsigned short instance) { /* Allocate zebra structure. */ zclient = zclient_new(master, &zclient_options_default, ospf_handlers, diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index adc338fb1ae4..86a5678fc4fa 100644 --- a/ospfd/ospf_zebra.h +++ b/ospfd/ospf_zebra.h @@ -69,6 +69,7 @@ extern int ospf_redistribute_set(struct ospf *, struct ospf_redist *, 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); +extern void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg); extern int ospf_distribute_list_out_set(struct ospf *, int, const char *); extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *); extern void ospf_routemap_set(struct ospf_redist *, const char *); @@ -79,7 +80,7 @@ extern int ospf_distance_set(struct vty *, struct ospf *, const char *, const char *, const char *); extern int ospf_distance_unset(struct vty *, struct ospf *, const char *, const char *, const char *); -extern void ospf_zebra_init(struct thread_master *, unsigned short); +extern void ospf_zebra_init(struct event_loop *m, unsigned short instance); 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( diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 0296d9d9f58d..3bafc313f6a5 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -5,7 +5,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "linklist.h" @@ -60,7 +60,10 @@ struct ospf_master *om; unsigned short ospf_instance; extern struct zclient *zclient; +extern struct zclient *zclient_sync; +/* OSPF config processing timer thread */ +struct event *t_ospf_cfg; static void ospf_remove_vls_through_area(struct ospf *, struct ospf_area *); static void ospf_network_free(struct ospf *, struct ospf_network *); @@ -385,8 +388,8 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) new->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; new->maxage_lsa = route_table_init(); new->t_maxage_walker = NULL; - thread_add_timer(master, ospf_lsa_maxage_walker, new, - OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker); + event_add_timer(master, ospf_lsa_maxage_walker, new, + OSPF_LSA_MAXAGE_CHECK_INTERVAL, &new->t_maxage_walker); /* Max paths initialization */ new->max_multipath = MULTIPATH_NUM; @@ -398,8 +401,8 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; new->lsa_refresh_timer = OSPF_LS_REFRESH_TIME; new->t_lsa_refresher = NULL; - thread_add_timer(master, ospf_lsa_refresh_walker, new, - new->lsa_refresh_interval, &new->t_lsa_refresher); + event_add_timer(master, ospf_lsa_refresh_walker, new, + new->lsa_refresh_interval, &new->t_lsa_refresher); new->lsa_refresher_started = monotime(NULL); new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1); @@ -419,6 +422,10 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) QOBJ_REG(new, ospf); new->fd = -1; + new->intf_socket_enabled = true; + + new->recv_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; + new->send_sock_bufsize = OSPF_DEFAULT_SOCK_BUFSIZE; return new; } @@ -441,7 +448,7 @@ static struct ospf *ospf_new(unsigned short instance, const char *name) return new; } - thread_add_read(master, ospf_read, new, new->fd, &new->t_read); + event_add_read(master, ospf_read, new, new->fd, &new->t_read); new->oi_running = 1; ospf_router_id_update(new); @@ -579,7 +586,7 @@ static struct ospf *ospf_lookup_by_name(const char *vrf_name) static void ospf_deferred_shutdown_finish(struct ospf *ospf) { ospf->stub_router_shutdown_time = OSPF_STUB_ROUTER_UNCONFIGURED; - THREAD_OFF(ospf->t_deferred_shutdown); + EVENT_OFF(ospf->t_deferred_shutdown); ospf_finish_final(ospf); @@ -596,9 +603,9 @@ static void ospf_deferred_shutdown_finish(struct ospf *ospf) } /* Timer thread for G-R */ -static void ospf_deferred_shutdown_timer(struct thread *t) +static void ospf_deferred_shutdown_timer(struct event *t) { - struct ospf *ospf = THREAD_ARG(t); + struct ospf *ospf = EVENT_ARG(t); ospf_deferred_shutdown_finish(ospf); } @@ -677,6 +684,8 @@ void ospf_terminate(void) */ zclient_stop(zclient); zclient_free(zclient); + zclient_stop(zclient_sync); + zclient_free(zclient_sync); done: frr_fini(); @@ -716,6 +725,7 @@ static void ospf_finish_final(struct ospf *ospf) if (!ospf->gr_info.prepare_in_progress) ospf_flush_self_originated_lsas_now(ospf); + XFREE(MTYPE_TMP, ospf->gr_info.exit_reason); /* Unregister redistribution */ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { @@ -761,7 +771,7 @@ static void ospf_finish_final(struct ospf *ospf) /* Clear static neighbors */ for (rn = route_top(ospf->nbr_nbma); rn; rn = route_next(rn)) if ((nbr_nbma = rn->info)) { - THREAD_OFF(nbr_nbma->t_poll); + EVENT_OFF(nbr_nbma->t_poll); if (nbr_nbma->nbr) { nbr_nbma->nbr->nbr_nbma = NULL; @@ -797,23 +807,23 @@ static void ospf_finish_final(struct ospf *ospf) } /* Cancel all timers. */ - THREAD_OFF(ospf->t_read); - THREAD_OFF(ospf->t_write); - THREAD_OFF(ospf->t_spf_calc); - THREAD_OFF(ospf->t_ase_calc); - THREAD_OFF(ospf->t_maxage); - THREAD_OFF(ospf->t_maxage_walker); - THREAD_OFF(ospf->t_abr_task); - THREAD_OFF(ospf->t_abr_fr); - THREAD_OFF(ospf->t_asbr_check); - THREAD_OFF(ospf->t_asbr_nssa_redist_update); - THREAD_OFF(ospf->t_distribute_update); - THREAD_OFF(ospf->t_lsa_refresher); - THREAD_OFF(ospf->t_opaque_lsa_self); - THREAD_OFF(ospf->t_sr_update); - THREAD_OFF(ospf->t_default_routemap_timer); - THREAD_OFF(ospf->t_external_aggr); - THREAD_OFF(ospf->gr_info.t_grace_period); + EVENT_OFF(ospf->t_read); + EVENT_OFF(ospf->t_write); + EVENT_OFF(ospf->t_spf_calc); + EVENT_OFF(ospf->t_ase_calc); + EVENT_OFF(ospf->t_maxage); + EVENT_OFF(ospf->t_maxage_walker); + EVENT_OFF(ospf->t_abr_task); + EVENT_OFF(ospf->t_abr_fr); + EVENT_OFF(ospf->t_asbr_check); + EVENT_OFF(ospf->t_asbr_nssa_redist_update); + EVENT_OFF(ospf->t_distribute_update); + EVENT_OFF(ospf->t_lsa_refresher); + EVENT_OFF(ospf->t_opaque_lsa_self); + EVENT_OFF(ospf->t_sr_update); + EVENT_OFF(ospf->t_default_routemap_timer); + EVENT_OFF(ospf->t_external_aggr); + EVENT_OFF(ospf->gr_info.t_grace_period); LSDB_LOOP (OPAQUE_AS_LSDB(ospf), rn, lsa) ospf_discard_from_db(ospf, ospf->lsdb, lsa); @@ -963,6 +973,7 @@ struct ospf_area *ospf_area_new(struct ospf *ospf, struct in_addr area_id) new->oiflist = list_new(); new->ranges = route_table_init(); + new->nssa_ranges = route_table_init(); if (area_id.s_addr == OSPF_AREA_BACKBONE) ospf->backbone = new; @@ -1006,6 +1017,7 @@ static void ospf_area_free(struct ospf_area *area) ospf_lsa_unlock(&area->router_lsa_self); route_table_finish(area->ranges); + route_table_finish(area->nssa_ranges); list_delete(&area->oiflist); if (EXPORT_NAME(area)) @@ -1015,8 +1027,8 @@ static void ospf_area_free(struct ospf_area *area) free(IMPORT_NAME(area)); /* Cancel timer. */ - THREAD_OFF(area->t_stub_router); - THREAD_OFF(area->t_opaque_lsa_self); + EVENT_OFF(area->t_stub_router); + EVENT_OFF(area->t_opaque_lsa_self); if (OSPF_IS_AREA_BACKBONE(area)) area->ospf->backbone = NULL; @@ -1029,13 +1041,14 @@ void ospf_area_check_free(struct ospf *ospf, struct in_addr area_id) struct ospf_area *area; area = ospf_area_lookup_by_area_id(ospf, area_id); - if (area && listcount(area->oiflist) == 0 && area->ranges->top == NULL - && !ospf_vl_count(ospf, area) - && area->shortcut_configured == OSPF_SHORTCUT_DEFAULT - && area->external_routing == OSPF_AREA_DEFAULT - && area->no_summary == 0 && area->default_cost == 1 - && EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL - && area->auth_type == OSPF_AUTH_NULL) { + if (area && listcount(area->oiflist) == 0 && + area->ranges->top == NULL && area->nssa_ranges->top == NULL && + !ospf_vl_count(ospf, area) && + area->shortcut_configured == OSPF_SHORTCUT_DEFAULT && + area->external_routing == OSPF_AREA_DEFAULT && + area->no_summary == 0 && area->default_cost == 1 && + EXPORT_NAME(area) == NULL && IMPORT_NAME(area) == NULL && + area->auth_type == OSPF_AUTH_NULL) { listnode_delete(ospf->areas, area); ospf_area_free(area); } @@ -1105,6 +1118,7 @@ struct ospf_interface *add_ospf_interface(struct connected *co, skip network type setting. */ oi->type = IF_DEF_PARAMS(co->ifp)->type; oi->ptp_dmvpn = IF_DEF_PARAMS(co->ifp)->ptp_dmvpn; + oi->p2mp_delay_reflood = IF_DEF_PARAMS(co->ifp)->p2mp_delay_reflood; /* Add pseudo neighbor. */ ospf_nbr_self_reset(oi, oi->ospf->router_id); @@ -1124,6 +1138,17 @@ struct ospf_interface *add_ospf_interface(struct connected *co, && if_is_operative(co->ifp)) ospf_if_up(oi); + /* + * RFC 3623 - Section 5 ("Unplanned Outages"): + * "The grace-LSAs are encapsulated in Link State Update Packets + * and sent out to all interfaces, even though the restarted + * router has no adjacencies and no knowledge of previous + * adjacencies". + */ + if (oi->ospf->gr_info.restart_in_progress && + oi->ospf->gr_info.reason == OSPF_GR_UNKNOWN_RESTART) + ospf_gr_unplanned_start_interface(oi); + return oi; } @@ -1451,7 +1476,7 @@ void ospf_ls_upd_queue_empty(struct ospf_interface *oi) } /* remove update event */ - THREAD_OFF(oi->t_ls_upd_event); + EVENT_OFF(oi->t_ls_upd_event); } void ospf_if_update(struct ospf *ospf, struct interface *ifp) @@ -1701,7 +1726,7 @@ int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id) return 1; } -int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) +int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id) { struct ospf_area *area; @@ -1709,22 +1734,14 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc) if (area == NULL) return 0; - /* argc < 5 -> 'no area x nssa' */ - if (argc < 5 && area->external_routing == OSPF_AREA_NSSA) { - ospf->anyNSSA--; - /* set NSSA area defaults */ - area->no_summary = 0; - area->suppress_fa = 0; - area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; - area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; - area->NSSATranslatorStabilityInterval = - OSPF_NSSA_TRANS_STABLE_DEFAULT; - ospf_area_type_set(area, OSPF_AREA_DEFAULT); - } else { - ospf_area_nssa_translator_role_set(ospf, area_id, - OSPF_NSSA_ROLE_CANDIDATE); - } - + ospf->anyNSSA--; + /* set NSSA area defaults */ + area->no_summary = 0; + area->suppress_fa = 0; + area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE; + area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED; + area->NSSATranslatorStabilityInterval = OSPF_NSSA_TRANS_STABLE_DEFAULT; + ospf_area_type_set(area, OSPF_AREA_DEFAULT); ospf_area_check_free(ospf, area_id); return 1; @@ -1782,6 +1799,51 @@ int ospf_area_nssa_translator_role_set(struct ospf *ospf, return 1; } +void ospf_area_nssa_default_originate_set(struct ospf *ospf, + struct in_addr area_id, int metric, + int metric_type) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return; + + if (!area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = true; + if (++ospf->nssa_default_import_check.refcnt == 1) { + ospf->nssa_default_import_check.status = false; + ospf_zebra_import_default_route(ospf, false); + } + } + + area->nssa_default_originate.metric_value = metric; + area->nssa_default_originate.metric_type = metric_type; +} + +void ospf_area_nssa_default_originate_unset(struct ospf *ospf, + struct in_addr area_id) +{ + struct ospf_area *area; + + area = ospf_area_lookup_by_area_id(ospf, area_id); + if (area == NULL) + return; + + if (area->nssa_default_originate.enabled) { + area->nssa_default_originate.enabled = false; + if (--ospf->nssa_default_import_check.refcnt == 0) { + ospf->nssa_default_import_check.status = false; + ospf_zebra_import_default_route(ospf, true); + } + area->nssa_default_originate.metric_value = -1; + area->nssa_default_originate.metric_type = -1; + + if (!IS_OSPF_ABR(ospf)) + ospf_abr_nssa_type7_defaults(ospf); + } +} + int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area, const char *list_name) { @@ -1859,9 +1921,9 @@ int ospf_timers_refresh_set(struct ospf *ospf, int interval) - (monotime(NULL) - ospf->lsa_refresher_started); if (time_left > interval) { - THREAD_OFF(ospf->t_lsa_refresher); - thread_add_timer(master, ospf_lsa_refresh_walker, ospf, - interval, &ospf->t_lsa_refresher); + EVENT_OFF(ospf->t_lsa_refresher); + event_add_timer(master, ospf_lsa_refresh_walker, ospf, interval, + &ospf->t_lsa_refresher); } ospf->lsa_refresh_interval = interval; @@ -1876,11 +1938,11 @@ int ospf_timers_refresh_unset(struct ospf *ospf) - (monotime(NULL) - ospf->lsa_refresher_started); if (time_left > OSPF_LSA_REFRESH_INTERVAL_DEFAULT) { - THREAD_OFF(ospf->t_lsa_refresher); + EVENT_OFF(ospf->t_lsa_refresher); ospf->t_lsa_refresher = NULL; - thread_add_timer(master, ospf_lsa_refresh_walker, ospf, - OSPF_LSA_REFRESH_INTERVAL_DEFAULT, - &ospf->t_lsa_refresher); + event_add_timer(master, ospf_lsa_refresh_walker, ospf, + OSPF_LSA_REFRESH_INTERVAL_DEFAULT, + &ospf->t_lsa_refresher); } ospf->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; @@ -1928,7 +1990,7 @@ static void ospf_nbr_nbma_delete(struct ospf *ospf, static void ospf_nbr_nbma_down(struct ospf_nbr_nbma *nbr_nbma) { - THREAD_OFF(nbr_nbma->t_poll); + EVENT_OFF(nbr_nbma->t_poll); if (nbr_nbma->nbr) { nbr_nbma->nbr->nbr_nbma = NULL; @@ -2117,7 +2179,7 @@ int ospf_nbr_nbma_poll_interval_set(struct ospf *ospf, struct in_addr nbr_addr, if (nbr_nbma->v_poll != interval) { nbr_nbma->v_poll = interval; if (nbr_nbma->oi && ospf_if_is_up(nbr_nbma->oi)) { - THREAD_OFF(nbr_nbma->t_poll); + EVENT_OFF(nbr_nbma->t_poll); OSPF_POLL_TIMER_ON(nbr_nbma->t_poll, ospf_poll_timer, nbr_nbma->v_poll); } @@ -2140,7 +2202,33 @@ int ospf_nbr_nbma_poll_interval_unset(struct ospf *ospf, struct in_addr addr) return 1; } -void ospf_master_init(struct thread_master *master) +/* + * Update socket bufsize(s), usually after config change + */ +void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize, + uint32_t sendsize) +{ + enum ospf_sock_type_e type = OSPF_SOCK_NONE; + + /* Figure out whether there's been a change */ + if (recvsize != ospf->recv_sock_bufsize) { + type = OSPF_SOCK_RECV; + ospf->recv_sock_bufsize = recvsize; + + if (sendsize != ospf->send_sock_bufsize) { + type = OSPF_SOCK_BOTH; + ospf->send_sock_bufsize = sendsize; + } + } else if (sendsize != ospf->send_sock_bufsize) { + type = OSPF_SOCK_SEND; + ospf->send_sock_bufsize = sendsize; + } + + if (type != OSPF_SOCK_NONE) + ospf_sock_bufsize_update(ospf, ospf->fd, type); +} + +void ospf_master_init(struct event_loop *master) { memset(&ospf_master, 0, sizeof(ospf_master)); @@ -2199,20 +2287,20 @@ static void ospf_set_redist_vrf_bitmaps(struct ospf *ospf, bool set) "%s: setting redist vrf %d bitmap for type %d", __func__, ospf->vrf_id, type); if (set) - vrf_bitmap_set(zclient->redist[AFI_IP][type], + vrf_bitmap_set(&zclient->redist[AFI_IP][type], ospf->vrf_id); else - vrf_bitmap_unset(zclient->redist[AFI_IP][type], + vrf_bitmap_unset(&zclient->redist[AFI_IP][type], ospf->vrf_id); } red_list = ospf->redist[DEFAULT_ROUTE]; if (red_list) { if (set) - vrf_bitmap_set(zclient->default_information[AFI_IP], + vrf_bitmap_set(&zclient->default_information[AFI_IP], ospf->vrf_id); else - vrf_bitmap_unset(zclient->default_information[AFI_IP], + vrf_bitmap_unset(&zclient->default_information[AFI_IP], ospf->vrf_id); } } @@ -2247,8 +2335,8 @@ static int ospf_vrf_enable(struct vrf *vrf) ret = ospf_sock_init(ospf); if (ret < 0 || ospf->fd <= 0) return 0; - thread_add_read(master, ospf_read, ospf, ospf->fd, - &ospf->t_read); + event_add_read(master, ospf_read, ospf, ospf->fd, + &ospf->t_read); ospf->oi_running = 1; ospf_router_id_update(ospf); } @@ -2286,7 +2374,7 @@ static int ospf_vrf_disable(struct vrf *vrf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf old_vrf_id %d unlinked", __func__, old_vrf_id); - THREAD_OFF(ospf->t_read); + EVENT_OFF(ospf->t_read); close(ospf->fd); ospf->fd = -1; } diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 4df65ea759d6..860140cb76f7 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -67,6 +67,12 @@ #define OSPF_LS_REFRESH_SHIFT (60 * 15) #define OSPF_LS_REFRESH_JITTER 60 +/* Default socket buffer size */ +#define OSPF_DEFAULT_SOCK_BUFSIZE (8 * 1024 * 1024) + +/* OSPF config processing timer thread */ +extern struct event *t_ospf_cfg; + struct ospf_external { unsigned short instance; struct route_table *external_info; @@ -78,7 +84,7 @@ struct ospf_master { struct list *ospf; /* OSPF thread master. */ - struct thread_master *master; + struct event_loop *master; /* Various OSPF global configuration. */ uint8_t options; @@ -145,7 +151,9 @@ struct ospf_gr_info { bool prepare_in_progress; bool finishing_restart; uint32_t grace_period; - struct thread *t_grace_period; + int reason; + char *exit_reason; + struct event *t_grace_period; }; /* OSPF instance structure. */ @@ -257,31 +265,30 @@ struct ospf { int redistribute; /* Num of redistributed protocols. */ /* Threads. */ - struct thread *t_abr_task; /* ABR task timer. */ - struct thread *t_abr_fr; /* ABR FR timer. */ - struct thread *t_asbr_check; /* ASBR check timer. */ - struct thread *t_asbr_nssa_redist_update; /* ASBR NSSA redistribution + struct event *t_abr_task; /* ABR task timer. */ + struct event *t_abr_fr; /* ABR FR timer. */ + struct event *t_asbr_check; /* ASBR check timer. */ + struct event *t_asbr_nssa_redist_update; /* ASBR NSSA redistribution update timer. */ - 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_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ - struct thread *t_sr_update; /* Segment Routing update timer */ + struct event *t_distribute_update; /* Distirbute list update timer. */ + struct event *t_spf_calc; /* SPF calculation timer. */ + struct event *t_ase_calc; /* ASE calculation timer. */ + struct event *t_opaque_lsa_self; /* Type-11 Opaque-LSAs origin event. */ + struct event *t_sr_update; /* Segment Routing update timer */ unsigned int maxage_delay; /* Delay on Maxage remover timer, sec */ - struct thread *t_maxage; /* MaxAge LSA remover timer. */ - struct thread *t_maxage_walker; /* MaxAge LSA checking timer. */ + struct event *t_maxage; /* MaxAge LSA remover timer. */ + struct event *t_maxage_walker; /* MaxAge LSA checking timer. */ - struct thread + struct event *t_deferred_shutdown; /* deferred/stub-router shutdown timer*/ - struct thread *t_write; + struct event *t_write; #define OSPF_WRITE_INTERFACE_COUNT_DEFAULT 20 - struct thread *t_default_routemap_timer; + struct event *t_default_routemap_timer; int write_oi_count; /* Num of packets sent per thread invocation */ - struct thread *t_read; + struct event *t_read; int fd; struct stream *ibuf; struct list *oi_write_q; @@ -303,6 +310,18 @@ struct ospf { int default_metric; /* Default metric for redistribute. */ + /* NSSA default-information-originate */ + struct { + /* # of NSSA areas requesting default information */ + uint16_t refcnt; + + /* + * Whether a default route known through non-OSPF protocol is + * present in the RIB. + */ + bool status; + } nssa_default_import_check; + #define OSPF_LSA_REFRESHER_GRANULARITY 10 #define OSPF_LSA_REFRESHER_SLOTS \ ((OSPF_LS_REFRESH_TIME + OSPF_LS_REFRESH_SHIFT) \ @@ -313,7 +332,7 @@ struct ospf { struct list *qs[OSPF_LSA_REFRESHER_SLOTS]; } lsa_refresh_queue; - struct thread *t_lsa_refresher; + struct event *t_lsa_refresher; time_t lsa_refresher_started; #define OSPF_LSA_REFRESH_INTERVAL_DEFAULT 10 uint16_t lsa_refresh_interval; @@ -382,7 +401,7 @@ struct ospf { /* delay timer to process external routes * with summary address. */ - struct thread *t_external_aggr; + struct event *t_external_aggr; /* delay interval in seconds */ uint16_t aggr_delay_interval; @@ -413,6 +432,13 @@ struct ospf { /* Flood Reduction configuration state */ bool fr_configured; + /* Socket buffer sizes */ + uint32_t recv_sock_bufsize; + uint32_t send_sock_bufsize; + + /* Per-interface write socket */ + bool intf_socket_enabled; + QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(ospf); @@ -518,6 +544,7 @@ struct ospf_area { #define OSPF_TRANSIT_FALSE 0 #define OSPF_TRANSIT_TRUE 1 struct route_table *ranges; /* Configured Area Ranges. */ + struct route_table *nssa_ranges; /* Configured NSSA Area Ranges. */ /* RFC3137 stub router state flags for area */ uint8_t stub_router_state; @@ -562,6 +589,13 @@ struct ospf_area { #define PREFIX_LIST_OUT(A) (A)->plist_out.list #define PREFIX_NAME_OUT(A) (A)->plist_out.name + /* NSSA default-information-originate */ + struct { + bool enabled; + int metric_type; + int metric_value; + } nssa_default_originate; + /* Shortest Path Tree. */ struct vertex *spf; struct list *spf_vertex_list; @@ -578,8 +612,8 @@ struct ospf_area { struct p_spaces_head *p_spaces; /* Threads. */ - struct thread *t_stub_router; /* Stub-router timer */ - struct thread *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */ + struct event *t_stub_router; /* Stub-router timer */ + struct event *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */ /* Statistics field. */ uint32_t spf_calculation; /* SPF Calculation Count. */ @@ -627,7 +661,7 @@ struct ospf_nbr_nbma { uint32_t v_poll; /* Poll timer thread. */ - struct thread *t_poll; + struct event *t_poll; /* State change. */ uint32_t state_change; @@ -654,16 +688,18 @@ struct ospf_nbr_nbma { #define LSA_OPTIONS_NSSA_GET(area) \ (((area)->external_routing == OSPF_AREA_NSSA) ? OSPF_OPTION_NP : 0) -#define OSPF_TIMER_ON(T,F,V) thread_add_timer (master,(F),ospf,(V),&(T)) -#define OSPF_AREA_TIMER_ON(T,F,V) thread_add_timer (master, (F), area, (V), &(T)) -#define OSPF_POLL_TIMER_ON(T,F,V) thread_add_timer (master, (F), nbr_nbma, (V), &(T)) +#define OSPF_TIMER_ON(T, F, V) event_add_timer(master, (F), ospf, (V), &(T)) +#define OSPF_AREA_TIMER_ON(T, F, V) \ + event_add_timer(master, (F), area, (V), &(T)) +#define OSPF_POLL_TIMER_ON(T, F, V) \ + event_add_timer(master, (F), nbr_nbma, (V), &(T)) /* Extern variables. */ extern struct ospf_master *om; extern unsigned short ospf_instance; extern const int ospf_redistributed_proto_max; extern struct zclient *zclient; -extern struct thread_master *master; +extern struct event_loop *master; extern int ospf_zlog; extern struct zebra_privs_t ospfd_privs; @@ -696,14 +732,18 @@ extern int ospf_area_no_summary_set(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_no_summary_unset(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id); -extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, - int argc); +extern int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, struct in_addr area_id); extern int ospf_area_nssa_translator_role_set(struct ospf *ospf, struct in_addr area_id, int role); +extern void ospf_area_nssa_default_originate_set(struct ospf *ospf, + struct in_addr area_id, + int metric, int metric_type); +extern void ospf_area_nssa_default_originate_unset(struct ospf *ospf, + struct in_addr area_id); extern int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area_id, const char *list_name); @@ -757,7 +797,7 @@ extern void ospf_interface_area_unset(struct ospf *ospf, struct interface *ifp); extern void ospf_route_map_init(void); -extern void ospf_master_init(struct thread_master *master); +extern void ospf_master_init(struct event_loop *master); extern void ospf_vrf_init(void); extern void ospf_vrf_terminate(void); extern void ospf_vrf_link(struct ospf *ospf, struct vrf *vrf); @@ -768,6 +808,9 @@ int ospf_area_nssa_no_summary_set(struct ospf *ospf, struct in_addr area_id); const char *ospf_get_name(const struct ospf *ospf); extern struct ospf_interface *add_ospf_interface(struct connected *co, struct ospf_area *area); +/* Update socket bufsize(s), after config change */ +void ospf_update_bufsize(struct ospf *ospf, uint32_t recvsize, + uint32_t sendsize); extern int p_spaces_compare_func(const struct p_space *a, const struct p_space *b); diff --git a/pathd/path_main.c b/pathd/path_main.c index 3d35b9f9b30a..c333162f2b4f 100644 --- a/pathd/path_main.c +++ b/pathd/path_main.c @@ -6,7 +6,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "log.h" #include "memory.h" @@ -41,7 +41,7 @@ struct zebra_privs_t pathd_privs = { struct option longopts[] = {{0}}; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; static struct frr_daemon_info pathd_di; diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c index 0c5e495044d1..ec9d8adfc145 100644 --- a/pathd/path_pcep.c +++ b/pathd/path_pcep.c @@ -58,7 +58,7 @@ static void notify_status(struct path *path, bool not_changed); /* Module Functions */ static int pcep_module_finish(void); -static int pcep_module_late_init(struct thread_master *tm); +static int pcep_module_late_init(struct event_loop *tm); static int pcep_module_init(void); /* ------------ Path Helper Functions ------------ */ @@ -310,7 +310,7 @@ int pathd_candidate_removed_handler(struct srte_candidate *candidate) * run before config load, so the CLI commands don't try to touch things that * aren't set up yet... */ -static int pcep_module_config_pre(struct thread_master *tm) +static int pcep_module_config_pre(struct event_loop *tm) { assert(pcep_g->fpt == NULL); assert(pcep_g->master == NULL); @@ -329,7 +329,7 @@ static int pcep_module_config_pre(struct thread_master *tm) return 0; } -static int pcep_module_late_init(struct thread_master *tm) +static int pcep_module_late_init(struct event_loop *tm) { hook_register(pathd_candidate_created, pathd_candidate_created_handler); hook_register(pathd_candidate_updated, pathd_candidate_updated_handler); diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h index 6f92d7636226..5c6a02372f23 100644 --- a/pathd/path_pcep.h +++ b/pathd/path_pcep.h @@ -294,7 +294,7 @@ struct path { struct pcep_glob { struct debug dbg; - struct thread_master *master; + struct event_loop *master; struct frr_pthread *fpt; uint8_t num_pce_opts_cli; struct pce_opts_cli *pce_opts_cli[MAX_PCE]; diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c index 21d127bcfe91..da7ee89f2fef 100644 --- a/pathd/path_pcep_config.c +++ b/pathd/path_pcep_config.c @@ -13,7 +13,7 @@ #include "pathd/path_pcep.h" #include "pathd/path_pcep_config.h" #include "pathd/path_pcep_debug.h" -#include "thread.h" +#include "frrevent.h" #define MAX_XPATH 256 #define MAX_FLOAT_LEN 22 diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c index 405749b50ff4..a00a11408668 100644 --- a/pathd/path_pcep_controller.c +++ b/pathd/path_pcep_controller.c @@ -78,28 +78,28 @@ struct get_pcep_session_args { /* Internal Functions Called From Main Thread */ static int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res); -static void pcep_refine_path_event_cb(struct thread *thread); +static void pcep_refine_path_event_cb(struct event *thread); /* Internal Functions Called From Controller Thread */ -static void pcep_thread_finish_event_handler(struct thread *thread); +static void pcep_thread_finish_event_handler(struct event *thread); /* Controller Thread Timer Handler */ static int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_timer_type timer_type, enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *payload, - struct thread **thread); + struct event **thread); static int schedule_thread_timer_with_cb( struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_timer_type timer_type, enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *payload, - struct thread **thread, pcep_ctrl_thread_callback timer_cb); -static void pcep_thread_timer_handler(struct thread *thread); + struct event **thread, pcep_ctrl_thread_callback timer_cb); +static void pcep_thread_timer_handler(struct event *thread); /* Controller Thread Socket read/write Handler */ static int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_socket_type type, bool is_read, - void *payload, int fd, struct thread **thread, + void *payload, int fd, struct event **thread, pcep_ctrl_thread_callback cb); /* Controller Thread Event Handler */ @@ -110,7 +110,7 @@ static int send_to_thread_with_cb(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_event_type type, uint32_t sub_type, void *payload, pcep_ctrl_thread_callback event_cb); -static void pcep_thread_event_handler(struct thread *thread); +static void pcep_thread_event_handler(struct event *thread); static int pcep_thread_event_update_pcc_options(struct ctrl_state *ctrl_state, struct pcc_opts *opts); static int pcep_thread_event_update_pce_options(struct ctrl_state *ctrl_state, @@ -135,7 +135,7 @@ pcep_thread_path_refined_event(struct ctrl_state *ctrl_state, /* Main Thread Event Handler */ static int send_to_main(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_main_event_type type, void *payload); -static void pcep_main_event_handler(struct thread *thread); +static void pcep_main_event_handler(struct event *thread); /* Helper functions */ static void set_ctrl_state(struct frr_pthread *fpt, @@ -152,7 +152,7 @@ static const char *timeout_type_name(enum pcep_ctrl_timeout_type type); /* ------------ API Functions Called from Main Thread ------------ */ -int pcep_ctrl_initialize(struct thread_master *main_thread, +int pcep_ctrl_initialize(struct event_loop *main_thread, struct frr_pthread **fpt, pcep_main_event_handler_t event_handler) { @@ -319,16 +319,16 @@ int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id, int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res) { - thread_add_event(fpt->master, pcep_thread_finish_event_handler, - (void *)fpt, 0, NULL); + event_add_event(fpt->master, pcep_thread_finish_event_handler, + (void *)fpt, 0, NULL); pthread_join(fpt->thread, res); return 0; } -void pcep_refine_path_event_cb(struct thread *thread) +void pcep_refine_path_event_cb(struct event *thread) { - struct pcep_refine_path_event_data *data = THREAD_ARG(thread); + struct pcep_refine_path_event_data *data = EVENT_ARG(thread); assert(data != NULL); struct ctrl_state *ctrl_state = data->ctrl_state; struct path *path = data->path; @@ -377,20 +377,20 @@ void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state, void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state, int pcc_id, int delay, - struct thread **thread) + struct event **thread) { schedule_thread_timer(ctrl_state, pcc_id, TM_CALCULATE_BEST_PCE, TO_UNDEFINED, delay, NULL, thread); } -void pcep_thread_cancel_timer(struct thread **thread) +void pcep_thread_cancel_timer(struct event **thread) { if (thread == NULL || *thread == NULL) { return; } - struct pcep_ctrl_timer_data *data = THREAD_ARG(*thread); + struct pcep_ctrl_timer_data *data = EVENT_ARG(*thread); PCEP_DEBUG("Timer %s / %s canceled", timer_type_name(data->timer_type), timeout_type_name(data->timeout_type)); if (data != NULL) { @@ -398,14 +398,14 @@ void pcep_thread_cancel_timer(struct thread **thread) } if ((*thread)->master->owner == pthread_self()) { - thread_cancel(thread); + event_cancel(thread); } else { - thread_cancel_async((*thread)->master, thread, NULL); + event_cancel_async((*thread)->master, thread, NULL); } } void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id, - int retry_count, struct thread **thread) + int retry_count, struct event **thread) { uint32_t delay = backoff_delay(MAX_RECONNECT_DELAY, 1, retry_count); PCEP_DEBUG("Schedule RECONNECT_PCC for %us (retry %u)", delay, @@ -417,7 +417,7 @@ void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id, void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *param, - struct thread **thread) + struct event **thread) { assert(timeout_type > TO_UNDEFINED); assert(timeout_type < TO_MAX); @@ -429,7 +429,7 @@ void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id, void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state, int delay, void *payload, - struct thread **thread, + struct event **thread, pcep_ctrl_thread_callback timer_cb) { PCEP_DEBUG("Schedule PCEPLIB_TIMER for %us", delay); @@ -440,7 +440,7 @@ void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state, void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state, int pcc_id, int delay, - struct thread **thread) + struct event **thread) { PCEP_DEBUG("Schedule session_timeout interval for %us", delay); schedule_thread_timer(ctrl_state, pcc_id, TM_SESSION_TIMEOUT_PCC, @@ -469,8 +469,8 @@ int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id, data->continue_lsp_update_handler = cb; data->payload = payload; - thread_add_event(ctrl_state->main, pcep_refine_path_event_cb, - (void *)data, 0, NULL); + event_add_event(ctrl_state->main, pcep_refine_path_event_cb, + (void *)data, 0, NULL); return 0; } @@ -493,10 +493,10 @@ void pcep_thread_path_refined_event(struct ctrl_state *ctrl_state, /* ------------ Internal Functions Called From Controller Thread ------------ */ -void pcep_thread_finish_event_handler(struct thread *thread) +void pcep_thread_finish_event_handler(struct event *thread) { int i; - struct frr_pthread *fpt = THREAD_ARG(thread); + struct frr_pthread *fpt = EVENT_ARG(thread); struct ctrl_state *ctrl_state = fpt->data; assert(ctrl_state != NULL); @@ -521,7 +521,7 @@ int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_timer_type timer_type, enum pcep_ctrl_timeout_type timeout_type, uint32_t delay, void *payload, - struct thread **thread, + struct event **thread, pcep_ctrl_thread_callback timer_cb) { assert(thread != NULL); @@ -535,8 +535,8 @@ int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id, data->pcc_id = pcc_id; data->payload = payload; - thread_add_timer(ctrl_state->self, timer_cb, (void *)data, delay, - thread); + event_add_timer(ctrl_state->self, timer_cb, (void *)data, delay, + thread); return 0; } @@ -544,17 +544,17 @@ int schedule_thread_timer_with_cb(struct ctrl_state *ctrl_state, int pcc_id, int schedule_thread_timer(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_timer_type timer_type, enum pcep_ctrl_timeout_type timeout_type, - uint32_t delay, void *payload, struct thread **thread) + uint32_t delay, void *payload, struct event **thread) { return schedule_thread_timer_with_cb(ctrl_state, pcc_id, timer_type, timeout_type, delay, payload, thread, pcep_thread_timer_handler); } -void pcep_thread_timer_handler(struct thread *thread) +void pcep_thread_timer_handler(struct event *thread) { /* data unpacking */ - struct pcep_ctrl_timer_data *data = THREAD_ARG(thread); + struct pcep_ctrl_timer_data *data = EVENT_ARG(thread); assert(data != NULL); struct ctrl_state *ctrl_state = data->ctrl_state; assert(ctrl_state != NULL); @@ -598,9 +598,9 @@ void pcep_thread_timer_handler(struct thread *thread) } } -void pcep_thread_pcep_event(struct thread *thread) +void pcep_thread_pcep_event(struct event *thread) { - struct pcep_ctrl_event_data *data = THREAD_ARG(thread); + struct pcep_ctrl_event_data *data = EVENT_ARG(thread); assert(data != NULL); struct ctrl_state *ctrl_state = data->ctrl_state; pcep_event *event = data->payload; @@ -624,7 +624,7 @@ void pcep_thread_pcep_event(struct thread *thread) int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_socket_type type, bool is_read, - void *payload, int fd, struct thread **thread, + void *payload, int fd, struct event **thread, pcep_ctrl_thread_callback socket_cb) { assert(thread != NULL); @@ -640,11 +640,11 @@ int schedule_thread_socket(struct ctrl_state *ctrl_state, int pcc_id, data->payload = payload; if (is_read) { - thread_add_read(ctrl_state->self, socket_cb, (void *)data, fd, - thread); + event_add_read(ctrl_state->self, socket_cb, (void *)data, fd, + thread); } else { - thread_add_write(ctrl_state->self, socket_cb, (void *)data, fd, - thread); + event_add_write(ctrl_state->self, socket_cb, (void *)data, fd, + thread); } return 0; @@ -656,7 +656,7 @@ int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload, struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data; return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, false, - payload, fd, (struct thread **)thread, + payload, fd, (struct event **)thread, socket_cb); } @@ -666,7 +666,7 @@ int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload, struct ctrl_state *ctrl_state = ((struct frr_pthread *)fpt)->data; return schedule_thread_socket(ctrl_state, 0, SOCK_PCEPLIB, true, - payload, fd, (struct thread **)thread, + payload, fd, (struct event **)thread, socket_cb); } @@ -702,15 +702,15 @@ int send_to_thread_with_cb(struct ctrl_state *ctrl_state, int pcc_id, data->pcc_id = pcc_id; data->payload = payload; - thread_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL); + event_add_event(ctrl_state->self, event_cb, (void *)data, 0, NULL); return 0; } -void pcep_thread_event_handler(struct thread *thread) +void pcep_thread_event_handler(struct event *thread) { /* data unpacking */ - struct pcep_ctrl_event_data *data = THREAD_ARG(thread); + struct pcep_ctrl_event_data *data = EVENT_ARG(thread); assert(data != NULL); struct ctrl_state *ctrl_state = data->ctrl_state; assert(ctrl_state != NULL); @@ -964,15 +964,15 @@ int send_to_main(struct ctrl_state *ctrl_state, int pcc_id, data->pcc_id = pcc_id; data->payload = payload; - thread_add_event(ctrl_state->main, pcep_main_event_handler, - (void *)data, 0, NULL); + event_add_event(ctrl_state->main, pcep_main_event_handler, (void *)data, + 0, NULL); return 0; } -void pcep_main_event_handler(struct thread *thread) +void pcep_main_event_handler(struct event *thread) { /* data unpacking */ - struct pcep_main_event_data *data = THREAD_ARG(thread); + struct pcep_main_event_data *data = EVENT_ARG(thread); assert(data != NULL); pcep_main_event_handler_t handler = data->handler; enum pcep_main_event_type type = data->type; diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h index c2359beb23c3..236b2677153b 100644 --- a/pathd/path_pcep_controller.h +++ b/pathd/path_pcep_controller.h @@ -33,8 +33,8 @@ enum pcep_pathd_event_type { }; struct ctrl_state { - struct thread_master *main; - struct thread_master *self; + struct event_loop *main; + struct event_loop *self; pcep_main_event_handler_t main_event_handler; struct pcc_opts *pcc_opts; int pcc_count; @@ -77,7 +77,7 @@ struct pcep_ctrl_socket_data { void *payload; }; -typedef void (*pcep_ctrl_thread_callback)(struct thread *); +typedef void (*pcep_ctrl_thread_callback)(struct event *); /* PCC connection information, populated in a thread-safe * manner with pcep_ctrl_get_pcc_info() */ @@ -97,7 +97,7 @@ struct pcep_pcc_info { }; /* Functions called from the main thread */ -int pcep_ctrl_initialize(struct thread_master *main_thread, +int pcep_ctrl_initialize(struct event_loop *main_thread, struct frr_pthread **fpt, pcep_main_event_handler_t event_handler); int pcep_ctrl_finalize(struct frr_pthread **fpt); @@ -134,25 +134,25 @@ void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id, struct path *path); void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id, struct path *path); -void pcep_thread_cancel_timer(struct thread **thread); +void pcep_thread_cancel_timer(struct event **thread); void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id, - int retry_count, struct thread **thread); + int retry_count, struct event **thread); void pcep_thread_schedule_timeout(struct ctrl_state *ctrl_state, int pcc_id, enum pcep_ctrl_timeout_type type, uint32_t delay, void *param, - struct thread **thread); + struct event **thread); void pcep_thread_schedule_session_timeout(struct ctrl_state *ctrl_state, int pcc_id, int delay, - struct thread **thread); + struct event **thread); void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state); void pcep_thread_schedule_sync_best_pce(struct ctrl_state *ctrl_state, int pcc_id, int delay, - struct thread **thread); + struct event **thread); void pcep_thread_schedule_pceplib_timer(struct ctrl_state *ctrl_state, int delay, void *payload, - struct thread **thread, + struct event **thread, pcep_ctrl_thread_callback cb); int pcep_thread_socket_read(void *fpt, void **thread, int fd, void *payload, pcep_ctrl_thread_callback cb); @@ -161,7 +161,7 @@ int pcep_thread_socket_write(void *fpt, void **thread, int fd, void *payload, int pcep_thread_send_ctrl_event(void *fpt, void *payload, pcep_ctrl_thread_callback cb); -void pcep_thread_pcep_event(struct thread *thread); +void pcep_thread_pcep_event(struct event *thread); int pcep_thread_pcc_count(struct ctrl_state *ctrl_state); /* Called by the PCC to refine a path in the main thread */ int pcep_thread_refine_path(struct ctrl_state *ctrl_state, int pcc_id, diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c index 502f2336c752..d43fdb083e36 100644 --- a/pathd/path_pcep_lib.c +++ b/pathd/path_pcep_lib.c @@ -33,8 +33,8 @@ static int pcep_lib_pceplib_socket_read_cb(void *fpt, void **thread, int fd, void *payload); static int pcep_lib_pceplib_socket_write_cb(void *fpt, void **thread, int fd, void *payload); -static void pcep_lib_socket_read_ready(struct thread *thread); -static void pcep_lib_socket_write_ready(struct thread *thread); +static void pcep_lib_socket_read_ready(struct event *thread); +static void pcep_lib_socket_write_ready(struct event *thread); /* pceplib pcep_event callbacks */ static void pcep_lib_pceplib_event_cb(void *fpt, pcep_event *event); @@ -229,18 +229,18 @@ int pcep_lib_pceplib_socket_read_cb(void *fpt, void **thread, int fd, /* Callbacks called by path_pcep_controller when a socket is ready to read/write */ -void pcep_lib_socket_write_ready(struct thread *thread) +void pcep_lib_socket_write_ready(struct event *thread) { - struct pcep_ctrl_socket_data *data = THREAD_ARG(thread); + struct pcep_ctrl_socket_data *data = EVENT_ARG(thread); assert(data != NULL); pceplib_external_socket_write(data->fd, data->payload); XFREE(MTYPE_PCEP, data); } -void pcep_lib_socket_read_ready(struct thread *thread) +void pcep_lib_socket_read_ready(struct event *thread) { - struct pcep_ctrl_socket_data *data = THREAD_ARG(thread); + struct pcep_ctrl_socket_data *data = EVENT_ARG(thread); assert(data != NULL); pceplib_external_socket_read(data->fd, data->payload); diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c index 0ff0b4403a22..f18eff28888a 100644 --- a/pathd/path_pcep_pcc.c +++ b/pathd/path_pcep_pcc.c @@ -190,17 +190,17 @@ void pcep_pcc_finalize(struct ctrl_state *ctrl_state, } if (pcc_state->t_reconnect != NULL) { - thread_cancel(&pcc_state->t_reconnect); + event_cancel(&pcc_state->t_reconnect); pcc_state->t_reconnect = NULL; } if (pcc_state->t_update_best != NULL) { - thread_cancel(&pcc_state->t_update_best); + event_cancel(&pcc_state->t_update_best); pcc_state->t_update_best = NULL; } if (pcc_state->t_session_timeout != NULL) { - thread_cancel(&pcc_state->t_session_timeout); + event_cancel(&pcc_state->t_session_timeout); pcc_state->t_session_timeout = NULL; } @@ -340,7 +340,7 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) assert(pcc_state->sess == NULL); if (pcc_state->t_reconnect != NULL) { - thread_cancel(&pcc_state->t_reconnect); + event_cancel(&pcc_state->t_reconnect); pcc_state->t_reconnect = NULL; } @@ -408,7 +408,7 @@ int pcep_pcc_enable(struct ctrl_state *ctrl_state, struct pcc_state *pcc_state) // In case some best pce alternative were waiting to activate if (pcc_state->t_update_best != NULL) { - thread_cancel(&pcc_state->t_update_best); + event_cancel(&pcc_state->t_update_best); pcc_state->t_update_best = NULL; } diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h index 039d67d2e25e..f3d0f290a522 100644 --- a/pathd/path_pcep_pcc.h +++ b/pathd/path_pcep_pcc.h @@ -41,7 +41,7 @@ struct req_map_data { struct req_entry { RB_ENTRY(req_entry) entry; - struct thread *t_retry; + struct event *t_retry; int retry_count; bool was_sent; struct path *path; @@ -66,9 +66,9 @@ struct pcc_state { pcep_session *sess; uint32_t retry_count; bool synchronized; - struct thread *t_reconnect; - struct thread *t_update_best; - struct thread *t_session_timeout; + struct event *t_reconnect; + struct event *t_update_best; + struct event *t_session_timeout; uint32_t next_reqid; uint32_t next_plspid; struct plspid_map_head plspid_map; diff --git a/pathd/path_ted.c b/pathd/path_ted.c index bc0da969e3b0..df23f9312744 100644 --- a/pathd/path_ted.c +++ b/pathd/path_ted.c @@ -28,8 +28,8 @@ static void path_ted_unregister_vty(void); static uint32_t path_ted_start_importing_igp(const char *daemon_str); static uint32_t path_ted_stop_importing_igp(void); static enum zclient_send_status path_ted_link_state_sync(void); -static void path_ted_timer_handler_sync(struct thread *thread); -static void path_ted_timer_handler_refresh(struct thread *thread); +static void path_ted_timer_handler_sync(struct event *thread); +static void path_ted_timer_handler_refresh(struct event *thread); static int path_ted_cli_debug_config_write(struct vty *vty); static int path_ted_cli_debug_set_all(uint32_t flags, bool set); @@ -41,7 +41,7 @@ struct ted_state ted_state_g = {}; * path_path_ted public API function implementations */ -void path_ted_init(struct thread_master *master) +void path_ted_init(struct event_loop *master) { ted_state_g.main = master; ted_state_g.link_state_delay_interval = TIMER_RETRY_DELAY; @@ -206,7 +206,7 @@ uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote) { uint32_t sid = MPLS_LABEL_NONE; struct ls_edge *edge; - uint64_t key; + struct ls_edge_key key; if (!path_ted_is_initialized()) return MPLS_LABEL_NONE; @@ -218,7 +218,8 @@ uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote) case IPADDR_V4: /* We have local and remote ip */ /* so check all attributes in ted */ - key = ((uint64_t)ntohl(local->ip._v4_addr.s_addr)) & 0xffffffff; + key.family = AF_INET; + IPV4_ADDR_COPY(&key.k.addr, &local->ip._v4_addr); edge = ls_find_edge_by_key(ted_state_g.ted, key); if (edge) { if (edge->attributes->standard.remote.s_addr @@ -232,8 +233,8 @@ uint32_t path_ted_query_type_f(struct ipaddr *local, struct ipaddr *remote) } break; case IPADDR_V6: - key = (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[2]) << 32 | - (uint64_t)ntohl(local->ip._v6_addr.s6_addr32[3]); + key.family = AF_INET6; + IPV6_ADDR_COPY(&key.k.addr6, &local->ip._v6_addr); edge = ls_find_edge_by_key(ted_state_g.ted, key); if (edge) { if ((0 == memcmp(&edge->attributes->standard.remote6, @@ -268,7 +269,7 @@ uint32_t path_ted_query_type_c(struct prefix *prefix, uint8_t algo) switch (prefix->family) { case AF_INET: case AF_INET6: - subnet = ls_find_subnet(ted_state_g.ted, *prefix); + subnet = ls_find_subnet(ted_state_g.ted, prefix); if (subnet) { if ((CHECK_FLAG(subnet->ls_pref->flags, LS_PREF_SR)) && (subnet->ls_pref->sr.algo == algo)) @@ -298,7 +299,7 @@ uint32_t path_ted_query_type_e(struct prefix *prefix, uint32_t iface_id) switch (prefix->family) { case AF_INET: case AF_INET6: - subnet = ls_find_subnet(ted_state_g.ted, *prefix); + subnet = ls_find_subnet(ted_state_g.ted, prefix); if (subnet && subnet->vertex && subnet->vertex->outgoing_edges) { /* from the vertex linked in subnet */ @@ -583,9 +584,9 @@ enum zclient_send_status path_ted_link_state_sync(void) PATH_TED_DEBUG("%s: PATHD-TED: Opaque asked for TED sync ", __func__); } - thread_add_timer(ted_state_g.main, path_ted_timer_handler_sync, - &ted_state_g, ted_state_g.link_state_delay_interval, - &ted_state_g.t_link_state_sync); + event_add_timer(ted_state_g.main, path_ted_timer_handler_sync, + &ted_state_g, ted_state_g.link_state_delay_interval, + &ted_state_g.t_link_state_sync); return status; } @@ -597,10 +598,10 @@ enum zclient_send_status path_ted_link_state_sync(void) * * @return status */ -void path_ted_timer_handler_sync(struct thread *thread) +void path_ted_timer_handler_sync(struct event *thread) { /* data unpacking */ - struct ted_state *data = THREAD_ARG(thread); + struct ted_state *data = EVENT_ARG(thread); assert(data != NULL); /* Retry the sync */ @@ -619,10 +620,9 @@ int path_ted_segment_list_refresh(void) int status = 0; path_ted_timer_refresh_cancel(); - thread_add_timer(ted_state_g.main, path_ted_timer_handler_refresh, - &ted_state_g, - ted_state_g.segment_list_refresh_interval, - &ted_state_g.t_segment_list_refresh); + event_add_timer(ted_state_g.main, path_ted_timer_handler_refresh, + &ted_state_g, ted_state_g.segment_list_refresh_interval, + &ted_state_g.t_segment_list_refresh); return status; } @@ -634,14 +634,14 @@ int path_ted_segment_list_refresh(void) * * @return status */ -void path_ted_timer_handler_refresh(struct thread *thread) +void path_ted_timer_handler_refresh(struct event *thread) { if (!path_ted_is_initialized()) return; PATH_TED_DEBUG("%s: PATHD-TED: Refresh sid from current TED", __func__); /* data unpacking */ - struct ted_state *data = THREAD_ARG(thread); + struct ted_state *data = EVENT_ARG(thread); assert(data != NULL); @@ -658,7 +658,7 @@ void path_ted_timer_handler_refresh(struct thread *thread) void path_ted_timer_sync_cancel(void) { if (ted_state_g.t_link_state_sync != NULL) { - thread_cancel(&ted_state_g.t_link_state_sync); + event_cancel(&ted_state_g.t_link_state_sync); ted_state_g.t_link_state_sync = NULL; } } @@ -673,7 +673,7 @@ void path_ted_timer_sync_cancel(void) void path_ted_timer_refresh_cancel(void) { if (ted_state_g.t_segment_list_refresh != NULL) { - thread_cancel(&ted_state_g.t_segment_list_refresh); + event_cancel(&ted_state_g.t_segment_list_refresh); ted_state_g.t_segment_list_refresh = NULL; } } diff --git a/pathd/path_ted.h b/pathd/path_ted.h index 10cdd545f12b..a1bc784b7f09 100644 --- a/pathd/path_ted.h +++ b/pathd/path_ted.h @@ -41,7 +41,7 @@ enum igp_import { IMPORT_OSPFv3 }; struct ted_state { - struct thread_master *main; + struct event_loop *main; /* Status of TED: enable or disable */ bool enabled; /* From which igp is going to receive data */ @@ -49,9 +49,9 @@ struct ted_state { /* The TED itself as in link_state.h */ struct ls_ted *ted; /* Timer for ted sync */ - struct thread *t_link_state_sync; + struct event *t_link_state_sync; /* Timer for refresh sid in segment list */ - struct thread *t_segment_list_refresh; + struct event *t_segment_list_refresh; /* delay interval in seconds */ uint32_t link_state_delay_interval; /* delay interval refresh in seconds */ @@ -84,7 +84,7 @@ struct ted_state { /* TED management functions */ bool path_ted_is_initialized(void); -void path_ted_init(struct thread_master *master); +void path_ted_init(struct event_loop *master); uint32_t path_ted_teardown(void); void path_ted_timer_sync_cancel(void); void path_ted_timer_refresh_cancel(void); diff --git a/pathd/path_zebra.c b/pathd/path_zebra.c index efc016f75028..826443f97906 100644 --- a/pathd/path_zebra.c +++ b/pathd/path_zebra.c @@ -5,7 +5,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "lib_errors.h" #include "if.h" @@ -318,7 +318,7 @@ static zclient_handler *const path_handlers[] = { * * @param master The master thread */ -void path_zebra_init(struct thread_master *master) +void path_zebra_init(struct event_loop *master) { struct zclient_options options = zclient_options_default; options.synchronous = true; @@ -344,4 +344,6 @@ void path_zebra_stop(void) { zclient_stop(zclient); zclient_free(zclient); + zclient_stop(zclient_sync); + zclient_free(zclient_sync); } diff --git a/pathd/path_zebra.h b/pathd/path_zebra.h index 0d435bb519b5..74a62e38b328 100644 --- a/pathd/path_zebra.h +++ b/pathd/path_zebra.h @@ -16,7 +16,7 @@ void path_zebra_add_sr_policy(struct srte_policy *policy, void path_zebra_delete_sr_policy(struct srte_policy *policy); int path_zebra_request_label(mpls_label_t label); void path_zebra_release_label(mpls_label_t label); -void path_zebra_init(struct thread_master *master); +void path_zebra_init(struct event_loop *master); void path_zebra_stop(void); #endif /* _FRR_PATH_MPLS_H_ */ diff --git a/pathd/pathd.c b/pathd/pathd.c index 86501f7ad83e..6c13503c7dcc 100644 --- a/pathd/pathd.c +++ b/pathd/pathd.c @@ -45,9 +45,9 @@ struct debug path_policy_debug; static void trigger_pathd_candidate_created(struct srte_candidate *candidate); -static void trigger_pathd_candidate_created_timer(struct thread *thread); +static void trigger_pathd_candidate_created_timer(struct event *thread); static void trigger_pathd_candidate_updated(struct srte_candidate *candidate); -static void trigger_pathd_candidate_updated_timer(struct thread *thread); +static void trigger_pathd_candidate_updated_timer(struct event *thread); static void trigger_pathd_candidate_removed(struct srte_candidate *candidate); static const char * srte_candidate_metric_name(enum srte_candidate_metric_type type); @@ -1293,13 +1293,13 @@ void trigger_pathd_candidate_created(struct srte_candidate *candidate) from changing the candidate by hand with the console */ if (candidate->hook_timer != NULL) return; - thread_add_timer(master, trigger_pathd_candidate_created_timer, - (void *)candidate, HOOK_DELAY, &candidate->hook_timer); + event_add_timer(master, trigger_pathd_candidate_created_timer, + (void *)candidate, HOOK_DELAY, &candidate->hook_timer); } -void trigger_pathd_candidate_created_timer(struct thread *thread) +void trigger_pathd_candidate_created_timer(struct event *thread) { - struct srte_candidate *candidate = THREAD_ARG(thread); + struct srte_candidate *candidate = EVENT_ARG(thread); candidate->hook_timer = NULL; hook_call(pathd_candidate_created, candidate); } @@ -1313,13 +1313,13 @@ void trigger_pathd_candidate_updated(struct srte_candidate *candidate) from changing the candidate by hand with the console */ if (candidate->hook_timer != NULL) return; - thread_add_timer(master, trigger_pathd_candidate_updated_timer, - (void *)candidate, HOOK_DELAY, &candidate->hook_timer); + event_add_timer(master, trigger_pathd_candidate_updated_timer, + (void *)candidate, HOOK_DELAY, &candidate->hook_timer); } -void trigger_pathd_candidate_updated_timer(struct thread *thread) +void trigger_pathd_candidate_updated_timer(struct event *thread) { - struct srte_candidate *candidate = THREAD_ARG(thread); + struct srte_candidate *candidate = EVENT_ARG(thread); candidate->hook_timer = NULL; hook_call(pathd_candidate_updated, candidate); } @@ -1329,7 +1329,7 @@ void trigger_pathd_candidate_removed(struct srte_candidate *candidate) /* The hook needs to be call synchronously, otherwise the candidate path will be already deleted when the handler is called */ if (candidate->hook_timer != NULL) { - thread_cancel(&candidate->hook_timer); + event_cancel(&candidate->hook_timer); candidate->hook_timer = NULL; } hook_call(pathd_candidate_removed, candidate); diff --git a/pathd/pathd.h b/pathd/pathd.h index f1a59b1167d9..73ad49226e2a 100644 --- a/pathd/pathd.h +++ b/pathd/pathd.h @@ -311,7 +311,7 @@ struct srte_candidate { uint32_t affinity_filters[MAX_AFFINITY_FILTER_TYPE]; /* Hooks delaying timer */ - struct thread *hook_timer; + struct event *hook_timer; }; RB_HEAD(srte_candidate_head, srte_candidate); @@ -371,7 +371,7 @@ extern struct srte_policy_head srte_policies; extern struct zebra_privs_t pathd_privs; /* master thread, defined in path_main.c */ -extern struct thread_master *master; +extern struct event_loop *master; /* pathd.c */ struct srte_segment_list *srte_segment_list_add(const char *name); diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c index 9b34815c4db3..c4708d3f08fe 100644 --- a/pbrd/pbr_main.c +++ b/pbrd/pbr_main.c @@ -8,7 +8,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "linklist.h" #include "if.h" @@ -56,7 +56,7 @@ struct zebra_privs_t pbr_privs = { struct option longopts[] = { { 0 } }; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* SIGHUP handler. */ static void sighup(void) diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 0e18f1198dfd..16cea3b4cd2d 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -6,7 +6,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "prefix.h" #include "table.h" diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index bb09a10b9829..ee9ee32f8b5c 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -125,7 +125,7 @@ DEFPY(pbr_map_match_src, pbr_map_match_src_cmd, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (pbrms->dst && pbrms->family && prefix->family != pbrms->family) { + if (pbrms->dst && prefix->family != pbrms->dst->family) { vty_out(vty, "Cannot mismatch families within match src/dst\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -161,7 +161,7 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd, if (!pbrms) return CMD_WARNING_CONFIG_FAILED; - if (pbrms->src && pbrms->family && prefix->family != pbrms->family) { + if (pbrms->src && prefix->family != pbrms->src->family) { vty_out(vty, "Cannot mismatch families within match src/dst\n"); return CMD_WARNING_CONFIG_FAILED; } @@ -199,6 +199,11 @@ DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd, return CMD_WARNING_CONFIG_FAILED; if (!no) { + if (!ip_proto) { + vty_out(vty, "Unable to convert (null) to proto id\n"); + return CMD_WARNING; + } + p = getprotobyname(ip_proto); if (!p) { vty_out(vty, "Unable to convert %s to proto id\n", diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 1b18853d2b87..53a02e14a599 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -6,7 +6,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "network.h" #include "prefix.h" @@ -516,7 +516,7 @@ pbr_encode_pbr_map_sequence_vrf(struct stream *s, stream_putl(s, pbr_vrf->vrf->data.l.table_id); } -static void pbr_encode_pbr_map_sequence(struct stream *s, +static bool pbr_encode_pbr_map_sequence(struct stream *s, struct pbr_map_sequence *pbrms, struct interface *ifp) { @@ -549,7 +549,14 @@ static void pbr_encode_pbr_map_sequence(struct stream *s, 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)); + else { + /* Not valid for install without table */ + return false; + } + stream_put(s, ifp->name, INTERFACE_NAMSIZ); + + return true; } bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms, @@ -593,11 +600,13 @@ bool pbr_send_pbr_map(struct pbr_map_sequence *pbrms, 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); + if (pbr_encode_pbr_map_sequence(s, pbrms, pmi->ifp)) { + stream_putw_at(s, 0, stream_get_endp(s)); + zclient_send_message(zclient); + } else { + DEBUGD(&pbr_dbg_zebra, "%s: %s seq %u encode failed, skipped", + __func__, pbrm->name, pbrms->seqno); + } return true; } diff --git a/pbrd/pbr_zebra.h b/pbrd/pbr_zebra.h index 850e5f06d13b..ef844ef797ad 100644 --- a/pbrd/pbr_zebra.h +++ b/pbrd/pbr_zebra.h @@ -11,7 +11,7 @@ struct pbr_interface { char mapname[100]; }; -extern struct thread_master *master; +extern struct event_loop *master; extern void pbr_zebra_init(void); diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index eb29a57e2008..262ce86c291e 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -540,6 +540,11 @@ DEFPY (interface_ipv6_mld_join, { char xpath[XPATH_MAXLEN]; + if (!IN6_IS_ADDR_MULTICAST(&group)) { + vty_out(vty, "Invalid Multicast Address\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (source_str) { if (IPV6_ADDR_SAME(&source, &in6addr_any)) { vty_out(vty, "Bad source address %s\n", source_str); @@ -738,7 +743,7 @@ DEFPY (interface_ipv6_mld_query_max_response_time, IPV6_STR IFACE_MLD_STR IFACE_MLD_QUERY_MAX_RESPONSE_TIME_STR - "Query response value in milliseconds\n") + "Query response value in deci-seconds\n") { return gm_process_query_max_response_time_cmd(vty, qmrt_str); } diff --git a/pimd/pim6_mld.c b/pimd/pim6_mld.c index fcece56c6faf..52496325c4e1 100644 --- a/pimd/pim6_mld.c +++ b/pimd/pim6_mld.c @@ -19,7 +19,7 @@ #include "lib/jhash.h" #include "lib/prefix.h" #include "lib/checksum.h" -#include "lib/thread.h" +#include "lib/frrevent.h" #include "termtable.h" #include "pimd/pim6_mld.h" @@ -45,7 +45,7 @@ DEFINE_MTYPE_STATIC(PIMD, GM_SG, "MLD (S,G)"); DEFINE_MTYPE_STATIC(PIMD, GM_GRP_PENDING, "MLD group query state"); DEFINE_MTYPE_STATIC(PIMD, GM_GSQ_PENDING, "MLD group/source query aggregate"); -static void gm_t_query(struct thread *t); +static void gm_t_query(struct event *t); static void gm_trigger_specific(struct gm_sg *sg); static void gm_sg_timer_start(struct gm_if *gm_ifp, struct gm_sg *sg, struct timeval expire_wait); @@ -99,7 +99,7 @@ static inline uint8_t in6_multicast_scope(const pim_addr *addr) return addr->s6_addr[1] & 0xf; } -static inline bool in6_multicast_nofwd(const pim_addr *addr) +bool in6_multicast_nofwd(const pim_addr *addr) { return in6_multicast_scope(addr) <= IPV6_MULTICAST_SCOPE_LINK; } @@ -182,13 +182,11 @@ DECLARE_HASH(gm_gsq_pends, struct gm_gsq_pending, itm, gm_gsq_pending_cmp, * interface -> (S,G) */ -static int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b) +int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b) { return pim_sgaddr_cmp(a->sgaddr, b->sgaddr); } -DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp); - static struct gm_sg *gm_sg_find(struct gm_if *gm_ifp, pim_addr grp, pim_addr src) { @@ -329,7 +327,7 @@ static void gm_expiry_calc(struct gm_query_timers *timers) static void gm_sg_free(struct gm_sg *sg) { /* t_sg_expiry is handled before this is reached */ - THREAD_OFF(sg->t_sg_query); + EVENT_OFF(sg->t_sg_query); gm_packet_sg_subs_fini(sg->subs_negative); gm_packet_sg_subs_fini(sg->subs_positive); XFREE(MTYPE_GM_SG, sg); @@ -346,7 +344,8 @@ static const char *const gm_states[] = { }; /* clang-format on */ -CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported"); +/* TODO: S,G entries in EXCLUDE (i.e. prune) unsupported" */ + /* tib_sg_gm_prune() below is an "un-join", it doesn't prune S,G when *,G is * joined. Whether we actually want/need to support this is a separate * question - it is almost never used. In fact this is exactly what RFC5790 @@ -356,6 +355,7 @@ CPP_NOTICE("TODO: S,G entries in EXCLUDE (i.e. prune) unsupported"); static void gm_sg_update(struct gm_sg *sg, bool has_expired) { struct gm_if *gm_ifp = sg->iface; + struct pim_interface *pim_ifp = gm_ifp->ifp->info; enum gm_sg_state prev, desired; bool new_join; struct gm_sg *grp = NULL; @@ -404,10 +404,13 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired) gm_expiry_calc(&timers); gm_sg_timer_start(gm_ifp, sg, timers.expire_wait); - THREAD_OFF(sg->t_sg_query); - sg->n_query = gm_ifp->cur_lmqc; + EVENT_OFF(sg->t_sg_query); sg->query_sbit = false; - gm_trigger_specific(sg); + /* Trigger the specific queries only for querier. */ + if (IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest)) { + sg->n_query = gm_ifp->cur_lmqc; + gm_trigger_specific(sg); + } } } prev = sg->state; @@ -438,6 +441,13 @@ static void gm_sg_update(struct gm_sg *sg, bool has_expired) } if (desired == GM_SG_NOINFO) { + /* multiple paths can lead to the last state going away; + * t_sg_expire can still be running if we're arriving from + * another path. + */ + if (has_expired) + EVENT_OFF(sg->t_sg_expire); + assertf((!sg->t_sg_expire && !gm_packet_sg_subs_count(sg->subs_positive) && !gm_packet_sg_subs_count(sg->subs_negative)), @@ -575,7 +585,7 @@ static void gm_sg_expiry_cancel(struct gm_sg *sg) { if (sg->t_sg_expire && PIM_DEBUG_GM_TRACE) zlog_debug(log_sg(sg, "alive, cancelling expiry timer")); - THREAD_OFF(sg->t_sg_expire); + EVENT_OFF(sg->t_sg_expire); sg->query_sbit = true; } @@ -589,7 +599,7 @@ static void gm_sg_expiry_cancel(struct gm_sg *sg) * everything else is thrown into pkt for creation of state in pass 2 */ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, - struct mld_v2_rec_hdr *rechdr) + struct mld_v2_rec_hdr *rechdr, size_t n_src) { /* NB: pkt->subscriber can be NULL here if the subscriber was not * previously seen! @@ -598,7 +608,6 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, struct gm_sg *grp; struct gm_packet_sg *old_grp = NULL; struct gm_packet_sg *item; - size_t n_src = ntohs(rechdr->n_src); size_t j; bool is_excl = false; @@ -641,7 +650,7 @@ static void gm_handle_v2_pass1(struct gm_packet_state *pkt, */ gm_packet_sg_drop(old_grp); gm_sg_update(grp, false); - CPP_NOTICE("need S,G PRUNE => NO_INFO transition here"); +/* TODO "need S,G PRUNE => NO_INFO transition here" */ } break; @@ -789,7 +798,8 @@ static void gm_handle_v2_pass2_excl(struct gm_packet_state *pkt, size_t offs) gm_sg_update(sg_grp, false); } -CPP_NOTICE("TODO: QRV/QQIC are not copied from queries to local state"); +/* TODO: QRV/QQIC are not copied from queries to local state" */ + /* on receiving a query, we need to update our robustness/query interval to * match, so we correctly process group/source specific queries after last * member leaves @@ -811,13 +821,24 @@ static void gm_handle_v2_report(struct gm_if *gm_ifp, return; } - /* errors after this may at least partially process the packet */ - gm_ifp->stats.rx_new_report++; - hdr = (struct mld_v2_report_hdr *)data; data += sizeof(*hdr); len -= sizeof(*hdr); + n_records = ntohs(hdr->n_records); + if (n_records > len / sizeof(struct mld_v2_rec_hdr)) { + /* note this is only an upper bound, records with source lists + * are larger. This is mostly here to make coverity happy. + */ + zlog_warn(log_pkt_src( + "malformed MLDv2 report (infeasible record count)")); + gm_ifp->stats.rx_drop_malformed++; + return; + } + + /* errors after this may at least partially process the packet */ + gm_ifp->stats.rx_new_report++; + /* can't have more *,G and S,G items than there is space for ipv6 * addresses, so just use this to allocate temporary buffer */ @@ -828,8 +849,6 @@ static void gm_handle_v2_report(struct gm_if *gm_ifp, pkt->iface = gm_ifp; pkt->subscriber = gm_subscriber_findref(gm_ifp, pkt_src->sin6_addr); - n_records = ntohs(hdr->n_records); - /* validate & remove state in v2_pass1() */ for (i = 0; i < n_records; i++) { struct mld_v2_rec_hdr *rechdr; @@ -867,7 +886,7 @@ static void gm_handle_v2_report(struct gm_if *gm_ifp, data += record_size; len -= record_size; - gm_handle_v2_pass1(pkt, rechdr); + gm_handle_v2_pass1(pkt, rechdr, n_src); } if (!pkt->n_active) { @@ -936,7 +955,8 @@ static void gm_handle_v1_report(struct gm_if *gm_ifp, item = gm_packet_sg_setup(pkt, grp, true, false); item->n_exclude = 0; - CPP_NOTICE("set v1-seen timer on grp here"); + +/* TODO "set v1-seen timer on grp here" */ /* } */ @@ -999,7 +1019,9 @@ static void gm_handle_v1_leave(struct gm_if *gm_ifp, if (old_grp) { gm_packet_sg_drop(old_grp); gm_sg_update(grp, false); - CPP_NOTICE("need S,G PRUNE => NO_INFO transition here"); + +/* TODO "need S,G PRUNE => NO_INFO transition here" */ + } } @@ -1022,9 +1044,9 @@ static void gm_handle_v1_leave(struct gm_if *gm_ifp, * its own path too and won't hit this. This is really only triggered when a * host straight up disappears. */ -static void gm_t_expire(struct thread *t) +static void gm_t_expire(struct event *t) { - struct gm_if *gm_ifp = THREAD_ARG(t); + struct gm_if *gm_ifp = EVENT_ARG(t); struct gm_packet_state *pkt; zlog_info(log_ifp("general expiry timer")); @@ -1041,8 +1063,8 @@ static void gm_t_expire(struct thread *t) log_ifp("next general expiry in %" PRId64 "ms"), remain_ms / 1000); - thread_add_timer_tv(router->master, gm_t_expire, gm_ifp, - &remain, &gm_ifp->t_expire); + event_add_timer_tv(router->master, gm_t_expire, gm_ifp, + &remain, &gm_ifp->t_expire); return; } @@ -1096,7 +1118,7 @@ static void gm_handle_q_general(struct gm_if *gm_ifp, gm_ifp->n_pending--; if (!gm_ifp->n_pending) - THREAD_OFF(gm_ifp->t_expire); + EVENT_OFF(gm_ifp->t_expire); } /* people might be messing with their configs or something */ @@ -1112,16 +1134,16 @@ static void gm_handle_q_general(struct gm_if *gm_ifp, zlog_debug( log_ifp("starting general timer @ 0: %pTVMu"), &pend->expiry); - thread_add_timer_tv(router->master, gm_t_expire, gm_ifp, - &timers->expire_wait, &gm_ifp->t_expire); + event_add_timer_tv(router->master, gm_t_expire, gm_ifp, + &timers->expire_wait, &gm_ifp->t_expire); } else if (PIM_DEBUG_GM_TRACE) zlog_debug(log_ifp("appending general timer @ %u: %pTVMu"), gm_ifp->n_pending, &pend->expiry); } -static void gm_t_sg_expire(struct thread *t) +static void gm_t_sg_expire(struct event *t) { - struct gm_sg *sg = THREAD_ARG(t); + struct gm_sg *sg = EVENT_ARG(t); struct gm_if *gm_ifp = sg->iface; struct gm_packet_sg *item; @@ -1201,15 +1223,15 @@ static void gm_sg_timer_start(struct gm_if *gm_ifp, struct gm_sg *sg, if (sg->t_sg_expire) { struct timeval remain; - remain = thread_timer_remain(sg->t_sg_expire); + remain = event_timer_remain(sg->t_sg_expire); if (timercmp(&remain, &expire_wait, <=)) return; - THREAD_OFF(sg->t_sg_expire); + EVENT_OFF(sg->t_sg_expire); } - thread_add_timer_tv(router->master, gm_t_sg_expire, sg, &expire_wait, - &sg->t_sg_expire); + event_add_timer_tv(router->master, gm_t_sg_expire, sg, &expire_wait, + &sg->t_sg_expire); } static void gm_handle_q_groupsrc(struct gm_if *gm_ifp, @@ -1225,7 +1247,7 @@ static void gm_handle_q_groupsrc(struct gm_if *gm_ifp, } } -static void gm_t_grp_expire(struct thread *t) +static void gm_t_grp_expire(struct event *t) { /* if we're here, that means when we received the group-specific query * there was one or more active S,G for this group. For *,G the timer @@ -1233,7 +1255,7 @@ static void gm_t_grp_expire(struct thread *t) * receive a report, so that work is left to gm_t_sg_expire and we * shouldn't worry about it here. */ - struct gm_grp_pending *pend = THREAD_ARG(t); + struct gm_grp_pending *pend = EVENT_ARG(t); struct gm_if *gm_ifp = pend->iface; struct gm_sg *sg, *sg_start, sg_ref = {}; @@ -1262,7 +1284,7 @@ static void gm_t_grp_expire(struct thread *t) * parallel. But if we received nothing for the *,G query, * the S,G query is kinda irrelevant. */ - THREAD_OFF(sg->t_sg_expire); + EVENT_OFF(sg->t_sg_expire); frr_each_safe (gm_packet_sg_subs, sg->subs_positive, item) /* this will also drop the EXCLUDE S,G lists */ @@ -1309,11 +1331,11 @@ static void gm_handle_q_group(struct gm_if *gm_ifp, if (pend) { struct timeval remain; - remain = thread_timer_remain(pend->t_expire); + remain = event_timer_remain(pend->t_expire); if (timercmp(&remain, &timers->expire_wait, <=)) return; - THREAD_OFF(pend->t_expire); + EVENT_OFF(pend->t_expire); } else { pend = XCALLOC(MTYPE_GM_GRP_PENDING, sizeof(*pend)); pend->grp = grp; @@ -1322,8 +1344,8 @@ static void gm_handle_q_group(struct gm_if *gm_ifp, } monotime(&pend->query); - thread_add_timer_tv(router->master, gm_t_grp_expire, pend, - &timers->expire_wait, &pend->t_expire); + event_add_timer_tv(router->master, gm_t_grp_expire, pend, + &timers->expire_wait, &pend->t_expire); if (PIM_DEBUG_GM_TRACE) zlog_debug(log_ifp("*,%pPAs S,G timer started: %pTHD"), &grp, @@ -1334,7 +1356,7 @@ static void gm_bump_querier(struct gm_if *gm_ifp) { struct pim_interface *pim_ifp = gm_ifp->ifp->info; - THREAD_OFF(gm_ifp->t_query); + EVENT_OFF(gm_ifp->t_query); if (pim_addr_is_any(pim_ifp->ll_lowest)) return; @@ -1343,12 +1365,12 @@ static void gm_bump_querier(struct gm_if *gm_ifp) gm_ifp->n_startup = gm_ifp->cur_qrv; - thread_execute(router->master, gm_t_query, gm_ifp, 0); + event_execute(router->master, gm_t_query, gm_ifp, 0); } -static void gm_t_other_querier(struct thread *t) +static void gm_t_other_querier(struct event *t) { - struct gm_if *gm_ifp = THREAD_ARG(t); + struct gm_if *gm_ifp = EVENT_ARG(t); struct pim_interface *pim_ifp = gm_ifp->ifp->info; zlog_info(log_ifp("other querier timer expired")); @@ -1356,7 +1378,7 @@ static void gm_t_other_querier(struct thread *t) gm_ifp->querier = pim_ifp->ll_lowest; gm_ifp->n_startup = gm_ifp->cur_qrv; - thread_execute(router->master, gm_t_query, gm_ifp, 0); + event_execute(router->master, gm_t_query, gm_ifp, 0); } static void gm_handle_query(struct gm_if *gm_ifp, @@ -1459,13 +1481,12 @@ static void gm_handle_query(struct gm_if *gm_ifp, if (IPV6_ADDR_CMP(&pkt_src->sin6_addr, &pim_ifp->ll_lowest) < 0) { unsigned int other_ms; - THREAD_OFF(gm_ifp->t_query); - THREAD_OFF(gm_ifp->t_other_querier); + EVENT_OFF(gm_ifp->t_query); + EVENT_OFF(gm_ifp->t_other_querier); other_ms = timers.qrv * timers.qqic_ms + timers.max_resp_ms / 2; - thread_add_timer_msec(router->master, gm_t_other_querier, - gm_ifp, other_ms, - &gm_ifp->t_other_querier); + event_add_timer_msec(router->master, gm_t_other_querier, gm_ifp, + other_ms, &gm_ifp->t_other_querier); } if (len == sizeof(struct mld_v1_pkt)) { @@ -1492,6 +1513,15 @@ static void gm_handle_query(struct gm_if *gm_ifp, gm_handle_q_group(gm_ifp, &timers, hdr->grp); gm_ifp->stats.rx_query_new_group++; } else { + /* this is checked above: + * if (len >= sizeof(struct mld_v2_query_hdr)) { + * size_t src_space = ntohs(hdr->n_src) * sizeof(pim_addr); + * if (len < sizeof(struct mld_v2_query_hdr) + src_space) { + */ + assume(ntohs(hdr->n_src) <= + (len - sizeof(struct mld_v2_query_hdr)) / + sizeof(pim_addr)); + gm_handle_q_groupsrc(gm_ifp, &timers, hdr->grp, hdr->srcs, ntohs(hdr->n_src)); gm_ifp->stats.rx_query_new_groupsrc++; @@ -1579,9 +1609,9 @@ static bool ip6_check_hopopts_ra(uint8_t *hopopts, size_t hopopt_len, return false; } -static void gm_t_recv(struct thread *t) +static void gm_t_recv(struct event *t) { - struct pim_instance *pim = THREAD_ARG(t); + struct pim_instance *pim = EVENT_ARG(t); union { char buf[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(256) /* hop options */ + @@ -1600,8 +1630,8 @@ static void gm_t_recv(struct thread *t) ssize_t nread; size_t pktlen; - thread_add_read(router->master, gm_t_recv, pim, pim->gm_socket, - &pim->t_gm_recv); + event_add_read(router->master, gm_t_recv, pim, pim->gm_socket, + &pim->t_gm_recv); iov->iov_base = rxbuf; iov->iov_len = sizeof(rxbuf); @@ -1851,9 +1881,9 @@ static void gm_send_query(struct gm_if *gm_ifp, pim_addr grp, } } -static void gm_t_query(struct thread *t) +static void gm_t_query(struct event *t) { - struct gm_if *gm_ifp = THREAD_ARG(t); + struct gm_if *gm_ifp = EVENT_ARG(t); unsigned int timer_ms = gm_ifp->cur_query_intv; if (gm_ifp->n_startup) { @@ -1861,15 +1891,15 @@ static void gm_t_query(struct thread *t) gm_ifp->n_startup--; } - thread_add_timer_msec(router->master, gm_t_query, gm_ifp, timer_ms, - &gm_ifp->t_query); + event_add_timer_msec(router->master, gm_t_query, gm_ifp, timer_ms, + &gm_ifp->t_query); gm_send_query(gm_ifp, PIMADDR_ANY, NULL, 0, false); } -static void gm_t_sg_query(struct thread *t) +static void gm_t_sg_query(struct event *t) { - struct gm_sg *sg = THREAD_ARG(t); + struct gm_sg *sg = EVENT_ARG(t); gm_trigger_specific(sg); } @@ -1888,9 +1918,9 @@ static void gm_send_specific(struct gm_gsq_pending *pend_gsq) XFREE(MTYPE_GM_GSQ_PENDING, pend_gsq); } -static void gm_t_gsq_pend(struct thread *t) +static void gm_t_gsq_pend(struct event *t) { - struct gm_gsq_pending *pend_gsq = THREAD_ARG(t); + struct gm_gsq_pending *pend_gsq = EVENT_ARG(t); gm_send_specific(pend_gsq); } @@ -1898,17 +1928,28 @@ static void gm_t_gsq_pend(struct thread *t) static void gm_trigger_specific(struct gm_sg *sg) { struct gm_if *gm_ifp = sg->iface; - struct pim_interface *pim_ifp = gm_ifp->ifp->info; struct gm_gsq_pending *pend_gsq, ref = {}; sg->n_query--; if (sg->n_query) - thread_add_timer_msec(router->master, gm_t_sg_query, sg, - gm_ifp->cur_query_intv_trig, - &sg->t_sg_query); + event_add_timer_msec(router->master, gm_t_sg_query, sg, + gm_ifp->cur_query_intv_trig, + &sg->t_sg_query); + + /* As per RFC 2271, s6 p14: + * E.g. a router that starts as a Querier, receives a + * Done message for a group and then receives a Query from a router with + * a lower address (causing a transition to the Non-Querier state) + * continues to send multicast-address-specific queries for the group in + * question until it either receives a Report or its timer expires, at + * which time it starts performing the actions of a Non-Querier for this + * group. + */ + /* Therefore here we do not need to check if this router is querier or + * not. This is called only for querier, hence it will work even if the + * router transitions from querier to non-querier. + */ - if (!IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest)) - return; if (gm_ifp->pim->gm_socket == -1) return; @@ -1931,9 +1972,8 @@ static void gm_trigger_specific(struct gm_sg *sg) pend_gsq->iface = gm_ifp; gm_gsq_pends_add(gm_ifp->gsq_pends, pend_gsq); - thread_add_timer_tv(router->master, gm_t_gsq_pend, pend_gsq, - &gm_ifp->cfg_timing_fuzz, - &pend_gsq->t_send); + event_add_timer_tv(router->master, gm_t_gsq_pend, pend_gsq, + &gm_ifp->cfg_timing_fuzz, &pend_gsq->t_send); } assert(pend_gsq->n_src < array_size(pend_gsq->srcs)); @@ -1942,7 +1982,7 @@ static void gm_trigger_specific(struct gm_sg *sg) pend_gsq->n_src++; if (pend_gsq->n_src == array_size(pend_gsq->srcs)) { - THREAD_OFF(pend_gsq->t_send); + EVENT_OFF(pend_gsq->t_send); gm_send_specific(pend_gsq); pend_gsq = NULL; } @@ -2039,8 +2079,8 @@ static void gm_vrf_socket_incref(struct pim_instance *pim) vrf->name); } - thread_add_read(router->master, gm_t_recv, pim, pim->gm_socket, - &pim->t_gm_recv); + event_add_read(router->master, gm_t_recv, pim, pim->gm_socket, + &pim->t_gm_recv); } static void gm_vrf_socket_decref(struct pim_instance *pim) @@ -2048,7 +2088,7 @@ static void gm_vrf_socket_decref(struct pim_instance *pim) if (--pim->gm_socket_if_count) return; - THREAD_OFF(pim->t_gm_recv); + EVENT_OFF(pim->t_gm_recv); close(pim->gm_socket); pim->gm_socket = -1; } @@ -2121,17 +2161,17 @@ void gm_group_delete(struct gm_if *gm_ifp) gm_packet_drop(pkt, false); while ((pend_grp = gm_grp_pends_pop(gm_ifp->grp_pends))) { - THREAD_OFF(pend_grp->t_expire); + EVENT_OFF(pend_grp->t_expire); XFREE(MTYPE_GM_GRP_PENDING, pend_grp); } while ((pend_gsq = gm_gsq_pends_pop(gm_ifp->gsq_pends))) { - THREAD_OFF(pend_gsq->t_send); + EVENT_OFF(pend_gsq->t_send); XFREE(MTYPE_GM_GSQ_PENDING, pend_gsq); } while ((sg = gm_sgs_pop(gm_ifp->sgs))) { - THREAD_OFF(sg->t_sg_expire); + EVENT_OFF(sg->t_sg_expire); assertf(!gm_packet_sg_subs_count(sg->subs_negative), "%pSG", &sg->sgaddr); assertf(!gm_packet_sg_subs_count(sg->subs_positive), "%pSG", @@ -2159,9 +2199,9 @@ void gm_ifp_teardown(struct interface *ifp) if (PIM_DEBUG_GM_EVENTS) zlog_debug(log_ifp("MLD stop")); - THREAD_OFF(gm_ifp->t_query); - THREAD_OFF(gm_ifp->t_other_querier); - THREAD_OFF(gm_ifp->t_expire); + EVENT_OFF(gm_ifp->t_query); + EVENT_OFF(gm_ifp->t_other_querier); + EVENT_OFF(gm_ifp->t_expire); frr_with_privs (&pimd_privs) { struct ipv6_mreq mreq; @@ -2204,7 +2244,7 @@ static void gm_update_ll(struct interface *ifp) gm_ifp->cur_ll_lowest = pim_ifp->ll_lowest; if (was_querier) gm_ifp->querier = pim_ifp->ll_lowest; - THREAD_OFF(gm_ifp->t_query); + EVENT_OFF(gm_ifp->t_query); if (pim_addr_is_any(gm_ifp->cur_ll_lowest)) { if (was_querier) @@ -2225,7 +2265,7 @@ static void gm_update_ll(struct interface *ifp) return; gm_ifp->n_startup = gm_ifp->cur_qrv; - thread_execute(router->master, gm_t_query, gm_ifp, 0); + event_execute(router->master, gm_t_query, gm_ifp, 0); } void gm_ifp_update(struct interface *ifp) @@ -2251,6 +2291,7 @@ void gm_ifp_update(struct interface *ifp) if (!pim_ifp->mld) { changed = true; gm_start(ifp); + assume(pim_ifp->mld != NULL); } gm_ifp = pim_ifp->mld; @@ -2386,6 +2427,8 @@ static void gm_show_if_one(struct vty *vty, struct interface *ifp, struct gm_if *gm_ifp = pim_ifp->mld; bool querier; + assume(js_if || tt); + querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest); if (js_if) { @@ -2468,6 +2511,19 @@ static void gm_show_if_vrf(struct vty *vty, struct vrf *vrf, const char *ifname, if (js) { js_if = json_object_new_object(); + /* + * If we have js as true and detail as false + * and if Coverity thinks that js_if is NULL + * because of a failed call to new then + * when we call gm_show_if_one below + * the tt can be deref'ed and as such + * FRR will crash. But since we know + * that json_object_new_object never fails + * then let's tell Coverity that this assumption + * is true. I'm not worried about fast path + * here at all. + */ + assert(js_if); json_object_object_add(js_vrf, ifp->name, js_if); } diff --git a/pimd/pim6_mld.h b/pimd/pim6_mld.h index 2af1c77c2453..7634fb2ec453 100644 --- a/pimd/pim6_mld.h +++ b/pimd/pim6_mld.h @@ -10,7 +10,7 @@ #include "typesafe.h" #include "pim_addr.h" -struct thread; +struct event; struct pim_instance; struct gm_packet_sg; struct gm_if; @@ -65,14 +65,14 @@ struct gm_sg { * (implies we haven't received any report yet, since it's cancelled * by that) */ - struct thread *t_sg_expire; + struct event *t_sg_expire; /* last-member-left triggered queries (group/group-source specific) * * this timer will be running even if we aren't the elected querier, * in case the election result changes midway through. */ - struct thread *t_sg_query; + struct event *t_sg_query; /* we must keep sending (QRV) queries even if we get a positive * response, to make sure other routers are updated. query_sbit @@ -113,6 +113,8 @@ struct gm_sg { */ struct gm_packet_sg *most_recent; }; +int gm_sg_cmp(const struct gm_sg *a, const struct gm_sg *b); +DECLARE_RBTREE_UNIQ(gm_sgs, struct gm_sg, itm, gm_sg_cmp); /* host tracking entry. addr will be one of: * @@ -226,7 +228,7 @@ struct gm_grp_pending { pim_addr grp; struct timeval query; - struct thread *t_expire; + struct event *t_expire; }; /* guaranteed MTU for IPv6 is 1280 bytes. IPv6 header is 40 bytes, MLDv2 @@ -247,7 +249,7 @@ struct gm_gsq_pending { struct gm_gsq_pends_item itm; struct gm_if *iface; - struct thread *t_send; + struct event *t_send; pim_addr grp; bool s_bit; @@ -300,7 +302,7 @@ struct gm_if_stats { struct gm_if { struct interface *ifp; struct pim_instance *pim; - struct thread *t_query, *t_other_querier, *t_expire; + struct event *t_query, *t_other_querier, *t_expire; bool stopping; @@ -352,5 +354,6 @@ static inline void gm_ifp_teardown(struct interface *ifp) #endif extern void gm_cli_init(void); +bool in6_multicast_nofwd(const pim_addr *addr); #endif /* PIM6_MLD_H */ diff --git a/pimd/pim_addr.h b/pimd/pim_addr.h index cc473d1f809b..94c63bbccc78 100644 --- a/pimd/pim_addr.h +++ b/pimd/pim_addr.h @@ -28,6 +28,7 @@ typedef struct in_addr pim_addr; #define PIM_MROUTE_DBG "mroute" #define PIMREG "pimreg" #define GM "IGMP" +#define IPPROTO_GM IPPROTO_IGMP #define PIM_ADDR_FUNCNAME(name) ipv4_##name @@ -57,6 +58,7 @@ typedef struct in6_addr pim_addr; #define PIM_MROUTE_DBG "mroute6" #define PIMREG "pim6reg" #define GM "MLD" +#define IPPROTO_GM IPPROTO_ICMPV6 #define PIM_ADDR_FUNCNAME(name) ipv6_##name diff --git a/pimd/pim_assert.c b/pimd/pim_assert.c index e46fa2cfacd8..86d9a745eaa0 100644 --- a/pimd/pim_assert.c +++ b/pimd/pim_assert.c @@ -493,12 +493,12 @@ static int pim_assert_cancel(struct pim_ifchannel *ch) return pim_assert_do(ch, metric); } -static void on_assert_timer(struct thread *t) +static void on_assert_timer(struct event *t) { struct pim_ifchannel *ch; struct interface *ifp; - ch = THREAD_ARG(t); + ch = EVENT_ARG(t); ifp = ch->interface; @@ -535,7 +535,7 @@ static void assert_timer_off(struct pim_ifchannel *ch) __func__, ch->sg_str, ch->interface->name); } } - THREAD_OFF(ch->t_ifassert_timer); + EVENT_OFF(ch->t_ifassert_timer); } static void pim_assert_timer_set(struct pim_ifchannel *ch, int interval) @@ -547,8 +547,8 @@ static void pim_assert_timer_set(struct pim_ifchannel *ch, int interval) __func__, ch->sg_str, interval, ch->interface->name); } - thread_add_timer(router->master, on_assert_timer, ch, interval, - &ch->t_ifassert_timer); + event_add_timer(router->master, on_assert_timer, ch, interval, + &ch->t_ifassert_timer); } static void pim_assert_timer_reset(struct pim_ifchannel *ch) diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index c249f323df5a..df9161943d37 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -54,7 +54,7 @@ void pim_bsm_write_config(struct vty *vty, struct interface *ifp) static void pim_bsm_rpinfo_free(struct bsm_rpinfo *bsrp_info) { - THREAD_OFF(bsrp_info->g2rp_timer); + EVENT_OFF(bsrp_info->g2rp_timer); XFREE(MTYPE_PIM_BSRP_INFO, bsrp_info); } @@ -140,15 +140,15 @@ static struct bsgrp_node *pim_bsm_new_bsgrp_node(struct route_table *rt, return bsgrp; } -static void pim_on_bs_timer(struct thread *t) +static void pim_on_bs_timer(struct event *t) { struct route_node *rn; struct bsm_scope *scope; struct bsgrp_node *bsgrp_node; struct bsm_rpinfo *bsrp; - scope = THREAD_ARG(t); - THREAD_OFF(scope->bs_timer); + scope = EVENT_ARG(t); + EVENT_OFF(scope->bs_timer); if (PIM_DEBUG_BSM) zlog_debug("%s: Bootstrap Timer expired for scope: %d", @@ -156,7 +156,6 @@ static void pim_on_bs_timer(struct thread *t) pim_nht_bsr_del(scope->pim, scope->current_bsr); /* Reset scope zone data */ - scope->accept_nofwd_bsm = false; scope->state = ACCEPT_ANY; scope->current_bsr = PIMADDR_ANY; scope->current_bsr_prio = 0; @@ -189,7 +188,7 @@ 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); + EVENT_OFF(scope->bs_timer); } static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout) @@ -199,13 +198,13 @@ static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout) zlog_debug("%s : Invalid scope(NULL).", __func__); return; } - THREAD_OFF(scope->bs_timer); + EVENT_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); + event_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) @@ -257,7 +256,7 @@ static bool is_hold_time_elapsed(void *data) return true; } -static void pim_on_g2rp_timer(struct thread *t) +static void pim_on_g2rp_timer(struct event *t) { struct bsm_rpinfo *bsrp; struct bsm_rpinfo *bsrp_node; @@ -268,8 +267,8 @@ static void pim_on_g2rp_timer(struct thread *t) uint16_t elapse; pim_addr bsrp_addr; - bsrp = THREAD_ARG(t); - THREAD_OFF(bsrp->g2rp_timer); + bsrp = EVENT_ARG(t); + EVENT_OFF(bsrp->g2rp_timer); bsgrp_node = bsrp->bsgrp_node; /* elapse time is the hold time of expired node */ @@ -331,15 +330,15 @@ static void pim_g2rp_timer_start(struct bsm_rpinfo *bsrp, int hold_time) zlog_debug("%s : Invalid brsp(NULL).", __func__); return; } - THREAD_OFF(bsrp->g2rp_timer); + EVENT_OFF(bsrp->g2rp_timer); if (PIM_DEBUG_BSM) zlog_debug( "%s : starting g2rp timer for grp: %pFX - rp: %pPAs with timeout %d secs(Actual Hold time : %d secs)", __func__, &bsrp->bsgrp_node->group, &bsrp->rp_address, hold_time, bsrp->rp_holdtime); - thread_add_timer(router->master, pim_on_g2rp_timer, bsrp, hold_time, - &bsrp->g2rp_timer); + event_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, @@ -358,7 +357,7 @@ static void pim_g2rp_timer_stop(struct bsm_rpinfo *bsrp) __func__, &bsrp->bsgrp_node->group, &bsrp->rp_address); - THREAD_OFF(bsrp->g2rp_timer); + EVENT_OFF(bsrp->g2rp_timer); } static bool is_hold_time_zero(void *data) @@ -1364,6 +1363,10 @@ int pim_bsm_process(struct interface *ifp, pim_sgaddr *sg, uint8_t *buf, } } + /* BSM packet is seen, so resetting accept_nofwd_bsm to false */ + if (pim->global_scope.accept_nofwd_bsm) + pim->global_scope.accept_nofwd_bsm = false; + if (!pim_addr_cmp(sg->grp, qpim_all_pim_routers_addr)) { /* Multicast BSMs are only accepted if source interface & IP * match RPF towards the BSR's IP address, or they have diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h index 5e40adb8060c..fb09e3b1cc36 100644 --- a/pimd/pim_bsm.h +++ b/pimd/pim_bsm.h @@ -59,7 +59,7 @@ struct bsm_scope { struct bsm_frags_head bsm_frags[1]; struct route_table *bsrp_table; /* group2rp mapping rcvd from BSR */ - struct thread *bs_timer; /* Boot strap timer */ + struct event *bs_timer; /* Boot strap timer */ }; /* BSM packet (= fragment) - this is stored as list in bsm_frags inside scope @@ -103,7 +103,7 @@ struct bsm_rpinfo { uint16_t rp_holdtime; /* RP holdtime - g2rp timer value */ pim_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 */ + struct event *g2rp_timer; /* Run only for elected RP node */ }; extern int pim_bsm_rpinfo_cmp(const struct bsm_rpinfo *a, diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 8c75b0a5b59d..2e90cf905337 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -67,7 +67,7 @@ static struct cmd_node debug_node = { }; static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[], - const int argc, int *idx) + const int argc, int *idx, bool uj) { struct vrf *vrf; @@ -76,9 +76,13 @@ static struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[], else vrf = vrf_lookup_by_id(VRF_DEFAULT); - if (!vrf) - vty_out(vty, "Specified VRF: %s does not exist\n", - argv[*idx]->arg); + if (!vrf) { + if (uj) + vty_json_empty(vty); + else + vty_out(vty, "Specified VRF: %s does not exist\n", + argv[*idx]->arg); + } return vrf; } @@ -822,19 +826,172 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, } } -static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) +static void igmp_source_json_helper(struct gm_source *src, + json_object *json_sources, char *source_str, + char *mmss, char *uptime) +{ + json_object *json_source = NULL; + + json_source = json_object_new_object(); + if (!json_source) + return; + + json_object_string_add(json_source, "source", source_str); + json_object_string_add(json_source, "timer", mmss); + json_object_boolean_add(json_source, "forwarded", + IGMP_SOURCE_TEST_FORWARDING(src->source_flags)); + json_object_string_add(json_source, "uptime", uptime); + json_object_array_add(json_sources, json_source); +} + +static void igmp_group_print(struct interface *ifp, struct vty *vty, bool uj, + json_object *json, struct gm_group *grp, + time_t now, bool detail) { - 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; + char group_str[INET_ADDRSTRLEN]; + char hhmmss[PIM_TIME_STRLEN]; + char uptime[PIM_TIME_STRLEN]; + + pim_inet4_dump("<group?>", 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(); + if (!json_iface) + return; + json_object_pim_ifp_add(json_iface, ifp); + json_object_object_add(json, ifp->name, json_iface); + json_groups = json_object_new_array(); + if (!json_groups) + return; + json_object_object_add(json_iface, "groups", + json_groups); + } + + json_object_object_get_ex(json_iface, "groups", &json_groups); + if (json_groups) { + json_group = json_object_new_object(); + if (!json_group) + return; + + json_object_string_add(json_group, "group", group_str); + if (grp->igmp_version == IGMP_DEFAULT_VERSION) + 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); + + if (detail) { + struct listnode *srcnode; + struct gm_source *src; + json_object *json_sources = NULL; + + json_sources = json_object_new_array(); + if (!json_sources) + return; + + json_object_object_add(json_group, "sources", + json_sources); + + for (ALL_LIST_ELEMENTS_RO( + grp->group_source_list, srcnode, + src)) { + char source_str[INET_ADDRSTRLEN]; + char mmss[PIM_TIME_STRLEN]; + char src_uptime[PIM_TIME_STRLEN]; + + pim_inet4_dump( + "<source?>", src->source_addr, + source_str, sizeof(source_str)); + pim_time_timer_to_mmss( + mmss, sizeof(mmss), + src->t_source_timer); + pim_time_uptime( + src_uptime, sizeof(src_uptime), + now - src->source_creation); + + igmp_source_json_helper( + src, json_sources, source_str, + mmss, src_uptime); + } + } + } + } else { + if (detail) { + struct listnode *srcnode; + struct gm_source *src; + + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + char source_str[INET_ADDRSTRLEN]; + + pim_inet4_dump("<source?>", src->source_addr, + source_str, sizeof(source_str)); + + vty_out(vty, + "%-16s %-15s %4s %8s %-15s %d %8s\n", + ifp->name, group_str, + grp->igmp_version == 3 + ? (grp->group_filtermode_isexcl + ? "EXCL" + : "INCL") + : "----", + hhmmss, source_str, grp->igmp_version, + uptime); + } + return; + } + + vty_out(vty, "%-16s %-15s %4s %8s %4d %d %8s\n", ifp->name, + 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); + } +} + +static void igmp_show_groups_interface_single(struct pim_instance *pim, + struct vty *vty, bool uj, + const char *ifname, + const char *grp_str, bool detail) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + struct pim_interface *pim_ifp = NULL; + struct gm_group *grp; now = pim_time_monotonic_sec(); if (uj) { json = json_object_new_object(); + if (!json) + return; json_object_int_add(json, "totalGroups", pim->gm_group_count); json_object_int_add(json, "watermarkLimit", pim->gm_watermark_limit); @@ -843,8 +1000,87 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) vty_out(vty, "Watermark warn limit(%s): %u\n", pim->gm_watermark_limit ? "Set" : "Not Set", pim->gm_watermark_limit); - vty_out(vty, - "Interface Group Mode Timer Srcs V Uptime \n"); + + if (!detail) + vty_out(vty, + "Interface Group Mode Timer Srcs V Uptime\n"); + else + vty_out(vty, + "Interface Group Mode Timer Source V Uptime\n"); + } + + ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id); + if (!ifp) { + if (uj) + vty_json(vty, json); + return; + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + if (uj) + vty_json(vty, json); + return; + } + + if (grp_str) { + struct in_addr group_addr; + struct gm_sock *igmp; + + if (inet_pton(AF_INET, grp_str, &group_addr) == 1) { + igmp = pim_igmp_sock_lookup_ifaddr( + pim_ifp->gm_socket_list, + pim_ifp->primary_address); + if (igmp) { + grp = find_group_by_addr(igmp, group_addr); + if (grp) + igmp_group_print(ifp, vty, uj, json, + grp, now, detail); + } + } + } else { + struct listnode *grpnode; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, grp)) + igmp_group_print(ifp, vty, uj, json, grp, now, detail); + } + + if (uj) { + if (detail) + vty_json_no_pretty(vty, json); + else + vty_json(vty, json); + } +} + +static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj, + const char *grp_str, bool detail) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + if (!json) + return; + json_object_int_add(json, "totalGroups", pim->gm_group_count); + json_object_int_add(json, "watermarkLimit", + pim->gm_watermark_limit); + } else { + vty_out(vty, "Total IGMP groups: %u\n", pim->gm_group_count); + vty_out(vty, "Watermark warn limit(%s): %u\n", + pim->gm_watermark_limit ? "Set" : "Not Set", + pim->gm_watermark_limit); + if (!detail) + vty_out(vty, + "Interface Group Mode Timer Srcs V Uptime\n"); + else + vty_out(vty, + "Interface Group Mode Timer Source V Uptime\n"); } /* scan interfaces */ @@ -856,78 +1092,38 @@ static void igmp_show_groups(struct pim_instance *pim, struct vty *vty, bool uj) if (!pim_ifp) continue; - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, - grp)) { - char group_str[INET_ADDRSTRLEN]; - char hhmmss[10]; - char uptime[10]; - - pim_inet4_dump("<group?>", 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); + if (grp_str) { + struct in_addr group_addr; + struct gm_sock *igmp; + + if (inet_pton(AF_INET, grp_str, &group_addr) == 1) { + igmp = pim_igmp_sock_lookup_ifaddr( + pim_ifp->gm_socket_list, + pim_ifp->primary_address); + if (igmp) { + grp = find_group_by_addr(igmp, + group_addr); + if (grp) + igmp_group_print(ifp, vty, uj, + json, grp, now, + detail); } - - json_group = json_object_new_object(); - 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 %4s %8s %4d %d %8s\n", - ifp->name, 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 interfaces */ + } else { + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, + grpnode, grp)) + igmp_group_print(ifp, vty, uj, json, grp, now, + detail); + } + } /* scan interfaces */ - if (uj) - vty_json(vty, json); + if (uj) { + if (detail) + vty_json_no_pretty(vty, json); + else + vty_json(vty, json); + } } static void igmp_show_group_retransmission(struct pim_instance *pim, @@ -981,24 +1177,175 @@ static void igmp_show_group_retransmission(struct pim_instance *pim, } /* scan interfaces */ } +static void igmp_sources_print(struct interface *ifp, char *group_str, + struct gm_source *src, time_t now, + json_object *json, struct vty *vty, bool uj) +{ + json_object *json_iface = NULL; + json_object *json_group = NULL; + json_object *json_sources = NULL; + char source_str[INET_ADDRSTRLEN]; + char mmss[PIM_TIME_STRLEN]; + char uptime[PIM_TIME_STRLEN]; + + pim_inet4_dump("<source?>", src->source_addr, source_str, + sizeof(source_str)); + pim_time_timer_to_mmss(mmss, sizeof(mmss), src->t_source_timer); + pim_time_uptime(uptime, sizeof(uptime), now - src->source_creation); + + if (uj) { + json_object_object_get_ex(json, ifp->name, &json_iface); + if (!json_iface) { + json_iface = json_object_new_object(); + if (!json_iface) + return; + json_object_string_add(json_iface, "name", ifp->name); + json_object_object_add(json, ifp->name, json_iface); + } + + json_object_object_get_ex(json_iface, group_str, &json_group); + if (!json_group) { + json_group = json_object_new_object(); + if (!json_group) + return; + json_object_string_add(json_group, "group", group_str); + json_object_object_add(json_iface, group_str, + json_group); + json_sources = json_object_new_array(); + if (!json_sources) + return; + json_object_object_add(json_group, "sources", + json_sources); + } + + json_object_object_get_ex(json_group, "sources", &json_sources); + if (json_sources) + igmp_source_json_helper(src, json_sources, source_str, + mmss, uptime); + } else { + vty_out(vty, "%-16s %-15s %-15s %5s %3s %8s\n", ifp->name, + group_str, source_str, mmss, + IGMP_SOURCE_TEST_FORWARDING(src->source_flags) ? "Y" + : "N", + uptime); + } +} + +static void igmp_show_sources_interface_single(struct pim_instance *pim, + struct vty *vty, bool uj, + const char *ifname, + const char *grp_str) +{ + struct interface *ifp; + time_t now; + json_object *json = NULL; + struct pim_interface *pim_ifp; + struct gm_group *grp; + + now = pim_time_monotonic_sec(); + + if (uj) { + json = json_object_new_object(); + if (!json) + return; + } else { + vty_out(vty, + "Interface Group Source Timer Fwd Uptime \n"); + } + + ifp = if_lookup_by_name(ifname, pim->vrf->vrf_id); + if (!ifp) { + if (uj) + vty_json(vty, json); + return; + } + + pim_ifp = ifp->info; + if (!pim_ifp) { + if (uj) + vty_json(vty, json); + return; + } + + if (grp_str) { + struct in_addr group_addr; + struct gm_sock *igmp; + struct listnode *srcnode; + struct gm_source *src; + char group_str[INET_ADDRSTRLEN]; + int res; + + res = inet_pton(AF_INET, grp_str, &group_addr); + if (res <= 0) { + if (uj) + vty_json(vty, json); + return; + } + + igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->gm_socket_list, + pim_ifp->primary_address); + if (!igmp) { + if (uj) + vty_json(vty, json); + return; + } + + grp = find_group_by_addr(igmp, group_addr); + if (!grp) { + if (uj) + vty_json(vty, json); + return; + } + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, srcnode, src)) + igmp_sources_print(ifp, group_str, src, now, json, vty, + uj); + } else { + struct listnode *grpnode; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, + grp)) { + char group_str[INET_ADDRSTRLEN]; + struct listnode *srcnode; + struct gm_source *src; + + pim_inet4_dump("<group?>", grp->group_addr, group_str, + sizeof(group_str)); + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) + igmp_sources_print(ifp, group_str, src, now, + json, vty, uj); + + } /* scan igmp groups */ + } + + if (uj) + vty_json(vty, json); +} + static void igmp_show_sources(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_source = NULL; - json_object *json_sources = NULL; now = pim_time_monotonic_sec(); - if (uj) + if (uj) { json = json_object_new_object(); - else + if (!json) + return; + } else { vty_out(vty, - "Interface Group Source Timer Fwd Uptime \n"); + "Interface Group Source Timer Fwd Uptime\n"); + } /* scan interfaces */ FOR_ALL_INTERFACES (pim->vrf, ifp) { @@ -1021,82 +1368,12 @@ static void igmp_show_sources(struct pim_instance *pim, struct vty *vty, /* scan group sources */ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - srcnode, src)) { - char source_str[INET_ADDRSTRLEN]; - char mmss[10]; - char uptime[10]; - - pim_inet4_dump("<source?>", src->source_addr, - source_str, sizeof(source_str)); - - pim_time_timer_to_mmss(mmss, sizeof(mmss), - src->t_source_timer); - - pim_time_uptime(uptime, sizeof(uptime), - now - src->source_creation); - - if (uj) { - json_object_object_get_ex( - json, ifp->name, &json_iface); - if (!json_iface) { - json_iface = - json_object_new_object(); - json_object_string_add( - json_iface, "name", - ifp->name); - json_object_object_add( - json, ifp->name, - json_iface); - } - json_object_object_get_ex(json_iface, - group_str, - &json_group); - - if (!json_group) { - json_group = - json_object_new_object(); - json_object_string_add( - json_group, "group", - group_str); - json_object_object_add( - json_iface, group_str, - json_group); - json_sources = - json_object_new_array(); - json_object_object_add( - json_group, "sources", - json_sources); - } - json_source = json_object_new_object(); - json_object_string_add(json_source, - "source", - source_str); - json_object_string_add(json_source, - "timer", mmss); - json_object_boolean_add( - json_source, "forwarded", - IGMP_SOURCE_TEST_FORWARDING( - src->source_flags)); - json_object_string_add( - json_source, "uptime", uptime); - json_object_array_add(json_sources, - json_source); - - } else { - vty_out(vty, - "%-16s %-15s %-15s %5s %3s %8s\n", - ifp->name, group_str, - source_str, mmss, - IGMP_SOURCE_TEST_FORWARDING( - src->source_flags) - ? "Y" - : "N", - uptime); - } - - } /* scan group sources */ + srcnode, src)) + igmp_sources_print(ifp, group_str, src, now, + json, vty, uj); } /* scan igmp groups */ } /* scan interfaces */ + if (uj) vty_json(vty, json); } @@ -1227,7 +1504,7 @@ DEFUN (clear_ip_interfaces, VRF_CMD_HELP_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1247,7 +1524,7 @@ DEFUN (clear_ip_igmp_interfaces, "Reset IGMP interfaces\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1355,7 +1632,7 @@ DEFUN (clear_ip_pim_bsr_db, "Reset pim bsr data\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1378,8 +1655,8 @@ DEFUN (show_ip_igmp_interface, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -1444,8 +1721,8 @@ DEFUN (show_ip_igmp_join, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -1487,39 +1764,47 @@ DEFUN (show_ip_igmp_join_vrf_all, return CMD_SUCCESS; } -DEFUN (show_ip_igmp_groups, - show_ip_igmp_groups_cmd, - "show ip igmp [vrf NAME] groups [json]", - SHOW_STR - IP_STR - IGMP_STR - VRF_CMD_HELP_STR - IGMP_GROUP_STR - JSON_STR) +DEFPY(show_ip_igmp_groups, + show_ip_igmp_groups_cmd, + "show ip igmp [vrf NAME$vrf_name] groups [INTERFACE$ifname [GROUP$grp_str]] [detail$detail] [json$json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + IGMP_GROUP_STR + "Interface name\n" + "Group address\n" + "Detailed Information\n" + JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); - bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json); if (!vrf) return CMD_WARNING; - igmp_show_groups(vrf->info, vty, uj); + if (ifname) + igmp_show_groups_interface_single(vrf->info, vty, !!json, + ifname, grp_str, !!detail); + else + igmp_show_groups(vrf->info, vty, !!json, NULL, !!detail); return CMD_SUCCESS; } -DEFUN (show_ip_igmp_groups_vrf_all, - show_ip_igmp_groups_vrf_all_cmd, - "show ip igmp vrf all groups [json]", - SHOW_STR - IP_STR - IGMP_STR - VRF_CMD_HELP_STR - IGMP_GROUP_STR - JSON_STR) +DEFPY(show_ip_igmp_groups_vrf_all, + show_ip_igmp_groups_vrf_all_cmd, + "show ip igmp vrf all groups [GROUP$grp_str] [detail$detail] [json$json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + IGMP_GROUP_STR + "Group address\n" + "Detailed Information\n" + JSON_STR) { - bool uj = use_json(argc, argv); + bool uj = !!json; struct vrf *vrf; bool first = true; @@ -1533,7 +1818,7 @@ DEFUN (show_ip_igmp_groups_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - igmp_show_groups(vrf->info, vty, uj); + igmp_show_groups(vrf->info, vty, uj, grp_str, !!detail); } if (uj) vty_out(vty, "}\n"); @@ -1552,7 +1837,7 @@ DEFUN (show_ip_igmp_groups_retransmissions, "IGMP group retransmissions\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1562,23 +1847,29 @@ DEFUN (show_ip_igmp_groups_retransmissions, return CMD_SUCCESS; } -DEFUN (show_ip_igmp_sources, - show_ip_igmp_sources_cmd, - "show ip igmp [vrf NAME] sources [json]", - SHOW_STR - IP_STR - IGMP_STR - VRF_CMD_HELP_STR - IGMP_SOURCE_STR - JSON_STR) +DEFPY(show_ip_igmp_sources, + show_ip_igmp_sources_cmd, + "show ip igmp [vrf NAME$vrf_name] sources [INTERFACE$ifname [GROUP$grp_str]] [json$json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + IGMP_SOURCE_STR + "Interface name\n" + "Group address\n" + JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, !!json); if (!vrf) return CMD_WARNING; - igmp_show_sources(vrf->info, vty, use_json(argc, argv)); + if (ifname) + igmp_show_sources_interface_single(vrf->info, vty, !!json, + ifname, grp_str); + else + igmp_show_sources(vrf->info, vty, !!json); return CMD_SUCCESS; } @@ -1594,7 +1885,7 @@ DEFUN (show_ip_igmp_sources_retransmissions, "IGMP source retransmissions\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1617,8 +1908,8 @@ DEFUN (show_ip_igmp_statistics, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -1746,7 +2037,7 @@ DEFUN (show_ip_pim_assert, "PIM interface assert\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1766,7 +2057,7 @@ DEFUN (show_ip_pim_assert_internal, "PIM interface internal assert state\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1786,7 +2077,7 @@ DEFUN (show_ip_pim_assert_metric, "PIM interface assert metric\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -1806,7 +2097,7 @@ DEFUN (show_ip_pim_assert_winner_metric, "PIM interface assert winner metric\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -2125,8 +2416,8 @@ DEFUN(show_ip_pim_mlag_up, show_ip_pim_mlag_up_cmd, 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); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf || !vrf->info) { vty_out(vty, "%s: VRF or Info missing\n", __func__); @@ -2598,7 +2889,7 @@ DEFUN (show_ip_rib, "Unicast address\n") { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); struct in_addr addr; const char *addr_str; struct pim_nexthop nexthop; @@ -2686,7 +2977,7 @@ DEFUN (show_ip_ssmpingd, VRF_CMD_HELP_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, false); if (!vrf) return CMD_WARNING; @@ -3141,8 +3432,8 @@ DEFUN (show_ip_pim_ssm_range, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -3192,8 +3483,8 @@ DEFUN (show_ip_pim_group_type, JSON_STR) { int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5086,7 +5377,7 @@ DEFUN (show_ip_msdp_mesh_group, bool uj = use_json(argc, argv); int idx = 2; struct pim_msdp_mg *mg; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); struct pim_instance *pim; struct json_object *json = NULL; @@ -5322,7 +5613,7 @@ DEFUN (show_ip_msdp_peer_detail, { bool uj = use_json(argc, argv); int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5562,7 +5853,7 @@ DEFUN (show_ip_msdp_sa_detail, { bool uj = use_json(argc, argv); int idx = 2; - struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5677,7 +5968,7 @@ DEFUN (show_ip_msdp_sa_sg, struct vrf *vrf; int idx = 2; - vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -5947,7 +6238,7 @@ DEFUN (show_ip_pim_vxlan_sg, struct vrf *vrf; int idx = 2; - vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; @@ -6007,7 +6298,7 @@ DEFUN_HIDDEN (show_ip_pim_vxlan_sg_work, struct vrf *vrf; int idx = 2; - vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx, uj); if (!vrf) return CMD_WARNING; diff --git a/pimd/pim_cmd.h b/pimd/pim_cmd.h index fb6693491ba5..d39d77cd2fde 100644 --- a/pimd/pim_cmd.h +++ b/pimd/pim_cmd.h @@ -60,4 +60,5 @@ void pim_cmd_init(void); +#define PIM_TIME_STRLEN 10 #endif /* PIM_CMD_H */ diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c index 75df09ec354e..716cb8db5beb 100644 --- a/pimd/pim_cmd_common.c +++ b/pimd/pim_cmd_common.c @@ -1148,13 +1148,18 @@ void pim_show_state(struct pim_instance *pim, struct vty *vty, "wrongInterface", c_oil->cc.wrong_if); } - } + } else #if PIM_IPV == 4 - else vty_out(vty, "%-6d %-15pPAs %-15pPAs %-3s %-16s ", c_oil->installed, oil_origin(c_oil), oil_mcastgrp(c_oil), isRpt ? "y" : "n", in_ifname); +#else + /* Add a new row for c_oil with no OIF */ + ttable_add_row(tt, "%d|%pPAs|%pPAs|%s|%s|%c", + c_oil->installed, oil_origin(c_oil), + oil_mcastgrp(c_oil), isRpt ? "y" : "n", + in_ifname, ' '); #endif for (oif_vif_index = 0; oif_vif_index < MAXVIFS; @@ -1225,6 +1230,13 @@ void pim_show_state(struct pim_instance *pim, struct vty *vty, #if PIM_IPV == 4 vty_out(vty, "%s%s", out_ifname, flag); #else + /* OIF found. + * Delete the existing row for c_oil, + * with no OIF. + * Add a new row for c_oil with OIF and + * flag. + */ + ttable_del_row(tt, tt->nrows - 1); ttable_add_row( tt, "%d|%pPAs|%pPAs|%s|%s|%s%s", c_oil->installed, @@ -2747,7 +2759,7 @@ void pim_show_interfaces_single(struct pim_instance *pim, struct vty *vty, } } - if (!found_ifname) + if (!found_ifname && !json) vty_out(vty, "%% No such interface\n"); } @@ -2832,6 +2844,8 @@ static int pim_print_json_pnc_cache_walkcb(struct hash_bucket *backet, json_object *json_row = NULL; json_object *json_ifp = NULL; json_object *json_arr = NULL; + struct pim_interface *pim_ifp = NULL; + bool pim_enable = false; for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) { first_ifindex = nh_node->ifindex; @@ -2851,6 +2865,14 @@ static int pim_print_json_pnc_cache_walkcb(struct hash_bucket *backet, json_ifp = json_object_new_object(); json_object_string_add(json_ifp, "interface", ifp ? ifp->name : "NULL"); + + if (ifp) + pim_ifp = ifp->info; + + if (pim_ifp && pim_ifp->pim_enable) + pim_enable = true; + + json_object_boolean_add(json_ifp, "pimEnabled", pim_enable); #if PIM_IPV == 4 json_object_string_addf(json_ifp, "nexthop", "%pI4", &nh_node->gate.ipv4); @@ -3200,7 +3222,7 @@ void pim_show_neighbors_single(struct pim_instance *pim, struct vty *vty, } } - if (!found_neighbor) + if (!found_neighbor && !json) vty_out(vty, "%% No such interface or neighbor\n"); } diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index d284912d1d26..7340eeaa607a 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -40,12 +40,11 @@ #include "pim6_mld.h" -#if PIM_IPV == 4 -static void pim_if_igmp_join_del_all(struct interface *ifp); -static int igmp_join_sock(const char *ifname, ifindex_t ifindex, - struct in_addr group_addr, struct in_addr source_addr, - struct pim_interface *pim_ifp); -#endif +static void pim_if_gm_join_del_all(struct interface *ifp); + +static int gm_join_sock(const char *ifname, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr, + struct pim_interface *pim_ifp); void pim_if_init(struct pim_instance *pim) { @@ -189,11 +188,9 @@ void pim_if_delete(struct interface *ifp) assert(pim_ifp); pim_ifp->pim->mcast_if_count--; -#if PIM_IPV == 4 if (pim_ifp->gm_join_list) { - pim_if_igmp_join_del_all(ifp); + pim_if_gm_join_del_all(ifp); } -#endif pim_ifchannel_delete_all(ifp); #if PIM_IPV == 4 @@ -560,7 +557,7 @@ void pim_if_addr_add(struct connected *ifc) /* Close socket and reopen with Source and Group */ close(ij->sock_fd); - join_fd = igmp_join_sock( + join_fd = gm_join_sock( ifp->name, ifp->ifindex, ij->group_addr, ij->source_addr, pim_ifp); if (join_fd < 0) { @@ -573,7 +570,7 @@ void pim_if_addr_add(struct connected *ifc) "<src?>", ij->source_addr, source_str, sizeof(source_str)); zlog_warn( - "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", + "%s: gm_join_sock() failure for IGMP group %s source %s on interface %s", __func__, group_str, source_str, ifp->name); /* warning only */ @@ -979,7 +976,13 @@ int pim_if_add_vif(struct interface *ifp, bool ispimreg, bool is_vxlan_term) } if (ifp->ifindex < 0) { - zlog_warn("%s: ifindex=%d < 1 on interface %s", __func__, + zlog_warn("%s: ifindex=%d < 0 on interface %s", __func__, + ifp->ifindex, ifp->name); + return -2; + } else if ((ifp->ifindex == PIM_OIF_PIM_REGISTER_VIF) && + ((strncmp(ifp->name, "pimreg", 6)) && + (strncmp(ifp->name, "pim6reg", 7)))) { + zlog_warn("%s: ifindex=%d on interface %s", __func__, ifp->ifindex, ifp->name); return -2; } @@ -1208,15 +1211,13 @@ long pim_if_t_suppressed_msec(struct interface *ifp) return t_suppressed_msec; } -#if PIM_IPV == 4 -static void igmp_join_free(struct gm_join *ij) +static void gm_join_free(struct gm_join *ij) { XFREE(MTYPE_PIM_IGMP_JOIN, ij); } -static struct gm_join *igmp_join_find(struct list *join_list, - struct in_addr group_addr, - struct in_addr source_addr) +static struct gm_join *gm_join_find(struct list *join_list, pim_addr group_addr, + pim_addr source_addr) { struct listnode *node; struct gm_join *ij; @@ -1224,38 +1225,33 @@ static struct gm_join *igmp_join_find(struct list *join_list, assert(join_list); for (ALL_LIST_ELEMENTS_RO(join_list, node, ij)) { - if ((group_addr.s_addr == ij->group_addr.s_addr) - && (source_addr.s_addr == ij->source_addr.s_addr)) + if ((!pim_addr_cmp(group_addr, ij->group_addr)) && + (!pim_addr_cmp(source_addr, ij->source_addr))) return ij; } return 0; } -static int igmp_join_sock(const char *ifname, ifindex_t ifindex, - struct in_addr group_addr, struct in_addr source_addr, - struct pim_interface *pim_ifp) +static int gm_join_sock(const char *ifname, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr, + struct pim_interface *pim_ifp) { int join_fd; pim_ifp->igmp_ifstat_joins_sent++; - join_fd = pim_socket_raw(IPPROTO_IGMP); + join_fd = pim_socket_raw(IPPROTO_GM); if (join_fd < 0) { pim_ifp->igmp_ifstat_joins_failed++; return -1; } - if (pim_igmp_join_source(join_fd, ifindex, group_addr, source_addr)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); + if (pim_gm_join_source(join_fd, ifindex, group_addr, source_addr)) { zlog_warn( - "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s", - __func__, join_fd, group_str, source_str, ifindex, + "%s: setsockopt(fd=%d) failure for " GM + " group %pPAs source %pPAs ifindex %d on interface %s: errno=%d: %s", + __func__, join_fd, &group_addr, &source_addr, ifindex, ifname, errno, safe_strerror(errno)); pim_ifp->igmp_ifstat_joins_failed++; @@ -1267,10 +1263,8 @@ static int igmp_join_sock(const char *ifname, ifindex_t ifindex, return join_fd; } -#if PIM_IPV == 4 -static struct gm_join *igmp_join_new(struct interface *ifp, - struct in_addr group_addr, - struct in_addr source_addr) +static struct gm_join *gm_join_new(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1279,19 +1273,12 @@ static struct gm_join *igmp_join_new(struct interface *ifp, pim_ifp = ifp->info; assert(pim_ifp); - join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, - source_addr, pim_ifp); + join_fd = gm_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr, + pim_ifp); if (join_fd < 0) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); - zlog_warn( - "%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", - __func__, group_str, source_str, ifp->name); + zlog_warn("%s: gm_join_sock() failure for " GM + " group %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, ifp->name); return 0; } @@ -1306,11 +1293,9 @@ static struct gm_join *igmp_join_new(struct interface *ifp, return ij; } -#endif /* PIM_IPV == 4 */ -#if PIM_IPV == 4 -ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) +ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1323,37 +1308,32 @@ ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, if (!pim_ifp->gm_join_list) { pim_ifp->gm_join_list = list_new(); - pim_ifp->gm_join_list->del = (void (*)(void *))igmp_join_free; + pim_ifp->gm_join_list->del = (void (*)(void *))gm_join_free; } - ij = igmp_join_find(pim_ifp->gm_join_list, group_addr, source_addr); + ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr); - /* This interface has already been configured to join this IGMP group + /* This interface has already been configured to join this IGMP/MLD + * group */ if (ij) { return ferr_ok(); } - (void)igmp_join_new(ifp, group_addr, source_addr); + (void)gm_join_new(ifp, group_addr, source_addr); if (PIM_DEBUG_GM_EVENTS) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); zlog_debug( - "%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", - __func__, source_str, group_str, ifp->name); + "%s: issued static " GM + " join for channel (S,G)=(%pPA,%pPA) on interface %s", + __func__, &source_addr, &group_addr, ifp->name); } return ferr_ok(); } -#endif /* PIM_IPV == 4 */ -int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) +int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr) { struct pim_interface *pim_ifp; struct gm_join *ij; @@ -1366,40 +1346,29 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, } if (!pim_ifp->gm_join_list) { - zlog_warn("%s: no IGMP join on interface %s", __func__, + zlog_warn("%s: no " GM " join on interface %s", __func__, ifp->name); return -2; } - ij = igmp_join_find(pim_ifp->gm_join_list, group_addr, source_addr); + ij = gm_join_find(pim_ifp->gm_join_list, group_addr, source_addr); if (!ij) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); - zlog_warn( - "%s: could not find IGMP group %s source %s on interface %s", - __func__, group_str, source_str, ifp->name); + zlog_warn("%s: could not find " GM + " group %pPAs source %pPAs on interface %s", + __func__, &group_addr, &source_addr, ifp->name); return -3; } if (close(ij->sock_fd)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", group_addr, group_str, - sizeof(group_str)); - pim_inet4_dump("<src?>", source_addr, source_str, - sizeof(source_str)); zlog_warn( - "%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", - __func__, ij->sock_fd, group_str, source_str, ifp->name, - errno, safe_strerror(errno)); + "%s: failure closing sock_fd=%d for " GM + " group %pPAs source %pPAs on interface %s: errno=%d: %s", + __func__, ij->sock_fd, &group_addr, &source_addr, + ifp->name, errno, safe_strerror(errno)); /* warning only */ } listnode_delete(pim_ifp->gm_join_list, ij); - igmp_join_free(ij); + gm_join_free(ij); if (listcount(pim_ifp->gm_join_list) < 1) { list_delete(&pim_ifp->gm_join_list); pim_ifp->gm_join_list = 0; @@ -1409,7 +1378,7 @@ int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, } __attribute__((unused)) -static void pim_if_igmp_join_del_all(struct interface *ifp) +static void pim_if_gm_join_del_all(struct interface *ifp) { struct pim_interface *pim_ifp; struct listnode *node; @@ -1427,22 +1396,9 @@ static void pim_if_igmp_join_del_all(struct interface *ifp) return; for (ALL_LIST_ELEMENTS(pim_ifp->gm_join_list, node, nextnode, ij)) - pim_if_igmp_join_del(ifp, ij->group_addr, ij->source_addr); -} -#else /* PIM_IPV != 4 */ -ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) -{ - return ferr_ok(); + pim_if_gm_join_del(ifp, ij->group_addr, ij->source_addr); } -int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr) -{ - return 0; -} -#endif /* PIM_IPV != 4 */ - /* RFC 4601 @@ -1801,3 +1757,61 @@ void pim_iface_init(void) if_zapi_callbacks(pim_ifp_create, pim_ifp_up, pim_ifp_down, pim_ifp_destroy); } + +static void pim_if_membership_clear(struct interface *ifp) +{ + struct pim_interface *pim_ifp; + + pim_ifp = ifp->info; + assert(pim_ifp); + + if (pim_ifp->pim_enable && pim_ifp->gm_enable) + return; + + pim_ifchannel_membership_clear(ifp); +} + +void pim_pim_interface_delete(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return; + + pim_ifp->pim_enable = false; + + pim_if_membership_clear(ifp); + + /* + * pim_sock_delete() removes all neighbors from + * pim_ifp->pim_neighbor_list. + */ + pim_sock_delete(ifp, "pim unconfigured on interface"); + pim_upstream_nh_if_update(pim_ifp->pim, ifp); + + if (!pim_ifp->gm_enable) { + pim_if_addr_del_all(ifp); + pim_if_delete(ifp); + } +} + +void pim_gm_interface_delete(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + return; + + pim_ifp->gm_enable = false; + + pim_if_membership_clear(ifp); + +#if PIM_IPV == 4 + igmp_sock_delete_all(ifp); +#else + gm_ifp_teardown(ifp); +#endif + + if (!pim_ifp->pim_enable) + pim_if_delete(ifp); +} diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 83079020b8b6..0312f719d300 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -104,10 +104,10 @@ struct pim_interface { struct gm_if *mld; int pim_sock_fd; /* PIM socket file descriptor */ - struct thread *t_pim_sock_read; /* thread for reading PIM socket */ + struct event *t_pim_sock_read; /* thread for reading PIM socket */ int64_t pim_sock_creation; /* timestamp of PIM socket creation */ - struct thread *t_pim_hello_timer; + struct event *t_pim_hello_timer; int pim_hello_period; int pim_default_holdtime; int pim_triggered_hello_delay; @@ -217,10 +217,10 @@ int pim_if_t_override_msec(struct interface *ifp); pim_addr pim_find_primary_addr(struct interface *ifp); -ferr_r pim_if_igmp_join_add(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr); -int pim_if_igmp_join_del(struct interface *ifp, struct in_addr group_addr, - struct in_addr source_addr); +ferr_r pim_if_gm_join_add(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr); +int pim_if_gm_join_del(struct interface *ifp, pim_addr group_addr, + pim_addr source_addr); void pim_if_update_could_assert(struct interface *ifp); @@ -243,5 +243,7 @@ bool pim_if_is_vrf_device(struct interface *ifp); int pim_if_ifchannel_count(struct pim_interface *pim_ifp); void pim_iface_init(void); +void pim_pim_interface_delete(struct interface *ifp); +void pim_gm_interface_delete(struct interface *ifp); #endif /* PIM_IFACE_H */ diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 136498beb713..da5518994193 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -7,7 +7,7 @@ #include <zebra.h> #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "if.h" #include "vrf.h" @@ -135,7 +135,7 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) * being inherited. So let's figure out what * needs to be done here */ - if (!pim_addr_is_any(ch->sg.src) && + if (!pim_addr_is_any(ch->sg.src) && ch->parent && pim_upstream_evaluate_join_desired_interface( ch->upstream, ch, ch->parent)) pim_channel_add_oif(ch->upstream->channel_oil, @@ -193,9 +193,9 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) ch->upstream = NULL; - THREAD_OFF(ch->t_ifjoin_expiry_timer); - THREAD_OFF(ch->t_ifjoin_prune_pending_timer); - THREAD_OFF(ch->t_ifassert_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_prune_pending_timer); + EVENT_OFF(ch->t_ifassert_timer); if (ch->parent) { listnode_delete(ch->parent->sources, ch); @@ -411,7 +411,7 @@ const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state) */ void reset_ifassert_state(struct pim_ifchannel *ch) { - THREAD_OFF(ch->t_ifassert_timer); + EVENT_OFF(ch->t_ifassert_timer); pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, PIMADDR_ANY, router->infinite_assert_metric); @@ -628,11 +628,11 @@ static void ifjoin_to_noinfo(struct pim_ifchannel *ch) delete_on_noinfo(ch); } -static void on_ifjoin_expiry_timer(struct thread *t) +static void on_ifjoin_expiry_timer(struct event *t) { struct pim_ifchannel *ch; - ch = THREAD_ARG(t); + ch = EVENT_ARG(t); if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: ifchannel %s expiry timer", __func__, @@ -642,14 +642,14 @@ static void on_ifjoin_expiry_timer(struct thread *t) /* ch may have been deleted */ } -static void on_ifjoin_prune_pending_timer(struct thread *t) +static void on_ifjoin_prune_pending_timer(struct event *t) { struct pim_ifchannel *ch; int send_prune_echo; /* boolean */ struct interface *ifp; struct pim_interface *pim_ifp; - ch = THREAD_ARG(t); + ch = EVENT_ARG(t); if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped", @@ -896,7 +896,7 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, previously received join message with holdtime=0xFFFF. */ if (ch->t_ifjoin_expiry_timer) { - unsigned long remain = thread_timer_remain_second( + unsigned long remain = event_timer_remain_second( ch->t_ifjoin_expiry_timer); if (remain > holdtime) { /* @@ -920,13 +920,13 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, return; } } - THREAD_OFF(ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); break; case PIM_IFJOIN_PRUNE: if (source_flags & PIM_ENCODE_RPT_BIT) { pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO); - THREAD_OFF(ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); delete_on_noinfo(ch); return; } else @@ -943,7 +943,7 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, * maximum of its current value and the HoldTime from the * triggering Join/Prune message. */ - THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + EVENT_OFF(ch->t_ifjoin_prune_pending_timer); /* Check if SGRpt join Received */ if ((source_flags & PIM_ENCODE_RPT_BIT) && @@ -955,7 +955,7 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, * I transitions to the NoInfo state.The ET and PPT are * cancelled. */ - THREAD_OFF(ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO); return; @@ -964,13 +964,13 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, pim_ifchannel_ifjoin_handler(ch, pim_ifp); if (ch->t_ifjoin_expiry_timer) { - unsigned long remain = thread_timer_remain_second( + unsigned long remain = event_timer_remain_second( ch->t_ifjoin_expiry_timer); if (remain > holdtime) return; } - THREAD_OFF(ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); break; case PIM_IFJOIN_PRUNE_TMP: @@ -980,8 +980,8 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, } if (holdtime != 0xFFFF) { - thread_add_timer(router->master, on_ifjoin_expiry_timer, ch, - holdtime, &ch->t_ifjoin_expiry_timer); + event_add_timer(router->master, on_ifjoin_expiry_timer, ch, + holdtime, &ch->t_ifjoin_expiry_timer); } } @@ -1030,15 +1030,15 @@ void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream, be taken not to use "ch" afterwards since it would be deleted. */ - THREAD_OFF(ch->t_ifjoin_prune_pending_timer); - THREAD_OFF(ch->t_ifjoin_expiry_timer); - thread_add_timer_msec( - router->master, on_ifjoin_prune_pending_timer, - ch, jp_override_interval_msec, - &ch->t_ifjoin_prune_pending_timer); - thread_add_timer(router->master, on_ifjoin_expiry_timer, - ch, holdtime, - &ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_prune_pending_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); + event_add_timer_msec(router->master, + on_ifjoin_prune_pending_timer, ch, + jp_override_interval_msec, + &ch->t_ifjoin_prune_pending_timer); + event_add_timer(router->master, on_ifjoin_expiry_timer, + ch, holdtime, + &ch->t_ifjoin_expiry_timer); pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream); } @@ -1068,15 +1068,15 @@ void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream, /* If we called ifjoin_prune() directly instead, care should be taken not to use "ch" afterwards since it would be deleted. */ - THREAD_OFF(ch->t_ifjoin_prune_pending_timer); - thread_add_timer_msec(router->master, - on_ifjoin_prune_pending_timer, ch, - jp_override_interval_msec, - &ch->t_ifjoin_prune_pending_timer); + EVENT_OFF(ch->t_ifjoin_prune_pending_timer); + event_add_timer_msec(router->master, + on_ifjoin_prune_pending_timer, ch, + jp_override_interval_msec, + &ch->t_ifjoin_prune_pending_timer); break; case PIM_IFJOIN_PRUNE: if (source_flags & PIM_ENCODE_RPT_BIT) { - THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + EVENT_OFF(ch->t_ifjoin_prune_pending_timer); /* * While in Prune State, Receive SGRpt Prune. * RFC 7761 Sec 4.5.3: @@ -1087,35 +1087,35 @@ void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream, * Join/Prune message. */ if (ch->t_ifjoin_expiry_timer) { - unsigned long rem = thread_timer_remain_second( + unsigned long rem = event_timer_remain_second( ch->t_ifjoin_expiry_timer); if (rem > holdtime) return; - THREAD_OFF(ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); } - thread_add_timer(router->master, on_ifjoin_expiry_timer, - ch, holdtime, - &ch->t_ifjoin_expiry_timer); + event_add_timer(router->master, on_ifjoin_expiry_timer, + ch, holdtime, + &ch->t_ifjoin_expiry_timer); } break; case PIM_IFJOIN_PRUNE_TMP: if (source_flags & PIM_ENCODE_RPT_BIT) { ch->ifjoin_state = PIM_IFJOIN_PRUNE; - THREAD_OFF(ch->t_ifjoin_expiry_timer); - thread_add_timer(router->master, on_ifjoin_expiry_timer, - ch, holdtime, - &ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); + event_add_timer(router->master, on_ifjoin_expiry_timer, + ch, holdtime, + &ch->t_ifjoin_expiry_timer); } break; case PIM_IFJOIN_PRUNE_PENDING_TMP: if (source_flags & PIM_ENCODE_RPT_BIT) { ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING; - THREAD_OFF(ch->t_ifjoin_expiry_timer); - thread_add_timer(router->master, on_ifjoin_expiry_timer, - ch, holdtime, - &ch->t_ifjoin_expiry_timer); + EVENT_OFF(ch->t_ifjoin_expiry_timer); + event_add_timer(router->master, on_ifjoin_expiry_timer, + ch, holdtime, + &ch->t_ifjoin_expiry_timer); } break; } @@ -1466,8 +1466,8 @@ void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom, break; if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP) - THREAD_OFF(child->t_ifjoin_prune_pending_timer); - THREAD_OFF(child->t_ifjoin_expiry_timer); + EVENT_OFF(child->t_ifjoin_prune_pending_timer); + EVENT_OFF(child->t_ifjoin_expiry_timer); PIM_IF_FLAG_UNSET_S_G_RPT(child->flags); child->ifjoin_state = PIM_IFJOIN_NOINFO; diff --git a/pimd/pim_ifchannel.h b/pimd/pim_ifchannel.h index 7ab491e99bd8..4b0ff955f9d8 100644 --- a/pimd/pim_ifchannel.h +++ b/pimd/pim_ifchannel.h @@ -86,13 +86,13 @@ struct pim_ifchannel { /* Per-interface (S,G) Join/Prune State (Section 4.1.4 of RFC4601) */ enum pim_ifjoin_state ifjoin_state; - struct thread *t_ifjoin_expiry_timer; - struct thread *t_ifjoin_prune_pending_timer; + struct event *t_ifjoin_expiry_timer; + struct event *t_ifjoin_prune_pending_timer; int64_t ifjoin_creation; /* Record uptime of ifjoin state */ /* Per-interface (S,G) Assert State (Section 4.6.1 of RFC4601) */ enum pim_ifassert_state ifassert_state; - struct thread *t_ifassert_timer; + struct event *t_ifassert_timer; pim_addr ifassert_winner; struct pim_assert_metric ifassert_winner_metric; int64_t ifassert_creation; /* Record uptime of ifassert state */ diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index bff0c0683ea8..063ba6edd275 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -29,7 +29,7 @@ #include "pim_tib.h" static void group_timer_off(struct gm_group *group); -static void pim_igmp_general_query(struct thread *t); +static void pim_igmp_general_query(struct event *t); void igmp_anysource_forward_start(struct pim_instance *pim, struct gm_group *group) @@ -325,11 +325,11 @@ struct gm_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, return NULL; } -static void pim_igmp_other_querier_expire(struct thread *t) +static void pim_igmp_other_querier_expire(struct event *t) { struct gm_sock *igmp; - igmp = THREAD_ARG(t); + igmp = EVENT_ARG(t); assert(!igmp->t_igmp_query_timer); @@ -377,7 +377,7 @@ void pim_igmp_other_querier_timer_on(struct gm_sock *igmp) "Querier %s resetting TIMER event for Other-Querier-Present", ifaddr_str); } - THREAD_OFF(igmp->t_other_querier_timer); + EVENT_OFF(igmp->t_other_querier_timer); } else { /* We are the current querier, then stop sending general queries: @@ -420,9 +420,9 @@ void pim_igmp_other_querier_timer_on(struct gm_sock *igmp) other_querier_present_interval_msec % 1000); } - thread_add_timer_msec(router->master, pim_igmp_other_querier_expire, - igmp, other_querier_present_interval_msec, - &igmp->t_other_querier_timer); + event_add_timer_msec(router->master, pim_igmp_other_querier_expire, + igmp, other_querier_present_interval_msec, + &igmp->t_other_querier_timer); } void pim_igmp_other_querier_timer_off(struct gm_sock *igmp) @@ -439,7 +439,7 @@ void pim_igmp_other_querier_timer_off(struct gm_sock *igmp) ifaddr_str, igmp->fd, igmp->interface->name); } } - THREAD_OFF(igmp->t_other_querier_timer); + EVENT_OFF(igmp->t_other_querier_timer); } int igmp_validate_checksum(char *igmp_msg, int igmp_msg_len) @@ -865,8 +865,8 @@ void pim_igmp_general_query_on(struct gm_sock *igmp) ifaddr_str, query_interval, startup_mode ? "startup" : "non-startup", igmp->fd); } - thread_add_timer(router->master, pim_igmp_general_query, igmp, - query_interval, &igmp->t_igmp_query_timer); + event_add_timer(router->master, pim_igmp_general_query, igmp, + query_interval, &igmp->t_igmp_query_timer); } void pim_igmp_general_query_off(struct gm_sock *igmp) @@ -883,11 +883,11 @@ void pim_igmp_general_query_off(struct gm_sock *igmp) ifaddr_str, igmp->fd, igmp->interface->name); } } - THREAD_OFF(igmp->t_igmp_query_timer); + EVENT_OFF(igmp->t_igmp_query_timer); } /* Issue IGMP general query */ -static void pim_igmp_general_query(struct thread *t) +static void pim_igmp_general_query(struct event *t) { struct gm_sock *igmp; struct in_addr dst_addr; @@ -895,7 +895,7 @@ static void pim_igmp_general_query(struct thread *t) struct pim_interface *pim_ifp; int query_buf_size; - igmp = THREAD_ARG(t); + igmp = EVENT_ARG(t); assert(igmp->interface); assert(igmp->interface->info); @@ -953,7 +953,7 @@ static void sock_close(struct gm_sock *igmp) igmp->interface->name); } } - THREAD_OFF(igmp->t_igmp_read); + EVENT_OFF(igmp->t_igmp_read); if (close(igmp->fd)) { flog_err( @@ -1045,7 +1045,7 @@ void igmp_group_delete(struct gm_group *group) igmp_source_delete(src); } - THREAD_OFF(group->t_group_query_retransmit_timer); + EVENT_OFF(group->t_group_query_retransmit_timer); group_timer_off(group); igmp_group_count_decr(pim_ifp); @@ -1208,10 +1208,10 @@ static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr, static void igmp_read_on(struct gm_sock *igmp); -static void pim_igmp_read(struct thread *t) +static void pim_igmp_read(struct event *t) { uint8_t buf[10000]; - struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t); + struct gm_sock *igmp = (struct gm_sock *)EVENT_ARG(t); struct sockaddr_storage from; struct sockaddr_storage to; socklen_t fromlen = sizeof(from); @@ -1243,8 +1243,8 @@ static void igmp_read_on(struct gm_sock *igmp) zlog_debug("Scheduling READ event on IGMP socket fd=%d", igmp->fd); } - thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd, - &igmp->t_igmp_read); + event_add_read(router->master, pim_igmp_read, igmp, igmp->fd, + &igmp->t_igmp_read); } struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list, @@ -1300,11 +1300,11 @@ struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list, source records. Source records whose timers are zero (from the previous EXCLUDE mode) are deleted. */ -static void igmp_group_timer(struct thread *t) +static void igmp_group_timer(struct event *t) { struct gm_group *group; - group = THREAD_ARG(t); + group = EVENT_ARG(t); if (PIM_DEBUG_GM_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -1348,7 +1348,7 @@ static void group_timer_off(struct gm_group *group) zlog_debug("Cancelling TIMER event for group %s on %s", group_str, group->interface->name); } - THREAD_OFF(group->t_group_timer); + EVENT_OFF(group->t_group_timer); } void igmp_group_timer_on(struct gm_group *group, long interval_msec, @@ -1375,8 +1375,8 @@ void igmp_group_timer_on(struct gm_group *group, long interval_msec, */ assert(group->group_filtermode_isexcl); - thread_add_timer_msec(router->master, igmp_group_timer, group, - interval_msec, &group->t_group_timer); + event_add_timer_msec(router->master, igmp_group_timer, group, + interval_msec, &group->t_group_timer); } struct gm_group *find_group_by_addr(struct gm_sock *igmp, diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index e275b5b39849..a1f19b3c6e1c 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -64,10 +64,10 @@ struct gm_sock { pim_addr ifaddr; time_t sock_creation; - struct thread *t_igmp_read; /* read: IGMP sockets */ - struct thread - *t_igmp_query_timer; /* timer: issue IGMP general queries */ - struct thread *t_other_querier_timer; /* timer: other querier present */ + struct event *t_igmp_read; /* read: IGMP sockets */ + /* timer: issue IGMP general queries */ + struct event *t_igmp_query_timer; + struct event *t_other_querier_timer; /* timer: other querier present */ pim_addr querier_addr; /* IP address of the querier */ int querier_query_interval; /* QQI */ int querier_robustness_variable; /* QRV */ @@ -144,7 +144,7 @@ static inline void pim_igmp_other_querier_timer_off(struct gm_sock *igmp) struct gm_source { pim_addr source_addr; - struct thread *t_source_timer; + struct event *t_source_timer; struct gm_group *source_group; /* back pointer */ time_t source_creation; uint32_t source_flags; @@ -165,11 +165,11 @@ struct gm_group { represents the time for the *filter-mode* of the group to expire and switch to INCLUDE mode. */ - struct thread *t_group_timer; + struct event *t_group_timer; /* Shared between group-specific and group-and-source-specific retransmissions */ - struct thread *t_group_query_retransmit_timer; + struct event *t_group_query_retransmit_timer; /* Counter exclusive for group-specific retransmissions (not used by group-and-source-specific retransmissions, diff --git a/pimd/pim_igmp_join.h b/pimd/pim_igmp_join.h index d6d22be3e4c7..0e9498c50c78 100644 --- a/pimd/pim_igmp_join.h +++ b/pimd/pim_igmp_join.h @@ -7,6 +7,8 @@ #ifndef PIM_IGMP_JOIN_H #define PIM_IGMP_JOIN_H +#include "pim_addr.h" + /* required headers #include'd by caller */ #ifndef SOL_IP @@ -26,35 +28,64 @@ struct group_source_req { }; #endif -static inline int pim_igmp_join_source(int fd, ifindex_t ifindex, - struct in_addr group_addr, - struct in_addr source_addr) +#if PIM_IPV == 4 +static inline int pim_gm_join_source(int fd, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr) { struct group_source_req req; - struct sockaddr_in group; - struct sockaddr_in source; + struct sockaddr_in group = {}; + struct sockaddr_in source = {}; memset(&req, 0, sizeof(req)); - memset(&group, 0, sizeof(group)); - group.sin_family = AF_INET; + + group.sin_family = PIM_AF; group.sin_addr = group_addr; group.sin_port = htons(0); - memcpy(&req.gsr_group, &group, sizeof(struct sockaddr_in)); + memcpy(&req.gsr_group, &group, sizeof(group)); - memset(&source, 0, sizeof(source)); - source.sin_family = AF_INET; + source.sin_family = PIM_AF; source.sin_addr = source_addr; source.sin_port = htons(0); - memcpy(&req.gsr_source, &source, sizeof(struct sockaddr_in)); + memcpy(&req.gsr_source, &source, sizeof(source)); req.gsr_interface = ifindex; - if (source_addr.s_addr == INADDR_ANY) + if (pim_addr_is_any(source_addr)) return setsockopt(fd, SOL_IP, MCAST_JOIN_GROUP, &req, sizeof(req)); else return setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, sizeof(req)); } +#else /* PIM_IPV != 4*/ +static inline int pim_gm_join_source(int fd, ifindex_t ifindex, + pim_addr group_addr, pim_addr source_addr) +{ + struct group_source_req req; + struct sockaddr_in6 group = {}; + struct sockaddr_in6 source = {}; + + memset(&req, 0, sizeof(req)); + + group.sin6_family = PIM_AF; + group.sin6_addr = group_addr; + group.sin6_port = htons(0); + memcpy(&req.gsr_group, &group, sizeof(group)); + + source.sin6_family = PIM_AF; + source.sin6_addr = source_addr; + source.sin6_port = htons(0); + memcpy(&req.gsr_source, &source, sizeof(source)); + + req.gsr_interface = ifindex; + + if (pim_addr_is_any(source_addr)) + return setsockopt(fd, SOL_IPV6, MCAST_JOIN_GROUP, &req, + sizeof(req)); + else + return setsockopt(fd, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP, &req, + sizeof(req)); +} +#endif /* PIM_IPV != 4*/ #endif /* PIM_IGMP_JOIN_H */ diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index f10bcaf04b42..18a9fb7c6c76 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -107,12 +107,12 @@ void igmp_group_reset_gmi(struct gm_group *group) igmp_group_timer_on(group, group_membership_interval_msec, ifp->name); } -static void igmp_source_timer(struct thread *t) +static void igmp_source_timer(struct event *t) { struct gm_source *source; struct gm_group *group; - source = THREAD_ARG(t); + source = EVENT_ARG(t); group = source->source_group; @@ -187,7 +187,7 @@ static void source_timer_off(struct gm_group *group, struct gm_source *source) group_str, source_str, group->interface->name); } - THREAD_OFF(source->t_source_timer); + EVENT_OFF(source->t_source_timer); } static void igmp_source_timer_on(struct gm_group *group, @@ -209,8 +209,8 @@ static void igmp_source_timer_on(struct gm_group *group, source_str, group->interface->name); } - thread_add_timer_msec(router->master, igmp_source_timer, source, - interval_msec, &source->t_source_timer); + event_add_timer_msec(router->master, igmp_source_timer, source, + interval_msec, &source->t_source_timer); /* RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules @@ -319,14 +319,6 @@ void igmp_source_free(struct gm_source *source) XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source); } -static void source_channel_oil_detach(struct gm_source *source) -{ - if (source->source_channel_oil) { - pim_channel_oil_del(source->source_channel_oil, __func__); - source->source_channel_oil = NULL; - } -} - /* igmp_source_delete: stop forwarding, and delete the source igmp_source_forward_stop: stop forwarding, but keep the source @@ -355,6 +347,7 @@ void igmp_source_delete(struct gm_source *source) source_timer_off(group, source); igmp_source_forward_stop(source); + source->source_channel_oil = NULL; /* sanity check that forwarding has been disabled */ if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { @@ -371,8 +364,6 @@ void igmp_source_delete(struct gm_source *source) /* warning only */ } - source_channel_oil_detach(source); - /* notice that listnode_delete() can't be moved into igmp_source_free() because the later is @@ -1200,13 +1191,13 @@ static int group_retransmit_sources(struct gm_group *group, return num_retransmit_sources_left; } -static void igmp_group_retransmit(struct thread *t) +static void igmp_group_retransmit(struct event *t) { struct gm_group *group; int num_retransmit_sources_left; int send_with_sflag_set; /* boolean */ - group = THREAD_ARG(t); + group = EVENT_ARG(t); if (PIM_DEBUG_GM_TRACE) { char group_str[INET_ADDRSTRLEN]; @@ -1281,9 +1272,8 @@ static void group_retransmit_timer_on(struct gm_group *group) group->interface->name); } - thread_add_timer_msec(router->master, igmp_group_retransmit, group, - lmqi_msec, - &group->t_group_query_retransmit_timer); + event_add_timer_msec(router->master, igmp_group_retransmit, group, + lmqi_msec, &group->t_group_query_retransmit_timer); } static long igmp_group_timer_remain_msec(struct gm_group *group) diff --git a/pimd/pim_instance.c b/pimd/pim_instance.c index b50d9954f7e3..6f33af06011e 100644 --- a/pimd/pim_instance.c +++ b/pimd/pim_instance.c @@ -45,11 +45,7 @@ static void pim_instance_terminate(struct pim_instance *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); - hash_free(pim->rpf_hash); - pim->rpf_hash = NULL; - } + hash_clean_and_free(&pim->rpf_hash, (void *)pim_rp_list_hash_clean); pim_if_terminate(pim); diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h index fa90e38a1605..11577ae46d01 100644 --- a/pimd/pim_instance.h +++ b/pimd/pim_instance.h @@ -64,7 +64,7 @@ enum pim_mlag_flags { }; struct pim_router { - struct thread_master *master; + struct event_loop *master; uint32_t debugs; @@ -92,7 +92,7 @@ struct pim_router { /* 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 event *zpthread_mlag_write; struct in_addr anycast_vtep_ip; struct in_addr local_vtep_ip; struct pim_mlag_stats mlag_stats; @@ -120,7 +120,7 @@ struct pim_instance { int send_v6_secondary; - struct thread *thread; + struct event *thread; int mroute_socket; int reg_sock; /* Socket to send register msg */ int64_t mroute_socket_creation; @@ -157,7 +157,7 @@ struct pim_instance { unsigned int gm_socket_if_count; int gm_socket; - struct thread *t_gm_recv; + struct event *t_gm_recv; unsigned int gm_group_count; unsigned int gm_watermark_limit; @@ -176,7 +176,7 @@ struct pim_instance { uint64_t bsm_dropped; /* If we need to rescan all our upstreams */ - struct thread *rpf_cache_refresher; + struct event *rpf_cache_refresher; int64_t rpf_cache_refresh_requests; int64_t rpf_cache_refresh_events; int64_t rpf_cache_refresh_last; diff --git a/pimd/pim_join.c b/pimd/pim_join.c index 20821d58b8f3..671f7a37a5a3 100644 --- a/pimd/pim_join.c +++ b/pimd/pim_join.c @@ -326,10 +326,9 @@ int pim_joinprune_recv(struct interface *ifp, struct pim_neighbor *neigh, if (PIM_IF_FLAG_TEST_S_G_RPT(child->flags)) { if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP) - THREAD_OFF( + EVENT_OFF( child->t_ifjoin_prune_pending_timer); - THREAD_OFF( - child->t_ifjoin_expiry_timer); + EVENT_OFF(child->t_ifjoin_expiry_timer); PIM_IF_FLAG_UNSET_S_G_RPT(child->flags); child->ifjoin_state = PIM_IFJOIN_NOINFO; delete_on_noinfo(child); diff --git a/pimd/pim_main.c b/pimd/pim_main.c index ce4326c61697..7db0a7676b2e 100644 --- a/pimd/pim_main.c +++ b/pimd/pim_main.c @@ -11,7 +11,7 @@ #include "lib/version.h" #include <getopt.h> #include "command.h" -#include "thread.h" +#include "frrevent.h" #include <signal.h> #include "memory.h" diff --git a/pimd/pim_mlag.c b/pimd/pim_mlag.c index 61cf2860fdf7..5d72eb65813c 100644 --- a/pimd/pim_mlag.c +++ b/pimd/pim_mlag.c @@ -921,7 +921,7 @@ int pim_zebra_mlag_process_down(ZAPI_CALLBACK_ARGS) return 0; } -static void pim_mlag_register_handler(struct thread *thread) +static void pim_mlag_register_handler(struct event *thread) { uint32_t bit_mask = 0; @@ -952,11 +952,11 @@ void pim_mlag_register(void) router->mlag_process_register = true; - thread_add_event(router->master, pim_mlag_register_handler, NULL, 0, - NULL); + event_add_event(router->master, pim_mlag_register_handler, NULL, 0, + NULL); } -static void pim_mlag_deregister_handler(struct thread *thread) +static void pim_mlag_deregister_handler(struct event *thread) { if (!zclient) return; @@ -980,8 +980,8 @@ void pim_mlag_deregister(void) router->mlag_process_register = false; - thread_add_event(router->master, pim_mlag_deregister_handler, NULL, 0, - NULL); + event_add_event(router->master, pim_mlag_deregister_handler, NULL, 0, + NULL); } void pim_if_configure_mlag_dualactive(struct pim_interface *pim_ifp) diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index adf0540f6526..b64fcdeb8723 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -46,7 +46,7 @@ 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->vrf_id != VRF_DEFAULT) { + if (enable && pim->vrf->vrf_id != VRF_DEFAULT) { frr_with_privs (&pimd_privs) { data = pim->vrf->data.l.table_id; @@ -767,7 +767,7 @@ int pim_mroute_msg(struct pim_instance *pim, const char *buf, size_t buf_size, return 0; } -static void mroute_read(struct thread *t) +static void mroute_read(struct event *t) { struct pim_instance *pim; static long long count; @@ -775,7 +775,7 @@ static void mroute_read(struct thread *t) int cont = 1; int rd; ifindex_t ifindex; - pim = THREAD_ARG(t); + pim = EVENT_ARG(t); while (cont) { rd = pim_socket_recvfromto(pim->mroute_socket, (uint8_t *)buf, @@ -809,13 +809,13 @@ static void mroute_read(struct thread *t) static void mroute_read_on(struct pim_instance *pim) { - thread_add_read(router->master, mroute_read, pim, pim->mroute_socket, - &pim->thread); + event_add_read(router->master, mroute_read, pim, pim->mroute_socket, + &pim->thread); } static void mroute_read_off(struct pim_instance *pim) { - THREAD_OFF(pim->thread); + EVENT_OFF(pim->thread); } int pim_mroute_socket_enable(struct pim_instance *pim) diff --git a/pimd/pim_msdp.c b/pimd/pim_msdp.c index dfa5ffeeec64..b1b6958fe166 100644 --- a/pimd/pim_msdp.c +++ b/pimd/pim_msdp.c @@ -12,7 +12,7 @@ #include <lib/prefix.h> #include <lib/sockunion.h> #include <lib/stream.h> -#include <lib/thread.h> +#include <frrevent.h> #include <lib/vty.h> #include <lib/plist.h> #include <lib/lib_errors.h> @@ -54,9 +54,9 @@ static void pim_msdp_sa_timer_expiry_log(struct pim_msdp_sa *sa, } /* RFC-3618:Sec-5.1 - global active source advertisement timer */ -static void pim_msdp_sa_adv_timer_cb(struct thread *t) +static void pim_msdp_sa_adv_timer_cb(struct event *t) { - struct pim_instance *pim = THREAD_ARG(t); + struct pim_instance *pim = EVENT_ARG(t); if (PIM_DEBUG_MSDP_EVENTS) { zlog_debug("MSDP SA advertisement timer expired"); @@ -68,20 +68,20 @@ static void pim_msdp_sa_adv_timer_cb(struct thread *t) static void pim_msdp_sa_adv_timer_setup(struct pim_instance *pim, bool start) { - THREAD_OFF(pim->msdp.sa_adv_timer); + EVENT_OFF(pim->msdp.sa_adv_timer); if (start) { - thread_add_timer(pim->msdp.master, pim_msdp_sa_adv_timer_cb, - pim, PIM_MSDP_SA_ADVERTISMENT_TIME, - &pim->msdp.sa_adv_timer); + event_add_timer(pim->msdp.master, pim_msdp_sa_adv_timer_cb, pim, + PIM_MSDP_SA_ADVERTISMENT_TIME, + &pim->msdp.sa_adv_timer); } } /* RFC-3618:Sec-5.3 - SA cache state timer */ -static void pim_msdp_sa_state_timer_cb(struct thread *t) +static void pim_msdp_sa_state_timer_cb(struct event *t) { struct pim_msdp_sa *sa; - sa = THREAD_ARG(t); + sa = EVENT_ARG(t); if (PIM_DEBUG_MSDP_EVENTS) { pim_msdp_sa_timer_expiry_log(sa, "state"); @@ -92,11 +92,11 @@ static void pim_msdp_sa_state_timer_cb(struct thread *t) static void pim_msdp_sa_state_timer_setup(struct pim_msdp_sa *sa, bool start) { - THREAD_OFF(sa->sa_state_timer); + EVENT_OFF(sa->sa_state_timer); if (start) { - thread_add_timer(sa->pim->msdp.master, - pim_msdp_sa_state_timer_cb, sa, - PIM_MSDP_SA_HOLD_TIME, &sa->sa_state_timer); + event_add_timer(sa->pim->msdp.master, + pim_msdp_sa_state_timer_cb, sa, + PIM_MSDP_SA_HOLD_TIME, &sa->sa_state_timer); } } @@ -861,11 +861,11 @@ static void pim_msdp_peer_timer_expiry_log(struct pim_msdp_peer *mp, } /* RFC-3618:Sec-5.4 - peer hold timer */ -static void pim_msdp_peer_hold_timer_cb(struct thread *t) +static void pim_msdp_peer_hold_timer_cb(struct event *t) { struct pim_msdp_peer *mp; - mp = THREAD_ARG(t); + mp = EVENT_ARG(t); if (PIM_DEBUG_MSDP_EVENTS) { pim_msdp_peer_timer_expiry_log(mp, "hold"); @@ -884,20 +884,20 @@ static void pim_msdp_peer_hold_timer_cb(struct thread *t) static void pim_msdp_peer_hold_timer_setup(struct pim_msdp_peer *mp, bool start) { struct pim_instance *pim = mp->pim; - THREAD_OFF(mp->hold_timer); + EVENT_OFF(mp->hold_timer); if (start) { - thread_add_timer(pim->msdp.master, pim_msdp_peer_hold_timer_cb, - mp, pim->msdp.hold_time, &mp->hold_timer); + event_add_timer(pim->msdp.master, pim_msdp_peer_hold_timer_cb, + mp, pim->msdp.hold_time, &mp->hold_timer); } } /* RFC-3618:Sec-5.5 - peer keepalive timer */ -static void pim_msdp_peer_ka_timer_cb(struct thread *t) +static void pim_msdp_peer_ka_timer_cb(struct event *t) { struct pim_msdp_peer *mp; - mp = THREAD_ARG(t); + mp = EVENT_ARG(t); if (PIM_DEBUG_MSDP_EVENTS) { pim_msdp_peer_timer_expiry_log(mp, "ka"); @@ -909,11 +909,10 @@ static void pim_msdp_peer_ka_timer_cb(struct thread *t) static void pim_msdp_peer_ka_timer_setup(struct pim_msdp_peer *mp, bool start) { - THREAD_OFF(mp->ka_timer); + EVENT_OFF(mp->ka_timer); if (start) { - thread_add_timer(mp->pim->msdp.master, - pim_msdp_peer_ka_timer_cb, mp, - mp->pim->msdp.keep_alive, &mp->ka_timer); + event_add_timer(mp->pim->msdp.master, pim_msdp_peer_ka_timer_cb, + mp, mp->pim->msdp.keep_alive, &mp->ka_timer); } } @@ -954,11 +953,11 @@ static void pim_msdp_peer_active_connect(struct pim_msdp_peer *mp) } /* RFC-3618:Sec-5.6 - connection retry on active peer */ -static void pim_msdp_peer_cr_timer_cb(struct thread *t) +static void pim_msdp_peer_cr_timer_cb(struct event *t) { struct pim_msdp_peer *mp; - mp = THREAD_ARG(t); + mp = EVENT_ARG(t); if (PIM_DEBUG_MSDP_EVENTS) { pim_msdp_peer_timer_expiry_log(mp, "connect-retry"); @@ -973,11 +972,11 @@ static void pim_msdp_peer_cr_timer_cb(struct thread *t) static void pim_msdp_peer_cr_timer_setup(struct pim_msdp_peer *mp, bool start) { - THREAD_OFF(mp->cr_timer); + EVENT_OFF(mp->cr_timer); if (start) { - thread_add_timer(mp->pim->msdp.master, - pim_msdp_peer_cr_timer_cb, mp, - mp->pim->msdp.connection_retry, &mp->cr_timer); + event_add_timer(mp->pim->msdp.master, pim_msdp_peer_cr_timer_cb, + mp, mp->pim->msdp.connection_retry, + &mp->cr_timer); } } @@ -1334,7 +1333,7 @@ static void pim_msdp_enable(struct pim_instance *pim) } /* MSDP init */ -void pim_msdp_init(struct pim_instance *pim, struct thread_master *master) +void pim_msdp_init(struct pim_instance *pim, struct event_loop *master) { pim->msdp.master = master; char hash_name[64]; @@ -1367,21 +1366,13 @@ void pim_msdp_exit(struct pim_instance *pim) while ((mg = SLIST_FIRST(&pim->msdp.mglist)) != NULL) pim_msdp_mg_free(pim, &mg); - if (pim->msdp.peer_hash) { - hash_clean(pim->msdp.peer_hash, NULL); - hash_free(pim->msdp.peer_hash); - pim->msdp.peer_hash = NULL; - } + hash_clean_and_free(&pim->msdp.peer_hash, NULL); if (pim->msdp.peer_list) { list_delete(&pim->msdp.peer_list); } - if (pim->msdp.sa_hash) { - hash_clean(pim->msdp.sa_hash, NULL); - hash_free(pim->msdp.sa_hash); - pim->msdp.sa_hash = NULL; - } + hash_clean_and_free(&pim->msdp.sa_hash, NULL); if (pim->msdp.sa_list) { list_delete(&pim->msdp.sa_list); diff --git a/pimd/pim_msdp.h b/pimd/pim_msdp.h index f0a03f076b84..ddc015f9b62b 100644 --- a/pimd/pim_msdp.h +++ b/pimd/pim_msdp.h @@ -74,7 +74,7 @@ struct pim_msdp_sa { /* rfc-3618 is missing default value for SA-hold-down-Period. pulled * this number from industry-standards */ #define PIM_MSDP_SA_HOLD_TIME ((3*60)+30) - struct thread *sa_state_timer; // 5.6 + struct event *sa_state_timer; // 5.6 int64_t uptime; struct pim_upstream *up; @@ -109,18 +109,18 @@ struct pim_msdp_peer { /* protocol timers */ #define PIM_MSDP_PEER_HOLD_TIME 75 - struct thread *hold_timer; // 5.4 + struct event *hold_timer; // 5.4 #define PIM_MSDP_PEER_KA_TIME 60 - struct thread *ka_timer; // 5.5 + struct event *ka_timer; // 5.5 #define PIM_MSDP_PEER_CONNECT_RETRY_TIME 30 - struct thread *cr_timer; // 5.6 + struct event *cr_timer; // 5.6 /* packet thread and buffers */ uint32_t packet_size; struct stream *ibuf; struct stream_fifo *obuf; - struct thread *t_read; - struct thread *t_write; + struct event *t_read; + struct event *t_write; /* stats */ uint32_t conn_attempts; @@ -167,12 +167,12 @@ enum pim_msdp_flags { struct pim_msdp_listener { int fd; union sockunion su; - struct thread *thread; + struct event *thread; }; struct pim_msdp { enum pim_msdp_flags flags; - struct thread_master *master; + struct event_loop *master; struct pim_msdp_listener listener; uint32_t rejected_accepts; @@ -182,7 +182,7 @@ struct pim_msdp { /* MSDP active-source info */ #define PIM_MSDP_SA_ADVERTISMENT_TIME 60 - struct thread *sa_adv_timer; // 5.6 + struct event *sa_adv_timer; // 5.6 struct hash *sa_hash; struct list *sa_list; uint32_t local_cnt; @@ -204,20 +204,20 @@ struct pim_msdp { }; #define PIM_MSDP_PEER_READ_ON(mp) \ - thread_add_read(mp->pim->msdp.master, pim_msdp_read, mp, mp->fd, \ - &mp->t_read) + event_add_read(mp->pim->msdp.master, pim_msdp_read, mp, mp->fd, \ + &mp->t_read) #define PIM_MSDP_PEER_WRITE_ON(mp) \ - thread_add_write(mp->pim->msdp.master, pim_msdp_write, mp, mp->fd, \ - &mp->t_write) + event_add_write(mp->pim->msdp.master, pim_msdp_write, mp, mp->fd, \ + &mp->t_write) -#define PIM_MSDP_PEER_READ_OFF(mp) thread_cancel(&mp->t_read) -#define PIM_MSDP_PEER_WRITE_OFF(mp) thread_cancel(&mp->t_write) +#define PIM_MSDP_PEER_READ_OFF(mp) event_cancel(&mp->t_read) +#define PIM_MSDP_PEER_WRITE_OFF(mp) event_cancel(&mp->t_write) #if PIM_IPV != 6 // struct pim_msdp *msdp; struct pim_instance; -void pim_msdp_init(struct pim_instance *pim, struct thread_master *master); +void pim_msdp_init(struct pim_instance *pim, struct event_loop *master); void pim_msdp_exit(struct pim_instance *pim); char *pim_msdp_state_dump(enum pim_msdp_peer_state state, char *buf, int buf_size); @@ -227,7 +227,7 @@ void pim_msdp_peer_established(struct pim_msdp_peer *mp); void pim_msdp_peer_pkt_rxed(struct pim_msdp_peer *mp); void pim_msdp_peer_stop_tcp_conn(struct pim_msdp_peer *mp, bool chg_state); void pim_msdp_peer_reset_tcp_conn(struct pim_msdp_peer *mp, const char *rc_str); -void pim_msdp_write(struct thread *thread); +void pim_msdp_write(struct event *thread); int pim_msdp_config_write(struct pim_instance *pim, struct vty *vty, const char *spaces); bool pim_msdp_peer_config_write(struct vty *vty, struct pim_instance *pim, @@ -308,7 +308,7 @@ void pim_msdp_peer_change_source(struct pim_msdp_peer *mp, #else /* PIM_IPV == 6 */ static inline void pim_msdp_init(struct pim_instance *pim, - struct thread_master *master) + struct event_loop *master) { } diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c index a6f318e61e60..a414736cccf1 100644 --- a/pimd/pim_msdp_packet.c +++ b/pimd/pim_msdp_packet.c @@ -8,7 +8,7 @@ #include <lib/log.h> #include <lib/network.h> #include <lib/stream.h> -#include <lib/thread.h> +#include "frrevent.h" #include <lib/vty.h> #include <lib/lib_errors.h> @@ -190,7 +190,7 @@ static void pim_msdp_write_proceed_actions(struct pim_msdp_peer *mp) } } -void pim_msdp_write(struct thread *thread) +void pim_msdp_write(struct event *thread) { struct pim_msdp_peer *mp; struct stream *s; @@ -200,7 +200,7 @@ void pim_msdp_write(struct thread *thread) int work_cnt = 0; int work_max_cnt = 100; - mp = THREAD_ARG(thread); + mp = EVENT_ARG(thread); mp->t_write = NULL; if (PIM_DEBUG_MSDP_INTERNAL) { @@ -686,13 +686,13 @@ static int pim_msdp_read_packet(struct pim_msdp_peer *mp) return 0; } -void pim_msdp_read(struct thread *thread) +void pim_msdp_read(struct event *thread) { struct pim_msdp_peer *mp; int rc; uint32_t len; - mp = THREAD_ARG(thread); + mp = EVENT_ARG(thread); mp->t_read = NULL; if (PIM_DEBUG_MSDP_INTERNAL) { diff --git a/pimd/pim_msdp_packet.h b/pimd/pim_msdp_packet.h index efb14cdf72f8..1584a2453927 100644 --- a/pimd/pim_msdp_packet.h +++ b/pimd/pim_msdp_packet.h @@ -51,7 +51,7 @@ #define PIM_MSDP_PKT_TYPE_STRLEN 16 void pim_msdp_pkt_ka_tx(struct pim_msdp_peer *mp); -void pim_msdp_read(struct thread *thread); +void pim_msdp_read(struct event *thread); void pim_msdp_pkt_sa_tx(struct pim_instance *pim); void pim_msdp_pkt_sa_tx_one(struct pim_msdp_sa *sa); void pim_msdp_pkt_sa_tx_to_one_peer(struct pim_msdp_peer *mp); diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c index a6b5ee11b61a..fe8d5e934aca 100644 --- a/pimd/pim_msdp_socket.c +++ b/pimd/pim_msdp_socket.c @@ -9,7 +9,7 @@ #include <lib/log.h> #include <lib/network.h> #include <lib/sockunion.h> -#include <lib/thread.h> +#include "frrevent.h" #include <lib/vty.h> #include <lib/if.h> #include <lib/vrf.h> @@ -50,10 +50,10 @@ static void pim_msdp_update_sock_send_buffer_size(int fd) } /* passive peer socket accept */ -static void pim_msdp_sock_accept(struct thread *thread) +static void pim_msdp_sock_accept(struct event *thread) { union sockunion su; - struct pim_instance *pim = THREAD_ARG(thread); + struct pim_instance *pim = EVENT_ARG(thread); int accept_sock; int msdp_sock; struct pim_msdp_peer *mp; @@ -61,15 +61,15 @@ static void pim_msdp_sock_accept(struct thread *thread) sockunion_init(&su); /* re-register accept thread */ - accept_sock = THREAD_FD(thread); + accept_sock = EVENT_FD(thread); if (accept_sock < 0) { flog_err(EC_LIB_DEVELOPMENT, "accept_sock is negative value %d", accept_sock); return; } pim->msdp.listener.thread = NULL; - thread_add_read(router->master, pim_msdp_sock_accept, pim, accept_sock, - &pim->msdp.listener.thread); + event_add_read(router->master, pim_msdp_sock_accept, pim, accept_sock, + &pim->msdp.listener.thread); /* accept client connection. */ msdp_sock = sockunion_accept(accept_sock, &su); @@ -192,8 +192,8 @@ int pim_msdp_sock_listen(struct pim_instance *pim) /* add accept thread */ listener->fd = sock; memcpy(&listener->su, &sin, socklen); - thread_add_read(pim->msdp.master, pim_msdp_sock_accept, pim, sock, - &listener->thread); + event_add_read(pim->msdp.master, pim_msdp_sock_accept, pim, sock, + &listener->thread); pim->msdp.flags |= PIM_MSDPF_LISTENER; return 0; diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index fa6f66414957..30d84710e667 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -45,20 +45,6 @@ MACRO_REQUIRE_SEMICOLON() #define yang_dnode_get_pimaddr yang_dnode_get_ipv4 #endif /* PIM_IPV != 6 */ -static void pim_if_membership_clear(struct interface *ifp) -{ - struct pim_interface *pim_ifp; - - pim_ifp = ifp->info; - assert(pim_ifp); - - if (pim_ifp->pim_enable && pim_ifp->gm_enable) { - return; - } - - pim_ifchannel_membership_clear(ifp); -} - /* * When PIM is disabled on interface, IGMPv3 local membership * information is not injected into PIM interface state. @@ -71,9 +57,13 @@ static void pim_if_membership_clear(struct interface *ifp) static void pim_if_membership_refresh(struct interface *ifp) { struct pim_interface *pim_ifp; +#if PIM_IPV == 4 struct listnode *grpnode; struct gm_group *grp; - +#else + struct gm_if *gm_ifp; + struct gm_sg *sg, *sg_start; +#endif pim_ifp = ifp->info; assert(pim_ifp); @@ -83,6 +73,11 @@ static void pim_if_membership_refresh(struct interface *ifp) if (!pim_ifp->gm_enable) return; +#if PIM_IPV == 6 + gm_ifp = pim_ifp->mld; + if (!gm_ifp) + return; +#endif /* * First clear off membership from all PIM (S,G) entries on the * interface @@ -90,6 +85,7 @@ static void pim_if_membership_refresh(struct interface *ifp) pim_ifchannel_membership_clear(ifp); +#if PIM_IPV == 4 /* * Then restore PIM (S,G) membership from all IGMPv3 (S,G) entries on * the interface @@ -116,6 +112,16 @@ static void pim_if_membership_refresh(struct interface *ifp) } /* scan group sources */ } /* scan igmp groups */ +#else + sg_start = gm_sgs_first(gm_ifp->sgs); + + frr_each_from (gm_sgs, gm_ifp->sgs, sg, sg_start) { + if (!in6_multicast_nofwd(&sg->sgaddr.grp)) { + pim_ifchannel_local_membership_add( + ifp, &sg->sgaddr, false /*is_vxlan*/); + } + } +#endif /* * Finally delete every PIM (S,G) entry lacking all state info @@ -141,32 +147,6 @@ static int pim_cmd_interface_add(struct interface *ifp) return 1; } -static int pim_cmd_interface_delete(struct interface *ifp) -{ - struct pim_interface *pim_ifp = ifp->info; - - if (!pim_ifp) - return 1; - - pim_ifp->pim_enable = false; - - pim_if_membership_clear(ifp); - - /* - * pim_sock_delete() removes all neighbors from - * pim_ifp->pim_neighbor_list. - */ - pim_sock_delete(ifp, "pim unconfigured on interface"); - pim_upstream_nh_if_update(pim_ifp->pim, ifp); - - if (!pim_ifp->gm_enable) { - pim_if_addr_del_all(ifp); - pim_if_delete(ifp); - } - - return 1; -} - static int interface_pim_use_src_cmd_worker(struct interface *ifp, pim_addr source_addr, char *errmsg, size_t errmsg_len) { @@ -258,7 +238,7 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, pim_addr rp_addr, if (result == PIM_RP_NO_PATH) { snprintfrr(errmsg, errmsg_len, "No Path to RP address specified: %pPA", &rp_addr); - return NB_ERR_INCONSISTENCY; + return NB_OK; } if (result == PIM_GROUP_OVERLAP) { @@ -1553,12 +1533,7 @@ int lib_interface_pim_address_family_destroy(struct nb_cb_destroy_args *args) if (!pim_ifp) return NB_OK; - if (!pim_cmd_interface_delete(ifp)) { - snprintf(args->errmsg, args->errmsg_len, - "Unable to delete interface information %s", - ifp->name); - return NB_ERR_INCONSISTENCY; - } + pim_pim_interface_delete(ifp); } return NB_OK; @@ -1606,11 +1581,7 @@ int lib_interface_pim_address_family_pim_enable_modify(struct nb_cb_modify_args if (!pim_ifp) return NB_ERR_INCONSISTENCY; - if (!pim_cmd_interface_delete(ifp)) { - snprintf(args->errmsg, args->errmsg_len, - "Unable to delete interface information"); - return NB_ERR_INCONSISTENCY; - } + pim_pim_interface_delete(ifp); } break; } @@ -2545,7 +2516,6 @@ int lib_interface_gmp_address_family_create(struct nb_cb_create_args *args) int lib_interface_gmp_address_family_destroy(struct nb_cb_destroy_args *args) { struct interface *ifp; - struct pim_interface *pim_ifp; switch (args->event) { case NB_EV_VALIDATE: @@ -2554,19 +2524,7 @@ int lib_interface_gmp_address_family_destroy(struct nb_cb_destroy_args *args) break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - pim_ifp = ifp->info; - - if (!pim_ifp) - return NB_OK; - - pim_ifp->gm_enable = false; - - pim_if_membership_clear(ifp); - - pim_if_addr_del_all_igmp(ifp); - - if (!pim_ifp->pim_enable) - pim_if_delete(ifp); + pim_gm_interface_delete(ifp); } return NB_OK; @@ -2580,7 +2538,6 @@ int lib_interface_gmp_address_family_enable_modify( { struct interface *ifp; bool gm_enable; - struct pim_interface *pim_ifp; int mcast_if_count; const char *ifp_name; const struct lyd_node *if_dnode; @@ -2610,25 +2567,8 @@ int lib_interface_gmp_address_family_enable_modify( if (gm_enable) return pim_cmd_gm_start(ifp); - else { - pim_ifp = ifp->info; - - if (!pim_ifp) - return NB_ERR_INCONSISTENCY; - - pim_ifp->gm_enable = false; - - pim_if_membership_clear(ifp); - -#if PIM_IPV == 4 - pim_if_addr_del_all_igmp(ifp); -#else - gm_ifp_teardown(ifp); -#endif - - if (!pim_ifp->pim_enable) - pim_if_delete(ifp); - } + else + pim_gm_interface_delete(ifp); } return NB_OK; } @@ -2880,10 +2820,9 @@ int lib_interface_gmp_address_family_robustness_variable_modify( int lib_interface_gmp_address_family_static_group_create( struct nb_cb_create_args *args) { -#if PIM_IPV == 4 struct interface *ifp; - struct ipaddr source_addr; - struct ipaddr group_addr; + pim_addr source_addr; + pim_addr group_addr; int result; const char *ifp_name; const struct lyd_node *if_dnode; @@ -2899,33 +2838,40 @@ int lib_interface_gmp_address_family_static_group_create( return NB_ERR_VALIDATION; } - yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr"); - if (pim_is_group_224_0_0_0_24(group_addr.ip._v4_addr)) { + yang_dnode_get_pimaddr(&group_addr, args->dnode, + "./group-addr"); +#if PIM_IPV == 4 + if (pim_is_group_224_0_0_0_24(group_addr)) { snprintf( args->errmsg, args->errmsg_len, "Groups within 224.0.0.0/24 are reserved and cannot be joined"); return NB_ERR_VALIDATION; } +#else + if (ipv6_mcast_reserved(&group_addr)) { + snprintf( + args->errmsg, args->errmsg_len, + "Groups within ffx2::/16 are reserved and cannot be joined"); + return NB_ERR_VALIDATION; + } +#endif break; case NB_EV_PREPARE: case NB_EV_ABORT: break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - yang_dnode_get_ip(&source_addr, args->dnode, "./source-addr"); - yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr"); - - result = pim_if_igmp_join_add(ifp, group_addr.ip._v4_addr, - source_addr.ip._v4_addr); + yang_dnode_get_pimaddr(&source_addr, args->dnode, + "./source-addr"); + yang_dnode_get_pimaddr(&group_addr, args->dnode, + "./group-addr"); + result = pim_if_gm_join_add(ifp, group_addr, source_addr); if (result) { snprintf(args->errmsg, args->errmsg_len, - "Failure joining IGMP group"); + "Failure joining " GM " group"); return NB_ERR_INCONSISTENCY; } } -#else - /* TBD Depends on MLD data structure changes */ -#endif /* PIM_IPV == 4 */ return NB_OK; } @@ -2933,8 +2879,8 @@ int lib_interface_gmp_address_family_static_group_destroy( struct nb_cb_destroy_args *args) { struct interface *ifp; - struct ipaddr source_addr; - struct ipaddr group_addr; + pim_addr source_addr; + pim_addr group_addr; int result; switch (args->event) { @@ -2944,22 +2890,17 @@ int lib_interface_gmp_address_family_static_group_destroy( break; case NB_EV_APPLY: ifp = nb_running_get_entry(args->dnode, NULL, true); - yang_dnode_get_ip(&source_addr, args->dnode, "./source-addr"); - yang_dnode_get_ip(&group_addr, args->dnode, "./group-addr"); - - result = pim_if_igmp_join_del(ifp, group_addr.ip._v4_addr, - source_addr.ip._v4_addr); + yang_dnode_get_pimaddr(&source_addr, args->dnode, + "./source-addr"); + yang_dnode_get_pimaddr(&group_addr, args->dnode, + "./group-addr"); + result = pim_if_gm_join_del(ifp, group_addr, source_addr); if (result) { - char src_str[INET_ADDRSTRLEN]; - char grp_str[INET_ADDRSTRLEN]; - - ipaddr2str(&source_addr, src_str, sizeof(src_str)); - ipaddr2str(&group_addr, grp_str, sizeof(grp_str)); - snprintf(args->errmsg, args->errmsg_len, - "%% Failure leaving IGMP group %s %s on interface %s: %d", - src_str, grp_str, ifp->name, result); + "%% Failure leaving " GM + " group %pPAs %pPAs on interface %s: %d", + &source_addr, &group_addr, ifp->name, result); return NB_ERR_INCONSISTENCY; } diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 0b7ea0ad9d1e..1cd7cce086fb 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -29,6 +29,7 @@ #include "pim_jp_agg.h" #include "pim_bfd.h" #include "pim_register.h" +#include "pim_oil.h" static void dr_election_by_addr(struct interface *ifp) { @@ -123,9 +124,10 @@ int pim_if_dr_election(struct interface *ifp) pim_if_update_could_assert(ifp); pim_if_update_assert_tracking_desired(ifp); - if (PIM_I_am_DR(pim_ifp)) + if (PIM_I_am_DR(pim_ifp)) { pim_ifp->am_i_dr = true; - else { + pim_clear_nocache_state(pim_ifp); + } else { if (pim_ifp->am_i_dr == true) { pim_reg_del_on_couldreg_fail(ifp); pim_ifp->am_i_dr = false; @@ -188,13 +190,13 @@ static void update_dr_priority(struct pim_neighbor *neigh, } } -static void on_neighbor_timer(struct thread *t) +static void on_neighbor_timer(struct event *t) { struct pim_neighbor *neigh; struct interface *ifp; char msg[100]; - neigh = THREAD_ARG(t); + neigh = EVENT_ARG(t); ifp = neigh->interface; @@ -220,7 +222,7 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) { neigh->holdtime = holdtime; - THREAD_OFF(neigh->t_expire_timer); + EVENT_OFF(neigh->t_expire_timer); /* 0xFFFF is request for no holdtime @@ -234,13 +236,13 @@ void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime) __func__, neigh->holdtime, &neigh->source_addr, neigh->interface->name); - thread_add_timer(router->master, on_neighbor_timer, neigh, - neigh->holdtime, &neigh->t_expire_timer); + event_add_timer(router->master, on_neighbor_timer, neigh, + neigh->holdtime, &neigh->t_expire_timer); } -static void on_neighbor_jp_timer(struct thread *t) +static void on_neighbor_jp_timer(struct event *t) { - struct pim_neighbor *neigh = THREAD_ARG(t); + struct pim_neighbor *neigh = EVENT_ARG(t); struct pim_rpf rpf; if (PIM_DEBUG_PIM_TRACE) @@ -253,15 +255,15 @@ static void on_neighbor_jp_timer(struct thread *t) rpf.rpf_addr = neigh->source_addr; pim_joinprune_send(&rpf, neigh->upstream_jp_agg); - thread_add_timer(router->master, on_neighbor_jp_timer, neigh, - router->t_periodic, &neigh->jp_timer); + event_add_timer(router->master, on_neighbor_jp_timer, neigh, + router->t_periodic, &neigh->jp_timer); } static void pim_neighbor_start_jp_timer(struct pim_neighbor *neigh) { - THREAD_OFF(neigh->jp_timer); - thread_add_timer(router->master, on_neighbor_jp_timer, neigh, - router->t_periodic, &neigh->jp_timer); + EVENT_OFF(neigh->jp_timer); + event_add_timer(router->master, on_neighbor_jp_timer, neigh, + router->t_periodic, &neigh->jp_timer); } static struct pim_neighbor * @@ -375,7 +377,7 @@ void pim_neighbor_free(struct pim_neighbor *neigh) delete_prefix_list(neigh); list_delete(&neigh->upstream_jp_agg); - THREAD_OFF(neigh->jp_timer); + EVENT_OFF(neigh->jp_timer); bfd_sess_free(&neigh->bfd_session); @@ -579,7 +581,7 @@ void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh, zlog_notice("PIM NEIGHBOR DOWN: neighbor %pPA on interface %s: %s", &neigh->source_addr, ifp->name, delete_message); - THREAD_OFF(neigh->t_expire_timer); + EVENT_OFF(neigh->t_expire_timer); pim_if_assert_on_neighbor_down(ifp, neigh->source_addr); diff --git a/pimd/pim_neighbor.h b/pimd/pim_neighbor.h index f7375745a153..69e9976de5fb 100644 --- a/pimd/pim_neighbor.h +++ b/pimd/pim_neighbor.h @@ -27,10 +27,10 @@ struct pim_neighbor { uint32_t dr_priority; uint32_t generation_id; struct list *prefix_list; /* list of struct prefix */ - struct thread *t_expire_timer; + struct event *t_expire_timer; struct interface *interface; - struct thread *jp_timer; + struct event *jp_timer; struct list *upstream_jp_agg; struct bfd_session_params *bfd_session; }; diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index d164e7ed81ca..5f0f2a5933dc 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -724,27 +724,20 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) return 0; } - if (cmd == ZEBRA_NEXTHOP_UPDATE) { - rpf.rpf_addr = pim_addr_from_prefix(&match); - pnc = pim_nexthop_cache_find(pim, &rpf); - if (!pnc) { - if (PIM_DEBUG_PIM_NHT) - zlog_debug( - "%s: Skipping NHT update, addr %pPA is not in local cached DB.", - __func__, &rpf.rpf_addr); - return 0; - } - } else { - /* - * We do not currently handle ZEBRA_IMPORT_CHECK_UPDATE - */ + rpf.rpf_addr = pim_addr_from_prefix(&match); + pnc = pim_nexthop_cache_find(pim, &rpf); + if (!pnc) { + if (PIM_DEBUG_PIM_NHT) + zlog_debug( + "%s: Skipping NHT update, addr %pPA is not in local cached DB.", + __func__, &rpf.rpf_addr); return 0; } pnc->last_update = pim_time_monotonic_usec(); if (nhr.nexthop_num) { - pnc->nexthop_num = 0; // Only increment for pim enabled rpf. + pnc->nexthop_num = 0; for (i = 0; i < nhr.nexthop_num; i++) { nexthop = nexthop_from_zapi_nexthop(&nhr.nexthops[i]); @@ -862,7 +855,8 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) nhlist_tail = nexthop; nhlist_head = nexthop; } - // Only keep track of nexthops which are PIM enabled. + + // Keep track of all nexthops, even PIM-disabled ones. pnc->nexthop_num++; } /* Reset existing pnc->nexthop before assigning new list */ diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index 63ddb85d001b..e1ec9b34a113 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -149,6 +149,31 @@ struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, return c_oil; } + +/* + * Clean up mroute and channel oil created for dropping pkts from directly + * connected source when the interface was non DR. + */ +void pim_clear_nocache_state(struct pim_interface *pim_ifp) +{ + struct channel_oil *c_oil; + + frr_each_safe (rb_pim_oil, &pim_ifp->pim->channel_oil_head, c_oil) { + + if ((!c_oil->up) || + !(PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(c_oil->up->flags))) + continue; + + if (*oil_parent(c_oil) != pim_ifp->mroute_vif_index) + continue; + + EVENT_OFF(c_oil->up->t_ka_timer); + PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(c_oil->up->flags); + PIM_UPSTREAM_FLAG_UNSET_SRC_STREAM(c_oil->up->flags); + pim_upstream_del(pim_ifp->pim, c_oil->up, __func__); + } +} + struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, const char *name) { diff --git a/pimd/pim_oil.h b/pimd/pim_oil.h index 45b337cc720f..dc66eaace454 100644 --- a/pimd/pim_oil.h +++ b/pimd/pim_oil.h @@ -182,6 +182,7 @@ struct channel_oil *pim_find_channel_oil(struct pim_instance *pim, pim_sgaddr *sg); struct channel_oil *pim_channel_oil_add(struct pim_instance *pim, pim_sgaddr *sg, const char *name); +void pim_clear_nocache_state(struct pim_interface *pim_ifp); struct channel_oil *pim_channel_oil_del(struct channel_oil *c_oil, const char *name); diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 1248db3de45f..4a272a4802b7 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -7,7 +7,7 @@ #include <zebra.h> #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "if.h" #include "network.h" @@ -31,7 +31,7 @@ #include "pim_bsm.h" #include <lib/lib_errors.h> -static void on_pim_hello_send(struct thread *t); +static void on_pim_hello_send(struct event *t); static const char *pim_pim_msgtype2str(enum pim_msg_type type) { @@ -70,7 +70,7 @@ static void sock_close(struct interface *ifp) pim_ifp->pim_sock_fd, ifp->name); } } - THREAD_OFF(pim_ifp->t_pim_sock_read); + EVENT_OFF(pim_ifp->t_pim_sock_read); if (PIM_DEBUG_PIM_TRACE) { if (pim_ifp->t_pim_hello_timer) { @@ -79,7 +79,7 @@ static void sock_close(struct interface *ifp) ifp->name); } } - THREAD_OFF(pim_ifp->t_pim_hello_timer); + EVENT_OFF(pim_ifp->t_pim_hello_timer); if (PIM_DEBUG_PIM_TRACE) { zlog_debug("Deleting PIM socket fd=%d on interface %s", @@ -334,7 +334,7 @@ int pim_pim_packet(struct interface *ifp, uint8_t *buf, size_t len, static void pim_sock_read_on(struct interface *ifp); -static void pim_sock_read(struct thread *t) +static void pim_sock_read(struct event *t) { struct interface *ifp, *orig_ifp; struct pim_interface *pim_ifp; @@ -350,8 +350,8 @@ static void pim_sock_read(struct thread *t) static long long count = 0; int cont = 1; - orig_ifp = ifp = THREAD_ARG(t); - fd = THREAD_FD(t); + orig_ifp = ifp = EVENT_ARG(t); + fd = EVENT_FD(t); pim_ifp = ifp->info; @@ -431,8 +431,8 @@ static void pim_sock_read_on(struct interface *ifp) zlog_debug("Scheduling READ event on PIM socket fd=%d", pim_ifp->pim_sock_fd); } - thread_add_read(router->master, pim_sock_read, ifp, - pim_ifp->pim_sock_fd, &pim_ifp->t_pim_sock_read); + event_add_read(router->master, pim_sock_read, ifp, pim_ifp->pim_sock_fd, + &pim_ifp->t_pim_sock_read); } static int pim_sock_open(struct interface *ifp) @@ -821,21 +821,20 @@ static void hello_resched(struct interface *ifp) zlog_debug("Rescheduling %d sec hello on interface %s", pim_ifp->pim_hello_period, ifp->name); } - THREAD_OFF(pim_ifp->t_pim_hello_timer); - thread_add_timer(router->master, on_pim_hello_send, ifp, - pim_ifp->pim_hello_period, - &pim_ifp->t_pim_hello_timer); + EVENT_OFF(pim_ifp->t_pim_hello_timer); + event_add_timer(router->master, on_pim_hello_send, ifp, + pim_ifp->pim_hello_period, &pim_ifp->t_pim_hello_timer); } /* Periodic hello timer */ -static void on_pim_hello_send(struct thread *t) +static void on_pim_hello_send(struct event *t) { struct pim_interface *pim_ifp; struct interface *ifp; - ifp = THREAD_ARG(t); + ifp = EVENT_ARG(t); pim_ifp = ifp->info; /* @@ -924,7 +923,7 @@ void pim_hello_restart_triggered(struct interface *ifp) return; } - THREAD_OFF(pim_ifp->t_pim_hello_timer); + EVENT_OFF(pim_ifp->t_pim_hello_timer); } random_msec = triggered_hello_delay_msec; @@ -935,8 +934,8 @@ void pim_hello_restart_triggered(struct interface *ifp) random_msec, ifp->name); } - thread_add_timer_msec(router->master, on_pim_hello_send, ifp, - random_msec, &pim_ifp->t_pim_hello_timer); + event_add_timer_msec(router->master, on_pim_hello_send, ifp, + random_msec, &pim_ifp->t_pim_hello_timer); } int pim_sock_add(struct interface *ifp) diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 5144fe67b8e6..b3e312e04726 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -9,7 +9,7 @@ #include "log.h" #include "if.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "vty.h" #include "plist.h" @@ -32,7 +32,7 @@ #include "pim_vxlan.h" #include "pim_addr.h" -struct thread *send_test_packet_timer = NULL; +struct event *send_test_packet_timer = NULL; void pim_register_join(struct pim_upstream *up) { @@ -85,7 +85,7 @@ void pim_register_stop_send(struct interface *ifp, pim_sgaddr *sg, pim_addr src, zlog_debug("%s: No pinfo!", __func__); return; } - if (pim_msg_send(pinfo->pim_sock_fd, src, originator, buffer, + if (pim_msg_send(pinfo->pim->reg_sock, src, originator, buffer, b1length + PIM_MSG_REGISTER_STOP_LEN, ifp)) { if (PIM_DEBUG_PIM_TRACE) { zlog_debug( @@ -743,8 +743,9 @@ void pim_reg_del_on_couldreg_fail(struct interface *ifp) && (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); + EVENT_OFF(up->t_rs_timer); up->reg_state = PIM_REG_NOINFO; + PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); } } } diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index e2ee6656d1d9..c7516242f5ca 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -47,9 +47,7 @@ void pim_rp_list_hash_clean(void *data) list_delete(&pnc->rp_list); - hash_clean(pnc->upstream_hash, NULL); - hash_free(pnc->upstream_hash); - pnc->upstream_hash = NULL; + hash_clean_and_free(&pnc->upstream_hash, NULL); if (pnc->nexthop) nexthops_free(pnc->nexthop); diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index 2fc30b491535..dadf29f5353a 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -196,7 +196,7 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss) { assert(ss); - THREAD_OFF(ss->t_sock_read); + EVENT_OFF(ss->t_sock_read); if (close(ss->sock_fd)) { zlog_warn( @@ -286,11 +286,11 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) return 0; } -static void ssmpingd_sock_read(struct thread *t) +static void ssmpingd_sock_read(struct event *t) { struct ssmpingd_sock *ss; - ss = THREAD_ARG(t); + ss = EVENT_ARG(t); ssmpingd_read_msg(ss); @@ -300,8 +300,8 @@ static void ssmpingd_sock_read(struct thread *t) static void ssmpingd_read_on(struct ssmpingd_sock *ss) { - thread_add_read(router->master, ssmpingd_sock_read, ss, ss->sock_fd, - &ss->t_sock_read); + event_add_read(router->master, ssmpingd_sock_read, ss, ss->sock_fd, + &ss->t_sock_read); } static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim, diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h index fe1583979333..71286e4964b1 100644 --- a/pimd/pim_ssmpingd.h +++ b/pimd/pim_ssmpingd.h @@ -17,7 +17,7 @@ struct ssmpingd_sock { struct pim_instance *pim; int sock_fd; /* socket */ - struct thread *t_sock_read; /* thread for reading socket */ + struct event *t_sock_read; /* thread for reading socket */ pim_addr source_addr; /* source address */ int64_t creation; /* timestamp of socket creation */ int64_t requests; /* counter */ diff --git a/pimd/pim_tib.c b/pimd/pim_tib.c index 6ffea868d804..4081786c1e49 100644 --- a/pimd/pim_tib.c +++ b/pimd/pim_tib.c @@ -163,4 +163,6 @@ void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg, per-interface (S,G) state. */ pim_ifchannel_local_membership_del(oif, &sg); + + pim_channel_oil_del(*oilp, __func__); } diff --git a/pimd/pim_time.c b/pimd/pim_time.c index c9555f5310cd..205945e5aee4 100644 --- a/pimd/pim_time.c +++ b/pimd/pim_time.c @@ -11,7 +11,7 @@ #include <time.h> #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "lib_errors.h" #include "pim_time.h" @@ -121,21 +121,21 @@ static int pim_time_hhmmss(char *buf, int buf_size, long sec) return wr != 8; } -void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t_timer) +void pim_time_timer_to_mmss(char *buf, int buf_size, struct event *t_timer) { if (t_timer) { pim_time_mmss(buf, buf_size, - thread_timer_remain_second(t_timer)); + event_timer_remain_second(t_timer)); } else { snprintf(buf, buf_size, "--:--"); } } -void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t_timer) +void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct event *t_timer) { if (t_timer) { pim_time_hhmmss(buf, buf_size, - thread_timer_remain_second(t_timer)); + event_timer_remain_second(t_timer)); } else { snprintf(buf, buf_size, "--:--:--"); } @@ -156,9 +156,9 @@ void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin) snprintf(buf, buf_size, "--:--:--"); } -long pim_time_timer_remain_msec(struct thread *t_timer) +long pim_time_timer_remain_msec(struct event *t_timer) { /* no timer thread running means timer has expired: return 0 */ - return t_timer ? thread_timer_remain_msec(t_timer) : 0; + return t_timer ? event_timer_remain_msec(t_timer) : 0; } diff --git a/pimd/pim_time.h b/pimd/pim_time.h index fd1e79e2bbd2..6c0e07396a6c 100644 --- a/pimd/pim_time.h +++ b/pimd/pim_time.h @@ -10,16 +10,16 @@ #include <stdint.h> #include <zebra.h> -#include "thread.h" +#include "frrevent.h" int64_t pim_time_monotonic_sec(void); int64_t pim_time_monotonic_dsec(void); int64_t pim_time_monotonic_usec(void); int pim_time_mmss(char *buf, int buf_size, long sec); -void pim_time_timer_to_mmss(char *buf, int buf_size, struct thread *t); -void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct thread *t); +void pim_time_timer_to_mmss(char *buf, int buf_size, struct event *t); +void pim_time_timer_to_hhmmss(char *buf, int buf_size, struct event *t); void pim_time_uptime(char *buf, int buf_size, int64_t uptime_sec); void pim_time_uptime_begin(char *buf, int buf_size, int64_t now, int64_t begin); -long pim_time_timer_remain_msec(struct thread *t_timer); +long pim_time_timer_remain_msec(struct event *t_timer); #endif /* PIM_TIME_H */ diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c index b0f11585961f..406f772ffe08 100644 --- a/pimd/pim_upstream.c +++ b/pimd/pim_upstream.c @@ -9,7 +9,7 @@ #include "log.h" #include "zclient.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "linklist.h" #include "vty.h" #include "plist.h" @@ -166,10 +166,10 @@ static void upstream_channel_oil_detach(struct pim_upstream *up) static void pim_upstream_timers_stop(struct pim_upstream *up) { - THREAD_OFF(up->t_ka_timer); - THREAD_OFF(up->t_rs_timer); - THREAD_OFF(up->t_msdp_reg_timer); - THREAD_OFF(up->t_join_timer); + EVENT_OFF(up->t_ka_timer); + EVENT_OFF(up->t_rs_timer); + EVENT_OFF(up->t_msdp_reg_timer); + EVENT_OFF(up->t_join_timer); } struct pim_upstream *pim_upstream_del(struct pim_instance *pim, @@ -181,7 +181,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim, if (PIM_DEBUG_PIM_TRACE) zlog_debug( - "%s(%s): Delete %s[%s] ref count: %d , flags: %d c_oil ref count %d (Pre decrement)", + "%s(%s): Delete %s[%s] ref count: %d, flags: %d c_oil ref count %d (Pre decrement)", __func__, name, up->sg_str, pim->vrf->name, up->ref_count, up->flags, up->channel_oil->oil_ref_count); @@ -289,11 +289,11 @@ void pim_upstream_send_join(struct pim_upstream *up) pim_jp_agg_single_upstream_send(&up->rpf, up, 1 /* join */); } -static void on_join_timer(struct thread *t) +static void on_join_timer(struct event *t) { struct pim_upstream *up; - up = THREAD_ARG(t); + up = EVENT_ARG(t); if (!up->rpf.source_nexthop.interface) { if (PIM_DEBUG_PIM_TRACE) @@ -323,7 +323,7 @@ static void join_timer_stop(struct pim_upstream *up) { struct pim_neighbor *nbr = NULL; - THREAD_OFF(up->t_join_timer); + EVENT_OFF(up->t_join_timer); if (up->rpf.source_nexthop.interface) nbr = pim_neighbor_find(up->rpf.source_nexthop.interface, @@ -353,9 +353,9 @@ void join_timer_start(struct pim_upstream *up) if (nbr) 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, - router->t_periodic, &up->t_join_timer); + EVENT_OFF(up->t_join_timer); + event_add_timer(router->master, on_join_timer, up, + router->t_periodic, &up->t_join_timer); } pim_jp_agg_upstream_verification(up, true); } @@ -370,7 +370,7 @@ void join_timer_start(struct pim_upstream *up) void pim_upstream_join_timer_restart(struct pim_upstream *up, struct pim_rpf *old) { - // THREAD_OFF(up->t_join_timer); + // EVENT_OFF(up->t_join_timer); join_timer_start(up); } @@ -382,9 +382,9 @@ static void pim_upstream_join_timer_restart_msec(struct pim_upstream *up, __func__, interval_msec, up->sg_str); } - THREAD_OFF(up->t_join_timer); - thread_add_timer_msec(router->master, on_join_timer, up, interval_msec, - &up->t_join_timer); + EVENT_OFF(up->t_join_timer); + event_add_timer_msec(router->master, on_join_timer, up, interval_msec, + &up->t_join_timer); } void pim_update_suppress_timers(uint32_t suppress_time) @@ -1361,7 +1361,7 @@ static void pim_upstream_fhr_kat_expiry(struct pim_instance *pim, up->sg_str); /* stop reg-stop timer */ - THREAD_OFF(up->t_rs_timer); + EVENT_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, __func__); @@ -1461,11 +1461,11 @@ struct pim_upstream *pim_upstream_keep_alive_timer_proc( return up; } -static void pim_upstream_keep_alive_timer(struct thread *t) +static void pim_upstream_keep_alive_timer(struct event *t) { struct pim_upstream *up; - up = THREAD_ARG(t); + up = EVENT_ARG(t); /* pull the stats and re-check */ if (pim_upstream_sg_running_proc(up)) @@ -1482,9 +1482,9 @@ void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) zlog_debug("kat start on %s with no stream reference", up->sg_str); } - THREAD_OFF(up->t_ka_timer); - thread_add_timer(router->master, pim_upstream_keep_alive_timer, up, - time, &up->t_ka_timer); + EVENT_OFF(up->t_ka_timer); + event_add_timer(router->master, pim_upstream_keep_alive_timer, up, time, + &up->t_ka_timer); /* any time keepalive is started against a SG we will have to * re-evaluate our active source database */ @@ -1494,9 +1494,9 @@ void pim_upstream_keep_alive_timer_start(struct pim_upstream *up, uint32_t time) } /* MSDP on RP needs to know if a source is registerable to this RP */ -static void pim_upstream_msdp_reg_timer(struct thread *t) +static void pim_upstream_msdp_reg_timer(struct event *t) { - struct pim_upstream *up = THREAD_ARG(t); + struct pim_upstream *up = EVENT_ARG(t); struct pim_instance *pim = up->channel_oil->pim; /* source is no longer active - pull the SA from MSDP's cache */ @@ -1505,9 +1505,9 @@ static void pim_upstream_msdp_reg_timer(struct thread *t) void pim_upstream_msdp_reg_timer_start(struct pim_upstream *up) { - THREAD_OFF(up->t_msdp_reg_timer); - thread_add_timer(router->master, pim_upstream_msdp_reg_timer, up, - PIM_MSDP_REG_RXED_PERIOD, &up->t_msdp_reg_timer); + EVENT_OFF(up->t_msdp_reg_timer); + event_add_timer(router->master, pim_upstream_msdp_reg_timer, up, + PIM_MSDP_REG_RXED_PERIOD, &up->t_msdp_reg_timer); pim_msdp_sa_local_update(up); } @@ -1680,12 +1680,12 @@ const char *pim_reg_state2str(enum pim_reg_state reg_state, char *state_str, return state_str; } -static void pim_upstream_register_stop_timer(struct thread *t) +static void pim_upstream_register_stop_timer(struct event *t) { struct pim_interface *pim_ifp; struct pim_instance *pim; struct pim_upstream *up; - up = THREAD_ARG(t); + up = EVENT_ARG(t); pim = up->channel_oil->pim; if (PIM_DEBUG_PIM_TRACE) { @@ -1713,6 +1713,7 @@ static void pim_upstream_register_stop_timer(struct thread *t) zlog_debug("%s: up %s RPF is not present", __func__, up->sg_str); up->reg_state = PIM_REG_NOINFO; + PIM_UPSTREAM_FLAG_UNSET_FHR(up->flags); return; } @@ -1749,7 +1750,7 @@ void pim_upstream_start_register_stop_timer(struct pim_upstream *up, { uint32_t time; - THREAD_OFF(up->t_rs_timer); + EVENT_OFF(up->t_rs_timer); if (!null_register) { uint32_t lower = (0.5 * router->register_suppress_time); @@ -1768,8 +1769,8 @@ void pim_upstream_start_register_stop_timer(struct pim_upstream *up, "%s: (S,G)=%s Starting upstream register stop timer %d", __func__, up->sg_str, time); } - thread_add_timer(router->master, pim_upstream_register_stop_timer, up, - time, &up->t_rs_timer); + event_add_timer(router->master, pim_upstream_register_stop_timer, up, + time, &up->t_rs_timer); } int pim_upstream_inherited_olist_decide(struct pim_instance *pim, @@ -2056,7 +2057,7 @@ static void pim_upstream_sg_running(void *arg) // 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", + zlog_debug("%s: %s[%s] is not installed in mroute", __func__, up->sg_str, pim->vrf->name); return; } diff --git a/pimd/pim_upstream.h b/pimd/pim_upstream.h index 890ba7d169bf..4e0926e29483 100644 --- a/pimd/pim_upstream.h +++ b/pimd/pim_upstream.h @@ -237,19 +237,19 @@ struct pim_upstream { struct pim_up_mlag mlag; - struct thread *t_join_timer; + struct event *t_join_timer; /* * RST(S,G) */ - struct thread *t_rs_timer; + struct event *t_rs_timer; #define PIM_REGISTER_SUPPRESSION_PERIOD (60) #define PIM_REGISTER_PROBE_PERIOD (5) /* * KAT(S,G) */ - struct thread *t_ka_timer; + struct event *t_ka_timer; #define PIM_KEEPALIVE_PERIOD (210) #define PIM_RP_KEEPALIVE_PERIOD \ (3 * router->register_suppress_time + router->register_probe_time) @@ -257,7 +257,7 @@ struct pim_upstream { /* on the RP we restart a timer to indicate if registers are being rxed * for * SG. This is needed by MSDP to determine its local SA cache */ - struct thread *t_msdp_reg_timer; + struct event *t_msdp_reg_timer; #define PIM_MSDP_REG_RXED_PERIOD (3 * (1.5 * router->register_suppress_time)) int64_t state_transition; /* Record current state uptime */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c index 948b615f71a3..657e84ae50aa 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -159,7 +159,7 @@ int pim_get_all_mcast_group(struct prefix *prefix) bool pim_addr_is_multicast(pim_addr addr) { #if PIM_IPV == 4 - if (IN_MULTICAST(addr.s_addr)) + if (IN_MULTICAST(ntohl(addr.s_addr))) return true; #else if (IN6_IS_ADDR_MULTICAST(&addr)) diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index 91628930f4f6..0f6547ee2e9a 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -335,18 +335,12 @@ static int gm_config_write(struct vty *vty, int writes, struct listnode *node; struct gm_join *ij; for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) { - char group_str[INET_ADDRSTRLEN]; - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<grp?>", ij->group_addr, group_str, - sizeof(group_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); - } + if (pim_addr_is_any(ij->source_addr)) + vty_out(vty, " ip igmp join %pPAs\n", + &ij->group_addr); + else + vty_out(vty, " ip igmp join %pPAs %pPAs\n", + &ij->group_addr, &ij->source_addr); ++writes; } } @@ -388,6 +382,21 @@ static int gm_config_write(struct vty *vty, int writes, vty_out(vty, " ipv6 mld last-member-query-interval %d\n", pim_ifp->gm_specific_query_max_response_time_dsec); + /* IF ipv6 mld join */ + if (pim_ifp->gm_join_list) { + struct listnode *node; + struct gm_join *ij; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_join_list, node, ij)) { + if (pim_addr_is_any(ij->source_addr)) + vty_out(vty, " ipv6 mld join %pPAs\n", + &ij->group_addr); + else + vty_out(vty, " ipv6 mld join %pPAs %pPAs\n", + &ij->group_addr, &ij->source_addr); + ++writes; + } + } + return writes; } #endif diff --git a/pimd/pim_vxlan.c b/pimd/pim_vxlan.c index 4c317a25464a..8df3c90f006c 100644 --- a/pimd/pim_vxlan.c +++ b/pimd/pim_vxlan.c @@ -169,7 +169,7 @@ void pim_vxlan_update_sg_reg_state(struct pim_instance *pim, pim_vxlan_del_work(vxlan_sg); } -static void pim_vxlan_work_timer_cb(struct thread *t) +static void pim_vxlan_work_timer_cb(struct event *t) { pim_vxlan_do_reg_work(); pim_vxlan_work_timer_setup(true /* start */); @@ -178,10 +178,10 @@ static void pim_vxlan_work_timer_cb(struct thread *t) /* global 1second timer used for periodic processing */ static void pim_vxlan_work_timer_setup(bool start) { - THREAD_OFF(vxlan_info.work_timer); + EVENT_OFF(vxlan_info.work_timer); if (start) - thread_add_timer(router->master, pim_vxlan_work_timer_cb, NULL, - PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer); + event_add_timer(router->master, pim_vxlan_work_timer_cb, NULL, + PIM_VXLAN_WORK_TIME, &vxlan_info.work_timer); } /**************************** vxlan origination mroutes *********************** @@ -225,7 +225,7 @@ static void pim_vxlan_orig_mr_up_del(struct pim_vxlan_sg *vxlan_sg) * if there are no other references. */ if (PIM_UPSTREAM_FLAG_TEST_SRC_STREAM(up->flags)) { - THREAD_OFF(up->t_ka_timer); + EVENT_OFF(up->t_ka_timer); up = pim_upstream_keep_alive_timer_proc(up); } else { /* this is really unexpected as we force vxlan @@ -1172,12 +1172,8 @@ 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, - (void (*)(void *))pim_vxlan_sg_del_item); - hash_free(pim->vxlan.sg_hash); - pim->vxlan.sg_hash = NULL; - } + hash_clean_and_free(&pim->vxlan.sg_hash, + (void (*)(void *))pim_vxlan_sg_del_item); } void pim_vxlan_terminate(void) diff --git a/pimd/pim_vxlan.h b/pimd/pim_vxlan.h index 6e1e066f07bc..9a135ca6b833 100644 --- a/pimd/pim_vxlan.h +++ b/pimd/pim_vxlan.h @@ -75,7 +75,7 @@ enum pim_vxlan_flags { struct pim_vxlan { enum pim_vxlan_flags flags; - struct thread *work_timer; + struct event *work_timer; struct list *work_list; struct listnode *next_work; int max_work_cnt; diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 29aac7f1c77d..92dcbf9d1d5b 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -55,6 +55,8 @@ static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; vrf_id_t new_vrf_id; + struct pim_instance *pim; + struct pim_interface *pim_ifp; ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, &new_vrf_id); @@ -65,8 +67,20 @@ static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) zlog_debug("%s: %s updating from %u to %u", __func__, ifp->name, vrf_id, new_vrf_id); + pim = pim_get_pim_instance(new_vrf_id); + if (!pim) + return 0; + if_update_to_new_vrf(ifp, new_vrf_id); + pim_ifp = ifp->info; + if (!pim_ifp) + return 0; + + pim_ifp->pim->mcast_if_count--; + pim_ifp->pim = pim; + pim_ifp->pim->mcast_if_count++; + return 0; } @@ -385,9 +399,9 @@ void pim_scan_oil(struct pim_instance *pim) pim_upstream_mroute_iif_update(c_oil, __func__); } -static void on_rpf_cache_refresh(struct thread *t) +static void on_rpf_cache_refresh(struct event *t) { - struct pim_instance *pim = THREAD_ARG(t); + struct pim_instance *pim = EVENT_ARG(t); /* update kernel multicast forwarding cache (MFC) */ pim_scan_oil(pim); @@ -417,9 +431,9 @@ void sched_rpf_cache_refresh(struct pim_instance *pim) router->rpf_cache_refresh_delay_msec); } - thread_add_timer_msec(router->master, on_rpf_cache_refresh, pim, - router->rpf_cache_refresh_delay_msec, - &pim->rpf_cache_refresher); + event_add_timer_msec(router->master, on_rpf_cache_refresh, pim, + router->rpf_cache_refresh_delay_msec, + &pim->rpf_cache_refresher); } static void pim_zebra_connected(struct zclient *zclient) diff --git a/pimd/pim_zlookup.c b/pimd/pim_zlookup.c index 08807d0dcc99..05a72269d664 100644 --- a/pimd/pim_zlookup.c +++ b/pimd/pim_zlookup.c @@ -11,7 +11,7 @@ #include "zclient.h" #include "stream.h" #include "network.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "vty.h" #include "lib_errors.h" @@ -27,17 +27,17 @@ #include "pim_addr.h" static struct zclient *zlookup = NULL; -struct thread *zlookup_read; +struct event *zlookup_read; static void zclient_lookup_sched(struct zclient *zlookup, int delay); -static void zclient_lookup_read_pipe(struct thread *thread); +static void zclient_lookup_read_pipe(struct event *thread); /* Connect to zebra for nexthop lookup. */ -static void zclient_lookup_connect(struct thread *t) +static void zclient_lookup_connect(struct event *t) { struct zclient *zlookup; - zlookup = THREAD_ARG(t); + zlookup = EVENT_ARG(t); if (zlookup->sock >= 0) { return; @@ -65,15 +65,15 @@ static void zclient_lookup_connect(struct thread *t) return; } - thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, - &zlookup_read); + event_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, + &zlookup_read); } /* Schedule connection with delay. */ static void zclient_lookup_sched(struct zclient *zlookup, int delay) { - thread_add_timer(router->master, zclient_lookup_connect, zlookup, delay, - &zlookup->t_connect); + event_add_timer(router->master, zclient_lookup_connect, zlookup, delay, + &zlookup->t_connect); zlog_notice("%s: zclient lookup connection scheduled for %d seconds", __func__, delay); @@ -82,8 +82,8 @@ static void zclient_lookup_sched(struct zclient *zlookup, int delay) /* Schedule connection for now. */ static void zclient_lookup_sched_now(struct zclient *zlookup) { - thread_add_event(router->master, zclient_lookup_connect, zlookup, 0, - &zlookup->t_connect); + event_add_event(router->master, zclient_lookup_connect, zlookup, 0, + &zlookup->t_connect); zlog_notice("%s: zclient lookup immediate connection scheduled", __func__); @@ -114,7 +114,7 @@ static void zclient_lookup_failed(struct zclient *zlookup) void zclient_lookup_free(void) { - THREAD_OFF(zlookup_read); + EVENT_OFF(zlookup_read); zclient_stop(zlookup); zclient_free(zlookup); zlookup = NULL; @@ -364,9 +364,9 @@ static int zclient_lookup_nexthop_once(struct pim_instance *pim, return zclient_read_nexthop(pim, zlookup, nexthop_tab, tab_size, addr); } -void zclient_lookup_read_pipe(struct thread *thread) +void zclient_lookup_read_pipe(struct event *thread) { - struct zclient *zlookup = THREAD_ARG(thread); + struct zclient *zlookup = EVENT_ARG(thread); struct pim_instance *pim = pim_get_pim_instance(VRF_DEFAULT); struct pim_zlookup_nexthop nexthop_tab[10]; pim_addr l = PIMADDR_ANY; @@ -378,8 +378,8 @@ void zclient_lookup_read_pipe(struct thread *thread) } zclient_lookup_nexthop_once(pim, nexthop_tab, 10, l); - thread_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, - &zlookup_read); + event_add_timer(router->master, zclient_lookup_read_pipe, zlookup, 60, + &zlookup_read); } int zclient_lookup_nexthop(struct pim_instance *pim, diff --git a/pimd/pim_zpthread.c b/pimd/pim_zpthread.c index bc515f27ed06..d6b2621ff4b6 100644 --- a/pimd/pim_zpthread.c +++ b/pimd/pim_zpthread.c @@ -133,7 +133,7 @@ static void pim_mlag_zebra_check_for_buffer_flush(uint32_t curr_msg_type, * Thsi thread reads the clients data from the Gloabl queue and encodes with * protobuf and pass on to the MLAG socket. */ -static void pim_mlag_zthread_handler(struct thread *event) +static void pim_mlag_zthread_handler(struct event *event) { struct stream *read_s; uint32_t wr_count = 0; @@ -209,8 +209,8 @@ int pim_mlag_signal_zpthread(void) 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); + event_add_event(router->master, pim_mlag_zthread_handler, NULL, + 0, &router->zpthread_mlag_write); } return (0); } diff --git a/pimd/subdir.am b/pimd/subdir.am index fd7255cb8709..9a7901ec3fbf 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -169,6 +169,7 @@ pimd_pim6d_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=6 pimd_pim6d_LDADD = lib/libfrr.la $(LIBCAP) endif +pimd_test_igmpv3_join_CFLAGS = $(AM_CFLAGS) -DPIM_IPV=4 pimd_test_igmpv3_join_LDADD = lib/libfrr.la pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c index 07070900d2fe..926e453c9db3 100644 --- a/pimd/test_igmpv3_join.c +++ b/pimd/test_igmpv3_join.c @@ -54,8 +54,8 @@ static int iface_solve_index(const char *ifname) int main(int argc, const char *argv[]) { - struct in_addr group_addr; - struct in_addr source_addr; + pim_addr group_addr; + pim_addr source_addr; const char *ifname; const char *group; const char *source; @@ -106,7 +106,7 @@ int main(int argc, const char *argv[]) exit(1); } - result = pim_igmp_join_source(fd, ifindex, group_addr, source_addr); + result = pim_gm_join_source(fd, ifindex, group_addr, source_addr); if (result) { fprintf(stderr, "%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s\n", diff --git a/pkgsrc/mgmtd.sh.in b/pkgsrc/mgmtd.sh.in new file mode 100644 index 000000000000..fb57c0a2fbda --- /dev/null +++ b/pkgsrc/mgmtd.sh.in @@ -0,0 +1,44 @@ +#!/bin/sh +# +# mgmtd is part of the quagga routing beast +# +# PROVIDE: mgmtd +# REQUIRE: none +## + +PATH=/sbin:/bin:/usr/sbin:/usr/bin:@prefix@/sbin:@prefix@/bin +export PATH + +if [ -f /etc/rc.subr ] +then + . /etc/rc.subr +fi + +name="mgmtd" +rcvar=$name +required_files="@sysconfdir@/${name}.conf" +command="@prefix@/sbin/${name}" +command_args="-d" + +start_precmd="zebra_precmd" +socket_dir=@localstatedir@ +pidfile="${socket_dir}/${name}.pid" + +zebra_precmd() +{ + rc_flags="$( + set -- $rc_flags + while [ $# -ne 0 ]; do + if [ X"$1" = X-P -o X"$1" = X-A ]; then + break + fi + shift + done + if [ $# -eq 0 ]; then + echo "-P 0" + fi + ) $rc_flags" +} + +load_rc_config $name +run_rc_command "$1" diff --git a/python/xref2vtysh.py b/python/xref2vtysh.py index a4f8560bb07f..6dd5c8866ed4 100644 --- a/python/xref2vtysh.py +++ b/python/xref2vtysh.py @@ -37,6 +37,8 @@ "lib/filter_cli.c": "VTYSH_ACL", "lib/if.c": "VTYSH_INTERFACE", "lib/keychain.c": "VTYSH_RIPD|VTYSH_EIGRPD|VTYSH_OSPF6D", + "lib/mgmt_be_client.c": "VTYSH_STATICD", + "lib/mgmt_fe_client.c": "VTYSH_MGMTD", "lib/lib_vty.c": "VTYSH_ALL", "lib/log_vty.c": "VTYSH_ALL", "lib/nexthop_group.c": "VTYSH_NH_GROUP", @@ -44,7 +46,7 @@ "lib/routemap.c": "VTYSH_RMAP", "lib/routemap_cli.c": "VTYSH_RMAP", "lib/spf_backoff.c": "VTYSH_ISISD", - "lib/thread.c": "VTYSH_ALL", + "lib/event.c": "VTYSH_ALL", "lib/vrf.c": "VTYSH_VRF", "lib/vty.c": "VTYSH_ALL", } @@ -325,7 +327,17 @@ def process(cls, nodes, name, origin, spec): def load(cls, xref): nodes = NodeDict() + mgmtname = "mgmtd/libmgmt_be_nb.la" for cmd_name, origins in xref.get("cli", {}).items(): + # If mgmtd has a yang version of a CLI command, make it the only daemon + # to handle it. For now, daemons can still be compiling their cmds into the + # binaries to allow for running standalone with CLI config files. When they + # do this they will also be present in the xref file, but we want to ignore + # those in vtysh. + if "yang" in origins.get(mgmtname, {}).get("attrs", []): + CommandEntry.process(nodes, cmd_name, mgmtname, origins[mgmtname]) + continue + for origin, spec in origins.items(): CommandEntry.process(nodes, cmd_name, origin, spec) return nodes diff --git a/python/xrelfo.py b/python/xrelfo.py index 4c956ca6ac67..a40b19e5fb52 100644 --- a/python/xrelfo.py +++ b/python/xrelfo.py @@ -40,7 +40,7 @@ # constants, need to be kept in sync manually... -XREFT_THREADSCHED = 0x100 +XREFT_EVENTSCHED = 0x100 XREFT_LOGMSG = 0x200 XREFT_DEFUN = 0x300 XREFT_INSTALL_ELEMENT = 0x301 @@ -110,7 +110,7 @@ class XrefThreadSched(ELFDissectStruct, XrelfoJson): struct = "xref_threadsched" -Xref.containers[XREFT_THREADSCHED] = XrefThreadSched +Xref.containers[XREFT_EVENTSCHED] = XrefThreadSched class XrefLogmsg(ELFDissectStruct, XrelfoJson): diff --git a/qpb/subdir.am b/qpb/subdir.am index e897822ecc12..21aa84df5c21 100644 --- a/qpb/subdir.am +++ b/qpb/subdir.am @@ -29,23 +29,3 @@ CLEANFILES += \ # end EXTRA_DIST += qpb/qpb.proto -SUFFIXES += .proto .pb-c.c .pb-c.h - -if HAVE_PROTOBUF - -# Rules -.proto.pb.h: - $(PROTOC) -I$(top_srcdir) --cpp_out=$(top_builddir) $^ - -AM_V_PROTOC_C = $(am__v_PROTOC_C_$(V)) -am__v_PROTOC_C_ = $(am__v_PROTOC_C_$(AM_DEFAULT_VERBOSITY)) -am__v_PROTOC_C_0 = @echo " PROTOC_C" $@; -am__v_PROTOC_C_1 = - -.proto.pb-c.c: - $(AM_V_PROTOC_C)$(PROTOC_C) -I$(top_srcdir) --c_out=$(top_builddir) $^ - $(AM_V_GEN)$(SED) -e '1i#include "config.h"' -i $@ -.pb-c.c.pb-c.h: - @/bin/true - -endif # HAVE_PROTOBUF diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 14973ba890e4..5eb0853ed778 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -184,7 +184,7 @@ BuildRequires: make BuildRequires: ncurses-devel BuildRequires: readline-devel BuildRequires: texinfo -BuildRequires: libyang2-devel +BuildRequires: libyang-devel >= 2 %if 0%{?rhel} && 0%{?rhel} < 7 #python27-devel is available from ius community repo for RedHat/CentOS 6 BuildRequires: python27-devel @@ -667,6 +667,7 @@ fi %{_sbindir}/ospfd %{_sbindir}/ripd %{_sbindir}/bgpd +%{_sbindir}/mgmtd %exclude %{_sbindir}/ssd %if %{with_watchfrr} %{_sbindir}/watchfrr @@ -716,6 +717,9 @@ fi %{_libdir}/frr/modules/dplane_fpm_nl.so %{_libdir}/frr/modules/zebra_irdp.so %{_libdir}/frr/modules/bgpd_bmp.so +%{_libdir}/libfrr_pb.so* +%{_libdir}/libfrrfpm_pb.so* +%{_libdir}/libmgmt_be_nb.so* %{_bindir}/* %config(noreplace) %{configdir}/[!v]*.conf* %config(noreplace) %attr(750,%{frr_user},%{frr_user}) %{configdir}/daemons @@ -775,6 +779,8 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons %{_libdir}/lib*.so %dir %{_includedir}/%{name} %{_includedir}/%{name}/*.h +%dir %{_includedir}/%{name}/mgmtd +%{_includedir}/%{name}/mgmtd/*.h %dir %{_includedir}/%{name}/ospfd %{_includedir}/%{name}/ospfd/*.h %if %{with_bfdd} @@ -793,9 +799,33 @@ sed -i 's/ -M rpki//' %{_sysconfdir}/frr/daemons %changelog -* Tue Feb 07 2023 Martin Winter <mwinter@opensourcerouting.org> - %{version} - -* Tue Feb 07 2023 Donatas Abraitis <donatas@opensourcerouting.org> - 8.5 +* Tue Jun 06 2023 Martin Winter <mwinter@opensourcerouting.org> - %{version} + +* Tue Jun 06 2023 Jafar Al-Gharaibeh <jafar@atcorp.com> - 9.0 + +* Fri Mar 10 2023 Jafar Al-Gharaibeh <jafar@atcorp.com> - 8.5 +- Major Highlights: +- Add support for per-VRF SRv6 SID +- Add BGP labeled-unicast Add-Path functionality +- Implementation of SNMP BGP4v2-MIB (IPv6 support) for better network management and monitoring +- Add BGP new command neighbor path-attribute discard +- Add BGP new command neighbor path-attribute treat-as-withdraw +- Implement L3 route-target auto/wildcard configuration +- Implement BGP ACCEPT_OWN Community Attribute (rfc7611) +- Implement The Accumulated IGP Metric Attribute for BGP (rfc7311) +- Implement graceful-shutdown command per neighbor +- Add BGP new command to configure TCP keepalives for a peer bgp tcp-keepalive +- Traffic control (TC) ZAPI implementation +- SRv6 uSID (microSID) implementation +- Start deprecating start-shell, ssh, and telnet commands due to security reasons +- Add VRRPv3 an ability to disable IPv4 pseudo-header checksum +- BFD integration for static routes +- Allow protocols to configure BFD sessions with automatic source selection +- Allow zero-length opaque LSAs for OSPF (rfc5250) +- Add ISIS new command set-overload-bit on-startup +- PIMv6 BSM support +- For a full list of new features and bug fixes, please refer to: +- https://frrouting.org/release/ * Tue Nov 01 2022 Jafar Al-Gharaibeh <jafar@atcorp.com> - 8.4 - New BGP command (neighbor PEER soo) to configure SoO to prevent routing loops and suboptimal routing on dual-homed sites. diff --git a/ripd/rip_bfd.c b/ripd/rip_bfd.c new file mode 100644 index 000000000000..b59db11a30a7 --- /dev/null +++ b/ripd/rip_bfd.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RIP BFD integration. + * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF") + */ + +#include <zebra.h> + +#include "lib/zclient.h" +#include "lib/bfd.h" + +#include "ripd/ripd.h" +#include "ripd/rip_bfd.h" +#include "ripd/rip_debug.h" + +DEFINE_MTYPE(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name"); + +extern struct zclient *zclient; + +static const char *rip_bfd_interface_profile(struct rip_interface *ri) +{ + struct rip *rip = ri->rip; + + if (ri->bfd.profile) + return ri->bfd.profile; + + if (rip->default_bfd_profile) + return rip->default_bfd_profile; + + return NULL; +} + +static void rip_bfd_session_change(struct bfd_session_params *bsp, + const struct bfd_session_status *bss, + void *arg) +{ + struct rip_peer *rp = arg; + + /* BFD peer went down. */ + if (bss->state == BFD_STATUS_DOWN && + bss->previous_state == BFD_STATUS_UP) { + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: peer %pI4: BFD Down", __func__, + &rp->addr); + + rip_peer_delete_routes(rp); + listnode_delete(rp->rip->peer_list, rp); + rip_peer_free(rp); + return; + } + + /* BFD peer went up. */ + if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN) + if (IS_RIP_DEBUG_EVENT) + zlog_debug("%s: peer %pI4: BFD Up", __func__, + &rp->addr); +} + +void rip_bfd_session_update(struct rip_peer *rp) +{ + struct rip_interface *ri = rp->ri; + + /* BFD configuration was removed. */ + if (ri == NULL || !ri->bfd.enabled) { + bfd_sess_free(&rp->bfd_session); + return; + } + + /* New BFD session. */ + if (rp->bfd_session == NULL) { + rp->bfd_session = bfd_sess_new(rip_bfd_session_change, rp); + bfd_sess_set_ipv4_addrs(rp->bfd_session, NULL, &rp->addr); + bfd_sess_set_interface(rp->bfd_session, ri->ifp->name); + bfd_sess_set_vrf(rp->bfd_session, rp->rip->vrf->vrf_id); + } + + /* Set new configuration. */ + bfd_sess_set_timers(rp->bfd_session, BFD_DEF_DETECT_MULT, + BFD_DEF_MIN_RX, BFD_DEF_MIN_TX); + bfd_sess_set_profile(rp->bfd_session, rip_bfd_interface_profile(ri)); + + bfd_sess_install(rp->bfd_session); +} + +void rip_bfd_interface_update(struct rip_interface *ri) +{ + struct rip *rip; + struct rip_peer *rp; + struct listnode *node; + + rip = ri->rip; + if (!rip) + return; + + for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, rp)) { + if (rp->ri != ri) + continue; + + rip_bfd_session_update(rp); + } +} + +void rip_bfd_instance_update(struct rip *rip) +{ + struct interface *ifp; + + FOR_ALL_INTERFACES (rip->vrf, ifp) { + struct rip_interface *ri; + + ri = ifp->info; + if (!ri) + continue; + + rip_bfd_interface_update(ri); + } +} + +void rip_bfd_init(struct event_loop *tm) +{ + bfd_protocol_integration_init(zclient, tm); +} diff --git a/ripd/rip_bfd.h b/ripd/rip_bfd.h new file mode 100644 index 000000000000..7621498b15a0 --- /dev/null +++ b/ripd/rip_bfd.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * RIP BFD integration. + * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF") + */ + +#ifndef _RIP_BFD_ +#define _RIP_BFD_ + +#include "frrevent.h" + +DECLARE_MTYPE(RIP_BFD_PROFILE); + +struct rip; +struct rip_interface; +struct rip_peer; + +void rip_bfd_session_update(struct rip_peer *rp); +void rip_bfd_interface_update(struct rip_interface *ri); +void rip_bfd_instance_update(struct rip *rip); +void rip_bfd_init(struct event_loop *tm); + +#endif /* _RIP_BFD_ */ diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c index cac29c00d458..097c708ab1c7 100644 --- a/ripd/rip_cli.c +++ b/ripd/rip_cli.c @@ -85,14 +85,33 @@ void cli_show_router_rip(struct vty *vty, const struct lyd_node *dnode, /* * XPath: /frr-ripd:ripd/instance/allow-ecmp */ -DEFPY_YANG (rip_allow_ecmp, +DEFUN_YANG (rip_allow_ecmp, rip_allow_ecmp_cmd, - "[no] allow-ecmp", + "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", + "Allow Equal Cost MultiPath\n" + "Number of paths\n") +{ + int idx_number = 0; + char mpaths[3] = {}; + uint32_t paths = MULTIPATH_NUM; + + if (argv_find(argv, argc, CMD_RANGE_STR(1, MULTIPATH_NUM), &idx_number)) + paths = strtol(argv[idx_number]->arg, NULL, 10); + snprintf(mpaths, sizeof(mpaths), "%u", paths); + + nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_rip_allow_ecmp, + no_rip_allow_ecmp_cmd, + "no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", NO_STR - "Allow Equal Cost MultiPath\n") + "Allow Equal Cost MultiPath\n" + "Number of paths\n") { - nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, - no ? "false" : "true"); + nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0); return nb_cli_apply_changes(vty, NULL); } @@ -100,10 +119,14 @@ DEFPY_YANG (rip_allow_ecmp, void cli_show_rip_allow_ecmp(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - if (!yang_dnode_get_bool(dnode, NULL)) - vty_out(vty, " no"); + uint8_t paths; - vty_out(vty, " allow-ecmp\n"); + paths = yang_dnode_get_uint8(dnode, NULL); + + if (!paths) + vty_out(vty, " no allow-ecmp\n"); + else + vty_out(vty, " allow-ecmp %d\n", paths); } /* @@ -581,6 +604,42 @@ void cli_show_rip_version(struct vty *vty, const struct lyd_node *dnode, } } +/* + * XPath: /frr-ripd:ripd/instance/default-bfd-profile + */ +DEFPY_YANG(rip_bfd_default_profile, rip_bfd_default_profile_cmd, + "bfd default-profile BFDPROF$profile", + "Bidirectional Forwarding Detection\n" + "BFD default profile\n" + "Profile name\n") +{ + nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_MODIFY, + profile); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFPY_YANG(no_rip_bfd_default_profile, no_rip_bfd_default_profile_cmd, + "no bfd default-profile [BFDPROF]", + NO_STR + "Bidirectional Forwarding Detection\n" + "BFD default profile\n" + "Profile name\n") +{ + nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, NULL); +} + +void cli_show_ripd_instance_default_bfd_profile(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " bfd default-profile %s\n", + yang_dnode_get_string(dnode, NULL)); +} + /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon */ @@ -979,6 +1038,66 @@ void cli_show_ip_rip_authentication_key_chain(struct vty *vty, yang_dnode_get_string(dnode, NULL)); } +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable + */ +DEFPY_YANG(ip_rip_bfd, ip_rip_bfd_cmd, "[no] ip rip bfd", + NO_STR IP_STR + "Routing Information Protocol\n" + "Enable BFD support\n") +{ + nb_cli_enqueue_change(vty, "./bfd-monitoring/enable", NB_OP_MODIFY, + no ? "false" : "true"); + + return nb_cli_apply_changes(vty, "./frr-ripd:rip"); +} + +void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " ip rip bfd\n"); +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd/profile + */ +DEFPY_YANG(ip_rip_bfd_profile, ip_rip_bfd_profile_cmd, + "[no] ip rip bfd profile BFDPROF$profile", + NO_STR IP_STR + "Routing Information Protocol\n" + "Enable BFD support\n" + "Use a pre-configured profile\n" + "Profile name\n") +{ + if (no) + nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", + NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", + NB_OP_MODIFY, profile); + + return nb_cli_apply_changes(vty, "./frr-ripd:rip"); +} + +DEFPY_YANG(no_ip_rip_bfd_profile, no_ip_rip_bfd_profile_cmd, + "no ip rip bfd profile", + NO_STR IP_STR + "Routing Information Protocol\n" + "Enable BFD support\n" + "Use a pre-configured profile\n") +{ + nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", NB_OP_DESTROY, + NULL); + return nb_cli_apply_changes(vty, "./frr-ripd:rip"); +} + +void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults) +{ + vty_out(vty, " ip rip bfd profile %s\n", + yang_dnode_get_string(dnode, NULL)); +} + /* * XPath: /frr-ripd:clear-rip-route */ @@ -1060,6 +1179,7 @@ void rip_cli_init(void) install_element(RIP_NODE, &rip_no_distribute_list_cmd); install_element(RIP_NODE, &rip_allow_ecmp_cmd); + install_element(RIP_NODE, &no_rip_allow_ecmp_cmd); install_element(RIP_NODE, &rip_default_information_originate_cmd); install_element(RIP_NODE, &rip_default_metric_cmd); install_element(RIP_NODE, &no_rip_default_metric_cmd); @@ -1078,6 +1198,8 @@ void rip_cli_init(void) install_element(RIP_NODE, &no_rip_timers_cmd); install_element(RIP_NODE, &rip_version_cmd); install_element(RIP_NODE, &no_rip_version_cmd); + install_element(RIP_NODE, &rip_bfd_default_profile_cmd); + install_element(RIP_NODE, &no_rip_bfd_default_profile_cmd); install_element(INTERFACE_NODE, &ip_rip_split_horizon_cmd); install_element(INTERFACE_NODE, &ip_rip_v2_broadcast_cmd); @@ -1092,6 +1214,9 @@ void rip_cli_init(void) install_element(INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd); install_element(INTERFACE_NODE, &no_ip_rip_authentication_key_chain_cmd); + install_element(INTERFACE_NODE, &ip_rip_bfd_cmd); + install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd); + install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd); install_element(ENABLE_NODE, &clear_ip_rip_cmd); } diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c index 3e62321725d0..b58015a67d89 100644 --- a/ripd/rip_interface.c +++ b/ripd/rip_interface.c @@ -14,7 +14,7 @@ #include "table.h" #include "log.h" #include "stream.h" -#include "thread.h" +#include "frrevent.h" #include "zclient.h" #include "filter.h" #include "sockopt.h" @@ -25,6 +25,7 @@ #include "zebra/connected.h" #include "ripd/ripd.h" +#include "ripd/rip_bfd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" @@ -413,7 +414,7 @@ static void rip_interface_clean(struct rip_interface *ri) ri->enable_interface = 0; ri->running = 0; - THREAD_OFF(ri->t_wakeup); + EVENT_OFF(ri->t_wakeup); } void rip_interfaces_clean(struct rip *rip) @@ -457,6 +458,7 @@ static void rip_interface_reset(struct rip_interface *ri) ri->sent_updates = 0; ri->passive = 0; + XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile); rip_interface_clean(ri); } @@ -472,7 +474,7 @@ int rip_if_down(struct interface *ifp) ri = ifp->info; - THREAD_OFF(ri->t_wakeup); + EVENT_OFF(ri->t_wakeup); rip = ri->rip; if (rip) { @@ -774,13 +776,13 @@ int rip_enable_if_delete(struct rip *rip, const char *ifname) } /* Join to multicast group and send request to the interface. */ -static void rip_interface_wakeup(struct thread *t) +static void rip_interface_wakeup(struct event *t) { struct interface *ifp; struct rip_interface *ri; /* Get interface. */ - ifp = THREAD_ARG(t); + ifp = EVENT_ARG(t); ri = ifp->info; @@ -885,8 +887,8 @@ void rip_enable_apply(struct interface *ifp) zlog_debug("turn on %s", ifp->name); /* Add interface wake up thread. */ - thread_add_timer(master, rip_interface_wakeup, ifp, 1, - &ri->t_wakeup); + event_add_timer(master, rip_interface_wakeup, ifp, 1, + &ri->t_wakeup); rip_connect_set(ifp, 1); } else if (ri->running) { /* Might as well clean up the route table as well @@ -1109,8 +1111,10 @@ void rip_interface_sync(struct interface *ifp) struct rip_interface *ri; ri = ifp->info; - if (ri) + if (ri) { ri->rip = ifp->vrf->info; + ri->ifp = ifp; + } } /* Called when interface structure allocated. */ diff --git a/ripd/rip_main.c b/ripd/rip_main.c index e26424adeb63..ac358ebbaf58 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -7,7 +7,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "memory.h" #include "prefix.h" @@ -21,8 +21,10 @@ #include "if_rmap.h" #include "libfrr.h" #include "routemap.h" +#include "bfd.h" #include "ripd/ripd.h" +#include "ripd/rip_bfd.h" #include "ripd/rip_nb.h" #include "ripd/rip_errors.h" @@ -32,6 +34,8 @@ static struct option longopts[] = {{0}}; /* ripd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; +uint32_t zebra_ecmp_count = MULTIPATH_NUM; + struct zebra_privs_t ripd_privs = { #if defined(FRR_USER) .user = FRR_USER, @@ -47,7 +51,7 @@ struct zebra_privs_t ripd_privs = { .cap_num_i = 0}; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; static struct frr_daemon_info ripd_di; @@ -65,6 +69,7 @@ static void sigint(void) { zlog_notice("Terminating on signal"); + bfd_protocol_integration_set_shutdown(true); rip_vrf_terminate(); if_rmap_terminate(); rip_zclient_stop(); @@ -162,6 +167,7 @@ int main(int argc, char **argv) rip_if_init(); rip_cli_init(); rip_zclient_init(master); + rip_bfd_init(master); frr_config_fork(); frr_run(master); diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c index 9947c01af56b..d11f1e1d34fe 100644 --- a/ripd/rip_nb.c +++ b/ripd/rip_nb.c @@ -10,6 +10,7 @@ #include "libfrr.h" #include "ripd/rip_nb.h" +#include "lib/if_rmap.h" /* clang-format off */ const struct frr_yang_module_info frr_ripd_info = { @@ -165,6 +166,28 @@ const struct frr_yang_module_info frr_ripd_info = { .modify = ripd_instance_redistribute_metric_modify, }, }, + { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map", + .cbs = { + .create = ripd_instance_if_route_maps_if_route_map_create, + .destroy = ripd_instance_if_route_maps_if_route_map_destroy, + .cli_show = cli_show_if_route_map, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map", + .cbs = { + .modify = ripd_instance_if_route_maps_if_route_map_in_route_map_modify, + .destroy = ripd_instance_if_route_maps_if_route_map_in_route_map_destroy, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map", + .cbs = { + .modify = ripd_instance_if_route_maps_if_route_map_out_route_map_modify, + .destroy = ripd_instance_if_route_maps_if_route_map_out_route_map_destroy, + } + }, { .xpath = "/frr-ripd:ripd/instance/static-route", .cbs = { @@ -216,6 +239,14 @@ const struct frr_yang_module_info frr_ripd_info = { .modify = ripd_instance_version_send_modify, }, }, + { + .xpath = "/frr-ripd:ripd/instance/default-bfd-profile", + .cbs = { + .modify = ripd_instance_default_bfd_profile_modify, + .destroy = ripd_instance_default_bfd_profile_destroy, + .cli_show = cli_show_ripd_instance_default_bfd_profile, + }, + }, { .xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon", .cbs = { @@ -279,6 +310,28 @@ const struct frr_yang_module_info frr_ripd_info = { .modify = lib_interface_rip_authentication_key_chain_modify, }, }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring", + .cbs = { + .create = lib_interface_rip_bfd_create, + .destroy = lib_interface_rip_bfd_destroy, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable", + .cbs = { + .cli_show = cli_show_ip_rip_bfd_enable, + .modify = lib_interface_rip_bfd_enable_modify, + }, + }, + { + .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile", + .cbs = { + .cli_show = cli_show_ip_rip_bfd_profile, + .modify = lib_interface_rip_bfd_profile_modify, + .destroy = lib_interface_rip_bfd_profile_destroy, + }, + }, { .xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor", .cbs = { @@ -337,6 +390,66 @@ const struct frr_yang_module_info frr_ripd_info = { .get_elem = ripd_instance_state_routes_route_interface_get_elem, }, }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop", + .cbs = { + .get_next = ripd_instance_state_routes_route_nexthops_nexthop_get_next, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem, + } + }, + { + .xpath = "/frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time", + .cbs = { + .get_elem = ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem, + } + }, { .xpath = "/frr-ripd:ripd/instance/state/routes/route/metric", .cbs = { diff --git a/ripd/rip_nb.h b/ripd/rip_nb.h index 99114c99288a..9929e0952b6f 100644 --- a/ripd/rip_nb.h +++ b/ripd/rip_nb.h @@ -52,6 +52,18 @@ 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_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args); +int ripd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args); +int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args); +int ripd_instance_if_route_maps_if_route_map_out_route_map_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); @@ -60,6 +72,8 @@ int ripd_instance_timers_holddown_interval_modify( 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); +int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args); +int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_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( @@ -89,6 +103,37 @@ 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); +const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next( + struct nb_cb_get_next_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem( + struct nb_cb_get_elem_args *args); +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_expire_time_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); @@ -108,6 +153,12 @@ 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); +int lib_interface_rip_bfd_create(struct nb_cb_create_args *args); +int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args); +int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args); +int lib_interface_rip_bfd_enable_destroy(struct nb_cb_destroy_args *args); +int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args); +int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args); /* Optional 'apply_finish' callbacks. */ void ripd_instance_redistribute_apply_finish( @@ -163,6 +214,9 @@ void cli_show_ip_rip_receive_version(struct vty *vty, bool show_defaults); void cli_show_ip_rip_send_version(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_ripd_instance_default_bfd_profile(struct vty *vty, + const struct lyd_node *dnode, + bool show_defaults); void cli_show_ip_rip_authentication_scheme(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); @@ -172,6 +226,10 @@ void cli_show_ip_rip_authentication_string(struct vty *vty, void cli_show_ip_rip_authentication_key_chain(struct vty *vty, const struct lyd_node *dnode, bool show_defaults); +void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); +void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode, + bool show_defaults); /* Notifications. */ extern void ripd_notif_send_auth_type_failure(const char *ifname); diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c index 2277ddc20457..8d3b6705968a 100644 --- a/ripd/rip_nb_config.c +++ b/ripd/rip_nb_config.c @@ -3,6 +3,7 @@ * Copyright (C) 1997, 1998, 1999 Kunihiro Ishiguro <kunihiro@zebra.org> * Copyright (C) 2018 NetDEF, Inc. * Renato Westphal + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include <zebra.h> @@ -13,6 +14,7 @@ #include "prefix.h" #include "table.h" #include "command.h" +#include "if_rmap.h" #include "routemap.h" #include "northbound.h" #include "libfrr.h" @@ -21,6 +23,7 @@ #include "ripd/rip_nb.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" +#include "ripd/rip_bfd.h" /* * XPath: /frr-ripd:ripd/instance @@ -96,9 +99,14 @@ int ripd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args) 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 = + MIN(yang_dnode_get_uint8(args->dnode, NULL), zebra_ecmp_count); + if (!rip->ecmp) { rip_ecmp_disable(rip); + return NB_OK; + } + + rip_ecmp_change(rip); return NB_OK; } @@ -523,7 +531,7 @@ int ripd_instance_non_passive_interface_create(struct nb_cb_create_args *args) rip = nb_running_get_entry(args->dnode, NULL, true); ifname = yang_dnode_get_string(args->dnode, NULL); - return rip_passive_nondefault_unset(rip, ifname); + return rip_passive_nondefault_set(rip, ifname); } int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args) @@ -537,7 +545,7 @@ int ripd_instance_non_passive_interface_destroy(struct nb_cb_destroy_args *args) rip = nb_running_get_entry(args->dnode, NULL, true); ifname = yang_dnode_get_string(args->dnode, NULL); - return rip_passive_nondefault_set(rip, ifname); + return rip_passive_nondefault_unset(rip, ifname); } /* @@ -680,6 +688,94 @@ int ripd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +/* + * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map + */ +int ripd_instance_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args) +{ + /* if_rmap is created when first routemap is added */ + return NB_OK; +} + +int ripd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * YANG will prune edit deletes up to the most general deleted node so + * we need to handle deleting any existing state underneath and not + * count on those more specific callbacks being called individually. + */ + + rip = nb_running_get_entry(args->dnode, NULL, true); + if_rmap_yang_destroy_cb(rip->if_rmap_ctx, args->dnode); + + return NB_OK; +} + +static void if_route_map_modify(const struct lyd_node *dnode, + enum if_rmap_type type, bool delete) +{ + struct rip *rip = nb_running_get_entry(dnode, NULL, true); + + if_rmap_yang_modify_cb(rip->if_rmap_ctx, dnode, type, delete); +} + +/* + * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/in-route-map + */ +int ripd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, false); + + return NB_OK; +} + +int ripd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, true); + + return NB_OK; +} + +/* + * XPath: /frr-ripd:ripd/instance/if-route-maps/if-route-map/out-route-map + */ +int ripd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, false); + + return NB_OK; +} + +int ripd_instance_if_route_maps_if_route_map_out_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, true); + + return NB_OK; +} + /* * XPath: /frr-ripd:ripd/instance/static-route */ @@ -815,6 +911,40 @@ int ripd_instance_version_send_modify(struct nb_cb_modify_args *args) return NB_OK; } +/* + * XPath: /frr-ripd:ripd/instance/default-bfd-profile + */ +int ripd_instance_default_bfd_profile_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); + XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile); + rip->default_bfd_profile = + XSTRDUP(MTYPE_RIP_BFD_PROFILE, + yang_dnode_get_string(args->dnode, NULL)); + rip_bfd_instance_update(rip); + + return NB_OK; +} + +int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args) +{ + struct rip *rip; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + rip = nb_running_get_entry(args->dnode, NULL, true); + XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile); + rip_bfd_instance_update(rip); + + return NB_OK; +} + /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon */ @@ -980,6 +1110,104 @@ int lib_interface_rip_authentication_password_destroy( return NB_OK; } +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring + */ +int lib_interface_rip_bfd_create(struct nb_cb_create_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->bfd.enabled = yang_dnode_get_bool(args->dnode, "./enable"); + XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile); + if (yang_dnode_exists(args->dnode, "./profile")) + ri->bfd.profile = XSTRDUP( + MTYPE_RIP_BFD_PROFILE, + yang_dnode_get_string(args->dnode, "./profile")); + + rip_bfd_interface_update(ri); + + return NB_OK; +} + +int lib_interface_rip_bfd_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->bfd.enabled = false; + XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile); + rip_bfd_interface_update(ri); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable + */ +int lib_interface_rip_bfd_enable_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->bfd.enabled = yang_dnode_get_bool(args->dnode, NULL); + rip_bfd_interface_update(ri); + + return NB_OK; +} + +/* + * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile + */ +int lib_interface_rip_bfd_profile_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_BFD_PROFILE, ri->bfd.profile); + ri->bfd.profile = XSTRDUP(MTYPE_RIP_BFD_PROFILE, + yang_dnode_get_string(args->dnode, NULL)); + rip_bfd_interface_update(ri); + + return NB_OK; +} + +int lib_interface_rip_bfd_profile_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_BFD_PROFILE, ri->bfd.profile); + rip_bfd_interface_update(ri); + + return NB_OK; +} + /* * XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain */ diff --git a/ripd/rip_nb_rpcs.c b/ripd/rip_nb_rpcs.c index be5528481930..bbe3d0f0f834 100644 --- a/ripd/rip_nb_rpcs.c +++ b/ripd/rip_nb_rpcs.c @@ -51,8 +51,8 @@ static void clear_rip_route(struct rip *rip) } if (rinfo) { - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); listnode_delete(list, rinfo); rip_info_free(rinfo); } diff --git a/ripd/rip_nb_state.c b/ripd/rip_nb_state.c index 0e2931b464ff..fa0d382a0e09 100644 --- a/ripd/rip_nb_state.c +++ b/ripd/rip_nb_state.c @@ -207,9 +207,170 @@ struct yang_data *ripd_instance_state_routes_route_prefix_get_elem( const struct route_node *rn = args->list_entry; const struct rip_info *rinfo = listnode_head(rn->info); + assert(rinfo); return yang_data_new_ipv4p(args->xpath, &rinfo->rp->p); } +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop + */ +const void *ripd_instance_state_routes_route_nexthops_nexthop_get_next( + struct nb_cb_get_next_args *args) +{ + const struct route_node *rn = args->parent_list_entry; + const struct listnode *node = args->list_entry; + + assert(rn); + if (node) + return listnextnode(node); + assert(rn->info); + return listhead((struct list *)rn->info); +} + +static inline const struct rip_info *get_rip_info(const void *info) +{ + return (const struct rip_info *)listgetdata( + (const struct listnode *)info); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/nh-type + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_nh_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + assert(rinfo); + return yang_data_new_enum(args->xpath, rinfo->nh.type); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/protocol + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_protocol_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + assert(rinfo); + return yang_data_new_enum(args->xpath, rinfo->type); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/rip-type + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_rip_type_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + assert(rinfo); + return yang_data_new_enum(args->xpath, rinfo->sub_type); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/gateway + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_gateway_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + if (rinfo->nh.type != NEXTHOP_TYPE_IPV4 && + rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX) + return NULL; + + return yang_data_new_ipv4(args->xpath, &rinfo->nh.gate.ipv4); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/interface + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_interface_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + const struct rip *rip = rip_info_get_instance(rinfo); + + if (rinfo->nh.type != NEXTHOP_TYPE_IFINDEX && + rinfo->nh.type != NEXTHOP_TYPE_IPV4_IFINDEX) + return NULL; + + return yang_data_new_string( + args->xpath, + ifindex2ifname(rinfo->nh.ifindex, rip->vrf->vrf_id)); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/from + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_from_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + if (rinfo->type != ZEBRA_ROUTE_RIP || rinfo->sub_type != RIP_ROUTE_RTE) + return NULL; + + return yang_data_new_ipv4(args->xpath, &rinfo->from); +} + +/* + * XPath: /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/tag + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_tag_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + return yang_data_new_uint32(args->xpath, rinfo->tag); +} + +/* + * XPath: + * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/external-metric + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_external_metric_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + + if ((rinfo->type == ZEBRA_ROUTE_RIP && + rinfo->sub_type == RIP_ROUTE_RTE) || + rinfo->metric == RIP_METRIC_INFINITY || rinfo->external_metric == 0) + return NULL; + return yang_data_new_uint32(args->xpath, rinfo->external_metric); +} + +/* + * XPath: + * /frr-ripd:ripd/instance/state/routes/route/nexthops/nexthop/expire-time + */ +struct yang_data * +ripd_instance_state_routes_route_nexthops_nexthop_expire_time_get_elem( + struct nb_cb_get_elem_args *args) +{ + const struct rip_info *rinfo = get_rip_info(args->list_entry); + struct event *event; + + if ((event = rinfo->t_timeout) == NULL) + event = rinfo->t_garbage_collect; + if (!event) + return NULL; + + return yang_data_new_uint32(args->xpath, + event_timer_remain_second(event)); +} + /* * XPath: /frr-ripd:ripd/instance/state/routes/route/next-hop */ diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c index a3cba598d2ff..3e8ddeccf267 100644 --- a/ripd/rip_peer.c +++ b/ripd/rip_peer.c @@ -9,10 +9,12 @@ #include "prefix.h" #include "command.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" +#include "table.h" #include "ripd/ripd.h" +#include "ripd/rip_bfd.h" DEFINE_MTYPE_STATIC(RIPD, RIP_PEER, "RIP peer"); @@ -21,9 +23,10 @@ static struct rip_peer *rip_peer_new(void) return XCALLOC(MTYPE_RIP_PEER, sizeof(struct rip_peer)); } -static void rip_peer_free(struct rip_peer *peer) +void rip_peer_free(struct rip_peer *peer) { - THREAD_OFF(peer->t_timeout); + bfd_sess_free(&peer->bfd_session); + EVENT_OFF(peer->t_timeout); XFREE(MTYPE_RIP_PEER, peer); } @@ -52,34 +55,37 @@ struct rip_peer *rip_peer_lookup_next(struct rip *rip, struct in_addr *addr) } /* RIP peer is timeout. */ -static void rip_peer_timeout(struct thread *t) +static void rip_peer_timeout(struct event *t) { struct rip_peer *peer; - peer = THREAD_ARG(t); + peer = EVENT_ARG(t); listnode_delete(peer->rip->peer_list, peer); rip_peer_free(peer); } /* Get RIP peer. At the same time update timeout thread. */ -static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr) +static struct rip_peer *rip_peer_get(struct rip *rip, struct rip_interface *ri, + struct in_addr *addr) { struct rip_peer *peer; peer = rip_peer_lookup(rip, addr); if (peer) { - THREAD_OFF(peer->t_timeout); + EVENT_OFF(peer->t_timeout); } else { peer = rip_peer_new(); peer->rip = rip; + peer->ri = ri; peer->addr = *addr; + rip_bfd_session_update(peer); listnode_add_sort(rip->peer_list, peer); } /* Update timeout thread. */ - thread_add_timer(master, rip_peer_timeout, peer, RIP_PEER_TIMER_DEFAULT, - &peer->t_timeout); + event_add_timer(master, rip_peer_timeout, peer, RIP_PEER_TIMER_DEFAULT, + &peer->t_timeout); /* Last update time set. */ time(&peer->uptime); @@ -87,24 +93,27 @@ static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr) return peer; } -void rip_peer_update(struct rip *rip, struct sockaddr_in *from, uint8_t version) +void rip_peer_update(struct rip *rip, struct rip_interface *ri, + struct sockaddr_in *from, uint8_t version) { struct rip_peer *peer; - peer = rip_peer_get(rip, &from->sin_addr); + peer = rip_peer_get(rip, ri, &from->sin_addr); peer->version = version; } -void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from) +void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri, + struct sockaddr_in *from) { struct rip_peer *peer; - peer = rip_peer_get(rip, &from->sin_addr); + peer = rip_peer_get(rip, ri, &from->sin_addr); peer->recv_badroutes++; } -void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from) +void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri, + struct sockaddr_in *from) { struct rip_peer *peer; - peer = rip_peer_get(rip, &from->sin_addr); + peer = rip_peer_get(rip, ri, &from->sin_addr); peer->recv_badpackets++; } @@ -136,7 +145,7 @@ void rip_peer_display(struct vty *vty, struct rip *rip) char timebuf[RIP_UPTIME_LEN]; for (ALL_LIST_ELEMENTS(rip->peer_list, node, nnode, peer)) { - vty_out(vty, " %-16pI4 %9d %9d %9d %s\n", + vty_out(vty, " %-17pI4 %9d %9d %9d %11s\n", &peer->addr, peer->recv_badpackets, peer->recv_badroutes, ZEBRA_RIP_DISTANCE_DEFAULT, rip_peer_uptime(peer, timebuf, RIP_UPTIME_LEN)); @@ -155,3 +164,46 @@ void rip_peer_list_del(void *arg) { rip_peer_free(arg); } + +void rip_peer_delete_routes(const struct rip_peer *peer) +{ + struct route_node *route_node; + + for (route_node = route_top(peer->rip->table); route_node; + route_node = route_next(route_node)) { + struct rip_info *route_entry; + struct listnode *listnode; + struct listnode *listnode_next; + struct list *list; + + list = route_node->info; + if (list == NULL) + continue; + + for (ALL_LIST_ELEMENTS(list, listnode, listnode_next, + route_entry)) { + if (!rip_route_rte(route_entry)) + continue; + if (route_entry->from.s_addr != peer->addr.s_addr) + continue; + + if (listcount(list) == 1) { + EVENT_OFF(route_entry->t_timeout); + EVENT_OFF(route_entry->t_garbage_collect); + listnode_delete(list, route_entry); + if (list_isempty(list)) { + list_delete((struct list **)&route_node + ->info); + route_unlock_node(route_node); + } + rip_info_free(route_entry); + + /* Signal the output process to trigger an + * update (see section 2.5). */ + rip_event(peer->rip, RIP_TRIGGERED_UPDATE, 0); + } else + rip_ecmp_delete(peer->rip, route_entry); + break; + } + } +} diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index ab80dd1b8a2d..0e5d4d54c97a 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -135,7 +135,7 @@ static struct variable rip_variables[] = { {RIP2PEERRCVBADPACKETS, COUNTER, RONLY, rip2PeerTable, 3, {4, 1, 5}}, {RIP2PEERRCVBADROUTES, COUNTER, RONLY, rip2PeerTable, 3, {4, 1, 6}}}; -extern struct thread_master *master; +extern struct event_loop *master; static uint8_t *rip2Globals(struct variable *v, oid name[], size_t *length, int exact, size_t *var_len, @@ -553,7 +553,7 @@ static uint8_t *rip2PeerTable(struct variable *v, oid name[], size_t *length, } /* Register RIPv2-MIB. */ -static int rip_snmp_init(struct thread_master *master) +static int rip_snmp_init(struct event_loop *master) { rip_ifaddr_table = route_table_init(); diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 4a58d1225e99..5bf51c2f1528 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -13,6 +13,7 @@ #include "zclient.h" #include "log.h" #include "vrf.h" +#include "bfd.h" #include "ripd/ripd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" @@ -29,7 +30,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, struct zapi_nexthop *api_nh; struct listnode *listnode = NULL; struct rip_info *rinfo = NULL; - int count = 0; + uint32_t count = 0; memset(&api, 0, sizeof(api)); api.vrf_id = rip->vrf->vrf_id; @@ -38,7 +39,7 @@ static void rip_zebra_ipv4_send(struct rip *rip, struct route_node *rp, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - if (count >= MULTIPATH_NUM) + if (count >= zebra_ecmp_count) break; api_nh = &api.nexthops[count]; api_nh->vrf_id = rip->vrf->vrf_id; @@ -196,6 +197,7 @@ void rip_zebra_vrf_register(struct vrf *vrf) vrf->name, vrf->vrf_id); zclient_send_reg_requests(zclient, vrf->vrf_id); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf->vrf_id); } void rip_zebra_vrf_deregister(struct vrf *vrf) @@ -208,11 +210,13 @@ void rip_zebra_vrf_deregister(struct vrf *vrf) vrf->name, vrf->vrf_id); zclient_send_dereg_requests(zclient, vrf->vrf_id); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_DEREGISTER, vrf->vrf_id); } static void rip_zebra_connected(struct zclient *zclient) { zclient_send_reg_requests(zclient, VRF_DEFAULT); + bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); } zclient_handler *const rip_handlers[] = { @@ -223,13 +227,19 @@ zclient_handler *const rip_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = rip_zebra_read_route, }; -void rip_zclient_init(struct thread_master *master) +static void rip_zebra_capabilities(struct zclient_capabilities *cap) +{ + zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count); +} + +void rip_zclient_init(struct event_loop *master) { /* Set default value to the zebra client structure. */ zclient = zclient_new(master, &zclient_options_default, rip_handlers, array_size(rip_handlers)); zclient_init(zclient, ZEBRA_ROUTE_RIP, 0, &ripd_privs); zclient->zebra_connected = rip_zebra_connected; + zclient->zebra_capabilities = rip_zebra_capabilities; } void rip_zclient_stop(void) diff --git a/ripd/ripd.c b/ripd/ripd.c index bde7e858f125..7bfcaadc74cc 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -11,7 +11,7 @@ #include "command.h" #include "prefix.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "log.h" #include "stream.h" @@ -34,6 +34,7 @@ #include "ripd/ripd.h" #include "ripd/rip_nb.h" +#include "ripd/rip_bfd.h" #include "ripd/rip_debug.h" #include "ripd/rip_errors.h" #include "ripd/rip_interface.h" @@ -50,7 +51,7 @@ DEFINE_MTYPE_STATIC(RIPD, RIP_DISTANCE, "RIP distance"); /* Prototypes. */ static void rip_output_process(struct connected *, struct sockaddr_in *, int, uint8_t); -static void rip_triggered_update(struct thread *); +static void rip_triggered_update(struct event *); static int rip_update_jitter(unsigned long); static void rip_distance_table_node_cleanup(struct route_table *table, struct route_node *node); @@ -121,15 +122,15 @@ struct rip *rip_info_get_instance(const struct rip_info *rinfo) } /* RIP route garbage collect timer. */ -static void rip_garbage_collect(struct thread *t) +static void rip_garbage_collect(struct event *t) { struct rip_info *rinfo; struct route_node *rp; - rinfo = THREAD_ARG(t); + rinfo = EVENT_ARG(t); /* Off timeout timer. */ - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); /* Get route_node pointer. */ rp = rinfo->rp; @@ -155,7 +156,10 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new) { struct route_node *rp = rinfo_new->rp; struct rip_info *rinfo = NULL; + struct rip_info *rinfo_exist = NULL; struct list *list = NULL; + struct listnode *node = NULL; + struct listnode *nnode = NULL; if (rp->info == NULL) rp->info = list_new(); @@ -166,6 +170,33 @@ struct rip_info *rip_ecmp_add(struct rip *rip, struct rip_info *rinfo_new) if (listcount(list) && !rip->ecmp) return NULL; + /* Add or replace an existing ECMP path with lower neighbor IP */ + if (listcount(list) && listcount(list) >= rip->ecmp) { + struct rip_info *from_highest = NULL; + + /* Find the rip_info struct that has the highest nexthop IP */ + for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist)) + if (!from_highest || + (from_highest && + IPV4_ADDR_CMP(&rinfo_exist->from, + &from_highest->from) > 0)) { + from_highest = rinfo_exist; + } + + /* If we have a route in ECMP group, delete the old + * one that has a higher next-hop address. Lower IP is + * preferred. + */ + if (rip->ecmp > 1 && from_highest && + IPV4_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) { + rip_ecmp_delete(rip, from_highest); + goto add_or_replace; + } + + return NULL; + } + +add_or_replace: rinfo = rip_info_new(); memcpy(rinfo, rinfo_new, sizeof(struct rip_info)); listnode_add(list, rinfo); @@ -211,14 +242,14 @@ struct rip_info *rip_ecmp_replace(struct rip *rip, struct rip_info *rinfo_new) if (tmp_rinfo == rinfo) continue; - THREAD_OFF(tmp_rinfo->t_timeout); - THREAD_OFF(tmp_rinfo->t_garbage_collect); + EVENT_OFF(tmp_rinfo->t_timeout); + EVENT_OFF(tmp_rinfo->t_garbage_collect); list_delete_node(list, node); rip_info_free(tmp_rinfo); } - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); memcpy(rinfo, rinfo_new, sizeof(struct rip_info)); if (rip_route_rte(rinfo)) { @@ -247,12 +278,12 @@ struct rip_info *rip_ecmp_delete(struct rip *rip, struct rip_info *rinfo) struct route_node *rp = rinfo->rp; struct list *list = (struct list *)rp->info; - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); if (listcount(list) > 1) { /* Some other ECMP entries still exist. Just delete this entry. */ - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_garbage_collect); listnode_delete(list, rinfo); if (rip_route_rte(rinfo) && CHECK_FLAG(rinfo->flags, RIP_RTF_FIB)) @@ -287,9 +318,9 @@ struct rip_info *rip_ecmp_delete(struct rip *rip, struct rip_info *rinfo) } /* Timeout RIP routes. */ -static void rip_timeout(struct thread *t) +static void rip_timeout(struct event *t) { - struct rip_info *rinfo = THREAD_ARG(t); + struct rip_info *rinfo = EVENT_ARG(t); struct rip *rip = rip_info_get_instance(rinfo); rip_ecmp_delete(rip, rinfo); @@ -298,9 +329,9 @@ static void rip_timeout(struct thread *t) static void rip_timeout_update(struct rip *rip, struct rip_info *rinfo) { if (rinfo->metric != RIP_METRIC_INFINITY) { - THREAD_OFF(rinfo->t_timeout); - thread_add_timer(master, rip_timeout, rinfo, rip->timeout_time, - &rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); + event_add_timer(master, rip_timeout, rinfo, rip->timeout_time, + &rinfo->t_timeout); } } @@ -644,8 +675,8 @@ static void rip_rte_process(struct rte *rte, struct sockaddr_in *from, assert(newinfo.metric != RIP_METRIC_INFINITY); - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); memcpy(rinfo, &newinfo, sizeof(struct rip_info)); rip_timeout_update(rip, rinfo); @@ -1123,7 +1154,7 @@ static void rip_response_process(struct rip_packet *packet, int size, if (from->sin_port != htons(RIP_PORT_DEFAULT)) { zlog_info("response doesn't come from RIP port: %d", from->sin_port); - rip_peer_bad_packet(rip, from); + rip_peer_bad_packet(rip, ri, from); return; } @@ -1137,7 +1168,7 @@ static void rip_response_process(struct rip_packet *packet, int size, zlog_info( "This datagram doesn't come from a valid neighbor: %pI4", &from->sin_addr); - rip_peer_bad_packet(rip, from); + rip_peer_bad_packet(rip, ri, from); return; } @@ -1147,7 +1178,7 @@ static void rip_response_process(struct rip_packet *packet, int size, ; /* Alredy done in rip_read () */ /* Update RIP peer. */ - rip_peer_update(rip, from, packet->version); + rip_peer_update(rip, ri, from, packet->version); /* Set RTE pointer. */ rte = packet->rte; @@ -1171,12 +1202,18 @@ static void rip_response_process(struct rip_packet *packet, int size, continue; } + if (packet->version == RIPv1 && rte->tag != 0) { + zlog_warn("RIPv1 reserved field is nonzero: %d", + ntohs(rte->tag)); + continue; + } + /* - is the destination address valid (e.g., unicast; not net 0 or 127) */ if (!rip_destination_check(rte->prefix)) { zlog_info( "Network is net 0 or net 127 or it is not unicast network"); - rip_peer_bad_route(rip, from); + rip_peer_bad_route(rip, ri, from); continue; } @@ -1186,7 +1223,7 @@ static void rip_response_process(struct rip_packet *packet, int size, /* - is the metric valid (i.e., between 1 and 16, inclusive) */ if (!(rte->metric >= 1 && rte->metric <= 16)) { zlog_info("Route's metric is not in the 1-16 range."); - rip_peer_bad_route(rip, from); + rip_peer_bad_route(rip, ri, from); continue; } @@ -1195,7 +1232,7 @@ static void rip_response_process(struct rip_packet *packet, int size, && rte->nexthop.s_addr != INADDR_ANY) { zlog_info("RIPv1 packet with nexthop value %pI4", &rte->nexthop); - rip_peer_bad_route(rip, from); + rip_peer_bad_route(rip, ri, from); continue; } @@ -1326,7 +1363,7 @@ static void rip_response_process(struct rip_packet *packet, int size, zlog_warn( "RIPv2 address %pI4 is not mask /%d applied one", &rte->prefix, ip_masklen(rte->mask)); - rip_peer_bad_route(rip, from); + rip_peer_bad_route(rip, ri, from); continue; } @@ -1500,7 +1537,7 @@ static int rip_send_packet(uint8_t *buf, int size, struct sockaddr_in *to, ret = sendmsg(rip->sock, &msg, 0); if (IS_RIP_DEBUG_EVENT) - zlog_debug("SEND to %pI4%d", &sin.sin_addr, + zlog_debug("SEND to %pI4 port %d", &sin.sin_addr, ntohs(sin.sin_port)); if (ret < 0) @@ -1598,7 +1635,7 @@ void rip_redistribute_delete(struct rip *rip, int type, int sub_type, RIP_TIMER_ON(rinfo->t_garbage_collect, rip_garbage_collect, rip->garbage_time); - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); rinfo->flags |= RIP_RTF_CHANGED; if (IS_RIP_DEBUG_EVENT) @@ -1643,7 +1680,7 @@ static void rip_request_process(struct rip_packet *packet, int size, return; /* RIP peer update. */ - rip_peer_update(rip, from, packet->version); + rip_peer_update(rip, ri, from, packet->version); lim = ((caddr_t)packet) + size; rte = packet->rte; @@ -1697,9 +1734,9 @@ static void rip_request_process(struct rip_packet *packet, int size, } /* First entry point of RIP packet. */ -static void rip_read(struct thread *t) +static void rip_read(struct event *t) { - struct rip *rip = THREAD_ARG(t); + struct rip *rip = EVENT_ARG(t); int sock; int ret; int rtenum; @@ -1711,11 +1748,11 @@ static void rip_read(struct thread *t) socklen_t fromlen; struct interface *ifp = NULL; struct connected *ifc; - struct rip_interface *ri; + struct rip_interface *ri = NULL; struct prefix p; /* Fetch socket then register myself. */ - sock = THREAD_FD(t); + sock = EVENT_FD(t); /* Add myself to tne next event */ rip_event(rip, RIP_READ, sock); @@ -1743,8 +1780,10 @@ static void rip_read(struct thread *t) /* Which interface is this packet comes from. */ ifc = if_lookup_address((void *)&from.sin_addr, AF_INET, rip->vrf->vrf_id); - if (ifc) + if (ifc) { ifp = ifc->ifp; + ri = ifp->info; + } /* RIP packet received */ if (IS_RIP_DEBUG_EVENT) @@ -1753,7 +1792,7 @@ static void rip_read(struct thread *t) ifp ? ifp->name : "unknown", rip->vrf_name); /* If this packet come from unknown interface, ignore it. */ - if (ifp == NULL) { + if (ifp == NULL || ri == NULL) { zlog_info( "%s: cannot find interface for packet from %pI4 port %d (VRF %s)", __func__, &from.sin_addr, ntohs(from.sin_port), @@ -1779,13 +1818,13 @@ static void rip_read(struct thread *t) if (len < RIP_PACKET_MINSIZ) { zlog_warn("packet size %d is smaller than minimum size %d", len, RIP_PACKET_MINSIZ); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } if (len > RIP_PACKET_MAXSIZ) { zlog_warn("packet size %d is larger than max size %d", len, RIP_PACKET_MAXSIZ); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1793,7 +1832,7 @@ static void rip_read(struct thread *t) if ((len - RIP_PACKET_MINSIZ) % 20) { zlog_warn("packet size %d is wrong for RIP packet alignment", len); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1807,7 +1846,7 @@ static void rip_read(struct thread *t) if (packet->version == 0) { zlog_info("version 0 with command %d received.", packet->command); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1823,12 +1862,11 @@ static void rip_read(struct thread *t) packet->version = RIPv2; /* Is RIP running or is this RIP neighbor ?*/ - ri = ifp->info; if (!ri->running && !rip_neighbor_lookup(rip, &from)) { if (IS_RIP_DEBUG_EVENT) zlog_debug("RIP is not enabled on interface %s.", ifp->name); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1842,7 +1880,7 @@ static void rip_read(struct thread *t) zlog_debug( " packet's v%d doesn't fit to if version spec", packet->version); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1857,7 +1895,7 @@ static void rip_read(struct thread *t) "packet RIPv%d is dropped because authentication disabled", packet->version); ripd_notif_send_auth_type_failure(ifp->name); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1893,7 +1931,7 @@ static void rip_read(struct thread *t) zlog_debug( "RIPv1 dropped because authentication enabled"); ripd_notif_send_auth_type_failure(ifp->name); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } } else if (ri->auth_type != RIP_NO_AUTH) { @@ -1906,7 +1944,7 @@ static void rip_read(struct thread *t) zlog_debug( "RIPv2 authentication failed: no auth RTE in packet"); ripd_notif_send_auth_type_failure(ifp->name); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1916,7 +1954,7 @@ static void rip_read(struct thread *t) zlog_debug( "RIPv2 dropped because authentication enabled"); ripd_notif_send_auth_type_failure(ifp->name); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } @@ -1952,7 +1990,7 @@ static void rip_read(struct thread *t) zlog_debug("RIPv2 %s authentication failure", auth_desc); ripd_notif_send_auth_failure(ifp->name); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); return; } } @@ -1971,16 +2009,16 @@ static void rip_read(struct thread *t) zlog_info( "Obsolete command %s received, please sent it to routed", lookup_msg(rip_msg, packet->command, NULL)); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); break; case RIP_POLL_ENTRY: zlog_info("Obsolete command %s received", lookup_msg(rip_msg, packet->command, NULL)); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); break; default: zlog_info("Unknown RIP command %d received", packet->command); - rip_peer_bad_packet(rip, &from); + rip_peer_bad_packet(rip, ri, &from); break; } } @@ -2478,9 +2516,9 @@ static void rip_update_process(struct rip *rip, int route_type) } /* RIP's periodical timer. */ -static void rip_update(struct thread *t) +static void rip_update(struct event *t) { - struct rip *rip = THREAD_ARG(t); + struct rip *rip = EVENT_ARG(t); if (IS_RIP_DEBUG_EVENT) zlog_debug("update timer fire!"); @@ -2490,7 +2528,7 @@ static void rip_update(struct thread *t) /* Triggered updates may be suppressed if a regular update is due by the time the triggered update would be sent. */ - THREAD_OFF(rip->t_triggered_interval); + EVENT_OFF(rip->t_triggered_interval); rip->trigger = 0; /* Register myself. */ @@ -2520,9 +2558,9 @@ static void rip_clear_changed_flag(struct rip *rip) } /* Triggered update interval timer. */ -static void rip_triggered_interval(struct thread *t) +static void rip_triggered_interval(struct event *t) { - struct rip *rip = THREAD_ARG(t); + struct rip *rip = EVENT_ARG(t); if (rip->trigger) { rip->trigger = 0; @@ -2531,13 +2569,13 @@ static void rip_triggered_interval(struct thread *t) } /* Execute triggered update. */ -static void rip_triggered_update(struct thread *t) +static void rip_triggered_update(struct event *t) { - struct rip *rip = THREAD_ARG(t); + struct rip *rip = EVENT_ARG(t); int interval; /* Cancel interval timer. */ - THREAD_OFF(rip->t_triggered_interval); + EVENT_OFF(rip->t_triggered_interval); rip->trigger = 0; /* Logging triggered update. */ @@ -2558,8 +2596,8 @@ static void rip_triggered_update(struct thread *t) update is triggered when the timer expires. */ interval = (frr_weak_random() % 5) + 1; - thread_add_timer(master, rip_triggered_interval, rip, interval, - &rip->t_triggered_interval); + event_add_timer(master, rip_triggered_interval, rip, interval, + &rip->t_triggered_interval); } /* Withdraw redistributed route. */ @@ -2587,7 +2625,7 @@ void rip_redistribute_withdraw(struct rip *rip, int type) rinfo->metric = RIP_METRIC_INFINITY; RIP_TIMER_ON(rinfo->t_garbage_collect, rip_garbage_collect, rip->garbage_time); - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); rinfo->flags |= RIP_RTF_CHANGED; if (IS_RIP_DEBUG_EVENT) { @@ -2624,6 +2662,36 @@ struct rip *rip_lookup_by_vrf_name(const char *vrf_name) return RB_FIND(rip_instance_head, &rip_instances, &rip); } +/* Update ECMP routes to zebra when `allow-ecmp` changed. */ +void rip_ecmp_change(struct rip *rip) +{ + struct route_node *rp; + struct rip_info *rinfo; + struct list *list; + struct listnode *node, *nextnode; + + for (rp = route_top(rip->table); rp; rp = route_next(rp)) { + list = rp->info; + if (list && listcount(list) > 1) { + while (listcount(list) > rip->ecmp) { + struct rip_info *from_highest = NULL; + + for (ALL_LIST_ELEMENTS(list, node, nextnode, + rinfo)) { + if (!from_highest || + (from_highest && + IPV4_ADDR_CMP( + &rinfo->from, + &from_highest->from) > 0)) + from_highest = rinfo; + } + + rip_ecmp_delete(rip, from_highest); + } + } + } +} + /* Create new RIP instance and set it to global variable. */ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket) { @@ -2633,7 +2701,7 @@ struct rip *rip_create(const char *vrf_name, struct vrf *vrf, int socket) rip->vrf_name = XSTRDUP(MTYPE_RIP_VRF_NAME, vrf_name); /* Set initial value. */ - rip->ecmp = yang_get_default_bool("%s/allow-ecmp", RIP_INSTANCE); + rip->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIP_INSTANCE); rip->default_metric = yang_get_default_uint8("%s/default-metric", RIP_INSTANCE); rip->distance = @@ -2766,21 +2834,21 @@ void rip_event(struct rip *rip, enum rip_event event, int sock) switch (event) { case RIP_READ: - thread_add_read(master, rip_read, rip, sock, &rip->t_read); + event_add_read(master, rip_read, rip, sock, &rip->t_read); break; case RIP_UPDATE_EVENT: - THREAD_OFF(rip->t_update); + EVENT_OFF(rip->t_update); jitter = rip_update_jitter(rip->update_time); - thread_add_timer(master, rip_update, rip, - sock ? 2 : rip->update_time + jitter, - &rip->t_update); + event_add_timer(master, rip_update, rip, + sock ? 2 : rip->update_time + jitter, + &rip->t_update); break; case RIP_TRIGGERED_UPDATE: if (rip->t_triggered_interval) rip->trigger = 1; else - thread_add_event(master, rip_triggered_update, rip, 0, - &rip->t_triggered_update); + event_add_event(master, rip_triggered_update, rip, 0, + &rip->t_triggered_update); break; default: break; @@ -2836,16 +2904,11 @@ uint8_t rip_distance_apply(struct rip *rip, struct rip_info *rinfo) if (access_list_apply(alist, &rinfo->rp->p) == FILTER_DENY) return 0; - - return rdistance->distance; - } else - return rdistance->distance; + } + return rdistance->distance; } - if (rip->distance) - return rip->distance; - - return 0; + return rip->distance; } static void rip_distance_show(struct vty *vty, struct rip *rip) @@ -2899,8 +2962,8 @@ void rip_ecmp_disable(struct rip *rip) if (tmp_rinfo == rinfo) continue; - THREAD_OFF(tmp_rinfo->t_timeout); - THREAD_OFF(tmp_rinfo->t_garbage_collect); + EVENT_OFF(tmp_rinfo->t_timeout); + EVENT_OFF(tmp_rinfo->t_garbage_collect); list_delete_node(list, node); rip_info_free(tmp_rinfo); } @@ -2923,15 +2986,15 @@ static void rip_vty_out_uptime(struct vty *vty, struct rip_info *rinfo) struct tm tm; #define TIME_BUF 25 char timebuf[TIME_BUF]; - struct thread *thread; + struct event *thread; if ((thread = rinfo->t_timeout) != NULL) { - clock = thread_timer_remain_second(thread); + clock = event_timer_remain_second(thread); 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); + clock = event_timer_remain_second(thread); gmtime_r(&clock, &tm); strftime(timebuf, TIME_BUF, "%M:%S", &tm); vty_out(vty, "%5s", timebuf); @@ -3106,7 +3169,7 @@ DEFUN (show_ip_rip_status, vty_out(vty, " Sending updates every %u seconds with +/-50%%,", rip->update_time); vty_out(vty, " next due in %lu seconds\n", - thread_timer_remain_second(rip->t_update)); + event_timer_remain_second(rip->t_update)); vty_out(vty, " Timeout after %u seconds,", rip->timeout_time); vty_out(vty, " garbage collect after %u seconds\n", rip->garbage_time); @@ -3209,9 +3272,6 @@ static int config_write_rip(struct vty *vty) /* Distribute configuration. */ config_write_distribute(vty, rip->distribute_ctx); - /* Interface routemap configuration */ - config_write_if_rmap(vty, rip->if_rmap_ctx); - vty_out(vty, "exit\n"); write = 1; @@ -3347,6 +3407,7 @@ void rip_clean(struct rip *rip) route_table_finish(rip->distance_table); RB_REMOVE(rip_instance_head, &rip_instances, rip); + XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile); XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name); XFREE(MTYPE_RIP, rip); } @@ -3492,8 +3553,8 @@ static void rip_instance_disable(struct rip *rip) rip_zebra_ipv4_delete(rip, rp); for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); rip_info_free(rinfo); } list_delete(&list); @@ -3505,12 +3566,12 @@ static void rip_instance_disable(struct rip *rip) rip_redistribute_disable(rip); /* Cancel RIP related timers. */ - THREAD_OFF(rip->t_update); - THREAD_OFF(rip->t_triggered_update); - THREAD_OFF(rip->t_triggered_interval); + EVENT_OFF(rip->t_update); + EVENT_OFF(rip->t_triggered_update); + EVENT_OFF(rip->t_triggered_interval); /* Cancel read thread. */ - THREAD_OFF(rip->t_read); + EVENT_OFF(rip->t_read); /* Close RIP socket. */ close(rip->sock); @@ -3536,18 +3597,10 @@ static int rip_vrf_new(struct vrf *vrf) static int rip_vrf_delete(struct vrf *vrf) { - struct rip *rip; - if (IS_RIP_DEBUG_EVENT) zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name, vrf->vrf_id); - rip = rip_lookup_by_vrf_name(vrf->name); - if (!rip) - return 0; - - rip_clean(rip); - return 0; } diff --git a/ripd/ripd.h b/ripd/ripd.h index 4894cb863269..ac4a51f586fd 100644 --- a/ripd/ripd.h +++ b/ripd/ripd.h @@ -10,6 +10,7 @@ #include "nexthop.h" #include "distribute.h" #include "memory.h" +#include "bfd.h" /* RIP version number. */ #define RIPv1 1 @@ -117,15 +118,15 @@ struct rip { struct list *peer_list; /* RIP threads. */ - struct thread *t_read; + struct event *t_read; /* Update and garbage timer. */ - struct thread *t_update; + struct event *t_update; /* Triggered update hack. */ int trigger; - struct thread *t_triggered_update; - struct thread *t_triggered_interval; + struct event *t_triggered_update; + struct event *t_triggered_interval; /* RIP timer values. */ uint32_t update_time; @@ -140,7 +141,7 @@ struct rip { struct route_table *distance_table; /* RIP ECMP flag */ - bool ecmp; + uint8_t ecmp; /* Are we in passive-interface default mode? */ bool passive_default; @@ -182,6 +183,9 @@ struct rip { /* RIP queries. */ long queries; } counters; + + /* Default BFD profile to use with BFD sessions. */ + char *default_bfd_profile; }; RB_HEAD(rip_instance_head, rip); RB_PROTOTYPE(rip_instance_head, rip, entry, rip_instance_compare) @@ -239,8 +243,8 @@ struct rip_info { uint8_t flags; /* Garbage collect timer. */ - struct thread *t_timeout; - struct thread *t_garbage_collect; + struct event *t_timeout; + struct event *t_garbage_collect; /* Route-map futures - this variables can be changed. */ struct in_addr nexthop_out; @@ -265,6 +269,9 @@ struct rip_interface { /* Parent routing instance. */ struct rip *rip; + /* Interface data from zebra. */ + struct interface *ifp; + /* RIP is enabled on this interface. */ int enable_network; int enable_interface; @@ -309,7 +316,7 @@ struct rip_interface { struct route_map *routemap[RIP_FILTER_MAX]; /* Wake up thread. */ - struct thread *t_wakeup; + struct event *t_wakeup; /* Interface statistics. */ int recv_badpackets; @@ -318,6 +325,12 @@ struct rip_interface { /* Passive interface. */ int passive; + + /* BFD information. */ + struct { + bool enabled; + char *profile; + } bfd; }; /* RIP peer information. */ @@ -325,6 +338,9 @@ struct rip_peer { /* Parent routing instance. */ struct rip *rip; + /* Back-pointer to RIP interface. */ + struct rip_interface *ri; + /* Peer address. */ struct in_addr addr; @@ -342,7 +358,10 @@ struct rip_peer { int recv_badroutes; /* Timeout thread. */ - struct thread *t_timeout; + struct event *t_timeout; + + /* BFD information */ + struct bfd_session_params *bfd_session; }; struct rip_distance { @@ -387,7 +406,7 @@ enum rip_event { }; /* Macro for timer turn on. */ -#define RIP_TIMER_ON(T,F,V) thread_add_timer (master, (F), rinfo, (V), &(T)) +#define RIP_TIMER_ON(T, F, V) event_add_timer(master, (F), rinfo, (V), &(T)) #define RIP_OFFSET_LIST_IN 0 #define RIP_OFFSET_LIST_OUT 1 @@ -418,7 +437,7 @@ extern void rip_if_init(void); extern void rip_route_map_init(void); extern void rip_zebra_vrf_register(struct vrf *vrf); extern void rip_zebra_vrf_deregister(struct vrf *vrf); -extern void rip_zclient_init(struct thread_master *); +extern void rip_zclient_init(struct event_loop *e); extern void rip_zclient_stop(void); extern int if_check_address(struct rip *rip, struct in_addr addr); extern struct rip *rip_lookup_by_vrf_id(vrf_id_t vrf_id); @@ -461,16 +480,20 @@ extern void rip_if_rmap_update_interface(struct interface *ifp); extern int rip_show_network_config(struct vty *vty, struct rip *rip); extern void rip_show_redistribute_config(struct vty *vty, struct rip *rip); -extern void rip_peer_update(struct rip *rip, struct sockaddr_in *from, - uint8_t version); -extern void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from); -extern void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from); +extern void rip_peer_free(struct rip_peer *peer); +extern void rip_peer_update(struct rip *rip, struct rip_interface *ri, + struct sockaddr_in *from, uint8_t version); +extern void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri, + struct sockaddr_in *from); +extern void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri, + struct sockaddr_in *from); extern void rip_peer_display(struct vty *vty, struct rip *rip); extern struct rip_peer *rip_peer_lookup(struct rip *rip, struct in_addr *addr); extern struct rip_peer *rip_peer_lookup_next(struct rip *rip, struct in_addr *addr); extern int rip_peer_list_cmp(struct rip_peer *p1, struct rip_peer *p2); extern void rip_peer_list_del(void *arg); +void rip_peer_delete_routes(const struct rip_peer *peer); extern void rip_info_free(struct rip_info *); extern struct rip *rip_info_get_instance(const struct rip_info *rinfo); @@ -509,9 +532,13 @@ extern struct zebra_privs_t ripd_privs; extern struct rip_instance_head rip_instances; /* Master thread structure. */ -extern struct thread_master *master; +extern struct event_loop *master; DECLARE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc)); DECLARE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc)); +extern void rip_ecmp_change(struct rip *rip); + +extern uint32_t zebra_ecmp_count; + #endif /* _ZEBRA_RIP_H */ diff --git a/ripd/subdir.am b/ripd/subdir.am index 98cc765c91cf..c793a6d68538 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -13,6 +13,7 @@ man8 += $(MANBUILD)/frr-ripd.8 endif ripd_ripd_SOURCES = \ + ripd/rip_bfd.c \ ripd/rip_cli.c \ ripd/rip_debug.c \ ripd/rip_errors.c \ @@ -31,10 +32,12 @@ ripd_ripd_SOURCES = \ # end clippy_scan += \ + ripd/rip_bfd.c \ ripd/rip_cli.c \ # end noinst_HEADERS += \ + ripd/rip_bfd.h \ ripd/rip_debug.h \ ripd/rip_errors.h \ ripd/rip_interface.h \ @@ -45,6 +48,7 @@ noinst_HEADERS += \ ripd_ripd_LDADD = lib/libfrr.la $(LIBCAP) nodist_ripd_ripd_SOURCES = \ yang/frr-ripd.yang.c \ + yang/frr-bfdd.yang.c \ # end ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c diff --git a/ripngd/ripng_cli.c b/ripngd/ripng_cli.c index 5e59dfd2c409..9a96e29313d6 100644 --- a/ripngd/ripng_cli.c +++ b/ripngd/ripng_cli.c @@ -85,14 +85,32 @@ void cli_show_router_ripng(struct vty *vty, const struct lyd_node *dnode, /* * XPath: /frr-ripngd:ripngd/instance/allow-ecmp */ -DEFPY_YANG (ripng_allow_ecmp, - ripng_allow_ecmp_cmd, - "[no] allow-ecmp", - NO_STR - "Allow Equal Cost MultiPath\n") +DEFUN_YANG (ripng_allow_ecmp, + ripng_allow_ecmp_cmd, + "allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", + "Allow Equal Cost MultiPath\n" + "Number of paths\n") +{ + int idx_number = 0; + char mpaths[3] = {}; + uint32_t paths = MULTIPATH_NUM; + + if (argv_find(argv, argc, CMD_RANGE_STR(1, MULTIPATH_NUM), &idx_number)) + paths = strtol(argv[idx_number]->arg, NULL, 10); + snprintf(mpaths, sizeof(mpaths), "%u", paths); + + nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, mpaths); + + return nb_cli_apply_changes(vty, NULL); +} + +DEFUN_YANG (no_ripng_allow_ecmp, + no_ripng_allow_ecmp_cmd, + "no allow-ecmp [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", NO_STR + "Allow Equal Cost MultiPath\n" + "Number of paths\n") { - nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, - no ? "false" : "true"); + nb_cli_enqueue_change(vty, "./allow-ecmp", NB_OP_MODIFY, 0); return nb_cli_apply_changes(vty, NULL); } @@ -100,10 +118,14 @@ DEFPY_YANG (ripng_allow_ecmp, void cli_show_ripng_allow_ecmp(struct vty *vty, const struct lyd_node *dnode, bool show_defaults) { - if (!yang_dnode_get_bool(dnode, NULL)) - vty_out(vty, " no"); + uint8_t paths; + + paths = yang_dnode_get_uint8(dnode, NULL); - vty_out(vty, " allow-ecmp\n"); + if (!paths) + vty_out(vty, " no allow-ecmp\n"); + else + vty_out(vty, " allow-ecmp %d\n", paths); } /* @@ -547,6 +569,7 @@ void ripng_cli_init(void) install_element(RIPNG_NODE, &ripng_no_ipv6_distribute_list_cmd); install_element(RIPNG_NODE, &ripng_allow_ecmp_cmd); + install_element(RIPNG_NODE, &no_ripng_allow_ecmp_cmd); install_element(RIPNG_NODE, &ripng_default_information_originate_cmd); install_element(RIPNG_NODE, &ripng_default_metric_cmd); install_element(RIPNG_NODE, &no_ripng_default_metric_cmd); diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 1fc6f2553e35..a37cb7d092a5 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -17,7 +17,7 @@ #include "zclient.h" #include "command.h" #include "agg_table.h" -#include "thread.h" +#include "frrevent.h" #include "privs.h" #include "vrf.h" #include "lib_errors.h" @@ -136,8 +136,8 @@ static int ripng_if_ipv6_lladdress_check(struct interface *ifp) struct prefix *p; p = connected->address; - if ((p->family == AF_INET6) - && IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) + if ((p->family == AF_INET6) && + IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) count++; } @@ -155,7 +155,7 @@ static int ripng_if_down(struct interface *ifp) ri = ifp->info; - THREAD_OFF(ri->t_wakeup); + EVENT_OFF(ri->t_wakeup); ripng = ri->ripng; @@ -301,7 +301,7 @@ void ripng_interface_clean(struct ripng *ripng) ri->enable_interface = 0; ri->running = 0; - THREAD_OFF(ri->t_wakeup); + EVENT_OFF(ri->t_wakeup); } } @@ -586,13 +586,13 @@ int ripng_enable_if_delete(struct ripng *ripng, const char *ifname) } /* Wake up interface. */ -static void ripng_interface_wakeup(struct thread *t) +static void ripng_interface_wakeup(struct event *t) { struct interface *ifp; struct ripng_interface *ri; /* Get interface. */ - ifp = THREAD_ARG(t); + ifp = EVENT_ARG(t); ri = ifp->info; @@ -634,9 +634,9 @@ static void ripng_connect_set(struct interface *ifp, int set) if (set) { /* Check once more whether this prefix is within a * "network IF_OR_PREF" one */ - if ((ripng_enable_if_lookup(ripng, connected->ifp->name) - >= 0) - || (ripng_enable_network_lookup2(connected) >= 0)) + if ((ripng_enable_if_lookup( + ripng, connected->ifp->name) >= 0) || + (ripng_enable_network_lookup2(connected) >= 0)) ripng_redistribute_add( ripng, ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, &address, @@ -698,8 +698,8 @@ void ripng_enable_apply(struct interface *ifp) zlog_info("RIPng INTERFACE ON %s", ifp->name); /* Add interface wake up thread. */ - thread_add_timer(master, ripng_interface_wakeup, ifp, 1, - &ri->t_wakeup); + event_add_timer(master, ripng_interface_wakeup, ifp, 1, + &ri->t_wakeup); ripng_connect_set(ifp, 1); } else { diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index 14e69834ca0e..9933dae5cd76 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -12,7 +12,7 @@ #include "vty.h" #include "command.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "log.h" #include "prefix.h" #include "if.h" @@ -32,6 +32,8 @@ struct option longopts[] = {{0}}; /* ripngd privileges */ zebra_capabilities_t _caps_p[] = {ZCAP_NET_RAW, ZCAP_BIND, ZCAP_SYS_ADMIN}; +uint32_t zebra_ecmp_count = MULTIPATH_NUM; + struct zebra_privs_t ripngd_privs = { #if defined(FRR_USER) .user = FRR_USER, @@ -48,7 +50,7 @@ struct zebra_privs_t ripngd_privs = { /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; static struct frr_daemon_info ripngd_di; diff --git a/ripngd/ripng_nb.c b/ripngd/ripng_nb.c index 63144d866b26..1c6d7191a3a8 100644 --- a/ripngd/ripng_nb.c +++ b/ripngd/ripng_nb.c @@ -10,6 +10,7 @@ #include "libfrr.h" #include "ripngd/ripng_nb.h" +#include "lib/if_rmap.h" /* clang-format off */ const struct frr_yang_module_info frr_ripngd_info = { @@ -114,6 +115,28 @@ const struct frr_yang_module_info frr_ripngd_info = { .modify = ripngd_instance_redistribute_metric_modify, }, }, + { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map", + .cbs = { + .create = ripngd_instance_if_route_maps_if_route_map_create, + .destroy = ripngd_instance_if_route_maps_if_route_map_destroy, + .cli_show = cli_show_if_route_map, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/in-route-map", + .cbs = { + .modify = ripngd_instance_if_route_maps_if_route_map_in_route_map_modify, + .destroy = ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy, + } + }, + { + .xpath = "/frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map", + .cbs = { + .modify = ripngd_instance_if_route_maps_if_route_map_out_route_map_modify, + .destroy = ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy, + } + }, { .xpath = "/frr-ripngd:ripngd/instance/static-route", .cbs = { diff --git a/ripngd/ripng_nb.h b/ripngd/ripng_nb.h index 675cef7c92b2..1c0e63c24197 100644 --- a/ripngd/ripng_nb.h +++ b/ripngd/ripng_nb.h @@ -39,6 +39,18 @@ int ripngd_instance_redistribute_route_map_destroy( 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_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args); +int ripngd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args); +int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args); +int ripngd_instance_if_route_maps_if_route_map_out_route_map_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); diff --git a/ripngd/ripng_nb_config.c b/ripngd/ripng_nb_config.c index 006bf79ce8d5..0b1bd68eca20 100644 --- a/ripngd/ripng_nb_config.c +++ b/ripngd/ripng_nb_config.c @@ -3,6 +3,7 @@ * Copyright (C) 1998 Kunihiro Ishiguro * Copyright (C) 2018 NetDEF, Inc. * Renato Westphal + * Copyright (C) 2023 LabN Consulting, L.L.C. */ #include <zebra.h> @@ -13,6 +14,7 @@ #include "prefix.h" #include "table.h" #include "command.h" +#include "if_rmap.h" #include "routemap.h" #include "agg_table.h" #include "northbound.h" @@ -127,9 +129,14 @@ int ripngd_instance_allow_ecmp_modify(struct nb_cb_modify_args *args) 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 = + MIN(yang_dnode_get_uint8(args->dnode, NULL), zebra_ecmp_count); + if (!ripng->ecmp) { ripng_ecmp_disable(ripng); + return NB_OK; + } + + ripng_ecmp_change(ripng); return NB_OK; } @@ -502,6 +509,93 @@ int ripngd_instance_redistribute_metric_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +/* + * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map + */ +int ripngd_instance_if_route_maps_if_route_map_create( + struct nb_cb_create_args *args) +{ + /* if_rmap is created when first routemap is added */ + return NB_OK; +} + +int ripngd_instance_if_route_maps_if_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + struct ripng *ripng; + + if (args->event != NB_EV_APPLY) + return NB_OK; + + /* + * YANG will prune edit deletes up to the most general deleted node so + * we need to handle deleting any existing state underneath and not + * count on those more specific callbacks being called individually. + */ + + ripng = nb_running_get_entry(args->dnode, NULL, true); + if_rmap_yang_destroy_cb(ripng->if_rmap_ctx, args->dnode); + + return NB_OK; +} + +static void if_route_map_modify(const struct lyd_node *dnode, + enum if_rmap_type type, bool delete) +{ + struct ripng *ripng = nb_running_get_entry(dnode, NULL, true); + + if_rmap_yang_modify_cb(ripng->if_rmap_ctx, dnode, type, delete); +} +/* + * XPath: /frr-ripng:ripng/instance/if-route-maps/if-route-map/in-route-map + */ +int ripngd_instance_if_route_maps_if_route_map_in_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, false); + + return NB_OK; +} + +int ripngd_instance_if_route_maps_if_route_map_in_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_IN, true); + + return NB_OK; +} + +/* + * XPath: /frr-ripngd:ripngd/instance/if-route-maps/if-route-map/out-route-map + */ +int ripngd_instance_if_route_maps_if_route_map_out_route_map_modify( + struct nb_cb_modify_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, false); + + return NB_OK; +} + +int ripngd_instance_if_route_maps_if_route_map_out_route_map_destroy( + struct nb_cb_destroy_args *args) +{ + if (args->event != NB_EV_APPLY) + return NB_OK; + + if_route_map_modify(args->dnode, IF_RMAP_OUT, true); + + return NB_OK; +} + /* * XPath: /frr-ripngd:ripngd/instance/static-route */ diff --git a/ripngd/ripng_nb_rpcs.c b/ripngd/ripng_nb_rpcs.c index 381ff717c1c6..b23572d49282 100644 --- a/ripngd/ripng_nb_rpcs.c +++ b/ripngd/ripng_nb_rpcs.c @@ -53,8 +53,8 @@ static void clear_ripng_route(struct ripng *ripng) } if (rinfo) { - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); listnode_delete(list, rinfo); ripng_info_free(rinfo); } diff --git a/ripngd/ripng_offset.c b/ripngd/ripng_offset.c index ba3ba558afda..d842f15a194a 100644 --- a/ripngd/ripng_offset.c +++ b/ripngd/ripng_offset.c @@ -83,9 +83,8 @@ int ripng_offset_list_apply_in(struct ripng *ripng, struct prefix_ipv6 *p, alist = access_list_lookup(AFI_IP6, OFFSET_LIST_IN_NAME(offset)); - if (alist - && access_list_apply(alist, (struct prefix *)p) - == FILTER_PERMIT) { + if (alist && access_list_apply(alist, (struct prefix *)p) == + FILTER_PERMIT) { *metric += OFFSET_LIST_IN_METRIC(offset); return 1; } @@ -97,9 +96,8 @@ int ripng_offset_list_apply_in(struct ripng *ripng, struct prefix_ipv6 *p, alist = access_list_lookup(AFI_IP6, OFFSET_LIST_IN_NAME(offset)); - if (alist - && access_list_apply(alist, (struct prefix *)p) - == FILTER_PERMIT) { + if (alist && access_list_apply(alist, (struct prefix *)p) == + FILTER_PERMIT) { *metric += OFFSET_LIST_IN_METRIC(offset); return 1; } @@ -121,9 +119,8 @@ int ripng_offset_list_apply_out(struct ripng *ripng, struct prefix_ipv6 *p, alist = access_list_lookup(AFI_IP6, OFFSET_LIST_OUT_NAME(offset)); - if (alist - && access_list_apply(alist, (struct prefix *)p) - == FILTER_PERMIT) { + if (alist && access_list_apply(alist, (struct prefix *)p) == + FILTER_PERMIT) { *metric += OFFSET_LIST_OUT_METRIC(offset); return 1; } @@ -136,9 +133,8 @@ int ripng_offset_list_apply_out(struct ripng *ripng, struct prefix_ipv6 *p, alist = access_list_lookup(AFI_IP6, OFFSET_LIST_OUT_NAME(offset)); - if (alist - && access_list_apply(alist, (struct prefix *)p) - == FILTER_PERMIT) { + if (alist && access_list_apply(alist, (struct prefix *)p) == + FILTER_PERMIT) { *metric += OFFSET_LIST_OUT_METRIC(offset); return 1; } diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index 75a9ed77fee1..901b548a42a1 100644 --- a/ripngd/ripng_peer.c +++ b/ripngd/ripng_peer.c @@ -13,7 +13,7 @@ #include "prefix.h" #include "command.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "ripngd/ripngd.h" @@ -28,7 +28,7 @@ static struct ripng_peer *ripng_peer_new(void) static void ripng_peer_free(struct ripng_peer *peer) { - THREAD_OFF(peer->t_timeout); + EVENT_OFF(peer->t_timeout); XFREE(MTYPE_RIPNG_PEER, peer); } @@ -60,11 +60,11 @@ struct ripng_peer *ripng_peer_lookup_next(struct ripng *ripng, /* RIPng peer is timeout. * Garbage collector. **/ -static void ripng_peer_timeout(struct thread *t) +static void ripng_peer_timeout(struct event *t) { struct ripng_peer *peer; - peer = THREAD_ARG(t); + peer = EVENT_ARG(t); listnode_delete(peer->ripng->peer_list, peer); ripng_peer_free(peer); } @@ -78,7 +78,7 @@ static struct ripng_peer *ripng_peer_get(struct ripng *ripng, peer = ripng_peer_lookup(ripng, addr); if (peer) { - THREAD_OFF(peer->t_timeout); + EVENT_OFF(peer->t_timeout); } else { peer = ripng_peer_new(); peer->ripng = ripng; @@ -87,8 +87,8 @@ static struct ripng_peer *ripng_peer_get(struct ripng *ripng, } /* Update timeout thread. */ - thread_add_timer(master, ripng_peer_timeout, peer, - RIPNG_PEER_TIMER_DEFAULT, &peer->t_timeout); + event_add_timer(master, ripng_peer_timeout, peer, + RIPNG_PEER_TIMER_DEFAULT, &peer->t_timeout); /* Last update time set. */ time(&peer->uptime); diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 3e6880b4df6f..b5f74be3f613 100644 --- a/ripngd/ripng_routemap.c +++ b/ripngd/ripng_routemap.c @@ -112,6 +112,70 @@ static const struct route_map_rule_cmd route_match_interface_cmd = { route_match_interface_free }; +/* match ipv6 address WORD */ + +static enum route_map_cmd_result_t +route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object) +{ + struct access_list *alist; + + alist = access_list_lookup(AFI_IP6, (char *)rule); + if (access_list_apply(alist, prefix) != FILTER_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void *route_match_ipv6_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ipv6_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +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 address prefix-list PREFIX_LIST */ + +static enum route_map_cmd_result_t +route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix, + void *object) +{ + struct prefix_list *plist; + + plist = prefix_list_lookup(AFI_IP6, (char *)rule); + if (prefix_list_apply(plist, prefix) != PREFIX_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void *route_match_ipv6_address_prefix_list_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void route_match_ipv6_address_prefix_list_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +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 +}; + /* `match tag TAG' */ /* Match function return 1 if match is success else return zero. */ static enum route_map_cmd_result_t route_match_tag(void *rule, @@ -327,6 +391,12 @@ void ripng_route_map_init(void) route_map_match_interface_hook(generic_match_add); route_map_no_match_interface_hook(generic_match_delete); + route_map_match_ipv6_address_hook(generic_match_add); + route_map_no_match_ipv6_address_hook(generic_match_delete); + + 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_metric_hook(generic_match_add); route_map_no_match_metric_hook(generic_match_delete); @@ -344,6 +414,8 @@ void ripng_route_map_init(void) route_map_install_match(&route_match_metric_cmd); route_map_install_match(&route_match_interface_cmd); + route_map_install_match(&route_match_ipv6_address_cmd); + route_map_install_match(&route_match_ipv6_address_prefix_list_cmd); route_map_install_match(&route_match_tag_cmd); route_map_install_set(&route_set_metric_cmd); route_map_install_set(&route_set_ipv6_nexthop_local_cmd); diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index b126a15b5bf1..49b8a197add1 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -30,7 +30,7 @@ static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp, struct zapi_nexthop *api_nh; struct listnode *listnode = NULL; struct ripng_info *rinfo = NULL; - int count = 0; + uint32_t count = 0; const struct prefix *p = agg_node_get_prefix(rp); memset(&api, 0, sizeof(api)); @@ -41,7 +41,7 @@ static void ripng_zebra_ipv6_send(struct ripng *ripng, struct agg_node *rp, SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - if (count >= MULTIPATH_NUM) + if (count >= zebra_ecmp_count) break; api_nh = &api.nexthops[count]; api_nh->vrf_id = ripng->vrf->vrf_id; @@ -227,8 +227,13 @@ static zclient_handler *const ripng_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ripng_zebra_read_route, }; +static void ripng_zebra_capabilities(struct zclient_capabilities *cap) +{ + zebra_ecmp_count = MIN(cap->ecmp, zebra_ecmp_count); +} + /* Initialize zebra structure and it's commands. */ -void zebra_init(struct thread_master *master) +void zebra_init(struct event_loop *master) { /* Allocate zebra structure. */ zclient = zclient_new(master, &zclient_options_default, ripng_handlers, @@ -236,6 +241,7 @@ void zebra_init(struct thread_master *master) zclient_init(zclient, ZEBRA_ROUTE_RIPNG, 0, &ripngd_privs); zclient->zebra_connected = ripng_zebra_connected; + zclient->zebra_capabilities = ripng_zebra_capabilities; } void ripng_zebra_stop(void) diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index f01371f41ebb..465b40bd3f61 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -8,7 +8,7 @@ #include "prefix.h" #include "filter.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "memory.h" #include "if.h" #include "stream.h" @@ -46,7 +46,7 @@ void ripng_output_process(struct interface *, struct sockaddr_in6 *, int); static void ripng_instance_enable(struct ripng *ripng, struct vrf *vrf, int sock); static void ripng_instance_disable(struct ripng *ripng); -static void ripng_triggered_update(struct thread *); +static void ripng_triggered_update(struct event *); static void ripng_if_rmap_update(struct if_rmap_ctx *ctx, struct if_rmap *if_rmap); @@ -408,15 +408,15 @@ static int ripng_lladdr_check(struct interface *ifp, struct in6_addr *addr) } /* RIPng route garbage collect timer. */ -static void ripng_garbage_collect(struct thread *t) +static void ripng_garbage_collect(struct event *t) { struct ripng_info *rinfo; struct agg_node *rp; - rinfo = THREAD_ARG(t); + rinfo = EVENT_ARG(t); /* Off timeout timer. */ - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); /* Get route_node pointer. */ rp = rinfo->rp; @@ -443,7 +443,10 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng, { struct agg_node *rp = rinfo_new->rp; struct ripng_info *rinfo = NULL; + struct ripng_info *rinfo_exist = NULL; struct list *list = NULL; + struct listnode *node = NULL; + struct listnode *nnode = NULL; if (rp->info == NULL) rp->info = list_new(); @@ -454,6 +457,33 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng, if (listcount(list) && !ripng->ecmp) return NULL; + /* Add or replace an existing ECMP path with lower neighbor IP */ + if (listcount(list) && listcount(list) >= ripng->ecmp) { + struct ripng_info *from_highest = NULL; + + /* Find the rip_info struct that has the highest nexthop IP */ + for (ALL_LIST_ELEMENTS(list, node, nnode, rinfo_exist)) + if (!from_highest || + (from_highest && + IPV6_ADDR_CMP(&rinfo_exist->from, + &from_highest->from) > 0)) { + from_highest = rinfo_exist; + } + + /* If we have a route in ECMP group, delete the old + * one that has a higher next-hop address. Lower IP is + * preferred. + */ + if (ripng->ecmp > 1 && from_highest && + IPV6_ADDR_CMP(&from_highest->from, &rinfo_new->from) > 0) { + ripng_ecmp_delete(ripng, from_highest); + goto add_or_replace; + } + + return NULL; + } + +add_or_replace: rinfo = ripng_info_new(); memcpy(rinfo, rinfo_new, sizeof(struct ripng_info)); listnode_add(list, rinfo); @@ -475,6 +505,36 @@ struct ripng_info *ripng_ecmp_add(struct ripng *ripng, return rinfo; } +/* Update ECMP routes to zebra when `allow-ecmp` changed. */ +void ripng_ecmp_change(struct ripng *ripng) +{ + struct agg_node *rp; + struct ripng_info *rinfo; + struct list *list; + struct listnode *node, *nextnode; + + for (rp = agg_route_top(ripng->table); rp; rp = agg_route_next(rp)) { + list = rp->info; + if (list && listcount(list) > 1) { + while (listcount(list) > ripng->ecmp) { + struct ripng_info *from_highest = NULL; + + for (ALL_LIST_ELEMENTS(list, node, nextnode, + rinfo)) { + if (!from_highest || + (from_highest && + IPV6_ADDR_CMP( + &rinfo->from, + &from_highest->from) > 0)) + from_highest = rinfo; + } + + ripng_ecmp_delete(ripng, from_highest); + } + } + } +} + /* Replace the ECMP list with the new route. * RETURN: the new entry added in the list */ @@ -503,14 +563,14 @@ struct ripng_info *ripng_ecmp_replace(struct ripng *ripng, /* Re-use the first entry, and delete the others. */ for (ALL_LIST_ELEMENTS(list, node, nextnode, tmp_rinfo)) if (tmp_rinfo != rinfo) { - THREAD_OFF(tmp_rinfo->t_timeout); - THREAD_OFF(tmp_rinfo->t_garbage_collect); + EVENT_OFF(tmp_rinfo->t_timeout); + EVENT_OFF(tmp_rinfo->t_garbage_collect); list_delete_node(list, node); ripng_info_free(tmp_rinfo); } - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); memcpy(rinfo, rinfo_new, sizeof(struct ripng_info)); if (ripng_route_rte(rinfo)) { @@ -542,7 +602,7 @@ struct ripng_info *ripng_ecmp_delete(struct ripng *ripng, struct agg_node *rp = rinfo->rp; struct list *list = (struct list *)rp->info; - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); if (rinfo->metric != RIPNG_METRIC_INFINITY) ripng_aggregate_decrement(rp, rinfo); @@ -550,10 +610,10 @@ struct ripng_info *ripng_ecmp_delete(struct ripng *ripng, if (listcount(list) > 1) { /* Some other ECMP entries still exist. Just delete this entry. */ - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_garbage_collect); listnode_delete(list, rinfo); - if (ripng_route_rte(rinfo) - && CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB)) + if (ripng_route_rte(rinfo) && + CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB)) /* The ADD message implies the update. */ ripng_zebra_ipv6_add(ripng, rp); ripng_info_free(rinfo); @@ -569,8 +629,8 @@ struct ripng_info *ripng_ecmp_delete(struct ripng *ripng, RIPNG_TIMER_ON(rinfo->t_garbage_collect, ripng_garbage_collect, ripng->garbage_time); - if (ripng_route_rte(rinfo) - && CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB)) + if (ripng_route_rte(rinfo) && + CHECK_FLAG(rinfo->flags, RIPNG_RTF_FIB)) ripng_zebra_ipv6_delete(ripng, rp); } @@ -585,9 +645,9 @@ struct ripng_info *ripng_ecmp_delete(struct ripng *ripng, } /* Timeout RIPng routes. */ -static void ripng_timeout(struct thread *t) +static void ripng_timeout(struct event *t) { - struct ripng_info *rinfo = THREAD_ARG(t); + struct ripng_info *rinfo = EVENT_ARG(t); struct ripng *ripng = ripng_info_get_instance(rinfo); ripng_ecmp_delete(ripng, rinfo); @@ -596,9 +656,9 @@ static void ripng_timeout(struct thread *t) static void ripng_timeout_update(struct ripng *ripng, struct ripng_info *rinfo) { if (rinfo->metric != RIPNG_METRIC_INFINITY) { - THREAD_OFF(rinfo->t_timeout); - thread_add_timer(master, ripng_timeout, rinfo, - ripng->timeout_time, &rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); + event_add_timer(master, ripng_timeout, rinfo, + ripng->timeout_time, &rinfo->t_timeout); } } @@ -616,8 +676,7 @@ static int ripng_filter(int ripng_distribute, struct prefix_ipv6 *p, /* Input distribute-list filtering. */ if (ri->list[ripng_distribute]) { if (access_list_apply(ri->list[ripng_distribute], - (struct prefix *)p) - == FILTER_DENY) { + (struct prefix *)p) == FILTER_DENY) { if (IS_RIPNG_DEBUG_PACKET) zlog_debug("%pFX filtered by distribute %s", p, inout); @@ -626,8 +685,7 @@ static int ripng_filter(int ripng_distribute, struct prefix_ipv6 *p, } if (ri->prefix[ripng_distribute]) { if (prefix_list_apply(ri->prefix[ripng_distribute], - (struct prefix *)p) - == PREFIX_DENY) { + (struct prefix *)p) == PREFIX_DENY) { if (IS_RIPNG_DEBUG_PACKET) zlog_debug("%pFX filtered by prefix-list %s", p, inout); @@ -758,9 +816,8 @@ static void ripng_route_process(struct rte *rte, struct sockaddr_in6 *from, } } rte->tag = htons(newinfo.tag_out); /* XXX */ - rte->metric = - newinfo.metric_out; /* XXX: the routemap uses the - metric_out field */ + rte->metric = newinfo.metric_out; /* XXX: the routemap uses the + metric_out field */ } /* Once the entry has been validated, update the metric by @@ -877,18 +934,18 @@ static void ripng_route_process(struct rte *rte, struct sockaddr_in6 *from, * but * highly recommended". */ - if (!ripng->ecmp && !same && rinfo->metric == rte->metric - && rinfo->t_timeout - && (thread_timer_remain_second(rinfo->t_timeout) - < (ripng->timeout_time / 2))) { + if (!ripng->ecmp && !same && rinfo->metric == rte->metric && + rinfo->t_timeout && + (event_timer_remain_second(rinfo->t_timeout) < + (ripng->timeout_time / 2))) { ripng_ecmp_replace(ripng, &newinfo); } /* Next, compare the metrics. If the datagram is from the same router as the existing route, and the new metric is different than the old one; or, if the new metric is lower than the old one; do the following actions: */ - else if ((same && rinfo->metric != rte->metric) - || rte->metric < rinfo->metric) { + else if ((same && rinfo->metric != rte->metric) || + rte->metric < rinfo->metric) { if (listcount(list) == 1) { if (newinfo.metric != RIPNG_METRIC_INFINITY) ripng_ecmp_replace(ripng, &newinfo); @@ -1007,12 +1064,12 @@ void ripng_redistribute_delete(struct ripng *ripng, int type, int sub_type, RIPNG_TIMER_ON(rinfo->t_garbage_collect, ripng_garbage_collect, ripng->garbage_time); - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); /* Aggregate count decrement. */ ripng_aggregate_decrement(rp, rinfo); - rinfo->flags |= RIPNG_RTF_CHANGED; + SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED); if (IS_RIPNG_DEBUG_EVENT) zlog_debug( @@ -1046,12 +1103,12 @@ void ripng_redistribute_withdraw(struct ripng *ripng, int type) RIPNG_TIMER_ON(rinfo->t_garbage_collect, ripng_garbage_collect, ripng->garbage_time); - THREAD_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_timeout); /* Aggregate count decrement. */ ripng_aggregate_decrement(rp, rinfo); - rinfo->flags |= RIPNG_RTF_CHANGED; + SET_FLAG(rinfo->flags, RIPNG_RTF_CHANGED); if (IS_RIPNG_DEBUG_EVENT) { struct prefix_ipv6 *p = @@ -1282,9 +1339,9 @@ static void ripng_request_process(struct ripng_packet *packet, int size, } /* First entry point of reading RIPng packet. */ -static void ripng_read(struct thread *thread) +static void ripng_read(struct event *thread) { - struct ripng *ripng = THREAD_ARG(thread); + struct ripng *ripng = EVENT_ARG(thread); int len; int sock; struct sockaddr_in6 from; @@ -1299,7 +1356,7 @@ static void ripng_read(struct thread *thread) /* Fetch thread data and set read pointer to empty for event managing. `sock' sould be same as ripng->sock. */ - sock = THREAD_FD(thread); + sock = EVENT_FD(thread); /* Add myself to the next event. */ ripng_event(ripng, RIPNG_READ, sock); @@ -1390,9 +1447,9 @@ static void ripng_clear_changed_flag(struct ripng *ripng) /* Regular update of RIPng route. Send all routing formation to RIPng enabled interface. */ -static void ripng_update(struct thread *t) +static void ripng_update(struct event *t) { - struct ripng *ripng = THREAD_ARG(t); + struct ripng *ripng = EVENT_ARG(t); struct interface *ifp; struct ripng_interface *ri; @@ -1430,7 +1487,7 @@ static void ripng_update(struct thread *t) /* Triggered updates may be suppressed if a regular update is due by the time the triggered update would be sent. */ - THREAD_OFF(ripng->t_triggered_interval); + EVENT_OFF(ripng->t_triggered_interval); ripng->trigger = 0; /* Reset flush event. */ @@ -1438,9 +1495,9 @@ static void ripng_update(struct thread *t) } /* Triggered update interval timer. */ -static void ripng_triggered_interval(struct thread *t) +static void ripng_triggered_interval(struct event *t) { - struct ripng *ripng = THREAD_ARG(t); + struct ripng *ripng = EVENT_ARG(t); if (ripng->trigger) { ripng->trigger = 0; @@ -1449,15 +1506,15 @@ static void ripng_triggered_interval(struct thread *t) } /* Execute triggered update. */ -void ripng_triggered_update(struct thread *t) +void ripng_triggered_update(struct event *t) { - struct ripng *ripng = THREAD_ARG(t); + struct ripng *ripng = EVENT_ARG(t); struct interface *ifp; struct ripng_interface *ri; int interval; /* Cancel interval timer. */ - THREAD_OFF(ripng->t_triggered_interval); + EVENT_OFF(ripng->t_triggered_interval); ripng->trigger = 0; /* Logging triggered update. */ @@ -1493,8 +1550,8 @@ void ripng_triggered_update(struct thread *t) update is triggered when the timer expires. */ interval = (frr_weak_random() % 5) + 1; - thread_add_timer(master, ripng_triggered_interval, ripng, interval, - &ripng->t_triggered_interval); + event_add_timer(master, ripng_triggered_interval, ripng, interval, + &ripng->t_triggered_interval); } /* Write routing table entry to the stream and return next index of @@ -1585,8 +1642,8 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, continue; /* Changed route only output. */ - if (route_type == ripng_changed_route - && (!(rinfo->flags & RIPNG_RTF_CHANGED))) + if (route_type == ripng_changed_route && + (!CHECK_FLAG(rinfo->flags, RIPNG_RTF_CHANGED))) continue; /* Split horizon. */ @@ -1597,9 +1654,10 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, for (ALL_LIST_ELEMENTS_RO(list, listnode, tmp_rinfo)) - if (tmp_rinfo->type == ZEBRA_ROUTE_RIPNG - && tmp_rinfo->ifindex - == ifp->ifindex) { + if (tmp_rinfo->type == + ZEBRA_ROUTE_RIPNG && + tmp_rinfo->ifindex == + ifp->ifindex) { suppress = 1; break; } @@ -1657,11 +1715,11 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, /* If the route is not connected or localy generated one, use default-metric value */ - if (rinfo->type != ZEBRA_ROUTE_RIPNG - && rinfo->type - != ZEBRA_ROUTE_CONNECT - && rinfo->metric - != RIPNG_METRIC_INFINITY) + if (rinfo->type != ZEBRA_ROUTE_RIPNG && + rinfo->type != + ZEBRA_ROUTE_CONNECT && + rinfo->metric != + RIPNG_METRIC_INFINITY) rinfo->metric_out = ripng->default_metric; } @@ -1678,16 +1736,15 @@ void ripng_output_process(struct interface *ifp, struct sockaddr_in6 *to, /* Perform split-horizon with poisoned reverse * for RIPng routes. **/ - if (ri->split_horizon - == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) { + if (ri->split_horizon == + RIPNG_SPLIT_HORIZON_POISONED_REVERSE) { struct ripng_info *tmp_rinfo = NULL; for (ALL_LIST_ELEMENTS_RO(list, listnode, tmp_rinfo)) - if ((tmp_rinfo->type - == ZEBRA_ROUTE_RIPNG) - && tmp_rinfo->ifindex - == ifp->ifindex) + if ((tmp_rinfo->type == + ZEBRA_ROUTE_RIPNG) && + tmp_rinfo->ifindex == ifp->ifindex) rinfo->metric_out = RIPNG_METRIC_INFINITY; } @@ -1814,7 +1871,7 @@ struct ripng *ripng_create(const char *vrf_name, struct vrf *vrf, int socket) "%s/timers/flush-interval", RIPNG_INSTANCE); ripng->default_metric = yang_get_default_uint8("%s/default-metric", RIPNG_INSTANCE); - ripng->ecmp = yang_get_default_bool("%s/allow-ecmp", RIPNG_INSTANCE); + ripng->ecmp = yang_get_default_uint8("%s/allow-ecmp", RIPNG_INSTANCE); /* Make buffer. */ ripng->ibuf = stream_new(RIPNG_MAX_PACKET_SIZE * 5); @@ -1898,25 +1955,24 @@ void ripng_event(struct ripng *ripng, enum ripng_event event, int sock) switch (event) { case RIPNG_READ: - thread_add_read(master, ripng_read, ripng, sock, - &ripng->t_read); + event_add_read(master, ripng_read, ripng, sock, &ripng->t_read); break; case RIPNG_UPDATE_EVENT: - THREAD_OFF(ripng->t_update); + EVENT_OFF(ripng->t_update); /* Update timer jitter. */ jitter = ripng_update_jitter(ripng->update_time); - thread_add_timer(master, ripng_update, ripng, - sock ? 2 : ripng->update_time + jitter, - &ripng->t_update); + event_add_timer(master, ripng_update, ripng, + sock ? 2 : ripng->update_time + jitter, + &ripng->t_update); break; case RIPNG_TRIGGERED_UPDATE: if (ripng->t_triggered_interval) ripng->trigger = 1; else - thread_add_event(master, ripng_triggered_update, ripng, - 0, &ripng->t_triggered_update); + event_add_event(master, ripng_triggered_update, ripng, + 0, &ripng->t_triggered_update); break; case RIPNG_ZEBRA: case RIPNG_REQUEST_EVENT: @@ -1932,15 +1988,15 @@ static void ripng_vty_out_uptime(struct vty *vty, struct ripng_info *rinfo) struct tm tm; #define TIME_BUF 25 char timebuf[TIME_BUF]; - struct thread *thread; + struct event *thread; if ((thread = rinfo->t_timeout) != NULL) { - clock = thread_timer_remain_second(thread); + clock = event_timer_remain_second(thread); 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); + clock = event_timer_remain_second(thread); gmtime_r(&clock, &tm); strftime(timebuf, TIME_BUF, "%M:%S", &tm); vty_out(vty, "%5s", timebuf); @@ -2129,7 +2185,7 @@ DEFUN (show_ipv6_ripng_status, vty_out(vty, " Sending updates every %u seconds with +/-50%%,", ripng->update_time); vty_out(vty, " next due in %lu seconds\n", - thread_timer_remain_second(ripng->t_update)); + event_timer_remain_second(ripng->t_update)); vty_out(vty, " Timeout after %u seconds,", ripng->timeout_time); vty_out(vty, " garbage collect after %u seconds\n", ripng->garbage_time); @@ -2195,9 +2251,8 @@ void ripng_ecmp_disable(struct ripng *ripng) /* Drop all other entries, except the first one. */ for (ALL_LIST_ELEMENTS(list, node, nextnode, tmp_rinfo)) if (tmp_rinfo != rinfo) { - THREAD_OFF(tmp_rinfo->t_timeout); - THREAD_OFF( - tmp_rinfo->t_garbage_collect); + EVENT_OFF(tmp_rinfo->t_timeout); + EVENT_OFF(tmp_rinfo->t_garbage_collect); list_delete_node(list, node); ripng_info_free(tmp_rinfo); } @@ -2233,7 +2288,6 @@ static int ripng_config_write(struct vty *vty) nb_cli_show_dnode_cmds(vty, dnode, false); config_write_distribute(vty, ripng->distribute_ctx); - config_write_if_rmap(vty, ripng->if_rmap_ctx); vty_out(vty, "exit\n"); @@ -2514,8 +2568,8 @@ static void ripng_instance_disable(struct ripng *ripng) ripng_zebra_ipv6_delete(ripng, rp); for (ALL_LIST_ELEMENTS_RO(list, listnode, rinfo)) { - THREAD_OFF(rinfo->t_timeout); - THREAD_OFF(rinfo->t_garbage_collect); + EVENT_OFF(rinfo->t_timeout); + EVENT_OFF(rinfo->t_garbage_collect); ripng_info_free(rinfo); } list_delete(&list); @@ -2534,12 +2588,12 @@ static void ripng_instance_disable(struct ripng *ripng) ripng_redistribute_disable(ripng); /* Cancel the RIPng timers */ - THREAD_OFF(ripng->t_update); - THREAD_OFF(ripng->t_triggered_update); - THREAD_OFF(ripng->t_triggered_interval); + EVENT_OFF(ripng->t_update); + EVENT_OFF(ripng->t_triggered_update); + EVENT_OFF(ripng->t_triggered_interval); /* Cancel the read thread */ - THREAD_OFF(ripng->t_read); + EVENT_OFF(ripng->t_read); /* Close the RIPng socket */ if (ripng->sock >= 0) { @@ -2567,17 +2621,10 @@ static int ripng_vrf_new(struct vrf *vrf) static int ripng_vrf_delete(struct vrf *vrf) { - struct ripng *ripng; - if (IS_RIPNG_DEBUG_EVENT) zlog_debug("%s: VRF deleted: %s(%u)", __func__, vrf->name, vrf->vrf_id); - ripng = ripng_lookup_by_vrf_name(vrf->name); - if (!ripng) - return 0; - - ripng_clean(ripng); return 0; } diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index 59b948b8f1f6..c7468b6317c5 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -121,16 +121,16 @@ struct ripng { struct list *offset_list_master; /* RIPng threads. */ - struct thread *t_read; - struct thread *t_update; + struct event *t_read; + struct event *t_update; /* Triggered update hack. */ int trigger; - struct thread *t_triggered_update; - struct thread *t_triggered_interval; + struct event *t_triggered_update; + struct event *t_triggered_interval; /* RIPng ECMP flag */ - bool ecmp; + uint8_t ecmp; /* RIPng redistribute configuration. */ struct { @@ -200,8 +200,8 @@ struct ripng_info { uint8_t flags; /* Garbage collect timer. */ - struct thread *t_timeout; - struct thread *t_garbage_collect; + struct event *t_timeout; + struct event *t_garbage_collect; /* Route-map features - this variables can be changed. */ struct in6_addr nexthop_out; @@ -254,7 +254,7 @@ struct ripng_interface { uint8_t default_only; /* Wake up thread. */ - struct thread *t_wakeup; + struct event *t_wakeup; /* Passive interface. */ int passive; @@ -282,7 +282,7 @@ struct ripng_peer { int recv_badroutes; /* Timeout thread. */ - struct thread *t_timeout; + struct event *t_timeout; }; /* All RIPng events. */ @@ -295,7 +295,7 @@ enum ripng_event { }; /* RIPng timer on/off macro. */ -#define RIPNG_TIMER_ON(T,F,V) thread_add_timer (master, (F), rinfo, (V), &(T)) +#define RIPNG_TIMER_ON(T, F, V) event_add_timer(master, (F), rinfo, (V), &(T)) #define RIPNG_OFFSET_LIST_IN 0 #define RIPNG_OFFSET_LIST_OUT 1 @@ -316,7 +316,7 @@ struct ripng_offset_list { /* Extern variables. */ extern struct zebra_privs_t ripngd_privs; -extern struct thread_master *master; +extern struct event_loop *master; extern struct ripng_instance_head ripng_instances; /* Prototypes. */ @@ -338,7 +338,7 @@ extern void ripng_zebra_vrf_register(struct vrf *vrf); extern void ripng_zebra_vrf_deregister(struct vrf *vrf); extern void ripng_terminate(void); /* zclient_init() is done by ripng_zebra.c:zebra_init() */ -extern void zebra_init(struct thread_master *); +extern void zebra_init(struct event_loop *master); extern void ripng_zebra_stop(void); extern void ripng_redistribute_conf_update(struct ripng *ripng, int type); extern void ripng_redistribute_conf_delete(struct ripng *ripng, int type); @@ -429,9 +429,12 @@ extern struct ripng_info *ripng_ecmp_replace(struct ripng *ripng, struct ripng_info *rinfo); extern struct ripng_info *ripng_ecmp_delete(struct ripng *ripng, struct ripng_info *rinfo); +extern void ripng_ecmp_change(struct ripng *ripng); extern void ripng_vrf_init(void); extern void ripng_vrf_terminate(void); extern void ripng_cli_init(void); +extern uint32_t zebra_ecmp_count; + #endif /* _ZEBRA_RIPNG_RIPNGD_H */ diff --git a/sharpd/sharp_logpump.c b/sharpd/sharp_logpump.c index cadd818953b3..5474e80b116a 100644 --- a/sharpd/sharp_logpump.c +++ b/sharpd/sharp_logpump.c @@ -11,7 +11,7 @@ #include "prefix.h" #include "nexthop.h" #include "log.h" -#include "thread.h" +#include "frrevent.h" #include "vrf.h" #include "zclient.h" #include "frr_pthread.h" @@ -28,9 +28,9 @@ static size_t lp_ctr, lp_expect; static struct rusage lp_rusage; static struct vty *lp_vty; -extern struct thread_master *master; +extern struct event_loop *master; -static void logpump_done(struct thread *thread) +static void logpump_done(struct event *thread) { double x; @@ -105,7 +105,7 @@ static void *logpump_run(void *arg) getrusage(RUSAGE_SELF, &lp_rusage); #endif - thread_add_timer_msec(master, logpump_done, NULL, 0, NULL); + event_add_timer_msec(master, logpump_done, NULL, 0, NULL); return NULL; } diff --git a/sharpd/sharp_main.c b/sharpd/sharp_main.c index 9d33fc89a7e0..fa85c2b44839 100644 --- a/sharpd/sharp_main.c +++ b/sharpd/sharp_main.c @@ -8,7 +8,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "prefix.h" #include "linklist.h" #include "if.h" @@ -55,7 +55,7 @@ struct zebra_privs_t sharp_privs = { struct option longopts[] = {{0}}; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* SIGHUP handler. */ static void sighup(void) diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c index 77b562d6a61e..f0a75a5fc23a 100644 --- a/sharpd/sharp_vty.c +++ b/sharpd/sharp_vty.c @@ -179,7 +179,7 @@ DEFPY (install_routes, <nexthop <A.B.C.D$nexthop4|X:X::X:X$nexthop6>|\ nexthop-group NHGNAME$nexthop_group>\ [backup$backup <A.B.C.D$backup_nexthop4|X:X::X:X$backup_nexthop6>] \ - (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt] [opaque WORD]", + (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt] [opaque WORD] [no-recurse$norecurse]", "Sharp routing Protocol\n" "install some routes\n" "Routes to install\n" @@ -201,7 +201,8 @@ DEFPY (install_routes, "Should we repeat this command\n" "How many times to repeat this command\n" "What opaque data to send down\n" - "The opaque data\n") + "The opaque data\n" + "No recursive nexthops\n") { struct vrf *vrf; struct prefix prefix; @@ -210,6 +211,7 @@ DEFPY (install_routes, sg.r.total_routes = routes; sg.r.installed_routes = 0; + sg.r.flags = 0; if (rpt >= 2) sg.r.repeat = rpt * 2; @@ -317,12 +319,16 @@ DEFPY (install_routes, else sg.r.opaque[0] = '\0'; + /* Default is to ask for recursive nexthop resolution */ + if (norecurse == NULL) + SET_FLAG(sg.r.flags, ZEBRA_FLAG_ALLOW_RECURSION); + 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, nhgid, &sg.r.nhop_group, &sg.r.backup_nhop_group, - rts, 0, sg.r.opaque); + rts, sg.r.flags, sg.r.opaque); return CMD_SUCCESS; } @@ -865,6 +871,24 @@ DEFPY (send_opaque_reg, return CMD_SUCCESS; } +/* Opaque notifications - register or unregister */ +DEFPY (send_opaque_notif_reg, + send_opaque_notif_reg_cmd, + "sharp send opaque notify <reg$reg | unreg> type (1-1000)", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Opaque notification messages\n" + "Send notify registration\n" + "Send notify unregistration\n" + "Opaque sub-type code\n" + "Opaque sub-type code\n") +{ + sharp_zebra_opaque_notif_reg((reg != NULL), type); + + return CMD_SUCCESS; +} + DEFPY (neigh_discover, neigh_discover_cmd, "sharp neigh discover [vrf NAME$vrf_name] <A.B.C.D$dst4|X:X::X:X$dst6> IFNAME$ifname", @@ -985,6 +1009,7 @@ DEFUN (show_sharp_ted, struct ls_edge *edge; struct ls_subnet *subnet; uint64_t key; + struct ls_edge_key ekey; bool verbose = false; bool uj = use_json(argc, argv); json_object *json = NULL; @@ -1035,8 +1060,9 @@ DEFUN (show_sharp_ted, return CMD_WARNING_CONFIG_FAILED; } /* Get the Edge from the Link State Database */ - key = ((uint64_t)ip_addr.s_addr) & 0xffffffff; - edge = ls_find_edge_by_key(sg.ted, key); + ekey.family = AF_INET; + IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr); + edge = ls_find_edge_by_key(sg.ted, ekey); if (!edge) { vty_out(vty, "No edge found for ID %pI4\n", &ip_addr); @@ -1059,7 +1085,7 @@ DEFUN (show_sharp_ted, return CMD_WARNING_CONFIG_FAILED; } /* Get the Subnet from the Link State Database */ - subnet = ls_find_subnet(sg.ted, pref); + subnet = ls_find_subnet(sg.ted, &pref); if (!subnet) { vty_out(vty, "No subnet found for ID %pFX\n", &pref); @@ -1245,6 +1271,7 @@ DEFPY (show_sharp_cspf, } if (path->status != SUCCESS) { vty_out(vty, "Path computation failed: %d\n", path->status); + cpath_del(path); return CMD_SUCCESS; } @@ -1260,7 +1287,7 @@ DEFPY (show_sharp_cspf, &edge->attributes->standard.remote6); } vty_out(vty, "\n"); - + cpath_del(path); return CMD_SUCCESS; } @@ -1403,6 +1430,7 @@ void sharp_vty_init(void) 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, &send_opaque_notif_reg_cmd); install_element(ENABLE_NODE, &neigh_discover_cmd); install_element(ENABLE_NODE, &import_te_cmd); diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index df06f5537e78..c095fec17b20 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -6,7 +6,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "network.h" #include "prefix.h" @@ -26,7 +26,7 @@ struct zclient *zclient = NULL; /* For registering threads. */ -extern struct thread_master *master; +extern struct event_loop *master; /* Privs info */ extern struct zebra_privs_t sharp_privs; @@ -247,7 +247,6 @@ static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance, memcpy(&api.prefix, p, sizeof(*p)); api.flags = flags; - SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); /* Only send via ID if nhgroup has been successfully installed */ @@ -790,7 +789,7 @@ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) zclient->session_id, info.type); if (info.type == LINK_STATE_UPDATE) { - lse = ls_stream2ted(sg.ted, s, false); + lse = ls_stream2ted(sg.ted, s, true); if (lse) { zlog_debug(" |- Got %s %s from Link State Database", status2txt[lse->status], @@ -805,6 +804,28 @@ static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) return 0; } +/* Handler for opaque notification messages */ +static int sharp_opq_notify_handler(ZAPI_CALLBACK_ARGS) +{ + struct stream *s; + struct zapi_opaque_notif_info info; + + s = zclient->ibuf; + + if (zclient_opaque_notif_decode(s, &info) != 0) + return -1; + + if (info.reg) + zlog_debug("%s: received opaque notification REG, type %u => %d/%d/%d", + __func__, info.msg_type, info.proto, info.instance, + info.session_id); + else + zlog_debug("%s: received opaque notification UNREG, type %u", + __func__, info.msg_type); + + return 0; +} + /* * Send OPAQUE messages, using subtype 'type'. */ @@ -840,6 +861,17 @@ void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, } } +/* + * Register/unregister for opaque notifications from zebra about 'type'. + */ +void sharp_zebra_opaque_notif_reg(bool is_reg, uint32_t type) +{ + if (is_reg) + zclient_opaque_request_notify(zclient, type); + else + zclient_opaque_drop_notify(zclient, type); +} + /* * Send OPAQUE registration messages, using subtype 'type'. */ @@ -1036,6 +1068,7 @@ static zclient_handler *const sharp_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = sharp_redistribute_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = sharp_redistribute_route, [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler, + [ZEBRA_OPAQUE_NOTIFY] = sharp_opq_notify_handler, [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = sharp_zebra_process_srv6_locator_chunk, }; diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h index cf8689799aa5..025b4d8f8257 100644 --- a/sharpd/sharp_zebra.h +++ b/sharpd/sharp_zebra.h @@ -39,10 +39,15 @@ int sharp_install_lsps_helper(bool install_p, bool update_p, 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'. */ +/* Send OPAQUE registration or notification registration messages, + * for opaque subtype 'type'. + */ void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, uint32_t session_id, uint32_t type); +/* Register/unregister for opaque notifications from zebra about 'type'. */ +void sharp_zebra_opaque_notif_reg(bool is_reg, uint32_t type); + extern void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p); diff --git a/staticd/static_bfd.c b/staticd/static_bfd.c index ccf7d206f2a5..507c64e6a4c9 100644 --- a/staticd/static_bfd.c +++ b/staticd/static_bfd.c @@ -88,6 +88,7 @@ void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn, bool mhop; int family; struct ipaddr source; + struct vrf *vrf = NULL; use_interface = false; use_source = yang_dnode_exists(dnode, "./source"); @@ -95,7 +96,7 @@ void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn, onlink = yang_dnode_exists(dnode, "../onlink") && yang_dnode_get_bool(dnode, "../onlink"); mhop = yang_dnode_get_bool(dnode, "./multi-hop"); - + vrf = vrf_lookup_by_name(yang_dnode_get_string(dnode, "../vrf")); family = static_next_hop_type_to_family(sn); if (family == AF_UNSPEC) @@ -133,6 +134,8 @@ void static_next_hop_bfd_monitor_enable(struct static_nexthop *sn, bfd_sess_set_profile(sn->bsp, use_profile ? yang_dnode_get_string( dnode, "./profile") : NULL); + if (vrf && vrf->vrf_id != VRF_UNKNOWN) + bfd_sess_set_vrf(sn->bsp, vrf->vrf_id); bfd_sess_set_hop_count(sn->bsp, (onlink || mhop == false) ? 1 : 254); @@ -201,7 +204,7 @@ void static_next_hop_bfd_profile(struct static_nexthop *sn, const char *name) bfd_sess_install(sn->bsp); } -void static_bfd_initialize(struct zclient *zc, struct thread_master *tm) +void static_bfd_initialize(struct zclient *zc, struct event_loop *tm) { /* Initialize BFD integration library. */ bfd_protocol_integration_init(zc, tm); diff --git a/staticd/static_main.c b/staticd/static_main.c index bc501b3d6947..165fb4d65204 100644 --- a/staticd/static_main.c +++ b/staticd/static_main.c @@ -8,7 +8,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "log.h" #include "memory.h" @@ -27,6 +27,8 @@ #include "static_debug.h" #include "static_nb.h" +#include "mgmt_be_client.h" + char backup_config_file[256]; bool mpls_enabled; @@ -49,14 +51,16 @@ struct zebra_privs_t static_privs = { struct option longopts[] = { { 0 } }; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; + +struct mgmt_be_client *mgmt_be_client; 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); + zlog_info("SIGHUP received and ignored"); } /* SIGINT / SIGTERM handler. */ @@ -64,6 +68,11 @@ static void sigint(void) { zlog_notice("Terminating on signal"); + /* Disable BFD events to avoid wasting processing. */ + bfd_protocol_integration_set_shutdown(true); + + mgmt_be_client_destroy(mgmt_be_client); + static_vrf_terminate(); static_zebra_stop(); @@ -98,7 +107,6 @@ struct frr_signal_t static_signals[] = { }; static const struct frr_yang_module_info *const staticd_yang_modules[] = { - &frr_filter_info, &frr_interface_info, &frr_vrf_info, &frr_routing_info, @@ -107,6 +115,10 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = { #define STATIC_VTY_PORT 2616 +/* + * NOTE: .flags == FRR_NO_SPLIT_CONFIG to avoid reading split config, mgmtd will + * do this for us now + */ FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT, .proghelp = "Implementation of STATIC.", @@ -116,7 +128,8 @@ FRR_DAEMON_INFO(staticd, STATIC, .vty_port = STATIC_VTY_PORT, .privs = &static_privs, .yang_modules = staticd_yang_modules, .n_yang_modules = array_size(staticd_yang_modules), -); + + .flags = FRR_NO_SPLIT_CONFIG); int main(int argc, char **argv, char **envp) { @@ -147,14 +160,20 @@ int main(int argc, char **argv, char **envp) static_zebra_init(); static_vty_init(); + /* Initialize MGMT backend functionalities */ + mgmt_be_client = mgmt_be_client_create("staticd", NULL, 0, master); + 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; + /* + * We set FRR_NO_SPLIT_CONFIG flag to avoid reading our config, but we + * still need to write one if vtysh tells us to. Setting the host + * config filename does this. + */ + host_config_set(config_default); frr_config_fork(); frr_run(master); diff --git a/staticd/static_routes.c b/staticd/static_routes.c index 3399686a90d0..1fbbf7e99d34 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -757,30 +757,6 @@ void static_ifindex_update(struct interface *ifp, bool up) static_ifindex_update_af(ifp, up, AFI_IP6, SAFI_MULTICAST); } -void static_get_nh_type(enum static_nh_type 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; diff --git a/staticd/static_routes.h b/staticd/static_routes.h index eb7953db2cfb..3be6fd6b1402 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -159,6 +159,31 @@ static_route_info_from_rnode(struct route_node *rn) return (struct static_route_info *)(rn->info); } +static inline void static_get_nh_type(enum static_nh_type 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; + }; +} + extern bool mpls_enabled; extern uint32_t zebra_ecmp_count; @@ -192,8 +217,6 @@ extern struct static_path *static_add_path(struct route_node *rn, uint32_t table_id, uint8_t distance); extern void static_del_path(struct static_path *pn); -extern void static_get_nh_type(enum static_nh_type stype, char *type, - size_t size); extern bool static_add_nexthop_validate(const char *nh_vrf_name, enum static_nh_type type, struct ipaddr *ipaddr); @@ -229,7 +252,7 @@ extern void static_next_hop_bfd_profile(struct static_nexthop *sn, extern void static_next_hop_bfd_multi_hop(struct static_nexthop *sn, bool mhop); /** Call this function after zebra client initialization. */ -extern void static_bfd_initialize(struct zclient *zc, struct thread_master *tm); +extern void static_bfd_initialize(struct zclient *zc, struct event_loop *tm); extern void static_bfd_show(struct vty *vty, bool isjson); diff --git a/staticd/static_vty.c b/staticd/static_vty.c index d87ca16c6198..3e58a44aa73a 100644 --- a/staticd/static_vty.c +++ b/staticd/static_vty.c @@ -13,6 +13,7 @@ #include "nexthop.h" #include "table.h" #include "srcdest_table.h" +#include "mgmt_be_client.h" #include "mpls.h" #include "northbound.h" #include "libfrr.h" @@ -285,10 +286,10 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, "false"); } - if (type == STATIC_IPV4_GATEWAY - || type == STATIC_IPV6_GATEWAY - || type == STATIC_IPV4_GATEWAY_IFNAME - || type == STATIC_IPV6_GATEWAY_IFNAME) { + 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)); @@ -368,25 +369,51 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) ret = nb_cli_apply_changes(vty, "%s", xpath_prefix); } else { - if (args->source) - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - buf_src_prefix, table_id, buf_nh_type, - args->nexthop_vrf, buf_gate_str, - args->interface_name); - else - snprintf(ab_xpath, sizeof(ab_xpath), - FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, - "frr-staticd:staticd", "staticd", args->vrf, - buf_prefix, - yang_afi_safi_value2identity(args->afi, - args->safi), - table_id, buf_nh_type, args->nexthop_vrf, - buf_gate_str, args->interface_name); + if (args->source) { + if (args->distance) + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH, + "frr-staticd:staticd", "staticd", + args->vrf, buf_prefix, + yang_afi_safi_value2identity( + args->afi, args->safi), + buf_src_prefix, table_id, distance, + buf_nh_type, args->nexthop_vrf, + buf_gate_str, args->interface_name); + else + snprintf( + ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", + args->vrf, buf_prefix, + yang_afi_safi_value2identity( + args->afi, args->safi), + buf_src_prefix, table_id, buf_nh_type, + args->nexthop_vrf, buf_gate_str, + args->interface_name); + } else { + if (args->distance) + snprintf(ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_NH_KEY_XPATH, + "frr-staticd:staticd", "staticd", + args->vrf, buf_prefix, + yang_afi_safi_value2identity( + args->afi, args->safi), + table_id, distance, buf_nh_type, + args->nexthop_vrf, buf_gate_str, + args->interface_name); + else + snprintf( + ab_xpath, sizeof(ab_xpath), + FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, + "frr-staticd:staticd", "staticd", + args->vrf, buf_prefix, + yang_afi_safi_value2identity( + args->afi, args->safi), + table_id, buf_nh_type, + args->nexthop_vrf, buf_gate_str, + args->interface_name); + } dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); if (!dnode) { @@ -1452,15 +1479,18 @@ DEFPY_YANG(debug_staticd, debug_staticd_cmd, "Debug route\n" "Debug bfd\n") { +#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY /* If no specific category, change all */ if (strmatch(argv[argc - 1]->text, "static")) static_debug_set(vty->node, !no, true, true, true); else static_debug_set(vty->node, !no, !!events, !!route, !!bfd); +#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ return CMD_SUCCESS; } +#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY DEFPY(staticd_show_bfd_routes, staticd_show_bfd_routes_cmd, "show bfd static route [json]$isjson", SHOW_STR @@ -1496,9 +1526,15 @@ static struct cmd_node debug_node = { .config_write = static_config_write_debug, }; +#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ + void static_vty_init(void) { +#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY install_node(&debug_node); + install_element(ENABLE_NODE, &show_debugging_static_cmd); + install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd); +#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */ install_element(CONFIG_NODE, &ip_mroute_dist_cmd); @@ -1516,9 +1552,8 @@ void static_vty_init(void) install_element(CONFIG_NODE, &ipv6_route_cmd); install_element(VRF_NODE, &ipv6_route_vrf_cmd); - install_element(ENABLE_NODE, &show_debugging_static_cmd); install_element(ENABLE_NODE, &debug_staticd_cmd); install_element(CONFIG_NODE, &debug_staticd_cmd); - install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd); + mgmt_be_client_lib_vty_init(); } diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index f220b476fbbd..4f3ccde53dab 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -6,7 +6,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "network.h" #include "prefix.h" diff --git a/staticd/static_zebra.h b/staticd/static_zebra.h index 5f21d6c3868e..c4f4ebdcbcd5 100644 --- a/staticd/static_zebra.h +++ b/staticd/static_zebra.h @@ -11,7 +11,7 @@ extern "C" { #endif -extern struct thread_master *master; +extern struct event_loop *master; extern void static_zebra_nht_register(struct static_nexthop *nh, bool reg); diff --git a/tests/.gitignore b/tests/.gitignore index f00177abd8bd..681438f4a591 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -29,6 +29,7 @@ frr_northbound* /lib/test_buffer /lib/test_checksum /lib/test_frrscript +/lib/test_darr /lib/test_frrlua /lib/test_graph /lib/test_grpc diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c index 926097f5711e..57aa2af9de79 100644 --- a/tests/bgpd/test_aspath.c +++ b/tests/bgpd/test_aspath.c @@ -26,7 +26,7 @@ /* need these to link in libbgp */ struct zebra_privs_t bgpd_privs = {}; -struct thread_master *master = NULL; +struct event_loop *master = NULL; static int failed = 0; @@ -1405,7 +1405,7 @@ int main(void) { int i = 0; qobj_init(); - bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE, + bgp_master_init(event_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE, list_new()); master = bm->master; bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c index 8ef57486717c..da17471fd181 100644 --- a/tests/bgpd/test_capability.c +++ b/tests/bgpd/test_capability.c @@ -30,7 +30,7 @@ /* need these to link in libbgp */ struct zebra_privs_t bgpd_privs = {}; -struct thread_master *master = NULL; +struct event_loop *master = NULL; static int failed = 0; static int tty = 0; @@ -925,7 +925,7 @@ int main(void) term_bgp_debug_as4 = -1UL; qobj_init(); - master = thread_master_create(NULL); + master = event_master_create(NULL); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_ecommunity.c b/tests/bgpd/test_ecommunity.c index bb360722c8c1..49322d3e2d3d 100644 --- a/tests/bgpd/test_ecommunity.c +++ b/tests/bgpd/test_ecommunity.c @@ -16,7 +16,7 @@ /* need these to link in libbgp */ struct zebra_privs_t bgpd_privs = {}; -struct thread_master *master = NULL; +struct event_loop *master = NULL; static int failed = 0; diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c index b1f3314f1f16..ae7903e0ccfc 100644 --- a/tests/bgpd/test_mp_attr.c +++ b/tests/bgpd/test_mp_attr.c @@ -35,7 +35,7 @@ /* need these to link in libbgp */ struct zebra_privs_t bgpd_privs = {}; -struct thread_master *master = NULL; +struct event_loop *master = NULL; static int failed = 0; static int tty = 0; @@ -1042,9 +1042,9 @@ static void parse_test(struct peer *peer, struct test_segment *t, int type) if (!parse_ret) { if (type == BGP_ATTR_MP_REACH_NLRI) - nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 0); + nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, false); else if (type == BGP_ATTR_MP_UNREACH_NLRI) - nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, 1); + nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, true); } handle_result(peer, t, parse_ret, nlri_ret); } @@ -1070,7 +1070,7 @@ int main(void) qobj_init(); cmd_init(0); bgp_vty_init(); - master = thread_master_create("test mp attr"); + master = event_master_create("test mp attr"); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c index 3662805971ae..0124ad9b2239 100644 --- a/tests/bgpd/test_mpath.c +++ b/tests/bgpd/test_mpath.c @@ -61,7 +61,7 @@ struct testcase_t__ { }; /* need these to link in libbgp */ -struct thread_master *master = NULL; +struct event_loop *master = NULL; extern struct zclient *zclient; struct zebra_privs_t bgpd_privs = { .user = NULL, @@ -378,7 +378,7 @@ int all_tests_count = array_size(all_tests); static int global_test_init(void) { qobj_init(); - master = thread_master_create(NULL); + master = event_master_create(NULL); zclient = zclient_new(master, &zclient_options_default, NULL, 0); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL); @@ -393,7 +393,7 @@ static int global_test_cleanup(void) { if (zclient != NULL) zclient_free(zclient); - thread_master_free(master); + event_master_free(master); return 0; } diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c index aeebbd9a353a..94c3feaa934d 100644 --- a/tests/bgpd/test_packet.c +++ b/tests/bgpd/test_packet.c @@ -25,7 +25,7 @@ /* need these to link in libbgp */ struct zebra_privs_t bgpd_privs = {}; -struct thread_master *master = NULL; +struct event_loop *master = NULL; static struct bgp *bgp; static as_t asn = 100; @@ -41,11 +41,11 @@ int main(int argc, char *argv[]) { struct peer *peer; int i, j; - struct thread t; + struct event t; qobj_init(); bgp_attr_init(); - master = thread_master_create(NULL); + master = event_master_create(NULL); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); vrf_init(NULL, NULL, NULL, NULL); bgp_option_set(BGP_OPT_NO_LISTEN); diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index dde38c8693bb..bc6eba906905 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -106,7 +106,7 @@ /* Required variables to link in libbgp */ struct zebra_privs_t bgpd_privs = {0}; -struct thread_master *master; +struct event_loop *master; enum test_state { TEST_SUCCESS, @@ -1363,7 +1363,7 @@ static void bgp_startup(void) zprivs_preinit(&bgpd_privs); zprivs_init(&bgpd_privs); - master = thread_master_create(NULL); + master = event_master_create(NULL); nb_init(master, NULL, 0, false); bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new()); bgp_option_set(BGP_OPT_NO_LISTEN); @@ -1412,7 +1412,7 @@ static void bgp_shutdown(void) nb_terminate(); yang_terminate(); zprivs_terminate(&bgpd_privs); - thread_master_free(master); + event_master_free(master); master = NULL; } diff --git a/tests/helpers/c/main.c b/tests/helpers/c/main.c index bb8acd2c4418..8af53a2ea40b 100644 --- a/tests/helpers/c/main.c +++ b/tests/helpers/c/main.c @@ -6,7 +6,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "memory.h" @@ -14,7 +14,7 @@ extern void test_init(void); -struct thread_master *master; +struct event_loop *master; struct option longopts[] = {{"daemon", no_argument, NULL, 'd'}, {"config_file", required_argument, NULL, 'f'}, @@ -33,17 +33,17 @@ DEFUN (daemon_exit, } static int timer_count; -static void test_timer(struct thread *thread) +static void test_timer(struct event *thread) { - int *count = THREAD_ARG(thread); + int *count = EVENT_ARG(thread); printf("run %d of timer\n", (*count)++); - thread_add_timer(master, test_timer, count, 5, NULL); + event_add_timer(master, test_timer, count, 5, NULL); } static void test_timer_init(void) { - thread_add_timer(master, test_timer, &timer_count, 10, NULL); + event_add_timer(master, test_timer, &timer_count, 10, NULL); } static void test_vty_init(void) @@ -82,7 +82,7 @@ int main(int argc, char **argv) int vty_port = 4000; int daemon_mode = 0; char *progname; - struct thread thread; + struct event thread; char *config_file = NULL; /* Set umask before anything for security */ @@ -92,7 +92,7 @@ int main(int argc, char **argv) progname = ((p = strrchr(argv[0], '/')) ? ++p : argv[0]); /* master init. */ - master = thread_master_create(NULL); + master = event_master_create(NULL); while (1) { int opt; @@ -152,7 +152,7 @@ int main(int argc, char **argv) } /* Create VTY socket */ - vty_serv_sock(vty_addr, vty_port, "/tmp/.heavy.sock"); + vty_serv_start(vty_addr, vty_port, "/tmp/.heavy.sock"); /* Configuration file read*/ if (!config_file) @@ -164,8 +164,8 @@ int main(int argc, char **argv) test_init(); /* Fetch next active thread. */ - while (thread_fetch(master, &thread)) - thread_call(&thread); + while (event_fetch(master, &thread)) + event_call(&thread); /* Not reached. */ exit(0); diff --git a/tests/isisd/test_common.c b/tests/isisd/test_common.c index 0f37752ce265..e47456965e0e 100644 --- a/tests/isisd/test_common.c +++ b/tests/isisd/test_common.c @@ -12,7 +12,7 @@ #include "test_common.h" -struct thread_master *master; +struct event_loop *master; struct zebra_privs_t isisd_privs; int isis_sock_init(struct isis_circuit *circuit) @@ -98,7 +98,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp, { struct prefix prefix; struct sr_prefix_cfg pcfg = {}; - struct sr_prefix_cfg *pcfg_p = NULL; + struct sr_prefix_cfg *pcfg_p[SR_ALGORITHM_COUNT] = {NULL}; if (str2prefix(prefix_str, &prefix) != 1) { zlog_debug("%s: invalid network: %s", __func__, prefix_str); @@ -106,7 +106,7 @@ static void lsp_add_ip_reach(struct isis_lsp *lsp, } if (CHECK_FLAG(tnode->flags, F_ISIS_TEST_NODE_SR)) { - pcfg_p = &pcfg; + pcfg_p[SR_ALGORITHM_SPF] = &pcfg; pcfg.sid = *next_sid_index; *next_sid_index = *next_sid_index + 1; @@ -163,31 +163,32 @@ static void lsp_add_reach(struct isis_lsp *lsp, static void lsp_add_router_capability(struct isis_lsp *lsp, const struct isis_test_node *tnode) { - struct isis_router_cap cap = {}; + struct isis_router_cap *cap; if (!tnode->router_id) return; - if (inet_pton(AF_INET, tnode->router_id, &cap.router_id) != 1) { + cap = isis_tlvs_init_router_capability(lsp->tlvs); + + 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 = + 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; + 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, diff --git a/tests/isisd/test_common.h b/tests/isisd/test_common.h index 9b83b30ba71a..f0c5493c9dee 100644 --- a/tests/isisd/test_common.h +++ b/tests/isisd/test_common.h @@ -65,7 +65,7 @@ extern int test_topology_load(const struct isis_topology *topology, struct lspdb_head lspdb[]); /* Global variables. */ -extern struct thread_master *master; +extern struct event_loop *master; extern struct zebra_privs_t isisd_privs; extern struct isis_topology test_topologies[]; diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 8f0b92d0fcd4..627ccfee6fa3 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -9,7 +9,7 @@ #include "memory.h" #include "sbuf.h" #include "stream.h" -#include "thread.h" +#include "frrevent.h" #include "isisd/isis_circuit.h" #include "isisd/isis_tlvs.h" diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index 0cccf05678ef..6eb180b501db 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -8,7 +8,7 @@ #include <lib/version.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "log.h" @@ -49,12 +49,13 @@ static void test_run_spf(struct vty *vty, const struct isis_topology *topology, /* 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); + spf_type, F_SPFTREE_NO_ADJACENCIES, + SR_ALGORITHM_SPF); isis_run_spf(spftree); /* Print the SPT and the corresponding routing table. */ isis_print_spftree(vty, spftree); - isis_print_routes(vty, spftree, false, false); + isis_print_routes(vty, spftree, NULL, false, false); /* Cleanup SPF tree. */ isis_spftree_del(spftree); @@ -71,8 +72,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, /* Run forward SPF in the root node. */ flags = F_SPFTREE_NO_ADJACENCIES; - spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, - SPF_TYPE_FORWARD, flags); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run forward SPF on all adjacent routers. */ @@ -84,9 +86,9 @@ static void test_run_lfa(struct vty *vty, const struct isis_topology *topology, /* Print the SPT and the corresponding main/backup routing tables. */ isis_print_spftree(vty, spftree_self); vty_out(vty, "Main:\n"); - isis_print_routes(vty, spftree_self, false, false); + isis_print_routes(vty, spftree_self, NULL, false, false); vty_out(vty, "Backup:\n"); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -107,8 +109,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, /* Run forward SPF in the root node. */ flags = F_SPFTREE_NO_ADJACENCIES; - spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, - SPF_TYPE_FORWARD, flags); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run reverse SPF in the root node. */ @@ -162,9 +165,9 @@ static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology, /* Print the SPT and the corresponding main/backup routing tables. */ isis_print_spftree(vty, spftree_self); vty_out(vty, "Main:\n"); - isis_print_routes(vty, spftree_self, false, false); + isis_print_routes(vty, spftree_self, NULL, false, false); vty_out(vty, "Backup:\n"); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -187,8 +190,9 @@ static void test_run_ti_lfa(struct vty *vty, /* Run forward SPF in the root node. */ flags = F_SPFTREE_NO_ADJACENCIES; - spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree, - SPF_TYPE_FORWARD, flags); + spftree_self = + isis_spftree_new(area, lspdb, root->sysid, level, tree, + SPF_TYPE_FORWARD, flags, SR_ALGORITHM_SPF); isis_run_spf(spftree_self); /* Run reverse SPF in the root node. */ @@ -224,7 +228,7 @@ static void test_run_ti_lfa(struct vty *vty, * Print the post-convergence SPT and the corresponding routing table. */ isis_print_spftree(vty, spftree_pc); - isis_print_routes(vty, spftree_self, false, true); + isis_print_routes(vty, spftree_self, NULL, false, true); /* Cleanup everything. */ isis_spftree_del(spftree_self); @@ -455,7 +459,7 @@ static void vty_do_exit(int isexit) cmd_terminate(); vty_terminate(); yang_terminate(); - thread_master_free(master); + event_master_free(master); log_memstats(stderr, "test-isis-spf"); if (!isexit) @@ -488,7 +492,7 @@ int main(int argc, char **argv) { char *p; char *progname; - struct thread thread; + struct event thread; bool debug = false; /* Set umask before anything for security */ @@ -521,7 +525,7 @@ int main(int argc, char **argv) } /* master init. */ - master = thread_master_create(NULL); + master = event_master_create(NULL); isis_master_init(master); /* Library inits. */ @@ -549,8 +553,8 @@ int main(int argc, char **argv) vty_stdio(vty_do_exit); /* Fetch next active thread. */ - while (thread_fetch(master, &thread)) - thread_call(&thread); + while (event_fetch(master, &thread)) + event_call(&thread); /* Not reached. */ exit(0); diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 29dad7d80f61..e0981b991acb 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -8,7 +8,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "memory.h" @@ -17,7 +17,7 @@ #include "common_cli.h" -struct thread_master *master; +struct event_loop *master; int dump_args(struct vty *vty, const char *descr, int argc, struct cmd_token *argv[]) @@ -39,7 +39,7 @@ static void vty_do_exit(int isexit) vty_terminate(); nb_terminate(); yang_terminate(); - thread_master_free(master); + event_master_free(master); log_memstats(stderr, "testcli"); if (!isexit) @@ -52,14 +52,14 @@ int test_log_prio = ZLOG_DISABLED; /* main routine. */ int main(int argc, char **argv) { - struct thread thread; + struct event thread; size_t yangcount; /* Set umask before anything for security */ umask(0027); /* master init. */ - master = thread_master_create(NULL); + master = event_master_create(NULL); zlog_aux_init("NONE: ", test_log_prio); @@ -81,8 +81,8 @@ int main(int argc, char **argv) vty_stdio(vty_do_exit); /* Fetch next active thread. */ - while (thread_fetch(master, &thread)) - thread_call(&thread); + while (event_fetch(master, &thread)) + event_call(&thread); /* Not reached. */ exit(0); diff --git a/tests/lib/cli/common_cli.h b/tests/lib/cli/common_cli.h index 7ef4d860bd96..4a1de940df74 100644 --- a/tests/lib/cli/common_cli.h +++ b/tests/lib/cli/common_cli.h @@ -22,7 +22,7 @@ extern void test_init(int argc, char **argv); /* functions provided by common cli * (includes main()) */ -extern struct thread_master *master; +extern struct event_loop *master; extern int test_log_prio; diff --git a/tests/lib/cli/test_commands.c b/tests/lib/cli/test_commands.c index 54db79575711..ea84120fc1ea 100644 --- a/tests/lib/cli/test_commands.c +++ b/tests/lib/cli/test_commands.c @@ -30,7 +30,7 @@ extern vector cmdvec; extern struct cmd_node vty_node; extern void test_init_cmd(void); /* provided in test-commands-defun.c */ -struct thread_master *master; /* dummy for libfrr*/ +struct event_loop *master; /* dummy for libfrr*/ static vector test_cmds; static char test_buf[32768]; diff --git a/tests/lib/cxxcompat.c b/tests/lib/cxxcompat.c index 7aab88eeb98c..4ad41fca425e 100644 --- a/tests/lib/cxxcompat.c +++ b/tests/lib/cxxcompat.c @@ -25,7 +25,6 @@ #include "lib/frr_pthread.h" #include "lib/frratomic.h" #include "lib/frrstr.h" -#include "lib/getopt.h" #include "lib/graph.h" #include "lib/hash.h" #include "lib/hook.h" @@ -78,7 +77,7 @@ #include "lib/stream.h" #include "lib/table.h" #include "lib/termtable.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/typesafe.h" #include "lib/typerb.h" #include "lib/vector.h" diff --git a/tests/lib/fuzz_zlog.c b/tests/lib/fuzz_zlog.c index ad9677a718df..d308f8eb2f7e 100644 --- a/tests/lib/fuzz_zlog.c +++ b/tests/lib/fuzz_zlog.c @@ -94,7 +94,7 @@ int main(int argc, char **argv) cfg->fd = fd; cmd_hostname_set("TEST"); - cfg->master = thread_master_create("TEST"); + cfg->master = event_master_create("TEST"); zlog_5424_apply_dst(cfg); diff --git a/tests/lib/northbound/test_oper_data.c b/tests/lib/northbound/test_oper_data.c index 3abda75f40fb..f82eddd3bf1b 100644 --- a/tests/lib/northbound/test_oper_data.c +++ b/tests/lib/northbound/test_oper_data.c @@ -6,7 +6,7 @@ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "memory.h" @@ -14,7 +14,7 @@ #include "log.h" #include "northbound.h" -static struct thread_master *master; +static struct event_loop *master; struct troute { struct prefix_ipv4 prefix; @@ -351,7 +351,7 @@ static void vty_do_exit(int isexit) vty_terminate(); nb_terminate(); yang_terminate(); - thread_master_free(master); + event_master_free(master); log_memstats(stderr, "test-nb-oper-data"); if (!isexit) @@ -361,7 +361,7 @@ static void vty_do_exit(int isexit) /* main routine. */ int main(int argc, char **argv) { - struct thread thread; + struct event thread; unsigned int num_vrfs = 2; unsigned int num_interfaces = 4; unsigned int num_routes = 6; @@ -377,7 +377,7 @@ int main(int argc, char **argv) umask(0027); /* master init. */ - master = thread_master_create(NULL); + master = event_master_create(NULL); zlog_aux_init("NONE: ", ZLOG_DISABLED); @@ -395,8 +395,8 @@ int main(int argc, char **argv) vty_stdio(vty_do_exit); /* Fetch next active thread. */ - while (thread_fetch(master, &thread)) - thread_call(&thread); + while (event_fetch(master, &thread)) + event_call(&thread); /* Not reached. */ exit(0); diff --git a/tests/lib/subdir.am b/tests/lib/subdir.am index 1bc092a49e61..6c1be5020181 100644 --- a/tests/lib/subdir.am +++ b/tests/lib/subdir.am @@ -15,8 +15,14 @@ tests_lib_test_frrscript_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_frrscript_CPPFLAGS = $(TESTS_CPPFLAGS) tests_lib_test_frrscript_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_frrscript_SOURCES = tests/lib/test_frrscript.c -EXTRA_DIST += tests/lib/test_frrscript.py +EXTRA_tests_lib_test_frrscript_DEPENDENCIES = copy_script +EXTRA_DIST += tests/lib/test_frrscript.py tests/lib/script1.lua +# For out-of-tree build, lua script needs to be in the build dir, rather than +# just available somewhere in the VPATH +copy_script: tests/lib/script1.lua + test -e tests/lib/script1.lua || \ + $(INSTALL_SCRIPT) $< tests/lib/script1.lua ############################################################################## GRPC_TESTS_LDADD = staticd/libstatic.a grpc/libfrrgrpc_pb.la -lgrpc++ -lprotobuf $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) -lm @@ -151,6 +157,13 @@ tests_lib_test_checksum_LDADD = $(ALL_TESTS_LDADD) tests_lib_test_checksum_SOURCES = tests/lib/test_checksum.c tests/helpers/c/prng.c +check_PROGRAMS += tests/lib/test_darr +tests_lib_test_darr_CFLAGS = $(TESTS_CFLAGS) +tests_lib_test_darr_CPPFLAGS = $(TESTS_CPPFLAGS) +tests_lib_test_darr_LDADD = $(ALL_TESTS_LDADD) +tests_lib_test_darr_SOURCES = tests/lib/test_darr.c + + check_PROGRAMS += tests/lib/test_graph tests_lib_test_graph_CFLAGS = $(TESTS_CFLAGS) tests_lib_test_graph_CPPFLAGS = $(TESTS_CPPFLAGS) diff --git a/tests/lib/test_assert.c b/tests/lib/test_assert.c index d68ee8a8191a..4440075b41fe 100644 --- a/tests/lib/test_assert.c +++ b/tests/lib/test_assert.c @@ -22,13 +22,13 @@ static void func_for_bt(int number) #include <zebra.h> #include "lib/zlog.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/sigevent.h" int main(int argc, char **argv) { int number = 10; - struct thread_master *master; + struct event_loop *master; zlog_aux_init("NONE: ", LOG_DEBUG); @@ -39,7 +39,7 @@ int main(int argc, char **argv) assertf(number > 1, "(B) the number was %d", number); /* set up SIGABRT handler */ - master = thread_master_create("test"); + master = event_master_create("test"); signal_init(master, 0, NULL); func_for_bt(number); diff --git a/tests/lib/test_buffer.c b/tests/lib/test_buffer.c index 9f501f4e35ec..bfb4600848f3 100644 --- a/tests/lib/test_buffer.c +++ b/tests/lib/test_buffer.c @@ -8,7 +8,7 @@ #include <lib_vty.h> #include <buffer.h> -struct thread_master *master; +struct event_loop *master; int main(int argc, char **argv) { diff --git a/tests/lib/test_checksum.c b/tests/lib/test_checksum.c index fcb79ee28d0d..329ae6087e12 100644 --- a/tests/lib/test_checksum.c +++ b/tests/lib/test_checksum.c @@ -11,7 +11,7 @@ #include "network.h" #include "prng.h" -struct thread_master *master; +struct event_loop *master; struct acc_vals { int c0; diff --git a/tests/lib/test_darr.c b/tests/lib/test_darr.c new file mode 100644 index 000000000000..9150aed09dfa --- /dev/null +++ b/tests/lib/test_darr.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * June 23 2023, Christian Hopps <chopps@labn.net> + * + * Copyright (c) 2023, LabN Consulting, L.L.C. + * + */ +#include <zebra.h> +#include "darr.h" + +/* + * Public functions to test: + * [x] - darr_append + * [x] - darr_append_n + * [x] - darr_append_nz + * [x] - darr_cap + * [-] - darr_ensure_cap + * [x] - darr_ensure_i + * [x] - darr_foreach_i + * [x] - darr_foreach_p + * [x] - darr_free + * [x] - darr_insert + * [ ] - darr_insertz + * [x] - darr_insert_n + * [x] - darr_insert_nz + * [x] - darr_maxi + * [x] - darr_pop + * [x] - darr_push + * [ ] - darr_pushz + * [x] - darr_remove + * [x] - darr_remove_n + * [x] - darr_reset + * [x] - darr_setlen + */ + +static void test_int(void) +{ + int z105[105] = {0}; + int a1[] = {0, 1, 2, 3, 4}; + int a2[] = {4, 3, 2, 1, 0}; + int *da1 = NULL; + int *da2 = NULL; + int *dap; + uint i; + + darr_ensure_i(da1, 0); + da1[0] = 0; + assert(darr_len(da1) == 1); + assert(darr_cap(da1) == 1); + + *darr_ensure_i(da1, 1) = 1; + assert(darr_len(da1) == 2); + assert(darr_cap(da1) == 2); + + darr_ensure_i(da1, 4); + darr_foreach_i (da1, i) + da1[i] = i; + + assert(darr_len(da1) == 5); + /* minimum non-pow2 array size for long long and smaller */ + assert(darr_cap(da1) == 8); + assert(!memcmp(da1, a1, sizeof(a1))); + + /* reverse the numbers */ + darr_foreach_p (da1, dap) + *dap = darr_end(da1) - dap - 1; + assert(!memcmp(da1, a2, sizeof(a2))); + + darr_append_n(da1, 100); + darr_foreach_p (da1, dap) + *dap = darr_end(da1) - dap - 1; + + darr_pop_n(da1, 100); + darr_append_nz(da1, 100); + assert(!memcmp(&da1[5], z105, _darr_esize(da1) * 100)); + + assert(darr_len(da1) == 105); + assert(darr_maxi(da1) == 127); + assert(darr_cap(da1) == 128); + + darr_setlen(da1, 102); + assert(darr_len(da1) == 102); + assert(darr_maxi(da1) == 127); + + int a3[] = { 0xdeadbeaf, 0x12345678 }; + + da1[0] = a3[0]; + da1[101] = a3[1]; + darr_remove_n(da1, 1, 100); + assert(darr_len(da1) == array_size(a3)); + assert(!memcmp(da1, a3, sizeof(a3))); + + da1[0] = a3[1]; + da1[1] = a3[0]; + + darr_insert_n(da1, 1, 100); + assert(darr_len(da1) == 102); + assert(da1[0] == a3[1]); + assert(da1[101] == a3[0]); + + darr_reset(da1); + assert(darr_len(da1) == 0); + assert(darr_maxi(da1) == 127); + assert(darr_cap(da1) == 128); + + /* we touch the length field of the freed block here somehow */ + darr_insert_n(da1, 100, 300); + assert(darr_len(da1) == 400); + assert(darr_cap(da1) == 512); + + da1[400 - 1] = 0x0BAD; + *darr_insert(da1, 0) = 0xF00D; + assert(da1[0] == 0xF00D); + assert(da1[400] == 0x0BAD); + assert(darr_len(da1) == 401); + assert(darr_cap(da1) == 512); + + darr_free(da1); + assert(da1 == NULL); + assert(darr_len(da1) == 0); + darr_setlen(da1, 0); + darr_reset(da1); + darr_free(da1); + + *darr_append(da2) = 0; + *darr_append(da2) = 1; + darr_push(da2, 2); + darr_push(da2, 3); + darr_push(da2, 4); + + assert(!memcmp(da2, a1, sizeof(a1))); + + assert(darr_pop(da2) == 4); + assert(darr_pop(da2) == 3); + assert(darr_pop(da2) == 2); + assert(darr_len(da2) == 2); + assert(darr_pop(da2) == 1); + assert(darr_pop(da2) == 0); + assert(darr_len(da2) == 0); + + darr_free(da2); +} + +static void test_struct(void) +{ + /* + *uwould like to use different sizes with padding but memcmp can't be + *used then. + */ + struct st { + long long a; + long long b; + }; + struct st z102[102] = {{0, 0}}; + struct st *da1 = NULL; + struct st *da2 = NULL; + struct st a1[] = { + {0, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, + }; + uint i; + + darr_ensure_i(da1, 0); + da1[0].a = 0; + da1[0].b = 0; + assert(darr_len(da1) == 1); + assert(darr_cap(da1) == 1); + + darr_ensure_i(da1, 1)->a = 1; + darr_ensure_i(da1, 1)->b = 1; + assert(darr_len(da1) == 2); + assert(darr_cap(da1) == 2); + + darr_ensure_i(da1, 4); + da1[2].a = 2; + da1[2].b = 2; + + da1[3].a = 3; + da1[3].b = 3; + + da1[4].a = 4; + da1[4].b = 4; + + assert(darr_len(da1) == 5); + /* minimum non-pow2 array size for long long and smaller */ + assert(darr_cap(da1) == 8); + assert(!memcmp(da1, a1, sizeof(a1))); + + darr_append_n(da1, 100); + + assert(darr_len(da1) == 105); + assert(darr_maxi(da1) == 127); + assert(darr_cap(da1) == 128); + + darr_setlen(da1, 102); + assert(darr_len(da1) == 102); + assert(darr_maxi(da1) == 127); + + struct st a2[] = { + {0xdeadbeaf, 0xdeadbeaf}, + {0x12345678, 0x12345678}, + }; + da1[0] = a2[0]; + da1[101] = a2[1]; + darr_remove_n(da1, 1, 100); + assert(darr_len(da1) == array_size(a2)); + assert(!memcmp(da1, a2, sizeof(a2))); + + da1[0] = a2[1]; + da1[1] = a2[0]; + + darr_insert_n(da1, 1, 100); + assert(darr_len(da1) == 102); + darr_foreach_i (da1, i) { + da1[i].a = i; + da1[i].b = i; + } + darr_remove_n(da1, 1, 100); + assert(darr_len(da1) == 2); + darr_insert_nz(da1, 1, 100); + assert(!memcmp(&da1[1], z102, 100 * sizeof(da1[0]))); + /* assert(da1[0] == a2[1]); */ + /* assert(da1[101] == a2[0]); */ + + darr_reset(da1); + assert(darr_len(da1) == 0); + assert(darr_maxi(da1) == 127); + assert(darr_cap(da1) == 128); + + /* we touch the length field of the freed block here somehow */ + darr_insert_n(da1, 100, 300); + + assert(darr_len(da1) == 400); + assert(darr_cap(da1) == 512); + + darr_free(da1); + assert(da1 == NULL); + + assert(darr_len(da1) == 0); + darr_setlen(da1, 0); + darr_reset(da1); + + darr_free(da1); + + struct st i0 = {0, 0}; + struct st i1 = {1, 1}; + struct st i2 = {2, 2}; + struct st i3 = {3, 3}; + struct st i4 = {4, 4}; + + *darr_append(da2) = i0; + *darr_append(da2) = i1; + darr_push(da2, i2); + darr_push(da2, i3); + darr_push(da2, i4); + + assert(!memcmp(da2, a1, sizeof(a1))); + + struct st p0, p1, p2, p3, p4; + + p4 = darr_pop(da2); + p3 = darr_pop(da2); + p2 = darr_pop(da2); + p1 = darr_pop(da2); + p0 = darr_pop(da2); + assert(darr_len(da2) == 0); + assert(p4.a == i4.a && p4.b == i4.b); + assert(p3.a == i3.a && p3.b == i3.b); + assert(p2.a == i2.a && p2.b == i2.b); + assert(p1.a == i1.a && p1.b == i1.b); + assert(p0.a == i0.a && p0.b == i0.b); + + darr_free(da2); +} + +int main(int argc, char **argv) +{ + test_int(); + test_struct(); +} diff --git a/tests/lib/test_grpc.cpp b/tests/lib/test_grpc.cpp index 282161c3df8a..87530d41d82a 100644 --- a/tests/lib/test_grpc.cpp +++ b/tests/lib/test_grpc.cpp @@ -14,7 +14,7 @@ #include "libfrr.h" #include "routing_nb.h" #include "northbound_cli.h" -#include "thread.h" +#include "frrevent.h" #include "vrf.h" #include "vty.h" @@ -34,13 +34,13 @@ #include <grpcpp/security/credentials.h> #include "grpc/frr-northbound.grpc.pb.h" -DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)); +DEFINE_HOOK(frr_late_init, (struct event_loop * tm), (tm)); DEFINE_KOOH(frr_fini, (), ()); struct vty *vty; bool mpls_enabled; -struct thread_master *master; +struct event_loop *master; struct zebra_privs_t static_privs = {0}; struct frrmod_runtime *grpc_module; char binpath[2 * MAXPATHLEN + 1]; @@ -66,7 +66,7 @@ static const struct frr_yang_module_info *const staticd_yang_modules[] = { &frr_staticd_info, &frr_vrf_info, }; -static void grpc_thread_stop(struct thread *thread); +static void grpc_thread_stop(struct event *thread); static void _err_print(const void *cookie, const char *errstr) { @@ -97,7 +97,7 @@ static void static_startup(void) static_debug_init(); - master = thread_master_create(NULL); + master = event_master_create(NULL); nb_init(master, staticd_yang_modules, array_size(staticd_yang_modules), false); @@ -114,7 +114,7 @@ static void static_startup(void) // Add a route vty = vty_new(); vty->type = vty::VTY_TERM; - vty_config_enter(vty, true, false); + vty_config_enter(vty, true, false, false); auto ret = cmd_execute(vty, "ip route 11.0.0.0/8 Null0", NULL, 0); assert(!ret); @@ -139,7 +139,7 @@ static void static_shutdown(void) cmd_terminate(); nb_terminate(); yang_terminate(); - thread_master_free(master); + event_master_free(master); master = NULL; } @@ -479,14 +479,14 @@ void *grpc_client_test_start(void *arg) // Signal FRR event loop to stop test_debug("client: pthread: adding event to stop us"); - thread_add_event(master, grpc_thread_stop, NULL, 0, NULL); + event_add_event(master, grpc_thread_stop, NULL, 0, NULL); test_debug("client: pthread: DONE (returning)"); return NULL; } -static void grpc_thread_start(struct thread *thread) +static void grpc_thread_start(struct event *thread) { struct frr_pthread_attr client = { .start = grpc_client_test_start, @@ -498,7 +498,7 @@ static void grpc_thread_start(struct thread *thread) frr_pthread_wait_running(pth); } -static void grpc_thread_stop(struct thread *thread) +static void grpc_thread_stop(struct event *thread) { std::cout << __func__ << ": frr_pthread_stop_all" << std::endl; frr_pthread_stop_all(); @@ -542,12 +542,12 @@ int main(int argc, char **argv) static_startup(); - thread_add_event(master, grpc_thread_start, NULL, 0, NULL); + event_add_event(master, grpc_thread_start, NULL, 0, NULL); /* Event Loop */ - struct thread thread; - while (thread_fetch(master, &thread)) - thread_call(&thread); + struct event thread; + while (event_fetch(master, &thread)) + event_call(&thread); return 0; } diff --git a/tests/lib/test_heavy.c b/tests/lib/test_heavy.c index 2d54fe6c68a5..1e56940831b0 100644 --- a/tests/lib/test_heavy.c +++ b/tests/lib/test_heavy.c @@ -13,7 +13,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "memory.h" diff --git a/tests/lib/test_heavy_thread.c b/tests/lib/test_heavy_thread.c index afbd20545183..4fb9ebfa3a4b 100644 --- a/tests/lib/test_heavy_thread.c +++ b/tests/lib/test_heavy_thread.c @@ -14,7 +14,7 @@ #include <zebra.h> #include <math.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "memory.h" @@ -22,7 +22,7 @@ #include "tests.h" -extern struct thread_master *master; +extern struct event_loop *master; enum { ITERS_FIRST = 0, ITERS_ERR = 100, @@ -56,9 +56,9 @@ static void slow_func(struct vty *vty, const char *str, const int i) printf("%s did %d, x = %g\n", str, i, x); } -static void clear_something(struct thread *thread) +static void clear_something(struct event *thread) { - struct work_state *ws = THREAD_ARG(thread); + struct work_state *ws = EVENT_ARG(thread); /* this could be like iterating through 150k of route_table * or worse, iterating through a list of peers, to bgp_stop them with @@ -67,9 +67,9 @@ static void clear_something(struct thread *thread) while (ws->i < ITERS_MAX) { slow_func(ws->vty, ws->str, ws->i); ws->i++; - if (thread_should_yield(thread)) { - thread_add_timer_msec(master, clear_something, ws, 0, - NULL); + if (event_should_yield(thread)) { + event_add_timer_msec(master, clear_something, ws, 0, + NULL); return; } } @@ -102,7 +102,7 @@ DEFUN (clear_foo, ws->vty = vty; ws->i = ITERS_FIRST; - thread_add_timer_msec(master, clear_something, ws, 0, NULL); + event_add_timer_msec(master, clear_something, ws, 0, NULL); return CMD_SUCCESS; } diff --git a/tests/lib/test_heavy_wq.c b/tests/lib/test_heavy_wq.c index 9b2cfa5730d1..225573ae92f6 100644 --- a/tests/lib/test_heavy_wq.c +++ b/tests/lib/test_heavy_wq.c @@ -13,7 +13,7 @@ */ #include <zebra.h> -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "command.h" #include "memory.h" @@ -27,7 +27,7 @@ DEFINE_MGROUP(TEST_HEAVYWQ, "heavy-wq test"); DEFINE_MTYPE_STATIC(TEST_HEAVYWQ, WQ_NODE, "heavy_wq_node"); DEFINE_MTYPE_STATIC(TEST_HEAVYWQ, WQ_NODE_STR, "heavy_wq_node->str"); -extern struct thread_master *master; +extern struct event_loop *master; static struct work_queue *heavy_wq; struct heavy_wq_node { diff --git a/tests/lib/test_memory.c b/tests/lib/test_memory.c index e4423b0b5b4c..aba4c3589cd2 100644 --- a/tests/lib/test_memory.c +++ b/tests/lib/test_memory.c @@ -15,7 +15,7 @@ DEFINE_MTYPE_STATIC(TEST_MEMORY, TEST, "generic test mtype"); * CVS */ -struct thread_master *master; +struct event_loop *master; #if 0 /* set to 1 to use system alloc directly */ #undef XMALLOC diff --git a/tests/lib/test_nexthop_iter.c b/tests/lib/test_nexthop_iter.c index 2bb0b1233cb1..91380f1111f5 100644 --- a/tests/lib/test_nexthop_iter.c +++ b/tests/lib/test_nexthop_iter.c @@ -13,7 +13,7 @@ #include "zebra/rib.h" #include "prng.h" -struct thread_master *master; +struct event_loop *master; static int verbose; static void str_append(char **buf, const char *repr) diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index d7bc0b3153f4..e26754857b89 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -50,7 +50,7 @@ Report bugs to %s\n", exit(status); } -struct thread_master *master; +struct event_loop *master; /* main routine. */ int main(int argc, char **argv) { diff --git a/tests/lib/test_resolver.c b/tests/lib/test_resolver.c index b4d8992b954c..d72ff4f5a109 100644 --- a/tests/lib/test_resolver.c +++ b/tests/lib/test_resolver.c @@ -26,7 +26,7 @@ #include "tests/lib/cli/common_cli.h" -extern struct thread_master *master; +extern struct event_loop *master; static void resolver_result(struct resolver_query *resq, const char *errstr, int numaddrs, union sockunion *addr) diff --git a/tests/lib/test_segv.c b/tests/lib/test_segv.c index 8532da7c2d9c..af5f3aec63e4 100644 --- a/tests/lib/test_segv.c +++ b/tests/lib/test_segv.c @@ -17,7 +17,7 @@ struct frr_signal_t sigs[] = {}; -struct thread_master *master; +struct event_loop *master; void func1(int *arg); void func3(void); @@ -49,19 +49,19 @@ void func3(void) func2(6, buf); } -static void threadfunc(struct thread *thread) +static void threadfunc(struct event *thread) { func3(); } int main(void) { - master = thread_master_create(NULL); + master = event_master_create(NULL); signal_init(master, array_size(sigs), sigs); zlog_aux_init("NONE: ", LOG_DEBUG); - thread_execute(master, threadfunc, 0, 0); + event_execute(master, threadfunc, 0, 0); exit(0); } diff --git a/tests/lib/test_sig.c b/tests/lib/test_sig.c index 897e004b4009..2bd7f309766e 100644 --- a/tests/lib/test_sig.c +++ b/tests/lib/test_sig.c @@ -35,18 +35,18 @@ struct frr_signal_t sigs[] = {{ .handler = &sigusr2, }}; -struct thread_master *master; -struct thread t; +struct event_loop *master; +struct event t; int main(void) { - master = thread_master_create(NULL); + master = event_master_create(NULL); signal_init(master, array_size(sigs), sigs); zlog_aux_init("NONE: ", LOG_DEBUG); - while (thread_fetch(master, &t)) - thread_call(&t); + while (event_fetch(master, &t)) + event_call(&t); exit(0); } diff --git a/tests/lib/test_srcdest_table.c b/tests/lib/test_srcdest_table.c index de0482d694f3..6d6c5156a71e 100644 --- a/tests/lib/test_srcdest_table.c +++ b/tests/lib/test_srcdest_table.c @@ -24,7 +24,7 @@ #define s6_addr32 __u6_addr.__u6_addr32 #endif /*s6_addr32*/ -struct thread_master *master; +struct event_loop *master; /* This structure is copied from lib/srcdest_table.c to which it is * private as far as other parts of Quagga are concerned. @@ -121,8 +121,7 @@ static struct test_state *test_state_new(void) static void test_state_free(struct test_state *test) { route_table_finish(test->table); - hash_clean(test->log, log_free); - hash_free(test->log); + hash_clean_and_free(&test->log, log_free); XFREE(MTYPE_TMP, test); } diff --git a/tests/lib/test_stream.c b/tests/lib/test_stream.c index 015dc41db1ed..d38dfe0eb037 100644 --- a/tests/lib/test_stream.c +++ b/tests/lib/test_stream.c @@ -6,12 +6,12 @@ #include <zebra.h> #include <stream.h> -#include <thread.h> +#include "frrevent.h" #include "printfrr.h" static unsigned long long ham = 0xdeadbeefdeadbeef; -struct thread_master *master; +struct event_loop *master; static void print_stream(struct stream *s) { diff --git a/tests/lib/test_table.c b/tests/lib/test_table.c index 41d700395672..51bfda23455b 100644 --- a/tests/lib/test_table.c +++ b/tests/lib/test_table.c @@ -25,7 +25,7 @@ typedef struct test_node_t_ { char *prefix_str; } test_node_t; -struct thread_master *master; +struct event_loop *master; /* * add_node diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c index 37eb4939a1f7..04c05168890d 100644 --- a/tests/lib/test_timer_correctness.c +++ b/tests/lib/test_timer_correctness.c @@ -16,14 +16,14 @@ #include "memory.h" #include "prng.h" -#include "thread.h" +#include "frrevent.h" #define SCHEDULE_TIMERS 800 #define REMOVE_TIMERS 200 #define TIMESTR_LEN strlen("4294967296.999999") -struct thread_master *master; +struct event_loop *master; static size_t log_buf_len; static size_t log_buf_pos; @@ -35,7 +35,7 @@ static char *expected_buf; static struct prng *prng; -static struct thread **timers; +static struct event **timers; static int timers_pending; @@ -54,7 +54,7 @@ static void terminate_test(void) exit_code = 0; } - thread_master_free(master); + event_master_free(master); XFREE(MTYPE_TMP, log_buf); XFREE(MTYPE_TMP, expected_buf); prng_free(prng); @@ -63,7 +63,7 @@ static void terminate_test(void) exit(exit_code); } -static void timer_func(struct thread *thread) +static void timer_func(struct event *thread) { int rv; @@ -94,10 +94,10 @@ static int cmp_timeval(const void *a, const void *b) int main(int argc, char **argv) { int i, j; - struct thread t; + struct event t; struct timeval **alarms; - master = thread_master_create(NULL); + master = event_master_create(NULL); log_buf_len = SCHEDULE_TIMERS * (TIMESTR_LEN + 1) + 1; log_buf_pos = 0; @@ -119,8 +119,8 @@ int main(int argc, char **argv) /* Schedule timers to expire in 0..5 seconds */ interval_msec = prng_rand(prng) % 5000; arg = XMALLOC(MTYPE_TMP, TIMESTR_LEN + 1); - thread_add_timer_msec(master, timer_func, arg, interval_msec, - &timers[i]); + event_add_timer_msec(master, timer_func, arg, interval_msec, + &timers[i]); ret = snprintf(arg, TIMESTR_LEN + 1, "%lld.%06lld", (long long)timers[i]->u.sands.tv_sec, (long long)timers[i]->u.sands.tv_usec); @@ -137,7 +137,7 @@ int main(int argc, char **argv) continue; XFREE(MTYPE_TMP, timers[index]->arg); - thread_cancel(&timers[index]); + event_cancel(&timers[index]); timers_pending--; } @@ -166,8 +166,8 @@ int main(int argc, char **argv) } XFREE(MTYPE_TMP, alarms); - while (thread_fetch(master, &t)) - thread_call(&t); + while (event_fetch(master, &t)) + event_call(&t); return 0; } diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c index 21976e8d3100..3ace076b4327 100644 --- a/tests/lib/test_timer_performance.c +++ b/tests/lib/test_timer_performance.c @@ -14,15 +14,15 @@ #include <stdio.h> #include <unistd.h> -#include "thread.h" +#include "frrevent.h" #include "prng.h" #define SCHEDULE_TIMERS 1000000 #define REMOVE_TIMERS 500000 -struct thread_master *master; +struct event_loop *master; -static void dummy_func(struct thread *thread) +static void dummy_func(struct event *thread) { } @@ -30,21 +30,21 @@ int main(int argc, char **argv) { struct prng *prng; int i; - struct thread **timers; + struct event **timers; struct timeval tv_start, tv_lap, tv_stop; unsigned long t_schedule, t_remove; - master = thread_master_create(NULL); + master = event_master_create(NULL); prng = prng_new(0); timers = calloc(SCHEDULE_TIMERS, sizeof(*timers)); /* create thread structures so they won't be allocated during the * time measurement */ for (i = 0; i < SCHEDULE_TIMERS; i++) { - thread_add_timer_msec(master, dummy_func, NULL, 0, &timers[i]); + event_add_timer_msec(master, dummy_func, NULL, 0, &timers[i]); } for (i = 0; i < SCHEDULE_TIMERS; i++) - thread_cancel(&timers[i]); + event_cancel(&timers[i]); monotime(&tv_start); @@ -52,8 +52,8 @@ int main(int argc, char **argv) long interval_msec; interval_msec = prng_rand(prng) % (100 * SCHEDULE_TIMERS); - thread_add_timer_msec(master, dummy_func, NULL, interval_msec, - &timers[i]); + event_add_timer_msec(master, dummy_func, NULL, interval_msec, + &timers[i]); } monotime(&tv_lap); @@ -62,7 +62,7 @@ int main(int argc, char **argv) int index; index = prng_rand(prng) % SCHEDULE_TIMERS; - thread_cancel(&timers[index]); + event_cancel(&timers[index]); } monotime(&tv_stop); @@ -80,7 +80,7 @@ int main(int argc, char **argv) fflush(stdout); free(timers); - thread_master_free(master); + event_master_free(master); prng_free(prng); return 0; } diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index 91528139b5d7..80c4005437fa 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -171,6 +171,11 @@ static void concat(test_, TYPE)(void) ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); +#if !IS_ATOMIC(REALTYPE) + assert(!list_member(&head, &itm[0])); + assert(!list_member(&head, &itm[1])); +#endif + #if IS_SORTED(REALTYPE) prng = prng_new(0); k = 0; diff --git a/tests/lib/test_zmq.c b/tests/lib/test_zmq.c index 59a596e1c895..2cd9d47cb4d4 100644 --- a/tests/lib/test_zmq.c +++ b/tests/lib/test_zmq.c @@ -12,7 +12,7 @@ DEFINE_MTYPE_STATIC(LIB, TESTBUF, "zmq test buffer"); DEFINE_MTYPE_STATIC(LIB, ZMQMSG, "zmq message"); -static struct thread_master *master; +static struct event_loop *master; static void msg_buf_free(void *data, void *hint) { @@ -212,8 +212,8 @@ static void serverpartfn(void *arg, void *zmqsock, zmq_msg_t *msg, printf("server recv: %s\n", buf); fflush(stdout); - frrzmq_thread_add_write_msg(master, serverwritefn, NULL, msg_id, - zmqsock, &cb); + frrzmq_event_add_write_msg(master, serverwritefn, NULL, msg_id, zmqsock, + &cb); } static void serverfn(void *arg, void *zmqsock) @@ -242,8 +242,8 @@ static void serverfn(void *arg, void *zmqsock) frrzmq_thread_cancel(&cb, &cb->read); frrzmq_thread_cancel(&cb, &cb->write); - frrzmq_thread_add_read_part(master, serverpartfn, NULL, NULL, zmqsock, - &cb); + frrzmq_event_add_read_part(master, serverpartfn, NULL, NULL, zmqsock, + &cb); } static void sigchld(void) @@ -264,9 +264,9 @@ static void run_server(int syncfd) { void *zmqsock; char dummy = 0; - struct thread t; + struct event t; - master = thread_master_create(NULL); + master = event_master_create(NULL); signal_init(master, array_size(sigs), sigs); frrzmq_init(); @@ -276,15 +276,15 @@ static void run_server(int syncfd) exit(1); } - frrzmq_thread_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb); + frrzmq_event_add_read_msg(master, serverfn, NULL, NULL, zmqsock, &cb); write(syncfd, &dummy, sizeof(dummy)); - while (thread_fetch(master, &t)) - thread_call(&t); + while (event_fetch(master, &t)) + event_call(&t); zmq_close(zmqsock); frrzmq_finish(); - thread_master_free(master); + event_master_free(master); log_memstats_stderr("test"); } diff --git a/tests/ospf6d/test_lsdb.c b/tests/ospf6d/test_lsdb.c index 4bc6e869b641..f9df73538a4b 100644 --- a/tests/ospf6d/test_lsdb.c +++ b/tests/ospf6d/test_lsdb.c @@ -59,7 +59,7 @@ DEFPY(lsa_set, lsa_set_cmd, lsa_check_resize(idx + 1); if (lsas[idx]) - ospf6_lsa_unlock(lsas[idx]); + ospf6_lsa_unlock(&lsas[idx]); lsas[idx] = ospf6_lsa_create_headeronly(&hdr); ospf6_lsa_lock(lsas[idx]); return CMD_SUCCESS; @@ -75,7 +75,7 @@ DEFPY(lsa_drop, lsa_drop_cmd, return CMD_SUCCESS; if (lsas[idx]->lock != 1) vty_out(vty, "refcount at %u\n", lsas[idx]->lock); - ospf6_lsa_unlock(lsas[idx]); + ospf6_lsa_unlock(&lsas[idx]); lsas[idx] = NULL; return CMD_SUCCESS; } diff --git a/tests/ospfd/common.c b/tests/ospfd/common.c index ef5e3ed1775c..e3941865ab7a 100644 --- a/tests/ospfd/common.c +++ b/tests/ospfd/common.c @@ -17,7 +17,7 @@ #include "common.h" -struct thread_master *master; +struct event_loop *master; struct zebra_privs_t ospfd_privs; diff --git a/tests/ospfd/common.h b/tests/ospfd/common.h index 6d3e63e35917..18e412bd3caf 100644 --- a/tests/ospfd/common.h +++ b/tests/ospfd/common.h @@ -30,7 +30,7 @@ extern int topology_load(struct vty *vty, struct ospf_topology *topology, struct ospf_test_node *root, struct ospf *ospf); /* Global variables. */ -extern struct thread_master *master; +extern struct event_loop *master; extern struct ospf_topology topo1; extern struct ospf_topology topo2; extern struct ospf_topology topo3; diff --git a/tests/ospfd/test_ospf_spf.c b/tests/ospfd/test_ospf_spf.c index b8a2aef69e12..fc6b8e89ec6e 100644 --- a/tests/ospfd/test_ospf_spf.c +++ b/tests/ospfd/test_ospf_spf.c @@ -1,7 +1,7 @@ #include <zebra.h> #include "getopt.h" -#include "thread.h" +#include "frrevent.h" #include <lib/version.h> #include "vty.h" #include "command.h" @@ -208,7 +208,7 @@ static void vty_do_exit(int isexit) cmd_terminate(); vty_terminate(); - thread_master_free(master); + event_master_free(master); if (!isexit) exit(0); @@ -240,7 +240,7 @@ int main(int argc, char **argv) { char *p; char *progname; - struct thread thread; + struct event thread; bool debug = false; /* Set umask before anything for security */ @@ -273,7 +273,7 @@ int main(int argc, char **argv) } /* master init. */ - master = thread_master_create(NULL); + master = event_master_create(NULL); /* Library inits. */ cmd_init(1); @@ -297,8 +297,8 @@ int main(int argc, char **argv) vty_stdio(vty_do_exit); /* Fetch next active thread. */ - while (thread_fetch(master, &thread)) - thread_call(&thread); + while (event_fetch(master, &thread)) + event_call(&thread); /* Not reached. */ exit(0); diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 index 6d870f355f92..9ce2f2e8258f 100644 --- a/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 +++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 @@ -1,9 +1,9 @@ log file ospf6d.log ! -debug ospf6 lsa unknown -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor +!debug ospf6 lsa unknown +!debug ospf6 zebra +!debug ospf6 interface +!debug ospf6 neighbor ! interface r1-eth4 ! 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 cb63da47153c..7e28f04e1c91 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 @@ -10,6 +10,7 @@ r1-eth0 is up Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5 Hello due in XX.XXXs Neighbor Count is 0, Adjacent neighbor count is 0 + Graceful Restart hello delay: 10s r1-eth3 is up ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST> Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0 @@ -22,3 +23,4 @@ r1-eth3 is up Timer intervals configured, Hello 1s, Dead 5s, Wait 5s, Retransmit 5 Hello due in XX.XXXs Neighbor Count is 0, Adjacent neighbor count is 0 + Graceful Restart hello delay: 10s diff --git a/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref b/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref index 0534b64d3389..865970065c72 100644 --- a/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref +++ b/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref @@ -3,7 +3,7 @@ Area test: Type: lan, Level: L1, SNPA: XXXX.XXXX.XXXX Level-1 Information: Metric: 10, Active neighbors: 0 - Hello interval: 3, Holddown count: 10 (pad) + Hello interval: 3, Holddown count: 10, Padding: yes CNSP interval: 10, PSNP interval: 2 LAN Priority: 64, is not DIS IP Prefix(es): @@ -17,7 +17,7 @@ Area test: Type: lan, Level: L2, SNPA: XXXX.XXXX.XXXX Level-2 Information: Metric: 10, Active neighbors: 0 - Hello interval: 3, Holddown count: 10 (pad) + Hello interval: 3, Holddown count: 10, Padding: yes CNSP interval: 10, PSNP interval: 2 LAN Priority: 64, is not DIS IP Prefix(es): 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 fe84d496ac0b..4b7c4de80618 100644 --- a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py +++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py @@ -82,6 +82,7 @@ def setup_module(module): # # Main router for i in range(1, 2): + net["r%s" % i].loadConf("mgmtd", "%s/r%s/zebra.conf" % (thisDir, i)) 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].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i)) @@ -197,6 +198,12 @@ def test_error_messages_daemons(): 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") + print("\n\n** Check for error messages in daemons") print("******************************************\n") @@ -287,6 +294,17 @@ def test_converge_protocols(): thisDir = os.path.dirname(os.path.realpath(__file__)) + # We need loopback to have a link local so it always is the + # "selected" router for fe80::/64 when we static compare below. + print("Adding link-local to loopback for stable results") + cmd = ( + "mac=`cat /sys/class/net/lo/address`; echo lo: $mac;" + " [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS;" + " ip address add dev lo scope link" + " fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64" + ) + net["r1"].cmd_raises(cmd) + print("\n\n** Waiting for protocols convergence") print("******************************************\n") diff --git a/tests/topotests/analyze.py b/tests/topotests/analyze.py index 360c9cf1e9e1..690786a07c53 100755 --- a/tests/topotests/analyze.py +++ b/tests/topotests/analyze.py @@ -7,17 +7,61 @@ # Copyright (c) 2021, LabN Consulting, L.L.C. # import argparse -import glob +import atexit import logging import os import re import subprocess import sys +import tempfile from collections import OrderedDict import xmltodict +def get_range_list(rangestr): + result = [] + for e in rangestr.split(","): + e = e.strip() + if not e: + continue + if e.find("-") == -1: + result.append(int(e)) + else: + start, end = e.split("-") + result.extend(list(range(int(start), int(end) + 1))) + return result + + +def dict_range_(dct, rangestr, dokeys): + keys = list(dct.keys()) + if not rangestr or rangestr == "all": + for key in keys: + if dokeys: + yield key + else: + yield dct[key] + return + + dlen = len(keys) + for index in get_range_list(rangestr): + if index >= dlen: + break + key = keys[index] + if dokeys: + yield key + else: + yield dct[key] + + +def dict_range_keys(dct, rangestr): + return dict_range_(dct, rangestr, True) + + +def dict_range_values(dct, rangestr): + return dict_range_(dct, rangestr, False) + + def get_summary(results): ntest = int(results["@tests"]) nfail = int(results["@failures"]) @@ -87,7 +131,7 @@ def get_filtered(tfilters, results, args): else: if not fname: fname = cname.replace(".", "/") + ".py" - if args.files_only or "@name" not in testcase: + if "@name" not in testcase: tcname = fname else: tcname = fname + "::" + testcase["@name"] @@ -95,9 +139,14 @@ def get_filtered(tfilters, results, args): return found_files -def dump_testcase(testcase): - expand_keys = ("failure", "error", "skipped") +def search_testcase(testcase, regexp): + for key, val in testcase.items(): + if regexp.search(str(val)): + return True + return False + +def dump_testcase(testcase): s = "" for key, val in testcase.items(): if isinstance(val, str) or isinstance(val, float) or isinstance(val, int): @@ -113,23 +162,50 @@ def dump_testcase(testcase): def main(): parser = argparse.ArgumentParser() + parser.add_argument( + "-a", + "--save-xml", + action="store_true", + help=( + "Move [container:]/tmp/topotests/topotests.xml " + "to --results value if --results does not exist yet" + ), + ) parser.add_argument( "-A", "--save", action="store_true", - help="Save /tmp/topotests{,.xml} in --rundir if --rundir does not yet exist", + help=( + "Move [container:]/tmp/topotests{,.xml} " + "to --results value if --results does not exist yet" + ), ) parser.add_argument( - "-F", - "--files-only", + "-C", + "--container", + help="specify docker/podman container of the run", + ) + parser.add_argument( + "--use-podman", action="store_true", - help="print test file names rather than individual full testcase names", + help="Use `podman` instead of `docker` for saving container data", ) parser.add_argument( "-S", "--select", - default="fe", - help="select results combination of letters: 'e'rrored 'f'ailed 'p'assed 's'kipped.", + help=( + "select results combination of letters: " + "'e'rrored 'f'ailed 'p'assed 's'kipped. " + "Default is 'fe', unless --search or --time which default to 'efps'" + ), + ) + parser.add_argument( + "-R", + "--search", + help=( + "filter results to those which match a regex. " + "All test text is search unless restricted by --errmsg or --errtext" + ), ) parser.add_argument( "-r", @@ -143,57 +219,147 @@ def main(): action="store_true", help="enumerate each item (results scoped)", ) - parser.add_argument("-T", "--test", help="print testcase at enumeration") + parser.add_argument( + "-T", "--test", help="select testcase at given ordinal from the enumerated list" + ) parser.add_argument( "--errmsg", action="store_true", help="print testcase error message" ) parser.add_argument( "--errtext", action="store_true", help="print testcase error text" ) + parser.add_argument( + "--full", action="store_true", help="print all logging for selected testcases" + ) parser.add_argument("--time", action="store_true", help="print testcase run times") parser.add_argument("-s", "--summary", action="store_true", help="print summary") parser.add_argument("-v", "--verbose", action="store_true", help="be verbose") args = parser.parse_args() - if args.save and args.results and not os.path.exists(args.results): - if not os.path.exists("/tmp/topotests"): - logging.critical('No "/tmp/topotests" directory to save') + if args.save and args.save_xml: + logging.critical("Only one of --save or --save-xml allowed") + sys.exit(1) + + scount = bool(args.save) + bool(args.save_xml) + + # + # Saving/Archiving results + # + + docker_bin = "podman" if args.use_podman else "docker" + contid = "" + if args.container: + # check for container existence + contid = args.container + try: + # p = + subprocess.run( + f"{docker_bin} inspect {contid}", + check=True, + shell=True, + errors="ignore", + capture_output=True, + ) + except subprocess.CalledProcessError: + print(f"{docker_bin} container '{contid}' does not exist") sys.exit(1) - subprocess.run(["mv", "/tmp/topotests", args.results]) - # # Old location for results - # if os.path.exists("/tmp/topotests.xml", args.results): - # subprocess.run(["mv", "/tmp/topotests.xml", args.results]) + # If you need container info someday... + # cont_info = json.loads(p.stdout) + + cppath = "/tmp/topotests" + if args.save_xml or scount == 0: + cppath += "/topotests.xml" + if contid: + cppath = contid + ":" + cppath + + tresfile = None + + if scount and args.results and not os.path.exists(args.results): + if not contid: + if not os.path.exists(cppath): + print(f"'{cppath}' doesn't exist to save") + sys.exit(1) + if args.save_xml: + subprocess.run(["cp", cppath, args.results]) + else: + subprocess.run(["mv", cppath, args.results]) + else: + try: + subprocess.run( + f"{docker_bin} cp {cppath} {args.results}", + check=True, + shell=True, + errors="ignore", + capture_output=True, + ) + except subprocess.CalledProcessError as error: + print(f"Can't {docker_bin} cp '{cppath}': %s", str(error)) + sys.exit(1) + + if "SUDO_USER" in os.environ: + subprocess.run(["chown", "-R", os.environ["SUDO_USER"], args.results]) + elif not args.results: + # User doesn't want to save results just use them inplace + if not contid: + if not os.path.exists(cppath): + print(f"'{cppath}' doesn't exist") + sys.exit(1) + args.results = cppath + else: + tresfile, tresname = tempfile.mkstemp( + suffix=".xml", prefix="topotests-", text=True + ) + atexit.register(lambda: os.unlink(tresname)) + os.close(tresfile) + try: + subprocess.run( + f"{docker_bin} cp {cppath} {tresname}", + check=True, + shell=True, + errors="ignore", + capture_output=True, + ) + except subprocess.CalledProcessError as error: + print(f"Can't {docker_bin} cp '{cppath}': %s", str(error)) + sys.exit(1) + args.results = tresname - assert ( - args.test is None or not args.files_only - ), "Can't have both --files and --test" + # + # Result option validation + # + + count = 0 + if args.errmsg: + count += 1 + if args.errtext: + count += 1 + if args.full: + count += 1 + if count > 1: + logging.critical("Only one of --full, --errmsg or --errtext allowed") + sys.exit(1) + + if args.time and count: + logging.critical("Can't use --full, --errmsg or --errtext with --time") + sys.exit(1) + + if args.enumerate and (count or args.time or args.test): + logging.critical( + "Can't use --enumerate with --errmsg, --errtext, --full, --test or --time" + ) + sys.exit(1) results = {} ttfiles = [] - if args.rundir: - basedir = os.path.realpath(args.rundir) - os.chdir(basedir) - - newfiles = glob.glob("tt-group-*/topotests.xml") - if newfiles: - ttfiles.extend(newfiles) - if os.path.exists("topotests.xml"): - ttfiles.append("topotests.xml") - else: - if args.results: - if os.path.exists(os.path.join(args.results, "topotests.xml")): - args.results = os.path.join(args.results, "topotests.xml") - if not os.path.exists(args.results): - logging.critical("%s doesn't exist", args.results) - sys.exit(1) - ttfiles = [args.results] - elif os.path.exists("/tmp/topotests/topotests.xml"): - ttfiles.append("/tmp/topotests/topotests.xml") - if not ttfiles: - if os.path.exists("/tmp/topotests.xml"): - ttfiles.append("/tmp/topotests.xml") + if os.path.exists(os.path.join(args.results, "topotests.xml")): + args.results = os.path.join(args.results, "topotests.xml") + if not os.path.exists(args.results): + logging.critical("%s doesn't exist", args.results) + sys.exit(1) + + ttfiles = [args.results] for f in ttfiles: m = re.match(r"tt-group-(\d+)/topotests.xml", f) @@ -201,6 +367,14 @@ def main(): with open(f) as xml_file: results[group] = xmltodict.parse(xml_file.read())["testsuites"]["testsuite"] + search_re = re.compile(args.search) if args.search else None + + if args.select is None: + if search_re or args.time: + args.select = "efsp" + else: + args.select = "fe" + filters = [] if "e" in args.select: filters.append("error") @@ -212,43 +386,44 @@ def main(): filters.append(None) found_files = get_filtered(filters, results, args) - if found_files: - if args.test is not None: - if args.test == "all": - keys = found_files.keys() - else: - keys = [list(found_files.keys())[int(args.test)]] - for key in keys: - testcase = found_files[key] - if args.errtext: - if "error" in testcase: - errmsg = testcase["error"]["#text"] - elif "failure" in testcase: - errmsg = testcase["failure"]["#text"] - else: - errmsg = "none found" - s = "{}: {}".format(key, errmsg) - elif args.time: - text = testcase["@time"] - s = "{}: {}".format(text, key) - elif args.errmsg: - if "error" in testcase: - errmsg = testcase["error"]["@message"] - elif "failure" in testcase: - errmsg = testcase["failure"]["@message"] - else: - errmsg = "none found" - s = "{}: {}".format(key, errmsg) + + if search_re: + found_files = { + k: v for k, v in found_files.items() if search_testcase(v, search_re) + } + + if args.enumerate: + # print the selected test names with ordinal + print("\n".join(["{} {}".format(i, x) for i, x in enumerate(found_files)])) + elif args.test is None and count == 0 and not args.time: + # print the selected test names + print("\n".join([str(x) for x in found_files])) + else: + rangestr = args.test if args.test else "all" + for key in dict_range_keys(found_files, rangestr): + testcase = found_files[key] + if args.time: + text = testcase["@time"] + s = "{}: {}".format(text, key) + elif args.errtext: + if "error" in testcase: + errmsg = testcase["error"]["#text"] + elif "failure" in testcase: + errmsg = testcase["failure"]["#text"] else: - s = dump_testcase(testcase) - print(s) - elif filters: - if args.enumerate: - print( - "\n".join(["{} {}".format(i, x) for i, x in enumerate(found_files)]) - ) + errmsg = "none found" + s = "{}: {}".format(key, errmsg) + elif args.errmsg: + if "error" in testcase: + errmsg = testcase["error"]["@message"] + elif "failure" in testcase: + errmsg = testcase["failure"]["@message"] + else: + errmsg = "none found" + s = "{}: {}".format(key, errmsg) else: - print("\n".join(found_files)) + s = dump_testcase(testcase) + print(s) if args.summary: print_summary(results, args) diff --git a/tests/topotests/babel_topo1/r1/babeld.conf b/tests/topotests/babel_topo1/r1/babeld.conf index 372d2edff1e8..4058362cc3b3 100644 --- a/tests/topotests/babel_topo1/r1/babeld.conf +++ b/tests/topotests/babel_topo1/r1/babeld.conf @@ -1,4 +1,3 @@ -log file eigrpd.log interface r1-eth0 babel hello-interval 1000 diff --git a/tests/topotests/babel_topo1/r2/babeld.conf b/tests/topotests/babel_topo1/r2/babeld.conf index 8a36dda5f8d5..bae4e59e0b42 100644 --- a/tests/topotests/babel_topo1/r2/babeld.conf +++ b/tests/topotests/babel_topo1/r2/babeld.conf @@ -1,4 +1,3 @@ -log file eigrpd.log ! interface r2-eth0 babel hello-interval 1000 diff --git a/tests/topotests/babel_topo1/r3/babeld.conf b/tests/topotests/babel_topo1/r3/babeld.conf index 1e9dc261f5b9..bfda3622dd48 100644 --- a/tests/topotests/babel_topo1/r3/babeld.conf +++ b/tests/topotests/babel_topo1/r3/babeld.conf @@ -1,4 +1,3 @@ -log file eigrpd.log ! interface r3-eth0 babel hello-interval 1000 diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.py b/tests/topotests/bfd_topo2/test_bfd_topo2.py index b720218e9bc8..636dbf354d85 100644 --- a/tests/topotests/bfd_topo2/test_bfd_topo2.py +++ b/tests/topotests/bfd_topo2/test_bfd_topo2.py @@ -45,6 +45,7 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) router.load_config(TopoRouter.RD_ZEBRA, daemon_file) diff --git a/tests/topotests/bfd_topo3/r5/bfdd.conf b/tests/topotests/bfd_topo3/r5/bfdd.conf index 6d4483acc4f7..ec62d8d275a5 100644 --- a/tests/topotests/bfd_topo3/r5/bfdd.conf +++ b/tests/topotests/bfd_topo3/r5/bfdd.conf @@ -1,6 +1,6 @@ -debug bfd network -debug bfd peer -debug bfd zebra +!debug bfd network +!debug bfd peer +!debug bfd zebra ! bfd profile slow-tx diff --git a/tests/topotests/bfd_topo3/r6/bfdd.conf b/tests/topotests/bfd_topo3/r6/bfdd.conf index 6d4483acc4f7..ec62d8d275a5 100644 --- a/tests/topotests/bfd_topo3/r6/bfdd.conf +++ b/tests/topotests/bfd_topo3/r6/bfdd.conf @@ -1,6 +1,6 @@ -debug bfd network -debug bfd peer -debug bfd zebra +!debug bfd network +!debug bfd peer +!debug bfd zebra ! bfd profile slow-tx diff --git a/tests/topotests/bgp_accept_own/ce1/bgpd.conf b/tests/topotests/bgp_accept_own/ce1/bgpd.conf index fa53a4291997..44f95b9bb33a 100644 --- a/tests/topotests/bgp_accept_own/ce1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/ce1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65010 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_accept_own/ce2/bgpd.conf b/tests/topotests/bgp_accept_own/ce2/bgpd.conf index cdf8898c90d7..d60fdcf7cbaf 100644 --- a/tests/topotests/bgp_accept_own/ce2/bgpd.conf +++ b/tests/topotests/bgp_accept_own/ce2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65020 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_accept_own/pe1/bgpd.conf b/tests/topotests/bgp_accept_own/pe1/bgpd.conf index 109e0eadbb1d..15466b4259c2 100644 --- a/tests/topotests/bgp_accept_own/pe1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/pe1/bgpd.conf @@ -1,8 +1,8 @@ ! -debug bgp updates -debug bgp vpn leak-from-vrf -debug bgp vpn leak-to-vrf -debug bgp nht +!debug bgp updates +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp nht ! router bgp 65001 bgp router-id 10.10.10.10 diff --git a/tests/topotests/bgp_accept_own/rr1/bgpd.conf b/tests/topotests/bgp_accept_own/rr1/bgpd.conf index 4f0a6ab0f1b7..ad0ee3e471c0 100644 --- a/tests/topotests/bgp_accept_own/rr1/bgpd.conf +++ b/tests/topotests/bgp_accept_own/rr1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65001 bgp router-id 10.10.10.101 diff --git a/tests/topotests/bgp_addpath_best_selected/__init__.py b/tests/topotests/bgp_addpath_best_selected/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf new file mode 100644 index 000000000000..ba10f7bcc0ff --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r1/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65001 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers connect 5 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf new file mode 100644 index 000000000000..b29940f46a31 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf new file mode 100644 index 000000000000..0c13824323cc --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r2/bgpd.conf @@ -0,0 +1,28 @@ +router bgp 65002 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 remote-as external + neighbor 192.168.7.7 remote-as external + neighbor 192.168.7.7 timers connect 5 + neighbor 192.168.2.3 remote-as external + neighbor 192.168.2.3 timers connect 5 + neighbor 192.168.2.3 weight 3 + neighbor 192.168.2.4 remote-as external + neighbor 192.168.2.4 timers connect 5 + neighbor 192.168.2.4 weight 4 + neighbor 192.168.2.5 remote-as external + neighbor 192.168.2.5 timers connect 5 + neighbor 192.168.2.5 weight 5 + neighbor 192.168.2.6 remote-as external + neighbor 192.168.2.6 timers connect 5 + neighbor 192.168.2.6 weight 6 + address-family ipv4 unicast + neighbor 192.168.1.1 addpath-tx-best-selected 1 + neighbor 192.168.1.1 prefix-list announce out + neighbor 192.168.7.7 addpath-tx-best-selected 2 + neighbor 192.168.7.7 prefix-list announce out + exit-address-family +! +ip prefix-list announce seq 5 permit 172.16.16.254/32 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf new file mode 100644 index 000000000000..90587d25d457 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r2/zebra.conf @@ -0,0 +1,10 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +int r2-eth1 + ip address 192.168.2.2/24 +! +int r2-eth2 + ip address 192.168.7.2/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf new file mode 100644 index 000000000000..98eb2e1711c1 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r3/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65003 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf new file mode 100644 index 000000000000..417a4844a54d --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r3/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r3-eth0 + ip address 192.168.2.3/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf new file mode 100644 index 000000000000..68245c4a21f3 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r4/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65004 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf new file mode 100644 index 000000000000..241e38693c01 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r4/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r4-eth0 + ip address 192.168.2.4/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf new file mode 100644 index 000000000000..0396cc07b486 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r5/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65005 + timers 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf new file mode 100644 index 000000000000..203d229f2705 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r5/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r5-eth0 + ip address 192.168.2.5/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf new file mode 100644 index 000000000000..d9e77b66d667 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r6/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65006 + timers 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers connect 5 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf new file mode 100644 index 000000000000..894dd30579a9 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r6/zebra.conf @@ -0,0 +1,7 @@ +! +int lo + ip address 172.16.16.254/32 +! +int r6-eth0 + ip address 192.168.2.6/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf b/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf new file mode 100644 index 000000000000..090846a458bf --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r7/bgpd.conf @@ -0,0 +1,7 @@ +! +router bgp 65007 + timers bgp 3 10 + no bgp ebgp-requires-policy + neighbor 192.168.7.2 remote-as external + neighbor 192.168.7.2 timers connect 5 +! diff --git a/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf b/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf new file mode 100644 index 000000000000..55c70bab8be8 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/r7/zebra.conf @@ -0,0 +1,4 @@ +! +int r7-eth0 + ip address 192.168.7.7/24 +! diff --git a/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py b/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py new file mode 100644 index 000000000000..dfd538f1c648 --- /dev/null +++ b/tests/topotests/bgp_addpath_best_selected/test_bgp_addpath_best_selected.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if Add-Path best selected paths are announced per neighbor. +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + for routern in range(1, 8): + 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.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["r5"]) + switch.add_link(tgen.gears["r6"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r7"]) + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, 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_addpath_best_selected(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def check_bgp_advertised_routes_to_r1(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 neighbors 192.168.1.1 advertised-routes detail json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.16.254/32": { + "paths": [ + { + "aspath": { + "string": "65005", + } + }, + { + "aspath": { + "string": "65006", + } + }, + ] + } + }, + "totalPrefixCounter": 2, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(check_bgp_advertised_routes_to_r1) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Received more/less Add-Path best paths, but should be only 1+1 (real best path)" + + def check_bgp_advertised_routes_to_r7(): + output = json.loads( + r2.vtysh_cmd( + "show bgp ipv4 neighbors 192.168.7.7 advertised-routes detail json" + ) + ) + expected = { + "advertisedRoutes": { + "172.16.16.254/32": { + "paths": [ + { + "aspath": { + "string": "65004", + } + }, + { + "aspath": { + "string": "65005", + } + }, + { + "aspath": { + "string": "65006", + } + }, + ] + } + }, + "totalPrefixCounter": 3, + } + + return topotest.json_cmp(output, expected) + + test_func = functools.partial(check_bgp_advertised_routes_to_r7) + success, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert ( + result is None + ), "Received more/less Add-Path best paths, but should be only 2+1 (real best path)" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_aigp/r2/bgpd.conf b/tests/topotests/bgp_aigp/r2/bgpd.conf index ae72f215c079..4db468753642 100644 --- a/tests/topotests/bgp_aigp/r2/bgpd.conf +++ b/tests/topotests/bgp_aigp/r2/bgpd.conf @@ -4,7 +4,8 @@ router bgp 65001 neighbor 10.0.0.1 remote-as internal neighbor 10.0.0.1 timers 1 3 neighbor 10.0.0.1 timers connect 1 - neighbor 192.168.24.4 remote-as external + neighbor 10.0.0.1 route-reflector-client + neighbor 192.168.24.4 remote-as internal neighbor 192.168.24.4 timers 1 3 neighbor 192.168.24.4 timers connect 1 neighbor 192.168.24.4 aigp diff --git a/tests/topotests/bgp_aigp/r3/bgpd.conf b/tests/topotests/bgp_aigp/r3/bgpd.conf index 7572e268c5c9..5ab712eaba65 100644 --- a/tests/topotests/bgp_aigp/r3/bgpd.conf +++ b/tests/topotests/bgp_aigp/r3/bgpd.conf @@ -4,7 +4,8 @@ router bgp 65001 neighbor 10.0.0.1 remote-as internal neighbor 10.0.0.1 timers 1 3 neighbor 10.0.0.1 timers connect 1 - neighbor 192.168.35.5 remote-as external + neighbor 10.0.0.1 route-reflector-client + neighbor 192.168.35.5 remote-as internal neighbor 192.168.35.5 timers 1 3 neighbor 192.168.35.5 timers connect 1 neighbor 192.168.35.5 aigp diff --git a/tests/topotests/bgp_aigp/r4/bgpd.conf b/tests/topotests/bgp_aigp/r4/bgpd.conf index d2b96b7b0aef..aa88bac91385 100644 --- a/tests/topotests/bgp_aigp/r4/bgpd.conf +++ b/tests/topotests/bgp_aigp/r4/bgpd.conf @@ -1,10 +1,11 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check - neighbor 192.168.24.2 remote-as external + neighbor 192.168.24.2 remote-as internal neighbor 192.168.24.2 timers 1 3 neighbor 192.168.24.2 timers connect 1 neighbor 192.168.24.2 aigp + neighbor 192.168.24.2 route-reflector-client neighbor 10.0.0.6 remote-as internal neighbor 10.0.0.6 timers 1 3 neighbor 10.0.0.6 timers connect 1 diff --git a/tests/topotests/bgp_aigp/r5/bgpd.conf b/tests/topotests/bgp_aigp/r5/bgpd.conf index 944872289d79..4fde262053ce 100644 --- a/tests/topotests/bgp_aigp/r5/bgpd.conf +++ b/tests/topotests/bgp_aigp/r5/bgpd.conf @@ -1,10 +1,11 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check - neighbor 192.168.35.3 remote-as external + neighbor 192.168.35.3 remote-as internal neighbor 192.168.35.3 timers 1 3 neighbor 192.168.35.3 timers connect 1 neighbor 192.168.35.3 aigp + neighbor 192.168.35.3 route-reflector-client neighbor 10.0.0.6 remote-as internal neighbor 10.0.0.6 timers 1 3 neighbor 10.0.0.6 timers connect 1 diff --git a/tests/topotests/bgp_aigp/r6/bgpd.conf b/tests/topotests/bgp_aigp/r6/bgpd.conf index 15d9437a8eaa..2faae7720c90 100644 --- a/tests/topotests/bgp_aigp/r6/bgpd.conf +++ b/tests/topotests/bgp_aigp/r6/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check neighbor 10.0.0.4 remote-as internal diff --git a/tests/topotests/bgp_aigp/r7/bgpd.conf b/tests/topotests/bgp_aigp/r7/bgpd.conf index 00d85cfaddae..639dcfeca8e0 100644 --- a/tests/topotests/bgp_aigp/r7/bgpd.conf +++ b/tests/topotests/bgp_aigp/r7/bgpd.conf @@ -1,4 +1,4 @@ -router bgp 65002 +router bgp 65001 no bgp ebgp-requires-policy no bgp network import-check neighbor 192.168.67.6 remote-as internal diff --git a/tests/topotests/bgp_aigp/test_bgp_aigp.py b/tests/topotests/bgp_aigp/test_bgp_aigp.py index 6fda45940514..655e9ad18485 100644 --- a/tests/topotests/bgp_aigp/test_bgp_aigp.py +++ b/tests/topotests/bgp_aigp/test_bgp_aigp.py @@ -14,7 +14,7 @@ r2 and r3 receives those routes with aigp-metric TLV increased by 20, and 30 appropriately. -r1 receives routes with aigp-metric TLV 91,101 and 92,102 appropriately. +r1 receives routes with aigp-metric TLV 111,131 and 112,132 appropriately. """ import os @@ -109,15 +109,15 @@ def _bgp_converge(): expected = { "paths": [ { - "aigpMetric": 101, + "aigpMetric": 111, "valid": True, - "bestpath": {"selectionReason": "Router ID"}, - "nexthops": [{"hostname": "r2", "accessible": True}], + "nexthops": [{"hostname": "r3", "accessible": True}], }, { - "aigpMetric": 91, + "aigpMetric": 131, "valid": True, - "nexthops": [{"hostname": "r3", "accessible": True}], + "bestpath": {"selectionReason": "Neighbor IP"}, + "nexthops": [{"hostname": "r2", "accessible": True}], }, ] } @@ -141,28 +141,30 @@ def _bgp_check_aigp_metric_bestpath(): "10.0.0.71/32": { "paths": [ { - "aigpMetric": 101, + "aigpMetric": 111, + "bestpath": {"selectionReason": "AIGP"}, "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], }, { - "aigpMetric": 91, + "aigpMetric": 131, "valid": True, - "bestpath": {"selectionReason": "AIGP"}, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [{"hostname": "r2", "accessible": True}], }, ], }, "10.0.0.72/32": { "paths": [ { - "aigpMetric": 102, + "aigpMetric": 112, + "bestpath": {"selectionReason": "AIGP"}, "valid": True, + "nexthops": [{"hostname": "r3", "accessible": True}], }, { - "aigpMetric": 92, + "aigpMetric": 132, "valid": True, - "bestpath": {"selectionReason": "AIGP"}, - "nexthops": [{"hostname": "r3", "accessible": True}], + "nexthops": [{"hostname": "r2", "accessible": True}], }, ], }, @@ -172,7 +174,7 @@ def _bgp_check_aigp_metric_bestpath(): # Initial converge, AIGP is not involved in best-path selection process test_func = functools.partial(_bgp_converge) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "can't converge initially" # Enable `bgp bestpath aigp` @@ -186,27 +188,27 @@ def _bgp_check_aigp_metric_bestpath(): # r4, 10.0.0.71/32 with aigp-metric 71 test_func = functools.partial(_bgp_check_aigp_metric, r4, "10.0.0.71/32", 71) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.71/32 is not 71" # r5, 10.0.0.72/32 with aigp-metric 72 test_func = functools.partial(_bgp_check_aigp_metric, r5, "10.0.0.72/32", 72) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.72/32 is not 72" # r2, 10.0.0.71/32 with aigp-metric 101 (71 + 30) test_func = functools.partial(_bgp_check_aigp_metric, r2, "10.0.0.71/32", 101) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.71/32 is not 101" # r3, 10.0.0.72/32 with aigp-metric 92 (72 + 20) test_func = functools.partial(_bgp_check_aigp_metric, r3, "10.0.0.72/32", 92) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "aigp-metric for 10.0.0.72/32 is not 92" # r1, check if AIGP is considered in best-path selection (lowest wins) test_func = functools.partial(_bgp_check_aigp_metric_bestpath) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "AIGP attribute is not considered in best-path selection" diff --git a/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json b/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json new file mode 100644 index 000000000000..4156c6d0f745 --- /dev/null +++ b/tests/topotests/bgp_always_compare_med/bgp_always_compare_med_topo1.json @@ -0,0 +1,152 @@ +{ + "address_types": ["ipv4", "ipv6"], + "ipv4base": "192.168.0.0", + "ipv4mask": 30, + "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"} + }, + "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" + } + } + }] + }, + "static_routes":[ + { + "network":"192.168.20.1/32", + "next_hop":"Null0" + }, + { + "network":"192:168:20::1/128", + "next_hop":"Null0" + }] + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"}, + "r1": {"ipv4": "auto", "ipv6": "auto"}, + "r4": {"ipv4": "auto", "ipv6": "auto"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r4": {"dest_link": {"r2": {}}} + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r2": {}}}, + "r4": {"dest_link": {"r2": {}}} + } + } + } + } + } + }, + "r3": { + "links": { + "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": {}}}, + "r4": {"dest_link": {"r3": {}}} + } + } + } + } + } + }, + "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": {}}}, + "r3": {"dest_link": {"r4": {}}} + } + } + } + } + } + } + } +} diff --git a/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py b/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py new file mode 100644 index 000000000000..fb72f4331d5d --- /dev/null +++ b/tests/topotests/bgp_always_compare_med/test_bgp_always_compare_med_topo1.py @@ -0,0 +1,1118 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# +# +################################################################################ +# Following tests are performed to validate BGP always compare MED functionality +################################################################################ +""" +1. Verify the BGP always compare MED functionality in between eBGP Peers +2. Verify the BGP always compare MED functionality in between eBGP Peers with by changing different AD values +3. Verify the BGP always compare MED functionality in between eBGP Peers by changing MED values in middle routers +4. Verify that BGP Always compare MED functionality by restarting BGP, Zebra and FRR services and clear BGP and + shutdown BGP neighbor +5. Verify BGP always compare MED functionality by performing shut/noshut on the interfaces in between BGP neighbors +""" + +import os +import sys +import time +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 lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + create_static_routes, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + check_address_types, + check_router_status, + create_static_routes, + create_prefix_lists, + create_route_maps, + kill_router_daemons, + shutdown_bringup_interface, + stop_router, + start_router, + delete_route_maps, +) + +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, verify_bgp_rib, create_router_bgp, clear_bgp +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Reading the data from JSON File for topology creation +topo = None + +# Global variables +ADDR_TYPES = check_address_types() +NETWORK1_1 = {"ipv4": "192.168.20.1/32", "ipv6": "192:168:20::1/128"} +NETWORK1_2 = {"ipv4": "192.168.30.1/32", "ipv6": "192:168:30::1/128"} +NETWORK1_3 = {"ipv4": "192.168.40.1/32", "ipv6": "192:168:40::1/128"} +NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"} + + +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... + json_file = "{}/bgp_always_compare_med_topo1.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + 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 API +# +########################################################################################################## + + +def initial_configuration(tgen, tc_name): + """ + API to do initial set of configuration + """ + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + + step("Configure static routes in R4") + for addr_type in ADDR_TYPES: + input_static_r4 = { + "r4": { + "static_routes": [ + { + "network": NETWORK1_1[addr_type], + "next_hop": NEXT_HOP_IP[addr_type], + } + ] + } + } + + logger.info("Configure static routes") + result = create_static_routes(tgen, input_static_r4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure redistribute static in R4") + input_static_redist_r4 = { + "r4": { + "bgp": { + "address_family": { + addr_type: { + "unicast": {"redistribute": [{"redist_type": "static"}]} + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_static_redist_r4) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + # Create prefix list + input_dict_23 = { + "r2": { + "prefix_lists": { + addr_type: { + "pf_ls_r2_{}".format(addr_type): [ + {"network": NETWORK1_1[addr_type], "action": "permit"} + ] + } + } + }, + "r3": { + "prefix_lists": { + "ipv4": { + "pf_ls_r3_{}".format(addr_type): [ + {"network": NETWORK1_1[addr_type], "action": "permit"} + ] + } + } + }, + } + result = create_prefix_lists(tgen, input_dict_23) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + # Create route map + input_dict_23 = { + "r2": { + "route_maps": { + "RMAP_MED_R2": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r2_{}".format(addr_type) + } + }, + "set": {"med": 300}, + } + ] + } + }, + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r3_{}".format(addr_type) + } + }, + "set": {"med": 200}, + } + ] + } + }, + } + result = create_route_maps(tgen, input_dict_23) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + input_dict_r2_r3 = { + "r2": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": { + "route_maps": [ + { + "name": "RMAP_MED_R2", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + "r3": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "RMAP_MED_R3", + "direction": "out", + } + ] + } + } + } + } + } + } + } + } + }, + } + result = create_router_bgp(tgen, topo, input_dict_r2_r3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + +########################################################################################################## +# +# Testcases +# +########################################################################################################## + + +def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_p0(request): + """ + Verify the BGP always compare MED functionality in between eBGP Peers + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + 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_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove 'bgp always-compare-med' command at R1.") + input_dict_r1 = { + "r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": False}} + } + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that 'bgp always-compare-med' command is removed") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove 'multi-path as-path relax' command at R1") + configure_bgp = { + "r1": { + "bgp": { + "local_as": "100", + "bestpath": {"aspath": "multipath-relax", "delete": True}, + } + } + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Verify route selection after removing 'multi-path as-path relax' command") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_by_changing_AD_values_p0( + request, +): + """ + Verify the BGP always compare MED functionality in between eBGP Peers with by changing different AD values. + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + 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_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure AD value=100 at R2 and AD value=200 at R3 towards R1") + input_dict_1 = { + "r2": { + "bgp": { + "local_as": 200, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 100, "ibgp": 100, "local": 100} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 100, "ibgp": 100, "local": 100} + } + }, + }, + } + }, + "r3": { + "bgp": { + "local_as": 300, + "address_family": { + "ipv4": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + "ipv6": { + "unicast": { + "distance": {"ebgp": 200, "ibgp": 200, "local": 200} + } + }, + }, + } + }, + } + + result = create_router_bgp(tgen, topo, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that inspite of AD values, always lowest MED value is getting " + "selected at destination router R1" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_bw_eBGP_peers_by_changing_MED_values_p1( + request, +): + """ + Verify the BGP always compare MED functionality in between eBGP Peers by changing MED values in middle routers + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + 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_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change the MED value 150 in R2 router.") + input_dict = {"r2": {"route_maps": ["RMAP_MED_R2"]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_2 = { + "r2": { + "route_maps": { + "RMAP_MED_R2": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r2_{}".format(addr_type) + } + }, + "set": {"med": 150}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_2) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that after changing MED, its chooses lowest MED value path") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Change the MED value 100 in R3 router.") + input_dict = {"r3": {"route_maps": ["RMAP_MED_R3"]}} + result = delete_route_maps(tgen, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + for addr_type in ADDR_TYPES: + input_dict_3 = { + "r3": { + "route_maps": { + "RMAP_MED_R3": [ + { + "action": "permit", + "match": { + addr_type: { + "prefix_lists": "pf_ls_r3_{}".format(addr_type) + } + }, + "set": {"med": 100}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that after changing MED, its chooses lowest MED value path") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_by_restarting_daemons_clear_bgp_shut_neighbors_p1( + request, +): + """ + Verify that BGP Always compare MED functionality by restarting BGP, Zebra and FRR services and clear BGP and shutdown BGP neighbor + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + 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_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Restart the BGPd/Zebra/FRR service on R1") + for daemon in ["bgpd", "zebra", "frr"]: + if daemon == "frr": + stop_router(tgen, "r1") + start_router(tgen, "r1") + else: + kill_router_daemons(tgen, "r1", daemon) + + step( + "Verify after restarting dameons and frr services, its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Clear bgp on R1") + clear_bgp(tgen, None, "r1") + + step("Verify after clearing BGP, its chooses lowest MED value path") + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Perform BGP neighborship shut/no shut") + for action, keyword in zip([True, False], ["shut", "noshut"]): + for addr_type in ADDR_TYPES: + input_dict = { + "r1": { + "bgp": { + "address_family": { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r1": {"shutdown": action}} + } + } + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify after {} BGP, its chooses lowest MED value path".format(keyword)) + if action: + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + else: + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_verify_bgp_always_compare_med_functionality_by_shut_noshut_interfaces_bw_bgp_neighbors_p1( + request, +): + """ + Verify BGP always compare MED functionality by performing shut/noshut on the interfaces in between BGP neighbors + """ + + # test case name + tc_name = request.node.name + write_test_header(tc_name) + + tgen = get_topogen() + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + initial_configuration(tgen, tc_name) + + step( + "Configure IPv4 and IPv6, eBGP neighbors between R1,R2 and R3 routers as per base config" + ) + step( + "Verify that IPv4 and IPv6 eBGP neighbors are configured in between routers by following " + "commands and verify that best path chosen by lowest MED value" + ) + + 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_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'multi-path as-path relax' command at R1.") + configure_bgp = { + "r1": {"bgp": {"local_as": "100", "bestpath": {"aspath": "multipath-relax"}}} + } + result = create_router_bgp(tgen, topo, configure_bgp) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'multi-path as-path relax' command, " + "its also chooses lowest MED to reach destination." + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh1 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0] + nh2 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=[nh1, nh2]) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + step("Configure 'bgp always-compare-med' command at R1.") + input_dict_r1 = {"r1": {"bgp": {"local_as": "100", "bgp_always_compare_med": True}}} + result = create_router_bgp(tgen, topo, input_dict_r1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify that after applying 'bgp always-compare-med', its chooses lowest MED value path" + ) + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + for action, keyword in zip([False, True], ["Shut", "No Shut"]): + step( + "{} the interface on the link between R3 & R4 and R2 & R4 routers".format( + keyword + ) + ) + intf2_4 = topo["routers"]["r2"]["links"]["r4"]["interface"] + intf3_4 = topo["routers"]["r3"]["links"]["r4"]["interface"] + for dut, intf in zip(["r2", "r3"], [intf2_4, intf3_4]): + shutdown_bringup_interface(tgen, dut, intf, action) + + for addr_type in ADDR_TYPES: + input_static_r1 = { + "r1": {"static_routes": [{"network": NETWORK1_1[addr_type]}]} + } + nh = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0] + + if action: + result = verify_bgp_rib(tgen, addr_type, "r1", input_static_r1) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", input_static_r1, next_hop=nh) + assert result is True, "Testcase {} : Failed \n Error: {}".format( + tc_name, result + ) + + else: + result = verify_bgp_rib( + tgen, addr_type, "r1", input_static_r1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Routes are still present in BGP table\n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, "r1", input_static_r1, next_hop=nh, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Routes are still 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_auth/test_bgp_auth1.py b/tests/topotests/bgp_auth/test_bgp_auth1.py index 566d391f7ab0..9d47106c072f 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth1.py +++ b/tests/topotests/bgp_auth/test_bgp_auth1.py @@ -158,8 +158,8 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.items(): router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") - router.load_config(TopoRouter.RD_OSPF) - router.load_config(TopoRouter.RD_BGP) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_auth/test_bgp_auth2.py b/tests/topotests/bgp_auth/test_bgp_auth2.py index 0e9942a22725..6b920367270f 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth2.py +++ b/tests/topotests/bgp_auth/test_bgp_auth2.py @@ -158,8 +158,8 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.items(): router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") - router.load_config(TopoRouter.RD_OSPF) - router.load_config(TopoRouter.RD_BGP) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_auth/test_bgp_auth3.py b/tests/topotests/bgp_auth/test_bgp_auth3.py index 99a8953b3fc3..2237c6b1b6dc 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth3.py +++ b/tests/topotests/bgp_auth/test_bgp_auth3.py @@ -157,8 +157,8 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.items(): router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") - router.load_config(TopoRouter.RD_OSPF) - router.load_config(TopoRouter.RD_BGP) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_auth/test_bgp_auth4.py b/tests/topotests/bgp_auth/test_bgp_auth4.py index dffef0eef700..d6fe42504bc2 100644 --- a/tests/topotests/bgp_auth/test_bgp_auth4.py +++ b/tests/topotests/bgp_auth/test_bgp_auth4.py @@ -157,8 +157,8 @@ def setup_module(mod): # For all registered routers, load the zebra configuration file for rname, router in router_list.items(): router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") - router.load_config(TopoRouter.RD_OSPF) - router.load_config(TopoRouter.RD_BGP) + router.load_config(TopoRouter.RD_OSPF, "") + router.load_config(TopoRouter.RD_BGP, "") # After copying the configurations, this function loads configured daemons. tgen.start_router() diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf index 2071c256dabe..e855f75c20de 100644 --- a/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf +++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf @@ -2,7 +2,9 @@ router bgp 65001 no bgp ebgp-requires-policy neighbor 192.168.255.2 remote-as external neighbor 192.168.255.2 timers 3 10 + neighbor 192.168.255.2 timers connect 1 neighbor 192.168.255.2 bfd + neighbor 192.168.255.2 passive address-family ipv4 redistribute connected exit-address-family diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf index 3279804e6ec5..faf2c6b39bbf 100644 --- a/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf +++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf @@ -2,6 +2,7 @@ router bgp 65002 no bgp ebgp-requires-policy neighbor 192.168.255.1 remote-as external neighbor 192.168.255.1 timers 3 10 + neighbor 192.168.255.1 timers connect 1 neighbor 192.168.255.1 bfd address-family ipv4 redistribute connected diff --git a/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py index 0bc0306d7db7..00142981c502 100644 --- a/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py +++ b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py @@ -88,13 +88,14 @@ def _bgp_bfd_down_notification(): expected = { "192.168.255.1": { "lastNotificationReason": "Cease/BFD Down", + "lastNotificationHardReset": True, } } return topotest.json_cmp(output, expected) step("Initial BGP converge") test_func = functools.partial(_bgp_converge) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "Failed to see BGP convergence on R2" step("Kill bfdd on R2") @@ -102,7 +103,7 @@ def _bgp_bfd_down_notification(): step("Check if we received Cease/BFD Down notification message") test_func = functools.partial(_bgp_bfd_down_notification) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) assert result is None, "Failed to see BGP Cease/BFD Down notification message on R2" diff --git a/tests/topotests/bgp_color_extcommunities/__init__.py b/tests/topotests/bgp_color_extcommunities/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf new file mode 100644 index 000000000000..d4ca392b1a28 --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r1/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.10/24 route-map rmap + neighbor 192.168.1.2 route-map rmap out + neighbor 192.168.1.2 activate + exit-address-family +! +route-map rmap permit 10 + set extcommunity color 1 + set extcommunity rt 80:987 + set extcommunity color 100 55555 200 +exit diff --git a/tests/topotests/bgp_color_extcommunities/r1/zebra.conf b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf new file mode 100644 index 000000000000..42a830372faf --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r1/zebra.conf @@ -0,0 +1,3 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 diff --git a/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf new file mode 100644 index 000000000000..2f83ada9d34e --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65002 + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_color_extcommunities/r2/zebra.conf b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf new file mode 100644 index 000000000000..cffe8273636d --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py new file mode 100644 index 000000000000..6d17cdb4d9cc --- /dev/null +++ b/tests/topotests/bgp_color_extcommunities/test_bgp_color_extcommunities.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright 2022 6WIND S.A. +# Copyright 2023 6WIND S.A. +# François Dumontet <francois.dumontet@6wind.com> +# + + +""" +test_bgp_color_extcommunity.py: Test the FRR BGP color extented +community feature +""" + +import os +import sys +import json +import functools +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + 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): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + logger.info("setup_module") + + router_list = tgen.routers() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_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_bgp_color_extended_communities(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.2": { + "pfxSnt": 1, + "state": "Established", + }, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed announcing 10.10.10.10/32 to r2" + + def _bgp_check_route(router, exists): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 10.10.10.10 json")) + if exists: + expected = { + "prefix": "10.10.10.0/24", + "paths": [ + { + "valid": True, + "extendedCommunity": { + "string": "RT:80:987 Color:100 Color:200 Color:55555" + }, + } + ], + } + else: + expected = {} + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_route, r2, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.0/24 ext community is correctly not installed, but SHOULD be" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_comm_list_match/r2/bgpd.conf b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf index 35ad2d32e6d4..98a978068888 100644 --- a/tests/topotests/bgp_comm_list_match/r2/bgpd.conf +++ b/tests/topotests/bgp_comm_list_match/r2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65002 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r1/bgpd.conf b/tests/topotests/bgp_confed1/r1/bgpd.conf index 8413ef7fc372..107d2ad8d204 100644 --- a/tests/topotests/bgp_confed1/r1/bgpd.conf +++ b/tests/topotests/bgp_confed1/r1/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 100 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r2/bgpd.conf b/tests/topotests/bgp_confed1/r2/bgpd.conf index 9f6a9852de06..fe13dfe72994 100644 --- a/tests/topotests/bgp_confed1/r2/bgpd.conf +++ b/tests/topotests/bgp_confed1/r2/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 200 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r3/bgpd.conf b/tests/topotests/bgp_confed1/r3/bgpd.conf index 3a018a42b308..74d5fd6e290c 100644 --- a/tests/topotests/bgp_confed1/r3/bgpd.conf +++ b/tests/topotests/bgp_confed1/r3/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 300 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_confed1/r4/bgpd.conf b/tests/topotests/bgp_confed1/r4/bgpd.conf index 134f221543ca..89a85e5a34bb 100644 --- a/tests/topotests/bgp_confed1/r4/bgpd.conf +++ b/tests/topotests/bgp_confed1/r4/bgpd.conf @@ -1,7 +1,7 @@ -debug bgp neighbor-events -debug bgp nht -debug bgp updates in -debug bgp updates out +!debug bgp neighbor-events +!debug bgp nht +!debug bgp updates in +!debug bgp updates out ! router bgp 400 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py index 50c34d45fad7..75e66566b75b 100644 --- a/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py +++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py @@ -19,12 +19,14 @@ import datetime from copy import deepcopy from lib.topolog import logger +from time import sleep # pylint: disable=C0413 # Import topogen and topotest helpers from lib.topogen import Topogen, get_topogen from lib.topojson import build_config_from_json from lib.topolog import logger +from lib import topotest from lib.bgp import ( verify_bgp_convergence, @@ -592,6 +594,7 @@ def test_verify_bgp_default_originate_with_default_static_route_p1(request): step("Taking uptime snapshot before configuring default - originate") uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + sleep(1) step( "Configure default-originate on R1 link-1 again for IPv4 and IPv6 address family" @@ -1031,6 +1034,7 @@ def test_verify_bgp_default_originate_with_default_static_route_p1(request): step("Taking uptime snapshot before removing redisctribute static ") uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict) uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict) + sleep(1) step("Remove redistribute static from IPv4 and IPv6 address family ") input_dict_1 = { @@ -1556,8 +1560,14 @@ def test_verify_default_originate_with_2way_ecmp_p2(request): step("Ping R1 configure IPv4 and IPv6 loopback address from R2") pingaddr = topo["routers"]["r1"]["links"]["lo"]["ipv4"].split("/")[0] router = tgen.gears["r2"] - output = router.run("ping -c 4 -w 4 {}".format(pingaddr)) - assert " 0% packet loss" in output, "Ping R1->R2 FAILED" + + def ping_router(): + output = router.run("ping -c 4 -w 4 {}".format(pingaddr)) + logger.info(output) + if " 0% packet loss" not in output: + return False + + _, res = topotest.run_and_expect(ping_router, None, count=10, wait=1) logger.info("Ping from R1 to R2 ... success") step("Shuting up the active route") diff --git a/tests/topotests/bgp_default_originate_timer/__init__.py b/tests/topotests/bgp_default_originate_timer/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf new file mode 100644 index 000000000000..f2a1c9005ad9 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r1/bgpd.conf @@ -0,0 +1,18 @@ +router bgp 65001 + no bgp ebgp-requires-policy + bgp default-originate timer 3600 + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + neighbor 192.168.1.2 default-originate route-map default + exit-address-family +! +bgp community-list standard r3 seq 5 permit 65003:1 +! +route-map default permit 10 + match community r3 +exit diff --git a/tests/topotests/bgp_default_originate_timer/r1/zebra.conf b/tests/topotests/bgp_default_originate_timer/r1/zebra.conf new file mode 100644 index 000000000000..3692361fb39e --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r1/zebra.conf @@ -0,0 +1,7 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! diff --git a/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf new file mode 100644 index 000000000000..7ca65a94a164 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_default_originate_timer/r2/zebra.conf b/tests/topotests/bgp_default_originate_timer/r2/zebra.conf new file mode 100644 index 000000000000..0c956566636b --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf b/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf new file mode 100644 index 000000000000..0a37913d733c --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r3/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 + address-family ipv4 unicast + redistribute connected route-map r1 + exit-address-family +! +route-map r1 permit 10 + set community 65003:1 +exit diff --git a/tests/topotests/bgp_default_originate_timer/r3/zebra.conf b/tests/topotests/bgp_default_originate_timer/r3/zebra.conf new file mode 100644 index 000000000000..20801f937e37 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/r3/zebra.conf @@ -0,0 +1,7 @@ +! +interface lo + ip address 10.10.10.10/32 +! +interface r3-eth0 + ip address 192.168.2.2/24 +! diff --git a/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py b/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py new file mode 100644 index 000000000000..b2ba936fb1f6 --- /dev/null +++ b/tests/topotests/bgp_default_originate_timer/test_bgp_default_originate_timer.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Check if `bgp default-originate timer` commands takes an effect: +1. Set bgp default-originate timer 3600 +2. No default route is advertised because the timer is running for 3600 seconds +3. We reduce it to 10 seconds +4. Default route is advertised +""" + +import os +import sys +import json +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + 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["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, 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_timer(): + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + + def _bgp_default_received_from_r1(): + output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 0.0.0.0/0 json")) + expected = { + "paths": [ + { + "nexthops": [ + { + "hostname": "r1", + "ip": "192.168.1.1", + } + ], + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_default_received_from_r1) + _, result = topotest.run_and_expect(test_func, not None, count=30, wait=1) + assert result is not None, "Seen default route received from r1, but should not" + + step("Set BGP default-originate timer to 10 seconds") + r1.vtysh_cmd( + """ + configure terminal + router bgp + bgp default-originate timer 10 + """ + ) + + step("Trigger BGP UPDATE from r3") + r3.vtysh_cmd( + """ + configure terminal + route-map r1 permit 10 + set metric 1 + """ + ) + + test_func = functools.partial(_bgp_default_received_from_r1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Did not see default route received from r1, but should" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf index af1353e0e01b..44b009e9cad4 100644 --- a/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf +++ b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf @@ -1,6 +1,6 @@ ! router bgp 65001 - timers 3 10 + timers bgp 3 10 no bgp ebgp-requires-policy neighbor 192.168.1.2 remote-as external neighbor 192.168.1.2 timers connect 5 diff --git a/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf index db68e554d478..8274e3f96d82 100644 --- a/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf +++ b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf @@ -1,5 +1,5 @@ router bgp 65002 - timers 3 10 + timers bgp 3 10 no bgp ebgp-requires-policy neighbor 192.168.1.1 remote-as external neighbor 192.168.1.1 timers connect 5 diff --git a/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf index 3ac6a08e4744..98eb2e1711c1 100644 --- a/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf +++ b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf @@ -1,5 +1,5 @@ router bgp 65003 - timers 3 10 + timers bgp 3 10 no bgp ebgp-requires-policy neighbor 192.168.2.2 remote-as external neighbor 192.168.2.2 timers connect 5 diff --git a/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf index 8ab405fbd8d5..68245c4a21f3 100644 --- a/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf +++ b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf @@ -1,5 +1,5 @@ router bgp 65004 - timers 3 10 + timers bgp 3 10 no bgp ebgp-requires-policy neighbor 192.168.2.2 remote-as external neighbor 192.168.2.2 timers connect 5 diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json index 23afa2c911cb..5528b590f3b4 100755 --- a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json +++ b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json @@ -22,12 +22,7 @@ "routers": { "r1": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r2": { "ipv4": "auto", "ipv6": "auto", @@ -129,12 +124,7 @@ }, "r2": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r1": { "ipv4": "auto", "ipv6": "auto", @@ -193,12 +183,7 @@ }, "r3": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r1": { "ipv4": "auto", "ipv6": "auto", @@ -287,12 +272,7 @@ }, "r4": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r3": { "ipv4": "auto", "ipv6": "auto", @@ -336,12 +316,7 @@ }, "r5": { "links": { - "lo": { - "ipv4": "auto", - "ipv6": "auto", - "type": "loopback", - "vrf": "RED" - }, + "r3": { "ipv4": "auto", "ipv6": "auto", diff --git a/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf index e2ff1df9657f..2f76d59d4ab7 100644 --- a/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf +++ b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp neighbor +!debug bgp neighbor ! router bgp 65001 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf new file mode 100644 index 000000000000..cdf4cb4feba5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/ospfd.conf new file mode 100644 index 000000000000..2db7edb806a0 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/P1/zebra.conf new file mode 100644 index 000000000000..95b5da84024f --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json new file mode 100644 index 000000000000..9f93635c211d --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgp.l2vpn.evpn.vni.json @@ -0,0 +1,19 @@ +{ + "vni":101, + "type":"L2", + "inKernel":"True", + "rd":"10.10.10.10:101", + "originatorIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "siteOfOrigin":"65000:0", + "advertiseGatewayMacip":"Disabled", + "advertiseSviMacIp":"Active", + "sviInterface":"br101", + "importRts":[ + "65000:101" + ], + "exportRts":[ + "65000:101" + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf new file mode 100644 index 000000000000..f839443025c8 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/bgpd.conf @@ -0,0 +1,18 @@ +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 + advertise-svi-ip + vni 101 + rd 10.10.10.10:101 + route-target import 65000:101 + route-target export 65000:101 + exit-vni + advertise-svi-ip diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json new file mode 100644 index 000000000000..4bea8b384f00 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/evpn.vni.json @@ -0,0 +1,17 @@ +{ + "vni":101, + "type":"L2", + "tenantVrf":"VRF-A", + "vxlanInterface":"vxlan101", + "vtepIp":"10.10.10.10", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numRemoteVteps":1, + "remoteVteps":[ + { + "ip":"10.30.30.30", + "flood":"HER" + } + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/ospfd.conf new file mode 100644 index 000000000000..f1c2b42dc12b --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE1/zebra.conf new file mode 100644 index 000000000000..e2699475c922 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json new file mode 100644 index 000000000000..63ac73014461 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgp.l2vpn.evpn.vni.json @@ -0,0 +1,19 @@ +{ + "vni":101, + "type":"L2", + "inKernel":"True", + "rd":"10.30.30.30:101", + "originatorIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "siteOfOrigin":"65000:0", + "advertiseGatewayMacip":"Disabled", + "advertiseSviMacIp":"Active", + "sviInterface":"br101", + "importRts":[ + "65000:101" + ], + "exportRts":[ + "65000:101" + ] +} + diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf new file mode 100644 index 000000000000..9a0830d8a3a0 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/bgpd.conf @@ -0,0 +1,18 @@ +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 + advertise-svi-ip + vni 101 + rd 10.30.30.30:101 + route-target import 65000:101 + route-target export 65000:101 + exit-vni + advertise-svi-ip diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json new file mode 100644 index 000000000000..5566fff954d1 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/evpn.vni.json @@ -0,0 +1,16 @@ +{ + "vni":101, + "type":"L2", + "tenantVrf":"VRF-A", + "vxlanInterface":"vxlan101", + "vtepIp":"10.30.30.30", + "mcastGroup":"0.0.0.0", + "advertiseGatewayMacip":"No", + "numRemoteVteps":1, + "remoteVteps":[ + { + "ip":"10.10.10.10", + "flood":"HER" + } + ] +} diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/ospfd.conf new file mode 100644 index 000000000000..065c99330313 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/PE2/zebra.conf new file mode 100644 index 000000000000..9738916ab015 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf new file mode 100644 index 000000000000..cdf4cb4feba5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf new file mode 100644 index 000000000000..cdf4cb4feba5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host1/zebra.conf new file mode 100644 index 000000000000..91fae9eeba6f --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf new file mode 100644 index 000000000000..cdf4cb4feba5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/bgpd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf new file mode 100644 index 000000000000..cdf4cb4feba5 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/ospfd.conf @@ -0,0 +1 @@ +! diff --git a/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/host2/zebra.conf new file mode 100644 index 000000000000..df9adeb3b5d0 --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_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_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py new file mode 100755 index 000000000000..558f7379e9dd --- /dev/null +++ b/tests/topotests/bgp_evpn_vxlan_macvrf_soo_topo1/test_bgp_evpn_vxlan_macvrf_soo.py @@ -0,0 +1,839 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later +# +# test_bgp_evpn_vxlan_macvrf_soo.py +# +# May 10 2023, Trey Aspelund <taspelund@nvidia.com> +# +# Copyright (C) 2023 NVIDIA Corporation +# +# Test MAC-VRF Site-of-Origin feature. +# Ensure: +# - routes received with SoO are installed w/o "mac-vrf soo" config +# - invalid "mac-vrf soo" config is rejected +# - valid "mac-vrf soo" config is applied to local VNIs +# - valid "mac-vrf soo" is set for locally originated type-2/3 routes +# - routes received with SoO are unimported/uninstalled from L2VNI/zebra +# - routes received with SoO are unimported/uninstalled from L3VNI/RIB +# - routes received with SoO are still present in global EVPN loc-rib +# + +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 +from lib.common_config import step + +pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # 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" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + p1 = tgen.gears["P1"] + host1 = tgen.gears["host1"] + host2 = tgen.gears["host2"] + + # Setup PEs with: + # - vrf: VRF-A + # - l3vni 404: vxlan404 / br404 + # - l2vni 101: vxlan101 / br101 + + ## Setup VRF + # pe1 + pe1.run("ip link add VRF-A type vrf table 4000") + pe1.run("ip link set VRF-A up") + # pe2 + pe2.run("ip link add VRF-A type vrf table 4000") + pe2.run("ip link set VRF-A up") + + ## Setup L3VNI bridge/vxlan + # pe1 + pe1.run("ip link add name br404 type bridge stp_state 0") + pe1.run("ip link set dev br404 addr aa:bb:cc:00:11:ff") + pe1.run("ip link set dev br404 master VRF-A addrgenmode none") + pe1.run("ip link set dev br404 up") + pe1.run( + "ip link add vxlan404 type vxlan id 404 dstport 4789 local 10.10.10.10 nolearning" + ) + pe1.run("ip link set dev vxlan404 master br404 addrgenmode none") + pe1.run("ip link set dev vxlan404 type bridge_slave neigh_suppress on learning off") + pe1.run("ip link set dev vxlan404 up") + # pe2 + pe2.run("ip link add name br404 type bridge stp_state 0") + pe2.run("ip link set dev br404 addr aa:bb:cc:00:22:ff") + pe2.run("ip link set dev br404 master VRF-A addrgenmode none") + pe2.run("ip link set dev br404 up") + pe2.run( + "ip link add vxlan404 type vxlan id 404 dstport 4789 local 10.30.30.30 nolearning" + ) + pe2.run("ip link set dev vxlan404 master br404 addrgenmode none") + pe2.run("ip link set dev vxlan404 type bridge_slave neigh_suppress on learning off") + pe2.run("ip link set dev vxlan404 up") + + ## Setup L2VNI bridge/vxlan + L2 PE/CE link + # pe1 + 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 addr aa:bb:cc:00:11:aa") + pe1.run("ip link set dev br101 master VRF-A") + 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 dev vxlan101 type bridge_slave neigh_suppress on learning off") + pe1.run("ip link set dev vxlan101 up") + pe1.run("ip link set dev PE1-eth0 master br101") + pe1.run("ip link set dev PE1-eth0 up") + # pe2 + 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 addr aa:bb:cc:00:22:ff") + pe2.run("ip link set dev br101 master VRF-A") + 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 dev vxlan101 type bridge_slave neigh_suppress on learning off") + pe2.run("ip link set dev vxlan101 up") + pe2.run("ip link set dev PE2-eth1 master br101") + pe2.run("ip link set dev PE2-eth1 up") + + ## Enable IPv4 Routing + p1.run("sysctl -w net.ipv4.ip_forward=1") + pe1.run("sysctl -w net.ipv4.ip_forward=1") + pe2.run("sysctl -w net.ipv4.ip_forward=1") + + ## tell hosts to send GARP upon IPv4 addr assignment + host1.run("sysctl -w net.ipv4.conf.host1-eth0.arp_announce=1") + host2.run("sysctl -w net.ipv4.conf.host2-eth0.arp_announce=1") + + ## Load FRR config on all nodes and start topo + router_list = tgen.routers() + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + 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 check_vni_macs_present(tgen, router, vni, maclist): + result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True) + for rname, ifname in maclist: + m = tgen.net.macs[(rname, ifname)] + if m not in result["macs"]: + return "MAC ({}) for interface {} on {} missing on {} from {}".format( + m, ifname, rname, router.name, json.dumps(result, indent=4) + ) + return None + + +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=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + + test_func = partial( + check_vni_macs_present, + tgen, + pe1, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe1.name) + + +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=45, wait=1) + assertmsg = '"{}" JSON output mismatches'.format(pe2.name) + assert result is None, assertmsg + + test_func = partial( + check_vni_macs_present, + tgen, + pe2, + 101, + (("host1", "host1-eth0"), ("host2", "host2-eth0")), + ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + if result: + logger.warning("%s", result) + assert None, '"{}" missing expected MACs'.format(pe2.name) + + +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() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + + 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) + + +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() + for line in int_lines: + line_items = line.split(": ") + if "HWaddr" in line_items[0]: + mac = line_items[1] + break + 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() + # 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"] + 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() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + 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 is_installed(json_paths, soo): + """ + check if any path has been selected as best. + optionally check for matching SoO on bestpath. + """ + best = False + soo_present = False + for path in json_paths: + path = path[0] + # sometimes "bestpath" is a bool, other times it's a dict + # either way, the key isn't present when the bool is false... + # so we may as well just check for the key's existence + best = "bestpath" in path + path_keys = path.keys() + if best: + if soo: + soo_present = soo in path["extendedCommunity"]["string"] + break + return (best and soo_present) if soo else best + + +def change_soo(pe, soo, vni): + soo_cmd_str = "mac-vrf soo " + if soo: + soo_cmd_str += soo + else: + soo_cmd_str = "no " + soo_cmd_str + pe.vtysh_cmd( + """ + configure terminal + router bgp 65000 + address-family l2vpn evpn + {} + """.format( + soo_cmd_str + ) + ) + bgp_l2vni = get_bgp_l2vni_fields(pe, vni) + l2vni_soo = bgp_l2vni[2] + return l2vni_soo == soo + + +def get_evpn_rt_json_str(vni, rd, oip=None, mac=None, ip=None): + "convert evpn route fields into a route string + global/l2vni cli syntax" + # type-3 + if oip: + rt_str = "[3]:[0]:[32]:[{}]".format(oip) + global_rt_cmd = "show bgp l2vpn evpn route rd {} type 3 json".format(rd) + l2vni_rt_cmd = "show bgp vni {} type 3 vtep {} json".format(vni, oip) + # type-2 + else: + rt_str = "[2]:[0]:[48]:[{}]".format(mac) + global_rt_cmd = "show bgp l2vpn evpn route rd {} type 2".format(rd) + l2vni_rt_cmd = "show bgp vni {} type 2 mac {}".format(vni, mac) + if ip: + ip_len = 128 if ":" in ip else 32 + rt_str += ":[{}]:[{}]".format(ip_len, ip) + l2vni_rt_cmd = "show bgp vni {} type 2 ip {}".format(vni, ip) + global_rt_cmd += " json" + l2vni_rt_cmd += " json" + return [rt_str, global_rt_cmd, l2vni_rt_cmd] + + +def get_evpn_rt_json(pe, vni, rd, oip=None, mac=None, ip=None): + "get json global/l2vni json blobs for the corresponding evpn route" + rt = get_evpn_rt_json_str(vni, rd, oip, mac, ip) + rt_str = rt.pop(0) + global_rt_cmd = rt.pop(0) + l2vni_rt_cmd = rt.pop(0) + logger.info( + "collecting global/l2vni evpn routes for pfx {} on {}".format(rt_str, pe.name) + ) + global_rt_json = pe.vtysh_cmd(global_rt_cmd, isjson=True) + logger.info("global evpn route for pfx {} on {}".format(rt_str, pe.name)) + logger.info(global_rt_json) + l2vni_rt_json = pe.vtysh_cmd(l2vni_rt_cmd, isjson=True) + logger.info("l2vni evpn route for pfx {} on {}".format(rt_str, pe.name)) + logger.info(l2vni_rt_json) + return [rt_str, global_rt_json, l2vni_rt_json] + + +def get_bgp_l2vni_fields(pe, vni): + bgp_vni_output = pe.vtysh_cmd( + "show bgp l2vpn evpn vni {} json".format(vni), isjson=True + ) + rd = bgp_vni_output["rd"] + oip = bgp_vni_output["originatorIp"] + soo = bgp_vni_output["siteOfOrigin"] + return [rd, oip, soo] + + +def rt_test(pe, vni, rd, oip, mac, ip, soo): + """ + Check installation status of a given route. + @pe = router where bgp routes are collected from + @vni = l2vni + @rd = rd of the route + @oip = originator-ip, set only for type-3 route + @mac = nlri mac, set only for type-2 + @ip = nlri ip, optionally set for type-2 + @soo = MAC-VRF SoO string, set if SoO needs to be + on the rt to be considered installed. + """ + rt = get_evpn_rt_json(pe, vni, rd, oip, mac, ip) + rt_str = rt.pop(0) + rt_global_json = rt.pop(0) + rt_l2vni_json = rt.pop(0) + + if ( + not rt_global_json + or rd not in rt_global_json + or rt_str not in rt_global_json[rd] + ): + global_installed = False + else: + global_json_paths = rt_global_json[rd][rt_str]["paths"] + global_installed = is_installed(global_json_paths, soo) + if not rt_l2vni_json: + l2vni_installed = False + else: + if not oip: + # json for RT2s in l2vni don't key by route string + l2vni_json_paths = rt_l2vni_json["paths"] + l2vni_installed = is_installed(l2vni_json_paths, soo) + elif rt_str in rt_l2vni_json and "paths" in rt_l2vni_json[rt_str]: + l2vni_json_paths = rt_l2vni_json[rt_str]["paths"] + l2vni_installed = is_installed(l2vni_json_paths, soo) + else: + l2vni_installed = False + return [global_installed, l2vni_installed] + + +def test_macvrf_soo(): + "Test MAC-VRF Site-of-Origin on pe1" + l2vni = 101 + l3vni = 404 + soo = "65000:0" + + 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"] + host2 = tgen.gears["host2"] + pe1 = tgen.gears["PE1"] + pe2 = tgen.gears["PE2"] + + # Collect pe2 RD/Originator-IP + pe2_bgp_vni = get_bgp_l2vni_fields(pe2, l2vni) + pe2_rd = pe2_bgp_vni[0] + pe2_oip = pe2_bgp_vni[1] + # Collect local addrs + h2_mac = host2.run("ip -br link show host2-eth0").split()[2] + h2_ip = host2.run("ip -4 -br addr show host2-eth0").split()[2].split("/")[0] + pe2_mac = pe2.run("ip -br link show br101").split()[2] + pe2_ip = pe2.run("ip -4 -br addr show br101").split()[2].split("/")[0] + # Route fields + pe2_svi_parms = [l2vni, pe2_rd, None, pe2_mac, pe2_ip] + pe2_imet_parms = [l2vni, pe2_rd, pe2_oip, None, None] + host2_mac_parms = [l2vni, pe2_rd, None, h2_mac, None] + host2_neigh_parms = [l2vni, pe2_rd, None, h2_mac, h2_ip] + # Route strings + pe2_svi_rt_str, _, _ = get_evpn_rt_json_str(*pe2_svi_parms) + pe2_imet_rt_str, _, _ = get_evpn_rt_json_str(*pe2_imet_parms) + host2_mac_rt_str, _, _ = get_evpn_rt_json_str(*host2_mac_parms) + host2_neigh_rt_str, _, _ = get_evpn_rt_json_str(*host2_neigh_parms) + + ## trigger mac/arp learn + host1.run("ping -c1 10.10.1.1") + host2.run("ping -c1 10.10.1.3") + + step("Test pe2/host2 routes are installed on pe1 (global/l2vni)") + + # expected state: + # - global table: present w/o soo + # - l2vni table: present w/o soo + assertmsg = "{} missing on {} in {}{} evpn table(s)" + global_parms = [pe2.name, "global", ""] + l2vni_parms = [pe2.name, "l2vni", l2vni] + # pe2's type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe2, *pe2_svi_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's type-3 for l2vni 101 + test_f = partial(rt_test, pe2, *pe2_imet_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Add valid SoO config to pe2") + test_f = partial(change_soo, pe2, soo, l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly applied on {}".format(soo, pe2.name) + assert res == True, assertmsg + + step("Test valid config applied to L2VNI on pe2") + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: present w/ soo + assertmsg = "{} not originated with soo {} by {} in {}{} evpn table(s)" + global_parms = [soo, pe2.name, "global", ""] + l2vni_parms = [soo, pe2.name, "l2vni", l2vni] + # type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe2, *pe2_svi_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # type-3 for l2vni 101 + test_f = partial(rt_test, pe2, *pe2_imet_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + + step("Test invalid SoO config on pe2") + test_f = partial(change_soo, pe2, "1:1:1", l2vni) + _, res = topotest.run_and_expect(test_f, False, count=10, wait=1) + assertmsg = "soo '1:1:1' should not have been allowed on {}".format(pe2.name) + assert res == False, assertmsg + + step("Test valid SoO applied to host2 routes (mac-only + mac/ip) on pe2") + + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: present w/ soo + assertmsg = "{} not originated with soo {} by {} in {}{} evpn table(s)" + global_parms = [soo, pe1.name, "global", ""] + l2vni_parms = [soo, pe1.name, "l2vni", l2vni] + # mac-only type-2 for host2 + test_f = partial(rt_test, pe2, *host2_mac_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe2, *host2_neigh_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Add valid SoO to pe1") + test_f = partial(change_soo, pe1, soo, l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly applied on {}".format(soo, pe1.name) + assert res == True, assertmsg + + step("Test pe2's routes are filtered from l2vni on pe1.") + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: not present + global_assertmsg = "{} with soo {} from {} missing from global evpn table" + l2vni_assertmsg = "{} with soo {} from {} not filtered from {}{} evpn table" + global_parms = [soo, pe1.name, "global", ""] + l2vni_parms = [soo, pe1.name, "l2vni", l2vni] + # pe2's svi route + test_f = partial(rt_test, pe1, *pe2_svi_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's imet route + test_f = partial(rt_test, pe1, *pe2_imet_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, False], count=30, wait=1) + assert res[0] == True, global_assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == False, l2vni_assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Remove SoO from pe1") + test_f = partial(change_soo, pe1, "", l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly removed from {}".format(soo, pe1.name) + assert res == True, assertmsg + + step("Test pe2/host2 routes are installed on pe1 (global/l2vni)") + ## expected state: + ## - global table: present w/ soo + ## - l2vni table: present w/ soo + assertmsg = "{} with soo {} missing on {} in {}{} evpn table" + global_parms = [soo, pe1.name, "global", ""] + l2vni_parms = [soo, pe1.name, "l2vni", l2vni] + # pe2's type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe1, *pe2_svi_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's type-3 for l2vni 101 + test_f = partial(rt_test, pe1, *pe2_imet_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, soo) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + step("Remove SoO from pe2") + test_f = partial(change_soo, pe2, "", l2vni) + _, res = topotest.run_and_expect(test_f, True, count=10, wait=1) + assertmsg = "soo '{}' not properly removed from {}".format(soo, pe2.name) + assert res == True, assertmsg + + step("Test pe2's 'self' routes are installed on pe1 (global/l2vni)") + ## expected state: + ## - global table: present w/o soo + ## - l2vni table: present w/o soo + assertmsg = "{} missing on {} in {}{} evpn table(s)" + global_parms = [pe1.name, "global", ""] + l2vni_parms = [pe1.name, "l2vni", l2vni] + # pe2's type-2 for l2vni 101 svi mac/ip + test_f = partial(rt_test, pe1, *pe2_svi_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_svi_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_svi_rt_str, *l2vni_parms) + # pe2's type-3 for l2vni 101 + test_f = partial(rt_test, pe1, *pe2_imet_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(pe2_imet_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(pe2_imet_rt_str, *l2vni_parms) + # mac-only type-2 for host2 + test_f = partial(rt_test, pe1, *host2_mac_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_mac_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_mac_rt_str, *l2vni_parms) + # mac+ip type-2 for host2 + test_f = partial(rt_test, pe1, *host2_neigh_parms, None) + _, res = topotest.run_and_expect(test_f, [True, True], count=30, wait=1) + assert res[0] == True, assertmsg.format(host2_neigh_rt_str, *global_parms) + assert res[1] == True, assertmsg.format(host2_neigh_rt_str, *l2vni_parms) + + # 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_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py index f8af210ed73a..65c0c3532a9c 100755 --- a/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py +++ b/tests/topotests/bgp_evpn_vxlan_svd_topo1/test_bgp_evpn_vxlan_svd.py @@ -83,6 +83,7 @@ def build_topo(tgen): switch.add_link(tgen.gears["PE2"]) switch.add_link(tgen.gears["host2"]) + def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe = tgen.gears[pe_name] @@ -100,7 +101,9 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): # setup single vxlan device pe.run( - "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format(tunnel_local_ip) + "ip link add dev vxlan0 type vxlan dstport 4789 local {0} nolearning external".format( + tunnel_local_ip + ) ) pe.run("ip link set dev vxlan0 master bridge") pe.run("bridge link set dev vxlan0 vlan_tunnel on") @@ -136,10 +139,12 @@ def setup_pe_router(tgen, pe_name, tunnel_local_ip, svi_ip, intf): pe.run("bridge vlan add dev vxlan0 vid 300") pe.run("bridge vlan add dev vxlan0 vid 300 tunnel_info id 300") + def setup_p_router(tgen, p_name): p1 = tgen.gears[p_name] p1.run("sysctl -w net.ipv4.ip_forward=1") + def setup_module(mod): "Sets up the pytest environment" @@ -180,7 +185,7 @@ def teardown_module(mod): "Teardown the pytest environment" tgen = get_topogen() - #tgen.mininet_cli() + # tgen.mininet_cli() # This function tears down the whole topology. tgen.stop_topology() @@ -204,17 +209,21 @@ def check_vni_macs_present(tgen, router, vni, maclist): ) return None + def check_flood_entry_present(pe, vni, vtep): if not topotest.iproute2_is_fdb_get_capable(): return None - output = pe.run("bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni)) + output = pe.run( + "bridge fdb get 00:00:00:00:00:00 dev vxlan0 vni {} self".format(vni) + ) if str(vtep) not in output: return output return None + def test_pe1_converge_evpn(): "Wait for protocol convergence" @@ -231,6 +240,15 @@ def test_pe1_converge_evpn(): _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + test_func = partial( check_vni_macs_present, tgen, @@ -249,11 +267,12 @@ def test_pe1_converge_evpn(): assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe1.name, vtep) assert result is None, assertmsg + def test_pe2_converge_evpn(): "Wait for protocol convergence" tgen = get_topogen() -#Don't run this test if we have any failure. + # Don't run this test if we have any failure. if tgen.routers_have_failure(): pytest.skip(tgen.errors) @@ -284,6 +303,7 @@ def test_pe2_converge_evpn(): assertmsg = '"{}" Flood FDB Entry for VTEP {} not found'.format(pe2.name, vtep) assert result is None, assertmsg + def mac_learn_test(host, local): "check the host MAC gets learned by the VNI" @@ -389,11 +409,11 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): if "HWaddr" in line_items[0]: mac = line_items[1] break - #print(host_output) + # 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) + # 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) @@ -417,7 +437,7 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): remote_output = remote.vtysh_cmd( "show evpn mac vni 101 mac {} json".format(mac) ) - #print(remote_output) + # print(remote_output) remote_output_json = json.loads(remote_output) type = remote_output_json[mac]["type"] if not remote_output_json[mac]["neighbors"] == "none": @@ -431,12 +451,12 @@ def ip_learn_test(tgen, host, local, remote, ip_addr): count += 1 sleep(1) - #print("tries: {}".format(count)) + # 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) + # print(log_output) assert converged == True, assertmsg if remote_output_json[mac]["neighbors"]["active"]: @@ -463,8 +483,8 @@ def test_ip_pe1_learn(): host1 = tgen.gears["host1"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe2.vtysh_cmd("debug zebra vxlan") - pe2.vtysh_cmd("debug zebra kernel") + # 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") @@ -482,13 +502,14 @@ def test_ip_pe2_learn(): host2 = tgen.gears["host2"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe1.vtysh_cmd("debug zebra vxlan") - pe1.vtysh_cmd("debug zebra kernel") + # 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 show_dvni_route(pe, vni, prefix, vrf): output = pe.vtysh_cmd("show ip route vrf {} {}".format(vrf, prefix)) @@ -502,6 +523,7 @@ def show_dvni_route(pe, vni, prefix, vrf): return None + def test_dvni(): "test Downstream VNI works as expected importing into PE1" @@ -517,7 +539,7 @@ def test_dvni(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) assertmsg = '"{}" DVNI route {} not found'.format(pe1.name, prefix) assert result is None, assertmsg - #tgen.mininet_cli() + # tgen.mininet_cli() def test_memory_leak(): 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 index 48b79ab5eed6..288404301215 100755 --- a/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py +++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py @@ -164,6 +164,15 @@ def test_pe1_converge_evpn(): _, result = topotest.run_and_expect(test_func, None, count=45, wait=1) assertmsg = '"{}" JSON output mismatches'.format(pe1.name) + # Let's ensure that the hosts have actually tried talking to + # each other. Otherwise under certain startup conditions + # they may not actually do any l2 arp'ing and as such + # the bridges won't know about the hosts on their networks + host1 = tgen.gears["host1"] + host1.run("ping -c 1 10.10.1.56") + host2 = tgen.gears["host2"] + host2.run("ping -c 1 10.10.1.55") + test_func = partial( check_vni_macs_present, tgen, @@ -171,6 +180,7 @@ def test_pe1_converge_evpn(): 101, (("host1", "host1-eth0"), ("host2", "host2-eth0")), ) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) if result: logger.warning("%s", result) @@ -385,8 +395,8 @@ def test_ip_pe1_learn(): host1 = tgen.gears["host1"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe2.vtysh_cmd("debug zebra vxlan") - pe2.vtysh_cmd("debug zebra kernel") + # 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") @@ -404,8 +414,8 @@ def test_ip_pe2_learn(): host2 = tgen.gears["host2"] pe1 = tgen.gears["PE1"] pe2 = tgen.gears["PE2"] - pe1.vtysh_cmd("debug zebra vxlan") - pe1.vtysh_cmd("debug zebra kernel") + # 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") diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json index 3b5f46d9346b..caf700d82a97 100644 --- a/tests/topotests/bgp_features/r1/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.2":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ], "192.168.0.3":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json index 47bb57cd00f7..3a168ba33592 100644 --- a/tests/topotests/bgp_features/r2/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.1":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ], "192.168.0.3":[ { - "priority":5, + "nbrPriority":5, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json index b84974ccca64..9f8c05949f8d 100644 --- a/tests/topotests/bgp_features/r3/ospf_neighbor.json +++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json @@ -2,13 +2,13 @@ "neighbors":{ "192.168.0.1":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ], "192.168.0.2":[ { - "priority":10, + "nbrPriority":10, "converged":"Full" } ] diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index c0a5a1905d74..43f4905d4186 100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -176,6 +176,19 @@ def test_bgp_convergence(): # tgen.mininet_cli() +def get_shut_msg_count(tgen): + shuts = {} + for rtrNum in [2, 4]: + shutmsg = tgen.net["r{}".format(rtrNum)].cmd_nostatus( + 'grep -c "NOTIFICATION.*Cease/Administrative Shutdown" bgpd.log', warn=False + ) + try: + shuts[rtrNum] = int(shutmsg.strip()) + except ValueError: + shuts[rtrNum] = 0 + return shuts + + def test_bgp_shutdown(): "Test BGP instance shutdown" @@ -185,6 +198,8 @@ def test_bgp_shutdown(): if tgen.routers_have_failure(): pytest.skip(tgen.errors) + shuts_before = get_shut_msg_count(tgen) + tgen.net["r1"].cmd( 'vtysh -c "conf t" -c "router bgp 65000" -c "bgp shutdown message ABCDabcd"' ) @@ -208,6 +223,11 @@ def test_bgp_shutdown(): ) assert res is None, assertmsg + shuts_after = get_shut_msg_count(tgen) + + for k in shuts_before: + assert shuts_before[k] + 1 == shuts_after[k] + def test_bgp_shutdown_message(): "Test BGP Peer Shutdown Message" @@ -222,18 +242,11 @@ def test_bgp_shutdown_message(): 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/Administrative Shutdown"' + 'grep -e "NOTIFICATION.*Cease/Administrative Shutdown.*ABCDabcd" bgpd.log' ) assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum) assert shut_message != "", assertmsg - assertmsg = "Incorrect BGP shutdown message received on router R{}".format( - rtrNum - ) - assert "ABCDabcd" in shut_message, assertmsg - - # tgen.mininet_cli() - def test_bgp_no_shutdown(): "Test BGP instance no shutdown" @@ -1050,7 +1063,7 @@ def test_bgp_delayopen_dual(): delay_stop = int(time.time()) assertmsg = "BGP peering between r2 and r5 was established before DelayOpenTimer (30sec) on r2 could expire" - assert (delay_stop - delay_start) > 30, assertmsg + assert (delay_stop - delay_start) >= 30, assertmsg # 3.8 unset delayopen on R2 and R5 logger.info("Disabling DelayOpenTimer for neighbor r5 on r2") diff --git a/tests/topotests/bgp_flowspec/peer1/exabgp.cfg b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg index cd1fae5aba24..383a95b6dd57 100644 --- a/tests/topotests/bgp_flowspec/peer1/exabgp.cfg +++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg @@ -1,5 +1,6 @@ neighbor 10.0.1.1 { router-id 10.0.1.101; +hold-time 10; local-address 10.0.1.101; local-as 100; peer-as 100; diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py index fd675dc8aeb5..a2be85962feb 100644 --- a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py +++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py @@ -51,6 +51,7 @@ from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger +from lib.common_config import generate_support_bundle # Required to instantiate the topology builder class. @@ -137,9 +138,12 @@ def test_bgp_convergence(): 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) + _, res = topotest.run_and_expect(test_func, None, count=210, wait=1) assertmsg = "BGP router network did not converge" - assert res is None, assertmsg + if res is not None: + generate_support_bundle() + assert res is None, assertmsg + generate_support_bundle() def test_bgp_flowspec(): @@ -183,7 +187,6 @@ def test_bgp_flowspec(): if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] ret = pytest.main(args) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py index 6388295c951f..1a8f8302ffef 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py @@ -1159,7 +1159,7 @@ def test_BGP_GR_TC_31_2_p1(request): reset_config_on_routers(tgen) logger.info( - "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Restart Mode] initialized " + "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Helper Mode] initialized " ) # Configure graceful-restart @@ -1251,7 +1251,7 @@ def test_BGP_GR_TC_31_2_p1(request): tc_name, result ) - logger.info("[Phase 2] : R2 Goes from Disable to Restart Mode ") + logger.info("[Phase 2] : R1 Goes from Disable to Restart Mode ") # Configure graceful-restart input_dict = { @@ -1356,31 +1356,7 @@ def test_BGP_GR_TC_31_2_p1(request): }, } - # 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 - ) - - 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 ") + logger.info("[Phase 4] : R1 is UP and GR state is correct ") for addr_type in ADDR_TYPES: result = verify_graceful_restart( @@ -2142,6 +2118,9 @@ def test_BGP_GR_TC_43_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -2432,6 +2411,9 @@ def test_BGP_GR_TC_44_p1(request): result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r2", ["bgpd"]) + write_test_footer(tc_name) @@ -2727,6 +2709,9 @@ def test_BGP_GR_TC_45_p1(request): result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol) assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py index 1166cdc0efb8..31aaa0b8a6df 100644 --- a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py +++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py @@ -763,6 +763,9 @@ def test_BGP_GR_TC_46_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -1023,6 +1026,9 @@ def test_BGP_GR_TC_47_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) @@ -1300,6 +1306,9 @@ def test_BGP_GR_TC_48_p1(request): tc_name, result ) + # restart the daemon or we get warnings in the follow-on tests + start_router_daemons(tgen, "r1", ["bgpd"]) + write_test_footer(tc_name) diff --git a/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py b/tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py similarity index 100% rename from tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py rename to tests/topotests/bgp_gr_functionality_topo3/test_bgp_gr_functionality_topo3.py diff --git a/tests/topotests/bgp_ipv6_ll_peering/__init__.py b/tests/topotests/bgp_ipv6_ll_peering/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf new file mode 100644 index 000000000000..724cbf84ab38 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r1/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65001 + bgp router-id 10.0.0.1 + no bgp ebgp-requires-policy + neighbor fe80:1::2 remote-as external + neighbor fe80:1::2 timers 3 10 + neighbor fe80:1::2 interface r1-eth0 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf new file mode 100644 index 000000000000..4e93d4f4e568 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r1/zebra.conf @@ -0,0 +1,4 @@ +! +interface r1-eth0 + ipv6 address fe80:1::1/64 +! diff --git a/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf b/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf new file mode 100644 index 000000000000..44f79dfc33af --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r2/bgpd.conf @@ -0,0 +1,6 @@ +router bgp 65002 + bgp router-id 10.0.0.2 + no bgp ebgp-requires-policy + neighbor fe80:1::1 remote-as external + neighbor fe80:1::1 timers 3 10 + neighbor fe80:1::1 interface r2-eth0 diff --git a/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf b/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf new file mode 100644 index 000000000000..1e703cda3691 --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/r2/zebra.conf @@ -0,0 +1,4 @@ +! +interface r2-eth0 + ipv6 address fe80:1::2/64 +! diff --git a/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py new file mode 100644 index 000000000000..ea974b5302fe --- /dev/null +++ b/tests/topotests/bgp_ipv6_ll_peering/test_bgp_ipv6_ll_peering.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Check if IPv6 Link-Local BGP peering works fine. +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + 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(build_topo, 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_ipv6_link_local_peering(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "fe80:1::2": { + "state": "Established", + } + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed to see BGP convergence on R2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) 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 8d42cfc0d89c..72211fee7f10 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -5,7 +5,7 @@ password zebra log stdout notifications log commands -log file bgpd.log debugging +log file bgpd.log #debug bgp vpn leak-to-vrf #debug bgp vpn leak-from-vrf 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 7b42b770b54e..edb3b699f913 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf @@ -4,7 +4,7 @@ hostname r2 password zebra log stdout notifications log commands -log file bgpd.log debugging +log file bgpd.log router bgp 5226 bgp router-id 2.2.2.2 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 ca9e62717259..ed76ed3c6325 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf @@ -4,7 +4,7 @@ hostname r4 password zebra log stdout notifications log commands -log file bgpd.log debug +log file bgpd.log #debug bgp vpn label #debug bgp nht 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 index eaa6aa4c3072..46993c7d9a46 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py @@ -62,6 +62,25 @@ "pass", "Adding {} routes".format(num), ) + luCommand( + "ce1", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce1", + wait, + wait_time=10, + ) + luCommand( + "ce2", + 'vtysh -c "show ip route summ" | grep "sharp" | cut -d " " -f 33', + str(num), + "wait", + "See all sharp routes in rib on ce2", + wait, + wait_time=10, + ) + rtrs = ["ce1", "ce2", "ce3"] for rtr in rtrs: luCommand( diff --git a/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py index 3af779c427c2..f4bb487e4091 100644 --- a/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py +++ b/tests/topotests/bgp_labeled_unicast_addpath/test_bgp_labeled_unicast_addpath.py @@ -82,53 +82,23 @@ def test_bgp_addpath_labeled_unicast(): r3 = tgen.gears["r3"] r4 = tgen.gears["r4"] - def _bgp_check_advertised_routes(prefix_num): - output = json.loads( - r3.vtysh_cmd( - "show bgp ipv4 labeled-unicast neighbors 192.168.34.4 advertised-routes json" - ) - ) + def _bgp_check_received_routes(pfxcount): + output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast summary json")) expected = { - "advertisedRoutes": { - "10.0.0.1/32": { - "appliedStatusSymbols": { - "*": True, - ">": True, - "=": True, - } + "peers": { + "192.168.34.3": { + "pfxRcd": pfxcount, + "state": "Established", } - }, - "totalPrefixCounter": prefix_num, + } } return topotest.json_cmp(output, expected) - test_func = functools.partial(_bgp_check_advertised_routes, 2) + test_func = functools.partial(_bgp_check_received_routes, 2) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert ( result is None - ), "Failed to advertise labeled-unicast with addpath (multipath)" - - def _bgp_check_received_routes(): - output = json.loads(r4.vtysh_cmd("show bgp ipv4 labeled-unicast json")) - expected = { - "routes": { - "10.0.0.1/32": [ - { - "valid": True, - "path": "65003 65001", - }, - { - "valid": True, - "path": "65003 65002", - }, - ] - } - } - return topotest.json_cmp(output, expected) - - test_func = functools.partial(_bgp_check_received_routes) - _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) - assert result is None, "Failed to receive labeled-unicast with addpath (multipath)" + ), "Failed to receive labeled-unicast with addpath (multipath=2)" step("Enable BGP session for R5") r3.vtysh_cmd( @@ -139,11 +109,11 @@ def _bgp_check_received_routes(): """ ) - test_func = functools.partial(_bgp_check_advertised_routes, 3) + test_func = functools.partial(_bgp_check_received_routes, 3) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert ( result is None - ), "Failed to advertise labeled-unicast with addpath (multipath)" + ), "Failed to receive labeled-unicast with addpath (multipath=3)" step("Disable BGP session for R5") r3.vtysh_cmd( @@ -154,11 +124,11 @@ def _bgp_check_received_routes(): """ ) - test_func = functools.partial(_bgp_check_advertised_routes, 2) + test_func = functools.partial(_bgp_check_received_routes, 2) _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) assert ( result is None - ), "Failed to advertise labeled-unicast with addpath (multipath)" + ), "Failed to receive labeled-unicast with addpath (multipath=2)" if __name__ == "__main__": diff --git a/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py index efecad3eb28a..930fd791b0a5 100644 --- a/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py +++ b/tests/topotests/bgp_local_as_dotplus_private_remove/test_bgp_local_as_dotplus_private_remove.py @@ -31,13 +31,14 @@ 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 pytestmark = [pytest.mark.bgpd] @@ -84,29 +85,43 @@ def test_bgp_remove_private_as(): 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": - 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"] - - if _bgp_converge("r2"): - assert len(_bgp_as_path("r2")) == 1 - assert '0.65000' not in _bgp_as_path("r2") - - if _bgp_converge("r4"): - assert len(_bgp_as_path("r4")) == 2 - assert '0.3000' in _bgp_as_path("r4") + r2 = tgen.gears["r2"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")) + expected = { + "192.168.255.1": { + "bgpState": "Established", + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initially" + + def _bgp_as_path(router, asn_path, asn_length): + output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json")) + expected = { + "paths": [ + { + "aspath": { + "string": asn_path, + "length": asn_length, + } + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_as_path, r2, "0.500", 1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Private ASNs not stripped" + + test_func = functools.partial(_bgp_as_path, r4, "0.500 0.3000", 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Private ASNs not stripped" if __name__ == "__main__": diff --git a/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf new file mode 100644 index 000000000000..a31439c9848d --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.2 remote-as 65501 +! + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + network 192.168.2.1/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.2 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf new file mode 100644 index 000000000000..b84574891e08 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r1/zebra.conf @@ -0,0 +1,6 @@ +interface r1-eth0 + ip address 192.0.2.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/32 +! \ No newline at end of file diff --git a/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf new file mode 100644 index 000000000000..41c2b9b6fa0a --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65501 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + bgp labeled-unicast explicit-null + neighbor 192.0.2.1 remote-as 65500 +! + address-family ipv4 unicast + no neighbor 192.0.2.1 activate + network 192.168.2.2/32 + exit-address-family + ! + address-family ipv4 labeled-unicast + neighbor 192.0.2.1 activate + exit-address-family +! diff --git a/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf new file mode 100644 index 000000000000..9a639610c16e --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/r2/zebra.conf @@ -0,0 +1,6 @@ +interface r2-eth0 + ip address 192.0.2.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/32 +! diff --git a/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py new file mode 100644 index 000000000000..0656e1ed41e4 --- /dev/null +++ b/tests/topotests/bgp_lu_explicitnull/test_bgp_lu_explicitnull.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_explicitnull.py +# +# Part of NetDEF Topology Tests +# +# Copyright 2023 by 6WIND S.A. +# + +""" +test_bgp_lu_explicitnull.py: Test BGP LU label allocation +""" + +import os +import sys +import json +import functools +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 + + +pytestmark = [pytest.mark.bgpd] + + +# Basic scenario for BGP-LU. Nodes are directly connected. +# The 192.168.2.2/32 prefix is advertised from r2 to r1 +# The explicit-null label should be used +# The 192.168.2.1/32 prefix is advertised from r1 to r2 +# The explicit-null label should be used +# Traffic from 192.168.2.1 to 192.168.2.2 should use explicit-null label +# +# AS65500 BGP-LU AS65501 +# +-----+ +-----+ +# | |.1 .2| | +# | 1 +----------------+ 2 + 192.168.0.2/32 +# | | 192.0.2.0/24 | | +# +-----+ +-----+ + + +def build_topo(tgen): + "Build function" + + # Create routers + tgen.add_router("r1") + tgen.add_router("r2") + + # r1-r2 + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # r1 + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + # r2 + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + +def setup_module(mod): + "Sets up the pytest environment" + # This function initiates the topology build with Topogen... + tgen = Topogen(build_topo, mod.__name__) + + # Skip if no mpls support + if not tgen.hasmpls: + logger.info("MPLS is not available, skipping test") + pytest.skip("MPLS is not available, skipping") + return + + # ... and here it calls Mininet initialization functions. + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + + # Enable mpls input for routers, so we can ping + sval = "net.mpls.conf.{}.input" + topotest.sysctl_assure(router_list["r2"], sval.format("r2-eth0"), 1) + topotest.sysctl_assure(router_list["r1"], sval.format("r1-eth0"), 1) + + # For all registred routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_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 check_show_ip_label_prefix_found(router, ipversion, prefix, label): + output = json.loads( + router.vtysh_cmd("show {} route {} json".format(ipversion, prefix)) + ) + expected = { + prefix: [ + {"prefix": prefix, "nexthops": [{"fib": True, "labels": [int(label)]}]} + ] + } + return topotest.json_cmp(output, expected) + + +def test_converge_bgplu(): + "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) + + # tgen.mininet_cli(); + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + # Check r1 gets prefix 192.168.2.2/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r1"], + "ip", + "192.168.2.2/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, prefix 192.168.2.2/32 from r2 not present" + + # Check r2 gets prefix 192.168.2.1/32 + test_func = functools.partial( + check_show_ip_label_prefix_found, + tgen.gears["r2"], + "ip", + "192.168.2.1/32", + "0", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, prefix 192.168.2.1/32 from r1 not present" + + +def test_traffic_connectivity(): + "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 _check_ping(name, dest_addr, src_addr): + tgen = get_topogen() + output = tgen.gears[name].run( + "ping {} -c 1 -w 1 -I {}".format(dest_addr, src_addr) + ) + logger.info(output) + if " 0% packet loss" not in output: + return True + + logger.info("r1, check ping 192.168.2.2 from 192.168.2.1 is OK") + tgen = get_topogen() + func = functools.partial(_check_ping, "r1", "192.168.2.2", "192.168.2.1") + # tgen.mininet_cli() + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "r1, ping to 192.168.2.2 from 192.168.2.1 fails" + + +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_lu_topo1/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json index 6db8e002f429..c66571f46332 100644 --- a/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json +++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json @@ -2,7 +2,5 @@ "ledger":506, "inUse":506, "requests":0, - "labelChunks":3, - "pending":0, - "reconnects":0 + "labelChunks":3 } diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json index 9f9e57511c4a..d35e4ef46304 100644 --- a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json +++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json @@ -2,7 +2,5 @@ "ledger":0, "inUse":0, "requests":0, - "labelChunks":0, - "pending":0, - "reconnects":0 + "labelChunks":1 } diff --git a/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json index 59ecd27f7fbe..faeaa3ec5f92 100644 --- a/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json +++ b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json @@ -2,7 +2,5 @@ "ledger":51, "inUse":51, "requests":0, - "labelChunks":1, - "pending":0, - "reconnects":0 + "labelChunks":1 } diff --git a/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json index 2c52192cd656..5f9d8e66241e 100644 --- a/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json +++ b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json @@ -2,7 +2,5 @@ "ledger":1, "inUse":1, "requests":0, - "labelChunks":1, - "pending":0, - "reconnects":0 + "labelChunks":1 } diff --git a/tests/topotests/bgp_node_target_extcommunities/__init__.py b/tests/topotests/bgp_node_target_extcommunities/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf new file mode 100644 index 000000000000..86983387d5ae --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r1/frr.conf @@ -0,0 +1,21 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router bgp 65001 + bgp router-id 192.168.1.1 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.3 remote-as external + neighbor 192.168.1.4 remote-as external + address-family ipv4 unicast + network 10.10.10.10/32 + neighbor 192.168.1.2 route-map rmap out + neighbor 192.168.1.3 route-map rmap out + neighbor 192.168.1.4 route-map rmap out + exit-address-family +! +route-map rmap permit 10 + set extcommunity nt 192.168.1.3:0 192.168.1.4:0 +exit diff --git a/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf new file mode 100644 index 000000000000..09fda78a3da9 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r2/frr.conf @@ -0,0 +1,8 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + bgp router-id 192.168.1.2 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf new file mode 100644 index 000000000000..4883f1f5c207 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r3/frr.conf @@ -0,0 +1,8 @@ +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router bgp 65003 + bgp router-id 192.168.1.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf new file mode 100644 index 000000000000..f518bd1fa08f --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/r4/frr.conf @@ -0,0 +1,8 @@ +! +int r4-eth0 + ip address 192.168.1.4/24 +! +router bgp 65004 + bgp router-id 192.168.1.4 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external diff --git a/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py new file mode 100644 index 000000000000..23e820b4fc39 --- /dev/null +++ b/tests/topotests/bgp_node_target_extcommunities/test_bgp_node_target_extcommunities.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if Node Target Extended Communities works. + +At r1 we set NT to 192.168.1.3 and 192.168.1.4 (this is the R3/R4 router-id), +and that means 10.10.10.10/32 MUST be installed on R3 and R4, but not on R2, +because this route does not have NT:192.168.1.2. +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + 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.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_node_target_extended_communities(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + r3 = tgen.gears["r3"] + r4 = tgen.gears["r4"] + + def _bgp_converge(): + output = json.loads(r1.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": { + "192.168.1.2": { + "pfxSnt": 1, + "state": "Established", + }, + "192.168.1.3": { + "pfxSnt": 1, + "state": "Established", + }, + "192.168.1.4": { + "pfxSnt": 1, + "state": "Established", + }, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed announcing 10.10.10.10/32 to r2, r3, and r4" + + def _bgp_check_route(router, exists): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + if exists: + expected = { + "routes": { + "10.10.10.10/32": [ + { + "valid": True, + } + ] + } + } + else: + expected = { + "routes": { + "10.10.10.10/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_route, r3, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is not installed, but SHOULD be" + + test_func = functools.partial(_bgp_check_route, r4, True) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is not installed, but SHOULD be" + + test_func = functools.partial(_bgp_check_route, r2, False) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "10.10.10.10/32 is installed, but SHOULD NOT be" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py index ebaab603412a..c97cd0bddabd 100644 --- a/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py +++ b/tests/topotests/bgp_path_attribute_discard/test_bgp_path_attribute_discard.py @@ -83,7 +83,7 @@ def _bgp_converge(): "paths": [ { "valid": True, - "originatorId": "10.0.0.2", + "originatorId": None, "community": { "string": "65001:102", }, @@ -98,12 +98,12 @@ def _bgp_converge(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert result is None, "Failed bgp convergence" - step("Discard atomic-aggregate, community, and originator-id attributes from peer1") + step("Discard atomic-aggregate, and community attributes from peer1") r1.vtysh_cmd( """ configure terminal router bgp - neighbor 10.0.0.2 path-attribute discard 6 8 9 + neighbor 10.0.0.2 path-attribute discard 6 8 """ ) @@ -137,7 +137,7 @@ def _bgp_check_if_attributes_discarded(): _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) assert ( result is None - ), "Failed to discard path attributes (atomic-aggregate, community, and originator-id)" + ), "Failed to discard path attributes (atomic-aggregate, community)" def test_memory_leak(): diff --git a/tests/topotests/bgp_peer_group/r3/bgpd.conf b/tests/topotests/bgp_peer_group/r3/bgpd.conf index eb2fca15fb96..5a1340fb0b88 100644 --- a/tests/topotests/bgp_peer_group/r3/bgpd.conf +++ b/tests/topotests/bgp_peer_group/r3/bgpd.conf @@ -1,7 +1,11 @@ ! router bgp 65003 - neighbor PG peer-group - neighbor PG remote-as external - neighbor PG timers 3 10 - neighbor 192.168.255.1 peer-group PG + no bgp ebgp-requires-policy + neighbor PG peer-group + neighbor PG remote-as external + neighbor PG timers 3 10 + neighbor 192.168.255.1 peer-group PG + address-family ipv4 unicast + redistribute connected + exit-address-family ! diff --git a/tests/topotests/bgp_peer_group/test_bgp_peer-group.py b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py index e8c3feb76fff..a91fade04924 100644 --- a/tests/topotests/bgp_peer_group/test_bgp_peer-group.py +++ b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py @@ -74,9 +74,26 @@ def _bgp_peer_group_configured(): return topotest.json_cmp(output, expected) test_func = functools.partial(_bgp_peer_group_configured) - success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed bgp convergence in r1" - assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"]) + def _bgp_peer_group_check_advertised_routes(): + output = json.loads( + tgen.gears["r3"].vtysh_cmd("show ip bgp neighbor PG advertised-routes json") + ) + expected = { + "advertisedRoutes": { + "192.168.255.0/24": { + "valid": True, + "best": True, + } + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_peer_group_check_advertised_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Failed checking advertised routes from r3" if __name__ == "__main__": diff --git a/tests/topotests/bgp_prefix_list_any/__init__.py b/tests/topotests/bgp_prefix_list_any/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf new file mode 100644 index 000000000000..14c28ca90684 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf @@ -0,0 +1,15 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + neighbor 2001:db8:1::2 remote-as external + address-family ipv4 unicast + network 192.168.0.1/32 + no neighbor 2001:db8:1::2 activate + exit-address-family + address-family ipv6 unicast + neighbor 2001:db8:1::2 activate + network 2001:db8::1/128 + exit-address-family +! diff --git a/tests/topotests/bgp_prefix_list_any/r1/zebra.conf b/tests/topotests/bgp_prefix_list_any/r1/zebra.conf new file mode 100644 index 000000000000..c01a8cf03373 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r1/zebra.conf @@ -0,0 +1,5 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf new file mode 100644 index 000000000000..733205928f3e --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf @@ -0,0 +1,50 @@ +! +!debug bgp updates +! +router bgp 65002 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.1 remote-as external + neighbor 2001:db8:1::1 remote-as external + address-family ipv4 unicast + network 10.10.10.1/32 + network 10.10.10.2/32 + network 10.10.10.3/32 + network 10.10.10.10/32 + no neighbor 2001:db8:1::1 activate + neighbor 192.168.1.1 route-map r1-v4 out + exit-address-family + address-family ipv6 unicast + network 2001:db8:10::1/128 + network 2001:db8:10::2/128 + network 2001:db8:10::3/128 + network 2001:db8:10::10/128 + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:1::1 route-map r1-v6 out + exit-address-family +! +ip prefix-list r1-1 seq 5 permit 10.10.10.1/32 +ip prefix-list r1-1 seq 10 permit 10.10.10.2/32 +ip prefix-list r1-1 seq 15 permit 10.10.10.3/32 +ip prefix-list r1-2 seq 5 permit 10.10.10.10/32 +! +ipv6 prefix-list r1-1 seq 5 permit 2001:db8:10::1/128 +ipv6 prefix-list r1-1 seq 10 permit 2001:db8:10::2/128 +ipv6 prefix-list r1-1 seq 15 permit 2001:db8:10::3/128 +ipv6 prefix-list r1-2 seq 5 permit 2001:db8:10::10/128 +! +route-map r1-v4 permit 10 + match ip address prefix-list r1-1 +exit +! +route-map r1-v4 permit 20 + match ip address prefix-list r1-2 +exit +! +route-map r1-v6 permit 10 + match ipv6 address prefix-list r1-1 +exit +! +route-map r1-v6 permit 20 + match ipv6 address prefix-list r1-2 +exit diff --git a/tests/topotests/bgp_prefix_list_any/r2/zebra.conf b/tests/topotests/bgp_prefix_list_any/r2/zebra.conf new file mode 100644 index 000000000000..e90135c786e1 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/r2/zebra.conf @@ -0,0 +1,5 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:db8:1::2/64 +! diff --git a/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py b/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py new file mode 100644 index 000000000000..0eb244717ef8 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if route-map works correctly when modifying prefix-list +from deny to permit with any, and vice-versa. +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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 + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, 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_route_map_prefix_list(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + def _bgp_prefixes_sent(count): + output = json.loads(r2.vtysh_cmd("show bgp summary json")) + expected = { + "ipv4Unicast": { + "peers": {"192.168.1.1": {"pfxSnt": count, "state": "Established"}} + }, + "ipv6Unicast": { + "peers": {"2001:db8:1::1": {"pfxSnt": count, "state": "Established"}} + }, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_prefixes_sent, 4) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Can't converge initial topology" + + r2.vtysh_cmd( + """ + configure terminal + ip prefix-list r1-2 seq 5 deny any + ipv6 prefix-list r1-2 seq 5 deny any + """ + ) + + test_func = functools.partial(_bgp_prefixes_sent, 3) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "Only 3 prefixes MUST be advertised, seeing more" + + r2.vtysh_cmd( + """ + configure terminal + ip prefix-list r1-2 seq 5 permit 10.10.10.10/32 + ipv6 prefix-list r1-2 seq 5 permit 2001:db8:10::10/128 + """ + ) + + test_func = functools.partial(_bgp_prefixes_sent, 4) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "More or less prefixes advertised to r1, MUST be 4" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json b/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json new file mode 100644 index 000000000000..4a066ae535f9 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_topo1/prefix_modify.json @@ -0,0 +1,120 @@ +{ + "address_types": ["ipv4"], + "ipv4base":"192.120.1.0", + "ipv4mask":24, + "link_ip_start":{"ipv4":"192.120.1.0", "v4mask":30, "ipv6":"fd00::", "v6mask":64}, + "routers":{ + "r1":{ + "links":{ + "lo": {"ipv4": "auto", "type": "loopback", "add_static_route":"yes"}, + "r2":{"ipv4":"auto"}, + "r3":{"ipv4":"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", "type": "loopback", "add_static_route":"yes"}, + "r1":{"ipv4":"auto"}, + "r2":{"ipv4":"auto"}, + "r4":{"ipv4":"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", "type": "loopback", "add_static_route":"yes"}, + "r3":{"ipv4":"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_modify.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py new file mode 100644 index 000000000000..541b9de342d5 --- /dev/null +++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_modify.py @@ -0,0 +1,404 @@ +#!/usr/bin/python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +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 modify prefix-list action +""" + +import sys +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 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, + step, + create_route_maps, + check_router_status, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp + +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd] + + +# Global variables +bgp_convergence = False + +IPV4_PF3 = "192.168.0.0/18" +IPV4_PF4 = "192.150.10.0/24" +IPV4_PF5 = "192.168.10.1/32" +IPV4_PF6 = "192.168.10.10/32" +IPV4_PF7 = "192.168.10.0/24" + + +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... + json_file = "{}/prefix_lists.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons 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_bug_prefix_lists_deny_to_permit_p1(request): + """ + Verify modification of prefix-list action + """ + + 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 + if tgen.routers_have_failure(): + check_router_status(tgen) + reset_config_on_routers(tgen) + + # base config + step("Configure IPV4 and IPv6 IBGP and EBGP session as mentioned in setup") + step("Configure static routes on R2 with Null 0 nexthop") + input_dict_1 = { + "r2": { + "static_routes": [ + {"network": IPV4_PF7, "no_of_ip": 1, "next_hop": "Null0"}, + {"network": IPV4_PF6, "no_of_ip": 1, "next_hop": "Null0"}, + ] + } + } + result = create_static_routes(tgen, input_dict_1) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Advertise static route in BGP using redistribute static command") + input_dict_4 = { + "r2": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"}, + ] + } + } + } + } + } + } + result = create_router_bgp(tgen, topo, input_dict_4) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "All the static route advertised in R4 as BGP " + "routes verify using 'show ip bgp'and 'show bgp'" + ) + dut = "r4" + protocol = "bgp" + + input_dict_route = { + "r4": {"static_routes": [{"network": IPV4_PF7}, {"network": IPV4_PF6}]} + } + + result = verify_rib(tgen, "ipv4", dut, input_dict_route) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Configure IPv4 and IPv6 prefix-list") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1_ipv4": [ + {"seqid": "5", "network": IPV4_PF7, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "10", "network": IPV4_PF7, "action": "permit"} + ] + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "configure route-map seq to permit IPV4 prefix list and seq" + "2 to permit IPV6 prefix list and apply it to out direction on R3" + ) + + input_dict_3 = { + "r3": { + "route_maps": { + "rmap_match_pf_1": [ + { + "action": "permit", + "match": {"ipv4": {"prefix_lists": "pf_list_1"}}, + } + ] + } + } + } + result = create_route_maps(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + input_dict_7 = { + "r3": { + "bgp": { + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r3": { + "route_maps": [ + { + "name": "rmap_match_pf_1", + "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 on R4 should not have any IPv4 and IPv6 BGP routes using " + "show ip bgp show bgp" + ) + + dut = "r4" + protocol = "bgp" + + result = verify_rib( + tgen, "ipv4", dut, input_dict_route, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n" "Error : Routes are still present \n {}".format( + tc_name, result + ) + + step("Modify IPv4/IPv6 prefix-list sequence 5 to another value on R3") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "5", "network": IPV4_PF4, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify /24 and /120 routes present on" + "R4 BGP table using show ip bgp show bgp" + ) + input_dict = {"r4": {"static_routes": [{"network": IPV4_PF7}]}} + + 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) + + step("Change prefix-list to same as original on R3") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "5", "network": IPV4_PF7, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step( + "Verify /24 and /120 routes removed on" + "R4 BGP table using show ip bgp show bgp" + ) + + result = verify_rib( + tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n" "Error : Routes are still present \n {}".format( + tc_name, result + ) + + step("Modify IPv4/IPv6 prefix-list sequence 5 to another value") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "5", "network": IPV4_PF4, "action": "deny"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("Clear BGP on R3 and verify the routes") + clear_bgp(tgen, "ipv4", "r3") + + result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("On R3 add prefix-list permit any for IPv4 and IPv6 seq 15") + input_dict_3 = { + "r3": { + "prefix_lists": { + "ipv4": { + "pf_list_1": [ + {"seqid": "15", "network": "any", "action": "permit"} + ], + } + } + } + } + result = create_prefix_lists(tgen, input_dict_3) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + step("verify /24 and /32 /120 and /128 routes are present on R4") + result = verify_rib(tgen, "ipv4", dut, input_dict_route) + 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_remove_private_as/test_bgp_remove_private_as.py b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py index 113423744711..e48f81c53dd8 100644 --- a/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py +++ b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py @@ -409,8 +409,6 @@ def _validate_paths(remove_type): # the old flag after each iteration so we only test the flags we expect. _change_remove_type(rmv_type, "del") - return True - if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_route_map_delay_timer/__init__.py b/tests/topotests/bgp_route_map_delay_timer/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf new file mode 100644 index 000000000000..e5325c91bcaf --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf @@ -0,0 +1,24 @@ +! +!debug bgp updates +!debug bgp neighbor +! +bgp route-map delay-timer 5 +! +router bgp 65001 + no bgp ebgp-requires-policy + no bgp network import-check + neighbor 192.168.1.2 remote-as external + address-family ipv4 unicast + network 10.10.10.1/32 + network 10.10.10.2/32 + network 10.10.10.3/32 + aggregate-address 10.10.10.0/24 summary-only + neighbor 192.168.1.2 unsuppress-map r2 + exit-address-family +! +ip prefix-list r1 seq 5 permit 10.10.10.1/32 +ip prefix-list r1 seq 10 permit 10.10.10.2/32 +! +route-map r2 permit 10 + match ip address prefix-list r1 +exit diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf new file mode 100644 index 000000000000..b29940f46a31 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf @@ -0,0 +1,4 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf new file mode 100644 index 000000000000..36653e6b1c44 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf @@ -0,0 +1,4 @@ +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external +! diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf new file mode 100644 index 000000000000..cffe8273636d --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf @@ -0,0 +1,4 @@ +! +int r2-eth0 + ip address 192.168.1.2/24 +! diff --git a/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py new file mode 100644 index 000000000000..15a077da2e50 --- /dev/null +++ b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" + +""" + +import os +import sys +import json +import pytest +import functools + +pytestmark = pytest.mark.bgpd + +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 + +pytestmark = [pytest.mark.bgpd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2")} + tgen = Topogen(topodef, 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_route_map_delay_timer(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _bgp_converge_1(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.0/24": {}, + "10.10.10.1/32": {}, + "10.10.10.2/32": {}, + "10.10.10.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge_1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, "10.10.10.3/32 should not be advertised to r2" + + # Set route-map delay-timer to max value and remove 10.10.10.2/32. + # After this, r1 MUST do not announce updates immediately, and wait + # 600 seconds before withdrawing 10.10.10.2/32. + r2.vtysh_cmd( + """ + configure terminal + bgp route-map delay-timer 600 + no ip prefix-list r1 seq 10 permit 10.10.10.2/32 + """ + ) + + def _bgp_converge_2(): + output = json.loads( + r1.vtysh_cmd( + "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.0/24": {}, + "10.10.10.1/32": {}, + "10.10.10.2/32": None, + "10.10.10.3/32": None, + } + } + return topotest.json_cmp(output, expected) + + # We are checking `not None` here to wait count*wait time and if we have different + # results than expected, it means good - 10.10.10.2/32 wasn't withdrawn immediately. + test_func = functools.partial(_bgp_converge_2) + _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5) + assert ( + result is not None + ), "10.10.10.2/32 advertised, but should not be advertised to r2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_match_source_protocol/__init__.py b/tests/topotests/bgp_route_map_match_source_protocol/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf new file mode 100644 index 000000000000..7c3efeea4b9f --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r1/frr.conf @@ -0,0 +1,32 @@ +! +interface lo + ip address 172.16.255.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +interface r1-eth1 + ip address 192.168.2.1/24 +! +ip route 10.10.10.10/32 192.168.2.2 +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 1 3 + neighbor 192.168.1.2 timers connect 1 + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 1 3 + neighbor 192.168.2.2 timers connect 1 + address-family ipv4 + redistribute connected + redistribute static + neighbor 192.168.1.2 route-map r2 out + neighbor 192.168.2.2 route-map r3 out + exit-address-family +! +route-map r2 permit 10 + match source-protocol static +route-map r3 permit 10 + match source-protocol connected +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf new file mode 100644 index 000000000000..7213975cc03a --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r2/frr.conf @@ -0,0 +1,10 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 1 3 + neighbor 192.168.1.1 timers connect 1 +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf new file mode 100644 index 000000000000..4a1d830b0aa6 --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/r3/frr.conf @@ -0,0 +1,10 @@ +! +interface r3-eth0 + ip address 192.168.2.2/24 +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 1 3 + neighbor 192.168.2.1 timers connect 1 +! diff --git a/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py new file mode 100644 index 000000000000..2828796405cd --- /dev/null +++ b/tests/topotests/bgp_route_map_match_source_protocol/test_bgp_route_map_match_source_protocol.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if r1 can announce only static routes to r2, and only connected +routes to r3 using `match source-protocol` with route-maps. +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + 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["r1"]) + switch.add_link(tgen.gears["r3"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for i, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_map_match_source_protocol(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_check_advertised_routes_r2(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.1.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "10.10.10.10/32": { + "valid": True, + } + }, + "totalPrefixCounter": 1, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to filter routes by source-protocol for r2" + + def _bgp_check_advertised_routes_r3(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd( + "show bgp ipv4 unicast neighbors 192.168.2.2 advertised-routes json" + ) + ) + expected = { + "advertisedRoutes": { + "192.168.1.0/24": { + "valid": True, + }, + "192.168.2.0/24": { + "valid": True, + }, + "172.16.255.1/32": { + "valid": True, + }, + }, + "totalPrefixCounter": 3, + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_check_advertised_routes_r3) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Failed to filter routes by source-protocol for r3" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf index c9ad0b1a5bd1..4aa11ec9d07f 100644 --- a/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf +++ b/tests/topotests/bgp_route_map_vpn_import/r1/bgpd.conf @@ -1,9 +1,9 @@ ! -debug bgp updates -debug bgp vpn leak-from-vrf -debug bgp vpn leak-to-vrf -debug bgp nht -debug route-map +!debug bgp updates +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp nht +!debug route-map ! router bgp 65001 bgp router-id 10.10.10.10 diff --git a/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf new file mode 100644 index 000000000000..1929dfa69ba1 --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf @@ -0,0 +1,2 @@ +router bgp 65001 + neighbor 192.168.2.1 remote-as external diff --git a/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py new file mode 100644 index 000000000000..673efc2c7332 --- /dev/null +++ b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 03 2023, Trey Aspelund <taspelund@nvidia.com> +# +# Copyright (C) 2023 NVIDIA Corporation +# +# Test if the CLI parser for RT/SoO ecoms correctly +# constrain user input to valid 4-byte ASN values. +# + +import os +import sys +import json +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.common_config import step + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + tgen.add_router("pe1") + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + pe1 = tgen.gears["pe1"] + pe1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "pe1/bgpd.conf")) + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_route_origin_parser(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + pe1 = tgen.gears["pe1"] + + def _invalid_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo" in run_cfg + + def _max_soo_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + neighbor 192.168.2.1 soo 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "soo 4294967295:65" in run_cfg + + def _invalid_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967296:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn" in run_cfg + + def _max_rt_accepted(): + pe1.vtysh_cmd( + """ + configure terminal + router bgp 65001 + address-family ipv4 unicast + rt vpn both 4294967295:65 + """ + ) + run_cfg = pe1.vtysh_cmd("show run") + return "rt vpn both 4294967295:65" in run_cfg + + step( + "Configure invalid 4-byte value SoO (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_soo_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of SoO accepted" + + step("Configure max 4-byte value SoO (4294967295:65), this should be accepted") + test_func = functools.partial(_max_soo_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of SoO not accepted" + + step( + "Configure invalid 4-byte value RT (4294967296:65), this should not be accepted" + ) + test_func = functools.partial(_invalid_rt_accepted) + _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5) + assert result is False, "invalid 4-byte value of RT accepted" + + step("Configure max 4-byte value RT (4294967295:65), this should be accepted") + test_func = functools.partial(_max_rt_accepted) + _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5) + assert result is True, "max 4-byte value of RT not accepted" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_aspath_exclude/__init__.py b/tests/topotests/bgp_set_aspath_exclude/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf new file mode 100644 index 000000000000..9bef24f93154 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r1/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 65001 + no bgp ebgp-requires-policy + neighbor 192.168.1.2 remote-as external + neighbor 192.168.1.2 timers 3 10 + address-family ipv4 unicast + neighbor 192.168.1.2 route-map r2 in + exit-address-family +! +ip prefix-list p1 seq 5 permit 172.16.255.31/32 +! +route-map r2 permit 10 + match ip address prefix-list p1 + set as-path exclude 65003 +route-map r2 permit 20 + set as-path exclude all +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf new file mode 100644 index 000000000000..acf120b20000 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 + ip address 192.168.1.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf new file mode 100644 index 000000000000..23367f94ff4f --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r2/bgpd.conf @@ -0,0 +1,8 @@ +! +router bgp 65002 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 timers 3 10 + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 timers 3 10 +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf new file mode 100644 index 000000000000..f229954341df --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r2/zebra.conf @@ -0,0 +1,9 @@ +! +interface r2-eth0 + ip address 192.168.1.2/24 +! +interface r2-eth1 + ip address 192.168.2.2/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf new file mode 100644 index 000000000000..b7a7ceda1347 --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r3/bgpd.conf @@ -0,0 +1,9 @@ +! +router bgp 65003 + no bgp ebgp-requires-policy + neighbor 192.168.2.2 remote-as external + neighbor 192.168.2.2 timers 3 10 + address-family ipv4 unicast + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf new file mode 100644 index 000000000000..3fa6c644844c --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/r3/zebra.conf @@ -0,0 +1,10 @@ +! +int lo + ip address 172.16.255.31/32 + ip address 172.16.255.32/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py new file mode 100644 index 000000000000..8af7e7d60d6f --- /dev/null +++ b/tests/topotests/bgp_set_aspath_exclude/test_bgp_set_aspath_exclude.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_set_aspath_exclude.py +# +# Copyright 2023 by 6WIND S.A. +# + +""" +Test if `set as-path exclude` is working correctly for route-maps. +""" + +import os +import sys +import json +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 + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + 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"]) + + +def setup_module(mod): + tgen = Topogen(build_topo, 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_set_aspath_exclude(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002"}], + "172.16.255.32/32": [{"path": ""}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, tgen.gears["r1"]) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert result is None, "Failed overriding incoming AS-PATH with route-map" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf index 1e98f4e491df..f586c1f99c0e 100644 --- a/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf +++ b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf @@ -9,6 +9,7 @@ router bgp 65001 ! ip prefix-list p1 seq 5 permit 172.16.255.31/32 ! +bgp route-map delay-timer 1 route-map r2 permit 10 match ip address prefix-list p1 set as-path replace 65003 diff --git a/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py index 463df2f2a688..0433c15e0a1e 100644 --- a/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py +++ b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py @@ -24,6 +24,7 @@ # pylint: disable=C0413 from lib import topotest from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger pytestmark = [pytest.mark.bgpd] @@ -63,7 +64,7 @@ def teardown_module(mod): tgen.stop_topology() -def test_bgp_maximum_prefix_out(): +def test_bgp_set_aspath_replace_test1(): tgen = get_topogen() if tgen.routers_have_failure(): @@ -85,6 +86,40 @@ def _bgp_converge(router): assert result is None, "Failed overriding incoming AS-PATH with route-map" +def test_bgp_set_aspath_replace_test2(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Configuring r1 to replace the matching AS with a configured ASN") + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure terminal\nroute-map r2 permit 10\nset as-path replace 65003 65500\n", + isjson=False, + ) + router.vtysh_cmd( + "configure terminal\nroute-map r2 permit 20\nset as-path replace any 65501\n", + isjson=False, + ) + + def _bgp_converge(router): + output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json")) + expected = { + "routes": { + "172.16.255.31/32": [{"path": "65002 65500"}], + "172.16.255.32/32": [{"path": "65501 65501"}], + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge, router) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5) + + assert ( + result is None + ), "Failed overriding incoming AS-PATH with route-map replace with configured ASN" + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf index 3512e66cec60..cf0013e1b7a1 100644 --- a/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf +++ b/tests/topotests/bgp_snmp_bgp4v2mib/r2/bgpd.conf @@ -1,5 +1,5 @@ ! -debug bgp updates +!debug bgp updates ! router bgp 65002 no bgp ebgp-requires-policy diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf new file mode 100644 index 000000000000..345979662981 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce1 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json new file mode 100644 index 000000000000..d19e3157723d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:1::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:1::/64": [ + { + "prefix": "2001:1::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf new file mode 100644 index 000000000000..bb5f93fe52b2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce1/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce1 +! +interface eth0 + ipv6 address 2001:1::2/64 + ip address 192.168.1.2/24 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:1::1 +ip route 0.0.0.0/0 192.168.1.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf new file mode 100644 index 000000000000..8ed997874941 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce2 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json new file mode 100644 index 000000000000..35ff14efadc3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:2::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:2::/64": [ + { + "prefix": "2001:2::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf new file mode 100644 index 000000000000..a52b83f2dc42 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce2/zebra.conf @@ -0,0 +1,16 @@ +log file zebra.log +! +hostname ce2 +! +interface eth0 + ipv6 address 2001:2::2/64 + ip address 192.168.2.2/24 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:2::1 +ip route 0.0.0.0/0 192.168.2.1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf new file mode 100644 index 000000000000..a85d9701c7b4 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce3 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json new file mode 100644 index 000000000000..2f2931f80f4d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:3::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:3::/64": [ + { + "prefix": "2001:3::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf new file mode 100644 index 000000000000..beca0b12116d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce3/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce3 +! +interface eth0 + ipv6 address 2001:3::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:3::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf new file mode 100644 index 000000000000..93fb32fd1b0a --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce4 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json new file mode 100644 index 000000000000..8a98768e0d0d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:4::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:4::/64": [ + { + "prefix": "2001:4::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf new file mode 100644 index 000000000000..7b21074df0ba --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce4/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce4 +! +interface eth0 + ipv6 address 2001:4::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:4::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf new file mode 100644 index 000000000000..2ab6f2d2a799 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce5 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json new file mode 100644 index 000000000000..80ff52ad6eb2 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:5::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:5::/64": [ + { + "prefix": "2001:5::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf new file mode 100644 index 000000000000..b5ad48e70956 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce5/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce5 +! +interface eth0 + ipv6 address 2001:5::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:5::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf new file mode 100644 index 000000000000..e0b654051425 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/bgpd.conf @@ -0,0 +1,8 @@ +frr defaults traditional +! +hostname ce6 +password zebra +! +log stdout notifications +log commands +log file bgpd.log diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json new file mode 100644 index 000000000000..ace6136f068b --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/ipv6_rib.json @@ -0,0 +1,58 @@ +{ + "::/0": [ + { + "prefix": "::/0", + "protocol": "static", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 1, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 73, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "2001:6::1", + "afi": "ipv6", + "interfaceName": "eth0", + "active": true, + "weight": 1 + } + ] + } + ], + "2001:6::/64": [ + { + "prefix": "2001:6::/64", + "protocol": "connected", + "vrfId": 0, + "vrfName": "default", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "table": 254, + "internalStatus": 16, + "internalFlags": 8, + "internalNextHopNum": 1, + "internalNextHopActiveNum": 1, + "nexthops": [ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf new file mode 100644 index 000000000000..7d19d9880bd3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/ce6/zebra.conf @@ -0,0 +1,14 @@ +log file zebra.log +! +hostname ce6 +! +interface eth0 + ipv6 address 2001:6::2/64 +! +ip forwarding +ipv6 forwarding +! +ipv6 route ::/0 2001:6::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf new file mode 100644 index 000000000000..bfc9db960aa7 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/bgpd.conf @@ -0,0 +1,79 @@ +frr defaults traditional +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 1 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::2 remote-as 2 + neighbor 2001::2 timers 3 10 + neighbor 2001::2 timers connect 1 + neighbor 2001::2 update-source 2001::1 + neighbor 2001::2 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::2 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::2 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 1 vrf vrf10 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! + address-family ipv4 unicast + network 192.168.1.0/24 + sid vpn export auto + rd vpn export 11:10 + rt vpn both 77:77 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 1 vrf vrf20 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 1:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json new file mode 100644 index 000000000000..6fc43e194dfd --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vpnv6_rib.json @@ -0,0 +1,169 @@ +{ + "vrfName": "default", + "tableVersion": 2, + "routerId": "192.0.2.1", + "defaultLocPrf": 100, + "localAS": 1, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::2", + "path": "2", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::2", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json new file mode 100644 index 000000000000..9783c7e0e65e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_no_sid_rib.json @@ -0,0 +1,23 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json new file mode 100644 index 000000000000..80c1acff8bb9 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_auto_sid_rib.json @@ -0,0 +1,53 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json new file mode 100644 index 000000000000..9783c7e0e65e --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_no_sid_rib.json @@ -0,0 +1,23 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json new file mode 100644 index 000000000000..07ca64b45bf5 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv4_manual_sid_rib.json @@ -0,0 +1,54 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json new file mode 100644 index 000000000000..6ac8dac25fde --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json new file mode 100644 index 000000000000..fac3d1d5f3ad --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_auto_sid_rib.json @@ -0,0 +1,107 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:2::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json new file mode 100644 index 000000000000..69ce312c4dd4 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json new file mode 100644 index 000000000000..04e230535ec0 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_afv6_manual_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json new file mode 100644 index 000000000000..3cac156bb287 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_auto_sid_rib.json @@ -0,0 +1,54 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json new file mode 100644 index 000000000000..163e9d626ac6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf4_manual_sid_rib.json @@ -0,0 +1,53 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "192.168.2.0\/24":[ + { + "prefix":"192.168.2.0\/24", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"2001::2", + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json new file mode 100644 index 000000000000..1313f20c6cfe --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_auto_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 16 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:1::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json new file mode 100644 index 000000000000..51f249b184f3 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf6_manual_sid_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "labels":[ + 128 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:8::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json new file mode 100644 index 000000000000..6ac8dac25fde --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_auto_no_sid_rib.json @@ -0,0 +1,77 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} + diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json new file mode 100644 index 000000000000..1c3dad089df6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_pervrf_manual_no_sid_rib.json @@ -0,0 +1,22 @@ +{ + "192.168.1.0\/24":[ + { + "prefix":"192.168.1.0\/24", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json new file mode 100644 index 000000000000..9579bb15de7c --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf10_rib.json @@ -0,0 +1,112 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:2::" + } + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json new file mode 100644 index 000000000000..25f146f4b730 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/vrf20_rib.json @@ -0,0 +1,106 @@ +{ + "2001:4::\/64":[ + { + "prefix":"2001:4::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:3::" + } + } + ] + } + ], + "2001:5::\/64":[ + { + "prefix":"2001:5::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:6::\/64":[ + { + "prefix":"2001:6::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:2:2:3::" + } + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf new file mode 100644 index 000000000000..cf31a5c11b59 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r1/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r1 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::1/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:1::1/64 + ip address 192.168.1.1/24 +! +interface eth2 vrf vrf10 + ipv6 address 2001:3::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:5::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:1:1::/64 block-len 40 node-len 24 func-bits 16 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:2:1::/64 2001::2 +ipv6 route 2001:db8:2:2::/64 2001::2 +ipv6 route 2001:db8:2:3::/64 2001::2 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf new file mode 100644 index 000000000000..892a9f73e513 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/bgpd.conf @@ -0,0 +1,80 @@ +frr defaults traditional +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug bgp neighbor-events +!debug bgp zebra +!debug bgp vnc verbose +!debug bgp update-groups +!debug bgp updates in +!debug bgp updates out +!debug bgp updates +!debug bgp vpn label +!debug bgp vpn leak-from-vrf +!debug bgp vpn leak-to-vrf +!debug bgp vpn rmap-event +! +router bgp 2 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + neighbor 2001::1 remote-as 1 + neighbor 2001::1 update-source 2001::2 + neighbor 2001::1 timers 3 10 + neighbor 2001::1 timers connect 1 + neighbor 2001::1 capability extended-nexthop + ! + address-family ipv4 vpn + neighbor 2001::1 activate + exit-address-family + ! + address-family ipv6 vpn + neighbor 2001::1 activate + exit-address-family + ! + segment-routing srv6 + locator loc1 + ! +! +router bgp 2 vrf vrf10 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:10 + rt vpn both 99:99 + import vpn + export vpn + redistribute connected + exit-address-family +! + address-family ipv4 unicast + network 192.168.2.0/24 + sid vpn export auto + rd vpn export 22:10 + rt vpn both 77:77 + import vpn + export vpn + redistribute connected + exit-address-family +! +router bgp 2 vrf vrf20 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + no bgp default ipv4-unicast + ! + address-family ipv6 unicast + sid vpn export auto + rd vpn export 2:20 + rt vpn both 88:88 + import vpn + export vpn + redistribute connected + exit-address-family +!! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json new file mode 100644 index 000000000000..538e8955efe4 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vpnv6_rib.json @@ -0,0 +1,169 @@ +{ + "vrfName": "default", + "tableVersion": 2, + "routerId": "192.0.2.2", + "defaultLocPrf": 100, + "localAS": 2, + "routes": { + "routeDistinguishers": { + "1:10": { + "2001:1::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:1::", + "prefixLen": 64, + "network": "2001:1::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:3::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:3::", + "prefixLen": 64, + "network": "2001:3::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "1:20": { + "2001:5::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:5::", + "prefixLen": 64, + "network": "2001:5::/64", + "metric": 0, + "weight": 0, + "peerId": "2001::1", + "path": "1", + "origin": "incomplete", + "nexthops": [ + { + "ip": "2001::1", + "hostname": "r1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:10": { + "2001:2::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:2::", + "prefixLen": 64, + "network": "2001:2::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf10", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "2:20": { + "2001:4::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:4::", + "prefixLen": 64, + "network": "2001:4::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "2001:6::/64": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "2001:6::", + "prefixLen": 64, + "network": "2001:6::/64", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf20", + "nexthops": [ + { + "ip": "::", + "hostname": "r2", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json new file mode 100644 index 000000000000..446bb8eb3c64 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf10_rib.json @@ -0,0 +1,106 @@ +{ + "2001:1::\/64":[ + { + "prefix":"2001:1::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ], + "2001:2::\/64":[ + { + "prefix":"2001:2::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:3::\/64":[ + { + "prefix":"2001:3::\/64", + "protocol":"bgp", + "vrfName":"vrf10", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 32 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:2::" + } + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf10", + "distance":0, + "metric":0, + "installed":true, + "table":10, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json new file mode 100644 index 000000000000..8bc2fc23f147 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/vrf20_rib.json @@ -0,0 +1,112 @@ +{ + "2001:4::\/64":[ + { + "prefix":"2001:4::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "2001:5::\/64":[ + { + "prefix":"2001:5::\/64", + "protocol":"bgp", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":20, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "afi":"ipv6", + "vrf":"default", + "active":true, + "labels":[ + 48 + ], + "weight":1, + "seg6local":{ + "action":"unspec" + }, + "seg6":{ + "segs":"2001:db8:1:1:3::" + } + } + ] + } + ], + "2001:6::\/64":[ + { + "prefix":"2001:6::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ], + "fe80::\/64":[ + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + }, + { + "prefix":"fe80::\/64", + "protocol":"connected", + "vrfName":"vrf20", + "distance":0, + "metric":0, + "installed":true, + "table":20, + "nexthops":[ + { + "flags":3, + "fib":true, + "directlyConnected":true, + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf new file mode 100644 index 000000000000..9771ee1cd76d --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/r2/zebra.conf @@ -0,0 +1,43 @@ +log file zebra.log +! +hostname r2 +password zebra +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface eth0 + ipv6 address 2001::2/64 +! +interface eth1 vrf vrf10 + ipv6 address 2001:2::1/64 + ip address 192.168.2.1/24 +! +interface eth2 vrf vrf20 + ipv6 address 2001:4::1/64 +! +interface eth3 vrf vrf20 + ipv6 address 2001:6::1/64 +! +segment-routing + srv6 + locators + locator loc1 + prefix 2001:db8:2:2::/64 block-len 40 node-len 24 func-bits 16 + ! + ! +! +ip forwarding +ipv6 forwarding +! +ipv6 route 2001:db8:1:1::/64 2001::1 +ipv6 route 2001:db8:1:2::/64 2001::1 +ipv6 route 2001:db8:1:3::/64 2001::1 +! +line vty +! diff --git a/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py new file mode 100755 index 000000000000..cddcf6a9a1f6 --- /dev/null +++ b/tests/topotests/bgp_srv6l3vpn_sid/test_bgp_srv6l3vpn_sid.py @@ -0,0 +1,453 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright 2023 6WIND S.A. +# Authored by Dmytro Shytyi <dmytro.shytyi@6wind.com> +# +# 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 re +import sys +import json +import functools +import pytest + +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 required_linux_kernel_version + + +def build_topo(tgen): + """ + CE1 CE3 CE5 + (eth0) (eth0) (eth0) + :2 :2 :2 + | | | + 192.168.1.0 | | + /24 | | + 2001: 2001: 2001: + 1::/64 3::/64 5::/64 + | | | + :1 :1 :1 + +-(eth1)--(eth2)---(eth3)-+ + | \ / | | + | (vrf10) (vrf20) | + | R1 | + +----------(eth0)---------+ + :1 + | + 2001::/64 + | + :2 + (eth0) + +----------(eth0)--------------+ + | R2 | + | (vrf10) (vrf20) | + | / / \ | + +-(eth1)-----(eth2)-----(eth3)-+ + :1 :1 :1 + | | | + +------+ +------+ +------+ + / 2001: \ / 2001: \ / 2001: \ + / 2::/64 \ 4::/64 / \ 6::/64 / + /192.168.2.0| / \ / + \ /24 / \ | | | + +------+ +------+ +------+ + | | | + :2 :2 :2 + (eth0) (eth0) (eth0) + CE2 CE4 CE6 + """ + + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("ce1") + tgen.add_router("ce2") + tgen.add_router("ce3") + tgen.add_router("ce4") + tgen.add_router("ce5") + tgen.add_router("ce6") + + tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0") + tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1") + tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2") + tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3") + tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3") + + +def setup_module(mod): + result = required_linux_kernel_version("5.11") + if result is not True: + pytest.skip("Kernel requirements are not met") + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + router_list = tgen.routers() + for i, (rname, router) in enumerate(tgen.routers().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.gears["r1"].run("modprobe vrf") + tgen.gears["r1"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r1"].run("ip link set vrf10 up") + tgen.gears["r1"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r1"].run("ip link set vrf20 up") + tgen.gears["r1"].run("ip link set eth1 master vrf10") + tgen.gears["r1"].run("ip link set eth2 master vrf10") + tgen.gears["r1"].run("ip link set eth3 master vrf20") + tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r1"].run("sysctl net.ipv4.conf.default.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.all.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.lo.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.eth0.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.eth1.rp_filter=0") + tgen.gears["r1"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0") + + tgen.gears["r2"].run("modprobe vrf") + tgen.gears["r2"].run("ip link add vrf10 type vrf table 10") + tgen.gears["r2"].run("ip link set vrf10 up") + tgen.gears["r2"].run("ip link add vrf20 type vrf table 20") + tgen.gears["r2"].run("ip link set vrf20 up") + tgen.gears["r2"].run("ip link set eth1 master vrf10") + tgen.gears["r2"].run("ip link set eth2 master vrf20") + tgen.gears["r2"].run("ip link set eth3 master vrf20") + tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1") + tgen.gears["r2"].run("sysctl net.ipv4.conf.default.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.all.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.lo.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.eth0.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.eth1.rp_filter=0") + tgen.gears["r2"].run("sysctl net.ipv4.conf.vrf10.rp_filter=0") + tgen.start_router() + + # FOR DEVELOPER: + # If you want to stop some specific line and start interactive shell, + # please use tgen.mininet_cli() to start it. + # Example: + # tgen=get_topogen() + # tgen.mininet_cli() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_ping(name, dest_addr, expect_connected): + def _check(name, dest_addr, match): + tgen = get_topogen() + output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr)) + logger.info(output) + if match not in output: + return True + + match = ", {} packet loss".format("0%" if expect_connected else "100%") + logger.info("[+] check {} {} {}".format(name, dest_addr, match)) + tgen = get_topogen() + func = functools.partial(_check, name, dest_addr, match) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file)) + tgen = get_topogen() + func = functools.partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=10, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json") + check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json") + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json") + check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json") + check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json") + check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json") + check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json") + check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json") + check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json") + check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json") + check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json") + check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json") + + +def test_ping(): + check_ping("ce1", "2001:2::2", True) + check_ping("ce1", "2001:3::2", True) + check_ping("ce1", "2001:4::2", False) + check_ping("ce1", "2001:5::2", False) + check_ping("ce1", "2001:6::2", False) + check_ping("ce4", "2001:1::2", False) + check_ping("ce4", "2001:2::2", False) + check_ping("ce4", "2001:3::2", False) + check_ping("ce4", "2001:5::2", True) + check_ping("ce4", "2001:6::2", True) + + +def test_sid_per_afv6_auto(): + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json") + check_ping("ce1", "2001:2::2", True) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + sid vpn export auto + """ + ) + check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_sid_rib.json") + check_ping("ce1", "2001:2::2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + +def test_sid_per_afv6_manual(): + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + sid vpn export 8 + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv6 unicast + no sid vpn export 8 + """ + ) + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_afv6_manual_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + +def test_sid_per_afv4_auto(): + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json") + check_ping("ce1", "192.168.2.2", True) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export auto + """ + ) + + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + sid vpn export auto + """ + ) + + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_sid_rib.json") + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export auto + """ + ) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_auto_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + + +def test_sid_per_afv4_manual(): + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + sid vpn export 8 + """ + ) + + check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_sid_rib.json") + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + address-family ipv4 unicast + no sid vpn export 8 + """ + ) + check_ping("ce1", "192.168.2.2", False) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_afv4_manual_no_sid_rib.json" + ) + + +def test_sid_per_vrf_auto(): + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + sid vpn per-vrf export auto + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_auto_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_auto_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + no sid vpn per-vrf export auto + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf_auto_no_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", False) + + +def test_sid_per_vrf_manual(): + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + sid vpn per-vrf export 8 + """ + ) + + check_rib( + "r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_pervrf6_manual_sid_rib.json" + ) + check_ping("ce1", "2001:2::2", True) + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf4_manual_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", True) + + get_topogen().gears["r2"].vtysh_cmd( + """ + configure terminal + router bgp 2 vrf vrf10 + no sid vpn per-vrf export 8 + """ + ) + + check_rib( + "r1", "show ip route vrf vrf10 json", "r1/vrf10_pervrf_manual_no_sid_rib.json" + ) + check_ping("ce1", "192.168.2.2", False) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf index a9319a6aeddb..cbc5ce1f099d 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::1/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf index 9e5fa0ac073f..449ca74d5e9b 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::2/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf index 2c560dfc066b..f913b9f00292 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r1/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::1/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf index b9277a9a8cdf..201d0cce23f3 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/r2/zebra.conf @@ -7,9 +7,9 @@ log stdout notifications log monitor notifications log commands ! -debug zebra packet -debug zebra dplane -debug zebra kernel +!debug zebra packet +!debug zebra dplane +!debug zebra kernel ! interface eth0 ipv6 address 2001::2/64 diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py index eb0f30f84a58..7c2c7cfdaa43 100644 --- a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py +++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf3/test_bgp_srv6l3vpn_to_bgp_vrf3.py @@ -98,7 +98,8 @@ def _check(name, dest_addr, match): tgen = get_topogen() output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr)) logger.info(output) - assert match in output, "ping fail" + if match not in output: + return "ping fail" match = ", {} packet loss".format("0%" if expect_connected else "100%") logger.info("[+] check {} {} {}".format(name, dest_addr, match)) diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf index caebb0e9221d..fb6980a13988 100644 --- a/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf +++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf @@ -2,12 +2,12 @@ access-list access seq 10 permit 192.168.1.1/32 ! ip route 192.168.1.1/32 10.0.0.10 ! -debug bgp bestpath -debug bgp nht -debug bgp updates -debug bgp update-groups -debug bgp zebra -debug zebra rib detail +!debug bgp bestpath +!debug bgp nht +!debug bgp updates +!debug bgp update-groups +!debug bgp zebra +!debug zebra rib detail ! router bgp 2 address-family ipv4 uni diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf index 010e86aad704..129b812036f4 100644 --- a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf +++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf @@ -1,6 +1,6 @@ -debug bgp updates -debug bgp bestpath 40.0.0.0/8 -debug bgp zebra +!debug bgp updates +!debug bgp bestpath 40.0.0.0/8 +!debug bgp zebra ! router bgp 2 no bgp ebgp-requires-policy @@ -8,4 +8,4 @@ router bgp 2 neighbor 10.0.0.1 remote-as 1 neighbor 10.0.0.10 remote-as 3 address-family ipv4 uni - network 60.0.0.0/24 \ No newline at end of file + network 60.0.0.0/24 diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py index ed8e41903f17..fd8a78b48500 100644 --- a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py +++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py @@ -94,7 +94,6 @@ def test_bgp_route(): expected, ) _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) - assertmsg = '"r3" JSON output mismatches' assert result is None, assertmsg json_file = "{}/r3/v4_route3.json".format(CWD) @@ -103,10 +102,11 @@ def test_bgp_route(): test_func = partial( topotest.router_json_cmp, r3, - "show ip route 10.0.0.3 json", + "show ip route 60.0.0.0 json", expected, ) _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result is None, assertmsg def test_bgp_better_admin_won(): @@ -217,6 +217,20 @@ def test_bgp_allow_as_in(): assertmsg = '"r2" 192.168.1.1/32 route should be gone' assert result is None, assertmsg +def test_local_vs_non_local(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r2 = tgen.gears["r2"] + + output = json.loads(r2.vtysh_cmd("show bgp ipv4 uni 60.0.0.0/24 json")) + paths = output["paths"] + for i in range(len(paths)): + if "fibPending" in paths[i]: + assert(False), "Route 60.0.0.0/24 should not have fibPending" + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] diff --git a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py index 47b2452b81bd..f89f3378fbf7 100644 --- a/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py +++ b/tests/topotests/bgp_unique_rid/test_bgp_unique_rid.py @@ -847,8 +847,6 @@ def test_bgp_unique_rid_chaos4_p2(): for intf in topo["routers"][rtr]["links"].keys(): topo1["routers"][rtr]["links"][intf].pop("ipv4") topo1["routers"][rtr]["links"][intf].pop("ipv6") - if intf is "lo": - topo1["routers"][rtr]["links"][intf].pop("ipv4") build_config_from_json(tgen, topo1, save_bkup=False) diff --git a/tests/topotests/bgp_vpnv4_asbr/__init__.py b/tests/topotests/bgp_vpnv4_asbr/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf new file mode 100644 index 000000000000..22372242d39f --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/h1/zebra.conf @@ -0,0 +1,7 @@ +log stdout +ip route 172.31.1.0/24 172.31.0.1 +ip route 172.31.2.0/24 172.31.0.1 +interface h1-eth0 + ip address 172.31.0.10/24 +! + diff --git a/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf new file mode 100644 index 000000000000..d650bc831a50 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/h2/zebra.conf @@ -0,0 +1,6 @@ +log stdout +ip route 172.31.0.0/24 172.31.1.1 +interface h2-eth0 + ip address 172.31.1.10/24 +! + diff --git a/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf new file mode 100644 index 000000000000..5676485849ba --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/h3/zebra.conf @@ -0,0 +1,6 @@ +log stdout +ip route 172.31.0.0/24 172.31.2.1 +interface h3-eth0 + ip address 172.31.2.10/24 +! + diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json new file mode 100644 index 000000000000..184ab312b6a3 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgp_ipv4_routes.json @@ -0,0 +1,49 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "172.31.0.10/32": [ + { + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.3", + "afi": "ipv4", + "used": true + } + ] + }, + { + "prefix": "172.31.0.10", + "prefixLen": 32, + "network": "172.31.0.10\/32", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.2", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.1/32": [ + { + "prefix": "172.31.0.1", + "prefixLen": 32, + "network": "172.31.0.1\/32", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf new file mode 100644 index 000000000000..3bbcc20e9eb8 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r1/bgpd.conf @@ -0,0 +1,29 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + no bgp ebgp-requires-policy + neighbor 192.0.2.100 remote-as 65500 + neighbor 192.0.2.100 update-source lo + neighbor 192.168.0.100 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.100 activate + no neighbor 192.0.2.100 activate + network 192.0.2.1/32 + exit-address-family + address-family ipv4 labeled-unicast + neighbor 192.168.0.100 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.100 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf new file mode 100644 index 000000000000..2f12b722b88a --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r1/zebra.conf @@ -0,0 +1,10 @@ +log stdout +interface lo + ip address 192.0.2.1/32 +! +interface r1-eth1 vrf vrf1 + ip address 172.31.0.1/24 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf new file mode 100644 index 000000000000..4c84d52bd989 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r2/bgpd.conf @@ -0,0 +1,31 @@ +debug bgp nht +debug bgp zebra +debug bgp labelpool +router bgp 65500 + bgp router-id 192.0.2.2 + no bgp ebgp-requires-policy + neighbor 192.0.2.100 remote-as 65500 + neighbor 192.0.2.100 update-source lo + neighbor 192.168.0.100 remote-as 65500 + neighbor 192.168.1.200 remote-as 65502 + address-family ipv4 unicast + no neighbor 192.168.0.100 activate + no neighbor 192.168.1.200 activate + network 192.0.2.2/32 + exit-address-family + address-family ipv4 labeled-unicast + neighbor 192.168.0.100 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.100 activate + neighbor 192.0.2.100 next-hop-self + neighbor 192.168.1.200 activate + exit-address-family +! +interface r2-eth1 + mpls bgp forwarding + mpls bgp l3vpn-multi-domain-switching +! +interface r2-eth0 + mpls bgp l3vpn-multi-domain-switching +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json b/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json new file mode 100644 index 000000000000..d33c5f5691d0 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r2/ipv4_vpn_summary.json @@ -0,0 +1,24 @@ +{ + "routerId":"192.0.2.2", + "as":65500, + "vrfId":0, + "vrfName":"default", + "peerCount":2, + "peers":{ + "192.0.2.100":{ + "remoteAs":65500, + "localAs":65500, + "version":4, + "state":"Established", + "peerState":"OK" + }, + "192.168.1.200":{ + "remoteAs":65502, + "localAs":65500, + "version":4, + "state":"Established", + "peerState":"OK" + } + }, + "totalPeers":2 +} diff --git a/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf new file mode 100644 index 000000000000..43508a4c6a25 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r2/zebra.conf @@ -0,0 +1,13 @@ +log stdout +ip route 192.168.1.3/32 r2-eth1 +interface lo + ip address 192.0.2.2/32 +! +interface r2-eth0 + ip address 192.168.0.2/24 + mpls enable +! +interface r2-eth1 + ip address 192.168.1.2/24 + mpls enable +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf new file mode 100644 index 000000000000..c5d5727fbaed --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r3/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.0.2.3 + no bgp ebgp-requires-policy + neighbor 192.168.1.200 remote-as 65502 + address-family ipv4 unicast + no neighbor 192.168.1.200 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.1.200 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.0.2.3 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:3 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r3-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf new file mode 100644 index 000000000000..6376785f8096 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/r3/zebra.conf @@ -0,0 +1,14 @@ +log stdout +ip route 192.168.1.3/32 r3-eth0 +interface r3-eth1 vrf vrf1 + ip address 172.31.1.1/24 +! +interface r3-eth2 vrf vrf1 + ip address 172.31.2.1/24 +! +interface r3-eth3 vrf vrf1 + ip address 172.31.3.1/24 +! +interface r3-eth0 + ip address 192.168.1.3/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf new file mode 100644 index 000000000000..845d71bc7e4d --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rr100/bgpd.conf @@ -0,0 +1,29 @@ +router bgp 65500 + bgp router-id 192.0.2.100 + no bgp ebgp-requires-policy + neighbor 192.0.2.2 remote-as 65500 + neighbor 192.0.2.2 update-source lo + neighbor 192.168.0.2 remote-as 65500 + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.1 update-source lo + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + no neighbor 192.0.2.1 activate + no neighbor 192.168.0.2 activate + no neighbor 192.0.2.2 activate + network 192.0.2.100/32 + exit-address-family + address-family ipv4 labeled-unicast + neighbor 192.168.0.1 activate + neighbor 192.168.0.2 activate + neighbor 192.168.0.1 route-reflector-client + neighbor 192.168.0.2 route-reflector-client + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.1 activate + neighbor 192.0.2.2 activate + neighbor 192.0.2.1 route-reflector-client + neighbor 192.0.2.2 route-reflector-client + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf new file mode 100644 index 000000000000..2fa5285182f3 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rr100/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface lo + ip address 192.0.2.100/32 +! +interface rr100-eth0 + ip address 192.168.0.100/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf b/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf new file mode 100644 index 000000000000..fa3cb54228bc --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rs200/bgpd.conf @@ -0,0 +1,19 @@ +debug bgp nht +debug bgp zebra +debug bgp labelpool +router bgp 65502 + bgp router-id 192.0.2.200 + no bgp ebgp-requires-policy + neighbor 192.168.1.3 remote-as 65501 + neighbor 192.168.1.2 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.1.2 activate + no neighbor 192.168.1.3 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.1.3 activate + neighbor 192.168.1.2 activate + neighbor 192.168.1.3 route-server-client + neighbor 192.168.1.2 route-server-client + exit-address-family +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf b/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf new file mode 100644 index 000000000000..98793ca0036b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/rs200/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface rs200-eth0 + ip address 192.168.1.200/24 +! diff --git a/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py new file mode 100644 index 000000000000..c0e57930a310 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_asbr/test_bgp_vpnv4_asbr.py @@ -0,0 +1,938 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_bgp_vpnv4_asbr.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by 6WIND +# + +""" + test_bgp_vpnv4_asbr.py: Test the FRR BGP daemon with rfc4364 option 10b + r1, r2, and r100 are in an iBGP AS, while r2, r3 do an eBGP peering + h1 is a host behind r1 VRF1, and {h2,h3} are hosts behind r3 VRF1 + The test demonstrates the connectivity across the network between h1 and h3. + + + +----------+ +----+--------+ +--------+ +--------+-----+ + | |172.31.0.0|vrf | r1 |192.168.0.0/24| r2 |192.168.1.0/24|r3 | vrf | + | h1 +----------+ | 1+------+-------+ +------+-------+3 | +--- 172.31.3.0/24 + | 10 | |VRF1|AS65500 | | | AS65500| | |AS65501 |VRF1 | + +----------+ +-------------+ | +--------+ | +--------+--+-++ + 192.0.2.1 | 192.0.2.2 | 172| | + +----------+ +----+--------+ 31| | + |rr100 | |rs200/AS65502| 1| | + +----------+ +-------------+ 0| | + 192.0.2.100 +--------+ /24| | + | | +----------+----+ | + |h3 | | | | + |10 | | h2 | | + +---+----+ | 10 | | + | +----------+ | + |172.31.2.0/24 | + +--------------------------------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# 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. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Allocate 8 devices + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r3") + tgen.add_router("h1") + tgen.add_router("h2") + tgen.add_router("h3") + tgen.add_router("rr100") + tgen.add_router("rs200") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["rr100"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rs200"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h2"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["h3"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r3"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + + for rname in ("r1", "r3"): + for cmd in cmds_list: + input = cmd.format(rname) + logger.info("input: " + cmd) + output = tgen.net[rname].cmd(cmd.format(rname)) + logger.info("output: " + output) + + cmds_list = [ + "ip link set dev {0}-eth2 master vrf1", + "ip link set dev {0}-eth3 master vrf1", + ] + for cmd in cmds_list: + input = cmd.format("r3") + logger.info("input: " + input) + output = tgen.net["r3"].cmd(input) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if rname in ("r1", "r2", "r3", "rr100", "rs200"): + 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 bgp_vpnv4_prefix_check(router, rd, prefix, label, nexthop): + """ + Dump and check 'show bgp ipv4 vpn <prefix> json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'rd': The route distinguisher expected + * 'prefix': The prefix expected + * 'label': The label expected associated with the ('rd','prefix') tuple + * 'nexthop': The nexthop expected associated with the ('rd','prefix') tuple + """ + + def _check(router, prefix, rd, label, nexthop): + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{0}, {1}, route distinguisher {2} not present".format( + router.name, prefix, rd + ) + for dumped_rd, pathes in dump.items(): + if dumped_rd != rd: + continue + for path in pathes["paths"]: + if "remoteLabel" not in path.keys(): + return "{0}, {1}, rd {2}, remoteLabel not present".format( + router.name, prefix, rd + ) + if str(path["remoteLabel"]) != label: + continue + + if "nexthops" not in path.keys(): + return "{0}, {1}, rd {2}, no nexthops present".format( + router.name, prefix, rd + ) + + for nh in path["nexthops"]: + if "ip" not in nh.keys(): + return "{0}, {1}, rd {2}, no ipv4 nexthop available".format( + router.name, prefix, rd + ) + if nh["ip"] != nexthop: + continue + return None + return "{0}, {1}, rd {2}, remoteLabel {3}, nexthop {4} not found".format( + router.name, prefix, rd, label, nexthop + ) + + func = functools.partial(_check, router, prefix, rd, label, nexthop) + success, result = topotest.run_and_expect(func, None, count=20, wait=0.5) + assert_msg = "{}, show bgp ipv4 vpn {}, rd {}, label {} nexthop {}".format( + router.name, prefix, rd, label, nexthop + ) + assert result is None, assert_msg + " not found" + logger.info(assert_msg + " found") + + +def mpls_table_get_entry(router, out_label, out_nexthop): + """ + Get the in_label from tuple (out_label, out_nexthop) + * 'router': the router to check + * 'out_label': The outgoing label expected + * 'out_nexthop': The outgoing nexthop expected + """ + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + for nh in label_info["nexthops"]: + if nh["type"] != "BGP" or "installed" not in nh.keys(): + continue + if "nexthop" in nh.keys(): + if nh["nexthop"] != out_nexthop: + continue + if "outLabelStack" in nh.keys(): + if out_label not in nh["outLabelStack"]: + continue + return in_label + return None + + +def mpls_table_check_entry(router, out_label, out_nexthop): + """ + Dump and check 'show mpls table json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'out_label': The outgoing label expected + * 'out_nexthop': The outgoing nexthop expected + """ + logger.info("Checking MPLS labels on {}".format(router.name)) + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + for nh in label_info["nexthops"]: + if nh["type"] != "BGP" or "installed" not in nh.keys(): + continue + if "nexthop" in nh.keys(): + if nh["nexthop"] != out_nexthop: + continue + if "outLabelStack" in nh.keys(): + if out_label not in nh["outLabelStack"]: + continue + logger.info( + "{}, show mpls table, entry in_label {} out_label {} out_nexthop {} found".format( + router.name, in_label, nh["outLabelStack"], nh["nexthop"] + ) + ) + return None + return "{}, show mpls table, entry matching in_label {} out_label {} out_nexthop {} not found".format( + router.name, in_label, out_label, out_nexthop + ) + + +def check_ping(name, dest_addr, expect_connected): + """ + Assert that ping to dest_addr is expected + * 'name': the router to set the ping from + * 'dest_addr': The destination ip address to ping + * 'expect_connected': True if ping is expected to pass + """ + + def _check(name, dest_addr, match): + tgen = get_topogen() + output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr)) + logger.info(output) + if match not in output: + return "ping fail" + + match = ", {} packet loss".format("0%" if expect_connected else "100%") + logger.info("[+] check {} {} {}".format(name, dest_addr, match)) + tgen = get_topogen() + func = functools.partial(_check, name, dest_addr, match) + success, result = topotest.run_and_expect(func, None, count=20, wait=0.5) + assert result is None, "Failed" + + +def check_show_bgp_vpn_prefix_found( + router, ipversion, prefix, rd, label=None, nexthop=None +): + """ + Check if a given vpn prefix is present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + if nexthop: + expected = { + rd: { + "prefix": prefix, + "paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}], + } + } + else: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + if nexthop: + expected = { + rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]} + } + else: + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + """ + Check if a given vpn prefix is not present in the BGP RIB + * 'router': the router to check BGP VPN RIB + * 'ipversion': The ip version to check: ipv4 or ipv6 + * 'prefix': the IP prefix to check + * 'rd': the route distinguisher to check + * 'label: the label to check + """ + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_mpls_table_entry_label_not_found(router, inlabel): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = {"inLabel": inlabel, "installed": True} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_ok(router, vpnv4_entries): + """ + Check on router that BGP l3vpn entries are present + Check there is an MPLS entry bound to that BGP L3VPN entry + Extract the Label value and check on the distributed router the BGP L3VPN entry + If check fail, an assert is triggered. + * 'router': the router to check BGP VPN RIB + * 'vpnv4_entries': dictionary that contains the list of prefixes, and the distributed router to look after + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + vpnv4_nexthops = {"r1": "192.0.2.2", "r3": "192.168.1.2"} + vpnv4_nht = {"192.0.2.1": "192.168.0.1", "192.168.1.3": "192.168.1.3"} + label_ip_entries = {} + + def _return_remote_label_nh_rd(router, prefix): + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + assert_msg = ( + "{}, prefix {} not available or label not found", + router.name, + prefix, + ) + assert dump, assert_msg + for rd, pathes in dump.items(): + for path in pathes["paths"]: + if "remoteLabel" not in path.keys(): + assert 0, assert_msg + for nh in path["nexthops"]: + if "ip" in nh.keys(): + return path["remoteLabel"], nh["ip"], rd + assert 0, assert_msg + + def _check_nexthop_available(router, prefix): + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + if not dump: + return "{0}, {1}, route distinguisher not present".format( + router.name, prefix + ) + for rd, pathes in dump.items(): + for path in pathes["paths"]: + if "remoteLabel" not in path.keys(): + return "{0}, {1}, remoteLabel not present".format( + router.name, prefix + ) + if "nexthops" not in path.keys(): + return "{0}, {1}, no nexthop available".format(router.name, prefix) + return None + + for prefix, rname_to_test in vpnv4_entries.items(): + func = functools.partial(_check_nexthop_available, router, prefix) + success, result = topotest.run_and_expect(func, None, count=20, wait=0.5) + assert result is None, "Failed to detect prefix {} on router {}".format( + prefix, router.name + ) + + for prefix, rname_to_test in vpnv4_entries.items(): + l3vpn_label, l3vpn_nh, l3vpn_rd = _return_remote_label_nh_rd(router, prefix) + logger.info( + "{0}, {1}, label value is {2}, nh is {3}".format( + router.name, prefix, l3vpn_label, l3vpn_nh + ) + ) + test_func = functools.partial( + mpls_table_check_entry, router, l3vpn_label, vpnv4_nht[l3vpn_nh] + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, result + + in_label = mpls_table_get_entry(router, l3vpn_label, vpnv4_nht[l3vpn_nh]) + label_ip_entries[prefix] = in_label + + bgp_vpnv4_prefix_check( + tgen.gears[rname_to_test], + l3vpn_rd, + prefix, + in_label, + vpnv4_nexthops[rname_to_test], + ) + + return label_ip_entries + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + Check that Labels are as expected in r1, r2,and r3 + Check ping connectivity between h1 and h2 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # check that r2 peerings are ok + logger.info("Checking BGP ipv4 vpn summary for r2") + router = tgen.gears["r2"] + json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 vpn summary json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + +def test_mpls_setup_ok(): + """ + tests for the r1 to r3 direction: checks for prefix=('172.31.1.0/24','172.31.2.0/24','172.31.3.0/24') + r2. get label from 'prefix' + check that r2. show mpls table has an entry with outbound label set to the label from 172.31.1.0/24 + r2. get label from mpls entry + check that r1: show bgp ipv4 vpn 172.31.1.0/24 has label from r2.mpls entry + tests for the r3 to r1 direction + r2. get label from 172.31.0.0/24 + check that r2. show mpls table has an entry with outbound label set that includes the label from 172.31.0.0/24 + r2. get label from mpls entry + check that r3: show bgp ipv4 vpn 172.31.0.0/24 has label from r2.mpls entry + check that h1. ping 172.31.1.10 (h2) is ok. + check that h1. ping 172.31.2.10 (h3) is ok. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r2"] + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + "172.31.0.0/24": "r3", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + logger.info("h1, check that ping from h1 to (h2,h3) is ok") + check_ping("h1", "172.31.1.10", True) + check_ping("h1", "172.31.2.10", True) + + +def test_r3_prefixes_removed(): + """ + Remove BGP redistributed updates from r3. + Check that the BGP VPN updates from the updates are not present on r2. + Check that the 'show bgp ipv4 vpn' and 'show mpls table' are ok for 172.31.3.0/24 + Remove the 172.31.3.0/24 update from BGP on r3. + Check that the BGP VPN updates from r3 are not present on r2. + Check that the 'show mpls table' entry previously seen disappeared + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r3"] + logger.info("{}, keeping only 172.31.3.0/24 network".format(router.name)) + router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nshutdown\n") + router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nshutdown\n") + + router = tgen.gears["r2"] + logger.info( + "{}, check that 'show bgp ipv4 vpn' has only 172.31.3.0/24 network from r3".format( + router.name + ) + ) + + for prefix in ("172.31.1.0/24", "172.31.2.0/24"): + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + prefix = "172.31.3.0/24" + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + vpnv4_checks = { + prefix: "r1", + } + label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + + router = tgen.gears["r3"] + logger.info("{}, removing {} network".format(router.name, prefix)) + router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nshutdown\n") + + router = tgen.gears["r2"] + logger.info( + "{}, check that 'show bgp ipv4 vpn' has not {} network from r3".format( + router.name, prefix + ) + ) + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} still present".format(router.name, prefix) + + logger.info( + "{}, check that 'show mpls table {}' is not present".format( + router.name, label_ip_entries[prefix] + ) + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, label_ip_entries[prefix] + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with in_label {} still present".format( + label_ip_entries[prefix] + ) + + +def test_r3_prefixes_added_back(): + """ + Add back the 172.31.3.0/24 network from r3 + Check on r2 that MPLS switching entry appears when the 1st BGP update is received + Check the IP connectivity (h1,h2) and (h1,h3) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + router = tgen.gears["r3"] + prefix = "172.31.3.0/24" + logger.info("{}, restoring the {} network from r3".format(router.name, prefix)) + router.vtysh_cmd("configure terminal\ninterface r3-eth3 vrf vrf1\nno shutdown\n") + + router = tgen.gears["r2"] + logger.info( + "{}, check that 'show bgp ipv4 vpn' has {} network from r3".format( + router.name, prefix + ) + ) + + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} not present".format(router.name, prefix) + + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + vpnv4_checks = { + prefix: "r1", + } + check_show_bgp_vpn_ok(router, vpnv4_checks) + + router = tgen.gears["r3"] + logger.info( + "{}, restoring the redistribute connected prefixes from r3".format(router.name) + ) + router.vtysh_cmd("configure terminal\ninterface r3-eth1 vrf vrf1\nno shutdown\n") + router.vtysh_cmd("configure terminal\ninterface r3-eth2 vrf vrf1\nno shutdown\n") + router = tgen.gears["r2"] + for prefix in ("172.31.1.0/24", "172.31.2.0/24"): + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {} not present".format(router.name, prefix) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_unconfigure_nexthop_change_nexthop_self(): + """ + Get the list of labels advertised from r2 to r1 + On r2, disable next-hop-self for 192.0.2.100 neighbor + Check that the list of labels are not present in 'show mpls table' + Check that r1 received the prefixes with the original (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + } + logger.info( + "{}, Get the list of labels allocated for prefixes from r3".format(router.name) + ) + label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + + logger.info( + "{}, disable next-hop-self for 192.0.2.100 neighbor".format(router.name) + ) + router = tgen.gears["r2"] + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 next-hop-self\n" + ) + + for prefix, label in label_ip_entries.items(): + logger.info( + "{}, check mpls entry for {} with in_label {} is not present'".format( + router.name, prefix, label + ) + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, label + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry for {} with in_label {} still present".format( + prefix, label + ) + + router = tgen.gears["r1"] + for prefix, label in label_ip_entries.items(): + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + prefix, + "444:3", + label=label, + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, mpls vpn update {} label {} is present".format( + router.name, prefix, label + ) + for prefix, label in label_ip_entries.items(): + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + nexthop="192.168.1.3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, mpls vpn update {} label {} is present".format( + router.name, prefix, label + ) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + tgen.gears["r2"].vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_reconfigure_nexthop_change_nexthop_self(): + """ + Get the list of labels advertised from r2 to r1 + On r2, enable next-hop-self for 192.0.2.100 neighbor + Check that the list of labels are present in 'show mpls table' + Check that r1 received the prefixes with the original (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + logger.info("{}, enable next-hop-self for 192.0.2.100 neighbor".format(router.name)) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 next-hop-self\n" + ) + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + logger.info("h1, check that ping from h1 to (h2,h3) is ok") + check_ping("h1", "172.31.1.10", True) + check_ping("h1", "172.31.2.10", True) + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +def test_declare_vpn_network_with_different_label(): + """ + declare a vpnv4 network on r3. + check that a new VPNv4 entry is received on r2. + Check that the list of labels are present in 'show mpls table' + Check that r1 received the prefixes with the new (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r3"] + logger.info( + "{}, declare static 33.33.33.33/32 network rd 33:33 label 33".format( + router.name + ) + ) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65501\nno bgp network import-check\n" + ) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65501\naddress-family ipv4 vpn\nnetwork 33.33.33.33/32 rd 444:3 label 33\n" + ) + + router = tgen.gears["r2"] + vpnv4_entries = { + "172.31.1.0/24": None, + "172.31.2.0/24": None, + "172.31.3.0/24": None, + "33.33.33.33/32": 33, + } + + for prefix, label in vpnv4_entries.items(): + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + router, + "ipv4", + prefix, + "444:3", + label=label, + nexthop="192.168.1.3", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {}, label {} not present".format( + router.name, prefix, label + ) + + vpnv4_checks = { + "172.31.1.0/24": "r1", + "172.31.2.0/24": "r1", + "172.31.3.0/24": "r1", + "33.33.33.33/32": "r1", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r1".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + +def test_filter_vpn_network_from_r1(): + """ + Get the list of labels in 'show mpls table' + filter network from r1 + check that the vpnv4 entry on r2 is not present + Check that the associated mpls entry is not present + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + + vpnv4_checks = { + "172.31.0.0/24": "r3", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on r2 and on r3".format( + router.name + ) + ) + label_ip_entries = check_show_bgp_vpn_ok(router, vpnv4_checks) + + for prefix, label in label_ip_entries.items(): + logger.info("{}, filter prefix {} from r1".format(router.name, prefix)) + router.vtysh_cmd( + "configure terminal\nroute-map rmap deny 1\nmatch ip next-hop address 192.0.2.1\n" + ) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nneighbor 192.0.2.100 route-map rmap in\n" + ) + logger.info( + "{}, check that prefix {} is not present".format(router.name, prefix) + ) + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + router, + "ipv4", + "172.31.0.0/24", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {}, is still present".format( + router.name, prefix + ) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + logger.info( + "{}, check that show mpls table {} is not present".format( + router.name, label + ) + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, int(label) + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry for {} with in_label {} still present".format( + prefix, label + ) + + +def test_unfilter_vpn_network_from_r1(): + """ + unfilter network from r1 + check that the vpnv4 entry on r2 is present + Check that the list of labels are present in 'show mpls table' + Check that r3 received the prefixes with the new (next-hop,label) + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r2"] + prefix = "172.31.0.0/24" + + logger.info("{}, filter prefix {} from r1".format(router.name, prefix)) + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv4 vpn\nno neighbor 192.0.2.100 route-map rmap in\n" + ) + + logger.info("{}, check that prefix {} is present".format(router.name, prefix)) + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, router, "ipv4", prefix, "444:1" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, vpnv4 update {}, is not present".format(router.name, prefix) + + vpnv4_checks = { + "172.31.0.0/24": "r3", + } + logger.info( + "{}, check that 'show bgp ipv4 vpn' and 'show mpls table' are set accordingly on all devices".format( + router.name + ) + ) + check_show_bgp_vpn_ok(router, vpnv4_checks) + + # diagnostic + logger.info("Dumping mplsvpn nexthop table") + router.vtysh_cmd("show bgp mplsvpn-nh-label-bind detail", isjson=False) + + +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_vpnv4_noretain/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf index 3d8773b8bf85..0709e43edf21 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf @@ -1,8 +1,12 @@ router bgp 65500 - bgp router-id 1.1.1.1 + bgp router-id 192.0.2.1 neighbor 10.125.0.2 remote-as 65500 address-family ipv4 unicast no neighbor 10.125.0.2 activate + label vpn export 100 + rd vpn export 192.0.2.1:0 + rt vpn import 192.0.2.2:400 + import vpn exit-address-family address-family ipv4 vpn neighbor 10.125.0.2 activate @@ -10,15 +14,33 @@ router bgp 65500 exit-address-family ! router bgp 65500 vrf vrf1 - bgp router-id 1.1.1.1 + bgp router-id 192.0.2.1 address-family ipv4 unicast redistribute connected label vpn export 101 - rd vpn export 444:1 - rt vpn import 51:100 52:100 - rt vpn export 51:100 + rd vpn export 192.0.2.1:1 + rt vpn import 192.0.2.2:100 + rt vpn export 192.0.2.1:100 + export vpn + import vpn + exit-address-family +! +router bgp 65500 vrf vrf3 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 103 + rd vpn export 192.0.2.1:3 + rt vpn export 192.0.2.1:300 export vpn + exit-address-family +! +router bgp 65500 vrf vrf4 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + label vpn export 104 + rd vpn export 192.0.2.1:4 + rt vpn import 192.0.2.1:300 import vpn exit-address-family ! - diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json deleted file mode 100644 index 28e153e3de89..000000000000 --- a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "vrfId":0, - "vrfName":"default", - "routerId":"1.1.1.1", - "defaultLocPrf":100, - "localAS":65500, - "routes":{ - "routeDistinguishers":{ - "444:1":{ - "10.201.0.0/24":[ - { - "valid":true, - "bestpath":true, - "selectionReason":"First path received", - "pathFrom":"external", - "prefix":"10.201.0.0", - "prefixLen":24, - "network":"10.201.0.0\/24", - "metric":0, - "weight":32768, - "peerId":"(unspec)", - "path":"", - "origin":"incomplete", - "announceNexthopSelf":true, - "nhVrfName":"vrf1", - "nexthops":[ - { - "ip":"0.0.0.0", - "hostname":"r1", - "afi":"ipv4", - "used":true - } - ] - } - ] - }, - "444:2":{ - "10.200.0.0/24":[ - { - "valid":true, - "bestpath":true, - "selectionReason":"First path received", - "pathFrom":"internal", - "prefix":"10.200.0.0", - "prefixLen":24, - "network":"10.200.0.0\/24", - "metric":0, - "locPrf":100, - "weight":0, - "peerId":"10.125.0.2", - "path":"", - "origin":"incomplete", - "nexthops":[ - { - "ip":"10.125.0.2", - "hostname":"r2", - "afi":"ipv4", - "used":true - } - ] - } - ] - }, - "444:3":{ - } - } - } -} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json new file mode 100644 index 000000000000..648bf854baad --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_all.json @@ -0,0 +1,175 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:2":{ + "10.202.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.202.0.0", + "prefixLen":24, + "network":"10.202.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:3":{ + "10.203.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.203.0.0", + "prefixLen":24, + "network":"10.203.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json similarity index 66% rename from tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json rename to tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json index 45f4acce6f95..f01607ac4e5a 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init.json @@ -1,21 +1,21 @@ { "vrfId":0, "vrfName":"default", - "routerId":"1.1.1.1", + "routerId":"192.0.2.1", "defaultLocPrf":100, "localAS":65500, "routes":{ "routeDistinguishers":{ - "444:1":{ - "10.201.0.0/24":[ + "192.0.2.1:1":{ + "10.101.0.0/24":[ { "valid":true, "bestpath":true, "selectionReason":"First path received", "pathFrom":"external", - "prefix":"10.201.0.0", + "prefix":"10.101.0.0", "prefixLen":24, - "network":"10.201.0.0\/24", + "network":"10.101.0.0\/24", "metric":0, "weight":32768, "peerId":"(unspec)", @@ -34,16 +34,44 @@ } ] }, - "444:2":{ - "10.200.0.0/24":[ + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ { "valid":true, "bestpath":true, "selectionReason":"First path received", "pathFrom":"internal", - "prefix":"10.200.0.0", + "prefix":"10.201.0.0", "prefixLen":24, - "network":"10.200.0.0\/24", + "network":"10.201.0.0\/24", "metric":0, "locPrf":100, "weight":0, @@ -61,16 +89,16 @@ } ] }, - "444:3":{ - "10.210.0.0/24":[ + "192.0.2.2:4":{ + "10.204.0.0/24":[ { "valid":true, "bestpath":true, "selectionReason":"First path received", "pathFrom":"internal", - "prefix":"10.210.0.0", + "prefix":"10.204.0.0", "prefixLen":24, - "network":"10.210.0.0\/24", + "network":"10.204.0.0\/24", "metric":0, "locPrf":100, "weight":0, diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json new file mode 100644 index 000000000000..6df6c69b8fc5 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json @@ -0,0 +1,148 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:2":{ + "10.202.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.202.0.0", + "prefixLen":24, + "network":"10.202.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json new file mode 100644 index 000000000000..7a17ff0f16a1 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json @@ -0,0 +1,148 @@ +{ + "vrfId":0, + "vrfName":"default", + "routerId":"192.0.2.1", + "defaultLocPrf":100, + "localAS":65500, + "routes":{ + "routeDistinguishers":{ + "192.0.2.1:1":{ + "10.101.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.101.0.0", + "prefixLen":24, + "network":"10.101.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf1", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.1:3":{ + "10.103.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"external", + "prefix":"10.103.0.0", + "prefixLen":24, + "network":"10.103.0.0\/24", + "metric":0, + "weight":32768, + "peerId":"(unspec)", + "path":"", + "origin":"incomplete", + "announceNexthopSelf":true, + "nhVrfName":"vrf3", + "nexthops":[ + { + "ip":"0.0.0.0", + "hostname":"r1", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:1":{ + "10.201.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.201.0.0", + "prefixLen":24, + "network":"10.201.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:3":{ + "10.203.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.203.0.0", + "prefixLen":24, + "network":"10.203.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + }, + "192.0.2.2:4":{ + "10.204.0.0/24":[ + { + "valid":true, + "bestpath":true, + "selectionReason":"First path received", + "pathFrom":"internal", + "prefix":"10.204.0.0", + "prefixLen":24, + "network":"10.204.0.0\/24", + "metric":0, + "locPrf":100, + "weight":0, + "peerId":"10.125.0.2", + "path":"", + "origin":"incomplete", + "nexthops":[ + { + "ip":"10.125.0.2", + "hostname":"r2", + "afi":"ipv4", + "used":true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf b/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf index 6f5cb6ec6826..233a6473b3ca 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf +++ b/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf @@ -10,5 +10,5 @@ router isis 1 is-type level-1 net 49.0002.0000.1994.00 segment-routing on - segment-routing prefix 1.1.1.1/32 index 11 + segment-routing prefix 192.0.2.1/32 index 11 ! diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf index 5b8b1e8ffb3b..f99cfafe3219 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf +++ b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf @@ -1,13 +1,16 @@ log stdout interface lo - ip address 1.1.1.1/32 + ip address 192.0.2.1/32 ! interface r1-gre0 ip address 192.168.0.1/24 ! -interface r1-eth1 vrf vrf1 - ip address 10.201.0.1/24 -! interface r1-eth0 ip address 10.125.0.1/24 ! +interface r1-eth1 vrf vrf1 + ip address 10.101.0.1/24 +! +interface r1-eth3 vrf vrf3 + ip address 10.103.0.1/24 +! \ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf index 235fb31177e0..729daef2bc14 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf +++ b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf @@ -1,35 +1,54 @@ router bgp 65500 - bgp router-id 2.2.2.2 + bgp router-id 192.0.2.2 neighbor 10.125.0.1 remote-as 65500 address-family ipv4 unicast no neighbor 10.125.0.1 activate exit-address-family address-family ipv4 vpn neighbor 10.125.0.1 activate - no bgp retain route-target all exit-address-family ! router bgp 65500 vrf vrf1 - bgp router-id 2.2.2.2 + bgp router-id 192.0.2.2 address-family ipv4 unicast redistribute connected - label vpn export 102 - rd vpn export 444:2 - rt vpn import 53:100 52:100 51:100 - rt vpn export 52:100 + label vpn export 201 + rd vpn export 192.0.2.2:1 + rt vpn import 192.0.2.1:100 192.0.2.2:100 192.0.2.2:200 + rt vpn export 192.0.2.2:100 export vpn import vpn exit-address-family ! router bgp 65500 vrf vrf2 - bgp router-id 2.2.2.2 + bgp router-id 192.0.2.2 address-family ipv4 unicast redistribute connected - label vpn export 102 - rd vpn export 444:3 - rt vpn both 53:100 52:100 51:100 - rt vpn both 53:100 + label vpn export 202 + rd vpn export 192.0.2.2:2 + rt vpn import 192.0.2.1:100 192.0.2.2:100 192.0.2.2:200 + rt vpn export 192.0.2.2:200 export vpn import vpn exit-address-family ! +router bgp 65500 vrf vrf3 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 203 + rd vpn export 192.0.2.2:3 + rt vpn export 192.0.2.2:300 + export vpn + exit-address-family +! +router bgp 65500 vrf vrf4 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 204 + rd vpn export 192.0.2.2:4 + rt vpn export 192.0.2.2:400 + export vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json new file mode 100644 index 000000000000..d8b8e88d936d --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_routes_all.json @@ -0,0 +1,177 @@ +{ + "vrfId": 0, + "vrfName": "default", + "routerId":"192.0.2.2", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "routeDistinguishers": { + "192.0.2.1:1": { + "10.101.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "prefix": "10.101.0.0", + "prefixLen": 24, + "network": "10.101.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "10.125.0.1", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "10.125.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.1:3": { + "10.103.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "internal", + "prefix": "10.103.0.0", + "prefixLen": 24, + "network": "10.103.0.0/24", + "metric": 0, + "locPrf": 100, + "weight": 0, + "peerId": "10.125.0.1", + "path": "", + "origin": "incomplete", + "nexthops": [ + { + "ip": "10.125.0.1", + "hostname": "r1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:1": { + "10.201.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "network": "10.201.0.0/24", + "prefixLen": 24, + "prefix": "10.201.0.0", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:2": { + "10.202.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "network": "10.202.0.0/24", + "prefixLen": 24, + "prefix": "10.202.0.0", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf2", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:3": { + "10.203.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.203.0.0", + "prefixLen": 24, + "network": "10.203.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf3", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "192.0.2.2:4": { + "10.204.0.0/24": [ + { + "valid": true, + "bestpath": true, + "selectionReason": "First path received", + "pathFrom": "external", + "prefix": "10.204.0.0", + "prefixLen": 24, + "network": "10.204.0.0/24", + "metric": 0, + "weight": 32768, + "peerId": "(unspec)", + "path": "", + "origin": "incomplete", + "announceNexthopSelf": true, + "nhVrfName": "vrf4", + "nexthops": [ + { + "ip": "0.0.0.0", + "hostname": "r2", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json new file mode 100644 index 000000000000..a4408f1915ac --- /dev/null +++ b/tests/topotests/bgp_vpnv4_noretain/r2/ipv4_vpn_summary.json @@ -0,0 +1,17 @@ +{ + "routerId":"192.0.2.2", + "as":65500, + "vrfId":0, + "vrfName":"default", + "peerCount":1, + "peers":{ + "10.125.0.1":{ + "remoteAs":65500, + "localAs":65500, + "version":4, + "state":"Established", + "peerState":"OK" + } + }, + "totalPeers":1 +} diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf b/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf index cbec8c367453..547d10f2bca9 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf +++ b/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf @@ -10,5 +10,5 @@ router isis 1 is-type level-1 net 49.0002.0000.1995.00 segment-routing on - segment-routing prefix 2.2.2.2/32 index 22 + segment-routing prefix 192.0.2.2/32 index 22 ! diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf index 7ec644ac2a90..f19ad9d3e8c2 100644 --- a/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf +++ b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf @@ -1,16 +1,22 @@ log stdout interface lo - ip address 2.2.2.2/32 + ip address 192.0.2.2/32 ! interface r2-gre0 ip address 192.168.0.2/24 ! +interface r2-eth0 + ip address 10.125.0.2/24 +! interface r2-eth1 vrf vrf1 - ip address 10.200.0.2/24 + ip address 10.201.0.2/24 ! interface r2-eth2 vrf vrf2 - ip address 10.210.0.2/24 + ip address 10.202.0.2/24 ! -interface r2-eth0 - ip address 10.125.0.2/24 +interface r2-eth3 vrf vrf3 + ip address 10.203.0.1/24 +! +interface r2-eth4 vrf vrf4 + ip address 10.204.0.1/24 ! diff --git a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py index 9b8ae4b7e330..f665040f7faa 100644 --- a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py +++ b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py @@ -17,6 +17,7 @@ import sys import json from functools import partial +from copy import deepcopy import pytest # Save the Current Working Directory to find configuration files. @@ -34,6 +35,7 @@ pytestmark = [pytest.mark.bgpd] + def build_topo(tgen): "Build function" @@ -53,41 +55,54 @@ def build_topo(tgen): switch = tgen.add_switch("s4") switch.add_link(tgen.gears["r2"]) - + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s6") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s7") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s8") + switch.add_link(tgen.gears["r2"]) + + def _populate_iface(): tgen = get_topogen() cmds_list = [ - 'modprobe mpls_router', - 'echo 100000 > /proc/sys/net/mpls/platform_labels', - 'ip link add vrf1 type vrf table 10', - 'ip link set dev vrf1 up', - 'ip link set dev {0}-eth1 master vrf1', - 'echo 1 > /proc/sys/net/mpls/conf/vrf1/input', + "modprobe mpls_router", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link add vrf1 type vrf table 10", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/vrf1/input", + "ip link add vrf2 type vrf table 20", + "ip link set dev vrf2 up", + "ip link set dev {0}-eth2 master vrf2", + "echo 1 > /proc/sys/net/mpls/conf/vrf2/input", + "ip link add vrf3 type vrf table 30", + "ip link set dev vrf3 up", + "ip link set dev {0}-eth3 master vrf3", + "echo 1 > /proc/sys/net/mpls/conf/vrf3/input", + "ip link add vrf4 type vrf table 40", + "ip link set dev vrf4 up", + "ip link set dev {0}-eth4 master vrf4", + "echo 1 > /proc/sys/net/mpls/conf/vrf4/input", ] - cmds_list_extra = [ - 'ip link add vrf2 type vrf table 20', - 'ip link set dev vrf2 up', - 'ip link set dev {0}-eth2 master vrf2', - 'echo 1 > /proc/sys/net/mpls/conf/vrf2/input', - ] - + for cmd in cmds_list: - input = cmd.format('r1', '1', '2') - logger.info('input: ' + cmd) - output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2')) - logger.info('output: ' + output) + input = cmd.format("r1", "1", "2") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1", "1", "2")) + logger.info("output: " + output) for cmd in cmds_list: - input = cmd.format('r2', '2', '1') - logger.info('input: ' + cmd) - output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1')) - logger.info('output: ' + output) + input = cmd.format("r2", "2", "1") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2", "2", "1")) + logger.info("output: " + output) - for cmd in cmds_list_extra: - input = cmd.format('r2', '2', '1') - logger.info('input: ' + cmd) - output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1')) - logger.info('output: ' + output) def setup_module(mod): "Sets up the pytest environment" @@ -96,7 +111,7 @@ def setup_module(mod): tgen.start_topology() router_list = tgen.routers() - _populate_iface() + _populate_iface() for rname, router in router_list.items(): router.load_config( @@ -136,27 +151,24 @@ def router_json_cmp_exact_filter(router, cmd, expected): if "version" in attr: attr.pop("version") - return topotest.json_cmp(json_output, expected, exact=True) + # filter out RD with no data (e.g. "444:3": {}) + json_tmp = deepcopy(json_output) + for rd, data in json_tmp["routes"]["routeDistinguishers"].items(): + if len(data.keys()) == 0: + json_output["routes"]["routeDistinguishers"].pop(rd) + return topotest.json_cmp(json_output, expected, exact=True) -def test_bgp_no_retain(): - """ - Check bgp no retain route-target all on r1 - """ +def check_show_bgp_ipv4_vpn(rname, json_file): tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) + router = tgen.gears[rname] - # Check IPv4 VPN routing tables on r1 - logger.info("Checking VPNv4 routes for convergence on r1") - router = tgen.gears["r1"] - json_file = "{}/{}/ipv4_vpn_routes.json".format(CWD, router.name) - if not os.path.isfile(json_file): - logger.info("skipping file {}".format(json_file)) - assert 0, "{} file not found".format(json_file) - return + logger.info("Checking VPNv4 routes for convergence on {}".format(rname)) + json_file = "{}/{}/{}".format(CWD, router.name, json_file) expected = json.loads(open(json_file).read()) test_func = partial( router_json_cmp_exact_filter, @@ -169,39 +181,306 @@ def test_bgp_no_retain(): assert result is None, assertmsg -def test_bgp_retain(): +def test_protocols_convergence_step0(): """ - Apply and check bgp retain route-target all on r1 + Assert that all protocols have converged """ - tgen = get_topogen() if tgen.routers_have_failure(): pytest.skip(tgen.errors) - # Check IPv4 VPN routing tables on r1 - logger.info("Checking VPNv4 routes on r1 after bgp no retain") - router = tgen.gears["r1"] - router.vtysh_cmd( - "configure\nrouter bgp 65500\naddress-family ipv4 vpn\nbgp retain route-target all\n" - ) - json_file = "{}/{}/ipv4_vpn_routes_unfiltered.json".format(CWD, router.name) - if not os.path.isfile(json_file): - logger.info("skipping file {}".format(json_file)) - assert 0, "{} file not found".format(json_file) - return - + # check that r2 peerings are ok + logger.info("Checking BGP ipv4 vpn summary for r2") + router = tgen.gears["r2"] + json_file = "{}/{}/ipv4_vpn_summary.json".format(CWD, router.name) expected = json.loads(open(json_file).read()) test_func = partial( - router_json_cmp_exact_filter, + topotest.router_json_cmp, router, - "show bgp ipv4 vpn json", + "show bgp ipv4 vpn summary json", expected, ) - _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) assertmsg = '"{}" JSON output mismatches'.format(router.name) assert result is None, assertmsg +def test_bgp_no_retain_step1(): + """ + Check bgp no retain route-target all on r1 + """ + + rname = "r1" + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_retain_step2(): + """ + Apply and check bgp retain route-target all on r1 + """ + rname = "r1" + cfg = """ +configure +router bgp 65500 + address-family ipv4 vpn + bgp retain route-target all +""" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_all.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_step3(): + """ + Apply and check no bgp retain route-target all on r1 + """ + rname = "r1" + + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure\nrouter bgp 65500\naddress-family ipv4 vpn\nno bgp retain route-target all\n" + ) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_add_vrf2_step4(): + """ + Add vrf2 on r1 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 192.0.2.1:200 + rt vpn import 192.0.2.2:200 + import vpn + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_unimport_vrf2_step5(): + """ + Unimport to vrf2 on r1 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + no import vpn + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_import_vrf2_step6(): + """ + Re-import to vrf2 on r1 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + import vpn + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf2.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_import_vrf1_step7(): + """ + Import r2 vrf1 into r1 vrf2 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rt vpn import 192.0.2.1:100 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_import_vrf3_step8(): + """ + Import r2 vrf3 into r1 vrf2 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rt vpn import 192.0.2.2:300 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_unimport_vrf3_step9(): + """ + Un-import r2 vrf3 into r1 vrf2 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + no rt vpn import 192.0.2.2:300 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_import_vrf3_step10(): + """ + Import r2 vrf3 into r1 vrf2 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 vrf vrf2 + address-family ipv4 unicast + rt vpn import 192.0.2.2:300 + exit-address-family +! +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init_plus_r2_vrf3.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_no_retain_remove_vrf2_step11(): + """ + Import r2 vrf3 into r1 vrf2 and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +no router bgp 65500 vrf vrf2 +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_no_retain_init.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + +def test_bgp_retain_step12(): + """ + Configure retain and check bgp vpnv4 table + """ + + rname = "r1" + cfg = """ +configure +router bgp 65500 + address-family ipv4 vpn + bgp retain route-target all +""" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears[rname] + router.vtysh_cmd(cfg) + + check_show_bgp_ipv4_vpn(rname, "ipv4_vpn_routes_all.json") + check_show_bgp_ipv4_vpn("r2", "ipv4_vpn_routes_all.json") + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json new file mode 100644 index 000000000000..31a1f3d6edce --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgp_ipv4_routes_vrf1.json @@ -0,0 +1,143 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "192.168.0.2", + "afi": "ipv4", + "used": true + } + ] + } + + ], + "172.31.0.11/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.11", + "prefixLen":32, + "network":"172.31.0.11/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.11", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.12/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.12", + "prefixLen":32, + "network":"172.31.0.12/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.12", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.13/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.13", + "prefixLen":32, + "network":"172.31.0.13/32", + "peerId":"192.168.255.13", + "nexthops":[ + { + "ip":"192.168.255.13", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.14/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.14", + "prefixLen":32, + "network":"172.31.0.14/32", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192.0.2.14", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.15/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.15", + "prefixLen":32, + "network":"172.31.0.15/32", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192.0.2.12", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.20/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.20", + "prefixLen":32, + "network":"172.31.0.20/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.11", + "afi":"ipv4", + "used":true + } + ] + } + ], + "172.31.0.111/32": [ + { + "valid":true, + "bestpath":true, + "prefix":"172.31.0.111", + "prefixLen":32, + "network":"172.31.0.111/32", + "peerId":"192.0.2.100", + "nexthops":[ + { + "ip":"192.0.2.11", + "afi":"ipv4", + "used":true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf new file mode 100644 index 000000000000..35fb2ec23d4e --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/bgpd.conf @@ -0,0 +1,30 @@ +router bgp 65500 + bgp router-id 192.168.0.1 + no bgp ebgp-requires-policy + neighbor 192.168.0.2 remote-as 65501 + address-family ipv4 unicast + no neighbor 192.168.0.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.2 activate + neighbor 192.168.0.2 soft-reconfiguration inbound + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.168.0.1 + neighbor 192.0.2.100 remote-as 65500 + neighbor 192.168.255.13 remote-as 65500 + address-family ipv4 unicast + redistribute connected + redistribute static + label vpn export allocation-mode per-nexthop + label vpn export auto + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r1-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json new file mode 100644 index 000000000000..da7d2818330c --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.200.0.0/24": [ + { + "prefix": "10.200.0.0/24", + "prefixLen": 24, + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.125.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + } + ] + } + ], + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops":[ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf new file mode 100644 index 000000000000..261859501402 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r1/zebra.conf @@ -0,0 +1,18 @@ +log stdout +debug zebra nht +!debug zebra kernel msgdump recv +!debug zebra dplane detailed +!debug zebra packet recv +interface r1-eth1 vrf vrf1 + ip address 192.0.2.1/24 +! +interface r1-eth2 vrf vrf1 + ip address 192.168.255.1/24 +! +interface r1-eth0 + ip address 192.168.0.1/24 +! +vrf vrf1 + ip route 172.31.0.14/32 192.0.2.14 + ip route 172.31.0.15/32 192.0.2.12 +exit-vrf diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf new file mode 100644 index 000000000000..5da91518b467 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/bgpd.conf @@ -0,0 +1,11 @@ +router bgp 65500 + bgp router-id 192.0.2.11 + no bgp network import-check + neighbor 192.0.2.100 remote-as 65500 + address-family ipv4 unicast + network 172.31.0.11/32 + network 172.31.0.111/32 + network 172.31.0.20/32 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf new file mode 100644 index 000000000000..a080757561fa --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r11/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r11-eth0 + ip address 192.0.2.11/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf new file mode 100644 index 000000000000..d3889f5040b3 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65500 + bgp router-id 192.0.2.12 + no bgp network import-check + neighbor 192.0.2.100 remote-as 65500 + address-family ipv4 unicast + network 172.31.0.12/32 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf new file mode 100644 index 000000000000..9ce3aba24706 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r12/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r12-eth0 + ip address 192.0.2.12/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf new file mode 100644 index 000000000000..21dbb588d5b5 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/bgpd.conf @@ -0,0 +1,9 @@ +router bgp 65500 + bgp router-id 192.168.255.13 + no bgp network import-check + address-family ipv4 unicast + neighbor 192.168.255.1 remote-as 65500 + network 172.31.0.13/32 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf new file mode 100644 index 000000000000..4d78b5f048fb --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r13/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r13-eth0 + ip address 192.168.255.13/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json new file mode 100644 index 000000000000..3407925d5c59 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65501, + "routes": + { + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0\/24", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json new file mode 100644 index 000000000000..46f4a1838678 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgp_vpnv4_routes.json @@ -0,0 +1,187 @@ +{ + "vrfName": "default", + "localAS": 65501, + "routes": + { + "routeDistinguishers": + { + "444:1": + { + "172.31.0.11/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.11", + "prefixLen": 32, + "network": "172.31.0.11\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.12/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.12", + "prefixLen": 32, + "network": "172.31.0.12\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.13/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.13", + "prefixLen": 32, + "network": "172.31.0.13\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.14/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.14", + "prefixLen": 32, + "network": "172.31.0.14\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.15/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.15", + "prefixLen": 32, + "network": "172.31.0.15\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.20/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.20", + "prefixLen": 32, + "network": "172.31.0.20\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "172.31.0.111/32": [ + { + "valid": true, + "bestpath": true, + "prefix": "172.31.0.111", + "prefixLen": 32, + "network": "172.31.0.111\/32", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.0.2.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "192.0.2.0", + "prefixLen": 24, + "network": "192.0.2.0\/24", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "192.168.255.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "192.168.255.0", + "prefixLen": 24, + "network": "192.168.255.0\/24", + "peerId": "192.168.0.1", + "nexthops": [ + { + "ip": "192.168.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ] + }, + "444:2": + { + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "peerId": "(unspec)", + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf new file mode 100644 index 000000000000..5fb79027a64e --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.168.0.2 + no bgp ebgp-requires-policy + neighbor 192.168.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192.168.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.168.0.1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.168.0.2 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf new file mode 100644 index 000000000000..b7283a35929a --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/r2/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r2-eth1 vrf vrf1 + ip address 10.200.0.2/24 +! +interface r2-eth0 + ip address 192.168.0.2/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf new file mode 100644 index 000000000000..ff323143041e --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65500 + bgp router-id 100.100.100.100 + no bgp network import-check + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.11 remote-as 65500 + neighbor 192.0.2.12 remote-as 65500 + address-family ipv4 unicast + neighbor 192.0.2.1 route-reflector-client + neighbor 192.0.2.11 route-reflector-client + neighbor 192.0.2.12 route-reflector-client + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf new file mode 100644 index 000000000000..315c22ab342f --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/rr/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface rr-eth0 + ip address 192.0.2.100/24 +! diff --git a/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py new file mode 100644 index 000000000000..ce278ed7a7e8 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_per_nexthop_label/test_bgp_vpnv4_per_nexthop_label.py @@ -0,0 +1,806 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_vpnv4_per_nexthop_label.py +# +# Copyright 2023 6WIND S.A. +# + +""" + test_bgp_vpnv4_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering + Let us exchange VPNv4 updates between both devices + Updates from r1 will originate from the same RD, but will have separate + label values. + + +----------+ + | r11 | + |192.0.2.11+---+ + | | | +----+--------+ +----------+ + +----------+ | 192.0.2.1 |vrf | r1 |192.168.0.0/24| r2 | + +-------------------+ | 1+--------------+ | + +----------+ | |VRF1|AS65500 | | AS65501 | + | r12 | | +-------------+ | VPNV4| |VPNV4 | + |192.0.2.12+---+ |192.168.255.1+-+--+--------+ +----------+ + | | | + +----------+ | + | + +----------+ | + | r13 | | + |192.168. +---------+ + | 255.13 | + +----------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# 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 + + +pytestmark = [pytest.mark.bgpd] + +PREFIXES_R11 = ["172.31.0.11/32", "172.31.0.20/32", "172.31.0.111/32"] +PREFIXES_R12 = ["172.31.0.12/32", "172.31.0.15/32"] +PREFIXES_R13 = ["172.31.0.13/32"] +PREFIXES_REDIST = ["172.31.0.14/32"] +PREFIXES_CONNECTED = ["192.168.255.0/24", "192.0.2.0/24"] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r11") + tgen.add_router("r12") + tgen.add_router("r13") + tgen.add_router("r14") + tgen.add_router("rr") + + 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["r11"]) + switch.add_link(tgen.gears["r12"]) + switch.add_link(tgen.gears["rr"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r13"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r14"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + cmds_list_plus = [ + "ip link set dev {0}-eth2 master vrf1", + ] + + for cmd in cmds_list: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list_plus: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r2") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_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 bgp_vpnv4_table_check(router, group, label_list=None, label_value_expected=None): + """ + Dump and check that vpnv4 entries have the same MPLS label value + * 'router': the router to check + * 'group': the list of prefixes to check. a single label value for the group has to be found + * 'label_list': check that the label values are not present in the vpnv4 entries + * that list is updated with the present label value + * 'label_value_expected': check that the mpls label read is the same as that value + """ + + stored_label_inited = False + for prefix in group: + dump = router.vtysh_cmd("show bgp ipv4 vpn {} json".format(prefix), isjson=True) + assert dump, "{0}, {1}, route distinguisher not present".format( + router.name, prefix + ) + for rd, pathes in dump.items(): + for path in pathes["paths"]: + assert ( + "remoteLabel" in path.keys() + ), "{0}, {1}, remoteLabel not present".format(router.name, prefix) + logger.info( + "{0}, {1}, label value is {2}".format( + router.name, prefix, path["remoteLabel"] + ) + ) + if stored_label_inited: + assert ( + path["remoteLabel"] == stored_label + ), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format( + router.name, prefix, stored_label, path["remoteLabel"] + ) + else: + stored_label = path["remoteLabel"] + stored_label_inited = True + if label_list is not None: + assert ( + stored_label not in label_list + ), "{0}, {1}, label already detected in a previous prefix".format( + router.name, prefix + ) + label_list.add(stored_label) + + if label_value_expected: + assert ( + path["remoteLabel"] == label_value_expected + ), "{0}, {1}, label value not expected (expected {2}, observed {3}".format( + router.name, prefix, label_value_expected, path["remoteLabel"] + ) + + +def bgp_vpnv4_table_check_all(router, label_list=None, same=False): + """ + Dump and check that vpnv4 entries are correctly configured with specific label values + * 'router': the router to check + * 'label_list': check that the label values are not present in the vpnv4 entries + * that list is updated with the present label value found. + * 'same': by default, set to False. Addresses groups are classified by addresses. + * if set to True, all entries of all groups should have a unique label value + """ + if same: + bgp_vpnv4_table_check( + router, + group=PREFIXES_R11 + + PREFIXES_R12 + + PREFIXES_R13 + + PREFIXES_REDIST + + PREFIXES_CONNECTED, + label_list=label_list, + ) + else: + for group in ( + PREFIXES_R11, + PREFIXES_R12, + PREFIXES_R13, + PREFIXES_REDIST, + PREFIXES_CONNECTED, + ): + bgp_vpnv4_table_check(router, group=group, label_list=label_list) + + +def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=None): + nexthop_list = [] + if blacklist: + nexthop_list.append(blacklist) + + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + if label_list is not None: + label_list.add(in_label) + for nh in label_info["nexthops"]: + if "installed" not in nh.keys(): + return "{} {} is not installed yet on {}".format( + in_label, label_info, router.name + ) + if nh["installed"] != True or nh["type"] != "BGP": + return "{}, show mpls table, nexthop is not installed".format( + router.name + ) + if "nexthop" in nh.keys(): + if nh["nexthop"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop address".format( + router.name + ) + nexthop_list.append(nh["nexthop"]) + elif "interface" in nh.keys(): + if nh["interface"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop interface".format( + router.name + ) + nexthop_list.append(nh["interface"]) + else: + return "{}, show mpls table, entry with neither nexthop nor interface".format( + router.name + ) + + if whitelist: + for entry in whitelist: + if entry not in nexthop_list: + return "{}, show mpls table, entry with nexthop {} not present in nexthop list".format( + router.name, entry + ) + return None + + +def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None): + """ + Dump and check 'show mpls table json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'blacklist': the list of nexthops (IP or interface) that should not be on output + * 'label_list': the list of labels that should be in inLabel value + * 'whitelist': the list of nexthops (IP or interface) that should be on output + """ + logger.info("Checking MPLS labels on {}".format(router.name)) + # Check r2 removed 172.31.0.30 vpnv4 update + test_func = functools.partial( + check_show_mpls_table, router, blacklist, label_list, whitelist + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, MPLS labels check fail: {}".format(router.name, result) + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found(router, inlabel, interface): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = { + "inLabel": inlabel, + "installed": True, + "nexthops": [{"interface": interface}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_not_found(router, inlabel): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = {"inlabel": inlabel, "installed": True} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def mpls_entry_get_interface(router, label): + """ + Assert that the label is in MPLS table + Assert an outgoing interface is programmed + return the outgoing interface + """ + outgoing_interface = None + + logger.info("Checking MPLS labels on {}".format(router.name)) + dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True) + assert dump, "{0}, label {1} not present".format(router.name, label) + + for nh in dump["nexthops"]: + assert ( + "interface" in nh.keys() + ), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format( + router.name, label + ) + + outgoing_interface = nh["interface"] + + return outgoing_interface + + +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 BGP IPv4 routing tables on VRF1 of r1 + logger.info("Checking BGP IPv4 routes for convergence on r1 VRF1") + router = tgen.gears["r1"] + json_file = "{}/{}/bgp_ipv4_routes_vrf1.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking BGP VPNv4 routes for convergence on r2") + router = tgen.gears["r2"] + json_file = "{}/{}/bgp_vpnv4_routes.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv4 vpn json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP labels received on r2 + logger.info("Checking BGP VPNv4 labels on r2") + label_list = set() + bgp_vpnv4_table_check_all(tgen.gears["r2"], label_list) + + # Check MPLS labels received on r1 + mpls_table_check(tgen.gears["r1"], label_list) + + +def test_flapping_bgp_vrf_down(): + """ + Turn down a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Unpeering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nno neighbor 192.0.2.100\n", + isjson=False, + ) + + def _bgp_prefix_not_found(router, vrf, ipversion, prefix): + output = json.loads( + router.vtysh_cmd( + "show bgp vrf {} {} {} json".format(vrf, ipversion, prefix) + ) + ) + expected = {"prefix": prefix} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + # Check prefix from r11 is not present + test_func = functools.partial( + _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv4", "172.31.0.11/32" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r1, prefix 172.31.0.11/32 from r11 did not disappear. r11 still connected to rr ?" + + # Check BGP updated received on r2 are not from r11 + logger.info("Checking BGP VPNv4 labels on r2") + for entry in PREFIXES_R11: + dump = tgen.gears["r2"].vtysh_cmd( + "show bgp ipv4 vpn {} json".format(entry), isjson=True + ) + for rd in dump: + assert False, "r2, {}, route distinguisher {} present".format(entry, rd) + + mpls_table_check(tgen.gears["r1"], blacklist=["192.0.2.11"]) + + +def test_flapping_bgp_vrf_up(): + """ + Turn up a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Peering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nneighbor 192.0.2.100 remote-as 65500\n", + isjson=False, + ) + + # Check r2 gets prefix 172.31.0.11/128 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.11/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, prefix 172.31.0.11/32 from r11 not present. r11 still disconnected from rr ?" + bgp_vpnv4_table_check_all(tgen.gears["r2"]) + + +def test_recursive_route(): + """ + Test static recursive route redistributed over BGP + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nip route 172.31.0.30/32 172.31.0.20\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + + # Check r2 received vpnv4 update with 172.31.0.30 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.30/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.30 not found" + + bgp_vpnv4_table_check(tgen.gears["r2"], group=PREFIXES_R11 + ["172.31.0.30/32"]) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False) + + logger.info("Disabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nno ip route 172.31.0.30/32 172.31.0.20\n", + isjson=False, + ) + logger.info("Checking BGP VPNv4 labels on r2") + + # Check r2 removed 172.31.0.30 vpnv4 update + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.30/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.30 still present" + + +def test_prefix_changes_interface(): + """ + Test BGP update for a given prefix learnt on different interface + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling a 172.31.0.50/32 prefix for r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32", + isjson=False, + ) + + # Check r2 received vpnv4 update with 172.31.0.50 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.50/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.50 not found" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + label_list = set() + bgp_vpnv4_table_check( + tgen.gears["r2"], + group=["172.31.0.11/32", "172.31.0.111/32", "172.31.0.50/32"], + label_list=label_list, + ) + + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r11 found" + + oldlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel)) + old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + oldlabel, old_outgoing_interface + ) + ) + + logger.info("Moving the 172.31.0.50/32 prefix from r11 to r13") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32", + isjson=False, + ) + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nnetwork 172.31.0.50/32", + isjson=False, + ) + + # Check r2 removed 172.31.0.50 vpnv4 update with old label + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.50/32", + "444:1", + label=oldlabel, + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, vpnv4 update 172.31.0.50 with old label {0} still present".format(oldlabel) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + # Check r2 received new 172.31.0.50 vpnv4 update + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv4", + "172.31.0.50/32", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv4 update 172.31.0.50 not found" + + label_list = set() + bgp_vpnv4_table_check( + tgen.gears["r2"], + group=PREFIXES_R13 + ["172.31.0.50/32"], + label_list=label_list, + ) + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r13 found" + + newlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(newlabel)) + new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + newlabel, new_outgoing_interface + ) + ) + if old_outgoing_interface == new_outgoing_interface: + assert 0, "r1, outgoing interface did not change whereas BGP update moved" + + logger.info("Restoring state by removing the 172.31.0.50/32 prefix from r13") + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv4 unicast\nno network 172.31.0.50/32", + isjson=False, + ) + + +def test_changing_default_label_value(): + """ + Change the MPLS default value + Check that r1 VPNv4 entries have the 222 label value + Check that MPLS entry with old label value is no more present + Check that MPLS entry for local traffic has inLabel set to 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + # counting the number of labels used in the VPNv4 table + label_list = set() + logger.info("r1, vpnv4 table, check the number of labels used before modification") + bgp_vpnv4_table_check_all(router, label_list) + old_len = len(label_list) + assert ( + old_len != 1 + ), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len) + + logger.info("r1, vrf1, changing the default MPLS label value to export to 222") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export 222\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_found, router, 222, "vrf1" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 222 not found" + + # check label repartition is ok + logger.info("r1, vpnv4 table, check the number of labels used after modification") + label_list = set() + bgp_vpnv4_table_check_all(router, label_list) + new_len = len(label_list) + assert ( + old_len == new_len + ), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format( + new_len, old_len + ) + + logger.info( + "r1, vpnv4 table, check that prefixes that were using the vrf label have refreshed the label value to 222" + ) + bgp_vpnv4_table_check( + router, group=["192.168.255.0/24", "192.0.2.0/24"], label_value_expected=222 + ) + + +def test_unconfigure_allocation_mode_nexthop(): + """ + Test unconfiguring allocation mode per nexthop + Check that show mpls table has no entry with label 17 (previously used) + Check that all VPN updates on r1 should have label value moved to 222 + Check that show mpls table will only have 222 label value + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Unconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno label vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is not present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv4 routes from r1 + logger.info("Checking vpnv4 routes on r1") + label_list = set() + bgp_vpnv4_table_check_all(router, label_list=label_list, same=True) + assert len(label_list) == 1, "r1, multiple Label values found for vpnv4 updates" + + new_label = label_list.pop() + assert ( + new_label == 222 + ), "r1, wrong label value in VPNv4 table, expected 222, observed {}".format( + new_label + ) + + # Check mpls table with 222 value + logger.info("Checking MPLS values on show mpls table of r1") + label_list = set() + label_list.add(222) + mpls_table_check(router, label_list=label_list) + + +def test_reconfigure_allocation_mode_nexthop(): + """ + Test re-configuring allocation mode per nexthop + Check that show mpls table has no entry with label 17 + Check that all VPN updates on r1 should have multiple label values and not only 222 + Check that show mpls table will have multiple label values and not only 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Reconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nlabel vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check that show mpls table has no entry with label 17 + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv4 routes from r1 + logger.info("Checking vpnv4 routes on r1") + label_list = set() + bgp_vpnv4_table_check_all(router, label_list=label_list) + assert len(label_list) != 1, "r1, only 1 label values found for vpnv4 updates" + + # Check mpls table with all values + logger.info("Checking MPLS values on show mpls table of r1") + mpls_table_check(router, label_list=label_list) + + +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_vpnv6_per_nexthop_label/__init__.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json new file mode 100644 index 000000000000..39ba7dd62202 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgp_ipv6_routes_vrf1.json @@ -0,0 +1,186 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "10:200::/64": [ + { + "valid": true, + "bestpath": true, + "prefix": "10:200::", + "prefixLen": 64, + "network": "10:200::/64", + "nexthops": [ + { + "ip": "192:168::2", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::11/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::11", + "prefixLen":128, + "network":"172:31::11/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::11", + "afi":"ipv6", + "scope":"global" + } + ] + } + ], + "172:31::12/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::12", + "prefixLen":128, + "network":"172:31::12/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::12", + "afi":"ipv6", + "scope":"global", + "used":true + }, + { + "scope": "link-local" + } + ] + } + ], + "172:31::13/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::13", + "prefixLen":128, + "network":"172:31::13/128", + "peerId":"192:168::255:13", + "nexthops":[ + { + "ip":"192:168::255:13", + "afi":"ipv6", + "scope": "global", + "used":true + }, + { + "scope": "link-local" + } + ] + } + ], + "172:31::14/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::14", + "prefixLen":128, + "network":"172:31::14/128", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192:2::14", + "afi":"ipv6", + "used":true + } + ] + } + ], + "172:31::15/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::15", + "prefixLen":128, + "network":"172:31::15/128", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"192:2::12", + "afi":"ipv6", + "used":true + } + ] + } + ], + "172:31::20/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::20", + "prefixLen":128, + "network":"172:31::20/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } + ], + "172:31::111/128": [ + { + "valid":true, + "bestpath":true, + "prefix":"172:31::111", + "prefixLen":128, + "network":"172:31::111/128", + "peerId":"192:2::100", + "nexthops":[ + { + "ip":"192:2::11", + "afi":"ipv6", + "scope":"global", + "used":true + } + ] + } + ], + "192:2::/64": [ + { + "valid":true, + "bestpath":true, + "prefix":"192:2::", + "prefixLen":64, + "network":"192:2::/64", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"::", + "afi":"ipv6", + "used":true + } + ] + } + ], + "192:168::255:0/112": [ + { + "valid":true, + "bestpath":true, + "prefix":"192:168::255:0", + "prefixLen":112, + "network":"192:168::255:0/112", + "peerId":"(unspec)", + "nexthops":[ + { + "ip":"::", + "afi":"ipv6", + "used":true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf new file mode 100644 index 000000000000..6bb0a88f09a7 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/bgpd.conf @@ -0,0 +1,45 @@ +debug bgp vpn leak-from-vrf +debug bgp vpn label +debug bgp nht +debug bgp updates out +router bgp 65500 + bgp router-id 192.168.0.1 + no bgp ebgp-requires-policy + neighbor 192:168::2 remote-as 65501 + address-family ipv4 unicast + no neighbor 192:168::2 activate + exit-address-family + address-family ipv6 vpn + neighbor 192:168::2 activate + neighbor 192:168::2 soft-reconfiguration inbound + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.168.0.1 + neighbor 192:2::100 remote-as 65500 + neighbor 192:168::255:13 remote-as 65500 + address-family ipv6 unicast + neighbor 192:2::100 activate + neighbor 192:2::100 route-map rmap in + neighbor 192:168::255:13 activate + neighbor 192:168::255:13 route-map rmap in + redistribute connected + redistribute static route-map rmap + label vpn export allocation-mode per-nexthop + label vpn export auto + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r1-eth0 + mpls bgp forwarding +! +bgp community-list 1 seq 5 permit 10:10 +! +route-map rmap permit 1 + set ipv6 next-hop prefer-global +! +route-map rmap permit 2 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf new file mode 100644 index 000000000000..bdad9ee8e71a --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r1/zebra.conf @@ -0,0 +1,18 @@ +log stdout +debug zebra nht +!debug zebra kernel msgdump recv +!debug zebra dplane detailed +!debug zebra packet recv +interface r1-eth1 vrf vrf1 + ipv6 address 192:2::1/64 +! +interface r1-eth2 vrf vrf1 + ipv6 address 192:168::255:1/112 +! +interface r1-eth0 + ip address 192:168::1/112 +! +vrf vrf1 + ipv6 route 172:31::14/128 192:2::14 + ipv6 route 172:31::15/128 192:2::12 +exit-vrf diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf new file mode 100644 index 000000000000..cb653d61b511 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 65500 + bgp router-id 11.11.11.11 + no bgp network import-check + neighbor 192:2::100 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:2::100 activate + ! + address-family ipv6 unicast + neighbor 192:2::100 activate + network 172:31::11/128 + network 172:31::111/128 + network 172:31::20/128 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf new file mode 100644 index 000000000000..a76080d6d98b --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r11/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r11-eth0 + ipv6 address 192:2::11/64 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf new file mode 100644 index 000000000000..d41fb18e4bd4 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/bgpd.conf @@ -0,0 +1,13 @@ +router bgp 65500 + bgp router-id 12.12.12.12 + no bgp network import-check + neighbor 192:2::100 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:2::100 activate + ! + address-family ipv6 unicast + neighbor 192:2::100 activate + network 172:31::12/128 + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf new file mode 100644 index 000000000000..df9cae49b26a --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r12/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r12-eth0 + ipv6 address 192:2::12/64 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf new file mode 100644 index 000000000000..04378825aeaf --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/bgpd.conf @@ -0,0 +1,12 @@ +router bgp 65500 + bgp router-id 13.13.13.13 + no bgp network import-check + neighbor 192:168::255:1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:168::255:1 activate + exit-address-family + address-family ipv6 unicast + neighbor 192:168::255:1 activate + network 172:31::0:13/128 + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf new file mode 100644 index 000000000000..dfe59944bceb --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r13/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface r13-eth0 + ipv6 address 192:168::255:13/112 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json new file mode 100644 index 000000000000..bb7d5c091ffe --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgp_vpnv6_routes.json @@ -0,0 +1,187 @@ +{ + "vrfName": "default", + "localAS": 65501, + "routes": + { + "routeDistinguishers": + { + "444:1": + { + "172:31::11/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::11", + "prefixLen": 128, + "network": "172:31::11/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::12/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::12", + "prefixLen": 128, + "network": "172:31::12/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::13/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::13", + "prefixLen": 128, + "network": "172:31::13/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::14/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::14", + "prefixLen": 128, + "network": "172:31::14/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::15/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::15", + "prefixLen": 128, + "network": "172:31::15/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::20/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::20", + "prefixLen": 128, + "network": "172:31::20/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "172:31::111/128": [ + { + "valid": true, + "bestpath": true, + "prefix": "172:31::111", + "prefixLen": 128, + "network": "172:31::111/128", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192:2::/64": [ + { + "valid": true, + "bestpath": true, + "prefix": "192:2::", + "prefixLen": 64, + "network": "192:2::/64", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ], + "192:168::255:0/112": [ + { + "valid": true, + "bestpath": true, + "prefix": "192:168::255:0", + "prefixLen": 112, + "network": "192:168::255:0/112", + "peerId": "192:168::1", + "nexthops": [ + { + "ip": "192:168::1", + "afi": "ipv6", + "used": true + } + ] + } + ] + }, + "444:2": + { + "10:200::/64": [ + { + "valid": true, + "bestpath": true, + "prefix": "10:200::", + "prefixLen": 64, + "network": "10:200::/64", + "peerId": "(unspec)", + "nhVrfName": "vrf1", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "used": true + } + ] + } + ] + } + } + } +} diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf new file mode 100644 index 000000000000..30e9959c91e5 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 192.168.0.2 + no bgp ebgp-requires-policy + neighbor 192:168::1 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:168::1 activate + exit-address-family + address-family ipv6 vpn + neighbor 192:168::1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 192.168.0.2 + address-family ipv6 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf new file mode 100644 index 000000000000..47cee952c768 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/r2/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r2-eth1 vrf vrf1 + ipv6 address 10:200::2/64 +! +interface r2-eth0 + ipv6 address 192:168::2/112 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf new file mode 100644 index 000000000000..8c7664b6a21e --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/bgpd.conf @@ -0,0 +1,24 @@ +router bgp 65500 + bgp router-id 100.100.100.100 + no bgp network import-check + neighbor 192:2::1 remote-as 65500 + neighbor 192:2::11 remote-as 65500 + neighbor 192:2::12 remote-as 65500 + address-family ipv4 unicast + no neighbor 192:2::1 activate + no neighbor 192:2::11 activate + no neighbor 192:2::12 activate + ! + address-family ipv6 unicast + neighbor 192:2::1 activate + neighbor 192:2::1 route-reflector-client + neighbor 192:2::1 nexthop-local unchanged + neighbor 192:2::11 activate + neighbor 192:2::11 route-reflector-client + neighbor 192:2::11 nexthop-local unchanged + neighbor 192:2::12 activate + neighbor 192:2::12 route-reflector-client + neighbor 192:2::12 nexthop-local unchanged + exit-address-family +! + diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf new file mode 100644 index 000000000000..94b82dcdd95a --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/rr/zebra.conf @@ -0,0 +1,4 @@ +log stdout +interface rr-eth0 + ipv6 address 192:2::100/64 +! diff --git a/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py new file mode 100644 index 000000000000..e936ccc1e444 --- /dev/null +++ b/tests/topotests/bgp_vpnv6_per_nexthop_label/test_bgp_vpnv6_per_nexthop_label.py @@ -0,0 +1,808 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# test_bgp_vpnv6_per_nexthop_label.py +# +# Copyright 2023 6WIND S.A. +# + +""" + test_bgp_vpnv6_per_nexthop_label.py: Test the FRR BGP daemon using EBGP peering + Let us exchange VPNv6 updates between both devices + Updates from r1 will originate from the same RD, but will have separate + label values. + + +----------+ + | r11 | + |192::2:11 +---+ + | | | +----+--------+ +----------+ + +----------+ | 192::2::1 |vrf | r1 |192:168::/112 | r2 | + +-------------------+ | 1+--------------+ | + +----------+ | |VRF1|AS65500 | | AS65501 | + | r12 | | +--------------+ | VPNV4| |VPNV4 | + |192::2:12 +---+ |192:168::255:1+-+--+--------+ +----------+ + | | | + +----------+ | + | + +----------+ | + | r13 | | + |192:168:: +--------+ + | 255:13 | + +----------+ +""" + +import os +import sys +import json +from functools import partial +import pytest +import functools + +# 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 + + +pytestmark = [pytest.mark.bgpd] + +PREFIXES_R11 = ["172:31::11/128", "172:31::20/128", "172:31::111/128"] +PREFIXES_R12 = ["172:31::12/128", "172:31::15/128"] +PREFIXES_REDIST_R14 = ["172:31::14/128"] +PREFIXES_CONNECTED = ["192:168::255/112", "192:2::/64"] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + tgen.add_router("r11") + tgen.add_router("r12") + tgen.add_router("r13") + tgen.add_router("r14") + tgen.add_router("rr") + + 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["r11"]) + switch.add_link(tgen.gears["r12"]) + switch.add_link(tgen.gears["rr"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s4") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r13"]) + + switch = tgen.add_switch("s5") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r14"]) + + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + "ip link add vrf1 type vrf table 10", + "echo 100000 > /proc/sys/net/mpls/platform_labels", + "ip link set dev vrf1 up", + "ip link set dev {0}-eth1 master vrf1", + "echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input", + ] + cmds_list_plus = [ + "ip link set dev {0}-eth2 master vrf1", + ] + + for cmd in cmds_list: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list_plus: + input = cmd.format("r1") + logger.info("input: " + cmd) + output = tgen.net["r1"].cmd(cmd.format("r1")) + logger.info("output: " + output) + + for cmd in cmds_list: + input = cmd.format("r2") + logger.info("input: " + cmd) + output = tgen.net["r2"].cmd(cmd.format("r2")) + logger.info("output: " + output) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_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 bgp_vpnv6_table_check(router, group, label_list=None, label_value_expected=None): + """ + Dump and check that vpnv6 entries have the same MPLS label value + * 'router': the router to check + * 'group': the list of prefixes to check. a single label value for the group has to be found + * 'label_list': check that the label values are not present in the vpnv6 entries + * that list is updated with the present label value + * 'label_value_expected': check that the mpls label read is the same as that value + """ + + stored_label_inited = False + for prefix in group: + dump = router.vtysh_cmd("show bgp ipv6 vpn {} json".format(prefix), isjson=True) + for rd, pathes in dump.items(): + for path in pathes["paths"]: + assert ( + "remoteLabel" in path.keys() + ), "{0}, {1}, remoteLabel not present".format(router.name, prefix) + logger.info( + "{0}, {1}, label value is {2}".format( + router.name, prefix, path["remoteLabel"] + ) + ) + if stored_label_inited: + assert ( + path["remoteLabel"] == stored_label + ), "{0}, {1}, label value not expected one (expected {2}, observed {3}".format( + router.name, prefix, stored_label, path["remoteLabel"] + ) + else: + stored_label = path["remoteLabel"] + stored_label_inited = True + if label_list is not None: + assert ( + stored_label not in label_list + ), "{0}, {1}, label already detected in a previous prefix".format( + router.name, prefix + ) + label_list.add(stored_label) + + if label_value_expected: + assert ( + path["remoteLabel"] == label_value_expected + ), "{0}, {1}, label value not expected (expected {2}, observed {3}".format( + router.name, prefix, label_value_expected, path["remoteLabel"] + ) + + +def bgp_vpnv6_table_check_all(router, label_list=None, same=False): + """ + Dump and check that vpnv6 entries are correctly configured with specific label values + * 'router': the router to check + * 'label_list': check that the label values are not present in the vpnv6 entries + * that list is updated with the present label value found. + * 'same': by default, set to False. Addresses groups are classified by addresses. + * if set to True, all entries of all groups should have a unique label value + """ + if same: + bgp_vpnv6_table_check( + router, + group=PREFIXES_R11 + + PREFIXES_R12 + + PREFIXES_REDIST_R14 + + PREFIXES_CONNECTED, + label_list=label_list, + ) + else: + for group in ( + PREFIXES_R11, + PREFIXES_R12, + PREFIXES_REDIST_R14, + PREFIXES_CONNECTED, + ): + bgp_vpnv6_table_check(router, group=group, label_list=label_list) + + +def check_show_mpls_table(router, blacklist=None, label_list=None, whitelist=None): + nexthop_list = [] + if blacklist: + nexthop_list.append(blacklist) + + dump = router.vtysh_cmd("show mpls table json", isjson=True) + for in_label, label_info in dump.items(): + if label_list is not None: + label_list.add(in_label) + for nh in label_info["nexthops"]: + if "installed" not in nh.keys(): + return "{} {} is not installed yet on {}".format(in_label, label_info, router.name) + if nh["installed"] != True or nh["type"] != "BGP": + return "{}, show mpls table, nexthop is not installed".format( + router.name + ) + if "nexthop" in nh.keys(): + if nh["nexthop"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop address".format( + router.name + ) + nexthop_list.append(nh["nexthop"]) + elif "interface" in nh.keys(): + if nh["interface"] in nexthop_list: + return "{}, show mpls table, duplicated or blacklisted nexthop interface".format( + router.name + ) + nexthop_list.append(nh["interface"]) + else: + return "{}, show mpls table, entry with neither nexthop nor interface".format( + router.name + ) + + if whitelist: + for entry in whitelist: + if entry not in nexthop_list: + return "{}, show mpls table, entry with nexthop {} not present in nexthop list".format( + router.name, entry + ) + return None + + +def mpls_table_check(router, blacklist=None, label_list=None, whitelist=None): + """ + Dump and check 'show mpls table json' output. An assert is triggered in case test fails + * 'router': the router to check + * 'blacklist': the list of nexthops (IP or interface) that should not be on output + * 'label_list': the list of labels that should be in inLabel value + * 'whitelist': the list of nexthops (IP or interface) that should be on output + """ + logger.info("Checking MPLS labels on {}".format(router.name)) + logger.info("Checking MPLS labels on {}".format(router.name)) + # Check r2 removed 172.31.0.30 vpnv4 update + test_func = functools.partial( + check_show_mpls_table, router, blacklist, label_list, whitelist + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "{}, MPLS labels check fail: {}".format(router.name, result) + + +def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + if label: + expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}} + else: + expected = {rd: {"prefix": prefix}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def check_show_bgp_vpn_prefix_found(router, ipversion, prefix, rd): + output = json.loads( + router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix)) + ) + expected = {rd: {"prefix": prefix}} + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_found(router, inlabel, interface): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = { + "inLabel": inlabel, + "installed": True, + "nexthops": [{"interface": interface}], + } + return topotest.json_cmp(output, expected) + + +def check_show_mpls_table_entry_label_not_found(router, inlabel): + output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel))) + expected = {"inlabel": inlabel, "installed": True} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + +def mpls_entry_get_interface(router, label): + """ + Assert that the label is in MPLS table + Assert an outgoing interface is programmed + return the outgoing interface + """ + outgoing_interface = None + + logger.info("Checking MPLS labels on {}".format(router.name)) + dump = router.vtysh_cmd("show mpls table {} json".format(label), isjson=True) + assert dump, "{}, show mpls table, inLabel {} not found".format(router.name, label) + + for nh in dump["nexthops"]: + assert ( + "interface" in nh.keys() + ), "{}, show mpls table, nexthop interface not present for MPLS entry {}".format( + router.name, label + ) + + outgoing_interface = nh["interface"] + + return outgoing_interface + + +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 BGP IPv6 routing tables on VRF1 of r1 + logger.info("Checking BGP IPv6 routes for convergence on r1 VRF1") + router = tgen.gears["r1"] + json_file = "{}/{}/bgp_ipv6_routes_vrf1.json".format(CWD, router.name) + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv6 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + logger.info("Checking BGP VPNv6 routes for convergence on r2") + router = tgen.gears["r2"] + json_file = "{}/{}/bgp_vpnv6_routes.json".format(CWD, router.name) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp ipv6 vpn json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP labels received on r2 + logger.info("Checking BGP VPNv6 labels on r2") + label_list = set() + bgp_vpnv6_table_check_all(tgen.gears["r2"], label_list) + + # Check MPLS labels received on r1 + mpls_table_check(tgen.gears["r1"], label_list) + + +def test_flapping_bgp_vrf_down(): + """ + Turn down a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Unpeering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nno neighbor 192:2::100\n", + isjson=False, + ) + + def _bgp_prefix_not_found(router, vrf, ipversion, prefix): + output = json.loads( + router.vtysh_cmd( + "show bgp vrf {} {} {} json".format(vrf, ipversion, prefix) + ) + ) + expected = {"prefix": prefix} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + # Check prefix from r11 is not present + test_func = functools.partial( + _bgp_prefix_not_found, tgen.gears["r1"], "vrf1", "ipv6", "172:31::11/128" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r1, prefix 172:31::11/128 from r11 did not disappear. r11 still connected to rr ?" + + # Check BGP updated received on r2 are not from r11 + logger.info("Checking BGP VPNv6 labels on r2") + for entry in PREFIXES_R11: + dump = tgen.gears["r2"].vtysh_cmd( + "show bgp ipv6 vpn {} json".format(entry), isjson=True + ) + for rd in dump: + assert False, "r2, {}, route distinguisher {} present".format(entry, rd) + + mpls_table_check(tgen.gears["r1"], blacklist=["192:2::11"]) + + +def test_flapping_bgp_vrf_up(): + """ + Turn up a remote BGP session + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + logger.info("Peering BGP on r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\nneighbor 192:2::100 remote-as 65500\n", + isjson=False, + ) + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp 65500\naddress-family ipv6 unicast\nneighbor 192:2::100 activate\n", + isjson=False, + ) + + # Check r2 gets prefix 172:31::11/128 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv6", + "172:31::11/128", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, prefix 172:31::11/128 from r11 not present. r11 still disconnected from rr ?" + bgp_vpnv6_table_check_all(tgen.gears["r2"]) + + +def test_recursive_route(): + """ + Test static recursive route redistributed over BGP + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nipv6 route 172:31::30/128 172:31::20\n", + isjson=False, + ) + logger.info("Checking BGP VPNv6 labels on r2") + # that route should be sent along with label for 192.0.2.11 + + def _prefix30_not_found(router): + output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json")) + expected = {"444:1": {"prefix": "172:31::30/128"}} + ret = topotest.json_cmp(output, expected) + if ret is None: + return "not good" + return None + + def _prefix30_found(router): + output = json.loads(router.vtysh_cmd("show bgp ipv6 vpn 172:31::30/128 json")) + expected = {"444:1": {"prefix": "172:31::30/128"}} + return topotest.json_cmp(output, expected) + + # Check r2 received vpnv6 update with 172:31::30 + test_func = functools.partial(_prefix30_found, tgen.gears["r2"]) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, VPNv6 update 172:31::30 not found" + + # that route should be sent along with label for 192::2:11 + bgp_vpnv6_table_check( + tgen.gears["r2"], + group=PREFIXES_R11 + ["172:31::30/128"], + ) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + logger.info("Dumping nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 nexthop detail", isjson=False) + + logger.info("Disabling recursive static route") + tgen.gears["r1"].vtysh_cmd( + "configure terminal\nvrf vrf1\nno ipv6 route 172:31::30/128 172:31::20\n", + isjson=False, + ) + + # Check r2 removed 172:31::30 vpnv6 update + test_func = functools.partial(_prefix30_not_found, tgen.gears["r2"]) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, VPNv6 update 172:31::30 still present" + + +def test_prefix_changes_interface(): + """ + Test BGP update for a given prefix learnt on different interface + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Enabling a 172:31::50/128 prefix for r11") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128", + isjson=False, + ) + + # Check r2 received vpnv6 update with 172:31::50 + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv6", + "172:31::50/128", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, VPNv6 update 172:31::50 not found" + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + label_list = set() + bgp_vpnv6_table_check( + tgen.gears["r2"], + group=PREFIXES_R11 + ["172:31::50/128"], + label_list=label_list, + ) + + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r11 found" + + oldlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(oldlabel)) + old_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], oldlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + oldlabel, old_outgoing_interface + ) + ) + + logger.info("Moving the 172:31::50/128 prefix from r11 to r13") + tgen.gears["r11"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128", + isjson=False, + ) + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nnetwork 172:31::50/128", + isjson=False, + ) + + # Check r2 removed 172:31::50 vpnv6 update with old label + test_func = functools.partial( + check_show_bgp_vpn_prefix_not_found, + tgen.gears["r2"], + "ipv6", + "172:31::50/128", + "444:1", + label=oldlabel, + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert ( + success + ), "r2, vpnv6 update 172:31::50 with old label {0} still present".format(oldlabel) + + # diagnostic + logger.info("Dumping label nexthop table") + tgen.gears["r1"].vtysh_cmd("show bgp vrf vrf1 label-nexthop detail", isjson=False) + + # Check r2 received new 172:31::50 vpnv6 update + test_func = functools.partial( + check_show_bgp_vpn_prefix_found, + tgen.gears["r2"], + "ipv6", + "172:31::50/128", + "444:1", + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r2, vpnv6 update 172:31::50 not found" + + label_list = set() + bgp_vpnv6_table_check( + tgen.gears["r2"], + group=["172:31::13/128", "172:31::50/128"], + label_list=label_list, + ) + assert ( + len(label_list) == 1 + ), "Multiple Label values found for updates from r13 found" + + newlabel = label_list.pop() + logger.info("r1, getting the outgoing interface used by label {}".format(newlabel)) + new_outgoing_interface = mpls_entry_get_interface(tgen.gears["r1"], newlabel) + logger.info( + "r1, outgoing interface used by label {} is {}".format( + newlabel, new_outgoing_interface + ) + ) + if old_outgoing_interface == new_outgoing_interface: + assert 0, "r1, outgoing interface did not change whereas BGP update moved" + + logger.info("Restoring state by removing the 172:31::50/128 prefix from r13") + tgen.gears["r13"].vtysh_cmd( + "configure terminal\nrouter bgp\naddress-family ipv6 unicast\nno network 172:31::50/128", + isjson=False, + ) + + +def test_changing_default_label_value(): + """ + Change the MPLS default value + Check that r1 VPNv6 entries have the 222 label value + Check that MPLS entry with old label value is no more present + Check that MPLS entry for local traffic has inLabel set to 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears["r1"] + + # counting the number of labels used in the VPNv6 table + label_list = set() + logger.info("r1, VPNv6 table, check the number of labels used before modification") + bgp_vpnv6_table_check_all(router, label_list) + old_len = len(label_list) + assert ( + old_len != 1 + ), "r1, number of labels used should be greater than 1, oberved {} ".format(old_len) + + logger.info("r1, vrf1, changing the default MPLS label value to export to 222") + router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nlabel vpn export 222\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 222 has vrf1 interface" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_found, router, 222, "vrf1" + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 222 not found" + + # check label repartition is ok + logger.info("r1, VPNv6 table, check the number of labels used after modification") + label_list = set() + bgp_vpnv6_table_check_all(router, label_list) + new_len = len(label_list) + assert ( + old_len == new_len + ), "r1, number of labels after modification differ from previous, observed {}, expected {} ".format( + new_len, old_len + ) + + logger.info( + "r1, VPNv6 table, check that prefixes that were using the vrf label have refreshed the label value to 222" + ) + bgp_vpnv6_table_check(router, group=PREFIXES_CONNECTED, label_value_expected=222) + + +def test_unconfigure_allocation_mode_nexthop(): + """ + Test unconfiguring allocation mode per nexthop + Check on r2 that new MPLS label values have been propagated + Check that show mpls table has no entry with label 17 (previously used) + Check that all VPN updates on r1 should have label value moved to 222 + Check that show mpls table will only have 222 label value + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Unconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + dump = router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nno label vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check r1 updated the MPLS entry with the 222 label value + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is not present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv6 routes from r1 + logger.info("Checking VPNv6 routes on r1") + label_list = set() + bgp_vpnv6_table_check_all(router, label_list=label_list, same=True) + assert len(label_list) == 1, "r1, multiple Label values found for VPNv6 updates" + + new_label = label_list.pop() + assert ( + new_label == 222 + ), "r1, wrong label value in VPNv6 table, expected 222, observed {}".format( + new_label + ) + + # Check mpls table with 222 value + logger.info("Checking MPLS values on show mpls table of r1") + label_list = set() + label_list.add(222) + mpls_table_check(router, label_list=label_list) + + +def test_reconfigure_allocation_mode_nexthop(): + """ + Test re-configuring allocation mode per nexthop + Check that show mpls table has no entry with label 17 + Check that all VPN updates on r1 should have multiple label values and not only 222 + Check that show mpls table will have multiple label values and not only 222 + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Reconfiguring allocation mode per nexthop") + router = tgen.gears["r1"] + dump = router.vtysh_cmd( + "configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv6 unicast\nlabel vpn export allocation-mode per-nexthop\n", + isjson=False, + ) + + # Check that show mpls table has no entry with label 17 + logger.info( + "r1, mpls table, check that MPLS entry with inLabel set to 17 is present" + ) + test_func = functools.partial( + check_show_mpls_table_entry_label_not_found, router, 17 + ) + success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert success, "r1, mpls entry with label 17 still present" + + # Check vpnv6 routes from r1 + logger.info("Checking VPNv6 routes on r1") + label_list = set() + bgp_vpnv6_table_check_all(router, label_list=label_list) + assert len(label_list) != 1, "r1, only 1 label values found for VPNv6 updates" + + # Check mpls table with all values + logger.info("Checking MPLS values on show mpls table of r1") + mpls_table_check(router, label_list=label_list) + + +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_md5_peering/__init__.py b/tests/topotests/bgp_vrf_md5_peering/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_vrf_md5_peering/exabgp.env b/tests/topotests/bgp_vrf_md5_peering/exabgp.env new file mode 100644 index 000000000000..28e642360a39 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/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' +##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_vrf_md5_peering/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg new file mode 100644 index 000000000000..326051390311 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/peer1/exabgp.cfg @@ -0,0 +1,13 @@ +neighbor 10.0.0.1 { + router-id 10.0.0.2; + local-address 10.0.0.2; + local-as 65001; + peer-as 65534; + md5 test123; + + static { + route 192.168.100.1/32 { + next-hop 10.0.0.2; + } + } +} diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf new file mode 100644 index 000000000000..9f2ee19357e6 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/r1/bgpd.conf @@ -0,0 +1,11 @@ +! +!debug bgp neighbor +! +router bgp 65534 vrf public + bgp router-id 10.0.0.1 + no bgp ebgp-requires-policy + neighbor 10.0.0.2 remote-as external + neighbor 10.0.0.2 timers 3 10 + neighbor 10.0.0.2 timers connect 1 + neighbor 10.0.0.2 password test123 +! diff --git a/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf b/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf new file mode 100644 index 000000000000..0c183ae785a4 --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/r1/zebra.conf @@ -0,0 +1,6 @@ +! +interface r1-eth0 vrf public + ip address 10.0.0.1/24 +! +ip forwarding +! diff --git a/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py b/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py new file mode 100644 index 000000000000..eefe586d7bfe --- /dev/null +++ b/tests/topotests/bgp_vrf_md5_peering/test_bgp_vrf_md5_peering.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# + +""" +Test if BGP MD5 basic authentication works per-VRF. +""" + +import os +import sys +import json +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 + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + r1 = tgen.add_router("r1") + peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1") + + switch = tgen.add_switch("s1") + switch.add_link(r1) + switch.add_link(peer1) + + +def setup_module(mod): + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + r1 = tgen.gears["r1"] + r1.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")) + r1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf")) + r1.start() + + peer = tgen.gears["peer1"] + peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env")) + + # VRF 'public' + r1.cmd_raises("ip link add public type vrf table 1001") + r1.cmd_raises("ip link set up dev public") + r1.cmd_raises("ip link set r1-eth0 master public") + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_bgp_vrf_md5_peering(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + def _bgp_converge(): + output = json.loads( + tgen.gears["r1"].vtysh_cmd("show ip bgp vrf public neighbor 10.0.0.2 json") + ) + expected = { + "10.0.0.2": { + "bgpState": "Established", + "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}}, + } + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_bgp_converge) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + + assert result is None, "Can't peer with md5 per-VRF" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py index fec9d485b5d3..5c1b97262c95 100644 --- a/tests/topotests/config_timing/test_config_timing.py +++ b/tests/topotests/config_timing/test_config_timing.py @@ -24,7 +24,7 @@ import os import sys import pytest - +from lib import topotest CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) @@ -109,7 +109,9 @@ def do_config( router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype) ) with open(config_file, "w") as f: - for i, net in enumerate(get_ip_networks(super_prefix, base_count, count)): + for i, net in enumerate( + get_ip_networks(super_prefix, base_count, count) + ): if i in bad_indices: if add: f.write("ip route {} {} bad_input\n".format(net, via)) @@ -131,7 +133,7 @@ def do_config( delta = (datetime.datetime.now() - tstamp).total_seconds() tot_delta += delta - router.logger.info( + router.logger.debug( "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format( load_command, output, delta ) @@ -148,10 +150,9 @@ def do_config( return tot_delta - # Number of static routes router = tgen.gears["r1"] - output = router.run("vtysh -h | grep address-sanitizer") + output = router.net.cmd_legacy("vtysh -h | grep address-sanitizer", warn=False) if output == "": logger.info("No Address Sanitizer, generating 10000 routes") prefix_count = 10000 @@ -164,20 +165,49 @@ def do_config( [u"2100:1111:2220::/44", u"2100:3333:4440::/44"], ] + # This apparently needed to allow for various mgmtd/staticd/zebra connections to form + # which then SLOWS execution down. If we don't include this value then the + # initial, baseline establishing, time is 2 time faster (e.g., 5s instead of 10s), + # but all later runs are slower and fail. + # + # This should be done differently based on actual facts. + topotest.sleep(5) + bad_indices = [] for ipv6 in [False, True]: base_delta = do_config( - prefix_count, prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0] + prefix_count, + prefix_count, + bad_indices, + 0, + 0, + True, + ipv6, + prefix_base[ipv6][0], ) # Another set of same number of prefixes do_config( - prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][1] + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][1], ) # Duplicate config do_config( - prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0] + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][0], ) # Remove 1/2 of duplicate @@ -194,15 +224,36 @@ def do_config( # Add all back in so 1/2 replicate 1/2 new do_config( - prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0] + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + True, + ipv6, + prefix_base[ipv6][0], ) # remove all delta = do_config( - prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][0] + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][0], ) delta += do_config( - prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][1] + prefix_count, + prefix_count, + bad_indices, + base_delta, + 3, + False, + ipv6, + prefix_base[ipv6][1], ) diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py index 2a57f6c26e40..f829ed2d12ae 100755 --- a/tests/topotests/conftest.py +++ b/tests/topotests/conftest.py @@ -1,27 +1,70 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- """ Topotest conftest.py file. """ # pylint: disable=consider-using-f-string +import contextlib import glob +import logging import os -import pdb import re +import resource import subprocess import sys import time -import resource +from pathlib import Path -import pytest import lib.fixtures -from lib import topolog -from lib.micronet import Commander, proc_error -from lib.micronet_cli import cli -from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous +import pytest +from lib.micronet_compat import Mininet from lib.topogen import diagnose_env, get_topogen -from lib.topolog import logger -from lib.topotest import g_extra_config as topotest_extra_config +from lib.topolog import get_test_logdir, logger from lib.topotest import json_cmp_result +from munet import cli +from munet.base import Commander, proc_error +from munet.cleanup import cleanup_current, cleanup_previous +from munet.config import ConfigOptionsProxy +from munet.testing.util import pause_test + +from lib import topolog, topotest + +try: + # Used by munet native tests + from munet.testing.fixtures import event_loop, unet # pylint: disable=all # noqa + + @pytest.fixture(scope="module") + def rundir_module(pytestconfig): + d = os.path.join(pytestconfig.option.rundir, get_test_logdir()) + logging.debug("rundir_module: test module rundir %s", d) + return d + +except (AttributeError, ImportError): + pass + + +# Remove this and use munet version when we move to pytest_asyncio +@contextlib.contextmanager +def chdir(ndir, desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +@contextlib.contextmanager +def log_handler(basename, logpath): + topolog.logstart(basename, logpath) + try: + yield + finally: + topolog.logfinish(basename, logpath) def pytest_addoption(parser): @@ -59,12 +102,34 @@ def pytest_addoption(parser): help="Comma-separated list of routers to spawn gdb on, or 'all'", ) + parser.addoption( + "--logd", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Tail-F the DAEMON log file on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( + "--memleaks", + action="store_true", + help="Report memstat results as errors", + ) + parser.addoption( "--pause", action="store_true", help="Pause after each test", ) + parser.addoption( + "--pause-at-end", + action="store_true", + help="Pause before taking munet down", + ) + parser.addoption( "--pause-on-error", action="store_true", @@ -78,6 +143,30 @@ def pytest_addoption(parser): help="Do not pause after (disables default when --shell or -vtysh given)", ) + parser.addoption( + "--pcap", + default="", + metavar="NET[,NET...]", + help="Comma-separated list of networks to capture packets on, or 'all'", + ) + + parser.addoption( + "--perf", + action="append", + metavar="DAEMON[,ROUTER[,...]", + help=( + "Collect performance data from given DAEMON on all or a subset of ROUTERs." + " Option can be given multiple times." + ), + ) + + parser.addoption( + "--perf-options", + metavar="OPTS", + default="-g", + help="Options to pass to `perf record`.", + ) + rundir_help = "directory for running in and log files" parser.addini("rundir", rundir_help, default="/tmp/topotests") parser.addoption("--rundir", metavar="DIR", help=rundir_help) @@ -132,8 +221,8 @@ def pytest_addoption(parser): ) -def check_for_memleaks(): - assert topotest_extra_config["valgrind_memleaks"] +def check_for_valgrind_memleaks(): + assert topotest.g_pytest_config.option.valgrind_memleaks leaks = [] tgen = get_topogen() # pylint: disable=redefined-outer-name @@ -175,32 +264,133 @@ def check_for_memleaks(): pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons)) +def check_for_memleaks(): + leaks = [] + tgen = get_topogen() # pylint: disable=redefined-outer-name + latest = [] + existing = [] + if tgen is not None: + logdir = tgen.logdir + if hasattr(tgen, "memstat_existing_files"): + existing = tgen.memstat_existing_files + latest = glob.glob(os.path.join(logdir, "*/*.err")) + + daemons = [] + for vfile in latest: + if vfile in existing: + continue + with open(vfile, encoding="ascii") as vf: + vfcontent = vf.read() + num = vfcontent.count("memstats:") + if num: + existing.append(vfile) # have summary don't check again + emsg = "{} types in {}".format(num, vfile) + leaks.append(emsg) + daemon = re.match(r".*test[a-z_A-Z0-9\+]*/(.*)\.err", vfile).group(1) + daemons.append("{}({})".format(daemon, num)) + + if tgen is not None: + tgen.memstat_existing_files = existing + + if leaks: + logger.error("memleaks found:\n\t%s", "\n\t".join(leaks)) + pytest.fail("memleaks found for daemons: " + " ".join(daemons)) + + +def check_for_core_dumps(): + dumps = [] + tgen = get_topogen() # pylint: disable=redefined-outer-name + latest = [] + + if tgen is not None: + logdir = tgen.logdir + cores = glob.glob(os.path.join(logdir, "*/*.dmp")) + + if cores: + logger.error("Cores found:\n\t%s", "\n\t".join(cores)) + pytest.fail("Core files found") + + +def check_for_backtraces(): + backtraces = [] + tgen = get_topogen() # pylint: disable=redefined-outer-name + latest = [] + existing = [] + if tgen is not None: + logdir = tgen.logdir + if hasattr(tgen, "backtraces_existing_files"): + existing = tgen.backtraces_existing_files + latest = glob.glob(os.path.join(logdir, "*/*.log")) + + daemons = [] + for vfile in latest: + if vfile in existing: + continue + with open(vfile, encoding="ascii") as vf: + vfcontent = vf.read() + backtrace = vfcontent.count("Backtrace:") + if backtrace: + existing.append(vfile) # have backtrace don't check again + emsg = "Backtrace found in {}, failing test".format(vfile) + backtraces.append(emsg) + + if tgen is not None: + tgen.backtrace_existing_files = existing + + if backtraces: + logger.error("Backtraces found in test suite, erroring") + logger.error(backtraces) + pytest.fail("Backtraces found") + + +@pytest.fixture(autouse=True, scope="module") +def module_autouse(request): + basename = get_test_logdir(request.node.nodeid, True) + logdir = Path(topotest.g_pytest_config.option.rundir) / basename + logpath = logdir / "exec.log" + + subprocess.check_call("mkdir -p -m 1777 {}".format(logdir), shell=True) + + with log_handler(basename, logpath): + sdir = os.path.dirname(os.path.realpath(request.fspath)) + with chdir(sdir, "module autouse fixture"): + yield + + @pytest.fixture(autouse=True, scope="module") def module_check_memtest(request): - del request # disable unused warning yield - if topotest_extra_config["valgrind_memleaks"]: + if request.config.option.valgrind_memleaks: + if get_topogen() is not None: + check_for_valgrind_memleaks() + if request.config.option.memleaks: if get_topogen() is not None: check_for_memleaks() + check_for_backtraces() + check_for_core_dumps() -def pytest_runtest_logstart(nodeid, location): - # location is (filename, lineno, testname) - topolog.logstart(nodeid, location, topotest_extra_config["rundir"]) - - -def pytest_runtest_logfinish(nodeid, location): - # location is (filename, lineno, testname) - topolog.logfinish(nodeid, location) +# +# Disable per test function logging as FRR CI system can't handle it. +# +# @pytest.fixture(autouse=True, scope="function") +# def function_autouse(request): +# # For tests we actually use the logdir name as the logfile base +# logbase = get_test_logdir(nodeid=request.node.nodeid, module=False) +# logbase = os.path.join(topotest.g_pytest_config.option.rundir, logbase) +# logpath = Path(logbase) +# path = Path(f"{logpath.parent}/exec-{logpath.name}.log") +# subprocess.check_call("mkdir -p -m 1777 {}".format(logpath.parent), shell=True) +# with log_handler(request.node.nodeid, path): +# yield @pytest.hookimpl(hookwrapper=True) def pytest_runtest_call(item: pytest.Item) -> None: "Hook the function that is called to execute the test." - del item # disable unused warning # For topology only run the CLI then exit - if topotest_extra_config["topology_only"]: + if item.config.option.topology_only: get_topogen().cli() pytest.exit("exiting after --topology-only") @@ -208,7 +398,9 @@ def pytest_runtest_call(item: pytest.Item) -> None: yield # Check for leaks if requested - if topotest_extra_config["valgrind_memleaks"]: + if item.config.option.valgrind_memleaks: + check_for_valgrind_memleaks() + if item.config.option.memleaks: check_for_memleaks() @@ -231,6 +423,7 @@ def pytest_configure(config): """ Assert that the environment is correctly configured, and get extra config. """ + topotest.g_pytest_config = ConfigOptionsProxy(config) if config.getoption("--collect-only"): return @@ -240,8 +433,10 @@ def pytest_configure(config): os.environ["PYTEST_TOPOTEST_WORKER"] = "" is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no" is_worker = False + wname = "" else: - os.environ["PYTEST_TOPOTEST_WORKER"] = os.environ["PYTEST_XDIST_WORKER"] + wname = os.environ["PYTEST_XDIST_WORKER"] + os.environ["PYTEST_TOPOTEST_WORKER"] = wname is_xdist = True is_worker = True @@ -252,11 +447,13 @@ def pytest_configure(config): # Set some defaults for the pytest.ini [pytest] section # --------------------------------------------------- - rundir = config.getoption("--rundir") + rundir = config.option.rundir if not rundir: rundir = config.getini("rundir") if not rundir: rundir = "/tmp/topotests" + config.option.rundir = rundir + if not config.getoption("--junitxml"): config.option.xmlpath = os.path.join(rundir, "topotests.xml") xmlpath = config.option.xmlpath @@ -269,12 +466,20 @@ def pytest_configure(config): mv_path = commander.get_exec_path("mv") commander.cmd_status([mv_path, xmlpath, xmlpath + suffix]) - topotest_extra_config["rundir"] = rundir - # Set the log_file (exec) to inside the rundir if not specified if not config.getoption("--log-file") and not config.getini("log_file"): config.option.log_file = os.path.join(rundir, "exec.log") + # Handle pytest-xdist each worker get's it's own top level log file + # `exec-worker-N.log` + if wname: + wname = wname.replace("gw", "worker-") + cpath = Path(config.option.log_file).absolute() + config.option.log_file = f"{cpath.parent}/{cpath.stem}-{wname}{cpath.suffix}" + elif is_xdist: + cpath = Path(config.option.log_file).absolute() + config.option.log_file = f"{cpath.parent}/{cpath.stem}-xdist{cpath.suffix}" + # Turn on live logging if user specified verbose and the config has a CLI level set if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"): if config.getoption("--log-cli-level", None) is None: @@ -300,77 +505,43 @@ def assert_feature_windows(b, feature): elif b and not is_xdist and not have_windows: pytest.exit("{} use requires byobu/TMUX/SCREEN/XTerm".format(feature)) - # --------------------------------------- - # Record our options in global dictionary - # --------------------------------------- - - topotest_extra_config["rundir"] = rundir - - asan_abort = config.getoption("--asan-abort") - topotest_extra_config["asan_abort"] = asan_abort - - gdb_routers = config.getoption("--gdb-routers") - gdb_routers = gdb_routers.split(",") if gdb_routers else [] - topotest_extra_config["gdb_routers"] = gdb_routers - - gdb_daemons = config.getoption("--gdb-daemons") - gdb_daemons = gdb_daemons.split(",") if gdb_daemons else [] - topotest_extra_config["gdb_daemons"] = gdb_daemons - assert_feature_windows(gdb_routers or gdb_daemons, "GDB") - - gdb_breakpoints = config.getoption("--gdb-breakpoints") - gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else [] - topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints - - cli_on_error = config.getoption("--cli-on-error") - topotest_extra_config["cli_on_error"] = cli_on_error - assert_feature_windows(cli_on_error, "--cli-on-error") - - shell = config.getoption("--shell") - topotest_extra_config["shell"] = shell.split(",") if shell else [] - assert_feature_windows(shell, "--shell") - - strace = config.getoption("--strace-daemons") - topotest_extra_config["strace_daemons"] = strace.split(",") if strace else [] - - shell_on_error = config.getoption("--shell-on-error") - topotest_extra_config["shell_on_error"] = shell_on_error - assert_feature_windows(shell_on_error, "--shell-on-error") - - topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra") - topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks") - - vtysh = config.getoption("--vtysh") - topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else [] - assert_feature_windows(vtysh, "--vtysh") - - vtysh_on_error = config.getoption("--vtysh-on-error") - topotest_extra_config["vtysh_on_error"] = vtysh_on_error - assert_feature_windows(vtysh_on_error, "--vtysh-on-error") - - pause_on_error = vtysh or shell or config.getoption("--pause-on-error") - if config.getoption("--no-pause-on-error"): - pause_on_error = False - - topotest_extra_config["pause_on_error"] = pause_on_error - assert_feature_windows(pause_on_error, "--pause-on-error") - - pause = config.getoption("--pause") - topotest_extra_config["pause"] = pause - assert_feature_windows(pause, "--pause") + # + # Check for window capability if given options that require window + # + assert_feature_windows(config.option.gdb_routers, "GDB") + assert_feature_windows(config.option.gdb_daemons, "GDB") + assert_feature_windows(config.option.cli_on_error, "--cli-on-error") + assert_feature_windows(config.option.shell, "--shell") + assert_feature_windows(config.option.shell_on_error, "--shell-on-error") + assert_feature_windows(config.option.vtysh, "--vtysh") + assert_feature_windows(config.option.vtysh_on_error, "--vtysh-on-error") + + if config.option.topology_only and is_xdist: + pytest.exit("Cannot use --topology-only with distributed test mode") - topology_only = config.getoption("--topology-only") - if topology_only and is_xdist: pytest.exit("Cannot use --topology-only with distributed test mode") - topotest_extra_config["topology_only"] = topology_only # Check environment now that we have config if not diagnose_env(rundir): pytest.exit("environment has errors, please read the logs in %s" % rundir) + # slave TOPOTESTS_CHECK_MEMLEAK to memleaks flag + if config.option.memleaks: + if "TOPOTESTS_CHECK_MEMLEAK" not in os.environ: + os.environ["TOPOTESTS_CHECK_MEMLEAK"] = "/dev/null" + else: + if "TOPOTESTS_CHECK_MEMLEAK" in os.environ: + del os.environ["TOPOTESTS_CHECK_MEMLEAK"] + if "TOPOTESTS_CHECK_STDERR" in os.environ: + del os.environ["TOPOTESTS_CHECK_STDERR"] + @pytest.fixture(autouse=True, scope="session") def setup_session_auto(): + # Aligns logs nicely + logging.addLevelName(logging.WARNING, " WARN") + logging.addLevelName(logging.INFO, " INFO") + if "PYTEST_TOPOTEST_WORKER" not in os.environ: is_worker = False elif not os.environ["PYTEST_TOPOTEST_WORKER"]: @@ -396,12 +567,7 @@ def pytest_runtest_setup(item): def pytest_runtest_makereport(item, call): "Log all assert messages to default logger with error level" - # Nothing happened - if call.when == "call": - pause = topotest_extra_config["pause"] - else: - pause = False - + pause = bool(item.config.getoption("--pause")) title = "unset" if call.excinfo is None: @@ -433,10 +599,7 @@ def pytest_runtest_makereport(item, call): # We want to pause, if requested, on any error not just test cases # (e.g., call.when == "setup") if not pause: - pause = ( - topotest_extra_config["pause_on_error"] - or topotest_extra_config["pause"] - ) + pause = item.config.option.pause_on_error or item.config.option.pause # (topogen) Set topology error to avoid advancing in the test. tgen = get_topogen() # pylint: disable=redefined-outer-name @@ -448,9 +611,9 @@ def pytest_runtest_makereport(item, call): isatty = sys.stdout.isatty() error_cmd = None - if error and topotest_extra_config["vtysh_on_error"]: + if error and item.config.option.vtysh_on_error: error_cmd = commander.get_exec_path(["vtysh"]) - elif error and topotest_extra_config["shell_on_error"]: + elif error and item.config.option.shell_on_error: error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"])) if error_cmd: @@ -502,31 +665,16 @@ def pytest_runtest_makereport(item, call): if p.wait(): logger.warning("xterm proc failed: %s:", proc_error(p, o, e)) - if error and topotest_extra_config["cli_on_error"]: + if error and item.config.option.cli_on_error: # Really would like something better than using this global here. # Not all tests use topogen though so get_topogen() won't work. if Mininet.g_mnet_inst: - cli(Mininet.g_mnet_inst, title=title, background=False) + cli.cli(Mininet.g_mnet_inst, title=title, background=False) else: logger.error("Could not launch CLI b/c no mininet exists yet") - while pause and isatty: - try: - user = raw_input( - 'PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ' - ) - except NameError: - user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ') - user = user.strip() - - if user == "cli": - cli(Mininet.g_mnet_inst) - elif user == "pdb": - pdb.set_trace() # pylint: disable=forgotten-debug-statement - elif user: - print('Unrecognized input: "%s"' % user) - else: - break + if pause and isatty: + pause_test() # diff --git a/tests/topotests/cspf_topo1/reference/sharp-ted.json b/tests/topotests/cspf_topo1/reference/sharp-ted.json index da240e87a35b..359b655f0122 100644 --- a/tests/topotests/cspf_topo1/reference/sharp-ted.json +++ b/tests/topotests/cspf_topo1/reference/sharp-ted.json @@ -53,7 +53,7 @@ ], "edges":[ { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -96,7 +96,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -139,7 +139,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -182,7 +182,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -226,7 +226,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -275,7 +275,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -318,7 +318,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -362,7 +362,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -405,7 +405,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -448,7 +448,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -491,7 +491,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -534,7 +534,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -578,7 +578,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -622,7 +622,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/evpn_pim_1/spine/bgp.summ.json b/tests/topotests/evpn_pim_1/spine/bgp.summ.json index 5ff4b096fd47..7f37cedb2bd7 100644 --- a/tests/topotests/evpn_pim_1/spine/bgp.summ.json +++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json @@ -8,7 +8,6 @@ "192.168.1.2":{ "remoteAs":65002, "version":4, - "tableVersion":0, "outq":0, "inq":0, "pfxRcd":3, @@ -21,7 +20,6 @@ "192.168.2.3":{ "remoteAs":65003, "version":4, - "tableVersion":0, "outq":0, "inq":0, "pfxRcd":3, diff --git a/tests/topotests/example_munet/munet.yaml b/tests/topotests/example_munet/munet.yaml new file mode 100644 index 000000000000..34e147010315 --- /dev/null +++ b/tests/topotests/example_munet/munet.yaml @@ -0,0 +1,17 @@ +version: 1 +topology: + ipv6-enable: true + networks-autonumber: true + networks: + - name: net1 + - name: net2 + nodes: + - name: r1 + kind: frr + connections: ["net1"] + - name: r2 + kind: frr + connections: ["net1", "net2"] + - name: r3 + kind: frr + connections: ["net2"] diff --git a/tests/topotests/example_munet/r1/daemons b/tests/topotests/example_munet/r1/daemons new file mode 100644 index 000000000000..a454c959236c --- /dev/null +++ b/tests/topotests/example_munet/r1/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r1/frr.conf b/tests/topotests/example_munet/r1/frr.conf new file mode 100644 index 000000000000..468bda5e0101 --- /dev/null +++ b/tests/topotests/example_munet/r1/frr.conf @@ -0,0 +1,7 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.1.1/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r1/vtysh.conf b/tests/topotests/example_munet/r1/vtysh.conf new file mode 100644 index 000000000000..f863f560f1e2 --- /dev/null +++ b/tests/topotests/example_munet/r1/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config \ No newline at end of file diff --git a/tests/topotests/example_munet/r2/daemons b/tests/topotests/example_munet/r2/daemons new file mode 100644 index 000000000000..a454c959236c --- /dev/null +++ b/tests/topotests/example_munet/r2/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r2/frr.conf b/tests/topotests/example_munet/r2/frr.conf new file mode 100644 index 000000000000..77d9892485d3 --- /dev/null +++ b/tests/topotests/example_munet/r2/frr.conf @@ -0,0 +1,10 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.1.2/24 + +interface eth1 + ip address 10.0.2.2/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r2/vtysh.conf b/tests/topotests/example_munet/r2/vtysh.conf new file mode 100644 index 000000000000..f863f560f1e2 --- /dev/null +++ b/tests/topotests/example_munet/r2/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config \ No newline at end of file diff --git a/tests/topotests/example_munet/r3/daemons b/tests/topotests/example_munet/r3/daemons new file mode 100644 index 000000000000..a454c959236c --- /dev/null +++ b/tests/topotests/example_munet/r3/daemons @@ -0,0 +1,6 @@ +zebra=1 +staticd=1 +vtysh_enable=1 +watchfrr_enable=1 +zebra_options="-d -F traditional --log=file:/var/log/frr/zebra.log" +staticd_options="-d -F traditional --log=file:/var/log/frr/staticd.log" diff --git a/tests/topotests/example_munet/r3/frr.conf b/tests/topotests/example_munet/r3/frr.conf new file mode 100644 index 000000000000..e0839e6d8a9a --- /dev/null +++ b/tests/topotests/example_munet/r3/frr.conf @@ -0,0 +1,7 @@ +log file /var/log/frr/frr.log +service integrated-vtysh-config + +interface eth0 + ip address 10.0.2.3/24 + +ip route 10.0.0.0/8 blackhole diff --git a/tests/topotests/example_munet/r3/vtysh.conf b/tests/topotests/example_munet/r3/vtysh.conf new file mode 100644 index 000000000000..f863f560f1e2 --- /dev/null +++ b/tests/topotests/example_munet/r3/vtysh.conf @@ -0,0 +1 @@ +service integrated-vtysh-config \ No newline at end of file diff --git a/tests/topotests/example_munet/test_munet.py b/tests/topotests/example_munet/test_munet.py new file mode 100644 index 000000000000..0d9599fa541d --- /dev/null +++ b/tests/topotests/example_munet/test_munet.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 23 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +async def test_native_test(unet): + o = unet.hosts["r1"].cmd_nostatus("ip addr") + print(o) diff --git a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py index 5eef879e3f29..ada8c0f5fbda 100644 --- a/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py +++ b/tests/topotests/isis_advertise_high_metrics/test_isis_advertise_high_metrics.py @@ -121,14 +121,14 @@ def _check_interface_metrics(router, expected_metrics): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_interface_metrics {router}") - isis_interface_output = router.vtysh_cmd( - "show isis interface detail json" - ) + isis_interface_output = router.vtysh_cmd("show isis interface detail json") intf_json = json.loads(isis_interface_output) for i in range(len(expected_metrics)): - metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0]["metric"] - if (metric != expected_metrics[i]): + metric = intf_json["areas"][0]["circuits"][i]["interface"]["levels"][0][ + "metric" + ] + if metric != expected_metrics[i]: intf_name = intf_json["areas"][0]["circuits"][i]["interface"]["name"] return "{} with expected metric {} on {} got {}".format( router.name, expected_metrics[i], intf_name, metric @@ -139,9 +139,7 @@ def _check_interface_metrics(router, expected_metrics): def check_interface_metrics(router, expected_metrics): "Verfiy metrics on router's isis interfaces" - assertmsg = _check_interface_metrics( - router, expected_metrics - ) + assertmsg = _check_interface_metrics(router, expected_metrics) assert assertmsg is True, assertmsg @@ -151,9 +149,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_lsp_metrics {router}") - isis_lsp_output = router.vtysh_cmd( - "show isis database detail {}".format(lsp) - ) + isis_lsp_output = router.vtysh_cmd("show isis database detail {}".format(lsp)) metrics_list = [int(i) for i in re.findall(r"Metric: (\d+)", isis_lsp_output)] if len(metrics_list) == 0: @@ -170,9 +166,7 @@ def _check_lsp_metrics(router, lsp, expected_metrics): def check_lsp_metrics(router, lsp, expected_metrics): "Verfiy metrics on router's lsp" - assertmsg = _check_lsp_metrics( - router, lsp, expected_metrics - ) + assertmsg = _check_lsp_metrics(router, lsp, expected_metrics) assert assertmsg is True, assertmsg @@ -183,14 +177,12 @@ def _check_ip_route(router, destination, expected_interface): tgen = get_topogen() router = tgen.gears[router] logger.info(f"check_ip_route {router}") - route_output = router.vtysh_cmd( - "show ip route {} json".format(destination) - ) + route_output = router.vtysh_cmd("show ip route {} json".format(destination)) route_json = json.loads(route_output) interface = route_json[destination][0]["nexthops"][0]["interfaceName"] - if (interface != expected_interface): + if interface != expected_interface: return "{} with expected route to {} got {} expected {}".format( router.name, destination, interface, expected_interface ) @@ -201,9 +193,7 @@ def _check_ip_route(router, destination, expected_interface): def check_ip_route(router, destination, expected_interface): "Verfiy IS-IS route" - assertmsg = _check_ip_route( - router, destination, expected_interface - ) + assertmsg = _check_ip_route(router, destination, expected_interface) assert assertmsg is True, assertmsg @@ -216,9 +206,7 @@ def test_isis_daemon_up(): for router in ["r1", "r2", "r3", "r4"]: r = tgen.gears[router] - daemons = r.vtysh_cmd( - "show daemons" - ) + daemons = r.vtysh_cmd("show daemons") assert "isisd" in daemons # Verify initial metric values. @@ -420,9 +408,9 @@ def test_isis_advertise_high_metrics_route(): Topology: r2 - / \ + // \\ r1 r4 - \ / + \\ // r3 Devices are configured with preferred route between r1 and r4: diff --git a/tests/topotests/isis_snmp/r5/ldpdconf b/tests/topotests/isis_snmp/r5/ldpdconf index fc700608b534..b3d10b07ecdb 100644 --- a/tests/topotests/isis_snmp/r5/ldpdconf +++ b/tests/topotests/isis_snmp/r5/ldpdconf @@ -1,10 +1,10 @@ hostname r5 log file ldpd.log ! -debug mpls ldp zebra -debug mpls ldp event -debug mpls ldp errors -debug mpls ldp sync +!debug mpls ldp zebra +!debug mpls ldp event +!debug mpls ldp errors +!debug mpls ldp sync ! mpls ldp router-id 3.3.3.3 diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf new file mode 100644 index 000000000000..5503baa58ca9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/isisd.conf @@ -0,0 +1,96 @@ +password 1 +hostname rt1 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +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 network point-to-point +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0001.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + advertise-definition + affinity exclude-any red + ! + flex-algo 202 + dataplane sr-mpls + advertise-definition + affinity exclude-any blue + ! + flex-algo 203 + dataplane sr-mpls + advertise-definition + affinity exclude-any green + ! + flex-algo 204 + dataplane sr-mpls + advertise-definition + affinity include-any blue green + ! + flex-algo 205 + dataplane sr-mpls + advertise-definition + affinity include-any red green + ! + flex-algo 206 + dataplane sr-mpls + advertise-definition + affinity include-any red blue + ! + flex-algo 207 + dataplane sr-mpls + advertise-definition + affinity include-all yellow orange + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 1 + segment-routing prefix 1.1.1.1/32 algorithm 201 index 101 + segment-routing prefix 1.1.1.1/32 algorithm 202 index 201 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + segment-routing prefix 1.1.1.1/32 algorithm 204 index 401 + segment-routing prefix 1.1.1.1/32 algorithm 205 index 501 + segment-routing prefix 1.1.1.1/32 algorithm 206 index 601 + segment-routing prefix 1.1.1.1/32 algorithm 207 index 701 + segment-routing prefix 2001:db8:1000::1/128 index 1001 + segment-routing prefix 2001:db8:1000::1/128 algorithm 201 index 1101 + segment-routing prefix 2001:db8:1000::1/128 algorithm 202 index 1201 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + segment-routing prefix 2001:db8:1000::1/128 algorithm 204 index 1401 + segment-routing prefix 2001:db8:1000::1/128 algorithm 205 index 1501 + segment-routing prefix 2001:db8:1000::1/128 algorithm 206 index 1601 + segment-routing prefix 2001:db8:1000::1/128 algorithm 207 index 1701 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref new file mode 100644 index 000000000000..750abc1fa3c2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set \ No newline at end of file diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref new file mode 100644 index 000000000000..13a96161af28 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_isis_flex_algo.ref @@ -0,0 +1,115 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref new file mode 100644 index 000000000000..d311e924d604 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref new file mode 100644 index 000000000000..ce31766da50c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_isis_flex_algo.ref @@ -0,0 +1,112 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref new file mode 100644 index 000000000000..d311e924d604 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref new file mode 100644 index 000000000000..e56e5af2f9ca --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_isis_flex_algo.ref @@ -0,0 +1,108 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref new file mode 100644 index 000000000000..d311e924d604 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref new file mode 100644 index 000000000000..942199031660 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_isis_flex_algo.ref @@ -0,0 +1,126 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set + diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref new file mode 100644 index 000000000000..2b16c53132d3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/step9/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20402, + "outLabelStack":[ + 20402 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20603, + "outLabelStack":[ + 20603 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20702, + "outLabelStack":[ + 20702 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.3" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21102, + "outLabelStack":[ + 21102 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21303, + "outLabelStack":[ + 21303 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21402, + "outLabelStack":[ + 21402 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21603, + "outLabelStack":[ + 21603 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21702, + "outLabelStack":[ + 21702 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf new file mode 100644 index 000000000000..5140eda73a76 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt1/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt1 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 1.1.1.1/32 + ipv6 address 2001:db8:1000::1/128 +! +interface eth-rt2 + ip address 10.12.0.1/24 + link-params + affinity red + exit-link-params +! +interface eth-rt3 + ip address 10.13.0.1/24 + link-params + affinity green yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf new file mode 100644 index 000000000000..8655e7434a86 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/isisd.conf @@ -0,0 +1,96 @@ +password 1 +hostname rt2 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +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 +! +interface eth-rt3 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0002.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + advertise-definition + affinity exclude-any red + ! + flex-algo 202 + dataplane sr-mpls + advertise-definition + affinity exclude-any blue + ! + flex-algo 203 + dataplane sr-mpls + advertise-definition + affinity exclude-any green + ! + flex-algo 204 + dataplane sr-mpls + advertise-definition + affinity include-any blue green + ! + flex-algo 205 + dataplane sr-mpls + advertise-definition + affinity include-any red green + ! + flex-algo 206 + dataplane sr-mpls + advertise-definition + affinity include-any red blue + ! + flex-algo 207 + dataplane sr-mpls + advertise-definition + affinity include-all yellow orange + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 2 + segment-routing prefix 2.2.2.2/32 algorithm 201 index 102 + segment-routing prefix 2.2.2.2/32 algorithm 202 index 202 + segment-routing prefix 2.2.2.2/32 algorithm 203 index 302 + segment-routing prefix 2.2.2.2/32 algorithm 204 index 402 + segment-routing prefix 2.2.2.2/32 algorithm 205 index 502 + segment-routing prefix 2.2.2.2/32 algorithm 206 index 602 + segment-routing prefix 2.2.2.2/32 algorithm 207 index 702 + segment-routing prefix 2001:db8:1000::2/128 index 1002 + segment-routing prefix 2001:db8:1000::2/128 algorithm 201 index 1102 + segment-routing prefix 2001:db8:1000::2/128 algorithm 202 index 1202 + segment-routing prefix 2001:db8:1000::2/128 algorithm 203 index 1302 + segment-routing prefix 2001:db8:1000::2/128 algorithm 204 index 1402 + segment-routing prefix 2001:db8:1000::2/128 algorithm 205 index 1502 + segment-routing prefix 2001:db8:1000::2/128 algorithm 206 index 1602 + segment-routing prefix 2001:db8:1000::2/128 algorithm 207 index 1702 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref new file mode 100644 index 000000000000..099045a14d10 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref new file mode 100644 index 000000000000..099045a14d10 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref new file mode 100644 index 000000000000..0ed0eb31ee5a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20311":{ + "inLabel":20311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21311":{ + "inLabel":21311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref new file mode 100644 index 000000000000..099045a14d10 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref new file mode 100644 index 000000000000..b10cb708663c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_isis_flex_algo.ref @@ -0,0 +1,114 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref new file mode 100644 index 000000000000..8b407387b425 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref new file mode 100644 index 000000000000..099045a14d10 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref new file mode 100644 index 000000000000..099045a14d10 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref new file mode 100644 index 000000000000..358c4310ed6c --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_isis_flex_algo.ref @@ -0,0 +1,111 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref new file mode 100644 index 000000000000..8b407387b425 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref new file mode 100644 index 000000000000..04d07e6414fc --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_isis_flex_algo.ref @@ -0,0 +1,107 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref new file mode 100644 index 000000000000..8b407387b425 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref new file mode 100644 index 000000000000..099045a14d10 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref new file mode 100644 index 000000000000..defa3efa6b4a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: yes + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref new file mode 100644 index 000000000000..3db0ebfbf637 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/step9/show_mpls_table.ref @@ -0,0 +1,482 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20203":{ + "inLabel":20203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20203, + "outLabelStack":[ + 20203 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20401, + "outLabelStack":[ + 20401 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20403":{ + "inLabel":20403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20503":{ + "inLabel":20503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20503, + "outLabelStack":[ + 20503 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20603":{ + "inLabel":20603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20701, + "outLabelStack":[ + 20701 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20703":{ + "inLabel":20703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21003":{ + "inLabel":21003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21101, + "outLabelStack":[ + 21101 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21103":{ + "inLabel":21103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21203":{ + "inLabel":21203, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21203, + "outLabelStack":[ + 21203 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21303":{ + "inLabel":21303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21401, + "outLabelStack":[ + 21401 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21403":{ + "inLabel":21403, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21503":{ + "inLabel":21503, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21503, + "outLabelStack":[ + 21503 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21603":{ + "inLabel":21603, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21701, + "outLabelStack":[ + 21701 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + }, + "21703":{ + "inLabel":21703, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf new file mode 100644 index 000000000000..388348fced86 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt2/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt2 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 2.2.2.2/32 + ipv6 address 2001:db8:1000::2/128 +! +interface eth-rt1 + ip address 10.12.0.2/24 + link-params + affinity red + exit-link-params +! +interface eth-rt3 + ip address 10.23.0.2/24 + link-params + affinity blue yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf new file mode 100644 index 000000000000..d77af81d7cac --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/isisd.conf @@ -0,0 +1,76 @@ +password 1 +hostname rt3 +log file isisd.log +! +!debug northbound +!debug isis events +!debug isis route-events +!debug isis spf-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +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 +! +interface eth-rt2 + ip router isis 1 + ipv6 router isis 1 + isis hello-multiplier 3 + isis network point-to-point +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.0003.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 201 + dataplane sr-mpls + flex-algo 202 + dataplane sr-mpls + flex-algo 203 + dataplane sr-mpls + flex-algo 204 + dataplane sr-mpls + flex-algo 205 + dataplane sr-mpls + flex-algo 206 + dataplane sr-mpls + flex-algo 207 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 3 + segment-routing prefix 3.3.3.3/32 algorithm 201 index 103 + segment-routing prefix 3.3.3.3/32 algorithm 202 index 203 + segment-routing prefix 3.3.3.3/32 algorithm 203 index 303 + segment-routing prefix 3.3.3.3/32 algorithm 204 index 403 + segment-routing prefix 3.3.3.3/32 algorithm 205 index 503 + segment-routing prefix 3.3.3.3/32 algorithm 206 index 603 + segment-routing prefix 3.3.3.3/32 algorithm 207 index 703 + segment-routing prefix 2001:db8:1000::3/128 index 1003 + segment-routing prefix 2001:db8:1000::3/128 algorithm 201 index 1103 + segment-routing prefix 2001:db8:1000::3/128 algorithm 202 index 1203 + segment-routing prefix 2001:db8:1000::3/128 algorithm 203 index 1303 + segment-routing prefix 2001:db8:1000::3/128 algorithm 204 index 1403 + segment-routing prefix 2001:db8:1000::3/128 algorithm 205 index 1503 + segment-routing prefix 2001:db8:1000::3/128 algorithm 206 index 1603 + segment-routing prefix 2001:db8:1000::3/128 algorithm 207 index 1703 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref new file mode 100644 index 000000000000..79547349368a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set \ No newline at end of file diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref new file mode 100644 index 000000000000..9ab0c74d0aa9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step1/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref new file mode 100644 index 000000000000..9ab0c74d0aa9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step10/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref new file mode 100644 index 000000000000..57755b00e67e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step11/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20311":{ + "inLabel":20311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20311, + "outLabelStack":[ + 20311 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21311":{ + "inLabel":21311, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21311, + "outLabelStack":[ + 21311 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref new file mode 100644 index 000000000000..9ab0c74d0aa9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step2/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref new file mode 100644 index 000000000000..2ccc4f1655d9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_isis_flex_algo.ref @@ -0,0 +1,114 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: Not found + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref new file mode 100644 index 000000000000..1b57f575b24b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step3/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref new file mode 100644 index 000000000000..9ab0c74d0aa9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step4/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref new file mode 100644 index 000000000000..9ab0c74d0aa9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step5/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref new file mode 100644 index 000000000000..903c0f2bedeb --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_isis_flex_algo.ref @@ -0,0 +1,111 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: None + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref new file mode 100644 index 000000000000..1b57f575b24b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step6/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref new file mode 100644 index 000000000000..f36d96579db8 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_isis_flex_algo.ref @@ -0,0 +1,107 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref new file mode 100644 index 000000000000..1b57f575b24b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step7/show_mpls_table.ref @@ -0,0 +1,450 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref new file mode 100644 index 000000000000..9ab0c74d0aa9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step8/show_mpls_table.ref @@ -0,0 +1,514 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21301":{ + "inLabel":21301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21301, + "outLabelStack":[ + 21301 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref new file mode 100644 index 000000000000..85182db33a50 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_isis_flex_algo.ref @@ -0,0 +1,125 @@ +Area 1: Algorithm 201 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000001 + Bit positions: 0 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 202 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000002 + Bit positions: 1 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 203 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: 0x00000004 + Bit positions: 2 + Include-all admin-group: not-set + Include-any admin-group: not-set + +Area 1: Algorithm 204 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000006 + Bit positions: 1, 2 + +Area 1: Algorithm 205 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000005 + Bit positions: 0, 2 + +Area 1: Algorithm 206 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: not-set + Include-any admin-group: 0x00000003 + Bit positions: 0, 1 + +Area 1: Algorithm 207 + + Enabled Data-Planes: SR-MPLS + + Elected and running Flexible-Algorithm Definition: + Source: 0000.0000.0002 + Priority: 128 + Equal to local: no + Local state: enabled + Calculation type: spf + Metric type: igp + Prefix-metric: disabled + Exclude SRLG: disabled + Exclude-any admin-group: not-set + Include-all admin-group: 0x00000018 + Bit positions: 3, 4 + Include-any admin-group: not-set diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref new file mode 100644 index 000000000000..8ae983a79013 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/step9/show_mpls_table.ref @@ -0,0 +1,482 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20201":{ + "inLabel":20201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20202":{ + "inLabel":20202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20202, + "outLabelStack":[ + 20202 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20401":{ + "inLabel":20401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20402":{ + "inLabel":20402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20501":{ + "inLabel":20501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20502":{ + "inLabel":20502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20502, + "outLabelStack":[ + 20502 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20601":{ + "inLabel":20601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20601, + "outLabelStack":[ + 20601 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20602":{ + "inLabel":20602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20701":{ + "inLabel":20701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.13.0.1" + } + ] + }, + "20702":{ + "inLabel":20702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "21001":{ + "inLabel":21001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21002":{ + "inLabel":21002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21101":{ + "inLabel":21101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21102":{ + "inLabel":21102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21201":{ + "inLabel":21201, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21202":{ + "inLabel":21202, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21202, + "outLabelStack":[ + 21202 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21302":{ + "inLabel":21302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21401":{ + "inLabel":21401, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21402":{ + "inLabel":21402, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21501":{ + "inLabel":21501, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21502":{ + "inLabel":21502, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21502, + "outLabelStack":[ + 21502 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21601":{ + "inLabel":21601, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":21601, + "outLabelStack":[ + 21601 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21602":{ + "inLabel":21602, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + }, + "21701":{ + "inLabel":21701, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt1" + } + ] + }, + "21702":{ + "inLabel":21702, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "interface":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf new file mode 100644 index 000000000000..fb45ee128269 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/rt3/zebra.conf @@ -0,0 +1,39 @@ +log file zebra.log +! +hostname rt3 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map red bit-position 0 +affinity-map blue bit-position 1 +affinity-map green bit-position 2 +affinity-map yellow bit-position 3 +affinity-map orange bit-position 4 +! +interface lo + ip address 3.3.3.3/32 + ipv6 address 2001:db8:1000::3/128 +! +interface eth-rt1 + ip address 10.13.0.3/24 + link-params + affinity green yellow orange + exit-link-params +! +interface eth-rt2 + ip address 10.23.0.3/24 + link-params + affinity blue yellow orange + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py new file mode 100755 index 000000000000..c81f63942bde --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo1/test_isis_sr_flex_algo_topo1.py @@ -0,0 +1,631 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com> +# Copyright 2023 6WIND S.A. + +""" +test_isis_sr_flex_algo_topo1.py: + +[+] Flex-Algos 201 exclude red +[+] Flex-Algos 202 exclude blue +[+] Flex-Algos 203 exclude green +[+] Flex-Algos 204 include-any blue green +[+] Flex-Algos 205 include-any red green +[+] Flex-Algos 206 include-any red blue +[+] Flex-Algos 207 include-all yellow orange + + +--------+ 10.12.0.0/24 +--------+ + | | red | | + | RT1 |----------------| RT2 | + | | | | + +--------+ +--------+ + 10.13.0.0/24 \\ / 10.23.0.0/24 + green \\ / blue + yellow \\ / yellow + orange +--------+ orange + | | + | RT3 | + | | + +--------+ +""" + +import os +import sys +import pytest +import json +import tempfile +from copy import deepcopy +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 + + +pytestmark = [pytest.mark.isisd] + +# Global multi-dimensional dictionary containing all expected outputs +outputs = {} + + +def build_topo(tgen): + "Build function" + + def connect_routers(tgen, left_idx, right_idx): + left = "rt{}".format(left_idx) + right = "rt{}".format(right_idx) + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx) + tgen.gears[left].run("ip link set eth-{} down".format(right)) + tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr)) + tgen.gears[left].run("ip link set eth-{} up".format(right)) + r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx) + tgen.gears[right].run("ip link set eth-{} down".format(left)) + tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr)) + tgen.gears[right].run("ip link set eth-{} up".format(left)) + + tgen.add_router("rt1") + tgen.add_router("rt2") + tgen.add_router("rt3") + connect_routers(tgen, 1, 2) + connect_routers(tgen, 2, 3) + connect_routers(tgen, 3, 1) + + # + # Populate multi-dimensional dictionary containing all expected outputs + # + number_of_steps = 11 + filenames = [ + "show_mpls_table.ref", + "show_isis_flex_algo.ref", + ] + for rname in ["rt1", "rt2", "rt3"]: + outputs[rname] = {} + for step in range(1, number_of_steps + 1): + outputs[rname][step] = {} + for filename in filenames: + # Get snapshots relative to the expected network convergence + filename_pullpath = "{}/{}/step{}/{}".format(CWD, rname, step, filename) + outputs[rname][step][filename] = open(filename_pullpath).read() + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir") + if not os.path.isfile(os.path.join(frrdir, "pathd")): + pytest.skip("pathd daemon wasn't built") + tgen.start_topology() + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname)) + ) + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + return tgen + + +def router_json_cmp_exact_filter(router, cmd, expected): + output = router.vtysh_cmd(cmd) + logger.info("{}: {}\n{}".format(router.name, cmd, output)) + + json_output = json.loads(output) + router_output = deepcopy(json_output) + + # filter out dynamic data from "show mpls table" + for label, data in json_output.items(): + if "1500" in label: + # filter out SR local labels + router_output.pop(label) + continue + nexthops = data.get("nexthops", []) + for i in range(len(nexthops)): + if "fe80::" in nexthops[i].get("nexthop"): + router_output.get(label).get("nexthops")[i].pop("nexthop") + elif "." in nexthops[i].get("nexthop"): + # IPv4, just checking the nexthop + router_output.get(label).get("nexthops")[i].pop("interface") + + return topotest.json_cmp(router_output, expected, exact=True) + + +def router_compare_json_output(rname, command, reference): + "Compare router JSON output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + expected = json.loads(reference) + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial( + router_json_cmp_exact_filter, 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 + + +def router_compare_output(rname, command, reference): + "Compare router output" + + logger.info('Comparing router "%s" "%s" output', rname, command) + + tgen = get_topogen() + + # Run test function until we get an result. Wait at most 60 seconds. + test_func = partial( + topotest.router_output_cmp, tgen.gears[rname], command, reference + ) + result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5) + assertmsg = '{} command "{}" output mismatches the expected result:\n{}'.format( + rname, command, diff + ) + assert result, assertmsg + + +# +# Step 1 +# +# Test initial network convergenece +# +# All flex-algo are defined and its fib entries are installed +# +def test_step1_mpls_lfib(): + logger.info("Test (step 1)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][1]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][1]["show_mpls_table.ref"] + ) + + +# +# Step 2 +# +# Action(s): +# - Disable flex-algo-203 definition advertisement on rt1 +# +# Expected change(s): +# - Nothing +# +# Description: +# No change occurs because it refers to the FAD set in rt2. +# +def test_step2_mpls_lfib(): + logger.info("Test (step 2)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no advertise-definition + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][2]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][2]["show_mpls_table.ref"] + ) + + +# +# Step 3 +# +# Action(s): +# - Disable flex-algo-203 definition advertisement on rt2 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# When all FADs are disappeared, all their prefix sid routes are withdrawn. +# +def test_step3_mpls_lfib(): + logger.info("Test (step 3)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no advertise-definition + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][3]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][3]["show_mpls_table.ref"] + ) + + +# +# Step 4 +# +# Action(s): +# - Enable flex-algo-203 definition advertisement on rt2 +# +# Expected change(s): +# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203 +# +# Description: +# Since the FAD is restored, the reachability to the Prefix-SID is restored. +# +def test_step4_mpls_lfib(): + logger.info("Test (step 4)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][4]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][4]["show_mpls_table.ref"] + ) + + +# +# Step 5 +# +# Action(s): +# - Enable flex-algo-203 definition advertisement on rt1 +# +# Expected change(s): +# - Nothing +# +# Description: +# This does not affect the FIB, since there is already a FAD for rt2. +# However, the FAD owner will be changed from rt2 to rt1. +# +def test_step5_mpls_lfib(): + logger.info("Test (step 5)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][5]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][5]["show_mpls_table.ref"] + ) + + +# +# Step 6 +# +# Action(s): +# - Disable flex-algo-203 SR-MPLS dataplane on rt1 +# - Disable flex-algo-203 SR-MPLS dataplane on rt2 +# - Disable flex-algo-203 SR-MPLS dataplane on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# Clear the Flex-Algo 203 whole settings on each routers. All routes related +# to it will be withdrawn. +# +def test_step6_mpls_lfib(): + logger.info("Test (step 6)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3"]: + tgen.gears[rname].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + no dataplane sr-mpls + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][6]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][6]["show_mpls_table.ref"] + ) + + +# +# Step 7 +# +# Action(s): +# - Disable flex-algo-203 all configuration on rt1 +# - Disable flex-algo-203 all configuration on rt2 +# - Disable flex-algo-203 all configuration on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should uninstall all Prefix-SIDs of flex-algo-203 +# +# Description: +# Clear the Flex-Algo 203 whole settings on each routers. All routes related +# to it will be withdrawn. +# +def test_step7_mpls_lfib(): + logger.info("Test (step 7)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for rname in ["rt1", "rt2", "rt3"]: + tgen.gears[rname].vtysh_cmd( + """ + configure terminal + router isis 1 + no flex-algo 203 + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][7]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][7]["show_mpls_table.ref"] + ) + + +# +# Step 8 +# +# Action(s): +# - Enable flex-algo-203 all configuration on rt1 +# - Enable flex-algo-203 all configuration on rt2 +# - Enable flex-algo-203 all configuration on rt3 +# +# Expected change(s): +# - rt1,rt2,rt3 should install all Prefix-SIDs of flex-algo-203 +# +# Description: +# All configurations were backed. +# +def test_step8_mpls_lfib(): + logger.info("Test (step 8)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + affinity exclude-any green + dataplane sr-mpls + """ + ) + + tgen.gears["rt2"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + advertise-definition + affinity exclude-any green + dataplane sr-mpls + """ + ) + + tgen.gears["rt3"].vtysh_cmd( + """ + configure terminal + router isis 1 + flex-algo 203 + dataplane sr-mpls + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][8]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][8]["show_mpls_table.ref"] + ) + + +# +# Step 9 +# +# Action(s): +# - Disable algorithm prefix-sid of algo-203 on rt1 +# +# Expected change(s): +# - rt1 should uninstall all Prefix-SIDs of flex-algo-203 +# - rt2 should uninstall Prefix-SIDs of rt1's flex-algo-203 +# - rt3 should uninstall Prefix-SIDs of rt1's flex-algo-203 +# +def test_step9_mpls_lfib(): + logger.info("Test (step 9)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + no segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + no segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][9]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][9]["show_mpls_table.ref"] + ) + + +# +# Step 10 +# +# Action(s): +# - Enable algorithm prefix-sid of algo-203 on rt1 +# +# Expected change(s): +# - rt1 should install all Prefix-SIDs of flex-algo-203 +# - rt2 should install Prefix-SIDs of rt1's flex-algo-203 +# - rt3 should install Prefix-SIDs of rt1's flex-algo-203 +# +def test_step10_mpls_lfib(): + logger.info("Test (step 10)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 301 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1301 + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][10]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][10]["show_mpls_table.ref"] + ) + + +# +# Step 11 +# +# Action(s): +# - Update algorithm prefix-sid of algo-203 on rt1 from 301 to 311 +# +# Expected change(s): +# - rt2 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311 +# - rt3 should update Prefix-SIDs of rt1's flex-algo-203 from 301 to 311 +# +def test_step11_mpls_lfib(): + logger.info("Test (step 11)") + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + tgen.gears["rt1"].vtysh_cmd( + """ + configure terminal + router isis 1 + segment-routing prefix 1.1.1.1/32 algorithm 203 index 311 + segment-routing prefix 2001:db8:1000::1/128 algorithm 203 index 1311 + """ + ) + + # For Developers + # tgen.mininet_cli() + for rname in ["rt1", "rt2", "rt3"]: + router_compare_output( + rname, "show isis flex-algo", outputs[rname][11]["show_isis_flex_algo.ref"] + ) + router_compare_json_output( + rname, "show mpls table json", outputs[rname][11]["show_mpls_table.ref"] + ) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_sr_flex_algo_topo2/README.md b/tests/topotests/isis_sr_flex_algo_topo2/README.md new file mode 100644 index 000000000000..20282c49eebe --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/README.md @@ -0,0 +1,8 @@ + +## test + +``` +fdk-enter rt9.pid iperf3 -s +fdk-enter rt0.pid iperf3 -B 111.111.111.111 -c 222.222.222.222 -P20 -t 100000 +fdk-enter rt0.pid watch -n0.1 ip -s link show +``` diff --git a/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh new file mode 100755 index 000000000000..527f05b60b83 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/configure-te.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +if [ $# -ne 1 ]; then + echo "invalid command syntax" 1>&2 + echo "Usage: $0 <0|128|129|130>" 1>&2 + exit 1 +fi + +case "$1" in + 0 ) echo ;; + 128 ) echo ;; + 129 ) echo ;; + 130 ) echo ;; + * ) echo "error" ; exit ;; +esac + +R0=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt0.pid) +R9=$(cat /tmp/topotests/isis_sr_flex_algo_topo2.test_isis_sr_flex_algo_topo2/rt9.pid) + +set -x + +cat <<EOF | nsenter -a -t $R0 vtysh +conf te +segment-routing + traffic-eng + policy color 1 endpoint 9.9.9.9 + name sid-algorithm + binding-sid 111 + candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1 + exit + exit +exit +EOF + +cat <<EOF | nsenter -a -t $R9 vtysh +conf te +segment-routing + traffic-eng + policy color 1 endpoint 10.10.10.10 + name sid-algorithm + binding-sid 222 + candidate-path preference 100 name sid-algorithm explicit segment-list sid-algorithm-$1 + exit + exit +exit +EOF diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf new file mode 100644 index 000000000000..3915bec385e2 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 1 + bgp router-id 10.10.10.10 + no bgp network import-check + neighbor 9.9.9.9 remote-as 1 + neighbor 9.9.9.9 update-source 10.10.10.10 + ! + address-family ipv4 unicast + network 10.255.0.0/24 + neighbor 9.9.9.9 next-hop-self + neighbor 9.9.9.9 route-map sr-te in + exit-address-family +! +route-map sr-te permit 10 + set sr-te color 1 +exit +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf new file mode 100644 index 000000000000..cbf25504ef4d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/isisd.conf @@ -0,0 +1,56 @@ +password 1 +hostname rt0 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1000.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + advertise-definition + ! + flex-algo 129 + dataplane sr-mpls + advertise-definition + ! + flex-algo 130 + dataplane sr-mpls + advertise-definition + affinity include-any blue + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 10.10.10.10/32 index 0 + segment-routing prefix 10.10.10.10/32 algorithm 128 index 100 + segment-routing prefix 10.10.10.10/32 algorithm 129 index 200 + segment-routing prefix 10.10.10.10/32 algorithm 130 index 300 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf new file mode 100644 index 000000000000..c51b4e024794 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/pathd.conf @@ -0,0 +1,20 @@ +log file pathd.log +! +hostname rt0 +! +segment-routing + traffic-eng + segment-list sid-algorithm-0 + index 10 mpls label 20009 + exit + segment-list sid-algorithm-128 + index 10 mpls label 20109 + exit + segment-list sid-algorithm-129 + index 10 mpls label 20209 + exit + segment-list sid-algorithm-130 + index 10 mpls label 20309 + exit + exit +exit diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json new file mode 100644 index 000000000000..51a1c25556fc --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/step1/route.json @@ -0,0 +1,438 @@ +{ + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.1" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf new file mode 100644 index 000000000000..89837d4cf5dd --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt0/zebra.conf @@ -0,0 +1,34 @@ +log file zebra.log +! +hostname rt0 +! +!log stdout notifications +!log monitor notifications +!log commands +! +debug zebra packet +debug zebra dplane +debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 10.10.10.10/32 +! +interface eth-rt1 + ip address 10.1.0.10/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt5 + ip address 10.5.0.10/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf new file mode 100644 index 000000000000..b6bba0c1c38e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt1 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt0 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1001.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 1.1.1.1/32 index 1 + segment-routing prefix 1.1.1.1/32 algorithm 128 index 101 + segment-routing prefix 1.1.1.1/32 algorithm 129 index 201 + segment-routing prefix 1.1.1.1/32 algorithm 130 index 301 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json new file mode 100644 index 000000000000..50066250b8b8 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20103, + "outLabelStack":[ + 20103 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + }, + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.1.0.10" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.2" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf new file mode 100644 index 000000000000..25a96290ae88 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt1/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt1 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt0 + ip address 10.1.0.1/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt2 + ip address 10.12.0.1/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt4 + ip address 10.14.0.1/24 +! +interface eth-rt5 + ip address 10.15.0.1/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf new file mode 100644 index 000000000000..f051a68e21d4 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/isisd.conf @@ -0,0 +1,54 @@ +password 1 +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 +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1002.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 2.2.2.2/32 index 2 + segment-routing prefix 2.2.2.2/32 algorithm 128 index 102 + segment-routing prefix 2.2.2.2/32 algorithm 129 index 202 + segment-routing prefix 2.2.2.2/32 algorithm 130 index 302 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json new file mode 100644 index 000000000000..679b41db039a --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/step1/route.json @@ -0,0 +1,408 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.12.0.1" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf new file mode 100644 index 000000000000..d739a732a927 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt2/zebra.conf @@ -0,0 +1,37 @@ +log file zebra.log +! +hostname rt2 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-rt1 + ip address 10.12.0.2/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt3 + ip address 10.23.0.2/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt6 + ip address 10.26.0.2/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf new file mode 100644 index 000000000000..644e656bfd13 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/isisd.conf @@ -0,0 +1,60 @@ +password 1 +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 +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt9 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1003.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 3.3.3.3/32 index 3 + segment-routing prefix 3.3.3.3/32 algorithm 128 index 103 + segment-routing prefix 3.3.3.3/32 algorithm 129 index 203 + segment-routing prefix 3.3.3.3/32 algorithm 130 index 303 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json new file mode 100644 index 000000000000..f930faa61f85 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + }, + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.4" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + }, + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.23.0.2" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.9" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf new file mode 100644 index 000000000000..5c3bed07632e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt3/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt3 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-rt2 + ip address 10.23.0.3/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt4 + ip address 10.34.0.3/24 +! +interface eth-rt7 + ip address 10.37.0.3/24 +! +interface eth-rt9 + ip address 10.39.0.3/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf new file mode 100644 index 000000000000..1ab200fbd89d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/isisd.conf @@ -0,0 +1,52 @@ +password 1 +hostname rt4 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1004.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 4.4.4.4/32 index 4 + segment-routing prefix 4.4.4.4/32 algorithm 128 index 104 + segment-routing prefix 4.4.4.4/32 algorithm 129 index 204 + segment-routing prefix 4.4.4.4/32 algorithm 130 index 304 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json new file mode 100644 index 000000000000..141e40d45565 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/step1/route.json @@ -0,0 +1,286 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + }, + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.14.0.1" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + }, + "20109":{ + "inLabel":20109, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20109, + "outLabelStack":[ + 20109 + ], + "distance":150, + "installed":true, + "nexthop":"10.34.0.3" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf new file mode 100644 index 000000000000..9c00013e70c9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt4/zebra.conf @@ -0,0 +1,31 @@ +log file zebra.log +! +hostname rt4 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt1 + ip address 10.14.0.4/24 +! +interface eth-rt3 + ip address 10.34.0.4/24 +! +interface eth-rt8 + ip address 10.48.0.4/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf new file mode 100644 index 000000000000..54cc37711eec --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt5 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt0 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt1 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1005.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 5.5.5.5/32 index 5 + segment-routing prefix 5.5.5.5/32 algorithm 128 index 105 + segment-routing prefix 5.5.5.5/32 algorithm 129 index 205 + segment-routing prefix 5.5.5.5/32 algorithm 130 index 305 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json new file mode 100644 index 000000000000..82ebfc075f92 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.15.0.1" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20007, + "outLabelStack":[ + 20007 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20207, + "outLabelStack":[ + 20207 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.5.0.10" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20307, + "outLabelStack":[ + 20307 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.6" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf new file mode 100644 index 000000000000..61c599db7b1d --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt5/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt5 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 5.5.5.5/32 +! +interface eth-rt0 + ip address 10.5.0.5/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt1 + ip address 10.15.0.5/24 +! +interface eth-rt6 + ip address 10.56.0.5/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt8 + ip address 10.58.0.5/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf new file mode 100644 index 000000000000..fc8660cfa6af --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/isisd.conf @@ -0,0 +1,54 @@ +password 1 +hostname rt6 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt2 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1006.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 6.6.6.6/32 index 6 + segment-routing prefix 6.6.6.6/32 algorithm 128 index 106 + segment-routing prefix 6.6.6.6/32 algorithm 129 index 206 + segment-routing prefix 6.6.6.6/32 algorithm 130 index 306 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json new file mode 100644 index 000000000000..2cc7277b419e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/step1/route.json @@ -0,0 +1,408 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.26.0.2" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.56.0.5" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20309, + "outLabelStack":[ + 20309 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf new file mode 100644 index 000000000000..b63401e11478 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt6/zebra.conf @@ -0,0 +1,37 @@ +log file zebra.log +! +hostname rt6 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 6.6.6.6/32 +! +interface eth-rt2 + ip address 10.26.0.6/24 +! +interface eth-rt5 + ip address 10.56.0.6/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt7 + ip address 10.67.0.6/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf new file mode 100644 index 000000000000..10dc9812a408 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/isisd.conf @@ -0,0 +1,60 @@ +password 1 +hostname rt7 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt6 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt8 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt9 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1007.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + flex-algo 130 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 7.7.7.7/32 index 7 + segment-routing prefix 7.7.7.7/32 algorithm 128 index 107 + segment-routing prefix 7.7.7.7/32 algorithm 129 index 207 + segment-routing prefix 7.7.7.7/32 algorithm 130 index 307 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json new file mode 100644 index 000000000000..aeaa6046ab20 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/step1/route.json @@ -0,0 +1,428 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.37.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + }, + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.8" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + }, + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20303, + "outLabelStack":[ + 20303 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.67.0.6" + } + ] + }, + "20309":{ + "inLabel":20309, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.9" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf new file mode 100644 index 000000000000..b5a28c7f1ace --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt7/zebra.conf @@ -0,0 +1,40 @@ +log file zebra.log +! +hostname rt7 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 7.7.7.7/32 +! +interface eth-rt3 + ip address 10.37.0.7/24 +! +interface eth-rt6 + ip address 10.67.0.7/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt8 + ip address 10.78.0.7/24 +! +interface eth-rt9 + ip address 10.79.0.7/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf new file mode 100644 index 000000000000..4ca45a8ad97b --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/isisd.conf @@ -0,0 +1,50 @@ +password 1 +hostname rt8 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt4 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt5 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1008.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 129 + dataplane sr-mpls + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 8.8.8.8/32 index 8 + segment-routing prefix 8.8.8.8/32 algorithm 128 index 108 + segment-routing prefix 8.8.8.8/32 algorithm 129 index 208 + segment-routing prefix 8.8.8.8/32 algorithm 130 index 308 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json new file mode 100644 index 000000000000..27470b76cc57 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/step1/route.json @@ -0,0 +1,286 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + }, + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20003, + "outLabelStack":[ + 20003 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.48.0.4" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20009":{ + "inLabel":20009, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20009, + "outLabelStack":[ + 20009 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.58.0.5" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + }, + "20209":{ + "inLabel":20209, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20209, + "outLabelStack":[ + 20209 + ], + "distance":150, + "installed":true, + "nexthop":"10.78.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf new file mode 100644 index 000000000000..dd63f8cc2fc3 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt8/zebra.conf @@ -0,0 +1,29 @@ +log file zebra.log +! +hostname rt8 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +interface lo + ip address 8.8.8.8/32 +! +interface eth-rt4 + ip address 10.48.0.8/24 +! +interface eth-rt5 + ip address 10.58.0.8/24 +! +interface eth-rt7 + ip address 10.78.0.8/24 +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf new file mode 100644 index 000000000000..386d8118e7c9 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/bgpd.conf @@ -0,0 +1,17 @@ +! +router bgp 1 + bgp router-id 9.9.9.9 + no bgp network import-check + neighbor 10.10.10.10 remote-as 1 + neighbor 10.10.10.10 update-source 9.9.9.9 + ! + address-family ipv4 unicast + network 10.255.9.0/24 + neighbor 10.10.10.10 next-hop-self + neighbor 10.10.10.10 route-map sr-te in + exit-address-family +! +route-map sr-te permit 10 + set sr-te color 1 +exit +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf new file mode 100644 index 000000000000..89eab274c772 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/isisd.conf @@ -0,0 +1,56 @@ +password 1 +hostname rt9 +log file isisd.log +! +!debug isis events +!debug isis spf-events +!debug isis route-events +!debug isis sr-events +!debug isis lsp-gen +! +affinity-map blue bit-position 0 +! +interface lo + ip router isis 1 + isis passive +! +interface eth-rt3 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +interface eth-rt7 + ip router isis 1 + isis hello-multiplier 3 + isis network point-to-point + isis circuit-type level-1 +! +router isis 1 + lsp-gen-interval 2 + net 49.0000.0000.0000.1009.00 + is-type level-1 + topology ipv6-unicast + mpls-te on + ! + flex-algo 128 + dataplane sr-mpls + advertise-definition + ! + flex-algo 129 + dataplane sr-mpls + advertise-definition + ! + flex-algo 130 + dataplane sr-mpls + advertise-definition + affinity include-any blue + ! + segment-routing on + segment-routing global-block 20000 23999 + segment-routing node-msd 8 + segment-routing prefix 9.9.9.9/32 index 9 + segment-routing prefix 9.9.9.9/32 algorithm 128 index 109 + segment-routing prefix 9.9.9.9/32 algorithm 129 index 209 + segment-routing prefix 9.9.9.9/32 algorithm 130 index 309 +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf new file mode 100644 index 000000000000..3f9a8d90597e --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/pathd.conf @@ -0,0 +1,20 @@ +log file pathd.log +! +hostname rt9 +! +segment-routing + traffic-eng + segment-list sid-algorithm-0 + index 10 mpls label 20000 + exit + segment-list sid-algorithm-128 + index 10 mpls label 20100 + exit + segment-list sid-algorithm-129 + index 10 mpls label 20200 + exit + segment-list sid-algorithm-130 + index 10 mpls label 20300 + exit + exit +exit diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json new file mode 100644 index 000000000000..e98680c5dc78 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/step1/route.json @@ -0,0 +1,438 @@ +{ + "20000":{ + "inLabel":20000, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20000, + "outLabelStack":[ + 20000 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20001":{ + "inLabel":20001, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20001, + "outLabelStack":[ + 20001 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20002":{ + "inLabel":20002, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20002, + "outLabelStack":[ + 20002 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20003":{ + "inLabel":20003, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20004":{ + "inLabel":20004, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20004, + "outLabelStack":[ + 20004 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20005":{ + "inLabel":20005, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20005, + "outLabelStack":[ + 20005 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20006":{ + "inLabel":20006, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20006, + "outLabelStack":[ + 20006 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20007":{ + "inLabel":20007, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20008":{ + "inLabel":20008, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20008, + "outLabelStack":[ + 20008 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20100":{ + "inLabel":20100, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20100, + "outLabelStack":[ + 20100 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20101":{ + "inLabel":20101, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20101, + "outLabelStack":[ + 20101 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20102":{ + "inLabel":20102, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20102, + "outLabelStack":[ + 20102 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20103":{ + "inLabel":20103, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20104":{ + "inLabel":20104, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20104, + "outLabelStack":[ + 20104 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20200":{ + "inLabel":20200, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20200, + "outLabelStack":[ + 20200 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20205":{ + "inLabel":20205, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20205, + "outLabelStack":[ + 20205 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20206":{ + "inLabel":20206, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20206, + "outLabelStack":[ + 20206 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20207":{ + "inLabel":20207, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20208":{ + "inLabel":20208, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20208, + "outLabelStack":[ + 20208 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20300":{ + "inLabel":20300, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + }, + { + "type":"SR (IS-IS)", + "outLabel":20300, + "outLabelStack":[ + 20300 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20301":{ + "inLabel":20301, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20301, + "outLabelStack":[ + 20301 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20302":{ + "inLabel":20302, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20302, + "outLabelStack":[ + 20302 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20303":{ + "inLabel":20303, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.39.0.3" + } + ] + }, + "20305":{ + "inLabel":20305, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20305, + "outLabelStack":[ + 20305 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20306":{ + "inLabel":20306, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":20306, + "outLabelStack":[ + 20306 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + }, + "20307":{ + "inLabel":20307, + "installed":true, + "nexthops":[ + { + "type":"SR (IS-IS)", + "outLabel":3, + "outLabelStack":[ + 3 + ], + "distance":150, + "installed":true, + "nexthop":"10.79.0.7" + } + ] + } +} diff --git a/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf new file mode 100644 index 000000000000..378a1969bea7 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/rt9/zebra.conf @@ -0,0 +1,34 @@ +log file zebra.log +! +hostname rt9 +! +log stdout notifications +log monitor notifications +log commands +! +!debug zebra packet +!debug zebra dplane +!debug zebra kernel +! +affinity-map blue bit-position 0 +! +interface lo + ip address 9.9.9.9/32 +! +interface eth-rt3 + ip address 10.39.0.9/24 + link-params + affinity blue + exit-link-params +! +interface eth-rt7 + ip address 10.79.0.9/24 + link-params + affinity blue + exit-link-params +! +ip forwarding +ipv6 forwarding +! +line vty +! diff --git a/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py new file mode 100755 index 000000000000..6a5f81def658 --- /dev/null +++ b/tests/topotests/isis_sr_flex_algo_topo2/test_isis_sr_flex_algo_topo2.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Part of NetDEF Topology Tests +# +# Copyright 2021 by LINE Corporation, Hiroki Shirokura <hiroki.shirokura@linecorp.com> +# Copyright 2023 6WIND S.A. + +""" +test_isis_sr_flex_algo_topo2.py: + +[+] Flex-Algos 128 +[+] Flex-Algos 129 +[+] Flex-Algos 130 include-any blue + + +--------+ +--------+ + | | | | + | RT1 |------------------| RT2 | + | | | | + +--------+ +--------+ + / | \\ | \\ + / | \\ | \\ ++--------+ | \\ | \\ +| | | +--------+ | +--------+ +| RT0 | | | | | | | +| | | | RT4 |------------------| RT3 | ++--------+ | | | | | | + \\ | +--------+ | +--------+ + \\ | | | | \\ + +--------+ | +--------+ | \\ + | | | | | | +--------+ + | RT5 |-------|----------| RT6 | | | | + | | | | | | | RT9 | + +--------+ | +--------+ | | | + \\ | \\ | +--------+ + \\ | \\ | / + \\ | \\ | / + +--------+ +--------+ + | | | | + | RT8 |------------------| RT7 | + | | | | + +--------+ +--------+ +""" + +import os +import sys +import pytest +import json +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 + + +pytestmark = [pytest.mark.isisd] + + +def build_topo(tgen): + "Build function" + + routers = [] + for i in range(0, 10): + rt = tgen.add_router("rt{}".format(i)) + rt.run("sysctl -w net.ipv4.fib_multipath_hash_policy=1") + + def connect_routers(tgen, left_idx, right_idx): + left = "rt{}".format(left_idx) + right = "rt{}".format(right_idx) + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + l_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, left_idx) + tgen.gears[left].run("ip link set eth-{} down".format(right)) + tgen.gears[left].run("ip link set eth-{} address {}".format(right, l_addr)) + tgen.gears[left].run("ip link set eth-{} up".format(right)) + tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right)) + r_addr = "52:54:00:{}:{}:{}".format(left_idx, right_idx, right_idx) + tgen.gears[right].run("ip link set eth-{} down".format(left)) + tgen.gears[right].run("ip link set eth-{} address {}".format(left, r_addr)) + tgen.gears[right].run("ip link set eth-{} up".format(left)) + tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left)) + + connect_routers(tgen, 0, 1) + connect_routers(tgen, 0, 5) + connect_routers(tgen, 1, 2) + connect_routers(tgen, 1, 4) + connect_routers(tgen, 1, 5) + connect_routers(tgen, 2, 3) + connect_routers(tgen, 2, 6) + connect_routers(tgen, 3, 4) + connect_routers(tgen, 3, 7) + connect_routers(tgen, 3, 9) + connect_routers(tgen, 4, 8) + connect_routers(tgen, 5, 6) + connect_routers(tgen, 5, 8) + connect_routers(tgen, 6, 7) + connect_routers(tgen, 7, 8) + connect_routers(tgen, 7, 9) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir") + if not os.path.isfile(os.path.join(frrdir, "pathd")): + pytest.skip("pathd daemon wasn't built") + tgen.start_topology() + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))) + router.load_config( TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))) + if rname in ["rt0", "rt9"]: + router.load_config( TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))) + router.load_config( TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))) + router.run("ip link add dum0 type dummy") + router.run("ip link set dum0 up") + if rname == "rt0": + router.run("ip addr add 10.255.0.1/24 dev dum0") + elif rname == "rt9": + router.run("ip addr add 10.255.9.1/24 dev dum0") + tgen.start_router() + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def setup_testcase(msg): + logger.info(msg) + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + return tgen + +def open_json_file(filename): + try: + with open(filename, "r") as f: + return json.load(f) + except IOError: + assert False, "Could not read file {}".format(filename) + + +def check_rib(name, cmd, expected_file): + def _check(name, cmd, expected_file): + logger.info("polling") + tgen = get_topogen() + router = tgen.gears[name] + output = json.loads(router.vtysh_cmd(cmd)) + expected = open_json_file("{}/{}".format(CWD, expected_file)) + return topotest.json_cmp(output, expected) + + logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file)) + tgen = get_topogen() + func = partial(_check, name, cmd, expected_file) + success, result = topotest.run_and_expect(func, None, count=120, wait=0.5) + assert result is None, "Failed" + + +def test_rib(): + check_rib("rt0", "show mpls table json", "rt0/step1/route.json") + check_rib("rt1", "show mpls table json", "rt1/step1/route.json") + check_rib("rt2", "show mpls table json", "rt2/step1/route.json") + check_rib("rt3", "show mpls table json", "rt3/step1/route.json") + check_rib("rt4", "show mpls table json", "rt4/step1/route.json") + check_rib("rt5", "show mpls table json", "rt5/step1/route.json") + check_rib("rt6", "show mpls table json", "rt6/step1/route.json") + check_rib("rt7", "show mpls table json", "rt7/step1/route.json") + check_rib("rt8", "show mpls table json", "rt8/step1/route.json") + check_rib("rt9", "show mpls table json", "rt9/step1/route.json") + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/isis_te_topo1/reference/ted_step1.json b/tests/topotests/isis_te_topo1/reference/ted_step1.json index d7711b7e5911..b70626e85db6 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step1.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step1.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -91,7 +91,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -132,7 +132,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -173,7 +173,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -215,7 +215,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -263,7 +263,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -306,7 +306,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -350,7 +350,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -391,7 +391,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -432,7 +432,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -473,7 +473,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -514,7 +514,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -556,7 +556,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -600,7 +600,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step10.json b/tests/topotests/isis_te_topo1/reference/ted_step10.json index 7d017b343005..f029e5aab018 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step10.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step10.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -108,7 +108,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -148,7 +148,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -189,7 +189,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -230,7 +230,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -271,7 +271,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -313,7 +313,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -361,7 +361,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -404,7 +404,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -462,7 +462,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -503,7 +503,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -544,7 +544,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -585,7 +585,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -626,7 +626,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -668,7 +668,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -711,7 +711,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step2.json b/tests/topotests/isis_te_topo1/reference/ted_step2.json index 7b2074b69c77..aa2bafb15a76 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step2.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step2.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -91,7 +91,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -133,7 +133,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -181,7 +181,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -224,7 +224,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -268,7 +268,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -309,7 +309,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -350,7 +350,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -392,7 +392,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -436,7 +436,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step3.json b/tests/topotests/isis_te_topo1/reference/ted_step3.json index 528138477a53..de6d108bb363 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step3.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step3.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -175,7 +175,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -217,7 +217,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -265,7 +265,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -308,7 +308,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -352,7 +352,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -393,7 +393,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -476,7 +476,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -520,7 +520,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step4.json b/tests/topotests/isis_te_topo1/reference/ted_step4.json index 528138477a53..de6d108bb363 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step4.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step4.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -175,7 +175,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -217,7 +217,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -265,7 +265,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -308,7 +308,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -352,7 +352,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -393,7 +393,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -476,7 +476,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -520,7 +520,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step5.json b/tests/topotests/isis_te_topo1/reference/ted_step5.json index 72e441d18612..7daee99297a3 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step5.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step5.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -175,7 +175,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -216,7 +216,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -257,7 +257,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -299,7 +299,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -347,7 +347,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -390,7 +390,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -475,7 +475,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -516,7 +516,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -557,7 +557,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -598,7 +598,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -640,7 +640,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -684,7 +684,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step6.json b/tests/topotests/isis_te_topo1/reference/ted_step6.json index a5f50c3ebab9..289eb1ebc2e9 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step6.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step6.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -94,7 +94,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -134,7 +134,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -175,7 +175,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -216,7 +216,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -257,7 +257,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -299,7 +299,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -347,7 +347,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -390,7 +390,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -434,7 +434,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -475,7 +475,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -516,7 +516,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -557,7 +557,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -598,7 +598,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -640,7 +640,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -683,7 +683,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step7.json b/tests/topotests/isis_te_topo1/reference/ted_step7.json index 447febce4882..18eb42fd326f 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step7.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step7.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -109,7 +109,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -149,7 +149,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -190,7 +190,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -231,7 +231,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -272,7 +272,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -314,7 +314,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -362,7 +362,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -405,7 +405,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -464,7 +464,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -505,7 +505,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -546,7 +546,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -587,7 +587,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -628,7 +628,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -670,7 +670,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -713,7 +713,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_te_topo1/reference/ted_step8.json b/tests/topotests/isis_te_topo1/reference/ted_step8.json index 510e034eba4f..ede36cf93dd9 100644 --- a/tests/topotests/isis_te_topo1/reference/ted_step8.json +++ b/tests/topotests/isis_te_topo1/reference/ted_step8.json @@ -50,7 +50,7 @@ ], "edges":[ { - "edge-id":1, + "edge-id":"2001:db8::1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -109,7 +109,7 @@ } }, { - "edge-id":2, + "edge-id":"2001:db8::2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -149,7 +149,7 @@ } }, { - "edge-id":65537, + "edge-id":"2001:db8:1::1:1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -190,7 +190,7 @@ } }, { - "edge-id":65538, + "edge-id":"2001:db8:1::1:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -231,7 +231,7 @@ } }, { - "edge-id":196610, + "edge-id":"2001:db8:3::3:2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -272,7 +272,7 @@ } }, { - "edge-id":196611, + "edge-id":"2001:db8:3::3:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -314,7 +314,7 @@ } }, { - "edge-id":196612, + "edge-id":"2001:db8:5::3:4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", @@ -362,7 +362,7 @@ ] }, { - "edge-id":262147, + "edge-id":"2001:db8:5::4:3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -405,7 +405,7 @@ } }, { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -464,7 +464,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -505,7 +505,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0001", @@ -546,7 +546,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -587,7 +587,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -628,7 +628,7 @@ } }, { - "edge-id":167772931, + "edge-id":"10.0.3.3", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0003", @@ -670,7 +670,7 @@ } }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0002", @@ -713,7 +713,7 @@ } }, { - "edge-id":167773188, + "edge-id":"10.0.4.4", "status":"Sync", "origin":"ISIS_L2", "advertised-router":"0000.0000.0004", diff --git a/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf index 955bd5caa0c8..620523512aae 100644 --- a/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf +++ b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf @@ -16,6 +16,7 @@ interface lo interface eth-sw1 ip router isis 1 ipv6 router isis 1 + isis hello-interval 1 isis hello-multiplier 3 isis priority 100 isis fast-reroute ti-lfa diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref index 26f0dffa7ae2..d86f9ef658d2 100644 --- a/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref @@ -11,14 +11,14 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0003", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 64, "state": "up" }, { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0002", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 64, "state": "up" } diff --git a/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf index f971c658d448..9c4b86208b00 100644 --- a/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf +++ b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf @@ -15,6 +15,7 @@ interface lo interface eth-sw1 ip router isis 1 ipv6 router isis 1 + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -22,6 +23,7 @@ interface eth-rt4-1 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -29,6 +31,7 @@ interface eth-rt4-2 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref index 1ea72a528bd5..69b6159d6240 100644 --- a/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref @@ -11,7 +11,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0004", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -30,7 +30,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0004", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -49,14 +49,14 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0001", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 100, "state": "up" }, { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0003", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 64, "state": "up" } diff --git a/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf index 64f091cfed8f..1883575b7b36 100644 --- a/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf +++ b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf @@ -15,6 +15,7 @@ interface lo interface eth-sw1 ip router isis 1 ipv6 router isis 1 + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -22,6 +23,7 @@ interface eth-rt5-1 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -29,6 +31,7 @@ interface eth-rt5-2 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref index d174b4a47510..e75d5fe859bd 100644 --- a/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref @@ -11,7 +11,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0005", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -30,7 +30,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0005", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -49,14 +49,14 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0001", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 100, "state": "up" }, { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0002", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 64, "state": "up" } diff --git a/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf index 9223852f7917..94f80d39d513 100644 --- a/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf +++ b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf @@ -16,6 +16,7 @@ interface eth-rt2-1 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -23,6 +24,7 @@ interface eth-rt2-2 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -30,6 +32,7 @@ interface eth-rt5 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -37,6 +40,7 @@ interface eth-rt6 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref index 2eb64b6fc98d..1d2c55958809 100644 --- a/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref @@ -11,7 +11,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0002", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -30,7 +30,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0002", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -49,7 +49,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0005", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -68,7 +68,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0006", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } diff --git a/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf index a08534cf3062..e83ae9677b1d 100644 --- a/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf +++ b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf @@ -16,6 +16,7 @@ interface eth-rt3-1 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -23,6 +24,7 @@ interface eth-rt3-2 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -30,6 +32,7 @@ interface eth-rt4 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -37,6 +40,7 @@ interface eth-rt6 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref index 1ff8c2cd4e77..f94b09e1613b 100644 --- a/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref @@ -11,7 +11,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0003", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -30,7 +30,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0003", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -49,7 +49,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0004", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -68,7 +68,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0006", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } diff --git a/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf index d92f822b8deb..462492e90950 100644 --- a/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf +++ b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf @@ -16,6 +16,7 @@ interface eth-rt4 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! @@ -23,6 +24,7 @@ interface eth-rt5 ip router isis 1 ipv6 router isis 1 isis network point-to-point + isis hello-interval 1 isis hello-multiplier 3 isis fast-reroute ti-lfa ! diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref index 734832358f70..9d4d47b2afd1 100644 --- a/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref +++ b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref @@ -11,7 +11,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0004", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } @@ -30,7 +30,7 @@ { "neighbor-sys-type": "level-1", "neighbor-sysid": "0000.0000.0005", - "hold-timer": 9, + "hold-timer": 3, "neighbor-priority": 0, "state": "up" } diff --git a/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py index 1ed52ab76d5c..f0724b9da671 100755 --- a/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py +++ b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py @@ -1021,7 +1021,8 @@ def test_rt6_step14(): rname, "show ipv6 route isis json", outputs[rname][11]["show_ipv6_route.ref"], - count=10, + count=20, + wait=2, ) router_compare_json_output( rname, diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index baacba613dd9..48e9d5336fdf 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -572,6 +572,98 @@ def test_isis_advertise_passive_only(): assert result is True, result +def test_isis_hello_padding_during_adjacency_formation(): + """Check that IIH packets is only padded when adjacency is still being formed + when isis hello padding during-adjacency-formation is configured + """ + tgen = get_topogen() + net = get_topogen().net + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Testing isis hello padding during-adjacency-formation behavior") + r3 = tgen.gears["r3"] + + # Reduce hello-multiplier to make the adjacency go down faster. + r3.vtysh_cmd( + """ + configure + interface r3-eth0 + isis hello-multiplier 2 + """ + ) + + r1 = tgen.gears["r1"] + cmd_output = r1.vtysh_cmd( + """ + configure + interface r1-eth0 + isis hello padding during-adjacency-formation + end + debug isis adj-packets + """ + ) + result = check_last_iih_packet_for_padding(r1, expect_padding=False) + assert result is True, result + + r3.vtysh_cmd( + """ + configure + interface r3-eth0 + shutdown + """ + ) + result = check_last_iih_packet_for_padding(r1, expect_padding=True) + assert result is True, result + + r3 = tgen.gears["r3"] + r3.vtysh_cmd( + """ + configure + interface r3-eth0 + no shutdown + """ + ) + result = check_last_iih_packet_for_padding(r1, expect_padding=False) + assert result is True, result + + +@retry(retry_timeout=5) +def check_last_iih_packet_for_padding(router, expect_padding): + logfilename = "{}/{}".format(router.gearlogdir, "isisd.log") + last_hello_packet_line = None + with open(logfilename, "r") as f: + lines = f.readlines() + for line in lines: + if re.search("Sending .+? IIH", line): + last_hello_packet_line = line + + if last_hello_packet_line is None: + return "Expected IIH packet in {}, but no packet found".format(logfilename) + + interface_name, packet_length = re.search( + r"Sending .+ IIH on (.+), length (\d+)", last_hello_packet_line + ).group(1, 2) + packet_length = int(packet_length) + interface_output = router.vtysh_cmd("show interface {} json".format(interface_name)) + interface_json = json.loads(interface_output) + padded_packet_length = interface_json[interface_name]["mtu"] - 3 + if expect_padding: + if packet_length == padded_packet_length: + return True + return ( + "Expected padded packet with length {}, got packet with length {}".format( + padded_packet_length, packet_length + ) + ) + if packet_length < padded_packet_length: + return True + return "Expected unpadded packet with length less than {}, got packet with length {}".format( + padded_packet_length, packet_length + ) + + @retry(retry_timeout=5) def check_advertised_prefixes(router, lsp_id, expected_prefixes): output = router.vtysh_cmd("show isis database detail {}".format(lsp_id)) diff --git a/tests/topotests/kinds.yaml b/tests/topotests/kinds.yaml new file mode 100644 index 000000000000..127790ed07d8 --- /dev/null +++ b/tests/topotests/kinds.yaml @@ -0,0 +1,30 @@ +version: 1 +kinds: + - name: frr + cmd: | + chown frr:frr -R /var/run/frr + chown frr:frr -R /var/log/frr + /usr/lib/frr/frrinit.sh start + tail -F /var/log/frr/frr.log + cleanup-cmd: | + /usr/lib/frr/frrinit.sh stop + volumes: + - "./%NAME%:/etc/frr" + - "%RUNDIR%/var.log.frr:/var/log/frr" + - "%RUNDIR%/var.run.frr:/var/run/frr" + cap-add: + - SYS_ADMIN + - AUDIT_WRITE + merge: ["volumes"] +cli: + commands: + - name: "" + exec: "vtysh -c '{}'" + format: "[ROUTER ...] COMMAND" + help: "execute vtysh COMMAND on the router[s]" + kinds: ["frr"] + - name: "vtysh" + exec: "/usr/bin/vtysh" + format: "vtysh ROUTER [ROUTER ...]" + new-window: true + kinds: ["frr"] 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 index 63281e9be33d..c1c231de3d3c 100644 --- 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 @@ -2,9 +2,9 @@ "neighbors":{ "2.2.2.2":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"r1-eth0:10.0.1.1" } ] 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 index f361d605ce30..ee69af5e2377 100644 --- 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 @@ -2,25 +2,25 @@ "neighbors":{ "1.1.1.1":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth0:10.0.1.2" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r2-eth1:10.0.2.2" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r2-eth1:10.0.2.2" } ] 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 index 38794357ff5b..3f76542e9421 100644 --- 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 @@ -2,17 +2,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r3-eth0:10.0.2.3" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r3-eth0:10.0.2.3" } ] 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 index fccca693b92b..5395cd25c990 100644 --- 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 @@ -3,17 +3,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r4-eth0:10.0.2.4" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r4-eth0:10.0.2.4" } ] 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 index 63281e9be33d..c1c231de3d3c 100644 --- a/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json @@ -2,9 +2,9 @@ "neighbors":{ "2.2.2.2":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"r1-eth0:10.0.1.1" } ] 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 index f361d605ce30..ee69af5e2377 100644 --- a/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json @@ -2,25 +2,25 @@ "neighbors":{ "1.1.1.1":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth0:10.0.1.2" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r2-eth1:10.0.2.2" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r2-eth1:10.0.2.2" } ] 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 index 38794357ff5b..3f76542e9421 100644 --- a/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json @@ -2,17 +2,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r3-eth0:10.0.2.3" } ], "4.4.4.4":[ { - "priority":3, + "nbrPriority":3, "converged":"Full", - "address":"10.0.2.4", + "ifaceAddress":"10.0.2.4", "ifaceName":"r3-eth0:10.0.2.3" } ] 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 index fccca693b92b..5395cd25c990 100644 --- a/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json @@ -3,17 +3,17 @@ "neighbors":{ "2.2.2.2":[ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"r4-eth0:10.0.2.4" } ], "3.3.3.3":[ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"r4-eth0:10.0.2.4" } ] diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json index 7efde22f3f3d..e25523d18d72 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.2", + "ifaceAddress": "10.0.1.2", "ifaceName": "r1-eth1:10.0.1.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", + "ifaceAddress": "10.0.2.3", "ifaceName": "r1-eth2:10.0.2.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json index 5bea193e01a2..fa2ea86d67ea 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "3.3.3.3": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json index 9966297d8a71..bf77e088d5df 100644 --- a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.1", + "ifaceAddress":"10.0.2.1", "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "2.2.2.2": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.2", + "ifaceAddress":"10.0.3.2", "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json index d9192f1104a4..f47c2dfad771 100644 --- a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json @@ -2,12 +2,12 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.2", - "requestCounter": 0 + "ifaceAddress": "10.0.1.2", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json index ea78592bd511..901282f876e6 100644 --- a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json @@ -2,40 +2,40 @@ "neighbors": { "1.1.1.1": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.1.1", - "requestCounter": 0 + "ifaceAddress": "10.0.1.1", + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", - "requestCounter": 0 + "ifaceAddress": "10.0.2.3", + "linkStateRequestListCounter": 0 }, { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.3.3", - "requestCounter": 0 + "ifaceAddress": "10.0.3.3", + "linkStateRequestListCounter": 0 } ], "4.4.4.4": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.4", - "requestCounter": 0 + "ifaceAddress": "10.0.2.4", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json index d3c50247eae7..164040ae3e02 100644 --- a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json @@ -2,30 +2,30 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.2", - "requestCounter": 0 + "ifaceAddress": "10.0.2.2", + "linkStateRequestListCounter": 0 }, { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.3.2", - "requestCounter": 0 + "ifaceAddress": "10.0.3.2", + "linkStateRequestListCounter": 0 } ], "4.4.4.4": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.4", - "requestCounter": 0 + "ifaceAddress": "10.0.2.4", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json index 20751a288447..98c759a6ff3a 100644 --- a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json @@ -2,22 +2,22 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.2", - "requestCounter": 0 + "ifaceAddress": "10.0.2.2", + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 1, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 1, "converged": "Full", - "address": "10.0.2.3", - "requestCounter": 0 + "ifaceAddress": "10.0.2.3", + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json index 90c8195416f1..9acb4f7b8c2f 100644 --- a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "2.2.2.2": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 2, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 2, "converged": "Full", - "address": "10.0.1.2", + "ifaceAddress": "10.0.1.2", "ifaceName": "r1-eth1:10.0.1.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ], "3.3.3.3": [ { - "dbSummaryCounter": 0, - "retransmitCounter": 0, - "priority": 2, + "databaseSummaryListCounter": 0, + "linkStateRetransmissionListCounter": 0, + "nbrPriority": 2, "converged": "Full", - "address": "10.0.2.3", + "ifaceAddress": "10.0.2.3", "ifaceName": "r1-eth2:10.0.2.1", - "requestCounter": 0 + "linkStateRequestListCounter": 0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json index 29dde53c6d7a..66341999020b 100644 --- a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"r2-eth1:10.0.1.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "3.3.3.3": [ { - "priority":2, + "nbrPriority":2, "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"r2-eth2:10.0.3.2", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json index 9966297d8a71..bf77e088d5df 100644 --- a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json @@ -2,24 +2,24 @@ "neighbors": { "1.1.1.1": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.2.1", + "ifaceAddress":"10.0.2.1", "ifaceName":"r3-eth1:10.0.2.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "2.2.2.2": [ { - "priority":1, + "nbrPriority":1, "converged":"Full", - "address":"10.0.3.2", + "ifaceAddress":"10.0.3.2", "ifaceName":"r3-eth2:10.0.3.3", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index a0b12b2d6425..21d4567d6ba0 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -50,6 +50,7 @@ def create_router_bgp(tgen, topo=None, input_dict=None, build=False, load_config "bgp": { "local_as": "200", "router_id": "22.22.22.22", + "bgp_always_compare_med": True, "graceful-restart": { "graceful-restart": True, "preserve-fw-state": True, @@ -164,7 +165,6 @@ def create_router_bgp(tgen, topo=None, input_dict=None, build=False, load_config router, ) else: - ipv4_data = bgp_addr_data.setdefault("ipv4", {}) ipv6_data = bgp_addr_data.setdefault("ipv6", {}) l2vpn_data = bgp_addr_data.setdefault("l2vpn", {}) @@ -344,6 +344,13 @@ def __create_bgp_global(tgen, input_dict, router, build=False): config_data.append(cmd) + if "bgp_always_compare_med" in bgp_data: + bgp_always_compare_med = bgp_data["bgp_always_compare_med"] + if bgp_always_compare_med == True: + config_data.append("bgp always-compare-med") + elif bgp_always_compare_med == False: + config_data.append("no bgp always-compare-med") + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return config_data @@ -530,6 +537,7 @@ def __create_bgp_unicast_neighbor( config_data.extend(neigh_addr_data) + config_data.append("exit") logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) return config_data @@ -645,7 +653,6 @@ def __create_l2vpn_evpn_address_family( if advertise_data: for address_type, unicast_type in advertise_data.items(): - if type(unicast_type) is dict: for key, value in unicast_type.items(): cmd = "advertise {} {}".format(address_type, key) @@ -1651,7 +1658,6 @@ def modify_as_number(tgen, topo, input_dict): 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(): @@ -1953,7 +1959,6 @@ def clear_bgp_and_verify(tgen, topo, router, rid=None): total_peer = 0 for addr_type in bgp_addr_type.keys(): - if not check_address_types(addr_type): continue @@ -2022,7 +2027,6 @@ def clear_bgp_and_verify(tgen, topo, router, rid=None): 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...", @@ -3278,7 +3282,6 @@ def verify_graceful_restart( 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"][ @@ -3326,7 +3329,6 @@ def verify_graceful_restart( if rmode is None: if "graceful-restart" in input_dict[peer]["bgp"]: - if ( "graceful-restart" in input_dict[peer]["bgp"]["graceful-restart"] @@ -3628,7 +3630,6 @@ def verify_eor(tgen, topo, addr_type, input_dict, dut, peer, expected=True): 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 @@ -3918,7 +3919,6 @@ def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer) "timer" ].items(): if rs_timer == "restart-time": - receivedTimer = value if ( show_bgp_graceful_json_out["timers"][ @@ -4777,7 +4777,6 @@ def get_prefix_count_route( tgen = get_topogen() for router, rnode in tgen.routers().items(): if router == dut: - if vrf: ipv4_cmd = "sh ip bgp vrf {} summary json".format(vrf) show_bgp_json_ipv4 = run_frr_cmd(rnode, ipv4_cmd, isjson=True) @@ -4871,7 +4870,6 @@ def verify_rib_default_route( connected_routes = {} for router, rnode in tgen.routers().items(): if router == dut: - ipv4_routes = run_frr_cmd(rnode, "sh ip bgp json", isjson=True) ipv6_routes = run_frr_cmd(rnode, "sh ip bgp ipv6 unicast json", isjson=True) is_ipv4_default_attrib_found = False diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py index cd449cb50c72..699c7a4da612 100644 --- a/tests/topotests/lib/bgprib.py +++ b/tests/topotests/lib/bgprib.py @@ -25,6 +25,7 @@ import json import re + # gpz: get rib in json form and compare against desired routes class BgpRib: def log(self, str): diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 3e02769d873a..e19d96f918ec 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -5,6 +5,7 @@ # ("NetDEF") in this file. # +import functools import ipaddress import json import os @@ -13,7 +14,6 @@ import subprocess import sys import traceback -import functools from collections import OrderedDict from copy import deepcopy from datetime import datetime, timedelta @@ -32,6 +32,8 @@ from lib.topogen import TopoRouter, get_topogen from lib.topolog import get_logger, logger from lib.topotest import frr_unicode, interface_set_status, version_cmp +from munet.testing.util import pause_test + from lib import topotest FRRCFG_FILE = "frr_json.conf" @@ -110,6 +112,7 @@ "debug zebra vxlan", "debug zebra nht", ], + "mgmt": [], "ospf": [ "debug ospf event", "debug ospf ism", @@ -242,7 +245,6 @@ def run_frr_cmd(rnode, cmd, isjson=False): def apply_raw_config(tgen, input_dict): - """ API to configure raw configuration on device. This can be used for any cli which has not been implemented in JSON. @@ -446,10 +448,11 @@ def check_router_status(tgen): try: router_list = tgen.routers() for router, rnode in router_list.items(): - result = rnode.check_router_running() if result != "": daemons = [] + if "mgmtd" in result: + daemons.append("mgmtd") if "bgpd" in result: daemons.append("bgpd") if "zebra" in result: @@ -489,7 +492,7 @@ def save_initial_config_on_routers(tgen): # Get all running configs in parallel procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -545,7 +548,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -567,7 +570,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info( + logger.debug( "Generating delta for router %s to new configuration (gen %d)", rname, gen ) procs[rname] = tgen.net.popen( @@ -596,7 +599,7 @@ def reset_config_on_routers(tgen, routerName=None): # procs = {} for rname in router_list: - logger.info("Applying delta config on router %s", rname) + logger.debug("Applying delta config on router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname, gen)], @@ -608,7 +611,7 @@ def reset_config_on_routers(tgen, routerName=None): output, _ = p.communicate() vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname, gen)) if not p.returncode: - router_list[rname].logger.info( + router_list[rname].logger.debug( '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format( vtysh_command, output ) @@ -637,7 +640,7 @@ def reset_config_on_routers(tgen, routerName=None): if show_router_config: procs = {} for rname in router_list: - logger.info("Fetching running config for router %s", rname) + logger.debug("Fetching running config for router %s", rname) procs[rname] = router_list[rname].popen( ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"], stdin=None, @@ -654,7 +657,7 @@ def reset_config_on_routers(tgen, routerName=None): output, ) else: - logger.info( + logger.debug( "Configuration on router %s after reset:\n%s", rname, output ) @@ -739,7 +742,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname) with open(frr_cfg_file, "r+") as cfg: data = cfg.read() - logger.info( + logger.debug( "Applying following configuration on router %s (gen: %d):\n%s", rname, gen, @@ -772,7 +775,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): frr_cfg_file = frr_cfg_file_fmt.format(rname) vtysh_command = "vtysh -f " + frr_cfg_file if not p.returncode: - router_list[rname].logger.info( + router_list[rname].logger.debug( '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format( vtysh_command, output ) @@ -818,7 +821,7 @@ def load_config_to_routers(tgen, routers, save_bkup=False): output, ) else: - logger.info("New configuration for router %s:\n%s", rname, output) + logger.debug("New configuration for router %s:\n%s", rname, output) logger.debug("Exiting API: load_config_to_routers") return not errors @@ -954,10 +957,10 @@ def generate_support_bundle(): bundle_procs[rname] = tgen.net[rname].popen(gen_sup_cmd, stdin=None) for rname, rnode in router_list.items(): - logger.info("Waiting on support bundle for %s", rname) + logger.debug("Waiting on support bundle for %s", rname) output, error = bundle_procs[rname].communicate() if output: - logger.info( + logger.debug( "Output from collecting support bundle for %s:\n%s", rname, output ) if error: @@ -1047,6 +1050,11 @@ def start_topology(tgen): feature.add("ospf6") break + # Loading empty mgmtd.conf file to router, to start the mgmtd daemon + router.load_config( + TopoRouter.RD_MGMTD, "{}/{}/mgmtd.conf".format(tgen.logdir, rname) + ) + # Loading empty zebra.conf file to router, to start the zebra deamon router.load_config( TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(tgen.logdir, rname) @@ -1226,15 +1234,15 @@ def add_interfaces_to_vlan(tgen, input_dict): cmd = "ip link add link {} name {} type vlan id {}".format( interface, vlan_intf, vlan ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) # Bringing interface up cmd = "ip link set {} up".format(vlan_intf) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) + logger.debug("result %s", result) # Assigning IP address ifaddr = ipaddress.ip_interface( @@ -1246,146 +1254,9 @@ def add_interfaces_to_vlan(tgen, input_dict): cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( ifaddr.version, vlan_intf, ifaddr ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) result = rnode.run(cmd) - logger.info("result %s", result) - - -def tcpdump_capture_start( - tgen, - router, - intf, - protocol=None, - grepstr=None, - timeout=0, - options=None, - cap_file=None, - background=True, -): - """ - API to capture network packets using tcp dump. - - Packages used : - - Parameters - ---------- - * `tgen`: topogen object. - * `router`: router on which ping has to be performed. - * `intf` : interface for capture. - * `protocol` : protocol for which packet needs to be captured. - * `grepstr` : string to filter out tcp dump output. - * `timeout` : Time for which packet needs to be captured. - * `options` : options for TCP dump, all tcpdump options can be used. - * `cap_file` : filename to store capture dump. - * `background` : Make tcp dump run in back ground. - - Usage - ----- - tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20, - options='-A -vv -x > r2bgp.txt ') - Returns - ------- - 1) True for successful capture - 2) errormsg - when tcp dump fails - """ - - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - - rnode = tgen.gears[router] - - if timeout > 0: - cmd = "timeout {}".format(timeout) - else: - cmd = "" - - cmdargs = "{} tcpdump".format(cmd) - - if intf: - cmdargs += " -i {}".format(str(intf)) - if protocol: - cmdargs += " {}".format(str(protocol)) - if options: - cmdargs += " -s 0 {}".format(str(options)) - - if cap_file: - file_name = os.path.join(tgen.logdir, router, cap_file) - cmdargs += " -w {}".format(str(file_name)) - # Remove existing capture file - rnode.run("rm -rf {}".format(file_name)) - - if grepstr: - cmdargs += ' | grep "{}"'.format(str(grepstr)) - - logger.info("Running tcpdump command: [%s]", cmdargs) - if not background: - rnode.run(cmdargs) - else: - # XXX this & is bogus doesn't work - # rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs)) - rnode.run("nohup {} > /dev/null 2>&1".format(cmdargs)) - - # Check if tcpdump process is running - if background: - result = rnode.run("pgrep tcpdump") - logger.debug("ps -ef | grep tcpdump \n {}".format(result)) - - if not result: - errormsg = "tcpdump is not running {}".format("tcpdump") - return errormsg - else: - logger.info("Packet capture started on %s: interface %s", router, intf) - - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return True - - -def tcpdump_capture_stop(tgen, router): - """ - API to capture network packets using tcp dump. - - Packages used : - - Parameters - ---------- - * `tgen`: topogen object. - * `router`: router on which ping has to be performed. - * `intf` : interface for capture. - * `protocol` : protocol for which packet needs to be captured. - * `grepstr` : string to filter out tcp dump output. - * `timeout` : Time for which packet needs to be captured. - * `options` : options for TCP dump, all tcpdump options can be used. - * `cap2file` : filename to store capture dump. - * `bakgrnd` : Make tcp dump run in back ground. - - Usage - ----- - tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20, - options='-A -vv -x > r2bgp.txt ') - Returns - ------- - 1) True for successful capture - 2) errormsg - when tcp dump fails - """ - - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - - rnode = tgen.gears[router] - - # Check if tcpdump process is running - result = rnode.run("ps -ef | grep tcpdump") - logger.debug("ps -ef | grep tcpdump \n {}".format(result)) - - if not re_search(r"{}".format("tcpdump"), result): - errormsg = "tcpdump is not running {}".format("tcpdump") - return errormsg - else: - # XXX this doesn't work with micronet - ppid = tgen.net.nameToNode[rnode.name].pid - rnode.run("set +m; pkill -P %s tcpdump &> /dev/null" % ppid) - logger.info("Stopped tcpdump capture") - - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return True + logger.debug("result %s", result) def create_debug_log_config(tgen, input_dict, build=False): @@ -1559,12 +1430,16 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): vrf["name"], vrf["id"] ) - logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd) + logger.debug( + "[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) + logger.debug( + "[DUT: %s]: Running kernel cmd [%s]", c_router, cmd + ) rnode.run(cmd) else: @@ -1573,14 +1448,14 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): cmd = "ip link add {} type vrf table {}".format( name, table_id ) - logger.info( + logger.debug( "[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( + logger.debug( "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd ) rnode.run(cmd) @@ -1608,7 +1483,7 @@ def create_vrf_cfg(tgen, topo, input_dict=None, build=False): interface_name, _vrf ) - logger.info( + logger.debug( "[DUT: %s]: Running" " kernel cmd [%s]", c_router, cmd, @@ -1675,7 +1550,7 @@ def create_interface_in_kernel( cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format( ifaddr.version, name, ifaddr ) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) if vrf: @@ -1707,7 +1582,7 @@ def shutdown_bringup_interface_in_kernel(tgen, dut, intf_name, ifaceaction=False action = "down" cmd = "{} {} {}".format(cmd, intf_name, action) - logger.info("[DUT: %s]: Running command: %s", dut, cmd) + logger.debug("[DUT: %s]: Running command: %s", dut, cmd) rnode.run(cmd) @@ -1902,7 +1777,6 @@ def interface_status(tgen, topo, input_dict): rlist = [] 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: @@ -1960,7 +1834,7 @@ def func_retry(*args, **kwargs): ) if initial_wait > 0: - logger.info("Waiting for [%s]s as initial delay", initial_wait) + logger.debug("Waiting for [%s]s as initial delay", initial_wait) sleep(initial_wait) invert_logic = not _expected @@ -2019,13 +1893,13 @@ def func_retry(*args, **kwargs): return saved_failure if saved_failure: - logger.info( + logger.debug( "RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short", retry_sleep, seconds_left, ) else: - logger.info( + logger.debug( "Sleeping %ds until next retry with %.1f retry time left", retry_sleep, seconds_left, @@ -2060,6 +1934,8 @@ def step(msg, reset=False): * ` msg` : Step message body. * `reset` : Reset step count to 1 when set to True. """ + if bool(topotest.g_pytest_config.get_option("--pause")): + pause_test("before :" + msg) _step = Stepper() _step(msg, reset) @@ -2177,6 +2053,7 @@ def _create_interfaces_ospf_cfg(ospf, c_data, data, ospf_keywords): interface_data += _create_interfaces_ospf_cfg( "ospf6", c_data, data, ospf_keywords + ["area"] ) + interface_data.append("exit") if interface_data: interface_data_dict[c_router] = interface_data @@ -2517,7 +2394,6 @@ def create_route_maps(tgen, input_dict, build=False): continue rmap_data = [] for rmap_name, rmap_value in input_dict[router]["route_maps"].items(): - for rmap_dict in rmap_value: del_action = rmap_dict.setdefault("delete", False) @@ -2590,7 +2466,7 @@ def create_route_maps(tgen, input_dict, build=False): nexthop = set_data.setdefault("nexthop", None) origin = set_data.setdefault("origin", None) ext_comm_list = set_data.setdefault("extcommunity", {}) - metrictype = set_data.setdefault("metric-type", {}) + metrictype = set_data.setdefault("metric-type", None) # Local Preference if local_preference: @@ -2990,7 +2866,6 @@ def addKernelRoute( 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: @@ -3181,7 +3056,6 @@ def configure_brctl(tgen, topo, input_dict): 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", []) @@ -3191,7 +3065,6 @@ def configure_brctl(tgen, topo, input_dict): 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 @@ -3283,196 +3156,6 @@ def configure_interface_mac(tgen, input_dict): return True -def socat_send_mld_join( - tgen, - server, - protocol_option, - mld_groups, - send_from_intf, - send_from_intf_ip=None, - port=12345, - reuseaddr=True, -): - """ - API to send MLD join using SOCAT tool - - Parameters: - ----------- - * `tgen` : Topogen object - * `server`: iperf server, from where IGMP join would be sent - * `protocol_option`: Protocol options, ex: UDP6-RECV - * `mld_groups`: IGMP group for which join has to be sent - * `send_from_intf`: Interface from which join would be sent - * `send_from_intf_ip`: Interface IP, default is None - * `port`: Port to be used, default is 12345 - * `reuseaddr`: True|False, bydefault True - - returns: - -------- - errormsg or True - """ - - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - - rnode = tgen.routers()[server] - socat_args = "socat -u " - - # UDP4/TCP4/UDP6/UDP6-RECV/UDP6-SEND - if protocol_option: - socat_args += "{}".format(protocol_option) - - if port: - socat_args += ":{},".format(port) - - if reuseaddr: - socat_args += "{},".format("reuseaddr") - - # Group address range to cover - if mld_groups: - if not isinstance(mld_groups, list): - mld_groups = [mld_groups] - - for mld_group in mld_groups: - socat_cmd = socat_args - join_option = "ipv6-join-group" - - if send_from_intf and not send_from_intf_ip: - socat_cmd += "{}='[{}]:{}'".format(join_option, mld_group, send_from_intf) - else: - socat_cmd += "{}='[{}]:{}:[{}]'".format( - join_option, mld_group, send_from_intf, send_from_intf_ip - ) - - socat_cmd += " STDOUT" - - socat_cmd += " &>{}/socat.logs &".format(tgen.logdir) - - # Run socat command to send IGMP join - logger.info("[DUT: {}]: Running command: [{}]".format(server, socat_cmd)) - output = rnode.run("set +m; {} sleep 0.5".format(socat_cmd)) - - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return True - - -def socat_send_pim6_traffic( - tgen, - server, - protocol_option, - mld_groups, - send_from_intf, - port=12345, - multicast_hops=True, -): - """ - API to send pim6 data taffic using SOCAT tool - - Parameters: - ----------- - * `tgen` : Topogen object - * `server`: iperf server, from where IGMP join would be sent - * `protocol_option`: Protocol options, ex: UDP6-RECV - * `mld_groups`: MLD group for which join has to be sent - * `send_from_intf`: Interface from which join would be sent - * `port`: Port to be used, default is 12345 - * `multicast_hops`: multicast-hops count, default is 255 - - returns: - -------- - errormsg or True - """ - - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - - rnode = tgen.routers()[server] - socat_args = "socat -u STDIO " - - # UDP4/TCP4/UDP6/UDP6-RECV/UDP6-SEND - if protocol_option: - socat_args += "'{}".format(protocol_option) - - # Group address range to cover - if mld_groups: - if not isinstance(mld_groups, list): - mld_groups = [mld_groups] - - for mld_group in mld_groups: - socat_cmd = socat_args - if port: - socat_cmd += ":[{}]:{},".format(mld_group, port) - - if send_from_intf: - socat_cmd += "interface={0},so-bindtodevice={0},".format(send_from_intf) - - if multicast_hops: - socat_cmd += "multicast-hops=255'" - - socat_cmd += " &>{}/socat.logs &".format(tgen.logdir) - - # Run socat command to send pim6 traffic - logger.info( - "[DUT: {}]: Running command: [set +m; ( while sleep 1; do date; done ) | {}]".format( - server, socat_cmd - ) - ) - - # Open a shell script file and write data to it, which will be - # used to send pim6 traffic continously - traffic_shell_script = "{}/{}/traffic.sh".format(tgen.logdir, server) - with open("{}".format(traffic_shell_script), "w") as taffic_sh: - taffic_sh.write( - "#!/usr/bin/env bash\n( while sleep 1; do date; done ) | {}\n".format( - socat_cmd - ) - ) - - rnode.run("chmod 755 {}".format(traffic_shell_script)) - output = rnode.run("{} &> /dev/null".format(traffic_shell_script)) - - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return True - - -def kill_socat(tgen, dut=None, action=None): - """ - Killing socat process if running for any router in topology - - Parameters: - ----------- - * `tgen` : Topogen object - * `dut` : Any iperf hostname to send igmp prune - * `action`: to kill mld join using socat - to kill mld traffic using socat - - Usage: - ------ - kill_socat(tgen, dut ="i6", action="remove_mld_join") - - """ - - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - - router_list = tgen.routers() - for router, rnode in router_list.items(): - if dut is not None and router != dut: - continue - - if action == "remove_mld_join": - cmd = "ps -ef | grep socat | grep UDP6-RECV | grep {}".format(router) - elif action == "remove_mld_traffic": - cmd = "ps -ef | grep socat | grep UDP6-SEND | grep {}".format(router) - else: - cmd = "ps -ef | grep socat".format(router) - - awk_cmd = "awk -F' ' '{print $2}' | xargs kill -9 &>/dev/null &" - cmd = "{} | {}".format(cmd, awk_cmd) - - logger.debug("[DUT: {}]: Running command: [{}]".format(router, cmd)) - rnode.run(cmd) - - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - - ############################################# # Verification APIs ############################################# @@ -3565,7 +3248,6 @@ def verify_rib( 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"]) @@ -4004,7 +3686,6 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None, protocol= 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"]) diff --git a/tests/topotests/lib/grpc-query.py b/tests/topotests/lib/grpc-query.py index 6457bbefdd54..5dd12d581edd 100755 --- a/tests/topotests/lib/grpc-query.py +++ b/tests/topotests/lib/grpc-query.py @@ -21,7 +21,8 @@ import grpc import grpc_tools - from micronet import commander + sys.path.append(os.path.dirname(CWD)) + from munet.base import commander commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .") commander.cmd_raises( diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 9aef41cd1cd7..1eb88f26d4c5 100644 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -177,7 +177,17 @@ def execTestFile(self, tstFile): self.log("unable to read: " + tstFile) sys.exit(1) - def command(self, target, command, regexp, op, result, returnJson, startt=None, force_result=False): + def command( + self, + target, + command, + regexp, + op, + result, + returnJson, + startt=None, + force_result=False, + ): global net if op == "jsoncmp_pass" or op == "jsoncmp_fail": returnJson = True @@ -326,7 +336,9 @@ def wait( if strict and (wait_count == 1): force_result = True - found = self.command(target, command, regexp, op, result, returnJson, startt, force_result) + found = self.command( + target, command, regexp, op, result, returnJson, startt, force_result + ) if found is not False: break @@ -342,6 +354,7 @@ def wait( # initialized by luStart LUtil = None + # entry calls def luStart( baseScriptDir=".", @@ -455,6 +468,7 @@ def luShowFail(): if printed > 0: logger.error("See %s for details of errors" % LUtil.fout_name) + # # Sets default wait type for luCommand(op="wait) (may be overridden by # specifying luCommand(op="wait-strict") or luCommand(op="wait-nostrict")). diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py index 8a8251010c25..5efbecd5e5bf 100755 --- a/tests/topotests/lib/mcast-tester.py +++ b/tests/topotests/lib/mcast-tester.py @@ -11,6 +11,7 @@ import argparse import json +import ipaddress import os import socket import struct @@ -35,13 +36,16 @@ def interface_name_to_index(name): def multicast_join(sock, ifindex, group, port): "Joins a multicast group." - mreq = struct.pack( - "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex - ) - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind((group, port)) - sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + + if ip_version == 4: + mreq = group.packed + struct.pack("@II", socket.INADDR_ANY, ifindex) + opt = socket.IP_ADD_MEMBERSHIP + else: + mreq = group.packed + struct.pack("@I", ifindex) + opt = socket.IPV6_JOIN_GROUP + sock.bind((str(group), port)) + sock.setsockopt(ip_proto, opt, mreq) # @@ -50,15 +54,14 @@ def multicast_join(sock, ifindex, group, port): parser = argparse.ArgumentParser(description="Multicast RX utility") parser.add_argument("group", help="Multicast IP") parser.add_argument("interface", help="Interface name") +parser.add_argument("--port", type=int, default=1000, help="port to send to") +parser.add_argument("--ttl", type=int, default=16, help="TTL/hops for sending packets") parser.add_argument("--socket", help="Point to topotest UNIX socket") parser.add_argument( "--send", help="Transmit instead of join with interval", type=float, default=0 ) args = parser.parse_args() -ttl = 16 -port = 1000 - # Get interface index/validate. ifindex = interface_name_to_index(args.interface) if ifindex is None: @@ -85,7 +88,12 @@ def multicast_join(sock, ifindex, group, port): # Set topotest socket non blocking so we can multiplex the main loop. toposock.setblocking(False) -msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +args.group = ipaddress.ip_address(args.group) +ip_version = args.group.version +ip_family = socket.AF_INET if ip_version == 4 else socket.AF_INET6 +ip_proto = socket.IPPROTO_IP if ip_version == 4 else socket.IPPROTO_IPV6 + +msock = socket.socket(ip_family, socket.SOCK_DGRAM, socket.IPPROTO_UDP) if args.send > 0: # Prepare multicast bit in that interface. msock.setsockopt( @@ -93,12 +101,18 @@ def multicast_join(sock, ifindex, group, port): 25, struct.pack("%ds" % len(args.interface), args.interface.encode("utf-8")), ) - # Set packets TTL. - msock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", ttl)) + + # Set packets TTL/hops. + ttlopt = socket.IP_MULTICAST_TTL if ip_version == 4 else socket.IPV6_MULTICAST_HOPS + if ip_version == 4: + msock.setsockopt(ip_proto, ttlopt, struct.pack("B", args.ttl)) + else: + msock.setsockopt(ip_proto, ttlopt, struct.pack("I", args.ttl)) + # Block to ensure packet send. msock.setblocking(True) else: - multicast_join(msock, ifindex, args.group, port) + multicast_join(msock, ifindex, args.group, args.port) def should_exit(): @@ -120,7 +134,7 @@ def should_exit(): counter = 0 while not should_exit(): if args.send > 0: - msock.sendto(b"test %d" % counter, (args.group, port)) + msock.sendto(b"test %d" % counter, (str(args.group), args.port)) counter += 1 time.sleep(args.send) diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py index 13810091682e..f4aa8278f17b 100644 --- a/tests/topotests/lib/micronet.py +++ b/tests/topotests/lib/micronet.py @@ -3,1004 +3,22 @@ # # July 9 2021, Christian Hopps <chopps@labn.net> # -# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2021-2023, LabN Consulting, L.L.C. # -import datetime -import logging -import os -import re -import shlex -import subprocess -import sys -import tempfile -import time as time_mod -import traceback - -root_hostname = subprocess.check_output("hostname") - -# This allows us to cleanup any leftovers later on -os.environ["MICRONET_PID"] = str(os.getpid()) - - -class Timeout(object): - def __init__(self, delta): - self.started_on = datetime.datetime.now() - self.expires_on = self.started_on + datetime.timedelta(seconds=delta) - - def elapsed(self): - elapsed = datetime.datetime.now() - self.started_on - return elapsed.total_seconds() - - def is_expired(self): - return datetime.datetime.now() > self.expires_on - - -def is_string(value): - """Return True if value is a string.""" - try: - return isinstance(value, basestring) # type: ignore - except NameError: - return isinstance(value, str) - - -def shell_quote(command): - """Return command wrapped in single quotes.""" - if sys.version_info[0] >= 3: - return shlex.quote(command) - return "'{}'".format(command.replace("'", "'\"'\"'")) # type: ignore - - -def cmd_error(rc, o, e): - s = "rc {}".format(rc) - o = "\n\tstdout: " + o.strip() if o and o.strip() else "" - e = "\n\tstderr: " + e.strip() if e and e.strip() else "" - return s + o + e - - -def proc_error(p, o, e): - args = p.args if is_string(p.args) else " ".join(p.args) - s = "rc {} pid {}\n\targs: {}".format(p.returncode, p.pid, args) - o = "\n\tstdout: " + o.strip() if o and o.strip() else "" - e = "\n\tstderr: " + e.strip() if e and e.strip() else "" - return s + o + e - - -def comm_error(p): - rc = p.poll() - assert rc is not None - if not hasattr(p, "saved_output"): - p.saved_output = p.communicate() - return proc_error(p, *p.saved_output) - - -class Commander(object): # pylint: disable=R0205 - """ - Commander. - - An object that can execute commands. - """ - - tmux_wait_gen = 0 - - def __init__(self, name, logger=None): - """Create a Commander.""" - self.name = name - self.last = None - self.exec_paths = {} - self.pre_cmd = [] - self.pre_cmd_str = "" - - if not logger: - self.logger = logging.getLogger(__name__ + ".commander." + name) - else: - self.logger = logger - - self.cwd = self.cmd_raises("pwd").strip() - - def set_logger(self, logfile): - self.logger = logging.getLogger(__name__ + ".commander." + self.name) - if is_string(logfile): - handler = logging.FileHandler(logfile, mode="w") - else: - handler = logging.StreamHandler(logfile) - - fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format( - self.__class__.__name__, self.name - ) - handler.setFormatter(logging.Formatter(fmt=fmtstr)) - self.logger.addHandler(handler) - - def set_pre_cmd(self, pre_cmd=None): - if not pre_cmd: - self.pre_cmd = [] - self.pre_cmd_str = "" - else: - self.pre_cmd = pre_cmd - self.pre_cmd_str = " ".join(self.pre_cmd) + " " - - def __str__(self): - return "Commander({})".format(self.name) - - def get_exec_path(self, binary): - """Return the full path to the binary executable. - - `binary` :: binary name or list of binary names - """ - if is_string(binary): - bins = [binary] - else: - bins = binary - for b in bins: - if b in self.exec_paths: - return self.exec_paths[b] - - rc, output, _ = self.cmd_status("which " + b, warn=False) - if not rc: - return os.path.abspath(output.strip()) - return None - - def get_tmp_dir(self, uniq): - return os.path.join(tempfile.mkdtemp(), uniq) - - def test(self, flags, arg): - """Run test binary, with flags and arg""" - test_path = self.get_exec_path(["test"]) - rc, output, _ = self.cmd_status([test_path, flags, arg], warn=False) - return not rc - - def path_exists(self, path): - """Check if path exists.""" - return self.test("-e", path) - - def _get_cmd_str(self, cmd): - if is_string(cmd): - return self.pre_cmd_str + cmd - cmd = self.pre_cmd + cmd - return " ".join(cmd) - - def _get_sub_args(self, cmd, defaults, **kwargs): - if is_string(cmd): - defaults["shell"] = True - pre_cmd = self.pre_cmd_str - else: - defaults["shell"] = False - pre_cmd = self.pre_cmd - cmd = [str(x) for x in cmd] - defaults.update(kwargs) - return pre_cmd, cmd, defaults - - def _popen(self, method, cmd, skip_pre_cmd=False, **kwargs): - if sys.version_info[0] >= 3: - defaults = { - "encoding": "utf-8", - "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE, - } - else: - defaults = { - "stdout": subprocess.PIPE, - "stderr": subprocess.PIPE, - } - pre_cmd, cmd, defaults = self._get_sub_args(cmd, defaults, **kwargs) - - self.logger.debug('%s: %s("%s", kwargs: %s)', self, method, cmd, defaults) - - actual_cmd = cmd if skip_pre_cmd else pre_cmd + cmd - p = subprocess.Popen(actual_cmd, **defaults) - if not hasattr(p, "args"): - p.args = actual_cmd - return p, actual_cmd - - def set_cwd(self, cwd): - self.logger.warning("%s: 'cd' (%s) does not work outside namespaces", self, cwd) - self.cwd = cwd - - def popen(self, cmd, **kwargs): - """ - Creates a pipe with the given `command`. - - Args: - command: `str` or `list` of command to open a pipe with. - **kwargs: kwargs is eventually passed on to Popen. If `command` is a string - then will be invoked with shell=True, otherwise `command` is a list and - will be invoked with shell=False. - - Returns: - a subprocess.Popen object. - """ - p, _ = self._popen("popen", cmd, **kwargs) - return p - - def cmd_status(self, cmd, raises=False, warn=True, stdin=None, **kwargs): - """Execute a command.""" - - # We are not a shell like mininet, so we need to intercept this - chdir = False - if not is_string(cmd): - cmds = cmd - else: - # XXX we can drop this when the code stops assuming it works - m = re.match(r"cd(\s*|\s+(\S+))$", cmd) - if m and m.group(2): - self.logger.warning( - "Bad call to 'cd' (chdir) emulating, use self.set_cwd():\n%s", - "".join(traceback.format_stack(limit=12)), - ) - assert is_string(cmd) - chdir = True - cmd += " && pwd" - - # If we are going to run under bash then we don't need shell=True! - cmds = ["/bin/bash", "-c", cmd] - - pinput = None - - if is_string(stdin) or isinstance(stdin, bytes): - pinput = stdin - stdin = subprocess.PIPE - - p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs) - stdout, stderr = p.communicate(input=pinput) - rc = p.wait() - - # For debugging purposes. - self.last = (rc, actual_cmd, cmd, stdout, stderr) - - if rc: - if warn: - self.logger.warning( - "%s: proc failed: %s:", self, proc_error(p, stdout, stderr) - ) - if raises: - # error = Exception("stderr: {}".format(stderr)) - # This annoyingly doesn't' show stderr when printed normally - error = subprocess.CalledProcessError(rc, actual_cmd) - error.stdout, error.stderr = stdout, stderr - raise error - elif chdir: - self.set_cwd(stdout.strip()) - - return rc, stdout, stderr - - def cmd_legacy(self, cmd, **kwargs): - """Execute a command with stdout and stderr joined, *IGNORES ERROR*.""" - - defaults = {"stderr": subprocess.STDOUT} - defaults.update(kwargs) - _, stdout, _ = self.cmd_status(cmd, raises=False, **defaults) - return stdout - - def cmd_raises(self, cmd, **kwargs): - """Execute a command. Raise an exception on errors""" - - rc, stdout, _ = self.cmd_status(cmd, raises=True, **kwargs) - assert rc == 0 - return stdout - - # Run a command in a new window (gnome-terminal, screen, tmux, xterm) - def run_in_window( - self, - cmd, - wait_for=False, - background=False, - name=None, - title=None, - forcex=False, - new_window=False, - tmux_target=None, - ): - """ - Run a command in a new window (TMUX, Screen or XTerm). - - Args: - wait_for: True to wait for exit from command or `str` as channel neme to signal on exit, otherwise False - background: Do not change focus to new window. - title: Title for new pane (tmux) or window (xterm). - name: Name of the new window (tmux) - forcex: Force use of X11. - new_window: Open new window (instead of pane) in TMUX - tmux_target: Target for tmux pane. - - Returns: - the pane/window identifier from TMUX (depends on `new_window`) - """ - - channel = None - if is_string(wait_for): - channel = wait_for - elif wait_for is True: - channel = "{}-wait-{}".format(os.getpid(), Commander.tmux_wait_gen) - Commander.tmux_wait_gen += 1 - - sudo_path = self.get_exec_path(["sudo"]) - nscmd = sudo_path + " " + self.pre_cmd_str + cmd - if "TMUX" in os.environ and not forcex: - cmd = [self.get_exec_path("tmux")] - if new_window: - cmd.append("new-window") - cmd.append("-P") - if name: - cmd.append("-n") - cmd.append(name) - if tmux_target: - cmd.append("-t") - cmd.append(tmux_target) - else: - cmd.append("split-window") - cmd.append("-P") - cmd.append("-h") - if not tmux_target: - tmux_target = os.getenv("TMUX_PANE", "") - if background: - cmd.append("-d") - if tmux_target: - cmd.append("-t") - cmd.append(tmux_target) - if title: - nscmd = "printf '\033]2;{}\033\\'; {}".format(title, nscmd) - if channel: - nscmd = 'trap "tmux wait -S {}; exit 0" EXIT; {}'.format(channel, nscmd) - cmd.append(nscmd) - elif "STY" in os.environ and not forcex: - # wait for not supported in screen for now - channel = None - cmd = [self.get_exec_path("screen")] - if title: - cmd.append("-t") - cmd.append(title) - if not os.path.exists( - "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) - ): - cmd = ["sudo", "-u", os.environ["SUDO_USER"]] + cmd - cmd.extend(nscmd.split(" ")) - elif "DISPLAY" in os.environ: - # We need it broken up for xterm - user_cmd = cmd - cmd = [self.get_exec_path("xterm")] - if "SUDO_USER" in os.environ: - cmd = [self.get_exec_path("sudo"), "-u", os.environ["SUDO_USER"]] + cmd - if title: - cmd.append("-T") - cmd.append(title) - cmd.append("-e") - cmd.append(sudo_path) - cmd.extend(self.pre_cmd) - cmd.extend(["bash", "-c", user_cmd]) - # if channel: - # return self.cmd_raises(cmd, skip_pre_cmd=True) - # else: - p = self.popen( - cmd, - skip_pre_cmd=True, - stdin=None, - shell=False, - ) - time_mod.sleep(2) - if p.poll() is not None: - self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) - return p - else: - self.logger.error( - "DISPLAY, STY, and TMUX not in environment, can't open window" - ) - raise Exception("Window requestd but TMUX, Screen and X11 not available") - - pane_info = self.cmd_raises(cmd, skip_pre_cmd=True).strip() - - # Re-adjust the layout - if "TMUX" in os.environ: - self.cmd_status( - "tmux select-layout -t {} tiled".format( - pane_info if not tmux_target else tmux_target - ), - skip_pre_cmd=True, - ) - - # Wait here if we weren't handed the channel to wait for - if channel and wait_for is True: - cmd = [self.get_exec_path("tmux"), "wait", channel] - self.cmd_status(cmd, skip_pre_cmd=True) - - return pane_info - - def delete(self): - pass - - -class LinuxNamespace(Commander): - """ - A linux Namespace. - - An object that creates and executes commands in a linux namespace - """ - - def __init__( - self, - name, - net=True, - mount=True, - uts=True, - cgroup=False, - ipc=False, - pid=False, - time=False, - user=False, - set_hostname=True, - private_mounts=None, - logger=None, - ): - """ - Create a new linux namespace. - - Args: - name: Internal name for the namespace. - net: Create network namespace. - mount: Create network namespace. - uts: Create UTS (hostname) namespace. - cgroup: Create cgroup namespace. - ipc: Create IPC namespace. - pid: Create PID namespace, also mounts new /proc. - time: Create time namespace. - user: Create user namespace, also keeps capabilities. - set_hostname: Set the hostname to `name`, uts must also be True. - private_mounts: List of strings of the form - "[/external/path:]/internal/path. If no external path is specified a - tmpfs is mounted on the internal path. Any paths specified are first - passed to `mkdir -p`. - logger: Passed to superclass. - """ - super(LinuxNamespace, self).__init__(name, logger) - - self.logger.debug("%s: Creating", self) - - self.intfs = [] - - nslist = [] - cmd = ["/usr/bin/unshare"] - flags = "" - self.a_flags = [] - self.ifnetns = {} - - if cgroup: - nslist.append("cgroup") - flags += "C" - if ipc: - nslist.append("ipc") - flags += "i" - if mount: - nslist.append("mnt") - flags += "m" - if net: - nslist.append("net") - flags += "n" - if pid: - nslist.append("pid") - flags += "f" - flags += "p" - cmd.append("--mount-proc") - if time: - # XXX this filename is probably wrong - nslist.append("time") - flags += "T" - if user: - nslist.append("user") - flags += "U" - cmd.append("--keep-caps") - if uts: - nslist.append("uts") - flags += "u" - - if flags: - aflags = flags.replace("f", "") - if aflags: - self.a_flags = ["-" + x for x in aflags] - cmd.extend(["-" + x for x in flags]) - - if pid: - cmd.append(commander.get_exec_path("tini")) - cmd.append("-vvv") - cmd.append("/bin/cat") - - # Using cat and a stdin PIPE is nice as it will exit when we do. However, we - # also detach it from the pgid so that signals do not propagate to it. This is - # b/c it would exit early (e.g., ^C) then, at least the main micronet proc which - # has no other processes like frr daemons running, will take the main network - # namespace with it, which will remove the bridges and the veth pair (because - # the bridge side veth is deleted). - self.logger.debug("%s: creating namespace process: %s", self, cmd) - p = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=open("/dev/null", "w"), - stderr=open("/dev/null", "w"), - text=True, - start_new_session=True, # detach from pgid so signals don't propagate - shell=False, - ) - self.p = p - self.pid = p.pid - - self.logger.debug("%s: namespace pid: %d", self, self.pid) - - # ----------------------------------------------- - # Now let's wait until unshare completes it's job - # ----------------------------------------------- - timeout = Timeout(30) - while p.poll() is None and not timeout.is_expired(): - for fname in tuple(nslist): - ours = os.readlink("/proc/self/ns/{}".format(fname)) - theirs = os.readlink("/proc/{}/ns/{}".format(self.pid, fname)) - # See if their namespace is different - if ours != theirs: - nslist.remove(fname) - if not nslist: - break - elapsed = int(timeout.elapsed()) - if elapsed <= 3: - time_mod.sleep(0.1) - elif elapsed > 10: - self.logger.warning("%s: unshare taking more than %ss", self, elapsed) - time_mod.sleep(3) - else: - self.logger.info("%s: unshare taking more than %ss", self, elapsed) - time_mod.sleep(1) - assert p.poll() is None, "unshare unexpectedly exited!" - assert not nslist, "unshare never unshared!" - - # Set pre-command based on our namespace proc - self.base_pre_cmd = ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid)] - if not pid: - self.base_pre_cmd.append("-F") - self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd]) - - # Remount sysfs and cgroup to pickup any changes - self.cmd_raises("mount -t sysfs sysfs /sys") - self.cmd_raises( - "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup" - ) - - # Set the hostname to the namespace name - if uts and set_hostname: - # Debugging get the root hostname - self.cmd_raises("hostname " + self.name) - nroot = subprocess.check_output("hostname") - if root_hostname != nroot: - result = self.p.poll() - assert root_hostname == nroot, "STATE of namespace process {}".format( - result - ) - - if private_mounts: - if is_string(private_mounts): - private_mounts = [private_mounts] - for m in private_mounts: - s = m.split(":", 1) - if len(s) == 1: - self.tmpfs_mount(s[0]) - else: - self.bind_mount(s[0], s[1]) - - o = self.cmd_legacy("ls -l /proc/{}/ns".format(self.pid)) - self.logger.debug("namespaces:\n %s", o) - - # Doing this here messes up all_protocols ipv6 check - self.cmd_raises("ip link set lo up") - - def __str__(self): - return "LinuxNamespace({})".format(self.name) - - def tmpfs_mount(self, inner): - self.cmd_raises("mkdir -p " + inner) - self.cmd_raises("mount -n -t tmpfs tmpfs " + inner) - - def bind_mount(self, outer, inner): - self.cmd_raises("mkdir -p " + inner) - self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) - - def add_vlan(self, vlanname, linkiface, vlanid): - self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises( - [ - ip_path, - "link", - "add", - "link", - linkiface, - "name", - vlanname, - "type", - "vlan", - "id", - vlanid, - ] - ) - self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) - - def add_loop(self, loopname): - self.logger.debug("Adding Linux iface: %s", loopname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) - self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) - - def add_l3vrf(self, vrfname, tableid): - self.logger.debug("Adding Linux VRF: %s", vrfname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises( - [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] - ) - self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) - - def del_iface(self, iface): - self.logger.debug("Removing Linux Iface: %s", iface) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "link", "del", iface]) - - def attach_iface_to_l3vrf(self, ifacename, vrfname): - self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - if vrfname: - self.cmd_raises( - [ip_path, "link", "set", "dev", ifacename, "master", vrfname] - ) - else: - self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) - - def add_netns(self, ns): - self.logger.debug("Adding network namespace %s", ns) - - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - if os.path.exists("/run/netns/{}".format(ns)): - self.logger.warning("%s: Removing existing nsspace %s", self, ns) - try: - self.delete_netns(ns) - except Exception as ex: - self.logger.warning( - "%s: Couldn't remove existing nsspace %s: %s", - self, - ns, - str(ex), - exc_info=True, - ) - self.cmd_raises([ip_path, "netns", "add", ns]) - - def delete_netns(self, ns): - self.logger.debug("Deleting network namespace %s", ns) - - ip_path = self.get_exec_path("ip") - assert ip_path, "XXX missing ip command!" - self.cmd_raises([ip_path, "netns", "delete", ns]) - - def set_intf_netns(self, intf, ns, up=False): - # In case a user hard-codes 1 thinking it "resets" - ns = str(ns) - if ns == "1": - ns = str(self.pid) - - self.logger.debug("Moving interface %s to namespace %s", intf, ns) - - cmd = "ip link set {} netns " + ns - if up: - cmd += " up" - self.intf_ip_cmd(intf, cmd) - if ns == str(self.pid): - # If we are returning then remove from dict - if intf in self.ifnetns: - del self.ifnetns[intf] - else: - self.ifnetns[intf] = ns - - def reset_intf_netns(self, intf): - self.logger.debug("Moving interface %s to default namespace", intf) - self.set_intf_netns(intf, str(self.pid)) - - def intf_ip_cmd(self, intf, cmd): - """Run an ip command for considering an interfaces possible namespace. - - `cmd` - format is run using the interface name on the command - """ - if intf in self.ifnetns: - assert cmd.startswith("ip ") - cmd = "ip -n " + self.ifnetns[intf] + cmd[2:] - self.cmd_raises(cmd.format(intf)) - - def set_cwd(self, cwd): - # Set pre-command based on our namespace proc - self.logger.debug("%s: new CWD %s", self, cwd) - self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + cwd]) - - def register_interface(self, ifname): - if ifname not in self.intfs: - self.intfs.append(ifname) - - def delete(self): - if self.p and self.p.poll() is None: - if sys.version_info[0] >= 3: - try: - self.p.terminate() - self.p.communicate(timeout=10) - except subprocess.TimeoutExpired: - self.p.kill() - self.p.communicate(timeout=2) - else: - self.p.kill() - self.p.communicate() - self.set_pre_cmd(["/bin/false"]) - - -class SharedNamespace(Commander): - """ - Share another namespace. - - An object that executes commands in an existing pid's linux namespace - """ - - def __init__(self, name, pid, aflags=("-a",), logger=None): - """ - Share a linux namespace. - - Args: - name: Internal name for the namespace. - pid: PID of the process to share with. - """ - super(SharedNamespace, self).__init__(name, logger) - - self.logger.debug("%s: Creating", self) - - self.pid = pid - self.intfs = [] - self.a_flags = aflags - - # Set pre-command based on our namespace proc - self.set_pre_cmd( - ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + self.cwd] - ) - - def __str__(self): - return "SharedNamespace({})".format(self.name) - - def set_cwd(self, cwd): - # Set pre-command based on our namespace proc - self.logger.debug("%s: new CWD %s", self, cwd) - self.set_pre_cmd( - ["/usr/bin/nsenter", *self.a_flags, "-t", str(self.pid), "--wd=" + cwd] - ) - - def register_interface(self, ifname): - if ifname not in self.intfs: - self.intfs.append(ifname) - - -class Bridge(SharedNamespace): - """ - A linux bridge. - """ - - next_brid_ord = 0 - - @classmethod - def _get_next_brid(cls): - brid_ord = cls.next_brid_ord - cls.next_brid_ord += 1 - return brid_ord - - def __init__(self, name=None, unet=None, logger=None): - """Create a linux Bridge.""" - - self.unet = unet - self.brid_ord = self._get_next_brid() - if name: - self.brid = name - else: - self.brid = "br{}".format(self.brid_ord) - name = self.brid - - super(Bridge, self).__init__(name, unet.pid, aflags=unet.a_flags, logger=logger) - - self.logger.debug("Bridge: Creating") - - assert len(self.brid) <= 16 # Make sure fits in IFNAMSIZE - self.cmd_raises("ip link delete {} || true".format(self.brid)) - self.cmd_raises("ip link add {} type bridge".format(self.brid)) - self.cmd_raises("ip link set {} up".format(self.brid)) - - self.logger.debug("%s: Created, Running", self) - - def __str__(self): - return "Bridge({})".format(self.brid) - - def delete(self): - """Stop the bridge (i.e., delete the linux resources).""" - - rc, o, e = self.cmd_status("ip link show {}".format(self.brid), warn=False) - if not rc: - rc, o, e = self.cmd_status( - "ip link delete {}".format(self.brid), warn=False - ) - if rc: - self.logger.error( - "%s: error deleting bridge %s: %s", - self, - self.brid, - cmd_error(rc, o, e), - ) - else: - self.logger.debug("%s: Deleted.", self) - - -class Micronet(LinuxNamespace): # pylint: disable=R0205 - """ - Micronet. - """ - - def __init__(self): - """Create a Micronet.""" - - self.hosts = {} - self.switches = {} - self.links = {} - self.macs = {} - self.rmacs = {} - - super(Micronet, self).__init__("micronet", mount=True, net=True, uts=True) - - self.logger.debug("%s: Creating", self) - - def __str__(self): - return "Micronet()" - - def __getitem__(self, key): - if key in self.switches: - return self.switches[key] - return self.hosts[key] - - def add_host(self, name, cls=LinuxNamespace, **kwargs): - """Add a host to micronet.""" - - self.logger.debug("%s: add_host %s", self, name) - - self.hosts[name] = cls(name, **kwargs) - # Create a new mounted FS for tracking nested network namespaces creatd by the - # user with `ip netns add` - self.hosts[name].tmpfs_mount("/run/netns") - - def add_link(self, name1, name2, if1, if2): - """Add a link between switch and host to micronet.""" - isp2p = False - if name1 in self.switches: - assert name2 in self.hosts - elif name2 in self.switches: - assert name1 in self.hosts - name1, name2 = name2, name1 - if1, if2 = if2, if1 - else: - # p2p link - assert name1 in self.hosts - assert name2 in self.hosts - isp2p = True - - lname = "{}:{}-{}:{}".format(name1, if1, name2, if2) - self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "") - self.links[lname] = (name1, if1, name2, if2) - - # And create the veth now. - if isp2p: - lhost, rhost = self.hosts[name1], self.hosts[name2] - lifname = "i1{:x}".format(lhost.pid) - rifname = "i2{:x}".format(rhost.pid) - self.cmd_raises( - "ip link add {} type veth peer name {}".format(lifname, rifname) - ) - - self.cmd_raises("ip link set {} netns {}".format(lifname, lhost.pid)) - lhost.cmd_raises("ip link set {} name {}".format(lifname, if1)) - lhost.cmd_raises("ip link set {} up".format(if1)) - lhost.register_interface(if1) - - self.cmd_raises("ip link set {} netns {}".format(rifname, rhost.pid)) - rhost.cmd_raises("ip link set {} name {}".format(rifname, if2)) - rhost.cmd_raises("ip link set {} up".format(if2)) - rhost.register_interface(if2) - else: - switch = self.switches[name1] - host = self.hosts[name2] - - assert len(if1) <= 16 and len(if2) <= 16 # Make sure fits in IFNAMSIZE - - self.logger.debug("%s: Creating veth pair for link %s", self, lname) - self.cmd_raises( - "ip link add {} type veth peer name {} netns {}".format( - if1, if2, host.pid - ) - ) - self.cmd_raises("ip link set {} netns {}".format(if1, switch.pid)) - switch.register_interface(if1) - host.register_interface(if2) - self.cmd_raises("ip link set {} master {}".format(if1, switch.brid)) - self.cmd_raises("ip link set {} up".format(if1)) - host.cmd_raises("ip link set {} up".format(if2)) - - # Cache the MAC values, and reverse mapping - self.get_mac(name1, if1) - self.get_mac(name2, if2) - - def add_switch(self, name): - """Add a switch to micronet.""" - - self.logger.debug("%s: add_switch %s", self, name) - self.switches[name] = Bridge(name, self) - - def get_mac(self, name, ifname): - if name in self.hosts: - dev = self.hosts[name] - else: - dev = self.switches[name] - - if (name, ifname) not in self.macs: - _, output, _ = dev.cmd_status("ip -o link show " + ifname) - m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output) - mac = m.group(2) - self.macs[(name, ifname)] = mac - self.rmacs[mac] = (name, ifname) - - return self.macs[(name, ifname)] - - def delete(self): - """Delete the micronet topology.""" - - self.logger.debug("%s: Deleting.", self) - - for lname, (_, _, rname, rif) in self.links.items(): - host = self.hosts[rname] - - self.logger.debug("%s: Deleting veth pair for link %s", self, lname) - - rc, o, e = host.cmd_status("ip link delete {}".format(rif), warn=False) - if rc: - self.logger.error( - "Error deleting veth pair %s: %s", lname, cmd_error(rc, o, e) - ) - - self.links = {} - - for host in self.hosts.values(): - try: - host.delete() - except Exception as error: - self.logger.error( - "%s: error while deleting host %s: %s", self, host, error - ) - - self.hosts = {} - - for switch in self.switches.values(): - try: - switch.delete() - except Exception as error: - self.logger.error( - "%s: error while deleting switch %s: %s", self, switch, error - ) - self.switches = {} - - self.logger.debug("%s: Deleted.", self) - - super(Micronet, self).delete() - - -# --------------------------- -# Root level utility function -# --------------------------- - - -def get_exec_path(binary): - base = Commander("base") - return base.get_exec_path(binary) - - -commander = Commander("micronet") +# flake8: noqa + +from munet.base import BaseMunet as Micronet +from munet.base import ( + Bridge, + Commander, + LinuxNamespace, + SharedNamespace, + Timeout, + cmd_error, + comm_error, + commander, + get_exec_path, + proc_error, + root_hostname, + shell_quote, +) diff --git a/tests/topotests/lib/micronet_cli.py b/tests/topotests/lib/micronet_cli.py deleted file mode 100644 index e54b75f71014..000000000000 --- a/tests/topotests/lib/micronet_cli.py +++ /dev/null @@ -1,306 +0,0 @@ -# -*- coding: utf-8 eval: (blacken-mode 1) -*- -# SPDX-License-Identifier: GPL-2.0-or-later -# -# July 24 2021, Christian Hopps <chopps@labn.net> -# -# Copyright (c) 2021, LabN Consulting, L.L.C. -# -import argparse -import logging -import os -import pty -import re -import readline -import select -import socket -import subprocess -import sys -import tempfile -import termios -import tty - - -ENDMARKER = b"\x00END\x00" - - -def lineiter(sock): - s = "" - while True: - sb = sock.recv(256) - if not sb: - return - - s += sb.decode("utf-8") - i = s.find("\n") - if i != -1: - yield s[:i] - s = s[i + 1 :] - - -def spawn(unet, host, cmd): - if sys.stdin.isatty(): - old_tty = termios.tcgetattr(sys.stdin) - tty.setraw(sys.stdin.fileno()) - try: - master_fd, slave_fd = pty.openpty() - - # use os.setsid() make it run in a new process group, or bash job - # control will not be enabled - p = unet.hosts[host].popen( - cmd, - preexec_fn=os.setsid, - stdin=slave_fd, - stdout=slave_fd, - stderr=slave_fd, - universal_newlines=True, - ) - - while p.poll() is None: - r, w, e = select.select([sys.stdin, master_fd], [], [], 0.25) - if sys.stdin in r: - d = os.read(sys.stdin.fileno(), 10240) - os.write(master_fd, d) - elif master_fd in r: - o = os.read(master_fd, 10240) - if o: - os.write(sys.stdout.fileno(), o) - finally: - # restore tty settings back - if sys.stdin.isatty(): - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) - - -def doline(unet, line, writef): - def host_cmd_split(unet, cmd): - csplit = cmd.split() - for i, e in enumerate(csplit): - if e not in unet.hosts: - break - hosts = csplit[:i] - if not hosts: - hosts = sorted(unet.hosts.keys()) - cmd = " ".join(csplit[i:]) - return hosts, cmd - - line = line.strip() - m = re.match(r"^(\S+)(?:\s+(.*))?$", line) - if not m: - return True - - cmd = m.group(1) - oargs = m.group(2) if m.group(2) else "" - if cmd == "q" or cmd == "quit": - return False - if cmd == "hosts": - writef("%% hosts: %s\n" % " ".join(sorted(unet.hosts.keys()))) - elif cmd in ["term", "vtysh", "xterm"]: - args = oargs.split() - if not args or (len(args) == 1 and args[0] == "*"): - args = sorted(unet.hosts.keys()) - hosts = [unet.hosts[x] for x in args if x in unet.hosts] - for host in hosts: - if cmd == "t" or cmd == "term": - host.run_in_window("bash", title="sh-%s" % host) - elif cmd == "v" or cmd == "vtysh": - host.run_in_window("vtysh", title="vt-%s" % host) - elif cmd == "x" or cmd == "xterm": - host.run_in_window("bash", title="sh-%s" % host, forcex=True) - elif cmd == "sh": - hosts, cmd = host_cmd_split(unet, oargs) - for host in hosts: - if sys.stdin.isatty(): - spawn(unet, host, cmd) - else: - if len(hosts) > 1: - writef("------ Host: %s ------\n" % host) - output = unet.hosts[host].cmd_legacy(cmd) - writef(output) - if len(hosts) > 1: - writef("------- End: %s ------\n" % host) - writef("\n") - elif cmd == "h" or cmd == "help": - writef( - """ -Commands: - help :: this help - sh [hosts] <shell-command> :: execute <shell-command> on <host> - term [hosts] :: open shell terminals for hosts - vtysh [hosts] :: open vtysh terminals for hosts - [hosts] <vtysh-command> :: execute vtysh-command on hosts\n\n""" - ) - else: - hosts, cmd = host_cmd_split(unet, line) - for host in hosts: - if len(hosts) > 1: - writef("------ Host: %s ------\n" % host) - output = unet.hosts[host].cmd_legacy('vtysh -c "{}"'.format(cmd)) - writef(output) - if len(hosts) > 1: - writef("------- End: %s ------\n" % host) - writef("\n") - return True - - -def cli_server_setup(unet): - sockdir = tempfile.mkdtemp("-sockdir", "pyt") - sockpath = os.path.join(sockdir, "cli-server.sock") - try: - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(10) - sock.bind(sockpath) - sock.listen(1) - return sock, sockdir, sockpath - except Exception: - unet.cmd_status("rm -rf " + sockdir) - raise - - -def cli_server(unet, server_sock): - sock, addr = server_sock.accept() - - # Go into full non-blocking mode now - sock.settimeout(None) - - for line in lineiter(sock): - line = line.strip() - - def writef(x): - xb = x.encode("utf-8") - sock.send(xb) - - if not doline(unet, line, writef): - return - sock.send(ENDMARKER) - - -def cli_client(sockpath, prompt="unet> "): - sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - sock.settimeout(10) - sock.connect(sockpath) - - # Go into full non-blocking mode now - sock.settimeout(None) - - print("\n--- Micronet CLI Starting ---\n\n") - while True: - if sys.version_info[0] == 2: - line = raw_input(prompt) # pylint: disable=E0602 - else: - line = input(prompt) - if line is None: - return - - # Need to put \n back - line += "\n" - - # Send the CLI command - sock.send(line.encode("utf-8")) - - def bendswith(b, sentinel): - slen = len(sentinel) - return len(b) >= slen and b[-slen:] == sentinel - - # Collect the output - rb = b"" - while not bendswith(rb, ENDMARKER): - lb = sock.recv(4096) - if not lb: - return - rb += lb - - # Remove the marker - rb = rb[: -len(ENDMARKER)] - - # Write the output - sys.stdout.write(rb.decode("utf-8")) - - -def local_cli(unet, outf, prompt="unet> "): - print("\n--- Micronet CLI Starting ---\n\n") - while True: - if sys.version_info[0] == 2: - line = raw_input(prompt) # pylint: disable=E0602 - else: - line = input(prompt) - if line is None: - return - if not doline(unet, line, outf.write): - return - - -def cli( - unet, - histfile=None, - sockpath=None, - force_window=False, - title=None, - prompt=None, - background=True, -): - logger = logging.getLogger("cli-client") - - if prompt is None: - prompt = "unet> " - - if force_window or not sys.stdin.isatty(): - # Run CLI in another window b/c we have no tty. - sock, sockdir, sockpath = cli_server_setup(unet) - - python_path = unet.get_exec_path(["python3", "python"]) - us = os.path.realpath(__file__) - cmd = "{} {}".format(python_path, us) - if histfile: - cmd += " --histfile=" + histfile - if title: - cmd += " --prompt={}".format(title) - cmd += " " + sockpath - - try: - unet.run_in_window(cmd, new_window=True, title=title, background=background) - return cli_server(unet, sock) - finally: - unet.cmd_status("rm -rf " + sockdir) - - if not unet: - logger.debug("client-cli using sockpath %s", sockpath) - - try: - if histfile is None: - histfile = os.path.expanduser("~/.micronet-history.txt") - if not os.path.exists(histfile): - if unet: - unet.cmd("touch " + histfile) - else: - subprocess.run("touch " + histfile) - if histfile: - readline.read_history_file(histfile) - except Exception: - pass - - try: - if sockpath: - cli_client(sockpath, prompt=prompt) - else: - local_cli(unet, sys.stdout, prompt=prompt) - except EOFError: - pass - except Exception as ex: - logger.critical("cli: got exception: %s", ex, exc_info=True) - raise - finally: - readline.write_history_file(histfile) - - -if __name__ == "__main__": - logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log") - logger = logging.getLogger("cli-client") - logger.info("Start logging cli-client") - - parser = argparse.ArgumentParser() - parser.add_argument("--histfile", help="file to user for history") - parser.add_argument("--prompt-text", help="prompt string to use") - parser.add_argument("socket", help="path to pair of sockets to communicate over") - args = parser.parse_args() - - prompt = "{}> ".format(args.prompt_text) if args.prompt_text else "unet> " - cli(None, args.histfile, args.socket, prompt=prompt) diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py index 5a69c56d8dde..b348c859884e 100644 --- a/tests/topotests/lib/micronet_compat.py +++ b/tests/topotests/lib/micronet_compat.py @@ -3,140 +3,44 @@ # # July 11 2021, Christian Hopps <chopps@labn.net> # -# Copyright (c) 2021, LabN Consulting, L.L.C +# Copyright (c) 2021-2023, LabN Consulting, L.L.C # - -import glob -import logging +import ipaddress import os -import signal -import time - -from lib.micronet import LinuxNamespace, Micronet -from lib.micronet_cli import cli - - -def get_pids_with_env(has_var, has_val=None): - result = {} - for pidenv in glob.iglob("/proc/*/environ"): - pid = pidenv.split("/")[2] - try: - with open(pidenv, "rb") as rfb: - envlist = [ - x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0") - ] - envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist] - envdict = dict(envlist) - if has_var not in envdict: - continue - if has_val is None: - result[pid] = envdict - elif envdict[has_var] == str(has_val): - result[pid] = envdict - except Exception: - # E.g., process exited and files are gone - pass - return result - - -def _kill_piddict(pids_by_upid, sig): - for upid, pids in pids_by_upid: - logging.info( - "Sending %s to (%s) of micronet pid %s", sig, ", ".join(pids), upid - ) - for pid in pids: - try: - os.kill(int(pid), sig) - except Exception: - pass - - -def _get_our_pids(): - ourpid = str(os.getpid()) - piddict = get_pids_with_env("MICRONET_PID", ourpid) - pids = [x for x in piddict if x != ourpid] - if pids: - return {ourpid: pids} - return {} - - -def _get_other_pids(): - piddict = get_pids_with_env("MICRONET_PID") - unet_pids = {d["MICRONET_PID"] for d in piddict.values()} - pids_by_upid = {p: set() for p in unet_pids} - for pid, envdict in piddict.items(): - pids_by_upid[envdict["MICRONET_PID"]].add(pid) - # Filter out any child pid sets whos micronet pid is still running - return {x: y for x, y in pids_by_upid.items() if x not in y} - - -def _get_pids_by_upid(ours): - if ours: - return _get_our_pids() - return _get_other_pids() - - -def _cleanup_pids(ours): - pids_by_upid = _get_pids_by_upid(ours).items() - if not pids_by_upid: - return - - _kill_piddict(pids_by_upid, signal.SIGTERM) - - # Give them 5 second to exit cleanly - logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids") - for _ in range(0, 5): - pids_by_upid = _get_pids_by_upid(ours).items() - if not pids_by_upid: - return - time.sleep(1) - - pids_by_upid = _get_pids_by_upid(ours).items() - _kill_piddict(pids_by_upid, signal.SIGKILL) - - -def cleanup_current(): - """Attempt to cleanup preview runs. - - Currently this only scans for old processes. - """ - logging.info("reaping current micronet processes") - _cleanup_pids(True) - -def cleanup_previous(): - """Attempt to cleanup preview runs. - - Currently this only scans for old processes. - """ - logging.info("reaping past micronet processes") - _cleanup_pids(False) +from munet import cli +from munet.base import BaseMunet, LinuxNamespace class Node(LinuxNamespace): """Node (mininet compat).""" - def __init__(self, name, **kwargs): - """ - Create a Node. - """ - self.params = kwargs + def __init__(self, name, rundir=None, **kwargs): + nkwargs = {} + if "unet" in kwargs: + nkwargs["unet"] = kwargs["unet"] if "private_mounts" in kwargs: - private_mounts = kwargs["private_mounts"] - else: - private_mounts = kwargs.get("privateDirs", []) + nkwargs["private_mounts"] = kwargs["private_mounts"] + if "logger" in kwargs: + nkwargs["logger"] = kwargs["logger"] - logger = kwargs.get("logger") + # This is expected by newer munet CLI code + self.config_dirname = "" + self.config = {"kind": "frr"} + self.mgmt_ip = None + self.mgmt_ip6 = None - super(Node, self).__init__(name, logger=logger, private_mounts=private_mounts) + super().__init__(name, **nkwargs) + + self.rundir = self.unet.rundir.joinpath(self.name) def cmd(self, cmd, **kwargs): """Execute a command, joins stdout, stderr, ignores exit status.""" return super(Node, self).cmd_legacy(cmd, **kwargs) - def config(self, lo="up", **params): + def config_host(self, lo="up", **params): """Called by Micronet when topology is built (but not started).""" # mininet brings up loopback here. del params @@ -148,25 +52,79 @@ def intfNames(self): def terminate(self): return + def add_vlan(self, vlanname, linkiface, vlanid): + self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ + ip_path, + "link", + "add", + "link", + linkiface, + "name", + vlanname, + "type", + "vlan", + "id", + vlanid, + ] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"]) + + def add_loop(self, loopname): + self.logger.debug("Adding Linux iface: %s", loopname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"]) + self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"]) + + def add_l3vrf(self, vrfname, tableid): + self.logger.debug("Adding Linux VRF: %s", vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises( + [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid] + ) + self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"]) + + def del_iface(self, iface): + self.logger.debug("Removing Linux Iface: %s", iface) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + self.cmd_raises([ip_path, "link", "del", iface]) + + def attach_iface_to_l3vrf(self, ifacename, vrfname): + self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname) + ip_path = self.get_exec_path("ip") + assert ip_path, "XXX missing ip command!" + if vrfname: + self.cmd_raises( + [ip_path, "link", "set", "dev", ifacename, "master", vrfname] + ) + else: + self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"]) + + set_cwd = LinuxNamespace.set_ns_cwd + class Topo(object): # pylint: disable=R0205 def __init__(self, *args, **kwargs): raise Exception("Remove Me") -class Mininet(Micronet): +class Mininet(BaseMunet): """ Mininet using Micronet. """ g_mnet_inst = None - def __init__(self, controller=None): + def __init__(self, rundir=None, pytestconfig=None, logger=None): """ Create a Micronet. """ - assert not controller - if Mininet.g_mnet_inst is not None: Mininet.g_mnet_inst.stop() Mininet.g_mnet_inst = self @@ -181,7 +139,145 @@ def __init__(self, controller=None): # to set permissions to root:frr 770 to make this unneeded in that case # os.umask(0) - super(Mininet, self).__init__() + super(Mininet, self).__init__( + pid=False, rundir=rundir, pytestconfig=pytestconfig, logger=logger + ) + + # From munet/munet/native.py + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + self.bind_mount(hosts_file, "/etc/hosts") + + # Common CLI commands for any topology + cdict = { + "commands": [ + # + # Window commands. + # + { + "name": "pcap", + "format": "pcap NETWORK", + "help": ( + "capture packets from NETWORK into file capture-NETWORK.pcap" + " the command is run within a new window which also shows" + " packet summaries. NETWORK can also be an interface specified" + " as HOST:INTF. To capture inside the host namespace." + ), + "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap", + "top-level": True, + "new-window": {"background": True}, + }, + { + "name": "term", + "format": "term HOST [HOST ...]", + "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all", + "exec": "bash", + "new-window": True, + }, + { + "name": "vtysh", + "exec": "/usr/bin/vtysh", + "format": "vtysh ROUTER [ROUTER ...]", + "new-window": True, + "kinds": ["frr"], + }, + { + "name": "xterm", + "format": "xterm HOST [HOST ...]", + "help": "open XTerm[s] on HOST[S], * for all", + "exec": "bash", + "new-window": { + "forcex": True, + }, + }, + { + "name": "logd", + "exec": "tail -F %RUNDIR%/{}.log", + "format": "logd HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the logfile of the given " + "DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + { + "name": "stdlog", + "exec": ( + "[ -e %RUNDIR%/frr.log ] && tail -F %RUNDIR%/frr.log " + "|| tail -F /var/log/frr.log" + ), + "format": "stdlog HOST [HOST ...]", + "help": "tail -f on the `frr.log` for the given HOST[S]", + "new-window": True, + }, + { + "name": "stdout", + "exec": "tail -F %RUNDIR%/{0}.err", + "format": "stdout HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the stdout of the given DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + { + "name": "stderr", + "exec": "tail -F %RUNDIR%/{0}.out", + "format": "stderr HOST [HOST ...] DAEMON", + "help": ( + "tail -f on the stderr of the given DAEMON for the given HOST[S]" + ), + "new-window": True, + }, + # + # Non-window commands. + # + { + "name": "", + "exec": "vtysh -c '{}'", + "format": "[ROUTER ...] COMMAND", + "help": "execute vtysh COMMAND on the router[s]", + "kinds": ["frr"], + }, + { + "name": "sh", + "format": "[HOST ...] sh <SHELL-COMMAND>", + "help": "execute <SHELL-COMMAND> on hosts", + "exec": "{}", + }, + { + "name": "shi", + "format": "[HOST ...] shi <INTERACTIVE-COMMAND>", + "help": "execute <INTERACTIVE-COMMAND> on HOST[s]", + "exec": "{}", + "interactive": True, + }, + ] + } + + cli.add_cli_config(self, cdict) + + shellopt = self.cfgopt.get_option_list("--shell") + if "all" in shellopt or "." in shellopt: + self.run_in_window("bash", title="munet") + + # This is expected by newer munet CLI code + self.config_dirname = "" + self.config = {} self.logger.debug("%s: Creating", self) @@ -219,12 +315,15 @@ def configure_hosts(self): host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf)) + # can be used by munet cli + host.mgmt_ip = ipaddress.ip_address(ip) + if "defaultRoute" in params: host.cmd_raises( "ip route add default {}".format(params["defaultRoute"]) ) - host.config() + host.config_host() self.configured_hosts.add(name) @@ -236,6 +335,24 @@ def add_host(self, name, cls=Node, **kwargs): def start(self): """Start the micronet topology.""" + pcapopt = self.cfgopt.get_option_list("--pcap") + if "all" in pcapopt: + pcapopt = self.switches.keys() + for pcap in pcapopt: + if ":" in pcap: + host, intf = pcap.split(":") + pcap = f"{host}-{intf}" + host = self.hosts[host] + else: + host = self + intf = pcap + pcapfile = f"{self.rundir}/capture-{pcap}.pcap" + host.run_in_window( + f"tshark -s 9200 -i {intf} -P -w {pcapfile}", + background=True, + title=f"cap:{pcap}", + ) + self.logger.debug("%s: Starting (no-op).", self) def stop(self): @@ -250,4 +367,4 @@ def stop(self): Mininet.g_mnet_inst = None def cli(self): - cli(self) + cli.cli(self) diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index 23b1f2e533cf..5486e904df74 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -302,7 +302,6 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): # ospf gr information gr_data = ospf_data.setdefault("graceful-restart", {}) if gr_data: - if "opaque" in gr_data and gr_data["opaque"]: cmd = "capability opaque" if gr_data.setdefault("delete", False): @@ -338,6 +337,7 @@ def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf): cmd = "no {}".format(cmd) config_data.append(cmd) + config_data.append("exit") logger.debug("Exiting lib API: create_ospf_global()") return config_data @@ -577,15 +577,15 @@ def verify_ospf_neighbor( "ospf": { "neighbors": { "r1": { - "state": "Full", + "nbrState": "Full", "role": "DR" }, "r2": { - "state": "Full", + "nbrState": "Full", "role": "DROther" }, "r3": { - "state": "Full", + "nbrState": "Full", "role": "DROther" } } @@ -642,13 +642,13 @@ def verify_ospf_neighbor( neighbor_ip = neighbor_ip.lower() nbr_rid = data_rid try: - nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0] - intf_state = show_ospf_json[nbr_rid][0]["state"].split("/")[1] + nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] + intf_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[1] except KeyError: errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid) return errormsg - nbr_state = nbr_data.setdefault("state", None) + nbr_state = nbr_data.setdefault("nbrState", None) nbr_role = nbr_data.setdefault("role", None) if nbr_state: @@ -710,6 +710,7 @@ def verify_ospf_neighbor( else: data_ip = topo["routers"][ospf_nbr]["links"] data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"] + logger.info("ospf neighbor %s: router-id: %s", router, data_rid) if ospf_nbr in data_ip: nbr_details = nbr_data[ospf_nbr] elif lan: @@ -724,11 +725,14 @@ def verify_ospf_neighbor( nh_state = None neighbor_ip = neighbor_ip.lower() nbr_rid = data_rid + try: - nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0] + nh_state = show_ospf_json[nbr_rid][0]["nbrState"].split("/")[0] except KeyError: - errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format( - router, nbr_rid, ospf_nbr + errormsg = ( + "[DUT: {}] missing OSPF neighbor {} with router-id {}".format( + router, ospf_nbr, nbr_rid + ) ) return errormsg @@ -842,7 +846,6 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) return errormsg for ospf_nbr, nbr_data in ospf_nbr_list.items(): - try: data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"] except KeyError: @@ -913,7 +916,6 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) return errormsg continue else: - for router, rnode in tgen.routers().items(): if "ospf6" not in topo["routers"][router]: continue @@ -944,7 +946,7 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][ "router_id" ] - + logger.info("ospf neighbor %s: router-id: %s", ospf_nbr, data_rid) if ospf_nbr in data_ip: nbr_details = nbr_data[ospf_nbr] elif lan: @@ -967,8 +969,10 @@ def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False) nh_state = get_index_val.get(neighbor_ip)["state"] intf_state = get_index_val.get(neighbor_ip)["ifState"] except TypeError: - errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format( - router, nbr_rid, ospf_nbr + errormsg = ( + "[DUT: {}] missing OSPF neighbor {} with router-id {}".format( + router, ospf_nbr, nbr_rid + ) ) return errormsg @@ -1760,7 +1764,6 @@ def verify_ospf6_rib( continue if st_rt in ospf_rib_json: - st_found = True found_routes.append(st_rt) @@ -2477,7 +2480,7 @@ def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None): input_dict = { "helperSupport":"Disabled", "strictLsaCheck":"Enabled", - "restartSupoort":"Planned and Unplanned Restarts", + "restartSupport":"Planned and Unplanned Restarts", "supportedGracePeriod":1800 } result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py index e5af24d41967..f69718a5bd4b 100644 --- a/tests/topotests/lib/pim.py +++ b/tests/topotests/lib/pim.py @@ -1,35 +1,35 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- # SPDX-License-Identifier: ISC # Copyright (c) 2019 by VMware, Inc. ("VMware") # Used Copyright (c) 2018 by Network Device Education Foundation, Inc. # ("NetDEF") in this file. import datetime +import functools import os import re import sys import traceback -import functools from copy import deepcopy from time import sleep -from lib import topotest - # Import common_config to use commomnly used APIs from lib.common_config import ( - create_common_configurations, HostApplicationHelper, InvalidCLIError, create_common_configuration, - InvalidCLIError, + create_common_configurations, + get_frr_ipv6_linklocal, retry, run_frr_cmd, validate_ip_address, - get_frr_ipv6_linklocal, ) from lib.micronet import get_exec_path from lib.topolog import logger from lib.topotest import frr_unicode +from lib import topotest + #### CWD = os.path.dirname(os.path.realpath(__file__)) @@ -593,7 +593,6 @@ def find_rp_details(tgen, topo): topo_data = topo["routers"] for router in router_list.keys(): - if "pim" not in topo_data[router]: continue @@ -1495,7 +1494,6 @@ def verify_mroutes( and data["outboundInterface"] in oil ): if return_uptime: - uptime_dict[grp_addr][src_address] = data["upTime"] logger.info( @@ -1917,7 +1915,6 @@ def show_pim_intf_traffic(rnode, dut, input_dict, output_dict): for intf, data in input_dict[dut].items(): interface_json = show_pim_intf_traffic_json[intf] for state in data: - # Verify Tx/Rx if state in interface_json: output_dict[dut][state] = interface_json[state] @@ -1990,7 +1987,6 @@ def show_pim_intf_traffic(rnode, dut, input_dict, output_dict): for intf, data in input_dict[dut].items(): interface_json = show_pim_intf_traffic_json[intf] for state in data: - # Verify Tx/Rx if state in interface_json: output_dict[dut][state] = interface_json[state] @@ -3007,7 +3003,6 @@ def verify_pim_upstream_rpf( logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) if "pim" in topo["routers"][dut]: - logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut) rnode = tgen.routers()[dut] @@ -3245,7 +3240,6 @@ def verify_pim_join( grp_addr = grp_addr.split("/")[0] for source, data in interface_json[grp_addr].items(): - # Verify pim join if pim_join: if data["group"] == grp_addr and data["channelJoinName"] == "JOIN": @@ -3338,7 +3332,6 @@ def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True): rnode = tgen.routers()[dut] for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): - statistics = False report = False if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: @@ -3623,7 +3616,6 @@ def verify_pim_config(tgen, input_dict, expected=True): rnode = tgen.routers()[dut] for interface, data in input_dict[dut]["pim"]["interfaces"].items(): - logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface) show_ip_igmp_intf_json = run_frr_cmd( @@ -3772,7 +3764,6 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=Tr elif ( interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0 ): - traffic_dict[traffic_type][interface][ "pktsIn" ] = interface_json["pktsIn"] @@ -3836,7 +3827,6 @@ def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=Tr interface_json["pktsOut"] != 0 and interface_json["bytesOut"] != 0 ): - traffic_dict[traffic_type][interface][ "pktsOut" ] = interface_json["pktsOut"] @@ -4232,7 +4222,6 @@ def verify_local_igmp_groups(tgen, dut, interface, group_addresses): group_addresses = [group_addresses] if interface not in show_ip_local_igmp_json: - errormsg = ( "[DUT %s]: Verifying local IGMP group received" " from interface %s [FAILED]!! " % (dut, interface) @@ -4319,7 +4308,6 @@ def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type= for intf, data in input_dict[dut].items(): interface_json = show_pim_intf_traffic_json[intf] for state in data: - # Verify Tx/Rx if state in interface_json: output_dict[dut][state] = interface_json[state] @@ -4524,12 +4512,10 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): for dut in input_dict.keys(): rnode = tgen.routers()[dut] - - for interface, data in input_dict[dut]["igmp"]["interfaces"].items(): - + for interface, data in input_dict[dut]["mld"]["interfaces"].items(): statistics = False report = False - if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]: + if "statistics" in input_dict[dut]["mld"]["interfaces"][interface]["mld"]: statistics = True cmd = "show ipv6 mld statistics" else: @@ -4556,6 +4542,8 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): rnode, "{} interface {} json".format(cmd, interface), isjson=True ) + show_ipv6_mld_intf_json = show_ipv6_mld_intf_json["default"] + if not report: if interface not in show_ipv6_mld_intf_json: errormsg = ( @@ -4636,7 +4624,7 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): for query, value in data["mld"]["query"].items(): if query == "query-interval": # Verifying IGMP interface query interval timer - if intf_detail_json["timerQueryInterval"] != value: + if intf_detail_json["timerQueryIntervalMsec"] != value * 1000: errormsg = ( "[DUT %s]: MLD interface: %s " " query-interval verification " @@ -4646,7 +4634,7 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): dut, interface, value, - intf_detail_json["timerQueryInterval"], + intf_detail_json["timerQueryIntervalMsec"], ) ) return errormsg @@ -4655,13 +4643,13 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): "[DUT %s]: MLD interface: %s " "query-interval is %s", dut, interface, - value, + value * 1000, ) if query == "query-max-response-time": # Verifying IGMP interface query max response timer if ( - intf_detail_json["timerQueryResponseIntervalMsec"] + intf_detail_json["timerQueryResponseTimerMsec"] != value * 100 ): errormsg = ( @@ -4672,8 +4660,8 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): % ( dut, interface, - value * 1000, - intf_detail_json["timerQueryResponseIntervalMsec"], + value * 100, + intf_detail_json["timerQueryResponseTimerMsec"], ) ) return errormsg @@ -4714,8 +4702,8 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): if query == "last-member-query-interval": # Verifying IGMP interface last member query interval if ( - intf_detail_json["timerLastMemberQueryMsec"] - != value * 100 * intf_detail_json["lastMemberQueryCount"] + intf_detail_json["timerLastMemberQueryIntervalMsec"] + != value * 100 ): errormsg = ( "[DUT %s]: MLD interface: %s " @@ -4725,8 +4713,10 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): % ( dut, interface, - value * 1000, - intf_detail_json["timerLastMemberQueryMsec"], + value * 100, + intf_detail_json[ + "timerLastMemberQueryIntervalMsec" + ], ) ) return errormsg @@ -4736,7 +4726,7 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): "last-member-query-interval is %s ms", dut, interface, - value * intf_detail_json["lastMemberQueryCount"] * 100, + value * 100, ) if "version" in data["mld"]: @@ -4761,7 +4751,7 @@ def verify_mld_config(tgen, input_dict, stats_return=False, expected=True): @retry(retry_timeout=60, diag_pct=0) -def verify_pim_nexthop(tgen, topo, dut, nexthop, addr_type): +def verify_pim_nexthop(tgen, topo, dut, nexthop, addr_type="ipv4"): """ Verify all PIM nexthop details using "show ip/ipv6 pim neighbor" cli @@ -4895,6 +4885,7 @@ def verify_mroute_summary( return True +@retry(retry_timeout=60, diag_pct=0) def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"): """ Verify multicast traffic by running @@ -4907,7 +4898,7 @@ def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"): Usage ----- - result = verify_sg_traffic(tgen, "r1", igmp_groups, srcaddress) + result = verify_sg_traffic(tgen, "r1", igmp_groups/mld_groups, srcaddress) Returns ------- @@ -4973,7 +4964,7 @@ def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"): after_traffic[grp] = show_mroute_sg_traffic_json[grp][src]["packets"] for grp in groups: - if after_traffic[grp] < before_traffic[grp]: + if after_traffic[grp] <= before_traffic[grp]: errormsg = ( "[DUT %s]: Verifying igmp group %s source %s not increamenting traffic" " [FAILED]!! " % (dut, grp, src) @@ -4988,6 +4979,201 @@ def verify_sg_traffic(tgen, dut, groups, src, addr_type="ipv4"): return result + +@retry(retry_timeout=60, diag_pct=0) +def verify_pim6_config(tgen, input_dict, expected=True): + """ + Verify pim interface details, verifying following configs: + drPriority + helloPeriod + helloReceived + helloSend + drAddress + + Parameters + ---------- + * `tgen`: topogen object + * `input_dict` : Input dict data, required to verify + timer + * `expected` : expected results from API, by-default True + + Usage + ----- + input_dict ={ + "l1": { + "mld": { + "interfaces": { + "l1-i1-eth1": { + "pim6": { + "drPriority" : 10, + "helloPeriod" : 5 + } + } + } + } + } + } + } + result = verify_pim6_config(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 interface, data in input_dict[dut]["pim6"]["interfaces"].items(): + logger.info( + "[DUT: %s]: Verifying PIM6 interface %s detail:", dut, interface + ) + + show_ipv6_pim_intf_json = run_frr_cmd( + rnode, "show ipv6 pim interface {} json".format(interface), isjson=True + ) + + if interface not in show_ipv6_pim_intf_json: + errormsg = ( + "[DUT %s]: PIM6 interface: %s " + " is not present in CLI output " + "[FAILED]!! " % (dut, interface) + ) + return errormsg + + intf_detail_json = show_ipv6_pim_intf_json[interface] + + for config, value in data.items(): + if config == "helloPeriod": + # Verifying PIM interface helloPeriod + if intf_detail_json["helloPeriod"] != value: + errormsg = ( + "[DUT %s]: PIM6 interface: %s " + " helloPeriod verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % (dut, interface, value, intf_detail_json["helloPeriod"]) + ) + return errormsg + + logger.info( + "[DUT %s]: PIM6 interface: %s " "helloPeriod is %s", + dut, + interface, + value, + ) + + if config == "drPriority": + # Verifying PIM interface drPriority + if intf_detail_json["drPriority"] != value: + errormsg = ( + "[DUT %s]: PIM6 interface: %s " + " drPriority verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % (dut, interface, value, intf_detail_json["drPriority"]) + ) + return errormsg + + logger.info( + "[DUT %s]: PIM6 interface: %s " "drPriority is %s", + dut, + interface, + value, + ) + + if config == "drAddress": + # Verifying PIM interface drAddress + if intf_detail_json["drAddress"] != value: + errormsg = ( + "[DUT %s]: PIM6 interface: %s " + " drAddress verification " + "[FAILED]!! Expected : %s," + " Found : %s" + % (dut, interface, value, intf_detail_json["drAddress"]) + ) + return errormsg + + logger.info( + "[DUT %s]: PIM6 interface: %s " "drAddress is %s", + dut, + interface, + value, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + + +@retry(retry_timeout=62) +def verify_local_mld_groups(tgen, dut, interface, group_addresses): + """ + Verify local MLD groups are received from an intended interface + by running "show ipv6 mld join json" command + Parameters + ---------- + * `tgen`: topogen object + * `dut`: device under test + * `interface`: interface, from which IGMP groups are configured + * `group_addresses`: MLD group address + Usage + ----- + dut = "r1" + interface = "r1-r0-eth0" + group_address = "ffaa::1" + result = verify_local_mld_groups(tgen, dut, interface, group_address) + Returns + ------- + errormsg(str) or True + """ + + logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) + + if dut not in tgen.routers(): + return False + + rnode = tgen.routers()[dut] + logger.info("[DUT: %s]: Verifying local MLD groups received:", dut) + show_ipv6_local_mld_json = run_frr_cmd( + rnode, "show ipv6 mld join json", isjson=True + ) + + if type(group_addresses) is not list: + group_addresses = [group_addresses] + + if interface not in show_ipv6_local_mld_json["default"]: + errormsg = ( + "[DUT %s]: Verifying local MLD group received" + " from interface %s [FAILED]!! " % (dut, interface) + ) + return errormsg + + for grp_addr in group_addresses: + found = False + if grp_addr in show_ipv6_local_mld_json["default"][interface]: + found = True + break + if not found: + errormsg = ( + "[DUT %s]: Verifying local MLD group received" + " from interface %s [FAILED]!! " + " Expected: %s " % (dut, interface, grp_addr) + ) + return errormsg + + logger.info( + "[DUT %s]: Verifying local MLD group %s received " + "from interface %s [PASSED]!! ", + dut, + grp_addr, + interface, + ) + + logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) + return True + # def cleanup(self): # super(McastTesterHelper, self).cleanup() diff --git a/tests/topotests/lib/test/test_json.py b/tests/topotests/lib/test/test_json.py index ae58d9b2d1fd..230c2bd7f7d8 100755 --- a/tests/topotests/lib/test/test_json.py +++ b/tests/topotests/lib/test/test_json.py @@ -616,7 +616,6 @@ def test_json_object_asterisk_matching(): def test_json_list_nested_with_objects(): - dcomplete = [{"key": 1, "list": [123]}, {"key": 2, "list": [123]}] dsub1 = [{"key": 2, "list": [123]}, {"key": 1, "list": [123]}] diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py index 16d89f079a72..6ddd223e25db 100644 --- a/tests/topotests/lib/topogen.py +++ b/tests/topotests/lib/topogen.py @@ -25,6 +25,7 @@ * After running stop Mininet with: tgen.stop_topology() """ +import configparser import grp import inspect import json @@ -33,20 +34,16 @@ import platform import pwd import re +import shlex import subprocess import sys from collections import OrderedDict -if sys.version_info[0] > 2: - import configparser -else: - import ConfigParser as configparser - import lib.topolog as topolog from lib.micronet import Commander from lib.micronet_compat import Mininet from lib.topolog import logger -from lib.topotest import g_extra_config +from munet.testing.util import pause_test from lib import topotest @@ -87,7 +84,7 @@ def get_exabgp_cmd(commander=None): """Return the command to use for ExaBGP version < 4.""" if commander is None: - commander = Commander("topogen") + commander = Commander("exabgp", logger=logging.getLogger("exabgp")) def exacmd_version_ok(exacmd): logger.debug("checking %s for exabgp < version 4", exacmd) @@ -110,7 +107,7 @@ def exacmd_version_ok(exacmd): exacmd = py2_path + " -m exabgp" if exacmd_version_ok(exacmd): return exacmd - py2_path = commander.get_exec_path("python") + py2_path = commander.get_exec_path("python") if py2_path: exacmd = py2_path + " -m exabgp" if exacmd_version_ok(exacmd): @@ -192,7 +189,7 @@ def _init_topo(self, topodef): self._load_config() # Create new log directory - self.logdir = topotest.get_logs_path(g_extra_config["rundir"]) + self.logdir = topotest.get_logs_path(topotest.g_pytest_config.option.rundir) subprocess.check_call( "mkdir -p {0} && chmod 1777 {0}".format(self.logdir), shell=True ) @@ -212,7 +209,14 @@ def _init_topo(self, topodef): # Mininet(Micronet) to build the actual topology. assert not inspect.isclass(topodef) - self.net = Mininet(controller=None) + self.net = Mininet( + rundir=self.logdir, + pytestconfig=topotest.g_pytest_config, + logger=topolog.get_logger("mu", log_level="debug"), + ) + + # Adjust the parent namespace + topotest.fix_netns_limits(self.net) # New direct way: Either a dictionary defines the topology or a build function # is supplied, or a json filename all of which build the topology by calling @@ -236,7 +240,6 @@ def _init_topo(self, topodef): self.add_topology_from_dict(topodef) def add_topology_from_dict(self, topodef): - keylist = ( topodef.keys() if isinstance(topodef, OrderedDict) @@ -451,7 +454,18 @@ 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. """ + pause = bool(self.net.cfgopt.get_option("--pause-at-end")) + pause = pause or bool(self.net.cfgopt.get_option("--pause")) + if pause: + try: + pause_test("Before MUNET delete") + except KeyboardInterrupt: + print("^C...continuing") + except Exception as error: + self.logger.error("\n...continuing after error: %s", error) + logger.info("stopping topology: {}".format(self.modname)) + errors = "" for gear in self.gears.values(): errors += gear.stop() @@ -485,7 +499,7 @@ def is_memleak_enabled(self): memleak_file = os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.config.get( self.CONFIG_SECTION, "memleak_path" ) - if memleak_file == "" or memleak_file == None: + if memleak_file == "" or memleak_file is None: return False return True @@ -504,7 +518,7 @@ def report_memory_leaks(self, testname=None): def set_error(self, message, code=None): "Sets an error message and signal other tests to skip." - logger.info(message) + logger.info("setting error msg: %s", message) # If no code is defined use a sequential number if code is None: @@ -713,6 +727,7 @@ class TopoRouter(TopoGear): RD_PATH = 17 RD_SNMP = 18 RD_PIM6 = 19 + RD_MGMTD = 20 RD = { RD_FRR: "frr", RD_ZEBRA: "zebra", @@ -734,6 +749,7 @@ class TopoRouter(TopoGear): RD_PBRD: "pbrd", RD_PATH: "pathd", RD_SNMP: "snmpd", + RD_MGMTD: "mgmtd", } def __init__(self, tgen, cls, name, **params): @@ -747,8 +763,8 @@ def __init__(self, tgen, cls, name, **params): """ super(TopoRouter, self).__init__(tgen, name, **params) self.routertype = params.get("routertype", "frr") - if "privateDirs" not in params: - params["privateDirs"] = self.PRIVATE_DIRS + if "private_mounts" not in params: + params["private_mounts"] = self.PRIVATE_DIRS # Propagate the router log directory logfile = self._setup_tmpdir() @@ -786,23 +802,23 @@ def load_frr_config(self, source, daemons=None): Start the daemons in the list If daemons is None, try to infer daemons from the config file """ - self.load_config(self.RD_FRR, source) + source_path = self.load_config(self.RD_FRR, source) if not daemons: # Always add zebra - self.load_config(self.RD_ZEBRA) + self.load_config(self.RD_ZEBRA, "") for daemon in self.RD: # This will not work for all daemons daemonstr = self.RD.get(daemon).rstrip("d") if daemonstr == "pim": - grep_cmd = "grep 'ip {}' {}".format(daemonstr, source) + grep_cmd = "grep 'ip {}' {}".format(daemonstr, source_path) else: - grep_cmd = "grep 'router {}' {}".format(daemonstr, source) - result = self.run(grep_cmd).strip() + grep_cmd = "grep 'router {}' {}".format(daemonstr, source_path) + result = self.run(grep_cmd, warn=False).strip() if result: - self.load_config(daemon) + self.load_config(daemon, "") else: for daemon in daemons: - self.load_config(daemon) + self.load_config(daemon, "") def load_config(self, daemon, source=None, param=None): """Loads daemon configuration from the specified source @@ -810,7 +826,7 @@ def load_config(self, daemon, source=None, param=None): TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6, TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP, TopoRouter.RD_PIM, TopoRouter.RD_PIM6, TopoRouter.RD_PBR, - TopoRouter.RD_SNMP. + TopoRouter.RD_SNMP, TopoRouter.RD_MGMTD. Possible `source` values are `None` for an empty config file, a path name which is used directly, or a file name with no path components which is first looked for @@ -820,8 +836,8 @@ def load_config(self, daemon, source=None, param=None): all routers. """ daemonstr = self.RD.get(daemon) - self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source)) - self.net.loadConf(daemonstr, source, param) + self.logger.debug('loading "{}" configuration: {}'.format(daemonstr, source)) + return self.net.loadConf(daemonstr, source, param) def check_router_running(self): """ @@ -856,7 +872,7 @@ def start(self): "conf t", "log file {}.log debug".format(daemon), "log commands", - "log timestamp precision 3", + "log timestamp precision 6", ] ), daemon=daemon, @@ -906,7 +922,7 @@ def startDaemons(self, daemons): "conf t", "log file {}.log debug".format(daemon), "log commands", - "log timestamp precision 3", + "log timestamp precision 6", ] ), daemon=daemon, @@ -941,18 +957,20 @@ def vtysh_cmd(self, command, isjson=False, daemon=None): if daemon is not None: dparam += "-d {}".format(daemon) - vtysh_command = 'vtysh {} -c "{}" 2>/dev/null'.format(dparam, command) + vtysh_command = "vtysh {} -c {} 2>/dev/null".format( + dparam, shlex.quote(command) + ) - self.logger.info('vtysh command => "{}"'.format(command)) + self.logger.debug("vtysh command => {}".format(shlex.quote(command))) output = self.run(vtysh_command) dbgout = output.strip() if dbgout: if "\n" in dbgout: dbgout = dbgout.replace("\n", "\n\t") - self.logger.info("vtysh result:\n\t{}".format(dbgout)) + self.logger.debug("vtysh result:\n\t{}".format(dbgout)) else: - self.logger.info('vtysh result: "{}"'.format(dbgout)) + self.logger.debug('vtysh result: "{}"'.format(dbgout)) if isjson is False: return output @@ -992,7 +1010,7 @@ def vtysh_multicmd(self, commands, pretty_output=True, daemon=None): dbgcmds = commands if is_string(commands) else "\n".join(commands) dbgcmds = "\t" + dbgcmds.replace("\n", "\n\t") - self.logger.info("vtysh command => FILE:\n{}".format(dbgcmds)) + self.logger.debug("vtysh command => FILE:\n{}".format(dbgcmds)) res = self.run(vtysh_command) os.unlink(fname) @@ -1001,9 +1019,9 @@ def vtysh_multicmd(self, commands, pretty_output=True, daemon=None): if dbgres: if "\n" in dbgres: dbgres = dbgres.replace("\n", "\n\t") - self.logger.info("vtysh result:\n\t{}".format(dbgres)) + self.logger.debug("vtysh result:\n\t{}".format(dbgres)) else: - self.logger.info('vtysh result: "{}"'.format(dbgres)) + self.logger.debug('vtysh result: "{}"'.format(dbgres)) return res def report_memory_leaks(self, testname): @@ -1017,7 +1035,7 @@ def report_memory_leaks(self, testname): memleak_file = ( os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.params["memleak_path"] ) - if memleak_file == "" or memleak_file == None: + if memleak_file == "" or memleak_file is None: return self.stop() @@ -1076,8 +1094,9 @@ class TopoSwitch(TopoGear): # pylint: disable=too-few-public-methods def __init__(self, tgen, name, **params): + logger = topolog.get_logger(name, log_level="debug") super(TopoSwitch, self).__init__(tgen, name, **params) - tgen.net.add_switch(name) + tgen.net.add_switch(name, logger=logger) def __str__(self): gear = super(TopoSwitch, self).__str__() @@ -1095,7 +1114,7 @@ def __init__(self, tgen, name, **params): * `ip`: the IP address (string) for the host interface * `defaultRoute`: the default route that will be installed (e.g. 'via 10.0.0.1') - * `privateDirs`: directories that will be mounted on a different domain + * `private_mounts`: directories that will be mounted on a different domain (e.g. '/etc/important_dir'). """ super(TopoHost, self).__init__(tgen, name, **params) @@ -1115,10 +1134,10 @@ def __init__(self, tgen, name, **params): def __str__(self): gear = super(TopoHost, self).__str__() - gear += ' TopoHost<ip="{}",defaultRoute="{}",privateDirs="{}">'.format( + gear += ' TopoHost<ip="{}",defaultRoute="{}",private_mounts="{}">'.format( self.params["ip"], self.params["defaultRoute"], - str(self.params["privateDirs"]), + str(self.params["private_mounts"]), ) return gear @@ -1141,10 +1160,10 @@ def __init__(self, tgen, name, **params): (e.g. 'via 10.0.0.1') Note: the different between a host and a ExaBGP peer is that this class - has a privateDirs already defined and contains functions to handle ExaBGP - things. + has a private_mounts already defined and contains functions to handle + ExaBGP things. """ - params["privateDirs"] = self.PRIVATE_DIRS + params["private_mounts"] = self.PRIVATE_DIRS super(TopoExaBGP, self).__init__(tgen, name, **params) def __str__(self): @@ -1174,7 +1193,7 @@ def start(self, peer_dir, env_file=None): self.run("chown -R exabgp:exabgp /etc/exabgp") output = self.run(exacmd + " -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg") - if output == None or len(output) == 0: + if output is None or len(output) == 0: output = "<none>" logger.info("{} exabgp started, output={}".format(self.name, output)) @@ -1189,6 +1208,7 @@ def stop(self, wait=True, assertOnError=True): # Diagnostic function # + # Disable linter branch warning. It is expected to have these here. # pylint: disable=R0912 def diagnose_env_linux(rundir): @@ -1269,6 +1289,7 @@ def diagnose_env_linux(rundir): "pim6d", "ldpd", "pbrd", + "mgmtd", ]: path = os.path.join(frrdir, fname) if not os.path.isfile(path): @@ -1283,9 +1304,10 @@ def diagnose_env_linux(rundir): logger.error("could not find {} in {}".format(fname, frrdir)) ret = False else: - if fname != "zebra": + if fname != "zebra" or fname != "mgmtd": continue + os.system("{} -v 2>&1 >{}/frr_mgmtd.txt".format(path, rundir)) os.system("{} -v 2>&1 >{}/frr_zebra.txt".format(path, rundir)) # Test MPLS availability diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 53e6945bee1a..901e4f623a16 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -206,7 +206,6 @@ def build_topo_from_json(tgen, topo=None): for destRouterLink, data in sorted( topo["switches"][curSwitch]["links"].items() ): - # Loopback interfaces if "dst_node" in data: destRouter = data["dst_node"] @@ -220,7 +219,6 @@ def build_topo_from_json(tgen, topo=None): destRouter = destRouterLink if destRouter in listAllRouters: - topo["routers"][destRouter]["links"][curSwitch] = deepcopy( topo["switches"][curSwitch]["links"][destRouterLink] ) @@ -316,6 +314,7 @@ def build_config_from_json(tgen, topo=None, save_bkup=True): func_dict = OrderedDict( [ ("vrfs", create_vrf_cfg), + ("ospf", create_router_ospf), ("links", create_interfaces_cfg), ("static_routes", create_static_routes), ("prefix_lists", create_prefix_lists), @@ -325,7 +324,6 @@ def build_config_from_json(tgen, topo=None, save_bkup=True): ("igmp", create_igmp_config), ("mld", create_mld_config), ("bgp", create_router_bgp), - ("ospf", create_router_ospf), ] ) @@ -342,7 +340,7 @@ def build_config_from_json(tgen, topo=None, save_bkup=True): result = load_config_to_routers(tgen, routers, save_bkup) if not result: logger.info("build_config_from_json: failed to configure topology") - pytest.exit(1) + assert False logger.info( "Built config now clearing ospf neighbors as that router-id might not be what is used" @@ -398,7 +396,7 @@ def setup_module_from_json(testfile, json_file=None): tgen = create_tgen_from_json(testfile, json_file) # Start routers (and their daemons) - start_topology(tgen, topo_daemons(tgen)) + start_topology(tgen) # Configure routers build_config_from_json(tgen) diff --git a/tests/topotests/lib/topolog.py b/tests/topotests/lib/topolog.py index b50167078982..aceb2cb03181 100644 --- a/tests/topotests/lib/topolog.py +++ b/tests/topotests/lib/topolog.py @@ -15,13 +15,6 @@ import logging import os -import subprocess -import sys - -if sys.version_info[0] > 2: - pass -else: - pass try: from xdist import is_xdist_controller @@ -31,8 +24,6 @@ def is_xdist_controller(): return False -BASENAME = "topolog" - # Helper dictionary to convert Topogen logging levels to Python's logging. DEBUG_TOPO2LOGGING = { "debug": logging.DEBUG, @@ -42,13 +33,43 @@ def is_xdist_controller(): "error": logging.ERROR, "critical": logging.CRITICAL, } -FORMAT = "%(asctime)s.%(msecs)03d %(levelname)s: %(name)s: %(message)s" +FORMAT = "%(asctime)s %(levelname)s: %(name)s: %(message)s" handlers = {} -logger = logging.getLogger("topolog") +logger = logging.getLogger("topo") + + +# Remove this and use munet version when we move to pytest_asyncio +def get_test_logdir(nodeid=None, module=False): + """Get log directory relative pathname.""" + xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "") + mode = os.getenv("PYTEST_XDIST_MODE", "no") + + # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running + # may be missing "::testname" if module is True + if not nodeid: + nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0] + + cur_test = nodeid.replace("[", "_").replace("]", "_") + if module: + idx = cur_test.rfind("::") + path = cur_test if idx == -1 else cur_test[:idx] + testname = "" + else: + path, testname = cur_test.split("::") + testname = testname.replace("/", ".") + path = path[:-3].replace("/", ".") + # We use different logdir paths based on how xdist is running. + if mode == "each": + if module: + return os.path.join(path, "worker-logs", xdist_worker) + return os.path.join(path, testname, xdist_worker) + assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}" + return path if module else os.path.join(path, testname) -def set_handler(l, target=None): + +def set_handler(lg, target=None): if target is None: h = logging.NullHandler() else: @@ -59,106 +80,81 @@ def set_handler(l, target=None): h.setFormatter(logging.Formatter(fmt=FORMAT)) # Don't filter anything at the handler level h.setLevel(logging.DEBUG) - l.addHandler(h) + lg.addHandler(h) return h -def set_log_level(l, level): +def set_log_level(lg, level): "Set the logging level." # Messages sent to this logger only are created if this level or above. log_level = DEBUG_TOPO2LOGGING.get(level, level) - l.setLevel(log_level) + lg.setLevel(log_level) -def get_logger(name, log_level=None, target=None): - l = logging.getLogger("{}.{}".format(BASENAME, name)) +def reset_logger(lg): + while lg.handlers: + x = lg.handlers.pop() + x.close() + lg.removeHandler(x) - if log_level is not None: - set_log_level(l, log_level) - if target is not None: - set_handler(l, target) - - return l - - -# nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running - - -def get_test_logdir(nodeid=None): - """Get log directory relative pathname.""" - xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "") - mode = os.getenv("PYTEST_XDIST_MODE", "no") +def get_logger(name, log_level=None, target=None, reset=True): + lg = logging.getLogger(name) - if not nodeid: - nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0] + if reset: + reset_logger(lg) - cur_test = nodeid.replace("[", "_").replace("]", "_") - path, testname = cur_test.split("::") - path = path[:-3].replace("/", ".") + if log_level is not None: + set_log_level(lg, log_level) - # We use different logdir paths based on how xdist is running. - if mode == "each": - return os.path.join(path, testname, xdist_worker) - elif mode == "load": - return os.path.join(path, testname) - else: - assert ( - mode == "no" or mode == "loadfile" or mode == "loadscope" - ), "Unknown dist mode {}".format(mode) + if target is not None: + set_handler(lg, target) - return path + return lg -def logstart(nodeid, location, rundir): +def logstart(nodeid, logpath): """Called from pytest before module setup.""" - - mode = os.getenv("PYTEST_XDIST_MODE", "no") worker = os.getenv("PYTEST_TOPOTEST_WORKER", "") + wstr = f" on worker {worker}" if worker else "" + handler_id = nodeid + worker + logpath = logpath.absolute() - # We only per-test log in the workers (or non-dist) - if not worker and mode != "no": - return + logging.debug("logstart: adding logging for %s%s at %s", nodeid, wstr, logpath) + root_logger = logging.getLogger() + handler = logging.FileHandler(logpath, mode="w") + handler.setFormatter(logging.Formatter(FORMAT)) - handler_id = nodeid + worker - assert handler_id not in handlers - - rel_log_dir = get_test_logdir(nodeid) - exec_log_dir = os.path.join(rundir, rel_log_dir) - subprocess.check_call( - "mkdir -p {0} && chmod 1777 {0}".format(exec_log_dir), shell=True - ) - exec_log_path = os.path.join(exec_log_dir, "exec.log") - - # Add test based exec log handler - h = set_handler(logger, exec_log_path) - handlers[handler_id] = h - - if worker: - logger.info( - "Logging on worker %s for %s into %s", worker, handler_id, exec_log_path - ) - else: - logger.info("Logging for %s into %s", handler_id, exec_log_path) + root_logger.addHandler(handler) + handlers[handler_id] = handler + logging.debug("logstart: added logging for %s%s at %s", nodeid, wstr, logpath) + return handler -def logfinish(nodeid, location): - """Called from pytest after module teardown.""" - # This function may not be called if pytest is interrupted. +def logfinish(nodeid, logpath): + """Called from pytest after module teardown.""" worker = os.getenv("PYTEST_TOPOTEST_WORKER", "") - handler_id = nodeid + worker + wstr = f" on worker {worker}" if worker else "" + + root_logger = logging.getLogger() - if handler_id in handlers: - # Remove test based exec log handler - if worker: - logger.info("Closing logs for %s", handler_id) + handler_id = nodeid + worker + if handler_id not in handlers: + logging.critical("can't find log handler to remove") + else: + logging.debug( + "logfinish: removing logging for %s%s at %s", nodeid, wstr, logpath + ) h = handlers[handler_id] - logger.removeHandler(handlers[handler_id]) + root_logger.removeHandler(h) h.flush() h.close() del handlers[handler_id] + logging.debug( + "logfinish: removed logging for %s%s at %s", nodeid, wstr, logpath + ) console_handler = set_handler(logger, None) diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 0cd60b228d08..737123005746 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -9,13 +9,13 @@ # Network Device Education Foundation, Inc. ("NetDEF") # +import configparser import difflib import errno import functools import glob import json import os -import pdb import platform import re import resource @@ -24,26 +24,22 @@ import sys import tempfile import time +import logging +from collections.abc import Mapping from copy import deepcopy import lib.topolog as topolog +from lib.micronet_compat import Node from lib.topolog import logger - -if sys.version_info[0] > 2: - import configparser - from collections.abc import Mapping -else: - import ConfigParser as configparser - from collections import Mapping +from munet.base import Timeout from lib import micronet -from lib.micronet_compat import Node -g_extra_config = {} +g_pytest_config = None def get_logs_path(rundir): - logspath = topolog.get_test_logdir() + logspath = topolog.get_test_logdir(module=True) return os.path.join(rundir, logspath) @@ -352,7 +348,7 @@ def run_and_expect(func, what, count=20, wait=3): count, wait ) - logger.info( + logger.debug( "'{}' polling started (interval {} secs, maximum {} tries)".format( func_name, wait, count ) @@ -366,7 +362,7 @@ def run_and_expect(func, what, count=20, wait=3): continue end_time = time.time() - logger.info( + logger.debug( "'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time ) @@ -409,7 +405,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): count, wait ) - logger.info( + logger.debug( "'{}' polling started (interval {} secs, maximum wait {} secs)".format( func_name, wait, int(wait * count) ) @@ -432,7 +428,7 @@ def run_and_expect_type(func, etype, count=20, wait=3, avalue=None): continue end_time = time.time() - logger.info( + logger.debug( "'{}' succeeded after {:.2f} seconds".format( func_name, end_time - start_time ) @@ -474,32 +470,6 @@ def int2dpid(dpid): ) -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: - if err.errno == errno.ESRCH: - # ESRCH == No such process - return False - elif err.errno == errno.EPERM: - # EPERM clearly means there's a process to deny access to - return True - else: - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) - raise - else: - return True - - def get_textdiff(text1, text2, title1="", title2="", **opts): "Returns empty string if same or formatted diff" @@ -582,6 +552,7 @@ def iproute2_is_vrf_capable(): pass return False + def iproute2_is_fdb_get_capable(): """ Checks if the iproute2 version installed on the system is capable of @@ -606,6 +577,7 @@ def iproute2_is_fdb_get_capable(): pass return False + def module_present_linux(module, load): """ Returns whether `module` is present. @@ -1017,7 +989,7 @@ def processAddressSanitizerError(asanErrorRe, output, router, component): ) if addressSanitizerLog: # Find Calling Test. Could be multiple steps back - testframe = sys._current_frames().values()[0] + testframe = list(sys._current_frames().values())[0] level = 0 while level < 10: test = os.path.splitext( @@ -1084,7 +1056,7 @@ def processAddressSanitizerError(asanErrorRe, output, router, component): # No Address Sanitizer Error in Output. Now check for AddressSanitizer daemon file if logdir: - filepattern = logdir + "/" + router + "/" + component + ".asan.*" + filepattern = logdir + "/" + router + ".asan." + component + ".*" logger.debug( "Log check for %s on %s, pattern %s\n" % (component, router, filepattern) ) @@ -1128,8 +1100,8 @@ def _sysctl_atleast(commander, variable, min_value): valstr = " ".join([str(x) for x in min_value]) else: valstr = str(min_value) - logger.info("Increasing sysctl %s from %s to %s", variable, cur_val, valstr) - commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr)) + logger.debug("Increasing sysctl %s from %s to %s", variable, cur_val, valstr) + commander.cmd_raises('sysctl -w {}="{}"'.format(variable, valstr)) def _sysctl_assure(commander, variable, value): @@ -1159,14 +1131,16 @@ def _sysctl_assure(commander, variable, value): valstr = " ".join([str(x) for x in value]) else: valstr = str(value) - logger.info("Changing sysctl %s from %s to %s", variable, cur_val, valstr) + logger.debug("Changing sysctl %s from %s to %s", variable, cur_val, valstr) commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr)) def sysctl_atleast(commander, variable, min_value, raises=False): try: if commander is None: - commander = micronet.Commander("topotest") + logger = logging.getLogger("topotest") + commander = micronet.Commander("sysctl", logger=logger) + return _sysctl_atleast(commander, variable, min_value) except subprocess.CalledProcessError as error: logger.warning( @@ -1182,7 +1156,8 @@ def sysctl_atleast(commander, variable, min_value, raises=False): def sysctl_assure(commander, variable, value, raises=False): try: if commander is None: - commander = micronet.Commander("topotest") + logger = logging.getLogger("topotest") + commander = micronet.Commander("sysctl", logger=logger) return _sysctl_assure(commander, variable, value) except subprocess.CalledProcessError as error: logger.warning( @@ -1202,7 +1177,7 @@ def rlimit_atleast(rname, min_value, raises=False): soft, hard = cval if soft < min_value: nval = (min_value, hard if min_value < hard else min_value) - logger.info("Increasing rlimit %s from %s to %s", rname, cval, nval) + logger.debug("Increasing rlimit %s from %s to %s", rname, cval, nval) resource.setrlimit(rname, nval) except subprocess.CalledProcessError as error: logger.warning( @@ -1213,7 +1188,6 @@ def rlimit_atleast(rname, min_value, raises=False): def fix_netns_limits(ns): - # Maximum read and write socket buffer sizes sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20]) sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20]) @@ -1301,7 +1275,8 @@ def fix_host_limits(): def setup_node_tmpdir(logdir, name): # Cleanup old log, valgrind, and core files. subprocess.check_call( - "rm -rf {0}/{1}.valgrind.* {1}.*.asan {0}/{1}/".format(logdir, name), shell=True + "rm -rf {0}/{1}.valgrind.* {0}/{1}.asan.* {0}/{1}/".format(logdir, name), + shell=True, ) # Setup the per node directory. @@ -1316,8 +1291,7 @@ def setup_node_tmpdir(logdir, name): class Router(Node): "A Node with IPv4/IPv6 forwarding enabled" - def __init__(self, name, **params): - + def __init__(self, name, *posargs, **params): # Backward compatibility: # Load configuration defaults like topogen. self.config_defaults = configparser.ConfigParser( @@ -1333,11 +1307,13 @@ def __init__(self, name, **params): os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini") ) + self.perf_daemons = {} + # If this topology is using old API and doesn't have logdir # specified, then attempt to generate an unique logdir. self.logdir = params.get("logdir") if self.logdir is None: - self.logdir = get_logs_path(g_extra_config["rundir"]) + self.logdir = get_logs_path(g_pytest_config.getoption("--rundir")) if not params.get("logger"): # If logger is present topogen has already set this up @@ -1345,7 +1321,7 @@ def __init__(self, name, **params): l = topolog.get_logger(name, log_level="debug", target=logfile) params["logger"] = l - super(Router, self).__init__(name, **params) + super(Router, self).__init__(name, *posargs, **params) self.daemondir = None self.hasmpls = False @@ -1371,6 +1347,7 @@ def __init__(self, name, **params): "pbrd": 0, "pathd": 0, "snmpd": 0, + "mgmtd": 0, } self.daemons_options = {"zebra": ""} self.reportCores = True @@ -1398,10 +1375,14 @@ def _config_frr(self, **params): if not os.path.isfile(zebra_path): raise Exception("FRR zebra binary doesn't exist at {}".format(zebra_path)) + mgmtd_path = os.path.join(self.daemondir, "mgmtd") + if not os.path.isfile(mgmtd_path): + raise Exception("FRR MGMTD binary doesn't exist at {}".format(mgmtd_path)) + # pylint: disable=W0221 # Some params are only meaningful for the parent class. - def config(self, **params): - super(Router, self).config(**params) + def config_host(self, **params): + super(Router, self).config_host(**params) # User did not specify the daemons directory, try to autodetect it. self.daemondir = params.get("daemondir") @@ -1415,6 +1396,10 @@ def config(self, **params): zpath = os.path.join(self.daemondir, "zebra") if not os.path.isfile(zpath): raise Exception("No zebra binary found in {}".format(zpath)) + + cpath = os.path.join(self.daemondir, "mgmtd") + if not os.path.isfile(zpath): + raise Exception("No MGMTD binary found in {}".format(cpath)) # Allow user to specify routertype when the path was specified. if params.get("routertype") is not None: self.routertype = params.get("routertype") @@ -1467,11 +1452,11 @@ def stopRouter(self, assertOnError=True, minErrorVersion="5.1"): logger.info("%s: stopping %s", self.name, ", ".join([x[0] for x in running])) for name, pid in running: - logger.info("{}: sending SIGTERM to {}".format(self.name, name)) + logger.debug("{}: sending SIGTERM to {}".format(self.name, name)) try: os.kill(pid, signal.SIGTERM) except OSError as err: - logger.info( + logger.debug( "%s: could not kill %s (%s): %s", self.name, name, pid, str(err) ) @@ -1488,21 +1473,22 @@ def stopRouter(self, assertOnError=True, minErrorVersion="5.1"): if not running: break - if not running: - return "" - - logger.warning( - "%s: sending SIGBUS to: %s", self.name, ", ".join([x[0] for x in running]) - ) - for name, pid in running: - pidfile = "/var/run/{}/{}.pid".format(self.routertype, name) - logger.info("%s: killing %s", self.name, name) - self.cmd("kill -SIGBUS %d" % pid) - self.cmd("rm -- " + pidfile) - - sleep( - 0.5, "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name - ) + if running: + logger.warning( + "%s: sending SIGBUS to: %s", + self.name, + ", ".join([x[0] for x in running]), + ) + for name, pid in running: + pidfile = "/var/run/{}/{}.pid".format(self.routertype, name) + logger.info("%s: killing %s", self.name, name) + self.cmd("kill -SIGBUS %d" % pid) + self.cmd("rm -- " + pidfile) + + sleep( + 0.5, + "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name, + ) errors = self.checkRouterCores(reportOnce=True) if self.checkRouterVersion("<", minErrorVersion): @@ -1515,10 +1501,13 @@ def stopRouter(self, assertOnError=True, minErrorVersion="5.1"): def removeIPs(self): for interface in self.intfNames(): try: - self.intf_ip_cmd(interface, "ip address flush " + interface) + self.intf_ip_cmd(interface, "ip -4 address flush " + interface) + self.intf_ip_cmd( + interface, "ip -6 address flush " + interface + " scope global" + ) except Exception as ex: logger.error("%s can't remove IPs %s", self, str(ex)) - # pdb.set_trace() + # breakpoint() # assert False, "can't remove IPs %s" % str(ex) def checkCapability(self, daemon, param): @@ -1542,6 +1531,11 @@ def loadConf(self, daemon, source=None, param=None): """ # Unfortunately this API allowsfor source to not exist for any and all routers. + source_was_none = source is None + if source_was_none: + source = f"{daemon}.conf" + + # "" to avoid loading a default config which is present in router dir if source: head, tail = os.path.split(source) if not head and not self.path_exists(tail): @@ -1549,7 +1543,7 @@ def loadConf(self, daemon, source=None, param=None): router_relative = os.path.join(script_dir, self.name, tail) if self.path_exists(router_relative): source = router_relative - self.logger.info( + self.logger.debug( "using router relative configuration: {}".format(source) ) @@ -1562,14 +1556,40 @@ def loadConf(self, daemon, source=None, param=None): if param is not None: self.daemons_options[daemon] = param conf_file = "/etc/{}/{}.conf".format(self.routertype, daemon) - if source is None or not os.path.exists(source): + if source and not os.path.exists(source): + logger.warning( + "missing config '%s' for '%s' creating empty file '%s'", + self.name, + source, + conf_file, + ) if daemon == "frr" or not self.unified_config: self.cmd_raises("rm -f " + conf_file) self.cmd_raises("touch " + conf_file) - else: - self.cmd_raises("cp {} {}".format(source, conf_file)) + self.cmd_raises( + "chown {0}:{0} {1}".format(self.routertype, conf_file) + ) + self.cmd_raises("chmod 664 {}".format(conf_file)) + elif source: + # copy zebra.conf to mgmtd folder, which can be used during startup + if daemon == "zebra" and not self.unified_config: + conf_file_mgmt = "/etc/{}/{}.conf".format(self.routertype, "mgmtd") + logger.debug( + "copying '%s' as '%s' on '%s'", + source, + conf_file_mgmt, + self.name, + ) + self.cmd_raises("cp {} {}".format(source, conf_file_mgmt)) + self.cmd_raises( + "chown {0}:{0} {1}".format(self.routertype, conf_file_mgmt) + ) + self.cmd_raises("chmod 664 {}".format(conf_file_mgmt)) - if not self.unified_config or daemon == "frr": + logger.debug( + "copying '%s' as '%s' on '%s'", source, conf_file, self.name + ) + self.cmd_raises("cp {} {}".format(source, conf_file)) self.cmd_raises("chown {0}:{0} {1}".format(self.routertype, conf_file)) self.cmd_raises("chmod 664 {}".format(conf_file)) @@ -1578,20 +1598,26 @@ def loadConf(self, daemon, source=None, param=None): self.cmd('echo "agentXSocket /etc/frr/agentx" >> /etc/snmp/frr.conf') self.cmd('echo "mibs +ALL" > /etc/snmp/snmp.conf') + if (daemon == "zebra") and (self.daemons["mgmtd"] == 0): + # Add mgmtd with zebra - if it exists + mgmtd_path = os.path.join(self.daemondir, "mgmtd") + if os.path.isfile(mgmtd_path): + self.daemons["mgmtd"] = 1 + self.daemons_options["mgmtd"] = "" + # Auto-Started mgmtd has no config, so it will read from zebra config + if (daemon == "zebra") and (self.daemons["staticd"] == 0): # Add staticd with zebra - if it exists - try: - staticd_path = os.path.join(self.daemondir, "staticd") - except: - pdb.set_trace() - + staticd_path = os.path.join(self.daemondir, "staticd") if os.path.isfile(staticd_path): 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)) - # print "Daemons after:", self.daemons + logger.warning("No daemon {} known".format(daemon)) + + return source if os.path.exists(source) else "" def runInWindow(self, cmd, title=None): return self.run_in_window(cmd, title) @@ -1616,8 +1642,6 @@ def startRouter(self, tgen=None): # 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))) - # Remove old core files - 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 @@ -1658,8 +1682,7 @@ def startRouter(self, tgen=None): # used self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels") - shell_routers = g_extra_config["shell"] - if "all" in shell_routers or self.name in shell_routers: + if g_pytest_config.name_in_option_list(self.name, "--shell"): self.run_in_window(os.getenv("SHELL", "bash"), title="sh-%s" % self.name) if self.daemons["eigrpd"] == 1: @@ -1676,8 +1699,7 @@ def startRouter(self, tgen=None): status = self.startRouterDaemons(tgen=tgen) - vtysh_routers = g_extra_config["vtysh"] - if "all" in vtysh_routers or self.name in vtysh_routers: + if g_pytest_config.name_in_option_list(self.name, "--vtysh"): self.run_in_window("vtysh", title="vt-%s" % self.name) if self.unified_config: @@ -1692,18 +1714,22 @@ 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)) + filename = "{}/{}/{}.{}".format(self.logdir, self.name, daemon, log) + log = "" + with open(filename) as file: + log = file.read() + return log def startRouterDaemons(self, daemons=None, tgen=None): "Starts FRR daemons for this router." - asan_abort = g_extra_config["asan_abort"] - gdb_breakpoints = g_extra_config["gdb_breakpoints"] - gdb_daemons = g_extra_config["gdb_daemons"] - gdb_routers = g_extra_config["gdb_routers"] - valgrind_extra = g_extra_config["valgrind_extra"] - valgrind_memleaks = g_extra_config["valgrind_memleaks"] - strace_daemons = g_extra_config["strace_daemons"] + asan_abort = bool(g_pytest_config.option.asan_abort) + gdb_breakpoints = g_pytest_config.get_option_list("--gdb-breakpoints") + gdb_daemons = g_pytest_config.get_option_list("--gdb-daemons") + gdb_routers = g_pytest_config.get_option_list("--gdb-routers") + valgrind_extra = bool(g_pytest_config.option.valgrind_extra) + valgrind_memleaks = bool(g_pytest_config.option.valgrind_memleaks) + strace_daemons = g_pytest_config.get_option_list("--strace-daemons") # Get global bundle data if not self.path_exists("/etc/frr/support_bundle_commands.conf"): @@ -1727,11 +1753,31 @@ def startRouterDaemons(self, daemons=None, tgen=None): self.reportCores = True # XXX: glue code forward ported from removed function. - if self.version == None: + if self.version is None: self.version = self.cmd( os.path.join(self.daemondir, "bgpd") + " -v" ).split()[2] logger.info("{}: running version: {}".format(self.name, self.version)) + + perfds = {} + perf_options = g_pytest_config.get_option("--perf-options", "-g") + for perf in g_pytest_config.get_option("--perf", []): + if "," in perf: + daemon, routers = perf.split(",", 1) + perfds[daemon] = routers.split(",") + else: + daemon = perf + perfds[daemon] = ["all"] + + logd_options = {} + for logd in g_pytest_config.get_option("--logd", []): + if "," in logd: + daemon, routers = logd.split(",", 1) + logd_options[daemon] = routers.split(",") + else: + daemon = logd + logd_options[daemon] = ["all"] + # If `daemons` was specified then some upper API called us with # specific daemons, otherwise just use our own configuration. daemons_list = [] @@ -1743,22 +1789,36 @@ def startRouterDaemons(self, daemons=None, tgen=None): if self.daemons[daemon] == 1: daemons_list.append(daemon) + tail_log_files = [] + check_daemon_files = [] + def start_daemon(daemon, extra_opts=None): daemon_opts = self.daemons_options.get(daemon, "") + + # get pid and vty filenames and remove the files + m = re.match(r"(.* |^)-n (\d+)( ?.*|$)", daemon_opts) + dfname = daemon if not m else "{}-{}".format(daemon, m.group(2)) + runbase = "/var/run/{}/{}".format(self.routertype, dfname) + # If this is a new system bring-up remove the pid/vty files, otherwise + # do not since apparently presence of the pidfile impacts BGP GR + self.cmd_status("rm -f {0}.pid {0}.vty".format(runbase)) + rediropt = " > {0}.out 2> {0}.err".format(daemon) if daemon == "snmpd": binary = "/usr/sbin/snmpd" cmdenv = "" cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format( daemon_opts - ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype) + ) + "{}.pid -x /etc/frr/agentx".format(runbase) + # check_daemon_files.append(runbase + ".pid") else: binary = os.path.join(self.daemondir, daemon) + check_daemon_files.extend([runbase + ".pid", runbase + ".vty"]) cmdenv = "ASAN_OPTIONS=" if asan_abort: - cmdenv = "abort_on_error=1:" - cmdenv += "log_path={0}/{1}.{2}.asan ".format( + cmdenv += "abort_on_error=1:" + cmdenv += "log_path={0}/{1}.asan.{2} ".format( self.logdir, self.name, daemon ) @@ -1781,9 +1841,15 @@ def start_daemon(daemon, extra_opts=None): daemon, self.logdir, self.name ) - cmdopt = "{} --command-log-always --log file:{}.log --log-level debug".format( - daemon_opts, daemon - ) + cmdopt = "{} --command-log-always ".format(daemon_opts) + cmdopt += "--log file:{}.log --log-level debug".format(daemon) + + if daemon in logd_options: + logdopt = logd_options[daemon] + if "all" in logdopt or self.name in logdopt: + tail_log_files.append( + "{}/{}/{}.log".format(self.logdir, self.name, daemon) + ) if extra_opts: cmdopt += " " + extra_opts @@ -1810,6 +1876,27 @@ def start_daemon(daemon, extra_opts=None): logger.info( "%s: %s %s launched in gdb window", self, self.routertype, daemon ) + elif daemon in perfds and ( + self.name in perfds[daemon] or "all" in perfds[daemon] + ): + cmdopt += rediropt + cmd = " ".join( + ["perf record {} --".format(perf_options), binary, cmdopt] + ) + p = self.popen(cmd) + self.perf_daemons[daemon] = p + if p.poll() and p.returncode: + self.logger.error( + '%s: Failed to launch "%s" (%s) with perf using: %s', + self, + daemon, + p.returncode, + cmd, + ) + else: + logger.debug( + "%s: %s %s started with perf", self, self.routertype, daemon + ) else: if daemon != "snmpd": cmdopt += " -d " @@ -1832,9 +1919,15 @@ def start_daemon(daemon, extra_opts=None): else "", ) else: - logger.info("%s: %s %s started", self, self.routertype, daemon) + logger.debug("%s: %s %s started", self, self.routertype, daemon) + + # Start mgmtd first + if "mgmtd" in daemons_list: + start_daemon("mgmtd") + while "mgmtd" in daemons_list: + daemons_list.remove("mgmtd") - # Start Zebra first + # Start Zebra after mgmtd if "zebra" in daemons_list: start_daemon("zebra", "-s 90000000") while "zebra" in daemons_list: @@ -1855,15 +1948,6 @@ def start_daemon(daemon, extra_opts=None): while "snmpd" in daemons_list: daemons_list.remove("snmpd") - if daemons is None: - # Fix Link-Local Addresses on initial startup - # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this - _, output, _ = self.cmd_status( - "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; echo $i: $mac; [ -z \"$mac\" ] && continue; 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", - stderr=subprocess.STDOUT, - ) - logger.debug("Set MACs:\n%s", output) - # Now start all the other daemons for daemon in daemons_list: if self.daemons[daemon] == 0: @@ -1871,16 +1955,50 @@ def start_daemon(daemon, extra_opts=None): start_daemon(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" + wait_time = 30 if (gdb_routers or gdb_daemons) else 10 + timeout = Timeout(wait_time) + for remaining in timeout: + if not check_daemon_files: + break + check = check_daemon_files[0] + if self.path_exists(check): + check_daemon_files.pop(0) + continue + self.logger.debug("Waiting {}s for {} to appear".format(remaining, check)) + time.sleep(0.5) + + if check_daemon_files: + assert False, "Timeout({}) waiting for {} to appear on {}".format( + wait_time, check_daemon_files[0], self.name + ) # Update the permissions on the log files self.cmd("chown frr:frr -R {}/{}".format(self.logdir, self.name)) self.cmd("chmod ug+rwX,o+r -R {}/{}".format(self.logdir, self.name)) + if "frr" in logd_options: + logdopt = logd_options["frr"] + if "all" in logdopt or self.name in logdopt: + tail_log_files.append("{}/{}/frr.log".format(self.logdir, self.name)) + + for tailf in tail_log_files: + self.run_in_window("tail -n10000 -F " + tailf, title=tailf, background=True) + return "" + def pid_exists(self, pid): + if pid <= 0: + return False + try: + # If we are not using PID namespaces then we will be a parent of the pid, + # otherwise the init process of the PID namespace will have reaped the proc. + os.waitpid(pid, os.WNOHANG) + except Exception: + pass + + rc, o, e = self.cmd_status("kill -0 " + str(pid), warn=False) + return rc == 0 or "No such process" not in e + def killRouterDaemons( self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1" ): @@ -1900,15 +2018,15 @@ def killRouterDaemons( if re.search(r"%s" % daemon, d): daemonpidfile = d.rstrip() daemonpid = self.cmd("cat %s" % daemonpidfile).rstrip() - if daemonpid.isdigit() and pid_exists(int(daemonpid)): - logger.info( + if daemonpid.isdigit() and self.pid_exists(int(daemonpid)): + logger.debug( "{}: killing {}".format( self.name, os.path.basename(daemonpidfile.rsplit(".", 1)[0]), ) ) - os.kill(int(daemonpid), signal.SIGKILL) - if pid_exists(int(daemonpid)): + self.cmd_status("kill -KILL {}".format(daemonpid)) + if self.pid_exists(int(daemonpid)): numRunning += 1 while wait and numRunning > 0: sleep( @@ -1922,7 +2040,7 @@ def killRouterDaemons( 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( + if daemonpid.isdigit() and self.pid_exists( int(daemonpid) ): logger.info( @@ -1933,8 +2051,10 @@ def killRouterDaemons( ), ) ) - os.kill(int(daemonpid), signal.SIGKILL) - if daemonpid.isdigit() and not pid_exists( + self.cmd_status( + "kill -KILL {}".format(daemonpid) + ) + if daemonpid.isdigit() and not self.pid_exists( int(daemonpid) ): numRunning -= 1 @@ -2165,7 +2285,7 @@ def report_memory_leaks(self, filename_prefix, testscript): log = self.getStdErr(daemon) if "memstats" in log: # Found memory leak - logger.info( + logger.warning( "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log) ) if not leakfound: diff --git a/tests/topotests/mgmt_config/r1/early-end-zebra.conf b/tests/topotests/mgmt_config/r1/early-end-zebra.conf new file mode 100644 index 000000000000..44a2f968253e --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end-zebra.conf @@ -0,0 +1,6 @@ +allow-external-route-update +end +ip multicast rpf-lookup-mode urib-only +end +ip table range 2 3 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-end.conf b/tests/topotests/mgmt_config/r1/early-end.conf new file mode 100644 index 000000000000..3aacad647138 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end.conf @@ -0,0 +1,8 @@ +ip route 15.1.0.0/24 101.0.0.2 +end +ip route 15.2.0.0/24 101.0.0.2 +end +ip route 15.3.0.0/24 101.0.0.2 +end +ip route 15.4.0.0/24 101.0.0.2 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-end2-zebra.conf b/tests/topotests/mgmt_config/r1/early-end2-zebra.conf new file mode 100644 index 000000000000..37619d52ace1 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end2-zebra.conf @@ -0,0 +1,7 @@ +conf t +allow-external-route-update +end +ip multicast rpf-lookup-mode urib-only +end +ip table range 2 3 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-end2.conf b/tests/topotests/mgmt_config/r1/early-end2.conf new file mode 100644 index 000000000000..229ccc7410bb --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-end2.conf @@ -0,0 +1,9 @@ +conf t +ip route 16.1.0.0/24 101.0.0.2 +end +ip route 16.2.0.0/24 101.0.0.2 +end +ip route 16.3.0.0/24 101.0.0.2 +end +ip route 16.4.0.0/24 101.0.0.2 +end \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit-zebra.conf b/tests/topotests/mgmt_config/r1/early-exit-zebra.conf new file mode 100644 index 000000000000..44f202dbcbb6 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit-zebra.conf @@ -0,0 +1,6 @@ +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only +exit +ip table range 2 3 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit.conf b/tests/topotests/mgmt_config/r1/early-exit.conf new file mode 100644 index 000000000000..c6a52df5d38a --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit.conf @@ -0,0 +1,8 @@ +ip route 13.1.0.0/24 101.0.0.2 +exit +ip route 13.2.0.0/24 101.0.0.2 +exit +ip route 13.3.0.0/24 101.0.0.2 +exit +ip route 13.4.0.0/24 101.0.0.2 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf b/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf new file mode 100644 index 000000000000..c7109bfd395d --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit2-zebra.conf @@ -0,0 +1,7 @@ +conf t +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only +exit +ip table range 2 3 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/early-exit2.conf b/tests/topotests/mgmt_config/r1/early-exit2.conf new file mode 100644 index 000000000000..79510c0aec3c --- /dev/null +++ b/tests/topotests/mgmt_config/r1/early-exit2.conf @@ -0,0 +1,9 @@ +conf t +ip route 14.1.0.0/24 101.0.0.2 +exit +ip route 14.2.0.0/24 101.0.0.2 +exit +ip route 14.3.0.0/24 101.0.0.2 +exit +ip route 14.4.0.0/24 101.0.0.2 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/mgmtd.conf b/tests/topotests/mgmt_config/r1/mgmtd.conf new file mode 100644 index 000000000000..318de765c841 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/mgmtd.conf @@ -0,0 +1,11 @@ +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client backend +debug mgmt client frontend + +ip route 12.0.0.0/24 101.0.0.2 + +ipv6 route 2012::/48 2101::2 \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/normal-exit.conf b/tests/topotests/mgmt_config/r1/normal-exit.conf new file mode 100644 index 000000000000..c6a52df5d38a --- /dev/null +++ b/tests/topotests/mgmt_config/r1/normal-exit.conf @@ -0,0 +1,8 @@ +ip route 13.1.0.0/24 101.0.0.2 +exit +ip route 13.2.0.0/24 101.0.0.2 +exit +ip route 13.3.0.0/24 101.0.0.2 +exit +ip route 13.4.0.0/24 101.0.0.2 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/one-exit-zebra.conf b/tests/topotests/mgmt_config/r1/one-exit-zebra.conf new file mode 100644 index 000000000000..0c38459702a6 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit-zebra.conf @@ -0,0 +1,3 @@ +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only diff --git a/tests/topotests/mgmt_config/r1/one-exit.conf b/tests/topotests/mgmt_config/r1/one-exit.conf new file mode 100644 index 000000000000..47147d44eb6f --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit.conf @@ -0,0 +1,3 @@ +ip route 20.1.0.0/24 101.0.0.2 +exit +ip route 20.2.0.0/24 101.0.0.2 diff --git a/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf b/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf new file mode 100644 index 000000000000..34acb76d92d9 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit2-zebra.conf @@ -0,0 +1,4 @@ +conf t +allow-external-route-update +exit +ip multicast rpf-lookup-mode urib-only \ No newline at end of file diff --git a/tests/topotests/mgmt_config/r1/one-exit2.conf b/tests/topotests/mgmt_config/r1/one-exit2.conf new file mode 100644 index 000000000000..262339a8543e --- /dev/null +++ b/tests/topotests/mgmt_config/r1/one-exit2.conf @@ -0,0 +1,4 @@ +conf t +ip route 21.1.0.0/24 101.0.0.2 +exit +ip route 21.2.0.0/24 101.0.0.2 diff --git a/tests/topotests/mgmt_config/r1/zebra.conf b/tests/topotests/mgmt_config/r1/zebra.conf new file mode 100644 index 000000000000..f3264efb0058 --- /dev/null +++ b/tests/topotests/mgmt_config/r1/zebra.conf @@ -0,0 +1,7 @@ +log timestamp precision 6 +log file frr-r1.log debug + +interface r1-eth0 + ip address 101.0.0.1/24 + ipv6 address 2101::1/64 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_config/test_config.py b/tests/topotests/mgmt_config/test_config.py new file mode 100644 index 000000000000..b07ed8f7fde4 --- /dev/null +++ b/tests/topotests/mgmt_config/test_config.py @@ -0,0 +1,385 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# June 10 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +""" +Test mgmtd parsing of configs. + +So: + +MGMTD matches zebra: + +one exit file: ONE: vty -f file +one exit redir: ONE: vty < file +early exit file: ONE: vty -f file +early exit redir: ONE: vty < file +early end file: ALL: vty -f file +early end redir: ONE: vty < file + +Raw tests: + +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh < didn't work after exit +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work after 1 exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits +FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end + +FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing + +Before fixed: + +one exit file: NONE: vty -f file +early exit file: NONE: vty -f file + +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_file - AssertionError: vtysh -f didn't work before exit +FAILED mgmt_config/test_config.py::test_mgmtd_one_exit_redir - AssertionError: vtysh < didn't work after exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_file - AssertionError: vtysh -f didn't work before exit +FAILED mgmt_config/test_config.py::test_mgmtd_early_exit_redir - AssertionError: vtysh < didn't work after 1 exits +FAILED mgmt_config/test_config.py::test_mgmtd_early_end_redir - AssertionError: vtysh < didn't work after 1 end + +FAILED mgmt_config/test_config.py::test_zebra_one_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_one_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_file - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_exit_redir - AssertionError: zebra second conf missing +FAILED mgmt_config/test_config.py::test_zebra_early_end_redir - AssertionError: zebra second conf missing + +""" +import ipaddress +import logging +import os +import re +from pathlib import Path + +import pytest +from lib.common_config import retry, step +from lib.topogen import Topogen, TopoRouter + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + + +@retry(retry_timeout=1, initial_wait=0.1) +def check_kernel(r1, prefix, expected=True): + net = ipaddress.ip_network(prefix) + if net.version == 6: + kernel = r1.cmd_nostatus("ip -6 route show", warn=not expected) + else: + kernel = r1.cmd_nostatus("ip -4 route show", warn=not expected) + + logging.debug("checking kernel routing table:\n%0.1920s", kernel) + route = f"{str(net)}(?: nhid [0-9]+)?.*proto (static|196)" + m = re.search(route, kernel) + if expected and not m: + return f"Failed to find \n'{route}'\n in \n'{kernel:.1920}'" + elif not expected and m: + return f"Failed found \n'{route}'\n in \n'{kernel:.1920}'" + return None + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + # configure mgmtd using current mgmtd config file + tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD) + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def save_log_snippet(logfile, content, savepath=None): + os.sync() + os.sync() + os.sync() + + with open(logfile, encoding="utf-8") as f: + buf = f.read() + assert content == buf[: len(content)] + newcontent = buf[len(content) :] + + if savepath: + with open(savepath, "w", encoding="utf-8") as f: + f.write(newcontent) + + return buf + + +def mapname(lname): + return lname.replace(".conf", "") + "-log.txt" + + +logbuf = "" + + +@pytest.fixture(scope="module") +def r1(tgen): + return tgen.gears["r1"].net + + +@pytest.fixture(scope="module") +def confdir(): + return Path(os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]) / "r1" + + +@pytest.fixture(scope="module") +def tempdir(r1): + return Path(r1.rundir) + + +@pytest.fixture(scope="module") +def logpath(tempdir): + return tempdir / "mgmtd.log" + + +@pytest.fixture(autouse=True, scope="function") +def cleanup_config(r1, tempdir, logpath): + global logbuf + + logbuf = save_log_snippet(logpath, logbuf, "/dev/null") + + yield + + r1.cmd_nostatus("vtysh -c 'conf t' -c 'no allow-external-route-update'") + r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip multicast rpf-lookup-mode urib-only'") + r1.cmd_nostatus("vtysh -c 'conf t' -c 'no ip table range 2 3'") + + logbuf = save_log_snippet(logpath, logbuf, "/dev/null") + + +def test_staticd_startup(r1): + r1.cmd_nostatus( + "vtysh -c 'debug mgmt client frontend' " + "-c 'debug mgmt client backend' " + "-c 'debug mgmt backend frontend datastore transaction'" + ) + step("Verifying routes are present on r1") + result = check_kernel(r1, "12.0.0.0/24", retry_timeout=3.0) + assert result is None + + +def test_mgmtd_one_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "20.1.0.0/24") + result2 = check_kernel(r1, "20.2.0.0/24") + + assert result1 is None, "vtysh -f didn't work before exit" + assert result2 is not None, "vtysh < worked after exit, unexpected" + + +def test_mgmtd_one_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit2.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "21.1.0.0/24") + result2 = check_kernel(r1, "21.2.0.0/24") + + assert result1 is None, "vtysh < didn't work before exit" + assert result2 is not None, "vtysh < worked after exit, unexpected" + + +def test_mgmtd_early_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "13.1.0.0/24") + result2 = check_kernel(r1, "13.2.0.0/24") + result3 = check_kernel(r1, "13.3.0.0/24") + + assert result1 is None, "vtysh -f didn't work before exit" + assert result2 is not None, "vtysh -f worked after 1 exit, unexpected" + assert result3 is not None, "vtysh -f worked after 2 exit, unexpected" + + +def test_mgmtd_early_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit2.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "14.1.0.0/24") + result2 = check_kernel(r1, "14.2.0.0/24") + result3 = check_kernel(r1, "14.3.0.0/24") + + assert result1 is None, "vtysh < didn't work before exit" + assert result2 is not None, "vtysh < worked after 1 exits, unexpected" + assert result3 is not None, "vtysh < worked after 2 exits, unexpected" + + +def test_mgmtd_early_end_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "15.1.0.0/24") + result2 = check_kernel(r1, "15.2.0.0/24") + result3 = check_kernel(r1, "15.3.0.0/24") + + assert result1 is None, "vtysh -f didn't work before end" + assert result2 is None, "vtysh -f didn't work after 1 end" + assert result3 is None, "vtysh -f didn't work after 2 ends" + + +def test_mgmtd_early_end_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end2.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + result1 = check_kernel(r1, "16.1.0.0/24") + result2 = check_kernel(r1, "16.2.0.0/24") + result3 = check_kernel(r1, "16.3.0.0/24") + + assert result1 is None, "vtysh < didn't work before end" + assert result2 is not None, "vtysh < worked after 1 end, unexpected" + assert result3 is not None, "vtysh < worked after 2 end, unexpected" + + +# +# Zebra +# + + +def test_zebra_one_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit-zebra.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + + +def test_zebra_one_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "one-exit2-zebra.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + + +def test_zebra_early_exit_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit-zebra.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected" + + +def test_zebra_early_exit_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-exit2-zebra.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected" + + +def test_zebra_early_end_file(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end-zebra.conf" + step(f"load {conf} file with vtysh -f ") + output = r1.cmd_nostatus(f"vtysh -f {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" in showrun + ), "zebra second conf missing" + assert "ip table range 2 3" in showrun, "zebra third missing" + + +def test_zebra_early_end_redir(r1, confdir, tempdir, logpath): + global logbuf + + conf = "early-end2-zebra.conf" + step(f"Redirect {conf} file into vtysh") + output = r1.cmd_nostatus(f"vtysh < {confdir / conf}") + logbuf = save_log_snippet(logpath, logbuf, tempdir / mapname(conf)) + print(output) + + showrun = r1.cmd_nostatus("vtysh -c 'show running'") + + assert "allow-external-route-update" in showrun, "zebra conf missing" + assert ( + "ip multicast rpf-lookup-mode urib-only" not in showrun + ), "zebra second conf present, unexpected" + assert "ip table range 2 3" not in showrun, "zebra third conf present, unexpected" diff --git a/tests/topotests/mgmt_startup/r1/mgmtd.conf b/tests/topotests/mgmt_startup/r1/mgmtd.conf new file mode 100644 index 000000000000..ecc829c66247 --- /dev/null +++ b/tests/topotests/mgmt_startup/r1/mgmtd.conf @@ -0,0 +1,13 @@ +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client backend +debug mgmt client frontend + +ip route 12.0.0.0/24 101.0.0.2 +ip route 13.0.0.0/24 101.0.0.3 + +ipv6 route 2012::/48 2101::2 +ipv6 route 2013::/48 2101::3 diff --git a/tests/topotests/mgmt_startup/r1/zebra.conf b/tests/topotests/mgmt_startup/r1/zebra.conf new file mode 100644 index 000000000000..98cc95ba2b4c --- /dev/null +++ b/tests/topotests/mgmt_startup/r1/zebra.conf @@ -0,0 +1,7 @@ +log timestamp precision 3 +log file frr2.log + +interface r1-eth0 + ip address 101.0.0.1/24 + ipv6 address 2101::1/64 +exit \ No newline at end of file diff --git a/tests/topotests/mgmt_startup/r2/staticd.conf b/tests/topotests/mgmt_startup/r2/staticd.conf new file mode 100644 index 000000000000..b581ed2dc3e6 --- /dev/null +++ b/tests/topotests/mgmt_startup/r2/staticd.conf @@ -0,0 +1,7 @@ +log timestamp precision 3 + +ip route 11.0.0.0/24 101.0.0.1 +ip route 13.0.0.0/24 101.0.0.3 + +ipv6 route 2011::/48 2102::1 +ipv6 route 2013::/48 2102::3 \ No newline at end of file diff --git a/tests/topotests/mgmt_startup/r2/zebra.conf b/tests/topotests/mgmt_startup/r2/zebra.conf new file mode 100644 index 000000000000..1d37a65737b8 --- /dev/null +++ b/tests/topotests/mgmt_startup/r2/zebra.conf @@ -0,0 +1,12 @@ +log timestamp precision 3 +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client backend +debug mgmt client frontend + +interface r2-eth0 + ip address 101.0.0.2/24 + ipv6 address 2101::2/64 diff --git a/tests/topotests/mgmt_startup/r3/zebra.conf b/tests/topotests/mgmt_startup/r3/zebra.conf new file mode 100644 index 000000000000..8419d7497555 --- /dev/null +++ b/tests/topotests/mgmt_startup/r3/zebra.conf @@ -0,0 +1,18 @@ +log timestamp precision 3 +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client backend +debug mgmt client frontend + +interface r3-eth0 + ip address 101.0.0.3/24 + ipv6 address 2101::3/64 + +ip route 11.0.0.0/24 101.0.0.1 +ip route 12.0.0.0/24 101.0.0.2 + +ipv6 route 2011::/48 2101::1 +ipv6 route 2012::/48 2101::2 diff --git a/tests/topotests/mgmt_startup/r4/frr.conf b/tests/topotests/mgmt_startup/r4/frr.conf new file mode 100644 index 000000000000..5f3b35d9cebf --- /dev/null +++ b/tests/topotests/mgmt_startup/r4/frr.conf @@ -0,0 +1,21 @@ +log timestamp precision 6 +log file frr.log + +debug northbound notifications +debug northbound libyang +debug northbound events +debug northbound callbacks +debug mgmt backend datastore frontend transaction +debug mgmt client backend +debug mgmt client frontend + +interface r4-eth0 + ip address 101.0.0.4/24 + ipv6 address 2101::4/64 +exit + +ip route 11.0.0.0/24 101.0.0.1 +ip route 12.0.0.0/24 101.0.0.2 + +ipv6 route 2012::/48 2101::2 +ipv6 route 2013::/48 2101::3 diff --git a/tests/topotests/mgmt_startup/test_bigconf.py b/tests/topotests/mgmt_startup/test_bigconf.py new file mode 100644 index 000000000000..4f46c8fabdca --- /dev/null +++ b/tests/topotests/mgmt_startup/test_bigconf.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# May 2 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +""" +Test static route startup functionality +""" + +import datetime +import logging +import os + +import pytest +from lib.common_config import step +from lib.topogen import Topogen, TopoRouter +from munet.base import Timeout +from util import check_kernel, check_vtysh_up, write_big_route_conf + +CWD = os.path.dirname(os.path.realpath(__file__)) + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + + +track = Timeout(0) +ROUTE_COUNT = 2500 +ROUTE_RANGE = [None, None] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + global start_time + topodef = { + "s1": ("r1",), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + prologue = open(f"{CWD}/r1/mgmtd.conf").read() + + confpath = f"{tgen.gears['r1'].gearlogdir}/r1-late-big.conf" + start, end = write_big_route_conf("10.0.0.0/8", ROUTE_COUNT, confpath, prologue) + ROUTE_RANGE[0] = start + ROUTE_RANGE[1] = end + + # configure mgmtd using current mgmtd config file + tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD, confpath) + + track.started_on = datetime.datetime.now() + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_staticd_latestart(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.routers()["r1"] + + step(f"Verifying {ROUTE_COUNT} startup routes are present") + + check_vtysh_up(r1) + logging.info("r1: vtysh connected after %ss", track.elapsed()) + + result = check_kernel(r1, ROUTE_RANGE[0], retry_timeout=60) + assert result is None + logging.info("r1: first route installed after %ss", track.elapsed()) + + result = check_kernel(r1, ROUTE_RANGE[1], retry_timeout=60) + assert result is None + logging.info("r1: last route installed after %ss", track.elapsed()) diff --git a/tests/topotests/mgmt_startup/test_cfgfile_var.py b/tests/topotests/mgmt_startup/test_cfgfile_var.py new file mode 100644 index 000000000000..6a54f7191011 --- /dev/null +++ b/tests/topotests/mgmt_startup/test_cfgfile_var.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# May 2 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +""" +Test static route functionality using old or new configuration files. + +User compat: + + - mgmtd split config will first look to `/etc/frr/zebra.conf` + then `/etc/frr/staticd.conf` and finally `/etc/frr/mgmtd.conf` + + - When new components are converted to mgmtd their split config should be + added here too. + +Topotest compat: + + - `mgmtd.conf` is copied to `/etc/frr/` for use by mgmtd when implicit load, + or explicit load no config specified. + + - `staticd.conf` is copied to `/etc/frr/` for use by mgmtd when staticd + is explicit load implict config, and explicit config. + +""" + +import pytest +from lib.common_config import step +from lib.topogen import Topogen, TopoRouter +from util import check_kernel + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1", "r2", "r3", "r4"), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + # configure mgmtd using current mgmtd config file + tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD, "mgmtd.conf") + + # user/topotest compat: + # configure mgmtd using old staticd config file, with explicity staticd + # load. + tgen.gears["r2"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r2"].load_config(TopoRouter.RD_STATIC, "staticd.conf") + + # user compat: + # configure mgmtd using backup config file `zebra.conf` + tgen.gears["r3"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + + # configure mgmtd using current mgmtd config file + tgen.gears["r4"].load_frr_config("frr.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_staticd_routes_present(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + for x in ["r1", "r2", "r3", "r4"]: + tgen.gears[x].net.cmd_nostatus( + "vtysh -c 'debug mgmt client frontend' " + "-c 'debug mgmt client backend' " + "-c 'debug mgmt backend frontend datastore transaction'" + ) + + r1 = tgen.routers()["r1"] + r2 = tgen.routers()["r2"] + r3 = tgen.routers()["r3"] + r4 = tgen.routers()["r4"] + + step("Verifying routes are present on r1") + result = check_kernel(r1, "12.0.0.0/24") + assert result is None + result = check_kernel(r1, "13.0.0.0/24") + assert result is None + + step("Verifying routes are present on r2") + result = check_kernel(r2, "11.0.0.0/24") + assert result is None + result = check_kernel(r2, "13.0.0.0/24") + assert result is None + + step("Verifying routes are present on r3") + result = check_kernel(r3, "11.0.0.0/24") + assert result is None + result = check_kernel(r3, "12.0.0.0/24") + assert result is None + + step("Verifying routes are present on r4") + result = check_kernel(r4, "11.0.0.0/24") + assert result is None + result = check_kernel(r4, "12.0.0.0/24") + assert result is None diff --git a/tests/topotests/mgmt_startup/test_late_bigconf.py b/tests/topotests/mgmt_startup/test_late_bigconf.py new file mode 100644 index 000000000000..0b5bf38d1047 --- /dev/null +++ b/tests/topotests/mgmt_startup/test_late_bigconf.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# May 2 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# + +""" +Verify large set of routes present when staticd (backend client) is started after it's +startup config is present during launch. +""" + +import logging +import os + +import pytest +from lib.common_config import step +from lib.topogen import Topogen, TopoRouter +from munet.base import Timeout +from util import check_kernel, check_vtysh_up, write_big_route_conf + +CWD = os.path.dirname(os.path.realpath(__file__)) + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + +track = Timeout(0) +ROUTE_COUNT = 2500 +ROUTE_RANGE = [None, None] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + global start_time + topodef = { + "s1": ("r1",), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + prologue = open(f"{CWD}/r1/mgmtd.conf").read() + + confpath = f"{tgen.gears['r1'].gearlogdir}/r1-late-big.conf" + start, end = write_big_route_conf("10.0.0.0/8", ROUTE_COUNT, confpath, prologue) + ROUTE_RANGE[0] = start + ROUTE_RANGE[1] = end + + # configure mgmtd using current mgmtd config file + tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD, confpath) + + # Explicit disable staticd now.. + tgen.gears["r1"].net.daemons["staticd"] = 0 + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_staticd_latestart(tgen): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.routers()["r1"] + + check_vtysh_up(r1) + logging.info("r1: vtysh connected after %ss", track.elapsed()) + + result = check_kernel(r1, ROUTE_RANGE[0], retry_timeout=60, expected=False) + assert result is not None, "first route present and should not be" + result = check_kernel(r1, ROUTE_RANGE[1], retry_timeout=60, expected=False) + assert result is not None, "last route present and should not be" + + step("Starting staticd") + t2 = Timeout(0) + r1.startDaemons(["staticd"]) + + result = check_kernel(r1, ROUTE_RANGE[0], retry_timeout=60) + assert result is None, "first route not present and should be" + logging.info("r1: elapsed time for first route %ss", t2.elapsed()) + + count = 0 + ocount = 0 + while count < ROUTE_COUNT: + rc, o, e = r1.net.cmd_status("ip -o route | wc -l") + if not rc: + if count > ocount + 100: + ocount = count + logging.info("r1: elapsed time for %d routes %s", count, t2.elapsed()) + count = int(o) + + result = check_kernel(r1, ROUTE_RANGE[1], retry_timeout=1200) + assert result is None, "last route not present and should be" + logging.info("r1: elapsed time for last route %ss", t2.elapsed()) diff --git a/tests/topotests/mgmt_startup/test_late_uniconf.py b/tests/topotests/mgmt_startup/test_late_uniconf.py new file mode 100644 index 000000000000..d4e7e07ad633 --- /dev/null +++ b/tests/topotests/mgmt_startup/test_late_uniconf.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# May 2 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# + +""" +Verify routes present when staticd (backend client) is started after it's startup +config, contained inside a unified configuration file, is present during launch. +""" +import pytest +from lib.topogen import Topogen +from util import _test_staticd_late_start + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r4",), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + # configure mgmtd using current mgmtd config file + tgen.gears["r4"].load_frr_config("frr.conf") + + # Explicit disable staticd now.. + tgen.gears["r4"].net.daemons["staticd"] = 0 + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_staticd_late_start(tgen): + return _test_staticd_late_start(tgen, tgen.routers()["r4"]) diff --git a/tests/topotests/mgmt_startup/test_latestart.py b/tests/topotests/mgmt_startup/test_latestart.py new file mode 100644 index 000000000000..1c97b9dd0fca --- /dev/null +++ b/tests/topotests/mgmt_startup/test_latestart.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# May 2 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +""" +Verify routes present when staticd (backend client) is started after it's startup config +is present during launch. +""" + +import pytest +from lib.topogen import Topogen, TopoRouter +from util import _test_staticd_late_start + +# pytestmark = [pytest.mark.staticd, pytest.mark.mgmtd] +pytestmark = [pytest.mark.staticd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1",), + } + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + # configure mgmtd using current mgmtd config file + tgen.gears["r1"].load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + tgen.gears["r1"].load_config(TopoRouter.RD_MGMTD) + + # Explicit disable staticd now.. + tgen.gears["r1"].net.daemons["staticd"] = 0 + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def test_staticd_late_start(tgen): + return _test_staticd_late_start(tgen, tgen.routers()["r1"]) diff --git a/tests/topotests/mgmt_startup/util.py b/tests/topotests/mgmt_startup/util.py new file mode 100644 index 000000000000..e366351326b5 --- /dev/null +++ b/tests/topotests/mgmt_startup/util.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# May 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# + +import ipaddress +import math +import re + +import pytest +from lib.common_config import retry, step +from lib.topolog import logger +from munet.base import proc_error + + +@retry(retry_timeout=30) +def check_vtysh_up(router): + rc, o, e = router.net.cmd_status("vtysh -c 'show version'") + return None if not rc else proc_error(rc, o, e) + + +@retry(retry_timeout=3, initial_wait=0.1) +def check_kernel(r1, prefix, expected=True): + net = ipaddress.ip_network(prefix) + if net.version == 6: + kernel = r1.net.cmd_nostatus("ip -6 route show", warn=not expected) + else: + kernel = r1.net.cmd_nostatus("ip -4 route show", warn=not expected) + + logger.debug("checking kernel routing table:\n%0.1920s", kernel) + route = f"{str(net)}(?: nhid [0-9]+)?.*proto (static|196)" + m = re.search(route, kernel) + if expected and not m: + return f"Failed to find \n'{route}'\n in \n'{kernel:.1920}'" + elif not expected and m: + return f"Failed found \n'{route}'\n in \n'{kernel:.1920}'" + return None + + +def get_ip_networks(super_prefix, count): + count_log2 = math.log(count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +def write_big_route_conf(super_prefix, count, confpath, prologue=""): + start = None + end = None + + with open(confpath, "w+", encoding="ascii") as f: + if prologue: + f.write(prologue + "\n") + for net in get_ip_networks(super_prefix, count): + end = net + if not start: + start = net + f.write(f"ip route {net} lo\n") + + return start, end + + +def _test_staticd_late_start(tgen, router): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # for x in ["r1"]: + # tgen.gears[x].net.cmd_nostatus( + # "vtysh -c 'debug mgmt client frontend' " + # "-c 'debug mgmt client backend' " + # "-c 'debug mgmt backend frontend datastore transaction'" + # ) + + step("Verifying startup route is not present w/o staticd running") + result = check_kernel(router, "12.0.0.0/24", expected=False) + assert result is not None + + step("Configure another static route verify is not present w/o staticd running") + router.net.cmd_nostatus("vtysh -c 'config t' -c 'ip route 12.1.0.0/24 101.0.0.2'") + result = check_kernel(router, "12.0.0.0/24", expected=False) + assert result is not None + result = check_kernel(router, "12.1.0.0/24", expected=False) + assert result is not None + + step("Starting staticd") + router.startDaemons(["staticd"]) + + step("Verifying both routes are present") + result = check_kernel(router, "12.0.0.0/24") + assert result is None + result = check_kernel(router, "12.1.0.0/24") + assert result is None diff --git a/tests/topotests/mgmt_tests/test_yang_mgmt.py b/tests/topotests/mgmt_tests/test_yang_mgmt.py new file mode 100644 index 000000000000..921b4e622cb8 --- /dev/null +++ b/tests/topotests/mgmt_tests/test_yang_mgmt.py @@ -0,0 +1,548 @@ +#!/usr/bin/python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# +""" + +1. Verify mgmt commit check. +2. Verify mgmt commit apply. +3. Verify mgmt commit abort. +4. Verify mgmt delete config. +5. Kill mgmtd - verify that static routes are intact. +6. Kill mgmtd - verify that watch frr restarts. +7. Show and CLI - Execute all the newly introduced commands of mgmtd. +8. Verify mgmt rollback functionality. + +""" +import sys +import time +import os +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/")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +# 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, + shutdown_bringup_interface, + stop_router, + start_router, + apply_raw_config, + kill_router_daemons, + start_router_daemons, +) +from lib.topolog import logger +from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +ADDR_TYPES = check_address_types() +NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]} +NETWORK2 = {"ipv4": "11.0.20.1/32", "ipv6": "2::1/128"} +PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"} + + +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... + json_file = "{}/yang_mgmt.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... 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 = ( + 'These tests will not run. (have kernel "{}", ' + "requires kernel >= 4.19)".format(platform.release()) + ) + pytest.skip(error_msg) + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + 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: %s", mod) + + 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 populate_nh(): + """ + Populate nexthops. + """ + + next_hop_ip = { + "nh1": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0], + }, + "nh2": { + "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0], + "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0], + }, + } + return next_hop_ip + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_mgmt_commit_check(request): + """ + Verify mgmt commit check. + + """ + 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) + + step("Mgmt Commit check") + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt commit check", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Mgmt Commit check") + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.2/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt commit check", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify that the route is not configured, as commit apply not done.") + + dut = "r1" + protocol = "static" + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": "1192.1.1.2/32", + "next_hop": "Null0", + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_mgmt_commit_apply(request): + """ + Verify mgmt commit apply. + + """ + 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) + + step("Mgmt Commit apply with Valid Configuration") + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Mgmt Commit apply with Invalid Configuration") + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.20/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify that the route is configured") + + dut = "r1" + protocol = "static" + input_dict_4 = {"r2": {"static_routes": [{"network": "192.1.1.20/32"}]}} + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_mgmt_commit_abort(request): + """ + Verify mgmt commit abort. + + """ + 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) + + step("Mgmt Commit abort") + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt commit abort", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify that the route is not configured") + + dut = "r1" + protocol = "static" + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": "192.1.1.3/32", + "next_hop": "Null0", + } + ] + } + } + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_mgmt_delete_config(request): + """ + Verify mgmt delete config. + + """ + 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) + + step("Mgmt - Configure a static route using commit apply") + + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/vrf default", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that the route is added to RIB") + dut = "r1" + protocol = "static" + input_dict_4 = { + "r2": { + "static_routes": [ + { + "network": "192.168.1.3/32", + "next_hop": "Null0", + } + ] + } + } + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Mgmt delete config") + raw_config = { + "r1": { + "raw_config": [ + "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.168.1.3/32'][afi-safi='frr-routing:ipv4-unicast']", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that the route is deleted from RIB") + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_mgmt_chaos_stop_start_frr(request): + """ + Kill mgmtd - verify that watch frr restarts. + + """ + 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) + next_hop_ip = populate_nh() + + step("Configure Static route with next hop null 0") + + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify that the route is configured and present in the zebra") + + dut = "r1" + protocol = "static" + input_dict_4 = {"r2": {"static_routes": [{"network": "192.1.11.200/32"}]}} + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Restart frr") + stop_router(tgen, "r1") + start_router(tgen, "r1") + step("Verify routes are intact in zebra.") + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("delete the configured route and ") + raw_config = { + "r1": { + "raw_config": [ + "mgmt delete-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify that the route is deleted and deleted from zebra") + + dut = "r1" + protocol = "static" + input_dict_4 = {"r1": {"static_routes": [{"network": "192.1.11.200/32"}]}} + result = verify_rib( + tgen, "ipv4", dut, input_dict_4, protocol=protocol, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + + write_test_footer(tc_name) + + +def test_mgmt_chaos_kill_daemon(request): + """ + Kill mgmtd - verify that static routes are intact + + """ + 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) + next_hop_ip = populate_nh() + + step("Configure Static route with next hop null 0") + + raw_config = { + "r1": { + "raw_config": [ + "mgmt set-config /frr-routing:routing/control-plane-protocols/control-plane-protocol[type='frr-staticd:staticd'][name='staticd'][vrf='default']/frr-staticd:staticd/route-list[prefix='192.1.11.200/32'][afi-safi='frr-routing:ipv4-unicast']/path-list[table-id='0'][distance='1']/frr-nexthops/nexthop[nh-type='blackhole'][vrf='default'][gateway=''][interface='(null)']/bh-type unspec", + "mgmt commit apply", + ] + } + } + + result = apply_raw_config(tgen, raw_config) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify that the route is configured and present in the zebra") + + dut = "r1" + protocol = "static" + input_dict_4 = {"r2": {"static_routes": [{"network": "192.1.11.200/32"}]}} + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Kill static daemon on R2.") + kill_router_daemons(tgen, "r1", ["staticd"]) + + step("Bring up staticd daemon on R2.") + start_router_daemons(tgen, "r1", ["staticd"]) + + step("Verify routes are intact in zebra.") + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + + step("Kill mgmt daemon on R2.") + kill_router_daemons(tgen, "r1", ["mgmtd"]) + + step("Bring up zebra daemon on R2.") + start_router_daemons(tgen, "r1", ["mgmtd"]) + + step("Verify routes are intact in zebra.") + result = verify_rib(tgen, "ipv4", dut, input_dict_4, protocol=protocol) + assert ( + result is True + ), "Testcase {} : Failed" "Error: Routes is missing 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/mgmt_tests/yang_mgmt.json b/tests/topotests/mgmt_tests/yang_mgmt.json new file mode 100644 index 000000000000..0fe3bb1dbea3 --- /dev/null +++ b/tests/topotests/mgmt_tests/yang_mgmt.json @@ -0,0 +1,157 @@ +{ + "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-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r2-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + } + }, + "r2": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r3": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r3": { + "dest_link": { + "r2": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": { + "ipv4": "auto", + "ipv6": "auto", + "type": "loopback" + }, + "r2": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link0": { + "ipv4": "auto", + "ipv6": "auto" + }, + "r1-link1": { + "ipv4": "auto", + "ipv6": "auto" + } + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + }, + "ipv6": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r3": { + "keepalivetimer": 1, + "holddowntimer": 4 + } + } + } + } + } + } + } + } + } + } +} diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py index 2f31608e6422..6d9304d864ed 100644 --- a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py +++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py @@ -71,6 +71,7 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) if os.path.isfile(daemon_file): router.load_config(TopoRouter.RD_ZEBRA, daemon_file) diff --git a/tests/topotests/msdp_topo1/test_msdp_topo1.py b/tests/topotests/msdp_topo1/test_msdp_topo1.py index 4a007e7d2074..1af58b0a010f 100755 --- a/tests/topotests/msdp_topo1/test_msdp_topo1.py +++ b/tests/topotests/msdp_topo1/test_msdp_topo1.py @@ -82,6 +82,7 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) if os.path.isfile(daemon_file): router.load_config(TopoRouter.RD_ZEBRA, daemon_file) diff --git a/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json new file mode 100644 index 000000000000..d7053bf6dd80 --- /dev/null +++ b/tests/topotests/multicast_mld_join_topo1/multicast_mld_local_join.json @@ -0,0 +1,249 @@ +{ + "address_types": ["ipv6"], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "r4": {"ipv6": "auto", "pim6": "enable"}, + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i1": {"ipv6": "auto", "pim6": "enable"}, + "i2": {"ipv6": "auto", "pim6": "enable"} + }, + + "bgp": { + "local_as": "100", + "router_id": "192.168.1.1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r4": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r4": { + "dest_link": { + "r1": {} + } + }, + "r2": { + "dest_link": { + "r1": {} + } + }, + "r3": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r4": { + "dest_link": { + "r2": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.3", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r3": {} + } + }, + "r4": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i4": {"ipv6": "auto", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r2": { + "dest_link": { + "r4": {} + } + }, + "r3": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "i1": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i2": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i4": { + "links": { + "r4": {"ipv6": "auto"} + } + } + } +} \ No newline at end of file diff --git a/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py new file mode 100644 index 000000000000..826d6e294106 --- /dev/null +++ b/tests/topotests/multicast_mld_join_topo1/test_multicast_mld_local_join.py @@ -0,0 +1,899 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# + +""" +Following tests are covered to test_multicast_pim_mld_local_tier_1: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: + +1. Verify static MLD group populated when static "ip mld join <grp>" in configured +2. Verify mroute and upstream populated with correct OIL/IIF with static imld join +3. Verify local MLD join not allowed for non multicast group +4. Verify static MLD group removed from DUT while removing "ip mld join" CLI +5. Verify static MLD groups after removing and adding MLD config +""" + +import sys +import time + +import pytest +from lib.common_config import ( + reset_config_on_routers, + start_topology, + step, + write_test_footer, + write_test_header, +) +from lib.pim import ( + McastTesterHelper, + create_mld_config, + create_pim_config, + verify_local_mld_groups, + verify_mld_groups, + verify_mroutes, + verify_pim_neighbors, + verify_pim_rp_info, + verify_upstream_iif, +) +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +r1_r2_links = [] +r1_r3_links = [] +r2_r1_links = [] +r2_r4_links = [] +r3_r1_links = [] +r3_r4_links = [] +r4_r2_links = [] +r4_r3_links = [] + +pytestmark = [pytest.mark.pim6d, pytest.mark.staticd] + +TOPOLOGY = """ + +-------------------+ + | | + i1--- R1-------R2----------R4---i2 + | | + +-------R3----------+ + + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send MLD + join and traffic + R1 - DUT (LHR) + R2 - RP + R3 - Transit + R4 - (FHR) + +""" +# Global variables + +GROUP_RANGE = "ffaa::/16" +RP_RANGE = "ff00::/8" +GROUP_RANGE_1 = [ + "ffaa::1/128", + "ffaa::2/128", + "ffaa::3/128", + "ffaa::4/128", + "ffaa::5/128", +] +MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"] +MLD_JOIN_RANGE_2 = [ + "ff02::1:ff00:0", + "ff02::d", + "fe80::250:56ff:feb7:d8d5", + "2001::4", + "2002::5", +] + + +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("Master Topology: \n {}".format(TOPOLOGY)) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "multicast_mld_local_join.json" + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + # Verify PIM neighbors + result = verify_pim_neighbors(tgen, topo) + assert result is True, " Verify PIM neighbor: Failed Error: {}".format(result) + + global app_helper + app_helper = McastTesterHelper(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() + + app_helper.cleanup() + + # 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_mld_local_joins_p0(request): + """ + Verify static MLD group populated when static + "ipv6 mld join <grp>" in configured + """ + + 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("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_mroute_with_mld_local_joins_p0(request): + """ + Verify mroute and upstream populated with correct OIL/IIF with + static mld join + """ + 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) + + app_helper.stop_all_hosts() + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + intf_r1_i2: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}}, + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + SOURCE = "Static" + oif = topo["routers"]["r1"]["links"]["r2"]["interface"] + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + result = app_helper.run_traffic("i4", MLD_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Verify mroutes not created with local interface ip ") + input_dict_local_sg = [ + { + "dut": "r1", + "src_address": intf_r1_i1, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": intf_r1_i2, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + for data in input_dict_local_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "sg created with local interface ip".format(tc_name, result) + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "upstream created with local interface ip".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_remove_add_mld_local_joins_p1(request): + """ + Verify static MLD group removed from DUT while + removing "ip mld join" CLI + """ + + 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) + + app_helper.stop_all_hosts() + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0] + SOURCE = "Static" + oif = topo["routers"]["r1"]["links"]["r2"]["interface"] + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + result = app_helper.run_traffic("i4", MLD_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove MLD join from DUT") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: { + "mld": { + "join": MLD_JOIN_RANGE_1, + "delete_attr": True, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join removed using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups( + tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD join still present".format( + tc_name, result + ) + + step("verify mld groups removed using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD groups still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add MLD join on DUT again") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: { + "mld": { + "join": MLD_JOIN_RANGE_1, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_remove_add_mld_config_with_local_joins_p1(request): + """ + Verify static MLD groups after removing + and adding MLD config + """ + + 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) + + app_helper.stop_all_hosts() + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the MLD on R11 interfac of R1 and configure local mld groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (ffaa::1-5) as R2") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( ffaa::1 to ffaa::5)") + result = app_helper.run_traffic("i4", MLD_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "'show ipv6 mroute' showing correct RPF and OIF interface for (*,G)" + " and (S,G) entries on all the nodes" + ) + source_i6 = topo["routers"]["i4"]["links"]["r4"]["ipv6"].split("/")[0] + + intf_r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": intf_r1_r2, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove mld and mld version 2 from DUT interface") + input_dict = { + "r1": { + "mld": { + "interfaces": {intf_r1_i1: {"mld": {"version": "1", "delete": True}}} + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups( + tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False + ) + assert result is not True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "MLD grsp still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add mld and mld version 2 from DUT interface") + input_dict = { + "r1": { + "mld": { + "interfaces": { + intf_r1_i1: {"mld": {"version": "1", "join": MLD_JOIN_RANGE_1}} + } + } + } + } + + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static mld join using show ipv6 mld join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify mld groups using show ipv6 mld groups") + interface = intf_r1_i1 + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local mld groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json b/tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json new file mode 100644 index 000000000000..341cd5a98ef1 --- /dev/null +++ b/tests/topotests/multicast_pim6_sm_topo1/multicast_pim6_sm_topo1.json @@ -0,0 +1,300 @@ +{ + "address_types": ["ipv6"], + "ipv6base": "fd00::", + "ipv6mask": 64, + "link_ip_start": { + "ipv6": "fd00::", + "v6mask": 64 + }, + "lo_prefix": { + "ipv6": "2001:db8:f::", + "v6mask": 128 + }, + "routers": { + "r1": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"}, + "i1": {"ipv6": "auto", "pim6": "enable"}, + "i6": {"ipv6": "auto", "pim6": "enable"}, + "i7": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "100", + "router_id": "192.168.1.1", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + + } + }, + "r4": { + "dest_link": { + "r1": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1": {} + + } + }, + "r4": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + }, + "mld": { + "interfaces": { + "r1-i1-eth0" :{ + "mld":{ + "version": 1 + } + } + } + } + }, + + "r2": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "i3": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "200", + "router_id": "192.168.1.2", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2": {} + } + }, + "r3": { + "dest_link": { + "r2": {} + + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r2": {"ipv6": "auto", "pim6": "enable"}, + "r5": {"ipv6": "auto", "pim6": "enable"}, + "i2": {"ipv6": "auto", "pim6": "enable"}, + "i8": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "300", + "router_id": "192.168.1.3", + "address_family": { + "ipv4": { + "unicast": { + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r3": {} + } + }, + "r5": { + "dest_link": { + "r3": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r1": {"ipv6": "auto", "pim6": "enable"}, + "r5": {"ipv6": "auto", "pim6": "enable"}, + "i4": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "400", + "router_id": "192.168.1.4", + "address_family": { + "ipv4": { + "unicast": { + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r5": { + "dest_link": { + "r4": {} + } + } + } + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r4": {} + } + }, + "r5": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + + "r5": { + "links": { + "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable"}, + "r3": {"ipv6": "auto", "pim6": "enable"}, + "r4": {"ipv6": "auto", "pim6": "enable"}, + "i5": {"ipv6": "auto", "pim6": "enable"} + }, + "bgp": { + "local_as": "500", + "router_id": "192.168.1.5", + "address_family": { + "ipv4": { + "unicast": { + } + }, + "ipv6": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r3": { + "dest_link": { + "r5": {} + } + }, + "r4": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + + "i1": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i2": { + "links": { + "r3": {"ipv6": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv6": "auto"} + } + }, + "i4": { + "links": { + "r4": {"ipv6": "auto"} + } + }, + "i5": { + "links": { + "r5": {"ipv6": "auto"} + } + }, + "i6": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i7": { + "links": { + "r1": {"ipv6": "auto"} + } + }, + "i8": { + "links": { + "r3": {"ipv6": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py new file mode 100644 index 000000000000..aff623705c6b --- /dev/null +++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm1.py @@ -0,0 +1,1558 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test multicast pim6 sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. Verify Multicast data traffic with static RP, (*,g) and +(s,g) OIL updated correctly +2. Verify mroute detail when receiver is present +outside of FRR +3. Verify (*,G) and (S,G) populated correctly +when FRR is the transit router +4. Verify (S,G) should not create if RP is not reachable +5. Verify modification of mld query timer should get update +accordingly +6. Verify modification of mld max query response timer +should get update accordingly +7. Verify removing the RP should not impact the multicast +data traffic +""" + +import datetime +import sys +import time + +import pytest +from lib.common_config import ( + get_frr_ipv6_linklocal, + required_linux_kernel_version, + reset_config_on_routers, + shutdown_bringup_interface, + start_topology, + step, + write_test_footer, + write_test_header, +) +from lib.pim import ( + McastTesterHelper, + clear_pim6_mroute, + create_mld_config, + create_pim_config, + verify_mld_config, + verify_mld_groups, + verify_mroute_summary, + verify_mroutes, + verify_pim_interface_traffic, + verify_pim_join, + verify_pim_nexthop, + verify_pim_state, + verify_sg_traffic, + verify_upstream_iif, +) +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +# Global variables +GROUP_RANGE = "ff00::/8" + +GROUP_RANGE_1 = [ + "ffaa::1/128", + "ffaa::2/128", + "ffaa::3/128", + "ffaa::4/128", + "ffaa::5/128", +] +MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"] + +GROUP_RANGE_2 = [ + "ffbb::1/128", + "ffbb::2/128", + "ffbb::3/128", + "ffbb::4/128", + "ffbb::5/128", +] +MLD_JOIN_RANGE_2 = ["ffbb::1", "ffbb::2", "ffbb::3", "ffbb::4", "ffbb::5"] +GROUP_RANGE_3 = [ + "ffcc::1/128", + "ffcc::2/128", + "ffcc::3/128", + "ffcc::4/128", + "ffcc::5/128", +] +MLD_JOIN_RANGE_3 = ["ffcc::1", "ffcc::2", "ffcc::3", "ffcc::4", "ffcc::5"] + +HELLO_TIMER = 1 +HOLD_TIMER = 3 +PREFERRED_NEXT_HOP = "link_local" +ASSERT_MSG = "Testcase {} : Failed Error: {}" + +pytestmark = [pytest.mark.pim6d] + + +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.19") + 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") + + json_file = "multicast_pim6_sm_topo1.json" + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... 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) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + global app_helper + app_helper = McastTesterHelper(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() + + app_helper.cleanup() + + # 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 verify_mroute_repopulated(uptime_before, uptime_after): + """ + API to compare uptime for mroutes + + Parameters + ---------- + * `uptime_before` : Uptime dictionary for any particular instance + * `uptime_after` : Uptime dictionary for any particular instance + """ + + for group in uptime_before.keys(): + for source in uptime_before[group].keys(): + if set(uptime_before[group]) != set(uptime_after[group]): + errormsg = ( + "mroute (%s, %s) has not come" + " up after mroute clear [FAILED!!]" % (source, group) + ) + return errormsg + + d1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S") + d2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S") + if d2 >= d1: + errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % ( + source, + group, + ) + return errormsg + + logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group) + + return True + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + 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"]["{}".format(dut)]["interface"] + if addr_type == "ipv6" and "link_local" in preferred_next_hop: + next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface) + else: + next_hop = next_hop_dict[addr_type] + + return next_hop + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_multicast_data_traffic_static_RP_send_traffic_then_join_p0(request): + """ + Verify Multicast data traffic with static RP, (*,g) and + (s,g) OIL updated correctly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("shut R1 to R4 and R3 to R5 link to simulate test topology") + r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + r3_r5 = topo["routers"]["r3"]["links"]["r5"]["interface"] + + shutdown_bringup_interface(tgen, "r1", r1_r4, False) + shutdown_bringup_interface(tgen, "r3", r3_r5, False) + + step( + "Configure RP as R2 (loopback interface) for the" + " group range ff00::/8 on all the routers" + ) + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Start traffic first and then send the mld join") + + step("Send multicast traffic from FRR3 to all the receivers" "ffaa::1-5") + + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + + step("verify upstream in NOT join Rej prune state on R3") + + input_dict_sg = [ + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + } + ] + + for data in input_dict_sg: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + joinState="NotJoined", + regState="RegPrune", + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("joinRx value before join sent") + r2_r1_intf = topo["routers"]["r2"]["links"]["r1"]["interface"] + state_dict = {"r2": {r2_r1_intf: ["joinRx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6") + assert isinstance( + state_before, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " "Error: {}".format( + tc_name, result + ) + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + input_dict = [ + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source, + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r2", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r2"]["links"]["r1"]["interface"], + }, + { + "dut": "r2", + "src_address": source, + "iif": topo["routers"]["r2"]["links"]["r3"]["interface"], + "oil": topo["routers"]["r2"]["links"]["r1"]["interface"], + }, + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify join state is joined") + for data in input_dict_sg: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + MLD_JOIN_RANGE_1, + joinState="Joined", + regState="RegPrune", + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("joinRx value after join sent") + state_after = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6") + assert isinstance( + state_after, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " "Error: {}".format( + tc_name, result + ) + + step( + "r1 sent PIM (*,G) join to r2 verify using" + "'show ipv6 pim interface traffic' on RP connected interface" + ) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("r1 sent PIM (S,G) join to r3 , verify using 'show ipv6 pim join'") + dut = "r3" + interface = topo["routers"]["r3"]["links"]["r2"]["interface"] + result = verify_pim_join(tgen, topo, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + _nexthop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0] + next_hop = next_hop_per_address_family(tgen, "r1", "r2", "ipv6", _nexthop) + + step("verify nexthop on r3 using 'show ipv6 pim nexthop'") + result = verify_pim_nexthop(tgen, topo, "r1", next_hop, "ipv6") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify mroute summary on r1 using 'show ipv6 mroute summary json'") + result = verify_mroute_summary( + tgen, "r1", sg_mroute=5, starg_mroute=5, total_mroute=10, addr_type="ipv6" + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_receiver_is_outside_frr_p0(request): + """ + Verify mroute detail when receiver is present + outside of FRR + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + 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("Configure static RP on r4 for group range " "(ffcc::1-5) and (ffbb::1-5)") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _MLD_JOIN_RANGE = MLD_JOIN_RANGE_2 + MLD_JOIN_RANGE_3 + + input_dict = { + "r4": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", _MLD_JOIN_RANGE, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify MLD joins received on r1") + dut = "r1" + interface = topo["routers"]["r1"]["links"]["i1"]["interface"] + result = verify_mld_groups(tgen, dut, interface, _MLD_JOIN_RANGE) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to all the receivers" "ffaa::1-5") + result = app_helper.run_traffic("i2", _MLD_JOIN_RANGE, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure one more receiver in r5 enable mld and send" + " join (ffaa::1-5) and (ffbb::1-5)" + ) + r5_i5 = topo["routers"]["r5"]["links"]["i5"]["interface"] + + input_dict = {"r5": {"mld": {"interfaces": {r5_i5: {"mld": {"version": "1"}}}}}} + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = app_helper.run_join("i5", _MLD_JOIN_RANGE, "r5") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("FRR1 has 10 (*.G) and 10 (S,G) verify using 'show ipv6 mroute'") + step( + "All the receiver are receiving traffic on FRR1 and (S,G) OIL is towards" + "receivers, verify using 'show mroute' 'show pim upstream'" + ) + step( + "All the receiver are receiving traffic on r5 and (S,G) OIL is " + "toward receivers, verify using 'show ipv6 mroute' 'show ipv6 pim upstream'" + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict = [ + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source, + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r5", + "src_address": "*", + "iif": topo["routers"]["r5"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r5"]["links"]["i5"]["interface"], + }, + { + "dut": "r5", + "src_address": source, + "iif": topo["routers"]["r5"]["links"]["r3"]["interface"], + "oil": topo["routers"]["r5"]["links"]["i5"]["interface"], + }, + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + _MLD_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "FRR3 has (S,G) OIL created toward r2/r5 receiver and FRR1 receiver" + "'show ipv6 pim '" + ) + input_dict = [ + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r5"]["interface"], + }, + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + }, + ] + for data in input_dict: + result = verify_pim_state( + tgen, + data["dut"], + data["iif"], + data["oil"], + _MLD_JOIN_RANGE, + data["src_address"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + _MLD_JOIN_RANGE, + joinState="Joined", + regState="RegPrune", + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Traffic is received fine on FRR1 and r5 " " 'show ipv6 mroute count' ") + + result = verify_sg_traffic(tgen, "r1", _MLD_JOIN_RANGE, source, "ipv6") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_sg_traffic(tgen, "r5", _MLD_JOIN_RANGE, source, "ipv6") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_frr_is_transit_router_p2(request): + """ + Verify (*,G) and (S,G) populated correctly + when FRR is the transit router + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP for (ffaa::1-5) in r5") + input_dict = { + "r5": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r5"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable mld on FRR1 interface and send mld join ") + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify mld groups received on R1") + dut = "r1" + interface = topo["routers"]["r1"]["links"]["i1"]["interface"] + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to ffaa::1-5 receivers") + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("shut the direct link to R1 ") + intf = topo["routers"]["r1"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "FRR4 has (S,G) and (*,G) ,created where incoming interface" + " toward FRR3 and OIL toward R2, verify using 'show ipv6 mroute'" + " 'show ipv6 pim state' " + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict = [ + { + "dut": "r5", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r5"]["links"]["r4"]["interface"], + }, + { + "dut": "r5", + "src_address": source, + "iif": topo["routers"]["r5"]["links"]["r3"]["interface"], + "oil": topo["routers"]["r5"]["links"]["r4"]["interface"], + }, + ] + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify multicast traffic") + result = verify_sg_traffic(tgen, "r5", MLD_JOIN_RANGE_1, source, "ipv6") + assert ( + result is True + ), "Testcase {} : Failed \n mroutes traffic " "still present \n Error: {}".format( + tc_name, result + ) + + step("Stop multicast traffic from FRR3") + dut = "i2" + intf = topo["routers"]["i2"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("(*,G) present on R5 after source shut") + + input_dict_1 = [ + { + "dut": "r5", + "src_address": "*", + "iif": "lo", + "oil": topo["routers"]["r5"]["links"]["r4"]["interface"], + }, + ] + for data in input_dict_1: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("stop mld receiver from FRR1") + dut = "i1" + intf = topo["routers"]["i1"]["links"]["r1"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "After stopping receiver (*, G) and (S, G) also got removed from transit" + " router 'show ipv6 mroute'" + ) + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed \n mroutes " + "not removed after removing the receivers \n Error: {}".format( + tc_name, result + ) + ) + + logger.info("Expected Behavior: {}".format(result)) + + write_test_footer(tc_name) + + +def test_verify_mroute_when_RP_unreachable_p1(request): + """ + Verify (S,G) should not create if RP is not reachable + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure RP on FRR2 (loopback interface) for " "the group range ffaa::1-5") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable mld on FRR1 interface and send mld join ffaa::1-5") + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to ffaa::1-5 receivers") + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure one MLD interface on FRR3 node and send MLD" " join (ffcc::1)") + r3_i8 = topo["routers"]["r3"]["links"]["i8"]["interface"] + input_dict = {"r3": {"mld": {"interfaces": {r3_i8: {"mld": {"version": "1"}}}}}} + result = create_mld_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i8", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify MLD groups received ") + dut = "r3" + interface = topo["routers"]["r3"]["links"]["i8"]["interface"] + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict = [ + { + "dut": "r3", + "src_address": "*", + "iif": topo["routers"]["r3"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["i8"]["interface"], + }, + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["i8"]["interface"], + }, + ] + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the RP connected interface from r3 ( r2 to r3) link") + dut = "r3" + intf = topo["routers"]["r3"]["links"]["r2"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step("Clear the mroute on r3") + clear_pim6_mroute(tgen, "r3") + + step( + "After Shut the RP interface and clear the mroute verify all " + "(*,G) and (S,G) got timeout from FRR3 node , verify using " + " 'show ipv6 mroute' " + ) + r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"] + r3_i8 = topo["routers"]["r3"]["links"]["i8"]["interface"] + + result = verify_mroutes( + tgen, "r3", "*", MLD_JOIN_RANGE_1, r3_r2, r3_i8, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \n mroutes are" " still present \n Error: {}".format( + tc_name, result + ) + logger.info("Expected Behavior: {}".format(result)) + + step("mld groups are present verify using 'show ip mld group'") + dut = "r1" + interface = topo["routers"]["r1"]["links"]["i1"]["interface"] + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_mld_query_timer_p0(request): + """ + Verify modification of mld query timer should get update + accordingly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i8", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable MLD on receiver interface") + intf_r3_i8 = topo["routers"]["r3"]["links"]["i8"]["interface"] + input_dict_1 = { + "r3": {"mld": {"interfaces": {intf_r3_i8: {"mld": {"version": "1"}}}}} + } + + result = create_mld_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify MLD groups received ") + dut = "r3" + interface = topo["routers"]["r3"]["links"]["i8"]["interface"] + result = verify_mld_groups(tgen, dut, interface, MLD_JOIN_RANGE_1) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for the" " group range ffaa::1-5") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to ffaa::1-5 receivers") + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict_4 = [ + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["i8"]["interface"], + }, + { + "dut": "r3", + "src_address": "*", + "iif": topo["routers"]["r3"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["i8"]["interface"], + }, + ] + for data in input_dict_4: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + mwait=20, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict_4: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Modify mld query interval default to other timer on FRR1" ", 3 times") + + input_dict_1 = { + "r3": { + "mld": { + "interfaces": {intf_r3_i8: {"mld": {"query": {"query-interval": 100}}}} + } + } + } + result = create_mld_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_2 = { + "r3": { + "mld": { + "interfaces": {intf_r3_i8: {"mld": {"query": {"query-interval": 200}}}} + } + } + } + result = create_mld_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "r3": { + "mld": { + "interfaces": {intf_r3_i8: {"mld": {"query": {"query-interval": 300}}}} + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "r3": { + "mld": { + "interfaces": { + intf_r3_i8: { + "mld": {"query": {"query-interval": 300, "delete": True}} + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("veriffy mroutes after query modification") + for data in input_dict_4: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + mwait=20, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_modify_mld_max_query_response_timer_p0(request): + """ + Verify modification of mld max query response timer + should get update accordingly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Enable mld on FRR1 interface and send MLD join") + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + step("Configure mld query response time to 10 sec on FRR1") + input_dict_1 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "query": {"query-max-response-time": 10}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8") + + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to ffaa::1-5 receivers") + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict_5 = [ + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source, + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + }, + ] + for data in input_dict_5: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + mwait=20, + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict_5: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Delete the PIM and mld on FRR1") + r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + input_dict_1 = {"r1": {"pim6": {"disable": [r1_i1]}}} + result = create_pim_config(tgen, topo, input_dict_1) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_2 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "delete": True, + "query": {"query-max-response-time": 10, "delete": True}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_2) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure PIM on FRR") + result = create_pim_config(tgen, topo["routers"]) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure max query response timer 100sec on FRR1") + input_dict_3 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "query": {"query-max-response-time": 100}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Remove and add max query response timer cli with different" + "timer 5 times on FRR1 Enable mld and mld version 2 on FRR1" + " on FRR1" + ) + + input_dict_3 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "query": {"query-max-response-time": 110}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "query": {"query-max-response-time": 120}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "query": {"query-max-response-time": 140}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + input_dict_3 = { + "r1": { + "mld": { + "interfaces": { + r1_i1: { + "mld": { + "version": "1", + "query": {"query-max-response-time": 150}, + } + } + } + } + } + } + result = create_mld_config(tgen, topo, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Enable mld and mld version 2 on FRR1 on FRR1") + + input_dict_4 = {"r1": {"mld": {"interfaces": {r1_i1: {"mld": {"version": "1"}}}}}} + result = create_mld_config(tgen, topo, input_dict_4) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = verify_mld_config(tgen, input_dict_3) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify that no core is observed") + if tgen.routers_have_failure(): + assert False, "Testcase {}: Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_impact_on_multicast_traffic_when_RP_removed_p0(request): + """ + Verify removing the RP should not impact the multicast + data traffic + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("send multicast traffic for group range ffaa::1-5") + + step("Send multicast traffic from FRR3 to ffaa::1-5 receivers") + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for group (ffaa::1) on r5") + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Enable mld on FRR1 interface and send MLD join") + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "After SPT switchover traffic is flowing (FRR3-FRR2-FRR1)" + " and (s,g) OIL updated correctly using 'show ipv6 mroute'" + " 'show ipv6 pim upstream'" + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict = [ + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + }, + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source, + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + ] + + for data in input_dict: + if data["dut"] == "r1": + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + for data in input_dict: + if data["dut"] == "r3": + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut and No shut RP interface in r5") + dut = "r5" + intf = "lo" + shutdown_bringup_interface(tgen, dut, intf, False) + shutdown_bringup_interface(tgen, dut, intf, True) + + step( + "After no shut of RP verify (*,G) entries re-populated again" + " and uptime go reset verify using 'show ipv6 mroute'" + " 'show ipv6 pim state'" + ) + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Remove static RP for group (ffaa::1) on r5") + input_dict = { + "r2": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + "delete": True, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("After remove of RP verify no impact on (s,g)") + + input_dict = [ + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + }, + { + "dut": "r1", + "src_address": source, + "iif": topo["routers"]["r1"]["links"]["r2"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify multicast traffic is flowing") + result = verify_sg_traffic(tgen, "r1", MLD_JOIN_RANGE_1, source, "ipv6") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py new file mode 100644 index 000000000000..767264a7c04b --- /dev/null +++ b/tests/topotests/multicast_pim6_sm_topo1/test_multicast_pim6_sm2.py @@ -0,0 +1,574 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2022 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# + +""" +Following tests are covered to test multicast pim6 sm: + +Test steps +- Create topology (setup module) +- Bring up topology + +Following tests are covered: +1. Verify (*,G) and (S,G) entry populated again after clear the +PIM nbr and mroute from FRR node +2. Verify SPT switchover working when RPT and SPT path is +different +""" + +import sys +import time + +import pytest +from lib.common_config import ( + required_linux_kernel_version, + reset_config_on_routers, + shutdown_bringup_interface, + start_topology, + step, + write_test_footer, + write_test_header, +) +from lib.pim import ( + McastTesterHelper, + clear_pim6_mroute, + create_pim_config, + verify_mroutes, + verify_pim_interface_traffic, + verify_sg_traffic, + verify_upstream_iif, +) +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json +from lib.topolog import logger + +# Global variables +GROUP_RANGE = "ff00::/8" + +GROUP_RANGE_1 = [ + "ffaa::1/128", + "ffaa::2/128", + "ffaa::3/128", + "ffaa::4/128", + "ffaa::5/128", +] +MLD_JOIN_RANGE_1 = ["ffaa::1", "ffaa::2", "ffaa::3", "ffaa::4", "ffaa::5"] + +GROUP_RANGE_2 = [ + "ffbb::1/128", + "ffbb::2/128", + "ffbb::3/128", + "ffbb::4/128", + "ffbb::5/128", +] +MLD_JOIN_RANGE_2 = ["ffbb::1", "ffbb::2", "ffbb::3", "ffbb::4", "ffbb::5"] +GROUP_RANGE_3 = [ + "ffcc::1/128", + "ffcc::2/128", + "ffcc::3/128", + "ffcc::4/128", + "ffcc::5/128", +] +MLD_JOIN_RANGE_3 = ["ffcc::1", "ffcc::2", "ffcc::3", "ffcc::4", "ffcc::5"] + +HELLO_TIMER = 1 +HOLD_TIMER = 3 +PREFERRED_NEXT_HOP = "link_local" +ASSERT_MSG = "Testcase {} : Failed Error: {}" + +pytestmark = [pytest.mark.pim6d] + + +@pytest.fixture(scope="function") +def app_helper(): + # helper = McastTesterHelper(get_topogen()) + # yield helepr + # helper.cleanup() + # Even better use contextmanager functionality: + with McastTesterHelper(get_topogen()) as ah: + yield ah + + +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.19") + 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") + + json_file = "multicast_pim6_sm_topo1.json" + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... 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) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + 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 verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] >= state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_clear_mroute_and_verify_multicast_data_p0(request, app_helper): + """ + Verify (*,G) and (S,G) entry populated again after clear the + PIM nbr and mroute from FRR node + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + reset_config_on_routers(tgen) + + app_helper.stop_all_hosts() + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Configure static RP on r4 for group (ffcc::1-5)") + input_dict = { + "r4": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Enable mld on FRR1 interface and send mld join ffaa::1 " + "to ffaa::5 from different interfaces" + ) + + step("send mld join (ffaa::1-5) to R1") + result = app_helper.run_join("i1", MLD_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send multicast traffic from FRR3 to all the receivers" "ffaa::1-5") + + result = app_helper.run_traffic("i2", MLD_JOIN_RANGE_1, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Clear the mroute on r1, wait for 5 sec") + result = clear_pim6_mroute(tgen, "r1") + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step( + "After clear ip mroute (*,g) entries are re-populated again" + " with same OIL and IIF, verify using 'show ipv6 mroute' and " + " 'show ipv6 pim upstream' " + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + input_dict = [ + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step("Clear the mroute on r3, wait for 5 sec") + result = clear_pim6_mroute(tgen, "r3") + assert result is True, "Testcase{}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + input_dict = [ + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + } + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step("Clear the mroute on r2, wait for 5 sec") + result = clear_pim6_mroute(tgen, "r2") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + input_dict = [ + { + "dut": "r2", + "src_address": source, + "iif": topo["routers"]["r2"]["links"]["r3"]["interface"], + "oil": topo["routers"]["r2"]["links"]["r1"]["interface"], + } + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Clear the mroute on r1, r3, wait for 5 sec") + result = clear_pim6_mroute(tgen, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + result = clear_pim6_mroute(tgen, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 mroute' showing correct RPF and OIF" + " interface for (*,G) and (S,G) entries on all the nodes" + ) + + input_dict = [ + { + "dut": "r1", + "src_address": "*", + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r3", + "src_address": source, + "iif": topo["routers"]["r3"]["links"]["i2"]["interface"], + "oil": topo["routers"]["r3"]["links"]["r2"]["interface"], + }, + ] + + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + MLD_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Verify 'show ipv6 pim upstream' showing correct OIL and IIF" + " on all the nodes" + ) + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], MLD_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "multicast traffic is resume for all the receivers using " + " 'show ip multicast' " + ) + result = verify_sg_traffic(tgen, "r1", MLD_JOIN_RANGE_1, source, "ipv6") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0( + request, app_helper +): + """ + Verify SPT switchover working when RPT and SPT path is + different + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + 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("Configure static RP for (ffcc::1-5) and " "(ffbb::1-5) in r5") + + _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3 + _MLD_JOIN_RANGE = MLD_JOIN_RANGE_2 + MLD_JOIN_RANGE_3 + + input_dict = { + "r5": { + "pim6": { + "rp": [ + { + "rp_addr": topo["routers"]["r5"]["links"]["lo"]["ipv6"].split( + "/" + )[0], + "group_addr_range": _GROUP_RANGE, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("send mld join (ffbb::1-5, ffcc::1-5) to R1") + result = app_helper.run_join("i1", _MLD_JOIN_RANGE, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("registerRx and registerStopTx value before traffic sent") + intf_r5 = topo["routers"]["r5"]["links"]["r3"]["interface"] + state_dict = {"r5": {intf_r5: ["registerRx", "registerStopTx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6") + assert isinstance( + state_before, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " "Error: {}".format( + tc_name, result + ) + + step( + "Send multicast traffic from FRR3 to all the receivers" "ffbb::1-5 , ffcc::1-5" + ) + result = app_helper.run_traffic("i2", _MLD_JOIN_RANGE, "r3") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Verify in FRR3 sending initial packet to RP using" + " 'show ipv6 mroute' and mroute OIL is towards RP." + ) + + source = topo["routers"]["i2"]["links"]["r3"]["ipv6"].split("/")[0] + + r3_i2 = topo["routers"]["r3"]["links"]["i2"]["interface"] + r3_r5 = topo["routers"]["r3"]["links"]["r5"]["interface"] + r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"] + r1_r2 = topo["routers"]["r1"]["links"]["r2"]["interface"] + r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + + result = verify_mroutes(tgen, "r3", source, _MLD_JOIN_RANGE, r3_i2, [r3_r5, r3_r2]) + assert result is True, "Testcase {} : " "Failed Error: {}".format(tc_name, result) + + result = verify_mroutes(tgen, "r3", source, _MLD_JOIN_RANGE, r3_i2, r3_r2) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + " After spt switchover traffic is flowing between" + " (LHR(FRR1)-FHR(FRR3)) and (S,G) OIL is updated toward FRR1" + " 'show mroute' and 'show pim upstream'" + ) + + input_dict = [ + {"dut": "r3", "src_address": source, "iif": r3_i2, "oil": r3_r2}, + {"dut": "r1", "src_address": source, "iif": r1_r2, "oil": r1_i1}, + ] + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + _MLD_JOIN_RANGE, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict: + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], _MLD_JOIN_RANGE + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Stop the traffic to all the receivers") + dut = "i2" + intf = topo["routers"]["i2"]["links"]["r3"]["interface"] + shutdown_bringup_interface(tgen, dut, intf, False) + + step( + "Null register packet being send periodically from FRR3 to RP, " + "verify using show ipv6 mroute on RP, have (S, G) entries null OIL" + " 'show ipv6 mroute' and verify show ip pim interface traffic" + "(In RP Register msg should be received and Register stop should" + " be transmitted)" + ) + + result = verify_upstream_iif( + tgen, "r3", "Unknown", source, _MLD_JOIN_RANGE, joinState="NotJoined" + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("registerRx and registerStopTx value after traffic sent") + state_after = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6") + assert isinstance( + state_after, dict + ), "Testcase {} : Failed \n state_before is not dictionary \n " "Error: {}".format( + tc_name, result + ) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py index 95b4004e14a8..23326337d69d 100755 --- a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py +++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp1.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- # SPDX-License-Identifier: ISC # @@ -41,57 +41,36 @@ 8. Verify PIM6 join send towards the higher preferred RP 9. Verify PIM6 prune send towards the lower preferred RP """ - -import os import sys -import json import time -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/")) - -# Required to instantiate the topology builder class. - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib.topogen import Topogen, get_topogen +import pytest from lib.common_config import ( - start_topology, - write_test_header, - write_test_footer, + check_router_status, reset_config_on_routers, - step, shutdown_bringup_interface, - kill_router_daemons, - start_router_daemons, - create_static_routes, - check_router_status, - socat_send_mld_join, - socat_send_pim6_traffic, - kill_socat, + start_topology, + step, + write_test_footer, + write_test_header, ) from lib.pim import ( + McastTesterHelper, + clear_pim6_interface_traffic, create_pim_config, - verify_upstream_iif, + get_pim6_interface_traffic, verify_join_state_and_timer, + verify_mld_groups, verify_mroutes, - verify_pim_neighbors, + verify_pim6_neighbors, verify_pim_interface_traffic, verify_pim_rp_info, verify_pim_state, - clear_pim6_interface_traffic, - clear_pim6_mroute, - verify_pim6_neighbors, - get_pim6_interface_traffic, - clear_pim6_interfaces, - verify_mld_groups, + verify_upstream_iif, ) +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json, build_topo_from_json from lib.topolog import logger -from lib.topojson import build_topo_from_json, build_config_from_json # Global variables GROUP_RANGE_1 = "ff08::/64" @@ -141,7 +120,7 @@ def setup_module(mod): logger.info("Running setup_module to create topology") # This function initiates the topology build with Topogen... - json_file = "{}/multicast_pim6_static_rp.json".format(CWD) + json_file = "multicast_pim6_static_rp.json" tgen = Topogen(json_file, mod.__name__) global TOPO TOPO = tgen.json_topo @@ -163,6 +142,9 @@ def setup_module(mod): result = verify_pim6_neighbors(tgen, TOPO) assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + global app_helper + app_helper = McastTesterHelper(tgen) + logger.info("Running setup_module() done") @@ -172,6 +154,8 @@ def teardown_module(): logger.info("Running teardown_module to delete topology") tgen = get_topogen() + app_helper.cleanup() + # Stop toplogy and Remove tmp files tgen.stop_topology() @@ -257,6 +241,8 @@ def test_pim6_add_delete_static_RP_p0(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Shut link b/w R1 and R3 and R1 and R4 as per testcase topology") intf_r1_r3 = TOPO["routers"]["r1"]["links"]["r3"]["interface"] intf_r1_r4 = TOPO["routers"]["r1"]["links"]["r4"]["interface"] @@ -310,11 +296,7 @@ def test_pim6_add_delete_static_RP_p0(request): ) step("send mld join {} to R1".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_1, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -454,6 +436,8 @@ def test_pim6_SPT_RPT_path_same_p1(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Shut link b/w R1->R3, R1->R4 and R3->R1, R3->R4 as per " "testcase topology") intf_r1_r3 = TOPO["routers"]["r1"]["links"]["r3"]["interface"] intf_r1_r4 = TOPO["routers"]["r1"]["links"]["r4"]["interface"] @@ -491,11 +475,7 @@ def test_pim6_SPT_RPT_path_same_p1(request): step( "Enable MLD on r1 interface and send MLD join {} to R1".format(GROUP_ADDRESS_1) ) - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_1, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -505,9 +485,8 @@ def test_pim6_SPT_RPT_path_same_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("Send multicast traffic from R5") - intf = TOPO["routers"]["r5"]["links"]["r3"]["interface"] SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0] - result = socat_send_pim6_traffic(tgen, "r5", "UDP6-SEND", GROUP_ADDRESS_1, intf) + result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r2: Verify RP info") @@ -627,6 +606,8 @@ def test_pim6_RP_configured_as_LHR_p1(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Enable MLD on r1 interface") step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers") @@ -662,11 +643,7 @@ def test_pim6_RP_configured_as_LHR_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("send mld join {} to R1".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_1, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -676,9 +653,8 @@ def test_pim6_RP_configured_as_LHR_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("r5: Send multicast traffic for group {}".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r5"]["links"]["r3"]["interface"] SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0] - result = socat_send_pim6_traffic(tgen, "r5", "UDP6-SEND", GROUP_ADDRESS_1, intf) + result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify (*, G) upstream IIF interface") @@ -759,6 +735,8 @@ def test_pim6_RP_configured_as_FHR_p1(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Enable MLD on r1 interface") step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers") step("r3: Configure r3(FHR) as RP") @@ -789,11 +767,7 @@ def test_pim6_RP_configured_as_FHR_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("send mld join {} to R1".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_1, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -803,9 +777,8 @@ def test_pim6_RP_configured_as_FHR_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("r5: Send multicast traffic for group {}".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r5"]["links"]["r3"]["interface"] SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0] - result = socat_send_pim6_traffic(tgen, "r5", "UDP6-SEND", GROUP_ADDRESS_1, intf) + result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify (*, G) upstream IIF interface") @@ -887,6 +860,8 @@ def test_pim6_SPT_RPT_path_different_p1(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Enable MLD on r1 interface") step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers") step("r2: Configure r2 as RP") @@ -918,11 +893,7 @@ def test_pim6_SPT_RPT_path_different_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("send mld join {} to R1".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_1, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -932,9 +903,8 @@ def test_pim6_SPT_RPT_path_different_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("r5: Send multicast traffic for group {}".format(GROUP_ADDRESS_1)) - intf = TOPO["routers"]["r5"]["links"]["r3"]["interface"] SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0] - result = socat_send_pim6_traffic(tgen, "r5", "UDP6-SEND", GROUP_ADDRESS_1, intf) + result = app_helper.run_traffic("r5", GROUP_ADDRESS_1, "r3") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify (*, G) upstream IIF interface") @@ -1057,6 +1027,8 @@ def test_pim6_send_join_on_higher_preffered_rp_p1(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Enable MLD on r1 interface") step("Enable the PIM66 on all the interfaces of r1, r2, r3 and r4 routers") step( @@ -1106,11 +1078,7 @@ def test_pim6_send_join_on_higher_preffered_rp_p1(request): ) step("r0: send mld join {} to R1".format(GROUP_ADDRESS_3)) - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_3, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_3, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") diff --git a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py index 2fedb6e517e5..39497e91edce 100755 --- a/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py +++ b/tests/topotests/multicast_pim6_static_rp_topo1/test_multicast_pim6_static_rp2.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +# -*- coding: utf-8 eval: (blacken-mode 1) -*- # SPDX-License-Identifier: ISC # @@ -33,55 +33,31 @@ import os import sys -import json import time -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/")) - -# Required to instantiate the topology builder class. - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib.topogen import Topogen, get_topogen +import pytest from lib.common_config import ( - start_topology, - write_test_header, - write_test_footer, + create_debug_log_config, reset_config_on_routers, - step, shutdown_bringup_interface, - kill_router_daemons, - start_router_daemons, - create_static_routes, - check_router_status, - socat_send_mld_join, - socat_send_pim6_traffic, - kill_socat, - create_debug_log_config, + start_topology, + step, + write_test_footer, + write_test_header, ) from lib.pim import ( + McastTesterHelper, create_pim_config, - verify_upstream_iif, verify_join_state_and_timer, + verify_mld_groups, verify_mroutes, - verify_pim_neighbors, - verify_pim_interface_traffic, - verify_pim_rp_info, - verify_pim_state, - clear_pim6_interface_traffic, - clear_pim6_mroute, verify_pim6_neighbors, - get_pim6_interface_traffic, - clear_pim6_interfaces, - verify_mld_groups, + verify_pim_rp_info, + verify_upstream_iif, ) +from lib.topogen import Topogen, get_topogen +from lib.topojson import build_config_from_json, build_topo_from_json from lib.topolog import logger -from lib.topojson import build_topo_from_json, build_config_from_json # Global variables GROUP_RANGE_1 = "ff08::/64" @@ -145,7 +121,7 @@ def setup_module(mod): logger.info("Running setup_module to create topology") # This function initiates the topology build with Topogen... - json_file = "{}/multicast_pim6_static_rp.json".format(CWD) + json_file = "multicast_pim6_static_rp.json" tgen = Topogen(json_file, mod.__name__) global TOPO TOPO = tgen.json_topo @@ -167,6 +143,9 @@ def setup_module(mod): result = verify_pim6_neighbors(tgen, TOPO) assert result is True, "setup_module :Failed \n Error:" " {}".format(result) + global app_helper + app_helper = McastTesterHelper(tgen) + logger.info("Running setup_module() done") @@ -176,6 +155,8 @@ def teardown_module(): logger.info("Running teardown_module to delete topology") tgen = get_topogen() + app_helper.cleanup() + # Stop toplogy and Remove tmp files tgen.stop_topology() @@ -262,6 +243,8 @@ def test_pim6_multiple_groups_same_RP_address_p2(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + input_dict = { "r1": {"debug": {"log_file": "r1_debug.log", "enable": ["pim6d"]}}, "r2": {"debug": {"log_file": "r2_debug.log", "enable": ["pim6d"]}}, @@ -302,10 +285,7 @@ def test_pim6_multiple_groups_same_RP_address_p2(request): group_address_list = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2 step("r0: Send MLD join for 10 groups") intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", group_address_list, intf, intf_ip - ) + result = app_helper.run_join("r0", group_address_list, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -315,9 +295,8 @@ def test_pim6_multiple_groups_same_RP_address_p2(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("r5: Send multicast traffic for group {}".format(group_address_list)) - intf = TOPO["routers"]["r5"]["links"]["r3"]["interface"] SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0] - result = socat_send_pim6_traffic(tgen, "r5", "UDP6-SEND", group_address_list, intf) + result = app_helper.run_traffic("r5", group_address_list, "r3") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify (*, G) upstream IIF interface") @@ -590,6 +569,8 @@ def test_pim6_multiple_groups_different_RP_address_p2(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Enable MLD on r1 interface") step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers") step("r2: Configure r2 as RP") @@ -643,11 +624,7 @@ def test_pim6_multiple_groups_different_RP_address_p2(request): group_address_list = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2 step("r0: Send MLD join for 10 groups") - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", group_address_list, intf, intf_ip - ) + result = app_helper.run_join("r0", group_address_list, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") @@ -657,9 +634,8 @@ def test_pim6_multiple_groups_different_RP_address_p2(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("r5: Send multicast traffic for group {}".format(group_address_list)) - intf = TOPO["routers"]["r5"]["links"]["r3"]["interface"] SOURCE_ADDRESS = TOPO["routers"]["r5"]["links"]["r3"]["ipv6"].split("/")[0] - result = socat_send_pim6_traffic(tgen, "r5", "UDP6-SEND", group_address_list, intf) + result = app_helper.run_traffic("r5", group_address_list, "r3") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify (*, G) upstream IIF interface") @@ -1186,6 +1162,8 @@ def test_pim6_delete_RP_shut_noshut_upstream_interface_p1(request): step("Creating configuration from JSON") reset_config_on_routers(tgen) + app_helper.stop_all_hosts() + step("Enable MLD on r1 interface") step("Enable the PIM6 on all the interfaces of r1, r2, r3 and r4 routers") step("r2: Configure r2 as RP") @@ -1217,11 +1195,7 @@ def test_pim6_delete_RP_shut_noshut_upstream_interface_p1(request): assert result is True, ASSERT_MSG.format(tc_name, result) step("r0: Send MLD join") - intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"] - intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0] - result = socat_send_mld_join( - tgen, "r0", "UDP6-RECV", GROUP_ADDRESS_1, intf, intf_ip - ) + result = app_helper.run_join("r0", GROUP_ADDRESS_1, "r1") assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) step("r1: Verify MLD groups") diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json new file mode 100644 index 000000000000..715aa1de72dd --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_group_all_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json new file mode 100644 index 000000000000..3bbcce1370fc --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_brief.json @@ -0,0 +1,51 @@ +{ + "totalGroups":5, + "watermarkLimit":0, + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "state":"up", + "address":"10.0.8.2", + "index":"*", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "groups":[ + { + "group":"225.1.1.1", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.2", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.3", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.4", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + }, + { + "group":"225.1.1.5", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + } + ] + } +} + diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json new file mode 100644 index 000000000000..876befa1b8ef --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_group_all_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.1","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.2","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.3","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.4","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]},{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json new file mode 100644 index 000000000000..a3fb496d25a3 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_brief.json @@ -0,0 +1,22 @@ +{ + "totalGroups":5, + "watermarkLimit":0, + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "state":"up", + "address":"10.0.8.2", + "index":"*", + "flagMulticast":true, + "flagBroadcast":true, + "lanDelayEnabled":true, + "groups":[ + { + "group":"225.1.1.5", + "timer":"*", + "sourcesCount":1, + "version":2, + "uptime":"*" + } + ] + } +} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json new file mode 100644 index 000000000000..11ac5a01e74b --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_single_if_single_group_detail.json @@ -0,0 +1 @@ +{"totalGroups":5,"watermarkLimit":0,"l1-i1-eth1":{"name":"l1-i1-eth1","state":"up","address":"10.0.8.2","index":"*","flagMulticast":true,"flagBroadcast":true,"lanDelayEnabled":true,"groups":[{"group":"225.1.1.5","timer":"*","sourcesCount":1,"version":2,"uptime":"*","sources":[{"source":"*","timer":"*","forwarded":true,"uptime":"*"}]}]}} diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json new file mode 100644 index 000000000000..10ae1afc90b6 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_group_all.json @@ -0,0 +1,61 @@ +{ + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "225.1.1.1":{ + "group":"225.1.1.1", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.2":{ + "group":"225.1.1.2", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.3":{ + "group":"225.1.1.3", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.4":{ + "group":"225.1.1.4", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + }, + "225.1.1.5":{ + "group":"225.1.1.5", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + } + } +} + diff --git a/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json new file mode 100644 index 000000000000..7a19975bee14 --- /dev/null +++ b/tests/topotests/multicast_pim_sm_topo3/igmp_source_single_if_single_group.json @@ -0,0 +1,16 @@ +{ + "l1-i1-eth1":{ + "name":"l1-i1-eth1", + "225.1.1.4":{ + "group":"225.1.1.4", + "sources":[ + { + "source":"*", + "timer":"*", + "forwarded":true, + "uptime":"*" + } + ] + } + } +} diff --git a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py index 2ffd3a3ac0ef..2c1241c0ccc9 100755 --- a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py +++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py @@ -42,6 +42,8 @@ import datetime import pytest from time import sleep +import json +import functools pytestmark = pytest.mark.pimd @@ -54,8 +56,8 @@ # pylint: disable=C0413 # Import topogen and topotest helpers -from lib.topogen import Topogen, get_topogen - +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen from lib.common_config import ( start_topology, write_test_header, @@ -1510,6 +1512,108 @@ def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request): ) assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + # IGMP JSON verification + step("Verify IGMP group and source JSON for single interface and group") + router = tgen.gears["l1"] + + reffile = os.path.join(CWD, "igmp_group_all_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for all interfaces and all groups is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_group_all_brief.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_group_all_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for all groups in interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_single_group_brief.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_single_if_single_group_detail.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp vrf default groups l1-i1-eth1 225.1.1.5 detail json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP group detailed output on l1 for interface l1-i1-eth1 and group 225.1.1.5 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_source_single_if_group_all.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp sources l1-i1-eth1 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + + reffile = os.path.join(CWD, "igmp_source_single_if_single_group.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, + router, + "show ip igmp sources l1-i1-eth1 225.1.1.4 json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=60, wait=2) + assertmsg = "IGMP source output on l1 for interface l1-i1-eth1 and group 225.1.1.4 is not as expected. Expected: {}".format( + expected + ) + assert res is None, assertmsg + step( "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from" " receiver interface of FRR1" diff --git a/tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json b/tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json new file mode 100644 index 000000000000..158e1135af9a --- /dev/null +++ b/tests/topotests/multicast_pim_uplink_topo2/multicast_pim_uplink_topo2.json @@ -0,0 +1,288 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2-link1": {"ipv4": "auto", "pim": "enable"}, + "r2-link2": {"ipv4": "auto", "pim": "enable"}, + "r2-link3": {"ipv4": "auto", "pim": "enable"}, + "r2-link4": {"ipv4": "auto", "pim": "enable"}, + "r3-link1": {"ipv4": "auto", "pim": "enable"}, + "r3-link2": {"ipv4": "auto", "pim": "enable"}, + "r3-link3": {"ipv4": "auto", "pim": "enable"}, + "r3-link4": {"ipv4": "auto", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"}, + "r5": {"ipv4": "auto", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {}, + "r1-link2": {}, + "r1-link3": {}, + "r1-link4": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {}, + "r1-link2": {}, + "r1-link3": {}, + "r1-link4": {} + } + }, + "r4": { + "dest_link": { + "r1": {} + } + }, + "r5": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1-link1": {"ipv4": "auto", "pim": "enable"}, + "r1-link2": {"ipv4": "auto", "pim": "enable"}, + "r1-link3": {"ipv4": "auto", "pim": "enable"}, + "r1-link4": {"ipv4": "auto", "pim": "enable"}, + "r4-link1": {"ipv4": "auto", "pim": "enable"}, + "r4-link2": {"ipv4": "auto", "pim": "enable"}, + "r4-link3": {"ipv4": "auto", "pim": "enable"}, + "r4-link4": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {}, + "r2-link2": {}, + "r2-link3": {}, + "r2-link4": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {}, + "r2-link2": {}, + "r2-link3": {}, + "r2-link4": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1-link1": {"ipv4": "auto", "pim": "enable"}, + "r1-link2": {"ipv4": "auto", "pim": "enable"}, + "r1-link3": {"ipv4": "auto", "pim": "enable"}, + "r1-link4": {"ipv4": "auto", "pim": "enable"}, + "r4-link1": {"ipv4": "auto", "pim": "enable"}, + "r4-link2": {"ipv4": "auto", "pim": "enable"}, + "r4-link3": {"ipv4": "auto", "pim": "enable"}, + "r4-link4": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2-link1": {"ipv4": "auto", "pim": "enable"}, + "r2-link2": {"ipv4": "auto", "pim": "enable"}, + "r2-link3": {"ipv4": "auto", "pim": "enable"}, + "r2-link4": {"ipv4": "auto", "pim": "enable"}, + "r3-link1": {"ipv4": "auto", "pim": "enable"}, + "r3-link2": {"ipv4": "auto", "pim": "enable"}, + "r3-link3": {"ipv4": "auto", "pim": "enable"}, + "r3-link4": {"ipv4": "auto", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r5": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {} + } + }, + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {} + } + }, + "r1": { + "dest_link": { + "r4": {} + } + }, + "r5": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r5": {} + } + }, + "r4": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "i1": { + "links": { + "r1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "r1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "r3": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "r4": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "r4": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "r5": {"ipv4": "auto"} + } + } + } +} diff --git a/tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py b/tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py new file mode 100644 index 000000000000..eb3246b51398 --- /dev/null +++ b/tests/topotests/multicast_pim_uplink_topo2/test_multicast_pim_uplink_topo2.py @@ -0,0 +1,1349 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# + +""" +Following tests are covered to test multicast pim sm: + +1. Verify changing RP address on DUT from Static to BSR , IIF and OIF + updated correctly +2. Verify when mroute RPT and SPT path is difference +3. Verify mroutes updated with correct OIL and IIF after shut / no shut of + upstream interface from DUT +4. Verify mroutes updated with correct OIL and IIF after shut / no +shut of downstream interface from FHR + + +""" + +import os +import sys +import time +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/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + reset_config_on_routers, + shutdown_bringup_interface, + required_linux_kernel_version, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_mroutes, + clear_pim_interface_traffic, + verify_upstream_iif, + clear_mroute, + verify_multicast_traffic, + verify_pim_rp_info, + verify_pim_interface_traffic, + McastTesterHelper, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +# Global variables +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +GROUP_RANGE_2 = [ + "226.1.1.1/32", + "226.1.1.2/32", + "226.1.1.3/32", + "226.1.1.4/32", + "226.1.1.5/32", +] +IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"] + +r1_r2_links = [] +r1_r3_links = [] +r2_r1_links = [] +r3_r1_links = [] +r2_r4_links = [] +r4_r2_links = [] +r4_r3_links = [] + +pytestmark = [pytest.mark.pimd] + + +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.19") + 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") + + testdir = os.path.dirname(os.path.realpath(__file__)) + json_file = "{}/multicast_pim_uplink_topo2.json".format(testdir) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... 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) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + # Pre-requisite data + get_interfaces_names(topo) + + # XXX Replace this using "with McastTesterHelper()... " in each test if possible. + global app_helper + app_helper = McastTesterHelper(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() + + app_helper.cleanup() + + # 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 get_interfaces_names(topo): + """ + API to fetch interfaces names and create list, which further would be used + for verification + + Parameters + ---------- + * `topo` : inout JSON data + """ + + for link in range(1, 5): + + intf = topo["routers"]["r1"]["links"]["r2-link{}".format(link)]["interface"] + r1_r2_links.append(intf) + + intf = topo["routers"]["r1"]["links"]["r3-link{}".format(link)]["interface"] + r1_r3_links.append(intf) + + intf = topo["routers"]["r2"]["links"]["r1-link{}".format(link)]["interface"] + r2_r1_links.append(intf) + + intf = topo["routers"]["r3"]["links"]["r1-link{}".format(link)]["interface"] + r3_r1_links.append(intf) + + intf = topo["routers"]["r2"]["links"]["r4-link{}".format(link)]["interface"] + r2_r4_links.append(intf) + + intf = topo["routers"]["r4"]["links"]["r2-link{}".format(link)]["interface"] + r4_r2_links.append(intf) + + intf = topo["routers"]["r4"]["links"]["r3-link{}".format(link)]["interface"] + r4_r3_links.append(intf) + + +def verify_state_incremented(state_before, state_after): + """ + API to compare interface traffic state incrementing + + Parameters + ---------- + * `state_before` : State dictionary for any particular instance + * `state_after` : State dictionary for any particular instance + """ + + for router, state_data in state_before.items(): + for state, value in state_data.items(): + if state_before[router][state] > state_after[router][state]: + errormsg = ( + "[DUT: %s]: state %s value has not" + " incremented, Initial value: %s, " + "Current value: %s [FAILED!!]" + % ( + router, + state, + state_before[router][state], + state_after[router][state], + ) + ) + return errormsg + + logger.info( + "[DUT: %s]: State %s value is " + "incremented, Initial value: %s, Current value: %s" + " [PASSED!!]", + router, + state, + state_before[router][state], + state_after[router][state], + ) + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_iif_oil_when_RP_address_changes_from_static_to_BSR_p1(request): + """ + Verify changing RP address on DUT from Static to BSR , IIF and OIF + updated correctly + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Shutdown interfaces which are not required") + intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"] + intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r4, False) + shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False) + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False) + + step("Enable IGMP on DUT and R4 interface") + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"] + for dut, intf in zip(["r2", "r4"], [intf_r2_i3, intf_r4_i7]): + input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}} + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send IGMP joins from DUT, and R4 for group range 225.1.1.1-5") + input_join = { + "i3": topo["routers"]["i3"]["links"]["r2"]["interface"], + "i7": topo["routers"]["i7"]["links"]["r4"]["interface"], + } + + for recvr, recvr_intf in input_join.items(): + result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP as R4 loopback interface for group range 225.1.1.1-5") + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Done in base config: " "Configure EBGP peering between all the nodes") + + step("Done in base config: " "Enable PIM on all the interfaces of all the nodes") + + step("Send traffic from DUT for group range 225.1.1.1-5") + + input_src = { + "i4": topo["routers"]["i4"]["links"]["r2"]["interface"], + "i6": topo["routers"]["i6"]["links"]["r4"]["interface"], + } + + for src, src_intf in input_src.items(): + result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("(*,G) IIF and OIL updated on both the nodes") + + step( + "(S,G) IIF updated towards shortest path to source on both the nodes " + ", verify using 'show ip mroute' and 'show ip mroute json'" + ) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + source_i4 = topo["routers"]["i4"]["links"]["r2"]["ipv4"].split("/")[0] + input_dict_star_sg = [ + { + "dut": "r2", + "src_address": "*", + "iif": r2_r4_links, + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "r4", + "src_address": "*", + "iif": "lo", + "oil": r4_r2_links + [topo["routers"]["r4"]["links"]["i7"]["interface"]], + }, + { + "dut": "r2", + "src_address": source_i6, + "iif": r2_r4_links, + "oil": topo["routers"]["r2"]["links"]["i3"]["interface"], + }, + { + "dut": "r2", + "src_address": source_i4, + "iif": topo["routers"]["r2"]["links"]["i4"]["interface"], + "oil": r2_r4_links + [topo["routers"]["r2"]["links"]["i3"]["interface"]], + }, + { + "dut": "r4", + "src_address": source_i6, + "iif": topo["routers"]["r4"]["links"]["i6"]["interface"], + "oil": r4_r2_links + [topo["routers"]["r4"]["links"]["i7"]["interface"]], + }, + { + "dut": "r4", + "src_address": source_i4, + "iif": r4_r2_links + r4_r3_links, + "oil": topo["routers"]["r4"]["links"]["i7"]["interface"], + }, + ] + + for data in input_dict_star_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "OIL is updated and traffic is received for all the groups on both " + "the nodes , verify using 'show ip multicast'; 'show ip multicast json'" + ) + + intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"] + intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"] + input_traffic = { + "r2": {"traffic_sent": [intf_r2_i3]}, + "r4": {"traffic_received": [intf_r4_i6]}, + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Change RP address for range 225.1.1.1-5 to cisco (BSRP) " "loopback interface" + ) + + input_dict = { + "r4": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + "delete": True, + } + ] + } + }, + "r5": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r5"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1, + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Send one more traffic stream from R4 to group range 225.1.1.1-5") + + result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("RP type is changed to BSRP for 225.1.1.1-5 groups range on DUT") + + rp_addr = topo["routers"]["r5"]["links"]["lo"]["ipv4"].split("/")[0] + + result = verify_pim_rp_info( + tgen, topo, "r5", GROUP_RANGE_1, "lo", rp_addr, "Static" + ) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "No impact seen on multicast data traffic for both groups range " + "verify using 'show ip multicast json' and 'show ip mroute json'" + ) + + for data in input_dict_star_sg: + if data["src_address"] != "*": + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Stop traffic and do clear mroute on all the node (make " + "sure (s,g) got timeout" + ) + + app_helper.stop_all_hosts() + clear_mroute(tgen) + + step("Verify (S,G) got cleared after stop of traffic and 'clear mroute'") + + for data in input_dict_star_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed " "Mroutes are still present \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_mroute_when_RPT_and_SPT_path_is_different_p1(request): + """ + Verify when mroute RPT and SPT path is difference + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Shut link from R3 to R1 and no shut R1 to R4 link to make star topology") + for i in range(1, 5): + intf = topo["routers"]["r3"]["links"]["r1-link{}".format(i)]["interface"] + shutdown_bringup_interface(tgen, "r3", intf, False) + + intf = topo["routers"]["r1"]["links"]["r3-link{}".format(i)]["interface"] + shutdown_bringup_interface(tgen, "r1", intf, False) + + intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"] + intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"] + intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"] + intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"] + shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False) + shutdown_bringup_interface(tgen, "r1", intf_r1_r4, True) + shutdown_bringup_interface(tgen, "r1", intf_r1_r5, True) + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, True) + shutdown_bringup_interface(tgen, "r5", intf_r5_r1, True) + + step("Done in base config: Connected one more route R5 before R1 ( R5-R1)") + + step("Enable IGMP on R5 and R4 interface") + intf_r5_i8 = topo["routers"]["r5"]["links"]["i8"]["interface"] + intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"] + for dut, intf in zip(["r4", "r5"], [intf_r4_i7, intf_r5_i8]): + input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}} + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send IGMP joins from R5, for group range 226.1.1.1-5") + input_join = { + "i8": topo["routers"]["i8"]["links"]["r5"]["interface"], + "i7": topo["routers"]["i7"]["links"]["r4"]["interface"], + } + + for recvr, recvr_intf in input_join.items(): + result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_2, join_intf=recvr_intf) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure RP as R2 for group range 226.1.1.1-5") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_2, + } + ] + } + } + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Done in base config: " "Configure EBGP peering between all the nodes") + + step("Done in base config: " "Enable PIM on all the interfaces of all the nodes") + + step("Send traffic from R3 for group range 226.1.1.1-5") + + result = app_helper.run_traffic("i5", IGMP_JOIN_RANGE_2, "r3") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("(*,G) IIF updated for 225.1.1.1-5 towards R2 and RP " "type is static on DUT") + + step("(S,G) on R5 has updated for all the groups") + + source_i5 = topo["routers"]["i5"]["links"]["r3"]["ipv4"].split("/")[0] + input_dict_star_sg = [ + { + "dut": "r1", + "src_address": "*", + "iif": r1_r2_links + r1_r3_links, + "oil": topo["routers"]["r1"]["links"]["r5"]["interface"], + }, + { + "dut": "r4", + "src_address": "*", + "iif": r4_r2_links + [intf_r4_r1], + "oil": topo["routers"]["r4"]["links"]["i7"]["interface"], + }, + { + "dut": "r1", + "src_address": source_i5, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["r5"]["interface"], + }, + { + "dut": "r4", + "src_address": source_i5, + "iif": r4_r2_links + r4_r3_links, + "oil": topo["routers"]["r4"]["links"]["i7"]["interface"], + }, + ] + + for data in input_dict_star_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(S,G) on R1 updated and has IIF toward R4 and OIL toward R5 , " + "RP path OIL is removed" + ) + + source_i5 = topo["routers"]["i5"]["links"]["r3"]["ipv4"].split("/")[0] + input_dict_sg = [ + {"dut": "r1", "src_address": source_i5, "iif": r1_r2_links, "oil": r1_r2_links}, + {"dut": "r4", "src_address": source_i5, "iif": r4_r2_links, "oil": r4_r2_links}, + ] + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed " "OIF and IIF are same \n Error: {}".format( + tc_name, result + ) + + step("Shut and no Shut of mroute OIL selected links from R1 towards R2 and R4") + + for i in range(1, 5): + intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"] + shutdown_bringup_interface(tgen, "r1", intf, False) + + for i in range(1, 5): + intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"] + shutdown_bringup_interface(tgen, "r1", intf, True) + + step( + "After shut and no shut of link verify mroute got populated as per " + "verification step 8" + ) + + for data in input_dict_star_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_2, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed " "OIF and IIF are same \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_mroutes_updated_with_correct_oil_iif_after_shut_noshut_upstream_interface_p0( + request, +): + """ + Verify mroutes updated with correct OIL and IIF after shut / no shut of + upstream interface from DUT + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Shutdown interfaces which are not required") + intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"] + intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"] + intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"] + intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False) + shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False) + + step("Enable IGMP on DUT receiver interface") + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + for dut, intf in zip(["r1", "r1"], [intf_r1_i1, intf_r1_i2]): + input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}} + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Verify pim interface traffic before sending join/traffic") + + intf_traffic = topo["routers"]["r4"]["links"]["r3-link1"]["interface"] + state_dict = {"r4": {intf_traffic: ["registerStopRx"]}} + state_before = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format( + tc_name, result + ) + + step("Send IGMP joins from DUT for group range 225.1.1.1-5") + result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure RP as R2 and R3 interface (225.1.1.1-3 on R2 and " + "225.1.1.4-5 on R3)" + ) + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1[0:3], + } + ] + } + }, + "r3": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1[3:5], + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Done in base config: " "Configure BGP peering between all the nodes") + + step("Done in base config: " "Enable PIM on all the interfaces of all the nodes") + + step("Send traffic from R4 for group range 225.1.1.1-5") + + result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) IIF is updated DUT-R2 any one interface for groups 225.1.1.1-3 " + "and DUT to R3 any one interface for groups 225.1.1.1-3" + ) + + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif_r1_r2": r1_r2_links, + "iif_r1_r3": r1_r3_links, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_starg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + data["iif_r1_r2"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r2"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + data["iif_r1_r3"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r3"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(S,G) IIF updated towards shortest path to source verify using " + "'show ip mroute' and 'show ip mroute json'" + ) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) and (S,G) OIL is updated and traffic is received for all " + "the groups verify using 'show ip multicast' and" + "'show ip multicast count json'" + ) + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + input_traffic = { + "r1": {"traffic_sent": [intf_r1_i1], "traffic_received": [intf_r1_r4]} + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "Register packets sent/received count is incrementing verify " + "using 'show ip pim interface traffic json'" + ) + + state_after = verify_pim_interface_traffic(tgen, state_dict) + assert isinstance( + state_before, dict + ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format( + tc_name, result + ) + + result = verify_state_incremented(state_before, state_after) + assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result) + + step("Shut interface connected from R4 to DUT") + intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r4, False) + + step( + "After shut of R4 to DUT interface verify (S,G) has taken " + "different path ( via R2 or R3 any link) , uptime got resetted " + "and OIL is updated accordingly No impact seen on (*,G) routes , " + "verify uptime for (*,G) using 'show ip mroute json' and " + "'show ip pim state'" + ) + + for data in input_dict_starg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + data["iif_r1_r2"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r2"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + data["iif_r1_r3"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r3"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": r1_r2_links + r1_r3_links, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut the interface connected from DUT to R2 one by one") + + for i in range(1, 5): + intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"] + shutdown_bringup_interface(tgen, "r1", intf, False) + + step( + "After shut of DUT to R2 all the interfaces (S,G) created via R3, " + "(S,G) uptime get reset and OIL is updated accordingly, No impact " + "seen on (*,G) routes verify using 'show ip mroute json'" + ) + + for data in input_dict_starg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + data["iif_r1_r3"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r3"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + data["iif_r1_r3"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r3"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": r1_r3_links, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +def test_mroutes_updated_with_correct_oil_iif_after_shut_noshut_downstream_interface_p0( + request, +): + """ + Verify mroutes updated with correct OIL and IIF after shut / no + shut of downstream interface from FHR + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("Shutdown interfaces which are not required") + intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"] + intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"] + intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"] + intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False) + shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False) + + step("Enable IGMP on DUT receiver interface") + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + for dut, intf in zip(["r1", "r1"], [intf_r1_i1, intf_r1_i2]): + input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}} + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Send IGMP joins from DUT for group range 225.1.1.1-5") + result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, "r1") + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step( + "Configure RP as R2 and R3 interface (225.1.1.1-3 on R2 and " + "225.1.1.4-5 on R3)" + ) + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1[0:3], + } + ] + } + }, + "r3": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE_1[3:5], + } + ] + } + }, + } + + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Done in base config: " "Configure BGP peering between all the nodes") + + step("Done in base config: " "Enable PIM on all the interfaces of all the nodes") + + step("Send traffic from R4 for group range 225.1.1.1-5") + + result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) IIF is updated DUT-R2 any one interface for groups 225.1.1.1-3 " + "and DUT to R3 any one interface for groups 225.1.1.1-3" + ) + + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif_r1_r2": r1_r2_links, + "iif_r1_r3": r1_r3_links, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_starg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + data["iif_r1_r2"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r2"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + data["iif_r1_r3"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r3"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(S,G) IIF updated towards shortest path to source verify using " + "'show ip mroute' and 'show ip mroute json'" + ) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": topo["routers"]["r1"]["links"]["r4"]["interface"], + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step( + "(*,G) and (S,G) OIL is updated and traffic is received for all " + "the groups verify using 'show ip multicast' and" + "'show ip multicast count json'" + ) + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + input_traffic = { + "r1": {"traffic_sent": [intf_r1_i1], "traffic_received": [intf_r1_r4]} + } + result = verify_multicast_traffic(tgen, input_traffic) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("Shut interface connected from R4 to DUT") + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"] + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False) + + step( + "After shut of R4 to DUT interface verify (S,G) has taken " + "different path ( via R2 or R3 any link) , uptime got resetted " + "and OIL is updated accordingly No impact seen on (*,G) routes , " + "verify uptime for (*,G) using 'show ip mroute json' and " + "'show ip pim state'" + ) + + for data in input_dict_starg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + data["iif_r1_r2"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r2"], + data["src_address"], + IGMP_JOIN_RANGE_1[0:3], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + data["iif_r1_r3"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif_r1_r3"], + data["src_address"], + IGMP_JOIN_RANGE_1[3:5], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": r1_r2_links + r1_r3_links, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + for data in input_dict_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json b/tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json new file mode 100644 index 000000000000..dc9e1ac49b57 --- /dev/null +++ b/tests/topotests/multicast_pim_uplink_topo3/multicast_pim_uplink_topo3.json @@ -0,0 +1,295 @@ +{ + "ipv4base": "10.0.0.0", + "ipv4mask": 24, + "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"}, + "lo_prefix": {"ipv4": "1.0.", "v4mask": 32}, + "routers": { + "r1": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2-link1": {"ipv4": "auto", "pim": "enable"}, + "r2-link2": {"ipv4": "auto", "pim": "enable"}, + "r2-link3": {"ipv4": "auto", "pim": "enable"}, + "r2-link4": {"ipv4": "auto", "pim": "enable"}, + "r3-link1": {"ipv4": "auto", "pim": "enable"}, + "r3-link2": {"ipv4": "auto", "pim": "enable"}, + "r3-link3": {"ipv4": "auto", "pim": "enable"}, + "r3-link4": {"ipv4": "auto", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"}, + "r5": {"ipv4": "auto", "pim": "enable"}, + "i1": {"ipv4": "auto", "pim": "enable"}, + "i2": {"ipv4": "auto", "pim": "enable"}, + "i9": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "100", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r1-link1": {}, + "r1-link2": {}, + "r1-link3": {}, + "r1-link4": {} + } + }, + "r3": { + "dest_link": { + "r1-link1": {}, + "r1-link2": {}, + "r1-link3": {}, + "r1-link4": {} + } + }, + "r4": { + "dest_link": { + "r1": {} + } + }, + "r5": { + "dest_link": { + "r1": {} + } + } + } + } + } + } + } + }, + "r2": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1-link1": {"ipv4": "auto", "pim": "enable"}, + "r1-link2": {"ipv4": "auto", "pim": "enable"}, + "r1-link3": {"ipv4": "auto", "pim": "enable"}, + "r1-link4": {"ipv4": "auto", "pim": "enable"}, + "r4-link1": {"ipv4": "auto", "pim": "enable"}, + "r4-link2": {"ipv4": "auto", "pim": "enable"}, + "r4-link3": {"ipv4": "auto", "pim": "enable"}, + "r4-link4": {"ipv4": "auto", "pim": "enable"}, + "i3": {"ipv4": "auto", "pim": "enable"}, + "i4": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "200", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r2-link1": {}, + "r2-link2": {}, + "r2-link3": {}, + "r2-link4": {} + } + }, + "r4": { + "dest_link": { + "r2-link1": {}, + "r2-link2": {}, + "r2-link3": {}, + "r2-link4": {} + } + } + } + } + } + } + } + }, + "r3": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1-link1": {"ipv4": "auto", "pim": "enable"}, + "r1-link2": {"ipv4": "auto", "pim": "enable"}, + "r1-link3": {"ipv4": "auto", "pim": "enable"}, + "r1-link4": {"ipv4": "auto", "pim": "enable"}, + "r4-link1": {"ipv4": "auto", "pim": "enable"}, + "r4-link2": {"ipv4": "auto", "pim": "enable"}, + "r4-link3": {"ipv4": "auto", "pim": "enable"}, + "r4-link4": {"ipv4": "auto", "pim": "enable"}, + "i5": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "300", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {} + } + }, + "r4": { + "dest_link": { + "r3-link1": {}, + "r3-link2": {}, + "r3-link3": {}, + "r3-link4": {} + } + } + } + } + } + } + } + }, + "r4": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r2-link1": {"ipv4": "auto", "pim": "enable"}, + "r2-link2": {"ipv4": "auto", "pim": "enable"}, + "r2-link3": {"ipv4": "auto", "pim": "enable"}, + "r2-link4": {"ipv4": "auto", "pim": "enable"}, + "r3-link1": {"ipv4": "auto", "pim": "enable"}, + "r3-link2": {"ipv4": "auto", "pim": "enable"}, + "r3-link3": {"ipv4": "auto", "pim": "enable"}, + "r3-link4": {"ipv4": "auto", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r5": {"ipv4": "auto", "pim": "enable"}, + "i6": {"ipv4": "auto", "pim": "enable"}, + "i7": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "400", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r2": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {} + } + }, + "r3": { + "dest_link": { + "r4-link1": {}, + "r4-link2": {}, + "r4-link3": {}, + "r4-link4": {} + } + }, + "r1": { + "dest_link": { + "r4": {} + } + }, + "r5": { + "dest_link": { + "r4": {} + } + } + } + } + } + } + } + }, + "r5": { + "links": { + "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}, + "r1": {"ipv4": "auto", "pim": "enable"}, + "r4": {"ipv4": "auto", "pim": "enable"}, + "i8": {"ipv4": "auto", "pim": "enable"} + }, + "bgp": { + "local_as": "500", + "address_family": { + "ipv4": { + "unicast": { + "redistribute": [ + {"redist_type": "static"}, + {"redist_type": "connected"} + ], + "neighbor": { + "r1": { + "dest_link": { + "r5": {} + } + }, + "r4": { + "dest_link": { + "r5": {} + } + } + } + } + } + } + } + }, + "i1": { + "links": { + "r1": {"ipv4": "auto"} + } + }, + "i2": { + "links": { + "r1": {"ipv4": "auto"} + } + }, + "i3": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i4": { + "links": { + "r2": {"ipv4": "auto"} + } + }, + "i5": { + "links": { + "r3": {"ipv4": "auto"} + } + }, + "i6": { + "links": { + "r4": {"ipv4": "auto"} + } + }, + "i7": { + "links": { + "r4": {"ipv4": "auto"} + } + }, + "i8": { + "links": { + "r5": {"ipv4": "auto"} + } + }, + "i9": { + "links": { + "r1": {"ipv4": "auto"} + } + } + + } +} diff --git a/tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py b/tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py new file mode 100644 index 000000000000..19a8cd0c1700 --- /dev/null +++ b/tests/topotests/multicast_pim_uplink_topo3/test_multicast_pim_uplink_topo3.py @@ -0,0 +1,916 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2023 by VMware, Inc. ("VMware") +# + +""" +Following tests are covered to test multicast pim sm: + +1. TC:1 Verify static IGMP group populated when static "ip igmp join <grp>" in configured +2. TC:2 Verify mroute and upstream populated with correct OIL/IIF with static igmp join +3. TC:3 Verify local IGMP join not allowed for "224.0.0.0/24" and non multicast group +4. TC:4 Verify static IGMP group removed from DUT while removing "ip igmp join" CLI +5. TC:5 Verify static IGMP groups after removing and adding IGMP config +""" + +import os +import sys +import time +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/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen + +from lib.common_config import ( + start_topology, + write_test_header, + write_test_footer, + step, + addKernelRoute, + reset_config_on_routers, + shutdown_bringup_interface, + required_linux_kernel_version, +) +from lib.pim import ( + create_pim_config, + create_igmp_config, + verify_igmp_groups, + verify_mroutes, + clear_pim_interface_traffic, + verify_upstream_iif, + clear_mroute, + verify_pim_rp_info, + verify_local_igmp_groups, + McastTesterHelper, +) +from lib.topolog import logger +from lib.topojson import build_config_from_json + +# Global variables +TOPOLOGY = """ + + i9 i3-+-i4 i6-+-i7 + | | | + i1--- R1-------R2----------R4------R5---i8 + | | | + i2 R3-------------------+ + + + | + i5 + + Description: + i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP + join and traffic + R1 - DUT (LHR/FHR) + R2 - RP + R3 - Transit + R4 - (LHR/FHR) + R5 - Transit +""" +# Global variables +RP_RANGE1 = "226.0.0.1/32" +RP_RANGE2 = "226.0.0.2/32" +RP_RANGE3 = "226.0.0.3/32" +RP_RANGE4 = "226.0.0.4/32" +RP_RANGE5 = "226.0.0.5/32" +RP_RANGE6 = "232.0.0.1/32" +RP_RANGE7 = "232.0.0.2/32" +RP_RANGE8 = "232.0.0.3/32" +RP_RANGE9 = "232.0.0.4/32" +RP_RANGE10 = "232.0.0.5/32" + +GROUP_RANGE = "224.0.0.0/4" +IGMP_GROUP = "225.1.1.1/32" +IGMP_JOIN = "225.1.1.1" +GROUP_RANGE_1 = [ + "225.1.1.1/32", + "225.1.1.2/32", + "225.1.1.3/32", + "225.1.1.4/32", + "225.1.1.5/32", +] +IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"] +IGMP_JOIN_RANGE_2 = ["224.0.0.1", "224.0.0.2", "224.0.0.3", "192.0.0.4", "192.0.0.5"] +IGMP_JOIN_RANGE_3 = [ + "226.0.0.1", + "226.0.0.2", + "226.0.0.3", + "226.0.0.4", + "226.0.0.5", + "232.0.0.1", + "232.0.0.2", + "232.0.0.3", + "232.0.0.4", + "232.0.0.5", +] +GROUP_RANGE_3 = [ + "226.0.0.1/32", + "226.0.0.2/32", + "226.0.0.3/32", + "226.0.0.4/32", + "226.0.0.5/32", + "232.0.0.1/32", + "232.0.0.2/32", + "232.0.0.3/32", + "232.0.0.4/32", + "232.0.0.5/32", +] + +r1_r2_links = [] +r1_r3_links = [] +r2_r1_links = [] +r2_r4_links = [] +r3_r1_links = [] +r3_r4_links = [] +r4_r2_links = [] +r4_r3_links = [] + +pytestmark = [pytest.mark.pimd] + + +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.19") + 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") + + testdir = os.path.dirname(os.path.realpath(__file__)) + json_file = "{}/multicast_pim_uplink_topo3.json".format(testdir) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... 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) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Creating configuration from JSON + build_config_from_json(tgen, tgen.json_topo) + + # Pre-requisite data + get_interfaces_names(topo) + + # XXX Replace this using "with McastTesterHelper()... " in each test if possible. + global app_helper + app_helper = McastTesterHelper(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() + + app_helper.cleanup() + + # 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 get_interfaces_names(topo): + """ + API to fetch interfaces names and create list, which further would be used + for verification + + Parameters + ---------- + * `topo` : inout JSON data + """ + + for link in range(1, 5): + + intf = topo["routers"]["r1"]["links"]["r2-link{}".format(link)]["interface"] + r1_r2_links.append(intf) + + intf = topo["routers"]["r1"]["links"]["r3-link{}".format(link)]["interface"] + r1_r3_links.append(intf) + + intf = topo["routers"]["r2"]["links"]["r1-link{}".format(link)]["interface"] + r2_r1_links.append(intf) + + intf = topo["routers"]["r3"]["links"]["r1-link{}".format(link)]["interface"] + r3_r1_links.append(intf) + + intf = topo["routers"]["r2"]["links"]["r4-link{}".format(link)]["interface"] + r2_r4_links.append(intf) + + intf = topo["routers"]["r4"]["links"]["r2-link{}".format(link)]["interface"] + r4_r2_links.append(intf) + + intf = topo["routers"]["r4"]["links"]["r3-link{}".format(link)]["interface"] + r4_r3_links.append(intf) + + +def shutdown_interfaces(tgen): + """ + API to Shut down interfaces which is not + used in all the testcases as part of this TDS + + Parameters + ---------- + * `tgen`: topogen object + + """ + logger.info("shutting down extra interfaces") + intf_r1_r4 = topo["routers"]["r1"]["links"]["r4"]["interface"] + intf_r1_r5 = topo["routers"]["r1"]["links"]["r5"]["interface"] + intf_r4_r1 = topo["routers"]["r4"]["links"]["r1"]["interface"] + intf_r5_r1 = topo["routers"]["r5"]["links"]["r1"]["interface"] + intf_r4_r5 = topo["routers"]["r4"]["links"]["r5"]["interface"] + intf_r5_r4 = topo["routers"]["r5"]["links"]["r4"]["interface"] + shutdown_bringup_interface(tgen, "r1", intf_r1_r4, False) + shutdown_bringup_interface(tgen, "r1", intf_r1_r5, False) + shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r1, False) + shutdown_bringup_interface(tgen, "r4", intf_r4_r5, False) + shutdown_bringup_interface(tgen, "r5", intf_r5_r4, False) + + +def config_to_send_igmp_join_and_traffic( + tgen, topo, tc_name, iperf, iperf_intf, GROUP_RANGE, join=False, traffic=False +): + """ + API to do pre-configuration to send IGMP join and multicast + traffic + + parameters: + ----------- + * `tgen`: topogen object + * `topo`: input json data + * `tc_name`: caller test case name + * `iperf`: router running iperf + * `iperf_intf`: interface name router running iperf + * `GROUP_RANGE`: group range + * `join`: IGMP join, default False + * `traffic`: multicast traffic, default False + """ + + if join: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + if traffic: + # Add route to kernal + result = addKernelRoute(tgen, iperf, iperf_intf, GROUP_RANGE) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + router_list = tgen.routers() + for router in router_list.keys(): + if router == iperf: + continue + + rnode = router_list[router] + rnode.run("echo 2 > /proc/sys/net/ipv4/conf/all/rp_filter") + + return True + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_ip_igmp_local_joins_p0(request): + """ + TC_1 Verify static IGMP group populated when static + "ip igmp join <grp>" in configured + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("shut down not required interfaces") + shutdown_interfaces(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the IGMP on R11 interfac of R1 and configure local igmp groups") + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "igmp": { + "interfaces": { + intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}}, + intf_r1_i2: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}}, + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static igmp join using show ip igmp join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify igmp groups using show ip igmp groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_mroute_with_igmp_local_joins_p0(request): + """ + TC_2 Verify mroute and upstream populated with correct OIL/IIF with + static igmp join + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("shut down not required interfaces") + shutdown_interfaces(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the IGMP on R11 interfac of R1 and configure local igmp groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "igmp": { + "interfaces": { + intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}}, + intf_r1_i2: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}}, + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static igmp join using show ip igmp join") + dut = "r1" + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify igmp groups using show ip igmp groups") + interfaces = [intf_r1_i1, intf_r1_i2] + for interface in interfaces: + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv4"].split("/")[0] + SOURCE = "Static" + oif = r1_r2_links + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + + r1_r2_r3 = r1_r2_links + r1_r3_links + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": "*", + "iif": r1_r2_links, + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": source_i6, + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + step("Verify mroutes and iff upstream for local igmp groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Verify mroutes not created with local interface ip ") + + input_dict_local_sg = [ + { + "dut": "r1", + "src_address": intf_r1_i1, + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + }, + { + "dut": "r1", + "src_address": intf_r1_i2, + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i2"]["interface"], + }, + ] + + for data in input_dict_local_sg: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "sg created with local interface ip".format(tc_name, result) + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert result is not True, ( + "Testcase {} : Failed Error: {}" + "upstream created with local interface ip".format(tc_name, result) + ) + + write_test_footer(tc_name) + + +def test_igmp_local_join_with_reserved_address_p0(request): + """ + TC_3 Verify local IGMP join not allowed for "224.0.0.0/24" + and non multicast group + """ + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("shut down not required interfaces") + shutdown_interfaces(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the IGMP on R11 interface of R1 and configure local igmp groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + input_dict = { + "r1": { + "igmp": { + "interfaces": { + intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_2}} + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static igmp join using show ip igmp join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_igmp_groups( + tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "IGMP join still present".format( + tc_name, result + ) + + step("verify igmp groups using show ip igmp groups") + interface = intf_r1_i1 + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "IGMP groups still present".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +def test_remove_add_igmp_local_joins_p1(request): + """ + TC_4 Verify static IGMP group removed from DUT while + removing "ip igmp join" CLI + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + + # Creating configuration from JSON + app_helper.stop_all_hosts() + clear_mroute(tgen) + reset_config_on_routers(tgen) + clear_pim_interface_traffic(tgen, topo) + + # Don"t run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step("shut down not required interfaces") + shutdown_interfaces(tgen) + + step("Enable the PIM on all the interfaces of R1, R2, R3, R4") + step("configure BGP on R1, R2, R3, R4 and enable redistribute static/connected") + step("Enable the IGMP on R11 interfac of R1 and configure local igmp groups") + + intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"] + intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"] + input_dict = { + "r1": { + "igmp": { + "interfaces": { + intf_r1_i1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}} + } + } + } + } + + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("Configure static RP for (225.1.1.1-5) as R2") + + input_dict = { + "r2": { + "pim": { + "rp": [ + { + "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split( + "/" + )[0], + "group_addr_range": GROUP_RANGE, + } + ] + } + } + } + result = create_pim_config(tgen, topo, input_dict) + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + step("verify static igmp join using show ip igmp join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify igmp groups using show ip igmp groups") + + interface = intf_r1_i1 + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify RP-info populated in DUT") + dut = "r1" + rp_address = topo["routers"]["r2"]["links"]["lo"]["ipv4"].split("/")[0] + SOURCE = "Static" + oif = r1_r2_links + result = verify_pim_rp_info(tgen, topo, dut, GROUP_RANGE_1, oif, rp_address, SOURCE) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Send traffic from R4 to all the groups ( 225.1.1.1 to 225.1.1.5)") + + result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4") + assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result) + + source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0] + + logger.info("waiting 30 sec for SPT switchover") + + r1_r2_r3 = r1_r2_links + r1_r3_links + input_dict_starg = [ + { + "dut": "r1", + "src_address": "*", + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + input_dict_sg = [ + { + "dut": "r1", + "src_address": source_i6, + "iif": r1_r2_r3, + "oil": topo["routers"]["r1"]["links"]["i1"]["interface"], + } + ] + + step("Verify mroutes and iff upstream for local igmp groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + step("Remove IGMP join from DUT") + input_dict = { + "r1": { + "igmp": { + "interfaces": { + intf_r1_i1: { + "igmp": { + "join": IGMP_JOIN_RANGE_1, + "delete_attr": True, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static igmp join removed using show ip igmp join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_igmp_groups( + tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False + ) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "IGMP join still present".format( + tc_name, result + ) + + step("verify igmp groups removed using show ip igmp groups") + interface = intf_r1_i1 + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False) + assert ( + result is not True + ), "Testcase {} :Failed \n Error: {}" "IGMP groups still present".format( + tc_name, result + ) + + step("Verify mroutes and iff upstream for local igmp groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, + data["dut"], + data["iif"], + data["src_address"], + IGMP_JOIN_RANGE_1, + expected=False, + ) + assert ( + result is not True + ), "Testcase {} : Failed Error: {}" "mroutes still present".format( + tc_name, result + ) + + step("Add IGMP join on DUT again") + input_dict = { + "r1": { + "igmp": { + "interfaces": { + intf_r1_i1: { + "igmp": { + "join": IGMP_JOIN_RANGE_1, + } + } + } + } + } + } + result = create_igmp_config(tgen, topo, input_dict) + assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result) + + step("verify static igmp join using show ip igmp join") + dut = "r1" + interface = intf_r1_i1 + result = verify_local_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("verify igmp groups using show ip igmp groups") + + interface = intf_r1_i1 + result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify mroutes and iff upstream for local igmp groups") + for input_dict in [input_dict_starg, input_dict_sg]: + for data in input_dict: + result = verify_mroutes( + tgen, + data["dut"], + data["src_address"], + IGMP_JOIN_RANGE_1, + data["iif"], + data["oil"], + ) + assert result is True, "Testcase {} : Failed Error: {}".format( + tc_name, result + ) + + result = verify_upstream_iif( + tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1 + ) + assert result is True, "Testcase {} : Failed 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/munet/__init__.py b/tests/topotests/munet/__init__.py new file mode 100644 index 000000000000..e1f18e51e612 --- /dev/null +++ b/tests/topotests/munet/__init__.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module to import various objects to root namespace.""" +from .base import BaseMunet +from .base import Bridge +from .base import Commander +from .base import LinuxNamespace +from .base import SharedNamespace +from .base import cmd_error +from .base import comm_error +from .base import get_exec_path +from .base import proc_error +from .native import L3Bridge +from .native import L3NamespaceNode +from .native import Munet +from .native import to_thread + + +__all__ = [ + "BaseMunet", + "Bridge", + "Commander", + "L3Bridge", + "L3NamespaceNode", + "LinuxNamespace", + "Munet", + "SharedNamespace", + "cmd_error", + "comm_error", + "get_exec_path", + "proc_error", + "to_thread", +] diff --git a/tests/topotests/munet/__main__.py b/tests/topotests/munet/__main__.py new file mode 100644 index 000000000000..4419ab94a284 --- /dev/null +++ b/tests/topotests/munet/__main__.py @@ -0,0 +1,236 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 2 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""The main function for standalone operation.""" +import argparse +import asyncio +import logging +import logging.config +import os +import subprocess +import sys + +from . import cli +from . import parser +from .base import get_event_loop +from .cleanup import cleanup_previous +from .compat import PytestConfig + + +logger = None + + +async def forever(): + while True: + await asyncio.sleep(3600) + + +async def run_and_wait(args, unet): + tasks = [] + + if not args.topology_only: + # add the cmd.wait()s returned from unet.run() + tasks += await unet.run() + + if sys.stdin.isatty() and not args.no_cli: + # Run an interactive CLI + task = cli.async_cli(unet) + else: + if args.no_wait: + logger.info("Waiting for all node cmd to complete") + task = asyncio.gather(*tasks, return_exceptions=True) + else: + logger.info("Waiting on signal to exit") + task = asyncio.create_task(forever()) + task = asyncio.gather(task, *tasks, return_exceptions=True) + + try: + await task + finally: + # Basically we are canceling tasks from unet.run() which are just async calls to + # node.cmd_p.wait() so we've stopped waiting for them to complete, but not + # actually canceld/killed the cmd_p process. + for task in tasks: + task.cancel() + + +async def async_main(args, config): + status = 3 + + # Setup the namespaces and network addressing. + + unet = await parser.async_build_topology( + config, rundir=args.rundir, args=args, pytestconfig=PytestConfig(args) + ) + logger.info("Topology up: rundir: %s", unet.rundir) + + try: + status = await run_and_wait(args, unet) + except KeyboardInterrupt: + logger.info("Exiting, received KeyboardInterrupt in async_main") + except asyncio.CancelledError as ex: + logger.info("task canceled error: %s cleaning up", ex) + except Exception as error: + logger.info("Exiting, unexpected exception %s", error, exc_info=True) + else: + logger.info("Exiting normally") + + logger.debug("main: async deleting") + try: + await unet.async_delete() + except KeyboardInterrupt: + status = 2 + logger.warning("Received KeyboardInterrupt while cleaning up.") + except Exception as error: + status = 2 + logger.info("Deleting, unexpected exception %s", error, exc_info=True) + return status + + +def main(*args): + ap = argparse.ArgumentParser(args) + cap = ap.add_argument_group(title="Config", description="config related options") + + cap.add_argument("-c", "--config", help="config file (yaml, toml, json, ...)") + cap.add_argument( + "-d", "--rundir", help="runtime directory for tempfiles, logs, etc" + ) + cap.add_argument( + "--kinds-config", + help="kinds config file, overrides default search (yaml, toml, json, ...)", + ) + cap.add_argument( + "--project-root", help="directory to stop searching for kinds config at" + ) + rap = ap.add_argument_group(title="Runtime", description="runtime related options") + rap.add_argument( + "-C", + "--cleanup", + action="store_true", + help="Remove the entire rundir (not just node subdirs) prior to running.", + ) + rap.add_argument( + "--gdb", metavar="NODE-LIST", help="comma-sep list of hosts to run gdb on" + ) + rap.add_argument( + "--gdb-breakpoints", + metavar="BREAKPOINT-LIST", + help="comma-sep list of breakpoints to set", + ) + rap.add_argument( + "--host", + action="store_true", + help="no isolation for top namespace, bridges exposed to default namespace", + ) + rap.add_argument( + "--pcap", + metavar="TARGET-LIST", + help="comma-sep list of capture targets (NETWORK or NODE:IFNAME)", + ) + rap.add_argument( + "--shell", metavar="NODE-LIST", help="comma-sep list of nodes to open shells on" + ) + rap.add_argument( + "--stderr", + metavar="NODE-LIST", + help="comma-sep list of nodes to open windows viewing stderr", + ) + rap.add_argument( + "--stdout", + metavar="NODE-LIST", + help="comma-sep list of nodes to open windows viewing stdout", + ) + rap.add_argument( + "--topology-only", + action="store_true", + help="Do not run any node commands", + ) + rap.add_argument("--unshare-inline", action="store_true", help=argparse.SUPPRESS) + rap.add_argument( + "--validate-only", + action="store_true", + help="Validate the config against the schema definition", + ) + rap.add_argument("-v", "--verbose", action="store_true", help="be verbose") + rap.add_argument( + "-V", "--version", action="store_true", help="print the verison number and exit" + ) + eap = ap.add_argument_group(title="Uncommon", description="uncommonly used options") + eap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)") + eap.add_argument( + "--no-kill", + action="store_true", + help="Do not kill previous running processes", + ) + eap.add_argument( + "--no-cli", action="store_true", help="Do not run the interactive CLI" + ) + eap.add_argument("--no-wait", action="store_true", help="Exit after commands") + + args = ap.parse_args() + + if args.version: + from importlib import metadata # pylint: disable=C0415 + + print(metadata.version("munet")) + sys.exit(0) + + rundir = args.rundir if args.rundir else "/tmp/munet" + args.rundir = rundir + + if args.cleanup: + if os.path.exists(rundir): + if not os.path.exists(f"{rundir}/config.json"): + logging.critical( + 'unsafe: won\'t clean up rundir "%s" as ' + "previous config.json not present", + rundir, + ) + sys.exit(1) + else: + subprocess.run(["/usr/bin/rm", "-rf", rundir], check=True) + + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + os.environ["MUNET_RUNDIR"] = rundir + + parser.setup_logging(args) + + global logger # pylint: disable=W0603 + logger = logging.getLogger("munet") + + config = parser.get_config(args.config) + logger.info("Loaded config from %s", config["config_pathname"]) + if not config["topology"]["nodes"]: + logger.critical("No nodes defined in config file") + return 1 + + if not args.no_kill: + cleanup_previous() + + loop = None + status = 4 + try: + parser.validate_config(config, logger, args) + if args.validate_only: + return 0 + # Executes the cmd for each node. + loop = get_event_loop() + status = loop.run_until_complete(async_main(args, config)) + except KeyboardInterrupt: + logger.info("Exiting, received KeyboardInterrupt in main") + except Exception as error: + logger.info("Exiting, unexpected exception %s", error, exc_info=True) + finally: + if loop: + loop.close() + + return status + + +if __name__ == "__main__": + exit_status = main() + sys.exit(exit_status) diff --git a/tests/topotests/munet/base.py b/tests/topotests/munet/base.py new file mode 100644 index 000000000000..06ca4deae2ad --- /dev/null +++ b/tests/topotests/munet/base.py @@ -0,0 +1,3111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 9 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements core functionality for library or standalone use.""" +import asyncio +import datetime +import errno +import ipaddress +import logging +import os +import platform +import re +import readline +import shlex +import signal +import subprocess +import sys +import tempfile +import time as time_mod + +from collections import defaultdict +from pathlib import Path +from typing import Union + +from . import config as munet_config +from . import linux + + +try: + import pexpect + + from pexpect.fdpexpect import fdspawn + from pexpect.popen_spawn import PopenSpawn + + have_pexpect = True +except ImportError: + have_pexpect = False + +PEXPECT_PROMPT = "PEXPECT_PROMPT>" +PEXPECT_CONTINUATION_PROMPT = "PEXPECT_PROMPT+" + +root_hostname = subprocess.check_output("hostname") +our_pid = os.getpid() + + +detailed_cmd_logging = False + + +class MunetError(Exception): + """A generic munet error.""" + + +class CalledProcessError(subprocess.CalledProcessError): + """Improved logging subclass of subprocess.CalledProcessError.""" + + def __str__(self): + o = self.output.strip() if self.output else "" + e = self.stderr.strip() if self.stderr else "" + s = f"returncode: {self.returncode} command: {self.cmd}" + o = "\n\tstdout: " + o if o else "" + e = "\n\tstderr: " + e if e else "" + return s + o + e + + def __repr__(self): + o = self.output.strip() if self.output else "" + e = self.stderr.strip() if self.stderr else "" + return f"munet.base.CalledProcessError({self.returncode}, {self.cmd}, {o}, {e})" + + +class Timeout: + """An object to passively monitor for timeouts.""" + + def __init__(self, delta): + self.delta = datetime.timedelta(seconds=delta) + self.started_on = datetime.datetime.now() + self.expires_on = self.started_on + self.delta + + def elapsed(self): + elapsed = datetime.datetime.now() - self.started_on + return elapsed.total_seconds() + + def is_expired(self): + return datetime.datetime.now() > self.expires_on + + def remaining(self): + remaining = self.expires_on - datetime.datetime.now() + return remaining.total_seconds() + + def __iter__(self): + return self + + def __next__(self): + remaining = self.remaining() + if remaining <= 0: + raise StopIteration() + return remaining + + +def fsafe_name(name): + return "".join(x if x.isalnum() else "_" for x in name) + + +def indent(s): + return "\t" + s.replace("\n", "\n\t") + + +def shell_quote(command): + """Return command wrapped in single quotes.""" + if sys.version_info[0] >= 3: + return shlex.quote(command) + return "'" + command.replace("'", "'\"'\"'") + "'" + + +def cmd_error(rc, o, e): + s = f"rc {rc}" + o = "\n\tstdout: " + o.strip() if o and o.strip() else "" + e = "\n\tstderr: " + e.strip() if e and e.strip() else "" + return s + o + e + + +def shorten(s): + s = s.strip() + i = s.find("\n") + if i > 0: + s = s[: i - 1] + if not s.endswith("..."): + s += "..." + if len(s) > 72: + s = s[:69] + if not s.endswith("..."): + s += "..." + return s + + +def comm_result(rc, o, e): + s = f"\n\treturncode {rc}" if rc else "" + o = "\n\tstdout: " + shorten(o) if o and o.strip() else "" + e = "\n\tstderr: " + shorten(e) if e and e.strip() else "" + return s + o + e + + +def proc_str(p): + if hasattr(p, "args"): + args = p.args if isinstance(p.args, str) else " ".join(p.args) + else: + args = "" + return f"proc pid: {p.pid} args: {args}" + + +def proc_error(p, o, e): + if hasattr(p, "args"): + args = p.args if isinstance(p.args, str) else " ".join(p.args) + else: + args = "" + + s = f"rc {p.returncode} pid {p.pid}" + a = "\n\targs: " + args if args else "" + o = "\n\tstdout: " + (o.strip() if o and o.strip() else "*empty*") + e = "\n\tstderr: " + (e.strip() if e and e.strip() else "*empty*") + return s + a + o + e + + +def comm_error(p): + rc = p.poll() + assert rc is not None + if not hasattr(p, "saved_output"): + p.saved_output = p.communicate() + return proc_error(p, *p.saved_output) + + +async def acomm_error(p): + rc = p.returncode + assert rc is not None + if not hasattr(p, "saved_output"): + p.saved_output = await p.communicate() + return proc_error(p, *p.saved_output) + + +def get_kernel_version(): + kvs = ( + subprocess.check_output("uname -r", shell=True, text=True).strip().split("-", 1) + ) + kv = kvs[0].split(".") + kv = [int(x) for x in kv] + return kv + + +def convert_number(value) -> int: + """Convert a number value with a possible suffix to an integer. + + >>> convert_number("100k") == 100 * 1024 + True + >>> convert_number("100M") == 100 * 1000 * 1000 + True + >>> convert_number("100Gi") == 100 * 1024 * 1024 * 1024 + True + >>> convert_number("55") == 55 + True + """ + if value is None: + raise ValueError("Invalid value None for convert_number") + rate = str(value) + base = 1000 + if rate[-1] == "i": + base = 1024 + rate = rate[:-1] + suffix = "KMGTPEZY" + index = suffix.find(rate[-1]) + if index == -1: + base = 1024 + index = suffix.lower().find(rate[-1]) + if index != -1: + rate = rate[:-1] + return int(rate) * base ** (index + 1) + + +def is_file_like(fo): + return isinstance(fo, int) or hasattr(fo, "fileno") + + +def get_tc_bits_value(user_value): + value = convert_number(user_value) / 1000 + return f"{value:03f}kbit" + + +def get_tc_bytes_value(user_value): + # Raw numbers are bytes in tc + return convert_number(user_value) + + +def get_tmp_dir(uniq): + return os.path.join(tempfile.mkdtemp(), uniq) + + +async def _async_get_exec_path(binary, cmdf, cache): + if isinstance(binary, str): + bins = [binary] + else: + bins = binary + for b in bins: + if b in cache: + return cache[b] + + rc, output, _ = await cmdf("which " + b, warn=False) + if not rc: + cache[b] = os.path.abspath(output.strip()) + return cache[b] + return None + + +def _get_exec_path(binary, cmdf, cache): + if isinstance(binary, str): + bins = [binary] + else: + bins = binary + for b in bins: + if b in cache: + return cache[b] + + rc, output, _ = cmdf("which " + b, warn=False) + if not rc: + cache[b] = os.path.abspath(output.strip()) + return cache[b] + return None + + +def get_event_loop(): + """Configure and return our non-thread using event loop. + + This function configures a new child watcher to not use threads. + Threads cannot be used when we inline unshare a PID namespace. + """ + policy = asyncio.get_event_loop_policy() + loop = policy.get_event_loop() + owatcher = policy.get_child_watcher() + logging.debug( + "event_loop_fixture: global policy %s, current loop %s, current watcher %s", + policy, + loop, + owatcher, + ) + + policy.set_child_watcher(None) + owatcher.close() + + try: + watcher = asyncio.PidfdChildWatcher() # pylint: disable=no-member + except Exception: + watcher = asyncio.SafeChildWatcher() + loop = policy.get_event_loop() + + logging.debug( + "event_loop_fixture: attaching new watcher %s to loop and setting in policy", + watcher, + ) + watcher.attach_loop(loop) + policy.set_child_watcher(watcher) + policy.set_event_loop(loop) + assert asyncio.get_event_loop_policy().get_child_watcher() is watcher + + return loop + + +class Commander: # pylint: disable=R0904 + """An object that can execute commands.""" + + tmux_wait_gen = 0 + + def __init__(self, name, logger=None, unet=None, **kwargs): + """Create a Commander. + + Args: + name: name of the commander object + logger: logger to use for logging commands a defualt is supplied if this + is None + unet: unet that owns this object, only used by Commander in run_in_window, + otherwise can be None. + """ + # del kwargs # deal with lint warning + # logging.warning("Commander: name %s kwargs %s", name, kwargs) + + self.name = name + self.unet = unet + self.deleting = False + self.last = None + self.exec_paths = {} + + if not logger: + logname = f"munet.{self.__class__.__name__.lower()}.{name}" + self.logger = logging.getLogger(logname) + self.logger.setLevel(logging.DEBUG) + else: + self.logger = logger + + super().__init__(**kwargs) + + @property + def is_vm(self): + return False + + @property + def is_container(self): + return False + + def set_logger(self, logfile): + self.logger = logging.getLogger(__name__ + ".commander." + self.name) + self.logger.setLevel(logging.DEBUG) + if isinstance(logfile, str): + handler = logging.FileHandler(logfile, mode="w") + else: + handler = logging.StreamHandler(logfile) + + fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format( + self.__class__.__name__, self.name + ) + handler.setFormatter(logging.Formatter(fmt=fmtstr)) + self.logger.addHandler(handler) + + def _get_pre_cmd(self, use_str, use_pty, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del use_pty + return "" if use_str else [] + + def __str__(self): + return f"{self.__class__.__name__}({self.name})" + + async def async_get_exec_path(self, binary): + """Return the full path to the binary executable. + + `binary` :: binary name or list of binary names + """ + return await _async_get_exec_path( + binary, self.async_cmd_status_nsonly, self.exec_paths + ) + + def get_exec_path(self, binary): + """Return the full path to the binary executable. + + `binary` :: binary name or list of binary names + """ + return _get_exec_path(binary, self.cmd_status_nsonly, self.exec_paths) + + def get_exec_path_host(self, binary): + """Return the full path to the binary executable. + + If the object is actually a derived class (e.g., a container) this method will + return the exec path for the native namespace rather than the container. The + path is the one which the other xxx_host methods will use. + + `binary` :: binary name or list of binary names + """ + return get_exec_path_host(binary) + + def test(self, flags, arg): + """Run test binary, with flags and arg.""" + test_path = self.get_exec_path(["test"]) + rc, _, _ = self.cmd_status([test_path, flags, arg], warn=False) + return not rc + + def test_nsonly(self, flags, arg): + """Run test binary, with flags and arg.""" + test_path = self.get_exec_path(["test"]) + rc, _, _ = self.cmd_status_nsonly([test_path, flags, arg], warn=False) + return not rc + + def path_exists(self, path): + """Check if path exists.""" + return self.test("-e", path) + + async def cleanup_pid(self, pid, kill_pid=None): + """Signal a pid to exit with escalating forcefulness.""" + if kill_pid is None: + kill_pid = pid + + for sn in (signal.SIGHUP, signal.SIGKILL): + self.logger.debug( + "%s: %s %s (wait %s)", self, signal.Signals(sn).name, kill_pid, pid + ) + + os.kill(kill_pid, sn) + + # No need to wait after this. + if sn == signal.SIGKILL: + return + + # try each signal, waiting 15 seconds for exit before advancing + wait_sec = 30 + self.logger.debug("%s: waiting %ss for pid to exit", self, wait_sec) + for _ in Timeout(wait_sec): + try: + status = os.waitpid(pid, os.WNOHANG) + if status == (0, 0): + await asyncio.sleep(0.1) + else: + self.logger.debug("pid %s exited status %s", pid, status) + return + except OSError as error: + if error.errno == errno.ECHILD: + self.logger.debug("%s: pid %s was reaped", self, pid) + else: + self.logger.warning( + "%s: error waiting on pid %s: %s", self, pid, error + ) + return + self.logger.debug("%s: timeout waiting on pid %s to exit", self, pid) + + def _get_sub_args(self, cmd_list, defaults, use_pty=False, ns_only=False, **kwargs): + """Returns pre-command, cmd, and default keyword args.""" + assert not isinstance(cmd_list, str) + + defaults["shell"] = False + pre_cmd_list = self._get_pre_cmd(False, use_pty, ns_only=ns_only, **kwargs) + cmd_list = [str(x) for x in cmd_list] + + # os_env = {k: v for k, v in os.environ.items() if k.startswith("MUNET")} + # env = {**os_env, **(kwargs["env"] if "env" in kwargs else {})} + env = {**(kwargs["env"] if "env" in kwargs else os.environ)} + if "MUNET_NODENAME" not in env: + env["MUNET_NODENAME"] = self.name + kwargs["env"] = env + + defaults.update(kwargs) + + return pre_cmd_list, cmd_list, defaults + + def _common_prologue(self, async_exec, method, cmd, skip_pre_cmd=False, **kwargs): + cmd_list = self._get_cmd_as_list(cmd) + if method == "_spawn": + defaults = { + "encoding": "utf-8", + "codec_errors": "ignore", + } + else: + defaults = { + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE, + } + if not async_exec: + defaults["encoding"] = "utf-8" + + pre_cmd_list, cmd_list, defaults = self._get_sub_args( + cmd_list, defaults, **kwargs + ) + + use_pty = kwargs.get("use_pty", False) + if method == "_spawn": + # spawn doesn't take "shell" keyword arg + if "shell" in defaults: + del defaults["shell"] + # this is required to avoid receiving a STOPPED signal on expect! + if not use_pty: + defaults["preexec_fn"] = os.setsid + defaults["env"]["PS1"] = "$ " + + if not detailed_cmd_logging: + pre_cmd_str = shlex.join(pre_cmd_list) if not skip_pre_cmd else "" + if "nsenter" in pre_cmd_str: + self.logger.debug('%s("%s")', method, shlex.join(cmd_list)) + elif pre_cmd_str: + self.logger.debug( + '%s("%s") [precmd: %s]', method, shlex.join(cmd_list), pre_cmd_str + ) + else: + self.logger.debug('%s("%s") [no precmd]', method, shlex.join(cmd_list)) + else: + self.logger.debug( + '%s: %s %s("%s", pre_cmd: "%s" use_pty: %s kwargs: %.120s)', + self, + "XXX" if method == "_spawn" else "", + method, + cmd_list, + pre_cmd_list if not skip_pre_cmd else "", + use_pty, + defaults, + ) + + actual_cmd_list = cmd_list if skip_pre_cmd else pre_cmd_list + cmd_list + return actual_cmd_list, defaults + + async def _async_popen(self, method, cmd, **kwargs): + """Create a new asynchronous subprocess.""" + acmd, kwargs = self._common_prologue(True, method, cmd, **kwargs) + p = await asyncio.create_subprocess_exec(*acmd, **kwargs) + return p, acmd + + def _popen(self, method, cmd, **kwargs): + """Create a subprocess.""" + acmd, kwargs = self._common_prologue(False, method, cmd, **kwargs) + p = subprocess.Popen(acmd, **kwargs) + return p, acmd + + def _fdspawn(self, fo, **kwargs): + defaults = {} + defaults.update(kwargs) + + if "echo" in defaults: + del defaults["echo"] + + if "encoding" not in defaults: + defaults["encoding"] = "utf-8" + if "codec_errors" not in defaults: + defaults["codec_errors"] = "ignore" + encoding = defaults["encoding"] + + self.logger.debug("%s: _fdspawn(%s, kwargs: %s)", self, fo, defaults) + + p = fdspawn(fo, **defaults) + + # We don't have TTY like conversions of LF to CRLF + p.crlf = os.linesep.encode(encoding) + + # we own the socket now detach the file descriptor to keep it from closing + if hasattr(fo, "detach"): + fo.detach() + + return p + + def _spawn(self, cmd, skip_pre_cmd=False, use_pty=False, echo=False, **kwargs): + logging.debug( + '%s: XXX _spawn: cmd "%s" skip_pre_cmd %s use_pty %s echo %s kwargs %s', + self, + cmd, + skip_pre_cmd, + use_pty, + echo, + kwargs, + ) + actual_cmd, defaults = self._common_prologue( + False, "_spawn", cmd, skip_pre_cmd=skip_pre_cmd, use_pty=use_pty, **kwargs + ) + + self.logger.debug( + '%s: XXX %s("%s", use_pty %s echo %s defaults: %s)', + self, + "PopenSpawn" if not use_pty else "pexpect.spawn", + actual_cmd, + use_pty, + echo, + defaults, + ) + + # We don't specify a timeout it defaults to 30s is that OK? + if not use_pty: + p = PopenSpawn(actual_cmd, **defaults) + else: + p = pexpect.spawn(actual_cmd[0], actual_cmd[1:], echo=echo, **defaults) + return p, actual_cmd + + def spawn( + self, + cmd, + spawned_re, + expects=(), + sends=(), + use_pty=False, + logfile=None, + logfile_read=None, + logfile_send=None, + trace=None, + **kwargs, + ): + """Create a spawned send/expect process. + + Args: + cmd: list of args to exec/popen with, or an already open socket + spawned_re: what to look for to know when done, `spawn` returns when seen + expects: a list of regex other than `spawned_re` to look for. Commonly, + "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. So e.g., the + username or password if thats what corresponding expect matched. Can + be the empty string to send nothing. + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + trace: if true then log send/expects + **kwargs - kwargs passed on the _spawn. + + Returns: + A pexpect process. + + Raises: + pexpect.TIMEOUT, pexpect.EOF as documented in `pexpect` + CalledProcessError if EOF is seen and `cmd` exited then + raises a CalledProcessError to indicate the failure. + """ + if is_file_like(cmd): + assert not use_pty + ac = "*socket*" + p = self._fdspawn(cmd, **kwargs) + else: + p, ac = self._spawn(cmd, use_pty=use_pty, **kwargs) + + if logfile: + p.logfile = logfile + if logfile_read: + p.logfile_read = logfile_read + if logfile_send: + p.logfile_send = logfile_send + + # for spawned shells (i.e., a direct command an not a console) + # this is wrong and will cause 2 prompts + if not use_pty: + # This isn't very nice looking + p.echo = False + if not is_file_like(cmd): + p.isalive = lambda: p.proc.poll() is None + if not hasattr(p, "close"): + p.close = p.wait + + # Do a quick check to see if we got the prompt right away, otherwise we may be + # at a console so we send a \n to re-issue the prompt + index = p.expect([spawned_re, pexpect.TIMEOUT, pexpect.EOF], timeout=0.1) + if index == 0: + assert p.match is not None + self.logger.debug( + "%s: got spawned_re quick: '%s' matching '%s'", + self, + p.match.group(0), + spawned_re, + ) + return p + + # Now send a CRLF to cause the prompt (or whatever else) to re-issue + p.send("\n") + try: + patterns = [spawned_re, *expects] + + self.logger.debug("%s: expecting: %s", self, patterns) + + while index := p.expect(patterns): + if trace: + assert p.match is not None + self.logger.debug( + "%s: got expect: '%s' matching %d '%s', sending '%s'", + self, + p.match.group(0), + index, + patterns[index], + sends[index - 1], + ) + if sends[index - 1]: + p.send(sends[index - 1]) + + self.logger.debug("%s: expecting again: %s", self, patterns) + self.logger.debug( + "%s: got spawned_re: '%s' matching '%s'", + self, + p.match.group(0), + spawned_re, + ) + return p + except pexpect.TIMEOUT: + self.logger.error( + "%s: TIMEOUT looking for spawned_re '%s' expect buffer so far:\n%s", + self, + spawned_re, + indent(p.buffer), + ) + raise + except pexpect.EOF as eoferr: + if p.isalive(): + raise + rc = p.status + before = indent(p.before) + error = CalledProcessError(rc, ac, output=before) + self.logger.error( + "%s: EOF looking for spawned_re '%s' before EOF:\n%s", + self, + spawned_re, + before, + ) + p.close() + raise error from eoferr + + async def shell_spawn( + self, + cmd, + prompt, + expects=(), + sends=(), + use_pty=False, + will_echo=False, + is_bourne=True, + init_newline=False, + **kwargs, + ): + """Create a shell REPL (read-eval-print-loop). + + Args: + cmd: shell and list of args to popen with, or an already open socket + prompt: the REPL prompt to look for, the function returns when seen + expects: a list of regex other than `spawned_re` to look for. Commonly, + "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. So e.g., the + username or password if thats what corresponding expect matched. Can + be the empty string to send nothing. + is_bourne: if False then do not modify shell prompt for internal + parser friently format, and do not expect continuation prompts. + init_newline: send an initial newline for non-bourne shell spawns, otherwise + expect the prompt simply from running the command + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + will_echo: bash is buggy in that it echo's to non-tty unlike any other + sh/ksh, set this value to true if running back + **kwargs - kwargs passed on the _spawn. + """ + combined_prompt = r"({}|{})".format(re.escape(PEXPECT_PROMPT), prompt) + + assert not is_file_like(cmd) or not use_pty + p = self.spawn( + cmd, + combined_prompt, + expects=expects, + sends=sends, + use_pty=use_pty, + echo=False, + **kwargs, + ) + assert not p.echo + + if not is_bourne: + if init_newline: + p.send("\n") + return ShellWrapper(p, prompt, will_echo=will_echo) + + ps1 = PEXPECT_PROMPT + ps2 = PEXPECT_CONTINUATION_PROMPT + + # Avoid problems when =/usr/bin/env= prints the values + ps1p = ps1[:5] + "${UNSET_V}" + ps1[5:] + ps2p = ps2[:5] + "${UNSET_V}" + ps2[5:] + + ps1 = re.escape(ps1) + ps2 = re.escape(ps2) + + extra = "PAGER=cat; export PAGER; TERM=dumb; unset HISTFILE; set +o emacs +o vi" + pchg = "PS1='{0}' PS2='{1}' PROMPT_COMMAND=''\n".format(ps1p, ps2p) + p.send(pchg) + return ShellWrapper(p, ps1, ps2, extra_init_cmd=extra, will_echo=will_echo) + + def popen(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + a subprocess.Popen object. + """ + return self._popen("popen", cmd, **kwargs)[0] + + def popen_nsonly(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + a subprocess.Popen object. + """ + return self._popen("popen_nsonly", cmd, ns_only=True, **kwargs)[0] + + async def async_popen(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `command` is a string then will be invoked with `bash -c`, otherwise + `command` is a list and will be invoked without a shell. + + Returns: + a asyncio.subprocess.Process object. + """ + p, _ = await self._async_popen("async_popen", cmd, **kwargs) + return p + + async def async_popen_nsonly(self, cmd, **kwargs): + """Creates a pipe with the given `command`. + + Args: + cmd: `str` or `list` of command to open a pipe with. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `command` is a string then will be invoked with `bash -c`, otherwise + `command` is a list and will be invoked without a shell. + + Returns: + a asyncio.subprocess.Process object. + """ + p, _ = await self._async_popen( + "async_popen_nsonly", cmd, ns_only=True, **kwargs + ) + return p + + async def async_cleanup_proc(self, p, pid=None): + """Terminate a process started with a popen call. + + Args: + p: return value from :py:`async_popen`, :py:`popen`, et al. + pid: pid to signal instead of p.pid, typically a child of + cmd_p == nsenter. + + Returns: + None on success, the ``p`` if multiple timeouts occur even + after a SIGKILL sent. + """ + if not p: + return None + + if p.returncode is not None: + if isinstance(p, subprocess.Popen): + o, e = p.communicate() + else: + o, e = await p.communicate() + self.logger.debug( + "%s: cmd_p already exited status: %s", self, proc_error(p, o, e) + ) + return None + + if pid is None: + pid = p.pid + + self.logger.debug("%s: terminate process: %s (pid %s)", self, proc_str(p), pid) + try: + # This will SIGHUP and wait a while then SIGKILL and return immediately + await self.cleanup_pid(p.pid, pid) + + # Wait another 2 seconds after the possible SIGKILL above for the + # parent nsenter to cleanup and exit + wait_secs = 2 + if isinstance(p, subprocess.Popen): + o, e = p.communicate(timeout=wait_secs) + else: + o, e = await asyncio.wait_for(p.communicate(), timeout=wait_secs) + self.logger.debug( + "%s: cmd_p exited after kill, status: %s", self, proc_error(p, o, e) + ) + except (asyncio.TimeoutError, subprocess.TimeoutExpired): + self.logger.warning("%s: SIGKILL timeout", self) + return p + except Exception as error: + self.logger.warning( + "%s: kill unexpected exception: %s", self, error, exc_info=True + ) + return p + return None + + @staticmethod + def _cmd_status_input(stdin): + pinput = None + if isinstance(stdin, (bytes, str)): + pinput = stdin + stdin = subprocess.PIPE + return pinput, stdin + + def _cmd_status_finish(self, p, c, ac, o, e, raises, warn): + rc = p.returncode + self.last = (rc, ac, c, o, e) + if not rc: + resstr = comm_result(rc, o, e) + if resstr: + self.logger.debug("%s", resstr) + else: + if warn: + self.logger.warning("%s: proc failed: %s", self, proc_error(p, o, e)) + if raises: + # error = Exception("stderr: {}".format(stderr)) + # This annoyingly doesnt' show stderr when printed normally + raise CalledProcessError(rc, ac, o, e) + return rc, o, e + + def _cmd_status(self, cmds, raises=False, warn=True, stdin=None, **kwargs): + """Execute a command.""" + pinput, stdin = Commander._cmd_status_input(stdin) + p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs) + o, e = p.communicate(pinput) + return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn) + + async def _async_cmd_status( + self, cmds, raises=False, warn=True, stdin=None, text=None, **kwargs + ): + """Execute a command.""" + pinput, stdin = Commander._cmd_status_input(stdin) + p, actual_cmd = await self._async_popen( + "async_cmd_status", cmds, stdin=stdin, **kwargs + ) + + if text is False: + encoding = None + else: + encoding = kwargs.get("encoding", "utf-8") + + if encoding is not None and isinstance(pinput, str): + pinput = pinput.encode(encoding) + o, e = await p.communicate(pinput) + if encoding is not None: + o = o.decode(encoding) if o is not None else o + e = e.decode(encoding) if e is not None else e + return self._cmd_status_finish(p, cmds, actual_cmd, o, e, raises, warn) + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If `cmd` is a string then the returned list uses bash and looks + like this: ["/bin/bash", "-c", cmd]. Some node types override + this function if they utilize a different shell as to return + a different list of values. + + Args: + cmd: list or string representing the command to execute. + + Returns: + list of commands to execute. + """ + if not isinstance(cmd, str): + cmds = cmd + else: + # Make sure the code doesn't think `cd` will work. + assert not re.match(r"cd(\s*|\s+(\S+))$", cmd) + cmds = ["/bin/bash", "-c", cmd] + return cmds + + def cmd_nostatus(self, cmd, **kwargs): + """Run given command returning output[s]. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + if "stderr" is in kwargs and not equal to subprocess.STDOUT, then + both stdout and stderr are returned, otherwise stderr is combined + with stdout and only stdout is returned. + """ + # + # This method serves as the basis for all derived sync cmd variations, so to + # override sync cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + + # XXX change this back to _cmd_status instead of cmd_status when we + # consolidate and cleanup the container overrides of *cmd_* functions + + cmds = cmd + if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT: + _, o, e = self.cmd_status(cmds, **kwargs) + return o, e + if "stderr" in kwargs: + del kwargs["stderr"] + _, o, _ = self.cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs) + return o + + def cmd_status(self, cmd, **kwargs): + """Run given command returning status and outputs. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + (status, output, error) are returned + status: the returncode of the command. + output: stdout as a string from the command. + error: stderr as a string from the command. + """ + # + # This method serves as the basis for all derived sync cmd variations, so to + # override sync cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + return self._cmd_status(cmd, **kwargs) + + def cmd_raises(self, cmd, **kwargs): + """Execute a command. Raise an exception on errors. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to Popen. If `command` is a string + then will be invoked with `bash -c`, otherwise `command` is a list and + will be invoked without a shell. + + Returns: + output: stdout as a string from the command. + + Raises: + CalledProcessError: on non-zero exit status + """ + _, stdout, _ = self._cmd_status(cmd, raises=True, **kwargs) + return stdout + + def cmd_nostatus_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return self.cmd_nostatus(cmd, ns_only=True, **kwargs) + + def cmd_status_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return self._cmd_status(cmd, ns_only=True, **kwargs) + + def cmd_raises_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + _, stdout, _ = self._cmd_status(cmd, raises=True, ns_only=True, **kwargs) + return stdout + + async def async_cmd_status(self, cmd, **kwargs): + """Run given command returning status and outputs. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + (status, output, error) are returned + status: the returncode of the command. + output: stdout as a string from the command. + error: stderr as a string from the command. + """ + # + # This method serves as the basis for all derived async cmd variations, so to + # override async cmd behavior simply override this function and *not* the other + # variations, unless you are changing only that variation's behavior + # + return await self._async_cmd_status(cmd, **kwargs) + + async def async_cmd_nostatus(self, cmd, **kwargs): + """Run given command returning output[s]. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + if "stderr" is in kwargs and not equal to subprocess.STDOUT, then + both stdout and stderr are returned, otherwise stderr is combined + with stdout and only stdout is returned. + + """ + # XXX change this back to _async_cmd_status instead of cmd_status when we + # consolidate and cleanup the container overrides of *cmd_* functions + + cmds = cmd + if "stderr" in kwargs and kwargs["stderr"] != subprocess.STDOUT: + _, o, e = await self._async_cmd_status(cmds, **kwargs) + return o, e + if "stderr" in kwargs: + del kwargs["stderr"] + _, o, _ = await self._async_cmd_status(cmds, stderr=subprocess.STDOUT, **kwargs) + return o + + async def async_cmd_raises(self, cmd, **kwargs): + """Execute a command. Raise an exception on errors. + + Args: + cmd: `str` or `list` of the command to execute. If a string is given + it is run using a shell, otherwise the list is executed directly + as the binary and arguments. + **kwargs: kwargs is eventually passed on to create_subprocess_exec. If + `cmd` is a string then will be invoked with `bash -c`, otherwise + `cmd` is a list and will be invoked without a shell. + + Returns: + output: stdout as a string from the command. + + Raises: + CalledProcessError: on non-zero exit status + """ + _, stdout, _ = await self._async_cmd_status(cmd, raises=True, **kwargs) + return stdout + + async def async_cmd_status_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + return await self._async_cmd_status(cmd, ns_only=True, **kwargs) + + async def async_cmd_raises_nsonly(self, cmd, **kwargs): + # Make sure the command runs on the host and not in any container. + _, stdout, _ = await self._async_cmd_status( + cmd, raises=True, ns_only=True, **kwargs + ) + return stdout + + def cmd_legacy(self, cmd, **kwargs): + """Execute a command with stdout and stderr joined, *IGNORES ERROR*.""" + defaults = {"stderr": subprocess.STDOUT} + defaults.update(kwargs) + _, stdout, _ = self._cmd_status(cmd, raises=False, **defaults) + return stdout + + # Run a command in a new window (gnome-terminal, screen, tmux, xterm) + def run_in_window( + self, + cmd, + wait_for=False, + background=False, + name=None, + title=None, + forcex=False, + new_window=False, + tmux_target=None, + ns_only=False, + ): + """Run a command in a new window (TMUX, Screen or XTerm). + + Args: + cmd: string to execute. + wait_for: True to wait for exit from command or `str` as channel neme to + signal on exit, otherwise False + background: Do not change focus to new window. + title: Title for new pane (tmux) or window (xterm). + name: Name of the new window (tmux) + forcex: Force use of X11. + new_window: Open new window (instead of pane) in TMUX + tmux_target: Target for tmux pane. + + Returns: + the pane/window identifier from TMUX (depends on `new_window`) + """ + channel = None + if isinstance(wait_for, str): + channel = wait_for + elif wait_for is True: + channel = "{}-wait-{}".format(our_pid, Commander.tmux_wait_gen) + Commander.tmux_wait_gen += 1 + + if forcex or ("TMUX" not in os.environ and "STY" not in os.environ): + root_level = False + else: + root_level = True + + # SUDO: The important thing to note is that with all these methods we are + # executing on the users windowing system, so even though we are normally + # running as root, we will not be when the command is dispatched. Also + # in the case of SCREEN and X11 we need to sudo *back* to the user as well + # This is also done by SSHRemote by defualt so we should *not* sudo back + # if we are SSHRemote. + + # XXX need to test ssh in screen + # XXX need to test ssh in Xterm + sudo_path = get_exec_path_host(["sudo"]) + # This first test case seems same as last but using list instead of string? + if self.is_vm and self.use_ssh: # pylint: disable=E1101 + if isinstance(cmd, str): + cmd = shlex.split(cmd) + cmd = ["/usr/bin/env", f"MUNET_NODENAME={self.name}"] + cmd + + # get the ssh cmd + cmd = self._get_pre_cmd(False, True, ns_only=ns_only) + [shlex.join(cmd)] + unet = self.unet # pylint: disable=E1101 + uns_cmd = unet._get_pre_cmd( # pylint: disable=W0212 + False, True, ns_only=True, root_level=root_level + ) + # get the nsenter for munet + nscmd = [ + sudo_path, + *uns_cmd, + *cmd, + ] + else: + # This is the command to execute to be inside the namespace. + # We are getting into trouble with quoting. + # Why aren't we passing in MUNET_RUNDIR? + cmd = f"/usr/bin/env MUNET_NODENAME={self.name} {cmd}" + # We need sudo b/c we are executing as the user inside the window system. + sudo_path = get_exec_path_host(["sudo"]) + nscmd = ( + sudo_path + + " " + + self._get_pre_cmd(True, True, ns_only=ns_only, root_level=root_level) + + " " + + cmd + ) + + if "TMUX" in os.environ and not forcex: + cmd = [get_exec_path_host("tmux")] + if new_window: + cmd.append("new-window") + cmd.append("-P") + if name: + cmd.append("-n") + cmd.append(name) + if tmux_target: + cmd.append("-t") + cmd.append(tmux_target) + else: + cmd.append("split-window") + cmd.append("-P") + cmd.append("-h") + if not tmux_target: + tmux_target = os.getenv("TMUX_PANE", "") + if background: + cmd.append("-d") + if tmux_target: + cmd.append("-t") + cmd.append(tmux_target) + + # nscmd is always added as single string argument + if not isinstance(nscmd, str): + nscmd = shlex.join(nscmd) + if title: + nscmd = f"printf '\033]2;{title}\033\\'; {nscmd}" + if channel: + nscmd = f'trap "tmux wait -S {channel}; exit 0" EXIT; {nscmd}' + cmd.append(nscmd) + + elif "STY" in os.environ and not forcex: + # wait for not supported in screen for now + channel = None + cmd = [get_exec_path_host("screen")] + if not os.path.exists( + "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"]) + ): + # XXX not appropriate for ssh + cmd = ["sudo", "-Eu", os.environ["SUDO_USER"]] + cmd + + if title: + cmd.append("-t") + cmd.append(title) + + if isinstance(nscmd, str): + nscmd = shlex.split(nscmd) + cmd.extend(nscmd) + elif "DISPLAY" in os.environ: + cmd = [get_exec_path_host("xterm")] + if "SUDO_USER" in os.environ: + # Do this b/c making things work as root with xauth seems hard + cmd = [ + get_exec_path_host("sudo"), + "-Eu", + os.environ["SUDO_USER"], + ] + cmd + if title: + cmd.append("-T") + cmd.append(title) + + cmd.append("-e") + if isinstance(nscmd, str): + cmd.extend(shlex.split(nscmd)) + else: + cmd.extend(nscmd) + + # if channel: + # return self.cmd_raises(cmd, skip_pre_cmd=True) + # else: + p = commander.popen( + cmd, + # skip_pre_cmd=True, + stdin=None, + shell=False, + ) + # We should reap the child and report the error then. + time_mod.sleep(2) + if p.poll() is not None: + self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p)) + return p + else: + self.logger.error( + "DISPLAY, STY, and TMUX not in environment, can't open window" + ) + raise Exception("Window requestd but TMUX, Screen and X11 not available") + + # pane_info = self.cmd_raises(cmd, skip_pre_cmd=True, ns_only=True).strip() + # We are prepending the nsenter command, so use unet.rootcmd + pane_info = commander.cmd_raises(cmd).strip() + + # Re-adjust the layout + if "TMUX" in os.environ: + cmd = [ + get_exec_path_host("tmux"), + "select-layout", + "-t", + pane_info if not tmux_target else tmux_target, + "tiled", + ] + commander.cmd_status(cmd) + + # Wait here if we weren't handed the channel to wait for + if channel and wait_for is True: + cmd = [get_exec_path_host("tmux"), "wait", channel] + # commander.cmd_status(cmd, skip_pre_cmd=True) + commander.cmd_status(cmd) + + return pane_info + + def delete(self): + """Calls self.async_delete within an exec loop.""" + asyncio.run(self.async_delete()) + + async def _async_delete(self): + """Delete this objects resources. + + This is the actual implementation of the resource cleanup, each class + should cleanup it's own resources, generally catching and reporting, + but not reraising any exceptions for it's own cleanup, then it should + invoke `super()._async_delete() without catching any exceptions raised + therein. See other examples in `base.py` or `native.py` + """ + self.logger.info("%s: deleted", self) + + async def async_delete(self): + """Delete the Commander (or derived object). + + The actual implementation for any class should be in `_async_delete` + new derived classes should look at the documentation for that function. + """ + try: + self.deleting = True + await self._async_delete() + except Exception as error: + self.logger.error("%s: error while deleting: %s", self, error) + + +class InterfaceMixin: + """A mixin class to support interface functionality.""" + + def __init__(self, *args, **kwargs): + # del kwargs # get rid of lint + # logging.warning("InterfaceMixin: args: %s kwargs: %s", args, kwargs) + + self._intf_addrs = defaultdict(lambda: [None, None]) + self.net_intfs = {} + self.next_intf_index = 0 + self.basename = "eth" + # self.basename = name + "-eth" + super().__init__(*args, **kwargs) + + @property + def intfs(self): + return sorted(self._intf_addrs.keys()) + + @property + def networks(self): + return sorted(self.net_intfs.keys()) + + def get_intf_addr(self, ifname, ipv6=False): + if ifname not in self._intf_addrs: + return None + return self._intf_addrs[ifname][bool(ipv6)] + + def set_intf_addr(self, ifname, ifaddr): + ifaddr = ipaddress.ip_interface(ifaddr) + self._intf_addrs[ifname][ifaddr.version == 6] = ifaddr + + def net_addr(self, netname, ipv6=False): + if netname not in self.net_intfs: + return None + return self.get_intf_addr(self.net_intfs[netname], ipv6=ipv6) + + def set_intf_basename(self, basename): + self.basename = basename + + def get_next_intf_name(self): + while True: + ifname = self.basename + str(self.next_intf_index) + self.next_intf_index += 1 + if ifname not in self._intf_addrs: + break + return ifname + + def get_ns_ifname(self, ifname): + """Return a namespace unique interface name. + + This function is primarily overriden by L3QemuVM, IOW by any class + that doesn't create it's own network namespace and will share that + with the root (unet) namespace. + + Args: + ifname: the interface name. + + Returns: + A name unique to the namespace of this object. By defualt the assumption + is the ifname is namespace unique. + """ + return ifname + + def register_interface(self, ifname): + if ifname not in self._intf_addrs: + self._intf_addrs[ifname] = [None, None] + + def register_network(self, netname, ifname): + if netname in self.net_intfs: + assert self.net_intfs[netname] == ifname + else: + self.net_intfs[netname] = ifname + + def get_linux_tc_args(self, ifname, config): + """Get interface constraints (jitter, delay, rate) for linux TC. + + The keys and their values are as follows: + + delay (int): number of microseconds + jitter (int): number of microseconds + jitter-correlation (float): % correlation to previous (default 10%) + loss (float): % of loss + loss-correlation (float): % correlation to previous (default 0%) + rate (int or str): bits per second, string allows for use of + {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000 + """ + del ifname # unused + + netem_args = "" + + def get_number(c, v, d=None): + if v not in c or c[v] is None: + return d + return convert_number(c[v]) + + delay = get_number(config, "delay") + if delay is not None: + netem_args += f" delay {delay}usec" + + jitter = get_number(config, "jitter") + if jitter is not None: + if not delay: + raise ValueError("jitter but no delay specified") + jitter_correlation = get_number(config, "jitter-correlation", 10) + netem_args += f" {jitter}usec {jitter_correlation}%" + + loss = get_number(config, "loss") + if loss is not None: + loss_correlation = get_number(config, "loss-correlation", 0) + if loss_correlation: + netem_args += f" loss {loss}% {loss_correlation}%" + else: + netem_args += f" loss {loss}%" + + if (o_rate := config.get("rate")) is None: + return netem_args, "" + + # + # This comment is not correct, but is trying to talk through/learn the + # machinery. + # + # tokens arrive at `rate` into token buffer. + # limit - number of bytes that can be queued waiting for tokens + # -or- + # latency - maximum amount of time a packet may sit in TBF queue + # + # So this just allows receiving faster than rate for latency amount of + # time, before dropping. + # + # latency = sizeofbucket(limit) / rate (peakrate?) + # + # 32kbit + # -------- = latency = 320ms + # 100kbps + # + # -but then- + # burst ([token] buffer) the largest number of instantaneous + # tokens available (i.e, bucket size). + + tbf_args = "" + DEFLIMIT = 1518 * 1 + DEFBURST = 1518 * 2 + try: + tc_rate = o_rate["rate"] + tc_rate = convert_number(tc_rate) + limit = convert_number(o_rate.get("limit", DEFLIMIT)) + burst = convert_number(o_rate.get("burst", DEFBURST)) + except (KeyError, TypeError): + tc_rate = convert_number(o_rate) + limit = convert_number(DEFLIMIT) + burst = convert_number(DEFBURST) + tbf_args += f" rate {tc_rate/1000}kbit" + if delay: + # give an extra 1/10 of buffer space to handle delay + tbf_args += f" limit {limit} burst {burst}" + else: + tbf_args += f" limit {limit} burst {burst}" + + return netem_args, tbf_args + + def set_intf_constraints(self, ifname, **constraints): + """Set interface outbound constraints. + + Set outbound constraints (jitter, delay, rate) for an interface. All arguments + may also be passed as a string and will be converted to numerical format. All + arguments are also optional. If not specified then that existing constraint will + be cleared. + + Args: + ifname: the name of the interface + delay (int): number of microseconds. + jitter (int): number of microseconds. + jitter-correlation (float): Percent correlation to previous (default 10%). + loss (float): Percent of loss. + loss-correlation (float): Percent correlation to previous (default 25%). + rate (int): bits per second, string allows for use of + {KMGTKiMiGiTi} prefixes "i" means K == 1024 otherwise K == 1000. + """ + nsifname = self.get_ns_ifname(ifname) + netem_args, tbf_args = self.get_linux_tc_args(nsifname, constraints) + count = 1 + selector = f"root handle {count}:" + if netem_args: + self.cmd_raises( + f"tc qdisc add dev {nsifname} {selector} netem {netem_args}" + ) + count += 1 + selector = f"parent {count-1}: handle {count}" + # Place rate limit after delay otherwise limit/burst too complex + if tbf_args: + self.cmd_raises(f"tc qdisc add dev {nsifname} {selector} tbf {tbf_args}") + + self.cmd_raises(f"tc qdisc show dev {nsifname}") + + +class LinuxNamespace(Commander, InterfaceMixin): + """A linux Namespace. + + An object that creates and executes commands in a linux namespace + """ + + def __init__( + self, + name, + net=True, + mount=True, + uts=True, + cgroup=False, + ipc=False, + pid=False, + time=False, + user=False, + unshare_inline=False, + set_hostname=True, + private_mounts=None, + **kwargs, + ): + """Create a new linux namespace. + + Args: + name: Internal name for the namespace. + net: Create network namespace. + mount: Create network namespace. + uts: Create UTS (hostname) namespace. + cgroup: Create cgroup namespace. + ipc: Create IPC namespace. + pid: Create PID namespace, also mounts new /proc. + time: Create time namespace. + user: Create user namespace, also keeps capabilities. + set_hostname: Set the hostname to `name`, uts must also be True. + private_mounts: List of strings of the form + "[/external/path:]/internal/path. If no external path is specified a + tmpfs is mounted on the internal path. Any paths specified are first + passed to `mkdir -p`. + unshare_inline: Unshare the process itself rather than using a proxy. + logger: Passed to superclass. + """ + # logging.warning("LinuxNamespace: name %s kwargs %s", name, kwargs) + + super().__init__(name, **kwargs) + + unet = self.unet + + self.logger.debug("%s: creating", self) + + self.cwd = os.path.abspath(os.getcwd()) + + self.nsflags = [] + self.ifnetns = {} + self.uflags = 0 + self.p_ns_fds = None + self.p_ns_fnames = None + self.pid_ns = False + self.init_pid = None + self.unshare_inline = unshare_inline + self.nsenter_fork = True + + # + # Collect the namespaces to unshare + # + if hasattr(self, "proc_path") and self.proc_path: # pylint: disable=no-member + pp = Path(self.proc_path) # pylint: disable=no-member + else: + pp = unet.proc_path if unet else Path("/proc") + pp = pp.joinpath("%P%", "ns") + + flags = "" + uflags = 0 + nslist = [] + nsflags = [] + if cgroup: + nselm = "cgroup" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "C" + uflags |= linux.CLONE_NEWCGROUP + if ipc: + nselm = "ipc" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "i" + uflags |= linux.CLONE_NEWIPC + if mount or pid: + # We need a new mount namespace for pid + nselm = "mnt" + nslist.append(nselm) + nsflags.append(f"--mount={pp / nselm}") + mount = True + flags += "m" + uflags |= linux.CLONE_NEWNS + if net: + nselm = "net" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + # if pid: + # os.system(f"touch /tmp/netns-{name}") + # cmd.append(f"--net=/tmp/netns-{name}") + # else: + flags += "n" + uflags |= linux.CLONE_NEWNET + if pid: + self.pid_ns = True + # We look for this b/c the unshare pid will share with /sibn/init + nselm = "pid_for_children" + nslist.append(nselm) + nsflags.append(f"--pid={pp / nselm}") + flags += "p" + uflags |= linux.CLONE_NEWPID + if time: + nselm = "time" + # XXX time_for_children? + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "T" + uflags |= linux.CLONE_NEWTIME + if user: + nselm = "user" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "U" + uflags |= linux.CLONE_NEWUSER + if uts: + nselm = "uts" + nslist.append(nselm) + nsflags.append(f"--{nselm}={pp / nselm}") + flags += "u" + uflags |= linux.CLONE_NEWUTS + + assert flags, "LinuxNamespace with no namespaces requested" + + # Should look path up using resources maybe... + mutini_path = get_our_script_path("mutini") + if not mutini_path: + mutini_path = get_our_script_path("mutini.py") + assert mutini_path + cmd = [mutini_path, f"--unshare-flags={flags}", "-v"] + fname = fsafe_name(self.name) + "-mutini.log" + fname = (unet or self).rundir.joinpath(fname) + stdout = open(fname, "w", encoding="utf-8") + stderr = subprocess.STDOUT + + # + # Save the current namespace info to compare against later + # + + if not unet: + nsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist} + else: + nsdict = { + x: os.readlink(f"{unet.proc_path}/{unet.pid}/ns/{x}") for x in nslist + } + + # + # (A) Basically we need to save the pid of the unshare call for nsenter. + # + # For `unet is not None` (node case) the level this exists at is based on wether + # unet is using a forking nsenter or not. So if unet.nsenter_fork == True then + # we need the child pid of the p.pid (child of pid returned to us), otherwise + # unet.nsenter_fork == False and we just use p.pid as it will be unshare after + # nsenter exec's it. + # + # For the `unet is None` (unet case) the unshare is at the top level or + # non-existent so we always save the returned p.pid. If we are unshare_inline we + # won't have a __pre_cmd but we can save our child_pid to kill later, otherwise + # we set unet.pid to None b/c there's literally nothing to do. + # + # --------------------------------------------------------------------------- + # Breakdown for nested (non-unet) namespace creation, and what PID + # to use for __pre_cmd nsenter use. + # --------------------------------------------------------------------------- + # + # tl;dr + # - for non-inline unshare: Use BBB with pid_for_children, unless none/none + # #then (AAA) returned + # - for inline unshare: use returned pid (AAA) with pid_for_children + # + # All commands use unet.popen to launch the unshare of mutini or cat. + # mutini for PID unshare, otherwise cat. AAA is the returned pid BBB is the + # child of the returned. + # + # Unshare Variant + # --------------- + # + # Here we are running mutini if we are creating new pid namespace workspace, + # cat otherwise. + # + # [PID+PID] pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA nsenter (forking, from unet) (in unet namespaces -pid) + # BBB - AAA AAA unshare --fork --kill-child (forking) + # CCC 1 BBB CCC mutini (non-forking since it is pid 1 in new namespace) + # + # Use BBB if we use pid_for_children, CCC for all + # + # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid + # tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA nsenter (forking) (in unet namespaces -pid) + # BBB AAA AAA unshare -> cat (from unshare non-forking) + # + # Use BBB for all + # + # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid + # tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA nsenter -> unshare --fork --kill-child + # BBB 1 AAA AAA mutini (non-forking since it is pid 1 in new namespace) + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree + # looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA nsenter -> unshare -> cat + # + # Use AAA for all, there's no BBB + # + # Inline-Unshare Variant + # ---------------------- + # + # For unshare_inline and new PID namespace we have unshared all but our PID + # namespace, but our children end up in the new namespace so the fork popen + # does is good enough. + # + # [PID+PID] pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child (forking) + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [PID+none] For non-pid workspace creation (but unet pid) we use cat and pid + # tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all + # + # [none+PID] For pid workspace creation (but NOT unet pid) we use mutini and pid + # tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none+none] For non-pid workspace and non-pid unet we use cat and pid tree + # looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all. + # + # + # --------------------------------------------------------------------------- + # Breakdown for unet namespace creation, and what PID to use for __pre_cmd + # --------------------------------------------------------------------------- + # + # tl;dr: save returned PID or nothing. + # - for non-inline unshare: Use AAA with pid_for_children (returned pid) + # - for inline unshare: no __precmd as the fork in popen is enough. + # + # Use commander to launch the unshare mutini/cat (for PID/none + # workspace PID) for non-inline case. AAA is the returned pid BBB is the child + # of the returned. + # + # Unshare Variant + # --------------- + # + # Here we are running mutini if we are creating new pid namespace workspace, + # cat otherwise. + # + # [PID] for unet pid creation pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA - uuu AAA unshare --fork --kill-child (forking) + # BBB 1 AAA BBB mutini + # + # Use AAA if we use pid_for_children, BBB for all + # + # [none] for unet non-pid, pid tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # AAA uuu AAA unshare -> cat + # + # Use AAA for all + # + # Inline-Unshare Variant + # ----------------------- + # + # For unshare_inline and new PID namespace we have unshared all but our PID + # namespace, but our children end up in the new namespace so the fork in popen + # does is good enough. + # + # [PID] for unet pid creation pid tree looks like this: + # + # PID NSPID PPID PGID + # uuu - N/A uuu main unet process + # AAA 1 uuu AAA mutini + # + # Save p / p.pid, but don't configure any nsenter, uneeded. + # + # Use nothing as the fork when doing a popen is enough to be in all the right + # namepsaces. + # + # [none] for unet non-pid, pid tree looks like this: + # + # PID PPID PGID + # uuu N/A uuu main unet process + # + # Nothing, no __pre_cmd. + # + # + + self.ppid = os.getppid() + self.unshare_inline = unshare_inline + if unshare_inline: + assert unet is None + self.uflags = uflags + # + # Open file descriptors for current namespaces for later resotration. + # + try: + kversion = [int(x) for x in platform.release().split("-")[0].split(".")] + kvok = kversion[0] > 5 or (kversion[0] == 5 and kversion[1] >= 8) + except ValueError: + kvok = False + if ( + not kvok + or sys.version_info[0] < 3 + or (sys.version_info[0] == 3 and sys.version_info[1] < 9) + ): + # get list of namespace file descriptors before we unshare + self.p_ns_fds = [] + self.p_ns_fnames = [] + tmpflags = uflags + for i in range(0, 64): + v = 1 << i + if (tmpflags & v) == 0: + continue + tmpflags &= ~v + if v in linux.namespace_files: + path = os.path.join("/proc/self", linux.namespace_files[v]) + if os.path.exists(path): + self.p_ns_fds.append(os.open(path, 0)) + self.p_ns_fnames.append(f"{path} -> {os.readlink(path)}") + self.logger.debug( + "%s: saving old namespace fd %s (%s)", + self, + self.p_ns_fnames[-1], + self.p_ns_fds[-1], + ) + if not tmpflags: + break + else: + self.p_ns_fds = None + self.p_ns_fnames = None + self.ppid_fd = linux.pidfd_open(self.ppid) + + self.logger.debug( + "%s: unshare to new namespaces %s", + self, + linux.clone_flag_string(uflags), + ) + + linux.unshare(uflags) + + if not pid: + p = None + self.pid = None + self.nsenter_fork = False + else: + # Need to fork to create the PID namespace, but we need to continue + # running from the parent so that things like pytest work. We'll execute + # a mutini process to manage the child init 1 duties. + # + # We (the parent pid) can no longer create threads, due to that being + # restricted by the kernel. See EINVAL in clone(2). + # + p = commander.popen( + [mutini_path, "-v"], + stdin=subprocess.PIPE, + stdout=stdout, + stderr=stderr, + text=True, + # new session/pgid so signals don't propagate + start_new_session=True, + shell=False, + ) + self.pid = p.pid + self.nsenter_fork = False + else: + # Using cat and a stdin PIPE is nice as it will exit when we do. However, + # we also detach it from the pgid so that signals do not propagate to it. + # This is b/c it would exit early (e.g., ^C) then, at least the main munet + # proc which has no other processes like frr daemons running, will take the + # main network namespace with it, which will remove the bridges and the + # veth pair (because the bridge side veth is deleted). + self.logger.debug("%s: creating namespace process: %s", self, cmd) + + # Use the parent unet process if we have one this will cause us to inherit + # the namespaces correctly even in the non-inline case. + parent = self.unet if self.unet else commander + + p = parent.popen( + cmd, + stdin=subprocess.PIPE, + stdout=stdout, + stderr=stderr, + text=True, + start_new_session=not unet, + shell=False, + ) + + # The pid number returned is in the global pid namespace. For unshare_inline + # this can be unfortunate b/c our /proc has been remounted in our new pid + # namespace and won't contain global pid namespace pids. To solve for this + # we get all the pid values for the process below. + + # See (A) above for when we need the child pid. + self.logger.debug("%s: namespace process: %s", self, proc_str(p)) + self.pid = p.pid + if unet and unet.nsenter_fork: + assert not unet.unshare_inline + # Need child pid of p.pid + pgrep = unet.rootcmd.get_exec_path("pgrep") + # a sing fork was done + child_pid = unet.rootcmd.cmd_raises([pgrep, "-o", "-P", str(p.pid)]) + self.pid = int(child_pid.strip()) + self.logger.debug("%s: child of namespace process: %s", self, pid) + + self.p = p + + # Let's always have a valid value. + if self.pid is None: + self.pid = our_pid + + # + # Let's find all our pids in the nested PID namespaces + # + if unet: + proc_path = unet.proc_path + else: + proc_path = self.proc_path if hasattr(self, "proc_path") else "/proc" + proc_path = f"{proc_path}/{self.pid}" + + pid_status = open(f"{proc_path}/status", "r", encoding="ascii").read() + m = re.search(r"\nNSpid:((?:\t[0-9]+)+)\n", pid_status) + self.pids = [int(x) for x in m.group(1).strip().split("\t")] + assert self.pids[0] == self.pid + + self.logger.debug("%s: namespace scoped pids: %s", self, self.pids) + + # ----------------------------------------------- + # Now let's wait until unshare completes it's job + # ----------------------------------------------- + timeout = Timeout(30) + if self.pid is not None and self.pid != our_pid: + while (not p or not p.poll()) and not timeout.is_expired(): + # check new namespace values against old (nsdict), unshare + # can actually take a bit to complete. + for fname in tuple(nslist): + # self.pid will be the global pid b/c we didn't unshare_inline + nspath = f"{proc_path}/ns/{fname}" + try: + nsf = os.readlink(nspath) + except OSError as error: + self.logger.debug( + "unswitched: error (ok) checking %s: %s", nspath, error + ) + continue + if nsdict[fname] != nsf: + self.logger.debug( + "switched: original %s current %s", nsdict[fname], nsf + ) + nslist.remove(fname) + elif unshare_inline: + logging.warning( + "unshare_inline not unshared %s == %s", nsdict[fname], nsf + ) + else: + self.logger.debug( + "unswitched: current %s elapsed: %s", nsf, timeout.elapsed() + ) + if not nslist: + self.logger.debug( + "all done waiting for unshare after %s", timeout.elapsed() + ) + break + + elapsed = int(timeout.elapsed()) + if elapsed <= 3: + time_mod.sleep(0.1) + else: + self.logger.info( + "%s: unshare taking more than %ss: %s", self, elapsed, nslist + ) + time_mod.sleep(1) + + if p is not None and p.poll(): + self.logger.error("%s: namespace process failed: %s", self, comm_error(p)) + assert p.poll() is None, "unshare failed" + + # + # Setup the pre-command to enter the target namespace from the running munet + # process using self.pid + # + + if pid: + nsenter_fork = True + elif unet and unet.nsenter_fork: + # if unet created a pid namespace we need to enter it since we aren't + # entering a child pid namespace we created for the node. Otherwise + # we have a /proc remounted under unet, but our process is running in + # the root pid namepsace + nselm = "pid_for_children" + nsflags.append(f"--pid={pp / nselm}") + nsenter_fork = True + else: + # We dont need a fork. + nsflags.append("-F") + nsenter_fork = False + + # Save nsenter values if running from root namespace + # we need this for the unshare_inline case when run externally (e.g., from + # within tmux server). + root_nsflags = [x.replace("%P%", str(self.pid)) for x in nsflags] + self.__root_base_pre_cmd = ["/usr/bin/nsenter", *root_nsflags] + self.__root_pre_cmd = list(self.__root_base_pre_cmd) + + if unshare_inline: + assert unet is None + # We have nothing to do here since our process is now in the correct + # namespaces and children will inherit from us, even the PID namespace will + # be corrent b/c commands are run by first forking. + self.nsenter_fork = False + self.nsflags = [] + self.__base_pre_cmd = [] + else: + # We will use nsenter + self.nsenter_fork = nsenter_fork + self.nsflags = nsflags + self.__base_pre_cmd = list(self.__root_base_pre_cmd) + + self.__pre_cmd = list(self.__base_pre_cmd) + + # Always mark new mount namespaces as recursive private + if mount: + # if self.p is None and not pid: + self.cmd_raises_nsonly("mount --make-rprivate /") + + # We need to remount the procfs for the new PID namespace, since we aren't using + # unshare(1) which does that for us. + if pid and unshare_inline: + assert mount + self.cmd_raises_nsonly("mount -t proc proc /proc") + + # We do not want cmd_status in child classes (e.g., container) for + # the remaining setup calls in this __init__ function. + + if net: + # Remount /sys to pickup any changes in the network, but keep root + # /sys/fs/cgroup. This pattern could be made generic and supported for any + # overlapping mounts + if mount: + tmpmnt = f"/tmp/cgm-{self.pid}" + self.cmd_status_nsonly( + f"mkdir {tmpmnt} && mount --rbind /sys/fs/cgroup {tmpmnt}" + ) + rc = o = e = None + for i in range(0, 10): + rc, o, e = self.cmd_status_nsonly( + "mount -t sysfs sysfs /sys", warn=False + ) + if not rc: + break + self.logger.debug( + "got error mounting new sysfs will retry: %s", + cmd_error(rc, o, e), + ) + time_mod.sleep(1) + else: + raise Exception(cmd_error(rc, o, e)) + + self.cmd_status_nsonly( + f"mount --move {tmpmnt} /sys/fs/cgroup && rmdir {tmpmnt}" + ) + + # Original micronet code + # self.cmd_raises_nsonly("mount -t sysfs sysfs /sys") + # self.cmd_raises_nsonly( + # "mount -o rw,nosuid,nodev,noexec,relatime " + # "-t cgroup2 cgroup /sys/fs/cgroup" + # ) + + # Set the hostname to the namespace name + if uts and set_hostname: + self.cmd_status_nsonly("hostname " + self.name) + nroot = subprocess.check_output("hostname") + if unshare_inline or (unet and unet.unshare_inline): + assert ( + root_hostname != nroot + ), f'hostname unchanged from "{nroot}" wanted "{self.name}"' + else: + # Assert that we didn't just change the host hostname + assert ( + root_hostname == nroot + ), f'root hostname "{root_hostname}" changed to "{nroot}"!' + + if private_mounts: + if isinstance(private_mounts, str): + private_mounts = [private_mounts] + for m in private_mounts: + s = m.split(":", 1) + if len(s) == 1: + self.tmpfs_mount(s[0]) + else: + self.bind_mount(s[0], s[1]) + + # this will fail if running inside the namespace with PID + if pid: + o = self.cmd_nostatus_nsonly("ls -l /proc/1/ns") + else: + o = self.cmd_nostatus_nsonly("ls -l /proc/self/ns") + + self.logger.debug("namespaces:\n %s", o) + + # will cache the path, which is important in delete to avoid running a shell + # which can hang during cleanup + self.ip_path = get_exec_path_host("ip") + if net: + self.cmd_status_nsonly([self.ip_path, "link", "set", "lo", "up"]) + + self.logger.info("%s: created", self) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del ns_only + del use_pty + pre_cmd = self.__root_pre_cmd if root_level else self.__pre_cmd + return shlex.join(pre_cmd) if use_str else list(pre_cmd) + + def tmpfs_mount(self, inner): + self.logger.debug("Mounting tmpfs on %s", inner) + self.cmd_raises("mkdir -p " + inner) + self.cmd_raises("mount -n -t tmpfs tmpfs " + inner) + + def bind_mount(self, outer, inner): + self.logger.debug("Bind mounting %s on %s", outer, inner) + if commander.test("-f", outer): + self.cmd_raises(f"mkdir -p {os.path.dirname(inner)} && touch {inner}") + else: + if not commander.test("-e", outer): + commander.cmd_raises_nsonly(f"mkdir -p {outer}") + self.cmd_raises(f"mkdir -p {inner}") + self.cmd_raises("mount --rbind {} {} ".format(outer, inner)) + + def add_netns(self, ns): + self.logger.debug("Adding network namespace %s", ns) + + if os.path.exists("/run/netns/{}".format(ns)): + self.logger.warning("%s: Removing existing nsspace %s", self, ns) + try: + self.delete_netns(ns) + except Exception as ex: + self.logger.warning( + "%s: Couldn't remove existing nsspace %s: %s", + self, + ns, + str(ex), + exc_info=True, + ) + self.cmd_raises_nsonly([self.ip_path, "netns", "add", ns]) + + def delete_netns(self, ns): + self.logger.debug("Deleting network namespace %s", ns) + self.cmd_raises_nsonly([self.ip_path, "netns", "delete", ns]) + + def set_intf_netns(self, intf, ns, up=False): + # In case a user hard-codes 1 thinking it "resets" + ns = str(ns) + if ns == "1": + ns = str(self.pid) + + self.logger.debug("Moving interface %s to namespace %s", intf, ns) + + cmd = [self.ip_path, "link", "set", intf, "netns", ns] + if up: + cmd.append("up") + self.intf_ip_cmd(intf, cmd) + if ns == str(self.pid): + # If we are returning then remove from dict + if intf in self.ifnetns: + del self.ifnetns[intf] + else: + self.ifnetns[intf] = ns + + def reset_intf_netns(self, intf): + self.logger.debug("Moving interface %s to default namespace", intf) + self.set_intf_netns(intf, str(self.pid)) + + def intf_ip_cmd(self, intf, cmd): + """Run an ip command, considering an interface's possible namespace.""" + if intf in self.ifnetns: + if isinstance(cmd, list): + assert cmd[0].endswith("ip") + cmd[1:1] = ["-n", self.ifnetns[intf]] + else: + assert cmd.startswith("ip ") + cmd = "ip -n " + self.ifnetns[intf] + cmd[2:] + self.cmd_raises_nsonly(cmd) + + def intf_tc_cmd(self, intf, cmd): + """Run a tc command, considering an interface's possible namespace.""" + if intf in self.ifnetns: + if isinstance(cmd, list): + assert cmd[0].endswith("tc") + cmd[1:1] = ["-n", self.ifnetns[intf]] + else: + assert cmd.startswith("tc ") + cmd = "tc -n " + self.ifnetns[intf] + cmd[2:] + self.cmd_raises_nsonly(cmd) + + def set_ns_cwd(self, cwd: Union[str, Path]): + """Common code for changing pre_cmd and pre_nscmd.""" + self.logger.debug("%s: new CWD %s", self, cwd) + self.__root_pre_cmd = self.__root_base_pre_cmd + ["--wd=" + str(cwd)] + if self.__pre_cmd: + self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)] + elif self.unshare_inline: + os.chdir(cwd) + + async def _async_delete(self): + if type(self) == LinuxNamespace: # pylint: disable=C0123 + self.logger.info("%s: deleting", self) + else: + self.logger.debug("%s: LinuxNamespace sub-class deleting", self) + + # Signal pid namespace proc to exit + if ( + (self.p is None or self.p.pid != self.pid) + and self.pid + and self.pid != our_pid + ): + self.logger.debug( + "cleanup pid on separate pid %s from proc pid %s", + self.pid, + self.p.pid if self.p else None, + ) + await self.cleanup_pid(self.pid) + + if self.p is not None: + self.logger.debug("cleanup proc pid %s", self.p.pid) + await self.async_cleanup_proc(self.p) + + # return to the previous namespace, need to do this in case anothe munet + # is being created, especially when it plans to inherit the parent's (host) + # namespace. + if self.uflags: + logging.info("restoring from inline unshare: cwd: %s", os.getcwd()) + # This only works in linux>=5.8 + if self.p_ns_fds is None: + self.logger.debug( + "%s: restoring namespaces %s", + self, + linux.clone_flag_string(self.uflags), + ) + # fd = linux.pidfd_open(self.ppid) + fd = self.ppid_fd + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, self.uflags) + except OSError as error: + self.logger.warning( + "%s: could not reset to old namespace fd %s: %s", + self, + fd, + error, + ) + if i == retry - 1: + raise + time_mod.sleep(1) + os.close(fd) + else: + while self.p_ns_fds: + fd = self.p_ns_fds.pop() + fname = self.p_ns_fnames.pop() + self.logger.debug( + "%s: restoring namespace from fd %s (%s)", self, fname, fd + ) + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, 0) + break + except OSError as error: + self.logger.warning( + "%s: could not reset to old namespace fd %s (%s): %s", + self, + fname, + fd, + error, + ) + if i == retry - 1: + raise + time_mod.sleep(1) + os.close(fd) + self.p_ns_fds = None + self.p_ns_fnames = None + logging.info("restored from unshare: cwd: %s", os.getcwd()) + + self.__root_base_pre_cmd = ["/bin/false"] + self.__base_pre_cmd = ["/bin/false"] + self.__root_pre_cmd = ["/bin/false"] + self.__pre_cmd = ["/bin/false"] + + await super()._async_delete() + + +class SharedNamespace(Commander): + """Share another namespace. + + An object that executes commands in an existing pid's linux namespace + """ + + def __init__(self, name, pid=None, nsflags=None, **kwargs): + """Share a linux namespace. + + Args: + name: Internal name for the namespace. + pid: PID of the process to share with. + nsflags: nsenter flags to pass to inherit namespaces from + """ + super().__init__(name, **kwargs) + + self.logger.debug("%s: Creating", self) + + self.cwd = os.path.abspath(os.getcwd()) + self.pid = pid if pid is not None else our_pid + + nsflags = (x.replace("%P%", str(self.pid)) for x in nsflags) if nsflags else [] + self.__base_pre_cmd = ["/usr/bin/nsenter", *nsflags] if nsflags else [] + self.__pre_cmd = self.__base_pre_cmd + self.ip_path = self.get_exec_path("ip") + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + """Get the pre-user-command values. + + The values returned here should be what is required to cause the user's command + to execute in the correct context (e.g., namespace, container, sshremote). + """ + del kwargs + del ns_only + del use_pty + assert not root_level + return shlex.join(self.__pre_cmd) if use_str else list(self.__pre_cmd) + + def set_ns_cwd(self, cwd: Union[str, Path]): + """Common code for changing pre_cmd and pre_nscmd.""" + self.logger.debug("%s: new CWD %s", self, cwd) + self.__pre_cmd = self.__base_pre_cmd + ["--wd=" + str(cwd)] + + +class Bridge(SharedNamespace, InterfaceMixin): + """A linux bridge.""" + + next_ord = 1 + + @classmethod + def _get_next_id(cls): + # Do not use `cls` here b/c that makes the variable class specific + n = Bridge.next_ord + Bridge.next_ord = n + 1 + return n + + def __init__(self, name=None, mtu=None, unet=None, **kwargs): + """Create a linux Bridge.""" + self.id = self._get_next_id() + if not name: + name = "br{}".format(self.id) + + unet_pid = our_pid if unet.pid is None else unet.pid + + super().__init__(name, pid=unet_pid, nsflags=unet.nsflags, unet=unet, **kwargs) + + self.set_intf_basename(self.name + "-e") + + self.mtu = mtu + + self.logger.debug("Bridge: Creating") + + assert len(self.name) <= 16 # Make sure fits in IFNAMSIZE + self.cmd_raises(f"ip link delete {name} || true") + self.cmd_raises(f"ip link add {name} type bridge") + if self.mtu: + self.cmd_raises(f"ip link set {name} mtu {self.mtu}") + self.cmd_raises(f"ip link set {name} up") + + self.logger.debug("%s: Created, Running", self) + + def get_ifname(self, netname): + return self.net_intfs[netname] if netname in self.net_intfs else None + + async def _async_delete(self): + """Stop the bridge (i.e., delete the linux resources).""" + if type(self) == Bridge: # pylint: disable=C0123 + self.logger.info("%s: deleting", self) + else: + self.logger.debug("%s: Bridge sub-class deleting", self) + + rc, o, e = await self.async_cmd_status( + [self.ip_path, "link", "show", self.name], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if not rc: + rc, o, e = await self.async_cmd_status( + [self.ip_path, "link", "delete", self.name], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if rc: + self.logger.error( + "%s: error deleting bridge %s: %s", + self, + self.name, + cmd_error(rc, o, e), + ) + await super()._async_delete() + + +class BaseMunet(LinuxNamespace): + """Munet.""" + + def __init__( + self, + name="munet", + isolated=True, + pid=True, + rundir=None, + pytestconfig=None, + **kwargs, + ): + """Create a Munet.""" + # logging.warning("BaseMunet: %s", name) + + self.hosts = {} + self.switches = {} + self.links = {} + self.macs = {} + self.rmacs = {} + self.isolated = isolated + + self.cli_server = None + self.cli_sockpath = None + self.cli_histfile = None + self.cli_in_window_cmds = {} + self.cli_run_cmds = {} + + # + # We need a directory for various files + # + if not rundir: + rundir = "/tmp/munet" + self.rundir = Path(rundir) + + # + # Always having a global /proc is required to keep things from exploding + # complexity with nested new pid namespaces.. + # + if pid: + self.proc_path = Path(tempfile.mkdtemp(suffix="-proc", prefix="mu-")) + logging.debug("%s: mounting /proc on proc_path %s", name, self.proc_path) + linux.mount("proc", str(self.proc_path), "proc") + else: + self.proc_path = Path("/proc") + + # + # Now create a root level commander that works regardless of whether we inline + # unshare or not. Save it in the global variable as well + # + + if not self.isolated: + self.rootcmd = commander + elif not pid: + nsflags = ( + f"--mount={self.proc_path / '1/ns/mnt'}", + f"--net={self.proc_path / '1/ns/net'}", + f"--uts={self.proc_path / '1/ns/uts'}", + # f"--ipc={self.proc_path / '1/ns/ipc'}", + # f"--time={self.proc_path / '1/ns/time'}", + # f"--cgroup={self.proc_path / '1/ns/cgroup'}", + ) + self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags) + else: + # XXX user + nsflags = ( + # XXX Backing up PID namespace just doesn't work. + # f"--pid={self.proc_path / '1/ns/pid_for_children'}", + f"--mount={self.proc_path / '1/ns/mnt'}", + f"--net={self.proc_path / '1/ns/net'}", + f"--uts={self.proc_path / '1/ns/uts'}", + # f"--ipc={self.proc_path / '1/ns/ipc'}", + # f"--time={self.proc_path / '1/ns/time'}", + # f"--cgroup={self.proc_path / '1/ns/cgroup'}", + ) + self.rootcmd = SharedNamespace("root", pid=1, nsflags=nsflags) + global roothost # pylint: disable=global-statement + + roothost = self.rootcmd + + self.cfgopt = munet_config.ConfigOptionsProxy(pytestconfig) + + super().__init__( + name, mount=True, net=isolated, uts=isolated, pid=pid, unet=None, **kwargs + ) + + # This allows us to cleanup any leftover running munet's + if "MUNET_PID" in os.environ: + if os.environ["MUNET_PID"] != str(our_pid): + logging.error( + "Found env MUNET_PID != our pid %s, instead its %s, changing", + our_pid, + os.environ["MUNET_PID"], + ) + os.environ["MUNET_PID"] = str(our_pid) + + # this is for testing purposes do not use + if not BaseMunet.g_unet: + BaseMunet.g_unet = self + + self.logger.debug("%s: Creating", self) + + def __getitem__(self, key): + if key in self.switches: + return self.switches[key] + return self.hosts[key] + + def add_host(self, name, cls=LinuxNamespace, **kwargs): + """Add a host to munet.""" + self.logger.debug("%s: add_host %s(%s)", self, cls.__name__, name) + + self.hosts[name] = cls(name, unet=self, **kwargs) + + # Create a new mounted FS for tracking nested network namespaces creatd by the + # user with `ip netns add` + + # XXX why is this failing with podman??? + # self.hosts[name].tmpfs_mount("/run/netns") + + return self.hosts[name] + + def add_link(self, node1, node2, if1, if2, mtu=None, **intf_constraints): + """Add a link between switch and node or 2 nodes. + + If constraints are given they are applied to each endpoint. See + `InterfaceMixin::set_intf_constraints()` for more info. + """ + isp2p = False + + try: + name1 = node1.name + except AttributeError: + if node1 in self.switches: + node1 = self.switches[node1] + else: + node1 = self.hosts[node1] + name1 = node1.name + + try: + name2 = node2.name + except AttributeError: + if node2 in self.switches: + node2 = self.switches[node2] + else: + node2 = self.hosts[node2] + name2 = node2.name + + if name1 in self.switches: + assert name2 in self.hosts + elif name2 in self.switches: + assert name1 in self.hosts + name1, name2 = name2, name1 + if1, if2 = if2, if1 + else: + # p2p link + assert name1 in self.hosts + assert name2 in self.hosts + isp2p = True + + lname = "{}:{}-{}:{}".format(name1, if1, name2, if2) + self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "") + self.links[lname] = (name1, if1, name2, if2) + + # And create the veth now. + if isp2p: + lhost, rhost = self.hosts[name1], self.hosts[name2] + lifname = "i1{:x}".format(lhost.pid) + + # Done at root level + nsif1 = lhost.get_ns_ifname(if1) + nsif2 = rhost.get_ns_ifname(if2) + + # Use pids[-1] to get the unet scoped pid for hosts + self.cmd_raises_nsonly( + f"ip link add {lifname} type veth peer name {nsif2}" + f" netns {rhost.pids[-1]}" + ) + self.cmd_raises_nsonly(f"ip link set {lifname} netns {lhost.pids[-1]}") + + lhost.cmd_raises_nsonly("ip link set {} name {}".format(lifname, nsif1)) + if mtu: + lhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu)) + lhost.cmd_raises_nsonly("ip link set {} up".format(nsif1)) + lhost.register_interface(if1) + + if mtu: + rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu)) + rhost.cmd_raises_nsonly("ip link set {} up".format(nsif2)) + rhost.register_interface(if2) + else: + switch = self.switches[name1] + rhost = self.hosts[name2] + + nsif1 = switch.get_ns_ifname(if1) + nsif2 = rhost.get_ns_ifname(if2) + + if mtu is None: + mtu = switch.mtu + + if len(nsif1) > 16: + self.logger.error('"%s" len %s > 16', nsif1, len(nsif1)) + elif len(nsif2) > 16: + self.logger.error('"%s" len %s > 16', nsif2, len(nsif2)) + assert len(nsif1) <= 16 and len(nsif2) <= 16 # Make sure fits in IFNAMSIZE + + self.logger.debug("%s: Creating veth pair for link %s", self, lname) + + # Use pids[-1] to get the unet scoped pid for hosts + # switch is already in our namespace so nothing to convert. + self.cmd_raises_nsonly( + f"ip link add {nsif1} type veth peer name {nsif2}" + f" netns {rhost.pids[-1]}" + ) + + if mtu: + # if switch.mtu: + # # the switch interface should match the switch config + # switch.cmd_raises_nsonly( + # "ip link set {} mtu {}".format(if1, switch.mtu) + # ) + switch.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif1, mtu)) + rhost.cmd_raises_nsonly("ip link set {} mtu {}".format(nsif2, mtu)) + + switch.register_interface(if1) + rhost.register_interface(if2) + rhost.register_network(switch.name, if2) + + switch.cmd_raises_nsonly(f"ip link set {nsif1} master {switch.name}") + + switch.cmd_raises_nsonly(f"ip link set {nsif1} up") + rhost.cmd_raises_nsonly(f"ip link set {nsif2} up") + + # Cache the MAC values, and reverse mapping + self.get_mac(name1, nsif1) + self.get_mac(name2, nsif2) + + # Setup interface constraints if provided + if intf_constraints: + node1.set_intf_constraints(if1, **intf_constraints) + node2.set_intf_constraints(if2, **intf_constraints) + + def add_switch(self, name, cls=Bridge, **kwargs): + """Add a switch to munet.""" + self.logger.debug("%s: add_switch %s(%s)", self, cls.__name__, name) + self.switches[name] = cls(name, unet=self, **kwargs) + return self.switches[name] + + def get_mac(self, name, ifname): + if name in self.hosts: + dev = self.hosts[name] + else: + dev = self.switches[name] + + nsifname = self.get_ns_ifname(ifname) + + if (name, ifname) not in self.macs: + _, output, _ = dev.cmd_status_nsonly("ip -o link show " + nsifname) + m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output) + mac = m.group(2) + self.macs[(name, ifname)] = mac + self.rmacs[mac] = (name, ifname) + + return self.macs[(name, ifname)] + + async def _delete_link(self, lname): + rname, rif = self.links[lname][2:4] + host = self.hosts[rname] + nsrif = host.get_ns_ifname(rif) + + self.logger.debug("%s: Deleting veth pair for link %s", self, lname) + rc, o, e = await host.async_cmd_status_nsonly( + [self.ip_path, "link", "delete", nsrif], + stdin=subprocess.DEVNULL, + start_new_session=True, + warn=False, + ) + if rc: + self.logger.error("Err del veth pair %s: %s", lname, cmd_error(rc, o, e)) + + async def _delete_links(self): + # for x in self.links: + # await self._delete_link(x) + return await asyncio.gather(*[self._delete_link(x) for x in self.links]) + + async def _async_delete(self): + """Delete the munet topology.""" + # logger = self.logger if False else logging + logger = self.logger + if type(self) == BaseMunet: # pylint: disable=C0123 + logger.info("%s: deleting.", self) + else: + logger.debug("%s: BaseMunet sub-class deleting.", self) + + logger.debug("Deleting links") + try: + await self._delete_links() + except Exception as error: + logger.error("%s: error deleting links: %s", self, error, exc_info=True) + + logger.debug("Deleting hosts and bridges") + try: + # Delete hosts and switches, wait for them all to complete + # even if there is an exception. + htask = [x.async_delete() for x in self.hosts.values()] + stask = [x.async_delete() for x in self.switches.values()] + await asyncio.gather(*htask, *stask, return_exceptions=True) + except Exception as error: + logger.error( + "%s: error deleting hosts and switches: %s", self, error, exc_info=True + ) + + self.links = {} + self.hosts = {} + self.switches = {} + + try: + if self.cli_server: + self.cli_server.cancel() + self.cli_server = None + if self.cli_sockpath: + await self.async_cmd_status( + "rm -rf " + os.path.dirname(self.cli_sockpath) + ) + self.cli_sockpath = None + except Exception as error: + logger.error( + "%s: error cli server or sockpaths: %s", self, error, exc_info=True + ) + + try: + if self.cli_histfile: + readline.write_history_file(self.cli_histfile) + self.cli_histfile = None + except Exception as error: + logger.error( + "%s: error saving history file: %s", self, error, exc_info=True + ) + + # XXX for some reason setns during the delete is changing our dir to /. + cwd = os.getcwd() + + try: + await super()._async_delete() + except Exception as error: + logger.error( + "%s: error deleting parent classes: %s", self, error, exc_info=True + ) + os.chdir(cwd) + + try: + if self.proc_path and str(self.proc_path) != "/proc": + logger.debug("%s: umount, remove proc_path %s", self, self.proc_path) + linux.umount(str(self.proc_path), 0) + os.rmdir(self.proc_path) + except Exception as error: + logger.warning( + "%s: error umount and removing proc_path %s: %s", + self, + self.proc_path, + error, + exc_info=True, + ) + try: + linux.umount(str(self.proc_path), linux.MNT_DETACH) + except Exception as error2: + logger.error( + "%s: error umount with detach proc_path %s: %s", + self, + self.proc_path, + error2, + exc_info=True, + ) + + if BaseMunet.g_unet == self: + BaseMunet.g_unet = None + + +BaseMunet.g_unet = None + +if True: # pylint: disable=using-constant-test + + class ShellWrapper: + """A Read-Execute-Print-Loop (REPL) interface. + + A newline or prompt changing command should be sent to the + spawned child prior to creation as the `prompt` will be `expect`ed + """ + + def __init__( + self, + spawn, + prompt, + continuation_prompt=None, + extra_init_cmd=None, + will_echo=False, + escape_ansi=False, + ): + self.echo = will_echo + self.escape = ( + re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]") if escape_ansi else None + ) + + logging.debug( + 'ShellWraper: XXX prompt "%s" will_echo %s child.echo %s', + prompt, + will_echo, + spawn.echo, + ) + + self.child = spawn + if self.child.echo: + logging.info("Setting child to echo") + self.child.setecho(False) + self.child.waitnoecho() + assert not self.child.echo + + self.prompt = prompt + self.cont_prompt = continuation_prompt + + # Use expect_exact if we can as it should be faster + self.expects = [prompt] + if re.escape(prompt) == prompt and hasattr(self.child, "expect_exact"): + self._expectf = self.child.expect_exact + else: + self._expectf = self.child.expect + if continuation_prompt: + self.expects.append(continuation_prompt) + if re.escape(continuation_prompt) != continuation_prompt: + self._expectf = self.child.expect + + if extra_init_cmd: + self.expect_prompt() + self.child.sendline(extra_init_cmd) + self.expect_prompt() + + def expect_prompt(self, timeout=-1): + return self._expectf(self.expects, timeout=timeout) + + def run_command(self, command, timeout=-1): + """Pexpect REPLWrapper compatible run_command. + + This will split `command` into lines and feed each one to the shell. + + Args: + command: string of commands separated by newlines, a trailing + newline will cause and empty line to be sent. + timeout: pexpect timeout value. + """ + lines = command.splitlines() + if command[-1] == "\n": + lines.append("") + output = "" + index = 0 + for line in lines: + self.child.sendline(line) + index = self.expect_prompt(timeout=timeout) + output += self.child.before + + if index: + if hasattr(self.child, "kill"): + self.child.kill(signal.SIGINT) + else: + self.child.send("\x03") + self.expect_prompt(timeout=30 if self.child.timeout is None else -1) + raise ValueError("Continuation prompt found at end of commands") + + if self.escape: + output = self.escape.sub("", output) + + return output + + def cmd_nostatus(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + (strip/cleaned \r) output + """ + output = self.run_command(cmd, timeout) + output = output.replace("\r\n", "\n") + if self.echo: + # remove the command + idx = output.find(cmd) + if idx == -1: + logging.warning( + "Didn't find command ('%s') in expected output ('%s')", + cmd, + output, + ) + else: + # Remove up to and including the command from the output stream + output = output[idx + len(cmd) :] + + return output.replace("\r", "").strip() + + def cmd_status(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + status and (strip/cleaned \r) output + """ + # Run the command getting the output + output = self.cmd_nostatus(cmd, timeout) + + # Now get the status + scmd = "echo $?" + rcstr = self.run_command(scmd) + rcstr = rcstr.replace("\r\n", "\n") + if self.echo: + # remove the command + idx = rcstr.find(scmd) + if idx == -1: + if self.echo: + logging.warning( + "Didn't find status ('%s') in expected output ('%s')", + scmd, + rcstr, + ) + try: + rc = int(rcstr) + except Exception: + rc = 255 + else: + rcstr = rcstr[idx + len(scmd) :].strip() + try: + rc = int(rcstr) + except ValueError as error: + logging.error( + "%s: error with expected status output: %s: %s", + self, + error, + rcstr, + exc_info=True, + ) + rc = 255 + return rc, output + + def cmd_raises(self, cmd, timeout=-1): + r"""Execute a shell command. + + Returns: + (strip/cleaned \r) ouptut + + Raises: + CalledProcessError: on non-zero exit status + """ + rc, output = self.cmd_status(cmd, timeout) + if rc: + raise CalledProcessError(rc, cmd, output) + return output + + +# --------------------------- +# Root level utility function +# --------------------------- + + +def get_exec_path(binary): + return commander.get_exec_path(binary) + + +def get_exec_path_host(binary): + return commander.get_exec_path(binary) + + +def get_our_script_path(script): + # would be nice to find this w/o using a path lookup + sdir = os.path.dirname(os.path.abspath(__file__)) + spath = os.path.join(sdir, script) + if os.path.exists(spath): + return spath + return get_exec_path(script) + + +commander = Commander("munet") +roothost = None diff --git a/tests/topotests/munet/cleanup.py b/tests/topotests/munet/cleanup.py new file mode 100644 index 000000000000..c641cda68596 --- /dev/null +++ b/tests/topotests/munet/cleanup.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""Provides functionality to cleanup processes on posix systems.""" +import glob +import logging +import os +import signal + + +def get_pids_with_env(has_var, has_val=None): + result = {} + for pidenv in glob.iglob("/proc/*/environ"): + pid = pidenv.split("/")[2] + try: + with open(pidenv, "rb") as rfb: + envlist = [ + x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0") + ] + envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist] + envdict = dict(envlist) + if has_var not in envdict: + continue + if has_val is None: + result[pid] = envdict + elif envdict[has_var] == str(has_val): + result[pid] = envdict + except Exception: + # E.g., process exited and files are gone + pass + return result + + +def _kill_piddict(pids_by_upid, sig): + ourpid = str(os.getpid()) + for upid, pids in pids_by_upid: + logging.info("Sending %s to (%s) of munet pid %s", sig, ", ".join(pids), upid) + for pid in pids: + try: + if pid != ourpid: + cmdline = open(f"/proc/{pid}/cmdline", "r", encoding="ascii").read() + cmdline = cmdline.replace("\x00", " ") + logging.info("killing proc %s (%s)", pid, cmdline) + os.kill(int(pid), sig) + except Exception: + pass + + +def _get_our_pids(): + ourpid = str(os.getpid()) + piddict = get_pids_with_env("MUNET_PID", ourpid) + pids = [x for x in piddict if x != ourpid] + if pids: + return {ourpid: pids} + return {} + + +def _get_other_pids(): + piddict = get_pids_with_env("MUNET_PID") + unet_pids = {d["MUNET_PID"] for d in piddict.values()} + pids_by_upid = {p: set() for p in unet_pids} + for pid, envdict in piddict.items(): + unet_pid = envdict["MUNET_PID"] + pids_by_upid[unet_pid].add(pid) + # Filter out any child pid sets whos munet pid is still running + return {x: y for x, y in pids_by_upid.items() if x not in y} + + +def _get_pids_by_upid(ours): + if ours: + return _get_our_pids() + return _get_other_pids() + + +def _cleanup_pids(ours): + pids_by_upid = _get_pids_by_upid(ours).items() + if not pids_by_upid: + return + + t = "current" if ours else "previous" + logging.info("Reaping %s munet processes", t) + + # _kill_piddict(pids_by_upid, signal.SIGTERM) + + # # Give them 5 second to exit cleanly + # logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids") + # for _ in range(0, 5): + # pids_by_upid = _get_pids_by_upid(ours).items() + # if not pids_by_upid: + # return + # time.sleep(1) + + pids_by_upid = _get_pids_by_upid(ours).items() + _kill_piddict(pids_by_upid, signal.SIGKILL) + + +def cleanup_current(): + """Attempt to cleanup preview runs. + + Currently this only scans for old processes. + """ + _cleanup_pids(True) + + +def cleanup_previous(): + """Attempt to cleanup preview runs. + + Currently this only scans for old processes. + """ + _cleanup_pids(False) diff --git a/tests/topotests/munet/cli.py b/tests/topotests/munet/cli.py new file mode 100644 index 000000000000..f631073bb18a --- /dev/null +++ b/tests/topotests/munet/cli.py @@ -0,0 +1,962 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# July 24 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements a CLI.""" +import argparse +import asyncio +import functools +import logging +import multiprocessing +import os +import pty +import re +import readline +import select +import shlex +import socket +import subprocess +import sys +import tempfile +import termios +import tty + + +try: + from . import linux + from .config import list_to_dict_with_key +except ImportError: + # We cannot use relative imports and still run this module directly as a script, and + # there are some use cases where we want to run this file as a script. + sys.path.append(os.path.dirname(os.path.realpath(__file__))) + import linux + + from config import list_to_dict_with_key + + +ENDMARKER = b"\x00END\x00" + +logger = logging.getLogger(__name__) + + +def lineiter(sock): + s = "" + while True: + sb = sock.recv(256) + if not sb: + return + + s += sb.decode("utf-8") + i = s.find("\n") + if i != -1: + yield s[:i] + s = s[i + 1 :] + + +# Would be nice to convert to async, but really not needed as used +def spawn(unet, host, cmd, iow, ns_only): + if sys.stdin.isatty(): + old_tty = termios.tcgetattr(sys.stdin) + tty.setraw(sys.stdin.fileno()) + + try: + master_fd, slave_fd = pty.openpty() + + ns = unet.hosts[host] if host and host != unet else unet + popenf = ns.popen_nsonly if ns_only else ns.popen + + # use os.setsid() make it run in a new process group, or bash job + # control will not be enabled + p = popenf( + cmd, + # _common_prologue, later in call chain, only does this for use_pty == False + preexec_fn=os.setsid, + stdin=slave_fd, + stdout=slave_fd, + stderr=slave_fd, + universal_newlines=True, + use_pty=True, + # XXX this is actually implementing "run on host" for real + # skip_pre_cmd=ns_only, + ) + iow.write("\r") + iow.flush() + + while p.poll() is None: + r, _, _ = select.select([sys.stdin, master_fd], [], [], 0.25) + if sys.stdin in r: + d = os.read(sys.stdin.fileno(), 10240) + os.write(master_fd, d) + elif master_fd in r: + o = os.read(master_fd, 10240) + if o: + iow.write(o.decode("utf-8", "ignore")) + iow.flush() + finally: + # restore tty settings back + if sys.stdin.isatty(): + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty) + + +def is_host_regex(restr): + return len(restr) > 2 and restr[0] == "/" and restr[-1] == "/" + + +def get_host_regex(restr): + if len(restr) < 3 or restr[0] != "/" or restr[-1] != "/": + return None + return re.compile(restr[1:-1]) + + +def host_in(restr, names): + """Determine if matcher is a regex that matches one of names.""" + if not (regexp := get_host_regex(restr)): + return restr in names + for name in names: + if regexp.fullmatch(name): + return True + return False + + +def expand_host(restr, names): + """Expand name or regexp into list of hosts.""" + hosts = [] + regexp = get_host_regex(restr) + if not regexp: + assert restr in names + hosts.append(restr) + else: + for name in names: + if regexp.fullmatch(name): + hosts.append(name) + return sorted(hosts) + + +def expand_hosts(restrs, names): + """Expand list of host names or regex into list of hosts.""" + hosts = [] + for restr in restrs: + hosts += expand_host(restr, names) + return sorted(hosts) + + +def host_cmd_split(unet, line, toplevel): + all_hosts = set(unet.hosts) + csplit = line.split() + i = 0 + banner = False + for i, e in enumerate(csplit): + if is_re := is_host_regex(e): + banner = True + if not host_in(e, all_hosts): + if not is_re: + break + else: + i += 1 + + if i == 0 and csplit and csplit[0] == "*": + hosts = sorted(all_hosts) + csplit = csplit[1:] + banner = True + elif i == 0 and csplit and csplit[0] == ".": + hosts = [unet] + csplit = csplit[1:] + else: + hosts = expand_hosts(csplit[:i], all_hosts) + csplit = csplit[i:] + + if not hosts and not csplit[:i]: + if toplevel: + hosts = [unet] + else: + hosts = sorted(all_hosts) + banner = True + + if not csplit: + return hosts, "", "", True + + i = line.index(csplit[0]) + i += len(csplit[0]) + return hosts, csplit[0], line[i:].strip(), banner + + +def win_cmd_host_split(unet, cmd, kinds, defall): + if kinds: + all_hosts = { + x for x in unet.hosts if unet.hosts[x].config.get("kind", "") in kinds + } + else: + all_hosts = set(unet.hosts) + + csplit = cmd.split() + i = 0 + for i, e in enumerate(csplit): + if not host_in(e, all_hosts): + if not is_host_regex(e): + break + else: + i += 1 + + if i == 0 and csplit and csplit[0] == "*": + hosts = sorted(all_hosts) + csplit = csplit[1:] + elif i == 0 and csplit and csplit[0] == ".": + hosts = [unet] + csplit = csplit[1:] + else: + hosts = expand_hosts(csplit[:i], all_hosts) + + if not hosts and defall and not csplit[:i]: + hosts = sorted(all_hosts) + + # Filter hosts based on cmd + cmd = " ".join(csplit[i:]) + return hosts, cmd + + +def proc_readline(fd, prompt, histfile): + """Read a line of input from user while running in a sub-process.""" + # How do we change the command though, that's what's displayed in ps normally + linux.set_process_name("Munet CLI") + try: + # For some reason sys.stdin is fileno == 16 and useless + sys.stdin = os.fdopen(0) + histfile = init_history(None, histfile) + line = input(prompt) + readline.write_history_file(histfile) + if line is None: + os.write(fd, b"\n") + os.write(fd, bytes(f":{str(line)}\n", encoding="utf-8")) + except EOFError: + os.write(fd, b"\n") + except KeyboardInterrupt: + os.write(fd, b"I\n") + except Exception as error: + os.write(fd, bytes(f"E{str(error)}\n", encoding="utf-8")) + + +async def async_input_reader(rfd): + """Read a line of input from the user input sub-process pipe.""" + rpipe = os.fdopen(rfd, mode="r") + reader = asyncio.StreamReader() + + def protocol_factory(): + return asyncio.StreamReaderProtocol(reader) + + loop = asyncio.get_event_loop() + transport, _ = await loop.connect_read_pipe(protocol_factory, rpipe) + o = await reader.readline() + transport.close() + + o = o.decode("utf-8").strip() + if not o: + return None + if o[0] == "I": + raise KeyboardInterrupt() + if o[0] == "E": + raise Exception(o[1:]) + assert o[0] == ":" + return o[1:] + + +# +# A lot of work to add async `input` handling without creating a thread. We cannot use +# threads when unshare_inline is used with pid namespace per kernel clone(2) +# restriction. +# +async def async_input(prompt, histfile): + """Asynchronously read a line from the user.""" + rfd, wfd = os.pipe() + p = multiprocessing.Process(target=proc_readline, args=(wfd, prompt, histfile)) + p.start() + logging.debug("started async_input input process: %s", p) + try: + return await async_input_reader(rfd) + finally: + logging.debug("joining async_input input process") + p.join() + + +def make_help_str(unet): + w = sorted([x if x else "" for x in unet.cli_in_window_cmds]) + ww = unet.cli_in_window_cmds + u = sorted([x if x else "" for x in unet.cli_run_cmds]) + uu = unet.cli_run_cmds + + s = ( + """ +Basic Commands: + cli :: open a secondary CLI window + help :: this help + hosts :: list hosts + quit :: quit the cli + + HOST can be a host or one of the following: + - '*' for all hosts + - '.' for the parent munet + - a regex specified between '/' (e.g., '/rtr.*/') + +New Window Commands:\n""" + + "\n".join([f" {ww[v][0]}\t:: {ww[v][1]}" for v in w]) + + """\nInline Commands:\n""" + + "\n".join([f" {uu[v][0]}\t:: {uu[v][1]}" for v in u]) + + "\n" + ) + return s + + +def get_shcmd(unet, host, kinds, execfmt, ucmd): + if host is None: + h = None + kind = None + elif host is unet or host == "": + h = unet + kind = "" + else: + h = unet.hosts[host] + kind = h.config.get("kind", "") + if kinds and kind not in kinds: + return "" + if not isinstance(execfmt, str): + execfmt = execfmt.get(kind, {}).get("exec", "") + if not execfmt: + return "" + + # Do substitutions for {} in string + numfmt = len(re.findall(r"{\d*}", execfmt)) + if numfmt > 1: + ucmd = execfmt.format(*shlex.split(ucmd)) + elif numfmt: + ucmd = execfmt.format(ucmd) + elif len(re.findall(r"{[a-zA-Z_][0-9a-zA-Z_\.]*}", execfmt)): + if execfmt.endswith('"'): + fstring = "f'''" + execfmt + "'''" + else: + fstring = 'f"""' + execfmt + '"""' + ucmd = eval( # pylint: disable=W0123 + fstring, + globals(), + {"host": h, "unet": unet, "user_input": ucmd}, + ) + else: + # No variable or usercmd substitution at all. + ucmd = execfmt + + # Do substitution for munet variables + ucmd = ucmd.replace("%CONFIGDIR%", str(unet.config_dirname)) + if host is None or host is unet: + ucmd = ucmd.replace("%RUNDIR%", str(unet.rundir)) + return ucmd.replace("%NAME%", ".") + ucmd = ucmd.replace("%RUNDIR%", str(os.path.join(unet.rundir, host))) + if h.mgmt_ip: + ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip)) + elif h.mgmt_ip6: + ucmd = ucmd.replace("%IPADDR%", str(h.mgmt_ip6)) + if h.mgmt_ip6: + ucmd = ucmd.replace("%IP6ADDR%", str(h.mgmt_ip6)) + return ucmd.replace("%NAME%", str(host)) + + +async def run_command( + unet, + outf, + line, + execfmt, + banner, + hosts, + toplevel, + kinds, + ns_only=False, + interactive=False, +): + """Runs a command on a set of hosts. + + Runs `execfmt`. Prior to executing the string the following transformations are + performed on it. + + `execfmt` may also be a dictionary of dicitonaries keyed on kind with `exec` holding + the kind's execfmt string. + + - if `{}` is present then `str.format` is called to replace `{}` with any extra + input values after the command and hosts are removed from the input. + - else if any `{digits}` are present then `str.format` is called to replace + `{digits}` with positional args obtained from the addittional user input + first passed to `shlex.split`. + - else f-string style interpolation is performed on the string with + the local variables `host` (the current node object or None), + `unet` (the Munet object), and `user_input` (the additional command input) + defined. + + The output is sent to `outf`. If `ns_only` is True then the `execfmt` is + run using `Commander.cmd_status_nsonly` otherwise it is run with + `Commander.cmd_status`. + """ + if kinds: + logging.info("Filtering hosts to kinds: %s", kinds) + hosts = [x for x in hosts if unet.hosts[x].config.get("kind", "") in kinds] + logging.info("Filtered hosts: %s", hosts) + + if not hosts: + if not toplevel: + return + hosts = [unet] + + # if unknowns := [x for x in hosts if x not in unet.hosts]: + # outf.write("%% Unknown host[s]: %s\n" % ", ".join(unknowns)) + # return + + # if sys.stdin.isatty() and interactive: + if interactive: + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, line) + if not shcmd: + continue + if len(hosts) > 1 or banner: + outf.write(f"------ Host: {host} ------\n") + spawn(unet, host if not toplevel else unet, shcmd, outf, ns_only) + if len(hosts) > 1 or banner: + outf.write(f"------- End: {host} ------\n") + outf.write("\n") + return + + aws = [] + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, line) + if not shcmd: + continue + if toplevel: + ns = unet + else: + ns = unet.hosts[host] if host and host != unet else unet + if ns_only: + cmdf = ns.async_cmd_status_nsonly + else: + cmdf = ns.async_cmd_status + aws.append(cmdf(shcmd, warn=False, stderr=subprocess.STDOUT)) + + results = await asyncio.gather(*aws, return_exceptions=True) + for host, result in zip(hosts, results): + if isinstance(result, Exception): + o = str(result) + "\n" + rc = -1 + else: + rc, o, _ = result + if len(hosts) > 1 or banner: + outf.write(f"------ Host: {host} ------\n") + if rc: + outf.write(f"*** non-zero exit status: {rc}\n") + outf.write(o) + if len(hosts) > 1 or banner: + outf.write(f"------- End: {host} ------\n") + + +cli_builtins = ["cli", "help", "hosts", "quit"] + + +class Completer: + """A completer class for the CLI.""" + + def __init__(self, unet): + self.unet = unet + + def complete(self, text, state): + line = readline.get_line_buffer() + tokens = line.split() + # print(f"\nXXX: tokens: {tokens} text: '{text}' state: {state}'\n") + + first_token = not tokens or (text and len(tokens) == 1) + + # If we have already have a builtin command we are done + if tokens and tokens[0] in cli_builtins: + return [None] + + cli_run_cmds = set(self.unet.cli_run_cmds.keys()) + top_run_cmds = {x for x in cli_run_cmds if self.unet.cli_run_cmds[x][3]} + cli_run_cmds -= top_run_cmds + cli_win_cmds = set(self.unet.cli_in_window_cmds.keys()) + hosts = set(self.unet.hosts.keys()) + is_window_cmd = bool(tokens) and tokens[0] in cli_win_cmds + done_set = set() + if bool(tokens): + if text: + done_set = set(tokens[:-1]) + else: + done_set = set(tokens) + + # Determine the domain for completions + if not tokens or first_token: + all_cmds = ( + set(cli_builtins) | hosts | cli_run_cmds | cli_win_cmds | top_run_cmds + ) + elif is_window_cmd: + all_cmds = hosts + elif tokens and tokens[0] in top_run_cmds: + # nothing to complete if a top level command + pass + elif not bool(done_set & cli_run_cmds): + all_cmds = hosts | cli_run_cmds + + if not text: + completes = all_cmds + else: + # print(f"\nXXX: all_cmds: {all_cmds} text: '{text}'\n") + completes = {x + " " for x in all_cmds if x.startswith(text)} + + # print(f"\nXXX: completes: {completes} text: '{text}' state: {state}'\n") + # remove any completions already present + completes -= done_set + completes = sorted(completes) + [None] + return completes[state] + + +async def doline( + unet, line, outf, background=False, notty=False +): # pylint: disable=R0911 + line = line.strip() + m = re.fullmatch(r"^(\S+)(?:\s+(.*))?$", line) + if not m: + return True + + cmd = m.group(1) + nline = m.group(2) if m.group(2) else "" + + if cmd in ("q", "quit"): + return False + + if cmd == "help": + outf.write(make_help_str(unet)) + return True + if cmd in ("h", "hosts"): + outf.write(f"% Hosts:\t{' '.join(sorted(unet.hosts.keys()))}\n") + return True + if cmd == "cli": + await remote_cli( + unet, + "secondary> ", + "Secondary CLI", + background, + ) + return True + + # + # In window commands + # + + if cmd in unet.cli_in_window_cmds: + execfmt, toplevel, kinds, kwargs = unet.cli_in_window_cmds[cmd][2:] + + # if toplevel: + # ucmd = " ".join(nline.split()) + # else: + hosts, ucmd = win_cmd_host_split(unet, nline, kinds, False) + if not hosts: + if not toplevel: + return True + hosts = [unet] + + if isinstance(execfmt, str): + found_brace = "{}" in execfmt + else: + found_brace = False + for d in execfmt.values(): + if "{}" in d["exec"]: + found_brace = True + break + if not found_brace and ucmd and not toplevel: + # CLI command does not expect user command so treat as hosts of which some + # must be unknown + unknowns = [x for x in ucmd.split() if x not in unet.hosts] + outf.write(f"% Unknown host[s]: {' '.join(unknowns)}\n") + return True + + try: + if not hosts and toplevel: + hosts = [unet] + + for host in hosts: + shcmd = get_shcmd(unet, host, kinds, execfmt, ucmd) + if toplevel or host == unet: + unet.run_in_window(shcmd, **kwargs) + else: + unet.hosts[host].run_in_window(shcmd, **kwargs) + except Exception as error: + outf.write(f"% Error: {error}\n") + return True + + # + # Inline commands + # + + toplevel = unet.cli_run_cmds[cmd][3] if cmd in unet.cli_run_cmds else False + # if toplevel: + # logging.debug("top-level: cmd: '%s' nline: '%s'", cmd, nline) + # hosts = None + # banner = False + # else: + + hosts, cmd, nline, banner = host_cmd_split(unet, line, toplevel) + hoststr = "munet" if hosts == [unet] else f"{hosts}" + logging.debug("hosts: '%s' cmd: '%s' nline: '%s'", hoststr, cmd, nline) + + if cmd in unet.cli_run_cmds: + pass + elif "" in unet.cli_run_cmds: + nline = f"{cmd} {nline}" + cmd = "" + else: + outf.write(f"% Unknown command: {cmd} {nline}\n") + return True + + execfmt, toplevel, kinds, ns_only, interactive = unet.cli_run_cmds[cmd][2:] + if interactive and notty: + outf.write("% Error: interactive command must be run from primary CLI\n") + return True + + await run_command( + unet, + outf, + nline, + execfmt, + banner, + hosts, + toplevel, + kinds, + ns_only, + interactive, + ) + + return True + + +async def cli_client(sockpath, prompt="munet> "): + """Implement the user-facing CLI for a remote munet reached by a socket.""" + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.settimeout(10) + sock.connect(sockpath) + + # Go into full non-blocking mode now + sock.settimeout(None) + + print("\n--- Munet CLI Starting ---\n\n") + while True: + line = input(prompt) + if line is None: + return + + # Need to put \n back + line += "\n" + + # Send the CLI command + sock.send(line.encode("utf-8")) + + def bendswith(b, sentinel): + slen = len(sentinel) + return len(b) >= slen and b[-slen:] == sentinel + + # Collect the output + rb = b"" + while not bendswith(rb, ENDMARKER): + lb = sock.recv(4096) + if not lb: + return + rb += lb + + # Remove the marker + rb = rb[: -len(ENDMARKER)] + + # Write the output + sys.stdout.write(rb.decode("utf-8", "ignore")) + + +async def local_cli(unet, outf, prompt, histfile, background): + """Implement the user-side CLI for local munet.""" + assert unet is not None + completer = Completer(unet) + readline.parse_and_bind("tab: complete") + readline.set_completer(completer.complete) + + print("\n--- Munet CLI Starting ---\n\n") + while True: + try: + line = await async_input(prompt, histfile) + if line is None: + return + + if not await doline(unet, line, outf, background): + return + except KeyboardInterrupt: + outf.write("%% Caught KeyboardInterrupt\nUse ^D or 'quit' to exit") + + +def init_history(unet, histfile): + try: + if histfile is None: + histfile = os.path.expanduser("~/.munet-history.txt") + if not os.path.exists(histfile): + if unet: + unet.cmd("touch " + histfile) + else: + subprocess.run("touch " + histfile, shell=True, check=True) + if histfile: + readline.read_history_file(histfile) + return histfile + except Exception as error: + logging.warning("init_history failed: %s", error) + return None + + +async def cli_client_connected(unet, background, reader, writer): + """Handle CLI commands inside the munet process from a socket.""" + # # Go into full non-blocking mode now + # client.settimeout(None) + logging.debug("cli client connected") + while True: + line = await reader.readline() + if not line: + logging.debug("client closed cli connection") + break + line = line.decode("utf-8").strip() + + class EncodingFile: + """Wrap a writer to encode in utf-8.""" + + def __init__(self, writer): + self.writer = writer + + def write(self, x): + self.writer.write(x.encode("utf-8", "ignore")) + + def flush(self): + self.writer.flush() + + if not await doline(unet, line, EncodingFile(writer), background, notty=True): + logging.debug("server closing cli connection") + return + + writer.write(ENDMARKER) + await writer.drain() + + +async def remote_cli(unet, prompt, title, background): + """Open a CLI in a new window.""" + try: + if not unet.cli_sockpath: + sockpath = os.path.join(tempfile.mkdtemp("-sockdir", "pty-"), "cli.sock") + ccfunc = functools.partial(cli_client_connected, unet, background) + s = await asyncio.start_unix_server(ccfunc, path=sockpath) + unet.cli_server = asyncio.create_task(s.serve_forever(), name="cli-task") + unet.cli_sockpath = sockpath + logging.info("server created on :\n%s\n", sockpath) + + # Open a new window with a new CLI + python_path = await unet.async_get_exec_path(["python3", "python"]) + us = os.path.realpath(__file__) + cmd = f"{python_path} {us}" + if unet.cli_histfile: + cmd += " --histfile=" + unet.cli_histfile + if prompt: + cmd += f" --prompt='{prompt}'" + cmd += " " + unet.cli_sockpath + unet.run_in_window(cmd, title=title, background=False) + except Exception as error: + logging.error("cli server: unexpected exception: %s", error) + + +def add_cli_in_window_cmd( + unet, name, helpfmt, helptxt, execfmt, toplevel, kinds, **kwargs +): + """Adds a CLI command to the CLI. + + The command `cmd` is added to the commands executable by the user from the CLI. See + `base.Commander.run_in_window` for the arguments that can be passed in `args` and + `kwargs` to this function. + + Args: + unet: unet object + name: command string (no spaces) + helpfmt: format of command to display in help (left side) + helptxt: help string for command (right side) + execfmt: interpreter `cmd` to pass to `host.run_in_window()`, if {} present then + allow for user commands to be entered and inserted. May also be a dict of dict + keyed on kind with sub-key of "exec" providing the `execfmt` string for that + kind. + toplevel: run command in common top-level namespaec not inside hosts + kinds: limit CLI command to nodes which match list of kinds. + **kwargs: keyword args to pass to `host.run_in_window()` + """ + unet.cli_in_window_cmds[name] = (helpfmt, helptxt, execfmt, toplevel, kinds, kwargs) + + +def add_cli_run_cmd( + unet, + name, + helpfmt, + helptxt, + execfmt, + toplevel, + kinds, + ns_only=False, + interactive=False, +): + """Adds a CLI command to the CLI. + + The command `cmd` is added to the commands executable by the user from the CLI. + See `run_command` above in the `doline` function and for the arguments that can + be passed in to this function. + + Args: + unet: unet object + name: command string (no spaces) + helpfmt: format of command to display in help (left side) + helptxt: help string for command (right side) + execfmt: format string to insert user cmds into for execution. May also be a + dict of dict keyed on kind with sub-key of "exec" providing the `execfmt` + string for that kind. + toplevel: run command in common top-level namespaec not inside hosts + kinds: limit CLI command to nodes which match list of kinds. + ns_only: Should execute the command on the host vs in the node namespace. + interactive: Should execute the command inside an allocated pty (interactive) + """ + unet.cli_run_cmds[name] = ( + helpfmt, + helptxt, + execfmt, + toplevel, + kinds, + ns_only, + interactive, + ) + + +def add_cli_config(unet, config): + """Adds CLI commands based on config. + + All exec strings will have %CONFIGDIR%, %NAME% and %RUNDIR% replaced with the + corresponding config directory and the current nodes `name` and `rundir`. + Additionally, the exec string will have f-string style interpolation performed + with the local variables `host` (node object or None), `unet` (Munet object) and + `user_input` (if provided to the CLI command) defined. + + The format of the config dictionary can be seen in the following example. + The first list entry represents the default command because it has no `name` key. + + commands: + - help: "run the given FRR command using vtysh" + format: "[HOST ...] FRR-CLI-COMMAND" + exec: "vtysh -c {}" + ns-only: false # the default + interactive: false # the default + - name: "vtysh" + help: "Open a FRR CLI inside new terminal[s] on the given HOST[s]" + format: "vtysh HOST [HOST ...]" + exec: "vtysh" + new-window: true + - name: "capture" + help: "Capture packets on a given network" + format: "pcap NETWORK" + exec: "tshark -s 9200 -i {0} -w /tmp/capture-{0}.pcap" + new-window: true + top-level: true # run in top-level container namespace, above hosts + + The `new_window` key can also be a dictionary which will be passed as keyward + arguments to the `Commander.run_in_window()` function. + + Args: + unet: unet object + config: dictionary of cli config + """ + for cli_cmd in config.get("commands", []): + name = cli_cmd.get("name", None) + helpfmt = cli_cmd.get("format", "") + helptxt = cli_cmd.get("help", "") + execfmt = list_to_dict_with_key(cli_cmd.get("exec-kind"), "kind") + if not execfmt: + execfmt = cli_cmd.get("exec", "bash -c '{}'") + toplevel = cli_cmd.get("top-level", False) + kinds = cli_cmd.get("kinds", []) + stdargs = (unet, name, helpfmt, helptxt, execfmt, toplevel, kinds) + new_window = cli_cmd.get("new-window", None) + if isinstance(new_window, dict): + add_cli_in_window_cmd(*stdargs, **new_window) + elif bool(new_window): + add_cli_in_window_cmd(*stdargs) + else: + # on-host is deprecated it really implemented "ns-only" + add_cli_run_cmd( + *stdargs, + cli_cmd.get("ns-only", cli_cmd.get("on-host")), + cli_cmd.get("interactive", False), + ) + + +def cli( + unet, + histfile=None, + sockpath=None, + force_window=False, + title=None, + prompt=None, + background=True, +): + asyncio.run( + async_cli(unet, histfile, sockpath, force_window, title, prompt, background) + ) + + +async def async_cli( + unet, + histfile=None, + sockpath=None, + force_window=False, + title=None, + prompt=None, + background=True, +): + if prompt is None: + prompt = "munet> " + + if force_window or not sys.stdin.isatty(): + await remote_cli(unet, prompt, title, background) + + if not unet: + logger.debug("client-cli using sockpath %s", sockpath) + + try: + if sockpath: + await cli_client(sockpath, prompt) + else: + await local_cli(unet, sys.stdout, prompt, histfile, background) + except KeyboardInterrupt: + print("\n...^C exiting CLI") + except EOFError: + pass + except Exception as ex: + logger.critical("cli: got exception: %s", ex, exc_info=True) + raise + + +if __name__ == "__main__": + # logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log") + logging.basicConfig(level=logging.DEBUG) + logger = logging.getLogger("cli-client") + logger.info("Start logging cli-client") + + parser = argparse.ArgumentParser() + parser.add_argument("--histfile", help="file to user for history") + parser.add_argument("--prompt", help="prompt string to use") + parser.add_argument("socket", help="path to pair of sockets to communicate over") + cli_args = parser.parse_args() + + cli_prompt = cli_args.prompt if cli_args.prompt else "munet> " + asyncio.run( + async_cli( + None, + cli_args.histfile, + cli_args.socket, + prompt=cli_prompt, + background=False, + ) + ) diff --git a/tests/topotests/munet/compat.py b/tests/topotests/munet/compat.py new file mode 100644 index 000000000000..e82a7d5b7729 --- /dev/null +++ b/tests/topotests/munet/compat.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# November 16 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Provide compatible APIs.""" + + +class PytestConfig: + """Pytest config duck-type-compatible object using argprase args.""" + + class Namespace: + """A namespace defined by a dictionary of values.""" + + def __init__(self, args): + self.args = args + + def __getattr__(self, attr): + return self.args[attr] if attr in self.args else None + + def __init__(self, args): + self.args = vars(args) + self.option = PytestConfig.Namespace(self.args) + + def getoption(self, name, default=None, skip=False): + assert not skip + if name.startswith("--"): + name = name[2:] + name = name.replace("-", "_") + if name in self.args: + return self.args[name] if self.args[name] is not None else default + return default diff --git a/tests/topotests/munet/config.py b/tests/topotests/munet/config.py new file mode 100644 index 000000000000..2870ae615c13 --- /dev/null +++ b/tests/topotests/munet/config.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# June 25 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2021-2022, LabN Consulting, L.L.C. +# +"""A module that defines common configuration utility functions.""" +import logging + +from collections.abc import Iterable +from copy import deepcopy +from typing import overload + + +def find_with_kv(lst, k, v): + if lst: + for e in lst: + if k in e and e[k] == v: + return e + return {} + + +def find_all_with_kv(lst, k, v): + rv = [] + if lst: + for e in lst: + if k in e and e[k] == v: + rv.append(e) + return rv + + +def find_matching_net_config(name, cconf, oconf): + p = find_all_with_kv(oconf.get("connections", {}), "to", name) + if not p: + return {} + + rname = cconf.get("remote-name", None) + if not rname: + return p[0] + + return find_with_kv(p, "name", rname) + + +def merge_using_key(a, b, k): + # First get a dict of indexes in `a` for the key value of `k` in objects of `a` + m = list(a) + mi = {o[k]: i for i, o in enumerate(m)} + for o in b: + bkv = o[k] + if bkv in mi: + m[mi[bkv]] = o + else: + mi[bkv] = len(m) + m.append(o) + return m + + +def list_to_dict_with_key(lst, k): + """Convert a YANG styl list of objects to dict of objects. + + This function converts a YANG style list of objects (dictionaries) to a plain python + dictionary of objects (dictionaries). The value for the supplied key for each + object is used to store the object in the new diciontary. + + This only works for lists of objects which are keyed on a single contained value. + + Args: + lst: a *list* of python dictionary objects. + k: the key value contained in each dictionary object in the list. + + Returns: + A dictionary of objects (dictionaries). + """ + return {x[k]: x for x in (lst if lst else [])} + + +def config_to_dict_with_key(c, ck, k): + """Convert the config item from a list of objects to dict. + + Use :py:func:`list_to_dict_with_key` to convert the list of objects + at ``c[ck]`` to a dict of the objects using the key ``k``. + + Args: + c: config dictionary + ck: The key identifying the list of objects from ``c``. + k: The key to pass to :py:func:`list_to_dict_with_key`. + + Returns: + A dictionary of objects (dictionaries). + """ + c[ck] = list_to_dict_with_key(c.get(ck, []), k) + return c[ck] + + +@overload +def config_subst(config: str, **kwargs) -> str: + ... + + +@overload +def config_subst(config: Iterable, **kwargs) -> Iterable: + ... + + +def config_subst(config: Iterable, **kwargs) -> Iterable: + if isinstance(config, str): + if "%RUNDIR%/%NAME%" in config: + config = config.replace("%RUNDIR%/%NAME%", "%RUNDIR%") + logging.warning( + "config '%RUNDIR%/%NAME%' should be changed to '%RUNDIR%' only, " + "converting automatically for now." + ) + for name, value in kwargs.items(): + config = config.replace(f"%{name.upper()}%", str(value)) + elif isinstance(config, Iterable): + try: + return {k: config_subst(config[k], **kwargs) for k in config} + except (KeyError, TypeError): + return [config_subst(x, **kwargs) for x in config] + return config + + +def value_merge_deepcopy(s1, s2): + """Merge values using deepcopy. + + Create a deepcopy of the result of merging the values from dicts ``s1`` and ``s2``. + If a key exists in both ``s1`` and ``s2`` the value from ``s2`` is used." + """ + d = {} + for k, v in s1.items(): + if k in s2: + d[k] = deepcopy(s2[k]) + else: + d[k] = deepcopy(v) + return d + + +def merge_kind_config(kconf, config): + mergekeys = kconf.get("merge", []) + config = deepcopy(config) + new = deepcopy(kconf) + for k in new: + if k not in config: + continue + + if k not in mergekeys: + new[k] = config[k] + elif isinstance(new[k], list): + new[k].extend(config[k]) + elif isinstance(new[k], dict): + new[k] = {**new[k], **config[k]} + else: + new[k] = config[k] + for k in config: + if k not in new: + new[k] = config[k] + return new + + +def cli_opt_list(option_list): + if not option_list: + return [] + if isinstance(option_list, str): + return [x for x in option_list.split(",") if x] + return [x for x in option_list if x] + + +def name_in_cli_opt_str(name, option_list): + ol = cli_opt_list(option_list) + return name in ol or "all" in ol + + +class ConfigOptionsProxy: + """Proxy options object to fill in for any missing pytest config.""" + + class DefNoneObject: + """An object that returns None for any attribute access.""" + + def __getattr__(self, attr): + return None + + def __init__(self, pytestconfig=None): + if isinstance(pytestconfig, ConfigOptionsProxy): + self.config = pytestconfig.config + self.option = self.config.option + else: + self.config = pytestconfig + if self.config: + self.option = self.config.option + else: + self.option = ConfigOptionsProxy.DefNoneObject() + + def getoption(self, opt, default=None): + if not self.config: + return default + + try: + value = self.config.getoption(opt) + return value if value is not None else default + except ValueError: + return default + + def get_option(self, opt, default=None): + return self.getoption(opt, default) + + def get_option_list(self, opt): + value = self.get_option(opt, "") + return cli_opt_list(value) + + def name_in_option_list(self, name, opt): + optlist = self.get_option_list(opt) + return "all" in optlist or name in optlist diff --git a/tests/topotests/munet/kinds.yaml b/tests/topotests/munet/kinds.yaml new file mode 100644 index 000000000000..0c278d37c9cf --- /dev/null +++ b/tests/topotests/munet/kinds.yaml @@ -0,0 +1,84 @@ +version: 1 +kinds: + - name: frr + cap-add: + # Zebra requires these + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - AUDIT_WRITE # needed for ssh pty allocation + - name: ceos + init: false + shell: false + merge: ["env"] + # Should we cap-drop some of these in privileged mode? + # ceos kind is special. munet will add args to /sbin/init for each + # environment variable of the form `systemd.setenv=ENVNAME=VALUE` for each + # environment varialbe named ENVNAME with a value of `VALUE`. If cmd: is + # changed to anything but `/sbin/init` munet will not do this. + cmd: /sbin/init + privileged: true + env: + - name: "EOS_PLATFORM" + value: "ceoslab" + - name: "container" + value: "docker" + - name: "ETBA" + value: "4" + - name: "SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT" + value: "1" + - name: "INTFTYPE" + value: "eth" + - name: "MAPETH0" + value: "1" + - name: "MGMT_INTF" + value: "eth0" + - name: "CEOS" + value: "1" + + # cap-add: + # # cEOS requires these, except GNMI still doesn't work + # # - NET_ADMIN + # # - NET_RAW + # # - SYS_ADMIN + # # - SYS_RESOURCE # Required for the CLI + + # All Caps + # - AUDIT_CONTROL + # - AUDIT_READ + # - AUDIT_WRITE + # - BLOCK_SUSPEND + # - CHOWN + # - DAC_OVERRIDE + # - DAC_READ_SEARCH + # - FOWNER + # - FSETID + # - IPC_LOCK + # - IPC_OWNER + # - KILL + # - LEASE + # - LINUX_IMMUTABLE + # - MKNOD + # - NET_ADMIN + # - NET_BIND_SERVICE + # - NET_BROADCAST + # - NET_RAW + # - SETFCAP + # - SETGID + # - SETPCAP + # - SETUID + # - SYSLOG + # - SYS_ADMIN + # - SYS_BOOT + # - SYS_CHROOT + # - SYS_MODULE + # - SYS_NICE + # - SYS_PACCT + # - SYS_PTRACE + # - SYS_RAWIO + # - SYS_RESOURCE + # - SYS_TIME + # - SYS_TTY_CONFIG + # - WAKE_ALARM + # - MAC_ADMIN - Smack project? + # - MAC_OVERRIDE - Smack project? diff --git a/tests/topotests/munet/linux.py b/tests/topotests/munet/linux.py new file mode 100644 index 000000000000..417f74566acd --- /dev/null +++ b/tests/topotests/munet/linux.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# June 10 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""A module that gives access to linux unshare system call.""" + +import ctypes # pylint: disable=C0415 +import ctypes.util # pylint: disable=C0415 +import errno +import functools +import os + + +libc = None + + +def raise_oserror(enum): + s = errno.errorcode[enum] if enum in errno.errorcode else str(enum) + error = OSError(s) + error.errno = enum + error.strerror = s + raise error + + +def _load_libc(): + global libc # pylint: disable=W0601,W0603 + if libc: + return + lcpath = ctypes.util.find_library("c") + libc = ctypes.CDLL(lcpath, use_errno=True) + + +def pause(): + if not libc: + _load_libc() + libc.pause() + + +MS_RDONLY = 1 +MS_NOSUID = 1 << 1 +MS_NODEV = 1 << 2 +MS_NOEXEC = 1 << 3 +MS_SYNCHRONOUS = 1 << 4 +MS_REMOUNT = 1 << 5 +MS_MANDLOCK = 1 << 6 +MS_DIRSYNC = 1 << 7 +MS_NOSYMFOLLOW = 1 << 8 +MS_NOATIME = 1 << 10 +MS_NODIRATIME = 1 << 11 +MS_BIND = 1 << 12 +MS_MOVE = 1 << 13 +MS_REC = 1 << 14 +MS_SILENT = 1 << 15 +MS_POSIXACL = 1 << 16 +MS_UNBINDABLE = 1 << 17 +MS_PRIVATE = 1 << 18 +MS_SLAVE = 1 << 19 +MS_SHARED = 1 << 20 +MS_RELATIME = 1 << 21 +MS_KERNMOUNT = 1 << 22 +MS_I_VERSION = 1 << 23 +MS_STRICTATIME = 1 << 24 +MS_LAZYTIME = 1 << 25 + + +def mount(source, target, fs, flags=0, options=""): + if not libc: + _load_libc() + libc.mount.argtypes = ( + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_ulong, + ctypes.c_char_p, + ) + fsenc = fs.encode() if fs else None + optenc = options.encode() if options else None + ret = libc.mount(source.encode(), target.encode(), fsenc, flags, optenc) + if ret < 0: + err = ctypes.get_errno() + raise OSError( + err, + f"Error mounting {source} ({fs}) on {target}" + f" with options '{options}': {os.strerror(err)}", + ) + + +# unmout options +MNT_FORCE = 0x1 +MNT_DETACH = 0x2 +MNT_EXPIRE = 0x4 +UMOUNT_NOFOLLOW = 0x8 + + +def umount(target, options): + if not libc: + _load_libc() + libc.umount.argtypes = (ctypes.c_char_p, ctypes.c_uint) + + ret = libc.umount(target.encode(), int(options)) + if ret < 0: + err = ctypes.get_errno() + raise OSError( + err, + f"Error umounting {target} with options '{options}': {os.strerror(err)}", + ) + + +def pidfd_open(pid, flags=0): + if hasattr(os, "pidfd_open") and os.pidfd_open is not pidfd_open: + return os.pidfd_open(pid, flags) # pylint: disable=no-member + + if not libc: + _load_libc() + + try: + pfof = libc.pidfd_open + except AttributeError: + __NR_pidfd_open = 434 + _pidfd_open = libc.syscall + _pidfd_open.restype = ctypes.c_int + _pidfd_open.argtypes = ctypes.c_long, ctypes.c_uint, ctypes.c_uint + pfof = functools.partial(_pidfd_open, __NR_pidfd_open) + + fd = pfof(int(pid), int(flags)) + if fd == -1: + raise_oserror(ctypes.get_errno()) + + return fd + + +if not hasattr(os, "pidfd_open"): + os.pidfd_open = pidfd_open + + +def setns(fd, nstype): # noqa: D402 + """See setns(2) manpage.""" + if not libc: + _load_libc() + + if libc.setns(int(fd), int(nstype)) == -1: + raise_oserror(ctypes.get_errno()) + + +def unshare(flags): # noqa: D402 + """See unshare(2) manpage.""" + if not libc: + _load_libc() + + if libc.unshare(int(flags)) == -1: + raise_oserror(ctypes.get_errno()) + + +CLONE_NEWTIME = 0x00000080 +CLONE_VM = 0x00000100 +CLONE_FS = 0x00000200 +CLONE_FILES = 0x00000400 +CLONE_SIGHAND = 0x00000800 +CLONE_PIDFD = 0x00001000 +CLONE_PTRACE = 0x00002000 +CLONE_VFORK = 0x00004000 +CLONE_PARENT = 0x00008000 +CLONE_THREAD = 0x00010000 +CLONE_NEWNS = 0x00020000 +CLONE_SYSVSEM = 0x00040000 +CLONE_SETTLS = 0x00080000 +CLONE_PARENT_SETTID = 0x00100000 +CLONE_CHILD_CLEARTID = 0x00200000 +CLONE_DETACHED = 0x00400000 +CLONE_UNTRACED = 0x00800000 +CLONE_CHILD_SETTID = 0x01000000 +CLONE_NEWCGROUP = 0x02000000 +CLONE_NEWUTS = 0x04000000 +CLONE_NEWIPC = 0x08000000 +CLONE_NEWUSER = 0x10000000 +CLONE_NEWPID = 0x20000000 +CLONE_NEWNET = 0x40000000 +CLONE_IO = 0x80000000 + +clone_flag_names = { + CLONE_NEWTIME: "CLONE_NEWTIME", + CLONE_VM: "CLONE_VM", + CLONE_FS: "CLONE_FS", + CLONE_FILES: "CLONE_FILES", + CLONE_SIGHAND: "CLONE_SIGHAND", + CLONE_PIDFD: "CLONE_PIDFD", + CLONE_PTRACE: "CLONE_PTRACE", + CLONE_VFORK: "CLONE_VFORK", + CLONE_PARENT: "CLONE_PARENT", + CLONE_THREAD: "CLONE_THREAD", + CLONE_NEWNS: "CLONE_NEWNS", + CLONE_SYSVSEM: "CLONE_SYSVSEM", + CLONE_SETTLS: "CLONE_SETTLS", + CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID", + CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID", + CLONE_DETACHED: "CLONE_DETACHED", + CLONE_UNTRACED: "CLONE_UNTRACED", + CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID", + CLONE_NEWCGROUP: "CLONE_NEWCGROUP", + CLONE_NEWUTS: "CLONE_NEWUTS", + CLONE_NEWIPC: "CLONE_NEWIPC", + CLONE_NEWUSER: "CLONE_NEWUSER", + CLONE_NEWPID: "CLONE_NEWPID", + CLONE_NEWNET: "CLONE_NEWNET", + CLONE_IO: "CLONE_IO", +} + + +def clone_flag_string(flags): + ns = [v for k, v in clone_flag_names.items() if k & flags] + if ns: + return "|".join(ns) + return "None" + + +namespace_files = { + CLONE_NEWUSER: "ns/user", + CLONE_NEWCGROUP: "ns/cgroup", + CLONE_NEWIPC: "ns/ipc", + CLONE_NEWUTS: "ns/uts", + CLONE_NEWNET: "ns/net", + CLONE_NEWPID: "ns/pid_for_children", + CLONE_NEWNS: "ns/mnt", + CLONE_NEWTIME: "ns/time_for_children", +} + +PR_SET_PDEATHSIG = 1 +PR_GET_PDEATHSIG = 2 +PR_SET_NAME = 15 +PR_GET_NAME = 16 + + +def set_process_name(name): + if not libc: + _load_libc() + + # Why does uncommenting this cause failure? + # libc.prctl.argtypes = ( + # ctypes.c_int, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ctypes.c_ulong, + # ) + + s = ctypes.create_string_buffer(bytes(name, encoding="ascii")) + sr = ctypes.byref(s) + libc.prctl(PR_SET_NAME, sr, 0, 0, 0) + + +def set_parent_death_signal(signum): + if not libc: + _load_libc() + + # Why does uncommenting this cause failure? + libc.prctl.argtypes = ( + ctypes.c_int, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.c_ulong, + ) + + libc.prctl(PR_SET_PDEATHSIG, signum, 0, 0, 0) diff --git a/tests/topotests/munet/logconf-mutest.yaml b/tests/topotests/munet/logconf-mutest.yaml new file mode 100644 index 000000000000..b450fb938227 --- /dev/null +++ b/tests/topotests/munet/logconf-mutest.yaml @@ -0,0 +1,84 @@ +version: 1 +formatters: + brief: + format: '%(levelname)5s: %(message)s' + operfmt: + class: munet.mulog.ColorFormatter + format: ' ------| %(message)s' + exec: + format: '%(asctime)s %(levelname)5s: %(name)s: %(message)s' + output: + format: '%(asctime)s %(levelname)5s: OUTPUT: %(message)s' + results: + # format: '%(asctime)s %(levelname)5s: %(message)s' + format: '%(message)s' + +handlers: + console: + level: WARNING + class: logging.StreamHandler + formatter: brief + stream: ext://sys.stderr + info_console: + level: INFO + class: logging.StreamHandler + formatter: brief + stream: ext://sys.stderr + oper_console: + level: DEBUG + class: logging.StreamHandler + formatter: operfmt + stream: ext://sys.stderr + exec: + level: DEBUG + class: logging.FileHandler + formatter: exec + filename: mutest-exec.log + mode: w + output: + level: DEBUG + class: munet.mulog.MultiFileHandler + root_path: "mutest.output" + formatter: output + filename: mutest-output.log + mode: w + results: + level: INFO + class: munet.mulog.MultiFileHandler + root_path: "mutest.results" + new_handler_level: DEBUG + formatter: results + filename: mutest-results.log + mode: w + +root: + level: DEBUG + handlers: [ "console", "exec" ] + +loggers: + # These are some loggers that get used... + # munet: + # level: DEBUG + # propagate: true + # munet.base.commander + # level: DEBUG + # propagate: true + # mutest.error: + # level: DEBUG + # propagate: true + mutest.output: + level: DEBUG + handlers: ["output", "exec"] + propagate: false + mutest.results: + level: DEBUG + handlers: [ "info_console", "exec", "output", "results" ] + # We don't propagate this b/c we want a lower level accept on the console + # Instead we use info_console and exec to cover what root would log to. + propagate: false + # This is used to debug the operation of mutest + mutest.oper: + # Records are emitted at DEBUG so this will normally filter everything + level: INFO + handlers: [ "oper_console" ] + propagate: false diff --git a/tests/topotests/munet/logconf.yaml b/tests/topotests/munet/logconf.yaml new file mode 100644 index 000000000000..430ee2096d4c --- /dev/null +++ b/tests/topotests/munet/logconf.yaml @@ -0,0 +1,32 @@ +version: 1 +formatters: + brief: + format: '%(asctime)s: %(levelname)s: %(message)s' + precise: + format: '%(asctime)s %(levelname)s: %(name)s: %(message)s' + +handlers: + console: + class: logging.StreamHandler + formatter: brief + level: INFO + stream: ext://sys.stderr + file: + class: logging.FileHandler + formatter: precise + level: DEBUG + filename: munet-exec.log + mode: w + +root: + level: DEBUG + handlers: [ "console", "file" ] + +# these are some loggers that get used. +# loggers: +# munet: +# level: DEBUG +# propagate: true +# munet.base.commander +# level: DEBUG +# propagate: true diff --git a/tests/topotests/munet/mucmd.py b/tests/topotests/munet/mucmd.py new file mode 100644 index 000000000000..5518c6dcfee9 --- /dev/null +++ b/tests/topotests/munet/mucmd.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 5 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A command that allows external command execution inside nodes.""" +import argparse +import json +import os +import subprocess +import sys + +from pathlib import Path + + +def newest_file_in(filename, paths, has_sibling=None): + new = None + newst = None + items = (x for y in paths for x in Path(y).rglob(filename)) + for e in items: + st = os.stat(e) + if has_sibling and not e.parent.joinpath(has_sibling).exists(): + continue + if not new or st.st_mtime_ns > newst.st_mtime_ns: + new = e + newst = st + continue + return new, newst + + +def main(*args): + ap = argparse.ArgumentParser(args) + ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc") + ap.add_argument("node", nargs="?", help="node to enter or run command inside") + ap.add_argument( + "shellcmd", + nargs=argparse.REMAINDER, + help="optional shell-command to execute on NODE", + ) + args = ap.parse_args() + if args.rundir: + configpath = Path(args.rundir).joinpath("config.json") + else: + configpath, _ = newest_file_in( + "config.json", + ["/tmp/munet", "/tmp/mutest", "/tmp/unet-test"], + has_sibling=args.node, + ) + print(f'Using "{configpath}"') + + if not configpath.exists(): + print(f'"{configpath}" not found') + return 1 + rundir = configpath.parent + + nodes = [] + config = json.load(open(configpath, encoding="utf-8")) + nodes = list(config.get("topology", {}).get("nodes", [])) + envcfg = config.get("mucmd", {}).get("env", {}) + + # If args.node is not a node it's part of shellcmd + if args.node and args.node not in nodes: + if args.node != ".": + args.shellcmd[0:0] = [args.node] + args.node = None + + if args.node: + name = args.node + nodedir = rundir.joinpath(name) + if not nodedir.exists(): + print('"{name}" node doesn\'t exist in "{rundir}"') + return 1 + rundir = nodedir + else: + name = "munet" + pidpath = rundir.joinpath("nspid") + pid = open(pidpath, encoding="ascii").read().strip() + + env = {**os.environ} + env["MUNET_NODENAME"] = name + env["MUNET_RUNDIR"] = str(rundir) + + for k in envcfg: + envcfg[k] = envcfg[k].replace("%NAME%", str(name)) + envcfg[k] = envcfg[k].replace("%RUNDIR%", str(rundir)) + + # Can't use -F if it's a new pid namespace + ecmd = "/usr/bin/nsenter" + eargs = [ecmd] + + output = subprocess.check_output(["/usr/bin/nsenter", "--help"], encoding="utf-8") + if " -a," in output: + eargs.append("-a") + else: + # -U doesn't work + for flag in ["-u", "-i", "-m", "-n", "-C", "-T"]: + if f" {flag}," in output: + eargs.append(flag) + eargs.append(f"--pid=/proc/{pid}/ns/pid_for_children") + eargs.append(f"--wd={rundir}") + eargs.extend(["-t", pid]) + eargs += args.shellcmd + # print("Using ", eargs) + return os.execvpe(ecmd, eargs, {**env, **envcfg}) + + +if __name__ == "__main__": + exit_status = main() + sys.exit(exit_status) diff --git a/tests/topotests/munet/mulog.py b/tests/topotests/munet/mulog.py new file mode 100644 index 000000000000..f840eae2d800 --- /dev/null +++ b/tests/topotests/munet/mulog.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 4 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Utilities for logging in munet.""" + +import logging + +from pathlib import Path + + +class MultiFileHandler(logging.FileHandler): + """A logging handler that logs to new files based on the logger name. + + The MultiFileHandler operates as a FileHandler with additional functionality. In + addition to logging to the specified logging file MultiFileHandler also creates new + FileHandlers for child loggers based on a root logging name path. + + The ``root_path`` determines when to create a new FileHandler. For each received log + record, ``root_path`` is removed from the logger name of the record if present, and + the resulting channel path (if any) determines the directory for a new log file to + also emit the record to. The new file path is constructed by starting with the + directory ``filename`` resides in, then joining the path determined above after + converting "." to "/" and finally by adding back the basename of ``filename``. + + record logger path => mutest.output.testingfoo + root_path => mutest.output + base filename => /tmp/mutest/mutest-exec.log + new logfile => /tmp/mutest/testingfoo/mutest-exec.log + + All messages are also emitted to the common FileLogger for ``filename``. + + If a log record is from a logger that does not start with ``root_path`` no file is + created and the normal emit occurs. + + Args: + root_path: the logging path of the root level for this handler. + new_handler_level: logging level for newly created handlers + log_dir: the log directory to put log files in. + filename: the base log file. + """ + + def __init__(self, root_path, filename=None, **kwargs): + self.__root_path = root_path + self.__basename = Path(filename).name + if root_path[-1] != ".": + self.__root_path += "." + self.__root_pathlen = len(self.__root_path) + self.__kwargs = kwargs + self.__log_dir = Path(filename).absolute().parent + self.__log_dir.mkdir(parents=True, exist_ok=True) + self.__filenames = {} + self.__added = set() + + if "new_handler_level" not in kwargs: + self.__new_handler_level = logging.NOTSET + else: + new_handler_level = kwargs["new_handler_level"] + del kwargs["new_handler_level"] + self.__new_handler_level = new_handler_level + + super().__init__(filename=filename, **kwargs) + + if self.__new_handler_level is None: + self.__new_handler_level = self.level + + def __log_filename(self, name): + if name in self.__filenames: + return self.__filenames[name] + + if not name.startswith(self.__root_path): + newname = None + else: + newname = name[self.__root_pathlen :] + newname = Path(newname.replace(".", "/")) + newname = self.__log_dir.joinpath(newname) + newname = newname.joinpath(self.__basename) + self.__filenames[name] = newname + + self.__filenames[name] = newname + return newname + + def emit(self, record): + newname = self.__log_filename(record.name) + if newname: + if newname not in self.__added: + self.__added.add(newname) + h = logging.FileHandler(filename=newname, **self.__kwargs) + h.setLevel(self.__new_handler_level) + h.setFormatter(self.formatter) + logging.getLogger(record.name).addHandler(h) + h.emit(record) + super().emit(record) + + +class ColorFormatter(logging.Formatter): + """A formatter that adds color sequences based on level.""" + + def __init__(self, fmt=None, datefmt=None, style="%", **kwargs): + grey = "\x1b[90m" + yellow = "\x1b[33m" + red = "\x1b[31m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + # basefmt = " ------| %(message)s " + + self.formatters = { + logging.DEBUG: logging.Formatter(grey + fmt + reset), + logging.INFO: logging.Formatter(grey + fmt + reset), + logging.WARNING: logging.Formatter(yellow + fmt + reset), + logging.ERROR: logging.Formatter(red + fmt + reset), + logging.CRITICAL: logging.Formatter(bold_red + fmt + reset), + } + # Why are we even bothering? + super().__init__(fmt, datefmt, style, **kwargs) + + def format(self, record): + formatter = self.formatters.get(record.levelno) + return formatter.format(record) diff --git a/tests/topotests/munet/munet-schema.json b/tests/topotests/munet/munet-schema.json new file mode 100644 index 000000000000..a1dcd878dd56 --- /dev/null +++ b/tests/topotests/munet/munet-schema.json @@ -0,0 +1,654 @@ +{ + "title": "labn-munet-config", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Generated by pyang from module labn-munet-config", + "type": "object", + "properties": { + "cli": { + "type": "object", + "properties": { + "commands": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exec": { + "type": "string" + }, + "exec-kind": { + "type": "array", + "items": { + "type": "object", + "properties": { + "kind": { + "type": "string" + }, + "exec": { + "type": "string" + } + } + } + }, + "format": { + "type": "string" + }, + "help": { + "type": "string" + }, + "interactive": { + "type": "boolean" + }, + "kinds": { + "type": "array", + "items": { + "type": "string" + } + }, + "name": { + "type": "string" + }, + "new-window": { + "type": "boolean" + }, + "top-level": { + "type": "boolean" + } + } + } + } + } + }, + "kinds": { + "type": "array", + "items": { + "type": "object", + "properties": { + "merge": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-add": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "cmd": { + "type": "string" + }, + "cleanup-cmd": { + "type": "string" + }, + "ready-cmd": { + "type": "string" + }, + "image": { + "type": "string" + }, + "server": { + "type": "string" + }, + "server-port": { + "type": "number" + }, + "qemu": { + "type": "object", + "properties": { + "bios": { + "type": "string" + }, + "disk": { + "type": "string" + }, + "kerenel": { + "type": "string" + }, + "initrd": { + "type": "string" + }, + "kvm": { + "type": "boolean" + }, + "ncpu": { + "type": "integer" + }, + "memory": { + "type": "string" + }, + "root": { + "type": "string" + }, + "cmdline-extra": { + "type": "string" + }, + "extra-args": { + "type": "string" + }, + "console": { + "type": "object", + "properties": { + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "expects": { + "type": "array", + "items": { + "type": "string" + } + }, + "sends": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer" + } + } + } + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "name": { + "type": "string" + }, + "hostintf": { + "type": "string" + }, + "physical": { + "type": "string" + }, + "remote-name": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "jitter": { + "type": "integer" + }, + "jitter-correlation": { + "type": "string" + }, + "loss": { + "type": "integer" + }, + "loss-correlation": { + "type": "string" + }, + "rate": { + "type": "object", + "properties": { + "rate": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "burst": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + } + } + } + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "gdb-cmd": { + "type": "string" + }, + "gdb-target-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "gdb-run-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "init": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "mounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "tmpfs-size": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "name": { + "type": "string" + }, + "podman": { + "type": "object", + "properties": { + "extra-args": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "privileged": { + "type": "boolean" + }, + "shell": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "topology": { + "type": "object", + "properties": { + "dns-network": { + "type": "string" + }, + "ipv6-enable": { + "type": "boolean" + }, + "networks-autonumber": { + "type": "boolean" + }, + "networks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + } + } + } + }, + "nodes": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "kind": { + "type": "string" + }, + "cap-add": { + "type": "array", + "items": { + "type": "string" + } + }, + "cap-remove": { + "type": "array", + "items": { + "type": "string" + } + }, + "cmd": { + "type": "string" + }, + "cleanup-cmd": { + "type": "string" + }, + "ready-cmd": { + "type": "string" + }, + "image": { + "type": "string" + }, + "server": { + "type": "string" + }, + "server-port": { + "type": "number" + }, + "qemu": { + "type": "object", + "properties": { + "bios": { + "type": "string" + }, + "disk": { + "type": "string" + }, + "kerenel": { + "type": "string" + }, + "initrd": { + "type": "string" + }, + "kvm": { + "type": "boolean" + }, + "ncpu": { + "type": "integer" + }, + "memory": { + "type": "string" + }, + "root": { + "type": "string" + }, + "cmdline-extra": { + "type": "string" + }, + "extra-args": { + "type": "string" + }, + "console": { + "type": "object", + "properties": { + "user": { + "type": "string" + }, + "password": { + "type": "string" + }, + "expects": { + "type": "array", + "items": { + "type": "string" + } + }, + "sends": { + "type": "array", + "items": { + "type": "string" + } + }, + "timeout": { + "type": "integer" + } + } + } + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "to": { + "type": "string" + }, + "ip": { + "type": "string" + }, + "ipv6": { + "type": "string" + }, + "name": { + "type": "string" + }, + "hostintf": { + "type": "string" + }, + "physical": { + "type": "string" + }, + "remote-name": { + "type": "string" + }, + "driver": { + "type": "string" + }, + "delay": { + "type": "integer" + }, + "jitter": { + "type": "integer" + }, + "jitter-correlation": { + "type": "string" + }, + "loss": { + "type": "integer" + }, + "loss-correlation": { + "type": "string" + }, + "rate": { + "type": "object", + "properties": { + "rate": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "limit": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + }, + "burst": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ] + } + } + } + } + } + }, + "env": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + }, + "gdb-cmd": { + "type": "string" + }, + "gdb-target-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "gdb-run-cmds": { + "type": "array", + "items": { + "type": "string" + } + }, + "init": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "mounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "destination": { + "type": "string" + }, + "source": { + "type": "string" + }, + "tmpfs-size": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "name": { + "type": "string" + }, + "podman": { + "type": "object", + "properties": { + "extra-args": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "privileged": { + "type": "boolean" + }, + "shell": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + } + ] + }, + "volumes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, + "version": { + "type": "integer" + } + } +} \ No newline at end of file diff --git a/tests/topotests/munet/mutest/__main__.py b/tests/topotests/munet/mutest/__main__.py new file mode 100644 index 000000000000..c87031112d61 --- /dev/null +++ b/tests/topotests/munet/mutest/__main__.py @@ -0,0 +1,445 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# December 2 2022, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2022, LabN Consulting, L.L.C. +# +"""Command to execute mutests.""" + +import asyncio +import logging +import os +import subprocess +import sys +import time + +from argparse import ArgumentParser +from argparse import Namespace +from copy import deepcopy +from pathlib import Path +from typing import Union + +from munet import parser +from munet.base import Bridge +from munet.base import get_event_loop +from munet.mutest import userapi as uapi +from munet.native import L3NodeMixin +from munet.native import Munet +from munet.parser import async_build_topology +from munet.parser import get_config + + +# We want all but critical to fit in 5 characters for alignment +logging.addLevelName(logging.WARNING, "WARN") +root_logger = logging.getLogger("") +exec_formatter = logging.Formatter("%(asctime)s %(levelname)5s: %(name)s: %(message)s") + + +async def get_unet(config: dict, croot: Path, rundir: Path, unshare: bool = False): + """Create and run a new Munet topology. + + The topology is built from the given ``config`` to run inside the path indicated + by ``rundir``. If ``unshare`` is True then the process will unshare into it's + own private namespace. + + Args: + config: a config dictionary obtained from ``munet.parser.get_config``. This + value will be modified and stored in the built ``Munet`` object. + croot: common root of all tests, used to search for ``kinds.yaml`` files. + rundir: the path to the run directory for this topology. + unshare: True to unshare the process into it's own private namespace. + + Yields: + Munet: The constructed and running topology. + """ + tasks = [] + unet = None + try: + try: + unet = await async_build_topology( + config, rundir=str(rundir), unshare_inline=unshare + ) + except Exception as error: + logging.debug("unet build failed: %s", error, exc_info=True) + raise + try: + tasks = await unet.run() + except Exception as error: + logging.debug("unet run failed: %s", error, exc_info=True) + raise + logging.debug("unet topology running") + try: + yield unet + except Exception as error: + logging.error("unet fixture: yield unet unexpected exception: %s", error) + raise + except KeyboardInterrupt: + logging.info("Received keyboard while building topology") + raise + finally: + if unet: + await unet.async_delete() + + # No one ever awaits these so cancel them + logging.debug("unet fixture: cleanup") + for task in tasks: + task.cancel() + + # Reset the class variables so auto number is predictable + logging.debug("unet fixture: resetting ords to 1") + L3NodeMixin.next_ord = 1 + Bridge.next_ord = 1 + + +def common_root(path1: Union[str, Path], path2: Union[str, Path]) -> Path: + """Find the common root between 2 paths. + + Args: + path1: Path + path2: Path + Returns: + Path: the shared root components between ``path1`` and ``path2``. + + Examples: + >>> common_root("/foo/bar/baz", "/foo/bar/zip/zap") + PosixPath('/foo/bar') + >>> common_root("/foo/bar/baz", "/fod/bar/zip/zap") + PosixPath('/') + """ + apath1 = Path(path1).absolute().parts + apath2 = Path(path2).absolute().parts + alen = min(len(apath1), len(apath2)) + common = None + for a, b in zip(apath1[:alen], apath2[:alen]): + if a != b: + break + common = common.joinpath(a) if common else Path(a) + return common + + +async def collect(args: Namespace): + """Collect test files. + + Files must match the pattern ``mutest_*.py``, and their containing + directory must have a munet config file present. This function also changes + the current directory to the common parent of all the tests, and paths are + returned relative to the common directory. + + Args: + args: argparse results + + Returns: + (commondir, tests, configs): where ``commondir`` is the path representing + the common parent directory of all the testsd, ``tests`` is a + dictionary of lists of test files, keyed on their containing directory + path, and ``configs`` is a dictionary of config dictionaries also keyed + on its containing directory path. The directory paths are relative to a + common ancestor. + """ + file_select = args.file_select + upaths = args.paths if args.paths else ["."] + globpaths = set() + for upath in (Path(x) for x in upaths): + if upath.is_file(): + paths = {upath.absolute()} + else: + paths = {x.absolute() for x in Path(upath).rglob(file_select)} + globpaths |= paths + tests = {} + configs = {} + + # Find the common root + # We don't actually need this anymore, the idea was prefix test names + # with uncommon paths elements to automatically differentiate them. + common = None + sortedpaths = [] + for path in sorted(globpaths): + sortedpaths.append(path) + dirpath = path.parent + common = common_root(common, dirpath) if common else dirpath + + ocwd = Path().absolute() + try: + os.chdir(common) + # Work with relative paths to the common directory + for path in (x.relative_to(common) for x in sortedpaths): + dirpath = path.parent + if dirpath not in configs: + try: + configs[dirpath] = get_config(search=[dirpath]) + except FileNotFoundError: + logging.warning( + "Skipping '%s' as munet.{yaml,toml,json} not found in '%s'", + path, + dirpath, + ) + continue + if dirpath not in tests: + tests[dirpath] = [] + tests[dirpath].append(path.absolute()) + finally: + os.chdir(ocwd) + return common, tests, configs + + +async def execute_test( + unet: Munet, + test: Path, + args: Namespace, + test_num: int, + exec_handler: logging.Handler, +) -> (int, int, int, Exception): + """Execute a test case script. + + Using the built and running topology in ``unet`` for targets + execute the test case script file ``test``. + + Args: + unet: a running topology. + test: path to the test case script file. + args: argparse results. + test_num: the number of this test case in the run. + exec_handler: exec file handler to add to test loggers which do not propagate. + """ + test_name = testname_from_path(test) + + # Get test case loggers + logger = logging.getLogger(f"mutest.output.{test_name}") + reslog = logging.getLogger(f"mutest.results.{test_name}") + logger.addHandler(exec_handler) + reslog.addHandler(exec_handler) + + # We need to send an info level log to cause the speciifc handler to be + # created, otherwise all these debug ones don't get through + reslog.info("") + + # reslog.debug("START: %s:%s from %s", test_num, test_name, test.stem) + # reslog.debug("-" * 70) + + targets = dict(unet.hosts.items()) + targets["."] = unet + + tc = uapi.TestCase( + str(test_num), test_name, test, targets, logger, reslog, args.full_summary + ) + passed, failed, e = tc.execute() + + run_time = time.time() - tc.info.start_time + + status = "PASS" if not (failed or e) else "FAIL" + + # Turn off for now + reslog.debug("-" * 70) + reslog.debug( + "stats: %d steps, %d pass, %d fail, %s abort, %4.2fs elapsed", + passed + failed, + passed, + failed, + 1 if e else 0, + run_time, + ) + reslog.debug("-" * 70) + reslog.debug("END: %s %s:%s\n", status, test_num, test_name) + + return passed, failed, e + + +def testname_from_path(path: Path) -> str: + """Return test name based on the path to the test file. + + Args: + path: path to the test file. + + Returns: + str: the name of the test. + """ + return str(Path(path).stem).replace("/", ".") + + +def print_header(reslog, unet): + targets = dict(unet.hosts.items()) + nmax = max(len(x) for x in targets) + nmax = max(nmax, len("TARGET")) + sum_fmt = uapi.TestCase.sum_fmt.format(nmax) + reslog.info(sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION") + reslog.info("-" * 70) + + +async def run_tests(args): + reslog = logging.getLogger("mutest.results") + + common, tests, configs = await collect(args) + results = [] + errlog = logging.getLogger("mutest.error") + reslog = logging.getLogger("mutest.results") + printed_header = False + tnum = 0 + start_time = time.time() + try: + for dirpath in tests: + test_files = tests[dirpath] + for test in test_files: + tnum += 1 + config = deepcopy(configs[dirpath]) + test_name = testname_from_path(test) + rundir = args.rundir.joinpath(test_name) + + # Add an test case exec file handler to the root logger and result + # logger + exec_path = rundir.joinpath("mutest-exec.log") + exec_path.parent.mkdir(parents=True, exist_ok=True) + exec_handler = logging.FileHandler(exec_path, "w") + exec_handler.setFormatter(exec_formatter) + root_logger.addHandler(exec_handler) + + try: + async for unet in get_unet(config, common, rundir): + if not printed_header: + print_header(reslog, unet) + printed_header = True + passed, failed, e = await execute_test( + unet, test, args, tnum, exec_handler + ) + except KeyboardInterrupt as error: + errlog.warning("KeyboardInterrupt while running test %s", test_name) + passed, failed, e = 0, 0, error + raise + except Exception as error: + logging.error( + "Error executing test %s: %s", test, error, exc_info=True + ) + errlog.error( + "Error executing test %s: %s", test, error, exc_info=True + ) + passed, failed, e = 0, 0, error + finally: + # Remove the test case exec file handler form the root logger. + root_logger.removeHandler(exec_handler) + results.append((test_name, passed, failed, e)) + + except KeyboardInterrupt: + pass + + run_time = time.time() - start_time + tnum = 0 + tpassed = 0 + tfailed = 0 + texc = 0 + + spassed = 0 + sfailed = 0 + + for result in results: + _, passed, failed, e = result + tnum += 1 + spassed += passed + sfailed += failed + if e: + texc += 1 + if failed or e: + tfailed += 1 + else: + tpassed += 1 + + reslog.info("") + reslog.info( + "run stats: %s steps, %s pass, %s fail, %s abort, %4.2fs elapsed", + spassed + sfailed, + spassed, + sfailed, + texc, + run_time, + ) + reslog.info("-" * 70) + + tnum = 0 + for result in results: + test_name, passed, failed, e = result + tnum += 1 + s = "FAIL" if failed or e else "PASS" + reslog.info(" %s %s:%s", s, tnum, test_name) + + reslog.info("-" * 70) + reslog.info( + "END RUN: %s test scripts, %s passed, %s failed", tnum, tpassed, tfailed + ) + + return 1 if tfailed else 0 + + +async def async_main(args): + status = 3 + try: + # For some reson we are not catching exceptions raised inside + status = await run_tests(args) + except KeyboardInterrupt: + logging.info("Exiting (async_main), received KeyboardInterrupt in main") + except Exception as error: + logging.info( + "Exiting (async_main), unexpected exception %s", error, exc_info=True + ) + logging.debug("async_main returns %s", status) + return status + + +def main(): + ap = ArgumentParser() + ap.add_argument( + "--dist", + type=int, + nargs="?", + const=-1, + default=0, + action="store", + metavar="NUM-THREADS", + help="Run in parallel, value is num. of threads or no value for auto", + ) + ap.add_argument("-d", "--rundir", help="runtime directory for tempfiles, logs, etc") + ap.add_argument( + "--file-select", default="mutest_*.py", help="shell glob for finding tests" + ) + ap.add_argument("--log-config", help="logging config file (yaml, toml, json, ...)") + ap.add_argument( + "-V", + "--full-summary", + action="store_true", + help="print full summary headers from docstrings", + ) + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose" + ) + ap.add_argument("paths", nargs="*", help="Paths to collect tests from") + args = ap.parse_args() + + rundir = args.rundir if args.rundir else "/tmp/mutest" + args.rundir = Path(rundir) + os.environ["MUNET_RUNDIR"] = rundir + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + + config = parser.setup_logging(args, config_base="logconf-mutest") + # Grab the exec formatter from the logging config + if fconfig := config.get("formatters", {}).get("exec"): + global exec_formatter # pylint: disable=W291,W0603 + exec_formatter = logging.Formatter( + fconfig.get("format"), fconfig.get("datefmt") + ) + + loop = None + status = 4 + try: + loop = get_event_loop() + status = loop.run_until_complete(async_main(args)) + except KeyboardInterrupt: + logging.info("Exiting (main), received KeyboardInterrupt in main") + except Exception as error: + logging.info("Exiting (main), unexpected exception %s", error, exc_info=True) + finally: + if loop: + loop.close() + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/mutest/userapi.py b/tests/topotests/munet/mutest/userapi.py new file mode 100644 index 000000000000..1df8c0d012b6 --- /dev/null +++ b/tests/topotests/munet/mutest/userapi.py @@ -0,0 +1,1111 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright 2017, 2022, LabN Consulting, L.L.C. +"""Mutest is a simple send/expect based testing framework. + +This module implements the basic send/expect functionality for mutest. The test +developer first creates a munet topology (:ref:`munet-config`) and then writes test +scripts ("test cases") which are composed of calls to the functions defined below +("steps"). In short these are: + +Send/Expect functions: + + - :py:func:`step` + + - :py:func:`step_json` + + - :py:func:`match_step` + + - :py:func:`match_step_json` + + - :py:func:`wait_step` + + - :py:func:`wait_step_json` + +Control/Utility functions: + + - :py:func:`script_dir` + + - :py:func:`include` + + - :py:func:`log` + + - :py:func:`test` + +Test scripts are located by the :command:`mutest` command by their name. The name of a +test script should take the form ``mutest_TESTNAME.py`` where ``TESTNAME`` is replaced +with a user chosen name for the test case. + +Here's a simple example test script which first checks that a specific forwarding entry +is in the FIB for the IP destination ``10.0.1.1``. Then it checks repeatedly for up to +10 seconds for a second forwarding entry in the FIB for the IP destination ``10.0.2.1``. + +.. code-block:: python + + match_step("r1", 'vtysh -c "show ip fib 10.0.1.1"', "Routing entry for 10.0.1.0/24", + "Check for FIB entry for 10.0.1.1") + + wait_step("r1", + 'vtysh -c "show ip fib 10.0.2.1"', + "Routing entry for 10.0.2.0/24", + desc="Check for FIB entry for 10.0.2.1", + timeout=10) + +Notice that the call arguments can be specified by their correct position in the list or +using keyword names, and they can also be specified over multiple lines if preferred. + +All of the functions are documented and defined below. +""" + +# pylint: disable=global-statement + +import functools +import json +import logging +import pprint +import re +import time + +from pathlib import Path +from typing import Any +from typing import Union + +from deepdiff import DeepDiff as json_cmp + +from munet.base import Commander + + +class TestCaseInfo: + """Object to hold nestable TestCase Results.""" + + def __init__(self, tag: str, name: str, path: Path): + self.path = path.absolute() + self.tag = tag + self.name = name + self.steps = 0 + self.passed = 0 + self.failed = 0 + self.start_time = time.time() + self.step_start_time = self.start_time + self.run_time = None + + def __repr__(self): + return ( + f"TestCaseInfo({self.tag} {self.name} steps {self.steps} " + f"p {self.passed} f {self.failed} path {self.path})" + ) + + +class TestCase: + """A mutest testcase. + + This is normally meant to be used internally by the mutest command to + implement the user API. See README-mutest.org for usage details on the + user API. + + Args: + tag: identity of the test in a run. (x.x...) + name: the name of the test case + path: the test file that is being executed. + targets: a dictionary of objects which implement ``cmd_nostatus(str)`` + output_logger: a logger for output and other messages from the test. + result_logger: a logger to output the results of test steps to. + full_summary: if True then print entire doctstring instead of + only the first line in the results report + + Attributes: + tag: identity of the test in a run + name: the name of the test + targets: dictionary of targets. + + steps: total steps executed so far. + passed: number of passing steps. + failed: number of failing steps. + + last: the last command output. + last_m: the last result of re.search during a matching step on the output with + newlines converted to spaces. + + :meta private: + """ + + # sum_hfmt = "{:5.5s} {:4.4s} {:>6.6s} {}" + # sum_dfmt = "{:5s} {:4.4s} {:^6.6s} {}" + sum_fmt = "%-8.8s %4.4s %{}s %6s %s" + + def __init__( + self, + tag: int, + name: str, + path: Path, + targets: dict, + output_logger: logging.Logger = None, + result_logger: logging.Logger = None, + full_summary: bool = False, + ): + + self.info = TestCaseInfo(tag, name, path) + self.__saved_info = [] + self.__short_doc_header = not full_summary + + self.__space_before_result = False + + # we are only ever in a section once, an include ends a section + # so are never in section+include, and another section ends a + # section, so we don't need __in_section to be save in the + # TestCaseInfo struct. + self.__in_section = False + + self.targets = targets + + self.last = "" + self.last_m = None + + self.rlog = result_logger + self.olog = output_logger + self.logf = functools.partial(self.olog.log, logging.INFO) + + oplog = logging.getLogger("mutest.oper") + self.oplogf = oplog.debug + self.oplogf("new TestCase: tag: %s name: %s path: %s", tag, name, path) + + # find the longerst target name and make target field that wide + nmax = max(len(x) for x in targets) + nmax = max(nmax, len("TARGET")) + self.sum_fmt = TestCase.sum_fmt.format(nmax) + + # Let's keep this out of summary for now + self.rlog.debug(self.sum_fmt, "NUMBER", "STAT", "TARGET", "TIME", "DESCRIPTION") + self.rlog.debug("-" * 70) + + @property + def tag(self): + return self.info.tag + + @property + def name(self): + return self.info.name + + @property + def steps(self): + return self.info.steps + + @property + def passed(self): + return self.info.passed + + @property + def failed(self): + return self.info.failed + + def execute(self): + """Execute the test case. + + :meta private: + """ + assert TestCase.g_tc is None + self.oplogf("execute") + try: + TestCase.g_tc = self + e = self.__exec_script(self.info.path, True, False) + except BaseException: + self.__end_test() + raise + return *self.__end_test(), e + + def __del__(self): + if TestCase.g_tc is self: + logging.error("Internal error, TestCase.__end_test() was not called!") + TestCase.g_tc = None + + def __push_execinfo(self, path: Path): + self.oplogf( + "__push_execinfo: path: %s current top is %s", + path, + pprint.pformat(self.info), + ) + newname = self.name + path.stem + self.info.steps += 1 + self.__saved_info.append(self.info) + tag = f"{self.info.tag}.{self.info.steps}" + self.info = TestCaseInfo(tag, newname, path) + self.oplogf("__push_execinfo: now on top: %s", pprint.pformat(self.info)) + + def __pop_execinfo(self): + # do something with tag? + finished_info = self.info + self.info = self.__saved_info.pop() + self.oplogf(" __pop_execinfo: poppped: %s", pprint.pformat(finished_info)) + self.oplogf(" __pop_execinfo: now on top: %s", pprint.pformat(self.info)) + return finished_info + + def __print_header(self, tag, header, add_newline=False): + # self.olog.info(self.sum_fmt, tag, "", "", "", header) + self.olog.info("== %s ==", f"TEST: {tag}. {header}") + if add_newline: + self.rlog.info("") + self.rlog.info("%s. %s", tag, header) + + def __exec_script(self, path, print_header, add_newline): + + # Below was the original method to avoid the global TestCase + # variable; however, we need global functions so we can import them + # into test scripts. Without imports pylint will complain about undefined + # functions and the resulting christmas tree of warnings is annoying. + # + # pylint: disable=possibly-unused-variable,exec-used,redefined-outer-name + # include = self.include + # log = self.logf + # match_step = self.match_step + # match_step_json = self.match_step_json + # step = self.step + # step_json = self.step_json + # test = self.test + # wait_step = self.wait_step + # wait_step_json = self.wait_step_json + + name = f"{path.stem}{self.tag}" + name = re.sub(r"\W|^(?=\d)", "_", name) + + _ok_result = "marker" + try: + self.oplogf("__exec_script: path %s", path) + script = open(path, "r", encoding="utf-8").read() + + # Load the script into a function. + script = script.strip() + s2 = ( + # f"async def _{name}(ok_result):\n" + f"def _{name}(ok_result):\n" + + " " + + script.replace("\n", "\n ") + + "\n return ok_result\n" + + "\n" + ) + exec(s2) + + # Extract any docstring as a title. + if print_header: + title = locals()[f"_{name}"].__doc__.lstrip() + if self.__short_doc_header and (title := title.lstrip()): + if (idx := title.find("\n")) != -1: + title = title[:idx].strip() + if not title: + title = f"Test from file: {self.info.path.name}" + self.__print_header(self.info.tag, title, add_newline) + self.__space_before_result = False + + # Execute the function. + result = locals()[f"_{name}"](_ok_result) + + # Here's where we can do async in the future if we want. + # result = await locals()[f"_{name}"](_ok_result) + except Exception as error: + logging.error( + "Unexpected exception executing %s: %s", name, error, exc_info=True + ) + return error + else: + if result is not _ok_result: + logging.info("%s returned early, result: %s", name, result) + else: + self.oplogf("__exec_script: name %s completed normally", name) + return None + + def __post_result(self, target, success, rstr, logstr=None): + self.oplogf( + "__post_result: target: %s success %s rstr %s", target, success, rstr + ) + if success: + self.info.passed += 1 + status = "PASS" + outlf = self.logf + reslf = self.rlog.info + else: + self.info.failed += 1 + status = "FAIL" + outlf = self.olog.warning + reslf = self.rlog.warning + + self.info.steps += 1 + if logstr is not None: + outlf("R:%d %s: %s" % (self.steps, status, logstr)) + + run_time = time.time() - self.info.step_start_time + + stepstr = f"{self.tag}.{self.steps}" + rtimes = _delta_time_str(run_time) + + if self.__space_before_result: + self.rlog.info("") + self.__space_before_result = False + + reslf(self.sum_fmt, stepstr, status, target, rtimes, rstr) + + # start counting for next step now + self.info.step_start_time = time.time() + + def __end_test(self) -> (int, int): + """End the test log final results. + + Returns: + number of steps, number passed, number failed, run time. + """ + self.oplogf("__end_test: __in_section: %s", self.__in_section) + if self.__in_section: + self.__end_section() + + passed, failed = self.info.passed, self.info.failed + + # No close for loggers + # self.olog.close() + # self.rlog.close() + self.olog = None + self.rlog = None + + assert ( + TestCase.g_tc == self + ), "TestCase global unexpectedly someon else in __end_test" + TestCase.g_tc = None + + self.info.run_time = time.time() - self.info.start_time + return passed, failed + + def _command( + self, + target: str, + cmd: str, + ) -> str: + """Execute a ``cmd`` and return result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + """ + out = self.targets[target].cmd_nostatus(cmd, warn=False) + self.last = out = out.rstrip() + report = out if out else "<no output>" + self.logf("COMMAND OUTPUT:\n%s", report) + return out + + def _command_json( + self, + target: str, + cmd: str, + ) -> dict: + """Execute a json ``cmd`` and return json result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + """ + out = self.targets[target].cmd_nostatus(cmd, warn=False) + self.last = out = out.rstrip() + try: + js = json.loads(out) + except Exception as error: + js = {} + self.olog.warning( + "JSON load failed. Check command output is in JSON format: %s", + error, + ) + self.logf("COMMAND OUTPUT:\n%s", out) + return js + + def _match_command( + self, + target: str, + cmd: str, + match: str, + expect_fail: bool, + flags: int, + ) -> (bool, Union[str, list]): + """Execute a ``cmd`` and check result. + + Args: + target: the target to execute the command on. + cmd: string to execute on the target. + match: regex to ``re.search()`` for in output. + expect_fail: if True then succeed when the regexp doesn't match. + flags: python regex flags to modify matching behavior + + Returns: + (success, matches): if the match fails then "matches" will be None, + otherwise if there were matching groups then groups() will be returned in + ``matches`` otherwise group(0) (i.e., the matching text). + """ + out = self._command(target, cmd) + search = re.search(match, out, flags) + self.last_m = search + if search is None: + success = expect_fail + ret = None + else: + success = not expect_fail + ret = search.groups() + if not ret: + ret = search.group(0) + + level = logging.DEBUG if success else logging.WARNING + self.olog.log(level, "matched:%s:", ret) + return success, ret + + def _match_command_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + expect_fail: bool, + ) -> Union[str, dict]: + """Execute a json ``cmd`` and check result. + + Args: + target: the target to execute the command on. + cmd: string to execut on the target. + match: A json ``str`` or object (``dict``) to compare against the json + output from ``cmd``. + expect_fail: if True then succeed when the json doesn't match. + """ + js = self._command_json(target, cmd) + try: + expect = json.loads(match) + except Exception as error: + expect = {} + self.olog.warning( + "JSON load failed. Check match value is in JSON format: %s", error + ) + + if json_diff := json_cmp(expect, js): + success = expect_fail + if not success: + self.logf("JSON DIFF:%s:" % json_diff) + return success, json_diff + + success = not expect_fail + return success, js + + def _wait( + self, + target: str, + cmd: str, + match: Union[str, dict], + is_json: bool, + timeout: float, + interval: float, + expect_fail: bool, + flags: int, + ) -> Union[str, dict]: + """Execute a command repeatedly waiting for result until timeout.""" + startt = time.time() + endt = startt + timeout + + success = False + ret = None + while not success and time.time() < endt: + if is_json: + success, ret = self._match_command_json(target, cmd, match, expect_fail) + else: + success, ret = self._match_command( + target, cmd, match, expect_fail, flags + ) + if not success: + time.sleep(interval) + return success, ret + + # --------------------- + # Public APIs for User + # --------------------- + + def include(self, pathname: str, new_section: bool = False): + """See :py:func:`~munet.mutest.userapi.include`. + + :meta private: + """ + path = Path(pathname) + path = self.info.path.parent.joinpath(path) + + self.oplogf( + "include: new path: %s create section: %s currently __in_section: %s", + path, + new_section, + self.__in_section, + ) + + if new_section: + self.oplogf("include: starting new exec section") + self.__start_exec_section(path) + our_info = self.info + # Note we do *not* mark __in_section True + else: + # swap the current path inside the top info + old_path = self.info.path + self.info.path = path + self.oplogf("include: swapped info path: new %s old %s", path, old_path) + + self.__exec_script(path, print_header=new_section, add_newline=new_section) + + if new_section: + # Something within the section creating include has also created a section + # end it, sections do not cross section creating file boundaries + if self.__in_section: + self.oplogf( + "include done: path: %s __in_section calling __end_section", path + ) + self.__end_section() + + # We should now be back to the info we started with, b/c we don't actually + # start a new section (__in_section) that then could have been ended inside + # the included file. + assert our_info == self.info + + self.oplogf( + "include done: path: %s new_section calling __end_section", path + ) + self.__end_section() + else: + # The current top path could be anything due to multiple inline includes as + # well as section swap in and out. Forcibly return the top path to the file + # we are returning to + self.info.path = old_path + self.oplogf("include: restored info path: %s", old_path) + + def __end_section(self): + self.oplogf("__end_section: __in_section: %s", self.__in_section) + info = self.__pop_execinfo() + passed, failed = info.passed, info.failed + self.info.passed += passed + self.info.failed += failed + self.__space_before_result = True + self.oplogf("__end_section setting __in_section to False") + self.__in_section = False + + def __start_exec_section(self, path): + self.oplogf("__start_exec_section: __in_section: %s", self.__in_section) + if self.__in_section: + self.__end_section() + + self.__push_execinfo(path) + self.__space_before_result = False + self.oplogf("NOT setting __in_section to True") + assert not self.__in_section + + def section(self, desc: str): + """See :py:func:`~munet.mutest.userapi.section`. + + :meta private: + """ + self.oplogf("section: __in_section: %s", self.__in_section) + # Grab path before we pop the current info off the top + path = self.info.path + old_steps = self.info.steps + + if self.__in_section: + self.__end_section() + + self.__push_execinfo(path) + add_nl = self.info.steps <= old_steps + + self.__space_before_result = False + self.__in_section = True + self.oplogf(" section setting __in_section to True") + self.__print_header(self.info.tag, desc, add_nl) + + def step(self, target: str, cmd: str) -> str: + """See :py:func:`~munet.mutest.userapi.step`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:STEP:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + ) + return self._command(target, cmd) + + def step_json(self, target: str, cmd: str) -> dict: + """See :py:func:`~munet.mutest.userapi.step_json`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:STEP_JSON:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + ) + return self._command_json(target, cmd) + + def match_step( + self, + target: str, + cmd: str, + match: str, + desc: str = "", + expect_fail: bool = False, + flags: int = re.DOTALL, + ) -> (bool, Union[str, list]): + """See :py:func:`~munet.mutest.userapi.match_step`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:MATCH_STEP:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + desc, + expect_fail, + flags, + ) + success, ret = self._match_command(target, cmd, match, expect_fail, flags) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def test_step(self, expr_or_value: Any, desc: str, target: str = "") -> bool: + """See :py:func:`~munet.mutest.userapi.test`. + + :meta private: + """ + success = bool(expr_or_value) + self.__post_result(target, success, desc) + return success + + def match_step_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + expect_fail: bool = False, + ) -> (bool, Union[str, dict]): + """See :py:func:`~munet.mutest.userapi.match_step_json`. + + :meta private: + """ + self.logf( + "#%s.%s:%s:MATCH_STEP_JSON:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + desc, + expect_fail, + ) + success, ret = self._match_command_json(target, cmd, match, expect_fail) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def wait_step( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=0.5, + expect_fail: bool = False, + flags: int = re.DOTALL, + ) -> (bool, Union[str, list]): + """See :py:func:`~munet.mutest.userapi.wait_step`. + + :meta private: + """ + if interval is None: + interval = min(timeout / 20, 0.25) + self.logf( + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + timeout, + interval, + desc, + expect_fail, + flags, + ) + success, ret = self._wait( + target, cmd, match, False, timeout, interval, expect_fail, flags + ) + if desc: + self.__post_result(target, success, desc) + return success, ret + + def wait_step_json( + self, + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=None, + expect_fail: bool = False, + ) -> (bool, Union[str, dict]): + """See :py:func:`~munet.mutest.userapi.wait_step_json`. + + :meta private: + """ + if interval is None: + interval = min(timeout / 20, 0.25) + self.logf( + "#%s.%s:%s:WAIT_STEP:%s:%s:%s:%s:%s:%s:%s", + self.tag, + self.steps + 1, + self.info.path, + target, + cmd, + match, + timeout, + interval, + desc, + expect_fail, + ) + success, ret = self._wait( + target, cmd, match, True, timeout, interval, expect_fail, 0 + ) + if desc: + self.__post_result(target, success, desc) + return success, ret + + +# A non-rentrant global to allow for simplified operations +TestCase.g_tc = None + +# pylint: disable=protected-access + + +def _delta_time_str(run_time: float) -> str: + if run_time < 0.0001: + return "0.0" + if run_time < 0.001: + return f"{run_time:1.4f}" + if run_time < 0.01: + return f"{run_time:2.3f}" + if run_time < 0.1: + return f"{run_time:3.2f}" + if run_time < 100: + return f"{run_time:4.1f}" + return f"{run_time:5f}s" + + +def section(desc: str): + """Start a new section for steps, with a description. + + This starts a new section of tests. The result is basically + the same as doing a non-inline include. The current test number + is used to form a new sub-set of test steps. So if the current + test number is 2.3, a section will now number subsequent steps + 2.3.1, 2.3.2, ... + + A subsequent :py:func:`section` or non-inline :py:func:`include` + call ends the current section and advances the base test number. + + Args: + desc: the description for the new section. + """ + TestCase.g_tc.section(desc) + + +def log(fmt, *args, **kwargs): + """Log a message in the testcase output log.""" + return TestCase.g_tc.logf(fmt, *args, **kwargs) + + +def include(pathname: str, new_section=False): + """Include a file as part of testcase. + + Args: + pathname: the file to include. + new_section: if a new section should be created, otherwise + commands are executed inline. + """ + return TestCase.g_tc.include(pathname, new_section) + + +def script_dir() -> Path: + """The pathname to the directory containing the current script file. + + When an include() is called the script_dir is updated to be current with the + includeded file, and is reverted to the previous value when the include completes. + """ + return TestCase.g_tc.info.path.parent + + +def get_target(name: str) -> Commander: + """Get the target object with the given ``name``.""" + return TestCase.g_tc.targets[name] + + +def step(target: str, cmd: str) -> str: + """Execute a ``cmd`` on a ``target`` and return the output. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execute on the target. + + Returns: + Returns the ``str`` output of the ``cmd``. + """ + return TestCase.g_tc.step(target, cmd) + + +def step_json(target: str, cmd: str) -> dict: + """Execute a json ``cmd`` on a ``target`` and return the json object. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execute on the target. + + Returns: + Returns the json object after parsing the ``cmd`` output. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.step_json(target, cmd) + + +def test_step(expr_or_value: Any, desc: str, target: str = "") -> bool: + """Evaluates ``expr_or_value`` and posts a result base on it bool(expr). + + If ``expr_or_value`` evaluates to a positive result (i.e., True, non-zero, non-None, + non-empty string, non-empty list, etc..) then a PASS result is recorded, otherwise + record a FAIL is recorded. + + Args: + expr: an expression or value to evaluate + desc: description of this test step. + target: optional target to associate with this test in the result string. + + Returns: + A bool indicating the test PASS or FAIL result. + """ + return TestCase.g_tc.test_step(expr_or_value, desc, target) + + +def match_step( + target: str, + cmd: str, + match: str, + desc: str = "", + expect_fail: bool = False, + flags: int = re.DOTALL, +) -> (bool, Union[str, list]): + """Execute a ``cmd`` on a ``target`` check result. + + Execute ``cmd`` on ``target`` and check if the regexp in ``match`` + matches or doesn't match (according to the ``expect_fail`` value) the + ``cmd`` output. + + If the ``match`` regexp includes groups and if the match succeeds + the group values will be returned in a list, otherwise the command + output is returned. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: regex to match against output. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed when the regexp doesn't match. + flags: python regex flags to modify matching behavior + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value will be a list from ``re.Match.groups()`` if non-empty, + otherwise ``re.Match.group(0)`` if there was a match otherwise None. + """ + return TestCase.g_tc.match_step(target, cmd, match, desc, expect_fail, flags) + + +def match_step_json( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + expect_fail: bool = False, +) -> (bool, Union[str, dict]): + """Execute a ``cmd`` on a ``target`` check result. + + Execute ``cmd`` on ``target`` and check if the json object in ``match`` + matches or doesn't match (according to the ``expect_fail`` value) the + json output from ``cmd``. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: A json ``str`` or object (``dict``) to compare against the json + output from ``cmd``. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed if the a json doesn't match. + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. The + second value is a ``str`` diff if there is a difference found in the json + compare, otherwise the value is the json object (``dict``) from the ``cmd``. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.match_step_json(target, cmd, match, desc, expect_fail) + + +def wait_step( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout: float = 10.0, + interval: float = 0.5, + expect_fail: bool = False, + flags: int = re.DOTALL, +) -> (bool, Union[str, list]): + """Execute a ``cmd`` on a ``target`` repeatedly, looking for a result. + + Execute ``cmd`` on ``target``, every ``interval`` seconds for up to ``timeout`` + seconds until the output of ``cmd`` does or doesn't match (according to the + ``expect_fail`` value) the ``match`` value. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: regexp to match against output. + timeout: The number of seconds to repeat the ``cmd`` looking for a match + (or non-match if ``expect_fail`` is True). + interval: The number of seconds between running the ``cmd``. If not + specified the value is calculated from the timeout value so that on + average the cmd will execute 10 times. The minimum calculated interval + is .25s, shorter values can be passed explicitly. + desc: description of test, if no description then no result is logged. + expect_fail: if True then succeed when the regexp *doesn't* match. + flags: python regex flags to modify matching behavior + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value will be a list from ``re.Match.groups()`` if non-empty, + otherwise ``re.Match.group(0)`` if there was a match otherwise None. + """ + return TestCase.g_tc.wait_step( + target, cmd, match, desc, timeout, interval, expect_fail, flags + ) + + +def wait_step_json( + target: str, + cmd: str, + match: Union[str, dict], + desc: str = "", + timeout=10, + interval=None, + expect_fail: bool = False, +) -> (bool, Union[str, dict]): + """Execute a cmd repeatedly and wait for matching result. + + Execute ``cmd`` on ``target``, every ``interval`` seconds until + the output of ``cmd`` matches or doesn't match (according to the + ``expect_fail`` value) ``match``, for up to ``timeout`` seconds. + + ``match`` is a regular expression to search for in the output of ``cmd`` when + ``is_json`` is False. + + When ``is_json`` is True ``match`` must be a json object or a ``str`` which + parses into a json object. Likewise, the ``cmd`` output is parsed into a json + object and then a comparison is done between the two json objects. + + Args: + target: the target to execute the ``cmd`` on. + cmd: string to execut on the ``target``. + match: A json object or str representation of one to compare against json + output from ``cmd``. + desc: description of test, if no description then no result is logged. + timeout: The number of seconds to repeat the ``cmd`` looking for a match + (or non-match if ``expect_fail`` is True). + interval: The number of seconds between running the ``cmd``. If not + specified the value is calculated from the timeout value so that on + average the cmd will execute 10 times. The minimum calculated interval + is .25s, shorter values can be passed explicitly. + expect_fail: if True then succeed if the a json doesn't match. + + Returns: + Returns a 2-tuple. The first value is a bool indicating ``success``. + The second value is a ``str`` diff if there is a difference found in the + json compare, otherwise the value is a json object (dict) from the ``cmd`` + output. + + If json parse fails, a warning is logged and an empty ``dict`` is used. + """ + return TestCase.g_tc.wait_step_json( + target, cmd, match, desc, timeout, interval, expect_fail + ) + + +def luInclude(filename, CallOnFail=None): + """Backward compatible API, do not use in new tests.""" + return include(filename) + + +def luLast(usenl=False): + """Backward compatible API, do not use in new tests.""" + del usenl + return TestCase.g_tc.last_m + + +def luCommand( + target, + cmd, + regexp=".", + op="none", + result="", + ltime=10, + returnJson=False, + wait_time=0.5, +): + """Backward compatible API, do not use in new tests. + + Only non-json is verified to any degree of confidence by code inspection. + + For non-json should return match.group() if match else return bool(op == "fail"). + + For json if no diff return the json else diff return bool(op == "jsoncmp_fail") + bug if no json from output (fail parse) could maybe generate diff, which could + then return + """ + if op == "wait": + if returnJson: + return wait_step_json(target, cmd, regexp, result, ltime, wait_time) + + success, _ = wait_step(target, cmd, regexp, result, ltime, wait_time) + match = luLast() + if success and match is not None: + return match.group() + return success + + if op == "none": + if returnJson: + return step_json(target, cmd) + return step(target, cmd) + + if returnJson and op in ("jsoncmp_fail", "jsoncmp_pass"): + expect_fail = op == "jsoncmp_fail" + return match_step_json(target, cmd, regexp, result, expect_fail) + + assert not returnJson + assert op in ("fail", "pass") + expect_fail = op == "fail" + success, _ = match_step(target, cmd, regexp, result, expect_fail) + match = luLast() + if success and match is not None: + return match.group() + return success diff --git a/tests/topotests/munet/mutestshare.py b/tests/topotests/munet/mutestshare.py new file mode 100644 index 000000000000..95ffa74e7b03 --- /dev/null +++ b/tests/topotests/munet/mutestshare.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# January 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A tiny init for namespaces in python inspired by the C program tini.""" +import argparse +import errno +import logging +import os +import shlex +import signal +import subprocess +import sys +import threading +import time + +from signal import Signals as S + +from . import linux +from .base import commander + + +child_pid = -1 +very_verbose = False +restore_signals = set() + + +def vdebug(*args, **kwargs): + if very_verbose: + logging.debug(*args, **kwargs) + + +def exit_with_status(pid, status): + try: + ec = status >> 8 if bool(status & 0xFF00) else status | 0x80 + logging.debug("reaped our child, exiting %s", ec) + sys.exit(ec) + except ValueError: + vdebug("pid %s didn't actually exit", pid) + + +def waitpid(tag): + logging.debug("%s: waitid for exiting processes", tag) + idobj = os.waitid(os.P_ALL, 0, os.WEXITED) + pid = idobj.si_pid + status = idobj.si_status + if pid == child_pid: + exit_with_status(pid, status) + else: + logging.debug("%s: reaped zombie pid %s with status %s", tag, pid, status) + + +def new_process_group(): + pid = os.getpid() + try: + pgid = os.getpgrp() + if pgid == pid: + logging.debug("already process group leader %s", pgid) + else: + logging.debug("creating new process group %s", pid) + os.setpgid(pid, 0) + except Exception as error: + logging.warning("unable to get new process group: %s", error) + return + + # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked + signal.signal(S.SIGTTIN, signal.SIG_IGN) + signal.signal(S.SIGTTOU, signal.SIG_IGN) + fd = sys.stdin.fileno() + if not os.isatty(fd): + logging.debug("stdin not a tty no foregrounding required") + else: + try: + # This will error if our session no longer associated with controlling tty. + pgid = os.tcgetpgrp(fd) + if pgid == pid: + logging.debug("process group already in foreground %s", pgid) + else: + logging.debug("making us the foreground pgid backgrounding %s", pgid) + os.tcsetpgrp(fd, pid) + except OSError as error: + if error.errno == errno.ENOTTY: + logging.debug("session is no longer associated with controlling tty") + else: + logging.warning("unable to foreground pgid %s: %s", pid, error) + signal.signal(S.SIGTTIN, signal.SIG_DFL) + signal.signal(S.SIGTTOU, signal.SIG_DFL) + + +def exec_child(exec_args): + # Restore signals to default handling: + for snum in restore_signals: + signal.signal(snum, signal.SIG_DFL) + + # Create new process group. + new_process_group() + + estring = shlex.join(exec_args) + try: + # and exec the process + logging.debug("child: executing '%s'", estring) + os.execvp(exec_args[0], exec_args) + # NOTREACHED + except Exception as error: + logging.warning("child: unable to execute '%s': %s", estring, error) + raise + + +def is_creating_pid_namespace(): + p1name = subprocess.check_output( + "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True + ) + p2name = subprocess.check_output( + "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True + ) + return p1name != p2name + + +def restore_namespace(ppid_fd, uflags): + fd = ppid_fd + retry = 3 + for i in range(0, retry): + try: + linux.setns(fd, uflags) + except OSError as error: + logging.warning("could not reset to old namespace fd %s: %s", fd, error) + if i == retry - 1: + raise + time.sleep(1) + os.close(fd) + + +def create_thread_test(): + def runthread(name): + logging.info("In thread: %s", name) + + logging.info("Create thread") + thread = threading.Thread(target=runthread, args=(1,)) + logging.info("Run thread") + thread.start() + logging.info("Join thread") + thread.join() + + +def run(args): + del args + # We look for this b/c the unshare pid will share with /sibn/init + # nselm = "pid_for_children" + # nsflags.append(f"--pid={pp / nselm}") + # mutini now forks when created this way + # cmd.append("--pid") + # cmd.append("--fork") + # cmd.append("--kill-child") + # cmd.append("--mount-proc") + + uflags = linux.CLONE_NEWPID + nslist = ["pid_for_children"] + uflags |= linux.CLONE_NEWNS + nslist.append("mnt") + uflags |= linux.CLONE_NEWNET + nslist.append("net") + + # Before values + pid = os.getpid() + nsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist} + + # + # UNSHARE + # + create_thread_test() + + ppid = os.getppid() + ppid_fd = linux.pidfd_open(ppid) + linux.unshare(uflags) + + # random syscall's fail until we fork a child to establish the new pid namespace. + global child_pid # pylint: disable=global-statement + child_pid = os.fork() + if not child_pid: + logging.info("In child sleeping") + time.sleep(1200) + sys.exit(1) + + # verify after values differ + nnsdict = {x: os.readlink(f"/tmp/mu-global-proc/{pid}/ns/{x}") for x in nslist} + assert not {k for k in nsdict if nsdict[k] == nnsdict[k]} + + # Remount / and any future mounts below it as private + commander.cmd_raises("mount --make-rprivate /") + # Mount a new /proc in our new namespace + commander.cmd_raises("mount -t proc proc /proc") + + # + # In NEW NS + # + + cid = os.fork() + if not cid: + logging.info("In second child sleeping") + time.sleep(4) + sys.exit(1) + logging.info("Waiting for second child") + os.waitpid(cid, 0) + + try: + create_thread_test() + except Exception as error: + print(error) + + # + # RESTORE + # + + logging.info("In new namespace, restoring old") + # Make sure we can go back, not sure since this is PID namespace, but maybe + restore_namespace(ppid_fd, uflags) + + # verify after values the same + nnsdict = {x: os.readlink(f"/proc/self/ns/{x}") for x in nslist} + assert nsdict == nnsdict + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="More -v's, more verbose" + ) + ap.add_argument("rest", nargs=argparse.REMAINDER) + args = ap.parse_args() + + level = logging.DEBUG if args.verbose else logging.INFO + if args.verbose > 1: + global very_verbose # pylint: disable=global-statement + very_verbose = True + logging.basicConfig( + level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s" + ) + + status = 4 + try: + run(args) + except KeyboardInterrupt: + logging.info("exiting (main), received KeyboardInterrupt in main") + except Exception as error: + logging.info("exiting (main), unexpected exception %s", error, exc_info=True) + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/mutini.py b/tests/topotests/munet/mutini.py new file mode 100755 index 000000000000..e5f993171400 --- /dev/null +++ b/tests/topotests/munet/mutini.py @@ -0,0 +1,432 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# January 28 2023, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2023, LabN Consulting, L.L.C. +# +"""A tiny init for namespaces in python inspired by the C program tini.""" + + +# pylint: disable=global-statement +import argparse +import errno +import logging +import os +import re +import shlex +import signal +import subprocess +import sys + +from signal import Signals as S + + +try: + from munet import linux +except ModuleNotFoundError: + # We cannot use relative imports and still run this module directly as a script, and + # there are some use cases where we want to run this file as a script. + sys.path.append(os.path.dirname(os.path.realpath(__file__))) + import linux + + +class g: + """Global variables for our program.""" + + child_pid = -1 + orig_pid = os.getpid() + exit_signal = False + pid_status_cache = {} + restore_signals = set() + very_verbose = False + + +unshare_flags = { + "C": linux.CLONE_NEWCGROUP, + "i": linux.CLONE_NEWIPC, + "m": linux.CLONE_NEWNS, + "n": linux.CLONE_NEWNET, + "p": linux.CLONE_NEWPID, + "u": linux.CLONE_NEWUTS, + "T": linux.CLONE_NEWTIME, +} + + +ignored_signals = { + S.SIGTTIN, + S.SIGTTOU, +} +abort_signals = { + S.SIGABRT, + S.SIGBUS, + S.SIGFPE, + S.SIGILL, + S.SIGKILL, + S.SIGSEGV, + S.SIGSTOP, + S.SIGSYS, + S.SIGTRAP, +} +no_prop_signals = abort_signals | ignored_signals | {S.SIGCHLD} + + +def vdebug(*args, **kwargs): + if g.very_verbose: + logging.debug(*args, **kwargs) + + +def get_pid_status_item(status, stat): + m = re.search(rf"(?:^|\n){stat}:\t(.*)(?:\n|$)", status) + return m.group(1).strip() if m else None + + +def pget_pid_status_item(pid, stat): + if pid not in g.pid_status_cache: + with open(f"/proc/{pid}/status", "r", encoding="utf-8") as f: + g.pid_status_cache[pid] = f.read().strip() + return get_pid_status_item(g.pid_status_cache[pid], stat).strip() + + +def get_pid_name(pid): + try: + return get_pid_status_item(g.pid_status_cache[pid], "Name") + except Exception: + return str(pid) + + +# def init_get_child_pids(): +# """Return list of "children" pids. +# We consider any process with a 0 parent pid to also be our child as it +# nsentered our pid namespace from an external parent. +# """ +# g.pid_status_cache.clear() +# pids = (int(x) for x in os.listdir("/proc") if x.isdigit() and x != "1") +# return ( +# x for x in pids if x == g.child_pid or pget_pid_status_item(x, "PPid") == "0" +# ) + + +def exit_with_status(status): + if os.WIFEXITED(status): + ec = os.WEXITSTATUS(status) + elif os.WIFSIGNALED(status): + ec = 0x80 | os.WTERMSIG(status) + else: + ec = 255 + logging.debug("exiting with code %s", ec) + sys.exit(ec) + + +def waitpid(tag): + logging.debug("%s: waitid for exiting process", tag) + idobj = os.waitid(os.P_ALL, 0, os.WEXITED) + pid = idobj.si_pid + status = idobj.si_status + + if pid != g.child_pid: + pidname = get_pid_name(pid) + logging.debug( + "%s: reaped zombie %s (%s) w/ status %s", tag, pid, pidname, status + ) + return + + logging.debug("reaped child with status %s", status) + exit_with_status(status) + # NOTREACHED + + +def sig_trasmit(signum, _): + signame = signal.Signals(signum).name + if g.child_pid == -1: + # We've received a signal after setting up to be init proc + # but prior to fork or fork returning with child pid + logging.debug("received %s prior to child exec, exiting", signame) + sys.exit(0x80 | signum) + + try: + os.kill(g.child_pid, signum) + except OSError as error: + if error.errno != errno.ESRCH: + logging.error( + "error forwarding signal %s to child, exiting: %s", signum, error + ) + sys.exit(0x80 | signum) + logging.debug("child pid %s exited prior to signaling", g.child_pid) + + +def sig_sigchld(signum, _): + assert signum == S.SIGCHLD + try: + waitpid("SIGCHLD") + except ChildProcessError as error: + logging.warning("got SIGCHLD but no pid to wait on: %s", error) + + +def setup_init_signals(): + valid = set(signal.valid_signals()) + named = set(x.value for x in signal.Signals) + for snum in sorted(named): + if snum not in valid: + continue + if S.SIGRTMIN <= snum <= S.SIGRTMAX: + continue + + sname = signal.Signals(snum).name + if snum == S.SIGCHLD: + vdebug("installing local handler for %s", sname) + signal.signal(snum, sig_sigchld) + g.restore_signals.add(snum) + elif snum in ignored_signals: + vdebug("installing ignore handler for %s", sname) + signal.signal(snum, signal.SIG_IGN) + g.restore_signals.add(snum) + elif snum in abort_signals: + vdebug("leaving default handler for %s", sname) + # signal.signal(snum, signal.SIG_DFL) + else: + vdebug("installing trasmit signal handler for %s", sname) + try: + signal.signal(snum, sig_trasmit) + g.restore_signals.add(snum) + except OSError as error: + logging.warning( + "failed installing signal handler for %s: %s", sname, error + ) + + +def new_process_group(): + """Create and lead a new process group. + + This function will create a new process group if we are not yet leading one, and + additionally foreground said process group in our session. This foregrounding + action is copied from tini, and I believe serves a purpose when serving as init + for a container (e.g., podman). + """ + pid = os.getpid() + try: + pgid = os.getpgrp() + if pgid == pid: + logging.debug("already process group leader %s", pgid) + else: + logging.debug("creating new process group %s", pid) + os.setpgid(pid, 0) + except Exception as error: + logging.warning("unable to get new process group: %s", error) + return + + # Block these in order to allow foregrounding, otherwise we'd get SIGTTOU blocked + signal.signal(S.SIGTTIN, signal.SIG_IGN) + signal.signal(S.SIGTTOU, signal.SIG_IGN) + fd = sys.stdin.fileno() + if not os.isatty(fd): + logging.debug("stdin not a tty no foregrounding required") + else: + try: + # This will error if our session no longer associated with controlling tty. + pgid = os.tcgetpgrp(fd) + if pgid == pid: + logging.debug("process group already in foreground %s", pgid) + else: + logging.debug("making us the foreground pgid backgrounding %s", pgid) + os.tcsetpgrp(fd, pid) + except OSError as error: + if error.errno == errno.ENOTTY: + logging.debug("session is no longer associated with controlling tty") + else: + logging.warning("unable to foreground pgid %s: %s", pid, error) + signal.signal(S.SIGTTIN, signal.SIG_DFL) + signal.signal(S.SIGTTOU, signal.SIG_DFL) + + +def is_creating_pid_namespace(): + p1name = subprocess.check_output( + "readlink /proc/self/pid", stderr=subprocess.STDOUT, shell=True + ) + p2name = subprocess.check_output( + "readlink /proc/self/pid_for_children", stderr=subprocess.STDOUT, shell=True + ) + return p1name != p2name + + +def be_init(new_pg, exec_args): + # + # Arrange for us to be killed when our parent dies, this will subsequently also kill + # all procs in any PID namespace we are init for. + # + logging.debug("set us to be SIGKILLed when parent exits") + linux.set_parent_death_signal(signal.SIGKILL) + + # If we are createing a new PID namespace for children... + if g.orig_pid != 1: + logging.debug("started as pid %s", g.orig_pid) + # assert is_creating_pid_namespace() + + # Fork to become pid 1 + logging.debug("forking to become pid 1") + child_pid = os.fork() + if child_pid: + logging.debug("in parent waiting on child pid %s to exit", child_pid) + status = os.wait() + logging.debug("got child exit status %s", status) + exit_with_status(status) + # NOTREACHED + + # We must be pid 1 now. + logging.debug("in child as pid %s", os.getpid()) + assert os.getpid() == 1 + + # We need a new /proc now. + logging.debug("mount new /proc") + linux.mount("proc", "/proc", "proc") + + # If the parent exists kill us using SIGKILL + logging.debug("set us to be SIGKILLed when parent exits") + linux.set_parent_death_signal(signal.SIGKILL) + + if not exec_args: + if not new_pg: + logging.debug("no exec args, no new process group") + # # if 0 == os.getpgid(0): + # status = os.setpgid(0, 1) + # logging.debug("os.setpgid(0, 1) == %s", status) + else: + logging.debug("no exec args, creating new process group") + # No exec so we are the "child". + new_process_group() + + # Reap children as init process + vdebug("installing local handler for SIGCHLD") + signal.signal(signal.SIGCHLD, sig_sigchld) + + while True: + logging.info("init: waiting to reap zombies") + linux.pause() + # NOTREACHED + + # Set (parent) signal handlers before any fork to avoid race + setup_init_signals() + + logging.debug("forking to execute child") + g.child_pid = os.fork() + if g.child_pid == 0: + # In child, restore signals to default handling: + for snum in g.restore_signals: + signal.signal(snum, signal.SIG_DFL) + + # XXX is a new pg right? + new_process_group() + logging.debug("child: executing '%s'", shlex.join(exec_args)) + os.execvp(exec_args[0], exec_args) + # NOTREACHED + + while True: + logging.info("parent: waiting for child pid %s to exit", g.child_pid) + waitpid("parent") + + +def unshare(flags): + """Unshare into new namespaces.""" + uflags = 0 + for flag in flags: + if flag not in unshare_flags: + raise ValueError(f"unknown unshare flag '{flag}'") + uflags |= unshare_flags[flag] + new_pid = bool(uflags & linux.CLONE_NEWPID) + new_mnt = bool(uflags & linux.CLONE_NEWNS) + + logging.debug("unshareing with flags: %s", linux.clone_flag_string(uflags)) + linux.unshare(uflags) + + if new_pid and not new_mnt: + try: + # If we are not creating new mount namspace, remount /proc private + # so that our mount of a new /proc doesn't affect parent namespace + logging.debug("remount /proc recursive private") + linux.mount("none", "/proc", None, linux.MS_REC | linux.MS_PRIVATE) + except OSError as error: + # EINVAL is OK b/c /proc not mounted may cause an error + if error.errno != errno.EINVAL: + raise + if new_mnt: + # Remount root as recursive private. + logging.debug("remount / recursive private") + linux.mount("none", "/", None, linux.MS_REC | linux.MS_PRIVATE) + + # if new_pid: + # logging.debug("mount new /proc") + # linux.mount("proc", "/proc", "proc") + + return new_pid + + +def main(): + # + # Parse CLI args. + # + + ap = argparse.ArgumentParser() + ap.add_argument( + "-P", + "--no-proc-group", + action="store_true", + help="set to inherit the process group", + ) + valid_flags = "".join(unshare_flags) + ap.add_argument( + "--unshare-flags", + help=( + f"string of unshare(1) flags. Supported values from '{valid_flags}'." + " 'm' will remount `/` recursive private. 'p' will remount /proc" + " and fork, and the child will be signaled to exit on exit of parent.." + ), + ) + ap.add_argument( + "-v", dest="verbose", action="count", default=0, help="more -v's, more verbose" + ) + ap.add_argument("rest", nargs=argparse.REMAINDER) + args = ap.parse_args() + + # + # Setup logging. + # + + level = logging.DEBUG if args.verbose else logging.INFO + if args.verbose > 1: + g.very_verbose = True + logging.basicConfig( + level=level, format="%(asctime)s mutini: %(levelname)s: %(message)s" + ) + + # + # Run program + # + + status = 5 + try: + new_pid = False + if args.unshare_flags: + new_pid = unshare(args.unshare_flags) + + if g.orig_pid != 1 and not new_pid: + # Simply hold the namespaces + while True: + logging.info("holding namespace waiting to be signaled to exit") + linux.pause() + # NOTREACHED + + be_init(not args.no_proc_group, args.rest) + # NOTREACHED + logging.critical("Exited from be_init!") + except KeyboardInterrupt: + logging.info("exiting (main), received KeyboardInterrupt in main") + status = 0x80 | signal.SIGINT + except Exception as error: + logging.info("exiting (main), do to exception %s", error, exc_info=True) + + sys.exit(status) + + +if __name__ == "__main__": + main() diff --git a/tests/topotests/munet/native.py b/tests/topotests/munet/native.py new file mode 100644 index 000000000000..fecf709d1aaa --- /dev/null +++ b/tests/topotests/munet/native.py @@ -0,0 +1,2941 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# October 1 2021, Christian Hopps <chopps@labn.net> +# +# Copyright (c) 2021-2022, LabN Consulting, L.L.C. +# +# pylint: disable=protected-access +"""A module that defines objects for standalone use.""" +import asyncio +import errno +import getpass +import ipaddress +import logging +import os +import random +import re +import shlex +import socket +import subprocess +import time + +from . import cli +from .base import BaseMunet +from .base import Bridge +from .base import Commander +from .base import LinuxNamespace +from .base import MunetError +from .base import Timeout +from .base import _async_get_exec_path +from .base import _get_exec_path +from .base import cmd_error +from .base import commander +from .base import fsafe_name +from .base import get_exec_path_host +from .config import config_subst +from .config import config_to_dict_with_key +from .config import find_matching_net_config +from .config import find_with_kv +from .config import merge_kind_config + + +class L3ContainerNotRunningError(MunetError): + """Exception if no running container exists.""" + + +def get_loopback_ips(c, nid): + if ip := c.get("ip"): + if ip == "auto": + return [ipaddress.ip_interface("10.255.0.0/32") + nid] + if isinstance(ip, str): + return [ipaddress.ip_interface(ip)] + return [ipaddress.ip_interface(x) for x in ip] + return [] + + +def make_ip_network(net, inc): + n = ipaddress.ip_network(net) + return ipaddress.ip_network( + (n.network_address + inc * n.num_addresses, n.prefixlen) + ) + + +def make_ip_interface(ia, inc): + ia = ipaddress.ip_interface(ia) + # this turns into a /32 fix this + ia = ia + ia.network.num_addresses * inc + # IPv6 + ia = ipaddress.ip_interface(str(ia).replace("/32", "/24").replace("/128", "/64")) + return ia + + +def get_ip_network(c, brid, ipv6=False): + ip = c.get("ipv6" if ipv6 else "ip") + if ip and str(ip) != "auto": + try: + ifip = ipaddress.ip_interface(ip) + if ifip.ip == ifip.network.network_address: + return ifip.network + return ifip + except ValueError: + return ipaddress.ip_network(ip) + if ipv6: + return make_ip_interface("fc00::fe/64", brid) + return make_ip_interface("10.0.0.254/24", brid) + + +def parse_pciaddr(devaddr): + comp = re.match( + "(?:([0-9A-Fa-f]{4}):)?([0-9A-Fa-f]{2}):([0-9A-Fa-f]{2}).([0-7])", devaddr + ).groups() + if comp[0] is None: + comp[0] = "0000" + return [int(x, 16) for x in comp] + + +def read_int_value(path): + return int(open(path, encoding="ascii").read()) + + +def read_str_value(path): + return open(path, encoding="ascii").read().strip() + + +def read_sym_basename(path): + return os.path.basename(os.readlink(path)) + + +async def to_thread(func): + """to_thread for python < 3.9.""" + try: + return await asyncio.to_thread(func) + except AttributeError: + logging.warning("Using backport to_thread") + return await asyncio.get_running_loop().run_in_executor(None, func) + + +def convert_ranges_to_bitmask(ranges): + bitmask = 0 + for r in ranges.split(","): + if "-" not in r: + bitmask |= 1 << int(r) + else: + x, y = (int(x) for x in r.split("-")) + for b in range(x, y + 1): + bitmask |= 1 << b + return bitmask + + +class L2Bridge(Bridge): + """A linux bridge with no IP network address.""" + + def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None): + """Create a linux Bridge.""" + super().__init__(name=name, unet=unet, logger=logger, mtu=mtu) + + self.config = config if config else {} + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + await super()._async_delete() + + +class L3Bridge(Bridge): + """A linux bridge with associated IP network address.""" + + def __init__(self, name=None, unet=None, logger=None, mtu=None, config=None): + """Create a linux Bridge.""" + super().__init__(name=name, unet=unet, logger=logger, mtu=mtu) + + self.config = config if config else {} + + self.ip_interface = get_ip_network(self.config, self.id) + if hasattr(self.ip_interface, "network"): + self.ip_address = self.ip_interface.ip + self.ip_network = self.ip_interface.network + self.cmd_raises(f"ip addr add {self.ip_interface} dev {name}") + else: + self.ip_address = None + self.ip_network = self.ip_interface + + self.logger.debug("%s: set IPv4 network address to %s", self, self.ip_interface) + self.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + + self.ip6_interface = None + if self.unet.ipv6_enable: + self.ip6_interface = get_ip_network(self.config, self.id, ipv6=True) + if hasattr(self.ip6_interface, "network"): + self.ip6_address = self.ip6_interface.ip + self.ip6_network = self.ip6_interface.network + self.cmd_raises(f"ip addr add {self.ip6_interface} dev {name}") + else: + self.ip6_address = None + self.ip6_network = self.ip6_interface + + self.logger.debug( + "%s: set IPv6 network address to %s", self, self.ip_interface + ) + self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + + self.is_nat = self.config.get("nat", False) + if self.is_nat: + self.cmd_raises( + "iptables -t nat -A POSTROUTING " + f"-s {self.ip_network} ! -d {self.ip_network} " + f"! -o {self.name} -j MASQUERADE" + ) + + def get_intf_addr(self, ifname, ipv6=False): + # None is a valid interface, we have the same address for all interfaces + # just make sure they aren't asking for something we don't have. + if ifname is not None and ifname not in self.intfs: + return None + return self.ip6_interface if ipv6 else self.ip_interface + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + if self.config.get("nat", False): + self.cmd_status( + "iptables -t nat -D POSTROUTING " + f"-s {self.ip_network} ! -d {self.ip_network} " + f"! -o {self.name} -j MASQUERADE" + ) + await super()._async_delete() + + +class NodeMixin: + """Node attributes and functionality.""" + + next_ord = 1 + + @classmethod + def _get_next_ord(cls): + # Do not use `cls` here b/c that makes the variable class specific + n = L3NodeMixin.next_ord + L3NodeMixin.next_ord = n + 1 + return n + + def __init__(self, *args, config=None, **kwargs): + """Create a Node.""" + super().__init__(*args, **kwargs) + + self.config = config if config else {} + config = self.config + + self.id = int(config["id"]) if "id" in config else self._get_next_ord() + + self.cmd_p = None + self.container_id = None + self.cleanup_called = False + + # Clear and create rundir early + assert self.unet is not None + self.rundir = self.unet.rundir.joinpath(self.name) + commander.cmd_raises(f"rm -rf {self.rundir}") + commander.cmd_raises(f"mkdir -p {self.rundir}") + + def _shebang_prep(self, config_key): + cmd = self.config.get(config_key, "").strip() + if not cmd: + return [] + + script_name = fsafe_name(config_key) + + # shell_cmd is a union and can be boolean or string + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + # i.e., "shell: true" + shell_cmd = "/bin/bash" + else: + # i.e., "shell: false" + shell_cmd = "" + + # If we have a shell_cmd then we create a cleanup_cmds file in run_cmd + # and volume mounted it + if shell_cmd: + # Create cleanup cmd file + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + + # Write out our cleanup cmd file at this time too. + cmdpath = os.path.join(self.rundir, f"{script_name}.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(f"#!{shell_cmd}\n") + cmdfile.write(cmd) + cmdfile.flush() + commander.cmd_raises(f"chmod 755 {cmdpath}") + + if self.container_id: + # XXX this counts on it being mounted in container, ugly + cmds = [f"/tmp/{script_name}.shebang"] + else: + cmds = [cmdpath] + else: + cmds = [] + if isinstance(cmd, str): + cmds.extend(shlex.split(cmd)) + else: + cmds.extend(cmd) + cmds = [ + x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds + ] + cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds] + cmds = [x.replace("%NAME%", str(self.name)) for x in cmds] + + return cmds + + async def _async_shebang_cmd(self, config_key, warn=True): + cmds = self._shebang_prep(config_key) + if not cmds: + return 0 + + rc, o, e = await self.async_cmd_status(cmds, warn=warn) + if not rc and warn and (o or e): + self.logger.info( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + elif rc and warn: + self.logger.warning( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + else: + self.logger.debug( + f"async_shebang_cmd ({config_key}): %s", cmd_error(rc, o, e) + ) + + return rc + + def has_run_cmd(self) -> bool: + return bool(self.config.get("cmd", "").strip()) + + async def get_proc_child_pid(self, p): + # commander is right for both unshare inline (our proc pidns) + # and non-inline (root pidns). + + # This doesn't work b/c we can't get back to the root pidns + + rootcmd = self.unet.rootcmd + pgrep = rootcmd.get_exec_path("pgrep") + spid = str(p.pid) + for _ in Timeout(4): + if p.returncode is not None: + self.logger.debug("%s: proc %s exited before getting child", self, p) + return None + + rc, o, e = await rootcmd.async_cmd_status( + [pgrep, "-o", "-P", spid], warn=False + ) + if rc == 0: + return int(o.strip()) + + await asyncio.sleep(0.1) + self.logger.debug( + "%s: no child of proc %s: %s", self, p, cmd_error(rc, o, e) + ) + self.logger.warning("%s: timeout getting child pid of proc %s", self, p) + return None + + async def run_cmd(self): + """Run the configured commands for this node.""" + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + cmds = self._shebang_prep("cmd") + if not cmds: + return + + stdout = open(os.path.join(self.rundir, "cmd.out"), "wb") + stderr = open(os.path.join(self.rundir, "cmd.err"), "wb") + self.cmd_pid = None + self.cmd_p = await self.async_popen( + cmds, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + start_new_session=True, # allows us to signal all children to exit + ) + + # If our process is actually the child of an nsenter fetch its pid. + if self.nsenter_fork: + self.cmd_pid = await self.get_proc_child_pid(self.cmd_p) + + self.logger.debug( + "%s: async_popen %s => %s (cmd_pid %s)", + self, + cmds, + self.cmd_p.pid, + self.cmd_pid, + ) + + self.pytest_hook_run_cmd(stdout, stderr) + + return self.cmd_p + + async def _async_cleanup_cmd(self): + """Run the configured cleanup commands for this node. + + This function is called by subclass' async_cleanup_cmd + """ + self.cleanup_called = True + + return await self._async_shebang_cmd("cleanup-cmd") + + def has_cleanup_cmd(self) -> bool: + return bool(self.config.get("cleanup-cmd", "").strip()) + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + return await self._async_cleanup_cmd() + + def has_ready_cmd(self) -> bool: + return bool(self.config.get("ready-cmd", "").strip()) + + async def async_ready_cmd(self): + """Run the configured ready commands for this node.""" + return not await self._async_shebang_cmd("ready-cmd", warn=False) + + def cmd_completed(self, future): + self.logger.debug("%s: cmd completed callback", self) + try: + status = future.result() + self.logger.debug( + "%s: node cmd_p completed result: %s cmd: %s", self, status, self.cmd_p + ) + self.cmd_pid = None + self.cmd_p = None + except asyncio.CancelledError: + # Should we stop the container if we have one? + self.logger.debug("%s: node cmd_p.wait() canceled", future) + + def pytest_hook_run_cmd(self, stdout, stderr): + """Handle pytest options related to running the node cmd. + + This function does things such as launch tail'ing windows + on the given files if requested by the user. + + Args: + stdout: file-like object with a ``name`` attribute, or a path to a file. + stderr: file-like object with a ``name`` attribute, or a path to a file. + """ + if not self.unet: + return + + outopt = self.unet.cfgopt.getoption("--stdout") + outopt = outopt if outopt is not None else "" + if outopt == "all" or self.name in outopt.split(","): + outname = stdout.name if hasattr(stdout, "name") else stdout + self.run_in_window(f"tail -F {outname}", title=f"O:{self.name}") + + if stderr: + erropt = self.unet.cfgopt.getoption("--stderr") + erropt = erropt if erropt is not None else "" + if erropt == "all" or self.name in erropt.split(","): + errname = stderr.name if hasattr(stderr, "name") else stderr + self.run_in_window(f"tail -F {errname}", title=f"E:{self.name}") + + def pytest_hook_open_shell(self): + if not self.unet: + return + + gdbcmd = self.config.get("gdb-cmd") + shellopt = self.unet.cfgopt.getoption("--gdb", "") + should_gdb = gdbcmd and (shellopt == "all" or self.name in shellopt.split(",")) + use_emacs = self.unet.cfgopt.getoption("--gdb-use-emacs", False) + + if should_gdb and not use_emacs: + cmds = self.config.get("gdb-target-cmds", []) + for cmd in cmds: + gdbcmd += f" '-ex={cmd}'" + + bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",") + for bp in bps: + gdbcmd += f" '-ex=b {bp}'" + + cmds = self.config.get("gdb-run-cmd", []) + for cmd in cmds: + gdbcmd += f" '-ex={cmd}'" + + self.run_in_window(gdbcmd) + elif should_gdb and use_emacs: + gdbcmd = gdbcmd.replace("gdb ", "gdb -i=mi ") + ecbin = self.get_exec_path("emacsclient") + # output = self.cmd_raises( + # [ecbin, "--eval", f"(gdb \"{gdbcmd} -ex='p 123456'\")"] + # ) + _ = self.cmd_raises([ecbin, "--eval", f'(gdb "{gdbcmd}")']) + + # can't figure out how to wait until symbols are loaded, until we do we just + # have to wait "long enough" for the symbol load to finish :/ + # for _ in range(100): + # output = self.cmd_raises( + # [ + # ecbin, + # "--eval", + # f"gdb-first-prompt", + # ] + # ) + # if output == "nil\n": + # break + # time.sleep(0.25) + + time.sleep(10) + + cmds = self.config.get("gdb-target-cmds", []) + for cmd in cmds: + # we may want to quote quotes in the cmd string + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + + bps = self.unet.cfgopt.getoption("--gdb-breakpoints", "").split(",") + for bp in bps: + cmd = f"br {bp}" + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + + cmds = self.config.get("gdb-run-cmds", []) + for cmd in cmds: + # we may want to quote quotes in the cmd string + self.cmd_raises( + [ + ecbin, + "--eval", + f'(gud-gdb-run-command-fetch-lines "{cmd}" "*gud-gdb*")', + ] + ) + gdbcmd += f" '-ex={cmd}'" + + shellopt = self.unet.cfgopt.getoption("--shell") + shellopt = shellopt if shellopt else "" + if shellopt == "all" or self.name in shellopt.split(","): + self.run_in_window("bash") + + async def _async_delete(self): + self.logger.debug("%s: NodeMixin sub-class _async_delete", self) + + if self.cmd_p: + await self.async_cleanup_proc(self.cmd_p, self.cmd_pid) + self.cmd_p = None + + # Next call users "cleanup_cmd:" + try: + if not self.cleanup_called: + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + # delete the LinuxNamespace/InterfaceMixin + await super()._async_delete() + + +class SSHRemote(NodeMixin, Commander): + """SSHRemote a node representing an ssh connection to something.""" + + def __init__( + self, + name, + server, + port=22, + user=None, + password=None, + idfile=None, + **kwargs, + ): + super().__init__(name, **kwargs) + + self.logger.debug("%s: creating", self) + + # Things done in LinuxNamepsace we need to replicate here. + self.rundir = self.unet.rundir.joinpath(self.name) + self.unet.cmd_raises(f"rm -rf {self.rundir}") + self.unet.cmd_raises(f"mkdir -p {self.rundir}") + + self.mgmt_ip = None + self.mgmt_ip6 = None + + self.port = port + + if user: + self.user = user + elif "SUDO_USER" in os.environ: + self.user = os.environ["SUDO_USER"] + else: + self.user = getpass.getuser() + self.password = password + self.idfile = idfile + + self.server = f"{self.user}@{server}" + + # Setup our base `pre-cmd` values + # + # We maybe should add environment variable transfer here in particular + # MUNET_NODENAME. The problem is the user has to explicitly approve + # of SendEnv variables. + self.__base_cmd = [ + get_exec_path_host("sudo"), + "-E", + f"-u{self.user}", + get_exec_path_host("ssh"), + ] + if port != 22: + self.__base_cmd.append(f"-p{port}") + self.__base_cmd.append("-q") + self.__base_cmd.append("-oStrictHostKeyChecking=no") + self.__base_cmd.append("-oUserKnownHostsFile=/dev/null") + if self.idfile: + self.__base_cmd.append(f"-i{self.idfile}") + # Would be nice but has to be accepted by server config so not very useful. + # self.__base_cmd.append("-oSendVar='TEST'") + self.__base_cmd_pty = list(self.__base_cmd) + self.__base_cmd_pty.append("-t") + self.__base_cmd.append(self.server) + self.__base_cmd_pty.append(self.server) + # self.set_pre_cmd(pre_cmd, pre_cmd_tty) + + self.logger.info("%s: created", self) + + def has_ready_cmd(self) -> bool: + return bool(self.config.get("ready-cmd", "").strip()) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, **kwargs): + pre_cmd = [] + if self.unet: + pre_cmd = self.unet._get_pre_cmd(False, use_pty, ns_only=False, **kwargs) + if ns_only: + return pre_cmd + + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else list(pre_cmd) + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If cmd is a string then [cmd] is returned, for most other + node types ["bash", "-c", cmd] is returned but in our case + ssh is the shell. + + Args: + cmd: list or string representing the command to execute. + str_shell: if True and `cmd` is a string then run the + command using bash -c + Returns: + list of commands to execute. + """ + return [cmd] if isinstance(cmd, str) else cmd + + +# Would maybe like to refactor this into L3 and Node +class L3NodeMixin(NodeMixin): + """A linux namespace with IP attributes.""" + + def __init__(self, *args, unet=None, **kwargs): + """Create an L3Node.""" + # logging.warning( + # "L3NodeMixin: config %s unet %s kwargs %s", config, unet, kwargs + # ) + super().__init__(*args, unet=unet, **kwargs) + + self.mgmt_ip = None # set in parser.py + self.mgmt_ip6 = None # set in parser.py + self.host_intfs = {} + self.phy_intfs = {} + self.phycount = 0 + self.phy_odrivers = {} + self.tapmacs = {} + + self.intf_tc_count = 0 + + # super().__init__(name=name, **kwargs) + + self.mount_volumes() + + # ----------------------- + # Setup node's networking + # ----------------------- + if not unet.ipv6_enable: + # Disable IPv6 + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1") + else: + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + + self.next_p2p_network = ipaddress.ip_network(f"10.254.{self.id}.0/31") + self.next_p2p_network6 = ipaddress.ip_network(f"fcff:ffff:{self.id:02x}::/127") + + self.loopback_ip = None + self.loopback_ips = get_loopback_ips(self.config, self.id) + self.loopback_ip = self.loopback_ips[0] if self.loopback_ips else None + if self.loopback_ip: + self.cmd_raises_nsonly(f"ip addr add {self.loopback_ip} dev lo") + self.cmd_raises_nsonly("ip link set lo up") + for i, ip in enumerate(self.loopback_ips[1:]): + self.cmd_raises_nsonly(f"ip addr add {ip} dev lo:{i}") + + # ------------------- + # Setup node's rundir + # ------------------- + + # Not host path based, but we assume same + self.set_ns_cwd(self.rundir) + + # Save the namespace pid + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + # Create a hosts file to map our name + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + if hasattr(self, "bind_mount"): + self.bind_mount(hosts_file, "/etc/hosts") + + async def console( + self, + concmd, + prompt=r"(^|\r?\n)[^#\$]*[#\$] ", + is_bourne=True, + user=None, + password=None, + expects=None, + sends=None, + use_pty=False, + will_echo=False, + logfile_prefix="console", + trace=True, + **kwargs, + ): + """Create a REPL (read-eval-print-loop) driving a console. + + Args: + concmd: string or list to popen with, or an already open socket + prompt: the REPL prompt to look for, the function returns when seen + is_bourne: True if the console is a bourne shell + user: user name to log in with + password: password to log in with + expects: a list of regex other than the prompt, the standard user, or + password to look for. "ogin:" or "[Pp]assword:"r. + sends: what to send when an element of `expects` matches. Can be the + empty string to send nothing. + use_pty: true for pty based expect, otherwise uses popen (pipes/files) + will_echo: bash is buggy in that it echo's to non-tty unlike any other + sh/ksh, set this value to true if running back + logfile_prefix: prefix for 3 logfiles opened to track the console i/o + trace: trace the send/expect sequence + **kwargs: kwargs passed on the _spawn. + """ + lfname = os.path.join(self.rundir, f"{logfile_prefix}-log.txt") + logfile = open(lfname, "a+", encoding="utf-8") + logfile.write("-- start logging for: '{}' --\n".format(concmd)) + + lfname = os.path.join(self.rundir, f"{logfile_prefix}-read-log.txt") + logfile_read = open(lfname, "a+", encoding="utf-8") + logfile_read.write("-- start read logging for: '{}' --\n".format(concmd)) + + lfname = os.path.join(self.rundir, f"{logfile_prefix}-send-log.txt") + logfile_send = open(lfname, "a+", encoding="utf-8") + logfile_send.write("-- start send logging for: '{}' --\n".format(concmd)) + + expects = [] if expects is None else expects + sends = [] if sends is None else sends + if user: + expects.append("ogin:") + sends.append(user + "\n") + if password is not None: + expects.append("assword:") + sends.append(password + "\n") + repl = await self.shell_spawn( + concmd, + prompt, + expects=expects, + sends=sends, + use_pty=use_pty, + will_echo=will_echo, + is_bourne=is_bourne, + logfile=logfile, + logfile_read=logfile_read, + logfile_send=logfile_send, + trace=trace, + **kwargs, + ) + return repl + + async def monitor( + self, + sockpath, + prompt=r"\(qemu\) ", + ): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(sockpath) + + pfx = os.path.basename(sockpath) + + lfname = os.path.join(self.rundir, f"{pfx}-log.txt") + logfile = open(lfname, "a+", encoding="utf-8") + logfile.write("-- start logging for: '{}' --\n".format(sock)) + + lfname = os.path.join(self.rundir, f"{pfx}-read-log.txt") + logfile_read = open(lfname, "a+", encoding="utf-8") + logfile_read.write("-- start read logging for: '{}' --\n".format(sock)) + + p = self.spawn(sock, prompt, logfile=logfile, logfile_read=logfile_read) + from .base import ShellWrapper # pylint: disable=C0415 + + p.send("\n") + return ShellWrapper(p, prompt, None, will_echo=True, escape_ansi=True) + + def mount_volumes(self): + for m in self.config.get("volumes", []): + if isinstance(m, str): + s = m.split(":", 1) + if len(s) == 1: + self.tmpfs_mount(s[0]) + else: + spath = s[0] + if spath[0] == ".": + spath = os.path.abspath( + os.path.join(self.unet.config_dirname, spath) + ) + self.bind_mount(spath, s[1]) + continue + raise NotImplementedError("complex mounts for non-containers") + + def get_ifname(self, netname): + for c in self.config["connections"]: + if c["to"] == netname: + return c["name"] + return None + + def set_lan_addr(self, switch, cconf): + if ip := cconf.get("ip"): + ipaddr = ipaddress.ip_interface(ip) + assert ipaddr.version == 4 + elif self.unet.autonumber and "ip" not in cconf: + self.logger.debug( + "%s: prefixlen of switch %s is %s", + self, + switch.name, + switch.ip_network.prefixlen, + ) + n = switch.ip_network + ipaddr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen)) + else: + ipaddr = None + + if ip := cconf.get("ipv6"): + ip6addr = ipaddress.ip_interface(ip) + assert ipaddr.version == 6 + elif self.unet.ipv6_enable and self.unet.autonumber and "ipv6" not in cconf: + self.logger.debug( + "%s: prefixlen of switch %s is %s", + self, + switch.name, + switch.ip6_network.prefixlen, + ) + n = switch.ip6_network + ip6addr = ipaddress.ip_interface((n.network_address + self.id, n.prefixlen)) + else: + ip6addr = None + + dns_network = self.unet.topoconf.get("dns-network") + for ip in (ipaddr, ip6addr): + if not ip: + continue + ipcmd = "ip " if ip.version == 4 else "ip -6 " + if dns_network and dns_network == switch.name: + if ip.version == 4: + self.mgmt_ip = ip.ip + else: + self.mgmt_ip6 = ip.ip + ifname = cconf["name"] + self.set_intf_addr(ifname, ip) + self.logger.debug("%s: adding %s to lan intf %s", self, ip, ifname) + if not self.is_vm: + self.intf_ip_cmd(ifname, ipcmd + f"addr add {ip} dev {ifname}") + if hasattr(switch, "is_nat") and switch.is_nat: + swaddr = ( + switch.ip_address if ip.version == 4 else switch.ip6_address + ) + self.cmd_raises(ipcmd + f"route add default via {swaddr}") + + def _set_p2p_addr(self, other, cconf, occonf, ipv6=False): + ipkey = "ipv6" if ipv6 else "ip" + ipaddr = ipaddress.ip_interface(cconf[ipkey]) if cconf.get(ipkey) else None + oipaddr = ipaddress.ip_interface(occonf[ipkey]) if occonf.get(ipkey) else None + self.logger.debug( + "%s: set_p2p_addr %s %s %s", self, other.name, ipaddr, oipaddr + ) + + if not ipaddr and not oipaddr: + if self.unet.autonumber: + if ipv6: + n = self.next_p2p_network6 + self.next_p2p_network6 = make_ip_network(n, 1) + else: + n = self.next_p2p_network + self.next_p2p_network = make_ip_network(n, 1) + + ipaddr = ipaddress.ip_interface(n) + oipaddr = ipaddress.ip_interface((ipaddr.ip + 1, n.prefixlen)) + else: + return + + if ipaddr: + ifname = cconf["name"] + self.set_intf_addr(ifname, ipaddr) + self.logger.debug("%s: adding %s to p2p intf %s", self, ipaddr, ifname) + if "physical" not in cconf and not self.is_vm: + self.intf_ip_cmd(ifname, f"ip addr add {ipaddr} dev {ifname}") + + if oipaddr: + oifname = occonf["name"] + other.set_intf_addr(oifname, oipaddr) + self.logger.debug( + "%s: adding %s to other p2p intf %s", other, oipaddr, oifname + ) + if "physical" not in occonf and not other.is_vm: + other.intf_ip_cmd(oifname, f"ip addr add {oipaddr} dev {oifname}") + + def set_p2p_addr(self, other, cconf, occonf): + self._set_p2p_addr(other, cconf, occonf, ipv6=False) + if self.unet.ipv6_enable: + self._set_p2p_addr(other, cconf, occonf, ipv6=True) + + async def add_host_intf(self, hname, lname, mtu=None): + if hname in self.host_intfs: + return + self.host_intfs[hname] = lname + self.unet.rootcmd.cmd_nostatus(f"ip link set {hname} down ") + self.unet.rootcmd.cmd_raises(f"ip link set {hname} netns {self.pid}") + self.cmd_raises(f"ip link set {hname} name {lname}") + if mtu: + self.cmd_raises(f"ip link set {lname} mtu {mtu}") + self.cmd_raises(f"ip link set {lname} up") + + async def rem_host_intf(self, hname): + lname = self.host_intfs[hname] + self.cmd_raises(f"ip link set {lname} down") + self.cmd_raises(f"ip link set {lname} name {hname}") + self.cmd_raises(f"ip link set {hname} netns 1") + del self.host_intfs[hname] + + async def add_phy_intf(self, devaddr, lname): + """Add a physical inteface (i.e. mv it to vfio-pci driver. + + This is primarily useful for Qemu, but also for things like TREX or DPDK + """ + if devaddr in self.phy_intfs: + return + self.phy_intfs[devaddr] = lname + index = len(self.phy_intfs) + + _, _, off, fun = parse_pciaddr(devaddr) + doffset = off * 8 + fun + + is_virtual = self.unet.rootcmd.path_exists( + f"/sys/bus/pci/devices/{devaddr}/physfn" + ) + if is_virtual: + pfname = self.unet.rootcmd.cmd_raises( + f"ls -1 /sys/bus/pci/devices/{devaddr}/physfn/net" + ).strip() + pdevaddr = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/physfn") + _, _, poff, pfun = parse_pciaddr(pdevaddr) + poffset = poff * 8 + pfun + + offset = read_int_value( + f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_offset" + ) + stride = read_int_value( + f"/sys/bus/pci/devices/{devaddr}/physfn/sriov_stride" + ) + vf = (doffset - offset - poffset) // stride + mac = f"02:cc:cc:cc:{index:02x}:{self.id:02x}" + # Some devices require the parent to be up (e.g., ixbge) + self.unet.rootcmd.cmd_raises(f"ip link set {pfname} up") + self.unet.rootcmd.cmd_raises(f"ip link set {pfname} vf {vf} mac {mac}") + self.unet.rootcmd.cmd_status(f"ip link set {pfname} vf {vf} trust on") + self.tapmacs[devaddr] = mac + + self.logger.info("Adding physical PCI device %s as %s", devaddr, lname) + + # Get interface name and set to down if present + ec, ifname, _ = self.unet.rootcmd.cmd_status( + f"ls /sys/bus/pci/devices/{devaddr}/net/", warn=False + ) + ifname = ifname.strip() + if not ec and ifname: + # XXX Should only do this is the device is up, and then likewise return it + # up on exit self.phy_intfs_hostname[devaddr] = ifname + self.logger.info( + "Setting physical PCI device %s named %s down", devaddr, ifname + ) + self.unet.rootcmd.cmd_status( + f"ip link set {ifname} down 2> /dev/null || true" + ) + + # Get the current bound driver, and unbind + try: + driver = read_sym_basename(f"/sys/bus/pci/devices/{devaddr}/driver") + driver = driver.strip() + except Exception: + driver = "" + if driver: + if driver == "vfio-pci": + self.logger.info( + "Physical PCI device %s already bound to vfio-pci", devaddr + ) + return + self.logger.info( + "Unbinding physical PCI device %s from driver %s", devaddr, driver + ) + self.phy_odrivers[devaddr] = driver + self.unet.rootcmd.cmd_raises( + f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/unbind" + ) + + # Add the device vendor and device id to vfio-pci in case it's the first time + vendor = read_str_value(f"/sys/bus/pci/devices/{devaddr}/vendor") + devid = read_str_value(f"/sys/bus/pci/devices/{devaddr}/device") + self.logger.info("Adding device IDs %s:%s to vfio-pci", vendor, devid) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {vendor} {devid} > /sys/bus/pci/drivers/vfio-pci/new_id", warn=False + ) + + if not self.unet.rootcmd.path_exists(f"/sys/bus/pci/driver/vfio-pci/{devaddr}"): + # Bind to vfio-pci if wasn't added with new_id + self.logger.info("Binding physical PCI device %s to vfio-pci", devaddr) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/bind" + ) + + async def rem_phy_intf(self, devaddr): + """Remove a physical inteface (i.e. mv it away from vfio-pci driver. + + This is primarily useful for Qemu, but also for things like TREX or DPDK + """ + lname = self.phy_intfs.get(devaddr, "") + if lname: + del self.phy_intfs[devaddr] + + # ifname = self.phy_intfs_hostname.get(devaddr, "") + # if ifname + # del self.phy_intfs_hostname[devaddr] + + driver = self.phy_odrivers.get(devaddr, "") + if not driver: + self.logger.info( + "Physical PCI device %s was bound to vfio-pci on entry", devaddr + ) + return + + self.logger.info( + "Unbinding physical PCI device %s from driver vfio-pci", devaddr + ) + self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/vfio-pci/unbind" + ) + + self.logger.info("Binding physical PCI device %s to driver %s", devaddr, driver) + ec, _, _ = self.unet.rootcmd.cmd_status( + f"echo {devaddr} > /sys/bus/pci/drivers/{driver}/bind" + ) + if not ec: + del self.phy_odrivers[devaddr] + + async def _async_delete(self): + self.logger.debug("%s: L3NodeMixin sub-class _async_delete", self) + + # XXX do we need to run the cleanup command before these infra changes? + + # remove any hostintf interfaces + for hname in list(self.host_intfs): + await self.rem_host_intf(hname) + + # remove any hostintf interfaces + for devaddr in list(self.phy_intfs): + await self.rem_phy_intf(devaddr) + + # delete the LinuxNamespace/InterfaceMixin + await super()._async_delete() + + +class L3NamespaceNode(L3NodeMixin, LinuxNamespace): + """A namespace L3 node.""" + + def __init__(self, name, pid=True, **kwargs): + # logging.warning( + # "L3NamespaceNode: name %s MRO: %s kwargs %s", + # name, + # L3NamespaceNode.mro(), + # kwargs, + # ) + super().__init__(name, pid=pid, **kwargs) + super().pytest_hook_open_shell() + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + await super()._async_delete() + + +class L3ContainerNode(L3NodeMixin, LinuxNamespace): + """An container (podman) based L3 node.""" + + def __init__(self, name, config, **kwargs): + """Create a Container Node.""" + self.cont_exec_paths = {} + self.container_id = None + self.container_image = config["image"] + self.extra_mounts = [] + assert self.container_image + + self.cmd_p = None + self.__base_cmd = [] + self.__base_cmd_pty = [] + + # don't we have a mutini or cat process? + super().__init__( + name=name, + config=config, + # pid=True, + # cgroup=True, + # private_mounts=["/sys/fs/cgroup:/sys/fs/cgroup"], + **kwargs, + ) + + @property + def is_container(self): + return True + + def get_exec_path(self, binary): + """Return the full path to the binary executable inside the image. + + `binary` :: binary name or list of binary names + """ + return _get_exec_path(binary, self.cmd_status, self.cont_exec_paths) + + async def async_get_exec_path(self, binary): + """Return the full path to the binary executable inside the image. + + `binary` :: binary name or list of binary names + """ + path = await _async_get_exec_path( + binary, self.async_cmd_status, self.cont_exec_paths + ) + return path + + def get_exec_path_host(self, binary): + """Return the full path to the binary executable on the host. + + `binary` :: binary name or list of binary names + """ + return get_exec_path_host(binary) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + if ns_only: + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + if not self.cmd_p: + if self.container_id: + s = f"{self}: Running command in namespace b/c container exited" + self.logger.warning("%s", s) + raise L3ContainerNotRunningError(s) + self.logger.debug("%s: Running command in namespace b/c no container", self) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + # We need to enter our namespaces when running the podman command + pre_cmd = super()._get_pre_cmd( + False, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else pre_cmd + + def tmpfs_mount(self, inner): + # eventually would be nice to support live mounting + assert not self.container_id + self.logger.debug("Mounting tmpfs on %s", inner) + self.extra_mounts.append(f"--mount=type=tmpfs,destination={inner}") + + def bind_mount(self, outer, inner): + # eventually would be nice to support live mounting + assert not self.container_id + # First bind the mount in the parent this allows things like /etc/hosts to work + # correctly when running "nsonly" commands + super().bind_mount(outer, inner) + # Then arrange for binding in the container as well. + self.logger.debug("Bind mounting %s on %s", outer, inner) + if not self.test_nsonly("-e", outer): + self.cmd_raises_nsonly(f"mkdir -p {outer}") + self.extra_mounts.append(f"--mount=type=bind,src={outer},dst={inner}") + + def mount_volumes(self): + args = [] + for m in self.config.get("volumes", []): + if isinstance(m, str): + s = m.split(":", 1) + if len(s) == 1: + args.append("--mount=type=tmpfs,destination=" + m) + else: + spath = s[0] + spath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), spath + ) + ) + if not self.test_nsonly("-e", spath): + self.cmd_raises_nsonly(f"mkdir -p {spath}") + args.append(f"--mount=type=bind,src={spath},dst={s[1]}") + continue + + for m in self.config.get("mounts", []): + margs = ["type=" + m["type"]] + for k, v in m.items(): + if k == "type": + continue + if v: + if k in ("src", "source"): + v = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), v + ) + ) + if not self.test_nsonly("-e", v): + self.cmd_raises_nsonly(f"mkdir -p {v}") + margs.append(f"{k}={v}") + else: + margs.append(f"{k}") + args.append("--mount=" + ",".join(margs)) + + if args: + # Need to work on a way to mount into live container too + self.extra_mounts += args + + def has_run_cmd(self) -> bool: + return True + + async def run_cmd(self): + """Run the configured commands for this node.""" + self.logger.debug("%s: starting container", self.name) + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + self.container_id = f"{self.name}-{os.getpid()}" + proc_path = self.unet.proc_path if self.unet else "/proc" + cmds = [ + get_exec_path_host("podman"), + "run", + f"--name={self.container_id}", + # f"--net=ns:/proc/{self.pid}/ns/net", + f"--net=ns:{proc_path}/{self.pid}/ns/net", + f"--hostname={self.name}", + f"--add-host={self.name}:127.0.0.1", + # We can't use --rm here b/c podman fails on "stop". + # u"--rm", + ] + + if self.config.get("init", True): + cmds.append("--init") + + if self.config.get("privileged", False): + cmds.append("--privileged") + # If we don't do this then the host file system is remounted read-only on + # exit! + cmds.append("--systemd=false") + else: + cmds.extend( + [ + # "--cap-add=SYS_ADMIN", + "--cap-add=NET_ADMIN", + "--cap-add=NET_RAW", + ] + ) + + # Add volumes: + if self.extra_mounts: + cmds += self.extra_mounts + + # Add environment variables: + envdict = self.config.get("env", {}) + if envdict is None: + envdict = {} + for k, v in envdict.items(): + cmds.append(f"--env={k}={v}") + + # Update capabilities + cmds += [f"--cap-add={x}" for x in self.config.get("cap-add", [])] + cmds += [f"--cap-drop={x}" for x in self.config.get("cap-drop", [])] + # cmds += [f"--expose={x.split(':')[0]}" for x in self.config.get("ports", [])] + cmds += [f"--publish={x}" for x in self.config.get("ports", [])] + + # Add extra flags from user: + if "podman" in self.config: + for x in self.config["podman"].get("extra-args", []): + cmds.append(x.strip()) + + # shell_cmd is a union and can be boolean or string + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + shell_cmd = "/bin/bash" + else: + shell_cmd = "" + + # Create shebang files, filled later on + for key in ("cleanup-cmd", "ready-cmd"): + shebang_cmd = self.config.get(key, "").strip() + if shell_cmd and shebang_cmd: + script_name = fsafe_name(key) + # Will write the file contents out when the command is run + shebang_cmdpath = os.path.join(self.rundir, f"{script_name}.shebang") + await self.async_cmd_raises_nsonly(f"touch {shebang_cmdpath}") + await self.async_cmd_raises_nsonly(f"chmod 755 {shebang_cmdpath}") + cmds += [ + # How can we override this? + # u'--entrypoint=""', + f"--volume={shebang_cmdpath}:/tmp/{script_name}.shebang", + ] + + cmd = self.config.get("cmd", "").strip() + + # See if we have a custom update for this `kind` + if kind := self.config.get("kind", None): + if kind in kind_run_cmd_update: + cmds, cmd = await kind_run_cmd_update[kind](self, shell_cmd, cmds, cmd) + + # Create running command file + if shell_cmd and cmd: + assert isinstance(cmd, str) + # make cmd \n terminated for script + cmd = cmd.rstrip() + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + cmdpath = os.path.join(self.rundir, "cmd.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(f"#!{shell_cmd}\n") + cmdfile.write(cmd) + cmdfile.flush() + self.cmd_raises_nsonly(f"chmod 755 {cmdpath}") + cmds += [ + # How can we override this? + # u'--entrypoint=""', + f"--volume={cmdpath}:/tmp/cmds.shebang", + self.container_image, + "/tmp/cmds.shebang", + ] + else: + # `cmd` is a direct run (no shell) cmd + cmds.append(self.container_image) + if cmd: + if isinstance(cmd, str): + cmds.extend(shlex.split(cmd)) + else: + cmds.extend(cmd) + + cmds = [ + x.replace("%CONFIGDIR%", str(self.unet.config_dirname)) for x in cmds + ] + cmds = [x.replace("%RUNDIR%", str(self.rundir)) for x in cmds] + cmds = [x.replace("%NAME%", str(self.name)) for x in cmds] + + stdout = open(os.path.join(self.rundir, "cmd.out"), "wb") + stderr = open(os.path.join(self.rundir, "cmd.err"), "wb") + # Using nsonly avoids using `podman exec` to execute the cmds. + self.cmd_p = await self.async_popen_nsonly( + cmds, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + start_new_session=True, # keeps main tty signals away from podman + ) + + self.logger.debug("%s: async_popen => %s", self, self.cmd_p.pid) + + self.pytest_hook_run_cmd(stdout, stderr) + + # --------------------------------------- + # Now let's wait until container shows up + # --------------------------------------- + timeout = Timeout(30) + while self.cmd_p.returncode is None and not timeout.is_expired(): + o = await self.async_cmd_raises_nsonly( + f"podman ps -q -f name={self.container_id}" + ) + if o.strip(): + break + elapsed = int(timeout.elapsed()) + if elapsed <= 3: + await asyncio.sleep(0.1) + else: + self.logger.info("%s: run_cmd taking more than %ss", self, elapsed) + await asyncio.sleep(1) + if self.cmd_p.returncode is not None: + # leave self.container_id set to cause exception on use + self.logger.warning( + "%s: run_cmd exited quickly (%ss) rc: %s", + self, + timeout.elapsed(), + self.cmd_p.returncode, + ) + elif timeout.is_expired(): + self.logger.critical( + "%s: timeout (%ss) waiting for container to start", + self.name, + timeout.elapsed(), + ) + assert not timeout.is_expired() + + # + # Set our precmd for executing in the container + # + self.__base_cmd = [ + get_exec_path_host("podman"), + "exec", + f"-eMUNET_RUNDIR={self.unet.rundir}", + f"-eMUNET_NODENAME={self.name}", + "-i", + ] + self.__base_cmd_pty = list(self.__base_cmd) # copy list to pty + self.__base_cmd.append(self.container_id) # end regular list + self.__base_cmd_pty.append("-t") # add pty flags + self.__base_cmd_pty.append(self.container_id) # end pty list + # self.set_pre_cmd(self.__base_cmd, self.__base_cmd_pty) # set both pre_cmd + + self.logger.info("%s: started container", self.name) + + self.pytest_hook_open_shell() + + return self.cmd_p + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + self.cleanup_called = True + + if "cleanup-cmd" not in self.config: + return + + if not self.cmd_p: + self.logger.warning("async_cleanup_cmd: container no longer running") + return + + return await self._async_cleanup_cmd() + + def cmd_completed(self, future): + try: + log = self.logger.debug if self.deleting else self.logger.warning + n = future.result() + if self.deleting: + log("contianer `cmd:` result: %s", n) + else: + log( + "contianer `cmd:` exited early, " + "try adding `tail -f /dev/null` to `cmd:`, result: %s", + n, + ) + except asyncio.CancelledError as error: + # Should we stop the container if we have one? or since we are canceled + # we know we will be deleting soon? + self.logger.warning( + "node container cmd wait() canceled: %s:%s", future, error + ) + self.cmd_p = None + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + if contid := self.container_id: + try: + if not self.cleanup_called: + self.logger.debug("calling user cleanup cmd") + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + # Clear the container_id field we want to act like a namespace now. + self.container_id = None + + o = "" + e = "" + if self.cmd_p: + self.logger.debug("podman stop on container: %s", contid) + if (rc := self.cmd_p.returncode) is None: + rc, o, e = await self.async_cmd_status_nsonly( + [get_exec_path_host("podman"), "stop", "--time=2", contid] + ) + if rc and rc < 128: + self.logger.warning( + "%s: podman stop on cmd failed: %s", + self, + cmd_error(rc, o, e), + ) + else: + # It's gone + self.cmd_p = None + + # now remove the container + self.logger.debug("podman rm on container: %s", contid) + rc, o, e = await self.async_cmd_status_nsonly( + [get_exec_path_host("podman"), "rm", contid] + ) + if rc: + self.logger.warning( + "%s: podman rm failed: %s", self, cmd_error(rc, o, e) + ) + else: + self.logger.debug( + "podman removed container %s: %s", contid, cmd_error(rc, o, e) + ) + + await super()._async_delete() + + +class L3QemuVM(L3NodeMixin, LinuxNamespace): + """An VM (qemu) based L3 node.""" + + def __init__(self, name, config, **kwargs): + """Create a Container Node.""" + self.cont_exec_paths = {} + self.launch_p = None + self.qemu_config = config["qemu"] + self.extra_mounts = [] + assert self.qemu_config + self.cmdrepl = None + self.conrepl = None + self.is_kvm = False + self.monrepl = None + self.tapfds = {} + self.cpu_thread_map = {} + + self.tapnames = {} + + self.use_ssh = False + self.__base_cmd = [] + self.__base_cmd_pty = [] + + super().__init__(name=name, config=config, pid=False, **kwargs) + + self.sockdir = self.rundir.joinpath("s") + self.cmd_raises(f"mkdir -p {self.sockdir}") + + self.qemu_config = config_subst( + self.qemu_config, + name=self.name, + rundir=os.path.join(self.rundir, self.name), + configdir=self.unet.config_dirname, + ) + self.ssh_keyfile = self.qemu_config.get("sshkey") + + @property + def is_vm(self): + return True + + def __setup_ssh(self): + if not self.ssh_keyfile: + self.logger.warning("%s: No sshkey config", self) + return False + if not self.mgmt_ip and not self.mgmt_ip6: + self.logger.warning("%s: No mgmt IP to ssh to", self) + return False + mgmt_ip = self.mgmt_ip if self.mgmt_ip else self.mgmt_ip6 + + # + # Since we have a keyfile shouldn't need to sudo + # self.user = os.environ.get("SUDO_USER", "") + # if not self.user: + # self.user = getpass.getuser() + # self.__base_cmd = [ + # get_exec_path_host("sudo"), + # "-E", + # f"-u{self.user}", + # get_exec_path_host("ssh"), + # ] + # + port = 22 + self.__base_cmd = [get_exec_path_host("ssh")] + if port != 22: + self.__base_cmd.append(f"-p{port}") + self.__base_cmd.append("-i") + self.__base_cmd.append(self.ssh_keyfile) + self.__base_cmd.append("-q") + self.__base_cmd.append("-oStrictHostKeyChecking=no") + self.__base_cmd.append("-oUserKnownHostsFile=/dev/null") + # Would be nice but has to be accepted by server config so not very useful. + # self.__base_cmd.append("-oSendVar='TEST'") + self.__base_cmd_pty = list(self.__base_cmd) + self.__base_cmd_pty.append("-t") + + user = self.qemu_config.get("sshuser", "root") + self.__base_cmd.append(f"{user}@{mgmt_ip}") + self.__base_cmd.append("--") + self.__base_cmd_pty.append(f"{user}@{mgmt_ip}") + # self.__base_cmd_pty.append("--") + return True + + def _get_cmd_as_list(self, cmd): + """Given a list or string return a list form for execution. + + If cmd is a string then [cmd] is returned, for most other + node types ["bash", "-c", cmd] is returned but in our case + ssh is the shell. + + Args: + cmd: list or string representing the command to execute. + str_shell: if True and `cmd` is a string then run the + command using bash -c + Returns: + list of commands to execute. + """ + if self.use_ssh and self.launch_p: + return [cmd] if isinstance(cmd, str) else cmd + return super()._get_cmd_as_list(cmd) + + def _get_pre_cmd(self, use_str, use_pty, ns_only=False, root_level=False, **kwargs): + if ns_only: + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + if not self.launch_p: + self.logger.debug("%s: Running command in namespace b/c no VM", self) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + if not self.use_ssh: + self.logger.debug( + "%s: Running command in namespace b/c no SSH configured", self + ) + return super()._get_pre_cmd( + use_str, use_pty, ns_only=True, root_level=root_level, **kwargs + ) + + pre_cmd = self.unet._get_pre_cmd(use_str, use_pty, ns_only=True) + + # This is going to run in the process namespaces. + # We really want it to run in the munet namespace which will + # be different unless unshare_inline was used. + # + # XXX grab the env from kwargs and add to podman exec + # env = kwargs.get("env", {}) + if use_pty: + pre_cmd = pre_cmd + self.__base_cmd_pty + else: + pre_cmd = pre_cmd + self.__base_cmd + return shlex.join(pre_cmd) if use_str else pre_cmd + + async def moncmd(self): + """Uses internal REPL to send cmmand to qemu monitor and get reply.""" + + def tmpfs_mount(self, inner): + # eventually would be nice to support live mounting + self.logger.debug("Mounting tmpfs on %s", inner) + self.extra_mounts.append(("", inner, "")) + + # + # bind_mount is actually being used to mount into the namespace + # + # def bind_mount(self, outer, inner): + # # eventually would be nice to support live mounting + # assert not self.container_id + # if self.test_host("-f", outer): + # self.logger.warning("Can't bind mount files with L3QemuVM: %s", outer) + # return + # self.logger.debug("Bind mounting %s on %s", outer, inner) + # if not self.test_host("-e", outer): + # self.cmd_raises(f"mkdir -p {outer}") + # self.extra_mounts.append((outer, inner, "")) + + def mount_volumes(self): + """Mount volumes from the config.""" + args = [] + for m in self.config.get("volumes", []): + if not isinstance(m, str): + continue + s = m.split(":", 1) + if len(s) == 1: + args.append(("", s[0], "")) + else: + spath = s[0] + spath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), spath + ) + ) + if not self.test_nsonly("-e", spath): + self.cmd_raises_nsonly(f"mkdir -p {spath}") + args.append((spath, s[1], "")) + + for m in self.config.get("mounts", []): + src = m.get("src", m.get("source", "")) + if src: + src = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), src + ) + ) + if not self.test_nsonly("-e", src): + self.cmd_raises_nsonly(f"mkdir -p {src}") + dst = m.get("dst", m.get("destination")) + assert dst, "destination path required for mount" + + margs = [] + for k, v in m.items(): + if k in ["destination", "dst", "source", "src"]: + continue + if k == "type": + assert v in ["bind", "tmpfs"] + continue + if not v: + margs.append(k) + else: + margs.append(f"{k}={v}") + args.append((src, dst, ",".join(margs))) + + if args: + self.extra_mounts += args + + async def run_cmd(self): + """Run the configured commands for this node inside VM.""" + self.logger.debug( + "[rundir %s exists %s]", self.rundir, os.path.exists(self.rundir) + ) + + cmd = self.config.get("cmd", "").strip() + if not cmd: + self.logger.debug("%s: no `cmd` to run", self) + return None + + shell_cmd = self.config.get("shell", "/bin/bash") + if not isinstance(shell_cmd, str): + if shell_cmd: + shell_cmd = "/bin/bash" + else: + shell_cmd = "" + + if shell_cmd: + cmd = cmd.rstrip() + cmd = f"#!{shell_cmd}\n" + cmd + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmd += "\n" + + # Write a copy to the rundir + cmdpath = os.path.join(self.rundir, "cmd.shebang") + with open(cmdpath, mode="w+", encoding="utf-8") as cmdfile: + cmdfile.write(cmd) + commander.cmd_raises(f"chmod 755 {cmdpath}") + + # Now write a copy inside the VM + self.conrepl.cmd_status("cat > /tmp/cmd.shebang << EOF\n" + cmd + "\nEOF") + self.conrepl.cmd_status("chmod 755 /tmp/cmd.shebang") + cmds = "/tmp/cmd.shebang" + else: + cmd = cmd.replace("%CONFIGDIR%", str(self.unet.config_dirname)) + cmd = cmd.replace("%RUNDIR%", str(self.rundir)) + cmd = cmd.replace("%NAME%", str(self.name)) + cmds = cmd + + # class future_proc: + # """Treat awaitable minimally as a proc.""" + # def __init__(self, aw): + # self.aw = aw + # # XXX would be nice to have a real value here + # self.returncode = 0 + # async def wait(self): + # if self.aw: + # return await self.aw + # return None + + class now_proc: + """Treat awaitable minimally as a proc.""" + + def __init__(self, output): + self.output = output + self.returncode = 0 + + async def wait(self): + return self.output + + if self.cmdrepl: + # self.cmd_p = future_proc( + # # We need our own console here b/c this is async and not returning + # # immediately + # # self.cmdrepl.run_command(cmds, timeout=120, async_=True) + # self.cmdrepl.run_command(cmds, timeout=120) + # ) + + # When run_command supports async_ arg we can use the above... + self.cmd_p = now_proc(self.cmdrepl.run_command(cmds, timeout=120)) + + # stdout and err both combined into logfile from the spawned repl + stdout = os.path.join(self.rundir, "_cmdcon-log.txt") + self.pytest_hook_run_cmd(stdout, None) + else: + # If we only have a console we can't run in parallel, so run to completion + self.cmd_p = now_proc(self.conrepl.run_command(cmds, timeout=120)) + + return self.cmd_p + + # InterfaceMixin override + # We need a name unique in the shared namespace. + def get_ns_ifname(self, ifname): + return self.name + ifname + + async def add_host_intf(self, hname, lname, mtu=None): + # L3QemuVM needs it's own add_host_intf for macvtap, We need to create the tap + # in the host then move that interface so that the ifindex/devfile are + # different. + + if hname in self.host_intfs: + return + + self.host_intfs[hname] = lname + index = len(self.host_intfs) + + tapindex = self.unet.tapcount + self.unet.tapcount = self.unet.tapcount + 1 + + tapname = f"tap{tapindex}" + self.tapnames[hname] = tapname + + mac = f"02:bb:bb:bb:{index:02x}:{self.id:02x}" + self.tapmacs[hname] = mac + + self.unet.rootcmd.cmd_raises( + f"ip link add link {hname} name {tapname} type macvtap" + ) + if mtu: + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} mtu {mtu}") + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} address {mac} up") + ifindex = self.unet.rootcmd.cmd_raises( + f"cat /sys/class/net/{tapname}/ifindex" + ).strip() + # self.unet.rootcmd.cmd_raises(f"ip link set {tapname} netns {self.pid}") + + tapfile = f"/dev/tap{ifindex}" + fd = os.open(tapfile, os.O_RDWR) + self.tapfds[hname] = fd + self.logger.info( + "%s: Add host intf: created macvtap interface %s (%s) on %s fd %s", + self, + tapname, + tapfile, + hname, + fd, + ) + + async def rem_host_intf(self, hname): + tapname = self.tapnames[hname] + self.unet.rootcmd.cmd_raises(f"ip link set {tapname} down") + self.unet.rootcmd.cmd_raises(f"ip link delete {tapname} type macvtap") + del self.tapnames[hname] + del self.host_intfs[hname] + + async def create_tap(self, index, ifname, mtu=None, driver="virtio-net-pci"): + # XXX we shouldn't be doign a tap on a bridge with a veth + # we should just be using a tap created earlier which was connected to the + # bridge. Except we need to handle the case of p2p qemu <-> namespace + # + ifname = self.get_ns_ifname(ifname) + brname = f"{self.name}br{index}" + + tapindex = self.unet.tapcount + self.unet.tapcount += 1 + + mac = f"02:aa:aa:aa:{index:02x}:{self.id:02x}" + # nic = "tap,model=virtio-net-pci" + # qemu -net nic,model=virtio,addr=1a:46:0b:ca:bc:7b -net tap,fd=3 3<>/dev/tap11 + self.cmd_raises(f"ip address flush dev {ifname}") + self.cmd_raises(f"ip tuntap add tap{tapindex} mode tap") + self.cmd_raises(f"ip link add name {brname} type bridge") + self.cmd_raises(f"ip link set dev {ifname} master {brname}") + self.cmd_raises(f"ip link set dev tap{tapindex} master {brname}") + if mtu: + self.cmd_raises(f"ip link set dev tap{tapindex} mtu {mtu}") + self.cmd_raises(f"ip link set dev {ifname} mtu {mtu}") + self.cmd_raises(f"ip link set dev tap{tapindex} up") + self.cmd_raises(f"ip link set dev {ifname} up") + self.cmd_raises(f"ip link set dev {brname} up") + dev = f"{driver},netdev=n{index},mac={mac}" + return [ + "-netdev", + f"tap,id=n{index},ifname=tap{tapindex},script=no,downscript=no", + "-device", + dev, + ] + + async def mount_mounts(self): + """Mount any shared directories.""" + self.logger.info("Mounting shared directories") + con = self.conrepl + for i, m in enumerate(self.extra_mounts): + outer, mp, uargs = m + if not outer: + con.cmd_raises(f"mkdir -p {mp}") + margs = f"-o {uargs}" if uargs else "" + con.cmd_raises(f"mount {margs} -t tmpfs tmpfs {mp}") + continue + + uargs = "" if uargs is None else uargs + margs = "trans=virtio" + if uargs: + margs += f",{uargs}" + self.logger.info("Mounting %s on %s with %s", outer, mp, margs) + con.cmd_raises(f"mkdir -p {mp}") + con.cmd_raises(f"mount -t 9p -o {margs} shared{i} {mp}") + + async def renumber_interfaces(self): + """Re-number the interfaces. + + After VM comes up need to renumber the interfaces now on the inside. + """ + self.logger.info("Renumbering interfaces") + con = self.conrepl + con.cmd_raises("sysctl -w net.ipv4.ip_forward=1") + if self.unet.ipv6_enable: + self.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1") + for ifname in sorted(self.intfs): + conn = find_with_kv(self.config.get("connections"), "name", ifname) + to = conn["to"] + switch = self.unet.switches.get(to) + mtu = conn.get("mtu") + if not mtu and switch: + mtu = switch.config.get("mtu") + if mtu: + con.cmd_raises(f"ip link set {ifname} mtu {mtu}") + con.cmd_raises(f"ip link set {ifname} up") + # In case there was some preconfig e.g., cloud-init + con.cmd_raises(f"ip -4 addr flush dev {ifname}") + sw_is_nat = switch and hasattr(switch, "is_nat") and switch.is_nat + if ifaddr := self.get_intf_addr(ifname, ipv6=False): + con.cmd_raises(f"ip addr add {ifaddr} dev {ifname}") + if sw_is_nat: + # In case there was some preconfig e.g., cloud-init + con.cmd_raises("ip route flush exact default") + con.cmd_raises(f"ip route add default via {switch.ip_address}") + if ifaddr := self.get_intf_addr(ifname, ipv6=True): + con.cmd_raises(f"ip -6 addr add {ifaddr} dev {ifname}") + if sw_is_nat: + # In case there was some preconfig e.g., cloud-init + con.cmd_raises("ip -6 route flush exact default") + con.cmd_raises(f"ip -6 route add default via {switch.ip6_address}") + con.cmd_raises("ip link set lo up") + + if self.unet.cfgopt.getoption("--coverage"): + con.cmd_raises("mount -t debugfs none /sys/kernel/debug") + + async def gather_coverage_data(self): + con = self.conrepl + + gcda = "/sys/kernel/debug/gcov" + tmpdir = con.cmd_raises("mktemp -d").strip() + dest = "/gcov-data.tgz" + con.cmd_raises(rf"find {gcda} -type d -exec mkdir -p {tmpdir}/{{}} \;") + con.cmd_raises( + rf"find {gcda} -name '*.gcda' -exec sh -c 'cat < $0 > {tmpdir}/$0' {{}} \;" + ) + con.cmd_raises( + rf"find {gcda} -name '*.gcno' -exec sh -c 'cp -d $0 {tmpdir}/$0' {{}} \;" + ) + con.cmd_raises(rf"tar cf - -C {tmpdir} sys | gzip -c > {dest}") + con.cmd_raises(rf"rm -rf {tmpdir}") + self.logger.info("Saved coverage data in VM at %s", dest) + if self.use_ssh: + ldest = os.path.join(self.rundir, "gcov-data.tgz") + self.cmd_raises(["/bin/cat", dest], stdout=open(ldest, "wb")) + self.logger.info("Saved coverage data on host at %s", ldest) + + async def _opencons( + self, + *cnames, + prompt=None, + is_bourne=True, + user="root", + password="", + expects=None, + sends=None, + timeout=-1, + ): + """Open consoles based on socket file names.""" + timeo = Timeout(timeout) + cons = [] + for cname in cnames: + sockpath = os.path.join(self.sockdir, cname) + connected = False + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + while self.launch_p.returncode is None and not timeo.is_expired(): + try: + sock.connect(sockpath) + connected = True + break + except OSError as error: + if error.errno == errno.ENOENT: + self.logger.debug("waiting for console socket: %s", sockpath) + else: + self.logger.warning( + "can't open console socket: %s", error.strerror + ) + raise + elapsed = int(timeo.elapsed()) + if elapsed <= 3: + await asyncio.sleep(0.25) + else: + self.logger.info( + "%s: launch (qemu) taking more than %ss", self, elapsed + ) + await asyncio.sleep(1) + + if connected: + if prompt is None: + prompt = r"(^|\r\n)[^#\$]*[#\$] " + cons.append( + await self.console( + sock, + prompt=prompt, + is_bourne=is_bourne, + user=user, + password=password, + use_pty=False, + logfile_prefix=cname, + will_echo=True, + expects=expects, + sends=sends, + timeout=timeout, + trace=True, + ) + ) + elif self.launch_p.returncode is not None: + self.logger.warning( + "%s: launch (qemu) exited quickly (%ss) rc: %s", + self, + timeo.elapsed(), + self.launch_p.returncode, + ) + raise Exception("Qemu launch exited early") + elif timeo.is_expired(): + self.logger.critical( + "%s: timeout (%ss) waiting for qemu to start", + self, + timeo.elapsed(), + ) + assert not timeo.is_expired() + + return cons + + async def set_cpu_affinity(self, afflist): + for i, aff in enumerate(afflist): + if not aff: + continue + # affmask = convert_ranges_to_bitmask(aff) + if i not in self.cpu_thread_map: + logging.warning("affinity %s given for missing vcpu %s", aff, i) + continue + logging.info("setting vcpu %s affinity to %s", i, aff) + tid = self.cpu_thread_map[i] + self.cmd_raises_nsonly(f"taskset -cp {aff} {tid}") + + async def launch(self): + """Launch qemu.""" + self.logger.info("%s: Launch Qemu", self) + + qc = self.qemu_config + cc = qc.get("console", {}) + bootd = "d" if "iso" in qc else "c" + # args = [get_exec_path_host("qemu-system-x86_64"), + # "-nodefaults", "-boot", bootd] + args = [get_exec_path_host("qemu-system-x86_64"), "-boot", bootd] + + args += ["-machine", "q35"] + + if qc.get("kvm"): + rc, _, e = await self.async_cmd_status_nsonly("ls -l /dev/kvm") + if rc: + self.logger.warning("Can't enable KVM no /dev/kvm: %s", e) + else: + # [args += ["-enable-kvm", "-cpu", "host"] + # uargs += ["-accel", "kvm", "-cpu", "Icelake-Server-v5"] + args += ["-accel", "kvm", "-cpu", "host"] + + if ncpu := qc.get("ncpu"): + # args += ["-smp", f"sockets={ncpu}"] + args += ["-smp", f"cores={ncpu}"] + # args += ["-smp", f"{ncpu},sockets={ncpu},cores=1,threads=1"] + + args.extend(["-m", str(qc.get("memory", "512M"))]) + + if "bios" in qc: + if qc["bios"] == "open-firmware": + args.extend(["-bios", "/usr/share/qemu/OVMF.fd"]) + else: + args.extend(["-bios", qc["bios"]]) + if "kernel" in qc: + args.extend(["-kernel", qc["kernel"]]) + if "initrd" in qc: + args.extend(["-initrd", qc["initrd"]]) + if "iso" in qc: + args.extend(["-cdrom", qc["iso"]]) + + # we only have append if we have a kernel + if "kernel" in qc: + args.append("-append") + root = qc.get("root", "/dev/ram0") + # Only 1 serial console the other ports (ttyS[123] hvc[01]) should have + # gettys in inittab + append = f"root={root} rw console=ttyS0" + if "cmdline-extra" in qc: + append += f" {qc['cmdline-extra']}" + args.append(append) + + if "extra-args" in qc: + if isinstance(qc["extra-args"], list): + args.extend(qc["extra-args"]) + else: + args.extend(shlex.split(qc["extra-args"])) + + # Walk the list of connections in order so we attach them the same way + pass_fds = [] + nnics = 0 + pciaddr = 3 + for index, conn in enumerate(self.config["connections"]): + devaddr = conn.get("physical", "") + hostintf = conn.get("hostintf", "") + if devaddr: + # if devaddr in self.tapmacs: + # mac = f",mac={self.tapmacs[devaddr]}" + # else: + # mac = "" + args += ["-device", f"vfio-pci,host={devaddr},addr={pciaddr}"] + elif hostintf: + fd = self.tapfds[hostintf] + mac = self.tapmacs[hostintf] + args += [ + "-nic", + f"tap,model=virtio-net-pci,mac={mac},fd={fd},addr={pciaddr}", + ] + pass_fds.append(fd) + nnics += 1 + elif not hostintf: + driver = conn.get("driver", "virtio-net-pci") + mtu = conn.get("mtu") + if not mtu and conn["to"] in self.unet.switches: + mtu = self.unet.switches[conn["to"]].config.get("mtu") + tapargs = await self.create_tap( + index, conn["name"], mtu=mtu, driver=driver + ) + tapargs[-1] += f",addr={pciaddr}" + args += tapargs + nnics += 1 + pciaddr += 1 + if not nnics: + args += ["-nic", "none"] + + dtpl = qc.get("disk-template") + diskpath = disk = qc.get("disk") + if dtpl and not disk: + disk = qc["disk"] = f"{self.name}-{os.path.basename(dtpl)}" + diskpath = os.path.join(self.rundir, disk) + if self.path_exists(diskpath): + logging.debug("Disk '%s' file exists, using.", diskpath) + else: + dtplpath = os.path.abspath( + os.path.join( + os.path.dirname(self.unet.config["config_pathname"]), dtpl + ) + ) + logging.info("Create disk '%s' from template '%s'", diskpath, dtplpath) + self.cmd_raises( + f"qemu-img create -f qcow2 -F qcow2 -b {dtplpath} {diskpath}" + ) + + if diskpath: + args.extend( + ["-drive", f"file={diskpath},if=none,id=sata-disk0,format=qcow2"] + ) + args.extend(["-device", "ahci,id=ahci"]) + args.extend(["-device", "ide-hd,bus=ahci.0,drive=sata-disk0"]) + + use_stdio = cc.get("stdio", True) + has_cmd = self.config.get("cmd") + use_cmdcon = has_cmd and use_stdio + + # + # Any extra serial/console ports beyond thw first, require entries in + # inittab to have getty running on them, modify inittab + # + # Use -serial stdio for output only, and as the first serial console + # which kernel uses for printk, as it has serious issues with dropped + # input chars for some reason. + # + # 4 serial ports (max), we'll add extra ports using virtual consoles. + _sd = self.sockdir + if use_stdio: + args += ["-serial", "stdio"] + args += ["-serial", f"unix:{_sd}/_console,server,nowait"] + if use_cmdcon: + args += [ + "-serial", + f"unix:{_sd}/_cmdcon,server,nowait", + ] + args += [ + "-serial", + f"unix:{_sd}/console,server,nowait", + # A 2 virtual consoles - /dev/hvc[01] + # Requires CONFIG_HVC_DRIVER=y CONFIG_VIRTIO_CONSOLE=y + "-device", + "virtio-serial", # serial console bus + "-chardev", + f"socket,path={_sd}/vcon0,server=on,wait=off,id=vcon0", + "-chardev", + f"socket,path={_sd}/vcon1,server=on,wait=off,id=vcon1", + "-device", + "virtconsole,chardev=vcon0", + "-device", + "virtconsole,chardev=vcon1", + # 2 monitors + "-monitor", + f"unix:{_sd}/_monitor,server,nowait", + "-monitor", + f"unix:{_sd}/monitor,server,nowait", + "-gdb", + f"unix:{_sd}/gdbserver,server,nowait", + ] + + for i, m in enumerate(self.extra_mounts): + args += [ + "-virtfs", + f"local,path={m[0]},mount_tag=shared{i},security_model=passthrough", + ] + + args += ["-nographic"] + + # + # Launch Qemu + # + + stdout = open(os.path.join(self.rundir, "qemu.out"), "wb") + stderr = open(os.path.join(self.rundir, "qemu.err"), "wb") + self.launch_p = await self.async_popen( + args, + stdin=subprocess.DEVNULL, + stdout=stdout, + stderr=stderr, + pass_fds=pass_fds, + # We don't need this here b/c we are only ever running qemu and that's all + # we need to kill for cleanup + # XXX reconcile this + start_new_session=True, # allows us to signal all children to exit + ) + + self.pytest_hook_run_cmd(stdout, stderr) + + # We've passed these on, so don't need these open here anymore. + for fd in pass_fds: + os.close(fd) + + self.logger.debug("%s: async_popen => %s", self, self.launch_p.pid) + + confiles = ["_console"] + if use_cmdcon: + confiles.append("_cmdcon") + + # + # Connect to the console socket, retrying + # + prompt = cc.get("prompt") + cons = await self._opencons( + *confiles, + prompt=prompt, + is_bourne=not bool(prompt), + user=cc.get("user", "root"), + password=cc.get("password", ""), + expects=cc.get("expects"), + sends=cc.get("sends"), + timeout=int(cc.get("timeout", 60)), + ) + self.conrepl = cons[0] + if use_cmdcon: + self.cmdrepl = cons[1] + self.monrepl = await self.monitor(os.path.join(self.sockdir, "_monitor")) + + # the monitor output has super annoying ANSI escapes in it + + output = self.monrepl.cmd_nostatus("info status") + self.logger.info("VM status: %s", output) + + output = self.monrepl.cmd_nostatus("info kvm") + self.logger.info("KVM status: %s", output) + + # + # Set thread affinity + # + output = self.monrepl.cmd_nostatus("info cpus") + matches = re.findall(r"CPU #(\d+): *thread_id=(\d+)", output) + self.cpu_thread_map = {int(k): int(v) for k, v in matches} + if cpuaff := self.qemu_config.get("cpu-affinity"): + await self.set_cpu_affinity(cpuaff) + + self.is_kvm = "disabled" not in output + + if qc.get("unix-os", True): + await self.renumber_interfaces() + + if self.extra_mounts: + await self.mount_mounts() + + self.use_ssh = bool(self.ssh_keyfile) + if self.use_ssh: + self.use_ssh = self.__setup_ssh() + + self.pytest_hook_open_shell() + + return self.launch_p + + def launch_completed(self, future): + self.logger.debug("%s: launch (qemu) completed called", self) + self.use_ssh = False + try: + n = future.result() + self.logger.debug("%s: node launch (qemu) completed result: %s", self, n) + except asyncio.CancelledError as error: + self.logger.debug( + "%s: node launch (qemu) cmd wait() canceled: %s", future, error + ) + + async def cleanup_qemu(self): + """Launch qemu.""" + if self.launch_p: + await self.async_cleanup_proc(self.launch_p) + + async def async_cleanup_cmd(self): + """Run the configured cleanup commands for this node.""" + self.cleanup_called = True + + if "cleanup-cmd" not in self.config: + return + + if not self.launch_p: + self.logger.warning("async_cleanup_cmd: qemu no longer running") + return + + raise NotImplementedError("Needs to be like run_cmd") + # return await self._async_cleanup_cmd() + + async def _async_delete(self): + self.logger.debug("%s: deleting", self) + + # Need to cleanup early b/c it is running on the VM + if self.cmd_p: + await self.async_cleanup_proc(self.cmd_p) + self.cmd_p = None + + try: + # Need to cleanup early b/c it is running on the VM + if not self.cleanup_called: + await self.async_cleanup_cmd() + except Exception as error: + self.logger.warning( + "Got an error during delete from async_cleanup_cmd: %s", error + ) + + try: + if not self.launch_p: + self.logger.warning("async_delete: qemu is not running") + else: + await self.cleanup_qemu() + except Exception as error: + self.logger.warning("%s: failued to cleanup qemu process: %s", self, error) + + await super()._async_delete() + + +class Munet(BaseMunet): + """Munet.""" + + def __init__( + self, + rundir=None, + config=None, + pid=True, + logger=None, + **kwargs, + ): + # logging.warning("Munet") + + if not rundir: + rundir = "/tmp/munet" + + if logger is None: + logger = logging.getLogger("munet.unet") + + super().__init__("munet", pid=pid, rundir=rundir, logger=logger, **kwargs) + + self.built = False + self.tapcount = 0 + + self.cmd_raises(f"mkdir -p {self.rundir} && chmod 755 {self.rundir}") + self.set_ns_cwd(self.rundir) + + if not config: + config = {} + self.config = config + if "config_pathname" in config: + self.config_pathname = os.path.realpath(config["config_pathname"]) + self.config_dirname = os.path.dirname(self.config_pathname) + else: + self.config_pathname = "" + self.config_dirname = "" + + # Done in BaseMunet now + # # We need some way to actually get back to the root namespace + # if not self.isolated: + # self.rootcmd = commander + # else: + # spid = str(pid) + # nsflags = (f"--mount={self.proc_path / spid / 'ns/mnt'}", + # f"--net={self.proc_path / spid / 'ns/net'}", + # f"--uts={self.proc_path / spid / 'ns/uts'}", + # f"--ipc={self.proc_path / spid / 'ns/ipc'}", + # f"--cgroup={self.proc_path / spid / 'ns/cgroup'}", + # f"--pid={self.proc_path / spid / 'ns/net'}", + # self.rootcmd = SharedNamespace("host", pid=1, nsflags=nsflags) + + # Save the namespace pid + with open(os.path.join(self.rundir, "nspid"), "w", encoding="ascii") as f: + f.write(f"{self.pid}\n") + + with open(os.path.join(self.rundir, "nspids"), "w", encoding="ascii") as f: + f.write(f'{" ".join([str(x) for x in self.pids])}\n') + + hosts_file = os.path.join(self.rundir, "hosts.txt") + with open(hosts_file, "w", encoding="ascii") as hf: + hf.write( + f"""127.0.0.1\tlocalhost {self.name} +::1\tip6-localhost ip6-loopback +fe00::0\tip6-localnet +ff00::0\tip6-mcastprefix +ff02::1\tip6-allnodes +ff02::2\tip6-allrouters +""" + ) + self.bind_mount(hosts_file, "/etc/hosts") + + # Common CLI commands for any topology + cdict = { + "commands": [ + { + "name": "pcap", + "format": "pcap NETWORK", + "help": ( + "capture packets from NETWORK into file capture-NETWORK.pcap" + " the command is run within a new window which also shows" + " packet summaries. NETWORK can also be an interface specified" + " as HOST:INTF. To capture inside the host namespace." + ), + "exec": "tshark -s 9200 -i {0} -P -w capture-{0}.pcap", + "top-level": True, + "new-window": {"background": True}, + }, + { + "name": "nsterm", + "format": "nsterm HOST [HOST ...]", + "help": ( + "open terminal[s] in the namespace only" + " (outside containers or VM), * for all" + ), + "exec": "bash", + "new-window": {"ns_only": True}, + }, + { + "name": "term", + "format": "term HOST [HOST ...]", + "help": "open terminal[s] (TMUX or XTerm) on HOST[S], * for all", + "exec": "bash", + "new-window": True, + }, + { + "name": "xterm", + "format": "xterm HOST [HOST ...]", + "help": "open XTerm[s] on HOST[S], * for all", + "exec": "bash", + "new-window": { + "forcex": True, + }, + }, + { + "name": "sh", + "format": "[HOST ...] sh <SHELL-COMMAND>", + "help": "execute <SHELL-COMMAND> on hosts", + "exec": "{}", + }, + { + "name": "shi", + "format": "[HOST ...] shi <INTERACTIVE-COMMAND>", + "help": "execute <INTERACTIVE-COMMAND> on HOST[s]", + "exec": "{}", + "interactive": True, + }, + { + "name": "stdout", + "exec": ( + "[ -e %RUNDIR%/qemu.out ] && tail -F %RUNDIR%/qemu.out " + "|| tail -F %RUNDIR%/cmd.out" + ), + "format": "stdout HOST [HOST ...]", + "help": "tail -f on the stdout of the qemu/cmd for this node", + "new-window": True, + }, + { + "name": "stderr", + "exec": ( + "[ -e %RUNDIR%/qemu.err ] && tail -F %RUNDIR%/qemu.err " + "|| tail -F %RUNDIR%/cmd.err" + ), + "format": "stderr HOST [HOST ...]", + "help": "tail -f on the stdout of the qemu/cmd for this node", + "new-window": True, + }, + ] + } + + cli.add_cli_config(self, cdict) + + if "cli" in config: + cli.add_cli_config(self, config["cli"]) + + if "topology" not in self.config: + self.config["topology"] = {} + + self.topoconf = self.config["topology"] + self.ipv6_enable = self.topoconf.get("ipv6-enable", False) + + if self.isolated: + if not self.ipv6_enable: + # Disable IPv6 + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=0") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=1") + else: + self.cmd_raises("sysctl -w net.ipv6.conf.all.autoconf=1") + self.cmd_raises("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + + # we really need overlay, but overlay-layers (used by overlay-images) + # counts on things being present in overlay so this temp stuff doesn't work. + # if self.isolated: + # # Let's hide podman details + # self.tmpfs_mount("/var/lib/containers/storage/overlay-containers") + + shellopt = self.cfgopt.getoption("--shell") + shellopt = shellopt if shellopt else "" + if shellopt == "all" or "." in shellopt.split(","): + self.run_in_window("bash") + + def __del__(self): + """Catch case of build object but not async_deleted.""" + if hasattr(self, "built"): + if not self.deleting: + logging.critical( + "Munet object deleted without calling `async_delete` for cleanup." + ) + s = super() + if hasattr(s, "__del__"): + s.__del__(self) + + async def _async_build(self, logger=None): + """Build the topology based on config.""" + if self.built: + self.logger.warning("%s: is already built", self) + return + + self.built = True + + # Allow for all networks to be auto-numbered + topoconf = self.topoconf + autonumber = self.autonumber + ipv6_enable = self.ipv6_enable + + # --------------------------------------------- + # Merge Kinds and perform variable substitution + # --------------------------------------------- + + kinds = self.config.get("kinds", {}) + + for name, conf in config_to_dict_with_key(topoconf, "networks", "name").items(): + if kind := conf.get("kind"): + if kconf := kinds[kind]: + conf = merge_kind_config(kconf, conf) + conf = config_subst( + conf, name=name, rundir=self.rundir, configdir=self.config_dirname + ) + if "ip" not in conf and autonumber: + conf["ip"] = "auto" + if "ipv6" not in conf and autonumber and ipv6_enable: + conf["ipv6"] = "auto" + topoconf["networks"][name] = conf + self.add_network(name, conf, logger=logger) + + for name, conf in config_to_dict_with_key(topoconf, "nodes", "name").items(): + if kind := conf.get("kind"): + if kconf := kinds[kind]: + conf = merge_kind_config(kconf, conf) + + config_to_dict_with_key( + conf, "env", "name" + ) # convert list of env objects to dict + + conf = config_subst( + conf, + name=name, + rundir=os.path.join(self.rundir, name), + configdir=self.config_dirname, + ) + topoconf["nodes"][name] = conf + self.add_l3_node(name, conf, logger=logger) + + # ------------------ + # Create connections + # ------------------ + + # Go through all connections and name them so they are sane to the user + # otherwise when we do p2p links the names/ords skip around based oddly + for name, node in self.hosts.items(): + nconf = node.config + if "connections" not in nconf: + continue + nconns = [] + for cconf in nconf["connections"]: + # Replace string only with a dictionary + if isinstance(cconf, str): + splitconf = cconf.split(":", 1) + cconf = {"to": splitconf[0]} + if len(splitconf) == 2: + cconf["name"] = splitconf[1] + # Allocate a name if not already assigned + if "name" not in cconf: + cconf["name"] = node.get_next_intf_name() + nconns.append(cconf) + nconf["connections"] = nconns + + for name, node in self.hosts.items(): + nconf = node.config + if "connections" not in nconf: + continue + for cconf in nconf["connections"]: + # Eventually can add support for unconnected intf here. + if "to" not in cconf: + continue + to = cconf["to"] + if to in self.switches: + switch = self.switches[to] + swconf = find_matching_net_config(name, cconf, switch.config) + await self.add_native_link(switch, node, swconf, cconf) + elif cconf["name"] not in node.intfs: + # Only add the p2p interface if not already there. + other = self.hosts[to] + oconf = find_matching_net_config(name, cconf, other.config) + await self.add_native_link(node, other, cconf, oconf) + + @property + def autonumber(self): + return self.topoconf.get("networks-autonumber", False) + + @autonumber.setter + def autonumber(self, value): + self.topoconf["networks-autonumber"] = bool(value) + + async def add_native_link(self, node1, node2, c1=None, c2=None): + """Add a link between switch and node or 2 nodes.""" + isp2p = False + + c1 = {} if c1 is None else c1 + c2 = {} if c2 is None else c2 + + if node1.name in self.switches: + assert node2.name in self.hosts + elif node2.name in self.switches: + assert node1.name in self.hosts + node1, node2 = node2, node1 + c1, c2 = c2, c1 + else: + # p2p link + assert node1.name in self.hosts + assert node1.name in self.hosts + isp2p = True + + if "name" not in c1: + c1["name"] = node1.get_next_intf_name() + if1 = c1["name"] + + if "name" not in c2: + c2["name"] = node2.get_next_intf_name() + if2 = c2["name"] + + do_add_link = True + for n, c in ((node1, c1), (node2, c2)): + if "hostintf" in c: + await n.add_host_intf(c["hostintf"], c["name"], mtu=c.get("mtu")) + do_add_link = False + elif "physical" in c: + await n.add_phy_intf(c["physical"], c["name"]) + do_add_link = False + if do_add_link: + assert "hostintf" not in c1 + assert "hostintf" not in c2 + assert "physical" not in c1 + assert "physical" not in c2 + + if isp2p: + mtu1 = c1.get("mtu") + mtu2 = c2.get("mtu") + mtu = mtu1 if mtu1 else mtu2 + if mtu1 and mtu2 and mtu1 != mtu2: + self.logger.error("mtus differ for add_link %s != %s", mtu1, mtu2) + else: + mtu = c2.get("mtu") + + super().add_link(node1, node2, if1, if2, mtu=mtu) + + if isp2p: + node1.set_p2p_addr(node2, c1, c2) + else: + node2.set_lan_addr(node1, c2) + + if "physical" not in c1 and not node1.is_vm: + node1.set_intf_constraints(if1, **c1) + if "physical" not in c2 and not node2.is_vm: + node2.set_intf_constraints(if2, **c2) + + def add_l3_node(self, name, config=None, **kwargs): + """Add a node to munet.""" + if config and config.get("image"): + cls = L3ContainerNode + elif config and config.get("qemu"): + cls = L3QemuVM + elif config and config.get("server"): + cls = SSHRemote + kwargs["server"] = config["server"] + kwargs["port"] = int(config.get("server-port", 22)) + if "ssh-identity-file" in config: + kwargs["idfile"] = config.get("ssh-identity-file") + if "ssh-user" in config: + kwargs["user"] = config.get("ssh-user") + if "ssh-password" in config: + kwargs["password"] = config.get("ssh-password") + else: + cls = L3NamespaceNode + return super().add_host(name, cls=cls, config=config, **kwargs) + + def add_network(self, name, config=None, **kwargs): + """Add a l2 or l3 switch to munet.""" + if config is None: + config = {} + + cls = L3Bridge if config.get("ip") else L2Bridge + mtu = kwargs.get("mtu", config.get("mtu")) + return super().add_switch(name, cls=cls, config=config, mtu=mtu, **kwargs) + + async def run(self): + tasks = [] + + hosts = self.hosts.values() + launch_nodes = [x for x in hosts if hasattr(x, "launch")] + launch_nodes = [x for x in launch_nodes if x.config.get("qemu")] + run_nodes = [x for x in hosts if hasattr(x, "has_run_cmd") and x.has_run_cmd()] + ready_nodes = [ + x for x in hosts if hasattr(x, "has_ready_cmd") and x.has_ready_cmd() + ] + + pcapopt = self.cfgopt.getoption("--pcap") + pcapopt = pcapopt if pcapopt else "" + if pcapopt == "all": + pcapopt = self.switches.keys() + if pcapopt: + for pcap in pcapopt.split(","): + if ":" in pcap: + host, intf = pcap.split(":") + pcap = f"{host}-{intf}" + host = self.hosts[host] + else: + host = self + intf = pcap + host.run_in_window( + f"tshark -s 9200 -i {intf} -P -w capture-{pcap}.pcap", + background=True, + title=f"cap:{pcap}", + ) + + if launch_nodes: + # would like a info when verbose here. + logging.debug("Launching nodes") + await asyncio.gather(*[x.launch() for x in launch_nodes]) + + # Watch for launched processes to exit + for node in launch_nodes: + task = asyncio.create_task( + node.launch_p.wait(), name=f"Node-{node.name}-launch" + ) + task.add_done_callback(node.launch_completed) + tasks.append(task) + + if run_nodes: + # would like a info when verbose here. + logging.debug("Running `cmd` on nodes") + await asyncio.gather(*[x.run_cmd() for x in run_nodes]) + + # Watch for run_cmd processes to exit + for node in run_nodes: + task = asyncio.create_task(node.cmd_p.wait(), name=f"Node-{node.name}-cmd") + task.add_done_callback(node.cmd_completed) + tasks.append(task) + + # Wait for nodes to be ready + if ready_nodes: + + async def wait_until_ready(x): + while not await x.async_ready_cmd(): + logging.debug("Waiting for ready on: %s", x) + await asyncio.sleep(0.25) + logging.debug("%s is ready!", x) + + logging.debug("Waiting for ready on nodes: %s", ready_nodes) + _, pending = await asyncio.wait( + [wait_until_ready(x) for x in ready_nodes], timeout=30 + ) + if pending: + logging.warning("Timeout waiting for ready: %s", pending) + for nr in pending: + nr.cancel() + raise asyncio.TimeoutError() + logging.debug("All nodes ready") + + return tasks + + async def _async_delete(self): + from .testing.util import async_pause_test # pylint: disable=C0415 + + self.logger.debug("%s: deleting.", self) + + if self.cfgopt.getoption("--coverage"): + nodes = ( + x for x in self.hosts.values() if hasattr(x, "gather_coverage_data") + ) + try: + await asyncio.gather(*(x.gather_coverage_data() for x in nodes)) + except Exception as error: + logging.warning("Error gathering coverage data: %s", error) + + pause = bool(self.cfgopt.getoption("--pause-at-end")) + pause = pause or bool(self.cfgopt.getoption("--pause")) + if pause: + try: + await async_pause_test("Before MUNET delete") + except KeyboardInterrupt: + print("^C...continuing") + except Exception as error: + self.logger.error("\n...continuing after error: %s", error) + + # XXX should we cancel launch and run tasks? + + try: + await super()._async_delete() + except Exception as error: + self.logger.error("Error cleaning up: %s", error, exc_info=True) + raise + + +async def run_cmd_update_ceos(node, shell_cmd, cmds, cmd): + cmd = cmd.strip() + if shell_cmd or cmd != "/sbin/init": + return cmds, cmd + + # + # Add flash dir and mount it + # + flashdir = os.path.join(node.rundir, "flash") + node.cmd_raises_nsonly(f"mkdir -p {flashdir} && chmod 775 {flashdir}") + cmds += [f"--volume={flashdir}:/mnt/flash"] + + # + # Startup config (if not present already) + # + if startup_config := node.config.get("startup-config", None): + dest = os.path.join(flashdir, "startup-config") + if os.path.exists(dest): + node.logger.info("Skipping copy of startup-config, already present") + else: + source = os.path.join(node.unet.config_dirname, startup_config) + node.cmd_raises_nsonly(f"cp {source} {dest} && chmod 664 {dest}") + + # + # system mac address (if not present already + # + dest = os.path.join(flashdir, "system_mac_address") + if os.path.exists(dest): + node.logger.info("Skipping system-mac generation, already present") + else: + random_arista_mac = "00:1c:73:%02x:%02x:%02x" % ( + random.randint(0, 255), + random.randint(0, 255), + random.randint(0, 255), + ) + system_mac = node.config.get("system-mac", random_arista_mac) + with open(dest, "w", encoding="ascii") as f: + f.write(system_mac + "\n") + node.cmd_raises_nsonly(f"chmod 664 {dest}") + + args = [] + + # Pass special args for the environment variables + if "env" in node.config: + args += [f"systemd.setenv={k}={v}" for k, v in node.config["env"].items()] + + return cmds, [cmd] + args + + +# XXX this is only used by the container code +kind_run_cmd_update = {"ceos": run_cmd_update_ceos} diff --git a/tests/topotests/munet/parser.py b/tests/topotests/munet/parser.py new file mode 100644 index 000000000000..4fc0c75a60ed --- /dev/null +++ b/tests/topotests/munet/parser.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# September 30 2021, Christian Hopps <chopps@labn.net> +# +# Copyright 2021, LabN Consulting, L.L.C. +# +"""A module that implements the standalone parser.""" +import asyncio +import importlib.resources +import json +import logging +import logging.config +import os +import subprocess +import sys +import tempfile + +from pathlib import Path + + +try: + import jsonschema # pylint: disable=C0415 + import jsonschema.validators # pylint: disable=C0415 + + from jsonschema.exceptions import ValidationError # pylint: disable=C0415 +except ImportError: + jsonschema = None + +from .config import list_to_dict_with_key +from .native import Munet + + +def get_schema(): + if get_schema.schema is None: + with importlib.resources.path("munet", "munet-schema.json") as datapath: + search = [str(datapath.parent)] + get_schema.schema = get_config(basename="munet-schema", search=search) + return get_schema.schema + + +get_schema.schema = None + +project_root_contains = [ + ".git", + "pyproject.toml", + "tox.ini", + "setup.cfg", + "setup.py", + "pytest.ini", + ".projectile", +] + + +def is_project_root(path: Path) -> bool: + + for contains in project_root_contains: + if path.joinpath(contains).exists(): + return True + return False + + +def find_project_root(config_path: Path, project_root=None): + if project_root is not None: + project_root = Path(project_root) + if project_root in config_path.parents: + return project_root + logging.warning( + "project_root %s is not a common ancestor of config file %s", + project_root, + config_path, + ) + return config_path.parent + for ppath in config_path.parents: + if is_project_root(ppath): + return ppath + return config_path.parent + + +def get_config(pathname=None, basename="munet", search=None, logf=logging.debug): + + cwd = os.getcwd() + + if not search: + search = [cwd] + elif isinstance(search, (str, Path)): + search = [search] + + if pathname: + pathname = os.path.join(cwd, pathname) + if not os.path.exists(pathname): + raise FileNotFoundError(pathname) + else: + for d in search: + logf("%s", f'searching in "{d}" for "{basename}".{{yaml, toml, json}}') + for ext in ("yaml", "toml", "json"): + pathname = os.path.join(d, basename + "." + ext) + if os.path.exists(pathname): + logf("%s", f'Found "{pathname}"') + break + else: + continue + break + else: + raise FileNotFoundError(basename + ".{json,toml,yaml} in " + f"{search}") + + _, ext = pathname.rsplit(".", 1) + + if ext == "json": + config = json.load(open(pathname, encoding="utf-8")) + elif ext == "toml": + import toml # pylint: disable=C0415 + + config = toml.load(pathname) + elif ext == "yaml": + import yaml # pylint: disable=C0415 + + config = yaml.safe_load(open(pathname, encoding="utf-8")) + else: + raise ValueError("Filename does not end with (.json|.toml|.yaml)") + + config["config_pathname"] = os.path.realpath(pathname) + return config + + +def setup_logging(args, config_base="logconf"): + # Create rundir and arrange for future commands to run in it. + + # Change CWD to the rundir prior to parsing config + old = os.getcwd() + os.chdir(args.rundir) + try: + search = [old] + with importlib.resources.path("munet", config_base + ".yaml") as datapath: + search.append(str(datapath.parent)) + + def logf(msg, *p, **k): + if args.verbose: + print("PRELOG: " + msg % p, **k, file=sys.stderr) + + config = get_config(args.log_config, config_base, search, logf=logf) + pathname = config["config_pathname"] + del config["config_pathname"] + + if "info_console" in config["handlers"]: + # mutest case + if args.verbose > 1: + config["handlers"]["console"]["level"] = "DEBUG" + config["handlers"]["info_console"]["level"] = "DEBUG" + elif args.verbose: + config["handlers"]["console"]["level"] = "INFO" + config["handlers"]["info_console"]["level"] = "DEBUG" + elif args.verbose: + # munet case + config["handlers"]["console"]["level"] = "DEBUG" + + # add the rundir path to the filenames + for v in config["handlers"].values(): + filename = v.get("filename") + if not filename: + continue + v["filename"] = os.path.join(args.rundir, filename) + + logging.config.dictConfig(dict(config)) + logging.info("Loaded logging config %s", pathname) + + return config + finally: + os.chdir(old) + + +def append_hosts_files(unet, netname): + if not netname: + return + + entries = [] + for name in ("munet", *list(unet.hosts)): + if name == "munet": + node = unet.switches[netname] + ifname = None + else: + node = unet.hosts[name] + if not hasattr(node, "_intf_addrs"): + continue + ifname = node.get_ifname(netname) + + for b in (False, True): + ifaddr = node.get_intf_addr(ifname, ipv6=b) + if ifaddr and hasattr(ifaddr, "ip"): + entries.append((name, ifaddr.ip)) + + for name in ("munet", *list(unet.hosts)): + node = unet if name == "munet" else unet.hosts[name] + if not hasattr(node, "rundir"): + continue + with open(os.path.join(node.rundir, "hosts.txt"), "a+", encoding="ascii") as hf: + hf.write("\n") + for e in entries: + hf.write(f"{e[1]}\t{e[0]}\n") + + +def validate_config(config, logger, args): + if jsonschema is None: + logger.debug("No validation w/o jsonschema module") + return True + + old = os.getcwd() + if args: + os.chdir(args.rundir) + + try: + validator = jsonschema.validators.Draft202012Validator(get_schema()) + validator.validate(instance=config) + logger.debug("Validated %s", config["config_pathname"]) + return True + except FileNotFoundError as error: + logger.info("No schema found: %s", error) + return False + except ValidationError as error: + logger.info("Validation failed: %s", error) + return False + finally: + if args: + os.chdir(old) + + +def load_kinds(args, search=None): + # Change CWD to the rundir prior to parsing config + cwd = os.getcwd() + if args: + os.chdir(args.rundir) + + args_config = args.kinds_config if args else None + try: + if search is None: + search = [cwd] + with importlib.resources.path("munet", "kinds.yaml") as datapath: + search.insert(0, str(datapath.parent)) + + configs = [] + if args_config: + configs.append(get_config(args_config, "kinds", search=[])) + else: + # prefer directories at the front of the list + for kdir in search: + try: + configs.append(get_config(basename="kinds", search=[kdir])) + except FileNotFoundError: + continue + + kinds = {} + for config in configs: + # XXX need to fix the issue with `connections: ["net0"]` not validating + # if jsonschema is not None: + # validator = jsonschema.validators.Draft202012Validator(get_schema()) + # validator.validate(instance=config) + + kinds_list = config.get("kinds", []) + kinds_dict = list_to_dict_with_key(kinds_list, "name") + if kinds_dict: + logging.info("Loading kinds config from %s", config["config_pathname"]) + if "kinds" in kinds: + kinds["kinds"].update(**kinds_dict) + else: + kinds["kinds"] = kinds_dict + + cli_list = config.get("cli", {}).get("commands", []) + if cli_list: + logging.info("Loading cli comands from %s", config["config_pathname"]) + if "cli" not in kinds: + kinds["cli"] = {} + if "commands" not in kinds["cli"]: + kinds["cli"]["commands"] = [] + kinds["cli"]["commands"].extend(cli_list) + + return kinds + except FileNotFoundError as error: + # if we have kinds in args but the file doesn't exist, raise the error + if args_config is not None: + raise error + return {} + finally: + if args: + os.chdir(cwd) + + +async def async_build_topology( + config=None, + logger=None, + rundir=None, + args=None, + unshare_inline=False, + pytestconfig=None, + search_root=None, + top_level_pidns=True, +): + + if not rundir: + rundir = tempfile.mkdtemp(prefix="unet") + subprocess.run(f"mkdir -p {rundir} && chmod 755 {rundir}", check=True, shell=True) + + isolated = not args.host if args else True + if not config: + config = get_config(basename="munet") + + # create search directories from common root if given + cpath = Path(config["config_pathname"]).absolute() + project_root = args.project_root if args else None + if not search_root: + search_root = find_project_root(cpath, project_root) + if not search_root: + search = [cpath.parent] + else: + search_root = Path(search_root).absolute() + if search_root in cpath.parents: + search = list(cpath.parents) + if remcount := len(search_root.parents): + search = search[0:-remcount] + + # load kinds along search path and merge into config + kinds = load_kinds(args, search=search) + config_kinds_dict = list_to_dict_with_key(config.get("kinds", []), "name") + config["kinds"] = {**kinds.get("kinds", {}), **config_kinds_dict} + + # mere CLI command from kinds into config as well. + kinds_cli_list = kinds.get("cli", {}).get("commands", []) + config_cli_list = config.get("cli", {}).get("commands", []) + if config_cli_list: + if kinds_cli_list: + config_cli_list.extend(list(kinds_cli_list)) + elif kinds_cli_list: + if "cli" not in config: + config["cli"] = {} + if "commands" not in config["cli"]: + config["cli"]["commands"] = [] + config["cli"]["commands"].extend(list(kinds_cli_list)) + + unet = Munet( + rundir=rundir, + config=config, + pytestconfig=pytestconfig, + isolated=isolated, + pid=top_level_pidns, + unshare_inline=args.unshare_inline if args else unshare_inline, + logger=logger, + ) + + try: + await unet._async_build(logger) # pylint: disable=W0212 + except Exception as error: + logging.critical("Failure building munet topology: %s", error, exc_info=True) + await unet.async_delete() + raise + except KeyboardInterrupt: + await unet.async_delete() + raise + + topoconf = config.get("topology") + if not topoconf: + return unet + + dns_network = topoconf.get("dns-network") + if dns_network: + append_hosts_files(unet, dns_network) + + # Write our current config to the run directory + with open(f"{unet.rundir}/config.json", "w", encoding="utf-8") as f: + json.dump(unet.config, f, indent=2) + + return unet + + +def build_topology(config=None, logger=None, rundir=None, args=None, pytestconfig=None): + return asyncio.run(async_build_topology(config, logger, rundir, args, pytestconfig)) diff --git a/tests/topotests/munet/testing/__init__.py b/tests/topotests/munet/testing/__init__.py new file mode 100644 index 000000000000..63cbfabda957 --- /dev/null +++ b/tests/topotests/munet/testing/__init__.py @@ -0,0 +1 @@ +"""Sub-package supporting munet use in pytest.""" diff --git a/tests/topotests/munet/testing/fixtures.py b/tests/topotests/munet/testing/fixtures.py new file mode 100644 index 000000000000..25039df5418a --- /dev/null +++ b/tests/topotests/munet/testing/fixtures.py @@ -0,0 +1,447 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""A module that implements pytest fixtures. + +To use in your project, in your conftest.py add: + + from munet.testing.fixtures import * +""" +import contextlib +import logging +import os + +from pathlib import Path +from typing import Union + +import pytest +import pytest_asyncio + +from ..base import BaseMunet +from ..base import Bridge +from ..base import get_event_loop +from ..cleanup import cleanup_current +from ..cleanup import cleanup_previous +from ..native import L3NodeMixin +from ..parser import async_build_topology +from ..parser import get_config +from .util import async_pause_test +from .util import pause_test + + +@contextlib.asynccontextmanager +async def achdir(ndir: Union[str, Path], desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +@contextlib.contextmanager +def chdir(ndir: Union[str, Path], desc=""): + odir = os.getcwd() + os.chdir(ndir) + if desc: + logging.debug("%s: chdir from %s to %s", desc, odir, ndir) + try: + yield + finally: + if desc: + logging.debug("%s: chdir back from %s to %s", desc, ndir, odir) + os.chdir(odir) + + +def get_test_logdir(nodeid=None, module=False): + """Get log directory relative pathname.""" + xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "") + mode = os.getenv("PYTEST_XDIST_MODE", "no") + + # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running + # may be missing "::testname" if module is True + if not nodeid: + nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0] + + cur_test = nodeid.replace("[", "_").replace("]", "_") + if module: + idx = cur_test.rfind("::") + path = cur_test if idx == -1 else cur_test[:idx] + testname = "" + else: + path, testname = cur_test.split("::") + testname = testname.replace("/", ".") + path = path[:-3].replace("/", ".") + + # We use different logdir paths based on how xdist is running. + if mode == "each": + if module: + return os.path.join(path, "worker-logs", xdist_worker) + return os.path.join(path, testname, xdist_worker) + assert mode in ("no", "load", "loadfile", "loadscope"), f"Unknown dist mode {mode}" + return path if module else os.path.join(path, testname) + + +def _push_log_handler(desc, logpath): + logpath = os.path.abspath(logpath) + logging.debug("conftest: adding %s logging at %s", desc, logpath) + root_logger = logging.getLogger() + handler = logging.FileHandler(logpath, mode="w") + fmt = logging.Formatter("%(asctime)s %(levelname)5s: %(message)s") + handler.setFormatter(fmt) + root_logger.addHandler(handler) + return handler + + +def _pop_log_handler(handler): + root_logger = logging.getLogger() + logging.debug("conftest: removing logging handler %s", handler) + root_logger.removeHandler(handler) + + +@contextlib.contextmanager +def log_handler(desc, logpath): + handler = _push_log_handler(desc, logpath) + try: + yield + finally: + _pop_log_handler(handler) + + +# ================= +# Sessions Fixtures +# ================= + + +@pytest.fixture(autouse=True, scope="session") +def session_autouse(): + if "PYTEST_TOPOTEST_WORKER" not in os.environ: + is_worker = False + elif not os.environ["PYTEST_TOPOTEST_WORKER"]: + is_worker = False + else: + is_worker = True + + if not is_worker: + # This is unfriendly to multi-instance + cleanup_previous() + + # We never pop as we want to keep logging + _push_log_handler("session", "/tmp/unet-test/pytest-session.log") + + yield + + if not is_worker: + cleanup_current() + + +# =============== +# Module Fixtures +# =============== + + +@pytest.fixture(autouse=True, scope="module") +def module_autouse(request): + logpath = get_test_logdir(request.node.name, True) + logpath = os.path.join("/tmp/unet-test", logpath, "pytest-exec.log") + with log_handler("module", logpath): + sdir = os.path.dirname(os.path.realpath(request.fspath)) + with chdir(sdir, "module autouse fixture"): + yield + + if BaseMunet.g_unet: + raise Exception("Base Munet was not cleaned up/deleted") + + +@pytest.fixture(scope="module") +def event_loop(): + """Create an instance of the default event loop for the session.""" + loop = get_event_loop() + try: + logging.info("event_loop_fixture: yielding with new event loop watcher") + yield loop + finally: + loop.close() + + +@pytest.fixture(scope="module") +def rundir_module(): + d = os.path.join("/tmp/unet-test", get_test_logdir(module=True)) + logging.debug("conftest: test module rundir %s", d) + return d + + +async def _unet_impl( + _rundir, _pytestconfig, unshare=None, top_level_pidns=None, param=None +): + try: + # Default is not to unshare inline if not specified otherwise + unshare_default = False + pidns_default = True + if isinstance(param, (tuple, list)): + pidns_default = bool(param[2]) if len(param) > 2 else True + unshare_default = bool(param[1]) if len(param) > 1 else False + param = str(param[0]) + elif isinstance(param, bool): + unshare_default = param + param = None + if unshare is None: + unshare = unshare_default + if top_level_pidns is None: + top_level_pidns = pidns_default + + logging.info("unet fixture: basename=%s unshare_inline=%s", param, unshare) + _unet = await async_build_topology( + config=get_config(basename=param) if param else None, + rundir=_rundir, + unshare_inline=unshare, + top_level_pidns=top_level_pidns, + pytestconfig=_pytestconfig, + ) + except Exception as error: + logging.debug( + "unet fixture: unet build failed: %s\nparam: %s", + error, + param, + exc_info=True, + ) + pytest.skip( + f"unet fixture: unet build failed: {error}", allow_module_level=True + ) + raise + + try: + tasks = await _unet.run() + except Exception as error: + logging.debug("unet fixture: unet run failed: %s", error, exc_info=True) + await _unet.async_delete() + pytest.skip(f"unet fixture: unet run failed: {error}", allow_module_level=True) + raise + + logging.debug("unet fixture: containers running") + + # Pytest is supposed to always return even if exceptions + try: + yield _unet + except Exception as error: + logging.error("unet fixture: yield unet unexpected exception: %s", error) + + logging.debug("unet fixture: module done, deleting unet") + await _unet.async_delete() + + # No one ever awaits these so cancel them + logging.debug("unet fixture: cleanup") + for task in tasks: + task.cancel() + + # Reset the class variables so auto number is predictable + logging.debug("unet fixture: resetting ords to 1") + L3NodeMixin.next_ord = 1 + Bridge.next_ord = 1 + + +@pytest.fixture(scope="module") +async def unet(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + The request param is either the basename of the config file or a tuple of the form: + (basename, unshare, top_level_pidns), with the second and third elements boolean and + optional, defaulting to False, True. + """ + param = request.param if hasattr(request, "param") else None + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet fixture"): + async for x in _unet_impl(rundir_module, pytestconfig, param=param): + yield x + + +@pytest.fixture(scope="module") +async def unet_share(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + This share variant keeps munet from unsharing the process to a new namespace so that + root level commands and actions are execute on the host, normally they are executed + in the munet namespace which allowing things like scapy inline in tests to work. + + The request param is either the basename of the config file or a tuple of the form: + (basename, top_level_pidns), the second value is a boolean. + """ + param = request.param if hasattr(request, "param") else None + if isinstance(param, (tuple, list)): + param = (param[0], False, param[1]) + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet_share fixture"): + async for x in _unet_impl( + rundir_module, pytestconfig, unshare=False, param=param + ): + yield x + + +@pytest.fixture(scope="module") +async def unet_unshare(request, rundir_module, pytestconfig): # pylint: disable=W0621 + """A unet creating fixutre. + + This unshare variant has the top level munet unshare the process inline so that + root level commands and actions are execute in a new namespace. This allows things + like scapy inline in tests to work. + + The request param is either the basename of the config file or a tuple of the form: + (basename, top_level_pidns), the second value is a boolean. + """ + param = request.param if hasattr(request, "param") else None + if isinstance(param, (tuple, list)): + param = (param[0], True, param[1]) + sdir = os.path.dirname(os.path.realpath(request.fspath)) + async with achdir(sdir, "unet_unshare fixture"): + async for x in _unet_impl( + rundir_module, pytestconfig, unshare=True, param=param + ): + yield x + + +# ================= +# Function Fixtures +# ================= + + +@pytest.fixture(autouse=True, scope="function") +async def function_autouse(request): + async with achdir( + os.path.dirname(os.path.realpath(request.fspath)), "func.fixture" + ): + yield + + +@pytest.fixture(autouse=True) +async def check_for_pause(request, pytestconfig): + # When we unshare inline we can't pause in the pytest_runtest_makereport hook + # so do it here. + if BaseMunet.g_unet and BaseMunet.g_unet.unshare_inline: + pause = bool(pytestconfig.getoption("--pause")) + if pause: + await async_pause_test(f"XXX before test '{request.node.name}'") + yield + + +@pytest.fixture(scope="function") +def stepf(pytestconfig): + class Stepnum: + """Track the stepnum in closure.""" + + num = 0 + + def inc(self): + self.num += 1 + + pause = pytestconfig.getoption("pause") + stepnum = Stepnum() + + def stepfunction(desc=""): + desc = f": {desc}" if desc else "" + if pause: + pause_test(f"before step {stepnum.num}{desc}") + logging.info("STEP %s%s", stepnum.num, desc) + stepnum.inc() + + return stepfunction + + +@pytest_asyncio.fixture(scope="function") +async def astepf(pytestconfig): + class Stepnum: + """Track the stepnum in closure.""" + + num = 0 + + def inc(self): + self.num += 1 + + pause = pytestconfig.getoption("pause") + stepnum = Stepnum() + + async def stepfunction(desc=""): + desc = f": {desc}" if desc else "" + if pause: + await async_pause_test(f"before step {stepnum.num}{desc}") + logging.info("STEP %s%s", stepnum.num, desc) + stepnum.inc() + + return stepfunction + + +@pytest.fixture(scope="function") +def rundir(): + d = os.path.join("/tmp/unet-test", get_test_logdir(module=False)) + logging.debug("conftest: test function rundir %s", d) + return d + + +# Configure logging +@pytest.hookimpl(hookwrapper=True, tryfirst=True) +def pytest_runtest_setup(item): + d = os.path.join( + "/tmp/unet-test", get_test_logdir(nodeid=item.nodeid, module=False) + ) + config = item.config + logging_plugin = config.pluginmanager.get_plugin("logging-plugin") + filename = Path(d, "pytest-exec.log") + logging_plugin.set_log_path(str(filename)) + logging.debug("conftest: test function setup: rundir %s", d) + yield + + +@pytest.fixture +async def unet_perfunc(request, rundir, pytestconfig): # pylint: disable=W0621 + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, param=param): + yield x + + +@pytest.fixture +async def unet_perfunc_unshare(request, rundir, pytestconfig): # pylint: disable=W0621 + """Build unet per test function with an optional topology basename parameter. + + The fixture can be parameterized to choose different config files. + For example, use as follows to run the test with unet_perfunc configured + first with a config file named `cfg1.yaml` then with config file `cfg2.yaml` + (where the actual files could end with `json` or `toml` rather than `yaml`). + + @pytest.mark.parametrize( + "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"] + ) + def test_example(unet_perfunc) + """ + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, unshare=True, param=param): + yield x + + +@pytest.fixture +async def unet_perfunc_share(request, rundir, pytestconfig): # pylint: disable=W0621 + """Build unet per test function with an optional topology basename parameter. + + This share variant keeps munet from unsharing the process to a new namespace so that + root level commands and actions are execute on the host, normally they are executed + in the munet namespace which allowing things like scapy inline in tests to work. + + The fixture can be parameterized to choose different config files. For example, use + as follows to run the test with unet_perfunc configured first with a config file + named `cfg1.yaml` then with config file `cfg2.yaml` (where the actual files could + end with `json` or `toml` rather than `yaml`). + + @pytest.mark.parametrize( + "unet_perfunc", ["cfg1", "cfg2]", indirect=["unet_perfunc"] + ) + def test_example(unet_perfunc) + """ + param = request.param if hasattr(request, "param") else None + async for x in _unet_impl(rundir, pytestconfig, unshare=False, param=param): + yield x diff --git a/tests/topotests/munet/testing/hooks.py b/tests/topotests/munet/testing/hooks.py new file mode 100644 index 000000000000..9b6a49a18ce0 --- /dev/null +++ b/tests/topotests/munet/testing/hooks.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""A module that implements pytest hooks. + +To use in your project, in your conftest.py add: + + from munet.testing.hooks import * +""" +import logging +import os +import sys +import traceback + +import pytest + +from ..base import BaseMunet # pylint: disable=import-error +from ..cli import cli # pylint: disable=import-error +from .util import pause_test + + +# =================== +# Hooks (non-fixture) +# =================== + + +def pytest_addoption(parser): + parser.addoption( + "--cli-on-error", + action="store_true", + help="CLI on test failure", + ) + + parser.addoption( + "--coverage", + action="store_true", + help="Enable coverage gathering if supported", + ) + + parser.addoption( + "--gdb", + default="", + metavar="HOST[,HOST...]", + help="Comma-separated list of nodes to launch gdb on, or 'all'", + ) + parser.addoption( + "--gdb-breakpoints", + default="", + metavar="BREAKPOINT[,BREAKPOINT...]", + help="Comma-separated list of breakpoints", + ) + parser.addoption( + "--gdb-use-emacs", + action="store_true", + help="Use emacsclient to run gdb instead of a shell", + ) + + parser.addoption( + "--pcap", + default="", + metavar="NET[,NET...]", + help="Comma-separated list of networks to capture packets on, or 'all'", + ) + + parser.addoption( + "--pause", + action="store_true", + help="Pause after each test", + ) + parser.addoption( + "--pause-at-end", + action="store_true", + help="Pause before taking munet down", + ) + parser.addoption( + "--pause-on-error", + action="store_true", + help="Pause after (disables default when --shell or -vtysh given)", + ) + parser.addoption( + "--no-pause-on-error", + dest="pause_on_error", + action="store_false", + help="Do not pause after (disables default when --shell or -vtysh given)", + ) + + parser.addoption( + "--shell", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to spawn shell on, or 'all'", + ) + + parser.addoption( + "--stdout", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to open tail-f stdout window on, or 'all'", + ) + + parser.addoption( + "--stderr", + default="", + metavar="NODE[,NODE...]", + help="Comma-separated list of nodes to open tail-f stderr window on, or 'all'", + ) + + +def pytest_configure(config): + if "PYTEST_XDIST_WORKER" not in os.environ: + os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no") + os.environ["PYTEST_IS_WORKER"] = "" + is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no" + is_worker = False + else: + os.environ["PYTEST_IS_WORKER"] = os.environ["PYTEST_XDIST_WORKER"] + is_xdist = True + is_worker = True + + # Turn on live logging if user specified verbose and the config has a CLI level set + if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"): + if config.getoption("--log-cli-level", None) is None: + # By setting the CLI option to the ini value it enables log_cli=1 + cli_level = config.getini("log_cli_level") + if cli_level is not None: + config.option.log_cli_level = cli_level + + have_tmux = bool(os.getenv("TMUX", "")) + have_screen = not have_tmux and bool(os.getenv("STY", "")) + have_xterm = not have_tmux and not have_screen and bool(os.getenv("DISPLAY", "")) + have_windows = have_tmux or have_screen or have_xterm + have_windows_pause = have_tmux or have_xterm + xdist_no_windows = is_xdist and not is_worker and not have_windows_pause + + for winopt in ["--shell", "--stdout", "--stderr"]: + b = config.getoption(winopt) + if b and xdist_no_windows: + pytest.exit( + f"{winopt} use requires byobu/TMUX/XTerm " + f"under dist {os.environ['PYTEST_XDIST_MODE']}" + ) + elif b and not is_xdist and not have_windows: + pytest.exit(f"{winopt} use requires byobu/TMUX/SCREEN/XTerm") + + +def pytest_runtest_makereport(item, call): + """Pause or invoke CLI as directed by config.""" + isatty = sys.stdout.isatty() + + pause = bool(item.config.getoption("--pause")) + skipped = False + + if call.excinfo is None: + error = False + elif call.excinfo.typename == "Skipped": + skipped = True + error = False + pause = False + else: + error = True + modname = item.parent.module.__name__ + exval = call.excinfo.value + logging.error( + "test %s/%s failed: %s: stdout: '%s' stderr: '%s'", + modname, + item.name, + exval, + exval.stdout if hasattr(exval, "stdout") else "NA", + exval.stderr if hasattr(exval, "stderr") else "NA", + ) + if not pause: + pause = item.config.getoption("--pause-on-error") + + if error and isatty and item.config.getoption("--cli-on-error"): + if not BaseMunet.g_unet: + logging.error("Could not launch CLI b/c no munet exists yet") + else: + print(f"\nCLI-ON-ERROR: {call.excinfo.typename}") + print(f"CLI-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}") + if hasattr(exval, "stdout") and exval.stdout: + print("stdout: " + exval.stdout.replace("\n", "\nstdout: ")) + if hasattr(exval, "stderr") and exval.stderr: + print("stderr: " + exval.stderr.replace("\n", "\nstderr: ")) + cli(BaseMunet.g_unet) + + if pause: + if skipped: + item.skip_more_pause = True + elif hasattr(item, "skip_more_pause"): + pass + elif call.when == "setup": + if error: + item.skip_more_pause = True + + # we can't asyncio.run() (which pause does) if we are unhsare_inline + # at this point, count on an autouse fixture to pause instead in this + # case + if not BaseMunet.g_unet or not BaseMunet.g_unet.unshare_inline: + pause_test(f"before test '{item.nodeid}'") + + # check for a result to try and catch setup (or module setup) failure + # e.g., after a module level fixture fails, we do not want to pause on every + # skipped test. + elif call.when == "teardown" and call.excinfo: + logging.warning( + "Caught exception during teardown: %s\n:Traceback:\n%s", + call.excinfo, + "".join(traceback.format_tb(call.excinfo.tb)), + ) + pause_test(f"after teardown after test '{item.nodeid}'") + elif call.when == "teardown" and call.result: + pause_test(f"after test '{item.nodeid}'") + elif error: + item.skip_more_pause = True + print(f"\nPAUSE-ON-ERROR: {call.excinfo.typename}") + print(f"PAUSE-ON-ERROR:\ntest {modname}/{item.name} failed: {exval}") + if hasattr(exval, "stdout") and exval.stdout: + print("stdout: " + exval.stdout.replace("\n", "\nstdout: ")) + if hasattr(exval, "stderr") and exval.stderr: + print("stderr: " + exval.stderr.replace("\n", "\nstderr: ")) + pause_test(f"PAUSE-ON-ERROR: '{item.nodeid}'") diff --git a/tests/topotests/munet/testing/util.py b/tests/topotests/munet/testing/util.py new file mode 100644 index 000000000000..a1a94bcd1bd1 --- /dev/null +++ b/tests/topotests/munet/testing/util.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 eval: (blacken-mode 1) -*- +# SPDX-License-Identifier: GPL-2.0-or-later +# +# April 22 2022, Christian Hopps <chopps@gmail.com> +# +# Copyright (c) 2022, LabN Consulting, L.L.C +# +"""Utility functions useful when using munet testing functionailty in pytest.""" +import asyncio +import datetime +import functools +import logging +import sys +import time + +from ..base import BaseMunet +from ..cli import async_cli + + +# ================= +# Utility Functions +# ================= + + +async def async_pause_test(desc=""): + isatty = sys.stdout.isatty() + if not isatty: + desc = f" for {desc}" if desc else "" + logging.info("NO PAUSE on non-tty terminal%s", desc) + return + + while True: + if desc: + print(f"\n== PAUSING: {desc} ==") + try: + user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ') + except EOFError: + print("^D...continuing") + break + user = user.strip() + if user == "cli": + await async_cli(BaseMunet.g_unet) + elif user == "pdb": + breakpoint() # pylint: disable=W1515 + elif user: + print(f'Unrecognized input: "{user}"') + else: + break + + +def pause_test(desc=""): + asyncio.run(async_pause_test(desc)) + + +def retry(retry_timeout, initial_wait=0, expected=True): + """decorator: retry while functions return is not None or raises an exception. + + * `retry_timeout`: Retry for at least this many seconds; after waiting + initial_wait seconds + * `initial_wait`: Sleeps for this many seconds before first executing function + * `expected`: if False then the return logic is inverted, except for exceptions, + (i.e., a non None ends the retry loop, and returns that value) + """ + + def _retry(func): + @functools.wraps(func) + def func_retry(*args, **kwargs): + retry_sleep = 2 + + # Allow the wrapped function's args to override the fixtures + _retry_timeout = kwargs.pop("retry_timeout", retry_timeout) + _expected = kwargs.pop("expected", expected) + _initial_wait = kwargs.pop("initial_wait", initial_wait) + retry_until = datetime.datetime.now() + datetime.timedelta( + seconds=_retry_timeout + _initial_wait + ) + + if initial_wait > 0: + logging.info("Waiting for [%s]s as initial delay", initial_wait) + time.sleep(initial_wait) + + while True: + seconds_left = (retry_until - datetime.datetime.now()).total_seconds() + try: + ret = func(*args, **kwargs) + if _expected and ret is None: + logging.debug("Function succeeds") + return ret + logging.debug("Function returned %s", ret) + except Exception as error: + logging.info("Function raised exception: %s", str(error)) + ret = error + + if seconds_left < 0: + logging.info("Retry timeout of %ds reached", _retry_timeout) + if isinstance(ret, Exception): + raise ret + return ret + + logging.info( + "Sleeping %ds until next retry with %.1f retry time left", + retry_sleep, + seconds_left, + ) + time.sleep(retry_sleep) + + func_retry._original = func # pylint: disable=W0212 + return func_retry + + return _retry diff --git a/tests/topotests/nhrp_topo/r1/sharp_route4.json b/tests/topotests/nhrp_topo/r1/sharp_route4.json new file mode 100644 index 000000000000..4c4b8eaccdb4 --- /dev/null +++ b/tests/topotests/nhrp_topo/r1/sharp_route4.json @@ -0,0 +1,46 @@ +{ + "4.4.4.1\/32":[ + { + "prefix":"4.4.4.1\/32", + "prefixLen":32, + "protocol":"sharp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "ip":"10.255.255.2", + "interfaceName":"r1-gre0", + "active":true + } + ] + } + ], + "5.5.5.1\/32":[ + { + "prefix":"5.5.5.1\/32", + "prefixLen":32, + "protocol":"sharp", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "installed":true, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthops":[ + { + "fib":true, + "ip":"10.255.255.2", + "interfaceName":"r1-gre0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.py b/tests/topotests/nhrp_topo/test_nhrp_topo.py index 879f5250ec73..78b82eda79b2 100644 --- a/tests/topotests/nhrp_topo/test_nhrp_topo.py +++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py @@ -108,6 +108,12 @@ def setup_module(mod): TopoRouter.RD_NHRP, os.path.join(CWD, "{}/nhrpd.conf".format(rname)) ) + # Include sharpd for r1 + if rname == "r1": + router.load_config( + TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) + ) + # Initialize all routers. logger.info("Launching NHRP") for name in router_list: @@ -201,6 +207,38 @@ def test_nhrp_connection(): logger.info("Check Ping IPv4 from R1 to R2 OK") +def test_route_install(): + "Test use of NHRP routes by other protocols (sharpd here)." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Testing route install over NHRP tunnel") + + # Install sharpd routes over an NHRP route + r1 = tgen.gears["r1"] + + # Install one recursive and one non-recursive sharpd route + r1.vtysh_cmd("sharp install route 4.4.4.1 nexthop 10.255.255.2 1") + + r1.vtysh_cmd("sharp install route 5.5.5.1 nexthop 10.255.255.2 1 no-recurse") + + json_file = "{}/{}/sharp_route4.json".format(CWD, "r1") + expected = json.loads(open(json_file).read()) + + test_func = partial( + topotest.router_json_cmp, r1, "show ip route sharp json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5) + + logger.info("Sharp routes:") + output = r1.vtysh_cmd("show ip route sharp") + logger.info(output) + + assertmsg = '"{}" JSON route output mismatches'.format(r1.name) + assert result is None, assertmsg + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf index 6c7cb9624082..e6e5733010fb 100644 --- a/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf +++ b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf @@ -1,35 +1,35 @@ -debug ospf6 lsa all -debug ospf6 message all -debug ospf6 route all -debug ospf6 spf time -debug ospf6 spf database -debug ospf6 zebra send -debug ospf6 zebra recv - -debug ospf6 lsa router -debug ospf6 lsa router originate -debug ospf6 lsa router examine -debug ospf6 lsa router flooding -debug ospf6 lsa as-external -debug ospf6 lsa as-external originate -debug ospf6 lsa as-external examine -debug ospf6 lsa as-external flooding -debug ospf6 lsa intra-prefix -debug ospf6 lsa intra-prefix originate -debug ospf6 lsa intra-prefix examine -debug ospf6 lsa intra-prefix flooding -debug ospf6 border-routers -debug ospf6 zebra -debug ospf6 interface -debug ospf6 neighbor -debug ospf6 flooding -debug ospf6 gr helper -debug ospf6 spf process -debug ospf6 route intra-area -debug ospf6 route inter-area -debug ospf6 abr -debug ospf6 asbr -debug ospf6 nssa +!debug ospf6 lsa all +!debug ospf6 message all +!debug ospf6 route all +!debug ospf6 spf time +!debug ospf6 spf database +!debug ospf6 zebra send +!debug ospf6 zebra recv +! +!debug ospf6 lsa router +!debug ospf6 lsa router originate +!debug ospf6 lsa router examine +!debug ospf6 lsa router flooding +!debug ospf6 lsa as-external +!debug ospf6 lsa as-external originate +!debug ospf6 lsa as-external examine +!debug ospf6 lsa as-external flooding +!debug ospf6 lsa intra-prefix +!debug ospf6 lsa intra-prefix originate +!debug ospf6 lsa intra-prefix examine +!debug ospf6 lsa intra-prefix flooding +!debug ospf6 border-routers +!debug ospf6 zebra +!debug ospf6 interface +!debug ospf6 neighbor +!debug ospf6 flooding +!debug ospf6 gr helper +!debug ospf6 spf process +!debug ospf6 route intra-area +!debug ospf6 route inter-area +!debug ospf6 abr +!debug ospf6 asbr +!debug ospf6 nssa ! interface r1-eth0 ipv6 ospf6 area 0 diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py index aa43b977b02f..45e1bc8db387 100755 --- a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py +++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py @@ -430,6 +430,144 @@ def test_gr_rt7(): check_routers(restarting="rt7") +# +# Test rt1 performing an unplanned graceful restart +# +def test_unplanned_gr_rt1(): + logger.info("Test: verify rt1 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt1", ["ospf6d"]) + + expect_grace_lsa(restarting="1.1.1.1", helper="rt2") + ensure_gr_is_in_zebra("rt1") + check_routers(restarting="rt1") + + +# +# Test rt2 performing an unplanned graceful restart +# +def test_unplanned_gr_rt2(): + logger.info("Test: verify rt2 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt2", ["ospf6d"]) + + expect_grace_lsa(restarting="2.2.2.2", helper="rt1") + expect_grace_lsa(restarting="2.2.2.2", helper="rt3") + ensure_gr_is_in_zebra("rt2") + check_routers(restarting="rt2") + + +# +# Test rt3 performing an unplanned graceful restart +# +def test_unplanned_gr_rt3(): + logger.info("Test: verify rt3 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt3", ["ospf6d"]) + + expect_grace_lsa(restarting="3.3.3.3", helper="rt2") + expect_grace_lsa(restarting="3.3.3.3", helper="rt4") + expect_grace_lsa(restarting="3.3.3.3", helper="rt6") + ensure_gr_is_in_zebra("rt3") + check_routers(restarting="rt3") + + +# +# Test rt4 performing an unplanned graceful restart +# +def test_unplanned_gr_rt4(): + logger.info("Test: verify rt4 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt4", ["ospf6d"]) + + expect_grace_lsa(restarting="4.4.4.4", helper="rt3") + expect_grace_lsa(restarting="4.4.4.4", helper="rt5") + ensure_gr_is_in_zebra("rt4") + check_routers(restarting="rt4") + + +# +# Test rt5 performing an unplanned graceful restart +# +def test_unplanned_gr_rt5(): + logger.info("Test: verify rt5 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt5", ["ospf6d"]) + + expect_grace_lsa(restarting="5.5.5.5", helper="rt4") + ensure_gr_is_in_zebra("rt5") + check_routers(restarting="rt5") + + +# +# Test rt6 performing an unplanned graceful restart +# +def test_unplanned_gr_rt6(): + logger.info("Test: verify rt6 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt6", ["ospf6d"]) + + expect_grace_lsa(restarting="6.6.6.6", helper="rt3") + expect_grace_lsa(restarting="6.6.6.6", helper="rt7") + ensure_gr_is_in_zebra("rt6") + check_routers(restarting="rt6") + + +# +# Test rt7 performing an unplanned graceful restart +# +def test_unplanned_gr_rt7(): + logger.info("Test: verify rt7 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False) + start_router_daemons(tgen, "rt7", ["ospf6d"]) + + expect_grace_lsa(restarting="6.6.6.6", helper="rt6") + ensure_gr_is_in_zebra("rt7") + check_routers(restarting="rt7") + + # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py index 9ec0266aa0f4..f95f7bbe5e8b 100644 --- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py +++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py @@ -129,6 +129,7 @@ def setup_module(mod): router_list = tgen.routers() for rname, router in router_list.items(): + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) if os.path.isfile(daemon_file): router.load_config(TopoRouter.RD_ZEBRA, daemon_file) diff --git a/tests/topotests/ospf_basic_functionality/ospf_lan.json b/tests/topotests/ospf_basic_functionality/ospf_lan.json index 126934c344e0..54863382b2bc 100644 --- a/tests/topotests/ospf_basic_functionality/ospf_lan.json +++ b/tests/topotests/ospf_basic_functionality/ospf_lan.json @@ -18,7 +18,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 98 } }, @@ -27,7 +27,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 99 } }, @@ -36,7 +36,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 0 } }, @@ -45,7 +45,7 @@ "ospf": { "area": "0.0.0.3", "hello_interval": 1, - "dead_interval": 4, + "dead_interval": 10, "priority": 0 } } @@ -135,4 +135,4 @@ } } } -} \ No newline at end of file +} diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py index 3d15e94a51e0..0531e81d4431 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py @@ -84,8 +84,8 @@ """ TOPOOLOGY = Please view in a fixed-width font such as Courier. - +---+ A0 +---+ - +R1 +------------+R2 | + +---+ A0 +---+ + |R1 +------------+R2 | +-+-+- +--++ | -- -- | | -- A0 -- | @@ -94,8 +94,8 @@ | -- -- | | -- -- | +-+-+- +-+-+ - +R0 +-------------+R3 | - +---+ A0 +---+ + |R0 +-------------+R3 | + +---+ A0 +---+ TESTCASES = 1. OSPF summarisation functionality. @@ -140,7 +140,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -260,11 +260,9 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf": { @@ -290,7 +288,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -306,7 +304,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Change the summary address mask to lower match (ex - 16 to 8)") ospf_summ_r1 = { @@ -334,7 +332,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -350,7 +348,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Change the summary address mask to higher match (ex - 8 to 24)") ospf_summ_r1 = { @@ -378,7 +376,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -399,7 +397,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step(" Un configure one of the summary address.") ospf_summ_r1 = { @@ -432,7 +430,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -459,7 +457,7 @@ def test_ospf_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -506,11 +504,9 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -538,7 +534,7 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -554,26 +550,26 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "Configure route map and & rule to permit configured summary address," @@ -635,7 +631,7 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -650,7 +646,7 @@ def test_ospf_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) write_test_footer(tc_name) @@ -697,7 +693,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -731,7 +727,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -747,26 +743,26 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) step("Delete the configured summary") ospf_summ_r1 = { @@ -789,19 +785,17 @@ def test_ospf_type5_summary_tc42_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name + assert result is not True, ( + "Testcase {} : Failed \n Expected: Summary Route should not present in RIB" + "Error: Summary Route still present in RIB".format(tc_name) ) step("show ip ospf summary should not have any summary address.") @@ -816,9 +810,10 @@ def test_ospf_type5_summary_tc42_p0(request): } dut = "r0" result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Summary Route should not present in OSPF DB" + "Error: Summary still present in DB".format(tc_name) + ) dut = "r1" step("All 5 routes are advertised after deletion of configured summary.") @@ -829,7 +824,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("configure the summary again and delete static routes .") ospf_summ_r1 = { @@ -857,7 +852,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) input_dict = { "r0": { @@ -874,18 +869,18 @@ def test_ospf_type5_summary_tc42_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Add back static routes.") input_dict_static_rtes = { @@ -901,18 +896,18 @@ def test_ospf_type5_summary_tc42_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} dut = "r1" @@ -923,7 +918,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show configure summaries.") @@ -940,7 +935,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Configure new static route which is matching configured summary.") input_dict_static_rtes = { @@ -982,7 +977,7 @@ def test_ospf_type5_summary_tc42_p0(request): ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"] - ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + ip_net = str(ipaddress.ip_interface("{}".format(ip)).network) ospf_summ_r1 = { "r0": { "ospf": {"summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]} @@ -1004,7 +999,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Shut one of the interface") intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"] @@ -1076,7 +1071,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}} @@ -1087,7 +1082,7 @@ def test_ospf_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -1113,18 +1108,18 @@ def test_ospf_type5_summary_tc42_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) ospf_summ_r1 = { "r0": { @@ -1188,11 +1183,9 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf": { @@ -1224,7 +1217,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1240,7 +1233,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -1263,19 +1256,17 @@ def test_ospf_type5_summary_tc45_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name + assert result is not True, ( + "Testcase {} : Failed \n Expected: Summary Routes should not be present in RIB. \n" + "Error: Summary Route still present in RIB".format(tc_name) ) step("show ip ospf summary should not have any summary address.") @@ -1292,7 +1283,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1317,7 +1308,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1333,7 +1324,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1363,7 +1354,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1382,7 +1373,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1403,10 +1394,9 @@ def test_ospf_type5_summary_tc45_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( @@ -1418,9 +1408,10 @@ def test_ospf_type5_summary_tc45_p0(request): tag="88888", expected=False, ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "Verify that boundary values tags are used for summary route" @@ -1439,7 +1430,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1470,24 +1461,24 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Configure summary first & then configure matching static route.") @@ -1528,7 +1519,7 @@ def test_ospf_type5_summary_tc45_p0(request): step("Repeat steps 1 to 10 of summarisation in non Back bone area.") reset_config_on_routers(tgen) - step("Change the area id on the interface on R0") + step("Change the area id on the interface on R0 to R1 from 0.0.0.0 to 0.0.0.1") input_dict = { "r0": { "links": { @@ -1558,7 +1549,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = create_interfaces_cfg(tgen, input_dict) assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - step("Change the area id on the interface ") + step("Change the area id on the interface on R1 to R0 from 0.0.0.0 to 0.0.0.1") input_dict = { "r1": { "links": { @@ -1589,7 +1580,7 @@ def test_ospf_type5_summary_tc45_p0(request): assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -1619,11 +1610,9 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf": { @@ -1655,7 +1644,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1691,19 +1680,17 @@ def test_ospf_type5_summary_tc45_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB " + "Error: Summary Route still present in RIB".format(tc_name) ) step("show ip ospf summary should not have any summary address.") @@ -1720,7 +1707,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1745,7 +1732,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1761,7 +1748,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1791,7 +1778,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1810,7 +1797,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1831,10 +1818,9 @@ def test_ospf_type5_summary_tc45_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( @@ -1846,9 +1832,10 @@ def test_ospf_type5_summary_tc45_p0(request): tag="88888", expected=False, ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "Verify that boundary values tags are used for summary route" @@ -1867,7 +1854,7 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1898,24 +1885,24 @@ def test_ospf_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Configure summary first & then configure matching static route.") @@ -1999,7 +1986,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -2031,20 +2018,20 @@ def test_ospf_type5_summary_tc46_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB." + "Error: Routes still present in RIB".format(tc_name) + ) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv4"][0]: { "summaryAddress": SUMMARY["ipv4"][0], @@ -2055,7 +2042,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -2080,10 +2067,9 @@ def test_ospf_type5_summary_tc46_p0(request): step("Verify that summary lsa is withdrawn from R1 and deleted from R0.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( @@ -2091,9 +2077,7 @@ def test_ospf_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed. Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -2109,7 +2093,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary still present in DB".format(tc_name) step("Reconfigure summary with no advertise.") ospf_summ_r1 = { @@ -2138,20 +2122,20 @@ def test_ospf_type5_summary_tc46_p0(request): dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB \n" + "Error: Routes still present in RIB".format(tc_name) + ) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv4"][0]: { "summaryAddress": SUMMARY["ipv4"][0], @@ -2162,7 +2146,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Change summary address from no advertise to advertise " @@ -2211,7 +2195,7 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2227,26 +2211,26 @@ def test_ospf_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB" + "Error: Routes is present in RIB".format(tc_name) + ) write_test_footer(tc_name) @@ -2293,11 +2277,9 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -2325,7 +2307,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2341,26 +2323,26 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step( "configure route map and add rule to permit configured static " @@ -2423,7 +2405,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -2461,18 +2443,18 @@ def test_ospf_type5_summary_tc47_p0(request): step("Verify that advertised summary route is flushed from neighbor.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Delete the configured route map.") @@ -2511,7 +2493,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -2526,7 +2508,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Reconfigure the route map with denying configure summary address.") @@ -2570,7 +2552,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Redistribute static/connected routes without route map.") @@ -2606,7 +2588,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv4"][0]: { @@ -2621,7 +2603,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step( "Configure rule to deny all the routes in route map and configure" @@ -2672,18 +2654,18 @@ def test_ospf_type5_summary_tc47_p0(request): step("Verify that no summary route is originated.") dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB" + "Error: Routes still present in RIB".format(tc_name) + ) routemaps = { "r0": { @@ -2773,7 +2755,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2789,7 +2771,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Change route map rule for 1 of the routes to deny.") # Create ip prefix list @@ -2822,7 +2804,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("add rule in route map to deny configured summary address.") # Create ip prefix list @@ -2855,7 +2837,7 @@ def test_ospf_type5_summary_tc47_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -2995,7 +2977,7 @@ def test_ospf_type5_summary_tc51_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) write_test_footer(tc_name) @@ -3042,11 +3024,9 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -3074,7 +3054,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3090,26 +3070,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB.\n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -3130,7 +3110,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3146,26 +3126,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("Kill OSPFd daemon on R0.") kill_router_daemons(tgen, "r0", ["ospfd"]) @@ -3176,7 +3156,7 @@ def test_ospf_type5_summary_tc49_p2(request): step("Verify OSPF neighbors are up after bringing back ospfd in R0") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -3194,7 +3174,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3210,26 +3190,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed \n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB\n" + "Error: Routes still present in RIB".format(tc_name) + ) step("restart zebrad") kill_router_daemons(tgen, "r0", ["zebra"]) @@ -3251,7 +3231,7 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -3267,26 +3247,26 @@ def test_ospf_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]} } dut = "r1" result = verify_ospf_rib(tgen, dut, input_dict, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( - tc_name, result + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in OSPF RIB. \n Error: " + "Routes still present in OSPF RIB {}".format(tc_name, result) ) result = verify_rib( tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False ) - assert ( - result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + assert result is not True, ( + "Testcase {} : Failed\n Expected: Routes should not be present in RIB.\n" + "Error: Routes still present in RIB".format(tc_name) + ) write_test_footer(tc_name) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py index cff59c3a4061..603aeadb855b 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py @@ -132,7 +132,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -235,11 +235,9 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r0 = { "r0": { @@ -259,9 +257,7 @@ def test_ospf_type5_summary_tc44_p0(request): "route is sent to R1." ) - step( - "Configure summary & redistribute static/connected route with " "metric type 2" - ) + step("Configure summary & redistribute static/connected route with metric type 2") input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}} dut = "r1" @@ -272,7 +268,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -288,7 +284,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Learn type 7 lsa from neighbours") @@ -312,7 +308,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed. Error: Routes is missing in RIB".format(tc_name) ospf_summ_r0 = { "r0": { @@ -340,7 +336,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) step("Verify that already originated summary is intact.") input_dict = { @@ -356,7 +352,7 @@ def test_ospf_type5_summary_tc44_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict) assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed. Error: Summary missing in OSPF DB".format(tc_name) dut = "r1" aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}} diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py index 40df0b230861..88219b84006e 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py @@ -100,7 +100,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -166,7 +166,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): step("Verify that the neighbour is not FULL between R1 and R2.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -192,7 +192,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -223,7 +223,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=10 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -245,7 +245,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -260,7 +260,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): "show ip ospf neighbor cmd." ) ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -274,7 +274,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -314,7 +314,7 @@ def test_ospf_authentication_simple_pass_tc28_p1(request): dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -361,7 +361,7 @@ def test_ospf_authentication_md5_tc29_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=6 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -393,7 +393,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -426,7 +426,7 @@ def test_ospf_authentication_md5_tc29_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=10 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -454,7 +454,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -469,7 +469,7 @@ def test_ospf_authentication_md5_tc29_p1(request): "show ip ospf neighbor cmd." ) ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -483,7 +483,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -528,7 +528,7 @@ def test_ospf_authentication_md5_tc29_p1(request): dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -576,7 +576,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): ospf_covergence = verify_ospf_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=10 ) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -608,7 +608,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -655,7 +655,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -687,7 +687,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -720,7 +720,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -765,7 +765,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -810,7 +810,7 @@ def test_ospf_authentication_different_auths_tc30_p1(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py index 124b36a5fa34..e58f081f9636 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py @@ -111,7 +111,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -177,7 +177,7 @@ def test_ospf_chaos_tc31_p1(request): step("Verify OSPF neighbors after base config is done.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -198,7 +198,7 @@ def test_ospf_chaos_tc31_p1(request): dut = "r0" # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -208,7 +208,7 @@ def test_ospf_chaos_tc31_p1(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -217,7 +217,7 @@ def test_ospf_chaos_tc31_p1(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -227,7 +227,7 @@ def test_ospf_chaos_tc31_p1(request): step("Verify OSPF neighbors are up after bringing back ospfd in R0") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -250,7 +250,7 @@ def test_ospf_chaos_tc31_p1(request): dut = "r1" # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is not True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -260,7 +260,7 @@ def test_ospf_chaos_tc31_p1(request): step("Verify OSPF neighbors are up after bringing back ospfd in R1") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -316,7 +316,7 @@ def test_ospf_chaos_tc32_p1(request): step("Verify OSPF neighbors after base config is done.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -338,7 +338,7 @@ def test_ospf_chaos_tc32_p1(request): step("Verify OSPF neighbors are up after restarting R0") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -361,7 +361,7 @@ def test_ospf_chaos_tc32_p1(request): step("Verify OSPF neighbors are up after restarting R1") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -421,7 +421,7 @@ def test_ospf_chaos_tc34_p1(request): step("Verify OSPF neighbors after base config is done.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -444,7 +444,7 @@ def test_ospf_chaos_tc34_p1(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -453,7 +453,7 @@ def test_ospf_chaos_tc34_p1(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -463,7 +463,7 @@ def test_ospf_chaos_tc34_p1(request): step("Verify OSPF neighbors are up after bringing back ospfd in R0") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -488,7 +488,7 @@ def test_ospf_chaos_tc34_p1(request): step("Verify OSPF neighbors are up after bringing back ospfd in R1") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py index d58e2503edcb..aba313db9f73 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py @@ -114,7 +114,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -168,7 +168,7 @@ def test_ospf_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -217,7 +217,7 @@ def test_ospf_ecmp_tc16_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -227,7 +227,7 @@ def test_ospf_ecmp_tc16_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -259,7 +259,7 @@ def test_ospf_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -286,7 +286,7 @@ def test_ospf_ecmp_tc16_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -303,7 +303,7 @@ def test_ospf_ecmp_tc16_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -343,7 +343,7 @@ def test_ospf_ecmp_tc17_p0(request): step("Verify that OSPF is up with 2 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -394,7 +394,7 @@ def test_ospf_ecmp_tc17_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -411,7 +411,7 @@ def test_ospf_ecmp_tc17_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py index 6a565571be3f..1eeb23e9f7c5 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py @@ -115,7 +115,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -169,7 +169,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -222,7 +222,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): dut = "r0" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -231,7 +231,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): dut = "r2" ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -261,7 +261,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -278,7 +278,7 @@ def test_ospf_lan_ecmp_tc18_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py index 53b1be6d716d..1358027f219a 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_lan.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py @@ -114,7 +114,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -165,9 +165,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -185,9 +185,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -197,7 +197,8 @@ def test_ospf_lan_tc1_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( - "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." + "Configure DR priority 100 on R0 and clear ospf neighbors " + "on all the routers." ) input_dict = { @@ -223,9 +224,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -235,7 +236,8 @@ def test_ospf_lan_tc1_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step( - "Configure DR pririty 150 on R0 and clear ospf neighbors " "on all the routers." + "Configure DR priority 150 on R0 and clear ospf neighbors " + "on all the routers." ) input_dict = { @@ -261,9 +263,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -297,9 +299,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "2-Way", "role": "DROther"}, - "r3": {"state": "2-Way", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "2-Way", "role": "DROther"}, + "r3": {"nbrState": "2-Way", "role": "DROther"}, } } } @@ -336,9 +338,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -355,7 +357,7 @@ def test_ospf_lan_tc1_p0(request): result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r0: OSPF neighbors-hip is up \n Error: {}".format( + ), "Testcase {} : Failed \n r0: OSPF neighbors-hip is up \n Error: {}".format( tc_name, result ) @@ -368,9 +370,9 @@ def test_ospf_lan_tc1_p0(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -423,9 +425,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -449,9 +451,9 @@ def test_ospf_lan_tc1_p0(request): "r1": { "ospf": { "neighbors": { - "r0": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r0": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -491,7 +493,7 @@ def test_ospf_lan_tc2_p0(request): "s1": { "ospf": { "priority": 98, - "timerDeadSecs": 4, + "timerDeadSecs": 10, "area": "0.0.0.3", "mcastMemberOspfDesignatedRouters": True, "mcastMemberOspfAllRouters": True, diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py index 0c4697cc212c..d669e21d4d2e 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py @@ -112,7 +112,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -220,11 +220,11 @@ def test_ospf_learning_tc15_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) - step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).") + step("Change area 1 as non nssa area (on the fly changing area type on DUT).") for rtr in ["r1", "r2", "r3"]: input_dict = { diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py index 858412f1d35a..a90d7dbdc05f 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py @@ -363,6 +363,185 @@ def test_ospf_p2mp_tc1_p0(request): write_test_footer(tc_name) +def test_ospf_p2mp_tc_delay_reflood(request): + """OSPF IFSM -Verify "delay-reflood" parameter in p2mp network.""" + tc_name = request.node.name + write_test_header(tc_name) + tgen = get_topogen() + r0 = tgen.gears["r0"] + + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + global topo + + step("Verify for interface with network type P2MP that delay-reflood is configured") + r0.vtysh_multicmd( + "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint delay-reflood" + ) + + dut = "r0" + input_dict = { + "r0": { + "links": { + "r1": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": True, + } + }, + "r2": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + "r3": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + delay_reflood_cfg = ( + tgen.net["r0"] + .cmd( + 'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"' + ) + .rstrip() + ) + + assertmsg = "delay-reflood' configuration applied, but not present in configuration" + assert ( + delay_reflood_cfg == " ip ospf network point-to-multipoint delay-reflood" + ), assertmsg + + step("Verify for interface with network type P2MP that delay-reflood is removed") + r0.vtysh_multicmd( + "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint" + ) + + input_dict = { + "r0": { + "links": { + "r1": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + "r2": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + "r3": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + delay_reflood_cfg = ( + tgen.net["r0"] + .cmd( + 'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"' + ) + .rstrip() + ) + assertmsg = ( + "delay-reflood' configuration removed, but still present in configuration" + ) + assert ( + delay_reflood_cfg != " ip ospf network point-to-multipoint delay-reflood" + ), assertmsg + + step( + "Verify for interface with network type P2MP that delay-reflood is removed with removal of network type" + ) + r0.vtysh_multicmd( + "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint delay-reflood" + ) + r0.vtysh_multicmd( + "conf t\ninterface r0-r1-eth0\nno ip ospf network point-to-multipoint" + ) + r0.vtysh_multicmd( + "conf t\ninterface r0-r1-eth0\nip ospf network point-to-multipoint" + ) + + input_dict = { + "r0": { + "links": { + "r1": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + "r2": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + "r3": { + "ospf": { + "mcastMemberOspfAllRouters": True, + "ospfEnabled": True, + "networkType": "POINTOMULTIPOINT", + "p2mpDelayReflood": False, + } + }, + } + } + } + result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) + assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) + + delay_reflood_cfg = ( + tgen.net["r0"] + .cmd( + 'vtysh -c "show running" | grep "^ ip ospf network point-to-multipoint delay-reflood"' + ) + .rstrip() + ) + assertmsg = ( + "delay-reflood' configuration removed, but still present in configuration" + ) + assert ( + delay_reflood_cfg != " ip ospf network point-to-multipoint delay-reflood" + ), assertmsg + + write_test_footer(tc_name) + + @retry(retry_timeout=30) def verify_ospf_json(tgen, dut, input_dict, cmd="show ip ospf database json"): del tgen @@ -411,17 +590,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -434,17 +613,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -457,17 +636,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.3": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } @@ -480,17 +659,17 @@ def test_ospf_nbrs(tgen): "neighbors": { "100.1.1.0": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.1": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], "100.1.1.2": [ { - "state": "Full/DROther", + "nbrState": "Full/DROther", } ], } diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py index dad6d915e8ef..c9f43cdfe497 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py @@ -127,7 +127,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -201,9 +201,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): redistribute_ospf(tgen, topo, "r0", "static", delete=True) - step( - "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32" - ) + step("Create prefix-list in R0 to permit 10.0.20.1/32 prefix & deny 10.0.20.2/32") # Create ip prefix list pfx_list = { @@ -294,7 +292,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -303,7 +301,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are present in fib \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are present in fib \n Error: {}".format( tc_name, result ) @@ -347,7 +345,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -356,7 +354,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -404,7 +402,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -413,7 +411,7 @@ def test_ospf_routemaps_functionality_tc19_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -464,7 +462,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, retry_timeout=4, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -479,7 +477,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -499,7 +497,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -508,7 +506,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -523,7 +521,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -532,7 +530,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -553,7 +551,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): result = verify_ospf_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: OSPF routes are present \n Error: {}".format( tc_name, result ) @@ -562,7 +560,7 @@ def test_ospf_routemaps_functionality_tc20_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n r1: routes are still present \n Error: {}".format( tc_name, result ) @@ -861,7 +859,7 @@ def test_ospf_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) @@ -930,7 +928,7 @@ def test_ospf_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) @@ -1078,7 +1076,7 @@ def test_ospf_routemaps_functionality_tc25_p0(request): # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py index 63c421ec8456..f0950a2db33e 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py @@ -123,7 +123,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -171,7 +171,7 @@ def test_ospf_redistribution_tc5_p0(request): step("Verify that OSPF neighbors are FULL.") ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -295,7 +295,7 @@ def test_ospf_redistribution_tc6_p0(request): step("Verify that OSPF neighbors are FULL.") ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -524,7 +524,7 @@ def test_ospf_redistribution_tc8_p1(request): step("Verify that OSPF neighbours are reset and forms new adjacencies.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -558,7 +558,7 @@ def test_ospf_rfc2328_appendinxE_p0(request): step("Verify that OSPF neighbours are Full.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py index 39bbab42e7d0..757d6fb1d581 100644 --- a/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py +++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py @@ -108,7 +108,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error {}".format( ospf_covergence ) @@ -358,7 +358,7 @@ def test_ospf_p2p_tc3_p0(request): # Api call verify whether BGP is converged ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -440,7 +440,7 @@ def test_ospf_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -486,7 +486,7 @@ def test_ospf_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -532,7 +532,7 @@ def test_ospf_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -575,7 +575,7 @@ def test_ospf_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -597,7 +597,7 @@ def test_ospf_show_p1(request): reset_config_on_routers(tgen) ospf_covergence = verify_ospf_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) dut = "r1" @@ -690,7 +690,7 @@ def test_ospf_dead_tc11_p0(request): result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("modify dead interval from default value to r1" "dead interval timer on r2") + step("modify dead interval from default value to r1 dead interval timer on r2") topo1 = { "r0": { @@ -714,11 +714,11 @@ def test_ospf_dead_tc11_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) - step("reconfigure the default dead interval timer value to " "default on r1 and r2") + step("reconfigure the default dead interval timer value to default on r1 and r2") topo1 = { "r0": { "links": { @@ -755,7 +755,7 @@ def test_ospf_dead_tc11_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -797,7 +797,7 @@ def test_ospf_dead_tc11_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error {}".format( ospf_covergence ) @@ -835,9 +835,7 @@ def test_ospf_dead_tc11_p0(request): result = create_interfaces_cfg(tgen, topo1) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Verify that timer value is deleted from intf & " "set to default value 40 sec." - ) + step("Verify that timer value is deleted from intf & set to default value 40 sec.") input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 40}}}}} dut = "r1" result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict) @@ -883,18 +881,14 @@ def test_ospf_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " "instead of Exstart. Error: {}".format(tc_name, result) ) - step( - "Verify that configured MTU value is updated in the show ip " "ospf interface." - ) + step("Verify that configured MTU value is updated in the show ip ospf interface.") dut = "r0" input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 1200}}}}} @@ -951,9 +945,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " @@ -970,9 +962,7 @@ def test_ospf_tc4_mtu_ignore_p0(request): result = verify_ospf_neighbor(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0." - ) + step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.") rtr0.run("ip link set {} mtu 9216".format(r0_r1_intf)) rtr1.run("ip link set {} mtu 9216".format(r1_r0_intf)) diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py index 1c265962304d..79374281cb7a 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py @@ -119,7 +119,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -182,20 +182,20 @@ def test_ospf_gr_helper_tc1_p0(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) - step("Verify that GR helper route is disabled by default to the in" "the DUT.") + step("Verify that GR helper route is disabled by default to the in the DUT.") input_dict = { "helperSupport": "Disabled", "strictLsaCheck": "Enabled", - "restartSupoort": "Planned and Unplanned Restarts", + "restartSupport": "Planned and Unplanned Restarts", "supportedGracePeriod": 1800, } dut = "r0" result = verify_ospf_gr_helper(tgen, topo, dut, input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify that DUT does not enter helper mode upon receiving the " "grace lsa.") + step("Verify that DUT does not enter helper mode upon receiving the grace lsa.") # send grace lsa scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt) @@ -205,7 +205,7 @@ def test_ospf_gr_helper_tc1_p0(request): result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format( + ), "Testcase {} : Failed. DUT entered helper role \n Error: {}".format( tc_name, result ) @@ -220,7 +220,7 @@ def test_ospf_gr_helper_tc1_p0(request): input_dict = { "helperSupport": "Enabled", "strictLsaCheck": "Enabled", - "restartSupoort": "Planned and Unplanned Restarts", + "restartSupport": "Planned and Unplanned Restarts", "supportedGracePeriod": 1800, } dut = "r0" @@ -234,7 +234,7 @@ def test_ospf_gr_helper_tc1_p0(request): assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) step("Perform GR in RR.") - step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.") + step("Verify that DUT does enter helper mode upon receiving the grace lsa.") input_dict = {"activeRestarterCnt": 1} gracelsa_sent = False repeat = 0 @@ -277,7 +277,7 @@ def test_ospf_gr_helper_tc1_p0(request): result = create_router_ospf(tgen, topo, ospf_gr_r0) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.") + step("Verify that DUT does enter helper mode upon receiving the grace lsa.") input_dict = {"activeRestarterCnt": 1} gracelsa_sent = False repeat = 0 @@ -306,7 +306,7 @@ def test_ospf_gr_helper_tc1_p0(request): result = create_router_ospf(tgen, topo, ospf_gr_r0) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Verify that GR helper router is disabled in the DUT for" " router id x.x.x.x") + step("Verify that GR helper router is disabled in the DUT for router id x.x.x.x") input_dict = {"enabledRouterIds": [{"routerId": "1.1.1.1"}]} dut = "r0" result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) @@ -343,7 +343,7 @@ def test_ospf_gr_helper_tc2_p0(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) ospf_gr_r0 = { "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}} } diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py index a3ccb58d38e5..46c0da309f7b 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py @@ -119,7 +119,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -188,10 +188,10 @@ def test_ospf_gr_helper_tc3_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) - step( - "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." - ) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) + + step("Configure DR priority 100 on R0 and clear ospf neighbors " + "on all the routers.") input_dict = { "r0": { @@ -216,9 +216,9 @@ def test_ospf_gr_helper_tc3_p1(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "Backup"}, - "r2": {"state": "Full", "role": "DROther"}, - "r3": {"state": "Full", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "Backup"}, + "r2": {"nbrState": "Full", "role": "DROther"}, + "r3": {"nbrState": "Full", "role": "DROther"}, } } } @@ -282,10 +282,8 @@ def test_ospf_gr_helper_tc4_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) - step( - "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers." - ) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) + step("Configure DR priority 0 on R0 and clear ospf neighbors on all the routers.") input_dict = { "r0": { @@ -310,9 +308,9 @@ def test_ospf_gr_helper_tc4_p1(request): "r0": { "ospf": { "neighbors": { - "r1": {"state": "Full", "role": "DR"}, - "r2": {"state": "2-Way", "role": "DROther"}, - "r3": {"state": "2-Way", "role": "DROther"}, + "r1": {"nbrState": "Full", "role": "DR"}, + "r2": {"nbrState": "2-Way", "role": "DROther"}, + "r3": {"nbrState": "2-Way", "role": "DROther"}, } } } diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py index 64aac2fba8ab..3be28196d884 100644 --- a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py +++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py @@ -119,7 +119,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -193,7 +193,7 @@ def test_ospf_gr_helper_tc7_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) ospf_gr_r0 = { "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}} } @@ -221,7 +221,7 @@ def test_ospf_gr_helper_tc7_p1(request): result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format( + ), "Testcase {} : Failed. DUT entered helper role \n Error: {}".format( tc_name, result ) @@ -253,7 +253,7 @@ def test_ospf_gr_helper_tc8_p1(request): ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True) assert ( ospf_covergence is True - ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence) + ), "OSPF is not after reset config \n Error: {}".format(ospf_covergence) ospf_gr_r0 = { "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}} } diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json index f82758101c0b..0212f9da9cda 100644 --- a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "2.2.2.2":[ { "converged":"Full", - "address":"10.0.1.2", + "ifaceAddress":"10.0.1.2", "ifaceName":"eth-rt2:10.0.1.1" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json index 5a0b09270204..31146604836e 100644 --- a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "1.1.1.1":[ { "converged":"Full", - "address":"10.0.1.1", + "ifaceAddress":"10.0.1.1", "ifaceName":"eth-rt1:10.0.1.2" } ], "3.3.3.3":[ { "converged":"Full", - "address":"10.0.2.3", + "ifaceAddress":"10.0.2.3", "ifaceName":"eth-rt3:10.0.2.2" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json index ab5e78414d1c..49a019d36d28 100644 --- a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json @@ -3,21 +3,21 @@ "2.2.2.2":[ { "converged":"Full", - "address":"10.0.2.2", + "ifaceAddress":"10.0.2.2", "ifaceName":"eth-rt2:10.0.2.3" } ], "4.4.4.4":[ { "converged":"Full", - "address":"10.0.3.4", + "ifaceAddress":"10.0.3.4", "ifaceName":"eth-rt4:10.0.3.3" } ], "6.6.6.6":[ { "converged":"Full", - "address":"10.0.4.6", + "ifaceAddress":"10.0.4.6", "ifaceName":"eth-rt6:10.0.4.3" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json index 405679c10e98..9ab49d7266fd 100644 --- a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "3.3.3.3":[ { "converged":"Full", - "address":"10.0.3.3", + "ifaceAddress":"10.0.3.3", "ifaceName":"eth-rt3:10.0.3.4" } ], "5.5.5.5":[ { "converged":"Full", - "address":"10.0.5.5", + "ifaceAddress":"10.0.5.5", "ifaceName":"eth-rt5:10.0.5.4" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json index 893d45436853..7d3d58977273 100644 --- a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "4.4.4.4":[ { "converged":"Full", - "address":"10.0.5.4", + "ifaceAddress":"10.0.5.4", "ifaceName":"eth-rt4:10.0.5.5" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json index 564a513ac6e9..506eb4086bb4 100644 --- a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json @@ -3,14 +3,14 @@ "3.3.3.3":[ { "converged":"Full", - "address":"10.0.4.3", + "ifaceAddress":"10.0.4.3", "ifaceName":"eth-rt3:10.0.4.6" } ], "7.7.7.7":[ { "converged":"Full", - "address":"10.0.6.7", + "ifaceAddress":"10.0.6.7", "ifaceName":"eth-rt7:10.0.6.6" } ] diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json index bc6b60697c70..642914800403 100644 --- a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json +++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json @@ -3,7 +3,7 @@ "6.6.6.6":[ { "converged":"Full", - "address":"10.0.6.6", + "ifaceAddress":"10.0.6.6", "ifaceName":"eth-rt6:10.0.6.7" } ] diff --git a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py index ca2a3b287c3c..73185d501def 100755 --- a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py +++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py @@ -434,6 +434,144 @@ def test_gr_rt7(): check_routers(restarting="rt7") +# +# Test rt1 performing an unplanned graceful restart +# +def test_unplanned_gr_rt1(): + logger.info("Test: verify rt1 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt1", ["ospfd"]) + + expect_grace_lsa(restarting="1.1.1.1", area="0.0.0.1", helper="rt2") + ensure_gr_is_in_zebra("rt1") + check_routers(restarting="rt1") + + +# +# Test rt2 performing an unplanned graceful restart +# +def test_unplanned_gr_rt2(): + logger.info("Test: verify rt2 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt2", ["ospfd"]) + + expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.1", helper="rt1") + expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.0", helper="rt3") + ensure_gr_is_in_zebra("rt2") + check_routers(restarting="rt2") + + +# +# Test rt3 performing an unplanned graceful restart +# +def test_unplanned_gr_rt3(): + logger.info("Test: verify rt3 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt3", ["ospfd"]) + + expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt2") + expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt4") + expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt6") + ensure_gr_is_in_zebra("rt3") + check_routers(restarting="rt3") + + +# +# Test rt4 performing an unplanned graceful restart +# +def test_unplanned_gr_rt4(): + logger.info("Test: verify rt4 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt4", ["ospfd"]) + + expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.0", helper="rt3") + expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.2", helper="rt5") + ensure_gr_is_in_zebra("rt4") + check_routers(restarting="rt4") + + +# +# Test rt5 performing an unplanned graceful restart +# +def test_unplanned_gr_rt5(): + logger.info("Test: verify rt5 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt5", ["ospfd"]) + + expect_grace_lsa(restarting="5.5.5.5", area="0.0.0.2", helper="rt4") + ensure_gr_is_in_zebra("rt5") + check_routers(restarting="rt5") + + +# +# Test rt6 performing an unplanned graceful restart +# +def test_unplanned_gr_rt6(): + logger.info("Test: verify rt6 performing an unplanned graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt6", ["ospfd"]) + + expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.0", helper="rt3") + expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.3", helper="rt7") + ensure_gr_is_in_zebra("rt6") + check_routers(restarting="rt6") + + +# +# Test rt7 performing an unplanned graceful restart +# +def test_unplanned_gr_rt7(): + logger.info("Test: verify rt7 performing a graceful restart") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False) + start_router_daemons(tgen, "rt7", ["ospfd"]) + + expect_grace_lsa(restarting="7.7.7.7", area="0.0.0.3", helper="rt6") + ensure_gr_is_in_zebra("rt7") + check_routers(restarting="rt7") + + # Memory leak test template def test_memory_leak(): "Run the memory leak test and report results." diff --git a/tests/topotests/ospf_metric_propagation/__init__.py b/tests/topotests/ospf_metric_propagation/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/ospf_metric_propagation/h1/frr.conf b/tests/topotests/ospf_metric_propagation/h1/frr.conf new file mode 100644 index 000000000000..1196a192dd50 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/h1/frr.conf @@ -0,0 +1,10 @@ +! +hostname h1 +password zebra +log file /tmp/h1-frr.log +! +ip route 0.0.0.0/0 10.0.91.1 +! +interface h1-eth0 + ip address 10.0.91.2/24 +! \ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/h2/frr.conf b/tests/topotests/ospf_metric_propagation/h2/frr.conf new file mode 100644 index 000000000000..f951fe6ba1d6 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/h2/frr.conf @@ -0,0 +1,10 @@ +! +hostname h2 +password zebra +log file /tmp/h2-frr.log +! +ip route 0.0.0.0/0 10.0.94.4 +! +interface h2-eth0 + ip address 10.0.94.2/24 +! \ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/r1/frr.conf b/tests/topotests/ospf_metric_propagation/r1/frr.conf new file mode 100644 index 000000000000..85230494dd15 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/frr.conf @@ -0,0 +1,96 @@ +! +hostname r1 +password zebra +log file /tmp/r1-frr.log +ip forwarding +! +interface r1-eth0 + ip address 10.0.1.1/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r1-eth1 vrf blue + ip address 10.0.10.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +interface r1-eth2 vrf green + ip address 10.0.91.1/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +! +router ospf + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.1.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.10.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.91.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map rmap + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map rmap + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +ip prefix-list min seq 5 permit 10.0.80.0/24 +route-map costmax permit 20 + set metric-type type-1 + set metric +1 + set metric-min 713 + match ip address prefix-list min + exit +! +ip prefix-list max seq 10 permit 10.0.70.0/24 +route-map costplus permit 30 + set metric-type type-1 + set metric +1 + set metric-max 13 + match ip address prefix-list max + exit +! +route-map costplus permit 40 + set metric-type type-1 + set metric +1 + exit \ No newline at end of file diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json new file mode 100644 index 000000000000..4f1ced81fb7b --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-1.json @@ -0,0 +1,35 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":34, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.10.5", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "vrf":"blue", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json new file mode 100644 index 000000000000..882d3ca4f09b --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-2.json @@ -0,0 +1,35 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":136, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json new file mode 100644 index 000000000000..cd528459ab23 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-3.json @@ -0,0 +1,35 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":1138, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json new file mode 100644 index 000000000000..133f37549e16 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-4.json @@ -0,0 +1,35 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":1218, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json new file mode 100644 index 000000000000..5d8050902144 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-5.json @@ -0,0 +1,35 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":238, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.1.2", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "vrf":"default", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json new file mode 100644 index 000000000000..1b59707b98f2 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r1/show_ip_route-6.json @@ -0,0 +1,35 @@ +{ + "10.0.94.0/24":[ + { + "prefix":"10.0.94.0/24", + "prefixLen":24, + "protocol":"bgp", + "vrfName":"green", + "selected":true, + "destSelected":true, + "distance":20, + "metric":136, + "installed":true, + "table":12, + "internalStatus":16, + "internalFlags":8, + "internalNextHopNum":1, + "internalNextHopActiveNum":1, + "nexthopGroupId":"*", + "installedNexthopGroupId":"*", + "uptime":"*", + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"10.0.10.5", + "afi":"ipv4", + "interfaceName":"r1-eth1", + "vrf":"blue", + "active":true, + "weight":1 + } + ] + } + ] +} diff --git a/tests/topotests/ospf_metric_propagation/r2/frr.conf b/tests/topotests/ospf_metric_propagation/r2/frr.conf new file mode 100644 index 000000000000..e67a374ff552 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r2/frr.conf @@ -0,0 +1,81 @@ +! +hostname r2 +password zebra +log file /tmp/r2-frr.log +ip forwarding +! +interface r2-eth0 + ip address 10.0.1.2/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r2-eth1 vrf blue + ip address 10.0.20.2/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r2-eth2 vrf green + ip address 10.0.70.2/24 + ip ospf cost 1000 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.1.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.20.0/24 area 0 +! + +router ospf vrf green + ospf router-id 10.0.255.2 + distance 20 + redistribute bgp route-map costplus + network 10.0.70.0/24 area 0 +! + +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/r3/frr.conf b/tests/topotests/ospf_metric_propagation/r3/frr.conf new file mode 100644 index 000000000000..175851d42763 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r3/frr.conf @@ -0,0 +1,79 @@ +! +hostname r3 +password zebra +log file /tmp/r3-frr.log +ip forwarding +! +interface r3-eth0 + ip address 10.0.3.3/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r3-eth1 vrf blue + ip address 10.0.30.3/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r3-eth2 vrf green + ip address 10.0.80.3/24 + ip ospf cost 1000 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.3.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.30.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.3 + distance 20 + redistribute bgp route-map costplus + network 10.0.80.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/r4/frr.conf b/tests/topotests/ospf_metric_propagation/r4/frr.conf new file mode 100644 index 000000000000..70a47e34fa67 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/r4/frr.conf @@ -0,0 +1,78 @@ +! +hostname r4 +password zebra +log file /tmp/r4-frr.log +ip forwarding +! +interface r4-eth0 + ip address 10.0.3.4/24 + ip ospf cost 100 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r4-eth1 vrf blue + ip address 10.0.40.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface r4-eth2 vrf green + ip address 10.0.94.4/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.4 + distance 20 + redistribute bgp route-map costplus + network 10.0.3.0/24 area 0 +! +router ospf vrf blue + ospf router-id 10.0.255.4 + distance 20 + redistribute bgp route-map costplus + network 10.0.40.0/24 area 0 +! +router ospf vrf green + ospf router-id 10.0.255.1 + distance 20 + redistribute bgp route-map costplus + network 10.0.94.0/24 area 0 +! +router bgp 99 + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf route-map costplus + import vrf route-map rmap + import vrf blue + import vrf green + ! +! +router bgp 99 vrf blue + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf green + ! +router bgp 99 vrf green + no bgp ebgp-requires-policy + address-family ipv4 unicast + redistribute connected + redistribute ospf + import vrf route-map rmap + import vrf default + import vrf blue + ! +! +route-map rmap permit 10 + set metric-type type-1 + set metric +1 + exit +! +route-map costplus permit 1 + set metric-type type-1 + set metric +1 + exit diff --git a/tests/topotests/ospf_metric_propagation/ra/frr.conf b/tests/topotests/ospf_metric_propagation/ra/frr.conf new file mode 100644 index 000000000000..7be9e5c33e90 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/ra/frr.conf @@ -0,0 +1,27 @@ +! +hostname ra +password zebra +log file /tmp/ra-frr.log +ip forwarding +! +interface ra-eth0 + ip address 10.0.50.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface ra-eth1 + ip address 10.0.10.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface ra-eth2 + ip address 10.0.20.5/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.5 + network 10.0.10.0/24 area 0 + network 10.0.20.0/24 area 0 + network 10.0.50.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/rb/frr.conf b/tests/topotests/ospf_metric_propagation/rb/frr.conf new file mode 100644 index 000000000000..a7dbf82278ce --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/rb/frr.conf @@ -0,0 +1,27 @@ +! +hostname rb +password zebra +log file /tmp/rb-frr.log +ip forwarding +! +interface rb-eth0 + ip address 10.0.50.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface rb-eth1 + ip address 10.0.30.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface rb-eth2 + ip address 10.0.40.6/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.6 + network 10.0.30.0/24 area 0 + network 10.0.40.0/24 area 0 + network 10.0.50.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/rc/frr.conf b/tests/topotests/ospf_metric_propagation/rc/frr.conf new file mode 100644 index 000000000000..f5a2ed7c4f17 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/rc/frr.conf @@ -0,0 +1,21 @@ +! +hostname rc +password zebra +log file /tmp/rc-frr.log +ip forwarding +! +interface rc-eth0 + ip address 10.0.70.7/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +interface rc-eth1 + ip address 10.0.80.7/24 + ip ospf hello-interval 1 + ip ospf dead-interval 30 +! +router ospf + ospf router-id 10.0.255.7 + network 10.0.70.0/24 area 0 + network 10.0.80.0/24 area 0 +! diff --git a/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py new file mode 100644 index 000000000000..085eb1f9c123 --- /dev/null +++ b/tests/topotests/ospf_metric_propagation/test_ospf_metric_propagation.py @@ -0,0 +1,385 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_metric_propagation.py +# +# Copyright (c) 2023 ATCorp +# Jafar Al-Gharaibeh +# + +import os +import sys +import json +from time import sleep +from functools import partial +import pytest + +# 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 + + +""" +test_ospf_metric_propagation.py: Test OSPF/BGP metric propagation +""" + +TOPOLOGY = """ + +-----+ +-----+ + eth1 | | eth0 | | eth2 + +-------------+ rA +---------------------------+ rB +---------------+ + | .5 | | .5 .6 | | .6 | + | +--+--+ 10.0.50.0/24 +--+--+ .6 | + | |.5 |.6 | + | eth2| eth1| | + 10.0.10.0/24 | | | + | 10.0.20.0/24 10.0.30.0/24 10.0.40.0/24 + |blue |blue |blue |blue + | | | | + eth1|.1 eth1|.2 eth1|.3 eth1|.4 + +-----+ +--+--+ +--+--+ +-----+ +-+---+ +-+---+ +------+ + | |eth0 eth2| | eth0 | |eth2 eth1| |eth2 eth3| | eth0 | |eth2 eth0| | + | h1 +----------+ R1 +----------+ R2 +-----------+ rC +----------+ R3 +------------+ R4 +---------+ h2 | + | | | | | | | | | | | | | | + +-----+.2 .1 +-----+.1 .2+-----+.2 .7 +-----+.7 .3+-----+.3 .4+-----+.4 .2+------+ + green green green green + + 10.0.91.0/24 10.0.1.0/24 10.0.70.0/24 10.0.80.0/24 10.0.3.0/24 10.0.94.0/24 +""" + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# Required to instantiate the topology builder class. + +pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + tgen.add_router("ra") + tgen.add_router("rb") + tgen.add_router("rc") + tgen.add_router("h1") + tgen.add_router("h2") + + # Interconect router 1, 2 + switch = tgen.add_switch("s1-2") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + # Interconect router 3, 4 + switch = tgen.add_switch("s3-4") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + # Interconect router a, b + switch = tgen.add_switch("sa-b") + switch.add_link(tgen.gears["ra"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 1, a + switch = tgen.add_switch("s1-a") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["ra"]) + + # Interconect router 2, a + switch = tgen.add_switch("s2-a") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["ra"]) + + # Interconect router 3, b + switch = tgen.add_switch("s3-b") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 4, b + switch = tgen.add_switch("s4-b") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["rb"]) + + # Interconect router 1, h1 + switch = tgen.add_switch("s1-h1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["h1"]) + + # Interconect router 4, h2 + switch = tgen.add_switch("s4-h2") + switch.add_link(tgen.gears["r4"]) + switch.add_link(tgen.gears["h2"]) + + # Interconect router 2, c + switch = tgen.add_switch("s2-c") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["rc"]) + + # Interconect router 3, c + switch = tgen.add_switch("s3-c") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["rc"]) + + +def setup_module(mod): + logger.info("OSPF Metric Propagation:\n {}".format(TOPOLOGY)) + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + vrf_setup_cmds = [ + "ip link add name blue type vrf table 11", + "ip link set dev blue up", + "ip link add name green type vrf table 12", + "ip link set dev green up", + ] + + # Starting Routers + router_list = tgen.routers() + + # Create VRFs and bind to interfaces + for routern in range(1, 5): + for cmd in vrf_setup_cmds: + tgen.net["r{}".format(routern)].cmd(cmd) + for routern in range(1, 5): + tgen.net["r{}".format(routern)].cmd( + "ip link set dev r{}-eth1 vrf blue up".format(routern) + ) + tgen.net["r{}".format(routern)].cmd( + "ip link set dev r{}-eth2 vrf green up".format(routern) + ) + + for rname, router in router_list.items(): + logger.info("Loading router %s" % rname) + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + # Initialize all routers. + tgen.start_router() + for router in router_list.values(): + if router.has_version("<", "4.20"): + tgen.set_error("unsupported version") + + +def teardown_module(mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_all_links_up(): + "Test path R1 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + r1 = tgen.gears["r1"] + json_file = "{}/r1/show_ip_route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_down(): + "Test path R1 -> R2 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r1"].cmd("ip link set dev r1-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-2.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_down(): + "Test path R1 -> R2 -> Rc -> R3 -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r2"].cmd("ip link set dev r2-eth1 down") + tgen.net["r1"].cmd("ip link set dev r1-eth0 down") + tgen.net["r2"].cmd("ip link set dev r2-eth2 down") + tgen.net["r2"].cmd("ip link set dev r2-eth2 up") + tgen.net["r1"].cmd("ip link set dev r1-eth0 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-3.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r3"].cmd("ip link set dev r3-eth1 down") + tgen.net["r1"].cmd("ip link set dev r1-eth0 down") + tgen.net["r3"].cmd("ip link set dev r3-eth0 down") + tgen.net["r3"].cmd("ip link set dev r3-eth0 up") + tgen.net["r1"].cmd("ip link set dev r1-eth0 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_4_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + tgen.net["r4"].cmd("ip link set dev r4-eth1 down") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_4_down(): + "Test path R1 -> R2 -> Rc -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring link 3 back up + tgen.net["r3"].cmd("ip link set dev r3-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-4.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_4_down(): + "Test path R1 -> R2 -> Ra -> Rb -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 2 up + tgen.net["r2"].cmd("ip link set dev r2-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-5.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_4_down(): + "Test path R1 -> Ra -> Rb -> R3 -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 1 up + tgen.net["r1"].cmd("ip link set dev r1-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-6.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + assert result is None, assertmsg + + +def test_link_1_2_3_4_up(): + "Test path R1 -> Ra -> Rb -> R4" + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip("skipped because of router(s) failure") + + # bring back link 4 up + tgen.net["r4"].cmd("ip link set dev r4-eth1 up") + r1 = tgen.gears["r1"] + + json_file = "{}/r1/show_ip_route-1.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf green 10.0.94.2 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + + assertmsg = "r1 JSON output mismatches" + 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/ospf_nssa_topo1/__init__.py b/tests/topotests/ospf_nssa_topo1/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf new file mode 100644 index 000000000000..6d23c8480644 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/ospfd.conf @@ -0,0 +1,22 @@ +password 1 +hostname rt1 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 0 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 0 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 1.1.1.1 +! diff --git a/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf new file mode 100644 index 000000000000..7ba3dc759183 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/staticd.conf @@ -0,0 +1,6 @@ +log file staticd.log +! +hostname rt1 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref new file mode 100644 index 000000000000..ac34417bda20 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step1/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref new file mode 100644 index 000000000000..ac34417bda20 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step10/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref new file mode 100644 index 000000000000..ac34417bda20 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step2/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref new file mode 100644 index 000000000000..ac34417bda20 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step3/show_ip_ospf_route.ref @@ -0,0 +1,115 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref new file mode 100644 index 000000000000..6a05555eecea --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step4/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref new file mode 100644 index 000000000000..6a05555eecea --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step5/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref new file mode 100644 index 000000000000..2d3c8c485fc3 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step6/show_ip_ospf_route.ref @@ -0,0 +1,91 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref new file mode 100644 index 000000000000..6a05555eecea --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step7/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref new file mode 100644 index 000000000000..f41ee3b69855 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step8/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref new file mode 100644 index 000000000000..2d3c8c485fc3 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/step9/show_ip_ospf_route.ref @@ -0,0 +1,91 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.0", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.1.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf new file mode 100644 index 000000000000..1df100564e60 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt1/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt1 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 1.1.1.1/32 +! +interface eth-rt2 + ip address 10.0.1.1/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf new file mode 100644 index 000000000000..12884d2ea95c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/ospfd.conf @@ -0,0 +1,35 @@ +password 1 +hostname rt2 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 0 +! +interface eth-rt1 + ip ospf network point-to-point + ip ospf area 0 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +interface eth-rt3 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +interface eth-rt4 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 2.2.2.2 + area 1 nssa +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf new file mode 100644 index 000000000000..b6d4233a4fe5 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/staticd.conf @@ -0,0 +1,6 @@ +log file staticd.log +! +hostname rt2 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c09411741a53 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step1/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c09411741a53 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step10/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref new file mode 100644 index 000000000000..a67dfb4685a1 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step2/show_ip_ospf_route.ref @@ -0,0 +1,139 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "0.0.0.0\/0":{ + "routeType":"N E2", + "cost":10, + "type2cost":1, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c09411741a53 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step3/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c7dd93c6e97b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step4/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref new file mode 100644 index 000000000000..9c3cfff6c0f7 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step5/show_ip_ospf_route.ref @@ -0,0 +1,117 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref new file mode 100644 index 000000000000..f6bbdfa594cd --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step6/show_ip_ospf_route.ref @@ -0,0 +1,103 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c7dd93c6e97b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step7/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c7dd93c6e97b --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step8/show_ip_ospf_route.ref @@ -0,0 +1,129 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "172.16.1.0\/24":{ + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref new file mode 100644 index 000000000000..c09411741a53 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/step9/show_ip_ospf_route.ref @@ -0,0 +1,127 @@ +{ + "1.1.1.1\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":"10.0.1.1", + "via":"eth-rt1" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.0", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt1" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt3" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt4" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.3", + "via":"eth-rt3" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.4", + "via":"eth-rt4" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf new file mode 100644 index 000000000000..fa274ebade06 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt2/zebra.conf @@ -0,0 +1,24 @@ +log file zebra.log +! +hostname rt2 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 2.2.2.2/32 +! +interface eth-rt1 + ip address 10.0.1.2/24 +! +interface eth-rt3 + ip address 10.0.2.2/24 +! +interface eth-rt4 + ip address 10.0.3.2/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf new file mode 100644 index 000000000000..9691a7c512ae --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/ospfd.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt3 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 1 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 3.3.3.3 + area 1 nssa + redistribute connected +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf new file mode 100644 index 000000000000..f0edd6c9cae0 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/staticd.conf @@ -0,0 +1,8 @@ +log file staticd.log +! +hostname rt3 +! +ip route 0.0.0.0/0 Null0 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref new file mode 100644 index 000000000000..a2d078ab133e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step1/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref new file mode 100644 index 000000000000..4619067abde5 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step10/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref new file mode 100644 index 000000000000..a2d078ab133e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step2/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref new file mode 100644 index 000000000000..a2d078ab133e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step3/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref new file mode 100644 index 000000000000..10387215b211 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step4/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref new file mode 100644 index 000000000000..4f8eaf1eaadc --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step5/show_ip_ospf_route.ref @@ -0,0 +1,138 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref new file mode 100644 index 000000000000..41e9f6718a1d --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step6/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref new file mode 100644 index 000000000000..10387215b211 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step7/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref new file mode 100644 index 000000000000..4619067abde5 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step8/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref new file mode 100644 index 000000000000..4619067abde5 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/step9/show_ip_ospf_route.ref @@ -0,0 +1,150 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.1\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.2\/32":{ + "routeType":"N E2", + "cost":20, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.2.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf new file mode 100644 index 000000000000..d943540f3b33 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt3/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt3 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 3.3.3.3/32 +! +interface eth-rt2 + ip address 10.0.2.3/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf new file mode 100644 index 000000000000..cba7cf71ed8e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/ospfd.conf @@ -0,0 +1,24 @@ +password 1 +hostname rt4 +log file ospfd.log +! +! debug ospf sr +! debug ospf te +! debug ospf event +! debug ospf lsa +! debug ospf zebra +! +interface lo + ip ospf area 1 +! +interface eth-rt2 + ip ospf network point-to-point + ip ospf area 1 + ip ospf hello-interval 3 + ip ospf dead-interval 12 +! +router ospf + router-id 4.4.4.4 + area 1 nssa + redistribute static +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf new file mode 100644 index 000000000000..e00ee5dfea2e --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/staticd.conf @@ -0,0 +1,9 @@ +log file staticd.log +! +hostname rt4 +! +ip route 172.16.1.1/32 Null0 +ip route 172.16.1.2/32 Null0 +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref new file mode 100644 index 000000000000..e57f542f1c57 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step1/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref new file mode 100644 index 000000000000..82a0e1a9b45f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step10/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref new file mode 100644 index 000000000000..e57f542f1c57 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step2/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref new file mode 100644 index 000000000000..e57f542f1c57 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step3/show_ip_ospf_route.ref @@ -0,0 +1,114 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref new file mode 100644 index 000000000000..5f51b3b3b82a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step4/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref new file mode 100644 index 000000000000..5f51b3b3b82a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step5/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref new file mode 100644 index 000000000000..5f51b3b3b82a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step6/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref new file mode 100644 index 000000000000..5f51b3b3b82a --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step7/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":20, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref new file mode 100644 index 000000000000..82a0e1a9b45f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step8/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref new file mode 100644 index 000000000000..82a0e1a9b45f --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/step9/show_ip_ospf_route.ref @@ -0,0 +1,126 @@ +{ + "0.0.0.0\/0":{ + "routeType":"N IA", + "cost":11, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "1.1.1.1\/32":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "2.2.2.2\/32":{ + "routeType":"N IA", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3\/32":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "4.4.4.4\/32":{ + "routeType":"N", + "cost":0, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"lo" + } + ] + }, + "10.0.1.0\/24":{ + "routeType":"N IA", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.2.0\/24":{ + "routeType":"N", + "cost":20, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "10.0.3.0\/24":{ + "routeType":"N", + "cost":10, + "area":"0.0.0.1", + "nexthops":[ + { + "ip":" ", + "directlyAttachedTo":"eth-rt2" + } + ] + }, + "2.2.2.2":{ + "routeType":"R ", + "cost":10, + "area":"0.0.0.1", + "routerType":"abr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "3.3.3.3":{ + "routeType":"R ", + "cost":20, + "area":"0.0.0.1", + "routerType":"asbr", + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + }, + "172.16.1.0\/24":{ + "routeType":"N E2", + "cost":10, + "type2cost":1000, + "tag":0, + "nexthops":[ + { + "ip":"10.0.3.2", + "via":"eth-rt2" + } + ] + } +} diff --git a/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf new file mode 100644 index 000000000000..588febe70b6c --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/rt4/zebra.conf @@ -0,0 +1,18 @@ +log file zebra.log +! +hostname rt4 +! +! debug zebra kernel +! debug zebra packet +! debug zebra mpls +! +interface lo + ip address 4.4.4.4/32 +! +interface eth-rt2 + ip address 10.0.3.4/24 +! +ip forwarding +! +line vty +! diff --git a/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py new file mode 100644 index 000000000000..432ddf098623 --- /dev/null +++ b/tests/topotests/ospf_nssa_topo1/test_ospf_nssa_topo1.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_ospf_nssa_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_ospf_nssa_topo1.py: + + +---------+ + | RT1 | + | 1.1.1.1 | + +---------+ + |eth-rt2 + | + |10.0.1.0/24 + | + |eth-rt1 + +---------+ + | RT2 | + | 2.2.2.2 | + +---------+ + eth-rt3| |eth-rt4 + | | + 10.0.2.0/24 | | 10.0.3.0/24 + +---------+ +--------+ + | | + |eth-rt2 |eth-rt2 + +---------+ +---------+ + | RT3 | | RT4 | + | 3.3.3.3 | | 4.4.4.4 | + +---------+ +---------+ + +""" + +import os +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. + +pytestmark = [pytest.mark.ospfd] + + +def build_topo(tgen): + "Build function" + + # + # Define FRR Routers + # + for router in ["rt1", "rt2", "rt3", "rt4"]: + 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["rt2"], nodeif="eth-rt3") + switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2") + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4") + switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2") + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + # For all registered routers, load the zebra configuration file + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.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): + "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_rib_step1(): + logger.info("Test (step 1): test initial network convergence") + 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"]: + router_compare_json_output( + rname, "show ip ospf route json", "step1/show_ip_ospf_route.ref" + ) + + +# +# Step 2 +# +# Action(s): +# -rt3: configure an NSSA default route +# +# Expected changes: +# -rt2: add NSSA default route pointing to rt3 +# +def test_rib_step2(): + logger.info("Test (step 2): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Adding NSSA default on rt4") + tgen.net["rt3"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa default-information-originate"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step2/show_ip_ospf_route.ref" + ) + + +# +# Step 3 +# +# Action(s): +# -rt3: remove NSSA default route +# +# Expected changes: +# -rt2: remove NSSA default route +# +def test_rib_step3(): + logger.info("Test (step 3): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing NSSA default on rt4") + tgen.net["rt3"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step3/show_ip_ospf_route.ref" + ) + + +# +# Step 4 +# +# Action(s): +# -rt2: configure an NSSA range for 172.16.1.0/24 +# +# Expected changes: +# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be removed +# -rt1: the 172.16.1.0/24 route should be added +# +def test_rib_step4(): + logger.info("Test (step 4): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Configuring NSSA range on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step4/show_ip_ospf_route.ref" + ) + + +# +# Step 5 +# +# Action(s): +# -rt4: remove the 172.16.1.1/32 static route +# +# Expected changes: +# -None (the 172.16.1.0/24 range is still active because of 172.16.1.2/32) +# +def test_rib_step5(): + logger.info("Test (step 5): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing first static route in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.1/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step5/show_ip_ospf_route.ref" + ) + + +# +# Step 6 +# +# Action(s): +# -rt4: remove the 172.16.1.2/32 static route +# +# Expected changes: +# -rt1: remove the 172.16.1.0/24 route since the NSSA range is no longer active +# +def test_rib_step6(): + logger.info("Test (step 6): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing second static route in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "no ip route 172.16.1.2/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step6/show_ip_ospf_route.ref" + ) + + +# +# Step 7 +# +# Action(s): +# -rt4: readd the 172.16.1.1/32 and 172.16.1.2/32 static routes +# +# Expected changes: +# -rt1: readd the 172.16.1.0/24 route since the NSSA range is active again +# +def test_rib_step7(): + logger.info("Test (step 7): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Readding static routes in rt4") + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.1/32 Null0"') + tgen.net["rt4"].cmd('vtysh -c "conf t" -c "ip route 172.16.1.2/32 Null0"') + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step7/show_ip_ospf_route.ref" + ) + + +# +# Step 8 +# +# Action(s): +# -rt2: update the NSSA range with a static cost +# +# Expected changes: +# -rt1: update the metric of the 172.16.1.0/24 route from 20 to 1000 +# +def test_rib_step8(): + logger.info("Test (step 8): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Updating the NSSA range cost on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 cost 1000"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step8/show_ip_ospf_route.ref" + ) + + +# +# Step 9 +# +# Action(s): +# -rt2: update the NSSA range to not advertise itself +# +# Expected changes: +# -rt1: the 172.16.1.0/24 route should be removed +# +def test_rib_step9(): + logger.info("Test (step 9): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Updating the NSSA range to not advertise itself") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "area 1 nssa range 172.16.1.0/24 not-advertise"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step9/show_ip_ospf_route.ref" + ) + + +# +# Step 10 +# +# Action(s): +# -rt2: remove the NSSA range +# +# Expected changes: +# -rt1: the 172.16.1.1/32 and 172.16.1.2/32 routes should be added +# +def test_rib_step10(): + logger.info("Test (step 10): verify OSPF routes") + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Removing NSSA range on rt2") + tgen.net["rt2"].cmd( + 'vtysh -c "conf t" -c "router ospf" -c "no area 1 nssa range 172.16.1.0/24"' + ) + + for rname in ["rt1", "rt2", "rt3", "rt4"]: + router_compare_json_output( + rname, "show ip ospf route json", "step10/show_ip_ospf_route.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/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py index 5b1a53b895b3..d5583ac06abb 100644 --- a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py +++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py @@ -111,9 +111,7 @@ def ospf_unconfigure_suppress_fa(router_name, area): tgen = get_topogen() router = tgen.gears[router_name] - router.vtysh_cmd( - "conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area) - ) + router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa\nexit\n".format(area)) def ospf_get_lsa_type5(router_name): diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step1.json b/tests/topotests/ospf_te_topo1/reference/ted_step1.json index 18aee4ffabac..8b2413a8940d 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step1.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step1.json @@ -57,7 +57,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -101,7 +101,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -142,7 +142,7 @@ } }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -183,7 +183,7 @@ } }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -224,7 +224,7 @@ } }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -266,7 +266,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -307,7 +307,7 @@ } }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -360,7 +360,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -404,7 +404,7 @@ } }, { - "edge-id":167773441, + "edge-id":"10.0.5.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step2.json b/tests/topotests/ospf_te_topo1/reference/ted_step2.json index 1ed7272560f8..625b57d15c07 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step2.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step2.json @@ -57,7 +57,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -101,7 +101,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -142,7 +142,7 @@ } }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -184,7 +184,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -225,7 +225,7 @@ } }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -278,7 +278,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -322,7 +322,7 @@ } }, { - "edge-id":167773441, + "edge-id":"10.0.5.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step3.json b/tests/topotests/ospf_te_topo1/reference/ted_step3.json index 0e79670c78f6..4cfec0f608f6 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step3.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step3.json @@ -49,7 +49,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -93,7 +93,7 @@ } }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -134,7 +134,7 @@ } }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -176,7 +176,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -217,7 +217,7 @@ } }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -270,7 +270,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step4.json b/tests/topotests/ospf_te_topo1/reference/ted_step4.json index 860dcb3f216a..e8e24d9805bb 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step4.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step4.json @@ -72,7 +72,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -128,7 +128,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -181,7 +181,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -223,7 +223,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -276,7 +276,7 @@ ] }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -329,7 +329,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step5.json b/tests/topotests/ospf_te_topo1/reference/ted_step5.json index 615a691c4516..4713cc0115fe 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step5.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step5.json @@ -72,7 +72,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -128,7 +128,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -181,7 +181,7 @@ ] }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -234,7 +234,7 @@ ] }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -287,7 +287,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -329,7 +329,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -382,7 +382,7 @@ ] }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -435,7 +435,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step6.json b/tests/topotests/ospf_te_topo1/reference/ted_step6.json index 3b84d258081f..aaac07b05169 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step6.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step6.json @@ -72,7 +72,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -128,7 +128,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -181,7 +181,7 @@ ] }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -234,7 +234,7 @@ ] }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -287,7 +287,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -329,7 +329,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -382,7 +382,7 @@ ] }, { - "edge-id":167773185, + "edge-id":"10.0.4.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.4", @@ -437,7 +437,7 @@ ] }, { - "edge-id":167773186, + "edge-id":"10.0.4.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step7.json b/tests/topotests/ospf_te_topo1/reference/ted_step7.json index 83f8a1d5d60f..56ed1f176b16 100644 --- a/tests/topotests/ospf_te_topo1/reference/ted_step7.json +++ b/tests/topotests/ospf_te_topo1/reference/ted_step7.json @@ -53,7 +53,7 @@ ], "edges":[ { - "edge-id":167772161, + "edge-id":"10.0.0.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -109,7 +109,7 @@ ] }, { - "edge-id":167772162, + "edge-id":"10.0.0.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -162,7 +162,7 @@ ] }, { - "edge-id":167772417, + "edge-id":"10.0.1.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.1", @@ -215,7 +215,7 @@ ] }, { - "edge-id":167772418, + "edge-id":"10.0.1.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", @@ -268,7 +268,7 @@ ] }, { - "edge-id":167772929, + "edge-id":"10.0.3.1", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.3", @@ -310,7 +310,7 @@ } }, { - "edge-id":167772930, + "edge-id":"10.0.3.2", "status":"Sync", "origin":"OSPFv2", "advertised-router":"10.0.255.2", diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py index b01c96226ef2..7a7ea85e2fd5 100644 --- a/tests/topotests/ospfapi/test_ospf_clientapi.py +++ b/tests/topotests/ospfapi/test_ospf_clientapi.py @@ -17,14 +17,27 @@ import sys import time from datetime import datetime, timedelta +from functools import partial import pytest - -from lib.common_config import retry, run_frr_cmd, step +from lib.common_config import ( + kill_router_daemons, + retry, + run_frr_cmd, + shutdown_bringup_interface, + start_router_daemons, + step, +) from lib.micronet import Timeout, comm_error from lib.topogen import Topogen, TopoRouter from lib.topotest import interface_set_status, json_cmp +# 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 + pytestmark = [pytest.mark.ospfd] CWD = os.path.dirname(os.path.realpath(__file__)) @@ -936,6 +949,638 @@ def test_ospf_opaque_delete_data3(tgen): _test_opaque_add_del(tgen, apibin) +def _test_opaque_add_restart_add(tgen, apibin): + "Test adding an opaque LSA and then restarting ospfd" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + p = None + pread = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+") + try: + step("reachable: check for add notification") + pread = r2.popen( + ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], + encoding=None, # don't buffer + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + p = r1.popen( + [ + apibin, + "-v", + "add,10,1.2.3.4,231,1", # seq = 80000001 + "wait, 5", + "add,10,1.2.3.4,231,1,feedaceebeef", # seq = 80000002 + "wait, 5", + ] + ) + add_input_dict = { + "areas": { + "1.2.3.4": { + "areaLocalOpaqueLsa": [ + { + "lsId": "231.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000002", + "checksum": "cd26", + }, + ], + "areaLocalOpaqueLsaCount": 1, + }, + }, + } + step("Wait for the Opaque LSA to be distributed") + json_cmd = "show ip ospf da json" + assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None + assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None + + step("Shutdown the interface on r1 to isolate it for r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", False) + + time.sleep(2) + step("Reset the client") + p.send_signal(signal.SIGINT) + time.sleep(2) + p.wait() + p = None + + # Verify the OLD LSA is still there unchanged on R2 + assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None + + step("Kill ospfd on R1") + kill_router_daemons(tgen, "r1", ["ospfd"]) + time.sleep(2) + + step("Bring ospfd on R1 back up") + start_router_daemons(tgen, "r1", ["ospfd"]) + + # This will start off with sequence num 80000001 + # But should advance to 80000003 when we reestablish with r2 + p = r1.popen( + [ + apibin, + "-v", + "add,10,1.2.3.4,231,1,feedaceecafebeef", # seq=80000001 + "wait, 5", + ] + ) + + # verify the old value on r2 doesn't change yet + time.sleep(2) + assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None + + json_cmd = "show ip ospf da opaque-area json" + new_add_input_dict = { + "areaLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "linkStateId": "231.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000001", + "checksum": "b07a", + "length": 28, + "opaqueDataLength": 8, + }, + ], + }, + }, + } + # verify new value with initial seq number on r1 + assert verify_ospf_database(tgen, r1, new_add_input_dict, json_cmd) is None + + step("Bring the interface on r1 back up for connection to r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", True) + + step("Verify area opaque LSA refresh") + + # Update the expected value to sequence number rev and new checksum + update_dict = new_add_input_dict["areaLocalOpaqueLsa"]["areas"]["1.2.3.4"][0] + update_dict["lsaSeqNumber"] = "80000003" + update_dict["checksum"] = "cb27" + + # should settle on the same value now. + assert verify_ospf_database(tgen, r1, new_add_input_dict, json_cmd) is None + assert verify_ospf_database(tgen, r2, new_add_input_dict, json_cmd) is None + + step("Shutdown the interface on r1 to isolate it for r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", False) + + time.sleep(2) + step("Reset the client") + p.send_signal(signal.SIGINT) + time.sleep(2) + p.wait() + p = None + + step("Kill ospfd on R1") + kill_router_daemons(tgen, "r1", ["ospfd"]) + time.sleep(2) + + step("Bring ospfd on R1 back up") + start_router_daemons(tgen, "r1", ["ospfd"]) + + step("Bring the interface on r1 back up for connection to r2") + shutdown_bringup_interface(tgen, "r1", "r1-eth0", True) + + step("Verify area opaque LSA Purging") + json_cmd = "show ip ospf da opaque-area json" + add_detail_input_dict = { + "areaLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [ + { + "lsaAge": 3600, + "linkStateId": "231.0.0.1", + "advertisingRouter": "1.0.0.0", + "lsaSeqNumber": "80000003", + "checksum": "cb27", + "length": 28, + "opaqueDataLength": 8, + }, + ], + }, + }, + } + assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None + assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None + step("Verify Area Opaque LSA removal after timeout (60 seconds)") + time.sleep(60) + json_cmd = "show ip ospf da opaque-area json" + timeout_detail_input_dict = { + "areaLocalOpaqueLsa": { + "areas": { + "1.2.3.4": [], + }, + }, + } + assert ( + verify_ospf_database(tgen, r1, timeout_detail_input_dict, json_cmd) is None + ) + assert ( + verify_ospf_database(tgen, r2, timeout_detail_input_dict, json_cmd) is None + ) + + except Exception: + if p: + p.terminate() + if p.wait(): + comm_error(p) + p = None + raise + finally: + if pread: + pread.terminate() + pread.wait() + if p: + p.terminate() + p.wait() + + +@pytest.mark.parametrize("tgen", [2], indirect=True) +def test_ospf_opaque_restart(tgen): + apibin = os.path.join(CLIENTDIR, "ospfclient.py") + rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + _test_opaque_add_restart_add(tgen, apibin) + + +def _test_opaque_interface_disable(tgen, apibin): + "Test disabling opaque capability on an interface" + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + tc_name = "opaque_interface_disable" + + p = None + pread = None + # Log to our stdin, stderr + pout = open(os.path.join(r1.net.logdir, "r1/intf-disable.log"), "a+") + try: + # STEP 1 in test_ospf_opaque_interface_disable and STEP 56 in CI tests + step("Disable OSPF opaque LSA Copability on r1's interface to r2") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nno ip ospf capability opaque") + time.sleep(15) + + # STEP 2 in test_ospf_opaque_interface_disable and STEP 57 in CI tests + step("Verify the r1 configuration of 'no ip ospf capability opaque'") + no_capability_opaque_cfg = ( + tgen.net["r1"] + .cmd( + 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque"' + ) + .rstrip() + ) + assertmsg = ( + "'no ip ospf capability opaque' applied, but not present in configuration" + ) + assert no_capability_opaque_cfg == " no ip ospf capability opaque", assertmsg + + # STEP 3 in test_ospf_opaque_interface_disable and STEP 58 in CI tests + step("Verify the ospf opaque option is not applied to the r1 interface") + r1_interface_without_opaque = { + "interfaces": { + "r1-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.1", + "ospfIfType": "Broadcast", + "opaqueCapable": False, + } + } + } + r1_interface_with_opaque = { + "interfaces": { + "r1-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.1", + "ospfIfType": "Broadcast", + "opaqueCapable": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_without_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" + assert result is None, assertmsg + + r1_neighbor_without_opaque = { + "neighbors": { + "2.0.0.0": [ + { + "optionsList": "*|-|-|-|-|-|E|-", + } + ] + } + } + r2_neighbor_without_opaque = { + "neighbors": { + "1.0.0.0": [ + { + "optionsList": "*|-|-|-|-|-|E|-", + } + ] + } + } + # STEP 4 in test_ospf_opaque_interface_disable and STEP 59 in CI tests + step("Verify that the r1 neighbor options don't include opaque") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF neighbor has opaque option in optionsList" + assert result is None, assertmsg + + # STEP 5 in test_ospf_opaque_interface_disable and STEP 60 in CI tests + step("Verify that the r1 neighbor options don't include opaque") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF neighbor has opaque option in optionsList" + assert result is None, assertmsg + + # STEP 6 in test_ospf_opaque_interface_disable and STEP 61 in CI tests + step( + "Verify no r2 configuration of 'no ip ospf capability opaque' in r2 configuration" + ) + rc, _, _ = tgen.net["r2"].cmd_status( + "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False + ) + assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration" + assert rc, assertmsg + + # STEP 7 in test_ospf_opaque_interface_disable and STEP 62 in CI tests + step("Verify the ospf opaque option is applied to the r2 interface") + r2_interface_without_opaque = { + "interfaces": { + "r2-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.2", + "ospfIfType": "Broadcast", + "opaqueCapable": False, + } + } + } + r2_interface_with_opaque = { + "interfaces": { + "r2-eth0": { + "ifUp": True, + "ospfEnabled": True, + "ipAddress": "10.0.1.2", + "ospfIfType": "Broadcast", + "opaqueCapable": True, + } + } + } + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF interface has opaque capability disabled" + assert result is None, assertmsg + + # STEP 8 in test_ospf_opaque_interface_disable and STEP 63 in CI tests + step("Install opaque LSAs on r1") + pread = r2.popen( + ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"], + encoding=None, # don't buffer + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + p = r1.popen( + [ + apibin, + "-v", + "add,9,10.0.1.1,230,1,feedaceedeadbeef", + "add,10,1.2.3.4,231,1,feedaceecafebeef", + "add,11,232,1,feedaceebaddbeef", + "wait,20", + ] + ) + opaque_LSAs_in_database = { + "areas": { + "1.2.3.4": { + "linkLocalOpaqueLsa": [ + { + "lsId": "230.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000001", + }, + ], + "linkLocalOpaqueLsaCount": 1, + "areaLocalOpaqueLsa": [ + { + "lsId": "231.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000001", + }, + ], + "areaLocalOpaqueLsaCount": 1, + }, + }, + "asExternalOpaqueLsa": [ + { + "lsId": "232.0.0.1", + "advertisedRouter": "1.0.0.0", + "sequenceNumber": "80000001", + }, + ], + "asExternalOpaqueLsaCount": 1, + } + opaque_area_empty_database = { + "routerId":"2.0.0.0", + "areaLocalOpaqueLsa":{ + "areas":{ + "1.2.3.4":[ + ] + } + } + } + + # STEP 9 in test_ospf_opaque_interface_disable and STEP 64 in CI tests + step("Check that LSAs are added on r1") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF database doesn't contain opaque LSAs" + assert result is None, assertmsg + + # STEP 10 in test_ospf_opaque_interface_disable and STEP 65 in CI tests + step("Check that LSAs are not added on r2") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", + opaque_area_empty_database, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF area database contains opaque LSAs" + assert result is None, assertmsg + + # STEP 11 in test_ospf_opaque_interface_disable and STEP 66 in CI tests + step("Enable OSPF opaque LSA Copability on r1's interface to r2") + r1.vtysh_multicmd("conf t\ninterface r1-eth0\nip ospf capability opaque") + time.sleep(15) + + # STEP 12 in test_ospf_opaque_interface_disable and STEP 67 in CI tests + step("Verify no r1 configuration of 'no ip ospf capability opaque'") + rc, _, _ = tgen.net["r1"].cmd_status( + "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False + ) + assertmsg = "'no ip ospf capability opaque' not applied, but not present in r1 configuration" + assert rc, assertmsg + + # STEP 13 in test_ospf_opaque_interface_disable and STEP 68 in CI tests + step("Verify the ospf opaque option is applied to the r1 interface") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf interface json", r1_interface_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" + assert result is None, assertmsg + + r1_neighbor_with_opaque = { + "neighbors": { + "2.0.0.0": [ + { + "optionsList": "*|O|-|-|-|-|E|-", + } + ] + } + } + r2_neighbor_with_opaque = { + "neighbors": { + "1.0.0.0": [ + { + "optionsList": "*|O|-|-|-|-|E|-", + } + ] + } + } + # STEP 14 in test_ospf_opaque_interface_disable and STEP 69 in CI tests + step("Verify that the r1 neighbor options include opaque") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" + assert result is None, assertmsg + + # STEP 15 in test_ospf_opaque_interface_disable and STEP 70 in CI tests + step("Verify that the r2 neighbor options include opaque") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" + assert result is None, assertmsg + + # STEP 16 in test_ospf_opaque_interface_disable and STEP 71 in CI tests + step("Check that LSAs are now added to r2") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF database doesn't contains opaque LSAs" + assert result is None, assertmsg + + # STEP 17 in test_ospf_opaque_interface_disable and STEP 72 in CI tests + step( + "Disable Opaque Capability on r2's interface to r1 using the interface address" + ) + r2.vtysh_multicmd( + "conf t\ninterface r2-eth0\nno ip ospf capability opaque 10.0.1.2" + ) + + # STEP 18 in test_ospf_opaque_interface_disable and STEP 73 in CI tests + step("Clear the OSPF process on r2 to clear the OSPF LSDB") + r2.vtysh_multicmd("clear ip ospf process") + time.sleep(15) + + # STEP 19 in test_ospf_opaque_interface_disable and STEP 74 in CI tests + step("Verify the r2 configuration of 'no ip ospf capability opaque 10.0.1.2'") + no_capability_opaque_cfg = ( + tgen.net["r2"] + .cmd_nostatus( + 'vtysh -c "show running ospfd" | grep "^ no ip ospf capability opaque 10.0.1.2"' + ) + .rstrip() + ) + assertmsg = "'no ip ospf capability opaque 10.0.1.2' applied, but not present in configuration" + assert ( + no_capability_opaque_cfg == " no ip ospf capability opaque 10.0.1.2" + ), assertmsg + + # STEP 20 in test_ospf_opaque_interface_disable and STEP 75 in CI tests + step("Verify the ospf opaque option is not applied to the r2 interface") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_without_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF interface doesn't have opaque capability disabled" + assert result is None, assertmsg + + # STEP 21 in test_ospf_opaque_interface_disable and STEP 76 in CI tests + step("Verify that the r1 neighbor options don't include opaque") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_without_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF neighbor has opaque option in optionsList" + assert result is None, assertmsg + + # STEP 22 in test_ospf_opaque_interface_disable and STEP 77 in CI tests + step("Verify that the r2 neighbor options don't include opaque") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_without_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF neighbor has opaque option in optionsList" + assert result is None, assertmsg + + # STEP 23 in test_ospf_opaque_interface_disable and STEP 78 in CI tests + step("Verify that r1 still has the opaque LSAs") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf database json", opaque_LSAs_in_database + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF database doesn't contain opaque LSAs" + assert result is None, assertmsg + + # STEP 24 in test_ospf_opaque_interface_disable and STEP 79 in CI tests + step("Verify that r2 doesn't have the opaque LSAs") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf database opaque-area json", + opaque_area_empty_database, True + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF area database contains opaque LSAs" + assert result is None, assertmsg + + # STEP 25 in test_ospf_opaque_interface_disable and STEP 80 in CI tests + step("Remove the 'no ip ospf capability opaque 10.0.1.2' config from r2 ") + r2.vtysh_multicmd( + "conf t\ninterface r2-eth0\nip ospf capability opaque 10.0.1.2" + ) + time.sleep(15) + + # STEP 26 in test_ospf_opaque_interface_disable and STEP 81 in CI tests + step("Verify the r2 removal of 'no ip ospf capability opaque 10.0.1.2'") + rc, _, _ = tgen.net["r2"].cmd_status( + "show running ospfd | grep -q 'ip ospf capability opaque'", warn=False + ) + assertmsg = "'no ip ospf capability opaque' not applied, but not present in r2 configuration" + assert rc, assertmsg + + # STEP 27 in test_ospf_opaque_interface_disable and STEP 82 in CI tests + step("Verify the ospf opaque option is applied to the r2 interface") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf interface json", r2_interface_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF interface doesn't have opaque capability disabled" + assert result is None, assertmsg + + # STEP 28 in test_ospf_opaque_interface_disable and STEP 83 in CI tests + step("Verify that the r2 neighbor options include opaque") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf neighbor detail json", r2_neighbor_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF neighbor doesn't have opaque option in optionsList" + assert result is None, assertmsg + + # STEP 29 in test_ospf_opaque_interface_disable and STEP 84 in CI tests + step("Verify that the r1 neighbor options include opaque") + test_func = partial( + topotest.router_json_cmp, r1, "show ip ospf neighbor detail json", r1_neighbor_with_opaque + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r1 OSPF neighbor doesn't have opaque option in optionsList" + assert result is None, assertmsg + + # STEP 30 in test_ospf_opaque_interface_disable and STEP 85 in CLI tests + step("Verify that r2 now has the opaque LSAs") + test_func = partial( + topotest.router_json_cmp, r2, "show ip ospf database json", opaque_LSAs_in_database + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assertmsg = "r2 OSPF database doesn't contain opaque LSAs" + assert result is None, assertmsg + + except Exception: + if p: + p.terminate() + if p.wait(): + comm_error(p) + p = None + raise + finally: + if pread: + pread.terminate() + pread.wait() + if p: + p.terminate() + p.wait() + + +@pytest.mark.parametrize("tgen", [2], indirect=True) +def test_ospf_opaque_interface_disable(tgen): + apibin = os.path.join(CLIENTDIR, "ospfclient.py") + rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"]) + logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e) + _test_opaque_interface_disable(tgen, apibin) + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py index 55166367c1e7..49c25ab8f6da 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py @@ -156,7 +156,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -280,7 +280,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -314,7 +314,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -330,9 +330,9 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -340,7 +340,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -349,7 +349,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -374,7 +374,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -383,9 +383,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -403,7 +401,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) dut = "r1" step("All 5 routes are advertised after deletion of configured summary.") @@ -414,7 +412,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("configure the summary again and delete static routes .") ospf_summ_r1 = { @@ -442,7 +440,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) input_dict = { "r0": { @@ -461,7 +459,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -470,7 +468,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Add back static routes.") input_dict_static_rtes = { @@ -488,7 +486,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -497,7 +495,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}} dut = "r1" @@ -508,7 +506,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show configure summaries.") @@ -525,7 +523,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure new static route which is matching configured summary.") input_dict_static_rtes = { @@ -567,7 +565,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"] - ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network) + ip_net = str(ipaddress.ip_interface("{}".format(ip)).network) ospf_summ_r1 = { "r0": { "ospf6": { @@ -591,7 +589,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Shut one of the interface") intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"] @@ -663,7 +661,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}} @@ -674,7 +672,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -702,7 +700,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -711,7 +709,7 @@ def test_ospfv3_type5_summary_tc42_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -774,11 +772,9 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf6": { @@ -804,7 +800,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -820,7 +816,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Change the summary address mask to lower match (ex - 16 to 8)") ospf_summ_r1 = { @@ -855,7 +851,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -871,7 +867,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Change the summary address mask to higher match (ex - 8 to 24)") ospf_summ_r1 = { @@ -899,7 +895,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step( "Verify that external routes(static / connected) are summarised" @@ -920,7 +916,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step(" Un configure one of the summary address.") ospf_summ_r1 = { @@ -955,7 +951,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) ospf_summ_r1 = { "r0": { @@ -982,7 +978,7 @@ def test_ospfv3_type5_summary_tc43_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -1030,11 +1026,9 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf6": { @@ -1066,7 +1060,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1082,7 +1076,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -1107,7 +1101,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1116,9 +1110,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -1136,7 +1128,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1161,7 +1153,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1177,7 +1169,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1207,7 +1199,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1226,7 +1218,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1251,7 +1243,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1266,7 +1258,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1287,7 +1279,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1318,7 +1310,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") @@ -1326,7 +1318,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1335,7 +1327,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Configure summary first & then configure matching static route.") @@ -1467,11 +1459,9 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { "ospf6": { @@ -1503,7 +1493,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1541,7 +1531,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1550,9 +1540,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -1570,7 +1558,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) step("Configure Min tag value") ospf_summ_r1 = { @@ -1595,7 +1583,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries with tag.") input_dict = { @@ -1611,7 +1599,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure Max Tag Value") ospf_summ_r1 = { @@ -1641,7 +1629,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1660,7 +1648,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("configure new static route with different tag.") input_dict_static_rtes_11 = { @@ -1685,7 +1673,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1700,7 +1688,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step( "Verify that boundary values tags are used for summary route" @@ -1721,7 +1709,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary address") ospf_summ_r1 = { @@ -1752,7 +1740,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that summary address is flushed from neighbor.") @@ -1760,7 +1748,7 @@ def ospfv3_type5_summary_tc45_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1769,7 +1757,7 @@ def ospfv3_type5_summary_tc45_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Configure summary first & then configure matching static route.") @@ -1853,7 +1841,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step( "Configure External Route summary in R0 to summarise 5" @@ -1887,7 +1875,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1896,9 +1884,9 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv6"][0]: { "summaryAddress": SUMMARY["ipv6"][0], @@ -1909,7 +1897,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Delete the configured summary") ospf_summ_r1 = { @@ -1936,7 +1924,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -1945,9 +1933,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed Error: Summary Route still present in RIB".format(tc_name) step("show ip ospf summary should not have any summary address.") input_dict = { @@ -1965,7 +1951,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary still present in DB".format(tc_name) step("Reconfigure summary with no advertise.") ospf_summ_r1 = { @@ -1996,7 +1982,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2005,9 +1991,9 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) - step("Verify that show ip ospf summary should show the " "configured summaries.") + step("Verify that show ip ospf summary should show the configured summaries.") input_dict = { SUMMARY["ipv6"][0]: { "summaryAddress": SUMMARY["ipv6"][0], @@ -2018,7 +2004,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step( "Change summary address from no advertise to advertise " @@ -2067,7 +2053,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2083,9 +2069,9 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") output = tgen.gears["r0"].vtysh_cmd( "show ipv6 ospf6 database as-external json", isjson=True ) @@ -2101,7 +2087,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2110,7 +2096,7 @@ def test_ospfv3_type5_summary_tc46_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is present in RIB".format(tc_name) write_test_footer(tc_name) @@ -2157,11 +2143,9 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -2189,7 +2173,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2205,9 +2189,9 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -2215,7 +2199,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2224,7 +2208,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step( "Configure route map and & rule to permit configured summary address," @@ -2287,7 +2271,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) input_dict = { SUMMARY["ipv6"][0]: { @@ -2302,7 +2286,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Configure metric type as 1 in route map.") @@ -2339,7 +2323,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Un configure metric type from route map.") @@ -2376,7 +2360,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) step("Change rule from permit to deny in prefix list.") pfx_list = { @@ -2407,7 +2391,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2416,7 +2400,7 @@ def test_ospfv3_type5_summary_tc48_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) write_test_footer(tc_name) @@ -2556,7 +2540,7 @@ def test_ospfv3_type5_summary_tc51_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) write_test_footer(tc_name) @@ -2603,11 +2587,9 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) - step( - "Configure External Route summary in R0 to summarise 5" " routes to one route." - ) + step("Configure External Route summary in R0 to summarise 5 routes to one route.") ospf_summ_r1 = { "r0": { @@ -2635,7 +2617,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2651,9 +2633,9 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -2661,7 +2643,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2670,7 +2652,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -2691,7 +2673,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes is missing in RIB".format(tc_name) step("Verify that show ip ospf summary should show the summaries.") input_dict = { @@ -2707,9 +2689,9 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6") assert ( result is True - ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name) + ), "Testcase {} : Failed Error: Summary missing in OSPF DB".format(tc_name) - step("Verify that originally advertised routes are withdraw from there" " peer.") + step("Verify that originally advertised routes are withdraw from there peer.") input_dict = { "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]} } @@ -2717,7 +2699,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): result = verify_ospf6_rib(tgen, dut, input_dict, expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format( + ), "Testcase {} : Failed \n Error: Routes still present in OSPF RIB {}".format( tc_name, result ) @@ -2726,7 +2708,7 @@ def test_ospfv3_type5_summary_tc49_p2(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes still present in RIB".format(tc_name) write_test_footer(tc_name) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py index 2f90c98785b3..58608e249b2e 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py @@ -111,7 +111,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf6_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf6_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "setup_module :Failed \n Error: {}".format( ospf6_covergence ) @@ -182,7 +182,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -214,7 +214,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -244,7 +244,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -272,7 +272,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -287,7 +287,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -301,7 +301,7 @@ def test_ospf6_auth_trailer_tc1_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -348,7 +348,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -380,7 +380,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -410,7 +410,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -438,7 +438,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -453,7 +453,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -467,7 +467,7 @@ def test_ospf6_auth_trailer_tc2_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -531,7 +531,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -561,7 +561,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -580,7 +580,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -606,7 +606,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -621,7 +621,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -635,7 +635,7 @@ def test_ospf6_auth_trailer_tc3_keychain_md5(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -699,7 +699,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -729,7 +729,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -748,7 +748,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=5 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -774,7 +774,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -789,7 +789,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): "show ip ospf6 neighbor cmd." ) ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -803,7 +803,7 @@ def test_ospf6_auth_trailer_tc4_keychain_sha256(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -850,7 +850,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -887,7 +887,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -919,7 +919,7 @@ def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -966,7 +966,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -998,7 +998,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1030,7 +1030,7 @@ def test_ospf6_auth_trailer_tc6_sha256_mismatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1102,7 +1102,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1132,7 +1132,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1162,7 +1162,7 @@ def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1234,7 +1234,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1264,7 +1264,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1294,7 +1294,7 @@ def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1342,7 +1342,7 @@ def test_ospf6_auth_trailer_tc9_keychain_not_configured(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1372,7 +1372,7 @@ def test_ospf6_auth_trailer_tc9_keychain_not_configured(request): ospf6_covergence = verify_ospf6_neighbor( tgen, topo, dut=dut, expected=False, retry_timeout=3 ) - assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is not True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) @@ -1402,7 +1402,7 @@ def test_ospf6_auth_trailer_tc10_no_auth_trailer(request): dut = "r2" ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format( + assert ospf6_covergence is True, "Testcase {} :Failed \n Error: {}".format( tc_name, ospf6_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py index 472364e84f1b..0c1e3fa43e87 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py @@ -115,7 +115,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -257,7 +257,7 @@ def test_ospfv3_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -352,7 +352,7 @@ def test_ospfv3_ecmp_tc16_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -390,7 +390,7 @@ def test_ospfv3_ecmp_tc17_p0(request): step("Verify that OSPF is up with 2 neighborship sessions.") dut = "r1" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py index 6b3e16965c6f..7c6773260edf 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py @@ -128,7 +128,7 @@ def setup_module(mod): pytest.skip(tgen.errors) # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -282,7 +282,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request): step("Verify that OSPF is up with 8 neighborship sessions.") ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -343,7 +343,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request): dut = "r0" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -352,7 +352,7 @@ def test_ospfv3_lan_ecmp_tc18_p0(request): dut = "r2" ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py index c0d8d718cc43..dc4ce888306e 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py @@ -73,7 +73,7 @@ def setup_module(mod): pytest.skip(tgen.errors) result = verify_ospf6_neighbor(tgen, topo) - assert result is True, "setup_module: Failed \n Error:" " {}".format(result) + assert result is True, "setup_module: Failed \n Error: {}".format(result) logger.info("Running setup_module() done") diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py index 138112775f9d..90548fb5ced4 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py @@ -138,7 +138,7 @@ def setup_module(mod): # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -277,7 +277,7 @@ def test_ospfv3_nssa_tc26_p0(request): result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result) + ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result) step("Now configure area 0 on interface of r1 connecting to r2.") @@ -349,7 +349,7 @@ def test_ospfv3_nssa_tc26_p0(request): result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False) assert ( result is not True - ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result) + ), "Testcase {} : Failed \n Nbrs are not down Error: {}".format(tc_name, result) step("Now configure area 2 on interface of r1 connecting to r2.") @@ -471,7 +471,7 @@ def test_ospfv3_learning_tc15_p0(request): result = verify_ospf6_neighbor(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).") + step("Change area 1 as non nssa area (on the fly changing area type on DUT).") for rtr in ["r1", "r2", "r3"]: input_dict = { diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py index b2cd0da24e78..069806a3ef46 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py @@ -129,7 +129,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -212,9 +212,7 @@ def test_ospfv3_routemaps_functionality_tc19_p0(request): result = create_router_ospf(tgen, topo, ospf_red_r1) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32" - ) + step("Create prefix-list in R0 to permit 10.0.20.1/32 prefix & deny 10.0.20.2/32") # Create ip prefix list pfx_list = { @@ -686,7 +684,7 @@ def test_ospfv3_routemaps_functionality_tc25_p0(request): # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -1078,7 +1076,7 @@ def test_ospfv3_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) @@ -1147,7 +1145,7 @@ def test_ospfv3_routemaps_functionality_tc24_p0(request): result = verify_prefix_lists(tgen, pfx_list) assert ( result is not True - ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format( + ), "Testcase {} : Failed \n Prefix list not present. Error: {}".format( tc_name, result ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py index 9e7f112efbc5..645dea8dec44 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py @@ -120,7 +120,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -256,7 +256,7 @@ def test_ospfv3_redistribution_tc5_p0(request): step("Verify that OSPF neighbors are FULL.") ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -616,7 +616,7 @@ def test_ospfv3_redistribution_tc8_p1(request): step("Verify that OSPF neighbours are reset and forms new adjacencies.") # Api call verify whether OSPF is converged ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py index 5d5ba1480fb1..7199f160fe5b 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py @@ -114,7 +114,7 @@ def setup_module(mod): pytest.skip(tgen.errors) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "setup_module :Failed \n Error: {}".format( ospf_covergence ) @@ -460,7 +460,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -513,7 +513,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -566,7 +566,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -618,7 +618,7 @@ def test_ospfv3_hello_tc10_p0(request): step("verify that ospf neighbours are full") ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut) - assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format( + assert ospf_covergence is True, "Testcase Failed \n Error: {}".format( ospf_covergence ) @@ -709,9 +709,7 @@ def test_ospfv3_hello_tc10_p0(request): result = create_interfaces_cfg(tgen, topo1) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Verify that timer value is deleted from intf & " "set to default value 40 sec." - ) + step("Verify that timer value is deleted from intf & set to default value 40 sec.") input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigHello": 10}}}}} dut = "r1" result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) @@ -763,7 +761,7 @@ def test_ospfv3_dead_tc11_p0(request): result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step("modify dead interval from default value to r1" "dead interval timer on r2") + step("modify dead interval from default value to r1 dead interval timer on r2") topo1 = { "r0": { @@ -799,7 +797,7 @@ def test_ospfv3_dead_tc11_p0(request): # reconfiguring deleted ospf process by resetting the configs. reset_config_on_routers(tgen) - step("reconfigure the default dead interval timer value to " "default on r1 and r2") + step("reconfigure the default dead interval timer value to default on r1 and r2") topo1 = { "r0": { "links": { @@ -920,9 +918,7 @@ def test_ospfv3_dead_tc11_p0(request): result = create_interfaces_cfg(tgen, topo1) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Verify that timer value is deleted from intf & " "set to default value 40 sec." - ) + step("Verify that timer value is deleted from intf & set to default value 40 sec.") input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 40}}}}} dut = "r1" result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict) @@ -967,18 +963,14 @@ def test_ospfv3_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0", ospf="ospf6") clear_ospf(tgen, "r1", ospf="ospf6") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf6_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " "instead of Exstart. Error: {}".format(tc_name, result) ) - step( - "Verify that configured MTU value is updated in the show ip " "ospf interface." - ) + step("Verify that configured MTU value is updated in the show ip ospf interface.") dut = "r0" input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 1400}}}}} @@ -1035,9 +1027,7 @@ def test_ospfv3_tc4_mtu_ignore_p0(request): clear_ospf(tgen, "r0", ospf="ospf6") - step( - "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State." - ) + step("Verify that OSPF neighborship between R0 and R1 is stuck in Exstart State.") result = verify_ospf6_neighbor(tgen, topo, expected=False) assert result is not True, ( "Testcase {} : Failed \n OSPF nbrs are Full " @@ -1054,9 +1044,7 @@ def test_ospfv3_tc4_mtu_ignore_p0(request): result = verify_ospf6_neighbor(tgen, topo) assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result) - step( - "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0." - ) + step("Configure ospf interface with jumbo MTU (9216). Reset ospf neighbors on R0.") rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf)) rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf)) @@ -1091,53 +1079,6 @@ def test_ospfv3_show_p1(request): result = create_debug_log_config(tgen, input_dict) - # Code coverage steps #Do Not upstream - input_dict_config = { - "r1": { - "raw_config": [ - "end", - "debug ospf6 event", - "debug ospf6 gr helper", - "debug ospf6 ism events", - "debug ospf6 ism status", - "debug ospf6 ism timers", - "debug ospf6 nsm events", - "debug ospf6 nsm status", - "debug ospf6 nsm timers ", - "debug ospf6 nssa", - "debug ospf6 lsa aggregate", - "debug ospf6 lsa flooding ", - "debug ospf6 lsa generate", - "debug ospf6 lsa install ", - "debug ospf6 lsa refresh", - "debug ospf6 packet all detail", - "debug ospf6 packet all recv", - "debug ospf6 packet all send", - "debug ospf6 packet dd detail", - "debug ospf6 packet dd recv", - "debug ospf6 packet dd send ", - "debug ospf6 packet hello detail", - "debug ospf6 packet hello recv", - "debug ospf6 packet hello send", - "debug ospf6 packet ls-ack detail", - "debug ospf6 packet ls-ack recv", - "debug ospf6 packet ls-ack send", - "debug ospf6 packet ls-request detail", - "debug ospf6 packet ls-request recv", - "debug ospf6 packet ls-request send", - "debug ospf6 packet ls-update detail", - "debug ospf6 packet ls-update recv", - "debug ospf6 packet ls-update send", - "debug ospf6 sr", - "debug ospf6 te ", - "debug ospf6 zebra interface", - "debug ospf6 zebra redistribute", - ] - } - } - - apply_raw_config(tgen, input_dict_config) - for rtr in topo["routers"]: clear_ospf(tgen, rtr, ospf="ospf6") @@ -1234,7 +1175,7 @@ def ospfv3_router_id_tc14_p2(request): clear_ospf(tgen, rtr, ospf="ospf6") ospf_covergence = verify_ospf6_neighbor(tgen, topo1) - assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format( + assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format( ospf_covergence ) @@ -1260,9 +1201,9 @@ def ospfv3_router_id_tc14_p2(request): assert result is True, "Testcase : Failed \n Error: {}".format(result) ospf_covergence = verify_ospf6_neighbor(tgen, topo, expected=False) - assert ( - ospf_covergence is not True - ), "OSPF NBRs are up.Failed \n Error:" " {}".format(ospf_covergence) + assert ospf_covergence is not True, "OSPF NBRs are up.Failed \n Error: {}".format( + ospf_covergence + ) topo1 = {} topo1 = deepcopy(topo) @@ -1305,7 +1246,7 @@ def ospfv3_router_id_tc14_p2(request): topo1["routers"]["r3"]["ospf6"]["router_id"] = "1.1.1.4" ospf_covergence = verify_ospf6_neighbor(tgen, topo1) - assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format( + assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format( ospf_covergence ) @@ -1313,7 +1254,7 @@ def ospfv3_router_id_tc14_p2(request): reset_config_on_routers(tgen) ospf_covergence = verify_ospf6_neighbor(tgen, topo) - assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format( + assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error: {}".format( ospf_covergence ) diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json index af83d6bea373..34ce481a3f63 100644 --- a/tests/topotests/pim_acl/r1/ospf_neighbor.json +++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json @@ -2,57 +2,57 @@ "neighbors":{ "192.168.0.11":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.11", + "ifaceAddress":"192.168.101.11", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.12":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.12", + "ifaceAddress":"192.168.101.12", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.13":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.13", + "ifaceAddress":"192.168.101.13", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.14":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.14", + "ifaceAddress":"192.168.101.14", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ], "192.168.0.15":[ { - "priority":0, + "nbrPriority":0, "converged":"Full", - "address":"192.168.101.15", + "ifaceAddress":"192.168.101.15", "ifaceName":"r1-eth1:192.168.101.1", - "retransmitCounter":0, - "requestCounter":0, - "dbSummaryCounter":0 + "linkStateRetransmissionListCounter":0, + "linkStateRequestListCounter":0, + "databaseSummaryListCounter":0 } ] } diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json index 1e70fcc36e37..198098d3d305 100644 --- a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json @@ -4,9 +4,9 @@ "neighbors":{ "192.168.0.11":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.11", + "ifaceAddress":"192.168.101.11", "ifaceName":"r1-eth1:192.168.101.1" } ] diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json index 7f2ab248cc1d..6fce225ffc8e 100644 --- a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json @@ -4,9 +4,9 @@ "neighbors":{ "192.168.0.12":[ { - "priority":10, + "nbrPriority":10, "converged":"Full", - "address":"192.168.101.12", + "ifaceAddress":"192.168.101.12", "ifaceName":"r1-eth3:192.168.101.1" } ] diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 6986e3051c95..0062cf3de297 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,8 @@ # Skip pytests example directory [pytest] +# asyncio_mode = auto + # We always turn this on inside conftest.py, default shown # addopts = --junitxml=<rundir>/topotests.xml @@ -24,7 +26,7 @@ log_file_date_format = %Y-%m-%d %H:%M:%S junit_logging = all junit_log_passing_tests = true -norecursedirs = .git example_test example_topojson_test lib docker +norecursedirs = .git example_munet example_test example_topojson_test lib munet docker # Directory to store test results and run logs in, default shown # rundir = /tmp/topotests diff --git a/tests/topotests/rip_allow_ecmp/__init__.py b/tests/topotests/rip_allow_ecmp/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/rip_allow_ecmp/r1/frr.conf b/tests/topotests/rip_allow_ecmp/r1/frr.conf new file mode 100644 index 000000000000..d8eb9a31d337 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r1/frr.conf @@ -0,0 +1,9 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router rip + allow-ecmp + network 192.168.1.0/24 + timers basic 5 15 10 +exit diff --git a/tests/topotests/rip_allow_ecmp/r2/frr.conf b/tests/topotests/rip_allow_ecmp/r2/frr.conf new file mode 100644 index 000000000000..d7ea6f310277 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r2/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/r3/frr.conf b/tests/topotests/rip_allow_ecmp/r3/frr.conf new file mode 100644 index 000000000000..2362c47b8407 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r3/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/r4/frr.conf b/tests/topotests/rip_allow_ecmp/r4/frr.conf new file mode 100644 index 000000000000..995c2beca9ec --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r4/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r4-eth0 + ip address 192.168.1.4/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/r5/frr.conf b/tests/topotests/rip_allow_ecmp/r5/frr.conf new file mode 100644 index 000000000000..57a06ec0e3bb --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/r5/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r5-eth0 + ip address 192.168.1.5/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py new file mode 100644 index 000000000000..7d958fd496d6 --- /dev/null +++ b/tests/topotests/rip_allow_ecmp/test_rip_allow_ecmp.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if RIP `allow-ecmp` command works correctly. +""" + +import os +import sys +import json +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.common_config import step + +pytestmark = [pytest.mark.ripd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3", "r4", "r5")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_rip_allow_ecmp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _show_rip_routes(): + xpath = ( + "/frr-ripd:ripd/instance[vrf='default']" + "/state/routes/route[prefix='10.10.10.1/32']" + ) + try: + output = json.loads( + r1.vtysh_cmd(f"show yang operational-data {xpath} ripd") + ) + except Exception: + return False + + try: + output = output["frr-ripd:ripd"]["instance"][0]["state"]["routes"] + except KeyError: + return False + + expected = { + "route": [ + { + "prefix": "10.10.10.1/32", + "nexthops": { + "nexthop": [ + { + "nh-type": "ip4", + "protocol": "rip", + "rip-type": "normal", + "gateway": "192.168.1.2", + "from": "192.168.1.2", + "tag": 0, + }, + { + "nh-type": "ip4", + "protocol": "rip", + "rip-type": "normal", + "gateway": "192.168.1.3", + "from": "192.168.1.3", + "tag": 0, + }, + ] + }, + "metric": 2, + }, + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_rip_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath in `show ip rip`" + + def _show_routes(nh_num): + output = json.loads(r1.vtysh_cmd("show ip route json")) + expected = { + "10.10.10.1/32": [ + { + "internalNextHopNum": nh_num, + "internalNextHopActiveNum": nh_num, + "nexthops": [ + { + "ip": "192.168.1.2", + "active": True, + }, + { + "ip": "192.168.1.3", + "active": True, + }, + ], + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes, 4) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath (4) in `show ip route`" + + step( + "Configure allow-ecmp 2, ECMP group routes SHOULD have next-hops with the lowest IPs" + ) + r1.vtysh_cmd( + """ + configure terminal + router rip + allow-ecmp 2 + """ + ) + + test_func = functools.partial(_show_rip_routes) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Can't see 10.10.10.1/32 as ECMP with the lowest next-hop IPs" + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Can't see 10.10.10.1/32 as multipath (2) in `show ip route`" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/rip_bfd_topo1/__init__.py b/tests/topotests/rip_bfd_topo1/__init__.py new file mode 100755 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/rip_bfd_topo1/r1/bfdd.conf b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf new file mode 100644 index 000000000000..e848bda89fba --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf @@ -0,0 +1,6 @@ +bfd + profile slow + receive-interval 1000 + transmit-interval 1000 + exit +exit diff --git a/tests/topotests/rip_bfd_topo1/r1/ripd.conf b/tests/topotests/rip_bfd_topo1/r1/ripd.conf new file mode 100644 index 000000000000..6cef84692a81 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r1/ripd.conf @@ -0,0 +1,17 @@ +interface r1-eth0 + ip rip bfd + ip rip bfd profile slow +exit +! +interface r1-eth1 + ip rip bfd + ip rip bfd profile slow +exit +! +router rip + allow-ecmp + network 192.168.0.1/24 + network 192.168.1.1/24 + redistribute connected + timers basic 10 40 30 +exit diff --git a/tests/topotests/rip_bfd_topo1/r1/zebra.conf b/tests/topotests/rip_bfd_topo1/r1/zebra.conf new file mode 100644 index 000000000000..e3800a9c6820 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +interface r1-eth0 + ip address 192.168.0.1/24 +exit +! +interface r1-eth1 + ip address 192.168.1.1/24 +exit +! +interface lo + ip address 10.254.254.1/32 +exit diff --git a/tests/topotests/rip_bfd_topo1/r2/bfdd.conf b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf new file mode 100644 index 000000000000..e848bda89fba --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf @@ -0,0 +1,6 @@ +bfd + profile slow + receive-interval 1000 + transmit-interval 1000 + exit +exit diff --git a/tests/topotests/rip_bfd_topo1/r2/ripd.conf b/tests/topotests/rip_bfd_topo1/r2/ripd.conf new file mode 100644 index 000000000000..35e4688c58e3 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/ripd.conf @@ -0,0 +1,11 @@ +interface r2-eth0 + ip rip bfd +exit +! +router rip + bfd default-profile slow + network 192.168.0.2/24 + redistribute connected + redistribute static + timers basic 10 40 30 +exit diff --git a/tests/topotests/rip_bfd_topo1/r2/staticd.conf b/tests/topotests/rip_bfd_topo1/r2/staticd.conf new file mode 100644 index 000000000000..6fe93748a062 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/staticd.conf @@ -0,0 +1 @@ +ip route 10.254.254.100/32 lo diff --git a/tests/topotests/rip_bfd_topo1/r2/zebra.conf b/tests/topotests/rip_bfd_topo1/r2/zebra.conf new file mode 100644 index 000000000000..cad922f6bb8b --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r2/zebra.conf @@ -0,0 +1,8 @@ +interface r2-eth0 + ip address 192.168.0.2/24 +exit +! +interface lo + ip address 10.254.254.2/32 +exit + diff --git a/tests/topotests/rip_bfd_topo1/r3/bfdd.conf b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf new file mode 100644 index 000000000000..e848bda89fba --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf @@ -0,0 +1,6 @@ +bfd + profile slow + receive-interval 1000 + transmit-interval 1000 + exit +exit diff --git a/tests/topotests/rip_bfd_topo1/r3/ripd.conf b/tests/topotests/rip_bfd_topo1/r3/ripd.conf new file mode 100644 index 000000000000..0df0bac531d1 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/ripd.conf @@ -0,0 +1,11 @@ +interface r3-eth0 + ip rip bfd + ip rip bfd profile slow +exit +! +router rip + network 192.168.1.2/24 + redistribute connected + redistribute static + timers basic 10 40 30 +exit diff --git a/tests/topotests/rip_bfd_topo1/r3/staticd.conf b/tests/topotests/rip_bfd_topo1/r3/staticd.conf new file mode 100644 index 000000000000..6fe93748a062 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/staticd.conf @@ -0,0 +1 @@ +ip route 10.254.254.100/32 lo diff --git a/tests/topotests/rip_bfd_topo1/r3/zebra.conf b/tests/topotests/rip_bfd_topo1/r3/zebra.conf new file mode 100644 index 000000000000..12ffeca42fe5 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/r3/zebra.conf @@ -0,0 +1,7 @@ +interface r3-eth0 + ip address 192.168.1.2/24 +exit +! +interface lo + ip address 10.254.254.3/32 +exit diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot new file mode 100644 index 000000000000..1480a8f76d5b --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.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="rip_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, + ]; + + # Switches + s1 [ + shape=oval, + label="s1\n192.168.0.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + s2 [ + shape=oval, + label="s1\n192.168.1.0/24", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- s1 [label="r1-eth0\n.1"]; + r2 -- s1 [label="r2-eth0\n.2"]; + + r1 -- s2 [label="r1-eth1\n.1"]; + r3 -- s2 [label="r1-eth0\n.2"]; +} diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png new file mode 100644 index 000000000000..e5e362e3e0a6 Binary files /dev/null and b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png differ diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py new file mode 100644 index 000000000000..71c90931fb38 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py @@ -0,0 +1,252 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# test_rip_bfd_topo1.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2023 by +# Network Device Education Foundation, Inc. ("NetDEF") +# + +""" +test_rip_bfd_topo1.py: Test RIP BFD integration. +""" + +import sys +import re +import pytest + +from functools import partial +from lib import topotest +from lib.topogen import Topogen, TopoRouter +from lib.topolog import logger + +pytestmark = [ + pytest.mark.bfdd, + pytest.mark.ripd, +] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = { + "s1": ("r1", "r2"), + "s2": ("r1", "r3") + } + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for router_name, router in router_list.items(): + router.load_config(TopoRouter.RD_BFD, "bfdd.conf") + router.load_config(TopoRouter.RD_RIP, "ripd.conf") + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + if router_name in ["r2", "r3"]: + router.load_config(TopoRouter.RD_STATIC, "staticd.conf") + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +@pytest.fixture(autouse=True) +def skip_on_failure(tgen): + "Test if routers is still running otherwise skip tests" + if tgen.routers_have_failure(): + pytest.skip("skipped because of previous test failure") + + +def show_rip_json(router): + "Get router 'show ip rip' JSON output" + output = router.vtysh_cmd("show ip rip") + routes = output.splitlines()[6:] + json = {} + + for route in routes: + match = re.match( + r"(.)\((.)\)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)", route) + if match is None: + continue + + route_entry = { + "code": match[1], + "subCode": match[2], + "nextHop": match[4], + "metric": int(match[5]), + "from": match[6], + } + + if json.get(match[3]) is None: + json[match[3]] = [] + + json[match[3]].append(route_entry) + + return json + + +def expect_routes(router, routes, time_amount): + "Expect 'routes' in 'router'." + + def test_function(): + "Internal test function." + return topotest.json_cmp(show_rip_json(router), routes) + + _, result = topotest.run_and_expect(test_function, + None, + count=time_amount, + wait=1) + assert result is None, "Unexpected routing table in {}".format( + router.name) + + +def expect_bfd_peers(router, peers): + "Expect 'peers' in 'router' BFD status." + test_func = partial( + topotest.router_json_cmp, + router, + "show bfd peers json", + peers, + ) + _, result = topotest.run_and_expect(test_func, None, count=20, wait=1) + assert result is None, "{} BFD peer status mismatch".format(router) + + +def test_rip_convergence(tgen): + "Test that RIP learns the neighbor routes." + + expect_routes( + tgen.gears["r1"], { + "10.254.254.2/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2" + }], + "10.254.254.3/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.1.2" + }], + "10.254.254.100/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2", + }, { + "code": "R", + "subCode": "n", + "from": "192.168.1.2", + }] + }, 40) + + expect_bfd_peers(tgen.gears["r1"], [{ + "peer": "192.168.0.2", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }, { + "peer": "192.168.1.2", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }]) + + expect_routes( + tgen.gears["r2"], { + "10.254.254.1/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.1" + }], + "10.254.254.3/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.1" + }], + "10.254.254.100/32": [{ + "code": "S", + "subCode": "r", + "from": "self" + }] + }, 40) + + expect_bfd_peers(tgen.gears["r2"], [{ + "peer": "192.168.0.1", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }]) + + expect_routes( + tgen.gears["r3"], { + "10.254.254.1/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.1.1" + }], + "10.254.254.2/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.1.1" + }], + "10.254.254.100/32": [{ + "code": "S", + "subCode": "r", + "from": "self" + }] + }, 40) + + expect_bfd_peers(tgen.gears["r3"], [{ + "peer": "192.168.1.1", + "status": "up", + "receive-interval": 1000, + "transmit-interval": 1000, + }]) + + +def test_rip_bfd_convergence(tgen): + "Test that RIP drop the gone neighbor routes." + + tgen.gears["r3"].link_enable("r3-eth0", False) + + expect_routes( + tgen.gears["r1"], { + "10.254.254.2/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2" + }], + "10.254.254.3/32": None, + "10.254.254.100/32": [{ + "code": "R", + "subCode": "n", + "from": "192.168.0.2", + }] + }, 6) + + expect_routes( + tgen.gears["r3"], { + "10.254.254.1/32": None, + "10.254.254.2/32": None, + "10.254.254.100/32": [{ + "code": "S", + "subCode": "r", + "from": "self" + }] + }, 6) + + +def test_memory_leak(tgen): + "Run the memory leak test and report results." + + 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/rip_passive_interface/__init__.py b/tests/topotests/rip_passive_interface/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/rip_passive_interface/r1/frr.conf b/tests/topotests/rip_passive_interface/r1/frr.conf new file mode 100644 index 000000000000..d8eb9a31d337 --- /dev/null +++ b/tests/topotests/rip_passive_interface/r1/frr.conf @@ -0,0 +1,9 @@ +! +int r1-eth0 + ip address 192.168.1.1/24 +! +router rip + allow-ecmp + network 192.168.1.0/24 + timers basic 5 15 10 +exit diff --git a/tests/topotests/rip_passive_interface/r2/frr.conf b/tests/topotests/rip_passive_interface/r2/frr.conf new file mode 100644 index 000000000000..d7ea6f310277 --- /dev/null +++ b/tests/topotests/rip_passive_interface/r2/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r2-eth0 + ip address 192.168.1.2/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_passive_interface/r3/frr.conf b/tests/topotests/rip_passive_interface/r3/frr.conf new file mode 100644 index 000000000000..2362c47b8407 --- /dev/null +++ b/tests/topotests/rip_passive_interface/r3/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ip address 10.10.10.1/32 +! +int r3-eth0 + ip address 192.168.1.3/24 +! +router rip + network 192.168.1.0/24 + network 10.10.10.1/32 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/rip_passive_interface/test_rip_passive_interface.py b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py new file mode 100644 index 000000000000..c2b28c4a3efe --- /dev/null +++ b/tests/topotests/rip_passive_interface/test_rip_passive_interface.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if RIP `passive-interface default` and `no passive-interface IFNAME` +combination works as expected. +""" + +import os +import sys +import json +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.common_config import step + +pytestmark = [pytest.mark.ripd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_rip_passive_interface(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _show_routes(nh_num): + output = json.loads(r1.vtysh_cmd("show ip route 10.10.10.1/32 json")) + expected = { + "10.10.10.1/32": [ + { + "internalNextHopNum": nh_num, + "internalNextHopActiveNum": nh_num, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2" + + step("Configure `passive-interface default` on r2") + r2.vtysh_cmd( + """ + configure terminal + router rip + passive-interface default + """ + ) + + test_func = functools.partial(_show_routes, 1) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 1" + + step("Configure `no passive-interface r2-eth0` on r2 towards r1") + r2.vtysh_cmd( + """ + configure terminal + router rip + no passive-interface r2-eth0 + """ + ) + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert result is None, "Got 10.10.10.1/32, but the next-hop count is not 2" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/rip_topo1/r1/rip_status.ref b/tests/topotests/rip_topo1/r1/rip_status.ref index 31ad46ab2eca..19fff1a8289e 100644 --- a/tests/topotests/rip_topo1/r1/rip_status.ref +++ b/tests/topotests/rip_topo1/r1/rip_status.ref @@ -18,5 +18,5 @@ Routing Protocol is "rip" r1-eth3 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update - 193.1.1.2 0 0 120 XX:XX:XX + 193.1.1.2 0 0 120 XX:XX:XX Distance: (default is 120) diff --git a/tests/topotests/rip_topo1/r2/rip_status.ref b/tests/topotests/rip_topo1/r2/rip_status.ref index 99841a62b0ed..468b7aece85f 100644 --- a/tests/topotests/rip_topo1/r2/rip_status.ref +++ b/tests/topotests/rip_topo1/r2/rip_status.ref @@ -14,6 +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 + 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/r3/rip_status.ref b/tests/topotests/rip_topo1/r3/rip_status.ref index 040d3c32a1a0..f423e83f24de 100644 --- a/tests/topotests/rip_topo1/r3/rip_status.ref +++ b/tests/topotests/rip_topo1/r3/rip_status.ref @@ -12,5 +12,5 @@ Routing Protocol is "rip" 193.1.2.0/24 Routing Information Sources: Gateway BadPackets BadRoutes Distance Last Update - 193.1.2.1 0 0 120 XX:XX:XX + 193.1.2.1 0 0 120 XX:XX:XX Distance: (default is 120) diff --git a/tests/topotests/rip_topo1/test_rip_topo1.py b/tests/topotests/rip_topo1/test_rip_topo1.py index d23962b77d21..9b0eaf9a1854 100644 --- a/tests/topotests/rip_topo1/test_rip_topo1.py +++ b/tests/topotests/rip_topo1/test_rip_topo1.py @@ -19,6 +19,7 @@ import sys import pytest from time import sleep +import functools sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -258,58 +259,43 @@ def test_zebra_ipv4_routingTable(): global fatal_error net = get_topogen().net + def _verify_ip_route(expected): + # Actual output from router + actual = ( + net["r%s" % i] + .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^R"') + .rstrip() + ) + # Drop timers on end of line + 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) + + return topotest.get_textdiff( + actual, + expected, + title1="actual Zebra IPv4 routing table", + title2="expected Zebra IPv4 routing table", + ) + # 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 print("\n\n** Verifing Zebra IPv4 Routing Table") print("******************************************\n") - failures = 0 for i in range(1, 4): 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) - # Actual output from router - actual = ( - net["r%s" % i] - .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^R"') - .rstrip() - ) - # Drop timers on end of line - 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) - - # Generate Diff - diff = topotest.get_textdiff( - actual, - expected, - title1="actual 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) - ) - 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, - ) + test_func = functools.partial(_verify_ip_route, expected) + success, _ = topotest.run_and_expect(test_func, "", count=30, wait=1) + assert success, "Failed verifying IPv4 routes for r{}".format(i) # Make sure that all daemons are still running for i in range(1, 4): @@ -344,7 +330,6 @@ def test_shutdown_check_stderr(): if __name__ == "__main__": - # 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_allow_ecmp/__init__.py b/tests/topotests/ripng_allow_ecmp/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/ripng_allow_ecmp/r1/frr.conf b/tests/topotests/ripng_allow_ecmp/r1/frr.conf new file mode 100644 index 000000000000..effb5df1ea0d --- /dev/null +++ b/tests/topotests/ripng_allow_ecmp/r1/frr.conf @@ -0,0 +1,9 @@ +! +int r1-eth0 + ipv6 address 2001:db8:1::1/64 +! +router ripng + allow-ecmp + network 2001:db8:1::/64 + timers basic 5 15 10 +exit diff --git a/tests/topotests/ripng_allow_ecmp/r2/frr.conf b/tests/topotests/ripng_allow_ecmp/r2/frr.conf new file mode 100644 index 000000000000..71da101c0d05 --- /dev/null +++ b/tests/topotests/ripng_allow_ecmp/r2/frr.conf @@ -0,0 +1,13 @@ +! +int lo + ipv6 address 2001:db8:2::1/64 +! +int r2-eth0 + ipv6 address 2001:db8:1::2/64 +! +router ripng + redistribute connected + network 2001:db8:1::/64 + network 2001:db8:2::/64 + timers basic 5 15 10 +exit diff --git a/tests/topotests/ripng_allow_ecmp/r3/frr.conf b/tests/topotests/ripng_allow_ecmp/r3/frr.conf new file mode 100644 index 000000000000..fe9594dcd812 --- /dev/null +++ b/tests/topotests/ripng_allow_ecmp/r3/frr.conf @@ -0,0 +1,14 @@ +! +int lo + ipv6 address 2001:db8:2::1/64 +! +int r3-eth0 + ipv6 address 2001:db8:1::3/64 +! +router ripng + redistribute connected + network 2001:db8:1::/64 + network 2001:db8:2::/64 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/ripng_allow_ecmp/r4/frr.conf b/tests/topotests/ripng_allow_ecmp/r4/frr.conf new file mode 100644 index 000000000000..0d3ea0bdeadf --- /dev/null +++ b/tests/topotests/ripng_allow_ecmp/r4/frr.conf @@ -0,0 +1,14 @@ +! +int lo + ipv6 address 2001:db8:2::1/64 +! +int r4-eth0 + ipv6 address 2001:db8:1::4/64 +! +router ripng + redistribute connected + network 2001:db8:1::/64 + network 2001:db8:2::/64 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/ripng_allow_ecmp/r5/frr.conf b/tests/topotests/ripng_allow_ecmp/r5/frr.conf new file mode 100644 index 000000000000..6d6ca565715b --- /dev/null +++ b/tests/topotests/ripng_allow_ecmp/r5/frr.conf @@ -0,0 +1,14 @@ +! +int lo + ipv6 address 2001:db8:2::1/64 +! +int r5-eth0 + ipv6 address 2001:db8:1::5/64 +! +router ripng + redistribute connected + network 2001:db8:1::/64 + network 2001:db8:2::/64 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py b/tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py new file mode 100644 index 000000000000..08bb9999288e --- /dev/null +++ b/tests/topotests/ripng_allow_ecmp/test_ripng_allow_ecmp.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if RIPng `allow-ecmp` command works correctly. +""" + +import os +import sys +import json +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.common_config import step + +pytestmark = [pytest.mark.ripngd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3", "r4", "r5")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ripng_allow_ecmp(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + + def _show_routes(nh_num): + output = json.loads(r1.vtysh_cmd("show ipv6 route json")) + expected = { + "2001:db8:2::/64": [ + { + "internalNextHopNum": nh_num, + "internalNextHopActiveNum": nh_num, + } + ] + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes, 4) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Can't see 2001:db8:2::/64 as multipath (4) in `show ipv6 route`" + + step( + "Configure allow-ecmp 2, ECMP group routes SHOULD have next-hops with the lowest IPs" + ) + r1.vtysh_cmd( + """ + configure terminal + router ripng + allow-ecmp 2 + """ + ) + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=1) + assert ( + result is None + ), "Can't see 2001:db8:2::/64 as multipath (2) in `show ipv6 route`" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ripng_route_map/__init__.py b/tests/topotests/ripng_route_map/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/ripng_route_map/r1/frr.conf b/tests/topotests/ripng_route_map/r1/frr.conf new file mode 100644 index 000000000000..6d0fb46d989f --- /dev/null +++ b/tests/topotests/ripng_route_map/r1/frr.conf @@ -0,0 +1,21 @@ +! +int r1-eth0 + ipv6 address 2001:db8::1/64 +! +router ripng + network 2001:db8::/64 + route-map rmap in r1-eth0 + timers basic 5 15 10 +exit +! +ipv6 access-list r2 seq 5 permit 2001:db8:2::/64 +! +ipv6 prefix-list r3 seq 5 permit 2001:db8:3::/64 +! +route-map rmap permit 10 + match ipv6 address r2 + set metric 12 +route-map rmap permit 20 + match ipv6 address prefix-list r3 + set metric 13 +exit diff --git a/tests/topotests/ripng_route_map/r2/frr.conf b/tests/topotests/ripng_route_map/r2/frr.conf new file mode 100644 index 000000000000..fb9d6702c491 --- /dev/null +++ b/tests/topotests/ripng_route_map/r2/frr.conf @@ -0,0 +1,14 @@ +! +int lo + ipv6 address 2001:db8:2::1/64 +! +int r2-eth0 + ipv6 address 2001:db8::2/64 +! +router ripng + redistribute connected + network 2001:db8::/64 + network 2001:db8:2::1/64 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/ripng_route_map/r3/frr.conf b/tests/topotests/ripng_route_map/r3/frr.conf new file mode 100644 index 000000000000..a6d07789c3bc --- /dev/null +++ b/tests/topotests/ripng_route_map/r3/frr.conf @@ -0,0 +1,14 @@ +! +int lo + ipv6 address 2001:db8:3::1/64 +! +int r3-eth0 + ipv6 address 2001:db8::3/64 +! +router ripng + redistribute connected + network 2001:db8::/64 + network 2001:db8:3::1/64 + timers basic 5 15 10 +exit + diff --git a/tests/topotests/ripng_route_map/test_ripng_route_map.py b/tests/topotests/ripng_route_map/test_ripng_route_map.py new file mode 100644 index 000000000000..e1cc88e9b68d --- /dev/null +++ b/tests/topotests/ripng_route_map/test_ripng_route_map.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# Copyright (c) 2023 by +# Donatas Abraitis <donatas@opensourcerouting.org> +# + +""" +Test if route-map for ripng basic functionality works. +""" + +import os +import sys +import json +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.common_config import step + +pytestmark = [pytest.mark.ripngd] + + +def setup_module(mod): + topodef = {"s1": ("r1", "r2", "r3")} + tgen = Topogen(topodef, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + + for _, (rname, router) in enumerate(router_list.items(), 1): + router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) + + tgen.start_router() + + +def teardown_module(mod): + tgen = get_topogen() + tgen.stop_topology() + + +def test_ripng_route_map(): + tgen = get_topogen() + + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.gears["r1"] + r2 = tgen.gears["r2"] + + def _show_routes(nh_num): + output = json.loads(r1.vtysh_cmd("show ipv6 route ripng json")) + expected = { + "2001:db8:2::/64": [ + { + "metric": 13, + } + ], + "2001:db8:3::/64": [ + { + "metric": 14, + } + ], + } + return topotest.json_cmp(output, expected) + + test_func = functools.partial(_show_routes, 2) + _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) + assert result is None, "Got routes, but metric is not set as expected" + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/ripng_topo1/test_ripng_topo1.py b/tests/topotests/ripng_topo1/test_ripng_topo1.py index ce2f5986d1ea..6bebf6044bc8 100644 --- a/tests/topotests/ripng_topo1/test_ripng_topo1.py +++ b/tests/topotests/ripng_topo1/test_ripng_topo1.py @@ -19,7 +19,7 @@ import sys import pytest from time import sleep - +import functools sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from lib import topotest @@ -273,6 +273,27 @@ def test_zebra_ipv6_routingTable(): global fatal_error net = get_topogen().net + def _verify_ip_route(expected): + # Actual output from router + 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 + 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) + + return topotest.get_textdiff( + actual, + expected, + title1="actual Zebra IPv6 routing table", + title2="expected Zebra IPv6 routing table", + ) + # Skip if previous fatal error condition is raised if fatal_error != "": pytest.skip(fatal_error) @@ -291,42 +312,9 @@ def test_zebra_ipv6_routingTable(): # 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 "^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 - 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) - - # Generate Diff - diff = topotest.get_textdiff( - actual, - expected, - title1="actual 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) - ) - 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, - ) + test_func = functools.partial(_verify_ip_route, expected) + success, _ = topotest.run_and_expect(test_func, "", count=30, wait=1) + assert success, "Failed verifying IPv6 routes for r{}".format(i) # Make sure that all daemons are running for i in range(1, 4): @@ -386,7 +374,6 @@ def test_shutdown_check_memleak(): if __name__ == "__main__": - # 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/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py index 63c0a86897fb..46777d7b4b2c 100644 --- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py @@ -207,7 +207,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name) + ), "Testcase {} : Failed Error: Routes are missing in RIB".format(tc_name) step("Configure IBGP IPv4 peering between R2 and R3 router.") step("Configure redistribute static in BGP on R2 router") @@ -262,7 +262,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format( + ), "Testcase {} : Failed Expected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -272,7 +272,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure the static route with nexthop N1") @@ -293,7 +293,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): tc_name, result ) - step("Remove the static route configured with nexthop N2 from" "running config") + step("Remove the static route configured with nexthop N2 from running config") input_dict_4 = { "r2": { @@ -329,7 +329,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -339,7 +339,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure the static route with nexthop N2") input_dict_4 = { @@ -372,7 +372,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = next_hop_ip["nh1"][addr_type] result = verify_rib( @@ -386,7 +386,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -396,7 +396,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Route is still present in RIB".format( tc_name ) @@ -411,7 +411,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \n Expected: Routes should not present in RIB \nError: Route is still present in RIB".format( tc_name ) @@ -422,20 +422,20 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Route is still present in RIB".format( tc_name ) @@ -454,7 +454,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Shut nexthop interface N2") intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] @@ -478,7 +478,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -490,20 +490,20 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Route is still present in RIB".format( tc_name ) @@ -522,20 +522,20 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Route is still present in RIB".format( tc_name ) @@ -555,13 +555,13 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -570,7 +570,7 @@ def test_static_route_2nh_p0_tc_1_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Route is still present in RIB".format( tc_name ) @@ -646,7 +646,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -674,7 +674,9 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are not active in RIB".format( + tc_name + ) step("Configure IBGP IPv4 peering between R2 and R3 router.") step("Explicit route is added in R3 for R2 nexthop rechability") @@ -715,7 +717,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): tc_name, result ) - step("Remove the static route configured with nexthop N1 from" "running config") + step("Remove the static route configured with nexthop N1 from running config") rt1_nh1 = { "r2": { "static_routes": [ @@ -765,7 +767,9 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are missing in RIB".format( + tc_name + ) rte2_nh2 = { "r2": { @@ -784,7 +788,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are not active in RIB".format(tc_name) step("Configure the static route with nexthop N1") rte1_nh1 = { @@ -804,7 +808,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): tc_name, result ) - step("Remove the static route configured with nexthop N2 from" "running config") + step("Remove the static route configured with nexthop N2 from running config") rte2_nh2 = { "r2": { "static_routes": [ @@ -839,7 +843,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -849,7 +853,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure the static route with nexthop N2") rte2_nh2 = { @@ -889,7 +893,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -899,7 +903,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("No shut the nexthop interface N1") shutdown_bringup_interface(tgen, dut, intf, True) @@ -915,7 +919,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Shut nexthop interface N2") intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] @@ -939,7 +943,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nExpected: Routes should not present in RIB \nError: Routes are still present in RIB".format( tc_name ) @@ -949,7 +953,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("No shut nexthop interface N2") shutdown_bringup_interface(tgen, dut, intf, True) @@ -977,7 +981,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -1005,7 +1009,9 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nExpected: Routes should not active in RIB \nError: Routes are not active in RIB".format( + tc_name + ) dut = "r3" protocol = "bgp" @@ -1022,7 +1028,9 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nExpected: Routes should not be active in RIB \nError: Routes are not active in RIB".format( + tc_name + ) dut = "r2" step("Reload the FRR router") @@ -1054,14 +1062,14 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) dut = "r3" protocol = "bgp" result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -1089,14 +1097,16 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nExpected: Routes should not be active in RIB \nError: Routes are not active in RIB".format( + tc_name + ) dut = "r3" protocol = "bgp" result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are not active in RIB".format(tc_name) result = verify_rib( tgen, @@ -1110,7 +1120,9 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nExpected: Routes should not be active in RIB \nError: Routes are not active in RIB".format( + tc_name + ) write_test_footer(tc_name) @@ -1182,7 +1194,7 @@ def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -1191,7 +1203,7 @@ def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -1246,9 +1258,7 @@ def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in BGP RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Route is missing in BGP RIB".format(tc_name) protocol = "bgp" result = verify_rib( @@ -1256,9 +1266,9 @@ def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) - step("Remove the static route on R3 configured with active" "interface") + step("Remove the static route on R3 configured with active interface") for addr_type in ADDR_TYPES: input_dict_4 = { "r3": { @@ -1293,9 +1303,7 @@ def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in BGP RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Route is missing in BGP RIB".format(tc_name) protocol = "bgp" result = verify_rib( @@ -1303,7 +1311,7 @@ def test_same_rte_from_bgp_static_p0_tc5_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) write_test_footer(tc_name) diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py index 530c49f6bf43..e01351506d9e 100644 --- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py @@ -281,7 +281,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: @@ -306,7 +306,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step( "Remove the static route configured with nexthop N1 to N8, one" @@ -353,7 +353,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed\nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed\nError: routes are still present in RIB".format( tc_name ) @@ -382,9 +382,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed\nError: Routes are" " missing in RIB".format( - tc_name - ) + ), "Testcase {} : Failed\nError: Routes are missing in RIB".format(tc_name) protocol = "static" step("Random shut of the nexthop interfaces") @@ -392,7 +390,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): # Shutdown interface dut = "r2" step( - " interface which is about to be shut no shut between r1 and r2 is " "%s", + " interface which is about to be shut no shut between r1 and r2 is %s", topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"], ) intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"] @@ -430,7 +428,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Remove random static route with all the nexthop") dut = "r2" @@ -469,7 +467,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) for addr_type in ADDR_TYPES: input_dict_4 = { @@ -523,7 +521,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Remove the redistribute static knob") for addr_type in ADDR_TYPES: @@ -558,7 +556,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -642,7 +640,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): @@ -660,7 +658,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step( "Remove the static route configured with nexthop N1 to N8, one" @@ -705,9 +703,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(tc_name) step("Configure the static route with nexthop N1 to N8, one by one") @@ -755,7 +751,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) @@ -772,7 +768,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Random shut of the nexthop interfaces") randnum = random.randint(0, 7) @@ -801,7 +797,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -815,7 +811,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) dut = "r2" protocol = "static" @@ -824,7 +820,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {}: Failed \n Error: Routes are missing in RIB".format(tc_name) protocol = "bgp" dut = "r3" @@ -833,7 +829,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {}: Failed \n Error: Routes are missing in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -846,7 +842,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -945,7 +941,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): @@ -962,7 +958,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step( "Remove the static route configured with nexthop N1 to N8, one" @@ -1007,9 +1003,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(tc_name) step("Configure the static route with nexthop N1 to N8, one by one") @@ -1057,7 +1051,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) @@ -1073,7 +1067,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Random shut of the nexthop interfaces") randnum = random.randint(0, 7) @@ -1102,7 +1096,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -1116,7 +1110,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) dut = "r2" protocol = "static" @@ -1132,7 +1126,7 @@ def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -1231,7 +1225,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Verify that highest AD nexthop are inactive") nh = [] @@ -1250,7 +1244,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: @@ -1314,7 +1308,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -1345,15 +1339,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): " value and all the nexthop populated in RIB and FIB again" ) for addr_type in ADDR_TYPES: - input_dict_4 = { - "r2": { - "static_routes": [ - { - "network": PREFIX1[addr_type], - } - ] - } - } + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} nh = NEXT_HOP_IP["nh1"][addr_type] result = verify_rib( tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True @@ -1390,7 +1376,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -1404,7 +1390,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) step("Remove random static route with all the nexthop") for addr_type in ADDR_TYPES: @@ -1494,9 +1480,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Route " " is missing in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -1592,7 +1576,7 @@ def test_static_route_delete_p0_tc11_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Verify that highest AD nexthop are inactive") nh = [] @@ -1610,7 +1594,7 @@ def test_static_route_delete_p0_tc11_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: @@ -1664,7 +1648,7 @@ def test_static_route_delete_p0_tc11_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -1735,7 +1719,7 @@ def test_static_route_delete_p0_tc11_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still active in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still active in RIB".format( tc_name ) diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py index 358047607ab7..9d35b7dcd3c8 100644 --- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py @@ -262,7 +262,7 @@ def test_staticroute_with_ecmp_p0_tc3_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: input_dict_2 = { @@ -317,11 +317,11 @@ def test_staticroute_with_ecmp_p0_tc3_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) - step("Configure the static route with nexthop N1 to N8, one by" "one") + step("Configure the static route with nexthop N1 to N8, one by one") for addr_type in ADDR_TYPES: # add static routes @@ -348,7 +348,7 @@ def test_staticroute_with_ecmp_p0_tc3_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Random shut of the nexthop interfaces") randnum = random.randint(0, 7) @@ -377,7 +377,7 @@ def test_staticroute_with_ecmp_p0_tc3_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -391,7 +391,7 @@ def test_staticroute_with_ecmp_p0_tc3_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -403,7 +403,7 @@ def test_staticroute_with_ecmp_p0_tc3_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -592,11 +592,9 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(tc_name) - step("Configure the static route with nexthop N1 to N8, one by" "one") + step("Configure the static route with nexthop N1 to N8, one by one") for addr_type in ADDR_TYPES: # add static routes for nhp in range(1, 9): @@ -685,7 +683,7 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -699,7 +697,7 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -812,7 +810,7 @@ def test_bgp_local_nexthop_p1_tc14_ebgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB of R2".format( + ), "Testcase {} : Failed \nError: routes are missing in RIB of R2".format( tc_name ) @@ -820,13 +818,13 @@ def test_bgp_local_nexthop_p1_tc14_ebgp(request): dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False) assert result is not True, ( - "Testcase {} : Failed \nError: Routes is" + "Testcase {} : Failed \nError: routes are" " still present in BGP RIB of R2".format(tc_name) ) result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False) assert result is not True, ( - "Testcase {} : Failed \nError: Routes is" + "Testcase {} : Failed \nError: routes are" " still present in RIB of R2".format(tc_name) ) @@ -890,7 +888,7 @@ def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) input_dict_nh = { "r1": { @@ -904,7 +902,7 @@ def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request): result = verify_ip_nht(tgen, input_dict_nh) assert ( result is True - ), "Testcase {} : Failed \nError: Nexthop is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Nexthop is missing in RIB".format(tc_name) step( "Shut / no shut IPv4 and IPv6 static next hop interface from" @@ -928,7 +926,7 @@ def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) shutdown_bringup_interface(tgen, dut, intf, True) @@ -974,9 +972,7 @@ def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes still present in RIB".format(tc_name) write_test_footer(tc_name) @@ -1002,7 +998,7 @@ def test_static_route_with_tag_p0_tc_13_ebgp(request): "Configure 2 IPv4 static route (S1 and S2) in R2 with same" "next hop N1 28.1.1.2" ) - step("Configure static route S1 with tag 1 and static route S2 with" "tag2") + step("Configure static route S1 with tag 1 and static route S2 with tag2") step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1") step("S2= ip route 20.1.1.1/24 28.1.1.2 tag 2") step("Enable redistribute static in BGP with route-map") @@ -1041,7 +1037,7 @@ def test_static_route_with_tag_p0_tc_13_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure route-map on R2 with allow tag1 and deny tag2") @@ -1229,7 +1225,7 @@ def test_static_route_with_tag_p0_tc_13_ebgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("shut/no shut of tag1 and tag2 nexthop") diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py index c1099bd97ead..2efc0fdf1b19 100644 --- a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py +++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py @@ -204,12 +204,12 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ebgp(request): ) clear_bgp(tgen, addr_type, "r2") - step(" All BGP nbrs are down as authentication is mismatch on both" " the sides") + step(" All BGP nbrs are down as authentication is mismatch on both the sides") bgp_convergence = verify_bgp_convergence(tgen, topo, expected=False) assert ( bgp_convergence is not True - ), "Testcase {} : " "Failed \n BGP nbrs must be down. Error: {}".format( + ), "Testcase {} : Failed \n BGP nbrs must be down. Error: {}".format( tc_name, bgp_convergence ) @@ -256,7 +256,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ebgp(request): step("All BGP nbrs are up as authentication is matched now") bgp_convergence = verify_bgp_convergence(tgen, topo) - assert bgp_convergence is True, "Testcase {} : Failed \n " "Error: {}".format( + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( tc_name, bgp_convergence ) @@ -306,7 +306,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ebgp(request): "show ip prefix list" ) result = verify_prefix_lists(tgen, input_dict_2) - assert result is not True, "Testcase {} : Failed \n" " Error: {}".format( + assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) @@ -409,7 +409,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ebgp(request): tc_name, result ) - step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from " "frr r1") + step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from frr r1") # Configure prefix list to bgp neighbor input_dict_4 = { "r2": { @@ -923,7 +923,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ebgp(request): ) assert ( result4 is not True - ), "Testcase {} : Failed \n" "routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n routes are still present \n Error: {}".format( tc_name, result4 ) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py index 176ce7e48b91..abae75e76e2f 100644 --- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py @@ -206,7 +206,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("Configure IBGP IPv4 peering between R2 and R3 router.") step("Configure redistribute static in BGP on R2 router") @@ -261,7 +261,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -271,7 +271,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("Configure the static route with nexthop N1") @@ -328,7 +328,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -338,7 +338,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("Configure the static route with nexthop N2") input_dict_4 = { @@ -371,7 +371,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) nh = NEXT_HOP_IP["nh1"][addr_type] result = verify_rib( @@ -385,7 +385,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -395,7 +395,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -410,7 +410,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -421,20 +421,20 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -453,7 +453,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("Shut nexthop interface N2") intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] @@ -477,7 +477,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -489,20 +489,20 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -521,20 +521,20 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name) result = verify_rib( tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -554,13 +554,13 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -569,7 +569,7 @@ def test_static_route_2nh_p0_tc_1_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Route is still present in RIB".format( tc_name ) @@ -645,7 +645,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -673,7 +673,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) step("Configure IBGP IPv4 peering between R2 and R3 router.") step("Explicit route is added in R3 for R2 nexthop rechability") @@ -764,7 +764,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -783,7 +783,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) step("Configure the static route with nexthop N1") rte1_nh1 = { @@ -838,7 +838,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -848,7 +848,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("Configure the static route with nexthop N2") rte2_nh2 = { @@ -888,7 +888,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -898,7 +898,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("No shut the nexthop interface N1") shutdown_bringup_interface(tgen, dut, intf, True) @@ -914,7 +914,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("Shut nexthop interface N2") intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"] @@ -938,7 +938,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed \nError: routes are still present in RIB".format( tc_name ) @@ -948,7 +948,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) step("No shut nexthop interface N2") shutdown_bringup_interface(tgen, dut, intf, True) @@ -976,7 +976,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -1004,7 +1004,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) dut = "r3" protocol = "bgp" @@ -1021,7 +1021,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) dut = "r2" step("Reload the FRR router") @@ -1053,14 +1053,14 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) dut = "r3" protocol = "bgp" result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are missing in RIB".format(tc_name) rte2_nh2 = { "r2": { @@ -1088,14 +1088,14 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) dut = "r3" protocol = "bgp" result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) result = verify_rib( tgen, @@ -1109,7 +1109,7 @@ def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: routes are not active in RIB".format(tc_name) write_test_footer(tc_name) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py index 20c68dc25f38..820a736ad7c1 100644 --- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py @@ -283,7 +283,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: @@ -308,7 +308,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step( "Remove the static route configured with nexthop N1 to N8, one" @@ -355,7 +355,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed\nError: Routes is" " still present in RIB".format( + ), "Testcase {} : Failed\nError: routes are still present in RIB".format( tc_name ) @@ -384,9 +384,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed\nError: Routes are" " missing in RIB".format( - tc_name - ) + ), "Testcase {} : Failed\nError: Routes are missing in RIB".format(tc_name) protocol = "static" step("Random shut of the nexthop interfaces") @@ -432,7 +430,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Remove random static route with all the nexthop") dut = "r2" @@ -471,7 +469,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) for addr_type in ADDR_TYPES: input_dict_4 = { @@ -525,7 +523,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Remove the redistribute static knob") for addr_type in ADDR_TYPES: @@ -560,7 +558,7 @@ def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -644,7 +642,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): @@ -662,7 +660,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are present in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are present in RIB".format(tc_name) step( "Remove the static route configured with nexthop N1 to N8, one" @@ -707,9 +705,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(tc_name) step("Configure the static route with nexthop N1 to N8, one by one") @@ -757,7 +753,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) @@ -774,7 +770,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Random shut of the nexthop interfaces") randnum = random.randint(0, 7) @@ -803,7 +799,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -817,7 +813,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) protocol = "bgp" # this is next hop reachability route in r3 as we are using ibgp @@ -844,7 +840,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) protocol = "static" dut = "r2" @@ -1051,7 +1047,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): @@ -1068,7 +1064,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step( "Remove the static route configured with nexthop N1 to N8, one" @@ -1113,9 +1109,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(tc_name) step("Configure the static route with nexthop N1 to N8, one by one") @@ -1163,7 +1157,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) nh = [] for nhp in range(2, 9): nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type]) @@ -1179,7 +1173,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Random shut of the nexthop interfaces") randnum = random.randint(0, 7) @@ -1208,7 +1202,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -1222,7 +1216,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) dut = "r2" protocol = "bgp" @@ -1251,7 +1245,7 @@ def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) protocol = "static" dut = "r2" @@ -1458,7 +1452,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Verify that highest AD nexthop are inactive") nh = [] @@ -1477,7 +1471,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: @@ -1541,7 +1535,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -1572,15 +1566,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): " value and all the nexthop populated in RIB and FIB again" ) for addr_type in ADDR_TYPES: - input_dict_4 = { - "r2": { - "static_routes": [ - { - "network": PREFIX1[addr_type], - } - ] - } - } + input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}} nh = NEXT_HOP_IP["nh1"][addr_type] result = verify_rib( tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True @@ -1617,7 +1603,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -1631,7 +1617,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) step("Remove random static route with all the nexthop") for addr_type in ADDR_TYPES: @@ -1767,7 +1753,7 @@ def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -1865,7 +1851,7 @@ def test_static_route_delete_p0_tc11_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Verify that highest AD nexthop are inactive") nh = [] @@ -1883,7 +1869,7 @@ def test_static_route_delete_p0_tc11_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: @@ -1937,7 +1923,7 @@ def test_static_route_delete_p0_tc11_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) @@ -2008,7 +1994,7 @@ def test_static_route_delete_p0_tc11_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still active in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still active in RIB".format( tc_name ) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py index 7f3592731113..1ad963f314e1 100644 --- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py @@ -261,7 +261,7 @@ def test_staticroute_with_ecmp_p0_tc3_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Configure redistribute static in BGP on R2 router") for addr_type in ADDR_TYPES: input_dict_2 = { @@ -316,11 +316,11 @@ def test_staticroute_with_ecmp_p0_tc3_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format( tc_name ) - step("Configure the static route with nexthop N1 to N8, one by" "one") + step("Configure the static route with nexthop N1 to N8, one by one") for addr_type in ADDR_TYPES: # add static routes @@ -347,7 +347,7 @@ def test_staticroute_with_ecmp_p0_tc3_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) step("Random shut of the nexthop interfaces") randnum = random.randint(0, 7) @@ -376,7 +376,7 @@ def test_staticroute_with_ecmp_p0_tc3_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -390,7 +390,7 @@ def test_staticroute_with_ecmp_p0_tc3_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -402,7 +402,7 @@ def test_staticroute_with_ecmp_p0_tc3_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name) + ), "Testcase {} : Failed \nError: Routes are missing in RIB".format(tc_name) write_test_footer(tc_name) @@ -591,11 +591,9 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format( - tc_name - ) + ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(tc_name) - step("Configure the static route with nexthop N1 to N8, one by" "one") + step("Configure the static route with nexthop N1 to N8, one by one") for addr_type in ADDR_TYPES: # add static routes for nhp in range(1, 9): @@ -684,7 +682,7 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request): ) assert ( result is not True - ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format( + ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format( tc_name ) @@ -698,7 +696,7 @@ def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request): ) assert ( result is True - ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name) + ), "Testcase {} : Failed \n Error: Routes are missing in RIB".format(tc_name) step("Reload the FRR router") # stop/start -> restart FRR router and verify @@ -852,7 +850,7 @@ def test_bgp_local_nexthop_p1_tc14_ibgp(request): result = verify_rib(tgen, addr_type, dut, input_dict_4) assert ( result is True - ), "Testcase {} : Failed \nError: Routes is" " missing in RIB of R2".format( + ), "Testcase {} : Failed \nError: routes are missing in RIB of R2".format( tc_name ) @@ -860,13 +858,13 @@ def test_bgp_local_nexthop_p1_tc14_ibgp(request): dut = "r3" result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False) assert result is not True, ( - "Testcase {} : Failed \nError: Routes is" + "Testcase {} : Failed \nError: routes are" " still present in BGP RIB of R2".format(tc_name) ) result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False) assert result is not True, ( - "Testcase {} : Failed \nError: Routes is" + "Testcase {} : Failed \nError: routes are" " still present in RIB of R2".format(tc_name) ) diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py index e50362b5714e..03782409593e 100644 --- a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py +++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py @@ -200,12 +200,12 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): ) clear_bgp(tgen, addr_type, "r2") - step(" All BGP nbrs are down as authentication is mismatch on both" " the sides") + step(" All BGP nbrs are down as authentication is mismatch on both the sides") bgp_convergence = verify_bgp_convergence(tgen, topo, expected=False) assert ( bgp_convergence is not True - ), "Testcase {} : " "Failed \n BGP nbrs must be down. Error: {}".format( + ), "Testcase {} : Failed \n BGP nbrs must be down. Error: {}".format( tc_name, bgp_convergence ) @@ -252,7 +252,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): step("All BGP nbrs are up as authentication is matched now") bgp_convergence = verify_bgp_convergence(tgen, topo) - assert bgp_convergence is True, "Testcase {} : Failed \n " "Error: {}".format( + assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format( tc_name, bgp_convergence ) @@ -302,7 +302,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): "show ip prefix list" ) result = verify_prefix_lists(tgen, input_dict_2) - assert result is not True, "Testcase {} : Failed \n" " Error: {}".format( + assert result is not True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) @@ -405,7 +405,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): tc_name, result ) - step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from " "frr r1") + step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from frr r1") # Configure prefix list to bgp neighbor input_dict_4 = { "r2": { @@ -919,7 +919,7 @@ def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request): ) assert ( result4 is not True - ), "Testcase {} : Failed \n" "routes are still present \n Error: {}".format( + ), "Testcase {} : Failed \n routes are still present \n Error: {}".format( tc_name, result4 ) diff --git a/tests/topotests/static_simple/r1/mgmtd.conf b/tests/topotests/static_simple/r1/mgmtd.conf new file mode 100644 index 000000000000..0f9f97ca1a33 --- /dev/null +++ b/tests/topotests/static_simple/r1/mgmtd.conf @@ -0,0 +1 @@ +log timestamp precision 3 diff --git a/tests/topotests/static_simple/r1/staticd.conf b/tests/topotests/static_simple/r1/staticd.conf new file mode 100644 index 000000000000..0f9f97ca1a33 --- /dev/null +++ b/tests/topotests/static_simple/r1/staticd.conf @@ -0,0 +1 @@ +log timestamp precision 3 diff --git a/tests/topotests/static_simple/r1/zebra.conf b/tests/topotests/static_simple/r1/zebra.conf new file mode 100644 index 000000000000..ec827617ab8d --- /dev/null +++ b/tests/topotests/static_simple/r1/zebra.conf @@ -0,0 +1,11 @@ +log timestamp precision 3 + +interface r1-eth0 + ip address 101.0.0.1/24 + ipv6 address 2101::1/64 +exit + +interface r1-eth1 vrf red + ip address 102.0.0.1/24 + ipv6 address 2102::1/64 +exit diff --git a/tests/topotests/static_simple/test_static_simple.py b/tests/topotests/static_simple/test_static_simple.py new file mode 100644 index 000000000000..817336ac21a6 --- /dev/null +++ b/tests/topotests/static_simple/test_static_simple.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2021, LabN Consulting, L.L.C. +# Copyright (c) 2019-2020 by +# Donatas Abraitis <donatas.abraitis@gmail.com> +# +""" +Test static route functionality +""" + +import datetime +import ipaddress +import math +import os +import sys +import re + +import pytest +from lib.topogen import TopoRouter, Topogen, get_topogen +from lib.topolog import logger +from lib.common_config import retry, step + +pytestmark = [pytest.mark.staticd] + + +@pytest.fixture(scope="module") +def tgen(request): + "Setup/Teardown the environment and provide tgen argument to tests" + + topodef = {"s1": ("r1",), "s2": ("r1",)} + + tgen = Topogen(topodef, request.module.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.items(): + # Setup VRF red + router.net.add_l3vrf("red", 10) + router.net.add_loop("lo-red") + router.net.attach_iface_to_l3vrf("lo-red", "red") + router.net.attach_iface_to_l3vrf(rname + "-eth1", "red") + # and select daemons to run + router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf") + router.load_config(TopoRouter.RD_MGMTD) + router.load_config(TopoRouter.RD_STATIC) + + tgen.start_router() + yield tgen + tgen.stop_topology() + + +def get_ip_networks(super_prefix, count): + count_log2 = math.log(count, 2) + if count_log2 != int(count_log2): + count_log2 = int(count_log2) + 1 + else: + count_log2 = int(count_log2) + network = ipaddress.ip_network(super_prefix) + return tuple(network.subnets(count_log2))[0:count] + + +def enable_debug(router): + router.vtysh_cmd("debug northbound callbacks configuration") + + +def disable_debug(router): + router.vtysh_cmd("no debug northbound callbacks configuration") + + +@retry(retry_timeout=3, initial_wait=0.1) +def check_kernel(r1, super_prefix, count, add, is_blackhole, vrf, matchvia): + network = ipaddress.ip_network(super_prefix) + vrfstr = f" vrf {vrf}" if vrf else "" + if network.version == 6: + kernel = r1.run(f"ip -6 route show{vrfstr}") + else: + kernel = r1.run(f"ip -4 route show{vrfstr}") + + logger.debug("checking kernel routing table%s:\n%s", vrfstr, kernel) + for i, net in enumerate(get_ip_networks(super_prefix, count)): + if not add: + assert str(net) not in kernel + continue + + if is_blackhole: + route = f"blackhole {str(net)} proto (static|196) metric 20" + else: + route = ( + f"{str(net)}(?: nhid [0-9]+)? {matchvia} " + "proto (static|196) metric 20" + ) + assert re.search(route, kernel), f"Failed to find \n'{route}'\n in \n'{kernel}'" + + +def do_config( + r1, + count, + add=True, + do_ipv6=False, + via=None, + vrf=None, + use_cli=False, +): + optype = "adding" if add else "removing" + iptype = "IPv6" if do_ipv6 else "IPv4" + + # + # Set the route details + # + + if vrf: + super_prefix = "2002::/48" if do_ipv6 else "20.0.0.0/8" + else: + super_prefix = "2001::/48" if do_ipv6 else "10.0.0.0/8" + + matchtype = "" + matchvia = "" + if via == "blackhole": + pass + elif via: + matchvia = f"dev {via}" + else: + if vrf: + via = "2102::2" if do_ipv6 else "102.0.0.2" + matchvia = f"via {via} dev r1-eth1" + else: + via = "2101::2" if do_ipv6 else "101.0.0.2" + matchvia = f"via {via} dev r1-eth0" + + vrfdbg = " in vrf {}".format(vrf) if vrf else "" + logger.debug("{} {} static {} routes{}".format(optype, count, iptype, vrfdbg)) + + # + # Generate config file in a retrievable place + # + + config_file = os.path.join( + r1.logdir, r1.name, "{}-routes-{}.conf".format(iptype.lower(), optype) + ) + with open(config_file, "w") as f: + if use_cli: + f.write("configure terminal\n") + if vrf: + f.write("vrf {}\n".format(vrf)) + + for i, net in enumerate(get_ip_networks(super_prefix, count)): + if add: + f.write("ip route {} {}\n".format(net, via)) + else: + f.write("no ip route {} {}\n".format(net, via)) + + # + # Load config file. + # + + if use_cli: + load_command = 'vtysh < "{}"'.format(config_file) + else: + load_command = 'vtysh -f "{}"'.format(config_file) + tstamp = datetime.datetime.now() + output = r1.cmd_raises(load_command) + delta = (datetime.datetime.now() - tstamp).total_seconds() + + # + # Verify the results are in the kernel + # + check_kernel(r1, super_prefix, count, add, via == "blackhole", vrf, matchvia) + + optyped = "added" if add else "removed" + logger.debug( + "{} {} {} static routes under {}{} in {}s".format( + optyped, count, iptype.lower(), super_prefix, vrfdbg, delta + ) + ) + + +def guts(tgen, vrf, use_cli): + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + r1 = tgen.routers()["r1"] + + step("add via gateway", reset=True) + do_config(r1, 1, True, False, vrf=vrf, use_cli=use_cli) + step("remove via gateway") + do_config(r1, 1, False, False, vrf=vrf, use_cli=use_cli) + + via = f"lo-{vrf}" if vrf else "lo" + step("add via loopback") + do_config(r1, 1, True, False, via=via, vrf=vrf, use_cli=use_cli) + step("remove via loopback") + do_config(r1, 1, False, False, via=via, vrf=vrf, use_cli=use_cli) + + step("add via blackhole") + do_config(r1, 1, True, False, via="blackhole", vrf=vrf, use_cli=use_cli) + step("remove via blackhole") + do_config(r1, 1, False, False, via="blackhole", vrf=vrf, use_cli=use_cli) + + +def test_static_cli(tgen): + guts(tgen, "", True) + + +def test_static_file(tgen): + guts(tgen, "", False) + + +def test_static_vrf_cli(tgen): + guts(tgen, "red", True) + + +def test_static_vrf_file(tgen): + guts(tgen, "red", False) diff --git a/tests/topotests/zebra_rib/r1/v4_route_1_vrf_before.json b/tests/topotests/zebra_rib/r1/v4_route_1_vrf_before.json new file mode 100644 index 000000000000..997d12f68d9c --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_1_vrf_before.json @@ -0,0 +1,27 @@ +{ + "3.5.1.0/24":[ + { + "prefix":"3.5.1.0/24", + "prefixLen":24, + "protocol":"kernel", + "vrfId":0, + "vrfName":"default", + "selected":true, + "destSelected":true, + "distance":0, + "metric":0, + "installed":true, + "table":254, + "nexthops":[ + { + "flags":3, + "fib":true, + "ip":"192.168.210.1", + "afi":"ipv4", + "interfaceName":"r1-eth0", + "active":true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 6137471ea690..05036fa7ad47 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -51,7 +51,8 @@ def config_macvlan(tgen, r_str, device, macvlan): def setup_module(mod): "Sets up the pytest environment" - topodef = {"s1": ("r1", "r1", "r1", "r1", "r1", "r1", "r1", "r1")} + # 8 links to 8 switches on r1 + topodef = {"s{}".format(x): ("r1",) for x in range(1, 9)} tgen = Topogen(topodef, mod.__name__) tgen.start_topology() @@ -76,6 +77,41 @@ def teardown_module(mod): tgen.stop_topology() +def test_zebra_kernel_route_vrf(): + "Test kernel routes should be removed after interface changes vrf" + logger.info("Test kernel routes should be removed after interface changes vrf") + vrf = "RED" + tgen = get_topogen() + r1 = tgen.gears["r1"] + + # Add kernel routes, the interface is initially in default vrf + r1.run("ip route add 3.5.1.0/24 via 192.168.210.1 dev r1-eth0") + json_file = "{}/r1/v4_route_1_vrf_before.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route 3.5.1.0/24 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=5, wait=1) + assert result is None, '"r1" JSON output mismatches' + + # Change the interface's vrf + r1.run("ip link add {} type vrf table 1".format(vrf)) + r1.run("ip link set {} up".format(vrf)) + r1.run("ip link set dev r1-eth0 master {}".format(vrf)) + + expected = "{}" + test_func = partial( + topotest.router_output_cmp, r1, "show ip route 3.5.1.0/24 json", expected + ) + result, diff = topotest.run_and_expect(test_func, "", count=5, wait=1) + assertmsg = "{} should not have the kernel route.\n{}".format('"r1"', diff) + assert result, assertmsg + + # Clean up + r1.run("ip link set dev r1-eth0 nomaster") + r1.run("ip link del dev {}".format(vrf)) + + 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") @@ -219,7 +255,7 @@ def check_static_map_correct_runs(): ) ok, result = topotest.run_and_expect( - check_static_map_correct_runs, "", count=5, wait=1 + check_static_map_correct_runs, "", count=10, wait=1 ) assert ok, result @@ -239,7 +275,7 @@ def check_sharp_map_correct_runs(): ) ok, result = topotest.run_and_expect( - check_sharp_map_correct_runs, "", count=5, wait=1 + check_sharp_map_correct_runs, "", count=10, wait=1 ) assert ok, result diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index b5892e8da512..d007c1d32580 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -1,9 +1,11 @@ #!/usr/bin/env perl # SPDX-License-Identifier: GPL-2.0-or-later +# # (c) 2001, Dave Jones. (the file handling bit) # (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit) # (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite) # (c) 2008-2010 Andy Whitcroft <apw@canonical.com> +# (c) 2010-2018 Joe Perches <joe@perches.com> use strict; use warnings; @@ -11,6 +13,7 @@ use File::Basename; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); +use Encode qw(decode encode); my $P = $0; my $D = dirname(abs_path($P)); @@ -19,7 +22,12 @@ use Getopt::Long qw(:config no_auto_abbrev); +my $frr = 1; + my $quiet = 0; +my $verbose = 0; +my %verbose_messages = (); +my %verbose_emitted = (); my $tree = 1; my $chk_signoff = 1; my $chk_patch = 1; @@ -40,6 +48,8 @@ my $fix = 0; my $fix_inplace = 0; my $root; +my $gitroot = $ENV{'GIT_DIR'}; +$gitroot = ".git" if !defined($gitroot); my %debug; my %camelcase = (); my %use_type = (); @@ -48,16 +58,24 @@ my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; -my $max_line_length = 80; +my $max_line_length = 100; my $ignore_perl_version = 0; my $minimum_perl_version = 5.10.0; my $min_conf_desc_length = 4; my $spelling_file = "$D/spelling.txt"; my $codespell = 0; my $codespellfile = "/usr/share/codespell/dictionary.txt"; -my $typedefsfile = ""; +my $user_codespellfile = ""; +my $conststructsfile = "$D/const_structs.checkpatch"; +my $docsfile = "$D/../doc/developer/checkpatch.rst"; +my $typedefsfile; my $color = "auto"; -my $allow_c99_comments = 1; +my $allow_c99_comments = 0; # Not in FRR. +#my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE +# git output parsing needs US English output, so first set backtick child process LANGUAGE +my $git_command ='export LANGUAGE=en_US.UTF-8; git'; +my $tabsize = 8; +my ${CONFIG_} = "CONFIG_"; sub help { my ($exitcode) = @_; @@ -68,6 +86,7 @@ sub help { Options: -q, --quiet quiet + -v, --verbose verbose mode --no-tree run without a kernel tree --no-signoff do not check for 'Signed-off-by' line --patch treat FILE as patchfile (default) @@ -90,8 +109,11 @@ sub help { --types TYPE(,TYPE2...) show only these comma separated message types --ignore TYPE(,TYPE2...) ignore various comma separated message types --show-types show the specific message type in the output - --max-line-length=n set the maximum line length, if exceeded, warn + --max-line-length=n set the maximum line length, (default $max_line_length) + if exceeded, warn on patches + requires --strict for use with --file --min-conf-desc-length=n set the min description length, if shorter, warn + --tab-size=n set the number of spaces for tab (default $tabsize) --root=PATH PATH to the kernel tree root --no-summary suppress the per-file summary --mailback only produce a report in case of warnings/errors @@ -112,11 +134,13 @@ sub help { --ignore-perl-version override checking of perl version. expect runtime errors. --codespell Use the codespell dictionary for spelling/typos - (default:/usr/share/codespell/dictionary.txt) + (default:$codespellfile) --codespellfile Use this codespell dictionary --typedefsfile Read additional types from this file --color[=WHEN] Use colors 'always', 'never', or only when output is a terminal ('auto'). Default is 'auto'. + --kconfig-prefix=WORD use WORD as a prefix for Kconfig symbols (default + ${CONFIG_}) -h, --help, --version display this help and exit When FILE is - read standard input. @@ -143,15 +167,51 @@ sub list_types { my $text = <$script>; close($script); - my @types = (); + my %types = (); # Also catch when type or level is passed through a variable - for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { - push (@types, $_); + while ($text =~ /(?:(\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) { + if (defined($1)) { + if (exists($types{$2})) { + $types{$2} .= ",$1" if ($types{$2} ne $1); + } else { + $types{$2} = $1; + } + } else { + $types{$2} = "UNDETERMINED"; + } } - @types = sort(uniq(@types)); + print("#\tMessage type\n\n"); - foreach my $type (@types) { + if ($color) { + print(" ( Color coding: "); + print(RED . "ERROR" . RESET); + print(" | "); + print(YELLOW . "WARNING" . RESET); + print(" | "); + print(GREEN . "CHECK" . RESET); + print(" | "); + print("Multiple levels / Undetermined"); + print(" )\n\n"); + } + + foreach my $type (sort keys %types) { + my $orig_type = $type; + if ($color) { + my $level = $types{$type}; + if ($level eq "ERROR") { + $type = RED . $type . RESET; + } elsif ($level eq "WARN") { + $type = YELLOW . $type . RESET; + } elsif ($level eq "CHK") { + $type = GREEN . $type . RESET; + } + } print(++$count . "\t" . $type . "\n"); + if ($verbose && exists($verbose_messages{$orig_type})) { + my $message = $verbose_messages{$orig_type}; + $message =~ s/\n/\n\t/g; + print("\t" . $message . "\n\n"); + } } exit($exitcode); @@ -183,6 +243,46 @@ sub list_types { unshift(@ARGV, @conf_args) if @conf_args; } +sub load_docs { + open(my $docs, '<', "$docsfile") + or warn "$P: Can't read the documentation file $docsfile $!\n"; + + my $type = ''; + my $desc = ''; + my $in_desc = 0; + + while (<$docs>) { + chomp; + my $line = $_; + $line =~ s/\s+$//; + + if ($line =~ /^\s*\*\*(.+)\*\*$/) { + if ($desc ne '') { + $verbose_messages{$type} = trim($desc); + } + $type = $1; + $desc = ''; + $in_desc = 1; + } elsif ($in_desc) { + if ($line =~ /^(?:\s{4,}|$)/) { + $line =~ s/^\s{4}//; + $desc .= $line; + $desc .= "\n"; + } else { + $verbose_messages{$type} = trim($desc); + $type = ''; + $desc = ''; + $in_desc = 0; + } + } + } + + if ($desc ne '') { + $verbose_messages{$type} = trim($desc); + } + close($docs); +} + # Perl's Getopt::Long allows options to take optional arguments after a space. # Prevent --color by itself from consuming other arguments foreach (@ARGV) { @@ -193,6 +293,7 @@ sub list_types { GetOptions( 'q|quiet+' => \$quiet, + 'v|verbose!' => \$verbose, 'tree!' => \$tree, 'signoff!' => \$chk_signoff, 'patch!' => \$chk_patch, @@ -209,6 +310,7 @@ sub list_types { 'list-types!' => \$list_types, 'max-line-length=i' => \$max_line_length, 'min-conf-desc-length=i' => \$min_conf_desc_length, + 'tab-size=i' => \$tabsize, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, @@ -219,17 +321,57 @@ sub list_types { 'debug=s' => \%debug, 'test-only=s' => \$tst_only, 'codespell!' => \$codespell, - 'codespellfile=s' => \$codespellfile, + 'codespellfile=s' => \$user_codespellfile, 'typedefsfile=s' => \$typedefsfile, 'color=s' => \$color, 'no-color' => \$color, #keep old behaviors of -nocolor 'nocolor' => \$color, #keep old behaviors of -nocolor + 'kconfig-prefix=s' => \${CONFIG_}, 'h|help' => \$help, 'version' => \$help -) or help(1); +) or $help = 2; + +if ($user_codespellfile) { + # Use the user provided codespell file unconditionally + $codespellfile = $user_codespellfile; +} elsif (!(-f $codespellfile)) { + # If /usr/share/codespell/dictionary.txt is not present, try to find it + # under codespell's install directory: <codespell_root>/data/dictionary.txt + if (($codespell || $help) && which("codespell") ne "" && which("python") ne "") { + my $python_codespell_dict = << "EOF"; + +import os.path as op +import codespell_lib +codespell_dir = op.dirname(codespell_lib.__file__) +codespell_file = op.join(codespell_dir, 'data', 'dictionary.txt') +print(codespell_file, end='') +EOF + + my $codespell_dict = `python -c "$python_codespell_dict" 2> /dev/null`; + $codespellfile = $codespell_dict if (-f $codespell_dict); + } +} -help(0) if ($help); +# $help is 1 if either -h, --help or --version is passed as option - exitcode: 0 +# $help is 2 if invalid option is passed - exitcode: 1 +help($help - 1) if ($help); + +die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix)); +die "$P: --verbose cannot be used with --terse\n" if ($verbose && $terse); + +if ($color =~ /^[01]$/) { + $color = !$color; +} elsif ($color =~ /^always$/i) { + $color = 1; +} elsif ($color =~ /^never$/i) { + $color = 0; +} elsif ($color =~ /^auto$/i) { + $color = (-t STDOUT); +} else { + die "$P: Invalid color mode: $color\n"; +} +load_docs() if ($verbose); list_types(0) if ($list_types); $fix = 1 if ($fix_inplace); @@ -237,11 +379,11 @@ sub list_types { my $exit = 0; +my $perl_version_ok = 1; if ($^V && $^V lt $minimum_perl_version) { + $perl_version_ok = 0; printf "$P: requires at least perl version %vd\n", $minimum_perl_version; - if (!$ignore_perl_version) { - exit(1); - } + exit(1) if (!$ignore_perl_version); } #if no filenames are given, push '-' to read patch from stdin @@ -249,17 +391,8 @@ sub list_types { push(@ARGV, '-'); } -if ($color =~ /^[01]$/) { - $color = !$color; -} elsif ($color =~ /^always$/i) { - $color = 1; -} elsif ($color =~ /^never$/i) { - $color = 0; -} elsif ($color =~ /^auto$/i) { - $color = (-t STDOUT); -} else { - die "Invalid color mode: $color\n"; -} +# skip TAB size 1 to avoid additional checks on $tabsize - 1 +die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2); sub hash_save_array_words { my ($hashRef, $arrayRef) = @_; @@ -343,9 +476,10 @@ sub hash_show_words { __force| __iomem| __must_check| - __init_refok| __kprobes| __ref| + __refconst| + __refdata| __rcu| __private }x; @@ -358,8 +492,9 @@ sub hash_show_words { # Notes to $Attribute: # We need \b after 'init' otherwise 'initconst' will cause a false positive in a check our $Attribute = qr{ - const| _Atomic| + const| + volatile| __percpu| __nocast| __safe| @@ -376,12 +511,14 @@ sub hash_show_words { __noclone| __deprecated| __read_mostly| + __ro_after_init| __kprobes| $InitAttribute| ____cacheline_aligned| ____cacheline_aligned_in_smp| ____cacheline_internodealigned_in_smp| - __weak + __weak| + __alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) }x; our $Modifier; our $Inline = qr{inline|__always_inline|noinline|__inline|__inline__}; @@ -393,7 +530,7 @@ sub hash_show_words { our $Hex = qr{(?i)0x[0-9a-f]+$Int_type?}; our $Int = qr{[0-9]+$Int_type?}; our $Octal = qr{0[0-7]+$Int_type?}; -our $String = qr{"[X\t]*"}; +our $String = qr{(?:\b[Lu])?"[X\t]*"}; our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; @@ -480,8 +617,19 @@ sub hash_show_words { seq_vprintf|seq_printf|seq_puts )}; +our $allocFunctions = qr{(?x: + (?:(?:devm_)? + (?:kv|k|v)[czm]alloc(?:_array)?(?:_node)? | + kstrdup(?:_const)? | + kmemdup(?:_nul)?) | + (?:\w+)?alloc_skb(?:_ip_align)? | + # dev_alloc_skb/netdev_alloc_skb, et al + dma_alloc_coherent +)}; + our $signature_tags = qr{(?xi: Signed-off-by:| + Co-developed-by:| Acked-by:| Tested-by:| Reviewed-by:| @@ -491,6 +639,88 @@ sub hash_show_words { Cc: )}; +our $tracing_logging_tags = qr{(?xi: + [=-]*> | + <[=-]* | + \[ | + \] | + start | + called | + entered | + entry | + enter | + in | + inside | + here | + begin | + exit | + end | + done | + leave | + completed | + out | + return | + [\.\!:\s]* +)}; + +sub edit_distance_min { + my (@arr) = @_; + my $len = scalar @arr; + if ((scalar @arr) < 1) { + # if underflow, return + return; + } + my $min = $arr[0]; + for my $i (0 .. ($len-1)) { + if ($arr[$i] < $min) { + $min = $arr[$i]; + } + } + return $min; +} + +sub get_edit_distance { + my ($str1, $str2) = @_; + $str1 = lc($str1); + $str2 = lc($str2); + $str1 =~ s/-//g; + $str2 =~ s/-//g; + my $len1 = length($str1); + my $len2 = length($str2); + # two dimensional array storing minimum edit distance + my @distance; + for my $i (0 .. $len1) { + for my $j (0 .. $len2) { + if ($i == 0) { + $distance[$i][$j] = $j; + } elsif ($j == 0) { + $distance[$i][$j] = $i; + } elsif (substr($str1, $i-1, 1) eq substr($str2, $j-1, 1)) { + $distance[$i][$j] = $distance[$i - 1][$j - 1]; + } else { + my $dist1 = $distance[$i][$j - 1]; #insert distance + my $dist2 = $distance[$i - 1][$j]; # remove + my $dist3 = $distance[$i - 1][$j - 1]; #replace + $distance[$i][$j] = 1 + edit_distance_min($dist1, $dist2, $dist3); + } + } + } + return $distance[$len1][$len2]; +} + +sub find_standard_signature { + my ($sign_off) = @_; + my @standard_signature_tags = ( + 'Signed-off-by:', 'Co-developed-by:', 'Acked-by:', 'Tested-by:', + 'Reviewed-by:', 'Reported-by:', 'Suggested-by:' + ); + foreach my $signature (@standard_signature_tags) { + return $signature if (get_edit_distance($sign_off, $signature) <= 2); + } + + return ""; +} + our @typeListMisordered = ( qr{char\s+(?:un)?signed}, qr{int\s+(?:(?:un)?signed\s+)?short\s}, @@ -579,12 +809,36 @@ sub hash_show_words { ["__ATTR", 2], ); +my $word_pattern = '\b[A-Z]?[a-z]{2,}\b'; + #Create a search pattern for all these functions to speed up a loop below our $mode_perms_search = ""; foreach my $entry (@mode_permission_funcs) { $mode_perms_search .= '|' if ($mode_perms_search ne ""); $mode_perms_search .= $entry->[0]; } +$mode_perms_search = "(?:${mode_perms_search})"; + +our %deprecated_apis = ( + "synchronize_rcu_bh" => "synchronize_rcu", + "synchronize_rcu_bh_expedited" => "synchronize_rcu_expedited", + "call_rcu_bh" => "call_rcu", + "rcu_barrier_bh" => "rcu_barrier", + "synchronize_sched" => "synchronize_rcu", + "synchronize_sched_expedited" => "synchronize_rcu_expedited", + "call_rcu_sched" => "call_rcu", + "rcu_barrier_sched" => "rcu_barrier", + "get_state_synchronize_sched" => "get_state_synchronize_rcu", + "cond_synchronize_sched" => "cond_synchronize_rcu", +); + +#Create a search pattern for all these strings to speed up a loop below +our $deprecated_apis_search = ""; +foreach my $entry (keys %deprecated_apis) { + $deprecated_apis_search .= '|' if ($deprecated_apis_search ne ""); + $deprecated_apis_search .= $entry; +} +$deprecated_apis_search = "(?:${deprecated_apis_search})"; our $mode_perms_world_writable = qr{ S_IWUGO | @@ -619,6 +873,37 @@ sub hash_show_words { $mode_perms_string_search .= '|' if ($mode_perms_string_search ne ""); $mode_perms_string_search .= $entry; } +our $single_mode_perms_string_search = "(?:${mode_perms_string_search})"; +our $multi_mode_perms_string_search = qr{ + ${single_mode_perms_string_search} + (?:\s*\|\s*${single_mode_perms_string_search})* +}x; + +sub perms_to_octal { + my ($string) = @_; + + return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/); + + my $val = ""; + my $oval = ""; + my $to = 0; + my $curpos = 0; + my $lastpos = 0; + while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { + $curpos = pos($string); + my $match = $2; + my $omatch = $1; + last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); + $lastpos = $curpos; + $to |= $mode_permission_string_types{$match}; + $val .= '\s*\|\s*' if ($val ne ""); + $val .= $match; + $oval .= $omatch; + } + $oval =~ s/^\s*\|\s*//; + $oval =~ s/\s*\|\s*$//; + return sprintf("%04o", $to); +} our $allowed_asm_includes = qr{(?x: irq| @@ -647,7 +932,7 @@ sub hash_show_words { $spelling_fix{$suspect} = $fix; } close($spelling); -} else { +} elsif (!$frr) { warn "No typos will be found - file '$spelling_file': $!\n"; } @@ -694,7 +979,7 @@ sub read_words { next; } - $$wordsRef .= '|' if ($$wordsRef ne ""); + $$wordsRef .= '|' if (defined $$wordsRef); $$wordsRef .= $line; } close($file); @@ -704,12 +989,19 @@ sub read_words { return 0; } -my $typeOtherTypedefs = ""; -if (length($typedefsfile)) { +my $const_structs; +if (!$frr && + show_type("CONST_STRUCT")) { + read_words(\$const_structs, $conststructsfile) + or warn "No structs that should be const will be found - file '$conststructsfile': $!\n"; +} + +if (defined($typedefsfile)) { + my $typeOtherTypedefs; read_words(\$typeOtherTypedefs, $typedefsfile) or warn "No additional types will be considered - file '$typedefsfile': $!\n"; + $typeTypedefs .= '|' . $typeOtherTypedefs if (defined $typeOtherTypedefs); } -$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne ""); sub build_types { my $mods = "(?x: \n" . join("|\n ", (@modifierList, @modifierListFile)) . "\n)"; @@ -748,12 +1040,12 @@ sub build_types { }x; $Type = qr{ $NonptrType - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} (?:\s+$Inline|\s+$Modifier)* }x; $TypeMisordered = qr{ $NonptrTypeMisordered - (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)? + (?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4} (?:\s+$Inline|\s+$Modifier)* }x; $Declare = qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type}; @@ -774,9 +1066,16 @@ sub build_types { our $declaration_macros = qr{(?x: (?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(| (?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(| - (?:$Storage\s+)?${Type}\s+uninitialized_var\s*\( + (?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\( )}; +our %allow_repeated_words = ( + add => '', + added => '', + bad => '', + be => '', +); + sub deparenthesize { my ($string) = @_; return "" if (!defined($string)); @@ -817,14 +1116,29 @@ sub seed_camelcase_file { } } +our %maintained_status = (); + sub is_maintained_obsolete { my ($filename) = @_; return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl")); - my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; + if (!exists($maintained_status{$filename})) { + $maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`; + } + + return $maintained_status{$filename} =~ /obsolete/i; +} + +sub is_SPDX_License_valid { + my ($license) = @_; - return $status =~ /obsolete/i; + return 1 if (!$tree || which("python3") eq "" || !(-x "$root/scripts/spdxcheck.py") || !(-e "$gitroot")); + + my $root_path = abs_path($root); + my $status = `cd "$root_path"; echo "$license" | scripts/spdxcheck.py -`; + return 0 if ($status ne ""); + return 1; } my $camelcase_seeded = 0; @@ -837,8 +1151,8 @@ sub seed_camelcase_includes { $camelcase_seeded = 1; - if (-e ".git") { - my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`; + if (-e "$gitroot") { + my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`; chomp $git_last_include_commit; $camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit"; } else { @@ -865,8 +1179,8 @@ sub seed_camelcase_includes { return; } - if (-e ".git") { - $files = `git ls-files "include/*.h"`; + if (-e "$gitroot") { + $files = `${git_command} ls-files "include/*.h"`; @include_files = split('\n', $files); } @@ -885,18 +1199,28 @@ sub seed_camelcase_includes { } } +sub git_is_single_file { + my ($filename) = @_; + + return 0 if ((which("git") eq "") || !(-e "$gitroot")); + + my $output = `${git_command} ls-files -- $filename 2>/dev/null`; + my $count = $output =~ tr/\n//; + return $count eq 1 && $output =~ m{^${filename}$}; +} + sub git_commit_info { my ($commit, $id, $desc) = @_; - return ($id, $desc) if ((which("git") eq "") || !(-e ".git")); + return ($id, $desc) if ((which("git") eq "") || !(-e "$gitroot")); - my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`; + my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`; $output =~ s/^\s*//gm; my @lines = split("\n", $output); return ($id, $desc) if ($#lines < 0); - if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) { + if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) { # Maybe one day convert this block of bash into something that returns # all matching commit ids, but it's very slow... # @@ -906,7 +1230,8 @@ sub git_commit_info { # git log --format='%H %s' -1 $line | # echo "commit $(cut -c 1-12,41-)" # done - } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) { + } elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./ || + $lines[0] =~ /^fatal: bad object $commit/) { $id = undef; } else { $id = substr($lines[0], 0, 12); @@ -927,7 +1252,7 @@ sub git_commit_info { # If input is git commits, extract all commits from the commit expressions. # For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'. -die "$P: No git repository found\n" if ($git && !-e ".git"); +die "$P: No git repository found\n" if ($git && !-e "$gitroot"); if ($git) { my @commits = (); @@ -940,7 +1265,7 @@ sub git_commit_info { } else { $git_range = "-1 $commit_expr"; } - my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`; + my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`; foreach my $line (split(/\n/, $lines)) { $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; next if (!defined($1) || !defined($2)); @@ -955,8 +1280,12 @@ sub git_commit_info { } my $vname; +$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"}; for my $filename (@ARGV) { my $FILE; + my $is_git_file = git_is_single_file($filename); + my $oldfile = $file; + $file = 1 if ($is_git_file); if ($git) { open($FILE, '-|', "git format-patch -M --stdout -1 $filename") || die "$P: $filename: git format-patch failed - $!\n"; @@ -979,6 +1308,7 @@ sub git_commit_info { while (<$FILE>) { chomp; push(@rawlines, $_); + $vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i); } close($FILE); @@ -1000,17 +1330,18 @@ sub git_commit_info { @modifierListFile = (); @typeListFile = (); build_types(); + $file = $oldfile if ($is_git_file); } if (!$quiet) { hash_show_words(\%use_type, "Used"); hash_show_words(\%ignore_type, "Ignored"); - if ($^V lt 5.10.0) { + if (!$perl_version_ok) { print << "EOM" NOTE: perl $^V is not modern enough to detect all possible issues. - An upgrade to at least perl v5.10.0 is suggested. + An upgrade to at least perl $minimum_perl_version is suggested. EOM } if ($exit) { @@ -1031,6 +1362,7 @@ sub top_of_kernel_tree { "COPYING", "configure.ac", "Makefile.am", "README.md", "doc", "lib", "vtysh", "watchfrr", "tests", "zebra", "bgpd", "ospfd", "ospf6d", "isisd", "staticd", + ); foreach my $check (@tree_check) { @@ -1045,6 +1377,8 @@ sub parse_email { my ($formatted_email) = @_; my $name = ""; + my $quoted = ""; + my $name_comment = ""; my $address = ""; my $comment = ""; @@ -1058,7 +1392,7 @@ sub parse_email { } elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) { $address = $1; $comment = $2 if defined $2; - $formatted_email =~ s/$address.*$//; + $formatted_email =~ s/\Q$address\E.*$//; $name = $formatted_email; $name = trim($name); $name =~ s/^\"|\"$//g; @@ -1075,42 +1409,76 @@ sub parse_email { } } - $name = trim($name); - $name =~ s/^\"|\"$//g; + # Extract comments from names excluding quoted parts + # "John D. (Doe)" - Do not extract + if ($name =~ s/\"(.+)\"//) { + $quoted = $1; + } + while ($name =~ s/\s*($balanced_parens)\s*/ /) { + $name_comment .= trim($1); + } + $name =~ s/^[ \"]+|[ \"]+$//g; + $name = trim("$quoted $name"); + $address = trim($address); $address =~ s/^\<|\>$//g; + $comment = trim($comment); if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?<!\\)"/\\"/g; ##escape quotes $name = "\"$name\""; } - return ($name, $address, $comment); + return ($name, $name_comment, $address, $comment); } sub format_email { - my ($name, $address) = @_; + my ($name, $name_comment, $address, $comment) = @_; my $formatted_email; - $name = trim($name); - $name =~ s/^\"|\"$//g; + $name =~ s/^[ \"]+|[ \"]+$//g; $address = trim($address); + $address =~ s/(?:\.|\,|\")+$//; ##trailing commas, dots or quotes if ($name =~ /[^\w \-]/i) { ##has "must quote" chars $name =~ s/(?<!\\)"/\\"/g; ##escape quotes $name = "\"$name\""; } + $name_comment = trim($name_comment); + $name_comment = " $name_comment" if ($name_comment ne ""); + $comment = trim($comment); + $comment = " $comment" if ($comment ne ""); + if ("$name" eq "") { $formatted_email = "$address"; } else { - $formatted_email = "$name <$address>"; + $formatted_email = "$name$name_comment <$address>"; } - + $formatted_email .= "$comment"; return $formatted_email; } +sub reformat_email { + my ($email) = @_; + + my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); + return format_email($email_name, $name_comment, $email_address, $comment); +} + +sub same_email_addresses { + my ($email1, $email2) = @_; + + my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1); + my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2); + + return $email1_name eq $email2_name && + $email1_address eq $email2_address && + $name1_comment eq $name2_comment && + $comment1 eq $comment2; +} + sub which { my ($bin) = @_; @@ -1144,7 +1512,7 @@ sub expand_tabs { if ($c eq "\t") { $res .= ' '; $n++; - for (; ($n % 8) != 0; $n++) { + for (; ($n % $tabsize) != 0; $n++) { $res .= ' '; } next; @@ -1200,7 +1568,7 @@ sub sanitise_line { for ($off = 1; $off < length($line); $off++) { $c = substr($line, $off, 1); - # Comments we are wacking completly including the begin + # Comments we are whacking completely including the begin # and end, all to $;. if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') { $sanitise_quote = '*/'; @@ -1269,12 +1637,18 @@ sub sanitise_line { $res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@; } + if ($allow_c99_comments && $res =~ m@(//.*$)@) { + my $match = $1; + $res =~ s/\Q$match\E/"$;" x length($match)/e; + } + return $res; } sub get_quoted_string { my ($line, $rawline) = @_; + return "" if (!defined($line) || !defined($rawline)); return "" if ($line !~ m/($String)/g); return substr($rawline, $-[0], $+[0] - $-[0]); } @@ -1567,8 +1941,16 @@ sub ctx_statement_level { sub ctx_locate_comment { my ($first_line, $end_line) = @_; + # If c99 comment on the current line, or the line before or after + my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@); + return $current_comment if (defined $current_comment); + ($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@); + return $current_comment if (defined $current_comment); + ($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@); + return $current_comment if (defined $current_comment); + # Catch a comment on the end of the line itself. - my ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); + ($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@); return $current_comment if (defined $current_comment); # Look through the context and try and figure out if there is a @@ -1622,6 +2004,28 @@ sub raw_line { return $line; } +sub get_stat_real { + my ($linenr, $lc) = @_; + + my $stat_real = raw_line($linenr, 0); + for (my $count = $linenr + 1; $count <= $lc; $count++) { + $stat_real = $stat_real . "\n" . raw_line($count, 0); + } + + return $stat_real; +} + +sub get_stat_here { + my ($linenr, $cnt, $here) = @_; + + my $herectx = $here . "\n"; + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + + return $herectx; +} + sub cat_vet { my ($vet) = @_; my ($res, $coded); @@ -1940,7 +2344,16 @@ sub report { splice(@lines, 1, 1); $output = join("\n", @lines); } - $output = (split('\n', $output))[0] . "\n" if ($terse); + + if ($terse) { + $output = (split('\n', $output))[0] . "\n"; + } + + if ($verbose && exists($verbose_messages{$type}) && + !exists($verbose_emitted{$type})) { + $output .= $verbose_messages{$type} . "\n\n"; + $verbose_emitted{$type} = 1; + } push(our @report, $output); @@ -2129,7 +2542,7 @@ sub string_find_replace { sub tabify { my ($leading) = @_; - my $source_indent = 8; + my $source_indent = $tabsize; my $max_spaces_before_tab = $source_indent - 1; my $spaces_to_tab = " " x $source_indent; @@ -2171,19 +2584,41 @@ sub pos_last_openparen { return length(expand_tabs(substr($line, 0, $last_openparen))) + 1; } -sub remove_defuns { - my @breakfast = (); - my $milktoast; - for my $tasty (@rawlines) { - $milktoast = $tasty; - if (($tasty =~ /^\+DEFPY/ || - $tasty =~ /^\+DEFUN/ || - $tasty =~ /^\+ALIAS/) .. ($tasty =~ /^\+\{/)) { - $milktoast = "\n"; - } - push(@breakfast, $milktoast); +sub get_raw_comment { + my ($line, $rawline) = @_; + my $comment = ''; + + for my $i (0 .. (length($line) - 1)) { + if (substr($line, $i, 1) eq "$;") { + $comment .= substr($rawline, $i, 1); + } } - @rawlines = @breakfast; + + return $comment; +} + +sub exclude_global_initialisers { + my ($realfile) = @_; + + # Do not check for BPF programs (tools/testing/selftests/bpf/progs/*.c, samples/bpf/*_kern.c, *.bpf.c). + return $realfile =~ m@^tools/testing/selftests/bpf/progs/.*\.c$@ || + $realfile =~ m@^samples/bpf/.*_kern\.c$@ || + $realfile =~ m@/bpf/.*\.bpf\.c$@; +} + +sub remove_defuns { + my @breakfast = (); + my $milktoast; + for my $tasty (@rawlines) { + $milktoast = $tasty; + if (($tasty =~ /^\+DEFPY/ || + $tasty =~ /^\+DEFUN/ || + $tasty =~ /^\+ALIAS/) .. ($tasty =~ /^\+\{/)) { + $milktoast = "\n"; + } + push(@breakfast, $milktoast); + } + @rawlines = @breakfast; } sub process { @@ -2202,16 +2637,24 @@ sub process { our $clean = 1; my $signoff = 0; + my $author = ''; + my $authorsignoff = 0; + my $author_sob = ''; my $is_patch = 0; + my $is_binding_patch = -1; my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch + my $has_patch_separator = 0; #Found a --- line my $has_commit_log = 0; #Encountered lines before patch + my $commit_log_lines = 0; #Number of commit log lines my $commit_log_possible_stack_dump = 0; my $commit_log_long_line = 0; my $commit_log_has_diff = 0; my $reported_maintainer_file = 0; my $non_utf8_charset = 0; + my $last_git_commit_id_linenr = -1; + my $last_blank_line = 0; my $last_coalesced_string_linenr = -1; @@ -2250,8 +2693,10 @@ sub process { my $camelcase_file_seeded = 0; + my $checklicenseline = 1; + sanitise_line_reset(); - remove_defuns(); + remove_defuns(); my $line; foreach my $rawline (@rawlines) { @@ -2262,7 +2707,7 @@ sub process { if ($rawline=~/^\+\+\+\s+(\S+)/) { $setup_docs = 0; - if ($1 =~ m@Documentation/admin-guide/kernel-parameters.rst$@) { + if ($1 =~ m@Documentation/admin-guide/kernel-parameters.txt$@) { $setup_docs = 1; } #next; @@ -2343,6 +2788,15 @@ sub process { $sline =~ s/$;/ /g; #with comments as spaces my $rawline = $rawlines[$linenr - 1]; + my $raw_comment = get_raw_comment($line, $rawline); + +# check if it's a mode change, rename or start of a patch + if (!$in_commit_log && + ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ || + ($line =~ /^rename (?:from|to) \S+\s*$/ || + $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) { + $is_patch = 1; + } #extract the line range in the file after the patch is applied if (!$in_commit_log && @@ -2443,6 +2897,20 @@ sub process { } else { $check = $check_orig; } + $checklicenseline = 1; + + if ($realfile !~ /^MAINTAINERS/) { + my $last_binding_patch = $is_binding_patch; + + $is_binding_patch = () = $realfile =~ m@^(?:Documentation/devicetree/|include/dt-bindings/)@; + + if (($last_binding_patch != -1) && + ($last_binding_patch ^ $is_binding_patch)) { + WARN("DT_SPLIT_BINDING_PATCH", + "DT binding docs and includes should be a separate patch. See: Documentation/devicetree/bindings/submitting-patches.rst\n"); + } + } + next; } @@ -2454,10 +2922,23 @@ sub process { $cnt_lines++ if ($realcnt != 0); +# Verify the existence of a commit log if appropriate +# 2 is used because a $signature is counted in $commit_log_lines + if ($in_commit_log) { + if ($line !~ /^\s*$/) { + $commit_log_lines++; #could be a $signature + } + } elsif ($has_commit_log && $commit_log_lines < 2) { + # FRR: not used by FRR + # WARN("COMMIT_MESSAGE", + # "Missing commit description - Add an appropriate one\n"); + $commit_log_lines = 2; #warn only once + } + # Check if the commit log has what seems like a diff which can confuse patch if ($in_commit_log && !$commit_log_has_diff && - (($line =~ m@^\s+diff\b.*a/[\w/]+@ && - $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) || + (($line =~ m@^\s+diff\b.*a/([\w/]+)@ && + $line =~ m@^\s+diff\b.*a/[\w/]+\s+b/$1\b@) || $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ || $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) { ERROR("DIFF_IN_COMMIT_MSG", @@ -2475,10 +2956,61 @@ sub process { } } +# Check the patch for a From: + if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) { + $author = $1; + my $curline = $linenr; + while(defined($rawlines[$curline]) && ($rawlines[$curline++] =~ /^[ \t]\s*(.*)/)) { + $author .= $1; + } + $author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i); + $author =~ s/"//g; + $author = reformat_email($author); + } + # Check the patch for a signoff: - if ($line =~ /^\s*signed-off-by:/i) { + if ($line =~ /^\s*signed-off-by:\s*(.*)/i) { $signoff++; $in_commit_log = 0; + if ($author ne '' && $authorsignoff != 1) { + if (same_email_addresses($1, $author)) { + $authorsignoff = 1; + } else { + my $ctx = $1; + my ($email_name, $email_comment, $email_address, $comment1) = parse_email($ctx); + my ($author_name, $author_comment, $author_address, $comment2) = parse_email($author); + + if (lc $email_address eq lc $author_address && $email_name eq $author_name) { + $author_sob = $ctx; + $authorsignoff = 2; + } elsif (lc $email_address eq lc $author_address) { + $author_sob = $ctx; + $authorsignoff = 3; + } elsif ($email_name eq $author_name) { + $author_sob = $ctx; + $authorsignoff = 4; + + my $address1 = $email_address; + my $address2 = $author_address; + + if ($address1 =~ /(\S+)\+\S+(\@.*)/) { + $address1 = "$1$2"; + } + if ($address2 =~ /(\S+)\+\S+(\@.*)/) { + $address2 = "$1$2"; + } + if ($address1 eq $address2) { + $authorsignoff = 5; + } + } + } + } + } + +# Check for patch separator + if ($line =~ /^---$/) { + $has_patch_separator = 1; + $in_commit_log = 0; } # Check if MAINTAINERS is being updated. If so, there's probably no need to @@ -2497,8 +3029,17 @@ sub process { my $ucfirst_sign_off = ucfirst(lc($sign_off)); if ($sign_off !~ /$signature_tags/) { - WARN("BAD_SIGN_OFF", - "Non-standard signature: $sign_off\n" . $herecurr); + my $suggested_signature = find_standard_signature($sign_off); + if ($suggested_signature eq "") { + WARN("BAD_SIGN_OFF", + "Non-standard signature: $sign_off\n" . $herecurr); + } else { + if (WARN("BAD_SIGN_OFF", + "Non-standard signature: '$sign_off' - perhaps '$suggested_signature'?\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/$sign_off/$suggested_signature/; + } + } } if (defined $space_before && $space_before ne "") { if (WARN("BAD_SIGN_OFF", @@ -2526,8 +3067,8 @@ sub process { } } - my ($email_name, $email_address, $comment) = parse_email($email); - my $suggested_email = format_email(($email_name, $email_address)); + my ($email_name, $name_comment, $email_address, $comment) = parse_email($email); + my $suggested_email = format_email(($email_name, $name_comment, $email_address, $comment)); if ($suggested_email eq "") { ERROR("BAD_SIGN_OFF", "Unrecognized email address: '$email'\n" . $herecurr); @@ -2537,11 +3078,77 @@ sub process { $dequoted =~ s/" </ </; # Don't force email to have quotes # Allow just an angle bracketed address - if ("$dequoted$comment" ne $email && - "<$email_address>$comment" ne $email && - "$suggested_email$comment" ne $email) { + if (!same_email_addresses($email, $suggested_email)) { + if (WARN("BAD_SIGN_OFF", + "email address '$email' might be better as '$suggested_email'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$email\E/$suggested_email/; + } + } + + # Address part shouldn't have comments + my $stripped_address = $email_address; + $stripped_address =~ s/\([^\(\)]*\)//g; + if ($email_address ne $stripped_address) { + if (WARN("BAD_SIGN_OFF", + "address part of email should not have comments: '$email_address'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$email_address\E/$stripped_address/; + } + } + + # Only one name comment should be allowed + my $comment_count = () = $name_comment =~ /\([^\)]+\)/g; + if ($comment_count > 1) { WARN("BAD_SIGN_OFF", - "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr); + "Use a single name comment in email: '$email'\n" . $herecurr); + } + + + # stable@vger.kernel.org or stable@kernel.org shouldn't + # have an email name. In addition comments should strictly + # begin with a # + if ($email =~ /^.*stable\@(?:vger\.)?kernel\.org/i) { + if (($comment ne "" && $comment !~ /^#.+/) || + ($email_name ne "")) { + my $cur_name = $email_name; + my $new_comment = $comment; + $cur_name =~ s/[a-zA-Z\s\-\"]+//g; + + # Remove brackets enclosing comment text + # and # from start of comments to get comment text + $new_comment =~ s/^\((.*)\)$/$1/; + $new_comment =~ s/^\[(.*)\]$/$1/; + $new_comment =~ s/^[\s\#]+|\s+$//g; + + $new_comment = trim("$new_comment $cur_name") if ($cur_name ne $new_comment); + $new_comment = " # $new_comment" if ($new_comment ne ""); + my $new_email = "$email_address$new_comment"; + + if (WARN("BAD_STABLE_ADDRESS_STYLE", + "Invalid email format for stable: '$email', prefer '$new_email'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/; + } + } + } elsif ($comment ne "" && $comment !~ /^(?:#.+|\(.+\))$/) { + my $new_comment = $comment; + + # Extract comment text from within brackets or + # c89 style /*...*/ comments + $new_comment =~ s/^\[(.*)\]$/$1/; + $new_comment =~ s/^\/\*(.*)\*\/$/$1/; + + $new_comment = trim($new_comment); + $new_comment =~ s/^[^\w]$//; # Single lettered comment with non word character is usually a typo + $new_comment = "($new_comment)" if ($new_comment ne ""); + my $new_email = format_email($email_name, $name_comment, $email_address, $new_comment); + + if (WARN("BAD_SIGN_OFF", + "Unexpected content after email: '$email', should be: '$new_email'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\Q$email\E/$new_email/; + } } } @@ -2555,6 +3162,24 @@ sub process { } else { $signatures{$sig_nospace} = 1; } + +# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email + if ($sign_off =~ /^co-developed-by:$/i) { + if ($email eq $author) { + WARN("BAD_SIGN_OFF", + "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline); + } + if (!defined $lines[$linenr]) { + WARN("BAD_SIGN_OFF", + "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline); + } elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) { + WARN("BAD_SIGN_OFF", + "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); + } elsif ($1 ne $email) { + WARN("BAD_SIGN_OFF", + "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]); + } + } } # Check email subject for common tools that don't need to be mentioned @@ -2564,16 +3189,13 @@ sub process { "A patch subject line should describe the change not the tool that found it\n" . $herecurr); } -# Check for old stable address - if ($line =~ /^\s*cc:\s*.*<?\bstable\@kernel\.org\b>?.*$/i) { - ERROR("STABLE_ADDRESS", - "The 'stable' address should be 'stable\@vger.kernel.org'\n" . $herecurr); - } - -# Check for unwanted Gerrit info - if ($in_commit_log && $line =~ /^\s*change-id:/i) { - ERROR("GERRIT_CHANGE_ID", - "Remove Gerrit Change-Id's before submitting upstream.\n" . $herecurr); +# Check for Gerrit Change-Ids not in any patch context + if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) { + if (ERROR("GERRIT_CHANGE_ID", + "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } } # Check if the commit log is in a possible stack dump @@ -2581,8 +3203,10 @@ sub process { ($line =~ /^\s*(?:WARNING:|BUG:)/ || $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ || # timestamp - $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/)) { - # stack dump address + $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) || + $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ || + $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) { + # stack dump address styles $commit_log_possible_stack_dump = 1; } @@ -2593,8 +3217,8 @@ sub process { # file delta changes $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ || # filename then : - $line =~ /^\s*(?:Fixes:|Link:)/i || - # A Fixes: or Link: line + $line =~ /^\s*(?:Fixes:|Link:|$signature_tags)/i || + # A Fixes: or Link: line or signature tag line $commit_log_possible_stack_dump)) { WARN("COMMIT_LOG_LONG_LINE", "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr); @@ -2607,11 +3231,30 @@ sub process { $commit_log_possible_stack_dump = 0; } +# Check for lines starting with a # + if ($in_commit_log && $line =~ /^#/) { + if (WARN("COMMIT_COMMENT_SYMBOL", + "Commit log lines starting with '#' are dropped by git as comments\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^/ /; + } + } + # Check for git id commit length and improperly formed commit descriptions - if ($in_commit_log && !$commit_log_possible_stack_dump && - $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink):/i && +# A correctly formed commit description is: +# commit <SHA-1 hash length 12+ chars> ("Complete commit subject") +# with the commit subject '("' prefix and '")' suffix +# This is a fairly compilicated block as it tests for what appears to be +# bare SHA-1 hash with minimum length of 5. It also avoids several types of +# possible SHA-1 matches. +# A commit match can span multiple lines so this block attempts to find a +# complete typical commit on a maximum of 3 lines + if ($perl_version_ok && + $in_commit_log && !$commit_log_possible_stack_dump && + $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i && $line !~ /^This reverts commit [0-9a-f]{7,40}/ && - ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || + (($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i || + ($line =~ /\bcommit\s*$/i && defined($rawlines[$linenr]) && $rawlines[$linenr] =~ /^\s*[0-9a-f]{5,}\b/i)) || ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i && $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i && $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) { @@ -2621,49 +3264,56 @@ sub process { my $long = 0; my $case = 1; my $space = 1; - my $hasdesc = 0; - my $hasparens = 0; my $id = '0123456789ab'; my $orig_desc = "commit description"; my $description = ""; + my $herectx = $herecurr; + my $has_parens = 0; + my $has_quotes = 0; + + my $input = $line; + if ($line =~ /(?:\bcommit\s+[0-9a-f]{5,}|\bcommit\s*$)/i) { + for (my $n = 0; $n < 2; $n++) { + if ($input =~ /\bcommit\s+[0-9a-f]{5,}\s*($balanced_parens)/i) { + $orig_desc = $1; + $has_parens = 1; + # Always strip leading/trailing parens then double quotes if existing + $orig_desc = substr($orig_desc, 1, -1); + if ($orig_desc =~ /^".*"$/) { + $orig_desc = substr($orig_desc, 1, -1); + $has_quotes = 1; + } + last; + } + last if ($#lines < $linenr + $n); + $input .= " " . trim($rawlines[$linenr + $n]); + $herectx .= "$rawlines[$linenr + $n]\n"; + } + $herectx = $herecurr if (!$has_parens); + } - if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { + if ($input =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) { $init_char = $1; $orig_commit = lc($2); - } elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) { + $short = 0 if ($input =~ /\bcommit\s+[0-9a-f]{12,40}/i); + $long = 1 if ($input =~ /\bcommit\s+[0-9a-f]{41,}/i); + $space = 0 if ($input =~ /\bcommit [0-9a-f]/i); + $case = 0 if ($input =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); + } elsif ($input =~ /\b([0-9a-f]{12,40})\b/i) { $orig_commit = lc($1); } - $short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i); - $long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i); - $space = 0 if ($line =~ /\bcommit [0-9a-f]/i); - $case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/); - if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) { - $orig_desc = $1; - $hasparens = 1; - } elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i && - defined $rawlines[$linenr] && - $rawlines[$linenr] =~ /^\s*[^"]+"\)/) { - $line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i; - $orig_desc = $1; - $rawlines[$linenr] =~ /^\s*([^"]+)"\)/; - $orig_desc .= " " . $1; - $hasparens = 1; - } - ($id, $description) = git_commit_info($orig_commit, $id, $orig_desc); if (defined($id) && - ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) { + ($short || $long || $space || $case || ($orig_desc ne $description) || !$has_quotes) && + $last_git_commit_id_linenr != $linenr - 1) { ERROR("GIT_COMMIT_ID", - "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr); + "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herectx); } + #don't report the next line if this line ends in commit and the sha1 hash is the next line + $last_git_commit_id_linenr = $linenr if ($line =~ /\bcommit\s*$/i); } # Check for added, moved or deleted files @@ -2674,8 +3324,16 @@ sub process { (defined($1) || defined($2))))) { $is_patch = 1; $reported_maintainer_file = 1; - #WARN("FILE_PATH_CHANGES", - # "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + # WARN("FILE_PATH_CHANGES", + # "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); + } + +# Check for adding new DT bindings not in schema format + if (!$in_commit_log && + ($line =~ /^new file mode\s*\d+\s*$/) && + ($realfile =~ m@^Documentation/devicetree/bindings/.*\.txt$@)) { + WARN("DT_SCHEMA_BINDING_PATCH", + "DT bindings should be in DT schema format. See: Documentation/devicetree/bindings/writing-schema.rst\n"); } # Check for wrappage within a valid hunk of the file @@ -2739,21 +3397,89 @@ sub process { # Check for various typo / spelling mistakes if (defined($misspellings) && ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) { + while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { my $typo = $1; + my $blank = copy_spacing($rawline); + my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); + my $hereptr = "$hereline$ptr\n"; my $typo_fix = $spelling_fix{lc($typo)}; $typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/); $typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/); my $msg_level = \&WARN; $msg_level = \&CHK if ($file); if (&{$msg_level}("TYPO_SPELLING", - "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) && + "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $hereptr) && $fix) { $fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/; } } } +# check for invalid commit id + if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) { + my $id; + my $description; + ($id, $description) = git_commit_info($2, undef, undef); + if (!defined($id)) { + WARN("UNKNOWN_COMMIT_ID", + "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr); + } + } + +# check for repeated words separated by a single space +# avoid false positive from list command eg, '-rw-r--r-- 1 root root' + if (($rawline =~ /^\+/ || $in_commit_log) && + $rawline !~ /[bcCdDlMnpPs\?-][rwxsStT-]{9}/) { + pos($rawline) = 1 if (!$in_commit_log); + while ($rawline =~ /\b($word_pattern) (?=($word_pattern))/g) { + + my $first = $1; + my $second = $2; + my $start_pos = $-[1]; + my $end_pos = $+[2]; + if ($first =~ /(?:struct|union|enum)/) { + pos($rawline) += length($first) + length($second) + 1; + next; + } + + next if (lc($first) ne lc($second)); + next if ($first eq 'long'); + + # check for character before and after the word matches + my $start_char = ''; + my $end_char = ''; + $start_char = substr($rawline, $start_pos - 1, 1) if ($start_pos > ($in_commit_log ? 0 : 1)); + $end_char = substr($rawline, $end_pos, 1) if ($end_pos < length($rawline)); + + next if ($start_char =~ /^\S$/); + next if (index(" \t.,;?!", $end_char) == -1); + + # avoid repeating hex occurrences like 'ff ff fe 09 ...' + if ($first =~ /\b[0-9a-f]{2,}\b/i) { + next if (!exists($allow_repeated_words{lc($first)})); + } + + if (WARN("REPEATED_WORD", + "Possible repeated word: '$first'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b$first $second\b/$first/; + } + } + + # if it's a repeated word on consecutive lines in a comment block + if ($prevline =~ /$;+\s*$/ && + $prevrawline =~ /($word_pattern)\s*$/) { + my $last_word = $1; + if ($rawline =~ /^\+\s*\*\s*$last_word /) { + if (WARN("REPEATED_WORD", + "Possible repeated word: '$last_word'\n" . $hereprev) && + $fix) { + $fixed[$fixlinenr] =~ s/(\+\s*\*\s*)$last_word /$1/; + } + } + } + } + # ignore non-hunk lines and lines being removed next if (!$hunk_line || $line =~ /^-/); @@ -2776,11 +3502,26 @@ sub process { $rpt_cleaners = 1; } +# Check for FSF mailing addresses. + if ($rawline =~ /\bwrite to the Free/i || + $rawline =~ /\b675\s+Mass\s+Ave/i || + $rawline =~ /\b59\s+Temple\s+Pl/i || + $rawline =~ /\b51\s+Franklin\s+St/i) { + my $herevet = "$here\n" . cat_vet($rawline) . "\n"; + my $msg_level = \&ERROR; + $msg_level = \&CHK if ($file); + &{$msg_level}("FSF_MAILING_ADDRESS", + "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet) + } + # check for Kconfig help text having a real description # Only applies when adding the entry originally, after that we do not have # sufficient context to determine whether it is indeed long enough. if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*config\s+/) { + # 'choice' is usually the last thing on the line (though + # Kconfig supports named choices), so use a word boundary + # (\b) rather than a whitespace character (\s) + $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) { my $length = 0; my $cnt = $realcnt; my $ln = $linenr + 1; @@ -2795,7 +3536,7 @@ sub process { next if ($f =~ /^-/); last if (!$file && $f =~ /^\@\@/); - if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate)\s*\"/) { + if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) { $is_start = 1; } elsif ($lines[$ln - 1] =~ /^\+\s*(?:---)?help(?:---)?$/) { $length = -1; @@ -2805,7 +3546,13 @@ sub process { $f =~ s/#.*//; $f =~ s/^\s+//; next if ($f =~ /^$/); - if ($f =~ /^\s*config\s/) { + + # This only checks context lines in the patch + # and so hopefully shouldn't trigger false + # positives, even though some of these are + # common words in help texts + if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice| + if|endif|menu|endmenu|source)\b/x) { $is_end = 1; last; } @@ -2818,22 +3565,44 @@ sub process { #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; } -# check for MAINTAINERS entries that don't have the right form - if ($realfile =~ /^MAINTAINERS$/ && - $rawline =~ /^\+[A-Z]:/ && - $rawline !~ /^\+[A-Z]:\t\S/) { - if (WARN("MAINTAINERS_STYLE", - "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; +# check MAINTAINERS entries + if ($realfile =~ /^MAINTAINERS$/) { +# check MAINTAINERS entries for the right form + if ($rawline =~ /^\+[A-Z]:/ && + $rawline !~ /^\+[A-Z]:\t\S/) { + if (WARN("MAINTAINERS_STYLE", + "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/; + } + } +# check MAINTAINERS entries for the right ordering too + my $preferred_order = 'MRLSWQBCPTFXNK'; + if ($rawline =~ /^\+[A-Z]:/ && + $prevrawline =~ /^[\+ ][A-Z]:/) { + $rawline =~ /^\+([A-Z]):\s*(.*)/; + my $cur = $1; + my $curval = $2; + $prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/; + my $prev = $1; + my $prevval = $2; + my $curindex = index($preferred_order, $cur); + my $previndex = index($preferred_order, $prev); + if ($curindex < 0) { + WARN("MAINTAINERS_STYLE", + "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr); + } else { + if ($previndex >= 0 && $curindex < $previndex) { + WARN("MAINTAINERS_STYLE", + "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev); + } elsif ((($prev eq 'F' && $cur eq 'F') || + ($prev eq 'X' && $cur eq 'X')) && + ($prevval cmp $curval) > 0) { + WARN("MAINTAINERS_STYLE", + "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev); + } + } } - } - -# discourage the use of boolean for type definition attributes of Kconfig options - if ($realfile =~ /Kconfig/ && - $line =~ /^\+\s*\bboolean\b/) { - WARN("CONFIG_TYPE_BOOLEAN", - "Use of boolean is deprecated, please use bool instead.\n" . $herecurr); } if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && @@ -2858,7 +3627,7 @@ sub process { my @compats = $rawline =~ /\"([a-zA-Z0-9\-\,\.\+_]+)\"/g; my $dt_path = $root . "/Documentation/devicetree/bindings/"; - my $vp_file = $dt_path . "vendor-prefixes.txt"; + my $vp_file = $dt_path . "vendor-prefixes.yaml"; foreach my $compat (@compats) { my $compat2 = $compat; @@ -2873,7 +3642,7 @@ sub process { next if $compat !~ /^([a-zA-Z0-9\-]+)\,/; my $vendor = $1; - `grep -Eq "^$vendor\\b" $vp_file`; + `grep -Eq "\\"\\^\Q$vendor\E,\\.\\*\\":" $vp_file`; if ( $? >> 8 ) { WARN("UNDOCUMENTED_DT_STRING", "DT compatible string vendor \"$vendor\" appears un-documented -- check $vp_file\n" . $herecurr); @@ -2881,15 +3650,83 @@ sub process { } } +# check for using SPDX license tag at beginning of files + if ($realline == $checklicenseline) { + if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { + $checklicenseline = 2; + } elsif ($rawline =~ /^\+/) { + my $comment = ""; + if (!$frr && + $realfile =~ /\.(h|s|S)$/) { + $comment = '/*'; + } elsif ($frr && + $realfile =~ /\.(h|s|S)$/) { + $comment = '//'; + } elsif ($realfile =~ /\.(c|dts|dtsi)$/) { + $comment = '//'; + } elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) { + $comment = '#'; + } elsif ($realfile =~ /\.rst$/) { + $comment = '..'; + } + +# check SPDX comment style for .[chsS] files + if ($realfile =~ /\.[chsS]$/ && + $rawline =~ /SPDX-License-Identifier:/ && + $rawline !~ m@^\+\s*\Q$comment\E\s*@) { + WARN("SPDX_LICENSE_TAG", + "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr); + } + + if ($comment !~ /^$/ && + $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) { + WARN("SPDX_LICENSE_TAG", + "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr); + } elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) { + my $spdx_license = $1; + if (!is_SPDX_License_valid($spdx_license)) { + WARN("SPDX_LICENSE_TAG", + "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr); + } + if ($realfile =~ m@^Documentation/devicetree/bindings/@ && + not $spdx_license =~ /GPL-2\.0.*BSD-2-Clause/) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + if (&{$msg_level}("SPDX_LICENSE_TAG", + + "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/; + } + } + } + } + } + +# check for embedded filenames + if ($rawline =~ /^\+.*\Q$realfile\E/) { + WARN("EMBEDDED_FILENAME", + "It's generally not useful to have the filename in the file\n" . $herecurr); + } + # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/); +# check for using SPDX-License-Identifier on the wrong line number + if ($realline != $checklicenseline && + $rawline =~ /\bSPDX-License-Identifier:/ && + substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) { + WARN("SPDX_LICENSE_TAG", + "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr); + } + # line length limit (with some exclusions) # # There are a few types of lines that may extend beyond $max_line_length: # logging functions like pr_info that end in a string # lines with a single string # #defines that are a single string +# lines with an RFC3986 like URL # # There are 3 different line length message types: # LONG_LINE_COMMENT a comment starts before but extends beyond $max_line_length @@ -2921,6 +3758,10 @@ sub process { $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { $msg_type = ""; + # URL ($rawline is used in case the URL is in a comment) + } elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) { + $msg_type = ""; + # Otherwise set the alternate message types # a comment starts before $max_line_length @@ -2936,36 +3777,34 @@ sub process { if ($msg_type ne "" && (show_type("LONG_LINE") || show_type($msg_type))) { - WARN($msg_type, - "line over $max_line_length characters\n" . $herecurr); + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}($msg_type, + "line length of $length exceeds $max_line_length columns\n" . $herecurr); } } # check for adding lines without a newline. if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) { - WARN("MISSING_EOF_NEWLINE", - "adding a line without newline at end of file\n" . $herecurr); + if (WARN("MISSING_EOF_NEWLINE", + "adding a line without newline at end of file\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr+1, "No newline at end of file"); + } } -# Blackfin: use hi/lo macros - if ($realfile =~ m@arch/blackfin/.*\.S$@) { - if ($line =~ /\.[lL][[:space:]]*=.*&[[:space:]]*0x[fF][fF][fF][fF]/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("LO_MACRO", - "use the LO() macro, not (... & 0xFFFF)\n" . $herevet); - } - if ($line =~ /\.[hH][[:space:]]*=.*>>[[:space:]]*16/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("HI_MACRO", - "use the HI() macro, not (... >> 16)\n" . $herevet); - } +# check for .L prefix local symbols in .S files + if ($realfile =~ /\.S$/ && + $line =~ /^\+\s*(?:[A-Z]+_)?SYM_[A-Z]+_(?:START|END)(?:_[A-Z_]+)?\s*\(\s*\.L/) { + WARN("AVOID_L_PREFIX", + "Avoid using '.L' prefixed local symbol names for denoting a range of code via 'SYM_*_START/END' annotations; see Documentation/asm-annotations.rst\n" . $herecurr); } # check we are in a valid source file C or perl if not then ignore this hunk next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/); # at the beginning of a line any tabs must come first and anything -# more than 8 must use tabs. +# more than $tabsize must use tabs. if ($rawline =~ /^\+\s* \t\s*\S/ || $rawline =~ /^\+\s* \s*/) { my $herevet = "$here\n" . cat_vet($rawline) . "\n"; @@ -2984,33 +3823,53 @@ sub process { "please, no space before tabs\n" . $herevet) && $fix) { while ($fixed[$fixlinenr] =~ - s/(^\+.*) {8,8}\t/$1\t\t/) {} + s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {} while ($fixed[$fixlinenr] =~ s/(^\+.*) +\t/$1\t/) {} } } +# check for assignments on the start of a line + if ($sline =~ /^\+\s+($Assignment)[^=]/) { + my $operator = $1; + if (CHK("ASSIGNMENT_CONTINUATIONS", + "Assignment operator '$1' should be on the previous line\n" . $hereprev) && + $fix && $prevrawline =~ /^\+/) { + # add assignment operator to the previous line, remove from current line + $fixed[$fixlinenr - 1] .= " $operator"; + $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//; + } + } + # check for && or || at the start of a line if ($rawline =~ /^\+\s*(&&|\|\|)/) { - CHK("LOGICAL_CONTINUATIONS", - "Logical continuations should be on the previous line\n" . $hereprev); + my $operator = $1; + if (CHK("LOGICAL_CONTINUATIONS", + "Logical continuations should be on the previous line\n" . $hereprev) && + $fix && $prevrawline =~ /^\+/) { + # insert logical operator at last non-comment, non-whitepsace char on previous line + $prevline =~ /[\s$;]*$/; + my $line_end = substr($prevrawline, $-[0]); + $fixed[$fixlinenr - 1] =~ s/\Q$line_end\E$/ $operator$line_end/; + $fixed[$fixlinenr] =~ s/\Q$operator\E\s*//; + } } # check indentation starts on a tab stop - if ($^V && $^V ge 5.10.0 && - $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$))/) { + if ($perl_version_ok && + $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) { my $indent = length($1); - if ($indent % 8) { + if ($indent % $tabsize) { if (WARN("TABSTOP", "Statements should start on a tabstop\n" . $herecurr) && $fix) { - $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/8)@e; + $fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e; } } } # check multi-line statement indentation matches previous line - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) { $prevline =~ /^\+(\t*)(.*)$/; my $oldindent = $1; @@ -3022,8 +3881,8 @@ sub process { my $newindent = $2; my $goodtabindent = $oldindent . - "\t" x ($pos / 8) . - " " x ($pos % 8); + "\t" x ($pos / $tabsize) . + " " x ($pos % $tabsize); my $goodspaceindent = $oldindent . " " x $pos; if ($newindent ne $goodtabindent && @@ -3061,7 +3920,7 @@ sub process { if ($realfile =~ m@^(drivers/net/|net/)@ && $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ && $rawline =~ /^\+[ \t]*\*/ && - $realline > 2) { + $realline > 3) { # Do not warn about the initial copyright comment block after SPDX-License-Identifier WARN("NETWORKING_BLOCK_COMMENT_STYLE", "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev); } @@ -3143,43 +4002,48 @@ sub process { } # check for missing blank lines after declarations - if ($sline =~ /^\+\s+\S/ && #Not at char 1 - # actual declarations - ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || +# (declarations must have the same indentation and not be at the start of line) + if (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/) { + # use temporaries + my $sl = $sline; + my $pl = $prevline; + # remove $Attribute/$Sparse uses to simplify comparisons + $sl =~ s/\b(?:$Attribute|$Sparse)\b//g; + $pl =~ s/\b(?:$Attribute|$Sparse)\b//g; + if (($pl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations - $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + $pl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define - $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + $pl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros - $prevline =~ /^\+\s+$declaration_macros/) && + $pl =~ /^\+\s+$declaration_macros/) && # for "else if" which can look like "$Ident $Ident" - !($prevline =~ /^\+\s+$c90_Keywords\b/ || + !($pl =~ /^\+\s+$c90_Keywords\b/ || # other possible extensions of declaration lines - $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || + $pl =~ /(?:$Compare|$Assignment|$Operators)\s*$/ || # not starting a section or a macro "\" extended line - $prevline =~ /(?:\{\s*|\\)$/) && + $pl =~ /(?:\{\s*|\\)$/) && # looks like a declaration - !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || + !($sl =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ || # function pointer declarations - $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || + $sl =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ || # foo bar; where foo is some local typedef or #define - $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || + $sl =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ || # known declaration macros - $sline =~ /^\+\s+$declaration_macros/ || + $sl =~ /^\+\s+$declaration_macros/ || # start of struct or union or enum - $sline =~ /^\+\s+(?:union|struct|enum|typedef)\b/ || + $sl =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ || # start or end of block or continuation of declaration - $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || + $sl =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ || # bitfield continuation - $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || + $sl =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ || # other possible extensions of declaration lines - $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) && - # indentation of previous and current line are the same - (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) { - if (WARN("LINE_SPACING", - "Missing a blank line after declarations\n" . $hereprev) && - $fix) { - fix_insert_line($fixlinenr, "\+"); + $sl =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/)) { + if (WARN("LINE_SPACING", + "Missing a blank line after declarations\n" . $hereprev) && + $fix) { + fix_insert_line($fixlinenr, "\+"); + } } } @@ -3232,12 +4096,16 @@ sub process { } # check indentation of a line with a break; -# if the previous line is a goto or return and is indented the same # of tabs +# if the previous line is a goto, return or break +# and is indented the same # of tabs if ($sline =~ /^\+([\t]+)break\s*;\s*$/) { my $tabs = $1; - if ($prevline =~ /^\+$tabs(?:goto|return)\b/) { - WARN("UNNECESSARY_BREAK", - "break is not useful after a goto or return\n" . $hereprev); + if ($prevline =~ /^\+$tabs(goto|return|break)\b/) { + if (WARN("UNNECESSARY_BREAK", + "break is not useful after a $1\n" . $hereprev) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } } } @@ -3247,18 +4115,6 @@ sub process { "CVS style keyword markers, these will _not_ be updated\n". $herecurr); } -# Blackfin: don't use __builtin_bfin_[cs]sync - if ($line =~ /__builtin_bfin_csync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("CSYNC", - "use the CSYNC() macro in asm/blackfin.h\n" . $herevet); - } - if ($line =~ /__builtin_bfin_ssync/) { - my $herevet = "$here\n" . cat_vet($line) . "\n"; - ERROR("SSYNC", - "use the SSYNC() macro in asm/blackfin.h\n" . $herevet); - } - # check for old HOTPLUG __dev<foo> section markings if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) { WARN("HOTPLUG_SECTION", @@ -3506,11 +4362,11 @@ sub process { #print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n"; if ($check && $s ne '' && - (($sindent % 8) != 0 || + (($sindent % $tabsize) != 0 || ($sindent < $indent) || ($sindent == $indent && ($s !~ /^\s*(?:\}|\{|else\b)/)) || - ($sindent > $indent + 8))) { + ($sindent > $indent + $tabsize))) { WARN("SUSPECT_CODE_INDENT", "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n"); } @@ -3532,6 +4388,17 @@ sub process { #ignore lines not being added next if ($line =~ /^[^\+]/); +# check for self assignments used to avoid compiler warnings +# e.g.: int foo = foo, *bar = NULL; +# struct foo bar = *(&(bar)); + if ($line =~ /^\+\s*(?:$Declare)?([A-Za-z_][A-Za-z\d_]*)\s*=/) { + my $var = $1; + if ($line =~ /^\+\s*(?:$Declare)?$var\s*=\s*(?:$var|\*\s*\(?\s*&\s*\(?\s*$var\s*\)?\s*\)?)\s*[;,]/) { + WARN("SELF_ASSIGNMENT", + "Do not use self-assignments to avoid compiler warnings\n" . $herecurr); + } + } + # check for dereferences that span multiple lines if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ && $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) { @@ -3627,17 +4494,13 @@ sub process { # no C99 // comments if ($line =~ m{//}) { - if ($rawlines[$linenr - 1] =~ /SPDX-License-Identifier:/) { - # ignore - } elsif (!$allow_c99_comments) { - if(ERROR("C99_COMMENTS", - "do not use C99 // comments\n" . $herecurr) && - $fix) { - my $line = $fixed[$fixlinenr]; - if ($line =~ /\/\/(.*)$/) { - my $comment = trim($1); - $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; - } + if (!$allow_c99_comments && ERROR("C99_COMMENTS", + "do not use C99 // comments\n" . $herecurr) && + $fix) { + my $line = $fixed[$fixlinenr]; + if ($line =~ /\/\/(.*)$/) { + my $comment = trim($1); + $fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@; } } else { WARN("C99_COMMENTS", @@ -3654,13 +4517,13 @@ sub process { if (defined $realline_next && exists $lines[$realline_next - 1] && !defined $suppress_export{$realline_next} && - ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/)) { # Handle definitions which produce identifiers with # a prefix: # XXX(foo); # EXPORT_SYMBOL(something_foo); my $name = $1; + $name =~ s/^\s*($Ident).*/$1/; if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ && $name =~ /^${Ident}_$2/) { #print "FOO C name<$name>\n"; @@ -3682,8 +4545,7 @@ sub process { } if (!defined $suppress_export{$linenr} && $prevline =~ /^.\s*$/ && - ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ || - $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) { + ($line =~ /EXPORT_SYMBOL.*\((.*)\)/)) { #print "FOO B <$lines[$linenr - 1]>\n"; $suppress_export{$linenr} = 2; } @@ -3694,7 +4556,8 @@ sub process { } # check for global initialisers. - if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) { + if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/ && + !exclude_global_initialisers($realfile)) { if (ERROR("GLOBAL_INITIALISERS", "do not initialise globals to $1\n" . $herecurr) && $fix) { @@ -3718,19 +4581,48 @@ sub process { "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr); } +# check for unnecessary <signed> int declarations of short/long/long long + while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) { + my $type = trim($1); + next if ($type !~ /\bint\b/); + next if ($type !~ /\b(?:short|long\s+long|long)\b/); + my $new_type = $type; + $new_type =~ s/\b\s*int\s*\b/ /; + $new_type =~ s/\b\s*(?:un)?signed\b\s*/ /; + $new_type =~ s/^const\s+//; + $new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/); + $new_type = "const $new_type" if ($type =~ /^const\b/); + $new_type =~ s/\s+/ /g; + $new_type = trim($new_type); + if (WARN("UNNECESSARY_INT", + "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/; + } + } + # check for static const char * arrays. if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) { WARN("STATIC_CONST_CHAR_ARRAY", "static const char * array should probably be static const char * const\n" . $herecurr); - } + } + +# check for initialized const char arrays that should be static const + if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) { + if (WARN("STATIC_CONST_CHAR_ARRAY", + "const array should probably be static const\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/; + } + } # check for static char foo[] = "bar" declarations. if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) { WARN("STATIC_CONST_CHAR_ARRAY", "static char array declaration should probably be static const char\n" . $herecurr); - } + } # check for const <foo> const where <foo> is not a pointer or array type if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) { @@ -3744,12 +4636,24 @@ sub process { } } +# check for const static or static <non ptr type> const declarations +# prefer 'static const <foo>' over 'const static <foo>' and 'static <foo> const' + if ($sline =~ /^\+\s*const\s+static\s+($Type)\b/ || + $sline =~ /^\+\s*static\s+($BasicType)\s+const\b/) { + if (WARN("STATIC_CONST", + "Move const after static - use 'static const $1'\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bconst\s+static\b/static const/; + $fixed[$fixlinenr] =~ s/\bstatic\s+($BasicType)\s+const\b/static const $1/; + } + } + # check for non-global char *foo[] = {"bar", ...} declarations. if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) { WARN("STATIC_CONST_CHAR_ARRAY", "char * array declaration might be better as static const\n" . $herecurr); - } + } # check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo) if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) { @@ -3765,7 +4669,7 @@ sub process { } # check for function declarations without arguments like "int foo()" - if ($line =~ /(\b$Type\s+$Ident)\s*\(\s*\)/) { + if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) { if (ERROR("FUNCTION_WITHOUT_ARGS", "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) && $fix) { @@ -3866,25 +4770,23 @@ sub process { "printk() should include KERN_<LEVEL> facility level\n" . $herecurr); } - if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { - my $orig = $1; +# prefer variants of (subsystem|netdev|dev|pr)_<level> to printk(KERN_<LEVEL> + if ($line =~ /\b(printk(_once|_ratelimited)?)\s*\(\s*KERN_([A-Z]+)/) { + my $printk = $1; + my $modifier = $2; + my $orig = $3; + $modifier = "" if (!defined($modifier)); my $level = lc($orig); $level = "warn" if ($level eq "warning"); my $level2 = $level; $level2 = "dbg" if ($level eq "debug"); + $level .= $modifier; + $level2 .= $modifier; WARN("PREFER_PR_LEVEL", - "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to printk(KERN_$orig ...\n" . $herecurr); - } - - if ($line =~ /\bpr_warning\s*\(/) { - if (WARN("PREFER_PR_LEVEL", - "Prefer pr_warn(... to pr_warning(...\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ - s/\bpr_warning\b/pr_warn/; - } + "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(... to $printk(KERN_$orig ...\n" . $herecurr); } +# prefer dev_<level> to dev_printk(KERN_<LEVEL> if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) { my $orig = $1; my $level = lc($orig); @@ -3894,6 +4796,12 @@ sub process { "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr); } +# trace_printk should not be used in production code. + if ($line =~ /\b(trace_printk|trace_puts|ftrace_vprintk)\s*\(/) { + WARN("TRACE_PRINTK", + "Do not use $1() in production code (this can be ignored if built only with a debug config option)\n" . $herecurr); + } + # ENOSYS means "bad syscall nr" and nothing else. This will have a small # number of false positives, but assembly files are not checked, so at # least the arch entry code will not trigger this warning. @@ -3902,16 +4810,29 @@ sub process { "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr); } +# ENOTSUPP is not a standard error code and should be avoided in new patches. +# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP. +# Similarly to ENOSYS warning a small number of false positives is expected. + if (!$file && $line =~ /\bENOTSUPP\b/) { + if (WARN("ENOTSUPP", + "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/; + } + } + # function brace can't be on same line, except for #defines of do while, # or if closed on same line - if (($line=~/$Type\s*$Ident\(.*\).*\s*{/) and - !($line=~/\#\s*define.*do\s\{/) and !($line=~/}/)) { + if ($perl_version_ok && + $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ && + $sline !~ /\#\s*define\b.*do\s*\{/ && + $sline !~ /}/) { if (ERROR("OPEN_BRACE", - "open brace '{' following function declarations go on the next line\n" . $herecurr) && + "open brace '{' following function definitions go on the next line\n" . $herecurr) && $fix) { fix_delete_line($fixlinenr, $rawline); my $fixed_line = $rawline; - $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/; + $fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*)\{(.*)$/; my $line1 = $1; my $line2 = $2; fix_insert_line($fixlinenr, ltrim($line1)); @@ -4028,7 +4949,7 @@ sub process { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /[{,]\s+$/) { + $prefix !~ /[{,:]\s+$/) { if (ERROR("BRACKET_SPACE", "space prohibited before open square bracket '['\n" . $herecurr) && $fix) { @@ -4205,7 +5126,7 @@ sub process { my $rtrim_before = 0; my $space_after = 0; if ($line=~/\#\s*define/) { - # ignore , spacing in macros + # FRR: ignore , spacing in macros } elsif ($ctx =~ /Wx./) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { @@ -4329,7 +5250,7 @@ sub process { # A colon needs no spaces before when it is # terminating a case value or a label. } elsif ($opv eq ':C' || $opv eq ':L') { - if ($ctx =~ /Wx./) { + if ($ctx =~ /Wx./ and $realfile !~ m@.*\.lds\.h$@) { if (ERROR("SPACING", "space prohibited before that '$op' $at\n" . $hereptr)) { $good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]); @@ -4347,7 +5268,7 @@ sub process { ($op eq '>' && $ca =~ /<\S+\@\S+$/)) { - $ok = 1; + $ok = 1; } # for asm volatile statements @@ -4413,7 +5334,7 @@ sub process { ## $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) { ## ## # Remove any bracketed sections to ensure we do not -## # falsly report the parameters of functions. +## # falsely report the parameters of functions. ## my $ln = $line; ## while ($ln =~ s/\([^\(\)]*\)//g) { ## } @@ -4425,11 +5346,11 @@ sub process { #need space before brace following if, while, etc if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) || - $line =~ /do\{/) { + $line =~ /\b(?:else|do)\{/) { if (ERROR("SPACING", "space required before the open brace '{'\n" . $herecurr) && $fix) { - $fixed[$fixlinenr] =~ s/^(\+.*(?:do|\)))\{/$1 {/; + $fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/; } } @@ -4443,7 +5364,7 @@ sub process { # closing brace should have a space following it when it has anything # on the line - if ($line =~ /}(?!(?:,|;|\)))\S/) { + if ($line =~ /}(?!(?:,|;|\)|\}))\S/) { if (ERROR("SPACING", "space required after that close brace '}'\n" . $herecurr) && $fix) { @@ -4518,7 +5439,9 @@ sub process { } # check for unnecessary parentheses around comparisons in if uses - if ($^V && $^V ge 5.10.0 && defined($stat) && +# when !drivers/staging or command-line uses --strict + if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) && + $perl_version_ok && defined($stat) && $stat =~ /(^.\s*if\s*($balanced_parens))/) { my $if_stat = $1; my $test = substr($2, 1, -1); @@ -4541,10 +5464,38 @@ sub process { } } +# check that goto labels aren't indented (allow a single space indentation) +# and ignore bitfield definitions like foo:1 +# Strictly, labels can have whitespace after the identifier and before the : +# but this is not allowed here as many ?: uses would appear to be labels + # FRR: has indented labels + if (!$frr && + $sline =~ /^.\s+[A-Za-z_][A-Za-z\d_]*:(?!\s*\d+)/ && + $sline !~ /^. [A-Za-z\d_][A-Za-z\d_]*:/ && + $sline !~ /^.\s+default:/) { + if (WARN("INDENTED_LABEL", + "labels should not be indented\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ + s/^(.)\s+/$1/; + } + } + +# check if a statement with a comma should be two statements like: +# foo = bar(), /* comma should be semicolon */ +# bar = baz(); + if (defined($stat) && + $stat =~ /^\+\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*,\s*(?:$Lval\s*$Assignment\s*)?$FuncArg\s*;\s*$/) { + my $cnt = statement_rawlines($stat); + my $herectx = get_stat_here($linenr, $cnt, $here); + WARN("SUSPECT_COMMA_SEMICOLON", + "Possible comma where semicolon could be used\n" . $herectx); + } + # return is not a function if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) { my $spacing = $1; - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) { my $value = $1; $value = deparenthesize($value); @@ -4568,10 +5519,10 @@ sub process { $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) { WARN("RETURN_VOID", "void function return statements are not generally useful\n" . $hereprev); - } + } # if statements using unnecessary parentheses - ie: if ((foo == bar)) - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /\bif\s*((?:\(\s*){2,})/) { my $openparens = $1; my $count = $openparens =~ tr@\(@\(@; @@ -4588,7 +5539,7 @@ sub process { # avoid cases like "foo + BAR < baz" # only fix matches surrounded by parentheses to avoid incorrect # conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5" - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) { my $lead = $1; my $const = $2; @@ -4616,7 +5567,7 @@ sub process { # Return of what appears to be an errno should normally be negative if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) { my $name = $1; - if ($name ne 'EOF' && $name ne 'ERROR') { + if ($name ne 'EOF' && $name ne 'ERROR' && $name !~ /^EPOLL/) { WARN("USE_NEGATIVE_ERRNO", "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr); } @@ -4661,15 +5612,37 @@ sub process { my ($s, $c) = ($stat, $cond); if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) { - ERROR("ASSIGN_IN_IF", - "do not use assignment in if condition\n" . $herecurr); + if (ERROR("ASSIGN_IN_IF", + "do not use assignment in if condition\n" . $herecurr) && + $fix && $perl_version_ok) { + if ($rawline =~ /^\+(\s+)if\s*\(\s*(\!)?\s*\(\s*(($Lval)\s*=\s*$LvalOrFunc)\s*\)\s*(?:($Compare)\s*($FuncArg))?\s*\)\s*(\{)?\s*$/) { + my $space = $1; + my $not = $2; + my $statement = $3; + my $assigned = $4; + my $test = $8; + my $against = $9; + my $brace = $15; + fix_delete_line($fixlinenr, $rawline); + fix_insert_line($fixlinenr, "$space$statement;"); + my $newline = "${space}if ("; + $newline .= '!' if defined($not); + $newline .= '(' if (defined $not && defined($test) && defined($against)); + $newline .= "$assigned"; + $newline .= " $test $against" if (defined($test) && defined($against)); + $newline .= ')' if (defined $not && defined($test) && defined($against)); + $newline .= ')'; + $newline .= " {" if (defined($brace)); + fix_insert_line($fixlinenr + 1, $newline); + } + } } # Find out what is on the end of the line after the # conditional. substr($s, 0, length($c), ''); $s =~ s/\n.*//g; - $s =~ s/$;//g; # Remove any comments + $s =~ s/$;//g; # Remove any comments if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ && $c !~ /}\s*while\s*/) { @@ -4708,7 +5681,7 @@ sub process { # if and else should not have general statements after it if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) { my $s = $1; - $s =~ s/$;//g; # Remove any comments + $s =~ s/$;//g; # Remove any comments if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) { ERROR("TRAILING_STATEMENTS", "trailing statements should be on next line\n" . $herecurr); @@ -4780,24 +5753,16 @@ sub process { while ($line =~ m{($Constant|$Lval)}g) { my $var = $1; -#gcc binary extension - if ($var =~ /^$Binary$/) { - if (WARN("GCC_BINARY_CONSTANT", - "Avoid gcc v4.3+ binary constant extension: <$var>\n" . $herecurr) && - $fix) { - my $hexval = sprintf("0x%x", oct($var)); - $fixed[$fixlinenr] =~ - s/\b$var\b/$hexval/; - } - } - #CamelCase if ($var !~ /^$Constant$/ && $var =~ /[A-Z][a-z]|[a-z][A-Z]/ && +#Ignore some autogenerated defines and enum values + $var !~ /^(?:[A-Z]+_){1,5}[A-Z]{1,3}[a-z]/ && #Ignore Page<foo> variants $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ && -#Ignore SI style variants like nS, mV and dB (ie: max_uV, regulator_min_uA_show) - $var !~ /^(?:[a-z_]*?)_?[a-z][A-Z](?:_[a-z_]+)?$/ && +#Ignore SI style variants like nS, mV and dB +#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE) + $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ && #Ignore some three character SI units explicitly, like MiB and KHz $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) { while ($var =~ m{($Ident)}g) { @@ -4879,6 +5844,7 @@ sub process { if (defined $define_args && $define_args ne "") { $define_args = substr($define_args, 1, length($define_args) - 2); $define_args =~ s/\s*//g; + $define_args =~ s/\\\+?//g; @def_args = split(",", $define_args); $complex = 1; } @@ -4889,13 +5855,13 @@ sub process { $dstat =~ s/\s*$//s; # Flatten any parentheses and braces - while ($dstat =~ s/\([^\(\)]*\)/1/ || - $dstat =~ s/\{[^\{\}]*\}/1/ || - $dstat =~ s/.\[[^\[\]]*\]/1/) + while ($dstat =~ s/\([^\(\)]*\)/1u/ || + $dstat =~ s/\{[^\{\}]*\}/1u/ || + $dstat =~ s/.\[[^\[\]]*\]/1u/) { } - # Flatten any obvious string concatentation. + # Flatten any obvious string concatenation. while ($dstat =~ s/($String)\s*$Ident/$1/ || $dstat =~ s/$Ident\s*($String)/$1/) { @@ -4920,12 +5886,8 @@ sub process { #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n"; $ctx =~ s/\n*$//; - my $herectx = $here . "\n"; my $stmt_cnt = statement_rawlines($ctx); - - for (my $n = 0; $n < $stmt_cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $stmt_cnt, $here); if ($dstat ne '' && $dstat !~ /^(?:$Ident|-?$Constant),$/ && # 10, // foo(), @@ -4936,6 +5898,7 @@ sub process { $dstat !~ /^\.$Ident\s*=/ && # .foo = $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ && # stringification #foo $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ && # do {...} while (...); // do {...} while (...) + $dstat !~ /^while\s*$Constant\s*$Constant\s*$/ && # while (...) {...} $dstat !~ /^for\s*$Constant$/ && # for (...) $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ && # for (...) bar() $dstat !~ /^do\s*{/ && # do {... @@ -4977,10 +5940,10 @@ sub process { next if ($arg =~ /\.\.\./); next if ($arg =~ /^type$/i); my $tmp_stmt = $define_stmt; - $tmp_stmt =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; + $tmp_stmt =~ s/\b(__must_be_array|offsetof|sizeof|sizeof_field|__stringify|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g; $tmp_stmt =~ s/\#+\s*$arg\b//g; $tmp_stmt =~ s/\b$arg\s*\#\#//g; - my $use_cnt = $tmp_stmt =~ s/\b$arg\b//g; + my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g; if ($use_cnt > 1) { CHK("MACRO_ARG_REUSE", "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx"); @@ -4997,12 +5960,9 @@ sub process { # check for macros with flow control, but without ## concatenation # ## concatenation is commonly a macro that defines a function so ignore those if ($has_flow_statement && !$has_arg_concat) { - my $herectx = $here . "\n"; my $cnt = statement_rawlines($ctx); + my $herectx = get_stat_here($linenr, $cnt, $here); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } WARN("MACRO_WITH_FLOW_CONTROL", "Macros with flow control statements should be avoided\n" . "$herectx"); } @@ -5022,7 +5982,7 @@ sub process { # do {} while (0) macro tests: # single-statement macros do not need to be enclosed in do while (0) loop, # macro should not end with a semicolon - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $realfile !~ m@/vmlinux.lds.h$@ && $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) { my $ln = $linenr; @@ -5042,11 +6002,7 @@ sub process { $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); if (($stmts =~ tr/;/;/) == 1 && $stmts !~ /^\s*(if|while|for|switch)\b/) { @@ -5060,27 +6016,13 @@ sub process { } elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) { $ctx =~ s/\n*$//; my $cnt = statement_rawlines($ctx); - my $herectx = $here . "\n"; - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); WARN("TRAILING_SEMICOLON", "macros should not use a trailing semicolon\n" . "$herectx"); } } -# make sure symbols are always wrapped with VMLINUX_SYMBOL() ... -# all assignments may have only one of the following with an assignment: -# . -# ALIGN(...) -# VMLINUX_SYMBOL(...) - if ($realfile eq 'vmlinux.lds.h' && $line =~ /(?:(?:^|\s)$Ident\s*=|=\s*$Ident(?:\s|$))/) { - WARN("MISSING_VMLINUX_SYMBOL", - "vmlinux.lds.h needs VMLINUX_SYMBOL() around C-visible symbols\n" . $herecurr); - } - # check for redundant bracing round if etc if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) { my ($level, $endln, @chunks) = @@ -5187,12 +6129,8 @@ sub process { } } if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) { - my $herectx = $here . "\n"; my $cnt = statement_rawlines($block); - - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); WARN("BRACES", "braces {} are not necessary for single statement blocks\n" . $herectx); @@ -5200,28 +6138,28 @@ sub process { } if (!defined $suppress_ifbraces{$linenr - 1} && - $line =~ /\b(frr_with_)/) { - my ($level, $endln, @chunks) = - ctx_statement_full($linenr, $realcnt, $-[0]); + $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); - # Check the condition. - my ($cond, $block) = @{$chunks[0]}; - #print "CHECKING<$linenr> cond<$cond> block<$block>\n"; - if (defined $cond) { - substr($block, 0, length($cond), ''); + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; } - 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); - } + WARN("BRACES", + "braces {} are mandatory for frr_with_* blocks\n" . $herectx); + } } # check for single line unbalanced braces @@ -5304,6 +6242,17 @@ sub process { "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr); } +# check for unnecessary function tracing like uses +# This does not use $logFunctions because there are many instances like +# 'dprintk(FOO, "%s()\n", __func__);' which do not match $logFunctions + if ($rawline =~ /^\+.*\([^"]*"$tracing_logging_tags{0,3}%s(?:\s*\(\s*\)\s*)?$tracing_logging_tags{0,3}(?:\\n)?"\s*,\s*__func__\s*\)\s*;/) { + if (WARN("TRACING_LOGGING", + "Unnecessary ftrace-like logging - prefer using ftrace\n" . $herecurr) && + $fix) { + fix_delete_line($fixlinenr, $rawline); + } + } + # check for spaces before a quoted newline if ($rawline =~ /^.*\".*\s\\n/) { if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE", @@ -5315,15 +6264,30 @@ sub process { } # concatenated string without spaces between elements - if ($line =~ /$String[A-Z_]/ || $line =~ /[A-Za-z0-9_]$String/) { - CHK("CONCATENATED_STRING", - "Concatenated strings should use spaces between elements\n" . $herecurr); + if ($line =~ /$String[A-Z_]/ || + ($line =~ /([A-Za-z0-9_]+)$String/ && $1 !~ /^[Lu]$/)) { + if (CHK("CONCATENATED_STRING", + "Concatenated strings should use spaces between elements\n" . $herecurr) && + $fix) { + while ($line =~ /($String)/g) { + my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); + $fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/; + $fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/; + } + } } # uncoalesced string fragments - if ($line =~ /$String\s*"/) { - CHK("STRING_FRAGMENTS", - "Consecutive strings are generally better as a single string\n" . $herecurr); + if ($line =~ /$String\s*[Lu]?"/) { + # FRR: WARN -> CHK + if (CHK("STRING_FRAGMENTS", + "Consecutive strings are generally better as a single string\n" . $herecurr) && + $fix) { + while ($line =~ /($String)(?=\s*")/g) { + my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]); + $fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e; + } + } } # check for non-standard and hex prefixed decimal printf formats @@ -5352,16 +6316,21 @@ sub process { } # check for line continuations in quoted strings with odd counts of " - if ($rawline =~ /\\$/ && $rawline =~ tr/"/"/ % 2) { + if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) { WARN("LINE_CONTINUATIONS", "Avoid line continuations in quoted strings\n" . $herecurr); } # warn about #if 0 if ($line =~ /^.\s*\#\s*if\s+0\b/) { - CHK("REDUNDANT_CODE", - "if this code is redundant consider removing it\n" . - $herecurr); + WARN("IF_0", + "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr); + } + +# warn about #if 1 + if ($line =~ /^.\s*\#\s*if\s+1\b/) { + WARN("IF_1", + "Consider removing the #if 1 and its #endif\n" . $herecurr); } # check for needless "if (<foo>) fn(<foo>)" uses @@ -5408,7 +6377,8 @@ sub process { my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0); # print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n"); - if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*(?:devm_)?(?:[kv][czm]alloc(?:_node|_array)?\b|kstrdup|kmemdup|(?:dev_)?alloc_skb)/) { + if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ && + $s !~ /\b__GFP_NOWARN\b/ ) { WARN("OOM_MESSAGE", "Possible unnecessary 'out of memory' message\n" . $hereprev); } @@ -5431,8 +6401,30 @@ sub process { "Avoid logging continuation uses where feasible\n" . $herecurr); } +# check for unnecessary use of %h[xudi] and %hh[xudi] in logging functions + if (defined $stat && + $line =~ /\b$logFunctions\s*\(/ && + index($stat, '"') >= 0) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = get_stat_real($linenr, $lc); + pos($stat_real) = index($stat_real, '"'); + while ($stat_real =~ /[^\"%]*(%[\#\d\.\*\-]*(h+)[idux])/g) { + my $pspec = $1; + my $h = $2; + my $lineoff = substr($stat_real, 0, $-[1]) =~ tr@\n@@; + if (WARN("UNNECESSARY_MODIFIER", + "Integer promotion: Using '$h' in '$pspec' is unnecessary\n" . "$here\n$stat_real\n") && + $fix && $fixed[$fixlinenr + $lineoff] =~ /^\+/) { + my $nspec = $pspec; + $nspec =~ s/h//g; + $fixed[$fixlinenr + $lineoff] =~ s/\Q$pspec\E/$nspec/; + } + } + } + # check for mask then right shift without a parentheses - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ && $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so WARN("MASK_THEN_SHIFT", @@ -5440,7 +6432,7 @@ sub process { } # check for pointer comparisons to NULL - if ($^V && $^V ge 5.10.0) { + if ($perl_version_ok) { while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) { my $val = $1; my $equal = "!"; @@ -5529,7 +6521,7 @@ sub process { # ignore udelay's < 10, however if (! ($delay < 10) ) { CHK("USLEEP_RANGE", - "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.txt\n" . $herecurr); + "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr); } if ($delay > 2000) { WARN("LONG_UDELAY", @@ -5541,7 +6533,7 @@ sub process { if ($line =~ /\bmsleep\s*\((\d+)\);/) { if ($1 < 20) { WARN("MSLEEP", - "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.txt\n" . $herecurr); + "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr); } } @@ -5589,8 +6581,7 @@ sub process { my $barriers = qr{ mb| rmb| - wmb| - read_barrier_depends + wmb }x; my $barrier_stems = qr{ mb__before_atomic| @@ -5631,6 +6622,14 @@ sub process { } } +# check for data_race without a comment. + if ($line =~ /\bdata_race\s*\(/) { + if (!ctx_has_comment($first_line, $linenr)) { + WARN("DATA_RACE", + "data_race without comment\n" . $herecurr); + } + } + # check of hardware specific defines if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) { CHK("ARCH_DEFINES", @@ -5670,55 +6669,86 @@ sub process { } } -# -# Kernel macros unnused for FRR -# -# Check for __attribute__ packed, prefer __packed -# if ($realfile !~ m@\binclude/uapi/@ && -# $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) { -# WARN("PREFER_PACKED", -# "__packed is preferred over __attribute__((packed))\n" . $herecurr); -# } - -# Check for __attribute__ aligned, prefer __aligned -# if ($realfile !~ m@\binclude/uapi/@ && -# $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) { -# WARN("PREFER_ALIGNED", -# "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr); -# } - -# Check for __attribute__ format(printf, prefer __printf -# if ($realfile !~ m@\binclude/uapi/@ && -# $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) { -# if (WARN("PREFER_PRINTF", -# "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) && -# $fix) { -# $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex; - -# } -# } +# Check for compiler attributes + # FRR: doesn't use kernel macro replacements for compiler attributes. + if (!$frr && + $realfile !~ m@\binclude/uapi/@ && + $rawline =~ /\b__attribute__\s*\(\s*($balanced_parens)\s*\)/) { + my $attr = $1; + $attr =~ s/\s*\(\s*(.*)\)\s*/$1/; + + my %attr_list = ( + "alias" => "__alias", + "aligned" => "__aligned", + "always_inline" => "__always_inline", + "assume_aligned" => "__assume_aligned", + "cold" => "__cold", + "const" => "__attribute_const__", + "copy" => "__copy", + "designated_init" => "__designated_init", + "externally_visible" => "__visible", + "format" => "printf|scanf", + "gnu_inline" => "__gnu_inline", + "malloc" => "__malloc", + "mode" => "__mode", + "no_caller_saved_registers" => "__no_caller_saved_registers", + "noclone" => "__noclone", + "noinline" => "noinline", + "nonstring" => "__nonstring", + "noreturn" => "__noreturn", + "packed" => "__packed", + "pure" => "__pure", + "section" => "__section", + "used" => "__used", + "weak" => "__weak" + ); + + while ($attr =~ /\s*(\w+)\s*(${balanced_parens})?/g) { + my $orig_attr = $1; + my $params = ''; + $params = $2 if defined($2); + my $curr_attr = $orig_attr; + $curr_attr =~ s/^[\s_]+|[\s_]+$//g; + if (exists($attr_list{$curr_attr})) { + my $new = $attr_list{$curr_attr}; + if ($curr_attr eq "format" && $params) { + $params =~ /^\s*\(\s*(\w+)\s*,\s*(.*)/; + $new = "__$1\($2"; + } else { + $new = "$new$params"; + } + if (WARN("PREFER_DEFINED_ATTRIBUTE_MACRO", + "Prefer $new over __attribute__(($orig_attr$params))\n" . $herecurr) && + $fix) { + my $remove = "\Q$orig_attr\E" . '\s*' . "\Q$params\E" . '(?:\s*,\s*)?'; + $fixed[$fixlinenr] =~ s/$remove//; + $fixed[$fixlinenr] =~ s/\b__attribute__/$new __attribute__/; + $fixed[$fixlinenr] =~ s/\}\Q$new\E/} $new/; + $fixed[$fixlinenr] =~ s/ __attribute__\s*\(\s*\(\s*\)\s*\)//; + } + } + } -# Check for __attribute__ format(scanf, prefer __scanf -# if ($realfile !~ m@\binclude/uapi/@ && -# $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) { -# if (WARN("PREFER_SCANF", -# "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) && -# $fix) { -# $fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex; -# } -# } + # Check for __attribute__ unused, prefer __always_unused or __maybe_unused + if ($attr =~ /^_*unused/) { + WARN("PREFER_DEFINED_ATTRIBUTE_MACRO", + "__always_unused or __maybe_unused is preferred over __attribute__((__unused__))\n" . $herecurr); + } + } # Check for __attribute__ weak, or __weak declarations (may have link issues) -# if ($^V && $^V ge 5.10.0 && -# $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && -# ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || -# $line =~ /\b__weak\b/)) { -# ERROR("WEAK_DECLARATION", -# "Using weak declarations can have unintended link defects\n" . $herecurr); -# } + if (!$frr && + $perl_version_ok && + $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ && + ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ || + $line =~ /\b__weak\b/)) { + ERROR("WEAK_DECLARATION", + "Using weak declarations can have unintended link defects\n" . $herecurr); + } # check for c99 types like uint8_t used outside of uapi/ and tools/ - if ($realfile !~ m@\binclude/uapi/@ && + if (!$frr && + $realfile !~ m@\binclude/uapi/@ && $realfile !~ m@\btools/@ && $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) { my $type = $1; @@ -5740,18 +6770,18 @@ sub process { if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) { my $cast = $1; my $const = $2; + my $suffix = ""; + my $newconst = $const; + $newconst =~ s/${Int_type}$//; + $suffix .= 'U' if ($cast =~ /\bunsigned\b/); + if ($cast =~ /\blong\s+long\b/) { + $suffix .= 'LL'; + } elsif ($cast =~ /\blong\b/) { + $suffix .= 'L'; + } if (WARN("TYPECAST_INT_CONSTANT", - "Unnecessary typecast of c90 int constant\n" . $herecurr) && + "Unnecessary typecast of c90 int constant - '$cast$const' could be '$const$suffix'\n" . $herecurr) && $fix) { - my $suffix = ""; - my $newconst = $const; - $newconst =~ s/${Int_type}$//; - $suffix .= 'U' if ($cast =~ /\bunsigned\b/); - if ($cast =~ /\blong\s+long\b/) { - $suffix .= 'LL'; - } elsif ($cast =~ /\blong\b/) { - $suffix .= 'L'; - } $fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/; } } @@ -5790,34 +6820,61 @@ sub process { } } - # check for vsprintf extension %p<foo> misuses - if (0 && $^V && $^V ge 5.10.0 && +# check for vsprintf extension %p<foo> misuses + if (!$frr && + $perl_version_ok && defined $stat && $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s && $1 !~ /^_*volatile_*$/) { - my $bad_extension = ""; + my $stat_real; + my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; for (my $count = $linenr; $count <= $lc; $count++) { + my $specifier; + my $extension; + my $qualifier; + my $bad_specifier = ""; my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0)); $fmt =~ s/%%//g; - if ($fmt =~ /(\%[\*\d\.]*p(?![\WFfSsBKRraEhMmIiUDdgVCbGNOx]).)/) { - $bad_extension = $1; - last; + + while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) { + $specifier = $1; + $extension = $2; + $qualifier = $3; + if ($extension !~ /[4SsBKRraEehMmIiUDdgVCbGNOxtf]/ || + ($extension eq "f" && + defined $qualifier && $qualifier !~ /^w/) || + ($extension eq "4" && + defined $qualifier && $qualifier !~ /^cc/)) { + $bad_specifier = $specifier; + last; + } + if ($extension eq "x" && !defined($stat_real)) { + if (!defined($stat_real)) { + $stat_real = get_stat_real($linenr, $lc); + } + WARN("VSPRINTF_SPECIFIER_PX", + "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n"); + } } - } - if ($bad_extension ne "") { - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); + if ($bad_specifier ne "") { + my $stat_real = get_stat_real($linenr, $lc); + my $ext_type = "Invalid"; + my $use = ""; + if ($bad_specifier =~ /p[Ff]/) { + $use = " - use %pS instead"; + $use =~ s/pS/ps/ if ($bad_specifier =~ /pf/); + } + + WARN("VSPRINTF_POINTER_EXTENSION", + "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n"); } - WARN("VSPRINTF_POINTER_EXTENSION", - "Invalid vsprintf pointer extension '$bad_extension'\n" . "$here\n$stat_real\n"); } } # Check for misused memsets - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) { @@ -5835,7 +6892,7 @@ sub process { } # Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar) -# if ($^V && $^V ge 5.10.0 && +# if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # if (WARN("PREFER_ETHER_ADDR_COPY", @@ -5846,7 +6903,7 @@ sub process { # } # Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar) -# if ($^V && $^V ge 5.10.0 && +# if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # WARN("PREFER_ETHER_ADDR_EQUAL", @@ -5855,7 +6912,7 @@ sub process { # check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr # check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr -# if ($^V && $^V ge 5.10.0 && +# if ($perl_version_ok && # defined $stat && # $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) { # @@ -5876,8 +6933,15 @@ sub process { # } # } +# strlcpy uses that should likely be strscpy + if (!$frr && + $line =~ /\bstrlcpy\s*\(/) { + WARN("STRLCPY", + "Prefer strscpy over strlcpy - see: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw\@mail.gmail.com/\n" . $herecurr); + } + # typecasts on min/max could be min_t/max_t - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) { if (defined $2 || defined $7) { @@ -5901,23 +6965,23 @@ sub process { } # check usleep_range arguments - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) { my $min = $1; my $max = $7; if ($min eq $max) { WARN("USLEEP_RANGE", - "usleep_range should not use min == max args; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); } elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ && $min > $max) { WARN("USLEEP_RANGE", - "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.txt\n" . "$here\n$stat\n"); + "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n"); } } # check for naked sscanf - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $line =~ /\bsscanf\b/ && ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ && @@ -5925,14 +6989,30 @@ sub process { $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } + my $stat_real = get_stat_real($linenr, $lc); WARN("NAKED_SSCANF", "unchecked sscanf return value\n" . "$here\n$stat_real\n"); } +# check for simple sscanf that should be kstrto<foo> + if (!$frr && + $perl_version_ok && + defined $stat && + $line =~ /\bsscanf\b/) { + my $lc = $stat =~ tr@\n@@; + $lc = $lc + $linenr; + my $stat_real = get_stat_real($linenr, $lc); + if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) { + my $format = $6; + my $count = $format =~ tr@%@%@; + if ($count == 1 && + $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) { + WARN("SSCANF_TO_KSTRTO", + "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n"); + } + } + } + # check for new externs in .h files. if ($realfile =~ /\.h$/ && $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) { @@ -5954,8 +7034,7 @@ sub process { if (defined $cond) { substr($s, 0, length($cond), ''); } - if ($s =~ /^\s*;/ && - $function_name ne 'uninitialized_var') + if ($s =~ /^\s*;/) { WARN("AVOID_EXTERNS", "externs should be avoided in .c files\n" . $herecurr); @@ -5988,7 +7067,7 @@ sub process { } # check for function definitions - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) { $context_function = $1; @@ -6016,26 +7095,26 @@ sub process { if (!grep(/$name/, @setup_docs)) { CHK("UNDOCUMENTED_SETUP", - "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.rst\n" . $herecurr); + "__setup appears un-documented -- check Documentation/admin-guide/kernel-parameters.txt\n" . $herecurr); } } -# check for pointless casting of kmalloc return - if ($line =~ /\*\s*\)\s*[kv][czm]alloc(_node){0,1}\b/) { +# check for pointless casting of alloc functions + if ($line =~ /\*\s*\)\s*$allocFunctions\b/) { WARN("UNNECESSARY_CASTS", "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr); } # alloc style # p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...) - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*([kv][mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { + if ($perl_version_ok && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) { CHK("ALLOC_SIZEOF_STRUCT", "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr); } # check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) { my $oldfunc = $3; @@ -6051,12 +7130,9 @@ sub process { } if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ && !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) { - my $ctx = ''; - my $herectx = $here . "\n"; my $cnt = statement_rawlines($stat); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); + if (WARN("ALLOC_WITH_MULTIPLY", "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) && $cnt == 1 && @@ -6067,14 +7143,15 @@ sub process { } # check for krealloc arg reuse - if ($^V && $^V ge 5.10.0 && - $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*\1\s*,/) { + if ($perl_version_ok && + $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ && + $1 eq $3) { WARN("KREALLOC_ARG_REUSE", "Reusing the krealloc arg is almost always a bug\n" . $herecurr); } # check for alloc argument mismatch - if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) { + if ($line =~ /\b((?:devm_)?(?:kcalloc|kmalloc_array))\s*\(\s*sizeof\b/) { WARN("ALLOC_ARRAY_ARGS", "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr); } @@ -6100,51 +7177,51 @@ sub process { } } +# check for IS_ENABLED() without CONFIG_<FOO> ($rawline for comments too) + if ($rawline =~ /\bIS_ENABLED\s*\(\s*(\w+)\s*\)/ && $1 !~ /^${CONFIG_}/) { + WARN("IS_ENABLED_CONFIG", + "IS_ENABLED($1) is normally used as IS_ENABLED(${CONFIG_}$1)\n" . $herecurr); + } + # check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE - if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { + if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(${CONFIG_}[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) { my $config = $1; if (WARN("PREFER_IS_ENABLED", - "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) && + "Prefer IS_ENABLED(<FOO>) to ${CONFIG_}<FOO> || ${CONFIG_}<FOO>_MODULE\n" . $herecurr) && $fix) { $fixed[$fixlinenr] = "\+#if IS_ENABLED($config)"; } } -# check for case / default statements not preceded by break/fallthrough/switch - if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) { - my $has_break = 0; - my $has_statement = 0; - my $count = 0; - my $prevline = $linenr; - while ($prevline > 1 && ($file || $count < 3) && !$has_break) { - $prevline--; - my $rline = $rawlines[$prevline - 1]; - my $fline = $lines[$prevline - 1]; - last if ($fline =~ /^\@\@/); - next if ($fline =~ /^\-/); - next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/); - $has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i); - next if ($fline =~ /^.[\s$;]*$/); - $has_statement = 1; - $count++; - $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/); - } - if (!$has_break && $has_statement) { - WARN("MISSING_BREAK", - "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr); +# check for /* fallthrough */ like comment, prefer fallthrough; + my @fallthroughs = ( + 'fallthrough', + '@fallthrough@', + 'lint -fallthrough[ \t]*', + 'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)', + '(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?', + 'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', + 'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?', + ); + if ($raw_comment ne '') { + foreach my $ft (@fallthroughs) { + if ($raw_comment =~ /$ft/) { + my $msg_level = \&WARN; + $msg_level = \&CHK if ($file); + &{$msg_level}("PREFER_FALLTHROUGH", + "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr); + last; + } } } # check for switch/default statements without a break; - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && defined $stat && $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { - my $ctx = ''; - my $herectx = $here . "\n"; my $cnt = statement_rawlines($stat); - for (my $n = 0; $n < $cnt; $n++) { - $herectx .= raw_line($linenr, $n) . "\n"; - } + my $herectx = get_stat_here($linenr, $cnt, $here); + WARN("DEFAULT_NO_BREAK", "switch default: should use break\n" . $herectx); } @@ -6203,20 +7280,53 @@ sub process { "consider using a completion\n" . $herecurr); } +# recommend kstrto* over simple_strto* and strict_strto* + if (!$frr && + $line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) { + WARN("CONSIDER_KSTRTO", + "$1 is obsolete, use k$3 instead\n" . $herecurr); + } + # check for __initcall(), use device_initcall() explicitly or more appropriate function please if ($line =~ /^.\s*__initcall\s*\(/) { WARN("USE_DEVICE_INITCALL", "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr); } +# check for spin_is_locked(), suggest lockdep instead + if ($line =~ /\bspin_is_locked\(/) { + WARN("USE_LOCKDEP", + "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr); + } + +# check for deprecated apis + if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) { + my $deprecated_api = $1; + my $new_api = $deprecated_apis{$deprecated_api}; + WARN("DEPRECATED_API", + "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr); + } + +# check for various structs that are normally const (ops, kgdb, device_tree) +# and avoid what seem like struct definitions 'struct foo {' + if (!$frr && + defined($const_structs) && + $line !~ /\bconst\b/ && + $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) { + WARN("CONST_STRUCT", + "struct $1 should normally be const\n" . $herecurr); + } + # use of NR_CPUS is usually wrong # ignore definitions of NR_CPUS and usage to define arrays as likely right +# ignore designated initializers using NR_CPUS if ($line =~ /\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ && $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ && $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ && $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ && - $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/) + $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/ && + $line !~ /^.\s*\.\w+\s*=\s*.*\bNR_CPUS\b/) { WARN("NR_CPUS", "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr); @@ -6229,12 +7339,29 @@ sub process { } # likely/unlikely comparisons similar to "(likely(foo) > 0)" - if ($^V && $^V ge 5.10.0 && + if ($perl_version_ok && $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) { WARN("LIKELY_MISUSE", "Using $1 should generally have parentheses around the comparison\n" . $herecurr); } +# return sysfs_emit(foo, fmt, ...) fmt without newline + if ($line =~ /\breturn\s+sysfs_emit\s*\(\s*$FuncArg\s*,\s*($String)/ && + substr($rawline, $-[6], $+[6] - $-[6]) !~ /\\n"$/) { + my $offset = $+[6] - 1; + if (WARN("SYSFS_EMIT", + "return sysfs_emit(...) formats should include a terminating newline\n" . $herecurr) && + $fix) { + substr($fixed[$fixlinenr], $offset, 0) = '\\n'; + } + } + +# nested likely/unlikely calls + if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) { + WARN("LIKELY_MISUSE", + "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr); + } + # whine mightly about in_atomic if ($line =~ /\bin_atomic\s*\(/) { if ($realfile =~ m@^drivers/@) { @@ -6246,34 +7373,6 @@ sub process { } } -# whine about ACCESS_ONCE - if ($^V && $^V ge 5.10.0 && - $line =~ /\bACCESS_ONCE\s*$balanced_parens\s*(=(?!=))?\s*($FuncArg)?/) { - my $par = $1; - my $eq = $2; - my $fun = $3; - $par =~ s/^\(\s*(.*)\s*\)$/$1/; - if (defined($eq)) { - if (WARN("PREFER_WRITE_ONCE", - "Prefer WRITE_ONCE(<FOO>, <BAR>) over ACCESS_ONCE(<FOO>) = <BAR>\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)\s*$eq\s*\Q$fun\E/WRITE_ONCE($par, $fun)/; - } - } else { - if (WARN("PREFER_READ_ONCE", - "Prefer READ_ONCE(<FOO>) over ACCESS_ONCE(<FOO>)\n" . $herecurr) && - $fix) { - $fixed[$fixlinenr] =~ s/\bACCESS_ONCE\s*\(\s*\Q$par\E\s*\)/READ_ONCE($par)/; - } - } - } - -# check for mutex_trylock_recursive usage - if ($line =~ /mutex_trylock_recursive/) { - ERROR("LOCKING", - "recursive locking is bad, do not use this ever.\n" . $herecurr); - } - # check for lockdep_set_novalidate_class if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ || $line =~ /__lockdep_no_validate__\s*\)/ ) { @@ -6291,9 +7390,70 @@ sub process { "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr); } +# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO> +# and whether or not function naming is typical and if +# DEVICE_ATTR permissions uses are unusual too + if ($perl_version_ok && + defined $stat && + $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) { + my $var = $1; + my $perms = $2; + my $show = $3; + my $store = $4; + my $octal_perms = perms_to_octal($perms); + if ($show =~ /^${var}_show$/ && + $store =~ /^${var}_store$/ && + $octal_perms eq "0644") { + if (WARN("DEVICE_ATTR_RW", + "Use DEVICE_ATTR_RW\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/; + } + } elsif ($show =~ /^${var}_show$/ && + $store =~ /^NULL$/ && + $octal_perms eq "0444") { + if (WARN("DEVICE_ATTR_RO", + "Use DEVICE_ATTR_RO\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/; + } + } elsif ($show =~ /^NULL$/ && + $store =~ /^${var}_store$/ && + $octal_perms eq "0200") { + if (WARN("DEVICE_ATTR_WO", + "Use DEVICE_ATTR_WO\n" . $herecurr) && + $fix) { + $fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/; + } + } elsif ($octal_perms eq "0644" || + $octal_perms eq "0444" || + $octal_perms eq "0200") { + my $newshow = "$show"; + $newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show"); + my $newstore = $store; + $newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store"); + my $rename = ""; + if ($show ne $newshow) { + $rename .= " '$show' to '$newshow'"; + } + if ($store ne $newstore) { + $rename .= " '$store' to '$newstore'"; + } + WARN("DEVICE_ATTR_FUNCTIONS", + "Consider renaming function(s)$rename\n" . $herecurr); + } else { + WARN("DEVICE_ATTR_PERMS", + "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr); + } + } + # Mode permission misuses where it seems decimal should be octal # This uses a shortcut match to avoid unnecessary uses of a slow foreach loop - if ($^V && $^V ge 5.10.0 && +# o Ignore module_param*(...) uses with a decimal 0 permission as that has a +# specific definition of not visible in sysfs. +# o Ignore proc_create*(...) uses with a decimal 0 permission as that means +# use the default permissions + if ($perl_version_ok && defined $stat && $line =~ /$mode_perms_search/) { foreach my $entry (@mode_permission_funcs) { @@ -6302,10 +7462,7 @@ sub process { my $lc = $stat =~ tr@\n@@; $lc = $lc + $linenr; - my $stat_real = raw_line($linenr, 0); - for (my $count = $linenr + 1; $count <= $lc; $count++) { - $stat_real = $stat_real . "\n" . raw_line($count, 0); - } + my $stat_real = get_stat_real($linenr, $lc); my $skip_args = ""; if ($arg_pos > 1) { @@ -6316,8 +7473,9 @@ sub process { if ($stat =~ /$test/) { my $val = $1; $val = $6 if ($skip_args ne ""); - if (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || - ($val =~ /^$Octal$/ && length($val) ne 4)) { + if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") && + (($val =~ /^$Int$/ && $val !~ /^$Octal$/) || + ($val =~ /^$Octal$/ && length($val) ne 4))) { ERROR("NON_OCTAL_PERMISSIONS", "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real); } @@ -6330,30 +7488,13 @@ sub process { } # check for uses of S_<PERMS> that could be octal for readability - if ($line =~ /\b$mode_perms_string_search\b/) { - my $val = ""; - my $oval = ""; - my $to = 0; - my $curpos = 0; - my $lastpos = 0; - while ($line =~ /\b(($mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) { - $curpos = pos($line); - my $match = $2; - my $omatch = $1; - last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos)); - $lastpos = $curpos; - $to |= $mode_permission_string_types{$match}; - $val .= '\s*\|\s*' if ($val ne ""); - $val .= $match; - $oval .= $omatch; - } - $oval =~ s/^\s*\|\s*//; - $oval =~ s/\s*\|\s*$//; - my $octal = sprintf("%04o", $to); + while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) { + my $oval = $1; + my $octal = perms_to_octal($oval); if (WARN("SYMBOLIC_PERMS", "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) && $fix) { - $fixed[$fixlinenr] =~ s/$val/$octal/; + $fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/; } } @@ -6375,6 +7516,12 @@ sub process { } } +# check for sysctl duplicate constants + if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) { + WARN("DUPLICATED_SYSCTL_CONST", + "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); + } + # check for usage of nonstandard fixed-width integral types if ($line =~ /u_int8_t/ || $line =~ /u_int32_t/ || @@ -6437,7 +7584,7 @@ sub process { exit(0); } - # This is not a patch, and we are are in 'no-patch' mode so + # This is not a patch, and we are in 'no-patch' mode so # just keep quiet. if (!$chk_patch && !$is_patch) { exit(0); @@ -6447,9 +7594,38 @@ sub process { ERROR("NOT_UNIFIED_DIFF", "Does not appear to be a unified-diff format patch\n"); } - if ($is_patch && $has_commit_log && $chk_signoff && $signoff == 0) { - ERROR("MISSING_SIGN_OFF", - "Missing Signed-off-by: line(s)\n"); + if ($is_patch && $has_commit_log && $chk_signoff) { + if ($signoff == 0) { + ERROR("MISSING_SIGN_OFF", + "Missing Signed-off-by: line(s)\n"); + } elsif ($authorsignoff != 1) { + # authorsignoff values: + # 0 -> missing sign off + # 1 -> sign off identical + # 2 -> names and addresses match, comments mismatch + # 3 -> addresses match, names different + # 4 -> names match, addresses different + # 5 -> names match, addresses excluding subaddress details (refer RFC 5233) match + + my $sob_msg = "'From: $author' != 'Signed-off-by: $author_sob'"; + + if ($authorsignoff == 0) { + ERROR("NO_AUTHOR_SIGN_OFF", + "Missing Signed-off-by: line by nominal patch author '$author'\n"); + } elsif ($authorsignoff == 2) { + CHK("FROM_SIGN_OFF_MISMATCH", + "From:/Signed-off-by: email comments mismatch: $sob_msg\n"); + } elsif ($authorsignoff == 3) { + WARN("FROM_SIGN_OFF_MISMATCH", + "From:/Signed-off-by: email name mismatch: $sob_msg\n"); + } elsif ($authorsignoff == 4) { + WARN("FROM_SIGN_OFF_MISMATCH", + "From:/Signed-off-by: email address mismatch: $sob_msg\n"); + } elsif ($authorsignoff == 5) { + WARN("FROM_SIGN_OFF_MISMATCH", + "From:/Signed-off-by: email subaddress mismatch: $sob_msg\n"); + } + } } print report_dump(); diff --git a/.dir-locals.el b/tools/emacs.dir-locals.el similarity index 100% rename from .dir-locals.el rename to tools/emacs.dir-locals.el diff --git a/tools/etc/frr/daemons b/tools/etc/frr/daemons index 2427bfff7771..c487e7e5f28c 100644 --- a/tools/etc/frr/daemons +++ b/tools/etc/frr/daemons @@ -40,6 +40,7 @@ pathd=no # vtysh_enable=yes zebra_options=" -A 127.0.0.1 -s 90000000" +mgmtd_options=" -A 127.0.0.1" bgpd_options=" -A 127.0.0.1" ospfd_options=" -A 127.0.0.1" ospf6d_options=" -A ::1" diff --git a/tools/frr-llvm-cg.c b/tools/frr-llvm-cg.c index 51e8fb7b7ec1..3a7222e421fc 100644 --- a/tools/frr-llvm-cg.c +++ b/tools/frr-llvm-cg.c @@ -271,12 +271,12 @@ static bool is_thread_sched(const char *name, size_t len) { #define thread_prefix "_" 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", + thread_prefix "event_add_read_write", + thread_prefix "event_add_timer", + thread_prefix "event_add_timer_msec", + thread_prefix "event_add_timer_tv", + thread_prefix "event_add_event", + thread_prefix "event_execute", }; size_t i; diff --git a/tools/frr-reload.py b/tools/frr-reload.py index a69b0a7bf1cb..3a7561cb6944 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -788,6 +788,8 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): # remote-as config. pg_dict = dict() + found_pg_cmd = False + # Find all peer-group commands; create dict of each peer-group # to store assoicated neighbor as value for ctx_keys, line in lines_to_add: @@ -809,6 +811,10 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): } found_pg_cmd = True + # Do nothing if there is no any "peer-group" + if found_pg_cmd is False: + return + # Find peer-group with remote-as command, also search neighbor # associated to peer-group and store into peer-group dict for ctx_keys, line in lines_to_add: @@ -850,7 +856,7 @@ def bgp_delete_nbr_remote_as_line(lines_to_add): for pg in pg_dict[ctx_keys[0]]: if pg_dict[ctx_keys[0]][pg]["remoteas"] == True: for nbr in pg_dict[ctx_keys[0]][pg]["nbr"]: - if re_nbr_rmtas.group(1) in nbr: + if re_nbr_rmtas.group(1) == nbr: lines_to_del_from_add.append((ctx_keys, line)) for ctx_keys, line in lines_to_del_from_add: @@ -885,7 +891,7 @@ def bgp_remove_neighbor_cfg(lines_to_del, del_nbr_dict): lines_to_del.remove((ctx_keys, line)) -def delete_move_lines(lines_to_add, lines_to_del): +def bgp_delete_move_lines(lines_to_add, lines_to_del): # This method handles deletion of bgp peer group config. # The objective is to delete config lines related to peers # associated with the peer-group and move the peer-group @@ -1060,6 +1066,39 @@ def delete_move_lines(lines_to_add, lines_to_del): return (lines_to_add, lines_to_del) +def pim_delete_move_lines(lines_to_add, lines_to_del): + + # Under interface context, if 'no ip pim' is present + # remove subsequent 'no ip pim <blah>' options as it + # they are implicitly deleted by 'no ip pim'. + # Remove all such depdendent options from delete + # pending list. + pim_disable = False + + for (ctx_keys, line) in lines_to_del: + if ctx_keys[0].startswith("interface") and line and line == "ip pim": + pim_disable = True + + if pim_disable: + for (ctx_keys, line) in lines_to_del: + if ( + ctx_keys[0].startswith("interface") + and line + and line.startswith("ip pim ") + ): + lines_to_del.remove((ctx_keys, line)) + + return (lines_to_add, lines_to_del) + + +def delete_move_lines(lines_to_add, lines_to_del): + + lines_to_add, lines_to_del = bgp_delete_move_lines(lines_to_add, lines_to_del) + lines_to_add, lines_to_del = pim_delete_move_lines(lines_to_add, lines_to_del) + + return (lines_to_add, lines_to_del) + + def ignore_delete_re_add_lines(lines_to_add, lines_to_del): # Quite possibly the most confusing (while accurate) variable names in history @@ -1474,10 +1513,17 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del): lines_to_add_to_del.append((tmp_ctx_keys, line)) for (ctx_keys, line) in lines_to_del_to_del: - lines_to_del.remove((ctx_keys, line)) + try: + lines_to_del.remove((ctx_keys, line)) + except ValueError: + pass for (ctx_keys, line) in lines_to_add_to_del: - lines_to_add.remove((ctx_keys, line)) + try: + lines_to_add.remove((ctx_keys, line)) + except ValueError: + pass + return (lines_to_add, lines_to_del) @@ -1531,11 +1577,31 @@ def compare_context_objects(newconf, running): pcclist_to_del = [] candidates_to_add = [] delete_bgpd = False + area_stub_no_sum = "area (\S+) stub no-summary" # Find contexts that are in newconf but not in running # Find contexts that are in running but not in newconf for (running_ctx_keys, running_ctx) in iteritems(running.contexts): + if running_ctx_keys in newconf.contexts: + newconf_ctx = newconf.contexts[running_ctx_keys] + + for line in running_ctx.lines: + # ospf area <> stub no-summary line removal requires + # to remoe area <> stub as no form of original + # retains the stub form. + # lines_to_del will contain: + # no area <x> stub no-summary and + # no area <x> stub + if ( + running_ctx_keys[0].startswith("router ospf") + and line not in newconf_ctx.dlines + ): + re_area_stub_no_sum = re.search(area_stub_no_sum, line) + if re_area_stub_no_sum: + new_del_line = "area %s stub" % re_area_stub_no_sum.group(1) + lines_to_del.append((running_ctx_keys, new_del_line)) + if running_ctx_keys not in newconf.contexts: # We check that the len is 1 here so that we only look at ('router bgp 10') diff --git a/tools/frr.in b/tools/frr.in index 1ffdade54f21..cd24a96054c3 100755 --- a/tools/frr.in +++ b/tools/frr.in @@ -17,6 +17,7 @@ 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 +B_PATH="@CFG_BIN@" VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr @@ -27,7 +28,7 @@ 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 pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd" +DAEMONS="mgmtd zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd" MAX_INSTANCES=5 RELOAD_SCRIPT="$D_PATH/frr-reload.py" @@ -582,7 +583,7 @@ case "$1" in NEW_CONFIG_FILE="${2:-$C_PATH/frr.conf}" [ ! -r $NEW_CONFIG_FILE ] && echo "Unable to read new configuration file $NEW_CONFIG_FILE" && exit 1 echo "Applying only incremental changes to running configuration from frr.conf" - "$RELOAD_SCRIPT" --reload --bindir "$D_PATH" --confdir "$C_PATH" --rundir "$V_PATH" "$C_PATH/frr.conf" + "$RELOAD_SCRIPT" --reload --bindir "$B_PATH" --confdir "$C_PATH" --rundir "$V_PATH" "$C_PATH/frr.conf" exit $? ;; diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in index f1db3a73d528..f1f70119097e 100755 --- a/tools/frrcommon.sh.in +++ b/tools/frrcommon.sh.in @@ -24,6 +24,7 @@ PATH=/bin:/usr/bin:/sbin:/usr/sbin D_PATH="@CFG_SBIN@" # /usr/lib/frr C_PATH="@CFG_SYSCONF@${suffix}" # /etc/frr V_PATH="@CFG_STATE@${suffix}" # /var/run/frr +B_PATH="@CFG_BIN@" VTYSH="@vtysh_bin@" # /usr/bin/vtysh FRR_USER="@enable_user@" # frr FRR_GROUP="@enable_group@" # frr @@ -35,7 +36,7 @@ FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter # - keep zebra first # - watchfrr does NOT belong in this list -DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd" +DAEMONS="zebra mgmtd bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd" RELOAD_SCRIPT="$D_PATH/frr-reload.py" # @@ -99,7 +100,7 @@ daemon_list() { for daemon in $DAEMONS; do eval cfg=\$$daemon eval inst=\$${daemon}_instances - [ "$daemon" = zebra -o "$daemon" = staticd ] && cfg=yes + [ "$daemon" = zebra -o "$daemon" = staticd -o "$daemon" = mgmtd ] && cfg=yes if [ -n "$cfg" -a "$cfg" != "no" -a "$cfg" != "0" ]; then if ! daemon_prep "$daemon" "$inst"; then continue diff --git a/tools/frrinit.sh.in b/tools/frrinit.sh.in index 2396b1a3260f..428d57c55b3d 100644 --- a/tools/frrinit.sh.in +++ b/tools/frrinit.sh.in @@ -122,8 +122,8 @@ reload) 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 --bindir "$D_PATH" --confdir "$C_PATH" --rundir "$V_PATH" "$NEW_CONFIG_FILE" `echo $nsopt` - exit $? + "$RELOAD_SCRIPT" --reload --bindir "$B_PATH" --confdir "$C_PATH" --rundir "$V_PATH" "$NEW_CONFIG_FILE" `echo $nsopt` + exit 0 ;; *) diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 5bb1d6911cbd..b14a6ecc473f 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -673,8 +673,8 @@ struct vrrp_vrouter *vrrp_lookup(const struct interface *ifp, uint8_t vrid) /* Forward decls */ static void vrrp_change_state(struct vrrp_router *r, int to); -static void vrrp_adver_timer_expire(struct thread *thread); -static void vrrp_master_down_timer_expire(struct thread *thread); +static void vrrp_adver_timer_expire(struct event *thread); +static void vrrp_master_down_timer_expire(struct event *thread); /* * Finds the first connected address of the appropriate family on a VRRP @@ -900,11 +900,11 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *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); + EVENT_OFF(r->t_adver_timer); + event_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)) { @@ -913,17 +913,17 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, "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); + EVENT_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); + EVENT_OFF(r->t_master_down_timer); + event_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 */ @@ -936,8 +936,8 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, break; case VRRP_STATE_BACKUP: if (pkt->hdr.priority == 0) { - THREAD_OFF(r->t_master_down_timer); - thread_add_timer_msec( + EVENT_OFF(r->t_master_down_timer); + event_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 @@ -947,11 +947,11 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, 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); + EVENT_OFF(r->t_master_down_timer); + event_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 */ @@ -976,9 +976,9 @@ static int vrrp_recv_advertisement(struct vrrp_router *r, struct ipaddr *src, /* * Read and process next IPvX datagram. */ -static void vrrp_read(struct thread *thread) +static void vrrp_read(struct event *thread) { - struct vrrp_router *r = THREAD_ARG(thread); + struct vrrp_router *r = EVENT_ARG(thread); struct vrrp_pkt *pkt; ssize_t pktsize; @@ -1039,7 +1039,7 @@ static void vrrp_read(struct thread *thread) memset(r->ibuf, 0x00, sizeof(r->ibuf)); if (resched) - thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); + event_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); } /* @@ -1240,7 +1240,13 @@ static int vrrp_socket(struct vrrp_router *r) } /* Turn off multicast loop on Tx */ - setsockopt_ipv6_multicast_loop(r->sock_tx, 0); + if (setsockopt_ipv6_multicast_loop(r->sock_tx, 0) < 0) { + zlog_warn(VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM + "Failed to turn off IPv6 multicast", + r->vr->vrid, family2str(r->family)); + failed = true; + goto done; + } /* Bind Rx socket to exact interface */ frr_with_privs(&vrrp_privs) { @@ -1405,7 +1411,7 @@ static void vrrp_change_state_backup(struct vrrp_router *r) vrrp_zebra_radv_set(r, false); /* Disable Adver_Timer */ - THREAD_OFF(r->t_adver_timer); + EVENT_OFF(r->t_adver_timer); r->advert_pending = false; r->garp_pending = false; @@ -1473,9 +1479,9 @@ static void vrrp_change_state(struct vrrp_router *r, int to) /* * Called when Adver_Timer expires. */ -static void vrrp_adver_timer_expire(struct thread *thread) +static void vrrp_adver_timer_expire(struct event *thread) { - struct vrrp_router *r = THREAD_ARG(thread); + struct vrrp_router *r = EVENT_ARG(thread); DEBUGD(&vrrp_dbg_proto, VRRP_LOGPFX VRRP_LOGPFX_VRID VRRP_LOGPFX_FAM @@ -1487,9 +1493,9 @@ static void vrrp_adver_timer_expire(struct thread *thread) 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); + event_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", @@ -1501,17 +1507,17 @@ static void vrrp_adver_timer_expire(struct thread *thread) /* * Called when Master_Down_Timer expires. */ -static void vrrp_master_down_timer_expire(struct thread *thread) +static void vrrp_master_down_timer_expire(struct event *thread) { - struct vrrp_router *r = THREAD_ARG(thread); + struct vrrp_router *r = EVENT_ARG(thread); 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); + event_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); } @@ -1561,7 +1567,7 @@ static int vrrp_startup(struct vrrp_router *r) } /* Schedule listener */ - thread_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); + event_add_read(master, vrrp_read, r, r->sock_rx, &r->t_read); /* Configure effective priority */ assert(listhead(r->addrs)); @@ -1583,16 +1589,16 @@ static int vrrp_startup(struct vrrp_router *r) } 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); + event_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); + event_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); } @@ -1632,10 +1638,10 @@ static int vrrp_shutdown(struct vrrp_router *r) } /* 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); + EVENT_OFF(r->t_adver_timer); + EVENT_OFF(r->t_master_down_timer); + EVENT_OFF(r->t_read); + EVENT_OFF(r->t_write); /* Protodown macvlan */ if (r->mvl_ifp) @@ -2411,6 +2417,5 @@ void vrrp_fini(void) list_delete(&vrs); - hash_clean(vrrp_vrouters_hash, NULL); - hash_free(vrrp_vrouters_hash); + hash_clean_and_free(&vrrp_vrouters_hash, NULL); } diff --git a/vrrpd/vrrp.h b/vrrpd/vrrp.h index 9da9c8a5182a..0ac9b1f49c1a 100644 --- a/vrrpd/vrrp.h +++ b/vrrpd/vrrp.h @@ -18,7 +18,7 @@ #include "lib/northbound.h" #include "lib/privs.h" #include "lib/stream.h" -#include "lib/thread.h" +#include "lib/frrevent.h" #include "lib/vty.h" /* Global definitions */ @@ -65,7 +65,7 @@ struct vrrp_defaults { extern struct vrrp_defaults vd; /* threadmaster */ -extern struct thread_master *master; +extern struct event_loop *master; /* privileges */ extern struct zebra_privs_t vrrp_privs; @@ -193,10 +193,10 @@ struct vrrp_router { uint32_t trans_cnt; } stats; - struct thread *t_master_down_timer; - struct thread *t_adver_timer; - struct thread *t_read; - struct thread *t_write; + struct event *t_master_down_timer; + struct event *t_adver_timer; + struct event *t_read; + struct event *t_write; }; /* diff --git a/vrrpd/vrrp_main.c b/vrrpd/vrrp_main.c index d3d230d20508..5245c7468924 100644 --- a/vrrpd/vrrp_main.c +++ b/vrrpd/vrrp_main.c @@ -6,11 +6,12 @@ */ #include <zebra.h> +#include <getopt.h> + #include <lib/version.h> #include "lib/command.h" #include "lib/filter.h" -#include "lib/getopt.h" #include "lib/if.h" #include "lib/libfrr.h" #include "lib/log.h" @@ -18,7 +19,7 @@ #include "lib/nexthop.h" #include "lib/privs.h" #include "lib/sigevent.h" -#include "lib/thread.h" +#include "lib/frrevent.h" #include "lib/vrf.h" #include "lib/vty.h" @@ -50,7 +51,7 @@ struct zebra_privs_t vrrp_privs = { struct option longopts[] = { {0} }; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; static struct frr_daemon_info vrrpd_di; diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c index 7a17de747ce7..9971df58a3aa 100644 --- a/vrrpd/vrrp_vty.c +++ b/vrrpd/vrrp_vty.c @@ -398,6 +398,7 @@ static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) json_object_string_add(j, "interface", vr->ifp->name); json_object_int_add(j, "advertisementInterval", vr->advertisement_interval * CS2MS); + json_object_int_add(j, "priority", vr->priority); /* v4 */ json_object_string_add(v4, "interface", vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 200427fb6e1d..ee52a98adb6c 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -49,19 +49,6 @@ char *vtysh_pager_name = NULL; /* VTY should add timestamp */ bool vtysh_add_timestamp; -/* VTY shell client structure */ -struct vtysh_client { - int fd; - const char *name; - int flag; - char path[MAXPATHLEN]; - struct vtysh_client *next; - - struct thread *log_reader; - int log_fd; - uint32_t lost_msgs; -}; - static bool stderr_tty; static bool stderr_stdout_same; @@ -119,7 +106,12 @@ static void vtysh_pager_envdef(bool fallback) /* --- */ +/* + * When updating this array, remember to change the array size here and in + * vtysh.h + */ struct vtysh_client vtysh_client[] = { + {.name = "mgmtd", .flag = VTYSH_MGMTD}, {.name = "zebra", .flag = VTYSH_ZEBRA}, {.name = "ripd", .flag = VTYSH_RIPD}, {.name = "ripngd", .flag = VTYSH_RIPNGD}, @@ -888,6 +880,13 @@ int vtysh_config_from_file(struct vty *vty, FILE *fp) if (strmatch(vty_buf_trimmed, "end")) continue; + if (strmatch(vty_buf_trimmed, "exit") && + vty->node == CONFIG_NODE) { + fprintf(stderr, "line %d: Warning[%d]...: %s\n", lineno, + vty->node, "early exit from config file"); + break; + } + ret = command_config_read_one_line(vty, &cmd, lineno, 1); switch (ret) { @@ -1183,6 +1182,13 @@ static struct cmd_node isis_node = { .parent_node = CONFIG_NODE, .prompt = "%s(config-router)# ", }; + +static struct cmd_node isis_flex_algo_node = { + .name = "isis-flex-algo", + .node = ISIS_FLEX_ALGO_NODE, + .parent_node = ISIS_NODE, + .prompt = "%s(config-router-flex-algo)# ", +}; #endif /* HAVE_ISISD */ #ifdef HAVE_FABRICD @@ -2103,6 +2109,14 @@ DEFUNSH(VTYSH_ISISD, router_isis, router_isis_cmd, vty->node = ISIS_NODE; return CMD_SUCCESS; } + +DEFUNSH(VTYSH_ISISD, isis_flex_algo, isis_flex_algo_cmd, "flex-algo (128-255)", + "Flexible Algorithm\n" + "Flexible Algorithm Number\n") +{ + vty->node = ISIS_FLEX_ALGO_NODE; + return CMD_SUCCESS; +} #endif /* HAVE_ISISD */ #ifdef HAVE_FABRICD @@ -2319,8 +2333,9 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_disable, vtysh_disable_cmd, "disable", } DEFUNSH(VTYSH_REALLYALL, vtysh_config_terminal, vtysh_config_terminal_cmd, - "configure [terminal]", + "configure [terminal [file-lock]]", "Configuration from vty interface\n" + "Configuration with locked datastores\n" "Configuration terminal\n") { vty->node = CONFIG_NODE; @@ -2341,7 +2356,7 @@ static int vtysh_exit(struct vty *vty) if (vty->node == CONFIG_NODE) { /* resync in case one of the daemons is somewhere else */ vtysh_execute("end"); - vtysh_execute("configure"); + vtysh_execute("configure terminal file-lock"); } return CMD_SUCCESS; } @@ -2586,6 +2601,18 @@ DEFUNSH(VTYSH_ISISD, vtysh_quit_isisd, vtysh_quit_isisd_cmd, "quit", { return vtysh_exit_isisd(self, vty, argc, argv); } + +DEFUNSH(VTYSH_ISISD, vtysh_exit_isis_flex_algo, vtysh_exit_isis_flex_algo_cmd, + "exit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit(vty); +} + +DEFUNSH(VTYSH_ISISD, vtysh_quit_isis_flex_algo, vtysh_quit_isis_flex_algo_cmd, + "quit", "Exit current mode and down to previous mode\n") +{ + return vtysh_exit_isisd(self, vty, argc, argv); +} #endif /* HAVE_ISISD */ #if HAVE_BFDD > 0 @@ -3544,7 +3571,7 @@ DEFUN (vtysh_copy_to_running, int ret; const char *fname = argv[1]->arg; - ret = vtysh_read_config(fname, true); + ret = vtysh_apply_config(fname, true, false); /* Return to enable mode - the 'read_config' api leaves us up a level */ vtysh_execute_no_pager("enable"); @@ -3727,9 +3754,9 @@ static void vtysh_log_print(struct vtysh_client *vclient, text + textpos); } -static void vtysh_log_read(struct thread *thread) +static void vtysh_log_read(struct event *thread) { - struct vtysh_client *vclient = THREAD_ARG(thread); + struct vtysh_client *vclient = EVENT_ARG(thread); struct { struct zlog_live_hdr hdr; char text[4096]; @@ -3737,8 +3764,8 @@ static void vtysh_log_read(struct thread *thread) const char *text; ssize_t ret; - thread_add_read(master, vtysh_log_read, vclient, vclient->log_fd, - &vclient->log_reader); + event_add_read(master, vtysh_log_read, vclient, vclient->log_fd, + &vclient->log_reader); ret = recv(vclient->log_fd, &buf, sizeof(buf), 0); @@ -3768,7 +3795,7 @@ static void vtysh_log_read(struct thread *thread) "log monitor connection closed unexpectedly"); buf.hdr.textlen = strlen(buf.text); - THREAD_OFF(vclient->log_reader); + EVENT_OFF(vclient->log_reader); close(vclient->log_fd); vclient->log_fd = -1; @@ -3834,9 +3861,9 @@ DEFPY (vtysh_terminal_monitor, if (fd != -1) { set_nonblocking(fd); vclient->log_fd = fd; - thread_add_read(master, vtysh_log_read, vclient, - vclient->log_fd, - &vclient->log_reader); + event_add_read(master, vtysh_log_read, vclient, + vclient->log_fd, + &vclient->log_reader); } if (ret != CMD_SUCCESS) { vty_out(vty, "%% failed to enable logs on %s\n", @@ -3890,7 +3917,7 @@ DEFPY (no_vtysh_terminal_monitor, * a close notification... */ if (vclient->log_fd != -1) { - THREAD_OFF(vclient->log_reader); + EVENT_OFF(vclient->log_reader); close(vclient->log_fd); vclient->log_fd = -1; @@ -4710,6 +4737,12 @@ void vtysh_init_vty(void) install_element(ISIS_NODE, &vtysh_exit_isisd_cmd); install_element(ISIS_NODE, &vtysh_quit_isisd_cmd); install_element(ISIS_NODE, &vtysh_end_all_cmd); + + install_node(&isis_flex_algo_node); + install_element(ISIS_NODE, &isis_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &vtysh_exit_isis_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &vtysh_quit_isis_flex_algo_cmd); + install_element(ISIS_FLEX_ALGO_NODE, &vtysh_end_all_cmd); #endif /* HAVE_ISISD */ /* fabricd */ diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 538837391b86..1c2cca9d902c 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -9,9 +9,9 @@ #include "memory.h" DECLARE_MGROUP(MVTYSH); -struct thread_master; +struct event_loop; -extern struct thread_master *master; +extern struct event_loop *master; #define VTYSH_ZEBRA 0x00001 #define VTYSH_RIPD 0x00002 @@ -34,6 +34,7 @@ extern struct thread_master *master; #define VTYSH_VRRPD 0x40000 #define VTYSH_PATHD 0x80000 #define VTYSH_PIM6D 0x100000 +#define VTYSH_MGMTD 0x200000 #define VTYSH_WAS_ACTIVE (-2) @@ -42,7 +43,12 @@ extern struct thread_master *master; /* 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_PIM6D|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD|VTYSH_PATHD +#define VTYSH_ALL \ + VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \ + VTYSH_LDPD | VTYSH_BGPD | VTYSH_ISISD | VTYSH_PIMD | \ + VTYSH_PIM6D | VTYSH_NHRPD | VTYSH_EIGRPD | VTYSH_BABELD | \ + VTYSH_SHARPD | VTYSH_PBRD | VTYSH_STATICD | VTYSH_BFDD | \ + VTYSH_FABRICD | VTYSH_VRRPD | VTYSH_PATHD | VTYSH_MGMTD #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_PIM6D|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA #define VTYSH_AFFMAP VTYSH_ZEBRA | VTYSH_ISISD #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD @@ -52,7 +58,7 @@ extern struct thread_master *master; VTYSH_EIGRPD | VTYSH_BABELD | VTYSH_PBRD | VTYSH_FABRICD | \ VTYSH_VRRPD #define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD -#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_STATICD +#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_STATICD | VTYSH_MGMTD #define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD @@ -92,7 +98,7 @@ void config_add_line(struct list *, const char *); int vtysh_mark_file(const char *filename); -int vtysh_read_config(const char *filename, bool dry_run); +int vtysh_apply_config(const char *config_file_path, bool dry_run, bool fork); int vtysh_write_config_integrated(void); void vtysh_config_parse_line(void *, const char *); @@ -113,4 +119,18 @@ extern int user_mode; extern bool vtysh_add_timestamp; +struct vtysh_client { + int fd; + const char *name; + int flag; + char path[MAXPATHLEN]; + struct vtysh_client *next; + + struct event *log_reader; + int log_fd; + uint32_t lost_msgs; +}; + +extern struct vtysh_client vtysh_client[22]; + #endif /* VTYSH_H */ diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 905761a01110..a5f790bbc62c 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -4,6 +4,7 @@ */ #include <zebra.h> +#include <sys/wait.h> #include "command.h" #include "linklist.h" @@ -606,7 +607,7 @@ static int vtysh_read_file(FILE *confp, bool dry_run) vty->node = CONFIG_NODE; vtysh_execute_no_pager("enable"); - vtysh_execute_no_pager("configure terminal"); + vtysh_execute_no_pager("configure terminal file-lock"); if (!dry_run) vtysh_execute_no_pager("XFRR_start_configuration"); @@ -625,18 +626,20 @@ static int vtysh_read_file(FILE *confp, bool dry_run) return (ret); } -/* Read up configuration file from config_default_dir. */ -int vtysh_read_config(const char *config_default_dir, bool dry_run) +/* + * Read configuration file and send it to all connected daemons + */ +static int vtysh_read_config(const char *config_file_path, bool dry_run) { FILE *confp = NULL; bool save; int ret; - confp = fopen(config_default_dir, "r"); + confp = fopen(config_file_path, "r"); if (confp == NULL) { fprintf(stderr, "%% Can't open configuration file %s due to '%s'.\n", - config_default_dir, safe_strerror(errno)); + config_file_path, safe_strerror(errno)); return CMD_ERR_NO_FILE; } @@ -648,7 +651,103 @@ int vtysh_read_config(const char *config_default_dir, bool dry_run) vtysh_add_timestamp = save; - return (ret); + return ret; +} + +int vtysh_apply_config(const char *config_file_path, bool dry_run, bool do_fork) +{ + /* + * We need to apply the whole config file to all daemons. Instead of + * having one client talk to N daemons, we fork N times and let each + * child handle one daemon. + */ + pid_t fork_pid = getpid(); + int status = 0; + int ret; + int my_client_type; + char my_client[64]; + + if (do_fork) { + for (unsigned int i = 0; i < array_size(vtysh_client); i++) { + /* Store name of client this fork will handle */ + strlcpy(my_client, vtysh_client[i].name, + sizeof(my_client)); + my_client_type = vtysh_client[i].flag; + fork_pid = fork(); + + /* If child, break */ + if (fork_pid == 0) + break; + } + + /* parent, wait for children */ + if (fork_pid != 0) { + int keep_status = 0; + + fprintf(stdout, + "Waiting for children to finish applying config...\n"); + while (wait(&status) > 0) { + if (!keep_status && WEXITSTATUS(status)) + keep_status = WEXITSTATUS(status); + } + + /* + * This will return the first status received + * that failed( if that happens ). This is + * good enough for the moment + */ + return keep_status; + } + + /* + * children, grow up to be cowboys + */ + for (unsigned int i = 0; i < array_size(vtysh_client); i++) { + if (my_client_type != vtysh_client[i].flag) { + struct vtysh_client *cl; + + /* + * If this is a client we aren't responsible + * for, disconnect + */ + for (cl = &vtysh_client[i]; cl; cl = cl->next) { + if (cl->fd >= 0) + close(cl->fd); + cl->fd = -1; + } + } else if (vtysh_client[i].fd == -1 && + vtysh_client[i].next == NULL) { + /* + * If this is the client we are responsible + * for, but we aren't already connected to that + * client, that means the client isn't up in + * the first place and we can exit early + */ + exit(0); + } + } + + fprintf(stdout, "[%d|%s] sending configuration\n", getpid(), + my_client); + } + + ret = vtysh_read_config(config_file_path, dry_run); + + if (ret) { + if (do_fork) + fprintf(stderr, + "[%d|%s] Configuration file[%s] processing failure: %d\n", + getpid(), my_client, frr_config, ret); + else + fprintf(stderr, + "Configuration file[%s] processing failure: %d\n", + frr_config, ret); + } else if (do_fork) { + fprintf(stderr, "[%d|%s] done\n", getpid(), my_client); + exit(0); + } + + return ret; } /* We don't write vtysh specific into file from vtysh. vtysh.conf should diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 25b667252e51..20254fcd537c 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -7,7 +7,6 @@ #include <sys/un.h> #include <setjmp.h> -#include <sys/wait.h> #include <pwd.h> #include <sys/file.h> #include <unistd.h> @@ -64,7 +63,7 @@ int execute_flag = 0; int user_mode; /* Master of threads. */ -struct thread_master *master; +struct event_loop *master; /* Command logging */ FILE *logfile; @@ -178,6 +177,8 @@ static void usage(int status) "-u --user Run as an unprivileged user\n" "-w, --writeconfig Write integrated config (frr.conf) and exit\n" "-H, --histfile Override history file\n" + "-t, --timestamp Print a timestamp before going to shell or reading the configuration\n" + " --no-fork Don't fork clients to handle daemons (slower for large configs)\n" "-h, --help Display this help and exit\n\n" "Note that multiple commands may be executed from the command\n" "line by passing multiple -c args, or by embedding linefeed\n" @@ -191,6 +192,7 @@ static void usage(int status) /* VTY shell options, we use GNU getopt library. */ #define OPTION_VTYSOCK 1000 #define OPTION_CONFDIR 1001 +#define OPTION_NOFORK 1002 struct option longopts[] = { {"boot", no_argument, NULL, 'b'}, /* For compatibility with older zebra/quagga versions */ @@ -210,37 +212,38 @@ struct option longopts[] = { {"pathspace", required_argument, NULL, 'N'}, {"user", no_argument, NULL, 'u'}, {"timestamp", no_argument, NULL, 't'}, + {"no-fork", no_argument, NULL, OPTION_NOFORK}, {0}}; bool vtysh_loop_exited; -static struct thread *vtysh_rl_read_thread; +static struct event *vtysh_rl_read_thread; -static void vtysh_rl_read(struct thread *thread) +static void vtysh_rl_read(struct event *thread) { - thread_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO, - &vtysh_rl_read_thread); + event_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO, + &vtysh_rl_read_thread); rl_callback_read_char(); } /* Read a string, and return a pointer to it. Returns NULL on EOF. */ static void vtysh_rl_run(void) { - struct thread thread; + struct event thread; - master = thread_master_create(NULL); + master = event_master_create(NULL); rl_callback_handler_install(vtysh_prompt(), vtysh_rl_callback); - thread_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO, - &vtysh_rl_read_thread); + event_add_read(master, vtysh_rl_read, NULL, STDIN_FILENO, + &vtysh_rl_read_thread); - while (!vtysh_loop_exited && thread_fetch(master, &thread)) - thread_call(&thread); + while (!vtysh_loop_exited && event_fetch(master, &thread)) + event_call(&thread); if (!vtysh_loop_exited) rl_callback_handler_remove(); - thread_master_free(master); + event_master_free(master); } static void log_it(const char *line) @@ -321,6 +324,7 @@ int main(int argc, char **argv, char **env) int dryrun = 0; int boot_flag = 0; bool ts_flag = false; + bool no_fork = false; const char *daemon_name = NULL; const char *inputfile = NULL; struct cmd_rec { @@ -392,6 +396,9 @@ int main(int argc, char **argv, char **env) ditch_suid = 1; /* option disables SUID */ snprintf(sysconfdir, sizeof(sysconfdir), "%s/", optarg); break; + case OPTION_NOFORK: + no_fork = true; + break; case 'N': if (strchr(optarg, '/') || strchr(optarg, '.')) { fprintf(stderr, @@ -440,6 +447,10 @@ int main(int argc, char **argv, char **env) } } + /* No need for forks if we're talking to 1 daemon */ + if (daemon_name) + no_fork = true; + if (ditch_suid) { elevuid = realuid; elevgid = realgid; @@ -452,7 +463,7 @@ int main(int argc, char **argv, char **env) } 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: Combining 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, @@ -483,7 +494,7 @@ int main(int argc, char **argv, char **env) /* Read vtysh configuration file before connecting to daemons. * (file may not be readable to calling user in SUID mode) */ suid_on(); - vtysh_read_config(vtysh_config, dryrun); + vtysh_apply_config(vtysh_config, dryrun, false); suid_off(); } /* Error code library system */ @@ -502,9 +513,9 @@ int main(int argc, char **argv, char **env) /* Start execution only if not in dry-run mode */ if (dryrun && !cmd) { if (inputfile) { - ret = vtysh_read_config(inputfile, dryrun); + ret = vtysh_apply_config(inputfile, dryrun, false); } else { - ret = vtysh_read_config(frr_config, dryrun); + ret = vtysh_apply_config(frr_config, dryrun, false); } exit(ret); @@ -583,10 +594,17 @@ int main(int argc, char **argv, char **env) return vtysh_write_config_integrated(); } - if (inputfile) { + if (boot_flag) + inputfile = frr_config; + + if (inputfile || boot_flag) { vtysh_flock_config(inputfile); - ret = vtysh_read_config(inputfile, dryrun); + ret = vtysh_apply_config(inputfile, dryrun, !no_fork); vtysh_unflock_config(); + + if (no_error) + ret = 0; + exit(ret); } @@ -703,23 +721,6 @@ int main(int argc, char **argv, char **env) exit(0); } - /* Boot startup configuration file. */ - if (boot_flag) { - vtysh_flock_config(frr_config); - ret = vtysh_read_config(frr_config, dryrun); - vtysh_unflock_config(); - if (ret) { - fprintf(stderr, - "Configuration file[%s] processing failure: %d\n", - frr_config, ret); - if (no_error) - exit(0); - else - exit(ret); - } else - exit(0); - } - vtysh_readline_init(); vty_hello(vty); diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c index a0667acc7ed8..111bda868d3f 100644 --- a/vtysh/vtysh_user.c +++ b/vtysh/vtysh_user.c @@ -42,7 +42,7 @@ static struct pam_conv conv = {PAM_CONV_FUNC, NULL}; static int vtysh_pam(const char *user) { - int ret; + int ret, second_ret; pam_handle_t *pamh = NULL; /* Start PAM. */ @@ -56,15 +56,18 @@ static int vtysh_pam(const char *user) fprintf(stderr, "vtysh_pam: Failure to initialize pam: %s(%d)", pam_strerror(pamh, ret), ret); - if (pam_acct_mgmt(pamh, 0) != PAM_SUCCESS) + second_ret = pam_acct_mgmt(pamh, 0); + if (second_ret != PAM_SUCCESS) fprintf(stderr, "%s: Failed in account validation: %s(%d)", - __func__, pam_strerror(pamh, ret), ret); + __func__, pam_strerror(pamh, second_ret), second_ret); /* close Linux-PAM */ - if (pam_end(pamh, ret) != PAM_SUCCESS) { + second_ret = pam_end(pamh, ret); + if (second_ret != PAM_SUCCESS) { pamh = NULL; - fprintf(stderr, "vtysh_pam: failed to release authenticator: %s(%d)\n", - pam_strerror(pamh, ret), ret); + fprintf(stderr, + "vtysh_pam: failed to release authenticator: %s(%d)\n", + pam_strerror(pamh, second_ret), second_ret); exit(1); } diff --git a/watchfrr/watchfrr.c b/watchfrr/watchfrr.c index 84f4d02a8af4..89199da1af79 100644 --- a/watchfrr/watchfrr.c +++ b/watchfrr/watchfrr.c @@ -6,7 +6,7 @@ */ #include <zebra.h> -#include <thread.h> +#include "frrevent.h" #include <log.h> #include <network.h> #include <sigevent.h> @@ -53,7 +53,7 @@ DEFINE_MGROUP(WATCHFRR, "watchfrr"); DEFINE_MTYPE_STATIC(WATCHFRR, WATCHFRR_DAEMON, "watchfrr daemon entry"); /* Needs to be global, referenced somewhere inside libfrr. */ -struct thread_master *master; +struct event_loop *master; static bool watch_only = false; const char *pathspace; @@ -86,15 +86,15 @@ struct restart_info { pid_t pid; struct timeval time; long interval; - struct thread *t_kill; + struct event *t_kill; int kills; }; static struct global_state { enum restart_phase phase; - struct thread *t_phase_hanging; - struct thread *t_startup_timeout; - struct thread *t_operational; + struct event *t_phase_hanging; + struct event *t_startup_timeout; + struct event *t_operational; const char *vtydir; long period; long timeout; @@ -149,9 +149,9 @@ struct daemon { int fd; struct timeval echo_sent; unsigned int connect_tries; - struct thread *t_wakeup; - struct thread *t_read; - struct thread *t_write; + struct event *t_wakeup; + struct event *t_read; + struct event *t_write; struct daemon *next; struct restart_info restart; @@ -195,7 +195,7 @@ static const struct option longopts[] = { {NULL, 0, NULL, 0}}; static int try_connect(struct daemon *dmn); -static void wakeup_send_echo(struct thread *t_wakeup); +static void wakeup_send_echo(struct event *t_wakeup); static void try_restart(struct daemon *dmn); static void phase_check(void); static void restart_done(struct daemon *dmn); @@ -347,9 +347,9 @@ static struct timeval *time_elapsed(struct timeval *result, return result; } -static void restart_kill(struct thread *t_kill) +static void restart_kill(struct event *t_kill) { - struct restart_info *restart = THREAD_ARG(t_kill); + struct restart_info *restart = EVENT_ARG(t_kill); struct timeval delay; time_elapsed(&delay, &restart->time); @@ -358,8 +358,8 @@ static void restart_kill(struct thread *t_kill) zlog_err( "%s %s child process appears to still be reading configuration, delaying for another %lu time", restart->what, restart->name, gs.restart_timeout); - thread_add_timer(master, restart_kill, restart, - gs.restart_timeout, &restart->t_kill); + event_add_timer(master, restart_kill, restart, + gs.restart_timeout, &restart->t_kill); return; } @@ -369,8 +369,8 @@ static void restart_kill(struct thread *t_kill) (long)delay.tv_sec, (restart->kills ? SIGKILL : SIGTERM)); kill(-restart->pid, (restart->kills ? SIGKILL : SIGTERM)); restart->kills++; - thread_add_timer(master, restart_kill, restart, gs.restart_timeout, - &restart->t_kill); + event_add_timer(master, restart_kill, restart, gs.restart_timeout, + &restart->t_kill); } static struct restart_info *find_child(pid_t child) @@ -415,7 +415,7 @@ static void sigchild(void) what = restart->what; restart->pid = 0; gs.numpids--; - thread_cancel(&restart->t_kill); + event_cancel(&restart->t_kill); /* Update restart time to reflect the time the command * completed. */ @@ -505,8 +505,8 @@ static int run_job(struct restart_info *restart, const char *cmdtype, snprintf(cmd, sizeof(cmd), command, restart->name); #pragma GCC diagnostic pop if ((restart->pid = run_background(cmd)) > 0) { - thread_add_timer(master, restart_kill, restart, - gs.restart_timeout, &restart->t_kill); + event_add_timer(master, restart_kill, restart, + gs.restart_timeout, &restart->t_kill); restart->what = cmdtype; gs.numpids++; } else @@ -529,34 +529,34 @@ static int run_job(struct restart_info *restart, const char *cmdtype, #define SET_READ_HANDLER(DMN) \ do { \ (DMN)->t_read = NULL; \ - thread_add_read(master, handle_read, (DMN), (DMN)->fd, \ - &(DMN)->t_read); \ + event_add_read(master, handle_read, (DMN), (DMN)->fd, \ + &(DMN)->t_read); \ } while (0); #define SET_WAKEUP_DOWN(DMN) \ do { \ (DMN)->t_wakeup = NULL; \ - thread_add_timer_msec(master, wakeup_down, (DMN), \ - FUZZY(gs.period), &(DMN)->t_wakeup); \ + event_add_timer_msec(master, wakeup_down, (DMN), \ + FUZZY(gs.period), &(DMN)->t_wakeup); \ } while (0); #define SET_WAKEUP_UNRESPONSIVE(DMN) \ do { \ (DMN)->t_wakeup = NULL; \ - thread_add_timer_msec(master, wakeup_unresponsive, (DMN), \ - FUZZY(gs.period), &(DMN)->t_wakeup); \ + event_add_timer_msec(master, wakeup_unresponsive, (DMN), \ + FUZZY(gs.period), &(DMN)->t_wakeup); \ } while (0); #define SET_WAKEUP_ECHO(DMN) \ do { \ (DMN)->t_wakeup = NULL; \ - thread_add_timer_msec(master, wakeup_send_echo, (DMN), \ - FUZZY(gs.period), &(DMN)->t_wakeup); \ + event_add_timer_msec(master, wakeup_send_echo, (DMN), \ + FUZZY(gs.period), &(DMN)->t_wakeup); \ } while (0); -static void wakeup_down(struct thread *t_wakeup) +static void wakeup_down(struct event *t_wakeup) { - struct daemon *dmn = THREAD_ARG(t_wakeup); + struct daemon *dmn = EVENT_ARG(t_wakeup); dmn->t_wakeup = NULL; if (try_connect(dmn) < 0) @@ -565,9 +565,9 @@ static void wakeup_down(struct thread *t_wakeup) try_restart(dmn); } -static void wakeup_init(struct thread *t_wakeup) +static void wakeup_init(struct event *t_wakeup) { - struct daemon *dmn = THREAD_ARG(t_wakeup); + struct daemon *dmn = EVENT_ARG(t_wakeup); dmn->t_wakeup = NULL; if (try_connect(dmn) < 0) { @@ -587,13 +587,13 @@ static void restart_done(struct daemon *dmn) dmn->name, state_str[dmn->state]); return; } - THREAD_OFF(dmn->t_wakeup); + EVENT_OFF(dmn->t_wakeup); if (try_connect(dmn) < 0) SET_WAKEUP_DOWN(dmn); } -static void daemon_restarting_operational(struct thread *thread) +static void daemon_restarting_operational(struct event *thread) { systemd_send_status("FRR Operational"); } @@ -612,9 +612,9 @@ static void daemon_down(struct daemon *dmn, const char *why) close(dmn->fd); dmn->fd = -1; } - THREAD_OFF(dmn->t_read); - THREAD_OFF(dmn->t_write); - THREAD_OFF(dmn->t_wakeup); + EVENT_OFF(dmn->t_read); + EVENT_OFF(dmn->t_write); + EVENT_OFF(dmn->t_wakeup); if (try_connect(dmn) < 0) SET_WAKEUP_DOWN(dmn); @@ -622,9 +622,9 @@ static void daemon_down(struct daemon *dmn, const char *why) phase_check(); } -static void handle_read(struct thread *t_read) +static void handle_read(struct event *t_read) { - struct daemon *dmn = THREAD_ARG(t_read); + struct daemon *dmn = EVENT_ARG(t_read); static const char resp[sizeof(PING_TOKEN) + 4] = PING_TOKEN "\n"; char buf[sizeof(resp) + 100]; ssize_t rc; @@ -688,7 +688,7 @@ static void handle_read(struct thread *t_read) dmn->name, (long)delay.tv_sec, (long)delay.tv_usec); SET_READ_HANDLER(dmn); - thread_cancel(&dmn->t_wakeup); + event_cancel(&dmn->t_wakeup); SET_WAKEUP_ECHO(dmn); } @@ -740,19 +740,19 @@ static void daemon_up(struct daemon *dmn, const char *why) if (gs.numdown == 0) { daemon_send_ready(0); - THREAD_OFF(gs.t_operational); + EVENT_OFF(gs.t_operational); - thread_add_timer(master, daemon_restarting_operational, NULL, - gs.operational_timeout, &gs.t_operational); + event_add_timer(master, daemon_restarting_operational, NULL, + gs.operational_timeout, &gs.t_operational); } SET_WAKEUP_ECHO(dmn); phase_check(); } -static void check_connect(struct thread *t_write) +static void check_connect(struct event *t_write) { - struct daemon *dmn = THREAD_ARG(t_write); + struct daemon *dmn = EVENT_ARG(t_write); int sockerr; socklen_t reslen = sizeof(sockerr); @@ -778,9 +778,9 @@ static void check_connect(struct thread *t_write) daemon_up(dmn, "delayed connect succeeded"); } -static void wakeup_connect_hanging(struct thread *t_wakeup) +static void wakeup_connect_hanging(struct event *t_wakeup) { - struct daemon *dmn = THREAD_ARG(t_wakeup); + struct daemon *dmn = EVENT_ARG(t_wakeup); char why[100]; dmn->t_wakeup = NULL; @@ -848,10 +848,10 @@ static int try_connect(struct daemon *dmn) zlog_debug("%s: connection in progress", dmn->name); dmn->state = DAEMON_CONNECTING; dmn->fd = sock; - thread_add_write(master, check_connect, dmn, dmn->fd, - &dmn->t_write); - thread_add_timer(master, wakeup_connect_hanging, dmn, - gs.timeout, &dmn->t_wakeup); + event_add_write(master, check_connect, dmn, dmn->fd, + &dmn->t_write); + event_add_timer(master, wakeup_connect_hanging, dmn, gs.timeout, + &dmn->t_wakeup); SET_READ_HANDLER(dmn); return 0; } @@ -862,7 +862,7 @@ static int try_connect(struct daemon *dmn) return 1; } -static void phase_hanging(struct thread *t_hanging) +static void phase_hanging(struct event *t_hanging) { gs.t_phase_hanging = NULL; flog_err(EC_WATCHFRR_CONNECTION, @@ -874,10 +874,10 @@ static void phase_hanging(struct thread *t_hanging) static void set_phase(enum restart_phase new_phase) { gs.phase = new_phase; - thread_cancel(&gs.t_phase_hanging); + event_cancel(&gs.t_phase_hanging); - thread_add_timer(master, phase_hanging, NULL, PHASE_TIMEOUT, - &gs.t_phase_hanging); + event_add_timer(master, phase_hanging, NULL, PHASE_TIMEOUT, + &gs.t_phase_hanging); } static void phase_check(void) @@ -938,7 +938,7 @@ static void phase_check(void) gs.start_command, 1, 0); } gs.phase = PHASE_NONE; - THREAD_OFF(gs.t_phase_hanging); + EVENT_OFF(gs.t_phase_hanging); zlog_notice("Phased global restart has completed."); break; } @@ -985,9 +985,9 @@ static void try_restart(struct daemon *dmn) run_job(&gs.restart, "restart", gs.restart_command, 0, 1); } -static void wakeup_unresponsive(struct thread *t_wakeup) +static void wakeup_unresponsive(struct event *t_wakeup) { - struct daemon *dmn = THREAD_ARG(t_wakeup); + struct daemon *dmn = EVENT_ARG(t_wakeup); dmn->t_wakeup = NULL; if (dmn->state != DAEMON_UNRESPONSIVE) @@ -1000,9 +1000,9 @@ static void wakeup_unresponsive(struct thread *t_wakeup) } } -static void wakeup_no_answer(struct thread *t_wakeup) +static void wakeup_no_answer(struct event *t_wakeup) { - struct daemon *dmn = THREAD_ARG(t_wakeup); + struct daemon *dmn = EVENT_ARG(t_wakeup); dmn->t_wakeup = NULL; dmn->state = DAEMON_UNRESPONSIVE; @@ -1015,11 +1015,11 @@ static void wakeup_no_answer(struct thread *t_wakeup) try_restart(dmn); } -static void wakeup_send_echo(struct thread *t_wakeup) +static void wakeup_send_echo(struct event *t_wakeup) { static const char echocmd[] = "echo " PING_TOKEN; ssize_t rc; - struct daemon *dmn = THREAD_ARG(t_wakeup); + struct daemon *dmn = EVENT_ARG(t_wakeup); dmn->t_wakeup = NULL; if (((rc = write(dmn->fd, echocmd, sizeof(echocmd))) < 0) @@ -1031,8 +1031,8 @@ static void wakeup_send_echo(struct thread *t_wakeup) daemon_down(dmn, why); } else { gettimeofday(&dmn->echo_sent, NULL); - thread_add_timer(master, wakeup_no_answer, dmn, gs.timeout, - &dmn->t_wakeup); + event_add_timer(master, wakeup_no_answer, dmn, gs.timeout, + &dmn->t_wakeup); } } @@ -1118,7 +1118,7 @@ static char *translate_blanks(const char *cmd, const char *blankstr) return res; } -static void startup_timeout(struct thread *t_wakeup) +static void startup_timeout(struct event *t_wakeup) { daemon_send_ready(1); } @@ -1282,8 +1282,8 @@ static void watchfrr_init(int argc, char **argv) struct daemon *dmn, **add = &gs.daemons; char alldaemons[512] = "", *p = alldaemons; - thread_add_timer_msec(master, startup_timeout, NULL, STARTUP_TIMEOUT, - &gs.t_startup_timeout); + event_add_timer_msec(master, startup_timeout, NULL, STARTUP_TIMEOUT, + &gs.t_startup_timeout); for (i = optind; i < argc; i++) { dmn = XCALLOC(MTYPE_WATCHFRR_DAEMON, sizeof(*dmn)); @@ -1293,8 +1293,8 @@ static void watchfrr_init(int argc, char **argv) gs.numdaemons++; gs.numdown++; dmn->fd = -1; - thread_add_timer_msec(master, wakeup_init, dmn, 0, - &dmn->t_wakeup); + event_add_timer_msec(master, wakeup_init, dmn, 0, + &dmn->t_wakeup); dmn->restart.interval = gs.min_restart_interval; *add = dmn; add = &dmn->next; diff --git a/yang/example/ripd.json b/yang/example/ripd.json index 00040622e5d7..799f46a6dc7e 100644 --- a/yang/example/ripd.json +++ b/yang/example/ripd.json @@ -23,7 +23,7 @@ "instance": [ { "vrf": "default", - "allow-ecmp": "true", + "allow-ecmp": 1, "distance": { "source": [ { diff --git a/yang/example/ripd.xml b/yang/example/ripd.xml index 2feddde2d86b..dad83619cea6 100644 --- a/yang/example/ripd.xml +++ b/yang/example/ripd.xml @@ -19,7 +19,7 @@ <ripd xmlns="http://frrouting.org/yang/ripd"> <instance> <vrf>default</vrf> - <allow-ecmp>true</allow-ecmp> + <allow-ecmp>1</allow-ecmp> <static-route>10.0.1.0/24</static-route> <distance> <source> diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 8e288194eced..4b6619739d85 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -23,6 +23,10 @@ module frr-bgp-route-map { prefix rt-types; } + import frr-route-types { + prefix frr-route-types; + } + organization "Free Range Routing"; contact @@ -168,6 +172,12 @@ module frr-bgp-route-map { "Match IPv6 next hop address"; } + identity source-protocol { + base frr-route-map:rmap-match-type; + description + "Match protocol via which the route was learnt"; + } + identity distance { base frr-route-map:rmap-set-type; description @@ -186,6 +196,12 @@ module frr-bgp-route-map { "Set BGP extended community attribute"; } + identity set-extcommunity-nt { + base frr-route-map:rmap-set-type; + description + "Set BGP extended community attribute"; + } + identity set-extcommunity-soo { base frr-route-map:rmap-set-type; description @@ -198,6 +214,12 @@ module frr-bgp-route-map { "Set BGP extended community attribute"; } +identity set-extcommunity-color { + base frr-route-map:rmap-set-type; + description + "Set BGP extended community attribute"; + } + identity set-ipv4-nexthop { base frr-route-map:rmap-set-type; description @@ -474,6 +496,43 @@ module frr-bgp-route-map { "ext-community link bandwidth types."; } + typedef asn-type { + type union { + type uint32 { + range "1..4294967295"; + } + type string { + pattern '(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|' + + '6[0-4][0-9]{3}|65[0-4][0-9]{2}|' + + '655[0-2][0-9]|6553[0-5])\.' + + '(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|' + + '65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])' { + error-message "AS dot should be in the form [0..65535].[0..65535]."; + } + pattern '^0\.0$' { + modifier "invert-match"; + error-message "AS dot can't be equal to 0.0."; + } + } + } + } + + typedef color-list { + type string { + pattern '((429496729[0-5]|42949672[0-8][0-9]|' + + '4294967[0-1][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[0-1][0-9]{8}|[1-3][0-9]{9}|' + + '[1-9][0-9]{0,8})(\s*))+'; + } + description + "The color-list type represent a set of colors of value (1..4294967295) + values are separated by white spaces"; + reference + "RFC 9012 - The BGP Tunnel Encapsulation Attribute"; + } + augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:rmap-match-condition/frr-route-map:match-condition" { case local-preference { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-local-preference')"; @@ -732,6 +791,13 @@ module frr-bgp-route-map { "IPv6 address"; } } + + case source-protocol { + when "derived-from-or-self(../frr-route-map:condition, 'frr-bgp-route-map:source-protocol')"; + leaf source-protocol { + type frr-route-types:frr-route-types; + } + } } augment "/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:rmap-set-action/frr-route-map:set-action" { @@ -739,7 +805,7 @@ module frr-bgp-route-map { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:distance')"; leaf distance { type uint8 { - range "0..255"; + range "1..255"; } } } @@ -765,6 +831,17 @@ module frr-bgp-route-map { } } + case extcommunity-nt { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-nt')"; + description + "Value of the ext-community"; + leaf extcommunity-nt { + type string; + description + "Set BGP ext-community node-target attribute"; + } + } + case extcommunity-soo { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-soo')"; description @@ -797,6 +874,19 @@ module frr-bgp-route-map { } } + case extcommunity-color { + when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:set-extcommunity-color')"; + description + "Value of the ext-community"; + leaf extcommunity-color { + type color-list; + description + "Set BGP ext-community color attribute with a list of colors"; + reference + "RFC9012"; + } + } + case ipv4-address { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:ipv4-vpn-address')"; description @@ -1004,16 +1094,12 @@ module frr-bgp-route-map { when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, 'frr-bgp-route-map:aggregator')"; container aggregator { leaf aggregator-asn { - type uint32 { - range "1..4294967295"; - } + type asn-type; description "ASN of the aggregator"; } leaf aggregator-address { - when "../aggregator-asn > 0 or " - + "../aggregator-asn <= 4294967295"; type inet:ipv4-address; description "IPv4 address of the aggregator"; diff --git a/yang/frr-gmp.yang b/yang/frr-gmp.yang index a18651db284b..c8a05a2bdba7 100644 --- a/yang/frr-gmp.yang +++ b/yang/frr-gmp.yang @@ -107,7 +107,7 @@ module frr-gmp { range "1..max"; } units seconds; - must ". * 10 >= ../query-max-response-time"; + must ". * 10 > ../query-max-response-time"; default "125"; description "The Query Interval is the interval between General Queries diff --git a/yang/frr-if-rmap.yang b/yang/frr-if-rmap.yang new file mode 100644 index 000000000000..0fa2c5eddfcd --- /dev/null +++ b/yang/frr-if-rmap.yang @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BSD-2-Clause +module frr-if-rmap { + yang-version 1.1; + namespace "http://frrouting.org/yang/frr-if-rmap"; + prefix frr-if-map; + + import frr-interface { + prefix frr-interface; + } + + import frr-route-map { + prefix frr-route-map; + } + + organization + "FRRouting"; + contact + "FRR Users List: <mailto:frog@lists.frrouting.org> + FRR Development List: <mailto:dev@lists.frrouting.org>"; + description + "This module defines route map settings + + Copyright 2023 LabN Consulting L.L.C + + 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 2023-04-09 { + description + "Initial revision"; + reference "FRRouting"; + } + + grouping if-route-maps-group { + description "Grouping for interface route maps"; + + container if-route-maps { + description "Collection of interface route-maps"; + + list if-route-map { + must "in-route-map or out-route-map"; + key "interface"; + description "Collection of route-maps for an interface"; + + leaf "interface" { + type frr-interface:interface-ref; + description "The interface the route maps are associated with"; + } + leaf "in-route-map" { + type frr-route-map:route-map-name; + description "Name of the ingress route map"; + } + leaf "out-route-map" { + type frr-route-map:route-map-name; + description "Name of the egress route map"; + } + } + } + } +} diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang index 0c2cf232fb7b..ae69d53ccc0e 100644 --- a/yang/frr-isisd.yang +++ b/yang/frr-isisd.yang @@ -116,6 +116,26 @@ module frr-isisd { associated with an interface."; } + typedef hello-padding-type { + type enumeration { + enum "always" { + value 0; + description + "Add padding to all hello packets."; + } + enum "disabled" { + value 1; + description + "Do not add any padding to hello packets."; + } + enum "during-adjacency-formation" { + value 2; + description + "Add padding to hello packets during adjacency formation only."; + } + } + } + typedef network-type { type enumeration { enum "unknown" { @@ -605,10 +625,10 @@ module frr-isisd { description "Parameters related to IS-IS hello PDUs."; leaf padding { - type boolean; - default "true"; + type hello-padding-type; + default "always"; description - "Add padding to IS-IS hello PDUs."; + "Type of padding for IS-IS hello packets."; } container interval { @@ -1192,6 +1212,20 @@ module frr-isisd { "Advertise prefixes of passive interfaces only"; } + leaf admin-group-send-zero { + type boolean; + default "false"; + description + "Allow sending the default admin-group value of 0x00000000"; + } + + leaf asla-legacy-flag { + type boolean; + default "false"; + description + "Set the legacy flag (aka. L-FLAG) in the ASLA Sub-TLV."; + } + container lsp { description "Configuration of Link-State Packets (LSP) parameters"; @@ -1574,6 +1608,13 @@ module frr-isisd { "Log changes to the IS-IS adjacencies in this area."; } + leaf log-pdu-drops { + type boolean; + default "false"; + description + "Log any dropped PDUs in this area."; + } + container mpls-te { presence "Present if MPLS-TE is enabled."; description @@ -1596,6 +1637,107 @@ module frr-isisd { } } + container flex-algos { + description + "Flex-Algo Table"; + list flex-algo { + key "flex-algo"; + description + "Configuration for an IS-IS Flex-Algo"; + leaf advertise-definition { + type boolean; + description + "If TRUE, Flex-Algo definition is advertised"; + } + container affinity-include-alls { + description + "Set the include-all affinity"; + leaf-list affinity-include-all { + type string; + max-elements "256"; + description + "Array of Attribute Names"; + } + } + container affinity-include-anies { + description + "Set the include-any affinity"; + leaf-list affinity-include-any { + type string; + max-elements "256"; + description + "Array of Attribute Names"; + } + } + container affinity-exclude-anies { + description + "Set the exclude-any affinity"; + leaf-list affinity-exclude-any { + type string; + max-elements "256"; + description + "Array of Attribute Names"; + } + } + leaf prefix-metric { + type empty; + description + "Use Flex-algo Prefix Metric"; + } + leaf metric-type { + default "igp"; + description + "Set the Flex-Algo metric-type"; + type enumeration { + enum "igp" { + value 0; + description + "IGP Metric"; + } + enum "min-uni-link-delay" { + value 1; + description + "RFC 8570 Sec 4.2 Min Unidirectional Link Delay"; + } + enum "te-default" { + value 2; + description + "RFC 5305 Sec 3.7 Traffic Engineering Default Metric"; + } + } + } + leaf priority { + type uint32 { + range "0..255"; + } + description + "Set the Flex-Algo priority"; + } + leaf dplane-sr-mpls { + type empty; + description + "Advertise and participate in the Flex-Algo Segment-Routing MPLS data-plane"; + } + leaf dplane-srv6 { + type empty; + description + "Advertise and participate in the Flex-Algo Segment-Routing SRv6 data-plane"; + } + leaf dplane-ip { + type empty; + description + "Advertise and participate in the Flex-Algo IP data-plane"; + } + leaf flex-algo { + type uint32 { + range "128..255"; + } + description + "Flex-Algo"; + } + } + } + container segment-routing { description "Segment Routing global configuration."; @@ -1724,6 +1866,87 @@ module frr-isisd { } } } + container algorithm-prefix-sids { + description + "Algorithm SID Table"; + list algorithm-prefix-sid { + key "prefix algo"; + description + "Assign prefix SID for algorithm to an + interface, ISISPHPFlag will be rejected + if set to disable, ISISEXPLICITNULLFlag + will override the value of ISISPHPFlag"; + leaf algo { + type uint32 { + range "128..255"; + } + description + "Algorithm"; + } + leaf prefix { + type inet:ip-prefix; + mandatory true; + 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 { + range "0..1048575"; + } + 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."; + } + leaf n-flag-clear { + type boolean; + default "false"; + description + "Not a node SID"; + } + } + } } container mpls { diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang index 746bf35d28fe..5f85a4cabc1b 100644 --- a/yang/frr-ripd.yang +++ b/yang/frr-ripd.yang @@ -10,9 +10,18 @@ module frr-ripd { import ietf-yang-types { prefix yang; } + import frr-if-rmap { + prefix frr-if-rmap; + } + import frr-bfdd { + prefix frr-bfdd; + } import frr-interface { prefix frr-interface; } + import frr-nexthop { + prefix frr-nexthop; + } import frr-vrf { prefix frr-vrf; } @@ -60,6 +69,7 @@ module frr-ripd { description "Changed interface references to use frr-interface:interface-ref typedef"; + reference "FRRouting"; } revision 2017-12-06 { description @@ -69,10 +79,35 @@ module frr-ripd { RFC 2453: RIP Version 2."; } + typedef rip-route-type { + type enumeration { + enum normal { + value 0; + description "Normal RIP route type."; + } + enum static { + value 1; + description "Static RIP route type."; + } + enum default { + value 2; + description "Default RIP route type."; + } + enum redistribute { + value 3; + description "Redistribute RIP route type."; + } + enum interface { + value 4; + description "Interface RIP route type."; + } + } + description + "Types of RIP routes."; + } + container ripd { - /* - * Routing instance configuration. - */ + description "rip routing instance data"; list instance { key "vrf"; description @@ -84,8 +119,8 @@ module frr-ripd { "VRF name."; } leaf allow-ecmp { - type boolean; - default "false"; + type uint8; + default 0; description "Allow equal-cost multi-path."; } @@ -229,9 +264,9 @@ module frr-ripd { "Redistributes routes learned from other routing protocols."; leaf protocol { type frr-route-types:frr-route-types-v4; + must '. != "rip"'; description "Routing protocol."; - must '. != "rip"'; } leaf route-map { type frr-route-map:route-map-ref; @@ -253,6 +288,9 @@ module frr-ripd { is 0."; } } + + uses frr-if-rmap:if-route-maps-group; + leaf-list static-route { type inet:ipv4-prefix; description @@ -291,11 +329,8 @@ module frr-ripd { } } container version { + description "version of rip"; leaf receive { - must - '(. = "1" and ../send = "1") or ' + - '(. = "2" and ../send = "2") or ' + - '(. = "1-2" and ../send = "2")'; type enumeration { enum "1" { value 1; @@ -313,15 +348,15 @@ module frr-ripd { "Accept both RIPv1 and RIPv2 updates."; } } + must + '(. = "1" and ../send = "1") or ' + + '(. = "2" and ../send = "2") or ' + + '(. = "1-2" and ../send = "2")'; default "1-2"; description "Advertisement reception - Version control."; } leaf send { - must - '(../receive = "1" and . = "1") or ' + - '(../receive = "2" and . = "2") or ' + - '(../receive = "1-2" and . = "2")'; type enumeration { enum "1" { value 1; @@ -334,12 +369,22 @@ module frr-ripd { "Send RIPv2 updates only."; } } + must + '(../receive = "1" and . = "1") or ' + + '(../receive = "2" and . = "2") or ' + + '(../receive = "1-2" and . = "2")'; default "2"; description "Advertisement transmission - Version control."; } } + leaf default-bfd-profile { + description + "Use this BFD profile for all peers by default."; + type frr-bfdd:profile-ref; + } + /* * Operational data. */ @@ -399,23 +444,81 @@ module frr-ripd { separated by the slash (/) character. The range of values for the prefix-length is 0 to 32."; } + container nexthops { + description "container of nexthops"; + list nexthop { + description "A list of nexthop objects."; + leaf nh-type { + type frr-nexthop:nexthop-type; + mandatory true; + description + "The nexthop type."; + } + leaf protocol { + type frr-route-types:frr-route-types-v4; + description + "The protocol originating this route."; + } + leaf rip-type { + type rip-route-type; + description + "The RIP type of route."; + } + leaf gateway { + type inet:ipv4-address; + description + "The nexthop gateway address."; + } + leaf interface { + type frr-interface:interface-ref; + description + "The nexthop egress interface."; + } + leaf from { + type inet:ipv4-address; + description + "The nexthop gateway address."; + } + leaf tag { + type uint32; + default "0"; + description + "Route tag"; + } + leaf external-metric { + type uint32; + description + "External metric if learned from external protocol."; + } + leaf expire-time { + type uint32; + description + "Seconds before route expires."; + } + } + } + leaf metric { + type uint8 { + range "0..16"; + } + description + "Route metric."; + } + /* + * Replaced by container `nexthops` above. + */ leaf next-hop { type inet:ipv4-address; + status deprecated; description "Next hop IPv4 address."; } leaf interface { type frr-interface:interface-ref; + status deprecated; description "The interface that the route uses."; } - leaf metric { - type uint8 { - range "0..16"; - } - description - "Route metric."; - } } } } @@ -426,6 +529,7 @@ module frr-ripd { * Per-interface configuration data */ augment "/frr-interface:lib/frr-interface:interface" { + description "rip interface data"; container rip { description "RIP interface parameters."; @@ -583,6 +687,24 @@ module frr-ripd { "Key-chain name."; } } + + container bfd-monitoring { + presence + "Present if BFD is configured for RIP peers in this interface."; + + leaf enable { + type boolean; + description + "Enable/disable BFD monitoring."; + default false; + } + + leaf profile { + type frr-bfdd:profile-ref; + description + "BFD profile to use."; + } + } } } diff --git a/yang/frr-ripngd.yang b/yang/frr-ripngd.yang index d7de4c398a35..4aeaf3640075 100644 --- a/yang/frr-ripngd.yang +++ b/yang/frr-ripngd.yang @@ -10,6 +10,9 @@ module frr-ripngd { import ietf-yang-types { prefix yang; } + import frr-if-rmap { + prefix frr-if-rmap; + } import frr-interface { prefix frr-interface; } @@ -83,8 +86,8 @@ module frr-ripngd { "VRF name."; } leaf allow-ecmp { - type boolean; - default "false"; + type uint8; + default 0; description "Allow equal-cost multi-path."; } @@ -196,6 +199,9 @@ module frr-ripngd { is 0."; } } + + uses frr-if-rmap:if-route-maps-group; + leaf-list static-route { type inet:ipv6-prefix; description diff --git a/yang/frr-route-map.yang b/yang/frr-route-map.yang index 224b2caef777..7cb13b60f2fc 100644 --- a/yang/frr-route-map.yang +++ b/yang/frr-route-map.yang @@ -160,6 +160,18 @@ module frr-route-map { "Set prefix/route metric"; } + identity set-min-metric { + base rmap-set-type; + description + "Set minimum prefix/route metric"; + } + + identity set-max-metric { + base rmap-set-type; + description + "Set maximum prefix/route metric"; + } + identity set-tag { base rmap-set-type; description @@ -346,6 +358,39 @@ module frr-route-map { } } + case set-min-metric { + when "derived-from-or-self(../action, 'set-min-metric')"; + choice minimun-metric-value { + description + "Mimimum metric to set or use"; + case min-metric { + leaf min-metric { + type uint32 { + range "0..4294967295"; + } + description + "Use the following mimumn metric value"; + } + } + } + } + + case set-max-metric { + when "derived-from-or-self(../action, 'set-max-metric')"; + choice maximum-metric-value { + description + "Maximum metric to set or use"; + case max-metric { + leaf max-metric { + type uint32 { + range "0..4294967295"; + } + description + "Use the following maximum metric value"; + } + } + } + } case set-tag { when "derived-from-or-self(../action, 'set-tag')"; leaf tag { diff --git a/yang/subdir.am b/yang/subdir.am index 82a6a01474a4..eb17c38dbc01 100644 --- a/yang/subdir.am +++ b/yang/subdir.am @@ -24,6 +24,7 @@ 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-if-rmap.yang dist_yangmodels_DATA += yang/frr-interface.yang dist_yangmodels_DATA += yang/frr-route-map.yang dist_yangmodels_DATA += yang/frr-zebra-route-map.yang diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c index 87245f47e8f3..fc140b07a34d 100644 --- a/zebra/dpdk/zebra_dplane_dpdk.c +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -645,7 +645,7 @@ static int zd_dpdk_init(void) zd_dpdk_vty_init(); frr_with_privs (&zserv_privs) { - rc = rte_eal_init(ARRAY_SIZE(argv), argv); + rc = rte_eal_init(array_size(argv), argv); } if (rc < 0) { zlog_warn("EAL init failed %s", rte_strerror(rte_errno)); @@ -694,7 +694,7 @@ static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early) } -static int zd_dpdk_plugin_init(struct thread_master *tm) +static int zd_dpdk_plugin_init(struct event_loop *tm) { int ret; diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 18aed7b376a8..02881a644705 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -83,22 +83,22 @@ struct fpm_nl_ctx { /* 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_nhg; - struct thread *t_dequeue; + struct event *t_connect; + struct event *t_read; + struct event *t_write; + struct event *t_event; + struct event *t_nhg; + struct event *t_dequeue; /* zebra events. */ - struct thread *t_lspreset; - struct thread *t_lspwalk; - struct thread *t_nhgreset; - struct thread *t_nhgwalk; - struct thread *t_ribreset; - struct thread *t_ribwalk; - struct thread *t_rmacreset; - struct thread *t_rmacwalk; + struct event *t_lspreset; + struct event *t_lspwalk; + struct event *t_nhgreset; + struct event *t_nhgwalk; + struct event *t_ribreset; + struct event *t_ribwalk; + struct event *t_rmacreset; + struct event *t_rmacwalk; /* Statistic counters. */ struct { @@ -156,26 +156,26 @@ enum fpm_nl_events { }; #define FPM_RECONNECT(fnc) \ - thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ - FNE_INTERNAL_RECONNECT, &(fnc)->t_event) + event_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) + event_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + (ev), NULL) /* * Prototypes. */ -static void fpm_process_event(struct thread *t); +static void fpm_process_event(struct event *t); static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx); -static void fpm_lsp_send(struct thread *t); -static void fpm_lsp_reset(struct thread *t); -static void fpm_nhg_send(struct thread *t); -static void fpm_nhg_reset(struct thread *t); -static void fpm_rib_send(struct thread *t); -static void fpm_rib_reset(struct thread *t); -static void fpm_rmac_send(struct thread *t); -static void fpm_rmac_reset(struct thread *t); +static void fpm_lsp_send(struct event *t); +static void fpm_lsp_reset(struct event *t); +static void fpm_nhg_send(struct event *t); +static void fpm_nhg_reset(struct event *t); +static void fpm_rib_send(struct event *t); +static void fpm_rib_reset(struct event *t); +static void fpm_rmac_send(struct event *t); +static void fpm_rmac_reset(struct event *t); /* * CLI. @@ -231,8 +231,8 @@ DEFUN(fpm_set_address, fpm_set_address_cmd, 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); + event_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_RECONNECT, &gfnc->t_event); return CMD_SUCCESS; } @@ -246,8 +246,8 @@ DEFUN(no_fpm_set_address, no_fpm_set_address_cmd, "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); + event_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_DISABLE, &gfnc->t_event); return CMD_SUCCESS; } @@ -260,8 +260,8 @@ DEFUN(fpm_use_nhg, fpm_use_nhg_cmd, if (gfnc->use_nhg) return CMD_SUCCESS; - thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, - FNE_TOGGLE_NHG, &gfnc->t_nhg); + event_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_TOGGLE_NHG, &gfnc->t_nhg); return CMD_SUCCESS; } @@ -276,8 +276,8 @@ DEFUN(no_fpm_use_nhg, no_fpm_use_nhg_cmd, if (!gfnc->use_nhg) return CMD_SUCCESS; - thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, - FNE_TOGGLE_NHG, &gfnc->t_nhg); + event_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_TOGGLE_NHG, &gfnc->t_nhg); return CMD_SUCCESS; } @@ -288,8 +288,8 @@ DEFUN(fpm_reset_counters, fpm_reset_counters_cmd, FPM_STR "FPM statistic counters\n") { - thread_add_event(gfnc->fthread->master, fpm_process_event, gfnc, - FNE_RESET_COUNTERS, &gfnc->t_event); + event_add_event(gfnc->fthread->master, fpm_process_event, gfnc, + FNE_RESET_COUNTERS, &gfnc->t_event); return CMD_SUCCESS; } @@ -409,19 +409,19 @@ static struct cmd_node fpm_node = { /* * FPM functions. */ -static void fpm_connect(struct thread *t); +static void fpm_connect(struct event *t); static void fpm_reconnect(struct fpm_nl_ctx *fnc) { /* Cancel all zebra threads first. */ - thread_cancel_async(zrouter.master, &fnc->t_lspreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_lspwalk, NULL); - 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); + event_cancel_async(zrouter.master, &fnc->t_lspreset, NULL); + event_cancel_async(zrouter.master, &fnc->t_lspwalk, NULL); + event_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); + event_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); + event_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); + event_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); + event_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); + event_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); /* * Grab the lock to empty the streams (data plane might try to @@ -437,20 +437,20 @@ static void fpm_reconnect(struct fpm_nl_ctx *fnc) stream_reset(fnc->ibuf); stream_reset(fnc->obuf); - THREAD_OFF(fnc->t_read); - THREAD_OFF(fnc->t_write); + EVENT_OFF(fnc->t_read); + EVENT_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); + event_add_timer(fnc->fthread->master, fpm_connect, fnc, 3, + &fnc->t_connect); } -static void fpm_read(struct thread *t) +static void fpm_read(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); fpm_msg_hdr_t fpm; ssize_t rv; char buf[65535]; @@ -482,8 +482,8 @@ static void fpm_read(struct thread *t) } /* Schedule the next read */ - thread_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket, - &fnc->t_read); + event_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket, + &fnc->t_read); /* We've got an interruption. */ if (rv == -2) @@ -587,7 +587,8 @@ static void fpm_read(struct thread *t) switch (hdr->nlmsg_type) { case RTM_NEWROUTE: ctx = dplane_ctx_alloc(); - dplane_ctx_set_op(ctx, DPLANE_OP_ROUTE_NOTIFY); + dplane_ctx_route_init(ctx, DPLANE_OP_ROUTE_NOTIFY, NULL, + NULL); if (netlink_route_change_read_unicast_internal( hdr, 0, false, ctx) != 1) { dplane_ctx_fini(&ctx); @@ -610,9 +611,9 @@ static void fpm_read(struct thread *t) stream_reset(fnc->ibuf); } -static void fpm_write(struct thread *t) +static void fpm_write(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); socklen_t statuslen; ssize_t bwritten; int rv, status; @@ -646,12 +647,12 @@ static void fpm_write(struct thread *t) * Starting with LSPs walk all FPM objects, marking them * as unsent and then replaying them. */ - thread_add_timer(zrouter.master, fpm_lsp_reset, fnc, 0, - &fnc->t_lspreset); + event_add_timer(zrouter.master, fpm_lsp_reset, fnc, 0, + &fnc->t_lspreset); /* Permit receiving messages now. */ - thread_add_read(fnc->fthread->master, fpm_read, fnc, - fnc->socket, &fnc->t_read); + event_add_read(fnc->fthread->master, fpm_read, fnc, fnc->socket, + &fnc->t_read); } frr_mutex_lock_autounlock(&fnc->obuf_mutex); @@ -708,15 +709,15 @@ static void fpm_write(struct thread *t) /* 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); + event_add_write(fnc->fthread->master, fpm_write, fnc, + fnc->socket, &fnc->t_write); return; } } -static void fpm_connect(struct thread *t) +static void fpm_connect(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); struct sockaddr_in *sin = (struct sockaddr_in *)&fnc->addr; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&fnc->addr; socklen_t slen; @@ -727,8 +728,8 @@ static void fpm_connect(struct thread *t) 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); + event_add_timer(fnc->fthread->master, fpm_connect, fnc, 3, + &fnc->t_connect); return; } @@ -753,18 +754,18 @@ static void fpm_connect(struct thread *t) 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); + event_add_timer(fnc->fthread->master, fpm_connect, fnc, 3, + &fnc->t_connect); return; } 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); + event_add_read(fnc->fthread->master, fpm_read, fnc, sock, + &fnc->t_read); + event_add_write(fnc->fthread->master, fpm_write, fnc, sock, + &fnc->t_write); /* * Starting with LSPs walk all FPM objects, marking them @@ -773,8 +774,8 @@ static void fpm_connect(struct thread *t) * If we are not connected, then delay the objects reset/send. */ if (!fnc->connecting) - thread_add_timer(zrouter.master, fpm_lsp_reset, fnc, 0, - &fnc->t_lspreset); + event_add_timer(zrouter.master, fpm_lsp_reset, fnc, 0, + &fnc->t_lspreset); } /** @@ -933,6 +934,7 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: case DPLANE_OP_NONE: + case DPLANE_OP_STARTUP_STAGE: break; } @@ -983,8 +985,8 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx) memory_order_relaxed); /* Tell the thread to start writing. */ - thread_add_write(fnc->fthread->master, fpm_write, fnc, fnc->socket, - &fnc->t_write); + event_add_write(fnc->fthread->master, fpm_write, fnc, fnc->socket, + &fnc->t_write); return 0; } @@ -1020,10 +1022,10 @@ static int fpm_lsp_send_cb(struct hash_bucket *bucket, void *arg) return HASHWALK_CONTINUE; } -static void fpm_lsp_send(struct thread *t) +static void fpm_lsp_send(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); - struct zebra_vrf *zvrf = vrf_info_lookup(VRF_DEFAULT); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); struct fpm_lsp_arg fla; fla.fnc = fnc; @@ -1038,12 +1040,12 @@ static void fpm_lsp_send(struct thread *t) WALK_FINISH(fnc, FNE_LSP_FINISHED); /* Now move onto routes */ - thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, - &fnc->t_nhgreset); + event_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, + &fnc->t_nhgreset); } else { /* Didn't finish - reschedule LSP walk */ - thread_add_timer(zrouter.master, fpm_lsp_send, fnc, 0, - &fnc->t_lspwalk); + event_add_timer(zrouter.master, fpm_lsp_send, fnc, 0, + &fnc->t_lspwalk); } } @@ -1080,9 +1082,9 @@ static int fpm_nhg_send_cb(struct hash_bucket *bucket, void *arg) return HASHWALK_CONTINUE; } -static void fpm_nhg_send(struct thread *t) +static void fpm_nhg_send(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); struct fpm_nhg_arg fna; fna.fnc = fnc; @@ -1099,19 +1101,19 @@ static void fpm_nhg_send(struct thread *t) /* 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); + event_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); + event_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, + &fnc->t_nhgwalk); } /** * Send all RIB installed routes to the connected data plane. */ -static void fpm_rib_send(struct thread *t) +static void fpm_rib_send(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); rib_dest_t *dest; struct route_node *rn; struct route_table *rt; @@ -1141,8 +1143,8 @@ static void fpm_rib_send(struct thread *t) /* Free the temporary allocated context. */ dplane_ctx_fini(&ctx); - thread_add_timer(zrouter.master, fpm_rib_send, - fnc, 1, &fnc->t_ribwalk); + event_add_timer(zrouter.master, fpm_rib_send, + fnc, 1, &fnc->t_ribwalk); return; } @@ -1158,8 +1160,8 @@ static void fpm_rib_send(struct thread *t) WALK_FINISH(fnc, FNE_RIB_FINISHED); /* Schedule next event: RMAC reset. */ - thread_add_event(zrouter.master, fpm_rmac_reset, fnc, 0, - &fnc->t_rmacreset); + event_add_event(zrouter.master, fpm_rmac_reset, fnc, 0, + &fnc->t_rmacreset); } /* @@ -1199,8 +1201,8 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *bucket, void *arg) 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); + event_add_timer(zrouter.master, fpm_rmac_send, fra->fnc, 1, + &fra->fnc->t_rmacwalk); fra->complete = false; } } @@ -1214,11 +1216,11 @@ static void fpm_enqueue_l3vni_table(struct hash_bucket *bucket, void *arg) hash_iterate(zl3vni->rmac_table, fpm_enqueue_rmac_table, zl3vni); } -static void fpm_rmac_send(struct thread *t) +static void fpm_rmac_send(struct event *t) { struct fpm_rmac_arg fra; - fra.fnc = THREAD_ARG(t); + fra.fnc = EVENT_ARG(t); fra.ctx = dplane_ctx_alloc(); fra.complete = true; hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra); @@ -1240,14 +1242,14 @@ static void fpm_nhg_reset_cb(struct hash_bucket *bucket, void *arg) UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_FPM); } -static void fpm_nhg_reset(struct thread *t) +static void fpm_nhg_reset(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); 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); + event_add_event(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk); } /* @@ -1260,23 +1262,23 @@ static void fpm_lsp_reset_cb(struct hash_bucket *bucket, void *arg) UNSET_FLAG(lsp->flags, LSP_FLAG_FPM); } -static void fpm_lsp_reset(struct thread *t) +static void fpm_lsp_reset(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); - struct zebra_vrf *zvrf = vrf_info_lookup(VRF_DEFAULT); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); hash_iterate(zvrf->lsp_table, fpm_lsp_reset_cb, NULL); /* Schedule next step: send LSPs */ - thread_add_event(zrouter.master, fpm_lsp_send, fnc, 0, &fnc->t_lspwalk); + event_add_event(zrouter.master, fpm_lsp_send, fnc, 0, &fnc->t_lspwalk); } /** * Resets the RIB FPM flags so we send all routes again. */ -static void fpm_rib_reset(struct thread *t) +static void fpm_rib_reset(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); rib_dest_t *dest; struct route_node *rn; struct route_table *rt; @@ -1295,7 +1297,7 @@ static void fpm_rib_reset(struct thread *t) } /* Schedule next step: send RIB routes. */ - thread_add_event(zrouter.master, fpm_rib_send, fnc, 0, &fnc->t_ribwalk); + event_add_event(zrouter.master, fpm_rib_send, fnc, 0, &fnc->t_ribwalk); } /* @@ -1315,20 +1317,20 @@ static void fpm_unset_l3vni_table(struct hash_bucket *bucket, void *arg) hash_iterate(zl3vni->rmac_table, fpm_unset_rmac_table, zl3vni); } -static void fpm_rmac_reset(struct thread *t) +static void fpm_rmac_reset(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); 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); + event_add_event(zrouter.master, fpm_rmac_send, fnc, 0, + &fnc->t_rmacwalk); } -static void fpm_process_queue(struct thread *t) +static void fpm_process_queue(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); struct zebra_dplane_ctx *ctx; bool no_bufs = false; uint64_t processed_contexts = 0; @@ -1371,8 +1373,8 @@ static void fpm_process_queue(struct thread *t) /* Re-schedule if we ran out of buffer space */ if (no_bufs) - thread_add_timer(fnc->fthread->master, fpm_process_queue, - fnc, 0, &fnc->t_dequeue); + event_add_timer(fnc->fthread->master, fpm_process_queue, fnc, 0, + &fnc->t_dequeue); /* * Let the dataplane thread know if there are items in the @@ -1387,10 +1389,10 @@ static void fpm_process_queue(struct thread *t) /** * Handles external (e.g. CLI, data plane or others) events. */ -static void fpm_process_event(struct thread *t) +static void fpm_process_event(struct event *t) { - struct fpm_nl_ctx *fnc = THREAD_ARG(t); - enum fpm_nl_events event = THREAD_VAL(t); + struct fpm_nl_ctx *fnc = EVENT_ARG(t); + enum fpm_nl_events event = EVENT_VAL(t); switch (event) { case FNE_DISABLE: @@ -1474,19 +1476,19 @@ static int fpm_nl_start(struct zebra_dplane_provider *prov) static int fpm_nl_finish_early(struct fpm_nl_ctx *fnc) { /* Disable all events and close socket. */ - THREAD_OFF(fnc->t_lspreset); - THREAD_OFF(fnc->t_lspwalk); - 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_OFF(fnc->t_event); - THREAD_OFF(fnc->t_nhg); - 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); + EVENT_OFF(fnc->t_lspreset); + EVENT_OFF(fnc->t_lspwalk); + EVENT_OFF(fnc->t_nhgreset); + EVENT_OFF(fnc->t_nhgwalk); + EVENT_OFF(fnc->t_ribreset); + EVENT_OFF(fnc->t_ribwalk); + EVENT_OFF(fnc->t_rmacreset); + EVENT_OFF(fnc->t_rmacwalk); + EVENT_OFF(fnc->t_event); + EVENT_OFF(fnc->t_nhg); + event_cancel_async(fnc->fthread->master, &fnc->t_read, NULL); + event_cancel_async(fnc->fthread->master, &fnc->t_write, NULL); + event_cancel_async(fnc->fthread->master, &fnc->t_connect, NULL); if (fnc->socket != -1) { close(fnc->socket); @@ -1575,8 +1577,8 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) 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); + event_add_timer(fnc->fthread->master, fpm_process_queue, fnc, 0, + &fnc->t_dequeue); /* Ensure dataplane thread is rescheduled if we hit the work limit */ if (counter >= limit) @@ -1585,7 +1587,7 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov) return 0; } -static int fpm_nl_new(struct thread_master *tm) +static int fpm_nl_new(struct event_loop *tm) { struct zebra_dplane_provider *prov = NULL; int rv; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 21cad01374d4..8767b2622c12 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -33,7 +33,7 @@ #include "table.h" #include "memory.h" #include "rib.h" -#include "thread.h" +#include "frrevent.h" #include "privs.h" #include "nexthop.h" #include "vrf.h" @@ -63,66 +63,22 @@ #include "zebra/zebra_trace.h" extern struct zebra_privs_t zserv_privs; -uint8_t frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; - -/* Note: on netlink systems, there should be a 1-to-1 mapping between interface - names and ifindex values. */ -static void set_ifindex(struct interface *ifp, ifindex_t ifi_index, - struct zebra_ns *zns) -{ - struct interface *oifp; - - if (((oifp = if_lookup_by_index_per_ns(zns, ifi_index)) != NULL) - && (oifp != ifp)) { - if (ifi_index == IFINDEX_INTERNAL) - flog_err( - EC_LIB_INTERFACE, - "Netlink is setting interface %s ifindex to reserved internal value %u", - ifp->name, ifi_index); - else { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "interface index %d was renamed from %s to %s", - ifi_index, oifp->name, ifp->name); - if (if_is_up(oifp)) - flog_err( - EC_LIB_INTERFACE, - "interface rename detected on up interface: index %d was renamed from %s to %s, results are uncertain!", - ifi_index, oifp->name, ifp->name); - if_delete_update(&oifp); - } - } - if_set_index(ifp, ifi_index); -} /* Utility function to parse hardware link-layer address and update ifp */ static void netlink_interface_update_hw_addr(struct rtattr **tb, - struct interface *ifp) + struct zebra_dplane_ctx *ctx) { - int i; - if (tb[IFLA_ADDRESS]) { int hw_addr_len; hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); if (hw_addr_len > INTERFACE_HWADDR_MAX) - zlog_debug("Hardware address is too large: %d", - hw_addr_len); - else { - ifp->hw_addr_len = hw_addr_len; - memcpy(ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), - hw_addr_len); - - for (i = 0; i < hw_addr_len; i++) - if (ifp->hw_addr[i] != 0) - break; - - if (i == hw_addr_len) - ifp->hw_addr_len = 0; - else - ifp->hw_addr_len = hw_addr_len; - } + zlog_warn("Hardware address is too large: %d", + hw_addr_len); + else + dplane_ctx_set_ifp_hw_addr(ctx, hw_addr_len, + RTA_DATA(tb[IFLA_ADDRESS])); } } @@ -237,26 +193,6 @@ static enum zebra_link_type netlink_to_zebra_link_type(unsigned int hwt) } } -static inline void zebra_if_set_ziftype(struct interface *ifp, - enum zebra_iftype zif_type, - enum zebra_slave_iftype 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, enum zebra_iftype *zif_type) { @@ -279,23 +215,16 @@ static void netlink_determine_zebra_iftype(const char *kind, *zif_type = ZEBRA_IF_VETH; else if (strcmp(kind, "bond") == 0) *zif_type = ZEBRA_IF_BOND; - else if (strcmp(kind, "bond_slave") == 0) - *zif_type = ZEBRA_IF_BOND_SLAVE; else if (strcmp(kind, "gre") == 0) *zif_type = ZEBRA_IF_GRE; } static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, - uint32_t ns_id, const char *name) + uint32_t ns_id, const char *name, + struct zebra_dplane_ctx *ctx) { - struct ifinfomsg *ifi; struct rtattr *linkinfo[IFLA_INFO_MAX + 1]; struct rtattr *attr[IFLA_VRF_MAX + 1]; - struct vrf *vrf = NULL; - struct zebra_vrf *zvrf; - uint32_t nl_table_id; - - ifi = NLMSG_DATA(h); netlink_parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb); @@ -317,74 +246,8 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, return; } - nl_table_id = *(uint32_t *)RTA_DATA(attr[IFLA_VRF_TABLE]); - - if (h->nlmsg_type == RTM_NEWLINK) { - if (IS_ZEBRA_DEBUG_KERNEL) - 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 = vrf_update((vrf_id_t)ifi->ifi_index, name); - if (!vrf) { - flog_err(EC_LIB_INTERFACE, "VRF %s id %u not created", - name, ifi->ifi_index); - return; - } - - /* - * This is the only place that we get the actual kernel table_id - * being used. We need it to set the table_id of the routes - * we are passing to the kernel.... And to throw some totally - * awesome parties. that too. - * - * At this point we *must* have a zvrf because the vrf_create - * callback creates one. We *must* set the table id - * before the vrf_enable because of( at the very least ) - * static routes being delayed for installation until - * during the vrf_enable callbacks. - */ - zvrf = (struct zebra_vrf *)vrf->info; - zvrf->table_id = nl_table_id; - - /* Enable the created VRF. */ - if (!vrf_enable(vrf)) { - flog_err(EC_LIB_INTERFACE, - "Failed to enable VRF %s id %u", name, - ifi->ifi_index); - return; - } - - } else // h->nlmsg_type == RTM_DELLINK - { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("RTM_DELLINK for VRF %s(%u)", name, - ifi->ifi_index); - - vrf = vrf_lookup_by_id((vrf_id_t)ifi->ifi_index); - - if (!vrf) { - flog_warn(EC_ZEBRA_VRF_NOT_FOUND, "%s: vrf not found", - __func__); - return; - } - - vrf_delete(vrf); - } + dplane_ctx_set_ifp_table_id( + ctx, *(uint32_t *)RTA_DATA(attr[IFLA_VRF_TABLE])); } static uint32_t get_iflink_speed(struct interface *interface, int *error) @@ -665,62 +528,59 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, * bridge interface is added or updated, take further actions to map * its members. Likewise, for VxLAN interface. */ -static void netlink_interface_update_l2info(struct interface *ifp, +static void netlink_interface_update_l2info(struct zebra_dplane_ctx *ctx, + enum zebra_iftype zif_type, struct rtattr *link_data, int add, ns_id_t link_nsid) { + struct zebra_l2info_bridge bridge_info; + struct zebra_l2info_vlan vlan_info; + struct zebra_l2info_vxlan vxlan_info; + struct zebra_l2info_gre gre_info; + if (!link_data) return; - if (IS_ZEBRA_IF_BRIDGE(ifp)) { - struct zebra_l2info_bridge bridge_info; - + switch (zif_type) { + case ZEBRA_IF_BRIDGE: netlink_extract_bridge_info(link_data, &bridge_info); - zebra_l2_bridge_add_update(ifp, &bridge_info, add); - } else if (IS_ZEBRA_IF_VLAN(ifp)) { - struct zebra_l2info_vlan vlan_info; - + dplane_ctx_set_ifp_bridge_info(ctx, &bridge_info); + break; + case ZEBRA_IF_VLAN: netlink_extract_vlan_info(link_data, &vlan_info); - zebra_l2_vlanif_update(ifp, &vlan_info); - zebra_evpn_acc_bd_svi_set(ifp->info, NULL, - !!if_is_operative(ifp)); - } else if (IS_ZEBRA_IF_VXLAN(ifp)) { - struct zebra_l2info_vxlan vxlan_info; - + dplane_ctx_set_ifp_vlan_info(ctx, &vlan_info); + break; + case ZEBRA_IF_VXLAN: 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); - } else if (IS_ZEBRA_IF_GRE(ifp)) { - struct zebra_l2info_gre gre_info; - + dplane_ctx_set_ifp_vxlan_info(ctx, &vxlan_info); + break; + case ZEBRA_IF_GRE: netlink_extract_gre_info(link_data, &gre_info); gre_info.link_nsid = link_nsid; - zebra_l2_greif_add_update(ifp, &gre_info, add); - if (link_nsid != NS_UNKNOWN && - gre_info.ifindex_link) - zebra_if_update_link(ifp, gre_info.ifindex_link, - link_nsid); + dplane_ctx_set_ifp_gre_info(ctx, &gre_info); + break; + case ZEBRA_IF_OTHER: + case ZEBRA_IF_VRF: + case ZEBRA_IF_MACVLAN: + case ZEBRA_IF_VETH: + case ZEBRA_IF_BOND: + break; } } -static int netlink_bridge_vxlan_vlan_vni_map_update(struct interface *ifp, - struct rtattr *af_spec) +static int +netlink_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx, + struct rtattr *af_spec) { int rem; - vni_t vni_id; - vlanid_t vid; uint16_t flags; struct rtattr *i; - struct zebra_vxlan_vni vni; - struct zebra_vxlan_vni *vnip; - struct hash *vni_table = NULL; + struct zebra_vxlan_vni_array *vniarray = NULL; struct zebra_vxlan_vni vni_end; struct zebra_vxlan_vni vni_start; struct rtattr *aftb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1]; + int32_t count = 0; memset(&vni_start, 0, sizeof(vni_start)); memset(&vni_end, 0, sizeof(vni_end)); @@ -739,80 +599,46 @@ static int netlink_bridge_vxlan_vlan_vni_map_update(struct interface *ifp, /* vlan-vni info missing */ return 0; + count++; flags = 0; - memset(&vni, 0, sizeof(vni)); + vniarray = XREALLOC( + MTYPE_TMP, vniarray, + sizeof(struct zebra_vxlan_vni_array) + + count * sizeof(struct zebra_vxlan_vni)); + + memset(&vniarray->vnis[count - 1], 0, + sizeof(struct zebra_vxlan_vni)); - vni.vni = *(vni_t *)RTA_DATA(aftb[IFLA_BRIDGE_VLAN_TUNNEL_ID]); - vni.access_vlan = *(vlanid_t *)RTA_DATA( + vniarray->vnis[count - 1].vni = + *(vni_t *)RTA_DATA(aftb[IFLA_BRIDGE_VLAN_TUNNEL_ID]); + vniarray->vnis[count - 1].access_vlan = *(vlanid_t *)RTA_DATA( aftb[IFLA_BRIDGE_VLAN_TUNNEL_VID]); if (aftb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]) flags = *(uint16_t *)RTA_DATA( aftb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]); - if (flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { - vni_start = vni; - continue; - } - - if (flags & BRIDGE_VLAN_INFO_RANGE_END) - vni_end = vni; - - if (!(flags & BRIDGE_VLAN_INFO_RANGE_END)) { - vni_start = vni; - vni_end = vni; - } - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "Vlan-Vni(%d:%d-%d:%d) update for VxLAN IF %s(%u)", - vni_start.access_vlan, vni_end.access_vlan, - vni_start.vni, vni_end.vni, ifp->name, - ifp->ifindex); - - if (!vni_table) { - vni_table = zebra_vxlan_vni_table_create(); - if (!vni_table) - return 0; - } - - for (vid = vni_start.access_vlan, vni_id = vni_start.vni; - vid <= vni_end.access_vlan; vid++, vni_id++) { - - memset(&vni, 0, sizeof(vni)); - vni.vni = vni_id; - vni.access_vlan = vid; - vnip = hash_get(vni_table, &vni, zebra_vxlan_vni_alloc); - if (!vnip) - return 0; - } - - memset(&vni_start, 0, sizeof(vni_start)); - memset(&vni_end, 0, sizeof(vni_end)); + vniarray->vnis[count - 1].flags = flags; } - if (vni_table) - zebra_vxlan_if_vni_table_add_update(ifp, vni_table); - + if (count) { + vniarray->count = count; + dplane_ctx_set_ifp_vxlan_vni_array(ctx, vniarray); + } return 0; } -static int netlink_bridge_vxlan_update(struct interface *ifp, - struct rtattr *af_spec) +static int netlink_bridge_vxlan_update(struct zebra_dplane_ctx *ctx, + struct rtattr *af_spec) { struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; struct bridge_vlan_info *vinfo; - struct zebra_if *zif; - vlanid_t access_vlan; + struct zebra_dplane_bridge_vlan_info bvinfo; if (!af_spec) return 0; - zif = (struct zebra_if *)ifp->info; - - /* Single vxlan devices has vni-vlan range to update */ - if (IS_ZEBRA_VXLAN_IF_SVD(zif)) - return netlink_bridge_vxlan_vlan_vni_map_update(ifp, af_spec); + netlink_bridge_vxlan_vlan_vni_map_update(ctx, af_spec); /* There is a 1-to-1 mapping of VLAN to VxLAN - hence * only 1 access VLAN is accepted. @@ -822,121 +648,73 @@ static int netlink_bridge_vxlan_update(struct interface *ifp, return 0; vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]); - if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID)) - return 0; + bvinfo.flags = vinfo->flags; + bvinfo.vid = vinfo->vid; - 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); + dplane_ctx_set_ifp_bridge_vlan_info(ctx, &bvinfo); return 0; } -static void netlink_bridge_vlan_update(struct interface *ifp, - struct rtattr *af_spec) +static void netlink_bridge_vlan_update(struct zebra_dplane_ctx *ctx, + 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); + struct zebra_dplane_bridge_vlan_info_array *bvarray = NULL; + int32_t count = 0; if (af_spec) { 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; - } + count++; + bvarray = XREALLOC( + MTYPE_TMP, bvarray, + sizeof(struct + zebra_dplane_bridge_vlan_info_array) + + count * sizeof(struct + zebra_dplane_bridge_vlan_info)); - if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) - vid_range_start = vinfo->vid; - - zebra_vlan_bitmap_compute(ifp, vid_range_start, - vinfo->vid); + vinfo = RTA_DATA(i); + bvarray->array[count - 1].flags = vinfo->flags; + bvarray->array[count - 1].vid = vinfo->vid; } } - zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap); - - bf_free(old_vlan_bitmap); + if (count) { + bvarray->count = count; + dplane_ctx_set_ifp_bridge_vlan_info_array(ctx, bvarray); + } } -static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, - int startup) +static int netlink_bridge_interface(struct zebra_dplane_ctx *ctx, + struct rtattr *af_spec, int startup) { - char *name = NULL; - struct ifinfomsg *ifi; - struct rtattr *tb[IFLA_MAX + 1]; - struct interface *ifp; - struct zebra_if *zif; - struct rtattr *af_spec; - - /* Fetch name and ifindex */ - ifi = NLMSG_DATA(h); - netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); - - if (tb[IFLA_IFNAME] == NULL) - return -1; - name = (char *)RTA_DATA(tb[IFLA_IFNAME]); - /* The interface should already be known, if not discard. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ifi->ifi_index); - if (!ifp) { - zlog_debug("Cannot find bridge IF %s(%u)", name, - ifi->ifi_index); - return 0; - } - - /* We are only interested in the access VLAN i.e., AF_SPEC */ - af_spec = tb[IFLA_AF_SPEC]; - - if (IS_ZEBRA_IF_VXLAN(ifp)) - return netlink_bridge_vxlan_update(ifp, af_spec); + netlink_bridge_vxlan_update(ctx, af_spec); /* 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); + netlink_bridge_vlan_update(ctx, af_spec); + dplane_provider_enqueue_to_zebra(ctx); return 0; } -static bool is_if_protodown_reason_only_frr(uint32_t rc_bitfield) -{ - /* This shouldn't be possible */ - assert(frr_protodown_r_bit < 32); - return (rc_bitfield == (((uint32_t)1) << frr_protodown_r_bit)); -} - /* * Process interface protodown dplane update. * * If the interface is an es bond member then it must follow EVPN's * protodown setting. */ -static void netlink_proc_dplane_if_protodown(struct zebra_if *zif, +static void netlink_proc_dplane_if_protodown(struct zebra_dplane_ctx *ctx, struct rtattr **tb) { bool protodown; - bool old_protodown; uint32_t rc_bitfield = 0; struct rtattr *pd_reason_info[IFLA_MAX + 1]; @@ -951,59 +729,9 @@ static void netlink_proc_dplane_if_protodown(struct zebra_if *zif, pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE]); } - /* - * Set our reason code to note it wasn't us. - * If the reason we got from the kernel is ONLY frr though, don't - * set it. - */ - COND_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_EXTERNAL, - protodown && rc_bitfield && - !is_if_protodown_reason_only_frr(rc_bitfield)); - - - old_protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); - if (protodown == old_protodown) - return; - - if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("interface %s dplane change, protdown %s", - zif->ifp->name, protodown ? "on" : "off"); - - /* Set protodown, respectively */ - COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, protodown); - - if (zebra_evpn_is_es_bond_member(zif->ifp)) { - /* Check it's not already being sent to the dplane first */ - if (protodown && - CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "bond mbr %s protodown on recv'd but already sent protodown on to the dplane", - zif->ifp->name); - return; - } - - if (!protodown && - CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN)) { - if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "bond mbr %s protodown off recv'd but already sent protodown off to the dplane", - zif->ifp->name); - return; - } - - if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "bond mbr %s reinstate protodown %s in the dplane", - zif->ifp->name, old_protodown ? "on" : "off"); - - if (old_protodown) - SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN); - else - SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN); - - dplane_intf_update(zif->ifp); - } + dplane_ctx_set_ifp_rc_bitfield(ctx, rc_bitfield); + dplane_ctx_set_ifp_protodown(ctx, protodown); + dplane_ctx_set_ifp_protodown_set(ctx, true); } static uint8_t netlink_parse_lacp_bypass(struct rtattr **linkinfo) @@ -1020,201 +748,6 @@ static uint8_t netlink_parse_lacp_bypass(struct rtattr **linkinfo) return bypass; } -/* - * Only called at startup to cleanup leftover protodown reasons we may - * have not cleaned up. We leave protodown set though. - */ -static void if_sweep_protodown(struct zebra_if *zif) -{ - bool protodown; - - protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); - - if (!protodown) - return; - - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("interface %s sweeping protodown %s reason 0x%x", - zif->ifp->name, protodown ? "on" : "off", - zif->protodown_rc); - - /* Only clear our reason codes, leave external if it was set */ - UNSET_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_ALL); - dplane_intf_update(zif->ifp); -} - -/* - * Called from interface_lookup_netlink(). This function is only used - * during bootstrap. - */ -static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) -{ - int len; - struct ifinfomsg *ifi; - struct rtattr *tb[IFLA_MAX + 1]; - struct rtattr *linkinfo[IFLA_MAX + 1]; - struct interface *ifp; - char *name = NULL; - char *kind = NULL; - char *desc = NULL; - char *slave_kind = NULL; - struct zebra_ns *zns = NULL; - vrf_id_t vrf_id = VRF_DEFAULT; - enum zebra_iftype zif_type = ZEBRA_IF_OTHER; - enum zebra_slave_iftype zif_slave_type = ZEBRA_IF_SLAVE_NONE; - ifindex_t bridge_ifindex = IFINDEX_INTERNAL; - ifindex_t link_ifindex = IFINDEX_INTERNAL; - ifindex_t bond_ifindex = IFINDEX_INTERNAL; - struct zebra_if *zif; - ns_id_t link_nsid = ns_id; - uint8_t bypass = 0; - - frrtrace(3, frr_zebra, netlink_interface, h, ns_id, startup); - - zns = zebra_ns_lookup(ns_id); - ifi = NLMSG_DATA(h); - - if (h->nlmsg_type != RTM_NEWLINK) - return 0; - - 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", - __func__, h->nlmsg_len, - (size_t)NLMSG_LENGTH(sizeof(struct ifinfomsg))); - return -1; - } - - /* We are interested in some AF_BRIDGE notifications. */ - if (ifi->ifi_family == AF_BRIDGE) - return netlink_bridge_interface(h, len, ns_id, startup); - - /* Looking up interface name. */ - memset(linkinfo, 0, sizeof(linkinfo)); - netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, - NLA_F_NESTED); - - /* check for wireless messages to ignore */ - if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s: ignoring IFLA_WIRELESS message", - __func__); - return 0; - } - - if (tb[IFLA_IFNAME] == NULL) - return -1; - name = (char *)RTA_DATA(tb[IFLA_IFNAME]); - - if (tb[IFLA_IFALIAS]) - desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); - - if (tb[IFLA_LINKINFO]) { - netlink_parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, - tb[IFLA_LINKINFO]); - - if (linkinfo[IFLA_INFO_KIND]) - kind = RTA_DATA(linkinfo[IFLA_INFO_KIND]); - - if (linkinfo[IFLA_INFO_SLAVE_KIND]) - slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]); - - if ((slave_kind != NULL) && strcmp(slave_kind, "bond") == 0) - netlink_determine_zebra_iftype("bond_slave", &zif_type); - else - netlink_determine_zebra_iftype(kind, &zif_type); - } - - /* If VRF, create the VRF structure itself. */ - if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { - netlink_vrf_change(h, tb[IFLA_LINKINFO], ns_id, name); - vrf_id = (vrf_id_t)ifi->ifi_index; - } - - if (tb[IFLA_MASTER]) { - if (slave_kind && (strcmp(slave_kind, "vrf") == 0) - && !vrf_is_backend_netns()) { - zif_slave_type = ZEBRA_IF_SLAVE_VRF; - vrf_id = *(uint32_t *)RTA_DATA(tb[IFLA_MASTER]); - } else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) { - zif_slave_type = ZEBRA_IF_SLAVE_BRIDGE; - bridge_ifindex = - *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]); - } else if (slave_kind && (strcmp(slave_kind, "bond") == 0)) { - zif_slave_type = ZEBRA_IF_SLAVE_BOND; - bond_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]); - bypass = netlink_parse_lacp_bypass(linkinfo); - } else - zif_slave_type = ZEBRA_IF_SLAVE_OTHER; - } - if (vrf_is_backend_netns()) - vrf_id = (vrf_id_t)ns_id; - - /* If linking to another interface, note it. */ - 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); - } - - ifp = if_get_by_name(name, vrf_id, NULL); - set_ifindex(ifp, ifi->ifi_index, zns); /* add it to ns struct */ - - 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, NULL); - ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; - - /* Set zebra interface type */ - zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); - if (IS_ZEBRA_IF_VRF(ifp)) - SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); - - /* - * Just set the @link/lower-device ifindex. During nldump interfaces are - * not ordered in any fashion so we may end up getting upper devices - * before lower devices. We will setup the real linkage once the dump - * is complete. - */ - zif = (struct zebra_if *)ifp->info; - zif->link_ifindex = link_ifindex; - - if (desc) { - XFREE(MTYPE_ZIF_DESC, zif->desc); - zif->desc = XSTRDUP(MTYPE_ZIF_DESC, desc); - } - - /* Hardware type and address. */ - ifp->ll_type = netlink_to_zebra_link_type(ifi->ifi_type); - - netlink_interface_update_hw_addr(tb, ifp); - - if_add_update(ifp); - - /* Extract and save L2 interface information, take additional actions. - */ - netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], - 1, link_nsid); - if (IS_ZEBRA_IF_BOND(ifp)) - zebra_l2if_update_bond(ifp, true); - if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id, - ZEBRA_BRIDGE_NO_ACTION); - else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) - zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass); - - if (tb[IFLA_PROTO_DOWN]) { - netlink_proc_dplane_if_protodown(zif, tb); - if_sweep_protodown(zif); - } - - return 0; -} - /* Request for specific interface or address information from the kernel */ static int netlink_request_intf_addr(struct nlsock *netlink_cmd, int family, int type, uint32_t filter_mask) @@ -1261,7 +794,7 @@ int interface_lookup_netlink(struct zebra_ns *zns) { int ret; struct zebra_dplane_info dp_info; - struct nlsock *netlink_cmd = &zns->netlink_cmd; + struct nlsock *netlink_cmd = &zns->netlink_dplane_out; /* Capture key info from ns struct */ zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/); @@ -1270,7 +803,7 @@ int interface_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_PACKET, RTM_GETLINK, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface, netlink_cmd, &dp_info, 0, + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true); if (ret < 0) return ret; @@ -1280,11 +813,18 @@ int interface_lookup_netlink(struct zebra_ns *zns) RTEXT_FILTER_BRVLAN); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface, netlink_cmd, &dp_info, 0, + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true); if (ret < 0) return ret; + return ret; +} + +void interface_list_tunneldump(struct zebra_ns *zns) +{ + int ret; + /* * So netlink_tunneldump_read will initiate a request * per tunnel to get data. If we are on a kernel that @@ -1297,13 +837,12 @@ int interface_lookup_netlink(struct zebra_ns *zns) */ ret = netlink_tunneldump_read(zns); if (ret < 0) - return ret; + return; - /* fixup linkages */ - zebra_if_update_all_links(zns); - return 0; + zebra_dplane_startup_stage(zns, ZEBRA_DPLANE_TUNNELS_READ); } + /** * interface_addr_lookup_netlink() - Look up interface addresses * @@ -1323,8 +862,8 @@ static int interface_addr_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_INET, RTM_GETADDR, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface_addr, netlink_cmd, &dp_info, - 0, true); + ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, + &dp_info, 0, true); if (ret < 0) return ret; @@ -1332,8 +871,8 @@ static int interface_addr_lookup_netlink(struct zebra_ns *zns) ret = netlink_request_intf_addr(netlink_cmd, AF_INET6, RTM_GETADDR, 0); if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface_addr, netlink_cmd, &dp_info, - 0, true); + ret = netlink_parse_info(netlink_interface_addr_dplane, netlink_cmd, + &dp_info, 0, true); if (ret < 0) return ret; @@ -1516,6 +1055,7 @@ static ssize_t netlink_intf_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: flog_err( EC_ZEBRA_NHG_FIB_UPDATE, "Context received for kernel interface update with incorrect OP code (%u)", @@ -1943,7 +1483,6 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id, /* Enqueue ctx for main pthread to process */ dplane_provider_enqueue_to_zebra(ctx); - return 0; } @@ -1953,25 +1492,21 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct rtattr *linkinfo[IFLA_MAX + 1]; - struct interface *ifp; char *name = NULL; char *kind = NULL; - char *desc = NULL; char *slave_kind = NULL; - struct zebra_ns *zns; vrf_id_t vrf_id = VRF_DEFAULT; enum zebra_iftype zif_type = ZEBRA_IF_OTHER; enum zebra_slave_iftype zif_slave_type = ZEBRA_IF_SLAVE_NONE; ifindex_t bridge_ifindex = IFINDEX_INTERNAL; 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; ifindex_t master_infindex = IFINDEX_INTERNAL; uint8_t bypass = 0; - zns = zebra_ns_lookup(ns_id); + frrtrace(3, frr_zebra, netlink_interface, h, ns_id, startup); + ifi = NLMSG_DATA(h); /* assume if not default zns, then new VRF */ @@ -2000,10 +1535,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) return -1; } - /* We are interested in some AF_BRIDGE notifications. */ - if (ifi->ifi_family == AF_BRIDGE) - return netlink_bridge_interface(h, len, ns_id, startup); - /* Looking up interface name. */ memset(linkinfo, 0, sizeof(linkinfo)); netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len, @@ -2050,18 +1581,47 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 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]); - } - /* See if interface is present. */ - ifp = if_lookup_by_name_per_ns(zns, name); + struct zebra_dplane_ctx *ctx = dplane_ctx_alloc(); + dplane_ctx_set_ns_id(ctx, ns_id); + dplane_ctx_set_ifp_link_nsid(ctx, link_nsid); + dplane_ctx_set_ifp_zif_type(ctx, zif_type); + dplane_ctx_set_ifindex(ctx, ifi->ifi_index); + dplane_ctx_set_ifname(ctx, name); + dplane_ctx_set_ifp_startup(ctx, startup); + dplane_ctx_set_ifp_family(ctx, ifi->ifi_family); + + /* We are interested in some AF_BRIDGE notifications. */ +#ifndef AF_BRIDGE +#define AF_BRIDGE 7 +#endif + if (ifi->ifi_family == AF_BRIDGE) { + dplane_ctx_set_op(ctx, DPLANE_OP_INTF_INSTALL); + return netlink_bridge_interface(ctx, tb[IFLA_AF_SPEC], startup); + } if (h->nlmsg_type == RTM_NEWLINK) { + dplane_ctx_set_ifp_link_ifindex(ctx, link_ifindex); + dplane_ctx_set_op(ctx, DPLANE_OP_INTF_INSTALL); + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_QUEUED); + if (tb[IFLA_IFALIAS]) { + dplane_ctx_set_ifp_desc(ctx, + RTA_DATA(tb[IFLA_IFALIAS])); + } + if (!tb[IFLA_MTU]) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "RTM_NEWLINK for interface %s(%u) without MTU set", + name, ifi->ifi_index); + return 0; + } + dplane_ctx_set_ifp_mtu(ctx, *(int *)RTA_DATA(tb[IFLA_MTU])); + /* 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], ns_id, name); - vrf_id = (vrf_id_t)ifi->ifi_index; + netlink_vrf_change(h, tb[IFLA_LINKINFO], ns_id, name, + ctx); + vrf_id = ifi->ifi_index; } if (tb[IFLA_MASTER]) { @@ -2084,264 +1644,45 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } + dplane_ctx_set_ifp_zif_slave_type(ctx, zif_slave_type); + dplane_ctx_set_ifp_vrf_id(ctx, vrf_id); + dplane_ctx_set_ifp_master_ifindex(ctx, master_infindex); + dplane_ctx_set_ifp_bridge_ifindex(ctx, bridge_ifindex); + dplane_ctx_set_ifp_bond_ifindex(ctx, bond_ifindex); + dplane_ctx_set_ifp_bypass(ctx, bypass); + dplane_ctx_set_ifp_zltype( + ctx, netlink_to_zebra_link_type(ifi->ifi_type)); + if (vrf_is_backend_netns()) - vrf_id = (vrf_id_t)ns_id; - if (ifp == NULL - || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { - /* 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", - name, ifi->ifi_index, vrf_id, zif_type, - zif_slave_type, master_infindex, - ifi->ifi_flags); - - if (ifp == NULL) { - /* unknown interface */ - ifp = if_get_by_name(name, vrf_id, NULL); - } else { - /* pre-configured interface, learnt now */ - if (ifp->vrf->vrf_id != vrf_id) - if_update_to_new_vrf(ifp, vrf_id); - } - - /* Update interface information. */ - set_ifindex(ifp, ifi->ifi_index, zns); - ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (!tb[IFLA_MTU]) { - zlog_debug( - "RTM_NEWLINK for interface %s(%u) without MTU set", - name, ifi->ifi_index); - return 0; - } - ifp->mtu6 = ifp->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]); - ifp->metric = 0; - ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; - - /* Set interface type */ - zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); - if (IS_ZEBRA_IF_VRF(ifp)) - SET_FLAG(ifp->status, - ZEBRA_INTERFACE_VRF_LOOPBACK); - - /* Update link. */ - zebra_if_update_link(ifp, link_ifindex, link_nsid); - - ifp->ll_type = - netlink_to_zebra_link_type(ifi->ifi_type); - netlink_interface_update_hw_addr(tb, ifp); - - /* Inform clients, install any configured addresses. */ - if_add_update(ifp); - - /* Extract and save L2 interface information, take - * additional actions. */ - 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, ns_id, - ZEBRA_BRIDGE_NO_ACTION); - else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) - zebra_l2if_update_bond_slave(ifp, bond_ifindex, - !!bypass); - - if (tb[IFLA_PROTO_DOWN]) - netlink_proc_dplane_if_protodown(ifp->info, tb); - if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zif = ifp->info; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "RTM_NEWLINK ADD for %s(%u), vlan-aware %d", - name, ifp->ifindex, - IS_ZEBRA_IF_BRIDGE_VLAN_AWARE( - zif)); - } - } else if (ifp->vrf->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", - name, ifp->ifindex, ifp->vrf->vrf_id, - vrf_id, ifi->ifi_flags); + dplane_ctx_set_ifp_vrf_id(ctx, ns_id); - if_handle_vrf_change(ifp, vrf_id); - } else { - bool was_bridge_slave, was_bond_slave; - uint8_t chgflags = ZEBRA_BRIDGE_NO_ACTION; - zif = ifp->info; + dplane_ctx_set_ifp_flags(ctx, ifi->ifi_flags & 0x0000fffff); - /* Interface update. */ - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "RTM_NEWLINK update for %s(%u) sl_type %d master %u flags 0x%x", - name, ifp->ifindex, zif_slave_type, - master_infindex, ifi->ifi_flags); + if (tb[IFLA_PROTO_DOWN]) { + dplane_ctx_set_ifp_protodown_set(ctx, true); + netlink_proc_dplane_if_protodown(ctx, tb); + } else + dplane_ctx_set_ifp_protodown_set(ctx, false); - set_ifindex(ifp, ifi->ifi_index, zns); - if (!tb[IFLA_MTU]) { - zlog_debug( - "RTM_NEWLINK for interface %s(%u) without MTU set", - name, ifi->ifi_index); - return 0; - } - ifp->mtu6 = ifp->mtu = *(int *)RTA_DATA(tb[IFLA_MTU]); - ifp->metric = 0; - - /* Update interface type - NOTE: Only slave_type can - * change. */ - was_bridge_slave = IS_ZEBRA_IF_BRIDGE_SLAVE(ifp); - was_bond_slave = IS_ZEBRA_IF_BOND_SLAVE(ifp); - zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); - - memcpy(old_hw_addr, ifp->hw_addr, INTERFACE_HWADDR_MAX); - - /* Update link. */ - zebra_if_update_link(ifp, link_ifindex, link_nsid); - - ifp->ll_type = - netlink_to_zebra_link_type(ifi->ifi_type); - netlink_interface_update_hw_addr(tb, ifp); - - if (tb[IFLA_PROTO_DOWN]) - netlink_proc_dplane_if_protodown(ifp->info, tb); - - if (if_is_no_ptm_operative(ifp)) { - bool is_up = if_is_operative(ifp); - ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (!if_is_no_ptm_operative(ifp) || - CHECK_FLAG(zif->flags, - ZIF_FLAG_PROTODOWN)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "Intf %s(%u) has gone DOWN", - name, ifp->ifindex); - if_down(ifp); - rib_update(RIB_UPDATE_KERNEL); - } else if (if_is_operative(ifp)) { - bool mac_updated = false; - - /* Must notify client daemons of new - * interface status. */ - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "Intf %s(%u) PTM up, notifying clients", - name, ifp->ifindex); - if_up(ifp, !is_up); - - /* Update EVPN VNI when SVI MAC change - */ - if (memcmp(old_hw_addr, ifp->hw_addr, - INTERFACE_HWADDR_MAX)) - mac_updated = true; - if (IS_ZEBRA_IF_VLAN(ifp) - && mac_updated) { - struct interface *link_if; - - link_if = - if_lookup_by_index_per_ns( - zebra_ns_lookup(NS_DEFAULT), - link_ifindex); - if (link_if) - zebra_vxlan_svi_up(ifp, - link_if); - } else if (mac_updated - && IS_ZEBRA_IF_BRIDGE(ifp)) { - zlog_debug( - "Intf %s(%u) bridge changed MAC address", - name, ifp->ifindex); - chgflags = - ZEBRA_BRIDGE_MASTER_MAC_CHANGE; - } - } - } else { - ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (if_is_operative(ifp) && - !CHECK_FLAG(zif->flags, - ZIF_FLAG_PROTODOWN)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "Intf %s(%u) has come UP", - name, ifp->ifindex); - if_up(ifp, true); - if (IS_ZEBRA_IF_BRIDGE(ifp)) - chgflags = - ZEBRA_BRIDGE_MASTER_UP; - } 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, link_nsid); - if (IS_ZEBRA_IF_BRIDGE(ifp)) - zebra_l2if_update_bridge(ifp, chgflags); - if (IS_ZEBRA_IF_BOND(ifp)) - zebra_l2if_update_bond(ifp, true); - if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) - zebra_l2if_update_bridge_slave( - ifp, bridge_ifindex, ns_id, chgflags); - else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) - zebra_l2if_update_bond_slave(ifp, bond_ifindex, - !!bypass); - if (IS_ZEBRA_IF_BRIDGE(ifp)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "RTM_NEWLINK update for %s(%u), vlan-aware %d", - name, ifp->ifindex, - IS_ZEBRA_IF_BRIDGE_VLAN_AWARE( - zif)); - } - } + netlink_interface_update_hw_addr(tb, ctx); - zif = ifp->info; - if (zif) { - XFREE(MTYPE_ZIF_DESC, zif->desc); - if (desc) - zif->desc = XSTRDUP(MTYPE_ZIF_DESC, desc); - } + /* Extract and save L2 interface information, take + * additional actions. */ + netlink_interface_update_l2info( + ctx, zif_type, linkinfo[IFLA_INFO_DATA], 1, link_nsid); } else { - /* Delete interface notification from kernel */ - if (ifp == NULL) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "RTM_DELLINK for unknown interface %s(%u)", - name, ifi->ifi_index); - return 0; - } - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("RTM_DELLINK for %s(%u)", name, - ifp->ifindex); - - UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); - - if (IS_ZEBRA_IF_BOND(ifp)) - zebra_l2if_update_bond(ifp, false); - if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) - zebra_l2if_update_bond_slave(ifp, bond_ifindex, false); - /* Special handling for bridge or VxLAN interfaces. */ - if (IS_ZEBRA_IF_BRIDGE(ifp)) - zebra_l2_bridge_del(ifp); - else if (IS_ZEBRA_IF_VXLAN(ifp)) - zebra_l2_vxlanif_del(ifp); - - if_delete_update(&ifp); - - /* If VRF, delete the VRF structure itself. */ - if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) - netlink_vrf_change(h, tb[IFLA_LINKINFO], ns_id, name); + zlog_debug("RTM_DELLINK for %s(%u), enqueuing to zebra", + name, ifi->ifi_index); + + dplane_ctx_set_op(ctx, DPLANE_OP_INTF_DELETE); + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_QUEUED); + + dplane_ctx_set_ifp_bond_ifindex(ctx, bond_ifindex); } + dplane_provider_enqueue_to_zebra(ctx); + return 0; } @@ -2399,9 +1740,10 @@ ssize_t netlink_intf_msg_encode(uint16_t cmd, return -1; nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_MASK, - (1 << frr_protodown_r_bit)); + (1 << if_netlink_get_frr_protodown_r_bit())); nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_VALUE, - ((int)pd_reason_val) << frr_protodown_r_bit); + ((int)pd_reason_val) + << if_netlink_get_frr_protodown_r_bit()); nl_attr_nest_end(&req->n, nest_protodown_reason); @@ -2417,6 +1759,13 @@ ssize_t netlink_intf_msg_encode(uint16_t cmd, void interface_list(struct zebra_ns *zns) { interface_lookup_netlink(zns); + + zebra_dplane_startup_stage(zns, ZEBRA_DPLANE_INTERFACES_READ); +} + +void interface_list_second(struct zebra_ns *zns) +{ + zebra_if_update_all_links(zns); /* We add routes for interface address, * so we need to get the nexthop info * from the kernel before we can do that @@ -2424,37 +1773,8 @@ void interface_list(struct zebra_ns *zns) netlink_nexthop_read(zns); interface_addr_lookup_netlink(zns); -} - -void if_netlink_set_frr_protodown_r_bit(uint8_t bit) -{ - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "Protodown reason bit index changed: bit-index %u -> bit-index %u", - frr_protodown_r_bit, bit); - - frr_protodown_r_bit = bit; -} -void if_netlink_unset_frr_protodown_r_bit(void) -{ - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "Protodown reason bit index changed: bit-index %u -> bit-index %u", - frr_protodown_r_bit, FRR_PROTODOWN_REASON_DEFAULT_BIT); - - frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; -} - - -bool if_netlink_frr_protodown_r_bit_is_set(void) -{ - return (frr_protodown_r_bit != FRR_PROTODOWN_REASON_DEFAULT_BIT); -} - -uint8_t if_netlink_get_frr_protodown_r_bit(void) -{ - return frr_protodown_r_bit; + zebra_dplane_startup_stage(zns, ZEBRA_DPLANE_ADDRESSES_READ); } /** @@ -2514,7 +1834,7 @@ int netlink_tunneldump_read(struct zebra_ns *zns) if (ret < 0) return ret; - ret = netlink_parse_info(netlink_interface, netlink_cmd, + ret = netlink_parse_info(netlink_link_change, netlink_cmd, &dp_info, 0, true); if (ret < 0) diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index ede6224188a1..9b31906a17da 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -42,17 +42,6 @@ extern int netlink_tunneldump_read(struct zebra_ns *zns); extern enum netlink_msg_status netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); -#define FRR_PROTODOWN_REASON_DEFAULT_BIT 7 -/* Protodown bit setter/getter - * - * Allow users to change the bit if it conflicts with another - * on their system. - */ -extern void if_netlink_set_frr_protodown_r_bit(uint8_t bit); -extern void if_netlink_unset_frr_protodown_r_bit(void); -extern bool if_netlink_frr_protodown_r_bit_is_set(void); -extern uint8_t if_netlink_get_frr_protodown_r_bit(void); - #ifdef __cplusplus } #endif diff --git a/zebra/interface.c b/zebra/interface.c index 1ba5e972f8d7..989763d13cd1 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -51,9 +51,9 @@ DEFINE_MTYPE(ZEBRA, ZIF_DESC, "Intf desc"); static void if_down_del_nbr_connected(struct interface *ifp); -static void if_zebra_speed_update(struct thread *thread) +static void if_zebra_speed_update(struct event *thread) { - struct interface *ifp = THREAD_ARG(thread); + struct interface *ifp = EVENT_ARG(thread); struct zebra_if *zif = ifp->info; uint32_t new_speed; bool changed = false; @@ -96,9 +96,9 @@ static void if_zebra_speed_update(struct thread *thread) return; zif->speed_update_count++; - thread_add_timer(zrouter.master, if_zebra_speed_update, ifp, - SPEED_UPDATE_SLEEP_TIME, &zif->speed_update); - thread_ignore_late_timer(zif->speed_update); + event_add_timer(zrouter.master, if_zebra_speed_update, ifp, + SPEED_UPDATE_SLEEP_TIME, &zif->speed_update); + event_ignore_late_timer(zif->speed_update); } } @@ -135,8 +135,11 @@ static int if_zebra_new_hook(struct interface *ifp) zebra_if->ifp = ifp; zebra_if->multicast = IF_ZEBRA_DATA_UNSPEC; + zebra_if->mpls_config = IF_ZEBRA_DATA_UNSPEC; zebra_if->shutdown = IF_ZEBRA_DATA_OFF; + zebra_if->link_nsid = NS_UNKNOWN; + zebra_if_nhg_dependents_init(zebra_if); zebra_ptm_if_init(zebra_if); @@ -161,9 +164,9 @@ static int if_zebra_new_hook(struct interface *ifp) * down upon startup. */ zebra_if->speed_update_count = 0; - thread_add_timer(zrouter.master, if_zebra_speed_update, ifp, 15, - &zebra_if->speed_update); - thread_ignore_late_timer(zebra_if->speed_update); + event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 15, + &zebra_if->speed_update); + event_ignore_late_timer(zebra_if->speed_update); return 0; } @@ -223,7 +226,7 @@ static int if_zebra_delete_hook(struct interface *ifp) XFREE(MTYPE_ZIF_DESC, zebra_if->desc); - THREAD_OFF(zebra_if->speed_update); + EVENT_OFF(zebra_if->speed_update); XFREE(MTYPE_ZINFO, zebra_if); } @@ -265,6 +268,9 @@ struct interface *if_link_per_ns(struct zebra_ns *ns, struct interface *ifp) /* Delete a VRF. This is called in vrf_terminate(). */ void if_unlink_per_ns(struct interface *ifp) { + if (!ifp->node) + return; + ifp->node->info = NULL; route_unlock_node(ifp->node); ifp->node = NULL; @@ -305,6 +311,14 @@ struct interface *if_lookup_by_name_per_ns(struct zebra_ns *ns, return NULL; } +struct interface *if_lookup_by_index_per_nsid(ns_id_t ns_id, uint32_t ifindex) +{ + struct zebra_ns *zns; + + zns = zebra_ns_lookup(ns_id); + return zns ? if_lookup_by_index_per_ns(zns, ifindex) : NULL; +} + const char *ifindex2ifname_per_ns(struct zebra_ns *zns, unsigned int ifindex) { struct interface *ifp; @@ -596,6 +610,11 @@ void if_add_update(struct interface *ifp) if_addr_wakeup(ifp); + if (if_data->mpls_config == IF_ZEBRA_DATA_ON) + dplane_intf_mpls_modify_state(ifp, true); + else if (if_data->mpls_config == IF_ZEBRA_DATA_OFF) + dplane_intf_mpls_modify_state(ifp, false); + if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "interface %s vrf %s(%u) index %d becomes active.", @@ -790,6 +809,8 @@ void if_delete_update(struct interface **pifp) if (ifp->vrf->vrf_id && !vrf_is_backend_netns()) if_handle_vrf_change(ifp, VRF_DEFAULT); + UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); + /* Reset some zebra interface params to default values. */ zif = ifp->info; if (zif) { @@ -830,6 +851,9 @@ void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id) /* This is to issue an UPDATE or a DELETE, as appropriate. */ zebra_interface_vrf_update_del(ifp, vrf_id); + if (if_is_vrf(ifp)) + return; + /* update VRF */ if_update_to_new_vrf(ifp, vrf_id); @@ -991,7 +1015,6 @@ void if_up(struct interface *ifp, bool install_connected) { struct zebra_if *zif; struct interface *link_if; - struct zebra_vrf *zvrf = ifp->vrf->info; zif = ifp->info; zif->up_count++; @@ -1024,8 +1047,7 @@ void if_up(struct interface *ifp, bool install_connected) link_if = ifp; zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zvrf->zns, - zif->link_ifindex); + link_if = zif->link; if (link_if) zebra_vxlan_svi_up(ifp, link_if); } else if (IS_ZEBRA_IF_MACVLAN(ifp)) { @@ -1038,9 +1060,9 @@ void if_up(struct interface *ifp, bool install_connected) if (zif->flags & ZIF_FLAG_EVPN_MH_UPLINK) zebra_evpn_mh_uplink_oper_update(zif); - thread_add_timer(zrouter.master, if_zebra_speed_update, ifp, 0, - &zif->speed_update); - thread_ignore_late_timer(zif->speed_update); + event_add_timer(zrouter.master, if_zebra_speed_update, ifp, 0, + &zif->speed_update); + event_ignore_late_timer(zif->speed_update); } /* Interface goes down. We have to manage different behavior of based @@ -1049,7 +1071,6 @@ void if_down(struct interface *ifp) { struct zebra_if *zif; struct interface *link_if; - struct zebra_vrf *zvrf = ifp->vrf->info; zif = ifp->info; zif->down_count++; @@ -1068,8 +1089,7 @@ void if_down(struct interface *ifp) link_if = ifp; zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zvrf->zns, - zif->link_ifindex); + link_if = zif->link; if (link_if) zebra_vxlan_svi_down(ifp, link_if); } else if (IS_ZEBRA_IF_MACVLAN(ifp)) { @@ -1109,6 +1129,7 @@ void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex, if (IS_ZEBRA_IF_VETH(ifp)) return; zif = (struct zebra_if *)ifp->info; + zif->link_nsid = ns_id; zif->link_ifindex = link_ifindex; zif->link = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), link_ifindex); @@ -1145,8 +1166,8 @@ void zebra_if_update_all_links(struct zebra_ns *zns) /* update SVI linkages */ if ((zif->link_ifindex != IFINDEX_INTERNAL) && !zif->link) { - zif->link = if_lookup_by_index_per_ns( - zns, zif->link_ifindex); + zif->link = if_lookup_by_index_per_nsid( + zif->link_nsid, zif->link_ifindex); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("interface %s/%d's lower fixup to %s/%d", ifp->name, ifp->ifindex, @@ -1278,6 +1299,9 @@ static void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx, const struct prefix *addr, *dest = NULL; enum dplane_op_e op; + if (!ifp) + return; + op = dplane_ctx_get_op(ctx); addr = dplane_ctx_get_intf_addr(ctx); @@ -1349,6 +1373,13 @@ static void zebra_if_update_ctx(struct zebra_dplane_ctx *ctx, bool pd_reason_val; bool down; + if (!ifp) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: Can't find ifp", __func__); + + return; + } + dp_res = dplane_ctx_get_status(ctx); pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx); down = dplane_ctx_intf_is_protodown(ctx); @@ -1396,6 +1427,13 @@ static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx, enum dplane_netconf_status_e mpls, mcast_on, linkdown; bool *mcast_set, *linkdown_set; + if (!ifp && ifindex != -1 && ifindex != -2) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: Can't find ifp(%u)", __func__, ifindex); + + return; + } + afi = dplane_ctx_get_afi(ctx); mpls = dplane_ctx_get_netconf_mpls(ctx); linkdown = dplane_ctx_get_netconf_linkdown(ctx); @@ -1418,7 +1456,7 @@ static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx, linkdown_set = &zrouter.default_linkdownv6; } } else { - zif = ifp ? ifp->info : NULL; + zif = ifp->info; if (!zif) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( @@ -1465,6 +1503,759 @@ static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx, (*linkdown_set ? "ON" : "OFF")); } +static void interface_vrf_change(enum dplane_op_e op, ifindex_t ifindex, + const char *name, uint32_t tableid, + ns_id_t ns_id) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf = NULL; + + if (op == DPLANE_OP_INTF_DELETE) { + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("DPLANE_OP_INTF_DELETE for VRF %s(%u)", name, + ifindex); + + vrf = vrf_lookup_by_id((vrf_id_t)ifindex); + if (!vrf) { + flog_warn(EC_ZEBRA_VRF_NOT_FOUND, + "%s(%u): vrf not found", name, ifindex); + return; + } + + vrf_delete(vrf); + } else { + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug( + "DPLANE_OP_INTF_UPDATE for VRF %s(%u) table %u", + name, ifindex, tableid); + + if (!vrf_lookup_by_id((vrf_id_t)ifindex)) { + vrf_id_t exist_id; + + exist_id = zebra_vrf_lookup_by_table(tableid, ns_id); + if (exist_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(exist_id); + + if (vrf) + flog_err(EC_ZEBRA_VRF_MISCONFIGURED, + "VRF %s id %u table id overlaps existing vrf %s(%d), misconfiguration exiting", + name, ifindex, vrf->name, + vrf->vrf_id); + else + flog_err(EC_ZEBRA_VRF_NOT_FOUND, + "VRF %s id %u does not exist", + name, ifindex); + + exit(-1); + } + } + + vrf = vrf_update((vrf_id_t)ifindex, name); + if (!vrf) { + flog_err(EC_LIB_INTERFACE, "VRF %s id %u not created", + name, ifindex); + return; + } + + /* + * This is the only place that we get the actual kernel table_id + * being used. We need it to set the table_id of the routes + * we are passing to the kernel.... And to throw some totally + * awesome parties. that too. + * + * At this point we *must* have a zvrf because the vrf_create + * callback creates one. We *must* set the table id + * before the vrf_enable because of( at the very least ) + * static routes being delayed for installation until + * during the vrf_enable callbacks. + */ + zvrf = (struct zebra_vrf *)vrf->info; + zvrf->table_id = tableid; + + /* Enable the created VRF. */ + if (!vrf_enable(vrf)) { + flog_err(EC_LIB_INTERFACE, + "Failed to enable VRF %s id %u", name, + ifindex); + return; + } + } +} + +/* + * Note: on netlink systems, there should be a 1-to-1 mapping + * between interface names and ifindex values. + */ +static void set_ifindex(struct interface *ifp, ifindex_t ifi_index, + struct zebra_ns *zns) +{ + struct interface *oifp; + + oifp = if_lookup_by_index_per_ns(zns, ifi_index); + if ((oifp != NULL) && (oifp != ifp)) { + if (ifi_index == IFINDEX_INTERNAL) + flog_err( + EC_LIB_INTERFACE, + "Netlink is setting interface %s ifindex to reserved internal value %u", + ifp->name, ifi_index); + else { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "interface index %d was renamed from %s to %s", + ifi_index, oifp->name, ifp->name); + if (if_is_up(oifp)) + flog_err( + EC_LIB_INTERFACE, + "interface rename detected on up interface: index %d was renamed from %s to %s, results are uncertain!", + ifi_index, oifp->name, ifp->name); + if_delete_update(&oifp); + } + } + if_set_index(ifp, ifi_index); +} + +static inline void zebra_if_set_ziftype(struct interface *ifp, + enum zebra_iftype zif_type, + enum zebra_slave_iftype 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 interface_update_hw_addr(struct zebra_dplane_ctx *ctx, + struct interface *ifp) +{ + int i; + + ifp->hw_addr_len = dplane_ctx_get_ifp_hw_addr_len(ctx); + memcpy(ifp->hw_addr, dplane_ctx_get_ifp_hw_addr(ctx), ifp->hw_addr_len); + + for (i = 0; i < ifp->hw_addr_len; i++) + if (ifp->hw_addr[i] != 0) + break; + + if (i == ifp->hw_addr_len) + ifp->hw_addr_len = 0; +} + +static void interface_update_l2info(struct zebra_dplane_ctx *ctx, + struct interface *ifp, + enum zebra_iftype zif_type, int add, + ns_id_t link_nsid) +{ + const struct zebra_l2info_vxlan *vxlan_info; + const struct zebra_l2info_gre *gre_info; + + switch (zif_type) { + case ZEBRA_IF_BRIDGE: + zebra_l2_bridge_add_update(ifp, + dplane_ctx_get_ifp_bridge_info(ctx)); + break; + case ZEBRA_IF_VLAN: + zebra_l2_vlanif_update(ifp, dplane_ctx_get_ifp_vlan_info(ctx)); + zebra_evpn_acc_bd_svi_set(ifp->info, NULL, + !!if_is_operative(ifp)); + break; + case ZEBRA_IF_VXLAN: + vxlan_info = dplane_ctx_get_ifp_vxlan_info(ctx); + 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); + break; + case ZEBRA_IF_GRE: + gre_info = dplane_ctx_get_ifp_gre_info(ctx); + zebra_l2_greif_add_update(ifp, gre_info, add); + if (link_nsid != NS_UNKNOWN && gre_info->ifindex_link) + zebra_if_update_link(ifp, gre_info->ifindex_link, + link_nsid); + break; + case ZEBRA_IF_OTHER: + case ZEBRA_IF_VRF: + case ZEBRA_IF_MACVLAN: + case ZEBRA_IF_VETH: + case ZEBRA_IF_BOND: + break; + } +} + +static bool is_if_protodown_reason_only_frr(uint32_t rc_bitfield) +{ + uint8_t frr_protodown_r_bit = if_netlink_get_frr_protodown_r_bit(); + + return (rc_bitfield == (((uint32_t)1) << frr_protodown_r_bit)); +} + +static void interface_if_protodown(struct interface *ifp, bool protodown, + uint32_t rc_bitfield) +{ + struct zebra_if *zif = ifp->info; + bool old_protodown; + + /* + * Set our reason code to note it wasn't us. + * If the reason we got from the kernel is ONLY frr though, don't + * set it. + */ + COND_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_EXTERNAL, + protodown && rc_bitfield && + !is_if_protodown_reason_only_frr(rc_bitfield)); + + + old_protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); + if (protodown == old_protodown) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("interface %s dplane change, protodown %s", + ifp->name, protodown ? "on" : "off"); + + /* Set protodown, respectively */ + COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, protodown); + + if (zebra_evpn_is_es_bond_member(ifp)) { + /* Check it's not already being sent to the dplane first */ + if (protodown && + CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "bond mbr %s protodown on recv'd but already sent protodown on to the dplane", + ifp->name); + return; + } + + if (!protodown && + CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "bond mbr %s protodown off recv'd but already sent protodown off to the dplane", + ifp->name); + return; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "bond mbr %s reinstate protodown %s in the dplane", + ifp->name, old_protodown ? "on" : "off"); + + if (old_protodown) + SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN); + else + SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN); + + dplane_intf_update(zif->ifp); + } +} + +static void if_sweep_protodown(struct zebra_if *zif) +{ + bool protodown; + + protodown = !!ZEBRA_IF_IS_PROTODOWN(zif); + + if (!protodown) + return; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("interface %s sweeping protodown %s reason 0x%x", + zif->ifp->name, protodown ? "on" : "off", + zif->protodown_rc); + + /* Only clear our reason codes, leave external if it was set */ + UNSET_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_ALL); + dplane_intf_update(zif->ifp); +} + +static void +interface_bridge_vxlan_vlan_vni_map_update(struct zebra_dplane_ctx *ctx, + struct interface *ifp) +{ + const struct zebra_vxlan_vni_array *vniarray = + dplane_ctx_get_ifp_vxlan_vni_array(ctx); + struct zebra_vxlan_vni vni_start, vni_end; + struct hash *vni_table = NULL; + struct zebra_vxlan_vni vni, *vnip; + vni_t vni_id; + vlanid_t vid; + int i; + + memset(&vni_start, 0, sizeof(vni_start)); + memset(&vni_end, 0, sizeof(vni_end)); + + for (i = 0; i < vniarray->count; i++) { + uint16_t flags = vniarray->vnis[i].flags; + + if (flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vni_start = vniarray->vnis[i]; + continue; + } + + if (flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_END) + vni_end = vniarray->vnis[i]; + + if (!(flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_END)) { + vni_start = vniarray->vnis[i]; + vni_end = vniarray->vnis[i]; + } + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug( + "Vlan-Vni(%d:%d-%d:%d) update for VxLAN IF %s(%u)", + vni_start.access_vlan, vni_end.access_vlan, + vni_start.vni, vni_end.vni, ifp->name, + ifp->ifindex); + + if (!vni_table) { + vni_table = zebra_vxlan_vni_table_create(); + if (!vni_table) + return; + } + + for (vid = vni_start.access_vlan, vni_id = vni_start.vni; + vid <= vni_end.access_vlan; vid++, vni_id++) { + + memset(&vni, 0, sizeof(vni)); + vni.vni = vni_id; + vni.access_vlan = vid; + vnip = hash_get(vni_table, &vni, zebra_vxlan_vni_alloc); + if (!vnip) + return; + } + + memset(&vni_start, 0, sizeof(vni_start)); + memset(&vni_end, 0, sizeof(vni_end)); + } + + if (vni_table) + zebra_vxlan_if_vni_table_add_update(ifp, vni_table); +} + +static void interface_bridge_vxlan_update(struct zebra_dplane_ctx *ctx, + struct interface *ifp) +{ + struct zebra_if *zif = ifp->info; + const struct zebra_dplane_bridge_vlan_info *bvinfo; + + if (IS_ZEBRA_VXLAN_IF_SVD(zif)) + interface_bridge_vxlan_vlan_vni_map_update(ctx, ifp); + + bvinfo = dplane_ctx_get_ifp_bridge_vlan_info(ctx); + + if (!(bvinfo->flags & DPLANE_BRIDGE_VLAN_INFO_PVID)) + return; + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", bvinfo->vid, + ifp->name, ifp->ifindex); + + zebra_l2_vxlanif_update_access_vlan(ifp, bvinfo->vid); +} + +static void interface_bridge_vlan_update(struct zebra_dplane_ctx *ctx, + struct interface *ifp) +{ + struct zebra_if *zif = ifp->info; + const struct zebra_dplane_bridge_vlan_info_array *bvarray; + struct zebra_dplane_bridge_vlan_info bvinfo; + bitfield_t old_vlan_bitmap; + uint16_t vid_range_start = 0; + int32_t i; + + /* 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); + + /* Could we have multiple bridge vlan infos? */ + bvarray = dplane_ctx_get_ifp_bridge_vlan_info_array(ctx); + if (!bvarray) + return; + + for (i = 0; i < bvarray->count; i++) { + bvinfo = bvarray->array[i]; + + if (bvinfo.flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vid_range_start = bvinfo.vid; + continue; + } + + if (!(bvinfo.flags & DPLANE_BRIDGE_VLAN_INFO_RANGE_END)) + vid_range_start = bvinfo.vid; + + zebra_vlan_bitmap_compute(ifp, vid_range_start, bvinfo.vid); + } + + zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap); + bf_free(old_vlan_bitmap); +} + +static void interface_bridge_handling(struct zebra_dplane_ctx *ctx, + struct interface *ifp, + enum zebra_iftype zif_type) +{ + struct zebra_if *zif; + + if (!ifp) { + zlog_warn("Cannot find bridge if %s(%u)", + dplane_ctx_get_ifname(ctx), + dplane_ctx_get_ifindex(ctx)); + return; + } + + if (IS_ZEBRA_IF_VXLAN(ifp)) + return interface_bridge_vxlan_update(ctx, ifp); + + /* + * build vlan bitmap associated with this interface if that + * device type is interested in the vlans + */ + zif = ifp->info; + if (bf_is_inited(zif->vlan_bitmap)) + interface_bridge_vlan_update(ctx, ifp); +} + +static void zebra_if_dplane_ifp_handling(struct zebra_dplane_ctx *ctx) +{ + enum dplane_op_e op = dplane_ctx_get_op(ctx); + const char *name = dplane_ctx_get_ifname(ctx); + ns_id_t ns_id = dplane_ctx_get_ns_id(ctx); + ifindex_t ifindex = dplane_ctx_get_ifindex(ctx); + ifindex_t bond_ifindex = dplane_ctx_get_ifp_bond_ifindex(ctx); + uint32_t tableid = dplane_ctx_get_ifp_table_id(ctx); + enum zebra_iftype zif_type = dplane_ctx_get_ifp_zif_type(ctx); + struct interface *ifp; + struct zebra_ns *zns; + + zns = zebra_ns_lookup(ns_id); + if (!zns) { + zlog_err("Where is our namespace?"); + return; + } + + if (IS_ZEBRA_DEBUG_DPLANE) + zlog_debug("%s for %s(%u)", dplane_op2str(op), name, ifindex); + + ifp = if_lookup_by_name_per_ns(zns, name); + if (op == DPLANE_OP_INTF_DELETE) { + /* Delete interface notification from kernel */ + if (ifp == NULL) { + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug( + "Delete LINK received for unknown interface %s(%u)", + name, ifindex); + return; + } + + if (IS_ZEBRA_IF_BOND(ifp)) + zebra_l2if_update_bond(ifp, false); + if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) + zebra_l2if_update_bond_slave(ifp, bond_ifindex, false); + /* Special handling for bridge or VxLAN interfaces. */ + if (IS_ZEBRA_IF_BRIDGE(ifp)) + zebra_l2_bridge_del(ifp); + else if (IS_ZEBRA_IF_VXLAN(ifp)) + zebra_l2_vxlanif_del(ifp); + + if_delete_update(&ifp); + + if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) + interface_vrf_change(op, ifindex, name, tableid, ns_id); + } else { + ifindex_t master_ifindex, bridge_ifindex, bond_ifindex, + link_ifindex; + enum zebra_slave_iftype zif_slave_type; + uint8_t bypass; + uint64_t flags; + vrf_id_t vrf_id; + uint32_t mtu; + ns_id_t link_nsid; + struct zebra_if *zif; + bool protodown, protodown_set, startup; + uint32_t rc_bitfield; + uint8_t old_hw_addr[INTERFACE_HWADDR_MAX]; + char *desc; + uint8_t family; + + /* If VRF, create or update the VRF structure itself. */ + if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) + interface_vrf_change(op, ifindex, name, tableid, ns_id); + + master_ifindex = dplane_ctx_get_ifp_master_ifindex(ctx); + zif_slave_type = dplane_ctx_get_ifp_zif_slave_type(ctx); + bridge_ifindex = dplane_ctx_get_ifp_bridge_ifindex(ctx); + bond_ifindex = dplane_ctx_get_ifp_bond_ifindex(ctx); + bypass = dplane_ctx_get_ifp_bypass(ctx); + flags = dplane_ctx_get_ifp_flags(ctx); + vrf_id = dplane_ctx_get_ifp_vrf_id(ctx); + mtu = dplane_ctx_get_ifp_mtu(ctx); + link_ifindex = dplane_ctx_get_ifp_link_ifindex(ctx); + link_nsid = dplane_ctx_get_ifp_link_nsid(ctx); + protodown_set = dplane_ctx_get_ifp_protodown_set(ctx); + protodown = dplane_ctx_get_ifp_protodown(ctx); + rc_bitfield = dplane_ctx_get_ifp_rc_bitfield(ctx); + startup = dplane_ctx_get_ifp_startup(ctx); + desc = dplane_ctx_get_ifp_desc(ctx); + family = dplane_ctx_get_ifp_family(ctx); + +#ifndef AF_BRIDGE + /* + * Work around to make free bsd happy at the moment + */ +#define AF_BRIDGE 7 +#endif + if (family == AF_BRIDGE) + return interface_bridge_handling(ctx, ifp, zif_type); + + if (ifp == NULL || + !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { + /* 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", + name, ifindex, vrf_id, zif_type, + zif_slave_type, master_ifindex); + + if (ifp == NULL) { + /* unknown interface */ + ifp = if_get_by_name(name, vrf_id, NULL); + } else { + /* pre-configured interface, learnt now */ + if (ifp->vrf->vrf_id != vrf_id) + if_update_to_new_vrf(ifp, vrf_id); + } + + /* Update interface information. */ + set_ifindex(ifp, ifindex, zns); + ifp->flags = flags; + ifp->mtu6 = ifp->mtu = mtu; + ifp->metric = 0; + ifp->speed = kernel_get_speed(ifp, NULL); + ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; + + /* Set interface type */ + zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); + if (IS_ZEBRA_IF_VRF(ifp)) + SET_FLAG(ifp->status, + ZEBRA_INTERFACE_VRF_LOOPBACK); + + /* Update link. */ + zebra_if_update_link(ifp, link_ifindex, link_nsid); + + /* + * Just set the @link/lower-device ifindex. During + * nldump interfaces are not ordered in any fashion so + * we may end up getting upper devices before lower + * devices. We will setup the real linkage once the dump + * is complete. + */ + zif = (struct zebra_if *)ifp->info; + zif->link_ifindex = link_ifindex; + + ifp->ll_type = dplane_ctx_get_ifp_zltype(ctx); + interface_update_hw_addr(ctx, ifp); + + /* Inform clients, install any configured addresses. */ + if_add_update(ifp); + + /* + * Extract and save L2 interface information, take + * additional actions. + */ + interface_update_l2info(ctx, ifp, zif_type, 1, + link_nsid); + if (IS_ZEBRA_IF_BOND(ifp)) + zebra_l2if_update_bond(ifp, true); + if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) + zebra_l2if_update_bridge_slave( + ifp, bridge_ifindex, ns_id, + ZEBRA_BRIDGE_NO_ACTION); + else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) + zebra_l2if_update_bond_slave(ifp, bond_ifindex, + !!bypass); + + if (protodown_set) { + interface_if_protodown(ifp, protodown, + rc_bitfield); + if (startup) + if_sweep_protodown(zif); + } + + if (IS_ZEBRA_IF_BRIDGE(ifp)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "RTM_NEWLINK ADD for %s(%u), vlan-aware %d", + name, ifp->ifindex, + IS_ZEBRA_IF_BRIDGE_VLAN_AWARE( + zif)); + } + } else if (ifp->vrf->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", + name, ifp->ifindex, ifp->vrf->vrf_id, + vrf_id); + + if_handle_vrf_change(ifp, vrf_id); + } else { + bool was_bridge_slave, was_bond_slave; + uint8_t chgflags = ZEBRA_BRIDGE_NO_ACTION; + + zif = ifp->info; + + /* Interface update. */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "RTM_NEWLINK update for %s(%u) sl_type %d master %u", + name, ifp->ifindex, zif_slave_type, + master_ifindex); + + set_ifindex(ifp, ifindex, zns); + ifp->mtu6 = ifp->mtu = mtu; + ifp->metric = 0; + + /* + * Update interface type - NOTE: Only slave_type can + * change. + */ + was_bridge_slave = IS_ZEBRA_IF_BRIDGE_SLAVE(ifp); + was_bond_slave = IS_ZEBRA_IF_BOND_SLAVE(ifp); + zebra_if_set_ziftype(ifp, zif_type, zif_slave_type); + + memcpy(old_hw_addr, ifp->hw_addr, INTERFACE_HWADDR_MAX); + + /* Update link. */ + zebra_if_update_link(ifp, link_ifindex, link_nsid); + + ifp->ll_type = dplane_ctx_get_ifp_zltype(ctx); + interface_update_hw_addr(ctx, ifp); + + if (protodown_set) + interface_if_protodown(ifp, protodown, + rc_bitfield); + + if (if_is_no_ptm_operative(ifp)) { + bool is_up = if_is_operative(ifp); + + ifp->flags = flags; + if (!if_is_no_ptm_operative(ifp) || + CHECK_FLAG(zif->flags, + ZIF_FLAG_PROTODOWN)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Intf %s(%u) has gone DOWN", + name, ifp->ifindex); + if_down(ifp); + rib_update(RIB_UPDATE_KERNEL); + } else if (if_is_operative(ifp)) { + bool mac_updated = false; + + /* + * Must notify client daemons of new + * interface status. + */ + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Intf %s(%u) PTM up, notifying clients", + name, ifp->ifindex); + if_up(ifp, !is_up); + + /* + * Update EVPN VNI when SVI MAC change + */ + if (memcmp(old_hw_addr, ifp->hw_addr, + INTERFACE_HWADDR_MAX)) + mac_updated = true; + if (IS_ZEBRA_IF_VLAN(ifp) && + mac_updated) { + struct interface *link_if; + + link_if = if_lookup_by_index_per_ns( + zebra_ns_lookup( + NS_DEFAULT), + link_ifindex); + if (link_if) + zebra_vxlan_svi_up( + ifp, link_if); + } else if (mac_updated && + IS_ZEBRA_IF_BRIDGE(ifp)) { + zlog_debug( + "Intf %s(%u) bridge changed MAC address", + name, ifp->ifindex); + chgflags = + ZEBRA_BRIDGE_MASTER_MAC_CHANGE; + } + } + } else { + ifp->flags = flags; + if (if_is_operative(ifp) && + !CHECK_FLAG(zif->flags, + ZIF_FLAG_PROTODOWN)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Intf %s(%u) has come UP", + name, ifp->ifindex); + if_up(ifp, true); + if (IS_ZEBRA_IF_BRIDGE(ifp)) + chgflags = + ZEBRA_BRIDGE_MASTER_UP; + } 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. + */ + interface_update_l2info(ctx, ifp, zif_type, 0, + link_nsid); + if (IS_ZEBRA_IF_BRIDGE(ifp)) + zebra_l2if_update_bridge(ifp, chgflags); + if (IS_ZEBRA_IF_BOND(ifp)) + zebra_l2if_update_bond(ifp, true); + if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) + zebra_l2if_update_bridge_slave( + ifp, bridge_ifindex, ns_id, chgflags); + else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) + zebra_l2if_update_bond_slave(ifp, bond_ifindex, + !!bypass); + if (IS_ZEBRA_IF_BRIDGE(ifp)) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "RTM_NEWLINK update for %s(%u), vlan-aware %d", + name, ifp->ifindex, + IS_ZEBRA_IF_BRIDGE_VLAN_AWARE( + zif)); + } + } + + zif = ifp->info; + if (zif) { + XFREE(MTYPE_ZIF_DESC, zif->desc); + if (desc[0]) + zif->desc = XSTRDUP(MTYPE_ZIF_DESC, desc); + } + } +} + void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) { struct zebra_ns *zns; @@ -1494,17 +2285,6 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) } ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (ifp == NULL) { - if (op != DPLANE_OP_INTF_NETCONFIG || - (ifindex != -1 && ifindex != -2)) { - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug( - "%s: can't find ifp at nsid %u index %d", - __func__, ns_id, ifindex); - - return; - } - } switch (op) { case DPLANE_OP_INTF_ADDR_ADD: @@ -1515,7 +2295,15 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: - zebra_if_update_ctx(ctx, ifp); + /* + * Queued from the dplane means it is something + * that we need to handle( create/delete the + * interface as needed ) + */ + if (dp_res == ZEBRA_DPLANE_REQUEST_QUEUED) + zebra_if_dplane_ifp_handling(ctx); + else + zebra_if_update_ctx(ctx, ifp); break; case DPLANE_OP_INTF_NETCONFIG: @@ -1570,6 +2358,7 @@ void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: break; /* should never hit here */ } } @@ -1685,9 +2474,6 @@ static const char *zebra_ziftype_2str(enum zebra_iftype zif_type) case ZEBRA_IF_BOND: return "bond"; - case ZEBRA_IF_BOND_SLAVE: - return "bond_slave"; - case ZEBRA_IF_MACVLAN: return "macvlan"; @@ -3001,10 +3787,10 @@ DEFPY (mpls, if (no) { dplane_intf_mpls_modify_state(ifp, false); - if_data->mpls = IF_ZEBRA_DATA_UNSPEC; + if_data->mpls_config = IF_ZEBRA_DATA_UNSPEC; } else { dplane_intf_mpls_modify_state(ifp, true); - if_data->mpls = IF_ZEBRA_DATA_ON; + if_data->mpls_config = IF_ZEBRA_DATA_ON; } return CMD_SUCCESS; @@ -4844,7 +5630,8 @@ static int if_config_write(struct vty *vty) IF_ZEBRA_DATA_ON ? "" : "no "); - if (if_data->mpls == IF_ZEBRA_DATA_ON) + + if (if_data->mpls_config == IF_ZEBRA_DATA_ON) vty_out(vty, " mpls enable\n"); } diff --git a/zebra/interface.h b/zebra/interface.h index c8ae906c916b..3b6799549b42 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -39,7 +39,6 @@ enum zebra_iftype { ZEBRA_IF_MACVLAN, /* MAC VLAN interface*/ ZEBRA_IF_VETH, /* VETH interface*/ ZEBRA_IF_BOND, /* Bond */ - ZEBRA_IF_BOND_SLAVE, /* Bond */ ZEBRA_IF_GRE, /* GRE interface */ }; @@ -114,6 +113,9 @@ struct zebra_if { /* MPLS status. */ bool mpls; + /* MPLS configuration */ + uint8_t mpls_config; + /* Linkdown status */ bool linkdown, linkdownv6; @@ -195,11 +197,12 @@ struct zebra_if { struct list *mac_list; /* Link fields - for sub-interfaces. */ + ns_id_t link_nsid; ifindex_t link_ifindex; struct interface *link; uint8_t speed_update_count; - struct thread *speed_update; + struct event *speed_update; /* * Does this interface have a v6 to v4 ll neighbor entry @@ -259,6 +262,8 @@ extern struct interface *if_lookup_by_index_per_ns(struct zebra_ns *, uint32_t); extern struct interface *if_lookup_by_name_per_ns(struct zebra_ns *, const char *); extern struct interface *if_link_per_ns(struct zebra_ns *, struct interface *); +extern struct interface *if_lookup_by_index_per_nsid(ns_id_t nsid, + uint32_t ifindex); extern const char *ifindex2ifname_per_ns(struct zebra_ns *, unsigned int); extern void if_unlink_per_ns(struct interface *); diff --git a/zebra/irdp.h b/zebra/irdp.h index 9b97bb93fa41..4330734992ce 100644 --- a/zebra/irdp.h +++ b/zebra/irdp.h @@ -114,7 +114,7 @@ struct irdp_interface { #define IF_SHUTDOWN (1<<6) struct interface *ifp; - struct thread *t_advertise; + struct event *t_advertise; unsigned long irdp_sent; uint16_t Lifetime; @@ -129,10 +129,10 @@ struct Adv { extern void irdp_if_init(void); extern int irdp_sock_init(void); extern int irdp_config_write(struct vty *, struct interface *); -extern void irdp_send_thread(struct thread *t_advert); +extern void irdp_send_thread(struct event *t_advert); extern void irdp_advert_off(struct interface *ifp); extern void process_solicit(struct interface *ifp); -extern void irdp_read_raw(struct thread *r); +extern void irdp_read_raw(struct event *r); extern void send_packet(struct interface *ifp, struct stream *s, uint32_t dst, struct prefix *p, uint32_t ttl); diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 0c9b6f3d3965..253e6a8dd617 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -24,7 +24,7 @@ #include "connected.h" #include "log.h" #include "zclient.h" -#include "thread.h" +#include "frrevent.h" #include "lib_errors.h" #include "zebra/interface.h" #include "zebra/rtadv.h" @@ -272,8 +272,8 @@ static void irdp_if_start(struct interface *ifp, int multicast, timer); irdp->t_advertise = NULL; - thread_add_timer(zrouter.master, irdp_send_thread, ifp, timer, - &irdp->t_advertise); + event_add_timer(zrouter.master, irdp_send_thread, ifp, timer, + &irdp->t_advertise); } static void irdp_if_stop(struct interface *ifp) diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c index ddcad92bdc7b..6548790e9a80 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -32,7 +32,7 @@ #include "connected.h" #include "log.h" #include "zclient.h" -#include "thread.h" +#include "frrevent.h" #include "privs.h" #include "libfrr.h" #include "lib_errors.h" @@ -56,7 +56,7 @@ extern struct zebra_privs_t zserv_privs; -struct thread *t_irdp_raw; +struct event *t_irdp_raw; /* Timer interval of irdp. */ int irdp_timer_interval = IRDP_DEFAULT_INTERVAL; @@ -97,7 +97,7 @@ int irdp_sock_init(void) return ret; }; - thread_add_read(zrouter.master, irdp_read_raw, NULL, sock, &t_irdp_raw); + event_add_read(zrouter.master, irdp_read_raw, NULL, sock, &t_irdp_raw); return sock; } @@ -190,10 +190,10 @@ static void irdp_advertisement(struct interface *ifp, struct prefix *p) stream_free(s); } -void irdp_send_thread(struct thread *t_advert) +void irdp_send_thread(struct event *t_advert) { uint32_t timer, tmp; - struct interface *ifp = THREAD_ARG(t_advert); + struct interface *ifp = EVENT_ARG(t_advert); struct zebra_if *zi = ifp->info; struct irdp_interface *irdp = zi->irdp; struct prefix *p; @@ -229,8 +229,8 @@ void irdp_send_thread(struct thread *t_advert) timer); irdp->t_advertise = NULL; - thread_add_timer(zrouter.master, irdp_send_thread, ifp, timer, - &irdp->t_advertise); + event_add_timer(zrouter.master, irdp_send_thread, ifp, timer, + &irdp->t_advertise); } void irdp_advert_off(struct interface *ifp) @@ -245,7 +245,7 @@ void irdp_advert_off(struct interface *ifp) if (!irdp) return; - THREAD_OFF(irdp->t_advertise); + EVENT_OFF(irdp->t_advertise); if (ifp->connected) for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, ifc)) { @@ -280,13 +280,13 @@ void process_solicit(struct interface *ifp) return; irdp->flags |= IF_SOLICIT; - THREAD_OFF(irdp->t_advertise); + EVENT_OFF(irdp->t_advertise); timer = (frr_weak_random() % MAX_RESPONSE_DELAY) + 1; irdp->t_advertise = NULL; - thread_add_timer(zrouter.master, irdp_send_thread, ifp, timer, - &irdp->t_advertise); + event_add_timer(zrouter.master, irdp_send_thread, ifp, timer, + &irdp->t_advertise); } static int irdp_finish(void) @@ -316,7 +316,7 @@ static int irdp_finish(void) return 0; } -static int irdp_init(struct thread_master *master) +static int irdp_init(struct event_loop *master) { irdp_if_init(); diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 4f4f3772dc80..2dfc027f1ddb 100644 --- a/zebra/irdp_packet.c +++ b/zebra/irdp_packet.c @@ -34,7 +34,7 @@ #include "sockunion.h" #include "sockunion.h" #include "stream.h" -#include "thread.h" +#include "frrevent.h" #include "vty.h" #include "zclient.h" #include "lib_errors.h" @@ -52,7 +52,7 @@ int irdp_sock = -1; -extern struct thread *t_irdp_raw; +extern struct event *t_irdp_raw; static void parse_irdp_packet(char *p, int len, struct interface *ifp) { @@ -209,7 +209,7 @@ static int irdp_recvmsg(int sock, uint8_t *buf, int size, int *ifindex) return ret; } -void irdp_read_raw(struct thread *r) +void irdp_read_raw(struct event *r) { struct interface *ifp; struct zebra_if *zi; @@ -217,9 +217,9 @@ void irdp_read_raw(struct thread *r) char buf[IRDP_RX_BUF]; int ret, ifindex = 0; - int irdp_sock = THREAD_FD(r); - thread_add_read(zrouter.master, irdp_read_raw, NULL, irdp_sock, - &t_irdp_raw); + int irdp_sock = EVENT_FD(r); + event_add_read(zrouter.master, irdp_read_raw, NULL, irdp_sock, + &t_irdp_raw); ret = irdp_recvmsg(irdp_sock, (uint8_t *)buf, IRDP_RX_BUF, &ifindex); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index a980b56b3384..c98f1ce60c1e 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -15,7 +15,7 @@ #include "table.h" #include "memory.h" #include "rib.h" -#include "thread.h" +#include "frrevent.h" #include "privs.h" #include "nexthop.h" #include "vrf.h" @@ -156,7 +156,7 @@ static const struct message rttype_str[] = {{RTN_UNSPEC, "none"}, {RTN_XRESOLVE, "resolver"}, {0}}; -extern struct thread_master *master; +extern struct event_loop *master; extern struct zebra_privs_t zserv_privs; @@ -398,7 +398,7 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWLINK: return netlink_link_change(h, ns_id, startup); case RTM_DELLINK: - return netlink_link_change(h, ns_id, startup); + return 0; case RTM_NEWNEIGH: case RTM_DELNEIGH: case RTM_GETNEIGH: @@ -474,6 +474,7 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, case RTM_NEWLINK: case RTM_DELLINK: + return netlink_link_change(h, ns_id, startup); default: break; @@ -482,9 +483,9 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id, return 0; } -static void kernel_read(struct thread *thread) +static void kernel_read(struct event *thread) { - struct zebra_ns *zns = (struct zebra_ns *)THREAD_ARG(thread); + struct zebra_ns *zns = (struct zebra_ns *)EVENT_ARG(thread); struct zebra_dplane_info dp_info; /* Capture key info from ns struct */ @@ -493,8 +494,8 @@ static void kernel_read(struct thread *thread) netlink_parse_info(netlink_information_fetch, &zns->netlink, &dp_info, 5, false); - thread_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, - &zns->t_netlink); + event_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, + &zns->t_netlink); } /* @@ -1169,7 +1170,6 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), h->nlmsg_type, h->nlmsg_len, h->nlmsg_seq, h->nlmsg_pid); - /* * Ignore messages that maybe sent from * other actors besides the kernel @@ -1289,18 +1289,15 @@ int netlink_request(struct nlsock *nl, void *req) return 0; } -static int nl_batch_read_resp(struct nl_batch *bth) +static int nl_batch_read_resp(struct nl_batch *bth, struct nlsock *nl) { struct nlmsghdr *h; struct sockaddr_nl snl; struct msghdr msg = {}; int status, seq; - struct nlsock *nl; struct zebra_dplane_ctx *ctx; bool ignore_msg; - nl = kernel_netlink_nlsock_lookup(bth->zns->sock); - msg.msg_name = (void *)&snl; msg.msg_namelen = sizeof(snl); @@ -1493,7 +1490,7 @@ static void nl_batch_send(struct nl_batch *bth) err = true; if (!err) { - if (nl_batch_read_resp(bth) == -1) + if (nl_batch_read_resp(bth, nl) == -1) err = true; } } @@ -1631,6 +1628,7 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, case DPLANE_OP_IPSET_DELETE: case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: + case DPLANE_OP_STARTUP_STAGE: return FRR_NETLINK_ERROR; case DPLANE_OP_GRE_SET: @@ -1777,17 +1775,11 @@ void kernel_init(struct zebra_ns *zns) * groups are added further below after SOL_NETLINK is verified to * exist. */ - groups = RTMGRP_LINK | - RTMGRP_IPV4_ROUTE | - RTMGRP_IPV4_IFADDR | - RTMGRP_IPV6_ROUTE | - RTMGRP_IPV6_IFADDR | - RTMGRP_IPV4_MROUTE | - RTMGRP_NEIGH | - ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) | - ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) | - ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)) | - ((uint32_t) 1 << (RTNLGRP_TC - 1)); + groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_MROUTE | + RTMGRP_NEIGH | ((uint32_t)1 << (RTNLGRP_IPV4_RULE - 1)) | + ((uint32_t)1 << (RTNLGRP_IPV6_RULE - 1)) | + ((uint32_t)1 << (RTNLGRP_NEXTHOP - 1)) | + ((uint32_t)1 << (RTNLGRP_TC - 1)); dplane_groups = (RTMGRP_LINK | RTMGRP_IPV4_IFADDR | @@ -1939,8 +1931,8 @@ void kernel_init(struct zebra_ns *zns) zns->t_netlink = NULL; - thread_add_read(zrouter.master, kernel_read, zns, - zns->netlink.sock, &zns->t_netlink); + event_add_read(zrouter.master, kernel_read, zns, zns->netlink.sock, + &zns->t_netlink); rt_netlink_init(); } @@ -1959,7 +1951,7 @@ static void kernel_nlsock_fini(struct nlsock *nls) void kernel_terminate(struct zebra_ns *zns, bool complete) { - THREAD_OFF(zns->t_netlink); + EVENT_OFF(zns->t_netlink); kernel_nlsock_fini(&zns->netlink); diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 915fd087fed2..fff3e6416fe0 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1255,7 +1255,7 @@ int rtm_write(int message, union sockunion *dest, union sockunion *mask, } -#include "thread.h" +#include "frrevent.h" #include "zebra/zserv.h" /* For debug purpose. */ @@ -1281,7 +1281,7 @@ static void rtmsg_debug(struct rt_msghdr *rtm) #endif /* RTAX_MAX */ /* Kernel routing table and interface updates via routing socket. */ -static void kernel_read(struct thread *thread) +static void kernel_read(struct event *thread) { int sock; int nbytes; @@ -1326,7 +1326,7 @@ static void kernel_read(struct thread *thread) } buf; /* Fetch routing socket. */ - sock = THREAD_FD(thread); + sock = EVENT_FD(thread); nbytes = read(sock, &buf, sizeof(buf)); @@ -1338,8 +1338,8 @@ static void kernel_read(struct thread *thread) * shortage and is not harmful for consistency of * reading the routing socket. Ignore it. */ - thread_add_read(zrouter.master, kernel_read, NULL, sock, - NULL); + event_add_read(zrouter.master, kernel_read, NULL, sock, + NULL); return; #else flog_err(EC_ZEBRA_RECVMSG_OVERRUN, @@ -1362,7 +1362,7 @@ static void kernel_read(struct thread *thread) if (nbytes == 0) return; - thread_add_read(zrouter.master, kernel_read, NULL, sock, NULL); + event_add_read(zrouter.master, kernel_read, NULL, sock, NULL); if (IS_ZEBRA_DEBUG_KERNEL) rtmsg_debug(&buf.r.rtm); @@ -1465,7 +1465,15 @@ static void routing_socket(struct zebra_ns *zns) } /* kernel_read needs rewrite. */ - thread_add_read(zrouter.master, kernel_read, NULL, routing_sock, NULL); + event_add_read(zrouter.master, kernel_read, NULL, routing_sock, NULL); +} + +void interface_list_second(struct zebra_ns *zns) +{ +} + +void interface_list_tunneldump(struct zebra_ns *zns) +{ } /* Exported interface function. This function simply calls @@ -1617,6 +1625,7 @@ void kernel_update_multi(struct dplane_ctx_list_head *ctx_list) case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: + case DPLANE_OP_STARTUP_STAGE: zlog_err("Unhandled dplane data for %s", dplane_op2str(dplane_ctx_get_op(ctx))); res = ZEBRA_DPLANE_REQUEST_FAILURE; diff --git a/zebra/label_manager.h b/zebra/label_manager.h index c0bd8e369fa7..cfbb4bd16987 100644 --- a/zebra/label_manager.h +++ b/zebra/label_manager.h @@ -14,7 +14,7 @@ #include <stdint.h> #include "lib/linklist.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/hook.h" #include "zebra/zserv.h" diff --git a/zebra/main.c b/zebra/main.c index c40a03551d50..bd4623be5556 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -8,7 +8,7 @@ #include <lib/version.h> #include "getopt.h" #include "command.h" -#include "thread.h" +#include "frrevent.h" #include "filter.h" #include "memory.h" #include "prefix.h" @@ -52,7 +52,7 @@ pid_t pid; /* Pacify zclient.o in libfrr, which expects this variable. */ -struct thread_master *master; +struct event_loop *master; /* Route retain mode flag. */ int retain_mode = 0; @@ -175,11 +175,6 @@ static void sigint(void) if (zrouter.lsp_process_q) work_queue_free_and_null(&zrouter.lsp_process_q); - vrf_terminate(); - - ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); - zebra_ns_notify_close(); - access_list_reset(); prefix_list_reset(); /* @@ -190,6 +185,8 @@ static void sigint(void) */ zebra_routemap_finish(); + rib_update_finish(); + list_delete(&zrouter.client_list); /* Indicate that all new dplane work has been enqueued. When that @@ -203,13 +200,23 @@ static void sigint(void) * Final shutdown step for the zebra main thread. This is run after all * async update processing has completed. */ -void zebra_finalize(struct thread *dummy) +void zebra_finalize(struct event *dummy) { zlog_info("Zebra final shutdown"); - /* Stop dplane thread and finish any cleanup */ + vrf_terminate(); + + /* + * Stop dplane thread and finish any cleanup + * This is before the zebra_ns_early_shutdown call + * because sockets that the dplane depends on are closed + * in those functions + */ zebra_dplane_shutdown(); + ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); + zebra_ns_notify_close(); + /* Final shutdown of ns resources */ ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); @@ -435,8 +442,8 @@ int main(int argc, char **argv) * we have to have route_read() called before. */ zrouter.startup_time = monotime(NULL); - thread_add_timer(zrouter.master, rib_sweep_route, NULL, - graceful_restart, &zrouter.sweeper); + event_add_timer(zrouter.master, rib_sweep_route, NULL, graceful_restart, + &zrouter.sweeper); /* Needed for BSD routing socket. */ pid = getpid(); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 8cdc419cce4a..11b7b7cf82d4 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -60,7 +60,7 @@ static void zebra_redistribute_default(struct zserv *client, vrf_id_t vrf_id) for (afi = AFI_IP; afi <= AFI_IP6; afi++) { - if (!vrf_bitmap_check(client->redist_default[afi], vrf_id)) + if (!vrf_bitmap_check(&client->redist_default[afi], vrf_id)) continue; /* Lookup table. */ @@ -145,17 +145,17 @@ static bool zebra_redistribute_check(const struct route_node *rn, return false; afi = family2afi(rn->p.family); - zvrf = vrf_info_lookup(re->vrf_id); + zvrf = zebra_vrf_lookup_by_id(re->vrf_id); if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table) return false; /* If default route and redistributed */ if (is_default_prefix(&rn->p) && - vrf_bitmap_check(client->redist_default[afi], re->vrf_id)) + 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)) + if (vrf_bitmap_check(&client->redist[afi][ZEBRA_ROUTE_ALL], re->vrf_id)) return true; /* @@ -171,7 +171,7 @@ static bool zebra_redistribute_check(const struct route_node *rn, } /* If redistribution is enabled for give route type. */ - if (vrf_bitmap_check(client->redist[afi][re->type], re->vrf_id)) + if (vrf_bitmap_check(&client->redist[afi][re->type], re->vrf_id)) return true; return false; @@ -256,12 +256,11 @@ void redistribute_delete(const struct route_node *rn, table = new_re->table; } - zlog_debug( - "%u:%u%pRN: Redist del: re %p (%u:%s), new re %p (%u:%s)", - vrfid, table, rn, old_re, old_inst, - old_re ? zebra_route_string(old_re->type) : "None", - new_re, new_inst, - new_re ? zebra_route_string(new_re->type) : "None"); + zlog_debug("(%u:%u):%pRN: Redist del: re %p (%u:%s), new re %p (%u:%s)", + vrfid, table, rn, old_re, old_inst, + old_re ? zebra_route_string(old_re->type) : "None", + new_re, new_inst, + new_re ? zebra_route_string(new_re->type) : "None"); } /* Skip invalid (e.g. linklocal) prefix */ @@ -331,14 +330,14 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) zvrf_id(zvrf), afi); } } else { - if (!vrf_bitmap_check(client->redist[afi][type], + if (!vrf_bitmap_check(&client->redist[afi][type], zvrf_id(zvrf))) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( "%s: setting vrf %s(%u) redist bitmap", __func__, VRF_LOGNAME(zvrf->vrf), zvrf_id(zvrf)); - vrf_bitmap_set(client->redist[afi][type], + vrf_bitmap_set(&client->redist[afi][type], zvrf_id(zvrf)); zebra_redistribute(client, type, 0, zvrf_id(zvrf), afi); } @@ -387,7 +386,7 @@ void zebra_redistribute_delete(ZAPI_HANDLER_ARGS) if (instance) redist_del_instance(&client->mi_redist[afi][type], instance); else - vrf_bitmap_unset(client->redist[afi][type], zvrf_id(zvrf)); + vrf_bitmap_unset(&client->redist[afi][type], zvrf_id(zvrf)); stream_failure: return; @@ -405,7 +404,7 @@ void zebra_redistribute_default_add(ZAPI_HANDLER_ARGS) return; } - vrf_bitmap_set(client->redist_default[afi], zvrf_id(zvrf)); + vrf_bitmap_set(&client->redist_default[afi], zvrf_id(zvrf)); zebra_redistribute_default(client, zvrf_id(zvrf)); stream_failure: @@ -424,7 +423,7 @@ void zebra_redistribute_default_delete(ZAPI_HANDLER_ARGS) return; } - vrf_bitmap_unset(client->redist_default[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(&client->redist_default[afi], zvrf_id(zvrf)); stream_failure: return; @@ -548,6 +547,10 @@ void zebra_interface_address_add_update(struct interface *ifp, client, ifp, ifc); } } + /* interface associated NHGs may have been deleted, + * re-sync zebra -> dplane NHGs + */ + zebra_interface_nhg_reinstall(ifp); } /* Interface address deletion. */ diff --git a/zebra/rib.h b/zebra/rib.h index 70968d3900c8..64bbaf3e7694 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -167,6 +167,10 @@ struct route_entry { #define RIB_KERNEL_ROUTE(R) RKERNEL_ROUTE((R)->type) +/* Define route types that are equivalent to "connected". */ +#define RIB_CONNECTED_ROUTE(R) \ + ((R)->type == ZEBRA_ROUTE_CONNECT || (R)->type == ZEBRA_ROUTE_NHRP) + /* meta-queue structure: * sub-queue 0: nexthop group objects * sub-queue 1: EVPN/VxLAN objects @@ -180,7 +184,7 @@ struct route_entry { * sub-queue 9: any other origin (if any) typically those that * don't generate routes */ -#define MQ_SIZE 10 +#define MQ_SIZE 11 struct meta_queue { struct list *subq[MQ_SIZE]; uint32_t size; /* sum of lengths of all subqueues */ @@ -323,6 +327,7 @@ enum rib_update_event { RIB_UPDATE_OTHER, RIB_UPDATE_MAX }; +void rib_update_finish(void); int route_entry_update_nhe(struct route_entry *re, struct nhg_hash_entry *new_nhghe); @@ -402,7 +407,7 @@ extern struct route_entry *rib_lookup_ipv4(struct prefix_ipv4 *p, extern void rib_update(enum rib_update_event event); extern void rib_update_table(struct route_table *table, enum rib_update_event event, int rtype); -extern void rib_sweep_route(struct thread *t); +extern void rib_sweep_route(struct event *t); extern void rib_sweep_table(struct route_table *table); extern void rib_close_table(struct route_table *table); extern void rib_init(void); @@ -464,6 +469,13 @@ extern uint8_t route_distance(int type); extern void zebra_rib_evaluate_rn_nexthops(struct route_node *rn, uint32_t seq, bool rt_delete); +/* + * rib_find_rn_from_ctx + * + * Returns a lock increased route_node for the appropriate + * table and prefix specified by the context. Developer + * should unlock the node when done. + */ extern struct route_node * rib_find_rn_from_ctx(const struct zebra_dplane_ctx *ctx); @@ -601,6 +613,12 @@ static inline struct nexthop_group *rib_get_fib_backup_nhg( return &(re->fib_backup_ng); } +extern void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance); + +extern int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance); + extern void zebra_vty_init(void); extern pid_t pid; diff --git a/zebra/router-id.c b/zebra/router-id.c index e8cb33c35c03..ef87d924fe54 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -284,7 +284,7 @@ DEFUN (ip_router_id, argv_find(argv, argc, "NAME", &idx); VRF_GET_ID(vrf_id, argv[idx]->arg, false); - zvrf = vrf_info_lookup(vrf_id); + zvrf = zebra_vrf_lookup_by_id(vrf_id); router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; @@ -321,7 +321,7 @@ DEFUN (ipv6_router_id, argv_find(argv, argc, "NAME", &idx); VRF_GET_ID(vrf_id, argv[idx]->arg, false); - zvrf = vrf_info_lookup(vrf_id); + zvrf = zebra_vrf_lookup_by_id(vrf_id); router_id_set(AFI_IP6, &rid, zvrf); return CMD_SUCCESS; @@ -403,7 +403,7 @@ DEFUN (no_ip_router_id, if (argv_find(argv, argc, "NAME", &idx)) VRF_GET_ID(vrf_id, argv[idx]->arg, false); - zvrf = vrf_info_lookup(vrf_id); + zvrf = zebra_vrf_lookup_by_id(vrf_id); router_id_set(AFI_IP, &rid, zvrf); return CMD_SUCCESS; @@ -437,7 +437,7 @@ DEFUN (no_ipv6_router_id, if (argv_find(argv, argc, "NAME", &idx)) VRF_GET_ID(vrf_id, argv[idx]->arg, false); - zvrf = vrf_info_lookup(vrf_id); + zvrf = zebra_vrf_lookup_by_id(vrf_id); router_id_set(AFI_IP6, &rid, zvrf); return CMD_SUCCESS; @@ -514,7 +514,7 @@ DEFUN (show_ip_router_id, vrf_name = argv[idx]->arg; } - zvrf = vrf_info_lookup(vrf_id); + zvrf = zebra_vrf_lookup_by_id(vrf_id); if (zvrf != NULL) { if (is_ipv6) { diff --git a/zebra/rt.h b/zebra/rt.h index 2e3495a03731..af170a22aa16 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -84,6 +84,8 @@ extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); * state. */ extern void interface_list(struct zebra_ns *zns); +extern void interface_list_tunneldump(struct zebra_ns *zns); +extern void interface_list_second(struct zebra_ns *zns); extern void kernel_init(struct zebra_ns *zns); extern void kernel_terminate(struct zebra_ns *zns, bool complete); extern void macfdb_read(struct zebra_ns *zns); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index e4ddbd95d776..a51d7b849b27 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -38,7 +38,7 @@ #include "table.h" #include "memory.h" #include "rib.h" -#include "thread.h" +#include "frrevent.h" #include "privs.h" #include "nexthop.h" #include "vrf.h" @@ -380,33 +380,6 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) return proto; } -/* -Pending: create an efficient table_id (in a tree/hash) based lookup) - */ -vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) -{ - struct vrf *vrf; - struct zebra_vrf *zvrf; - - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - zvrf = vrf->info; - if (zvrf == NULL) - continue; - /* case vrf with netns : match the netnsid */ - if (vrf_is_backend_netns()) { - if (ns_id == zvrf_id(zvrf)) - return zvrf_id(zvrf); - } else { - /* VRF is VRF_BACKEND_VRF_LITE */ - if (zvrf->table_id != table_id) - continue; - return zvrf_id(zvrf); - } - } - - return VRF_DEFAULT; -} - /** * @parse_encap_mpls() - Parses encapsulated mpls attributes * @tb: Pointer to rtattr to look for nested items in. @@ -790,7 +763,7 @@ int netlink_route_change_read_unicast_internal(struct nlmsghdr *h, table = rtm->rtm_table; /* Map to VRF */ - vrf_id = vrf_lookup_by_table(table, ns_id); + vrf_id = zebra_vrf_lookup_by_table(table, ns_id); if (vrf_id == VRF_DEFAULT) { if (!is_zebra_valid_kernel_table(table) && !is_zebra_main_routing_table(table)) @@ -1079,7 +1052,7 @@ static int netlink_route_change_read_multicast(struct nlmsghdr *h, else table = rtm->rtm_table; - vrf = vrf_lookup_by_table(table, ns_id); + vrf = zebra_vrf_lookup_by_table(table, ns_id); if (tb[RTA_IIF]) iif = *(int *)RTA_DATA(tb[RTA_IIF]); @@ -4699,6 +4672,7 @@ static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: case DPLANE_OP_NONE: + case DPLANE_OP_STARTUP_STAGE: ret = -1; } diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 3ca59ce676f3..d9d0ee76249a 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -90,7 +90,6 @@ extern int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, uint16_t vid); extern int netlink_neigh_read_specific_ip(const 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 diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 1e0769fb6d74..9af41cbc393a 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -9,7 +9,7 @@ #include "memory.h" #include "sockopt.h" -#include "thread.h" +#include "frrevent.h" #include "if.h" #include "stream.h" #include "log.h" @@ -475,9 +475,9 @@ static void rtadv_send_packet(int sock, struct interface *ifp, zif->ra_sent++; } -static void rtadv_timer(struct thread *thread) +static void rtadv_timer(struct event *thread) { - struct zebra_vrf *zvrf = THREAD_ARG(thread); + struct zebra_vrf *zvrf = EVENT_ARG(thread); struct vrf *vrf; struct interface *ifp; struct zebra_if *zif; @@ -817,7 +817,7 @@ static void rtadv_process_packet(uint8_t *buf, unsigned int len, return; } -static void rtadv_read(struct thread *thread) +static void rtadv_read(struct event *thread) { int sock; int len; @@ -825,9 +825,9 @@ static void rtadv_read(struct thread *thread) struct sockaddr_in6 from; ifindex_t ifindex = 0; int hoplimit = -1; - struct zebra_vrf *zvrf = THREAD_ARG(thread); + struct zebra_vrf *zvrf = EVENT_ARG(thread); - sock = THREAD_FD(thread); + sock = EVENT_FD(thread); zvrf->rtadv.ra_read = NULL; /* Register myself. */ @@ -2800,26 +2800,26 @@ static void rtadv_event(struct zebra_vrf *zvrf, enum rtadv_event event, int val) switch (event) { case RTADV_START: - thread_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock, - &rtadv->ra_read); - thread_add_event(zrouter.master, rtadv_timer, zvrf, 0, - &rtadv->ra_timer); + event_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock, + &rtadv->ra_read); + event_add_event(zrouter.master, rtadv_timer, zvrf, 0, + &rtadv->ra_timer); break; case RTADV_STOP: - THREAD_OFF(rtadv->ra_timer); - THREAD_OFF(rtadv->ra_read); + EVENT_OFF(rtadv->ra_timer); + EVENT_OFF(rtadv->ra_read); break; case RTADV_TIMER: - thread_add_timer(zrouter.master, rtadv_timer, zvrf, val, - &rtadv->ra_timer); + event_add_timer(zrouter.master, rtadv_timer, zvrf, val, + &rtadv->ra_timer); break; case RTADV_TIMER_MSEC: - thread_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val, - &rtadv->ra_timer); + event_add_timer_msec(zrouter.master, rtadv_timer, zvrf, val, + &rtadv->ra_timer); break; case RTADV_READ: - thread_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock, - &rtadv->ra_read); + event_add_read(zrouter.master, rtadv_read, zvrf, rtadv->sock, + &rtadv->ra_read); break; default: break; diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 2338efe0bd09..1ec376a10626 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -30,8 +30,8 @@ struct rtadv { struct adv_if_list_head adv_if; struct adv_if_list_head adv_msec_if; - struct thread *ra_read; - struct thread *ra_timer; + struct event *ra_read; + struct event *ra_timer; }; PREDECL_RBTREE_UNIQ(rtadv_prefixes); diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index c7832992ea06..3bb4936b8167 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -116,9 +116,9 @@ static ssize_t netlink_rule_msg_encode( return 0; } - /* dsfield, if specified */ + /* dsfield, if specified; mask off the ECN bits */ if (filter_bm & PBR_FILTER_DSFIELD) - req->frh.tos = dsfield; + req->frh.tos = dsfield & 0xfc; /* protocol to match on */ if (filter_bm & PBR_FILTER_IP_PROTOCOL) @@ -400,6 +400,7 @@ int netlink_rules_read(struct zebra_ns *zns) ret = netlink_parse_info(netlink_rule_change, &zns->netlink_cmd, &dp_info, 0, true); + return ret; } diff --git a/zebra/sample_plugin.c b/zebra/sample_plugin.c index 0ccf736f24d5..b5f7b88375cb 100644 --- a/zebra/sample_plugin.c +++ b/zebra/sample_plugin.c @@ -76,7 +76,7 @@ static int sample_process(struct zebra_dplane_provider *prov) * Init entry point called during zebra startup. This is registered during * module init. */ -static int init_sample_plugin(struct thread_master *tm) +static int init_sample_plugin(struct event_loop *tm) { int ret; diff --git a/zebra/table_manager.h b/zebra/table_manager.h index deed4fa30b5c..f8e99a357dba 100644 --- a/zebra/table_manager.h +++ b/zebra/table_manager.h @@ -9,7 +9,7 @@ #include <stdint.h> #include "lib/linklist.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/ns.h" #include "zebra/zserv.h" diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index 1ff188c76d5d..5ac87540588e 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -801,11 +801,17 @@ int zsend_route_notify_owner(const struct route_node *rn, int zsend_route_notify_owner_ctx(const struct zebra_dplane_ctx *ctx, enum zapi_route_notify_owner note) { - return (route_notify_internal( - rib_find_rn_from_ctx(ctx), dplane_ctx_get_type(ctx), - dplane_ctx_get_instance(ctx), dplane_ctx_get_vrf(ctx), - dplane_ctx_get_table(ctx), note, dplane_ctx_get_afi(ctx), - dplane_ctx_get_safi(ctx))); + int result; + struct route_node *rn = rib_find_rn_from_ctx(ctx); + + result = route_notify_internal( + rn, dplane_ctx_get_type(ctx), dplane_ctx_get_instance(ctx), + dplane_ctx_get_vrf(ctx), dplane_ctx_get_table(ctx), note, + dplane_ctx_get_afi(ctx), dplane_ctx_get_safi(ctx)); + + route_unlock_node(rn); + + return result; } static void zread_route_notify_request(ZAPI_HANDLER_ARGS) @@ -989,7 +995,7 @@ void zsend_nhrp_neighbor_notify(int cmd, struct interface *ifp, family2addrsize(sockunion_family(&ip))); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { - if (!vrf_bitmap_check(client->nhrp_neighinfo[afi], + if (!vrf_bitmap_check(&client->nhrp_neighinfo[afi], ifp->vrf->vrf_id)) continue; @@ -1010,7 +1016,7 @@ int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p, struct stream *s; /* Check this client need interface information. */ - if (!vrf_bitmap_check(client->ridinfo[afi], vrf_id)) + if (!vrf_bitmap_check(&client->ridinfo[afi], vrf_id)) return 0; s = stream_new(ZEBRA_MAX_PACKET_SIZ); @@ -1330,7 +1336,7 @@ static void zread_fec_register(ZAPI_HANDLER_ARGS) uint32_t label_index = MPLS_INVALID_LABEL_INDEX; s = msg; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return; @@ -1393,7 +1399,7 @@ static void zread_fec_unregister(ZAPI_HANDLER_ARGS) uint16_t flags; s = msg; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return; @@ -2275,7 +2281,7 @@ static void zread_router_id_add(ZAPI_HANDLER_ARGS) } /* Router-id information is needed. */ - vrf_bitmap_set(client->ridinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_set(&client->ridinfo[afi], zvrf_id(zvrf)); router_id_get(afi, &p, zvrf); @@ -2311,7 +2317,7 @@ static void zread_router_id_delete(ZAPI_HANDLER_ARGS) goto stream_failure; } - vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(&client->ridinfo[afi], zvrf_id(zvrf)); stream_failure: return; @@ -2337,7 +2343,7 @@ void zsend_capabilities_all_clients(void) struct zebra_vrf *zvrf; struct zserv *client; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) { /* Do not send unsolicited messages to synchronous clients. */ if (client->synchronous) @@ -2401,10 +2407,11 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) for (afi = AFI_IP; afi < AFI_MAX; afi++) { 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)); - vrf_bitmap_unset(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); + 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)); + vrf_bitmap_unset(&client->nhrp_neighinfo[afi], zvrf_id(zvrf)); } } @@ -3557,7 +3564,7 @@ static inline void zebra_neigh_register(ZAPI_HANDLER_ARGS) afi); goto stream_failure; } - vrf_bitmap_set(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_set(&client->nhrp_neighinfo[afi], zvrf_id(zvrf)); stream_failure: return; } @@ -3573,7 +3580,7 @@ static inline void zebra_neigh_unregister(ZAPI_HANDLER_ARGS) afi); goto stream_failure; } - vrf_bitmap_unset(client->nhrp_neighinfo[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(&client->nhrp_neighinfo[afi], zvrf_id(zvrf)); stream_failure: return; } @@ -3976,8 +3983,7 @@ void zserv_handle_commands(struct zserv *client, struct stream_fifo *fifo) 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. + * special messages that are handled the 'opaque zapi' module. */ if (zebra_opaque_handles_msgid(hdr.command)) { /* Reset message buffer */ diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index af1dd6b5fd87..6a685267a960 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -180,10 +180,43 @@ struct dplane_br_port_info { */ struct dplane_intf_info { + enum zebra_iftype zif_type; + ifindex_t bond_ifindex; + ifindex_t link_ifindex; + int32_t mtu; + vrf_id_t vrf_id; + enum zebra_slave_iftype zif_slave_type; + ifindex_t master_ifindex; + ifindex_t bridge_ifindex; + ns_id_t link_nsid; + enum zebra_slave_iftype zslave_type; + uint8_t bypass; + enum zebra_link_type zltype; + bool startup; + uint8_t family; + struct zebra_vxlan_vni_array *vniarray; + struct zebra_dplane_bridge_vlan_info bvinfo; + struct zebra_dplane_bridge_vlan_info_array *bvarray; + + char desc[128]; + + int32_t hw_addr_len; + uint8_t hw_addr[INTERFACE_HWADDR_MAX]; + + uint32_t table_id; + + struct zebra_l2info_bridge binfo; + struct zebra_l2info_vlan vinfo; + struct zebra_l2info_vxlan vxinfo; + struct zebra_l2info_gre grinfo; + + uint32_t rc_bitfield; + uint32_t metric; uint32_t flags; bool protodown; + bool protodown_set; bool pd_reason_val; #define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */ @@ -397,6 +430,7 @@ struct zebra_dplane_ctx { struct dplane_neigh_table neightable; struct dplane_gre_ctx gre; struct dplane_netconf_info netconf; + enum zebra_dplane_startup_notifications spot; } u; /* Namespace info, used especially for netlink kernel communication */ @@ -476,10 +510,10 @@ struct dplane_zns_info { struct zebra_dplane_info info; /* Request data from the OS */ - struct thread *t_request; + struct event *t_request; /* Read event */ - struct thread *t_read; + struct event *t_read; /* List linkage */ struct zns_info_list_item link; @@ -582,13 +616,13 @@ static struct zebra_dplane_globals { struct frr_pthread *dg_pthread; /* Event-delivery context 'master' for the dplane */ - struct thread_master *dg_master; + struct event_loop *dg_master; /* Event/'thread' pointer for queued updates */ - struct thread *dg_t_update; + struct event *dg_t_update; /* Event pointer for pending shutdown check loop */ - struct thread *dg_t_shutdown_check; + struct event *dg_t_shutdown_check; } zdplane_info; @@ -609,7 +643,7 @@ DECLARE_DLIST(zns_info_list, struct dplane_zns_info, link); #define DPLANE_PROV_UNLOCK(p) pthread_mutex_unlock(&((p)->dp_mutex)) /* Prototypes */ -static void dplane_thread_loop(struct thread *event); +static void dplane_thread_loop(struct event *event); static enum zebra_dplane_result lsp_update_internal(struct zebra_lsp *lsp, enum dplane_op_e op); static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw, @@ -633,7 +667,7 @@ neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, */ /* Obtain thread_master for dataplane thread */ -struct thread_master *dplane_get_thread_master(void) +struct event_loop *dplane_get_thread_master(void) { return zdplane_info.dg_master; } @@ -808,8 +842,14 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_NONE: case DPLANE_OP_IPSET_ADD: case DPLANE_OP_IPSET_DELETE: + break; case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: + if (ctx->u.intf.vniarray) + XFREE(MTYPE_TMP, ctx->u.intf.vniarray); + if (ctx->u.intf.bvarray) + XFREE(MTYPE_TMP, ctx->u.intf.bvarray); + break; case DPLANE_OP_INTF_DELETE: case DPLANE_OP_TC_QDISC_INSTALL: case DPLANE_OP_TC_QDISC_UNINSTALL: @@ -833,6 +873,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) break; case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_NETCONFIG: + case DPLANE_OP_STARTUP_STAGE: break; } } @@ -1156,6 +1197,8 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_TC_FILTER_UPDATE: ret = "TC__FILTER_UPDATE"; break; + case DPLANE_OP_STARTUP_STAGE: + ret = "STARTUP_STAGE"; } return ret; @@ -1297,6 +1340,422 @@ const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx) return ctx->zd_ifname; } +void dplane_ctx_set_ifp_bridge_vlan_info_array( + struct zebra_dplane_ctx *ctx, + struct zebra_dplane_bridge_vlan_info_array *bvarray) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.bvarray = bvarray; +} + +const struct zebra_dplane_bridge_vlan_info_array * +dplane_ctx_get_ifp_bridge_vlan_info_array(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.bvarray; +} + +void dplane_ctx_set_ifp_vxlan_vni_array(struct zebra_dplane_ctx *ctx, + struct zebra_vxlan_vni_array *vniarray) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.vniarray = vniarray; +} + +const struct zebra_vxlan_vni_array * +dplane_ctx_get_ifp_vxlan_vni_array(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.vniarray; +} + +void dplane_ctx_set_ifp_bridge_vlan_info( + struct zebra_dplane_ctx *ctx, + struct zebra_dplane_bridge_vlan_info *bvinfo) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.bvinfo = *bvinfo; +} + +const struct zebra_dplane_bridge_vlan_info * +dplane_ctx_get_ifp_bridge_vlan_info(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.intf.bvinfo; +} + +void dplane_ctx_set_ifp_family(struct zebra_dplane_ctx *ctx, uint8_t family) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.family = family; +} + +uint8_t dplane_ctx_get_ifp_family(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.family; +} + +void dplane_ctx_set_ifp_zltype(struct zebra_dplane_ctx *ctx, + enum zebra_link_type zltype) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.zltype = zltype; +} + +enum zebra_link_type +dplane_ctx_get_ifp_zltype(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.zltype; +} + +void dplane_ctx_set_ifp_link_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t link_ifindex) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.link_ifindex = link_ifindex; +} + +ifindex_t dplane_ctx_get_ifp_link_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.link_ifindex; +} + +void dplane_ctx_set_ifp_desc(struct zebra_dplane_ctx *ctx, const char *desc) +{ + DPLANE_CTX_VALID(ctx); + + strlcpy(ctx->u.intf.desc, desc, sizeof(ctx->u.intf.desc)); +} + +char *dplane_ctx_get_ifp_desc(struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.desc; +} + +void dplane_ctx_set_ifp_flags(struct zebra_dplane_ctx *ctx, uint64_t flags) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.flags = flags; +} + +uint64_t dplane_ctx_get_ifp_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.flags; +} + +void dplane_ctx_set_ifp_bypass(struct zebra_dplane_ctx *ctx, uint8_t bypass) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.bypass = bypass; +} + +uint8_t dplane_ctx_get_ifp_bypass(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.bypass; +} + +void dplane_ctx_set_ifp_bridge_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t bridge_ifindex) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.bridge_ifindex = bridge_ifindex; +} + +ifindex_t dplane_ctx_get_ifp_bridge_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.bridge_ifindex; +} + +void dplane_ctx_set_ifp_zif_slave_type(struct zebra_dplane_ctx *ctx, + enum zebra_slave_iftype zslave_type) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.zslave_type = zslave_type; +} + +enum zebra_slave_iftype +dplane_ctx_get_ifp_zif_slave_type(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.zslave_type; +} + +void dplane_ctx_set_ifp_master_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t master_ifindex) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.master_ifindex = master_ifindex; +} + +ifindex_t dplane_ctx_get_ifp_master_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.master_ifindex; +} + +void dplane_ctx_set_ifp_mtu(struct zebra_dplane_ctx *ctx, uint32_t mtu) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.mtu = mtu; +} + +uint32_t dplane_ctx_get_ifp_mtu(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.mtu; +} + +void dplane_ctx_set_ifp_vrf_id(struct zebra_dplane_ctx *ctx, vrf_id_t vrf_id) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.vrf_id = vrf_id; +} + +vrf_id_t dplane_ctx_get_ifp_vrf_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.vrf_id; +} + +void dplane_ctx_set_ifp_link_nsid(struct zebra_dplane_ctx *ctx, + ns_id_t link_nsid) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.link_nsid = link_nsid; +} + +ns_id_t dplane_ctx_get_ifp_link_nsid(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.link_nsid; +} + +void dplane_ctx_set_ifp_startup(struct zebra_dplane_ctx *ctx, bool startup) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.startup = startup; +} + +bool dplane_ctx_get_ifp_startup(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.startup; +} + +void dplane_ctx_set_ifp_protodown_set(struct zebra_dplane_ctx *ctx, bool set) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.protodown_set = set; +} + +bool dplane_ctx_get_ifp_protodown_set(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.protodown_set; +} + +void dplane_ctx_set_ifp_protodown(struct zebra_dplane_ctx *ctx, bool protodown) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.protodown = protodown; +} + +bool dplane_ctx_get_ifp_protodown(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.protodown; +} + +ifindex_t dplane_ctx_get_ifp_bond_ifindex(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.bond_ifindex; +} + +void dplane_ctx_set_ifp_rc_bitfield(struct zebra_dplane_ctx *ctx, + uint32_t rc_bitfield) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.rc_bitfield = rc_bitfield; +} + +uint32_t dplane_ctx_get_ifp_rc_bitfield(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.rc_bitfield; +} + +void dplane_ctx_set_ifp_gre_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_gre *grinfo) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.grinfo = *grinfo; +} + +const struct zebra_l2info_gre * +dplane_ctx_get_ifp_gre_info(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.intf.grinfo; +} + +void dplane_ctx_set_ifp_vxlan_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_vxlan *vxinfo) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.vxinfo = *vxinfo; +} + +const struct zebra_l2info_vxlan * +dplane_ctx_get_ifp_vxlan_info(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.intf.vxinfo; +} + +void dplane_ctx_set_ifp_vlan_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_vlan *vinfo) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.vinfo = *vinfo; +} + +const struct zebra_l2info_vlan * +dplane_ctx_get_ifp_vlan_info(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.intf.vinfo; +} + +void dplane_ctx_set_ifp_bridge_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_bridge *binfo) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.binfo = *binfo; +} + +const struct zebra_l2info_bridge * +dplane_ctx_get_ifp_bridge_info(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return &ctx->u.intf.binfo; +} + +void dplane_ctx_set_ifp_table_id(struct zebra_dplane_ctx *ctx, + uint32_t table_id) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.table_id = table_id; +} + +uint32_t dplane_ctx_get_ifp_table_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.table_id; +} + +void dplane_ctx_set_ifp_hw_addr(struct zebra_dplane_ctx *ctx, + int32_t hw_addr_len, uint8_t *hw_addr) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.hw_addr_len = hw_addr_len; + memcpy(ctx->u.intf.hw_addr, hw_addr, hw_addr_len); +} + +int32_t dplane_ctx_get_ifp_hw_addr_len(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.hw_addr_len; +} + +const uint8_t *dplane_ctx_get_ifp_hw_addr(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.hw_addr; +} + +void dplane_ctx_set_ifp_bond_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t bond_ifindex) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.bond_ifindex = bond_ifindex; +} + +enum zebra_iftype +dplane_ctx_get_ifp_zif_type(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.intf.zif_type; +} + +void dplane_ctx_set_ifp_zif_type(struct zebra_dplane_ctx *ctx, + enum zebra_iftype zif_type) +{ + DPLANE_CTX_VALID(ctx); + + ctx->u.intf.zif_type = zif_type; +} + void dplane_ctx_set_ifname(struct zebra_dplane_ctx *ctx, const char *ifname) { DPLANE_CTX_VALID(ctx); @@ -2798,7 +3257,7 @@ int dplane_ctx_route_init_basic(struct zebra_dplane_ctx *ctx, { int ret = EINVAL; - if (!ctx || !re) + if (!ctx) return ret; dplane_intf_extra_list_init(&ctx->u.rinfo.intf_extra_list); @@ -2806,6 +3265,13 @@ int dplane_ctx_route_init_basic(struct zebra_dplane_ctx *ctx, ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; + /* This function may be called to create/init a dplane context, not + * necessarily to copy a route object. Let's return if there is no route + * object to copy. + */ + if (!re) + return AOK; + ctx->u.rinfo.zd_type = re->type; ctx->u.rinfo.zd_old_type = re->type; @@ -2837,6 +3303,8 @@ int dplane_ctx_route_init_basic(struct zebra_dplane_ctx *ctx, /* * Initialize a context block for a route update from zebra data structs. + * If the `rn` or `re` parameters are NULL, this function only initializes the + * dplane context without copying a route object into it. */ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, struct route_node *rn, struct route_entry *re) @@ -2853,9 +3321,17 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, const struct interface *ifp; struct dplane_intf_extra *if_extra; - if (!ctx || !rn || !re) + if (!ctx) return ret; + /* + * Initialize the dplane context and return, if there is no route + * object to copy + */ + if (!re || !rn) + return dplane_ctx_route_init_basic(ctx, op, NULL, NULL, NULL, + AFI_UNSPEC, SAFI_UNSPEC); + /* * Let's grab the data from the route_node * so that we can call a helper function @@ -3745,6 +4221,11 @@ dplane_route_update_internal(struct route_node *rn, NEXTHOP_FLAG_FIB); } + if ((op == DPLANE_OP_ROUTE_UPDATE) && old_re && re && + (old_re != re) && + !CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) + SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); + dplane_ctx_free(&ctx); return ZEBRA_DPLANE_REQUEST_SUCCESS; } @@ -3780,18 +4261,12 @@ tc_qdisc_update_internal(enum dplane_op_e op, /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } - /* Init context with info from zebra data structs */ ret = dplane_ctx_tc_qdisc_init(ctx, op, qdisc); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, memory_order_relaxed); @@ -3800,8 +4275,7 @@ tc_qdisc_update_internal(enum dplane_op_e op, } else { atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -3817,18 +4291,12 @@ tc_class_update_internal(enum dplane_op_e op, struct zebra_tc_class *class) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } - /* Init context with info from zebra data structs */ ret = dplane_ctx_tc_class_init(ctx, op, class); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, memory_order_relaxed); @@ -3837,8 +4305,7 @@ tc_class_update_internal(enum dplane_op_e op, struct zebra_tc_class *class) } else { atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -3854,18 +4321,12 @@ tc_filter_update_internal(enum dplane_op_e op, struct zebra_tc_filter *filter) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } - /* Init context with info from zebra data structs */ ret = dplane_ctx_tc_filter_init(ctx, op, filter); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_tcs_in, 1, memory_order_relaxed); @@ -3874,8 +4335,7 @@ tc_filter_update_internal(enum dplane_op_e op, struct zebra_tc_filter *filter) } else { atomic_fetch_add_explicit(&zdplane_info.dg_tcs_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); } return result; @@ -3938,16 +4398,11 @@ dplane_nexthop_update_internal(struct nhg_hash_entry *nhe, enum dplane_op_e op) /* 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); @@ -4078,8 +4533,6 @@ dplane_route_notif_update(struct route_node *rn, 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); @@ -4211,10 +4664,6 @@ dplane_lsp_notif_update(struct zebra_lsp *lsp, enum dplane_op_e op, /* 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); @@ -4254,8 +4703,7 @@ dplane_lsp_notif_update(struct zebra_lsp *lsp, enum dplane_op_e op, 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; } @@ -4584,16 +5032,11 @@ dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op) /* Obtain context block */ ctx = dplane_ctx_alloc(); - if (!ctx) { - ret = ENOMEM; - goto done; - } ret = dplane_ctx_intf_init(ctx, op, ifp); if (ret == AOK) ret = dplane_update_enqueue(ctx); -done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1, memory_order_relaxed); @@ -4634,18 +5077,6 @@ enum zebra_dplane_result dplane_intf_update(const struct interface *ifp) return ret; } -/* - * Enqueue a interface delete for the dataplane. - */ -enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp) -{ - enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; - - if (ifp) - ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_DELETE); - return ret; -} - /* * Enqueue vxlan/evpn mac add (or update). */ @@ -5332,8 +5763,10 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, ctx = dplane_ctx_alloc(); - if (!ifp) - return result; + if (!ifp) { + ret = EINVAL; + goto done; + } if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { zlog_debug("init dplane ctx %s: if %s link %s%s", @@ -5345,8 +5778,11 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, ctx->zd_op = op; ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; zns = zebra_ns_lookup(ifp->vrf->vrf_id); - if (!zns) - return result; + if (!zns) { + ret = EINVAL; + goto done; + } + dplane_ctx_ns_init(ctx, zns, false); dplane_ctx_set_ifname(ctx, ifp->name); @@ -5365,6 +5801,7 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, /* Enqueue context for processing */ ret = dplane_update_enqueue(ctx); +done: /* Update counter */ atomic_fetch_add_explicit(&zdplane_info.dg_gre_set_in, 1, memory_order_relaxed); @@ -5375,8 +5812,7 @@ dplane_gre_set(struct interface *ifp, struct interface *ifp_link, atomic_fetch_add_explicit( &zdplane_info.dg_gre_set_errors, 1, memory_order_relaxed); - if (ctx) - dplane_ctx_free(&ctx); + dplane_ctx_free(&ctx); result = ZEBRA_DPLANE_REQUEST_FAILURE; } return result; @@ -5574,7 +6010,7 @@ int dplane_provider_register(const char *name, struct zebra_dplane_provider **prov_p) { int ret = 0; - struct zebra_dplane_provider *p = NULL, *last; + struct zebra_dplane_provider *p = NULL, *last, *prev = NULL; /* Validate */ if (fp == NULL) { @@ -5617,10 +6053,11 @@ int dplane_provider_register(const char *name, frr_each (dplane_prov_list, &zdplane_info.dg_providers, last) { if (last->dp_priority > p->dp_priority) break; + prev = last; } if (last) - dplane_prov_list_add_after(&zdplane_info.dg_providers, last, p); + dplane_prov_list_add_after(&zdplane_info.dg_providers, prev, p); else dplane_prov_list_add_tail(&zdplane_info.dg_providers, p); @@ -5760,6 +6197,21 @@ void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov, memory_order_relaxed); } +static struct zebra_dplane_ctx * +dplane_provider_dequeue_out_ctx(struct zebra_dplane_provider *prov) +{ + struct zebra_dplane_ctx *ctx; + + ctx = dplane_ctx_list_pop(&(prov->dp_ctx_out_list)); + if (!ctx) + return NULL; + + atomic_fetch_sub_explicit(&(prov->dp_out_queued), 1, + memory_order_relaxed); + + return ctx; +} + /* * Accessor for provider object */ @@ -5773,28 +6225,28 @@ bool dplane_provider_is_threaded(const struct zebra_dplane_provider *prov) * Callback when an OS (netlink) incoming event read is ready. This runs * in the dplane pthread. */ -static void dplane_incoming_read(struct thread *event) +static void dplane_incoming_read(struct event *event) { - struct dplane_zns_info *zi = THREAD_ARG(event); + struct dplane_zns_info *zi = EVENT_ARG(event); kernel_dplane_read(&zi->info); /* Re-start read task */ - thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi, - zi->info.sock, &zi->t_read); + event_add_read(zdplane_info.dg_master, dplane_incoming_read, zi, + zi->info.sock, &zi->t_read); } /* * Callback in the dataplane pthread that requests info from the OS and * initiates netlink reads. */ -static void dplane_incoming_request(struct thread *event) +static void dplane_incoming_request(struct event *event) { - struct dplane_zns_info *zi = THREAD_ARG(event); + struct dplane_zns_info *zi = EVENT_ARG(event); /* Start read task */ - thread_add_read(zdplane_info.dg_master, dplane_incoming_read, zi, - zi->info.sock, &zi->t_read); + event_add_read(zdplane_info.dg_master, dplane_incoming_read, zi, + zi->info.sock, &zi->t_read); /* Send requests */ netlink_request_netconf(zi->info.sock); @@ -5812,9 +6264,8 @@ static void dplane_kernel_info_request(struct dplane_zns_info *zi) * pthread is running, we'll initiate this later on. */ if (zdplane_info.dg_master) - thread_add_event(zdplane_info.dg_master, - dplane_incoming_request, zi, 0, - &zi->t_request); + event_add_event(zdplane_info.dg_master, dplane_incoming_request, + zi, 0, &zi->t_request); } #endif /* HAVE_NETLINK */ @@ -5875,11 +6326,11 @@ void zebra_dplane_ns_enable(struct zebra_ns *zns, bool enabled) /* Stop any outstanding tasks */ if (zdplane_info.dg_master) { - thread_cancel_async(zdplane_info.dg_master, - &zi->t_request, NULL); + event_cancel_async(zdplane_info.dg_master, + &zi->t_request, NULL); - thread_cancel_async(zdplane_info.dg_master, &zi->t_read, - NULL); + event_cancel_async(zdplane_info.dg_master, &zi->t_read, + NULL); } XFREE(MTYPE_DP_NS, zi); @@ -5898,9 +6349,8 @@ int dplane_provider_work_ready(void) * available. */ if (zdplane_info.dg_run) { - thread_add_event(zdplane_info.dg_master, - dplane_thread_loop, NULL, 0, - &zdplane_info.dg_t_update); + event_add_event(zdplane_info.dg_master, dplane_thread_loop, + NULL, 0, &zdplane_info.dg_t_update); } return AOK; @@ -6091,6 +6541,7 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: break; } } @@ -6263,6 +6714,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) break; case DPLANE_OP_NONE: + case DPLANE_OP_STARTUP_STAGE: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_other_errors, 1, memory_order_relaxed); @@ -6597,7 +7049,7 @@ static bool dplane_work_pending(void) * final zebra shutdown. * This runs in the dplane pthread context. */ -static void dplane_check_shutdown_status(struct thread *event) +static void dplane_check_shutdown_status(struct event *event) { struct dplane_zns_info *zi; @@ -6609,8 +7061,8 @@ static void dplane_check_shutdown_status(struct thread *event) zns_info_list_del(&zdplane_info.dg_zns_list, zi); if (zdplane_info.dg_master) { - THREAD_OFF(zi->t_read); - THREAD_OFF(zi->t_request); + EVENT_OFF(zi->t_read); + EVENT_OFF(zi->t_request); } XFREE(MTYPE_DP_NS, zi); @@ -6618,10 +7070,9 @@ static void dplane_check_shutdown_status(struct thread *event) if (dplane_work_pending()) { /* Reschedule dplane check on a short timer */ - thread_add_timer_msec(zdplane_info.dg_master, - dplane_check_shutdown_status, - NULL, 100, - &zdplane_info.dg_t_shutdown_check); + event_add_timer_msec(zdplane_info.dg_master, + dplane_check_shutdown_status, NULL, 100, + &zdplane_info.dg_t_shutdown_check); /* TODO - give up and stop waiting after a short time? */ @@ -6629,7 +7080,7 @@ static void dplane_check_shutdown_status(struct thread *event) /* We appear to be done - schedule a final callback event * for the zebra main pthread. */ - thread_add_event(zrouter.master, zebra_finalize, NULL, 0, NULL); + event_add_event(zrouter.master, zebra_finalize, NULL, 0, NULL); } } @@ -6648,9 +7099,8 @@ void zebra_dplane_finish(void) if (IS_ZEBRA_DEBUG_DPLANE) zlog_debug("Zebra dataplane fini called"); - thread_add_event(zdplane_info.dg_master, - dplane_check_shutdown_status, NULL, 0, - &zdplane_info.dg_t_shutdown_check); + event_add_event(zdplane_info.dg_master, dplane_check_shutdown_status, + NULL, 0, &zdplane_info.dg_t_shutdown_check); } /* @@ -6664,7 +7114,7 @@ void zebra_dplane_finish(void) * pthread can look for other pending work - such as i/o work on behalf of * providers. */ -static void dplane_thread_loop(struct thread *event) +static void dplane_thread_loop(struct event *event) { struct dplane_ctx_list_head work_list; struct dplane_ctx_list_head error_list; @@ -6791,7 +7241,7 @@ static void dplane_thread_loop(struct thread *event) dplane_provider_lock(prov); while (counter < limit) { - ctx = dplane_ctx_list_pop(&(prov->dp_ctx_out_list)); + ctx = dplane_provider_dequeue_out_ctx(prov); if (ctx) { dplane_ctx_list_add_tail(&work_list, ctx); counter++; @@ -6857,10 +7307,6 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_run = false; - 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); /* Destroy pthread */ @@ -6929,14 +7375,14 @@ void zebra_dplane_start(void) zdplane_info.dg_run = true; /* Enqueue an initial event for the dataplane pthread */ - thread_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0, - &zdplane_info.dg_t_update); + event_add_event(zdplane_info.dg_master, dplane_thread_loop, NULL, 0, + &zdplane_info.dg_t_update); /* Enqueue requests and reads if necessary */ frr_each (zns_info_list, &zdplane_info.dg_zns_list, zi) { #if defined(HAVE_NETLINK) - thread_add_read(zdplane_info.dg_master, dplane_incoming_read, - zi, zi->info.sock, &zi->t_read); + event_add_read(zdplane_info.dg_master, dplane_incoming_read, zi, + zi->info.sock, &zi->t_read); dplane_kernel_info_request(zi); #endif } @@ -6959,6 +7405,25 @@ void zebra_dplane_start(void) frr_pthread_run(zdplane_info.dg_pthread, NULL); } +enum zebra_dplane_startup_notifications +dplane_ctx_get_startup_spot(struct zebra_dplane_ctx *ctx) +{ + return ctx->u.spot; +} + +void zebra_dplane_startup_stage(struct zebra_ns *zns, + enum zebra_dplane_startup_notifications spot) +{ + struct zebra_dplane_ctx *ctx = dplane_ctx_alloc(); + + ctx->zd_op = DPLANE_OP_STARTUP_STAGE; + ctx->zd_status = ZEBRA_DPLANE_REQUEST_QUEUED; + + ctx->u.spot = spot; + dplane_ctx_set_ns_id(ctx, zns->ns_id); + + dplane_provider_enqueue_to_zebra(ctx); +} /* * Initialize the dataplane module at startup; called by zebra rib_init() */ diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 3a4f3c217403..4be3f28874b9 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -87,6 +87,11 @@ enum zebra_dplane_result { ZEBRA_DPLANE_REQUEST_FAILURE, }; +enum zebra_dplane_startup_notifications { + ZEBRA_DPLANE_INTERFACES_READ, + ZEBRA_DPLANE_TUNNELS_READ, + ZEBRA_DPLANE_ADDRESSES_READ, +}; /* * API between the zebra dataplane system and the main zebra processing * context. @@ -188,7 +193,10 @@ enum dplane_op_e { DPLANE_OP_TC_CLASS_UPDATE, DPLANE_OP_TC_FILTER_ADD, DPLANE_OP_TC_FILTER_DELETE, - DPLANE_OP_TC_FILTER_UPDATE + DPLANE_OP_TC_FILTER_UPDATE, + + /* Startup Control */ + DPLANE_OP_STARTUP_STAGE, }; /* @@ -323,6 +331,118 @@ 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); void dplane_ctx_set_ifindex(struct zebra_dplane_ctx *ctx, ifindex_t ifindex); +void dplane_ctx_set_ifp_bond_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t ifindex); +ifindex_t dplane_ctx_get_ifp_bond_ifindex(const struct zebra_dplane_ctx *ctx); +enum zebra_iftype +dplane_ctx_get_ifp_zif_type(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_zif_type(struct zebra_dplane_ctx *ctx, + enum zebra_iftype zif_type); +void dplane_ctx_set_ifp_table_id(struct zebra_dplane_ctx *ctx, + uint32_t table_id); +uint32_t dplane_ctx_get_ifp_table_id(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_hw_addr(struct zebra_dplane_ctx *ctx, + int32_t hw_addr_len, uint8_t *hw_addr); +int32_t dplane_ctx_get_ifp_hw_addr_len(const struct zebra_dplane_ctx *ctx); +const uint8_t *dplane_ctx_get_ifp_hw_addr(const struct zebra_dplane_ctx *ctx); +struct zebra_l2info_bridge; +void dplane_ctx_set_ifp_bridge_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_bridge *binfo); +const struct zebra_l2info_bridge * +dplane_ctx_get_ifp_bridge_info(const struct zebra_dplane_ctx *ctx); +struct zebra_l2info_vlan; +void dplane_ctx_set_ifp_vlan_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_vlan *vinfo); +const struct zebra_l2info_vlan * +dplane_ctx_get_ifp_vlan_info(const struct zebra_dplane_ctx *ctx); +struct zebra_l2info_vxlan; +void dplane_ctx_set_ifp_vxlan_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_vxlan *vxinfo); +const struct zebra_l2info_vxlan * +dplane_ctx_get_ifp_vxlan_info(const struct zebra_dplane_ctx *ctx); +struct zebra_l2info_gre; +void dplane_ctx_set_ifp_gre_info(struct zebra_dplane_ctx *ctx, + struct zebra_l2info_gre *greinfo); +const struct zebra_l2info_gre * +dplane_ctx_get_ifp_gre_info(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_zltype(struct zebra_dplane_ctx *ctx, + enum zebra_link_type zlt); +enum zebra_link_type +dplane_ctx_get_ifp_zltype(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_link_nsid(struct zebra_dplane_ctx *ctx, ns_id_t ns_id); +ns_id_t dplane_ctx_get_ifp_link_nsid(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_desc(struct zebra_dplane_ctx *ctx, const char *desc); +char *dplane_ctx_get_ifp_desc(struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_mtu(struct zebra_dplane_ctx *ctx, uint32_t mtu); +uint32_t dplane_ctx_get_ifp_mtu(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_vrf_id(struct zebra_dplane_ctx *ctx, vrf_id_t vrf_id); +vrf_id_t dplane_ctx_get_ifp_vrf_id(const struct zebra_dplane_ctx *ctx); +enum zebra_slave_iftype; +void dplane_ctx_set_ifp_zif_slave_type(struct zebra_dplane_ctx *ctx, + enum zebra_slave_iftype zslave_type); +enum zebra_slave_iftype +dplane_ctx_get_ifp_zif_slave_type(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_master_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t master_ifindex); +ifindex_t dplane_ctx_get_ifp_master_ifindex(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_bridge_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t bridge_ifindex); +ifindex_t dplane_ctx_get_ifp_bridge_ifindex(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_bypass(struct zebra_dplane_ctx *ctx, uint8_t bypass); +uint8_t dplane_ctx_get_ifp_bypass(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_flags(struct zebra_dplane_ctx *ctx, uint64_t flags); +uint64_t dplane_ctx_get_ifp_flags(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_protodown(struct zebra_dplane_ctx *ctx, bool protodown); +bool dplane_ctx_get_ifp_protodown(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_startup(struct zebra_dplane_ctx *ctx, bool startup); +bool dplane_ctx_get_ifp_startup(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_protodown_set(struct zebra_dplane_ctx *ctx, bool set); +bool dplane_ctx_get_ifp_protodown_set(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_rc_bitfield(struct zebra_dplane_ctx *ctx, + uint32_t rc_bitfield); +uint32_t dplane_ctx_get_ifp_rc_bitfield(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_link_ifindex(struct zebra_dplane_ctx *ctx, + ifindex_t link_ifindex); +ifindex_t dplane_ctx_get_ifp_link_ifindex(const struct zebra_dplane_ctx *ctx); +void dplane_ctx_set_ifp_family(struct zebra_dplane_ctx *ctx, uint8_t family); +uint8_t dplane_ctx_get_ifp_family(const struct zebra_dplane_ctx *ctx); +struct zebra_vxlan_vni_array; +void dplane_ctx_set_ifp_vxlan_vni_array(struct zebra_dplane_ctx *ctx, + struct zebra_vxlan_vni_array *vniarray); + +/* + * These defines mirror the values for bridge values in linux + * at this point since we only have a linux implementation + * we don't need to do any type of translation. Let's just + * pass these through and use them + */ +#define DPLANE_BRIDGE_VLAN_INFO_PVID \ + (1 << 1) /* VLAN is PVID, ingress untagged */ +#define DPLANE_BRIDGE_VLAN_INFO_RANGE_BEGIN \ + (1 << 3) /* VLAN is start of vlan range */ +#define DPLANE_BRIDGE_VLAN_INFO_RANGE_END \ + (1 << 4) /* VLAN is end of vlan range */ +const struct zebra_vxlan_vni_array * +dplane_ctx_get_ifp_vxlan_vni_array(const struct zebra_dplane_ctx *ctx); +struct zebra_dplane_bridge_vlan_info { + uint16_t flags; + uint16_t vid; +}; +void dplane_ctx_set_ifp_bridge_vlan_info( + struct zebra_dplane_ctx *ctx, + struct zebra_dplane_bridge_vlan_info *bvinfo); +const struct zebra_dplane_bridge_vlan_info * +dplane_ctx_get_ifp_bridge_vlan_info(const struct zebra_dplane_ctx *ctx); + +struct zebra_dplane_bridge_vlan_info_array { + int count; + struct zebra_dplane_bridge_vlan_info array[0]; +}; +void dplane_ctx_set_ifp_bridge_vlan_info_array( + struct zebra_dplane_ctx *ctx, + struct zebra_dplane_bridge_vlan_info_array *bvarray); +const struct zebra_dplane_bridge_vlan_info_array * +dplane_ctx_get_ifp_bridge_vlan_info_array(const struct zebra_dplane_ctx *ctx); /* Retrieve last/current provider id */ uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx); @@ -747,7 +867,6 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, */ enum zebra_dplane_result dplane_intf_add(const struct interface *ifp); enum zebra_dplane_result dplane_intf_update(const struct interface *ifp); -enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp); /* * Enqueue tc link changes for the dataplane. @@ -1006,7 +1125,7 @@ void dplane_provider_lock(struct zebra_dplane_provider *prov); void dplane_provider_unlock(struct zebra_dplane_provider *prov); /* Obtain thread_master for dataplane thread */ -struct thread_master *dplane_get_thread_master(void); +struct event_loop *dplane_get_thread_master(void); /* Providers should (generally) limit number of updates per work cycle */ int dplane_provider_get_work_limit(const struct zebra_dplane_provider *prov); @@ -1067,6 +1186,9 @@ void zebra_dplane_pre_finish(void); void zebra_dplane_finish(void); void zebra_dplane_shutdown(void); +void zebra_dplane_startup_stage(struct zebra_ns *zns, + enum zebra_dplane_startup_notifications spot); + /* * decision point for sending a routing update through the old * straight to zebra master pthread or through the dplane to @@ -1077,6 +1199,9 @@ void dplane_rib_add_multipath(afi_t afi, safi_t safi, struct prefix *p, struct nexthop_group *ng, int startup, struct zebra_dplane_ctx *ctx); +enum zebra_dplane_startup_notifications +dplane_ctx_get_startup_spot(struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index a2d310a10b2c..6d5cd66143d3 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -378,7 +378,7 @@ static char *zebra_evpn_zebra_mac_flag_dump(struct zebra_mac *mac, char *buf, return buf; } -static void zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t) +static void zebra_evpn_dad_mac_auto_recovery_exp(struct event *t) { struct zebra_vrf *zvrf = NULL; struct zebra_mac *mac = NULL; @@ -386,10 +386,10 @@ static void zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t) struct listnode *node = NULL; struct zebra_neigh *nbr = NULL; - mac = THREAD_ARG(t); + mac = EVENT_ARG(t); /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(mac->zevpn->vrf_id); + zvrf = zebra_vrf_lookup_by_id(mac->zevpn->vrf_id); if (!zvrf) return; @@ -575,7 +575,7 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, } /* Start auto recovery timer for this MAC */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); + EVENT_OFF(mac->dad_mac_auto_recovery_timer); if (zvrf->dad_freeze && zvrf->dad_freeze_time) { if (IS_ZEBRA_DEBUG_VXLAN) { char mac_buf[MAC_BUF_SIZE]; @@ -588,10 +588,10 @@ static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, 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); + event_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), @@ -615,7 +615,7 @@ void zebra_evpn_print_mac(struct zebra_mac *mac, void *ctxt, json_object *json) struct zebra_vrf *zvrf; struct timeval detect_start_time = {0, 0}; char timebuf[MONOTIME_STRLEN]; - char thread_buf[THREAD_TIMER_STRLEN]; + char thread_buf[EVENT_TIMER_STRLEN]; time_t uptime; char up_str[MONOTIME_STRLEN]; @@ -692,9 +692,9 @@ void zebra_evpn_print_mac(struct zebra_mac *mac, void *ctxt, json_object *json) if (mac->hold_timer) json_object_string_add( json_mac, "peerActiveHold", - thread_timer_to_hhmmss(thread_buf, - sizeof(thread_buf), - mac->hold_timer)); + event_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); @@ -784,9 +784,9 @@ void zebra_evpn_print_mac(struct zebra_mac *mac, void *ctxt, json_object *json) 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)); + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); vty_out(vty, "\n"); vty_out(vty, " Local Seq: %u Remote Seq: %u\n", mac->loc_seq, mac->rem_seq); @@ -1038,12 +1038,11 @@ int zebra_evpn_macip_send_msg_to_client(vni_t vni, char flag_buf[MACIP_BUF_SIZE]; zlog_debug( - "Send MACIP %s f %s MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s", + "Send MACIP %s f %s state %u MAC %pEA IP %pIA seq %u L2-VNI %u ESI %s to %s", (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", zclient_evpn_dump_macip_flags(flags, flag_buf, sizeof(flag_buf)), - macaddr, ip, seq, vni, - es ? es->esi_str : "-", + state, macaddr, ip, seq, vni, es ? es->esi_str : "-", zebra_route_string(client->proto)); } @@ -1152,7 +1151,7 @@ int zebra_evpn_mac_del(struct zebra_evpn *zevpn, struct zebra_mac *mac) zebra_evpn_mac_stop_hold_timer(mac); /* Cancel auto recovery */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); + EVENT_OFF(mac->dad_mac_auto_recovery_timer); /* If the MAC is freed before the neigh we will end up * with a stale pointer against the neigh. @@ -1341,16 +1340,26 @@ int zebra_evpn_mac_send_add_to_client(vni_t vni, const struct ethaddr *macaddr, int zebra_evpn_mac_send_del_to_client(vni_t vni, const struct ethaddr *macaddr, uint32_t flags, bool force) { + int state = ZEBRA_NEIGH_ACTIVE; + 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; + + /* MAC is LOCAL and DUP_DETECTED, this local mobility event + * is not known to bgpd. Upon receiving local delete + * ask bgp to reinstall the best route (remote entry). + */ + if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL) && + CHECK_FLAG(flags, ZEBRA_MAC_DUPLICATE)) + state = ZEBRA_NEIGH_INACTIVE; } return zebra_evpn_macip_send_msg_to_client( - vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, - ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL); + vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, state, NULL, + ZEBRA_MACIP_DEL); } /* @@ -1500,7 +1509,7 @@ void zebra_evpn_mac_send_add_del_to_client(struct zebra_mac *mac, * external neighmgr daemon to probe existing hosts to independently * establish their presence on the ES. */ -static void zebra_evpn_mac_hold_exp_cb(struct thread *t) +static void zebra_evpn_mac_hold_exp_cb(struct event *t) { struct zebra_mac *mac; bool old_bgp_ready; @@ -1508,7 +1517,7 @@ static void zebra_evpn_mac_hold_exp_cb(struct thread *t) bool old_static; bool new_static; - mac = THREAD_ARG(t); + mac = EVENT_ARG(t); /* the purpose of the hold timer is to age out the peer-active * flag */ @@ -1561,8 +1570,8 @@ static inline void zebra_evpn_mac_start_hold_timer(struct zebra_mac *mac) zebra_evpn_zebra_mac_flag_dump(mac, mac_buf, sizeof(mac_buf))); } - thread_add_timer(zrouter.master, zebra_evpn_mac_hold_exp_cb, mac, - zmh_info->mac_hold_time, &mac->hold_timer); + event_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(struct zebra_mac *mac) @@ -1581,7 +1590,7 @@ void zebra_evpn_mac_stop_hold_timer(struct zebra_mac *mac) sizeof(mac_buf))); } - THREAD_OFF(mac->hold_timer); + EVENT_OFF(mac->hold_timer); } void zebra_evpn_sync_mac_del(struct zebra_mac *mac) @@ -1685,6 +1694,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, struct zebra_mac *mac; bool inform_bgp = false; bool inform_dataplane = false; + bool mac_inactive = false; bool seq_change = false; bool es_change = false; uint32_t tmp_seq; @@ -1701,6 +1711,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, */ inform_bgp = true; inform_dataplane = true; + mac_inactive = true; /* create the MAC and associate it with the dest ES */ mac = zebra_evpn_mac_add(zevpn, macaddr); @@ -1812,6 +1823,7 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, if (es_change) { inform_bgp = true; inform_dataplane = true; + mac_inactive = true; } /* if peer-flag is being set notify dataplane that the @@ -1867,9 +1879,9 @@ struct zebra_mac *zebra_evpn_proc_sync_mac_update(struct zebra_evpn *zevpn, * the activity as we are yet to establish activity * locally */ - zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, - false /* force_clear_static */, - __func__); + zebra_evpn_sync_mac_dp_install( + mac, mac_inactive /* set_inactive */, + false /* force_clear_static */, __func__); } return mac; @@ -2432,7 +2444,7 @@ int zebra_evpn_del_local_mac(struct zebra_evpn *zevpn, struct zebra_mac *mac, /* Remove MAC from BGP. */ zebra_evpn_mac_send_del_to_client(zevpn->vni, &mac->macaddr, mac->flags, - false /* force */); + clear_static /* force */); zebra_evpn_es_mac_deref_entry(mac); diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h index befb1c398d43..ba612f769690 100644 --- a/zebra/zebra_evpn_mac.h +++ b/zebra/zebra_evpn_mac.h @@ -114,14 +114,14 @@ struct zebra_mac { /* Duplicate mac detection */ uint32_t dad_count; - struct thread *dad_mac_auto_recovery_timer; + struct event *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; + struct event *hold_timer; /* number of neigh entries (using this mac) that have * ZEBRA_MAC_ES_PEER_ACTIVE or ZEBRA_NEIGH_ES_PEER_PROXY diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index 334dde3b1012..a5092c629ac7 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -535,19 +535,26 @@ static bool zebra_evpn_acc_vl_cmp(const void *p1, const void *p2) } /* Lookup VLAN based broadcast domain */ -struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid, - struct interface *br_if) +struct zebra_evpn_access_bd * +zebra_evpn_acc_vl_find_index(vlanid_t vid, ifindex_t bridge_ifindex) { struct zebra_evpn_access_bd *acc_bd; struct zebra_evpn_access_bd tmp; tmp.vid = vid; - tmp.bridge_ifindex = br_if->ifindex; + tmp.bridge_ifindex = bridge_ifindex; acc_bd = hash_lookup(zmh_info->evpn_vlan_table, &tmp); return acc_bd; } +/* Lookup VLAN based broadcast domain */ +struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid, + struct interface *br_if) +{ + return zebra_evpn_acc_vl_find_index(vid, br_if->ifindex); +} + /* A new broadcast domain can be created when a VLAN member or VLAN<=>VxLAN_IF * mapping is added. */ @@ -842,9 +849,9 @@ void zebra_evpn_access_bd_bridge_cleanup(vlanid_t vid, struct interface *br_if, void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, bool set) { - struct interface *br_if; struct zebra_vxlan_vni *vni; struct zebra_evpn_access_bd *acc_bd; + ifindex_t br_ifindex; if (!zif) return; @@ -854,11 +861,12 @@ void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, struct zebra_evpn *zevpn, if (!vni) return; - br_if = zif->brslave_info.br_if; - if (!br_if) + /* Use the index as the pointer can be stale (deleted) */ + br_ifindex = zif->brslave_info.bridge_ifindex; + if (!zif->brslave_info.br_if || br_ifindex == IFINDEX_INTERNAL) return; - acc_bd = zebra_evpn_acc_vl_find(vni->access_vlan, br_if); + acc_bd = zebra_evpn_acc_vl_find_index(vni->access_vlan, br_ifindex); if (!acc_bd) return; @@ -2201,11 +2209,11 @@ static void zebra_evpn_mh_advertise_svi_mac(void) zebra_evpn_acc_vl_adv_svi_mac_all(); } -static void zebra_evpn_es_df_delay_exp_cb(struct thread *t) +static void zebra_evpn_es_df_delay_exp_cb(struct event *t) { struct zebra_evpn_es *es; - es = THREAD_ARG(t); + es = EVENT_ARG(t); if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es %s df-delay expired", es->esi_str); @@ -2269,9 +2277,9 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, /* Start the DF delay timer on the local ES */ if (!es->df_delay_timer) - thread_add_timer(zrouter.master, zebra_evpn_es_df_delay_exp_cb, - es, ZEBRA_EVPN_MH_DF_DELAY_TIME, - &es->df_delay_timer); + event_add_timer(zrouter.master, zebra_evpn_es_df_delay_exp_cb, + es, ZEBRA_EVPN_MH_DF_DELAY_TIME, + &es->df_delay_timer); /* See if the local VTEP can function as DF on the ES */ if (!zebra_evpn_es_run_df_election(es, __func__)) { @@ -2314,7 +2322,7 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es **esp) es->flags &= ~(ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_READY_FOR_BGP); - THREAD_OFF(es->df_delay_timer); + EVENT_OFF(es->df_delay_timer); /* clear EVPN protodown flags on the access port */ zebra_evpn_mh_clear_protodown_es(es); @@ -3139,7 +3147,7 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, char alg_buf[EVPN_DF_ALG_STR_LEN]; struct zebra_evpn_es_vtep *es_vtep; struct listnode *node; - char thread_buf[THREAD_TIMER_STRLEN]; + char thread_buf[EVENT_TIMER_STRLEN]; if (json) { json_object *json_vteps; @@ -3157,6 +3165,9 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, json_array_string_add(json_flags, "local"); if (es->flags & ZEBRA_EVPNES_REMOTE) json_array_string_add(json_flags, "remote"); + if (es->flags & ZEBRA_EVPNES_LOCAL && + !(es->flags & ZEBRA_EVPNES_NON_DF)) + json_array_string_add(json_flags, "df"); if (es->flags & ZEBRA_EVPNES_NON_DF) json_array_string_add(json_flags, "nonDF"); if (es->flags & ZEBRA_EVPNES_BYPASS) @@ -3181,9 +3192,9 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, if (es->df_delay_timer) json_object_string_add( json, "dfDelayTimer", - thread_timer_to_hhmmss(thread_buf, - sizeof(thread_buf), - es->df_delay_timer)); + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + es->df_delay_timer)); json_object_int_add(json, "nexthopGroup", es->nhg_id); if (listcount(es->es_vtep_list)) { json_vteps = json_object_new_array(); @@ -3226,9 +3237,9 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, : "df"); if (es->df_delay_timer) vty_out(vty, " DF delay: %s\n", - thread_timer_to_hhmmss(thread_buf, - sizeof(thread_buf), - es->df_delay_timer)); + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + es->df_delay_timer)); vty_out(vty, " DF preference: %u\n", es->df_pref); vty_out(vty, " Nexthop group: %u\n", es->nhg_id); vty_out(vty, " VTEPs:\n"); @@ -3522,15 +3533,15 @@ DEFPY(zebra_evpn_mh_uplink, zebra_evpn_mh_uplink_cmd, "[no] evpn mh uplink", void zebra_evpn_mh_json(json_object *json) { json_object *json_array; - char thread_buf[THREAD_TIMER_STRLEN]; + char thread_buf[EVENT_TIMER_STRLEN]; json_object_int_add(json, "macHoldtime", zmh_info->mac_hold_time); json_object_int_add(json, "neighHoldtime", zmh_info->neigh_hold_time); json_object_int_add(json, "startupDelay", zmh_info->startup_delay_time); json_object_string_add( json, "startupDelayTimer", - thread_timer_to_hhmmss(thread_buf, sizeof(thread_buf), - zmh_info->startup_delay_timer)); + event_timer_to_hhmmss(thread_buf, sizeof(thread_buf), + zmh_info->startup_delay_timer)); json_object_int_add(json, "uplinkConfigCount", zmh_info->uplink_cfg_cnt); json_object_int_add(json, "uplinkActiveCount", @@ -3555,15 +3566,15 @@ void zebra_evpn_mh_json(json_object *json) void zebra_evpn_mh_print(struct vty *vty) { char pd_buf[ZEBRA_PROTODOWN_RC_STR_LEN]; - char thread_buf[THREAD_TIMER_STRLEN]; + char thread_buf[EVENT_TIMER_STRLEN]; vty_out(vty, "EVPN MH:\n"); vty_out(vty, " mac-holdtime: %ds, neigh-holdtime: %ds\n", zmh_info->mac_hold_time, zmh_info->neigh_hold_time); vty_out(vty, " startup-delay: %ds, start-delay-timer: %s\n", zmh_info->startup_delay_time, - thread_timer_to_hhmmss(thread_buf, sizeof(thread_buf), - zmh_info->startup_delay_timer)); + event_timer_to_hhmmss(thread_buf, sizeof(thread_buf), + zmh_info->startup_delay_timer)); vty_out(vty, " uplink-cfg-cnt: %u, uplink-active-cnt: %u\n", zmh_info->uplink_cfg_cnt, zmh_info->uplink_oper_up_cnt); if (zmh_info->protodown_rc) @@ -3917,7 +3928,7 @@ void zebra_evpn_mh_uplink_oper_update(struct zebra_if *zif) new_protodown); } -static void zebra_evpn_mh_startup_delay_exp_cb(struct thread *t) +static void zebra_evpn_mh_startup_delay_exp_cb(struct event *t) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("startup-delay expired"); @@ -3931,7 +3942,7 @@ static void zebra_evpn_mh_startup_delay_timer_start(const char *rc) if (zmh_info->startup_delay_timer) { if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("startup-delay timer cancelled"); - THREAD_OFF(zmh_info->startup_delay_timer); + EVENT_OFF(zmh_info->startup_delay_timer); } if (zmh_info->startup_delay_time) { @@ -3939,10 +3950,10 @@ static void zebra_evpn_mh_startup_delay_timer_start(const char *rc) zlog_debug( "startup-delay timer started for %d sec on %s", zmh_info->startup_delay_time, rc); - thread_add_timer(zrouter.master, - zebra_evpn_mh_startup_delay_exp_cb, NULL, - zmh_info->startup_delay_time, - &zmh_info->startup_delay_timer); + event_add_timer(zrouter.master, + zebra_evpn_mh_startup_delay_exp_cb, NULL, + zmh_info->startup_delay_time, + &zmh_info->startup_delay_timer); zebra_evpn_mh_update_protodown( ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY, true /* set */); } else { diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 89a917628d2e..59a41d060699 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -87,7 +87,7 @@ struct zebra_evpn_es { * imported before running the DF election. */ #define ZEBRA_EVPN_MH_DF_DELAY_TIME 3 /* seconds */ - struct thread *df_delay_timer; + struct event *df_delay_timer; }; RB_HEAD(zebra_es_rb_head, zebra_evpn_es); RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); @@ -248,7 +248,7 @@ struct zebra_evpn_mh_info { */ int startup_delay_time; /* seconds */ #define ZEBRA_EVPN_MH_STARTUP_DELAY_DEF (3 * 60) - struct thread *startup_delay_timer; + struct event *startup_delay_timer; /* Number of configured uplinks */ uint32_t uplink_cfg_cnt; @@ -344,6 +344,8 @@ extern void zebra_evpn_if_es_print(struct vty *vty, json_object *json, struct zebra_if *zif); extern struct zebra_evpn_access_bd * zebra_evpn_acc_vl_find(vlanid_t vid, struct interface *br_if); +struct zebra_evpn_access_bd * +zebra_evpn_acc_vl_find_index(vlanid_t vid, ifindex_t bridge_ifindex); extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid, struct interface *br_if); extern void zebra_evpn_es_cleanup(void); diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index acd60aa08f61..74141e4f34d3 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -397,7 +397,7 @@ void zebra_evpn_sync_neigh_static_chg(struct zebra_neigh *n, bool old_n_static, * external neighmgr daemon to probe existing hosts to independently * establish their presence on the ES. */ -static void zebra_evpn_neigh_hold_exp_cb(struct thread *t) +static void zebra_evpn_neigh_hold_exp_cb(struct event *t) { struct zebra_neigh *n; bool old_bgp_ready; @@ -405,7 +405,7 @@ static void zebra_evpn_neigh_hold_exp_cb(struct thread *t) bool old_n_static; bool new_n_static; - n = THREAD_ARG(t); + n = EVENT_ARG(t); /* the purpose of the hold timer is to age out the peer-active * flag */ @@ -444,8 +444,8 @@ static inline void zebra_evpn_neigh_start_hold_timer(struct zebra_neigh *n) if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold start", n->zevpn->vni, &n->ip, &n->emac, n->flags); - thread_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n, - zmh_info->neigh_hold_time, &n->hold_timer); + event_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(struct zebra_neigh *n, @@ -576,7 +576,7 @@ int zebra_evpn_neigh_del(struct zebra_evpn *zevpn, struct zebra_neigh *n) listnode_delete(n->mac->neigh_list, n); /* Cancel auto recovery */ - THREAD_OFF(n->dad_ip_auto_recovery_timer); + EVENT_OFF(n->dad_ip_auto_recovery_timer); /* Cancel proxy hold timer */ zebra_evpn_neigh_stop_hold_timer(n); @@ -1080,13 +1080,13 @@ static int zebra_evpn_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, return 0; } -static void zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t) +static void zebra_evpn_dad_ip_auto_recovery_exp(struct event *t) { struct zebra_vrf *zvrf = NULL; struct zebra_neigh *nbr = NULL; struct zebra_evpn *zevpn = NULL; - nbr = THREAD_ARG(t); + nbr = EVENT_ARG(t); /* since this is asynchronous we need sanity checks*/ zvrf = vrf_info_lookup(nbr->zevpn->vrf_id); @@ -1223,7 +1223,7 @@ static void zebra_evpn_dup_addr_detect_for_neigh( nbr->dad_dup_detect_time = monotime(NULL); /* Start auto recovery timer for this IP */ - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + EVENT_OFF(nbr->dad_ip_auto_recovery_timer); if (zvrf->dad_freeze && zvrf->dad_freeze_time) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( @@ -1231,10 +1231,10 @@ static void zebra_evpn_dup_addr_detect_for_neigh( __func__, &nbr->emac, &nbr->ip, 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); + event_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; @@ -1680,7 +1680,7 @@ void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) 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); + EVENT_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, @@ -1706,7 +1706,7 @@ void zebra_evpn_print_neigh(struct zebra_neigh *n, void *ctxt, struct zebra_vrf *zvrf = NULL; struct timeval detect_start_time = {0, 0}; char timebuf[MONOTIME_STRLEN]; - char thread_buf[THREAD_TIMER_STRLEN]; + char thread_buf[EVENT_TIMER_STRLEN]; time_t uptime; char up_str[MONOTIME_STRLEN]; @@ -1746,9 +1746,9 @@ void zebra_evpn_print_neigh(struct zebra_neigh *n, void *ctxt, } if (n->hold_timer) { vty_out(vty, " (ht: %s)", - thread_timer_to_hhmmss(thread_buf, - sizeof(thread_buf), - n->hold_timer)); + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); sync_info = true; } if (!sync_info) @@ -1769,9 +1769,9 @@ void zebra_evpn_print_neigh(struct zebra_neigh *n, void *ctxt, if (n->hold_timer) json_object_string_add( json, "peerActiveHold", - thread_timer_to_hhmmss(thread_buf, - sizeof(thread_buf), - n->hold_timer)); + event_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); } if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { if (n->mac->es) { diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h index 18ef8546de31..c82b3673b15e 100644 --- a/zebra/zebra_evpn_neigh.h +++ b/zebra/zebra_evpn_neigh.h @@ -92,7 +92,7 @@ struct zebra_neigh { /* Duplicate ip detection */ uint32_t dad_count; - struct thread *dad_ip_auto_recovery_timer; + struct event *dad_ip_auto_recovery_timer; struct timeval detect_start_time; @@ -101,7 +101,7 @@ struct zebra_neigh { time_t uptime; /* used for ageing out the PEER_ACTIVE flag */ - struct thread *hold_timer; + struct event *hold_timer; }; /* @@ -158,7 +158,7 @@ static inline void zebra_evpn_neigh_stop_hold_timer(struct zebra_neigh *n) if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) zlog_debug("sync-neigh vni %u ip %pIA mac %pEA 0x%x hold stop", n->zevpn->vni, &n->ip, &n->emac, n->flags); - THREAD_OFF(n->hold_timer); + EVENT_OFF(n->hold_timer); } void zebra_evpn_sync_neigh_static_chg(struct zebra_neigh *n, bool old_n_static, diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 15765b5e1857..699f3ed11064 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -11,7 +11,7 @@ #include "log.h" #include "libfrr.h" #include "stream.h" -#include "thread.h" +#include "frrevent.h" #include "network.h" #include "command.h" #include "lib/version.h" @@ -156,7 +156,7 @@ struct zfpm_glob { */ enum zfpm_msg_format message_format; - struct thread_master *master; + struct event_loop *master; enum zfpm_state state; @@ -204,15 +204,15 @@ struct zfpm_glob { /* * Threads for I/O. */ - struct thread *t_connect; - struct thread *t_write; - struct thread *t_read; + struct event *t_connect; + struct event *t_write; + struct event *t_read; /* * Thread to clean up after the TCP connection to the FPM goes down * and the state that belongs to it. */ - struct thread *t_conn_down; + struct event *t_conn_down; struct { struct zfpm_rnodes_iter iter; @@ -222,7 +222,7 @@ struct zfpm_glob { * Thread to take actions once the TCP conn to the FPM comes up, and * the state that belongs to it. */ - struct thread *t_conn_up; + struct event *t_conn_up; struct { struct zfpm_rnodes_iter iter; @@ -251,7 +251,7 @@ struct zfpm_glob { /* * Stats interval timer. */ - struct thread *t_stats; + struct event *t_stats; /* * If non-zero, the last time when statistics were cleared. @@ -269,8 +269,8 @@ static struct zfpm_glob *zfpm_g = &zfpm_glob_space; static int zfpm_trigger_update(struct route_node *rn, const char *reason); -static void zfpm_read_cb(struct thread *thread); -static void zfpm_write_cb(struct thread *thread); +static void zfpm_read_cb(struct event *thread); +static void zfpm_write_cb(struct event *thread); static void zfpm_set_state(enum zfpm_state state, const char *reason); static void zfpm_start_connect_timer(const char *reason); @@ -283,9 +283,9 @@ union g_addr ipv4ll_gateway; /* * zfpm_thread_should_yield */ -static inline int zfpm_thread_should_yield(struct thread *t) +static inline int zfpm_thread_should_yield(struct event *t) { - return thread_should_yield(t); + return event_should_yield(t); } /* @@ -460,8 +460,8 @@ static inline void zfpm_read_on(void) assert(!zfpm_g->t_read); assert(zfpm_g->sock >= 0); - thread_add_read(zfpm_g->master, zfpm_read_cb, 0, zfpm_g->sock, - &zfpm_g->t_read); + event_add_read(zfpm_g->master, zfpm_read_cb, 0, zfpm_g->sock, + &zfpm_g->t_read); } /* @@ -472,8 +472,8 @@ static inline void zfpm_write_on(void) assert(!zfpm_g->t_write); assert(zfpm_g->sock >= 0); - thread_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, - &zfpm_g->t_write); + event_add_write(zfpm_g->master, zfpm_write_cb, 0, zfpm_g->sock, + &zfpm_g->t_write); } /* @@ -481,7 +481,7 @@ static inline void zfpm_write_on(void) */ static inline void zfpm_read_off(void) { - THREAD_OFF(zfpm_g->t_read); + EVENT_OFF(zfpm_g->t_read); } /* @@ -489,12 +489,17 @@ static inline void zfpm_read_off(void) */ static inline void zfpm_write_off(void) { - THREAD_OFF(zfpm_g->t_write); + EVENT_OFF(zfpm_g->t_write); } static inline void zfpm_connect_off(void) { - THREAD_OFF(zfpm_g->t_connect); + EVENT_OFF(zfpm_g->t_connect); +} + +static inline void zfpm_conn_down_off(void) +{ + EVENT_OFF(zfpm_g->t_conn_down); } /* @@ -503,7 +508,7 @@ static inline void zfpm_connect_off(void) * Callback for actions to be taken when the connection to the FPM * comes up. */ -static void zfpm_conn_up_thread_cb(struct thread *thread) +static void zfpm_conn_up_thread_cb(struct event *thread) { struct route_node *rnode; struct zfpm_rnodes_iter *iter; @@ -542,8 +547,8 @@ static void zfpm_conn_up_thread_cb(struct thread *thread) zfpm_g->stats.t_conn_up_yields++; zfpm_rnodes_iter_pause(iter); - thread_add_timer_msec(zfpm_g->master, zfpm_conn_up_thread_cb, - NULL, 0, &zfpm_g->t_conn_up); + event_add_timer_msec(zfpm_g->master, zfpm_conn_up_thread_cb, + NULL, 0, &zfpm_g->t_conn_up); return; } @@ -568,15 +573,15 @@ static void zfpm_connection_up(const char *detail) /* * Start thread to push existing routes to the FPM. */ - THREAD_OFF(zfpm_g->t_conn_up); + EVENT_OFF(zfpm_g->t_conn_up); zfpm_rnodes_iter_init(&zfpm_g->t_conn_up_state.iter); zfpm_g->fpm_mac_dump_done = false; zfpm_debug("Starting conn_up thread"); - thread_add_timer_msec(zfpm_g->master, zfpm_conn_up_thread_cb, NULL, 0, - &zfpm_g->t_conn_up); + event_add_timer_msec(zfpm_g->master, zfpm_conn_up_thread_cb, NULL, 0, + &zfpm_g->t_conn_up); zfpm_g->stats.t_conn_up_starts++; } @@ -619,7 +624,7 @@ static void zfpm_connect_check(void) * Callback that is invoked to clean up state after the TCP connection * to the FPM goes down. */ -static void zfpm_conn_down_thread_cb(struct thread *thread) +static void zfpm_conn_down_thread_cb(struct event *thread) { struct route_node *rnode; struct zfpm_rnodes_iter *iter; @@ -635,8 +640,6 @@ static void zfpm_conn_down_thread_cb(struct thread *thread) 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; while ((rnode = zfpm_rnodes_iter_next(iter))) { @@ -667,9 +670,8 @@ static void zfpm_conn_down_thread_cb(struct thread *thread) zfpm_g->stats.t_conn_down_yields++; zfpm_rnodes_iter_pause(iter); - zfpm_g->t_conn_down = NULL; - thread_add_timer_msec(zfpm_g->master, zfpm_conn_down_thread_cb, - NULL, 0, &zfpm_g->t_conn_down); + event_add_timer_msec(zfpm_g->master, zfpm_conn_down_thread_cb, + NULL, 0, &zfpm_g->t_conn_down); return; } @@ -712,9 +714,9 @@ static void zfpm_connection_down(const char *detail) */ assert(!zfpm_g->t_conn_down); 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, - &zfpm_g->t_conn_down); + zfpm_conn_down_off(); + event_add_timer_msec(zfpm_g->master, zfpm_conn_down_thread_cb, NULL, 0, + &zfpm_g->t_conn_down); zfpm_g->stats.t_conn_down_starts++; zfpm_set_state(ZFPM_STATE_IDLE, detail); @@ -723,7 +725,7 @@ static void zfpm_connection_down(const char *detail) /* * zfpm_read_cb */ -static void zfpm_read_cb(struct thread *thread) +static void zfpm_read_cb(struct event *thread) { size_t already; struct stream *ibuf; @@ -1152,7 +1154,7 @@ static void zfpm_build_updates(void) /* * zfpm_write_cb */ -static void zfpm_write_cb(struct thread *thread) +static void zfpm_write_cb(struct event *thread) { struct stream *s; int num_writes; @@ -1234,7 +1236,7 @@ static void zfpm_write_cb(struct thread *thread) /* * zfpm_connect_cb */ -static void zfpm_connect_cb(struct thread *t) +static void zfpm_connect_cb(struct event *t) { int sock, ret; struct sockaddr_in serv; @@ -1388,8 +1390,8 @@ static void zfpm_start_connect_timer(const char *reason) delay_secs = zfpm_calc_connect_delay(); zfpm_debug("scheduling connect in %ld seconds", delay_secs); - thread_add_timer(zfpm_g->master, zfpm_connect_cb, 0, delay_secs, - &zfpm_g->t_connect); + event_add_timer(zfpm_g->master, zfpm_connect_cb, 0, delay_secs, + &zfpm_g->t_connect); zfpm_set_state(ZFPM_STATE_ACTIVE, reason); } @@ -1664,7 +1666,7 @@ static void zfpm_iterate_rmac_table(struct hash_bucket *bucket, void *args) /* * struct zfpm_statsimer_cb */ -static void zfpm_stats_timer_cb(struct thread *t) +static void zfpm_stats_timer_cb(struct event *t) { zfpm_g->t_stats = NULL; @@ -1697,7 +1699,7 @@ static void zfpm_stop_stats_timer(void) return; zfpm_debug("Stopping existing stats timer"); - THREAD_OFF(zfpm_g->t_stats); + EVENT_OFF(zfpm_g->t_stats); } /* @@ -1707,8 +1709,8 @@ void zfpm_start_stats_timer(void) { assert(!zfpm_g->t_stats); - thread_add_timer(zfpm_g->master, zfpm_stats_timer_cb, 0, - ZFPM_STATS_IVL_SECS, &zfpm_g->t_stats); + event_add_timer(zfpm_g->master, zfpm_stats_timer_cb, 0, + ZFPM_STATS_IVL_SECS, &zfpm_g->t_stats); } /* @@ -1978,7 +1980,7 @@ static struct cmd_node zebra_node = { * * Returns true on success. */ -static int zfpm_init(struct thread_master *master) +static int zfpm_init(struct event_loop *master) { int enable = 1; uint16_t port = 0; @@ -2042,10 +2044,13 @@ static int zfpm_fini(void) zfpm_write_off(); zfpm_read_off(); zfpm_connect_off(); + zfpm_conn_down_off(); zfpm_stop_stats_timer(); hook_unregister(rib_update, zfpm_trigger_update); + hook_unregister(zebra_rmac_update, zfpm_trigger_rmac_update); + return 0; } diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index 5adca14d7171..ba34951e76e7 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -252,20 +252,15 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, rib_dest_t *dest, struct route_entry *re) { struct nexthop *nexthop; - struct rib_table_info *table_info = - rib_table_info(rib_dest_table(dest)); - struct zebra_vrf *zvrf = table_info->zvrf; memset(ri, 0, sizeof(*ri)); ri->prefix = rib_dest_prefix(dest); ri->af = rib_dest_af(dest); - if (zvrf && zvrf->zns) - ri->nlmsg_pid = zvrf->zns->netlink_dplane_out.snl.nl_pid; + ri->nlmsg_pid = pid; ri->nlmsg_type = cmd; - ri->rtm_table = table_info->table_id; ri->rtm_protocol = RTPROT_UNSPEC; /* @@ -280,6 +275,8 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, return 0; } + ri->rtm_table = re->table; + ri->rtm_protocol = netlink_proto_from_route_type(re->type); ri->rtm_type = RTN_UNICAST; ri->metric = &re->metric; diff --git a/zebra/zebra_gr.c b/zebra/zebra_gr.c index 3bd5b51fbc2d..cf2056b7acfd 100644 --- a/zebra/zebra_gr.c +++ b/zebra/zebra_gr.c @@ -13,7 +13,7 @@ #include "lib/prefix.h" #include "lib/command.h" #include "lib/if.h" -#include "lib/thread.h" +#include "frrevent.h" #include "lib/stream.h" #include "lib/memory.h" #include "lib/table.h" @@ -39,11 +39,11 @@ DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_GR, "GR"); * Forward declaration. */ static struct zserv *zebra_gr_find_stale_client(struct zserv *client); -static void zebra_gr_route_stale_delete_timer_expiry(struct thread *thread); +static void zebra_gr_route_stale_delete_timer_expiry(struct event *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); - + struct client_gr_info *info); +static void zebra_gr_delete_stale_route_table_afi(struct event *event); /* * Debug macros. */ @@ -53,7 +53,6 @@ static void zebra_gr_process_client_stale_routes(struct zserv *client, zlog_debug(msg, ##__VA_ARGS__); \ } while (0) - /* * Client connection functions */ @@ -80,13 +79,14 @@ void zebra_gr_stale_client_cleanup(struct list *client_list) /* Cancel the stale timer */ if (info->t_stale_removal != NULL) { - THREAD_OFF(info->t_stale_removal); + EVENT_OFF(info->t_stale_removal); info->t_stale_removal = NULL; + info->do_delete = true; /* Process the stale routes */ - thread_execute( - zrouter.master, - zebra_gr_route_stale_delete_timer_expiry, - info, 1); + event_execute( + zrouter.master, + zebra_gr_route_stale_delete_timer_expiry, + info, 0); } } } @@ -101,6 +101,8 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) info = XCALLOC(MTYPE_ZEBRA_GR, sizeof(struct client_gr_info)); + info->stale_client_ptr = client; + TAILQ_INSERT_TAIL(&(client->gr_info_queue), info, gr_info); return info; } @@ -108,17 +110,18 @@ static struct client_gr_info *zebra_gr_client_info_create(struct zserv *client) /* * A helper function to delete and destroy client info. */ -static void zebra_gr_client_info_delte(struct zserv *client, - struct client_gr_info *info) +static void zebra_gr_client_info_delete(struct zserv *client, + struct client_gr_info *info) { - TAILQ_REMOVE(&(client->gr_info_queue), info, gr_info); + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); - THREAD_OFF(info->t_stale_removal); + TAILQ_REMOVE(&(client->gr_info_queue), info, gr_info); - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); + EVENT_OFF(info->t_stale_removal); - LOG_GR("%s: Instance info is being deleted for client %s", __func__, - zebra_route_string(client->proto)); + LOG_GR("%s: Instance info is being deleted for client %s vrf %s(%u)", + __func__, zebra_route_string(client->proto), VRF_LOGNAME(vrf), + info->vrf_id); /* Delete all the stale routes. */ info->do_delete = true; @@ -154,16 +157,18 @@ int32_t zebra_gr_client_disconnect(struct zserv *client) TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { if (ZEBRA_CLIENT_GR_ENABLED(info->capabilities) && (info->t_stale_removal == NULL)) { - thread_add_timer( + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); + + event_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", + LOG_GR("%s: Client %s vrf %s(%u) Stale timer update to %d", __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, info->stale_removal_time); } } @@ -180,6 +185,7 @@ static void zebra_gr_delete_stale_client(struct client_gr_info *info) { struct client_gr_info *bgp_info; struct zserv *s_client = NULL; + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); s_client = info->stale_client_ptr; @@ -204,8 +210,9 @@ static void zebra_gr_delete_stale_client(struct client_gr_info *info) return; } - LOG_GR("%s: Client %s is being deleted", __func__, - zebra_route_string(s_client->proto)); + LOG_GR("%s: Client %s vrf %s(%u) is being deleted", __func__, + zebra_route_string(s_client->proto), VRF_LOGNAME(vrf), + info->vrf_id); TAILQ_INIT(&(s_client->gr_info_queue)); listnode_delete(zrouter.stale_client_list, s_client); @@ -279,31 +286,65 @@ void zebra_gr_client_reconnect(struct zserv *client) zserv_client_delete(old_client); } +struct zebra_gr_afi_clean { + struct client_gr_info *info; + afi_t afi; + uint8_t proto; + uint8_t instance; + + struct event *t_gac; +}; + /* * Functions to deal with capabilities */ /* - * Update the graceful restart information - * for the client instance. - * This function handles all the capabilities that are received. + * Function to decode and call appropriate functions + * to handle client capabilities. */ -static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) +void zread_client_capabilities(ZAPI_HANDLER_ARGS) { + struct zapi_cap api; struct client_gr_info *info = NULL; + struct stream *s; + struct vrf *vrf; + + 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; + } + + vrf = vrf_lookup_by_id(api.vrf_id); + + /* + * If this ever matters uncomment and add safi to the + * arrays as needed to track + */ + if (api.safi != SAFI_UNICAST) + 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; + } /* 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) + 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) { + switch (api.cap) { case ZEBRA_CLIENT_GR_DISABLE: if (!info) return; @@ -315,7 +356,7 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) if ((info->gr_enable) && (client->gr_instance_count > 0)) client->gr_instance_count--; - zebra_gr_client_info_delte(client, info); + zebra_gr_client_info_delete(client, info); break; case ZEBRA_CLIENT_GR_CAPABILITIES: /* Allocate bgp info */ @@ -326,13 +367,14 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) if (!info->gr_enable) { client->gr_instance_count++; - LOG_GR("%s: Cient %s GR enabled count %d", __func__, - zebra_route_string(client->proto), + LOG_GR("%s: Cient %s vrf %s(%u) GR enabled count %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), api.vrf_id, client->gr_instance_count); - info->capabilities = api->cap; - info->stale_removal_time = api->stale_removal_time; - info->vrf_id = api->vrf_id; + info->capabilities = api.cap; + info->stale_removal_time = api.stale_removal_time; + info->vrf_id = api.vrf_id; info->gr_enable = true; } break; @@ -343,87 +385,52 @@ static void zebra_client_update_info(struct zserv *client, struct zapi_cap *api) /* 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); + LOG_GR("%s: vrf %s(%u) Stale time: %d is now update to: %d", + __func__, VRF_LOGNAME(vrf), info->vrf_id, + info->stale_removal_time, + api.stale_removal_time); - 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; - } -} + if (!info) { + LOG_GR("%s: Client %s route update complete for AFI %d, SAFI %d, no Graceful Restart communication, returning", + __func__, zebra_route_string(client->proto), + api.afi, api.safi); + return; + } + + LOG_GR("%s: Client %s vrf %s(%u) route update complete for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, api.afi, api.safi); + info->route_sync[api.afi] = true; -/* - * 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 + * Schedule for after anything already in the meta Q */ - zebra_client_update_info(client, api); - zebra_gr_process_client_stale_routes(client, api->vrf_id); + rib_add_gr_run(api.afi, api.vrf_id, client->proto, + client->instance); + zebra_gr_process_client_stale_routes(client, info); 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; - } + case ZEBRA_CLIENT_ROUTE_UPDATE_PENDING: + if (!info) { + LOG_GR("%s: Client %s route update pending for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), + api.afi, api.safi); + } else { + LOG_GR("%s: Client %s vrf %s(%u) route update pending for AFI %d, SAFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, api.afi, + api.safi); - /* 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; + info->af_enabled[api.afi] = true; + } + break; } - /* Call the capabilities handler */ - zebra_client_capabilities_handler(client, &api); } - /* * Stale route handling */ @@ -432,38 +439,33 @@ void zread_client_capabilities(ZAPI_HANDLER_ARGS) * Delete all the stale routes that have not been refreshed * post restart. */ -static void zebra_gr_route_stale_delete_timer_expiry(struct thread *thread) +static void zebra_gr_route_stale_delete_timer_expiry(struct event *thread) { - struct client_gr_info *info; + struct client_gr_info *info = EVENT_ARG(thread); int32_t cnt = 0; struct zserv *client; + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); - 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); /* Restart the timer */ if (cnt > 0) { - LOG_GR("%s: Client %s processed %d routes. Start timer again", - __func__, zebra_route_string(client->proto), cnt); + LOG_GR("%s: Client %s vrf %s(%u) processed %d routes. Start timer again", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, cnt); - thread_add_timer(zrouter.master, - zebra_gr_route_stale_delete_timer_expiry, info, - ZEBRA_DEFAULT_STALE_UPDATE_DELAY, - &info->t_stale_removal); + event_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 stale routes processed", __func__, - zebra_route_string(client->proto)); + LOG_GR("%s: Client %s vrf %s(%u) all stale routes processed", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id); - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); - info->current_afi = 0; zebra_gr_delete_stale_client(info); } } @@ -472,14 +474,13 @@ static void zebra_gr_route_stale_delete_timer_expiry(struct thread *thread) /* * Function to process to check if route entry is stale * or has been updated. + * + * Returns true when a node is deleted else false */ -static void zebra_gr_process_route_entry(struct zserv *client, +static bool zebra_gr_process_route_entry(struct zserv *client, struct route_node *rn, struct route_entry *re) { - 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) @@ -487,7 +488,62 @@ static void zebra_gr_process_route_entry(struct zserv *client, __func__, zebra_route_string(client->proto), &rn->p); rib_delnode(rn, re); + + return true; + } + + return false; +} + +static void zebra_gr_delete_stale_route_table_afi(struct event *event) +{ + struct zebra_gr_afi_clean *gac = EVENT_ARG(event); + struct route_table *table; + struct route_node *rn; + struct route_entry *re, *next; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(gac->info->vrf_id); + int32_t n = 0; + + if (!zvrf) + goto done; + + table = zvrf->table[gac->afi][SAFI_UNICAST]; + if (!table) + goto done; + + for (rn = route_top(table); 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 == gac->proto && + re->instance == gac->instance && + zebra_gr_process_route_entry( + gac->info->stale_client_ptr, 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) && + (gac->info->do_delete == false)) { + event_add_timer( + zrouter.master, + zebra_gr_delete_stale_route_table_afi, + gac, ZEBRA_DEFAULT_STALE_UPDATE_DELAY, + &gac->t_gac); + } + } } + +done: + XFREE(MTYPE_ZEBRA_GR, gac); } /* @@ -498,90 +554,32 @@ static void zebra_gr_process_route_entry(struct zserv *client, 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; + afi_t 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__); + LOG_GR("%s: Stale client %s(%u) not present", __func__, + zvrf->vrf->name, zvrf->vrf->vrf_id); 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)); + LOG_GR("%s: Client %s %s(%u) stale routes are being deleted", __func__, + zebra_route_string(proto), zvrf->vrf->name, zvrf->vrf->vrf_id); /* Process routes for all AFI */ - for (afi = curr_afi; afi < AFI_MAX; afi++) { - table = zvrf->table[afi][SAFI_UNICAST]; + for (afi = AFI_IP; afi < AFI_MAX; afi++) { - 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_ZEBRA_GR, - sizeof(struct prefix)); - prefix_copy( - info->current_prefix, - &rn->p); - return n; - } - } - } - } /* - * Reset the current prefix to indicate processing completion - * of the current AFI + * Schedule for immediately after anything in the + * meta-Q */ - XFREE(MTYPE_ZEBRA_GR, info->current_prefix); + rib_add_gr_run(afi, info->vrf_id, proto, instance); } return 0; } @@ -592,23 +590,15 @@ static int32_t zebra_gr_delete_stale_route(struct client_gr_info *info, */ 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; + zvrf = zebra_vrf_lookup_by_id(info->vrf_id); if (zvrf == NULL) { - LOG_GR("%s: Invalid VRF entry %d", __func__, info->vrf_id); + LOG_GR("%s: Invalid VRF entry %u", __func__, info->vrf_id); return -1; } @@ -621,44 +611,63 @@ static int32_t zebra_gr_delete_stale_routes(struct client_gr_info *info) * 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) { - 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 */ - FOREACH_AFI_SAFI_NSF (afi, 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; - } + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + if (info->af_enabled[afi] && !info->route_sync[afi]) { + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); + + LOG_GR("%s: Client %s vrf: %s(%u) route update not completed for AFI %d", + __func__, zebra_route_string(client->proto), + VRF_LOGNAME(vrf), info->vrf_id, afi); + return; } } /* * Route update completed for all AFI, SAFI - * Cancel the stale timer and process the routes + * Cancel the stale timer, routes are already being processed */ if (info->t_stale_removal) { - LOG_GR("%s: Client %s canceled stale delete timer vrf %d", + struct vrf *vrf = vrf_lookup_by_id(info->vrf_id); + + LOG_GR("%s: Client %s canceled stale delete timer vrf %s(%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); + VRF_LOGNAME(vrf), info->vrf_id); + EVENT_OFF(info->t_stale_removal); } } + +void zebra_gr_process_client(afi_t afi, vrf_id_t vrf_id, uint8_t proto, + uint8_t instance) +{ + struct zserv *client = zserv_find_client(proto, instance); + struct client_gr_info *info = NULL; + struct zebra_gr_afi_clean *gac; + + if (client == NULL) + return; + + TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { + if (info->vrf_id == vrf_id) + break; + } + + if (info == NULL) + return; + + gac = XCALLOC(MTYPE_ZEBRA_GR, sizeof(*gac)); + gac->info = info; + gac->afi = afi; + gac->proto = proto; + gac->instance = instance; + + event_add_event(zrouter.master, zebra_gr_delete_stale_route_table_afi, + gac, 0, &gac->t_gac); +} diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 2eea772f9f5c..39c1319f3155 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -244,8 +244,7 @@ void zebra_l2if_update_bond(struct interface *ifp, bool add) * map slaves (if any) to the bridge. */ void zebra_l2_bridge_add_update(struct interface *ifp, - struct zebra_l2info_bridge *bridge_info, - int add) + const struct zebra_l2info_bridge *bridge_info) { struct zebra_if *zif; struct zebra_l2_bridge_if *br; @@ -284,7 +283,7 @@ void zebra_l2if_update_bridge(struct interface *ifp, uint8_t chgflags) * VLAN Id and this cannot change. */ void zebra_l2_vlanif_update(struct interface *ifp, - struct zebra_l2info_vlan *vlan_info) + const struct zebra_l2info_vlan *vlan_info) { struct zebra_if *zif; @@ -301,7 +300,7 @@ void zebra_l2_vlanif_update(struct interface *ifp, * clients about GRE information. */ void zebra_l2_greif_add_update(struct interface *ifp, - struct zebra_l2info_gre *gre_info, int add) + const struct zebra_l2info_gre *gre_info, int add) { struct zebra_if *zif; struct in_addr old_vtep_ip; @@ -328,7 +327,8 @@ void zebra_l2_greif_add_update(struct interface *ifp, * IP and VLAN mapping, but the latter is handled separately. */ void zebra_l2_vxlanif_add_update(struct interface *ifp, - struct zebra_l2info_vxlan *vxlan_info, int add) + const struct zebra_l2info_vxlan *vxlan_info, + int add) { struct zebra_if *zif; uint16_t chgflags = 0; diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index 3be002656ac8..588917f4c036 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -80,6 +80,12 @@ struct zebra_vxlan_vni { vni_t vni; /* VNI */ vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */ struct in_addr mcast_grp; + uint16_t flags; +}; + +struct zebra_vxlan_vni_array { + uint16_t count; + struct zebra_vxlan_vni vnis[0]; }; enum { @@ -159,18 +165,19 @@ 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_bridge_add_update(struct interface *ifp, - struct zebra_l2info_bridge *bridge_info, - int add); +extern void +zebra_l2_bridge_add_update(struct interface *ifp, + const struct zebra_l2info_bridge *bridge_info); extern void zebra_l2_bridge_del(struct interface *ifp); extern void zebra_l2_vlanif_update(struct interface *ifp, - struct zebra_l2info_vlan *vlan_info); + const struct zebra_l2info_vlan *vlan_info); extern void zebra_l2_greif_add_update(struct interface *ifp, - struct zebra_l2info_gre *vxlan_info, + const struct zebra_l2info_gre *vxlan_info, int add); -extern void zebra_l2_vxlanif_add_update(struct interface *ifp, - struct zebra_l2info_vxlan *vxlan_info, - int add); +extern void +zebra_l2_vxlanif_add_update(struct interface *ifp, + const struct zebra_l2info_vxlan *vxlan_info, + int add); extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_greif_del(struct interface *ifp); diff --git a/zebra/zebra_l2_bridge_if.c b/zebra/zebra_l2_bridge_if.c index b85d39bcd6d9..00450ddd36a0 100644 --- a/zebra/zebra_l2_bridge_if.c +++ b/zebra/zebra_l2_bridge_if.c @@ -142,10 +142,7 @@ static void *zebra_l2_bridge_vlan_alloc(void *p) static void zebra_l2_bridge_vlan_table_destroy(struct hash *vlan_table) { - if (vlan_table) { - hash_clean(vlan_table, zebra_l2_bridge_vlan_free); - hash_free(vlan_table); - } + hash_clean_and_free(&vlan_table, zebra_l2_bridge_vlan_free); } static struct hash *zebra_l2_bridge_vlan_table_create(void) diff --git a/zebra/zebra_mlag.c b/zebra/zebra_mlag.c index b4a0c575a25e..7715eab0a8d6 100644 --- a/zebra/zebra_mlag.c +++ b/zebra/zebra_mlag.c @@ -36,8 +36,8 @@ uint8_t mlag_rd_buffer[ZEBRA_MLAG_BUF_LIMIT]; static bool test_mlag_in_progress; static int zebra_mlag_signal_write_thread(void); -static void zebra_mlag_terminate_pthread(struct thread *event); -static void zebra_mlag_post_data_from_main_thread(struct thread *thread); +static void zebra_mlag_terminate_pthread(struct event *event); +static void zebra_mlag_post_data_from_main_thread(struct event *thread); static void zebra_mlag_publish_process_state(struct zserv *client, zebra_message_types_t msg_type); @@ -114,8 +114,8 @@ void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len) * 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); + event_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread, + s, 0, NULL); } /**********************End of MLAG Interaction********************************/ @@ -132,7 +132,7 @@ void zebra_mlag_process_mlag_data(uint8_t *data, uint32_t len) * This thread reads the clients data from the Global queue and encodes with * protobuf and pass on to the MLAG socket. */ -static void zebra_mlag_client_msg_handler(struct thread *event) +static void zebra_mlag_client_msg_handler(struct event *event) { struct stream *s; uint32_t wr_count = 0; @@ -173,9 +173,9 @@ static void zebra_mlag_client_msg_handler(struct thread *event) * main thread. */ if (msg_type == MLAG_DEREGISTER) { - thread_add_event(zrouter.master, - zebra_mlag_terminate_pthread, - NULL, 0, NULL); + event_add_event(zrouter.master, + zebra_mlag_terminate_pthread, + NULL, 0, NULL); } } @@ -237,9 +237,9 @@ static int zebra_mlag_signal_write_thread(void) * 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); + event_add_event(zrouter.mlag_info.th_master, + zebra_mlag_client_msg_handler, NULL, 0, + &zrouter.mlag_info.t_write); return 0; } @@ -279,8 +279,8 @@ static void zebra_mlag_publish_process_state(struct zserv *client, 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); + event_add_event(zrouter.master, zebra_mlag_post_data_from_main_thread, + s, 0, NULL); } /**************************End of Multi-entrant Apis**************************/ @@ -292,9 +292,9 @@ static void zebra_mlag_publish_process_state(struct zserv *client, * 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 void zebra_mlag_post_data_from_main_thread(struct thread *thread) +static void zebra_mlag_post_data_from_main_thread(struct event *thread) { - struct stream *s = THREAD_ARG(thread); + struct stream *s = EVENT_ARG(thread); struct stream *zebra_s = NULL; struct listnode *node; struct zserv *client; @@ -338,8 +338,6 @@ static void zebra_mlag_post_data_from_main_thread(struct thread *thread) } } - stream_free(s); - return; stream_failure: stream_free(s); if (zebra_s) @@ -376,7 +374,7 @@ static void zebra_mlag_spawn_pthread(void) * all clients are un-registered for MLAG Updates, terminate the * MLAG write thread */ -static void zebra_mlag_terminate_pthread(struct thread *event) +static void zebra_mlag_terminate_pthread(struct event *event) { if (IS_ZEBRA_DEBUG_MLAG) zlog_debug("Zebra MLAG write thread terminate called"); diff --git a/zebra/zebra_mlag_private.c b/zebra/zebra_mlag_private.c index 334eb6dc4695..f348c50755c1 100644 --- a/zebra/zebra_mlag_private.c +++ b/zebra/zebra_mlag_private.c @@ -12,7 +12,7 @@ #include "hook.h" #include "module.h" -#include "thread.h" +#include "frrevent.h" #include "frr_pthread.h" #include "libfrr.h" #include "lib/version.h" @@ -32,11 +32,11 @@ * */ -static struct thread_master *zmlag_master; +static struct event_loop *zmlag_master; static int mlag_socket; -static void zebra_mlag_connect(struct thread *thread); -static void zebra_mlag_read(struct thread *thread); +static void zebra_mlag_connect(struct event *thread); +static void zebra_mlag_read(struct event *thread); /* * Write the data to MLAGD @@ -55,11 +55,11 @@ static int zebra_mlag_private_write_data(uint8_t *data, uint32_t len) static void zebra_mlag_sched_read(void) { - thread_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, - &zrouter.mlag_info.t_read); + event_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, + &zrouter.mlag_info.t_read); } -static void zebra_mlag_read(struct thread *thread) +static void zebra_mlag_read(struct event *thread) { static uint32_t mlag_rd_buf_offset; uint32_t *msglen; @@ -151,7 +151,7 @@ static void zebra_mlag_read(struct thread *thread) zebra_mlag_sched_read(); } -static void zebra_mlag_connect(struct thread *thread) +static void zebra_mlag_connect(struct event *thread) { struct sockaddr_un svr = {0}; @@ -173,8 +173,8 @@ static void zebra_mlag_connect(struct thread *thread) 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); + event_add_timer(zmlag_master, zebra_mlag_connect, NULL, 10, + &zrouter.mlag_info.t_read); return; } @@ -184,8 +184,8 @@ static void zebra_mlag_connect(struct thread *thread) 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); + event_add_read(zmlag_master, zebra_mlag_read, NULL, mlag_socket, + &zrouter.mlag_info.t_read); /* * Connection is established with MLAGD, post to clients */ @@ -197,8 +197,8 @@ static void zebra_mlag_connect(struct thread *thread) */ static int zebra_mlag_private_monitor_state(void) { - thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, - &zrouter.mlag_info.t_read); + event_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, + &zrouter.mlag_info.t_read); return 0; } @@ -225,8 +225,8 @@ static int zebra_mlag_private_open_channel(void) /* * Connect only if any clients are showing interest */ - thread_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, - &zrouter.mlag_info.t_read); + event_add_event(zmlag_master, zebra_mlag_connect, NULL, 0, + &zrouter.mlag_info.t_read); } return 0; } diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index d25505839c2a..ef109774d97d 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -13,7 +13,7 @@ #include "log.h" #include "sockunion.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "workqueue.h" #include "prefix.h" #include "routemap.h" @@ -97,8 +97,8 @@ static struct zebra_nhlfe *nhlfe_find(struct nhlfe_list_head *list, static struct zebra_nhlfe * nhlfe_add(struct zebra_lsp *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); + ifindex_t ifindex, vrf_id_t vrf_id, uint8_t num_labels, + const mpls_label_t *labels, bool is_backup); static int nhlfe_del(struct zebra_nhlfe *nhlfe); static void nhlfe_free(struct zebra_nhlfe *nhlfe); static void nhlfe_out_label_update(struct zebra_nhlfe *nhlfe, @@ -212,11 +212,11 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, changed++; } else { /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type, - &nexthop->gate, nexthop->ifindex, - nexthop->nh_label->num_labels, - nexthop->nh_label->label, - false /*backup*/); + nhlfe = nhlfe_add( + lsp, lsp_type, nexthop->type, &nexthop->gate, + nexthop->ifindex, nexthop->vrf_id, + nexthop->nh_label->num_labels, + nexthop->nh_label->label, false /*backup*/); if (!nhlfe) return -1; @@ -887,7 +887,7 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) struct zebra_lsp *lsp; struct zebra_nhlfe *oldbest, *newbest; char buf[BUFSIZ], buf2[BUFSIZ]; - struct zebra_vrf *zvrf = vrf_info_lookup(VRF_DEFAULT); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); enum zebra_dplane_result res; lsp = (struct zebra_lsp *)data; @@ -1028,7 +1028,7 @@ static void lsp_processq_del(struct work_queue *wq, void *data) if (zebra_router_in_shutdown()) return; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); assert(zvrf); lsp_table = zvrf->lsp_table; @@ -1236,6 +1236,7 @@ static int nhlfe_nhop_match(struct zebra_nhlfe *nhlfe, /* * Locate NHLFE that matches with passed info. + * TODO: handle vrf_id if vrf backend is netns based */ static struct zebra_nhlfe *nhlfe_find(struct nhlfe_list_head *list, enum lsp_types_t lsp_type, @@ -1261,7 +1262,8 @@ static struct zebra_nhlfe *nhlfe_find(struct nhlfe_list_head *list, static struct zebra_nhlfe * nhlfe_alloc(struct zebra_lsp *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) + ifindex_t ifindex, vrf_id_t vrf_id, uint8_t num_labels, + const mpls_label_t *labels) { struct zebra_nhlfe *nhlfe; struct nexthop *nexthop; @@ -1278,7 +1280,7 @@ nhlfe_alloc(struct zebra_lsp *lsp, enum lsp_types_t lsp_type, nexthop_add_labels(nexthop, lsp_type, num_labels, labels); - nexthop->vrf_id = VRF_DEFAULT; + nexthop->vrf_id = vrf_id; nexthop->type = gtype; switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -1313,29 +1315,20 @@ nhlfe_alloc(struct zebra_lsp *lsp, enum lsp_types_t lsp_type, * Add primary or backup NHLFE. Base entry must have been created and * duplicate check done. */ -static struct zebra_nhlfe *nhlfe_add(struct zebra_lsp *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) +static struct zebra_nhlfe * +nhlfe_add(struct zebra_lsp *lsp, enum lsp_types_t lsp_type, + enum nexthop_types_t gtype, const union g_addr *gate, + ifindex_t ifindex, vrf_id_t vrf_id, uint8_t num_labels, + const mpls_label_t *labels, bool is_backup) { struct zebra_nhlfe *nhlfe; if (!lsp) return NULL; - /* Must have labels */ - if (num_labels == 0 || labels == NULL) { - if (IS_ZEBRA_DEBUG_MPLS) - zlog_debug("%s: invalid nexthop: no labels", __func__); - - return NULL; - } - /* Allocate new object */ - nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, - labels); + nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, vrf_id, + num_labels, labels); if (!nhlfe) return NULL; @@ -1510,16 +1503,18 @@ static json_object *nhlfe_json(struct zebra_nhlfe *nhlfe) 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])); - + if (nexthop->nh_label) { + 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)) @@ -1530,6 +1525,10 @@ static json_object *nhlfe_json(struct zebra_nhlfe *nhlfe) case NEXTHOP_TYPE_IPV4_IFINDEX: json_object_string_addf(json_nhlfe, "nexthop", "%pI4", &nexthop->gate.ipv4); + if (nexthop->ifindex) + json_object_string_add(json_nhlfe, "interface", + ifindex2ifname(nexthop->ifindex, + nexthop->vrf_id)); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -1776,7 +1775,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: /* Look for zebra LSP object */ - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (zvrf == NULL) break; @@ -1884,6 +1883,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: break; } /* Switch */ @@ -2092,7 +2092,7 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) dplane_ctx_get_in_label(ctx)); /* Look for zebra LSP object */ - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (zvrf == NULL) return; @@ -2242,8 +2242,8 @@ zebra_mpls_lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t lsp_type, 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*/); + return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, VRF_DEFAULT, + num_labels, out_labels, false /*backup*/); } /* @@ -2257,8 +2257,8 @@ struct zebra_nhlfe *zebra_mpls_lsp_add_backup_nhlfe( 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, true); + return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, VRF_DEFAULT, + num_labels, out_labels, true); } /* @@ -2270,12 +2270,10 @@ struct zebra_nhlfe *zebra_mpls_lsp_add_nh(struct zebra_lsp *lsp, { struct zebra_nhlfe *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*/); + nhlfe = nhlfe_add( + lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, nh->vrf_id, + nh->nh_label ? nh->nh_label->num_labels : 0, + nh->nh_label ? nh->nh_label->label : NULL, false /*backup*/); return nhlfe; } @@ -2290,12 +2288,10 @@ struct zebra_nhlfe *zebra_mpls_lsp_add_backup_nh(struct zebra_lsp *lsp, { struct zebra_nhlfe *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); + nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, + nh->vrf_id, + nh->nh_label ? nh->nh_label->num_labels : 0, + nh->nh_label ? nh->nh_label->label : NULL, true); return nhlfe; } @@ -2467,7 +2463,7 @@ int zebra_mpls_fec_unregister(struct zebra_vrf *zvrf, struct prefix *p, */ static int zebra_mpls_cleanup_fecs_for_client(struct zserv *client) { - struct zebra_vrf *zvrf = vrf_info_lookup(VRF_DEFAULT); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); struct route_node *rn; struct zebra_fec *fec; struct listnode *node; @@ -3113,7 +3109,7 @@ static struct zebra_nhlfe * lsp_add_nhlfe(struct zebra_lsp *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) + ifindex_t ifindex, vrf_id_t vrf_id, bool is_backup) { struct zebra_nhlfe *nhlfe; char buf[MPLS_LABEL_STRLEN]; @@ -3133,13 +3129,18 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, struct nexthop *nh = nhlfe->nexthop; assert(nh); - assert(nh->nh_label); /* Clear deleted flag (in case it was set) */ UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); - if (nh->nh_label->num_labels == num_out_labels - && !memcmp(nh->nh_label->label, out_labels, - sizeof(mpls_label_t) * num_out_labels)) + + if (!nh->nh_label || num_out_labels == 0) + /* No change */ + return nhlfe; + + if (nh->nh_label && + 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 nhlfe; @@ -3160,7 +3161,7 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, } /* Update out label(s), trigger processing. */ - if (nh->nh_label->num_labels == num_out_labels) + if (nh->nh_label && nh->nh_label->num_labels == num_out_labels) memcpy(nh->nh_label->label, out_labels, sizeof(mpls_label_t) * num_out_labels); else { @@ -3170,7 +3171,7 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, } } else { /* Add LSP entry to this nexthop */ - nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, + nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, vrf_id, num_out_labels, out_labels, is_backup); if (!nhlfe) return NULL; @@ -3179,8 +3180,11 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, char buf2[MPLS_LABEL_STRLEN]; nhlfe2str(nhlfe, buf, sizeof(buf)); - mpls_label2str(num_out_labels, out_labels, buf2, - sizeof(buf2), 0, 0); + if (num_out_labels) + mpls_label2str(num_out_labels, out_labels, buf2, + sizeof(buf2), 0, 0); + else + snprintf(buf2, sizeof(buf2), "-"); zlog_debug("Add LSP in-label %u type %d %snexthop %s out-label(s) %s", lsp->ile.in_label, type, backup_str, buf, @@ -3199,6 +3203,8 @@ lsp_add_nhlfe(struct zebra_lsp *lsp, enum lsp_types_t type, /* * Install an LSP and forwarding entry; used primarily * from vrf zapi message processing. + * TODO: handle vrf_id parameter when mpls API extends to interface or SRTE + * changes */ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, uint8_t num_out_labels, @@ -3220,7 +3226,7 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, lsp = hash_get(lsp_table, &tmp_ile, lsp_alloc); nhlfe = lsp_add_nhlfe(lsp, type, num_out_labels, out_labels, gtype, - gate, ifindex, false /*backup*/); + gate, ifindex, VRF_DEFAULT, false /*backup*/); if (nhlfe == NULL) return -1; @@ -3239,8 +3245,8 @@ static int lsp_znh_install(struct zebra_lsp *lsp, enum lsp_types_t type, { struct zebra_nhlfe *nhlfe; - nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, - znh->type, &znh->gate, znh->ifindex, + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, znh->type, + &znh->gate, znh->ifindex, znh->vrf_id, false /*backup*/); if (nhlfe == NULL) return -1; @@ -3277,9 +3283,9 @@ static int lsp_backup_znh_install(struct zebra_lsp *lsp, enum lsp_types_t type, { struct zebra_nhlfe *nhlfe; - nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, - znh->labels, znh->type, &znh->gate, - znh->ifindex, true /*backup*/); + nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, znh->type, + &znh->gate, znh->ifindex, znh->vrf_id, + true /*backup*/); if (nhlfe == NULL) { if (IS_ZEBRA_DEBUG_MPLS) zlog_debug("%s: unable to add backup nhlfe, label: %u", @@ -3610,8 +3616,8 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, } else { /* Add static LSP entry to this nexthop */ - nhlfe = nhlfe_add(lsp, ZEBRA_LSP_STATIC, gtype, gate, - ifindex, 1, &out_label, false /*backup*/); + nhlfe = nhlfe_add(lsp, ZEBRA_LSP_STATIC, gtype, gate, ifindex, + VRF_DEFAULT, 1, &out_label, false /*backup*/); if (!nhlfe) return -1; @@ -3730,14 +3736,20 @@ void zebra_mpls_print_lsp(struct vty *vty, struct zebra_vrf *zvrf, /* Lookup table. */ lsp_table = zvrf->lsp_table; - if (!lsp_table) + if (!lsp_table) { + if (use_json) + vty_out(vty, "{}\n"); return; + } /* If entry is not present, exit. */ tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); - if (!lsp) + if (!lsp) { + if (use_json) + vty_out(vty, "{}\n"); return; + } if (use_json) { json = lsp_json(lsp); @@ -3814,7 +3826,8 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, break; } - if (nexthop->type != NEXTHOP_TYPE_IFINDEX) + if (nexthop->type != NEXTHOP_TYPE_IFINDEX && + nexthop->nh_label) out_label_str = mpls_label2str( nexthop->nh_label->num_labels, &nexthop->nh_label->label[0], @@ -4052,10 +4065,8 @@ static void lsp_table_free(void *p) void zebra_mpls_close_tables(struct zebra_vrf *zvrf) { hash_iterate(zvrf->lsp_table, lsp_uninstall_from_kernel, NULL); - hash_clean(zvrf->lsp_table, lsp_table_free); - hash_free(zvrf->lsp_table); - hash_clean(zvrf->slsp_table, lsp_table_free); - hash_free(zvrf->slsp_table); + hash_clean_and_free(&zvrf->lsp_table, lsp_table_free); + hash_clean_and_free(&zvrf->slsp_table, lsp_table_free); route_table_finish(zvrf->fec_table[AFI_IP]); route_table_finish(zvrf->fec_table[AFI_IP6]); } diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index ee6f7045f558..5015f2ed1adf 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -291,6 +291,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: return -1; } @@ -502,6 +503,7 @@ enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: break; } diff --git a/zebra/zebra_mpls_vty.c b/zebra/zebra_mpls_vty.c index 7e3cdd738e0a..6b8859e0cae3 100644 --- a/zebra/zebra_mpls_vty.c +++ b/zebra/zebra_mpls_vty.c @@ -41,7 +41,7 @@ static int zebra_mpls_transit_lsp(struct vty *vty, int add_cmd, return CMD_WARNING_CONFIG_FAILED; } - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) { vty_out(vty, "%% Default VRF does not exist\n"); return CMD_WARNING_CONFIG_FAILED; @@ -185,7 +185,7 @@ static int zebra_mpls_bind(struct vty *vty, int add_cmd, const char *prefix, uint32_t label; int ret; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) { vty_out(vty, "%% Default VRF does not exist\n"); return CMD_WARNING_CONFIG_FAILED; @@ -273,7 +273,7 @@ static int zebra_mpls_config(struct vty *vty) int write = 0; struct zebra_vrf *zvrf; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return 0; @@ -296,7 +296,7 @@ DEFUN (show_mpls_fec, struct prefix p; int ret; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return 0; @@ -326,7 +326,7 @@ DEFUN (show_mpls_table, struct zebra_vrf *zvrf; bool uj = use_json(argc, argv); - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); zebra_mpls_print_lsp_table(vty, zvrf, uj); return CMD_SUCCESS; } @@ -344,7 +344,7 @@ DEFUN (show_mpls_table_lsp, struct zebra_vrf *zvrf; bool uj = use_json(argc, argv); - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); label = atoi(argv[3]->arg); zebra_mpls_print_lsp(vty, zvrf, label, uj); return CMD_SUCCESS; diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index cdba0c21aba9..4260d29c43a5 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -17,7 +17,7 @@ #include <sys/inotify.h> #include <sys/stat.h> -#include "thread.h" +#include "frrevent.h" #include "ns.h" #include "command.h" #include "memory.h" @@ -41,18 +41,18 @@ #define ZEBRA_NS_POLLING_MAX_RETRIES 200 DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo"); -static struct thread *zebra_netns_notify_current; +static struct event *zebra_netns_notify_current; struct zebra_netns_info { const char *netnspath; unsigned int retries; }; -static void zebra_ns_ready_read(struct thread *t); +static void zebra_ns_ready_read(struct event *t); static void zebra_ns_notify_create_context_from_entry_name(const char *name); static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, int stop_retry); -static void zebra_ns_notify_read(struct thread *t); +static void zebra_ns_notify_read(struct event *t); static struct vrf *vrf_handler_create(struct vty *vty, const char *vrfname) { @@ -133,9 +133,9 @@ static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, XFREE(MTYPE_NETNS_MISC, zns_info); return 0; } - thread_add_timer_msec(zrouter.master, zebra_ns_ready_read, - (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC, - NULL); + event_add_timer_msec(zrouter.master, zebra_ns_ready_read, + (void *)zns_info, ZEBRA_NS_POLLING_INTERVAL_MSEC, + NULL); return 0; } @@ -165,6 +165,17 @@ static int zebra_ns_delete(char *name) if_down(ifp); } + if (IS_ZEBRA_IF_BOND(ifp)) + zebra_l2if_update_bond(ifp, false); + if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) + zebra_l2if_update_bond_slave(ifp, IFINDEX_INTERNAL, + false); + /* Special handling for bridge or VxLAN interfaces. */ + if (IS_ZEBRA_IF_BRIDGE(ifp)) + zebra_l2_bridge_del(ifp); + else if (IS_ZEBRA_IF_VXLAN(ifp)) + zebra_l2_vxlanif_del(ifp); + UNSET_FLAG(ifp->flags, IFF_UP); if_delete_update(&ifp); } @@ -218,9 +229,9 @@ static bool zebra_ns_notify_is_default_netns(const char *name) return false; } -static void zebra_ns_ready_read(struct thread *t) +static void zebra_ns_ready_read(struct event *t) { - struct zebra_netns_info *zns_info = THREAD_ARG(t); + struct zebra_netns_info *zns_info = EVENT_ARG(t); const char *netnspath; int err, stop_retry = 0; @@ -269,16 +280,16 @@ static void zebra_ns_ready_read(struct thread *t) zebra_ns_continue_read(zns_info, 1); } -static void zebra_ns_notify_read(struct thread *t) +static void zebra_ns_notify_read(struct event *t) { - int fd_monitor = THREAD_FD(t); + int fd_monitor = EVENT_FD(t); struct inotify_event *event; char buf[BUFSIZ]; ssize_t len; char event_name[NAME_MAX + 1]; - thread_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, - &zebra_netns_notify_current); + event_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, + &zebra_netns_notify_current); len = read(fd_monitor, buf, sizeof(buf)); if (len < 0) { flog_err_sys(EC_ZEBRA_NS_NOTIFY_READ, @@ -350,8 +361,8 @@ static void zebra_ns_notify_read(struct thread *t) sizeof(struct zebra_netns_info)); netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES; netnsinfo->netnspath = netnspath; - thread_add_timer_msec(zrouter.master, zebra_ns_ready_read, - (void *)netnsinfo, 0, NULL); + event_add_timer_msec(zrouter.master, zebra_ns_ready_read, + (void *)netnsinfo, 0, NULL); } } @@ -416,8 +427,8 @@ void zebra_ns_notify_init(void) "NS notify watch: failed to add watch (%s)", safe_strerror(errno)); } - thread_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, - &zebra_netns_notify_current); + event_add_read(zrouter.master, zebra_ns_notify_read, NULL, fd_monitor, + &zebra_netns_notify_current); } void zebra_ns_notify_close(void) @@ -431,7 +442,7 @@ void zebra_ns_notify_close(void) fd = zebra_netns_notify_current->u.fd; if (zebra_netns_notify_current->master != NULL) - THREAD_OFF(zebra_netns_notify_current); + EVENT_OFF(zebra_netns_notify_current); /* auto-removal of notify items */ if (fd > 0) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 654cf50d49c2..bb507893b0e5 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1125,13 +1125,23 @@ static void zebra_nhg_handle_uninstall(struct nhg_hash_entry *nhe) zebra_nhg_free(nhe); } -static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe) +static void zebra_nhg_handle_install(struct nhg_hash_entry *nhe, bool install) { /* 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) + frr_each_safe (nhg_connected_tree, &nhe->nhg_dependents, rb_node_dep) { zebra_nhg_set_valid(rb_node_dep->nhe); + /* install dependent NHG into kernel */ + if (install) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s nh id %u (flags 0x%x) associated dependent NHG %pNG install", + __func__, nhe->id, nhe->flags, + rb_node_dep->nhe); + zebra_nhg_install_kernel(rb_node_dep->nhe); + } + } } /* @@ -1619,7 +1629,7 @@ void zebra_nhg_free(struct nhg_hash_entry *nhe) nhe->nhg.nexthop); } - THREAD_OFF(nhe->timer); + EVENT_OFF(nhe->timer); zebra_nhg_free_members(nhe); @@ -1644,7 +1654,7 @@ void zebra_nhg_hash_free(void *p) nhe->nhg.nexthop); } - THREAD_OFF(nhe->timer); + EVENT_OFF(nhe->timer); nexthops_free(nhe->nhg.nexthop); @@ -1683,9 +1693,9 @@ void zebra_nhg_hash_free_zero_id(struct hash_bucket *b, void *arg) } } -static void zebra_nhg_timer(struct thread *thread) +static void zebra_nhg_timer(struct event *thread) { - struct nhg_hash_entry *nhe = THREAD_ARG(thread); + struct nhg_hash_entry *nhe = EVENT_ARG(thread); if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("Nexthop Timer for nhe: %pNG", nhe); @@ -1707,8 +1717,8 @@ void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe) !CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND)) { nhe->refcnt = 1; SET_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND); - thread_add_timer(zrouter.master, zebra_nhg_timer, nhe, - zrouter.nhg_keep, &nhe->timer); + event_add_timer(zrouter.master, zebra_nhg_timer, nhe, + zrouter.nhg_keep, &nhe->timer); return; } @@ -1727,8 +1737,8 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) nhe->refcnt++; - if (thread_is_scheduled(nhe->timer)) { - THREAD_OFF(nhe->timer); + if (event_is_scheduled(nhe->timer)) { + EVENT_OFF(nhe->timer); nhe->refcnt--; UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND); } @@ -2171,11 +2181,7 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, 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 the interface exists and its operative, it's active */ if (ifp && (if_is_operative(ifp))) return 1; else @@ -2386,7 +2392,10 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, continue; } - if ((match->type == ZEBRA_ROUTE_CONNECT) || + /* If the candidate match's type is considered "connected", + * we consider it first. + */ + if (RIB_CONNECTED_ROUTE(match) || (RIB_SYSTEM_ROUTE(match) && RSYSTEM_ROUTE(type))) { match = zebra_nhg_connected_ifindex(rn, match, nexthop->ifindex); @@ -2407,6 +2416,10 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, return 0; } + /* NHRP special case: need to indicate onlink */ + if (match->type == ZEBRA_ROUTE_NHRP) + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK); + if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug( "%s: CONNECT match %p (%pNG), newhop %pNHv", @@ -2576,6 +2589,8 @@ static unsigned nexthop_active_check(struct route_node *rn, if (IS_ZEBRA_DEBUG_NHG_DETAIL) zlog_debug("%s: re %p, nexthop %pNHv", __func__, re, nexthop); + vrf_id = zvrf_id(rib_dest_vrf(rib_dest_from_rnode(rn))); + /* * If this is a kernel route, then if the interface is *up* then * by golly gee whiz it's a good route. @@ -2585,13 +2600,12 @@ static unsigned nexthop_active_check(struct route_node *rn, ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); - if (ifp && (if_is_operative(ifp) || if_is_up(ifp))) { + if (ifp && ifp->vrf->vrf_id == vrf_id && if_is_up(ifp)) { SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); goto skip_check; } } - vrf_id = zvrf_id(rib_dest_vrf(rib_dest_from_rnode(rn))); switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, @@ -3080,6 +3094,12 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) /* Resolve it first */ nhe = zebra_nhg_resolve(nhe); + if (zebra_nhg_set_valid_if_active(nhe)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: valid flag set for nh %pNG", __func__, + 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); @@ -3106,7 +3126,7 @@ void zebra_nhg_install_kernel(struct nhg_hash_entry *nhe) break; case ZEBRA_DPLANE_REQUEST_SUCCESS: SET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); - zebra_nhg_handle_install(nhe); + zebra_nhg_handle_install(nhe, false); break; } } @@ -3180,7 +3200,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) 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); + zebra_nhg_handle_install(nhe, true); /* If daemon nhg, send it an update */ if (PROTO_OWNED(nhe)) @@ -3254,6 +3274,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: break; } } @@ -3507,7 +3528,7 @@ struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type, /* Dont call the dec API, we dont want to uninstall the ID */ old->refcnt = 0; - THREAD_OFF(old->timer); + EVENT_OFF(old->timer); zebra_nhg_free(old); old = NULL; } @@ -3651,3 +3672,66 @@ static ssize_t printfrr_nhghe(struct fbuf *buf, struct printfrr_eargs *ea, ret += bputs(buf, "]"); return ret; } + +/* + * On interface add the nexthop that resolves to this intf needs + * a re-install. There are following scenarios when the nexthop group update + * gets skipped: + * 1. When upper level protocol sends removal of NHG, there is + * timer running to keep NHG for 180 seconds, during this interval, same route + * with same set of nexthops installation is given , the same NHG is used + * but since NHG is not reinstalled on interface address add, it is not aware + * in Dplan/Kernel. + * 2. Due to a quick port flap due to interface add and delete + * to be processed in same queue one after another. Zebra believes that + * there is no change in nhg in this case. Hence this re-install will + * make sure the nexthop group gets updated to Dplan/Kernel. + */ +void zebra_interface_nhg_reinstall(struct interface *ifp) +{ + struct nhg_connected *rb_node_dep = NULL; + struct zebra_if *zif = ifp->info; + struct nexthop *nh; + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s: Installing interface %s associated NHGs into kernel", + __func__, ifp->name); + + frr_each (nhg_connected_tree, &zif->nhg_dependents, rb_node_dep) { + nh = rb_node_dep->nhe->nhg.nexthop; + if (zebra_nhg_set_valid_if_active(rb_node_dep->nhe)) { + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug( + "%s: Setting the valid flag for nhe %pNG, interface: %s", + __func__, rb_node_dep->nhe, ifp->name); + } + /* Check for singleton NHG associated to interface */ + if (nexthop_is_ifindex_type(nh) && + zebra_nhg_depends_is_empty(rb_node_dep->nhe)) { + struct nhg_connected *rb_node_dependent; + + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s install nhe %pNG nh type %u flags 0x%x", + __func__, rb_node_dep->nhe, nh->type, + rb_node_dep->nhe->flags); + zebra_nhg_install_kernel(rb_node_dep->nhe); + + /* mark depedent uninstall, when interface associated + * singleton is installed, install depedent + */ + frr_each_safe (nhg_connected_tree, + &rb_node_dep->nhe->nhg_dependents, + rb_node_dependent) { + if (IS_ZEBRA_DEBUG_NHG) + zlog_debug( + "%s dependent nhe %pNG unset installed flag", + __func__, + rb_node_dependent->nhe); + UNSET_FLAG(rb_node_dependent->nhe->flags, + NEXTHOP_GROUP_INSTALLED); + } + } + } +} diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 5830952d5aca..6179be3442e7 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -89,7 +89,7 @@ struct nhg_hash_entry { */ struct nhg_connected_tree_head nhg_depends, nhg_dependents; - struct thread *timer; + struct event *timer; /* * Is this nexthop group valid, ie all nexthops are fully resolved. @@ -358,6 +358,7 @@ extern uint8_t zebra_nhg_nhe2grp(struct nh_grp *grp, struct nhg_hash_entry *nhe, /* 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); +extern void zebra_interface_nhg_reinstall(struct interface *ifp); /* Forward ref of dplane update context type */ struct zebra_dplane_ctx; diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 6bb5e971e616..ffdb9df531ef 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -23,6 +23,7 @@ #include "rib.h" #include "table_manager.h" #include "zebra_errors.h" +#include "zebra_dplane.h" extern struct zebra_privs_t zserv_privs; @@ -101,6 +102,36 @@ int zebra_ns_disabled(struct ns *ns) return zebra_ns_disable_internal(zns, true); } +void zebra_ns_startup_continue(struct zebra_dplane_ctx *ctx) +{ + struct zebra_ns *zns = zebra_ns_lookup(dplane_ctx_get_ns_id(ctx)); + enum zebra_dplane_startup_notifications spot; + + if (!zns) { + zlog_err("%s: No Namespace associated with %u", __func__, + dplane_ctx_get_ns_id(ctx)); + return; + } + + spot = dplane_ctx_get_startup_spot(ctx); + + switch (spot) { + case ZEBRA_DPLANE_INTERFACES_READ: + interface_list_tunneldump(zns); + break; + case ZEBRA_DPLANE_TUNNELS_READ: + interface_list_second(zns); + break; + case ZEBRA_DPLANE_ADDRESSES_READ: + route_read(zns); + + vlan_read(zns); + kernel_read_pbr_rules(zns); + kernel_read_tc_qdisc(zns); + break; + } +} + /* Do global enable actions - open sockets, read kernel config etc. */ int zebra_ns_enable(ns_id_t ns_id, void **info) { @@ -111,11 +142,6 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) kernel_init(zns); zebra_dplane_ns_enable(zns, true); interface_list(zns); - route_read(zns); - - vlan_read(zns); - kernel_read_pbr_rules(zns); - kernel_read_tc_qdisc(zns); return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 5e8764854ed3..edf261197190 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -46,7 +46,7 @@ struct zebra_ns { */ struct nlsock netlink_dplane_out; struct nlsock netlink_dplane_in; - struct thread *t_netlink; + struct event *t_netlink; #endif struct route_table *if_table; @@ -68,6 +68,8 @@ int zebra_ns_final_shutdown(struct ns *ns, void **param_out __attribute__((unused))); int zebra_ns_config_write(struct vty *vty, struct ns *ns); +void zebra_ns_startup_continue(struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/zebra_opaque.c b/zebra/zebra_opaque.c index 7cfb3294eb89..90533955a4cf 100644 --- a/zebra/zebra_opaque.c +++ b/zebra/zebra_opaque.c @@ -26,10 +26,16 @@ struct opq_client_reg { int instance; uint32_t session_id; + int flags; + struct opq_client_reg *next; struct opq_client_reg *prev; }; +/* Registration is for receiving or for notifications */ +#define OPQ_CLIENT_FLAG_RECV 0x01 +#define OPQ_CLIENT_FLAG_NOTIFY 0x02 + /* Opaque message registration info */ struct opq_msg_reg { struct opq_regh_item item; @@ -77,10 +83,10 @@ static struct zebra_opaque_globals { struct frr_pthread *pthread; /* Event-delivery context 'master' for the module */ - struct thread_master *master; + struct event_loop *master; /* Event/'thread' pointer for queued zapi messages */ - struct thread *t_msgs; + struct event *t_msgs; /* Input fifo queue to the module, and lock to protect it. */ pthread_mutex_t mutex; @@ -94,19 +100,23 @@ static const char LOG_NAME[] = "Zebra Opaque"; /* Prototypes */ /* Main event loop, processing incoming message queue */ -static void process_messages(struct thread *event); +static void process_messages(struct event *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 handle_opq_notif_req(const struct zmsghdr *hdr, struct stream *msg); +static int handle_opq_notif_unreg(const struct zapi_opaque_notif_info *info); 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 bool opq_client_notif_match(const struct opq_client_reg *client, + const struct zapi_opaque_notif_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 struct opq_client_reg *opq_client_alloc(uint8_t proto, uint16_t instance, + uint32_t session_id); 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); @@ -148,8 +158,8 @@ void zebra_opaque_start(void) 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); + event_add_event(zo_info.master, process_messages, NULL, 0, + &zo_info.t_msgs); /* And start the pthread */ frr_pthread_run(zo_info.pthread, NULL); @@ -213,6 +223,7 @@ bool zebra_opaque_handles_msgid(uint16_t id) case ZEBRA_OPAQUE_MESSAGE: case ZEBRA_OPAQUE_REGISTER: case ZEBRA_OPAQUE_UNREGISTER: + case ZEBRA_OPAQUE_NOTIFY: ret = true; break; default: @@ -243,13 +254,13 @@ uint32_t zebra_opaque_enqueue_batch(struct stream_fifo *batch) } } - /* Schedule module pthread to process the batch */ + /* Schedule module's pthread to process the batch */ if (counter > 0) { if (IS_ZEBRA_DEBUG_RECV && IS_ZEBRA_DEBUG_DETAIL) zlog_debug("%s: received %u messages", __func__, counter); - thread_add_event(zo_info.master, process_messages, NULL, 0, - &zo_info.t_msgs); + event_add_event(zo_info.master, process_messages, NULL, 0, + &zo_info.t_msgs); } return counter; @@ -258,7 +269,7 @@ uint32_t zebra_opaque_enqueue_batch(struct stream_fifo *batch) /* * Pthread event loop, process the incoming message queue. */ -static void process_messages(struct thread *event) +static void process_messages(struct event *event) { struct stream_fifo fifo; struct stream *msg; @@ -317,14 +328,46 @@ static void process_messages(struct thread *event) 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); + event_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); } +/* + * Helper to acquire/lock a client session and send the message in 's'. + * Note that 's' is enqueued for an io pthread, so don't free it + * or touch it if this returns 'true'. + */ +static bool opq_send_message(uint8_t proto, uint16_t instance, + uint32_t session_id, struct stream *s) +{ + bool ret = false; + struct zserv *zclient; + + /* + * 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(proto, instance, session_id); + if (zclient) { + /* + * 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, s); + + zserv_release_client(zclient); + ret = true; + } + + return ret; +} + /* * Process (dispatch) or drop opaque messages. */ @@ -336,7 +379,6 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) 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) { @@ -350,6 +392,9 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) } else if (hdr.command == ZEBRA_OPAQUE_UNREGISTER) { handle_opq_unregistration(&hdr, msg); continue; + } else if (hdr.command == ZEBRA_OPAQUE_NOTIFY) { + handle_opq_notif_req(&hdr, msg); + continue; } /* We only process OPAQUE messages - drop anything else */ @@ -381,9 +426,9 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) if (CHECK_FLAG(info.flags, ZAPI_OPAQUE_FLAG_UNICAST)) { - if (client->proto != info.proto || - client->instance != info.instance || - client->session_id != info.session_id) + if (client->proto != info.dest_proto || + client->instance != info.dest_instance || + client->session_id != info.dest_session_id) continue; if (IS_ZEBRA_DEBUG_RECV && @@ -400,36 +445,25 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) dup = stream_dup(msg); } + if (IS_ZEBRA_DEBUG_SEND && IS_ZEBRA_DEBUG_DETAIL) + zlog_debug("%s: sending %s to client %s", + __func__, (dup ? "dup" : "msg"), + opq_client2str(buf, sizeof(buf), + client)); + /* * 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_SEND && - IS_ZEBRA_DEBUG_DETAIL) - 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 (opq_send_message(client->proto, client->instance, + client->session_id, + (dup ? dup : msg))) { + /* Message is gone - don't touch it */ if (dup) dup = NULL; else msg = NULL; - - zserv_release_client(zclient); } else { if (IS_ZEBRA_DEBUG_RECV && IS_ZEBRA_DEBUG_DETAIL) @@ -457,6 +491,66 @@ static int dispatch_opq_messages(struct stream_fifo *msg_fifo) return 0; } +/* Enqueue registration client object */ +static void opq_enqueue_client(struct opq_msg_reg *reg, + struct opq_client_reg *client) +{ + client->next = reg->clients; + if (reg->clients) + reg->clients->prev = client; + reg->clients = client; +} + +/* Dequeue registration client object */ +static void opq_dequeue_client(struct opq_msg_reg *reg, + struct opq_client_reg *client) +{ + if (client->prev) + client->prev->next = client->next; + if (client->next) + client->next->prev = client->prev; + if (reg->clients == client) + reg->clients = client->next; +} + +/* + * Send notification messages to any interested clients in 'reg', + * about 'server'; the sense is 'registered' (or not). + * The 'server' is not required for un-registrations. + */ +static void opq_send_notifications(const struct opq_msg_reg *reg, + const struct opq_client_reg *server, + bool registered) +{ + const struct opq_client_reg *client; + struct stream *msg = NULL; + + /* If there are any notification clients, send them a message */ + for (client = reg->clients; client; client = client->next) { + if (CHECK_FLAG(client->flags, OPQ_CLIENT_FLAG_NOTIFY)) { + msg = stream_new(ZEBRA_SMALL_PACKET_SIZE); + + if (registered) { + zclient_opaque_notif_encode(msg, reg->type, + registered, + server->proto, + server->instance, + server->session_id); + } else { + zclient_opaque_notif_encode(msg, reg->type, + registered, 0, 0, 0); + } + + /* Locate zebra client and enqueue message to it */ + if (!opq_send_message(client->proto, client->instance, + client->session_id, msg)) { + /* Error - need to free the message */ + stream_free(msg); + } + } + } +} + /* * Process a register/unregister message */ @@ -499,7 +593,9 @@ static int handle_opq_registration(const struct zmsghdr *hdr, goto done; } - client = opq_client_alloc(&info); + client = opq_client_alloc(info.proto, info.instance, + info.session_id); + SET_FLAG(client->flags, OPQ_CLIENT_FLAG_RECV); if (IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: client %s registers for %u", @@ -508,17 +604,20 @@ static int handle_opq_registration(const struct zmsghdr *hdr, info.type); /* Link client into registration */ - client->next = reg->clients; - if (reg->clients) - reg->clients->prev = client; - reg->clients = client; + opq_enqueue_client(reg, client); + + /* Send notifications to any clients who want them */ + opq_send_notifications(reg, client, true); + } 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); + client = opq_client_alloc(info.proto, info.instance, + info.session_id); + SET_FLAG(client->flags, OPQ_CLIENT_FLAG_RECV); if (IS_ZEBRA_DEBUG_RECV) zlog_debug("%s: client %s registers for new reg %u", @@ -545,8 +644,9 @@ static int handle_opq_unregistration(const struct zmsghdr *hdr, { int ret = 0; struct zapi_opaque_reg_info info; - struct opq_client_reg *client; + struct opq_client_reg *client, *tclient; struct opq_msg_reg key, *reg; + int scount; char buf[50]; memset(&info, 0, sizeof(info)); @@ -571,11 +671,16 @@ static int handle_opq_unregistration(const struct zmsghdr *hdr, goto done; } - /* Look for client */ - for (client = reg->clients; client != NULL; - client = client->next) { - if (opq_client_match(client, &info)) - break; + /* Look for client info, count servers and notif clients too */ + client = NULL; + scount = 0; + + for (tclient = reg->clients; tclient != NULL; tclient = tclient->next) { + if (opq_client_match(tclient, &info)) + client = tclient; + + if (CHECK_FLAG(tclient->flags, OPQ_CLIENT_FLAG_RECV)) + scount++; } if (client == NULL) { @@ -592,19 +697,18 @@ static int handle_opq_unregistration(const struct zmsghdr *hdr, __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_dequeue_client(reg, client); opq_client_free(&client); + scount--; /* Is registration empty now? */ if (reg->clients == NULL) { + opq_regh_del(&opq_reg_hash, reg); opq_reg_free(®); + } else if (scount == 0) { + /* Send notifications if no more servers for the message. */ + opq_send_notifications(reg, NULL, false); } done: @@ -613,13 +717,182 @@ static int handle_opq_unregistration(const struct zmsghdr *hdr, return ret; } +/* + * Handle requests about opaque notifications. + */ +static int handle_opq_notif_req(const struct zmsghdr *hdr, struct stream *msg) +{ + int ret; + struct zapi_opaque_notif_info info = {}; + struct opq_client_reg *client; + struct opq_msg_reg key, *reg; + char buf[50]; + + ret = zclient_opaque_notif_decode(msg, &info); + if (ret < 0) + goto done; + + /* Handle deregistration */ + if (!info.reg) { + ret = handle_opq_notif_unreg(&info); + goto done; + } + + memset(&key, 0, sizeof(key)); + + key.type = info.msg_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_notif_match(client, &info)) + break; + } + + if (client) { + /* Oops - duplicate ? */ + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: duplicate opq notif reg client %s", + __func__, opq_client2str(buf, + sizeof(buf), + client)); + goto done; + } + + client = opq_client_alloc(info.proto, info.instance, + info.session_id); + SET_FLAG(client->flags, OPQ_CLIENT_FLAG_NOTIFY); + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: client %s registers for notif %u", + __func__, + opq_client2str(buf, sizeof(buf), client), + info.msg_type); + + /* Link client into registration */ + opq_enqueue_client(reg, client); + + /* Send notification if any registered servers */ + /* Look for a server */ + for (client = reg->clients; client != NULL; + client = client->next) { + if (CHECK_FLAG(client->flags, OPQ_CLIENT_FLAG_RECV)) + break; + } + if (client) + opq_send_notifications(reg, client, true); + + } else if (info.reg) { + /* + * No existing registrations - create one, add the + * client, and add registration to hash. + */ + reg = opq_reg_alloc(info.msg_type); + client = opq_client_alloc(info.proto, info.instance, + info.session_id); + SET_FLAG(client->flags, OPQ_CLIENT_FLAG_NOTIFY); + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: client %s registers for new notif %u", + __func__, + opq_client2str(buf, sizeof(buf), client), + info.msg_type); + + reg->clients = client; + + opq_regh_add(&opq_reg_hash, reg); + } + +done: + stream_free(msg); + return ret; +} + +/* + * Unregister notification + */ +static int handle_opq_notif_unreg(const struct zapi_opaque_notif_info *info) +{ + int ret = 0; + struct opq_client_reg *client; + struct opq_msg_reg key, *reg; + char buf[50]; + + memset(&key, 0, sizeof(key)); + + key.type = info->msg_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 notif for unknown type %u", + __func__, zebra_route_string(info->proto), + info->instance, info->session_id, + info->msg_type); + goto done; + } + + /* Look for client */ + for (client = reg->clients; client != NULL; client = client->next) { + if (opq_client_notif_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 notif for %u", + __func__, zebra_route_string(info->proto), + info->instance, info->session_id, + info->msg_type); + goto done; + } + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: client %s unregisters notif for %u", __func__, + opq_client2str(buf, sizeof(buf), client), + info->msg_type); + + /* Dequeue client object */ + opq_dequeue_client(reg, client); + + opq_client_free(&client); + + /* Is registration empty now? */ + if (reg->clients == NULL) { + opq_regh_del(&opq_reg_hash, reg); + opq_reg_free(®); + } + +done: + + 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) + /* look for matching client, skip notifications */ + if (client->proto == info->proto && client->instance == info->instance && + client->session_id == info->session_id && + CHECK_FLAG(client->flags, OPQ_CLIENT_FLAG_RECV)) + return true; + else + return false; +} + +/* Compare helper for clients registered for notifications */ +static bool opq_client_notif_match(const struct opq_client_reg *client, + const struct zapi_opaque_notif_info *info) +{ + /* look for matching client, only for notifications */ + if (client->proto == info->proto && client->instance == info->instance && + client->session_id == info->session_id && + CHECK_FLAG(client->flags, OPQ_CLIENT_FLAG_NOTIFY)) return true; else return false; @@ -655,16 +928,16 @@ 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) +static struct opq_client_reg *opq_client_alloc(uint8_t proto, uint16_t instance, + uint32_t session_id) { 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; + client->proto = proto; + client->instance = instance; + client->session_id = session_id; return client; } diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index a8f4953d5a1e..a678e7173471 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -16,6 +16,7 @@ #include "ptm_lib.h" #include "rib.h" #include "stream.h" +#include "lib/version.h" #include "vrf.h" #include "vty.h" #include "lib_errors.h" @@ -84,7 +85,7 @@ static ptm_lib_handle_t *ptm_hdl; struct zebra_ptm_cb ptm_cb; static int zebra_ptm_socket_init(void); -void zebra_ptm_sock_read(struct thread *thread); +void zebra_ptm_sock_read(struct event *thread); static void zebra_ptm_install_commands(void); static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt); void zebra_bfd_peer_replay_req(void); @@ -141,9 +142,9 @@ void zebra_ptm_finish(void) free(ptm_cb.in_data); /* Cancel events. */ - THREAD_OFF(ptm_cb.t_read); - THREAD_OFF(ptm_cb.t_write); - THREAD_OFF(ptm_cb.t_timer); + EVENT_OFF(ptm_cb.t_read); + EVENT_OFF(ptm_cb.t_write); + EVENT_OFF(ptm_cb.t_timer); if (ptm_cb.wb) buffer_free(ptm_cb.wb); @@ -152,7 +153,7 @@ void zebra_ptm_finish(void) close(ptm_cb.ptm_sock); } -static void zebra_ptm_flush_messages(struct thread *thread) +static void zebra_ptm_flush_messages(struct event *thread) { ptm_cb.t_write = NULL; @@ -169,13 +170,13 @@ static void zebra_ptm_flush_messages(struct thread *thread) ptm_cb.ptm_sock = -1; zebra_ptm_reset_status(0); ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return; case BUFFER_PENDING: ptm_cb.t_write = NULL; - thread_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, - ptm_cb.ptm_sock, &ptm_cb.t_write); + event_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, + ptm_cb.ptm_sock, &ptm_cb.t_write); break; case BUFFER_EMPTY: break; @@ -193,22 +194,22 @@ static int zebra_ptm_send_message(char *data, int size) ptm_cb.ptm_sock = -1; zebra_ptm_reset_status(0); ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return -1; case BUFFER_EMPTY: - THREAD_OFF(ptm_cb.t_write); + EVENT_OFF(ptm_cb.t_write); break; case BUFFER_PENDING: - thread_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, - ptm_cb.ptm_sock, &ptm_cb.t_write); + event_add_write(zrouter.master, zebra_ptm_flush_messages, NULL, + ptm_cb.ptm_sock, &ptm_cb.t_write); break; } return 0; } -void zebra_ptm_connect(struct thread *t) +void zebra_ptm_connect(struct event *t) { int init = 0; @@ -220,8 +221,8 @@ void zebra_ptm_connect(struct thread *t) if (ptm_cb.ptm_sock != -1) { if (init) { ptm_cb.t_read = NULL; - thread_add_read(zrouter.master, zebra_ptm_sock_read, - NULL, ptm_cb.ptm_sock, &ptm_cb.t_read); + event_add_read(zrouter.master, zebra_ptm_sock_read, + NULL, ptm_cb.ptm_sock, &ptm_cb.t_read); zebra_bfd_peer_replay_req(); } zebra_ptm_send_status_req(); @@ -232,8 +233,8 @@ void zebra_ptm_connect(struct thread *t) ptm_cb.reconnect_time = ZEBRA_PTM_RECONNECT_TIME_MAX; ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); } else if (ptm_cb.reconnect_time >= ZEBRA_PTM_RECONNECT_TIME_MAX) { ptm_cb.reconnect_time = ZEBRA_PTM_RECONNECT_TIME_INITIAL; } @@ -629,13 +630,13 @@ static int zebra_ptm_handle_msg_cb(void *arg, void *in_ctxt) } } -void zebra_ptm_sock_read(struct thread *thread) +void zebra_ptm_sock_read(struct event *thread) { int sock; int rc; errno = 0; - sock = THREAD_FD(thread); + sock = EVENT_FD(thread); if (sock == -1) return; @@ -656,15 +657,14 @@ void zebra_ptm_sock_read(struct thread *thread) ptm_cb.ptm_sock = -1; zebra_ptm_reset_status(0); ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, - &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } ptm_cb.t_read = NULL; - thread_add_read(zrouter.master, zebra_ptm_sock_read, NULL, - ptm_cb.ptm_sock, &ptm_cb.t_read); + event_add_read(zrouter.master, zebra_ptm_sock_read, NULL, + ptm_cb.ptm_sock, &ptm_cb.t_read); } /* BFD peer/dst register/update */ @@ -698,8 +698,8 @@ void zebra_ptm_bfd_dst_register(ZAPI_HANDLER_ARGS) if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } @@ -857,8 +857,8 @@ void zebra_ptm_bfd_dst_deregister(ZAPI_HANDLER_ARGS) if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } @@ -985,8 +985,8 @@ void zebra_ptm_bfd_client_register(ZAPI_HANDLER_ARGS) if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return; } @@ -1044,8 +1044,8 @@ int zebra_ptm_bfd_client_deregister(struct zserv *client) if (ptm_cb.ptm_sock == -1) { ptm_cb.t_timer = NULL; - thread_add_timer(zrouter.master, zebra_ptm_connect, NULL, - ptm_cb.reconnect_time, &ptm_cb.t_timer); + event_add_timer(zrouter.master, zebra_ptm_connect, NULL, + ptm_cb.reconnect_time, &ptm_cb.t_timer); return 0; } @@ -1185,7 +1185,7 @@ struct ptm_process { TAILQ_HEAD(ppqueue, ptm_process) ppqueue; DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_PTM_BFD_PROCESS, - "PTM BFD process registration table."); + "PTM BFD process reg table"); /* * Prototypes. diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h index 9cea96555b65..4d09474b7f22 100644 --- a/zebra/zebra_ptm.h +++ b/zebra/zebra_ptm.h @@ -26,9 +26,9 @@ struct zebra_ptm_cb { struct buffer *wb; /* Buffer of data waiting to be written to ptm. */ - struct thread *t_read; /* Thread for read */ - struct thread *t_write; /* Thread for write */ - struct thread *t_timer; /* Thread for timer */ + struct event *t_read; /* Thread for read */ + struct event *t_write; /* Thread for write */ + struct event *t_timer; /* Thread for timer */ char *out_data; char *in_data; @@ -53,11 +53,11 @@ struct zebra_ptm_cb { (protocol) == ZEBRA_ROUTE_OSPF6 || (protocol) == ZEBRA_ROUTE_ISIS || \ (protocol) == ZEBRA_ROUTE_PIM || \ (protocol) == ZEBRA_ROUTE_OPENFABRIC || \ - (protocol) == ZEBRA_ROUTE_STATIC) + (protocol) == ZEBRA_ROUTE_STATIC || (protocol) == ZEBRA_ROUTE_RIP) void zebra_ptm_init(void); void zebra_ptm_finish(void); -void zebra_ptm_connect(struct thread *t); +void zebra_ptm_connect(struct event *t); void zebra_ptm_write(struct vty *vty); int zebra_ptm_get_enable_state(void); diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index dc56ba3c3042..12dcac1de5f5 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -7,7 +7,7 @@ #include "log.h" #include "memory.h" -#include "thread.h" +#include "frrevent.h" #include "command.h" #include "vrf.h" #include "lib/json.h" @@ -33,7 +33,7 @@ DEFINE_HOOK(pw_uninstall, (struct zebra_pw * pw), (pw)); static int zebra_pw_enabled(struct zebra_pw *); static void zebra_pw_install(struct zebra_pw *); static void zebra_pw_uninstall(struct zebra_pw *); -static void zebra_pw_install_retry(struct thread *thread); +static void zebra_pw_install_retry(struct event *thread); static int zebra_pw_check_reachability(const struct zebra_pw *); static void zebra_pw_update_status(struct zebra_pw *, int); @@ -89,7 +89,7 @@ void zebra_pw_del(struct zebra_vrf *zvrf, struct zebra_pw *pw) dplane_pw_uninstall(pw); } - THREAD_OFF(pw->install_retry_timer); + EVENT_OFF(pw->install_retry_timer); /* unlink and release memory */ RB_REMOVE(zebra_pw_head, &zvrf->pseudowires, pw); @@ -207,16 +207,16 @@ void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus) pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL); /* schedule to retry later */ - THREAD_OFF(pw->install_retry_timer); - thread_add_timer(zrouter.master, zebra_pw_install_retry, pw, - PW_INSTALL_RETRY_INTERVAL, &pw->install_retry_timer); + EVENT_OFF(pw->install_retry_timer); + event_add_timer(zrouter.master, zebra_pw_install_retry, pw, + PW_INSTALL_RETRY_INTERVAL, &pw->install_retry_timer); zebra_pw_update_status(pw, pwstatus); } -static void zebra_pw_install_retry(struct thread *thread) +static void zebra_pw_install_retry(struct event *thread) { - struct zebra_pw *pw = THREAD_ARG(thread); + struct zebra_pw *pw = EVENT_ARG(thread); zebra_pw_install(pw); } @@ -407,7 +407,7 @@ DEFUN_NOSH (pseudowire_if, const char *ifname; int idx = 0; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return CMD_WARNING; @@ -439,7 +439,7 @@ DEFUN (no_pseudowire_if, const char *ifname; int idx = 0; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return CMD_WARNING; @@ -563,7 +563,7 @@ DEFUN (show_pseudowires, struct zebra_vrf *zvrf; struct zebra_pw *pw; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return 0; @@ -602,7 +602,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) struct nexthop *nexthop; struct nexthop_group *nhg; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return; @@ -758,7 +758,7 @@ static void vty_show_mpls_pseudowire_detail_json(struct vty *vty) struct zebra_vrf *zvrf; struct zebra_pw *pw; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return; @@ -794,7 +794,7 @@ static int zebra_pw_config(struct vty *vty) struct zebra_vrf *zvrf; struct zebra_pw *pw; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return 0; diff --git a/zebra/zebra_pw.h b/zebra/zebra_pw.h index d939cc07eaa9..fd94d5e5edfe 100644 --- a/zebra/zebra_pw.h +++ b/zebra/zebra_pw.h @@ -38,7 +38,7 @@ struct zebra_pw { uint8_t protocol; struct zserv *client; struct rnh *rnh; - struct thread *install_retry_timer; + struct event *install_retry_timer; QOBJ_FIELDS; }; DECLARE_QOBJ_TYPE(zebra_pw); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 6c499b77d70a..93b33c9abdf2 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -18,7 +18,7 @@ #include "sockunion.h" #include "srcdest_table.h" #include "table.h" -#include "thread.h" +#include "frrevent.h" #include "vrf.h" #include "workqueue.h" #include "nexthop_group_private.h" @@ -56,7 +56,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, WQ_WRAPPER, "WQ wrapper"); * Event, list, and mutex for delivery of dataplane results */ static pthread_mutex_t dplane_mutex; -static struct thread *t_dplane; +static struct event *t_dplane; static struct dplane_ctx_list_head rib_dplane_q; DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), @@ -64,7 +64,12 @@ DEFINE_HOOK(rib_update, (struct route_node * rn, const char *reason), DEFINE_HOOK(rib_shutdown, (struct route_node * rn), (rn)); -/* Meta Q's specific names */ +/* + * Meta Q's specific names + * + * If you add something here ensure that you + * change MQ_SIZE as well over in rib.h + */ enum meta_queue_indexes { META_QUEUE_NHG, META_QUEUE_EVPN, @@ -76,6 +81,7 @@ enum meta_queue_indexes { META_QUEUE_NOTBGP, META_QUEUE_BGP, META_QUEUE_OTHER, + META_QUEUE_GR_RUN, }; /* Each route type's string and default distance value. */ @@ -250,6 +256,8 @@ static const char *subqueue2str(enum meta_queue_indexes index) return "BGP Routes"; case META_QUEUE_OTHER: return "Other Routes"; + case META_QUEUE_GR_RUN: + return "Graceful Restart"; } return "Unknown"; @@ -632,7 +640,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, { struct nexthop *nexthop; struct rib_table_info *info = srcdest_rnode_table_info(rn); - struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); const struct prefix *p, *src_p; enum zebra_dplane_result ret; @@ -715,7 +723,7 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) { struct nexthop *nexthop; struct rib_table_info *info = srcdest_rnode_table_info(rn); - struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(re->vrf_id); if (info->safi != SAFI_UNICAST) { UNSET_FLAG(re->status, ROUTE_ENTRY_INSTALLED); @@ -1410,7 +1418,7 @@ static void rib_process(struct route_node *rn) static void zebra_rib_evaluate_mpls(struct route_node *rn) { rib_dest_t *dest = rib_dest_from_rnode(rn); - struct zebra_vrf *zvrf = vrf_info_lookup(VRF_DEFAULT); + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!dest) return; @@ -1430,7 +1438,7 @@ static void zebra_rib_evaluate_mpls(struct route_node *rn) */ static bool rib_route_match_ctx(const struct route_entry *re, const struct zebra_dplane_ctx *ctx, - bool is_update) + bool is_update, bool async) { bool result = false; @@ -1446,13 +1454,12 @@ static bool rib_route_match_ctx(const struct route_entry *re, /* We use an extra test for statics, and another for * kernel routes. */ - if (re->type == ZEBRA_ROUTE_STATIC && + if (re->type == ZEBRA_ROUTE_STATIC && !async && (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)) { + re->metric != dplane_ctx_get_old_metric(ctx)) { result = false; } } @@ -1474,7 +1481,7 @@ static bool rib_route_match_ctx(const struct route_entry *re, /* We use an extra test for statics, and another for * kernel routes. */ - if (re->type == ZEBRA_ROUTE_STATIC && + if (re->type == ZEBRA_ROUTE_STATIC && !async && (re->distance != dplane_ctx_get_distance(ctx) || re->tag != dplane_ctx_get_tag(ctx))) { result = false; @@ -1898,7 +1905,7 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) struct rib_table_info *info; bool rt_delete = false; - zvrf = vrf_info_lookup(dplane_ctx_get_vrf(ctx)); + zvrf = zebra_vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); vrf = vrf_lookup_by_id(dplane_ctx_get_vrf(ctx)); /* Locate rn and re(s) from ctx */ @@ -1938,13 +1945,13 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) RNODE_FOREACH_RE(rn, rib) { if (re == NULL) { - if (rib_route_match_ctx(rib, ctx, false)) + if (rib_route_match_ctx(rib, ctx, false, false)) re = rib; } /* Check for old route match */ if (is_update && (old_re == NULL)) { - if (rib_route_match_ctx(rib, ctx, true /*is_update*/)) + if (rib_route_match_ctx(rib, ctx, true, false)) old_re = rib; } @@ -2175,6 +2182,7 @@ static void rib_process_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_TC_FILTER_ADD: case DPLANE_OP_TC_FILTER_DELETE: case DPLANE_OP_TC_FILTER_UPDATE: + case DPLANE_OP_STARTUP_STAGE: break; } @@ -2263,7 +2271,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx) * info. */ RNODE_FOREACH_RE(rn, re) { - if (rib_route_match_ctx(re, ctx, false /*!update*/)) + if (rib_route_match_ctx(re, ctx, false, true)) break; } @@ -2566,7 +2574,7 @@ static void process_subq_early_label(struct listnode *lnode) if (!w) return; - zvrf = vrf_info_lookup(w->vrf_id); + zvrf = zebra_vrf_lookup_by_id(w->vrf_id); if (!zvrf) { XFREE(MTYPE_WQ_WRAPPER, w); return; @@ -3089,6 +3097,23 @@ static void process_subq_early_route(struct listnode *lnode) process_subq_early_route_add(ere); } +struct meta_q_gr_run { + afi_t afi; + vrf_id_t vrf_id; + uint8_t proto; + uint8_t instance; +}; + +static void process_subq_gr_run(struct listnode *lnode) +{ + struct meta_q_gr_run *gr_run = listgetdata(lnode); + + zebra_gr_process_client(gr_run->afi, gr_run->vrf_id, gr_run->proto, + gr_run->instance); + + XFREE(MTYPE_WQ_WRAPPER, gr_run); +} + /* * Examine the specified subqueue; process one entry and return 1 if * there is a node, return 0 otherwise. @@ -3122,6 +3147,9 @@ static unsigned int process_subq(struct list *subq, case META_QUEUE_OTHER: process_subq_route(lnode, qindex); break; + case META_QUEUE_GR_RUN: + process_subq_gr_run(lnode); + break; } list_delete_node(subq, lnode); @@ -3727,6 +3755,23 @@ static void early_route_meta_queue_free(struct meta_queue *mq, struct list *l, } } +static void rib_meta_queue_gr_run_free(struct meta_queue *mq, struct list *l, + struct zebra_vrf *zvrf) +{ + struct meta_q_gr_run *gr_run; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(l, node, nnode, gr_run)) { + if (zvrf && zvrf->vrf->vrf_id != gr_run->vrf_id) + continue; + + XFREE(MTYPE_WQ_WRAPPER, gr_run); + node->data = NULL; + list_delete_node(l, node); + mq->size--; + } +} + void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf) { enum meta_queue_indexes i; @@ -3754,6 +3799,9 @@ void meta_queue_free(struct meta_queue *mq, struct zebra_vrf *zvrf) case META_QUEUE_OTHER: rib_meta_queue_free(mq, mq->subq[i], zvrf); break; + case META_QUEUE_GR_RUN: + rib_meta_queue_gr_run_free(mq, mq->subq[i], zvrf); + break; } if (!zvrf) list_delete(&mq->subq[i]); @@ -3930,6 +3978,10 @@ void rib_delnode(struct route_node *rn, struct route_entry *re) if (IS_ZEBRA_DEBUG_RIB) rnode_debug(rn, re->vrf_id, "rn %p, re %p, removing", (void *)rn, (void *)re); + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + route_entry_dump(&rn->p, NULL, re); + SET_FLAG(re->status, ROUTE_ENTRY_REMOVED); afi = (rn->p.family == AF_INET) @@ -4075,8 +4127,8 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zclient_dump_route_flags(re->flags, flags_buf, sizeof(flags_buf)), _dump_re_status(re, status_buf, sizeof(status_buf))); - zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr, - nexthop_group_nexthop_num(&(re->nhe->nhg)), + zlog_debug("%s: tag == %u, nexthop_num == %u, nexthop_active_num == %u", + straddr, re->tag, nexthop_group_nexthop_num(&(re->nhe->nhg)), nexthop_group_active_nexthop_num(&(re->nhe->nhg))); /* Dump nexthops */ @@ -4094,6 +4146,17 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, zlog_debug("%s: dump complete", straddr); } +static int rib_meta_queue_gr_run_add(struct meta_queue *mq, void *data) +{ + listnode_add(mq->subq[META_QUEUE_GR_RUN], data); + mq->size++; + + if (IS_ZEBRA_DEBUG_RIB_DETAILED) + zlog_debug("Graceful Run adding"); + + return 0; +} + static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data) { struct zebra_early_route *ere = data; @@ -4110,6 +4173,20 @@ static int rib_meta_queue_early_route_add(struct meta_queue *mq, void *data) return 0; } +int rib_add_gr_run(afi_t afi, vrf_id_t vrf_id, uint8_t proto, uint8_t instance) +{ + struct meta_q_gr_run *gr_run; + + gr_run = XCALLOC(MTYPE_WQ_WRAPPER, sizeof(*gr_run)); + + gr_run->afi = afi; + gr_run->proto = proto; + gr_run->vrf_id = vrf_id; + gr_run->instance = instance; + + return mq_add_handler(gr_run, rib_meta_queue_gr_run_add); +} + struct route_entry *zebra_rib_route_entry_new(vrf_id_t vrf_id, int type, uint8_t instance, uint32_t flags, uint32_t nhe_id, @@ -4393,11 +4470,11 @@ static void rib_update_ctx_fini(struct rib_update_ctx **ctx) XFREE(MTYPE_RIB_UPDATE_CTX, *ctx); } -static void rib_update_handler(struct thread *thread) +static void rib_update_handler(struct event *thread) { struct rib_update_ctx *ctx; - ctx = THREAD_ARG(thread); + ctx = EVENT_ARG(thread); rib_update_handle_vrf_all(ctx->event, ZEBRA_ROUTE_ALL); @@ -4408,20 +4485,39 @@ static void rib_update_handler(struct thread *thread) * 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]; +static struct event *t_rib_update_threads[RIB_UPDATE_MAX]; + +void rib_update_finish(void) +{ + int i; + + for (i = RIB_UPDATE_KERNEL; i < RIB_UPDATE_MAX; i++) { + if (event_is_scheduled(t_rib_update_threads[i])) { + struct rib_update_ctx *ctx; + + ctx = EVENT_ARG(t_rib_update_threads[i]); + + rib_update_ctx_fini(&ctx); + EVENT_OFF(t_rib_update_threads[i]); + } + } +} /* Schedule a RIB update event for all vrfs */ void rib_update(enum rib_update_event event) { struct rib_update_ctx *ctx; - if (thread_is_scheduled(t_rib_update_threads[event])) + if (event_is_scheduled(t_rib_update_threads[event])) + return; + + if (zebra_router_in_shutdown()) return; ctx = rib_update_ctx_init(0, event); - thread_add_event(zrouter.master, rib_update_handler, ctx, 0, - &t_rib_update_threads[event]); + event_add_event(zrouter.master, rib_update_handler, ctx, 0, + &t_rib_update_threads[event]); if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Scheduled VRF (ALL), event %s", __func__, @@ -4494,7 +4590,7 @@ void rib_sweep_table(struct route_table *table) } /* Sweep all RIB tables. */ -void rib_sweep_route(struct thread *t) +void rib_sweep_route(struct event *t) { struct vrf *vrf; struct zebra_vrf *zvrf; @@ -4606,12 +4702,27 @@ static void handle_pw_result(struct zebra_dplane_ctx *ctx) * Handle results from the dataplane system. Dequeue update context * structs, dispatch to appropriate internal handlers. */ -static void rib_process_dplane_results(struct thread *thread) +static void rib_process_dplane_results(struct event *thread) { struct zebra_dplane_ctx *ctx; struct dplane_ctx_list_head ctxlist; bool shut_p = false; +#ifdef HAVE_SCRIPTING + char *script_name = + frrscript_names_get_script_name(ZEBRA_ON_RIB_PROCESS_HOOK_CALL); + + int ret = 1; + struct frrscript *fs = NULL; + + if (script_name) { + fs = frrscript_new(script_name); + if (fs) + ret = frrscript_load(fs, ZEBRA_ON_RIB_PROCESS_HOOK_CALL, + NULL); + } +#endif /* HAVE_SCRIPTING */ + /* Dequeue a list of completed updates with one lock/unlock cycle */ do { @@ -4645,24 +4756,7 @@ static void rib_process_dplane_results(struct thread *thread) continue; } -#ifdef HAVE_SCRIPTING - char *script_name = frrscript_names_get_script_name( - ZEBRA_ON_RIB_PROCESS_HOOK_CALL); - - int ret = 1; - struct frrscript *fs; - - if (script_name) { - fs = frrscript_new(script_name); - if (fs) - ret = frrscript_load( - fs, ZEBRA_ON_RIB_PROCESS_HOOK_CALL, - NULL); - } -#endif /* HAVE_SCRIPTING */ - while (ctx) { - #ifdef HAVE_SCRIPTING if (ret == 0) frrscript_call(fs, @@ -4769,6 +4863,9 @@ static void rib_process_dplane_results(struct thread *thread) case DPLANE_OP_GRE_SET: case DPLANE_OP_NONE: break; + case DPLANE_OP_STARTUP_STAGE: + zebra_ns_startup_continue(ctx); + break; } /* Dispatch by op code */ @@ -4777,6 +4874,11 @@ static void rib_process_dplane_results(struct thread *thread) } } while (1); + +#ifdef HAVE_SCRIPTING + if (fs) + frrscript_delete(fs); +#endif } /* @@ -4793,8 +4895,8 @@ static int rib_dplane_results(struct dplane_ctx_list_head *ctxlist) } /* Ensure event is signalled to zebra main pthread */ - thread_add_event(zrouter.master, rib_process_dplane_results, NULL, 0, - &t_dplane); + event_add_event(zrouter.master, rib_process_dplane_results, NULL, 0, + &t_dplane); return 0; } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 2666dc232f08..28b83ce8b6f6 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -13,7 +13,7 @@ #include "log.h" #include "sockunion.h" #include "linklist.h" -#include "thread.h" +#include "frrevent.h" #include "workqueue.h" #include "prefix.h" #include "routemap.h" @@ -326,7 +326,7 @@ void zebra_register_rnh_pseudowire(vrf_id_t vrf_id, struct zebra_pw *pw, *nht_exists = false; - zvrf = vrf_info_lookup(vrf_id); + zvrf = zebra_vrf_lookup_by_id(vrf_id); if (!zvrf) return; @@ -1532,11 +1532,15 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, seg6local_context2str(buf, sizeof(buf), &nexthop->nh_srv6->seg6local_ctx, nexthop->nh_srv6->seg6local_action); - vty_out(vty, ", seg6local %s %s", - seg6local_action2str( - nexthop->nh_srv6->seg6local_action), - buf); - vty_out(vty, ", seg6 %pI6", &nexthop->nh_srv6->seg6_segs); + if (nexthop->nh_srv6->seg6local_action != + ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) + vty_out(vty, ", seg6local %s %s", + seg6local_action2str( + nexthop->nh_srv6->seg6local_action), + buf); + if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any)) + vty_out(vty, ", seg6 %pI6", + &nexthop->nh_srv6->seg6_segs); } if (nexthop->weight) diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 22fdf1d3d51f..555680b8d336 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -28,7 +28,7 @@ #include "zebra/zebra_routemap_clippy.c" static uint32_t zebra_rmap_update_timer = ZEBRA_RMAP_DEFAULT_UPDATE_TIMER; -static struct thread *zebra_t_rmap_update = NULL; +static struct event *zebra_t_rmap_update = NULL; char *zebra_import_table_routemap[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX]; struct nh_rmap_obj { @@ -1041,7 +1041,7 @@ route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object) } alist = access_list_lookup(AFI_IP, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1104,7 +1104,7 @@ route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix, } plist = prefix_list_lookup(AFI_IP, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1145,7 +1145,7 @@ route_match_address(afi_t afi, void *rule, const struct prefix *prefix, alist = access_list_lookup(afi, (char *)rule); if (alist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Access-List Specified: %s does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1207,7 +1207,7 @@ route_match_address_prefix_list(void *rule, const struct prefix *prefix, plist = prefix_list_lookup(afi, (char *)rule); if (plist == NULL) { - if (CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL)) + if (unlikely(CHECK_FLAG(rmap_debug, DEBUG_ROUTEMAP_DETAIL))) zlog_debug( "%s: Prefix List %s specified does not exist defaulting to NO_MATCH", __func__, (char *)rule); @@ -1724,7 +1724,7 @@ static void zebra_route_map_process_update_cb(char *rmap_name) zebra_nht_rm_update(rmap_name); } -static void zebra_route_map_update_timer(struct thread *thread) +static void zebra_route_map_update_timer(struct event *thread) { if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("Event driven route-map update triggered"); @@ -1749,7 +1749,7 @@ static void zebra_route_map_set_delay_timer(uint32_t value) if (!value && zebra_t_rmap_update) { /* Event driven route map updates is being disabled */ /* But there's a pending timer. Fire it off now */ - THREAD_OFF(zebra_t_rmap_update); + EVENT_OFF(zebra_t_rmap_update); zebra_route_map_update_timer(NULL); } } @@ -1759,7 +1759,7 @@ 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_OFF(zebra_t_rmap_update); + EVENT_OFF(zebra_t_rmap_update); route_map_finish(); } @@ -1876,10 +1876,10 @@ static void zebra_route_map_mark_update(const char *rmap_name) { /* rmap_update_timer of 0 means don't do route updates */ if (zebra_rmap_update_timer) - THREAD_OFF(zebra_t_rmap_update); + EVENT_OFF(zebra_t_rmap_update); - thread_add_timer(zrouter.master, zebra_route_map_update_timer, - NULL, zebra_rmap_update_timer, &zebra_t_rmap_update); + event_add_timer(zrouter.master, zebra_route_map_update_timer, NULL, + zebra_rmap_update_timer, &zebra_t_rmap_update); } static void zebra_route_map_add(const char *rmap_name) diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 21e09a81f755..1b3e31ee4232 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -218,7 +218,7 @@ void zebra_router_terminate(void) { struct zebra_router_table *zrt, *tmp; - THREAD_OFF(zrouter.sweeper); + EVENT_OFF(zrouter.sweeper); RB_FOREACH_SAFE (zrt, zebra_router_table_head, &zrouter.tables, tmp) zebra_router_free_table(zrt); @@ -232,20 +232,15 @@ void zebra_router_terminate(void) /* Free NHE in ID table only since it has unhashable entries as well */ hash_iterate(zrouter.nhgs_id, zebra_nhg_hash_free_zero_id, NULL); - 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_and_free(&zrouter.nhgs_id, zebra_nhg_hash_free); + hash_clean_and_free(&zrouter.nhgs, NULL); - hash_clean(zrouter.rules_hash, zebra_pbr_rules_free); - hash_free(zrouter.rules_hash); + hash_clean_and_free(&zrouter.rules_hash, zebra_pbr_rules_free); - hash_clean(zrouter.ipset_entry_hash, zebra_pbr_ipset_entry_free), - hash_clean(zrouter.ipset_hash, zebra_pbr_ipset_free); - hash_free(zrouter.ipset_hash); - hash_free(zrouter.ipset_entry_hash); - hash_clean(zrouter.iptable_hash, zebra_pbr_iptable_free); - hash_free(zrouter.iptable_hash); + hash_clean_and_free(&zrouter.ipset_entry_hash, + zebra_pbr_ipset_entry_free); + hash_clean_and_free(&zrouter.ipset_hash, zebra_pbr_ipset_free); + hash_clean_and_free(&zrouter.iptable_hash, zebra_pbr_iptable_free); #ifdef HAVE_SCRIPTING zebra_script_destroy(); @@ -264,6 +259,8 @@ void zebra_router_init(bool asic_offload, bool notify_on_ack) { zrouter.sequence_num = 0; + zrouter.protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; + zrouter.allow_delete = false; zrouter.packets_to_process = ZEBRA_ZAPI_PACKETS_TO_PROCESS; diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 3112fcab4647..a7f0f135f91a 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -110,7 +110,7 @@ struct zebra_mlag_info { struct frr_pthread *zebra_pth_mlag; /* MLAG Thread context 'master' */ - struct thread_master *th_master; + struct event_loop *th_master; /* * Event for Initial MLAG Connection setup & Data Read @@ -118,16 +118,16 @@ struct zebra_mlag_info { * so no issues. * */ - struct thread *t_read; + struct event *t_read; /* Event for MLAG write */ - struct thread *t_write; + struct event *t_write; }; struct zebra_router { atomic_bool in_shutdown; /* Thread master */ - struct thread_master *master; + struct event_loop *master; /* Lists of clients who have connected to us */ struct list *client_list; @@ -194,7 +194,7 @@ struct zebra_router { * Time for when we sweep the rib from old routes */ time_t startup_time; - struct thread *sweeper; + struct event *sweeper; /* * The hash of nexthop groups associated with this router @@ -228,6 +228,8 @@ struct zebra_router { /* Should we allow non FRR processes to delete our routes */ bool allow_delete; + + uint8_t protodown_r_bit; }; #define GRACEFUL_RESTART_TIME 60 @@ -286,6 +288,32 @@ static inline bool zebra_router_in_shutdown(void) return atomic_load_explicit(&zrouter.in_shutdown, memory_order_relaxed); } +#define FRR_PROTODOWN_REASON_DEFAULT_BIT 7 +/* Protodown bit setter/getter + * + * Allow users to change the bit if it conflicts with another + * on their system. + */ +static inline void if_netlink_set_frr_protodown_r_bit(uint8_t bit) +{ + zrouter.protodown_r_bit = bit; +} + +static inline void if_netlink_unset_frr_protodown_r_bit(void) +{ + zrouter.protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT; +} + +static inline bool if_netlink_frr_protodown_r_bit_is_set(void) +{ + return (zrouter.protodown_r_bit != FRR_PROTODOWN_REASON_DEFAULT_BIT); +} + +static inline uint8_t if_netlink_get_frr_protodown_r_bit(void) +{ + return zrouter.protodown_r_bit; +} + /* zebra_northbound.c */ extern const struct frr_yang_module_info frr_zebra_info; diff --git a/zebra/zebra_script.c b/zebra/zebra_script.c index 3f107cb48d45..9f8490ac66ff 100644 --- a/zebra/zebra_script.c +++ b/zebra/zebra_script.c @@ -415,6 +415,7 @@ void lua_pushzebra_dplane_ctx(lua_State *L, const struct zebra_dplane_ctx *ctx) /* Not currently handled */ case DPLANE_OP_INTF_NETCONFIG: /*NYI*/ case DPLANE_OP_NONE: + case DPLANE_OP_STARTUP_STAGE: break; } /* Dispatch by op code */ } diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index c8b652f54a36..e06733cb8c68 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -536,7 +536,7 @@ static uint8_t *ipCidrTable(struct variable *v, oid objid[], size_t *objid_len, return NULL; } -static int zebra_snmp_init(struct thread_master *tm) +static int zebra_snmp_init(struct event_loop *tm) { smux_init(tm); REGISTER_MIB("mibII/ipforward", zebra_variables, variable, ipfw_oid); diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 3365cdcdbaa8..b246d445dafa 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -376,6 +376,33 @@ struct zebra_vrf *zebra_vrf_alloc(struct vrf *vrf) return zvrf; } +/* + * Pending: create an efficient table_id (in a tree/hash) based lookup) + */ +vrf_id_t zebra_vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + + RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { + zvrf = vrf->info; + if (zvrf == NULL) + continue; + /* case vrf with netns : match the netnsid */ + if (vrf_is_backend_netns()) { + if (ns_id == zvrf_id(zvrf)) + return zvrf_id(zvrf); + } else { + /* VRF is VRF_BACKEND_VRF_LITE */ + if (zvrf->table_id != table_id) + continue; + return zvrf_id(zvrf); + } + } + + return VRF_DEFAULT; +} + /* Lookup VRF by identifier. */ struct zebra_vrf *zebra_vrf_lookup_by_id(vrf_id_t vrf_id) { diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index b23b7282610b..aef83cd8f172 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -237,6 +237,7 @@ extern struct route_table *zebra_vrf_get_table_with_table_id(afi_t afi, extern void zebra_vrf_update_all(struct zserv *client); extern struct zebra_vrf *zebra_vrf_lookup_by_id(vrf_id_t vrf_id); extern struct zebra_vrf *zebra_vrf_lookup_by_name(const char *); +extern vrf_id_t zebra_vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id); extern struct zebra_vrf *zebra_vrf_alloc(struct vrf *vrf); extern struct route_table *zebra_vrf_table(afi_t, safi_t, vrf_id_t); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 6d6143002999..d100dc0e6962 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -566,7 +566,7 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, if (re->mtu) vty_out(vty, ", mtu %u", re->mtu); if (re->vrf_id != VRF_DEFAULT) { - zvrf = vrf_info_lookup(re->vrf_id); + zvrf = zebra_vrf_lookup_by_id(re->vrf_id); vty_out(vty, ", vrf %s", zvrf_name(zvrf)); } if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) @@ -1179,12 +1179,12 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, json_object_string_add(json, "type", zebra_route_string(nhe->type)); json_object_int_add(json, "refCount", nhe->refcnt); - if (thread_is_scheduled(nhe->timer)) + if (event_is_scheduled(nhe->timer)) json_object_string_add( json, "timeToDeletion", - thread_timer_to_hhmmss(time_left, - sizeof(time_left), - nhe->timer)); + event_timer_to_hhmmss(time_left, + sizeof(time_left), + nhe->timer)); json_object_string_add(json, "uptime", up_str); json_object_string_add(json, "vrf", vrf_id_to_name(nhe->vrf_id)); @@ -1193,11 +1193,11 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe, vty_out(vty, "ID: %u (%s)\n", nhe->id, zebra_route_string(nhe->type)); vty_out(vty, " RefCnt: %u", nhe->refcnt); - if (thread_is_scheduled(nhe->timer)) + if (event_is_scheduled(nhe->timer)) vty_out(vty, " Time to Deletion: %s", - thread_timer_to_hhmmss(time_left, - sizeof(time_left), - nhe->timer)); + event_timer_to_hhmmss(time_left, + sizeof(time_left), + nhe->timer)); vty_out(vty, "\n"); vty_out(vty, " Uptime: %s\n", up_str); @@ -2706,7 +2706,7 @@ DEFUN (default_vrf_vni_mapping, struct zebra_vrf *zvrf = NULL; int filter = 0; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return CMD_WARNING; @@ -2745,7 +2745,7 @@ DEFUN (no_default_vrf_vni_mapping, vni_t vni = strtoul(argv[2]->arg, NULL, 10); struct zebra_vrf *zvrf = NULL; - zvrf = vrf_info_lookup(VRF_DEFAULT); + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); if (!zvrf) return CMD_WARNING; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 19f1839ac7c4..132862e690f6 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -194,7 +194,7 @@ static int l3vni_rmac_nh_list_cmp(void *p1, void *p2) const struct ipaddr *vtep_ip1 = p1; const struct ipaddr *vtep_ip2 = p2; - return !ipaddr_cmp(vtep_ip1, vtep_ip2); + return ipaddr_cmp(vtep_ip1, vtep_ip2); } static void l3vni_rmac_nh_free(struct ipaddr *vtep_ip) @@ -2582,19 +2582,18 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, struct zebra_mac *zrmac = NULL; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u doesn't exist\n", l3vni); return; @@ -2603,7 +2602,7 @@ void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, zrmac = zl3vni_rmac_lookup(zl3vni, rmac); if (!zrmac) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% Requested RMAC doesn't exist in L3-VNI %u\n", @@ -2624,13 +2623,18 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) struct rmac_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zl3vni = zl3vni_lookup(l3vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% L3-VNI %u does not exist\n", l3vni); return; @@ -2639,9 +2643,6 @@ void zebra_vxlan_print_rmacs_l3vni(struct vty *vty, vni_t l3vni, bool use_json) if (!num_rmacs) return; - if (use_json) - json = json_object_new_object(); - memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.json = json; @@ -2663,15 +2664,14 @@ void zebra_vxlan_print_rmacs_all_l3vni(struct vty *vty, bool use_json) json_object *json = NULL; void *args[2]; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; hash_iterate(zrouter.l3vni_table, @@ -2690,15 +2690,14 @@ void zebra_vxlan_print_specific_nh_l3vni(struct vty *vty, vni_t l3vni, struct zebra_neigh *n = NULL; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - /* If vni=0 passed, assume svd lookup */ if (!l3vni) n = svd_nh_lookup(ip); @@ -2799,15 +2798,14 @@ void zebra_vxlan_print_nh_all_l3vni(struct vty *vty, bool use_json) json_object *json = NULL; void *args[2]; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; hash_iterate(zrouter.l3vni_table, @@ -2828,24 +2826,23 @@ void zebra_vxlan_print_l3vni(struct vty *vty, vni_t vni, bool use_json) json_object *json = NULL; struct zebra_l3vni *zl3vni = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } zl3vni = zl3vni_lookup(vni); if (!zl3vni) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - if (use_json) - json = json_object_new_object(); - args[0] = vty; args[1] = json; zl3vni_print(zl3vni, (void *)args); @@ -2900,12 +2897,18 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -2914,9 +2917,6 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - /* 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. @@ -2951,12 +2951,14 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; void *args[3]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; @@ -2979,12 +2981,14 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, json_object *json = NULL; void *args[3]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; @@ -3008,12 +3012,18 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_neigh *n; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3026,8 +3036,6 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, vni); return; } - if (use_json) - json = json_object_new_object(); zebra_evpn_print_neigh(n, vty, json); @@ -3048,12 +3056,18 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3062,9 +3076,6 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - memset(&wctx, 0, sizeof(wctx)); wctx.zevpn = zevpn; wctx.vty = vty; @@ -3094,12 +3105,20 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, struct neigh_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { - vty_out(vty, "%% VNI %u does not exist\n", vni); + if (use_json) + vty_json(vty, json); + else + vty_out(vty, "%% VNI %u does not exist\n", vni); return; } @@ -3111,9 +3130,6 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, if (!num_neigh) return; - if (use_json) - json = json_object_new_object(); - /* 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. @@ -3155,8 +3171,12 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) @@ -3218,13 +3238,13 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, struct mac_walk_ctx wctx; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; @@ -3246,13 +3266,13 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, struct mac_walk_ctx wctx; json_object *json = NULL; + if (use_json) + json = json_object_new_object(); + if (!is_evpn_enabled()) { - if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); return; } - if (use_json) - json = json_object_new_object(); memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; @@ -3275,12 +3295,14 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct mac_walk_ctx wctx; json_object *json = NULL; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + memset(&wctx, 0, sizeof(wctx)); wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; @@ -3303,13 +3325,18 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, struct zebra_mac *mac; json_object *json = NULL; - if (!is_evpn_enabled()) + if (use_json) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; @@ -3317,7 +3344,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (use_json) - vty_out(vty, "{}\n"); + vty_json(vty, json); else vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", @@ -3325,10 +3352,8 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, return; } - if (use_json) - json = json_object_new_object(); - zebra_evpn_print_mac(mac, vty, json); + if (use_json) vty_json(vty, json); } @@ -3449,7 +3474,7 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, mac->detect_start_time.tv_sec = 0; mac->detect_start_time.tv_usec = 0; mac->dad_dup_detect_time = 0; - THREAD_OFF(mac->dad_mac_auto_recovery_timer); + EVENT_OFF(mac->dad_mac_auto_recovery_timer); /* warn-only action return */ if (!zvrf->dad_freeze) @@ -3531,7 +3556,7 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, 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); + EVENT_OFF(nbr->dad_ip_auto_recovery_timer); if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { zebra_evpn_neigh_send_add_to_client(zevpn->vni, ip, &nbr->emac, @@ -3566,7 +3591,7 @@ static void zevpn_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) mac->detect_start_time.tv_sec = 0; mac->detect_start_time.tv_usec = 0; mac->dad_dup_detect_time = 0; - THREAD_OFF(mac->dad_mac_auto_recovery_timer); + EVENT_OFF(mac->dad_mac_auto_recovery_timer); /* Remove all IPs as duplicate associcated with this MAC */ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { @@ -3693,8 +3718,11 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; json_object *json_mac = NULL; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } + zevpn = zebra_evpn_lookup(vni); if (!zevpn) { if (use_json) @@ -3745,12 +3773,14 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct zebra_l3vni *zl3vni = NULL; struct zebra_evpn *zevpn = NULL; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + args[0] = vty; args[1] = json; @@ -3787,8 +3817,13 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json_object *json = NULL; struct zebra_vrf *zvrf = NULL; - if (!is_evpn_enabled()) + if (uj) + json = json_object_new_object(); + + if (!is_evpn_enabled()) { + vty_json(vty, json); return; + } zvrf = zebra_vrf_get_evpn(); @@ -3797,7 +3832,6 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) num_vnis = num_l2vnis + num_l3vnis; if (uj) { - json = json_object_new_object(); json_object_string_add(json, "advertiseGatewayMacip", zvrf->advertise_gw_macip ? "Yes" : "No"); json_object_string_add(json, "advertiseSviMacip", @@ -3819,6 +3853,8 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json_object_int_add(json, "detectionTime", zvrf->dad_time); json_object_int_add(json, "detectionFreezeTime", zvrf->dad_freeze_time); + json_object_boolean_add(json, "isDetectionFreeze", + zvrf->dad_freeze); zebra_evpn_mh_json(json); } else { vty_out(vty, "L2 VNIs: %u\n", num_l2vnis); @@ -3858,12 +3894,15 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, json_object *json = NULL; void *args[2]; - if (!is_evpn_enabled()) - return; - if (use_json) json = json_object_new_object(); - else + + if (!is_evpn_enabled()) { + vty_json(vty, json); + return; + } + + if (!use_json) vty_out(vty, "%-10s %-4s %-21s %-8s %-8s %-15s %-37s\n", "VNI", "Type", "VxLAN IF", "# MACs", "# ARPs", "# Remote VTEPs", "Tenant VRF"); @@ -3941,8 +3980,11 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, struct zebra_ns *zns = NULL; struct zebra_evpn_show zes; - if (!is_evpn_enabled()) + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); return; + } zns = zebra_ns_lookup(NS_DEFAULT); if (!zns) @@ -5232,7 +5274,9 @@ int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf) vni = zl3vni->vni; zl3vni_del(zl3vni); - zebra_vxlan_handle_vni_transition(zvrf, vni, 0); + + if (!zrouter.in_shutdown) + zebra_vxlan_handle_vni_transition(zvrf, vni, 0); return 0; } diff --git a/zebra/zebra_vxlan_if.c b/zebra/zebra_vxlan_if.c index 08e07b60a299..3cc7e499bf6b 100644 --- a/zebra/zebra_vxlan_if.c +++ b/zebra/zebra_vxlan_if.c @@ -610,10 +610,7 @@ struct hash *zebra_vxlan_vni_table_create(void) void zebra_vxlan_vni_table_destroy(struct hash *vni_table) { - if (vni_table) { - hash_clean(vni_table, zebra_vxlan_vni_free); - hash_free(vni_table); - } + hash_clean_and_free(&vni_table, zebra_vxlan_vni_free); } int zebra_vxlan_if_vni_table_destroy(struct zebra_if *zif) diff --git a/zebra/zserv.c b/zebra/zserv.c index e064c27ad172..2db228b158d1 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -34,7 +34,7 @@ #include "lib/sockopt.h" /* for setsockopt_so_recvbuf, setsockopt... */ #include "lib/sockunion.h" /* for sockopt_reuseaddr, sockopt_reuseport */ #include "lib/stream.h" /* for STREAM_SIZE, stream (ptr only), ... */ -#include "lib/thread.h" /* for thread (ptr only), THREAD_ARG, ... */ +#include "frrevent.h" /* for thread (ptr only), EVENT_ARG, ... */ #include "lib/vrf.h" /* for vrf_info_lookup, VRF_DEFAULT */ #include "lib/vty.h" /* for vty_out, vty (ptr only) */ #include "lib/zclient.h" /* for zmsghdr, ZEBRA_HEADER_SIZE, ZEBRA... */ @@ -100,7 +100,7 @@ enum zserv_event { /* * Zebra server event driver for all client threads. * - * This is essentially a wrapper around thread_add_event() that centralizes + * This is essentially a wrapper around event_add_event() that centralizes * those scheduling calls into one place. * * All calls to this function schedule an event on the pthread running the @@ -118,7 +118,7 @@ static void zserv_client_event(struct zserv *client, /* * Zebra server event driver for the main thread. * - * This is essentially a wrapper around thread_add_event() that centralizes + * This is essentially a wrapper around event_add_event() that centralizes * those scheduling calls into one place. * * All calls to this function schedule an event on Zebra's main pthread. @@ -181,15 +181,16 @@ void zserv_log_message(const char *errmsg, struct stream *msg, */ static void zserv_client_fail(struct zserv *client) { - flog_warn(EC_ZEBRA_CLIENT_IO_ERROR, - "Client '%s' encountered an error and is shutting down.", - zebra_route_string(client->proto)); + flog_warn( + EC_ZEBRA_CLIENT_IO_ERROR, + "Client '%s' (session id %d) encountered an error and is shutting down.", + zebra_route_string(client->proto), client->session_id); atomic_store_explicit(&client->pthread->running, false, memory_order_relaxed); - THREAD_OFF(client->t_read); - THREAD_OFF(client->t_write); + EVENT_OFF(client->t_read); + EVENT_OFF(client->t_write); zserv_event(client, ZSERV_HANDLE_CLIENT_FAIL); } @@ -213,9 +214,9 @@ static void zserv_client_fail(struct zserv *client) * allows us to expose information about input and output queues to the user in * terms of number of packets rather than size of data. */ -static void zserv_write(struct thread *thread) +static void zserv_write(struct event *thread) { - struct zserv *client = THREAD_ARG(thread); + struct zserv *client = EVENT_ARG(thread); struct stream *msg; uint32_t wcmd = 0; struct stream_fifo *cache; @@ -306,9 +307,9 @@ static void zserv_write(struct thread *thread) * * Any failure in any of these actions is handled by terminating the client. */ -static void zserv_read(struct thread *thread) +static void zserv_read(struct event *thread) { - struct zserv *client = THREAD_ARG(thread); + struct zserv *client = EVENT_ARG(thread); int sock; size_t already; struct stream_fifo *cache; @@ -321,7 +322,7 @@ static void zserv_read(struct thread *thread) memory_order_relaxed); cache = stream_fifo_new(); p2p = p2p_orig; - sock = THREAD_FD(thread); + sock = EVENT_FD(thread); while (p2p) { ssize_t nb; @@ -462,12 +463,12 @@ static void zserv_client_event(struct zserv *client, { switch (event) { case ZSERV_CLIENT_READ: - thread_add_read(client->pthread->master, zserv_read, client, - client->sock, &client->t_read); + event_add_read(client->pthread->master, zserv_read, client, + client->sock, &client->t_read); break; case ZSERV_CLIENT_WRITE: - thread_add_write(client->pthread->master, zserv_write, client, - client->sock, &client->t_write); + event_add_write(client->pthread->master, zserv_write, client, + client->sock, &client->t_write); break; } } @@ -491,9 +492,9 @@ static void zserv_client_event(struct zserv *client, * rely on the read thread to handle queuing this task enough times to process * everything on the input queue. */ -static void zserv_process_messages(struct thread *thread) +static void zserv_process_messages(struct event *thread) { - struct zserv *client = THREAD_ARG(thread); + struct zserv *client = EVENT_ARG(thread); struct stream *msg; struct stream_fifo *cache = stream_fifo_new(); uint32_t p2p = zrouter.packets_to_process; @@ -507,8 +508,6 @@ static void zserv_process_messages(struct thread *thread) stream_fifo_push(cache, msg); } - msg = NULL; - /* Need to reschedule processing work if there are still * packets in the fifo. */ @@ -570,7 +569,7 @@ DEFINE_KOOH(zserv_client_close, (struct zserv *client), (client)); * - Free associated resources * - Free client structure * - * This does *not* take any action on the struct thread * fields. These are + * This does *not* take any action on the struct event * fields. These are * managed by the owning pthread and any tasks associated with them must have * been stopped prior to invoking this function. */ @@ -583,23 +582,27 @@ static void zserv_client_free(struct zserv *client) /* Close file descriptor. */ if (client->sock) { - unsigned long nroutes; - unsigned long nnhgs; + unsigned long nroutes = 0; + unsigned long nnhgs = 0; close(client->sock); if (DYNAMIC_CLIENT_GR_DISABLED(client)) { - zebra_mpls_client_cleanup_vrf_label(client->proto); + if (!client->synchronous) { + zebra_mpls_client_cleanup_vrf_label( + client->proto); - nroutes = rib_score_proto(client->proto, - client->instance); + 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)); /* Not worrying about instance for now */ - nnhgs = zebra_nhg_score_proto(client->proto); + if (!client->synchronous) + nnhgs = zebra_nhg_score_proto(client->proto); zlog_notice( "client %d disconnected %lu %s nhgs removed from the rib", client->sock, nnhgs, @@ -628,13 +631,13 @@ 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++) { - vrf_bitmap_free(client->redist[afi][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->nhrp_neighinfo[afi]); + vrf_bitmap_free(&client->redist_default[afi]); + vrf_bitmap_free(&client->ridinfo[afi]); + vrf_bitmap_free(&client->nhrp_neighinfo[afi]); } /* @@ -670,9 +673,9 @@ void zserv_close_client(struct zserv *client) zlog_debug("Closing client '%s'", zebra_route_string(client->proto)); - thread_cancel_event(zrouter.master, client); - THREAD_OFF(client->t_cleanup); - THREAD_OFF(client->t_process); + event_cancel_event(zrouter.master, client); + EVENT_OFF(client->t_cleanup); + EVENT_OFF(client->t_process); /* destroy pthread */ frr_pthread_destroy(client->pthread); @@ -709,9 +712,9 @@ void zserv_close_client(struct zserv *client) * already have been closed and the thread will most likely have died, but its * resources still need to be cleaned up. */ -static void zserv_handle_client_fail(struct thread *thread) +static void zserv_handle_client_fail(struct event *thread) { - struct zserv *client = THREAD_ARG(thread); + struct zserv *client = EVENT_ARG(thread); zserv_close_client(client); } @@ -752,10 +755,10 @@ static struct zserv *zserv_client_create(int sock) /* Initialize flags */ for (afi = AFI_IP; afi < AFI_MAX; afi++) { 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->nhrp_neighinfo[afi] = vrf_bitmap_init(); + vrf_bitmap_init(&client->redist[afi][i]); + vrf_bitmap_init(&client->redist_default[afi]); + vrf_bitmap_init(&client->ridinfo[afi]); + vrf_bitmap_init(&client->nhrp_neighinfo[afi]); } /* Add this client to linked list. */ @@ -831,9 +834,9 @@ void zserv_release_client(struct zserv *client) * main pthread. */ if (client->is_closed) - thread_add_event(zrouter.master, - zserv_handle_client_fail, - client, 0, &client->t_cleanup); + event_add_event(zrouter.master, + zserv_handle_client_fail, + client, 0, &client->t_cleanup); } } @@ -846,14 +849,14 @@ void zserv_release_client(struct zserv *client) /* * Accept socket connection. */ -static void zserv_accept(struct thread *thread) +static void zserv_accept(struct event *thread) { int accept_sock; int client_sock; struct sockaddr_in client; socklen_t len; - accept_sock = THREAD_FD(thread); + accept_sock = EVENT_FD(thread); /* Reregister myself. */ zserv_event(NULL, ZSERV_ACCEPT); @@ -953,16 +956,15 @@ void zserv_event(struct zserv *client, enum zserv_event event) { switch (event) { case ZSERV_ACCEPT: - thread_add_read(zrouter.master, zserv_accept, NULL, zsock, - NULL); + event_add_read(zrouter.master, zserv_accept, NULL, zsock, NULL); break; case ZSERV_PROCESS_MESSAGES: - thread_add_event(zrouter.master, zserv_process_messages, client, - 0, &client->t_process); + event_add_event(zrouter.master, zserv_process_messages, client, + 0, &client->t_process); break; case ZSERV_HANDLE_CLIENT_FAIL: - thread_add_event(zrouter.master, zserv_handle_client_fail, - client, 0, &client->t_cleanup); + event_add_event(zrouter.master, zserv_handle_client_fail, + client, 0, &client->t_cleanup); } } @@ -1149,14 +1151,10 @@ static void zebra_show_stale_client_detail(struct vty *vty, if (info->t_stale_removal) { vty_out(vty, "Stale delete timer: %ld sec\n", - thread_timer_remain_second( + event_timer_remain_second( info->t_stale_removal)); } } - vty_out(vty, "Current AFI : %d\n", info->current_afi); - if (info->current_prefix) - vty_out(vty, "Current prefix : %pFX\n", - info->current_prefix); } } vty_out(vty, "\n"); diff --git a/zebra/zserv.h b/zebra/zserv.h index 1226cd7115b2..90aa4d53f46d 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -19,7 +19,7 @@ #include "lib/vrf.h" /* for vrf_bitmap_t */ #include "lib/zclient.h" /* for redist_proto */ #include "lib/stream.h" /* for stream, stream_fifo */ -#include "lib/thread.h" /* for thread, thread_master */ +#include "frrevent.h" /* for thread, thread_master */ #include "lib/linklist.h" /* for list */ #include "lib/workqueue.h" /* for work_queue */ #include "lib/hook.h" /* for DECLARE_HOOK, DECLARE_KOOH */ @@ -51,9 +51,6 @@ 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; @@ -64,13 +61,12 @@ struct client_gr_info { 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]; + bool af_enabled[AFI_MAX]; + bool route_sync[AFI_MAX]; /* Book keeping */ - struct prefix *current_prefix; void *stale_client_ptr; - struct thread *t_stale_removal; + struct event *t_stale_removal; TAILQ_ENTRY(client_gr_info) gr_info; }; @@ -105,14 +101,14 @@ struct zserv { struct buffer *wb; /* Threads for read/write. */ - struct thread *t_read; - struct thread *t_write; + struct event *t_read; + struct event *t_write; /* Event for message processing, for the main pthread */ - struct thread *t_process; + struct event *t_process; /* Event for the main pthread */ - struct thread *t_cleanup; + struct event *t_cleanup; /* This client's redistribute flag. */ struct redist_proto mi_redist[AFI_MAX][ZEBRA_ROUTE_MAX]; @@ -378,7 +374,7 @@ void zserv_log_message(const char *errmsg, struct stream *msg, struct zmsghdr *hdr); /* TODO */ -__attribute__((__noreturn__)) void zebra_finalize(struct thread *event); +__attribute__((__noreturn__)) void zebra_finalize(struct event *event); /* * Graceful restart functions.