diff options
| -rw-r--r-- | bgpd/bgp_mplsvpn.c | 2 | ||||
| -rw-r--r-- | doc/developer/building-frr-for-openwrt.rst | 4 | ||||
| -rw-r--r-- | doc/developer/fuzzing.rst | 164 | ||||
| -rw-r--r-- | doc/developer/index.rst | 1 | ||||
| -rw-r--r-- | doc/user/basic.rst | 2 | ||||
| -rw-r--r-- | doc/user/ospf6d.rst | 7 | ||||
| -rw-r--r-- | doc/user/vrrp.rst | 2 | ||||
| -rw-r--r-- | lib/thread.c | 7 | ||||
| -rw-r--r-- | lib/thread.h | 4 | ||||
| -rw-r--r-- | lib/vrf.h | 2 | ||||
| -rw-r--r-- | nhrpd/nhrp_route.c | 12 | ||||
| -rw-r--r-- | ospf6d/ospf6_abr.c | 13 | ||||
| -rw-r--r-- | ospf6d/ospf6_bfd.c | 5 | ||||
| -rw-r--r-- | ospf6d/ospf6_bfd.h | 6 | ||||
| -rw-r--r-- | ospf6d/ospf6_interface.c | 2 | ||||
| -rw-r--r-- | ospf6d/ospf6_neighbor.c | 442 | ||||
| -rw-r--r-- | ospf6d/ospf6_neighbor.h | 2 | ||||
| -rw-r--r-- | ospfd/ospf_gr_helper.c | 16 | ||||
| -rw-r--r-- | ospfd/ospf_opaque.h | 2 | ||||
| -rw-r--r-- | tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py | 6 | ||||
| -rw-r--r-- | tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py | 6 | ||||
| -rw-r--r-- | tests/topotests/lib/common_config.py | 277 | ||||
| -rw-r--r-- | tests/topotests/route-scale/r1/installed.routes.json | 8 | ||||
| -rw-r--r-- | tests/topotests/route-scale/test_route_scale.py | 25 | ||||
| -rw-r--r-- | zebra/rt_netlink.c | 8 |
25 files changed, 610 insertions, 415 deletions
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 0c527efb8c..5877390377 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -265,7 +265,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, if (STREAM_READABLE(data) != 0) { flog_err( EC_BGP_UPDATE_RCV, - "%s [Error] Update packet error / VPN (%td data remaining after parsing)", + "%s [Error] Update packet error / VPN (%zu data remaining after parsing)", peer->host, STREAM_READABLE(data)); return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH; } diff --git a/doc/developer/building-frr-for-openwrt.rst b/doc/developer/building-frr-for-openwrt.rst index 9bd1296dad..47cf2cbd43 100644 --- a/doc/developer/building-frr-for-openwrt.rst +++ b/doc/developer/building-frr-for-openwrt.rst @@ -51,7 +51,7 @@ to work and it may be needed to run a ``make`` for the entire build environment. Add ``V=s`` to get more debugging output. More information about OpenWrt buildsystem can be found `here -<https://openwrt.org/docs/guide-developer/build-system/use-buildsystem>`_. +<https://openwrt.org/docs/guide-developer/build-system/use-buildsystem>`__. Work with sources ----------------- @@ -59,7 +59,7 @@ Work with sources To update to a newer version, or change other options, you need to edit the ``feeds/packages/frr/Makefile``. More information about working with patches in OpenWrt buildsystem can be found `here -<https://openwrt.org/docs/guide-developer/build-system/use-patches-with-buildsystem>`_. +<https://openwrt.org/docs/guide-developer/build-system/use-patches-with-buildsystem>`__. Usage ----- diff --git a/doc/developer/fuzzing.rst b/doc/developer/fuzzing.rst new file mode 100644 index 0000000000..8a3318745e --- /dev/null +++ b/doc/developer/fuzzing.rst @@ -0,0 +1,164 @@ +.. _fuzzing: + +Fuzzing +======= + +This page describes the fuzzing targets and supported fuzzers available in FRR +and how to use them. Familiarity with fuzzing techniques and tools is assumed. + +Overview +-------- + +It is well known that networked applications tend to be difficult to fuzz on +their network-facing attack surfaces. Approaches involving actual network +transmission tend to be slow and are subject to intermediate devices and +networking stacks which tend to drop fuzzed packets, especially if the fuzzing +surface covers IP itself. Some time was spent on fuzzing FRR this way with some +mediocre results but attention quickly turned towards skipping the actual +networking and instead adding fuzzing targets directly in the packet processing +code for use by more traditional in- and out-of-process fuzzers. Results from +this approach have been very fruitful. + +The patches to add fuzzing targets are kept in a separate git branch. Typically +it is better to keep them in the main branch so they are kept up to date and do +not need to be constantly synchronized with the main codebase. Unfortunately, +changes to FRR to support fuzzing necessarily extend far beyond the +entrypoints. Checksums must be disarmed, interactions with the kernel must be +skipped, sockets and files must be avoided, desired under/overflows must be +marked, etc. There are the usual ``LD_PRELOAD`` libraries to emulate these +things (preeny et al) but FRR is a very kernel-reliant program and these +libraries tend to create annoying problems when used with FRR for whatever +reason. Keeping this code in the main codebase is cluttering, difficult to work +with / around, and runs the risk of accidentally introducing bugs even if +``#ifdef``'d out. Consequently it's in a separate branch that is rebased on +``master`` from time to time. + + +Code +---- + +The git branch with fuzzing targets is located here: + +https://github.com/FRRouting/frr/tree/fuzz + +To build libFuzzer targets, pass ``--enable-libfuzzer`` to ``configure``. +To build AFL targets, compile with ``afl-clang`` as usual. + +Fuzzing with sanitizers is strongly recommended, especially ASAN, which you can +enable by passing ``--enable-address-sanitizer`` to ``configure``. + +Suggested UBSAN flags: ``-fsanitize-recover=unsigned-integer-overflow,implicit-conversion -fsanitize=unsigned-integer-overflow,implicit-conversion,nullability-arg,nullability-assign,nullability-return`` +Recommended cflags: ``-Wno-all -g3 -O3 -funroll-loops`` + +Design +------ + +All fuzzing targets have support for libFuzzer and AFL. This is done by writing +the target as a libFuzzer entrypoint (``LLVMFuzzerTestOneInput()``) and calling +it from the AFL entrypoint in ``main()``. New targets should use this rule. + +When adding AFL entrypoints, it's a good idea to use AFL persistent mode for +better performance. Grep ``bgpd/bgp_main.c`` for ``__AFL_INIT()`` for an +example of how to do this in FRR. Typically it involves moving all internal +daemon setup into a setup function. Then this setup function is called exactly +once for the lifetime of the process. In ``LLVMFuzzerTestOneInput()`` this +means you need to call it at the start of the function protected by a static +boolean that is set to true, since that function is your entrypoint. You also +need to call it prior to ``__AFL_INIT()`` in ``main()`` because ``main()`` is +your entrypoint in the AFL case. + +Adding support to daemons +^^^^^^^^^^^^^^^^^^^^^^^^^ + +This section describes how to add entrypoints to daemons that do not have any +yet. + +Because libFuzzer has its own ``main()`` function, when adding fuzzing support +to a daemon that doesn't have any targets already, ``main()`` needs to be +``#ifdef``'d out like so: + +.. code:: c + + #ifndef FUZZING_LIBFUZZER + + int main(int argc, char **argv) + { + ... + } + + #endif /* FUZZING_LIBFUZZER */ + + +The ``FUZZING_LIBFUZZER`` macro is set by ``--enable-libfuzzer``. + +Because libFuzzer can only be linked into daemons that have +``LLVMFuzzerTestOneInput()`` implemented, we can't pass ``-fsanitize=fuzzer`` +to all daemons in ``AM_CFLAGS``. It needs to go into a variable specific to +each daemon. Since it can be thought of as a kind of sanitizer, for daemons +that have libFuzzer support there are now individual flags variables for those +daemons named ``DAEMON_SAN_FLAGS`` (e.g. ``BGPD_SAN_FLAGS``, +``ZEBRA_SAN_FLAGS``). This variable has the contents of the generic +``SAN_FLAGS`` plus any fuzzing-related flags. It is used in daemons' +``subdir.am`` in place of ``SAN_FLAGS``. Daemons that don't support libFuzzer +still use ``SAN_FLAGS``. If you want to add fuzzing support to a daemon you +need to do this flag variable conversion; look at ``configure.ac`` for +examples, it is fairly straightforward. Remember to update ``subdir.am`` to use +the new variable. + +Do note that when fuzzing is enabled, ``SAN_FLAGS`` gains +``-fsanitize=fuzzer-no-link``; the result is that all daemons are instrumented +for fuzzing but only the ones with ``LLVMFuzzerTestOneInput()`` actually get +linked with libFuzzer. + + +Targets +------- + +A given daemon can have lots of different paths that are interesting to fuzz. +There's not really a great way to handle this, most fuzzers assume the program +has one entrypoint. The approach taken in FRR for multiple entrypoints is to +control which path is taken within ``LLVMFuzzerTestOneInput()`` using +``#ifdef`` and passing whatever controlling macro definition you want. Take a +look at that function for the daemon you're interested in fuzzing, pick the +target, add ``#define MY_TARGET 1`` somewhere before the ``#ifdef`` switch, +recompile. + +.. list-table:: Fuzzing Targets + + * - Daemon + - Target + - Fuzzers + * - bgpd + - packet parser + - libfuzzer, afl + * - ospfd + - packet parser + - libfuzzer, afl + * - pimd + - packet parser + - libfuzzer, afl + * - vrrpd + - packet parser + - libfuzzer, afl + * - vrrpd + - zapi parser + - libfuzzer, afl + * - zebra + - netlink + - libfuzzer, afl + * - zebra + - zserv / zapi + - libfuzzer, afl + + +Fuzzer Notes +------------ + +Some interesting seed corpuses for various daemons are available `here +<https://github.com/qlyoung/frr-fuzz/tree/master/samples>`_. + +For libFuzzer, you need to pass ``-rss_limit_mb=0`` if you are fuzzing with +ASAN enabled, as you should. + +For AFL, afl++ is strongly recommended; afl proper isn't really maintained +anymore. diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 1ba0f31c8a..5a7da806ff 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -9,6 +9,7 @@ FRRouting Developer's Guide packaging process-architecture library + fuzzing tracing testing bgpd diff --git a/doc/user/basic.rst b/doc/user/basic.rst index 0bdcccaf74..7a450bec53 100644 --- a/doc/user/basic.rst +++ b/doc/user/basic.rst @@ -26,7 +26,7 @@ forms the initial command set for a routing beast as it is starting. Config files are generally found in |INSTALL_PREFIX_ETC|. Config Methods -^^^^^^^^^^^^^^ +-------------- There are two ways of configuring FRR. diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index 6295ba9293..dd53d8f8b4 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -179,10 +179,11 @@ Showing OSPF6 information To see OSPF interface configuration like costs. -.. index:: show ipv6 ospf6 neighbor -.. clicmd:: show ipv6 ospf6 neighbor +.. index:: show ipv6 ospf6 neighbor [json] +.. clicmd:: show ipv6 ospf6 neighbor [json] - Shows state and chosen (Backup) DR of neighbor. + Shows state and chosen (Backup) DR of neighbor. JSON output can be + obtained by appending 'json' at the end. .. index:: show ipv6 ospf6 request-list A.B.C.D .. clicmd:: show ipv6 ospf6 request-list A.B.C.D diff --git a/doc/user/vrrp.rst b/doc/user/vrrp.rst index 8ab050e9a0..cb70da3f3b 100644 --- a/doc/user/vrrp.rst +++ b/doc/user/vrrp.rst @@ -503,6 +503,7 @@ The following configuration is then generated for you: vrrp 5 ip 10.0.2.16 vrrp 5 ipv6 2001:db8::370:7334 + VRRP is automatically activated. Global defaults, if set, are applied. You can then edit this configuration with **vtysh** as needed, and commit it by @@ -516,6 +517,7 @@ My virtual routers are not seeing each others' advertisements ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Check: + - Is your kernel at least 5.1? - Did you set the macvlan devices to ``bridge`` mode? - If using IPv4 virtual addresses, does the parent of the macvlan devices have diff --git a/lib/thread.c b/lib/thread.c index db53e267f8..e71fd74bd9 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -115,10 +115,9 @@ static void vty_out_cpu_thread_history(struct vty *vty, struct cpu_thread_history *a) { vty_out(vty, "%5zu %10zu.%03zu %9zu %8zu %9zu %8zu %9zu", - (size_t)a->total_active, a->cpu.total / 1000, - a->cpu.total % 1000, (size_t)a->total_calls, - (size_t)(a->cpu.total / a->total_calls), a->cpu.max, - (size_t)(a->real.total / a->total_calls), a->real.max); + a->total_active, a->cpu.total / 1000, a->cpu.total % 1000, + a->total_calls, (a->cpu.total / a->total_calls), a->cpu.max, + (a->real.total / a->total_calls), a->real.max); vty_out(vty, " %c%c%c%c%c %s\n", a->types & (1 << THREAD_READ) ? 'R' : ' ', a->types & (1 << THREAD_WRITE) ? 'W' : ' ', diff --git a/lib/thread.h b/lib/thread.h index 682a17b9f3..eb1b107e7b 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -115,8 +115,8 @@ struct thread { struct cpu_thread_history { int (*func)(struct thread *); - atomic_uint_fast32_t total_calls; - atomic_uint_fast32_t total_active; + atomic_size_t total_calls; + atomic_size_t total_active; struct time_stats { atomic_size_t total, max; } real; @@ -43,7 +43,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX }; #endif #define VRF_NAMSIZ 36 -#define NS_NAMSIZ 16 +#define NS_NAMSIZ 36 /* * The command strings diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c index 2bc2d91597..e7d35b90ff 100644 --- a/nhrpd/nhrp_route.c +++ b/nhrpd/nhrp_route.c @@ -350,10 +350,22 @@ void nhrp_zebra_init(void) zclient_init(zclient, ZEBRA_ROUTE_NHRP, 0, &nhrpd_privs); } +static void nhrp_table_node_cleanup(struct route_table *table, + struct route_node *node) +{ + if (!node->info) + return; + + XFREE(MTYPE_NHRP_ROUTE, node->info); +} + void nhrp_zebra_terminate(void) { zclient_stop(zclient); zclient_free(zclient); + + zebra_rib[AFI_IP]->cleanup = nhrp_table_node_cleanup; + zebra_rib[AFI_IP6]->cleanup = nhrp_table_node_cleanup; route_table_finish(zebra_rib[AFI_IP]); route_table_finish(zebra_rib[AFI_IP6]); } diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index f087289df6..c71b30a2d4 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -196,7 +196,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, if (ADV_ROUTER_IN_PREFIX(&route->prefix) == area->ospf6->router_id) { zlog_debug( - "%s: Skipping ASBR announcement for ABR (%pFX)", + "%s: Skipping ASBR announcement for ABR (%pI4)", __func__, &ADV_ROUTER_IN_PREFIX(&route->prefix)); return 0; @@ -208,7 +208,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, || IS_OSPF6_DEBUG_ORIGINATE(INTER_ROUTER)) { is_debug++; zlog_debug( - "Originating summary in area %s for ASBR %pFX", + "Originating summary in area %s for ASBR %pI4", area->name, &ADV_ROUTER_IN_PREFIX(&route->prefix)); } @@ -225,9 +225,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, if (is_debug) zlog_debug( "%s: route %pFX with cost %u is not best, ignore.", - __func__, - &ADV_ROUTER_IN_PREFIX( - &route->prefix), + __func__, &route->prefix, route->path.cost); return 0; } @@ -405,8 +403,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, if (is_debug) zlog_debug( "prefix %pFX was denied by export list", - &ADV_ROUTER_IN_PREFIX( - &route->prefix)); + &route->prefix); return 0; } } @@ -418,7 +415,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route, if (is_debug) zlog_debug( "prefix %pFX was denied by filter-list out", - &ADV_ROUTER_IN_PREFIX(&route->prefix)); + &route->prefix); return 0; } diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c index 4e50ab5244..523ed7f181 100644 --- a/ospf6d/ospf6_bfd.c +++ b/ospf6d/ospf6_bfd.c @@ -55,12 +55,13 @@ void ospf6_bfd_info_free(void **bfd_info) /* * ospf6_bfd_show_info - Show BFD info structure */ -void ospf6_bfd_show_info(struct vty *vty, void *bfd_info, int param_only) +void ospf6_bfd_show_info(struct vty *vty, void *bfd_info, int param_only, + json_object *json_obj, bool use_json) { if (param_only) bfd_show_param(vty, bfd_info, 1, 0, 0, NULL); else - bfd_show_info(vty, bfd_info, 0, 1, 0, NULL); + bfd_show_info(vty, bfd_info, 0, 1, use_json, json_obj); } /* diff --git a/ospf6d/ospf6_bfd.h b/ospf6d/ospf6_bfd.h index 19dff1ff7c..b4e798e911 100644 --- a/ospf6d/ospf6_bfd.h +++ b/ospf6d/ospf6_bfd.h @@ -19,7 +19,7 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - +#include "lib/json.h" #ifndef OSPF6_BFD_H #define OSPF6_BFD_H @@ -35,8 +35,8 @@ extern void ospf6_bfd_info_nbr_create(struct ospf6_interface *oi, extern void ospf6_bfd_info_free(void **bfd_info); -extern void ospf6_bfd_show_info(struct vty *vty, void *bfd_info, - int param_only); +extern void ospf6_bfd_show_info(struct vty *vty, void *bfd_info, int param_only, + json_object *json_obj, bool use_json); extern void ospf6_bfd_reg_dereg_nbr(struct ospf6_neighbor *on, int command); #endif /* OSPF6_BFD_H */ diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index 75917b9d85..2d1b5e7b5a 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -996,7 +996,7 @@ static int ospf6_interface_show(struct vty *vty, struct interface *ifp) (oi->thread_send_lsack ? "on" : "off")); for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) vty_out(vty, " %s\n", lsa->name); - ospf6_bfd_show_info(vty, oi->bfd_info, 1); + ospf6_bfd_show_info(vty, oi->bfd_info, 1, NULL, false); return 0; } diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c index 9f13ecffa1..c1905e8c1e 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -44,6 +44,7 @@ #include "ospf6_lsa.h" #include "ospf6_spf.h" #include "ospf6_zebra.h" +#include "lib/json.h" DEFINE_HOOK(ospf6_neighbor_change, (struct ospf6_neighbor * on, int state, int next_state), @@ -595,7 +596,8 @@ int inactivity_timer(struct thread *thread) /* vty functions */ /* show neighbor structure */ -static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on) +static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on, + json_object *json_array, bool use_json) { char router_id[16]; char duration[64]; @@ -603,6 +605,7 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on) char nstate[16]; char deadtime[64]; long h, m, s; + json_object *json_route; /* Router-ID (Name) */ inet_ntop(AF_INET, &on->router_id, router_id, sizeof(router_id)); @@ -641,23 +644,43 @@ static void ospf6_neighbor_show(struct vty *vty, struct ospf6_neighbor *on) /* vty_out (vty, "%-15s %3d %11s %6s/%-12s %11s %s[%s]\n", - "Neighbor ID", "Pri", "DeadTime", "State", "", "Duration", - "I/F", "State"); + "Neighbor ID", "Pri", "DeadTime", "State", "IfState", + "Duration", "I/F", "State"); */ - - vty_out(vty, "%-15s %3d %11s %8s/%-12s %11s %s[%s]\n", router_id, - on->priority, deadtime, ospf6_neighbor_state_str[on->state], - nstate, duration, on->ospf6_if->interface->name, - ospf6_interface_state_str[on->ospf6_if->state]); + if (use_json) { + json_route = json_object_new_object(); + + json_object_string_add(json_route, "neighborId", router_id); + json_object_int_add(json_route, "priority", on->priority); + json_object_string_add(json_route, "deadTime", deadtime); + json_object_string_add(json_route, "state", + ospf6_neighbor_state_str[on->state]); + json_object_string_add(json_route, "ifState", nstate); + json_object_string_add(json_route, "duration", duration); + json_object_string_add(json_route, "interfaceName", + on->ospf6_if->interface->name); + json_object_string_add( + json_route, "interfaceState", + ospf6_interface_state_str[on->ospf6_if->state]); + + json_object_array_add(json_array, json_route); + } else + vty_out(vty, "%-15s %3d %11s %8s/%-12s %11s %s[%s]\n", + router_id, on->priority, deadtime, + ospf6_neighbor_state_str[on->state], nstate, duration, + on->ospf6_if->interface->name, + ospf6_interface_state_str[on->ospf6_if->state]); } static void ospf6_neighbor_show_drchoice(struct vty *vty, - struct ospf6_neighbor *on) + struct ospf6_neighbor *on, + json_object *json_array, bool use_json) { char router_id[16]; char drouter[16], bdrouter[16]; char duration[64]; struct timeval now, res; + json_object *json_route; /* vty_out (vty, "%-15s %6s/%-11s %-15s %-15s %s[%s]\n", @@ -673,19 +696,39 @@ static void ospf6_neighbor_show_drchoice(struct vty *vty, timersub(&now, &on->last_changed, &res); timerstring(&res, duration, sizeof(duration)); - vty_out(vty, "%-15s %8s/%-11s %-15s %-15s %s[%s]\n", router_id, - ospf6_neighbor_state_str[on->state], duration, drouter, - bdrouter, on->ospf6_if->interface->name, - ospf6_interface_state_str[on->ospf6_if->state]); + if (use_json) { + json_route = json_object_new_object(); + json_object_string_add(json_route, "routerId", router_id); + json_object_string_add(json_route, "state", + ospf6_neighbor_state_str[on->state]); + json_object_string_add(json_route, "duration", duration); + json_object_string_add(json_route, "dRouter", drouter); + json_object_string_add(json_route, "bdRouter", bdrouter); + json_object_string_add(json_route, "interfaceName", + on->ospf6_if->interface->name); + json_object_string_add( + json_route, "interfaceState", + ospf6_interface_state_str[on->ospf6_if->state]); + + json_object_array_add(json_array, json_route); + } else + vty_out(vty, "%-15s %8s/%-11s %-15s %-15s %s[%s]\n", router_id, + ospf6_neighbor_state_str[on->state], duration, drouter, + bdrouter, on->ospf6_if->interface->name, + ospf6_interface_state_str[on->ospf6_if->state]); } static void ospf6_neighbor_show_detail(struct vty *vty, - struct ospf6_neighbor *on) + struct ospf6_neighbor *on, + json_object *json, bool use_json) { char drouter[16], bdrouter[16]; char linklocal_addr[64], duration[32]; struct timeval now, res; struct ospf6_lsa *lsa, *lsanext; + json_object *json_neighbor; + json_object *json_array; + char db_desc_str[20]; inet_ntop(AF_INET6, &on->linklocal_addr, linklocal_addr, sizeof(linklocal_addr)); @@ -696,149 +739,333 @@ static void ospf6_neighbor_show_detail(struct vty *vty, timersub(&now, &on->last_changed, &res); timerstring(&res, duration, sizeof(duration)); - vty_out(vty, " Neighbor %s\n", on->name); - vty_out(vty, " Area %s via interface %s (ifindex %d)\n", - on->ospf6_if->area->name, on->ospf6_if->interface->name, - on->ospf6_if->interface->ifindex); - vty_out(vty, " His IfIndex: %d Link-local address: %s\n", - on->ifindex, linklocal_addr); - vty_out(vty, " State %s for a duration of %s\n", - ospf6_neighbor_state_str[on->state], duration); - vty_out(vty, " His choice of DR/BDR %s/%s, Priority %d\n", drouter, - bdrouter, on->priority); - vty_out(vty, " DbDesc status: %s%s%s SeqNum: %#lx\n", - (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) ? "Initial " - : ""), - (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) ? "More " : ""), - (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) ? "Master" - : "Slave"), - (unsigned long)ntohl(on->dbdesc_seqnum)); - - vty_out(vty, " Summary-List: %d LSAs\n", on->summary_list->count); - for (ALL_LSDB(on->summary_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - vty_out(vty, " Request-List: %d LSAs\n", on->request_list->count); - for (ALL_LSDB(on->request_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - vty_out(vty, " Retrans-List: %d LSAs\n", on->retrans_list->count); - for (ALL_LSDB(on->retrans_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - timerclear(&res); - if (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, - (on->thread_send_dbdesc ? "on" : "off")); - for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - timerclear(&res); - if (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, - (on->thread_send_lsreq ? "on" : "off")); - for (ALL_LSDB(on->request_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - timerclear(&res); - if (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, - (on->thread_send_lsupdate ? "on" : "off")); - for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - timerclear(&res); - if (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, - (on->thread_send_lsack ? "on" : "off")); - for (ALL_LSDB(on->lsack_list, lsa, lsanext)) - vty_out(vty, " %s\n", lsa->name); - - ospf6_bfd_show_info(vty, on->bfd_info, 0); + if (use_json) { + json_neighbor = json_object_new_object(); + json_object_string_add(json_neighbor, "area", + on->ospf6_if->area->name); + json_object_string_add(json_neighbor, "interface", + on->ospf6_if->interface->name); + json_object_int_add(json_neighbor, "interfaceIndex", + on->ospf6_if->interface->ifindex); + json_object_int_add(json_neighbor, "neighborInterfaceIndex", + on->ifindex); + json_object_string_add(json_neighbor, "linkLocalAddress", + linklocal_addr); + json_object_string_add(json_neighbor, "neighborState", + ospf6_neighbor_state_str[on->state]); + json_object_string_add(json_neighbor, "neighborStateDuration", + duration); + json_object_string_add(json_neighbor, "neighborDRouter", + drouter); + json_object_string_add(json_neighbor, "neighborBdRouter", + bdrouter); + snprintf(db_desc_str, sizeof(db_desc_str), "%s%s%s", + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) + ? "Initial " + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) + ? "More" + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) + ? "Master" + : "Slave")); + json_object_string_add(json_neighbor, "dbDescStatus", + db_desc_str); + + json_object_int_add(json_neighbor, "dbDescSeqNumber", + (unsigned long)ntohl(on->dbdesc_seqnum)); + + json_array = json_object_new_array(); + json_object_int_add(json_neighbor, "summaryListCount", + on->summary_list->count); + for (ALL_LSDB(on->summary_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "summaryListLsa", + json_array); + + json_array = json_object_new_array(); + json_object_int_add(json_neighbor, "requestListCount", + on->request_list->count); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "requestListLsa", + json_array); + + json_array = json_object_new_array(); + json_object_int_add(json_neighbor, "reTransListCount", + on->retrans_list->count); + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "reTransListLsa", + json_array); + + + timerclear(&res); + if (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", + on->dbdesc_list->count); + json_object_string_add(json_neighbor, "pendingLsaDbDescTime", + duration); + json_object_string_add(json_neighbor, "dbDescSendThread", + (on->thread_send_dbdesc ? "on" : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaDbDesc", + json_array); + + timerclear(&res); + if (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", + on->request_list->count); + json_object_string_add(json_neighbor, "pendingLsaLsReqTime", + duration); + json_object_string_add(json_neighbor, "lsReqSendThread", + (on->thread_send_lsreq ? "on" : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaLsReq", + json_array); + + + timerclear(&res); + if (on->thread_send_lsupdate) + timersub(&on->thread_send_lsupdate->u.sands, &now, + &res); + timerstring(&res, duration, sizeof(duration)); + json_object_int_add(json_neighbor, "pendingLsaLsUpdateCount", + on->lsupdate_list->count); + json_object_string_add(json_neighbor, "pendingLsaLsUpdateTime", + duration); + json_object_string_add( + json_neighbor, "lsUpdateSendThread", + (on->thread_send_lsupdate ? "on" : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaLsUpdate", + json_array); + + timerclear(&res); + if (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", + on->lsack_list->count); + json_object_string_add(json_neighbor, "pendingLsaLsAckTime", + duration); + json_object_string_add(json_neighbor, "lsAckSendThread", + (on->thread_send_lsack ? "on" : "off")); + json_array = json_object_new_array(); + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) + json_object_array_add( + json_array, json_object_new_string(lsa->name)); + json_object_object_add(json_neighbor, "pendingLsaLsAck", + json_array); + + ospf6_bfd_show_info(vty, on->bfd_info, 0, json_neighbor, + use_json); + + json_object_object_add(json, on->name, json_neighbor); + + + } else { + vty_out(vty, " Neighbor %s\n", on->name); + vty_out(vty, " Area %s via interface %s (ifindex %d)\n", + on->ospf6_if->area->name, on->ospf6_if->interface->name, + on->ospf6_if->interface->ifindex); + vty_out(vty, " His IfIndex: %d Link-local address: %s\n", + on->ifindex, linklocal_addr); + vty_out(vty, " State %s for a duration of %s\n", + ospf6_neighbor_state_str[on->state], duration); + vty_out(vty, " His choice of DR/BDR %s/%s, Priority %d\n", + drouter, bdrouter, on->priority); + vty_out(vty, " DbDesc status: %s%s%s SeqNum: %#lx\n", + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT) + ? "Initial " + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MBIT) + ? "More " + : ""), + (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT) + ? "Master" + : "Slave"), + (unsigned long)ntohl(on->dbdesc_seqnum)); + + vty_out(vty, " Summary-List: %d LSAs\n", + on->summary_list->count); + for (ALL_LSDB(on->summary_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + vty_out(vty, " Request-List: %d LSAs\n", + on->request_list->count); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + vty_out(vty, " Retrans-List: %d LSAs\n", + on->retrans_list->count); + for (ALL_LSDB(on->retrans_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (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, + (on->thread_send_dbdesc ? "on" : "off")); + for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (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, + (on->thread_send_lsreq ? "on" : "off")); + for (ALL_LSDB(on->request_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (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, + (on->thread_send_lsupdate ? "on" : "off")); + for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + timerclear(&res); + if (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, + (on->thread_send_lsack ? "on" : "off")); + for (ALL_LSDB(on->lsack_list, lsa, lsanext)) + vty_out(vty, " %s\n", lsa->name); + + ospf6_bfd_show_info(vty, on->bfd_info, 0, NULL, use_json); + } } DEFUN (show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd, - "show ipv6 ospf6 neighbor [<detail|drchoice>]", + "show ipv6 ospf6 neighbor [<detail|drchoice>] [json]", SHOW_STR IP6_STR OSPF6_STR "Neighbor list\n" "Display details\n" - "Display DR choices\n") + "Display DR choices\n" + JSON_STR) { int idx_type = 4; struct ospf6_neighbor *on; struct ospf6_interface *oi; struct ospf6_area *oa; struct listnode *i, *j, *k; - void (*showfunc)(struct vty *, struct ospf6_neighbor *); struct ospf6 *ospf6; + json_object *json = NULL; + json_object *json_array = NULL; + bool uj = use_json(argc, argv); + void (*showfunc)(struct vty *, struct ospf6_neighbor *, + json_object *json, bool use_json); ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); showfunc = ospf6_neighbor_show; - if (argc == 5) { + if ((uj && argc == 6) || (!uj && argc == 5)) { if (!strncmp(argv[idx_type]->arg, "de", 2)) showfunc = ospf6_neighbor_show_detail; else if (!strncmp(argv[idx_type]->arg, "dr", 2)) showfunc = ospf6_neighbor_show_drchoice; } - if (showfunc == ospf6_neighbor_show) - vty_out(vty, "%-15s %3s %11s %8s/%-12s %11s %s[%s]\n", - "Neighbor ID", "Pri", "DeadTime", "State", "IfState", - "Duration", "I/F", "State"); - else if (showfunc == ospf6_neighbor_show_drchoice) - vty_out(vty, "%-15s %8s/%-11s %-15s %-15s %s[%s]\n", "RouterID", - "State", "Duration", "DR", "BDR", "I/F", "State"); + if (uj) { + json = json_object_new_object(); + json_array = json_object_new_array(); + } else { + if (showfunc == ospf6_neighbor_show) + vty_out(vty, "%-15s %3s %11s %8s/%-12s %11s %s[%s]\n", + "Neighbor ID", "Pri", "DeadTime", "State", + "IfState", "Duration", "I/F", "State"); + else if (showfunc == ospf6_neighbor_show_drchoice) + vty_out(vty, "%-15s %8s/%-11s %-15s %-15s %s[%s]\n", + "RouterID", "State", "Duration", "DR", "BDR", + "I/F", "State"); + } for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) - for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) - (*showfunc)(vty, on); - + for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) { + if (showfunc == ospf6_neighbor_show_detail) + (*showfunc)(vty, on, json, uj); + else + (*showfunc)(vty, on, json_array, uj); + } + + if (uj) { + if (showfunc != ospf6_neighbor_show_detail) + json_object_object_add(json, "neighbors", json_array); + else + json_object_free(json_array); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } return CMD_SUCCESS; } DEFUN (show_ipv6_ospf6_neighbor_one, show_ipv6_ospf6_neighbor_one_cmd, - "show ipv6 ospf6 neighbor A.B.C.D", + "show ipv6 ospf6 neighbor A.B.C.D [json]", SHOW_STR IP6_STR OSPF6_STR "Neighbor list\n" "Specify Router-ID as IPv4 address notation\n" - ) + JSON_STR) { int idx_ipv4 = 4; struct ospf6_neighbor *on; struct ospf6_interface *oi; struct ospf6_area *oa; struct listnode *i, *j, *k; - void (*showfunc)(struct vty *, struct ospf6_neighbor *); + void (*showfunc)(struct vty *, struct ospf6_neighbor *, + json_object *json, bool use_json); uint32_t router_id; struct ospf6 *ospf6; + json_object *json = NULL; + bool uj = use_json(argc, argv); ospf6 = ospf6_lookup_by_vrf_name(VRF_DEFAULT_NAME); OSPF6_CMD_CHECK_RUNNING(ospf6); showfunc = ospf6_neighbor_show_detail; + if (uj) + json = json_object_new_object(); if ((inet_pton(AF_INET, argv[idx_ipv4]->arg, &router_id)) != 1) { vty_out(vty, "Router-ID is not parsable: %s\n", @@ -849,8 +1076,15 @@ DEFUN (show_ipv6_ospf6_neighbor_one, for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, i, oa)) for (ALL_LIST_ELEMENTS_RO(oa->if_list, j, oi)) for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, k, on)) - (*showfunc)(vty, on); + (*showfunc)(vty, on, json, uj); + + if (uj) { + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } return CMD_SUCCESS; } diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index 1a45a1966a..94300ff2ba 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -64,7 +64,7 @@ struct ospf6_neighbor { /* Options field (Capability) */ char options[3]; - /* IPaddr of I/F on our side link */ + /* IPaddr of I/F on neighbour's link */ struct in6_addr linklocal_addr; /* For Database Exchange */ diff --git a/ospfd/ospf_gr_helper.c b/ospfd/ospf_gr_helper.c index 9c029a49ba..a86e1b8401 100644 --- a/ospfd/ospf_gr_helper.c +++ b/ospfd/ospf_gr_helper.c @@ -251,7 +251,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, /* Check TLV len against overall LSA */ if (sum + TLV_SIZE(tlvh) > length) { if (IS_DEBUG_OSPF_GR_HELPER) - zlog_debug("%s: Malformed packet: Invalid TLV len:%zu", + zlog_debug("%s: Malformed packet: Invalid TLV len:%u", __func__, TLV_SIZE(tlvh)); return OSPF_GR_FAILURE; } @@ -260,7 +260,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, case GRACE_PERIOD_TYPE: if (TLV_SIZE(tlvh) < sizeof(struct grace_tlv_graceperiod)) { - zlog_debug("%s: Malformed packet: Invalid grace TLV len:%zu", + zlog_debug("%s: Malformed packet: Invalid grace TLV len:%u", __func__, TLV_SIZE(tlvh)); return OSPF_GR_FAILURE; } @@ -277,7 +277,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, case RESTART_REASON_TYPE: if (TLV_SIZE(tlvh) < sizeof(struct grace_tlv_restart_reason)) { - zlog_debug("%s: Malformed packet: Invalid reason TLV len:%zu", + zlog_debug("%s: Malformed packet: Invalid reason TLV len:%u", __func__, TLV_SIZE(tlvh)); return OSPF_GR_FAILURE; } @@ -292,7 +292,7 @@ static int ospf_extract_grace_lsa_fields(struct ospf_lsa *lsa, case RESTARTER_IP_ADDR_TYPE: if (TLV_SIZE(tlvh) < sizeof(struct grace_tlv_restart_addr)) { - zlog_debug("%s: Malformed packet: Invalid addr TLV len:%zu", + zlog_debug("%s: Malformed packet: Invalid addr TLV len:%u", __func__, TLV_SIZE(tlvh)); return OSPF_GR_FAILURE; } @@ -1018,7 +1018,7 @@ static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa) tlvh = TLV_HDR_NEXT(tlvh)) { /* Check TLV len */ if (sum + TLV_SIZE(tlvh) > length) { - vty_out(vty, "%% Invalid TLV length: %zu\n", + vty_out(vty, "%% Invalid TLV length: %u\n", TLV_SIZE(tlvh)); return; } @@ -1028,7 +1028,7 @@ static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa) if (TLV_SIZE(tlvh) < sizeof(struct grace_tlv_graceperiod)) { vty_out(vty, - "%% Invalid grace TLV length %zu\n", + "%% Invalid grace TLV length %u\n", TLV_SIZE(tlvh)); return; } @@ -1043,7 +1043,7 @@ static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa) if (TLV_SIZE(tlvh) < sizeof(struct grace_tlv_restart_reason)) { vty_out(vty, - "%% Invalid reason TLV length %zu\n", + "%% Invalid reason TLV length %u\n", TLV_SIZE(tlvh)); return; } @@ -1058,7 +1058,7 @@ static void show_ospf_grace_lsa_info(struct vty *vty, struct ospf_lsa *lsa) if (TLV_SIZE(tlvh) < sizeof(struct grace_tlv_restart_addr)) { vty_out(vty, - "%% Invalid addr TLV length %zu\n", + "%% Invalid addr TLV length %u\n", TLV_SIZE(tlvh)); return; } diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h index f02f34c9af..c63b8ebdaf 100644 --- a/ospfd/ospf_opaque.h +++ b/ospfd/ospf_opaque.h @@ -94,7 +94,7 @@ struct tlv_header { #define TLV_BODY_SIZE(tlvh) (ROUNDUP(ntohs((tlvh)->length), sizeof(uint32_t))) -#define TLV_SIZE(tlvh) (TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh)) +#define TLV_SIZE(tlvh) (uint32_t)(TLV_HDR_SIZE + TLV_BODY_SIZE(tlvh)) #define TLV_HDR_TOP(lsah) \ (struct tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py index 12069a12dc..54a3c699f3 100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -292,6 +292,10 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} logger.info("Verifying %s routes on r3", addr_type) + + # Only test the count of nexthops; the actual nexthop addresses + # can vary and are not deterministic. + # result = verify_rib( tgen, addr_type, @@ -299,7 +303,9 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): input_dict_1, next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], protocol=protocol, + count_only=True ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py index 50aa281d34..73724ac069 100644 --- a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -293,6 +293,10 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}} logger.info("Verifying %s routes on r3", addr_type) + + # Test only the count of nexthops, not the specific nexthop addresses - + # they're not deterministic + # result = verify_rib( tgen, addr_type, @@ -300,7 +304,9 @@ def test_modify_ecmp_max_paths(request, ecmp_num, test_type): input_dict_1, next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)], protocol=protocol, + count_only=True ) + assert result is True, "Testcase {} : Failed \n Error: {}".format( tc_name, result ) diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index d83f946c42..e42992e294 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -2559,6 +2559,7 @@ def verify_rib( tag=None, metric=None, fib=None, + count_only=False ): """ Data will be read from input_dict or input JSON file, API will generate @@ -2576,6 +2577,8 @@ def verify_rib( * `next_hop`[optional]: next_hop which needs to be verified, default: static * `protocol`[optional]: protocol, default = None + * `count_only`[optional]: count of nexthops only, not specific addresses, + default = False Usage ----- @@ -2739,7 +2742,23 @@ def verify_rib( for rib_r in rib_routes_json[st_rt][0]["nexthops"] ] - if found_hops: + # Check only the count of nexthops + if count_only: + if len(next_hop) == len(found_hops): + nh_found = True + else: + errormsg = ( + "Nexthops are missing for " + "route {} in RIB of router {}: " + "expected {}, found {}\n".format( + st_rt, dut, len(next_hop), + len(found_hops) + ) + ) + return errormsg + + # Check the actual nexthops + elif found_hops: missing_list_of_nexthops = set( found_hops ).difference(next_hop) @@ -3019,262 +3038,6 @@ def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): for st_rt in ip_list: st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) - # st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) - - _addr_type = validate_ip_address(st_rt) - if _addr_type != addr_type: - continue - - if st_rt in rib_routes_json: - st_found = True - found_routes.append(st_rt) - - if next_hop: - if type(next_hop) is not list: - next_hop = [next_hop] - - count = 0 - for nh in next_hop: - for nh_dict in rib_routes_json[st_rt][0][ - "nexthops" - ]: - if nh_dict["ip"] != nh: - continue - else: - count += 1 - - if count == len(next_hop): - nh_found = True - else: - missing_routes.append(st_rt) - errormsg = ( - "Nexthop {} is Missing" - " for route {} in " - "RIB of router {}\n".format( - next_hop, st_rt, dut - ) - ) - return errormsg - - else: - missing_routes.append(st_rt) - - if len(missing_routes) > 0: - errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format( - dut, missing_routes - ) - return errormsg - - if nh_found: - logger.info( - "Found next_hop {} for all routes in RIB" - " of router {}\n".format(next_hop, dut) - ) - - if found_routes: - logger.info( - "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n", - dut, - found_routes, - ) - - continue - - if "bgp" in input_dict[routerInput]: - if ( - "advertise_networks" - not in input_dict[routerInput]["bgp"]["address_family"][addr_type][ - "unicast" - ] - ): - continue - - found_routes = [] - missing_routes = [] - advertise_network = input_dict[routerInput]["bgp"]["address_family"][ - addr_type - ]["unicast"]["advertise_networks"] - - # Continue if there are no network advertise - if len(advertise_network) == 0: - continue - - for advertise_network_dict in advertise_network: - if "vrf" in advertise_network_dict: - cmd = "{} vrf {} json".format(command, static_route["vrf"]) - else: - cmd = "{} json".format(command) - - rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) - - # Verifying output dictionary rib_routes_json is not empty - if bool(rib_routes_json) is False: - errormsg = "No route found in rib of router {}..".format(router) - return errormsg - - start_ip = advertise_network_dict["network"] - if "no_of_network" in advertise_network_dict: - no_of_network = advertise_network_dict["no_of_network"] - else: - no_of_network = 1 - - # Generating IPs for verification - ip_list = generate_ips(start_ip, no_of_network) - st_found = False - nh_found = False - - for st_rt in ip_list: - # st_rt = str(ipaddr.IPNetwork(unicode(st_rt))) - st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) - - _addr_type = validate_ip_address(st_rt) - if _addr_type != addr_type: - continue - - if st_rt in rib_routes_json: - st_found = True - found_routes.append(st_rt) - - if next_hop: - if type(next_hop) is not list: - next_hop = [next_hop] - - count = 0 - for nh in next_hop: - for nh_dict in rib_routes_json[st_rt][0]["nexthops"]: - if nh_dict["ip"] != nh: - continue - else: - count += 1 - - if count == len(next_hop): - nh_found = True - else: - missing_routes.append(st_rt) - errormsg = ( - "Nexthop {} is Missing" - " for route {} in " - "RIB of router {}\n".format(next_hop, st_rt, dut) - ) - return errormsg - else: - missing_routes.append(st_rt) - - if len(missing_routes) > 0: - errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format( - dut, missing_routes - ) - return errormsg - - if nh_found: - logger.info( - "Found next_hop {} for all routes in RIB" - " of router {}\n".format(next_hop, dut) - ) - - if found_routes: - logger.info( - "[DUT: {}]: Verified routes FIB" - ", found routes are: {}\n".format(dut, found_routes) - ) - - logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name)) - return True - - -@retry(attempts=5, wait=2, return_is_str=True, initial_wait=2) -def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None): - """ - Data will be read from input_dict or input JSON file, API will generate - same prefixes, which were redistributed by either create_static_routes() or - advertise_networks_using_network_command() and will verify next_hop and - each prefix/routes is present in "show ip/ipv6 fib json" - command o/p. - - Parameters - ---------- - * `tgen` : topogen object - * `addr_type` : ip type, ipv4/ipv6 - * `dut`: Device Under Test, for which user wants to test the data - * `input_dict` : input dict, has details of static routes - * `next_hop`[optional]: next_hop which needs to be verified, - default: static - - Usage - ----- - input_routes_r1 = { - "r1": { - "static_routes": [{ - "network": ["1.1.1.1/32], - "next_hop": "Null0", - "vrf": "RED" - }] - } - } - result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1) - - Returns - ------- - errormsg(str) or True - """ - - logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name)) - - router_list = tgen.routers() - for routerInput in input_dict.keys(): - for router, rnode in router_list.items(): - if router != dut: - continue - - logger.info("Checking router %s FIB routes:", router) - - # Verifying RIB routes - if addr_type == "ipv4": - command = "show ip fib" - else: - command = "show ipv6 fib" - - found_routes = [] - missing_routes = [] - - if "static_routes" in input_dict[routerInput]: - static_routes = input_dict[routerInput]["static_routes"] - - for static_route in static_routes: - if "vrf" in static_route and static_route["vrf"] is not None: - - logger.info( - "[DUT: {}]: Verifying routes for VRF:" - " {}".format(router, static_route["vrf"]) - ) - - cmd = "{} vrf {}".format(command, static_route["vrf"]) - - else: - cmd = "{}".format(command) - - cmd = "{} json".format(cmd) - - rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True) - - # Verifying output dictionary rib_routes_json is not empty - if bool(rib_routes_json) is False: - errormsg = "[DUT: {}]: No route found in fib".format(router) - return errormsg - - network = static_route["network"] - if "no_of_ip" in static_route: - no_of_ip = static_route["no_of_ip"] - else: - no_of_ip = 1 - - # Generating IPs for verification - ip_list = generate_ips(network, no_of_ip) - st_found = False - nh_found = False - - for st_rt in ip_list: - st_rt = str(ipaddress.ip_network(frr_unicode(st_rt))) _addr_type = validate_ip_address(st_rt) if _addr_type != addr_type: diff --git a/tests/topotests/route-scale/r1/installed.routes.json b/tests/topotests/route-scale/r1/installed.routes.json index 25d209f9eb..24a45dca81 100644 --- a/tests/topotests/route-scale/r1/installed.routes.json +++ b/tests/topotests/route-scale/r1/installed.routes.json @@ -6,11 +6,11 @@ "type":"connected" }, { - "fib":1000000, - "rib":1000000, + "fib":200000, + "rib":200000, "type":"sharp" } ], - "routesTotal":1000032, - "routesTotalFib":1000032 + "routesTotal":200032, + "routesTotalFib":200032 } diff --git a/tests/topotests/route-scale/test_route_scale.py b/tests/topotests/route-scale/test_route_scale.py index 8aedfc198c..bbd6ef8d60 100644 --- a/tests/topotests/route-scale/test_route_scale.py +++ b/tests/topotests/route-scale/test_route_scale.py @@ -122,15 +122,20 @@ def run_one_setup(r1, s): expected_installed = s["expect_in"] expected_removed = s["expect_rem"] - count = s["count"] + retries = s["retries"] wait = s["wait"] - logger.info("Testing 1 million routes X {} ecmp".format(s["ecmp"])) + for d in expected_installed["routes"]: + if d["type"] == "sharp": + count = d["rib"] + break + + logger.info("Testing {} routes X {} ecmp".format(count, s["ecmp"])) r1.vtysh_cmd( "sharp install route 1.0.0.0 \ - nexthop-group {} 1000000".format( - s["nhg"] + nexthop-group {} {}".format( + s["nhg"], count ), isjson=False, ) @@ -138,21 +143,21 @@ def run_one_setup(r1, s): test_func = partial( topotest.router_json_cmp, r1, "show ip route summary json", expected_installed ) - success, result = topotest.run_and_expect(test_func, None, count, wait) + success, result = topotest.run_and_expect(test_func, None, retries, wait) assert success, "Route scale test install failed:\n{}".format(result) output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes X {} ecmp installed".format(s["ecmp"])) + logger.info("{} routes X {} ecmp installed".format(count, s["ecmp"])) logger.info(output) - r1.vtysh_cmd("sharp remove route 1.0.0.0 1000000", isjson=False) + r1.vtysh_cmd("sharp remove route 1.0.0.0 {}".format(count), isjson=False) test_func = partial( topotest.router_json_cmp, r1, "show ip route summary json", expected_removed ) - success, result = topotest.run_and_expect(test_func, None, count, wait) + success, result = topotest.run_and_expect(test_func, None, retries, wait) assert success, "Route scale test remove failed:\n{}".format(result) output = r1.vtysh_cmd("sharp data route", isjson=False) - logger.info("1 million routes x {} ecmp removed".format(s["ecmp"])) + logger.info("{} routes x {} ecmp removed".format(count, s["ecmp"])) logger.info(output) @@ -174,7 +179,7 @@ def test_route_install(): # dict keys of params: ecmp number, corresponding nhg name, timeout, # number of times to wait - scale_keys = ["ecmp", "nhg", "wait", "count", "expect_in", "expect_rem"] + scale_keys = ["ecmp", "nhg", "wait", "retries", "expect_in", "expect_rem"] # Table of defaults, used for timeout values and 'expected' objects scale_defaults = dict( diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index ef51989a0c..4dc8c2a6eb 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -3179,10 +3179,14 @@ ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, update_flags = dplane_ctx_mac_get_update_flags(ctx); if (update_flags & DPLANE_MAC_REMOTE) { flags |= NTF_SELF; - if (dplane_ctx_mac_is_sticky(ctx)) + if (dplane_ctx_mac_is_sticky(ctx)) { + /* NUD_NOARP prevents the entry from expiring */ + state |= NUD_NOARP; + /* sticky the entry from moving */ flags |= NTF_STICKY; - else + } else { flags |= NTF_EXT_LEARNED; + } /* if it was static-local previously we need to clear the * notify flags on replace with remote */ |
