diff options
120 files changed, 5067 insertions, 2267 deletions
diff --git a/.clang-format b/.clang-format index a620b5c2c0..b01157b051 100644 --- a/.clang-format +++ b/.clang-format @@ -28,6 +28,9 @@ ForEachMacros: - 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 diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c index f11717b41f..193c96a169 100644 --- a/bgpd/bgp_errors.c +++ b/bgpd/bgp_errors.c @@ -475,6 +475,12 @@ static struct log_ref ferr_bgp_err[] = { .suggestion = "Get log files from router and open an issue", }, { + .code = EC_BGP_NO_LL_ADDRESS_AVAILABLE, + .title = "BGP v6 peer with no LL address on outgoing interface", + .description = "BGP when using a v6 peer requires a v6 LL address to be configured on the outgoing interface as per RFC 4291 section 2.1", + .suggestion = "Add a v6 LL address to the outgoing interfaces as per RFC", + }, + { .code = END_FERR, } }; diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h index 20056d382a..0b71af3fc6 100644 --- a/bgpd/bgp_errors.h +++ b/bgpd/bgp_errors.h @@ -101,6 +101,7 @@ enum bgp_log_refs { EC_BGP_ROUTER_ID_SAME, EC_BGP_INVALID_BGP_INSTANCE, EC_BGP_INVALID_ROUTE, + EC_BGP_NO_LL_ADDRESS_AVAILABLE, }; extern void bgp_error_init(void); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 8313c12e61..d768733545 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -49,10 +49,8 @@ extern struct zclient *zclient; -static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, - int is_bgp_static_route); -static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, - int is_bgp_static_route); +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); @@ -92,8 +90,7 @@ static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) } /* only unregister if this is the last nh for this prefix*/ if (!bnc_existing_for_prefix(bnc)) - unregister_zebra_rnh( - bnc, CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)); + unregister_zebra_rnh(bnc); bnc_free(bnc); } } @@ -308,7 +305,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) && !is_default_host_route(&bnc->prefix)) - register_zebra_rnh(bnc, is_bgp_static_route); + register_zebra_rnh(bnc); if (pi && pi->nexthop != bnc) { /* Unlink from existing nexthop cache, if any. This will also @@ -387,7 +384,7 @@ void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer) zlog_debug( "Freeing connected NHT node %p for peer %s(%s)", bnc, peer->host, bnc->bgp->name_pretty); - unregister_zebra_rnh(bnc, 0); + unregister_zebra_rnh(bnc); bnc_free(bnc); } } @@ -665,6 +662,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) struct bgp_nexthop_cache_head *tree = NULL; struct bgp_nexthop_cache *bnc_nhc, *bnc_import; struct bgp *bgp; + struct prefix match; struct zapi_route nhr; afi_t afi; @@ -677,16 +675,16 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) return; } - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &nhr)) { zlog_err("%s[%s]: Failure to decode nexthop update", __func__, bgp->name_pretty); return; } - afi = family2afi(nhr.prefix.family); + afi = family2afi(match.family); tree = &bgp->nexthop_cache_table[afi]; - bnc_nhc = bnc_find(tree, &nhr.prefix, nhr.srte_color); + bnc_nhc = bnc_find(tree, &match, nhr.srte_color); if (!bnc_nhc) { if (BGP_DEBUG(nht, NHT)) zlog_debug( @@ -697,7 +695,7 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) tree = &bgp->import_check_table[afi]; - bnc_import = bnc_find(tree, &nhr.prefix, nhr.srte_color); + bnc_import = bnc_find(tree, &match, nhr.srte_color); if (!bnc_import) { if (BGP_DEBUG(nht, NHT)) zlog_debug( @@ -914,8 +912,7 @@ static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command) * RETURNS: * void. */ -static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, - int is_bgp_import_route) +static void register_zebra_rnh(struct bgp_nexthop_cache *bnc) { /* Check if we have already registered */ if (bnc->flags & BGP_NEXTHOP_REGISTERED) @@ -936,8 +933,7 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc, * RETURNS: * void. */ -static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc, - int is_bgp_import_route) +static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc) { /* Check if we have already registered */ if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) @@ -1172,7 +1168,7 @@ void bgp_nht_register_nexthops(struct bgp *bgp) frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi], bnc) { - register_zebra_rnh(bnc, 0); + register_zebra_rnh(bnc); } } } diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 8fac36cf60..09db041780 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -665,9 +665,6 @@ static void bgp_write_notify(struct peer *peer) assert(stream_get_endp(s) >= BGP_HEADER_SIZE); - /* Stop collecting data within the socket */ - sockopt_cork(peer->fd, 0); - /* * socket is in nonblocking mode, if we can't deliver the NOTIFY, well, * we only care about getting a clean shutdown at this point. diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 1860686381..485495924a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -5376,10 +5376,10 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) bgp_attr_get_community( pi->attr), COMMUNITY_NO_LLGR)) - break; + continue; if (!CHECK_FLAG(pi->flags, BGP_PATH_STALE)) - break; + continue; /* * If this is VRF leaked route @@ -5409,9 +5409,9 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi) !community_include( bgp_attr_get_community(pi->attr), COMMUNITY_NO_LLGR)) - break; + continue; if (!CHECK_FLAG(pi->flags, BGP_PATH_STALE)) - break; + continue; if (safi == SAFI_UNICAST && (peer->bgp->inst_type == BGP_INSTANCE_TYPE_VRF || diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 857462a601..c724b938d1 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -369,7 +369,7 @@ static void bgpd_sync_callback(struct thread *thread) thread_add_read(bm->master, bgpd_sync_callback, NULL, socket, &t_rpki); if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) { - while (read(socket, &rec, sizeof(struct pfx_record) != -1)) + while (read(socket, &rec, sizeof(struct pfx_record)) != -1) ; atomic_store_explicit(&rtr_update_overflow, 0, diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c0a9a38773..78eaac7806 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -882,6 +882,12 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, */ if (!v6_ll_avail && if_is_loopback(ifp)) v6_ll_avail = true; + else { + flog_warn( + EC_BGP_NO_LL_ADDRESS_AVAILABLE, + "Interface: %s does not have a v6 LL address associated with it, waiting until one is created for it", + ifp->name); + } } else /* Link-local address. */ { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 16488eb4a4..38a106359e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1223,7 +1223,7 @@ int bgp_global_gr_init(struct bgp *bgp) { /*Event -> */ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ - GLOBAL_INVALID, GLOBAL_HELPER, + GLOBAL_GR, GLOBAL_HELPER, /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ GLOBAL_DISABLE, GLOBAL_INVALID }, diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst index 6a7f9c4ca9..4ef0ca8416 100644 --- a/doc/developer/frr-release-procedure.rst +++ b/doc/developer/frr-release-procedure.rst @@ -204,7 +204,7 @@ Stage 3 - Publish .. code-block:: console - cp <old-version>.md <version>.md + cp content/release/<old-version>.md content/release/<new-version>.md Paste the GitHub release announcement text into this document, and **remove line breaks**. In other words, this:: @@ -220,10 +220,17 @@ Stage 3 - Publish This is very important otherwise the announcement will be unreadable on the website. - Make sure to add a link to the GitHub releases page at the top. + To get the number of commiters and commits, here is a couple of handy commands: + + .. code-block:: console - Once finished, manually add a new entry into ``index.html`` to link to this - new announcement. Look at past commits to see how to do this. + # The number of commits + % git log --oneline --no-merges base_8.2...base_8.1 | wc -l + + # The number of commiters + % git shortlog --summary --no-merges base_8.2...base_8.1 | wc -l + + Make sure to add a link to the GitHub releases page at the top. #. Deploy the updated ``frr-www`` on the frrouting.org web server and verify that the announcement text is visible. diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst index dc8f236927..4eaa85115e 100644 --- a/doc/developer/lists.rst +++ b/doc/developer/lists.rst @@ -1,23 +1,23 @@ .. _lists: -List implementations +Type-safe containers ==================== .. note:: - The term *list* is used generically for lists, skiplists, trees and hash - tables in this document. + This section previously used the term *list*; it was changed to *container* + to be more clear. -Common list interface ---------------------- +Common container interface +-------------------------- -FRR includes a set of list-like data structure implementations with abstracted +FRR includes a set of container implementations with abstracted common APIs. The purpose of this is easily allow swapping out one data structure for another while also making the code easier to read and write. -There is one API for unsorted lists and a similar but not identical API for -sorted lists - and heaps use a middle ground of both. +There is one API for unsorted containers and a similar but not identical API +for sorted containers - and heaps use a middle ground of both. -For unsorted lists, the following implementations exist: +For unsorted containers, the following implementations exist: - single-linked list with tail pointer (e.g. STAILQ in BSD) @@ -31,7 +31,7 @@ Being partially sorted, the oddball structure: - an 8-ary heap -For sorted lists, these data structures are implemented: +For sorted containers, these data structures are implemented: - single-linked list @@ -44,7 +44,7 @@ For sorted lists, these data structures are implemented: - hash table (note below) Except for hash tables, each of the sorted data structures has a variant with -unique and non-unique list items. Hash tables always require unique items +unique and non-unique items. Hash tables always require unique items and mostly follow the "sorted" API but use the hash value as sorting key. Also, iterating while modifying does not work with hash tables. Conversely, the heap always has non-unique items, but iterating while modifying @@ -60,7 +60,7 @@ in the future: The APIs are all designed to be as type-safe as possible. This means that -there will be a compiler warning when an item doesn't match the list, or +there will be a compiler warning when an item doesn't match the container, or the return value has a different type, or other similar situations. **You should never use casts with these APIs.** If a cast is neccessary in relation to these APIs, there is probably something wrong with the overall design. @@ -100,35 +100,39 @@ Available types: Functions provided: -+------------------------------------+------+------+------+---------+------------+ -| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ | -+====================================+======+======+======+=========+============+ -| _init, _fini | yes | yes | yes | yes | yes | -+------------------------------------+------+------+------+---------+------------+ -| _first, _next, _next_safe, | yes | yes | yes | yes | yes | -| | | | | | | -| _const_first, _const_next | | | | | | -+------------------------------------+------+------+------+---------+------------+ -| _swap_all | yes | yes | yes | yes | yes | -+------------------------------------+------+------+------+---------+------------+ -| _anywhere | yes | -- | -- | -- | -- | -+------------------------------------+------+------+------+---------+------------+ -| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- | -+------------------------------------+------+------+------+---------+------------+ -| _add | -- | yes | yes | yes | yes | -+------------------------------------+------+------+------+---------+------------+ -| _member | yes | yes | yes | yes | yes | -+------------------------------------+------+------+------+---------+------------+ -| _del, _pop | yes | yes | yes | yes | yes | -+------------------------------------+------+------+------+---------+------------+ -| _find, _const_find | -- | -- | yes | yes | -- | -+------------------------------------+------+------+------+---------+------------+ -| _find_lt, _find_gteq, | -- | -- | -- | yes | yes | -| | | | | | | -| _const_find_lt, _const_find_gteq | | | | | | -+------------------------------------+------+------+------+---------+------------+ -| use with frr_each() macros | yes | yes | yes | yes | yes | -+------------------------------------+------+------+------+---------+------------+ ++------------------------------------+-------+------+------+---------+------------+ +| Function | LIST | HEAP | HASH | \*_UNIQ | \*_NONUNIQ | ++====================================+=======+======+======+=========+============+ +| _init, _fini | yes | yes | yes | yes | yes | ++------------------------------------+-------+------+------+---------+------------+ +| _first, _next, _next_safe, | yes | yes | yes | yes | yes | +| | | | | | | +| _const_first, _const_next | | | | | | ++------------------------------------+-------+------+------+---------+------------+ +| _last, _prev, _prev_safe, | DLIST | -- | -- | RB only | RB only | +| | only | | | | | +| _const_last, _const_prev | | | | | | ++------------------------------------+-------+------+------+---------+------------+ +| _swap_all | yes | yes | yes | yes | yes | ++------------------------------------+-------+------+------+---------+------------+ +| _anywhere | yes | -- | -- | -- | -- | ++------------------------------------+-------+------+------+---------+------------+ +| _add_head, _add_tail, _add_after | yes | -- | -- | -- | -- | ++------------------------------------+-------+------+------+---------+------------+ +| _add | -- | yes | yes | yes | yes | ++------------------------------------+-------+------+------+---------+------------+ +| _member | yes | yes | yes | yes | yes | ++------------------------------------+-------+------+------+---------+------------+ +| _del, _pop | yes | yes | yes | yes | yes | ++------------------------------------+-------+------+------+---------+------------+ +| _find, _const_find | -- | -- | yes | yes | -- | ++------------------------------------+-------+------+------+---------+------------+ +| _find_lt, _find_gteq, | -- | -- | -- | yes | yes | +| | | | | | | +| _const_find_lt, _const_find_gteq | | | | | | ++------------------------------------+-------+------+------+---------+------------+ +| use with frr_each() macros | yes | yes | yes | yes | yes | ++------------------------------------+-------+------+------+---------+------------+ @@ -136,7 +140,7 @@ Datastructure type setup ------------------------ Each of the data structures has a ``PREDECL_*`` and a ``DECLARE_*`` macro to -set up an "instantiation" of the list. This works somewhat similar to C++ +set up an "instantiation" of the container. This works somewhat similar to C++ templating, though much simpler. **In all following text, the Z prefix is replaced with a name choosen @@ -174,8 +178,8 @@ The common setup pattern will look like this: ``XXX`` is replaced with the name of the data structure, e.g. ``SKIPLIST`` or ``ATOMLIST``. The ``DECLARE_XXX`` invocation can either occur in a `.h` -file (if the list needs to be accessed from several C files) or it can be -placed in a `.c` file (if the list is only accessed from that file.) The +file (if the container needs to be accessed from several C files) or it can be +placed in a `.c` file (if the container is only accessed from that file.) The ``PREDECL_XXX`` invocation defines the ``struct Z_item`` and ``struct Z_head`` types and must therefore occur before these are used. @@ -196,7 +200,7 @@ The following iteration macros work across all data structures: for (item = Z_first(&head); item; item = Z_next(&head, item)) - Note that this will fail if the list is modified while being iterated + Note that this will fail if the container is modified while being iterated over. .. c:macro:: frr_each_safe(Z, head, item) @@ -220,8 +224,8 @@ The following iteration macros work across all data structures: .. c:macro:: frr_each_from(Z, head, item, from) - Iterates over the list, starting at item ``from``. This variant is "safe" - as in the previous macro. Equivalent to: + Iterates over the container, starting at item ``from``. This variant is + "safe" as in the previous macro. Equivalent to: .. code-block:: c @@ -236,6 +240,13 @@ The following iteration macros work across all data structures: resume iteration after breaking out of the loop by keeping the ``from`` value persistent and reusing it for the next loop. +.. c:macro:: frr_rev_each(Z, head, item) +.. c:macro:: frr_rev_each_safe(Z, head, item) +.. c:macro:: frr_rev_each_from(Z, head, item, from) + + Reverse direction variants of the above. Only supported on containers that + implement ``_last`` and ``_prev`` (i.e. ``RBTREE`` and ``DLIST``). + To iterate over ``const`` pointers, add ``_const`` to the name of the datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes ``frr_each (mylist_const, head, item)``. @@ -243,24 +254,24 @@ datastructure (``Z`` above), e.g. ``frr_each (mylist, head, item)`` becomes Common API ---------- -The following documentation assumes that a list has been defined using -``Z`` as the name, and ``itemtype`` being the type of the list items (e.g. +The following documentation assumes that a container has been defined using +``Z`` as the name, and ``itemtype`` being the type of the items (e.g. ``struct item``.) .. c:function:: void Z_init(struct Z_head *) - Initializes the list for use. For most implementations, this just sets + Initializes the container for use. For most implementations, this just sets some values. Hash tables are the only implementation that allocates memory in this call. .. c:function:: void Z_fini(struct Z_head *) - Reverse the effects of :c:func:`Z_init()`. The list must be empty + Reverse the effects of :c:func:`Z_init()`. The container must be empty when this function is called. .. warning:: - This function may ``assert()`` if the list is not empty. + This function may ``assert()`` if the container is not empty. .. c:function:: size_t Z_count(const struct Z_head *) @@ -270,7 +281,7 @@ The following documentation assumes that a list has been defined using .. note:: - For atomic lists with concurrent access, the value will already be + For atomic containers with concurrent access, the value will already be outdated by the time this function returns and can therefore only be used as an estimate. @@ -291,6 +302,12 @@ The following documentation assumes that a list has been defined using empty. This is O(1) for all data structures except red-black trees where it is O(log n). +.. c:function:: const itemtype *Z_const_last(const struct Z_head *) +.. c:function:: itemtype *Z_last(struct Z_head *) + + Last item in the structure, or ``NULL``. Only available on containers + that support reverse iteration (i.e. ``RBTREE`` and ``DLIST``). + .. c:function:: itemtype *Z_pop(struct Z_head *) Remove and return the first item in the structure, or ``NULL`` if the @@ -300,7 +317,7 @@ The following documentation assumes that a list has been defined using This function can be used to build queues (with unsorted structures) or priority queues (with sorted structures.) - Another common pattern is deleting all list items: + Another common pattern is deleting all container items: .. code-block:: c @@ -329,16 +346,23 @@ The following documentation assumes that a list has been defined using Same as :c:func:`Z_next()`, except that ``NULL`` is returned if ``prev`` is ``NULL``. +.. c:function:: const itemtype *Z_const_prev(const struct Z_head *, const itemtype *next) +.. c:function:: itemtype *Z_prev(struct Z_head *, itemtype *next) +.. c:function:: itemtype *Z_prev_safe(struct Z_head *, itemtype *next) + + As above, but preceding item. Only available on structures that support + reverse iteration (i.e. ``RBTREE`` and ``DLIST``). + .. c:function:: itemtype *Z_del(struct Z_head *, itemtype *item) - Remove ``item`` from the list and return it. + Remove ``item`` from the container and return it. .. note:: This function's behaviour is undefined if ``item`` is not actually - on the list. Some structures return ``NULL`` in this case while others - return ``item``. The function may also call ``assert()`` (but most - don't.) + on the container. Some structures return ``NULL`` in this case while + others return ``item``. The function may also call ``assert()`` (but + most don't.) .. c:function:: itemtype *Z_swap_all(struct Z_head *, struct Z_head *) @@ -427,8 +451,8 @@ API for sorted structures ------------------------- Sorted data structures do not need to have an insertion position specified, -therefore the insertion calls are different from unsorted lists. Also, -sorted lists can be searched for a value. +therefore the insertion calls are different from unsorted containers. Also, +sorted containers can be searched for a value. .. c:macro:: DECLARE_XXX_UNIQ(Z, type, field, compare_func) @@ -439,7 +463,7 @@ sorted lists can be searched for a value. created for this instantiation. ``DECLARE_XXX(foo, ...)`` gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note that this must match the value given in ``PREDECL_XXX(foo)``. - :param typename type: Specifies the data type of the list items, e.g. + :param typename type: Specifies the data type of the items, e.g. ``struct item``. Note that ``struct`` must be added here, it is not automatically added. :param token field: References a struct member of ``type`` that must be @@ -448,29 +472,29 @@ sorted lists can be searched for a value. :param funcptr compare_func: Item comparison function, must have the following function signature: ``int function(const itemtype *, const itemtype*)``. This function - may be static if the list is only used in one file. + may be static if the container is only used in one file. .. c:macro:: DECLARE_XXX_NONUNIQ(Z, type, field, compare_func) - Same as above, but allow adding multiple items to the list that compare + Same as above, but allow adding multiple items to the container that compare as equal in ``compare_func``. Ordering between these items is undefined - and depends on the list implementation. + and depends on the container implementation. .. c:function:: itemtype *Z_add(struct Z_head *, itemtype *item) Insert an item at the appropriate sorted position. If another item exists - in the list that compares as equal (``compare_func()`` == 0), ``item`` is - not inserted into the list and the already-existing item in the list is + in the container that compares as equal (``compare_func()`` == 0), ``item`` + is not inserted and the already-existing item in the container is returned. Otherwise, on successful insertion, ``NULL`` is returned. - For ``_NONUNIQ`` lists, this function always returns NULL since ``item`` - can always be successfully added to the list. + For ``_NONUNIQ`` containers, this function always returns NULL since + ``item`` can always be successfully added to the container. .. c:function:: const itemtype *Z_const_find(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find(struct Z_head *, const itemtype *ref) - Search the list for an item that compares equal to ``ref``. If no equal - item is found, return ``NULL``. + Search the container for an item that compares equal to ``ref``. If no + equal item is found, return ``NULL``. This function is likely used with a temporary stack-allocated value for ``ref`` like so: @@ -483,21 +507,21 @@ sorted lists can be searched for a value. .. note:: - The ``Z_find()`` function is only available for lists that contain - unique items (i.e. ``DECLARE_XXX_UNIQ``.) This is because on a list - containing non-unique items, more than one item may compare as equal to + The ``Z_find()`` function is only available for containers that contain + unique items (i.e. ``DECLARE_XXX_UNIQ``.) This is because on a container + with non-unique items, more than one item may compare as equal to the item that is searched for. .. c:function:: const itemtype *Z_const_find_gteq(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find_gteq(struct Z_head *, const itemtype *ref) - Search the list for an item that compares greater or equal to + Search the container for an item that compares greater or equal to ``ref``. See :c:func:`Z_find()` above. .. c:function:: const itemtype *Z_const_find_lt(const struct Z_head *, const itemtype *ref) .. c:function:: itemtype *Z_find_lt(struct Z_head *, const itemtype *ref) - Search the list for an item that compares less than + Search the container for an item that compares less than ``ref``. See :c:func:`Z_find()` above. @@ -511,7 +535,7 @@ API for hash tables created for this instantiation. ``DECLARE_XXX(foo, ...)`` gives ``struct foo_item``, ``foo_add()``, ``foo_count()``, etc. Note that this must match the value given in ``PREDECL_XXX(foo)``. - :param typename type: Specifies the data type of the list items, e.g. + :param typename type: Specifies the data type of the items, e.g. ``struct item``. Note that ``struct`` must be added here, it is not automatically added. :param token field: References a struct member of ``type`` that must be @@ -520,7 +544,7 @@ API for hash tables :param funcptr compare_func: Item comparison function, must have the following function signature: ``int function(const itemtype *, const itemtype*)``. This function - may be static if the list is only used in one file. For hash tables, + may be static if the container is only used in one file. For hash tables, this function is only used to check for equality, the ordering is ignored. :param funcptr hash_func: Hash calculation function, must have the @@ -725,13 +749,9 @@ Head removal (pop) and deallocation: FAQ --- -What are the semantics of ``const`` in the list APIs? +What are the semantics of ``const`` in the container APIs? ``const`` pointers to list heads and/or items are interpreted to mean that - both the list itself as well as the data items are read-only. - -Why is there no "is this item on a/the list" test? - It's slow for several of the data structures, and the work of adding it - just hasn't been done. It can certainly be added if it's needed. + both the container itself as well as the data items are read-only. Why is it ``PREDECL`` + ``DECLARE`` instead of ``DECLARE`` + ``DEFINE``? The rule is that a ``DEFINE`` must be in a ``.c`` file, and linked exactly diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index 4e6fc04206..7046361204 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -163,6 +163,10 @@ Networking data types - :c:union:`prefixptr` (dereference to get :c:struct:`prefix`) - :c:union:`prefixconstptr` (dereference to get :c:struct:`prefix`) + Options: + + ``%pFXh``: (address only) :frrfmtout:`1.2.3.0` / :frrfmtout:`fe80::1234` + .. frrfmt:: %pPSG4 (struct prefix_sg *) :frrfmtout:`(*,1.2.3.4)` diff --git a/doc/user/index.rst b/doc/user/index.rst index cadf4cb9cf..5a018a5583 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -54,6 +54,7 @@ Protocols ospf6d pathd pim + pimv6 pbr ripd ripngd diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index f7d42d8200..d2859670dd 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -272,7 +272,7 @@ ISIS interface Showing ISIS information ======================== -.. clicmd:: show isis summary +.. clicmd:: show isis [vrf <NAME|all>] summary [json] Show summary information about ISIS. @@ -280,17 +280,17 @@ Showing ISIS information Show information about ISIS node. -.. clicmd:: show isis interface [detail] [IFNAME] +.. clicmd:: show isis [vrf <NAME|all>] interface [detail] [IFNAME] [json] Show state and configuration of ISIS specified interface, or all interfaces if no interface is given with or without details. -.. clicmd:: show isis neighbor [detail] [SYSTEMID] +.. clicmd:: show isis [vrf <NAME|all>] neighbor [detail] [SYSTEMID] [json] Show state and information of ISIS specified neighbor, or all neighbors if no system id is given with or without details. -.. clicmd:: show isis database [detail] [LSPID] +.. clicmd:: show isis [vrf <NAME|all>] database [detail] [LSPID] [json] Show the ISIS database globally, for a specific LSP id without or with details. diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 1c3a0110ac..30363dfdf6 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -505,10 +505,12 @@ cause great confusion. Display information about a S,G pair and how the RPF would be chosen. This is especially useful if there are ECMP's available from the RPF lookup. -.. clicmd:: show ip pim rp-info +.. clicmd:: show ip pim [vrf NAME] rp-info [A.B.C.D/M] [json] Display information about RP's that are configured on this router. + You can filter the output by specifying an arbitrary group. + .. clicmd:: show ip pim rpf Display information about currently being used S,G's and their RPF lookup diff --git a/doc/user/pimv6.rst b/doc/user/pimv6.rst new file mode 100644 index 0000000000..e71cf4631c --- /dev/null +++ b/doc/user/pimv6.rst @@ -0,0 +1,288 @@ +.. _pimv6: + +***** +PIMv6 +***** + +PIMv6 -- Protocol Independent Multicast for IPv6 + +*pim6d* supports pim-sm as well as MLD v1 and v2. PIMv6 is +vrf aware and can work within the context of vrf's in order to +do S,G mrouting. + +.. _starting-and-stopping-pim6d: + +Starting and Stopping pim6d +=========================== + +The default configuration file name of *pim6d*'s is :file:`pim6d.conf`. When +invoked *pim6d* searches directory |INSTALL_PREFIX_ETC|. If +:file:`pim6d.conf` is not there then next search current directory. + +*pim6d* requires zebra for proper operation. Additionally *pim6d* depends on +routing properly setup and working in the network that it is working on. + +:: + + # zebra -d + # pim6d -d + + +Please note that *zebra* must be invoked before *pim6d*. + +To stop *pim6d* please use:: + + kill `cat /var/run/pim6d.pid` + +Certain signals have special meanings to *pim6d*. + ++---------+---------------------------------------------------------------------+ +| Signal | Meaning | ++=========+=====================================================================+ +| SIGUSR1 | Rotate the *pim6d* logfile | ++---------+---------------------------------------------------------------------+ +| SIGINT | *pim6d* sweeps all installed PIM mroutes then terminates gracefully.| +| SIGTERM | | ++---------+---------------------------------------------------------------------+ + +*pim6d* invocation options. Common options that can be specified +(:ref:`common-invocation-options`). + +.. clicmd:: ipv6 pim rp X:X::X:X Y:Y::Y:Y/M + + In order to use pimv6, it is necessary to configure a RP for join messages to + be sent to. Currently the only methodology to do this is via static rp + commands. All routers in the pimv6 network must agree on these values. The + first ipv6 address is the RP's address and the second value is the matching + prefix of group ranges covered. This command is vrf aware, to configure for + a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim rp X:X::X:X prefix-list WORD + + This CLI helps in configuring RP address for a range of groups specified + by the prefix-list. + +.. clicmd:: ipv6 pim rp keep-alive-timer (1-65535) + + Modify the time out value for a S,G flow from 1-65535 seconds at RP. + The normal keepalive period for the KAT(S,G) defaults to 210 seconds. + However, at the RP, the keepalive period must be at least the + Register_Suppression_Time, or the RP may time out the (S,G) state + before the next Null-Register arrives. Thus, the KAT(S,G) is set to + max(Keepalive_Period, RP_Keepalive_Period) when a Register-Stop is sent. + If choosing a value below 31 seconds be aware that some hardware platforms + cannot see data flowing in better than 30 second chunks. This command is + vrf aware, to configure for a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim spt-switchover infinity-and-beyond [prefix-list PLIST] + + On the last hop router if it is desired to not switch over to the SPT tree + configure this command. Optional parameter prefix-list can be use to control + which groups to switch or not switch. If a group is PERMIT as per the + PLIST, then the SPT switchover does not happen for it and if it is DENY, + then the SPT switchover happens. + This command is vrf aware, to configure for a vrf, + enter the vrf submode. + +.. clicmd:: ipv6 pim join-prune-interval (1-65535) + + Modify the join/prune interval that pim uses to the new value. Time is + specified in seconds. This command is vrf aware, to configure for a vrf, + enter the vrf submode. The default time is 60 seconds. If you enter + a value smaller than 60 seconds be aware that this can and will affect + convergence at scale. + +.. clicmd:: ipv6 pim keep-alive-timer (1-65535) + + Modify the time out value for a S,G flow from 1-65535 seconds. If choosing + a value below 31 seconds be aware that some hardware platforms cannot see data + flowing in better than 30 second chunks. This command is vrf aware, to + configure for a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim packets (1-255) + + When processing packets from a neighbor process the number of packets + incoming at one time before moving on to the next task. The default value is + 3 packets. This command is only useful at scale when you can possibly have + a large number of pim control packets flowing. This command is vrf aware, to + configure for a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim register-suppress-time (1-65535) + + Modify the time that pim will register suppress a FHR will send register + notifications to the kernel. This command is vrf aware, to configure for a + vrf, enter the vrf submode. + +.. _pimv6-interface-configuration: + +PIMv6 Interface Configuration +============================= + +PIMv6 interface commands allow you to configure an interface as either a Receiver +or a interface that you would like to form pimv6 neighbors on. If the interface +is in a vrf, enter the interface command with the vrf keyword at the end. + +.. clicmd:: ipv6 pim active-active + + Turn on pim active-active configuration for a Vxlan interface. This + command will not do anything if you do not have the underlying ability + of a mlag implementation. + +.. clicmd:: ipv6 pim drpriority (1-4294967295) + + Set the DR Priority for the interface. This command is useful to allow the + user to influence what node becomes the DR for a lan segment. + +.. clicmd:: ipv6 pim hello (1-65535) (1-65535) + + Set the pim hello and hold interval for a interface. + +.. clicmd:: ipv6 pim + + Tell pim that we would like to use this interface to form pim neighbors + over. Please note that this command does not enable the reception of MLD + reports on the interface. Refer to the next ``ipv6 mld`` command for MLD + management. + +.. clicmd:: ipv6 pim use-source X:X::X:X + + If you have multiple addresses configured on a particular interface + and would like pim to use a specific source address associated with + that interface. + +.. clicmd:: ipv6 mld + + Tell pim to receive MLD reports and Query on this interface. The default + version is v2. This command is useful on a LHR. + +.. clicmd:: ipv6 mld join X:X::X:X [Y:Y::Y:Y] + + Join multicast group or source-group on an interface. + +.. clicmd:: ipv6 mld query-interval (1-65535) + + Set the MLD query interval that PIM will use. + +.. clicmd:: ipv6 mld query-max-response-time (1-65535) + + Set the MLD query response timeout value. If an report is not returned in + the specified time we will assume the S,G or \*,G has timed out. + +.. clicmd:: ipv6 mld version (1-2) + + Set the MLD version used on this interface. The default value is 2. + +.. clicmd:: ipv6 multicast boundary oil WORD + + Set a PIMv6 multicast boundary, based upon the WORD prefix-list. If a PIMv6 + join or MLD report is received on this interface and the Group is denied by + the prefix-list, PIMv6 will ignore the join or report. + +.. clicmd:: ipv6 mld last-member-query-count (1-255) + + Set the MLD last member query count. The default value is 2. 'no' form of + this command is used to configure back to the default value. + +.. clicmd:: ipv6 MLD last-member-query-interval (1-65535) + + Set the MLD last member query interval in deciseconds. The default value is + 10 deciseconds. 'no' form of this command is used to to configure back to the + default value. + +.. clicmd:: ipv6 mroute INTERFACE X:X::X:X [Y:Y::Y:Y] + + Set a static multicast route for a traffic coming on the current interface to + be forwarded on the given interface if the traffic matches the group address + and optionally the source address. + +.. _show-pimv6-information: + +Show PIMv6 Information +====================== + +All PIMv6 show commands are vrf aware and typically allow you to insert a +specified vrf command if information is desired about a specific vrf. If no +vrf is specified then the default vrf is assumed. Finally the special keyword +'all' allows you to look at all vrfs for the command. Naming a vrf 'all' will +cause great confusion. + +.. clicmd:: show ipv6 pim [vrf NAME] group-type [json] + + Display SSM group ranges. + +.. clicmd:: show ipv6 pim interface + + Display information about interfaces PIM is using. + +.. clicmd:: show ipv6 pim [vrf NAME] join [X:X::X:X [X:X::X:X]] [json] +.. clicmd:: show ipv6 pim vrf all join [json] + + Display information about PIM joins received. If one address is specified + then we assume it is the Group we are interested in displaying data on. + If the second address is specified then it is Source Group. + +.. clicmd:: show ipv6 pim [vrf NAME] local-membership [json] + + Display information about PIM interface local-membership. + +.. clicmd:: show ipv6 pim [vrf NAME] neighbor [detail|WORD] [json] +.. clicmd:: show ipv6 pim vrf all neighbor [detail|WORD] [json] + + Display information about PIM neighbors. + +.. clicmd:: show ipv6 pim [vrf NAME] nexthop + + Display information about pim nexthops that are being used. + +.. clicmd:: show ipv6 pim [vrf NAME] nexthop-lookup X:X::X:X X:X::X:X + + Display information about a S,G pair and how the RPF would be chosen. This + is especially useful if there are ECMP's available from the RPF lookup. + +.. clicmd:: show ipv6 pim [vrf NAME] rp-info [json] +.. clicmd:: show ipv6 pim vrf all rp-info [json] + + Display information about RP's that are configured on this router. + +.. clicmd:: show ipv6 pim [vrf NAME] rpf [json] +.. clicmd:: show ipv6 pim vrf all rpf [json] + + Display information about currently being used S,G's and their RPF lookup + information. Additionally display some statistics about what has been + happening on the router. + +.. clicmd:: show ipv6 pim [vrf NAME] secondary + + Display information about an interface and all the secondary addresses + associated with it. + +.. clicmd:: show ipv6 pim [vrf NAME] state [X:X::X:X [X:X::X:X]] [json] +.. clicmd:: show ipv6 pim vrf all state [X:X::X:X [X:X::X:X]] [json] + + Display information about known S,G's and incoming interface as well as the + OIL and how they were chosen. + +.. clicmd:: show ipv6 pim [vrf NAME] upstream [X:X::X:X [Y:Y::Y:Y]] [json] +.. clicmd:: show ipv6 pim vrf all upstream [json] + + Display upstream information about a S,G mroute. Allow the user to + specify sub Source and Groups that we are interested in. + +.. clicmd:: show ipv6 pim [vrf NAME] upstream-join-desired [json] + + Display upstream information for S,G's and if we desire to + join the multicast tree + +.. clicmd:: show ipv6 pim [vrf NAME] upstream-rpf [json] + + Display upstream information for S,G's and the RPF data associated with them. + +PIMv6 Debug Commands +==================== + +The debugging subsystem for PIMv6 behaves in accordance with how FRR handles +debugging. You can specify debugging at the enable CLI mode as well as the +configure CLI mode. If you specify debug commands in the configuration cli +mode, the debug commands can be persistent across restarts of the FRR pim6d if +the config was written out. + diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 31158cb5f7..14ace2c856 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -30,6 +30,7 @@ user_RSTFILES = \ doc/user/packet-dumps.rst \ doc/user/pathd.rst \ doc/user/pim.rst \ + doc/user/pimv6.rst \ doc/user/ripd.rst \ doc/user/pbr.rst \ doc/user/ripngd.rst \ diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst index 15c6088b7a..221e9c6fe2 100644 --- a/doc/user/zebra.rst +++ b/doc/user/zebra.rst @@ -29,7 +29,7 @@ Besides the common invocation options (:ref:`common-invocation-options`), the Zebra, when started, will read in routes. Those routes that Zebra identifies that it was the originator of will be swept in TIME seconds. If no time is specified then we will sweep those routes immediately. - Under the *BSD's, there is no way to properly store the originating + Under the \*BSD's, there is no way to properly store the originating route and the route types in this case will show up as a static route with an admin distance of 255. diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 06909c4306..2729dce382 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -464,6 +464,220 @@ void isis_adj_expire(struct thread *thread) } /* + * show isis neighbor [detail] json + */ +void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, + char detail) +{ + json_object *iface_json, *ipv4_addr_json, *ipv6_link_json, + *ipv6_non_link_json, *topo_json, *dis_flaps_json, + *area_addr_json, *adj_sid_json; + time_t now; + struct isis_dynhn *dyn; + int level; + char buf[256]; + + json_object_string_add(json, "adj", isis_adj_name(adj)); + + if (detail == ISIS_UI_LEVEL_BRIEF) { + if (adj->circuit) + json_object_string_add(json, "interface", + adj->circuit->interface->name); + else + json_object_string_add(json, "interface", + "NULL circuit!"); + json_object_int_add(json, "level", adj->level); + json_object_string_add(json, "state", + adj_state2string(adj->adj_state)); + now = time(NULL); + if (adj->last_upd) { + if (adj->last_upd + adj->hold_time < now) + json_object_string_add(json, "last-upd", + "expiring"); + else + json_object_string_add( + json, "expires-in", + time2string(adj->last_upd + + adj->hold_time - now)); + } + json_object_string_add(json, "snpa", snpa_print(adj->snpa)); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) { + struct sr_adjacency *sra; + struct listnode *anode; + + level = adj->level; + iface_json = json_object_new_object(); + json_object_object_add(json, "interface", iface_json); + if (adj->circuit) + json_object_string_add(iface_json, "name", + adj->circuit->interface->name); + else + json_object_string_add(iface_json, "name", + "null-circuit"); + json_object_int_add(json, "level", adj->level); + json_object_string_add(iface_json, "state", + adj_state2string(adj->adj_state)); + now = time(NULL); + if (adj->last_upd) { + if (adj->last_upd + adj->hold_time < now) + json_object_string_add(iface_json, "last-upd", + "expiring"); + else + json_object_string_add( + json, "expires-in", + time2string(adj->last_upd + + adj->hold_time - now)); + } else + json_object_string_add(json, "expires-in", + time2string(adj->hold_time)); + json_object_int_add(iface_json, "adj-flaps", adj->flaps); + json_object_string_add(iface_json, "last-ago", + time2string(now - adj->last_flap)); + json_object_string_add(iface_json, "circuit-type", + circuit_t2string(adj->circuit_t)); + json_object_string_add(iface_json, "speaks", + nlpid2string(&adj->nlpids)); + if (adj->mt_count != 1 || + adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) { + topo_json = json_object_new_object(); + json_object_object_add(iface_json, "topologies", + topo_json); + for (unsigned int i = 0; i < adj->mt_count; i++) { + snprintfrr(buf, sizeof(buf), "topo-%d", i); + json_object_string_add( + topo_json, buf, + isis_mtid2str(adj->mt_set[i])); + } + } + json_object_string_add(iface_json, "snpa", + snpa_print(adj->snpa)); + if (adj->circuit && + (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { + dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); + if (dyn) { + snprintfrr(buf, sizeof(buf), "%s-%02x", + dyn->hostname, + adj->lanid[ISIS_SYS_ID_LEN]); + 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_int_add(iface_json, "lan-prio", + adj->prio[adj->level - 1]); + + dis_flaps_json = json_object_new_object(); + json_object_object_add(iface_json, "dis-flaps", + dis_flaps_json); + json_object_string_add( + dis_flaps_json, "dis-record", + isis_disflag2string( + adj->dis_record[ISIS_LEVELS + level - 1] + .dis)); + json_object_int_add(dis_flaps_json, "last", + adj->dischanges[level - 1]); + json_object_string_add( + dis_flaps_json, "ago", + time2string(now - (adj->dis_record[ISIS_LEVELS + + level - 1] + .last_dis_change))); + } + + if (adj->area_address_count) { + area_addr_json = json_object_new_object(); + json_object_object_add(iface_json, "area-address", + 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)); + } + } + if (adj->ipv4_address_count) { + ipv4_addr_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv4-address", + ipv4_addr_json); + for (unsigned int i = 0; i < adj->ipv4_address_count; + i++){ + inet_ntop(AF_INET, &adj->ipv4_addresses[i], buf, + sizeof(buf)); + json_object_string_add(ipv4_addr_json, "ipv4", buf); + } + } + if (adj->ll_ipv6_count) { + ipv6_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-link-local", + ipv6_link_json); + for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], buf, + sizeof(buf)); + json_object_string_add(ipv6_link_json, "ipv6", + buf); + } + } + if (adj->global_ipv6_count) { + ipv6_non_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-global", + ipv6_non_link_json); + for (unsigned int i = 0; i < adj->global_ipv6_count; + i++) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &adj->global_ipv6_addrs[i], + buf, sizeof(buf)); + json_object_string_add(ipv6_non_link_json, + "ipv6", buf); + } + } + + adj_sid_json = json_object_new_object(); + json_object_object_add(iface_json, "adj-sid", adj_sid_json); + for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) { + const char *adj_type; + const char *backup; + uint32_t sid; + + switch (sra->adj->circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + adj_type = "LAN Adjacency-SID"; + sid = sra->u.ladj_sid->sid; + break; + case CIRCUIT_T_P2P: + adj_type = "Adjacency-SID"; + sid = sra->u.adj_sid->sid; + break; + default: + continue; + } + backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)" + : ""; + + json_object_string_add(adj_sid_json, "nexthop", + (sra->nexthop.family == AF_INET) + ? "IPv4" + : "IPv6"); + json_object_string_add(adj_sid_json, "adj-type", + adj_type); + json_object_string_add(adj_sid_json, "is-backup", + backup); + json_object_int_add(adj_sid_json, "sid", sid); + } + } + return; +} + +/* * show isis neighbor [detail] */ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 4d84c5ca4d..7467a619cb 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -144,6 +144,8 @@ const char *isis_adj_yang_state(enum isis_adj_state state); void isis_adj_expire(struct thread *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, + char detail); 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); diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 1b0447226d..da75f196ba 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -933,6 +933,151 @@ void circuit_update_nlpids(struct isis_circuit *circuit) return; } +void isis_circuit_print_json(struct isis_circuit *circuit, + struct json_object *json, char detail) +{ + int level; + json_object *iface_json, *ipv4_addr_json, *ipv6_link_json, + *ipv6_non_link_json, *hold_json, *lan_prio_json, *levels_json, + *level_json; + char buf_prx[INET6_BUFSIZ]; + char buf[255]; + + snprintfrr(buf, sizeof(buf), "0x%x", circuit->circuit_id); + if (detail == ISIS_UI_LEVEL_BRIEF) { + iface_json = json_object_new_object(); + json_object_object_add(json, "interface", iface_json); + json_object_string_add(iface_json, "name", + circuit->interface->name); + json_object_string_add(iface_json, "circuit-id", buf); + json_object_string_add(iface_json, "state", + circuit_state2string(circuit->state)); + json_object_string_add(iface_json, "type", + circuit_type2string(circuit->circ_type)); + json_object_string_add(iface_json, "level", + circuit_t2string(circuit->is_type)); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) { + struct listnode *node; + struct prefix *ip_addr; + + iface_json = json_object_new_object(); + json_object_object_add(json, "interface", iface_json); + json_object_string_add(iface_json, "name", + circuit->interface->name); + json_object_string_add(iface_json, "state", + circuit_state2string(circuit->state)); + if (circuit->is_passive) + json_object_string_add(iface_json, "is-passive", + "passive"); + else + json_object_string_add(iface_json, "is-passive", + "active"); + json_object_string_add(iface_json, "circuit-id", buf); + json_object_string_add(iface_json, "type", + circuit_type2string(circuit->circ_type)); + 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)); + + + levels_json = json_object_new_array(); + json_object_object_add(iface_json, "levels", levels_json); + for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + if ((circuit->is_type & level) == 0) + continue; + level_json = json_object_new_object(); + json_object_string_add(level_json, "level", + circuit_t2string(level)); + if (circuit->area->newmetric) + json_object_int_add(level_json, "metric", + circuit->te_metric[0]); + else + json_object_int_add(level_json, "metric", + circuit->metric[0]); + if (!circuit->is_passive) { + json_object_int_add(level_json, + "active-neighbors", + circuit->upadjcount[0]); + json_object_int_add(level_json, + "hello-interval", + circuit->hello_interval[0]); + hold_json = json_object_new_object(); + json_object_object_add(level_json, "holddown", + hold_json); + json_object_int_add( + hold_json, "count", + circuit->hello_multiplier[0]); + json_object_string_add( + hold_json, "pad", + (circuit->pad_hellos ? "yes" : "no")); + json_object_int_add(level_json, "cnsp-interval", + circuit->csnp_interval[0]); + json_object_int_add(level_json, "psnp-interval", + circuit->psnp_interval[0]); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + lan_prio_json = + json_object_new_object(); + json_object_object_add(level_json, + "lan", + lan_prio_json); + json_object_int_add( + lan_prio_json, "priority", + circuit->priority[0]); + json_object_string_add( + lan_prio_json, "is-dis", + (circuit->u.bc.is_dr[0] + ? "yes" + : "no")); + } + } + json_object_array_add(levels_json, level_json); + } + + if (circuit->ip_addrs && listcount(circuit->ip_addrs) > 0) { + ipv4_addr_json = json_object_new_object(); + json_object_object_add(iface_json, "ip-prefix", + ipv4_addr_json); + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, + ip_addr)) { + snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX", + ip_addr); + json_object_string_add(ipv4_addr_json, "ip", + buf_prx); + } + } + if (circuit->ipv6_link && listcount(circuit->ipv6_link) > 0) { + ipv6_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-link-locals", + ipv6_link_json); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, + ip_addr)) { + snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX", + ip_addr); + json_object_string_add(ipv6_link_json, "ipv6", + buf_prx); + } + } + if (circuit->ipv6_non_link && + listcount(circuit->ipv6_non_link) > 0) { + ipv6_non_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-prefixes", + ipv6_non_link_json); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, + ip_addr)) { + snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX", + ip_addr); + json_object_string_add(ipv6_non_link_json, + "ipv6", buf_prx); + } + } + } + return; +} + void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, char detail) { diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 7465780848..5ff0390c26 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -206,6 +206,8 @@ void isis_circuit_down(struct isis_circuit *); void circuit_update_nlpids(struct isis_circuit *circuit); void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, char detail); +void isis_circuit_print_json(struct isis_circuit *circuit, + struct json_object *json, char detail); size_t isis_circuit_pdu_size(struct isis_circuit *circuit); void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 463d26f6c7..eb7e9e725e 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -733,8 +733,48 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size) } /* this function prints the lsp on show isis database */ -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, - struct isis *isis) +void lsp_print_common(struct isis_lsp *lsp, struct vty *vty, struct json_object *json, + char dynhost, struct isis *isis) +{ + if (json) { + return lsp_print_json(lsp, json, dynhost, isis); + } else { + return lsp_print_vty(lsp, vty, dynhost, isis); + } +} + +void lsp_print_json(struct isis_lsp *lsp, struct json_object *json, + char dynhost, struct isis *isis) +{ + char LSPid[255]; + char age_out[8]; + char b[200]; + json_object *own_json; + char buf[256]; + + lspid_print(lsp->hdr.lsp_id, LSPid, sizeof(LSPid), dynhost, 1, isis); + own_json = json_object_new_object(); + json_object_object_add(json, "lsp", own_json); + json_object_string_add(own_json, "id", LSPid); + json_object_string_add(own_json, "own", lsp->own_lsp ? "*" : " "); + json_object_int_add(json, "pdu-len", lsp->hdr.pdu_len); + snprintfrr(buf, sizeof(buf), "0x%08x", lsp->hdr.seqno); + json_object_string_add(json, "seq-number", buf); + snprintfrr(buf, sizeof(buf), "0x%04hx", lsp->hdr.checksum); + json_object_string_add(json, "chksum", buf); + if (lsp->hdr.rem_lifetime == 0) { + snprintf(age_out, sizeof(age_out), "(%d)", lsp->age_out); + age_out[7] = '\0'; + json_object_string_add(json, "holdtime", age_out); + } else { + json_object_int_add(json, "holdtime", lsp->hdr.rem_lifetime); + } + json_object_string_add( + json, "att-p-ol", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b))); +} + +void lsp_print_vty(struct isis_lsp *lsp, struct vty *vty, + char dynhost, struct isis *isis) { char LSPid[255]; char age_out[8]; @@ -754,30 +794,40 @@ void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b))); } -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, - struct isis *isis) +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, + struct json_object *json, char dynhost, + struct isis *isis) { - lsp_print(lsp, vty, dynhost, isis); - if (lsp->tlvs) - vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs)); - vty_out(vty, "\n"); + if (json) { + lsp_print_json(lsp, json, dynhost, isis); + if (lsp->tlvs) { + isis_format_tlvs(lsp->tlvs, json); + } + } else { + lsp_print_vty(lsp, vty, dynhost, isis); + if (lsp->tlvs) + vty_multiline(vty, " ", "%s", + isis_format_tlvs(lsp->tlvs, NULL)); + vty_out(vty, "\n"); + } } /* print all the lsps info in the local lspdb */ -int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost, struct isis *isis) +int lsp_print_all(struct vty *vty, struct json_object *json, + struct lspdb_head *head, char detail, char dynhost, + struct isis *isis) { struct isis_lsp *lsp; int lsp_count = 0; if (detail == ISIS_UI_LEVEL_BRIEF) { frr_each (lspdb, head, lsp) { - lsp_print(lsp, vty, dynhost, isis); + lsp_print_common(lsp, vty, json, dynhost, isis); lsp_count++; } } else if (detail == ISIS_UI_LEVEL_DETAIL) { frr_each (lspdb, head, lsp) { - lsp_print_detail(lsp, vty, dynhost, isis); + lsp_print_detail(lsp, vty, json, dynhost, isis); lsp_count++; } } @@ -1264,7 +1314,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (!fragments) { zlog_warn("BUG: could not fragment own LSP:"); log_multiline(LOG_WARNING, " ", "%s", - isis_format_tlvs(tlvs)); + isis_format_tlvs(tlvs, NULL)); isis_free_tlvs(tlvs); return; } diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index f42d702b37..b13b2a35e6 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -120,12 +120,19 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, char frag, struct isis *isis); -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, - struct isis *isis); -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, +void lsp_print_common(struct isis_lsp *lsp, struct vty *vty, + struct json_object *json, char dynhost, struct isis *isis); -int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost, struct isis *isis); +void lsp_print_vty(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis); +void lsp_print_json(struct isis_lsp *lsp, struct json_object *json, + char dynhost, struct isis *isis); +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, + struct json_object *json, char dynhost, + struct isis *isis); +int lsp_print_all(struct vty *vty, struct json_object *json, + struct lspdb_head *head, char detail, char dynhost, + struct isis *isis); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 517c9ec5aa..1a54d47f3c 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -2209,7 +2209,7 @@ int send_csnp(struct isis_circuit *circuit, int level) circuit->interface->name, stream_get_endp(circuit->snd_stream)); log_multiline(LOG_DEBUG, " ", "%s", - isis_format_tlvs(tlvs)); + isis_format_tlvs(tlvs, NULL)); if (IS_DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), @@ -2368,7 +2368,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) circuit->interface->name, stream_get_endp(circuit->snd_stream)); log_multiline(LOG_DEBUG, " ", "%s", - isis_format_tlvs(tlvs)); + isis_format_tlvs(tlvs, NULL)); if (IS_DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 04b5cf1a67..d5b02f3881 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -2683,3 +2683,15 @@ void isis_spf_print(struct isis_spftree *spftree, struct vty *vty) vty_out(vty, " run count : %u\n", spftree->runcount); } +void isis_spf_print_json(struct isis_spftree *spftree, struct json_object *json) +{ + char uptime[MONOTIME_STRLEN]; + time_t cur; + cur = time(NULL); + cur -= spftree->last_run_timestamp; + frrtime_to_interval(cur, uptime, sizeof(uptime)); + json_object_string_add(json, "last-run-elapsed", uptime); + json_object_int_add(json, "last-run-duration-usec", + spftree->last_run_duration); + json_object_int_add(json, "last-run-count", spftree->runcount); +} diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 5b3aa59379..815db7b226 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -75,6 +75,8 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, 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, + struct json_object *json); void isis_run_spf(struct isis_spftree *spftree); struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, uint8_t *sysid, diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index f1aae7caf1..d3d59fb435 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -22,6 +22,7 @@ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +#include <json-c/json_object.h> #include <zebra.h> #ifdef CRYPTO_INTERNAL @@ -57,7 +58,8 @@ typedef void (*free_item_func)(struct isis_item *i); typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s, struct sbuf *log, void *dest, int indent); typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent); + struct sbuf *buf, struct json_object *json, + int indent); typedef struct isis_item *(*copy_item_func)(struct isis_item *i); struct tlv_ops { @@ -208,152 +210,430 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) /* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, - struct sbuf *buf, int indent, - uint16_t mtid) + struct sbuf *buf, struct json_object *json, + int indent, uint16_t mtid) { + char aux_buf[255]; + char cnt_buf[255]; /* Standard metrics */ - if (IS_SUBTLV(exts, EXT_ADM_GRP)) - sbuf_push(buf, indent, "Administrative Group: 0x%x\n", - exts->adm_group); + if (IS_SUBTLV(exts, EXT_ADM_GRP)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "0x%x", + exts->adm_group); + json_object_string_add(json, "adm-group", aux_buf); + } else + sbuf_push(buf, indent, "Administrative Group: 0x%x\n", + exts->adm_group); + } if (IS_SUBTLV(exts, EXT_LLRI)) { - sbuf_push(buf, indent, "Link Local ID: %u\n", - exts->local_llri); - sbuf_push(buf, indent, "Link Remote ID: %u\n", - exts->remote_llri); - } - if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) - sbuf_push(buf, indent, "Local Interface IP Address(es): %pI4\n", - &exts->local_addr); - if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) - sbuf_push(buf, indent, - "Remote Interface IP Address(es): %pI4\n", - &exts->neigh_addr); - if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) - sbuf_push(buf, indent, - "Local Interface IPv6 Address(es): %pI6\n", - &exts->local_addr6); - if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) - sbuf_push(buf, indent, - "Remote Interface IPv6 Address(es): %pI6\n", - &exts->neigh_addr6); - if (IS_SUBTLV(exts, EXT_MAX_BW)) - sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n", - exts->max_bw); - if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) - sbuf_push(buf, indent, - "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", - exts->max_rsv_bw); + if (json) { + json_object_int_add(json, "link-local-id", + exts->local_llri); + json_object_int_add(json, "link-remote-id", + exts->remote_llri); + } else { + sbuf_push(buf, indent, "Link Local ID: %u\n", + exts->local_llri); + sbuf_push(buf, indent, "Link Remote ID: %u\n", + exts->remote_llri); + } + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) { + if (json) { + inet_ntop(AF_INET, &exts->local_addr, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "local-iface-ip", aux_buf); + } else + sbuf_push(buf, indent, + "Local Interface IP Address(es): %pI4\n", + &exts->local_addr); + } + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) { + if (json) { + inet_ntop(AF_INET, &exts->neigh_addr, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "remote-iface-ip", + aux_buf); + } else + sbuf_push(buf, indent, + "Remote Interface IP Address(es): %pI4\n", + &exts->neigh_addr); + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) { + if (json) { + inet_ntop(AF_INET6, &exts->local_addr6, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "local-iface-ipv6", + aux_buf); + } else + sbuf_push(buf, indent, + "Local Interface IPv6 Address(es): %pI6\n", + &exts->local_addr6); + } + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) { + if (json) { + inet_ntop(AF_INET6, &exts->neigh_addr6, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "remote-iface-ipv6", + aux_buf); + } else + sbuf_push(buf, indent, + "Remote Interface IPv6 Address(es): %pI6\n", + &exts->neigh_addr6); + } + if (IS_SUBTLV(exts, EXT_MAX_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + exts->max_bw); + json_object_string_add(json, "max-bandwith-bytes-sec", + aux_buf); + } else + sbuf_push(buf, indent, + "Maximum Bandwidth: %g (Bytes/sec)\n", + exts->max_bw); + } + if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + exts->max_rsv_bw); + json_object_string_add( + json, "max-res-bandwith-bytes-sec", aux_buf); + } else + sbuf_push( + buf, indent, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + exts->max_rsv_bw); + } if (IS_SUBTLV(exts, EXT_UNRSV_BW)) { - sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); - for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { - sbuf_push(buf, indent + 2, - "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", - j, exts->unrsv_bw[j], - j + 1, exts->unrsv_bw[j + 1]); + if (json) { + struct json_object *unrsv_json; + unrsv_json = json_object_new_object(); + json_object_object_add(json, "unrsv-bandwith-bytes-sec", + unrsv_json); + for (int j = 0; j < MAX_CLASS_TYPE; j += 1) { + snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", j); + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + exts->unrsv_bw[j]); + json_object_string_add(unrsv_json, cnt_buf, + aux_buf); + } + } else { + sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); + for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { + sbuf_push( + buf, indent + 2, + "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + j, exts->unrsv_bw[j], j + 1, + exts->unrsv_bw[j + 1]); + } } } - if (IS_SUBTLV(exts, EXT_TE_METRIC)) - sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n", - exts->te_metric); - if (IS_SUBTLV(exts, EXT_RMT_AS)) - sbuf_push(buf, indent, - "Inter-AS TE Remote AS number: %u\n", - exts->remote_as); - if (IS_SUBTLV(exts, EXT_RMT_IP)) - sbuf_push(buf, indent, - "Inter-AS TE Remote ASBR IP address: %pI4\n", - &exts->remote_ip); + if (IS_SUBTLV(exts, EXT_TE_METRIC)) { + if (json) { + json_object_int_add(json, "te-metric", exts->te_metric); + } else + sbuf_push(buf, indent, + "Traffic Engineering Metric: %u\n", + exts->te_metric); + } + if (IS_SUBTLV(exts, EXT_RMT_AS)) { + if (json) { + json_object_int_add(json, "inter-as-te-remote-as", + exts->remote_as); + } else + sbuf_push(buf, indent, + "Inter-AS TE Remote AS number: %u\n", + exts->remote_as); + } + if (IS_SUBTLV(exts, EXT_RMT_IP)) { + if (json) { + inet_ntop(AF_INET6, &exts->remote_ip, aux_buf, + sizeof(aux_buf)); + json_object_string_add( + json, "inter-as-te-remote-asbr-ip", aux_buf); + } else + sbuf_push(buf, indent, + "Inter-AS TE Remote ASBR IP address: %pI4\n", + &exts->remote_ip); + } /* Extended metrics */ - if (IS_SUBTLV(exts, EXT_DELAY)) - sbuf_push(buf, indent, - "%s Average Link Delay: %u (micro-sec)\n", - IS_ANORMAL(exts->delay) ? "Anomalous" : "Normal", - exts->delay); + if (IS_SUBTLV(exts, EXT_DELAY)) { + if (json) { + struct json_object *avg_json; + avg_json = json_object_new_object(); + json_object_object_add(json, "avg-delay", avg_json); + json_object_string_add(avg_json, "delay", + IS_ANORMAL(exts->delay) + ? "Anomalous" + : "Normal"); + json_object_int_add(avg_json, "micro-sec", exts->delay); + } else + sbuf_push(buf, indent, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(exts->delay) ? "Anomalous" + : "Normal", + exts->delay); + } if (IS_SUBTLV(exts, EXT_MM_DELAY)) { - sbuf_push(buf, indent, "%s Min/Max Link Delay: %u / %u (micro-sec)\n", - IS_ANORMAL(exts->min_delay) ? "Anomalous" : "Normal", - exts->min_delay & TE_EXT_MASK, - exts->max_delay & TE_EXT_MASK); + if (json) { + struct json_object *avg_json; + avg_json = json_object_new_object(); + json_object_object_add(json, "max-min-delay", avg_json); + json_object_string_add(avg_json, "delay", + IS_ANORMAL(exts->min_delay) + ? "Anomalous" + : "Normal"); + snprintfrr(aux_buf, sizeof(aux_buf), "%u / %u", + exts->min_delay & TE_EXT_MASK, + exts->max_delay & TE_EXT_MASK); + json_object_string_add(avg_json, "micro-sec", aux_buf); + + } else + sbuf_push( + buf, indent, + "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(exts->min_delay) ? "Anomalous" + : "Normal", + exts->min_delay & TE_EXT_MASK, + exts->max_delay & TE_EXT_MASK); } if (IS_SUBTLV(exts, EXT_DELAY_VAR)) { - sbuf_push(buf, indent, - "Delay Variation: %u (micro-sec)\n", - exts->delay_var & TE_EXT_MASK); - } - if (IS_SUBTLV(exts, EXT_PKT_LOSS)) - sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", - IS_ANORMAL(exts->pkt_loss) ? "Anomalous" : "Normal", - (float)((exts->pkt_loss & TE_EXT_MASK) - * LOSS_PRECISION)); - if (IS_SUBTLV(exts, EXT_RES_BW)) - sbuf_push(buf, indent, - "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", - exts->res_bw); - if (IS_SUBTLV(exts, EXT_AVA_BW)) - sbuf_push(buf, indent, - "Unidir. Available Bandwidth: %g (Bytes/sec)\n", - exts->ava_bw); - if (IS_SUBTLV(exts, EXT_USE_BW)) - sbuf_push(buf, indent, - "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", - exts->use_bw); + if (json) { + json_object_int_add(json, "delay-variation-micro-sec", + exts->delay_var & TE_EXT_MASK); + } else + sbuf_push(buf, indent, + "Delay Variation: %u (micro-sec)\n", + exts->delay_var & TE_EXT_MASK); + } + if (IS_SUBTLV(exts, EXT_PKT_LOSS)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (float)((exts->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + struct json_object *link_json; + link_json = json_object_new_object(); + json_object_object_add(json, "link-packet-loss", + link_json); + json_object_string_add(link_json, "loss", + IS_ANORMAL(exts->pkt_loss) + ? "Anomalous" + : "Normal"); + json_object_string_add(link_json, "percentaje", + aux_buf); + } else + sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(exts->pkt_loss) ? "Anomalous" + : "Normal", + (float)((exts->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + } + if (IS_SUBTLV(exts, EXT_RES_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (exts->res_bw)); + json_object_string_add(json, + "unidir-residual-band-bytes-sec", + aux_buf); + } else + sbuf_push( + buf, indent, + "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", + exts->res_bw); + } + if (IS_SUBTLV(exts, EXT_AVA_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (exts->ava_bw)); + json_object_string_add( + json, "unidir-available-band-bytes-sec", + aux_buf); + } else + sbuf_push( + buf, indent, + "Unidir. Available Bandwidth: %g (Bytes/sec)\n", + exts->ava_bw); + } + if (IS_SUBTLV(exts, EXT_USE_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (exts->use_bw)); + json_object_string_add(json, + "unidir-utilized-band-bytes-sec", + aux_buf); + } else + sbuf_push( + buf, indent, + "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", + exts->use_bw); + } /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ if (IS_SUBTLV(exts, EXT_ADJ_SID)) { struct isis_adj_sid *adj; - for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj; - adj = adj->next) { - sbuf_push( - buf, indent, - "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n", - adj->sid, adj->weight, - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG - ? '1' - : '0'); - } + if (json) { + struct json_object *arr_adj_json, *flags_json; + arr_adj_json = json_object_new_array(); + json_object_object_add(json, "adj-sid", arr_adj_json); + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; + adj; adj = adj->next) { + snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", + adj->sid); + flags_json = json_object_new_object(); + json_object_int_add(flags_json, "sid", + adj->sid); + json_object_int_add(flags_json, "weight", + adj->weight); + json_object_string_add( + flags_json, "flag-f", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-b", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-v", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-l", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-s", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-p", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? "1" + : "0"); + json_object_array_add(arr_adj_json, flags_json); + } + } else + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; + adj; adj = adj->next) { + sbuf_push( + buf, indent, + "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n", + adj->sid, adj->weight, + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? '1' + : '0'); + } } /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */ if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) { struct isis_lan_adj_sid *lan; - - for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; - lan; lan = lan->next) { - continue; - sbuf_push(buf, indent, - "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n" - " Neighbor-ID: %s\n", - lan->sid, lan->weight, - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG - ? '1' - : '0', - isis_format_id(lan->neighbor_id, 6)); - } + if (json) { + struct json_object *arr_adj_json, *flags_json; + arr_adj_json = json_object_new_array(); + json_object_object_add(json, "lan-adj-sid", + arr_adj_json); + for (lan = (struct isis_lan_adj_sid *) + exts->adj_sid.head; + lan; lan = lan->next) { + if (((mtid == ISIS_MT_IPV4_UNICAST) && + (lan->family != AF_INET)) || + ((mtid == ISIS_MT_IPV6_UNICAST) && + (lan->family != AF_INET6))) + continue; + snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", + lan->sid); + flags_json = json_object_new_object(); + json_object_int_add(flags_json, "sid", + lan->sid); + json_object_int_add(flags_json, "weight", + lan->weight); + json_object_string_add( + flags_json, "flag-f", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-b", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-v", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-l", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-s", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-p", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? "1" + : "0"); + json_object_array_add(arr_adj_json, flags_json); + } + } else + + for (lan = (struct isis_lan_adj_sid *) + exts->lan_sid.head; + lan; lan = lan->next) { + if (((mtid == ISIS_MT_IPV4_UNICAST) && + (lan->family != AF_INET)) || + ((mtid == ISIS_MT_IPV6_UNICAST) && + (lan->family != AF_INET6))) + continue; + sbuf_push( + buf, indent, + "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n" + " Neighbor-ID: %s\n", + lan->sid, lan->weight, + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? '1' + : '0', + isis_format_id(lan->neighbor_id, 6)); + } } } @@ -880,26 +1160,64 @@ static struct isis_item *copy_item_prefix_sid(struct isis_item *i) } static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; - sbuf_push(buf, indent, "SR Prefix-SID "); - if (sid->flags & ISIS_PREFIX_SID_VALUE) { - sbuf_push(buf, 0, "Label: %u, ", sid->value); + if (json) { + struct json_object *sr_json; + sr_json = json_object_new_object(); + json_object_object_add(json, "sr", sr_json); + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + json_object_int_add(sr_json, "label", sid->value); + } else { + json_object_int_add(sr_json, "index", sid->value); + } + json_object_int_add(sr_json, "alg", sid->algorithm); + json_object_string_add( + sr_json, "readvertised", + ((sid->flags & ISIS_PREFIX_SID_READVERTISED) ? "yes" + : "")); + json_object_string_add( + sr_json, "node", + ((sid->flags & ISIS_PREFIX_SID_NODE) ? "yes" : "")); + json_object_string_add(sr_json, "php", + ((sid->flags & ISIS_PREFIX_SID_NO_PHP) + ? "no-php" + : "php")); + json_object_string_add( + sr_json, "explicit-null", + ((sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL) ? "yes" + : "")); + json_object_string_add( + sr_json, "value", + ((sid->flags & ISIS_PREFIX_SID_VALUE) ? "yes" : "")); + json_object_string_add( + sr_json, "local", + ((sid->flags & ISIS_PREFIX_SID_LOCAL) ? "yes" : "")); + } else { - sbuf_push(buf, 0, "Index: %u, ", sid->value); + sbuf_push(buf, indent, "SR Prefix-SID "); + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + sbuf_push(buf, 0, "Label: %u, ", sid->value); + } else { + sbuf_push(buf, 0, "Index: %u, ", sid->value); + } + sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm); + sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n", + sid->flags & ISIS_PREFIX_SID_READVERTISED + ? " READVERTISED" + : "", + sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", + sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP" + : " PHP", + sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL + ? " EXPLICIT-NULL" + : "", + sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", + sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); } - sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm); - sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n", - sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" - : "", - sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", - sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP" : " PHP", - sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" - : "", - sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", - sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); } static void free_item_prefix_sid(struct isis_item *i) @@ -977,7 +1295,7 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s, sid.value = stream_getl(s); } - format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, indent + 2); + format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, NULL, indent + 2); append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid)); return 0; } @@ -997,14 +1315,21 @@ static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p) } static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, + int indent) { if (!p) return; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n", - prefix2str(p, prefixbuf, sizeof(prefixbuf))); + if (json) { + prefix2str(p, prefixbuf, sizeof(prefixbuf)); + json_object_string_add(json, "ipv6-src-prefix", prefixbuf); + } else { + sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n", + prefix2str(p, prefixbuf, sizeof(prefixbuf))); + } } static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p, @@ -1080,7 +1405,8 @@ static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *src, struct isis_item_list *dest); static void format_items_(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items, - struct sbuf *buf, int indent); + struct sbuf *buf, struct json_object *json, + int indent); #define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items); @@ -1124,12 +1450,12 @@ static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) } static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, - &subtlvs->prefix_sids, buf, indent); + &subtlvs->prefix_sids, buf, json, indent); - format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent); + format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, json, indent); } static void isis_free_subtlvs(struct isis_subtlvs *subtlvs) @@ -1189,12 +1515,18 @@ static struct isis_item *copy_item_area_address(struct isis_item *i) } static void format_item_area_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_area_address *addr = (struct isis_area_address *)i; - sbuf_push(buf, indent, "Area Address: %s\n", - isonet_print(addr->addr, addr->len)); + 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)); + } } static void free_item_area_address(struct isis_item *i) @@ -1251,7 +1583,7 @@ static int unpack_item_area_address(uint16_t mtid, uint8_t len, stream_get(rv->addr, s, rv->len); format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv, - log, indent + 2); + log, NULL, indent + 2); append_item(&tlvs->area_addresses, (struct isis_item *)rv); return 0; out: @@ -1271,12 +1603,21 @@ static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i) } static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; - sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n", - isis_format_id(r->id, 7), r->metric); + 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_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); } static void free_item_oldstyle_reach(struct isis_item *i) @@ -1327,7 +1668,7 @@ static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len, stream_forward_getp(s, 3); /* Skip other metrics */ stream_get(rv->id, s, 7); - format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log, + format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv); return 0; @@ -1344,11 +1685,17 @@ static struct isis_item *copy_item_lan_neighbor(struct isis_item *i) } static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; - sbuf_push(buf, indent, "LAN Neighbor: %s\n", isis_format_id(n->mac, 6)); + 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)); } static void free_item_lan_neighbor(struct isis_item *i) @@ -1389,7 +1736,7 @@ static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len, struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(rv->mac, s, 6); - format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->lan_neighbor, (struct isis_item *)rv); return 0; } @@ -1409,10 +1756,23 @@ static struct isis_item *copy_item_lsp_entry(struct isis_item *i) } static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + 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)); + 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, @@ -1462,7 +1822,7 @@ static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s, rv->seqno = stream_getl(s); rv->checksum = stream_getw(s); - format_item_lsp_entry(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_lsp_entry(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->lsp_entries, (struct isis_item *)rv); return 0; } @@ -1484,19 +1844,40 @@ static struct isis_item *copy_item_extended_reach(struct isis_item *i) } static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; - sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)", - (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", - isis_format_id(r->id, 7), r->metric); - if (mtid != ISIS_MT_IPV4_UNICAST) - sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); - sbuf_push(buf, 0, "\n"); + if (json) { + struct json_object *reach_json; + reach_json = json_object_new_object(); + json_object_object_add(json, "ext-reach", reach_json); + 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_int_add(reach_json, "metric", r->metric); + if (mtid != ISIS_MT_IPV4_UNICAST) + json_object_string_add(reach_json, "mt-name", + isis_mtid2str(mtid)); + + if (r->subtlvs) + format_item_ext_subtlvs(r->subtlvs, NULL, json, + indent + 2, mtid); + } 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); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); - if (r->subtlvs) - format_item_ext_subtlvs(r->subtlvs, buf, indent + 2, mtid); + if (r->subtlvs) + format_item_ext_subtlvs(r->subtlvs, buf, NULL, + indent + 2, mtid); + } } static void free_item_extended_reach(struct isis_item *i) @@ -1579,7 +1960,7 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len, } } - format_item_extended_reach(mtid, (struct isis_item *)rv, log, + format_item_extended_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(items, (struct isis_item *)rv); return 0; @@ -1603,11 +1984,20 @@ static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i) } static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; + if (json) { + struct json_object *old_json; + old_json = json_object_new_object(); + json_object_object_add(json, "old-ip-reach-style", old_json); + json_object_string_add(old_json, "prefix", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf))); + json_object_int_add(old_json, "metric", r->metric); + } else sbuf_push(buf, indent, "IP Reachability: %s (Metric: %hhu)\n", prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric); @@ -1669,7 +2059,7 @@ static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len, stream_get(&mask, s, 4); rv->prefix.prefixlen = ip_masklen(mask); - format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log, + format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(dest, (struct isis_item *)rv); return 0; @@ -1689,17 +2079,32 @@ static void copy_tlv_protocols_supported(struct isis_protocols_supported *src, } static void format_tlv_protocols_supported(struct isis_protocols_supported *p, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { if (!p || !p->count || !p->protocols) return; - sbuf_push(buf, indent, "Protocols Supported: "); - for (uint8_t i = 0; i < p->count; i++) { - sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]), - (i + 1 < p->count) ? ", " : ""); + if (json) { + struct json_object *protocol_json; + char buf[255]; + + protocol_json = json_object_new_object(); + json_object_object_add(json, "protocols-supported", + protocol_json); + for (uint8_t i = 0; i < p->count; i++) { + snprintfrr(buf, sizeof(buf), "%d", i); + json_object_string_add(protocol_json, buf, + nlpid2str(p->protocols[i])); + } + } else { + sbuf_push(buf, indent, "Protocols Supported: "); + for (uint8_t i = 0; i < p->count; i++) { + sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]), + (i + 1 < p->count) ? ", " : ""); + } + sbuf_push(buf, 0, "\n"); } - sbuf_push(buf, 0, "\n"); } static void free_tlv_protocols_supported(struct isis_protocols_supported *p) @@ -1746,7 +2151,7 @@ static int unpack_tlv_protocols_supported(enum isis_tlv_context context, tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len); stream_get(tlvs->protocols_supported.protocols, s, tlv_len); - format_tlv_protocols_supported(&tlvs->protocols_supported, log, + format_tlv_protocols_supported(&tlvs->protocols_supported, log, NULL, indent + 2); return 0; } @@ -1762,13 +2167,18 @@ static struct isis_item *copy_item_ipv4_address(struct isis_item *i) } static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; char addrbuf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf); + if (json) { + json_object_string_add(json, "ipv4", addrbuf); + } else { + sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf); + } } static void free_item_ipv4_address(struct isis_item *i) @@ -1809,7 +2219,7 @@ static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len, struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(&rv->addr, s, 4); - format_item_ipv4_address(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_ipv4_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->ipv4_address, (struct isis_item *)rv); return 0; } @@ -1826,13 +2236,17 @@ static struct isis_item *copy_item_ipv6_address(struct isis_item *i) } static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; char addrbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf); + if (json) + json_object_string_add(json, "ipv6", addrbuf); + else + sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf); } static void free_item_ipv6_address(struct isis_item *i) @@ -1873,7 +2287,7 @@ static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len, struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(&rv->addr, s, IPV6_MAX_BYTELEN); - format_item_ipv6_address(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_ipv6_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->ipv6_address, (struct isis_item *)rv); return 0; } @@ -1890,13 +2304,19 @@ static struct isis_item *copy_item_global_ipv6_address(struct isis_item *i) } static void format_item_global_ipv6_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, + int indent) { struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; char addrbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "Global IPv6 Interface Address: %s\n", addrbuf); + if (json) + json_object_string_add(json, "global-ipv6", addrbuf); + else + sbuf_push(buf, indent, "Global IPv6 Interface Address: %s\n", + addrbuf); } static void free_item_global_ipv6_address(struct isis_item *i) @@ -1937,7 +2357,7 @@ static int unpack_item_global_ipv6_address(uint16_t mtid, uint8_t len, struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(&rv->addr, s, IPV6_MAX_BYTELEN); - format_item_global_ipv6_address(mtid, (struct isis_item *)rv, log, + format_item_global_ipv6_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->global_ipv6_address, (struct isis_item *)rv); return 0; @@ -1956,14 +2376,23 @@ static struct isis_item *copy_item_mt_router_info(struct isis_item *i) } static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; - sbuf_push(buf, indent, "MT Router Info: %s%s%s\n", - isis_mtid2str(info->mtid), - info->overload ? " Overload" : "", - info->attached ? " Attached" : ""); + if (json) { + struct json_object *mt_json; + mt_json = json_object_new_object(); + json_object_object_add(json, "mt", mt_json); + json_object_int_add(mt_json, "mtid", info->mtid); + json_object_string_add(mt_json, "overload", info->overload?"true":"false"); + json_object_string_add(mt_json, "attached", info->attached?"true":"false"); + } else + sbuf_push(buf, indent, "MT Router Info: %s%s%s\n", + isis_mtid2str(info->mtid), + info->overload ? " Overload" : "", + info->attached ? " Attached" : ""); } static void free_item_mt_router_info(struct isis_item *i) @@ -2015,7 +2444,7 @@ static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len, rv->attached = entry & ISIS_MT_AT_MASK; rv->mtid = entry & ISIS_MT_MASK; - format_item_mt_router_info(mtid, (struct isis_item *)rv, log, + format_item_mt_router_info(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->mt_router_info, (struct isis_item *)rv); return 0; @@ -2034,14 +2463,17 @@ static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id) } static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { if (!id) return; char addrbuf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf); + if (json) + json_object_string_add(json, "te-router-id", addrbuf); + else + sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf); } static void free_tlv_te_router_id(struct in_addr *id) @@ -2085,7 +2517,7 @@ static int unpack_tlv_te_router_id(enum isis_tlv_context context, tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4); stream_get(tlvs->te_router_id, s, 4); - format_tlv_te_router_id(tlvs->te_router_id, log, indent + 2); + format_tlv_te_router_id(tlvs->te_router_id, log, NULL, indent + 2); return 0; } @@ -2107,22 +2539,46 @@ static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i) } static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s", - (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", - prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric, - r->down ? " Down" : ""); - if (mtid != ISIS_MT_IPV4_UNICAST) - sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); - sbuf_push(buf, 0, "\n"); - - if (r->subtlvs) { - sbuf_push(buf, indent, " Subtlvs:\n"); - format_subtlvs(r->subtlvs, buf, indent + 4); + if (json) { + struct json_object *ext_json; + ext_json = json_object_new_object(); + json_object_object_add(json, "ext-ip-reach", ext_json); + json_object_string_add( + json, "mt-id", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT"); + json_object_string_add( + json, "ip-reach", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf))); + json_object_int_add(json, "ip-reach-metric", r->metric); + json_object_string_add(json, "down", r->down ? "yes" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + json_object_string_add(json, "mt-name", + isis_mtid2str(mtid)); + if (r->subtlvs) { + struct json_object *subtlv_json; + subtlv_json = json_object_new_object(); + json_object_object_add(json, "subtlvs", subtlv_json); + format_subtlvs(r->subtlvs, NULL, subtlv_json, 0); + } + } else { + sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), + r->metric, r->down ? " Down" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, NULL, indent + 4); + } } } @@ -2216,7 +2672,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, if (orig_prefix != rv->prefix.prefix.s_addr) sbuf_push(log, indent + 2, "WARNING: Prefix had hostbits set.\n"); - format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log, + format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) { @@ -2273,12 +2729,15 @@ static char *copy_tlv_dynamic_hostname(const char *hostname) } static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { if (!hostname) return; - sbuf_push(buf, indent, "Hostname: %s\n", hostname); + if (json) + json_object_string_add(json, "hostname", hostname); + else + sbuf_push(buf, indent, "Hostname: %s\n", hostname); } static void free_tlv_dynamic_hostname(char *hostname) @@ -2356,14 +2815,18 @@ static struct in6_addr *copy_tlv_te_router_id_ipv6(const struct in6_addr *id) } static void format_tlv_te_router_id_ipv6(const struct in6_addr *id, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { if (!id) return; char addrbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, id, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "IPv6 TE Router ID: %s\n", addrbuf); + if (json) + json_object_string_add(json, "ipv6-te-router-id", addrbuf); + else + sbuf_push(buf, indent, "IPv6 TE Router ID: %s\n", addrbuf); } static void free_tlv_te_router_id_ipv6(struct in6_addr *id) @@ -2409,7 +2872,7 @@ static int unpack_tlv_te_router_id_ipv6(enum isis_tlv_context context, tlvs->te_router_id_ipv6 = XCALLOC(MTYPE_ISIS_TLV, IPV6_MAX_BYTELEN); stream_get(tlvs->te_router_id_ipv6, s, IPV6_MAX_BYTELEN); - format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, log, indent + 2); + format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, log, NULL, indent + 2); return 0; } @@ -2429,26 +2892,50 @@ static struct isis_spine_leaf *copy_tlv_spine_leaf( } static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { if (!spine_leaf) return; - sbuf_push(buf, indent, "Spine-Leaf-Extension:\n"); - if (spine_leaf->has_tier) { - if (spine_leaf->tier == ISIS_TIER_UNDEFINED) { - sbuf_push(buf, indent, " Tier: undefined\n"); - } else { - sbuf_push(buf, indent, " Tier: %hhu\n", - spine_leaf->tier); + char aux_buf[255]; + + if (json) { + struct json_object *spine_json; + spine_json = json_object_new_object(); + json_object_object_add(json, "spine-leaf-extension", + spine_json); + if (spine_leaf->has_tier) { + snprintfrr(aux_buf, sizeof(aux_buf), "%hhu", + spine_leaf->tier); + json_object_string_add( + spine_json, "tier", + (spine_leaf->tier == ISIS_TIER_UNDEFINED) + ? "undefined" + : aux_buf); + } + json_object_string_add(spine_json, "flag-leaf", + spine_leaf->is_leaf ? "yes" : ""); + json_object_string_add(spine_json, "flag-spine", + spine_leaf->is_spine ? "yes" : ""); + json_object_string_add(spine_json, "flag-backup", + spine_leaf->is_backup ? "yes" : ""); + } else { + sbuf_push(buf, indent, "Spine-Leaf-Extension:\n"); + if (spine_leaf->has_tier) { + if (spine_leaf->tier == ISIS_TIER_UNDEFINED) { + sbuf_push(buf, indent, " Tier: undefined\n"); + } else { + sbuf_push(buf, indent, " Tier: %hhu\n", + spine_leaf->tier); + } } - } - - sbuf_push(buf, indent, " Flags:%s%s%s\n", - spine_leaf->is_leaf ? " LEAF" : "", - spine_leaf->is_spine ? " SPINE" : "", - spine_leaf->is_backup ? " BACKUP" : ""); + sbuf_push(buf, indent, " Flags:%s%s%s\n", + spine_leaf->is_leaf ? " LEAF" : "", + spine_leaf->is_spine ? " SPINE" : "", + spine_leaf->is_backup ? " BACKUP" : ""); + } } static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf) @@ -2562,25 +3049,45 @@ static struct isis_threeway_adj *copy_tlv_threeway_adj( return rv; } -static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, - struct sbuf *buf, int indent) +static void +format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, + struct sbuf *buf, struct json_object *json, int indent) { if (!threeway_adj) return; - sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n"); - sbuf_push(buf, indent, " State: %s (%d)\n", - isis_threeway_state_name(threeway_adj->state), - threeway_adj->state); - sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n", - threeway_adj->local_circuit_id); - if (!threeway_adj->neighbor_set) - return; + if (json) { + struct json_object *three_json; + three_json = json_object_new_object(); + json_object_object_add(json, "p2p-three-way-adj", three_json); + json_object_string_add( + three_json, "state-name", + isis_threeway_state_name(threeway_adj->state)); + json_object_int_add(three_json, "state", threeway_adj->state); + json_object_int_add(three_json, "ext-local-circuit-id", + 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_int_add(three_json, "neigh-ext-circuit-id", + threeway_adj->neighbor_circuit_id); + } else { + sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n"); + sbuf_push(buf, indent, " State: %s (%d)\n", + isis_threeway_state_name(threeway_adj->state), + threeway_adj->state); + sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n", + threeway_adj->local_circuit_id); + if (!threeway_adj->neighbor_set) + return; - sbuf_push(buf, indent, " Neighbor System ID: %s\n", - isis_format_id(threeway_adj->neighbor_id, 6)); - sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", - threeway_adj->neighbor_circuit_id); + sbuf_push(buf, indent, " Neighbor System ID: %s\n", + isis_format_id(threeway_adj->neighbor_id, 6)); + sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", + threeway_adj->neighbor_circuit_id); + } } static void free_tlv_threeway_adj(struct isis_threeway_adj *threeway_adj) @@ -2663,24 +3170,51 @@ static struct isis_item *copy_item_ipv6_reach(struct isis_item *i) } static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s", - (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ", - prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), - r->metric, - r->down ? " Down" : "", - r->external ? " External" : ""); - if (mtid != ISIS_MT_IPV4_UNICAST) - sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); - sbuf_push(buf, 0, "\n"); - - if (r->subtlvs) { - sbuf_push(buf, indent, " Subtlvs:\n"); - format_subtlvs(r->subtlvs, buf, indent + 4); + if (json) { + struct json_object *reach_json; + reach_json = json_object_new_object(); + json_object_object_add(json, "ipv6-reach", reach_json); + json_object_string_add(reach_json, "mt-id", + (mtid == ISIS_MT_IPV4_UNICAST) ? "" + : "mt"); + json_object_string_add( + reach_json, "prefix", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf))); + json_object_int_add(reach_json, "metric", r->metric); + json_object_string_add(reach_json, "down", + r->down ? "yes" : ""); + json_object_string_add(reach_json, "external", + r->external ? "yes" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + json_object_string_add(reach_json, "mt-name", + isis_mtid2str(mtid)); + if (r->subtlvs) { + struct json_object *subtlvs_json; + subtlvs_json = json_object_new_object(); + json_object_object_add(json, "subtlvs", subtlvs_json); + format_subtlvs(r->subtlvs, NULL, subtlvs_json, 0); + } + } else { + sbuf_push(buf, indent, + "%sIPv6 Reachability: %s (Metric: %u)%s%s", + (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), + r->metric, r->down ? " Down" : "", + r->external ? " External" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, NULL, indent + 4); + } } } @@ -2773,7 +3307,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix))) sbuf_push(log, indent + 2, "WARNING: Prefix had hostbits set.\n"); - format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); if (control & ISIS_IPV6_REACH_SUBTLV) { consume += 1; @@ -2834,6 +3368,77 @@ static struct isis_router_cap *copy_tlv_router_cap( return rv; } +static void format_tlv_router_cap_json(const struct isis_router_cap *router_cap, + struct json_object *json) +{ + char addrbuf[INET_ADDRSTRLEN]; + + if (!router_cap) + return; + + /* Router ID and Flags */ + struct json_object *cap_json; + cap_json = json_object_new_object(); + json_object_object_add(json, "router-capability", cap_json); + inet_ntop(AF_INET, &router_cap->router_id, addrbuf, sizeof(addrbuf)); + json_object_string_add(cap_json, "id", addrbuf); + json_object_string_add( + cap_json, "flag-d", + router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? "1" : "0"); + json_object_string_add( + cap_json, "flag-s", + router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? "1" : "0"); + + /* Segment Routing Global Block as per RFC8667 section #3.1 */ + if (router_cap->srgb.range_size != 0) { + struct json_object *gb_json; + gb_json = json_object_new_object(); + json_object_object_add(json, "segment-routing-gb", gb_json); + json_object_string_add(gb_json, "ipv4", + IS_SR_IPV4(&router_cap->srgb) ? "1" + : "0"); + json_object_string_add(gb_json, "ipv6", + IS_SR_IPV6(&router_cap->srgb) ? "1" + : "0"); + json_object_int_add(gb_json, "global-block-base", + router_cap->srgb.lower_bound); + json_object_int_add(gb_json, "global-block-range", + router_cap->srgb.range_size); + } + + /* Segment Routing Local Block as per RFC8667 section #3.3 */ + if (router_cap->srlb.range_size != 0) { + struct json_object *lb_json; + lb_json = json_object_new_object(); + json_object_object_add(json, "segment-routing-lb", lb_json); + json_object_int_add(lb_json, "global-block-base", + router_cap->srlb.lower_bound); + json_object_int_add(lb_json, "global-block-range", + router_cap->srlb.range_size); + } + + /* Segment Routing Algorithms as per RFC8667 section #3.2 */ + if (router_cap->algo[0] != SR_ALGORITHM_UNSET) { + char buf[255]; + struct json_object *alg_json; + alg_json = json_object_new_object(); + json_object_object_add(json, "segment-routing-algorithm", + alg_json); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (router_cap->algo[i] != SR_ALGORITHM_UNSET) { + snprintfrr(buf, sizeof(buf), "%d", i); + json_object_string_add(alg_json, buf, + router_cap->algo[i] == 0 + ? "SPF" + : "Strict SPF"); + } + } + + /* Segment Routing Node MSD as per RFC8491 section #2 */ + if (router_cap->msd != 0) + json_object_int_add(json, "msd", router_cap->msd); +} + static void format_tlv_router_cap(const struct isis_router_cap *router_cap, struct sbuf *buf, int indent) { @@ -3177,26 +3782,40 @@ static struct isis_item *copy_item_auth(struct isis_item *i) } static void format_item_auth(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_auth *auth = (struct isis_auth *)i; char obuf[768]; - sbuf_push(buf, indent, "Authentication:\n"); + if (json) + json_object_string_add(json, "test-auth", "ok"); + else + sbuf_push(buf, indent, "Authentication:\n"); switch (auth->type) { case ISIS_PASSWD_TYPE_CLEARTXT: zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length); - sbuf_push(buf, indent, " Password: %s\n", obuf); + if (json) + json_object_string_add(json, "auth-pass", obuf); + else + sbuf_push(buf, indent, " Password: %s\n", obuf); break; case ISIS_PASSWD_TYPE_HMAC_MD5: for (unsigned int j = 0; j < 16; j++) { - snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j, - "%02hhx", auth->value[j]); + snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j, "%02hhx", + auth->value[j]); } - sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf); + if (json) + json_object_string_add(json, "auth-hmac-md5", obuf); + else + sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf); break; default: - sbuf_push(buf, indent, " Unknown (%hhu)\n", auth->type); + if (json) + json_object_int_add(json, "auth-unknown", auth->type); + else + sbuf_push(buf, indent, " Unknown (%hhu)\n", + auth->type); break; } } @@ -3270,7 +3889,7 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s, rv->offset = stream_get_getp(s); stream_get(rv->value, s, rv->length); - format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_auth(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->isis_auth, (struct isis_item *)rv); return 0; } @@ -3294,17 +3913,36 @@ static struct isis_purge_originator *copy_tlv_purge_originator( } static void format_tlv_purge_originator(struct isis_purge_originator *poi, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { if (!poi) return; - 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))); + 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))); + } + } 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))); + } } } @@ -3417,12 +4055,12 @@ static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, static void format_item(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, int indent) { const struct tlv_ops *ops = tlv_table[context][type]; if (ops && ops->format_item) { - ops->format_item(mtid, i, buf, indent); + ops->format_item(mtid, i, buf, json, indent); return; } @@ -3431,12 +4069,13 @@ static void format_item(uint16_t mtid, enum isis_tlv_context context, static void format_items_(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_item *i; for (i = items->head; i; i = i->next) - format_item(mtid, context, type, i, buf, indent); + format_item(mtid, context, type, i, buf, json, indent); } static void free_item(enum isis_tlv_context tlv_context, @@ -3765,12 +4404,12 @@ static void free_mt_items(enum isis_tlv_context context, static void format_mt_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_mt_item_list *m, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { struct isis_item_list *n; RB_FOREACH (n, isis_mt_item_list, m) { - format_items_(n->mtid, context, type, n, buf, indent); + format_items_(n->mtid, context, type, n, buf, json, indent); } } @@ -3917,87 +4556,100 @@ struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs) return rv; } -static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent) +static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, struct json_object *json, int indent) { - format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent); + format_tlv_protocols_supported(&tlvs->protocols_supported, buf, json, + indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf, - indent); + json, indent); - format_tlv_purge_originator(tlvs->purge_originator, buf, indent); + format_tlv_purge_originator(tlvs->purge_originator, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, - &tlvs->area_addresses, buf, indent); + &tlvs->area_addresses, buf, json, indent); if (tlvs->mt_router_info_empty) { - sbuf_push(buf, indent, "MT Router Info: None\n"); + if (json) + json_object_string_add(json, "mt-router-info", "none"); + else + sbuf_push(buf, indent, "MT Router Info: None\n"); } else { format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, - &tlvs->mt_router_info, buf, indent); + &tlvs->mt_router_info, buf, json, indent); } format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH, - &tlvs->oldstyle_reach, buf, indent); + &tlvs->oldstyle_reach, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS, - &tlvs->lan_neighbor, buf, indent); + &tlvs->lan_neighbor, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries, - buf, indent); - - format_tlv_dynamic_hostname(tlvs->hostname, buf, indent); - format_tlv_te_router_id(tlvs->te_router_id, buf, indent); - format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, buf, indent); - format_tlv_router_cap(tlvs->router_cap, buf, indent); + buf, json, indent); + + format_tlv_dynamic_hostname(tlvs->hostname, buf, json, indent); + format_tlv_te_router_id(tlvs->te_router_id, buf, json, indent); + format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, buf, json, + indent); + if (json) + format_tlv_router_cap_json(tlvs->router_cap, json); + else + format_tlv_router_cap(tlvs->router_cap, buf, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH, - &tlvs->extended_reach, buf, indent); + &tlvs->extended_reach, buf, json, indent); format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach, - buf, indent); + buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH, - &tlvs->oldstyle_ip_reach, buf, indent); + &tlvs->oldstyle_ip_reach, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT, - &tlvs->oldstyle_ip_reach_ext, buf, indent); + &tlvs->oldstyle_ip_reach_ext, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, - &tlvs->ipv4_address, buf, indent); + &tlvs->ipv4_address, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, - &tlvs->ipv6_address, buf, indent); + &tlvs->ipv6_address, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_GLOBAL_IPV6_ADDRESS, - &tlvs->global_ipv6_address, buf, indent); + &tlvs->global_ipv6_address, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH, - &tlvs->extended_ip_reach, buf, indent); + &tlvs->extended_ip_reach, buf, json, indent); format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH, - &tlvs->mt_ip_reach, buf, indent); + &tlvs->mt_ip_reach, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach, - buf, indent); + buf, json, indent); format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, - &tlvs->mt_ipv6_reach, buf, indent); + &tlvs->mt_ipv6_reach, buf, json, indent); - format_tlv_threeway_adj(tlvs->threeway_adj, buf, indent); + format_tlv_threeway_adj(tlvs->threeway_adj, buf, json, indent); - format_tlv_spine_leaf(tlvs->spine_leaf, buf, indent); + format_tlv_spine_leaf(tlvs->spine_leaf, buf, json, indent); } -const char *isis_format_tlvs(struct isis_tlvs *tlvs) +const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json) { - static struct sbuf buf; + if (json) { + format_tlvs(tlvs, NULL, json, 0); + return NULL; + } else { + static struct sbuf buf; - if (!sbuf_buf(&buf)) - sbuf_init(&buf, NULL, 0); + if (!sbuf_buf(&buf)) + sbuf_init(&buf, NULL, 0); - sbuf_reset(&buf); - format_tlvs(tlvs, &buf, 0); - return sbuf_buf(&buf); + sbuf_reset(&buf); + format_tlvs(tlvs, &buf, NULL, 0); + return sbuf_buf(&buf); + } } void isis_free_tlvs(struct isis_tlvs *tlvs) diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 0c6ed11cb6..364e38aba1 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -549,7 +549,7 @@ void isis_free_tlvs(struct isis_tlvs *tlvs); struct isis_tlvs *isis_alloc_tlvs(void); int isis_unpack_tlvs(size_t avail_len, struct stream *stream, struct isis_tlvs **dest, const char **error_log); -const char *isis_format_tlvs(struct isis_tlvs *tlvs); +const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json); struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs); struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); diff --git a/isisd/isisd.c b/isisd/isisd.c index 3fa2b7cc20..369b83396a 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -109,12 +109,19 @@ DEFINE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area)); int isis_area_get(struct vty *, const char *); int area_net_title(struct vty *, const char *); int area_clear_net_title(struct vty *, const char *); -int show_isis_interface_common(struct vty *, const char *ifname, char, - const char *vrf_name, bool all_vrf); -int show_isis_neighbor_common(struct vty *, const char *id, char, - const char *vrf_name, bool all_vrf); -int clear_isis_neighbor_common(struct vty *, const char *id, const char *vrf_name, +int show_isis_interface_common(struct vty *, struct json_object *json, + const char *ifname, char, const char *vrf_name, bool all_vrf); +int show_isis_interface_common_vty(struct vty *, const char *ifname, char, + const char *vrf_name, bool all_vrf); +int show_isis_interface_common_json(struct json_object *json, + const char *ifname, char, + const char *vrf_name, bool all_vrf); +int show_isis_neighbor_common(struct vty *, struct json_object *json, + const char *id, char, const char *vrf_name, + bool all_vrf); +int clear_isis_neighbor_common(struct vty *, const char *id, + const char *vrf_name, bool all_vrf); /* Link ISIS instance to VRF. */ void isis_vrf_link(struct isis *isis, struct vrf *vrf) @@ -202,7 +209,7 @@ struct isis *isis_new(const char *vrf_name) /* * Default values */ - isis->max_area_addrs = 3; + isis->max_area_addrs = ISIS_DEFAULT_MAX_AREA_ADDRESSES; isis->process_id = getpid(); isis->router_id = 0; isis->area_list = list_new(); @@ -933,10 +940,125 @@ int area_clear_net_title(struct vty *vty, const char *net_title) /* * 'show isis interface' command */ - -int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, +int show_isis_interface_common(struct vty *vty, struct json_object *json, + const char *ifname, char detail, const char *vrf_name, bool all_vrf) { + if (json) { + return show_isis_interface_common_json(json, ifname, detail, + vrf_name, all_vrf); + } else { + return show_isis_interface_common_vty(vty, ifname, detail, + vrf_name, all_vrf); + } +} + +int show_isis_interface_common_json(struct json_object *json, + const char *ifname, char detail, + const char *vrf_name, bool all_vrf) +{ + struct listnode *anode, *cnode, *inode; + struct isis_area *area; + struct isis_circuit *circuit; + struct isis *isis; + struct json_object *areas_json, *area_json; + struct json_object *circuits_json, *circuit_json; + if (!im) { + // IS-IS Routing Process not enabled + json_object_string_add(json, "is-is-routing-process-enabled", + "no"); + return CMD_SUCCESS; + } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", + areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, + anode, area)) { + area_json = json_object_new_object(); + json_object_string_add( + area_json, "area", + area->area_tag ? area->area_tag + : "null"); + circuits_json = json_object_new_array(); + json_object_object_add(area_json, + "circuits", + circuits_json); + for (ALL_LIST_ELEMENTS_RO( + area->circuit_list, cnode, + circuit)) { + circuit_json = + json_object_new_object(); + json_object_int_add( + circuit_json, "circuit", + circuit->circuit_id); + if (!ifname) + isis_circuit_print_json( + circuit, + circuit_json, + detail); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_print_json( + circuit, + circuit_json, + detail); + json_object_array_add( + circuits_json, + circuit_json); + } + json_object_array_add(areas_json, + area_json); + } + } + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { + area_json = json_object_new_object(); + json_object_string_add(area_json, "area", + area->area_tag + ? area->area_tag + : "null"); + + circuits_json = json_object_new_array(); + json_object_object_add(area_json, "circuits", + circuits_json); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, + cnode, circuit)) { + circuit_json = json_object_new_object(); + json_object_int_add( + circuit_json, "circuit", + circuit->circuit_id); + if (!ifname) + isis_circuit_print_json( + circuit, circuit_json, + detail); + else if ( + strcmp(circuit->interface->name, + ifname) == 0) + isis_circuit_print_json( + circuit, circuit_json, + detail); + json_object_array_add(circuits_json, + circuit_json); + } + json_object_array_add(areas_json, area_json); + } + } + } + return CMD_SUCCESS; +} + +int show_isis_interface_common_vty(struct vty *vty, const char *ifname, + char detail, const char *vrf_name, + bool all_vrf) +{ struct listnode *anode, *cnode, *inode; struct isis_area *area; struct isis_circuit *circuit; @@ -990,8 +1112,7 @@ int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, circuit, vty, detail); else if ( strcmp(circuit->interface->name, - ifname) - == 0) + ifname) == 0) isis_circuit_print_vty( circuit, vty, detail); } @@ -1003,63 +1124,90 @@ int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, DEFUN(show_isis_interface, show_isis_interface_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] interface", + "show " PROTO_NAME " [vrf <NAME|all>] interface [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" + "json output\n" "IS-IS interface\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, - vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + res = show_isis_interface_common(vty, json, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_interface_detail, show_isis_interface_detail_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] interface detail", + "show " PROTO_NAME " [vrf <NAME|all>] interface detail [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" "IS-IS interface\n" - "show detailed information\n") + "show detailed information\n" + "json output\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + res = show_isis_interface_common(vty, json, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_interface_arg, show_isis_interface_arg_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] interface WORD", + "show " PROTO_NAME " [vrf <NAME|all>] interface WORD [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" "IS-IS interface\n" - "IS-IS interface name\n") + "IS-IS interface name\n" + "json output\n") { + int res = CMD_SUCCESS; int idx_word = 0; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); char *ifname = argv_find(argv, argc, "WORD", &idx_word) ? argv[idx_word]->arg : NULL; - return show_isis_interface_common(vty, ifname, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + res = show_isis_interface_common( + vty, json, ifname, ISIS_UI_LEVEL_DETAIL, vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } static int id_to_sysid(struct isis *isis, const char *id, uint8_t *sysid) @@ -1079,8 +1227,65 @@ static int id_to_sysid(struct isis *isis, const char *id, uint8_t *sysid) return 0; } -static void isis_neighbor_common(struct vty *vty, const char *id, char detail, - struct isis *isis, uint8_t *sysid) +static void isis_neighbor_common_json(struct json_object *json, const char *id, + char detail, struct isis *isis, + uint8_t *sysid) +{ + struct listnode *anode, *cnode, *node; + struct isis_area *area; + struct isis_circuit *circuit; + struct list *adjdb; + struct isis_adjacency *adj; + struct json_object *areas_json, *area_json; + struct json_object *circuits_json, *circuit_json; + int i; + + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + area_json = json_object_new_object(); + json_object_string_add(area_json, "area", + area->area_tag ? area->area_tag + : "null"); + circuits_json = json_object_new_array(); + json_object_object_add(area_json, "circuits", circuits_json); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) { + circuit_json = json_object_new_object(); + json_object_int_add(circuit_json, "circuit", + circuit->circuit_id); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + for (i = 0; i < 2; i++) { + adjdb = circuit->u.bc.adjdb[i]; + if (adjdb && adjdb->count) { + for (ALL_LIST_ELEMENTS_RO( + adjdb, node, adj)) + if (!id || + !memcmp(adj->sysid, + sysid, + ISIS_SYS_ID_LEN)) + isis_adj_print_json( + adj, + circuit_json, + detail); + } + } + } else if (circuit->circ_type == CIRCUIT_T_P2P && + circuit->u.p2p.neighbor) { + adj = circuit->u.p2p.neighbor; + if (!id || + !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) + isis_adj_print_json(adj, circuit_json, + detail); + } + json_object_array_add(circuits_json, circuit_json); + } + json_object_array_add(areas_json, area_json); + } +} + +static void isis_neighbor_common_vty(struct vty *vty, const char *id, + char detail, struct isis *isis, + uint8_t *sysid) { struct listnode *anode, *cnode, *node; struct isis_area *area; @@ -1103,9 +1308,8 @@ static void isis_neighbor_common(struct vty *vty, const char *id, char detail, if (adjdb && adjdb->count) { for (ALL_LIST_ELEMENTS_RO( adjdb, node, adj)) - if (!id - || !memcmp( - adj->sysid, + if (!id || + !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) isis_adj_print_vty( @@ -1114,24 +1318,35 @@ static void isis_neighbor_common(struct vty *vty, const char *id, char detail, detail); } } - } else if (circuit->circ_type == CIRCUIT_T_P2P - && circuit->u.p2p.neighbor) { + } else if (circuit->circ_type == CIRCUIT_T_P2P && + circuit->u.p2p.neighbor) { adj = circuit->u.p2p.neighbor; - if (!id - || !memcmp(adj->sysid, sysid, - ISIS_SYS_ID_LEN)) + if (!id || + !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) isis_adj_print_vty(adj, vty, detail); } } } +} +static void isis_neighbor_common(struct vty *vty, struct json_object *json, + const char *id, char detail, struct isis *isis, + uint8_t *sysid) +{ + if (json) { + isis_neighbor_common_json(json, id, detail,isis,sysid); + } else { + isis_neighbor_common_vty(vty, id, detail,isis,sysid); + } } + /* * 'show isis neighbor' command */ -int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, - const char *vrf_name, bool all_vrf) +int show_isis_neighbor_common(struct vty *vty, struct json_object *json, + const char *id, char detail, const char *vrf_name, + bool all_vrf) { struct listnode *node; uint8_t sysid[ISIS_SYS_ID_LEN]; @@ -1150,8 +1365,8 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, id); return CMD_SUCCESS; } - isis_neighbor_common(vty, id, detail, isis, - sysid); + isis_neighbor_common(vty, json, id, detail, + isis, sysid); } return CMD_SUCCESS; } @@ -1161,7 +1376,8 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, vty_out(vty, "Invalid system id %s\n", id); return CMD_SUCCESS; } - isis_neighbor_common(vty, id, detail, isis, sysid); + isis_neighbor_common(vty, json, id, detail, isis, + sysid); } } @@ -1254,64 +1470,91 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_ DEFUN(show_isis_neighbor, show_isis_neighbor_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] neighbor", + "show " PROTO_NAME " [vrf <NAME|all>] neighbor [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All vrfs\n" - "IS-IS neighbor adjacencies\n") + "IS-IS neighbor adjacencies\n" + "json output\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, - vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + res = show_isis_neighbor_common(vty, json, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_neighbor_detail, show_isis_neighbor_detail_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] neighbor detail", + "show " PROTO_NAME " [vrf <NAME|all>] neighbor detail [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "all vrfs\n" "IS-IS neighbor adjacencies\n" - "show detailed information\n") + "show detailed information\n" + "json output\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + res = show_isis_neighbor_common(vty, json, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_neighbor_arg, show_isis_neighbor_arg_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] neighbor WORD", + "show " PROTO_NAME " [vrf <NAME|all>] neighbor WORD [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All vrfs\n" "IS-IS neighbor adjacencies\n" - "System id\n") + "System id\n" + "json output\n") { + int res = CMD_SUCCESS; int idx_word = 0; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); char *id = argv_find(argv, argc, "WORD", &idx_word) ? argv[idx_word]->arg : NULL; - return show_isis_neighbor_common(vty, id, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + res = show_isis_neighbor_common(vty, json, id, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(clear_isis_neighbor, @@ -2056,7 +2299,152 @@ DEFUN(show_isis_spf_ietf, show_isis_spf_ietf_cmd, return CMD_SUCCESS; } -static void common_isis_summary(struct vty *vty, struct isis *isis) + +static const char *pdu_counter_index_to_name_json(enum pdu_counter_index index) +{ + switch (index) { + case L1_LAN_HELLO_INDEX: + return "l1-iih"; + case L2_LAN_HELLO_INDEX: + return "l2-iih"; + case P2P_HELLO_INDEX: + return "p2p-iih"; + case L1_LINK_STATE_INDEX: + return "l1-lsp"; + case L2_LINK_STATE_INDEX: + return "l2-lsp"; + case FS_LINK_STATE_INDEX: + return "fs-lsp"; + case L1_COMPLETE_SEQ_NUM_INDEX: + return "l1-csnp"; + case L2_COMPLETE_SEQ_NUM_INDEX: + return "l2-csnp"; + case L1_PARTIAL_SEQ_NUM_INDEX: + return "l1-psnp"; + case L2_PARTIAL_SEQ_NUM_INDEX: + return "l2-psnp"; + default: + return "???????"; + } +} + +static void common_isis_summary_json(struct json_object *json, + struct isis *isis) +{ + int level; + json_object *areas_json, *area_json, *tx_pdu_json, *rx_pdu_json, + *levels_json, *level_json; + struct listnode *node, *node2; + struct isis_area *area; + 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)); + + cur = time(NULL); + cur -= isis->uptime; + frrtime_to_interval(cur, uptime, sizeof(uptime)); + json_object_string_add(json, "up-time", uptime); + if (isis->area_list) + json_object_int_add(json, "number-areas", + isis->area_list->count); + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + area_json = json_object_new_object(); + json_object_string_add(area_json, "area", + area->area_tag ? area->area_tag + : "null"); + + + if (fabricd) { + uint8_t tier = fabricd_tier(area); + snprintfrr(stier, sizeof(stier), "%s", &tier); + json_object_string_add(area_json, "tier", + tier == ISIS_TIER_UNDEFINED + ? "undefined" + : stier); + } + + if (listcount(area->area_addrs) > 0) { + struct area_addr *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)); + } + } + + tx_pdu_json = json_object_new_object(); + json_object_object_add(area_json, "tx-pdu-type", tx_pdu_json); + for (int i = 0; i < PDU_COUNTER_SIZE; i++) { + if (!area->pdu_tx_counters[i]) + continue; + json_object_int_add(tx_pdu_json, + pdu_counter_index_to_name_json(i), + area->pdu_tx_counters[i]); + } + json_object_int_add(tx_pdu_json, "lsp-rxmt", + area->lsp_rxmt_count); + + rx_pdu_json = json_object_new_object(); + json_object_object_add(area_json, "rx-pdu-type", rx_pdu_json); + for (int i = 0; i < PDU_COUNTER_SIZE; i++) { + if (!area->pdu_rx_counters[i]) + continue; + json_object_int_add(rx_pdu_json, + pdu_counter_index_to_name_json(i), + area->pdu_rx_counters[i]); + } + + levels_json = json_object_new_array(); + json_object_object_add(area_json, "levels", levels_json); + for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + if ((area->is_type & level) == 0) + continue; + level_json = json_object_new_object(); + json_object_int_add(level_json, "id", level); + json_object_int_add(level_json, "lsp0-regenerated", + area->lsp_gen_count[level - 1]); + json_object_int_add(level_json, "lsp-purged", + area->lsp_purge_count[level - 1]); + if (area->spf_timer[level - 1]) + json_object_string_add(level_json, "spf", + "pending"); + else + json_object_string_add(level_json, "spf", + "no pending"); + json_object_int_add(level_json, "minimum-interval", + area->min_spf_interval[level - 1]); + if (area->spf_delay_ietf[level - 1]) + json_object_string_add( + level_json, "ietf-spf-delay-activated", + "not used"); + if (area->ip_circuits) { + isis_spf_print_json( + area->spftree[SPFTREE_IPV4][level - 1], + level_json); + } + if (area->ipv6_circuits) { + isis_spf_print_json( + area->spftree[SPFTREE_IPV6][level - 1], + level_json); + } + json_object_array_add(levels_json, level_json); + } + json_object_array_add(areas_json, area_json); + } +} + +static void common_isis_summary_vty(struct vty *vty, struct isis *isis) { struct listnode *node, *node2; struct isis_area *area; @@ -2156,10 +2544,21 @@ static void common_isis_summary(struct vty *vty, struct isis *isis) } } +static void common_isis_summary(struct vty *vty, struct json_object *json, + struct isis *isis) +{ + if (json) { + common_isis_summary_json(json, isis); + } else { + common_isis_summary_vty(vty, isis); + } +} + DEFUN(show_isis_summary, show_isis_summary_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] summary", + "show " PROTO_NAME " [vrf <NAME|all>] summary [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" + "json output\n" "summary\n") { struct listnode *node; @@ -2167,25 +2566,30 @@ DEFUN(show_isis_summary, show_isis_summary_cmd, struct isis *isis; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) if (!im) { vty_out(vty, PROTO_NAME " is not running\n"); return CMD_SUCCESS; } + if (uj) + json = json_object_new_object(); if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - common_isis_summary(vty, isis); + common_isis_summary(vty, json, isis); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis != NULL) - common_isis_summary(vty, isis); + common_isis_summary(vty, json, isis); } - vty_out(vty, "\n"); + if (uj) + vty_json(vty, json); return CMD_SUCCESS; } @@ -2250,9 +2654,40 @@ struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str, return lsp; } -void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, - int level, struct lspdb_head *lspdb, - const char *sysid_str, int ui_level) +void show_isis_database_lspdb_json(struct json_object *json, + struct isis_area *area, int level, + struct lspdb_head *lspdb, + const char *sysid_str, int ui_level) +{ + struct isis_lsp *lsp; + int lsp_count; + + if (lspdb_count(lspdb) > 0) { + lsp = lsp_for_sysid(lspdb, sysid_str, area->isis); + + if (lsp != NULL || sysid_str == NULL) { + json_object_int_add(json, "id", level + 1); + } + + if (lsp) { + if (ui_level == ISIS_UI_LEVEL_DETAIL) + lsp_print_detail(lsp, NULL, json, + area->dynhostname, area->isis); + else + lsp_print_json(lsp, json, area->dynhostname, + area->isis); + } else if (sysid_str == NULL) { + lsp_count = + lsp_print_all(NULL, json, lspdb, ui_level, + area->dynhostname, area->isis); + + json_object_int_add(json, "count", lsp_count); + } + } +} +void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *sysid_str, int ui_level) { struct isis_lsp *lsp; int lsp_count; @@ -2271,14 +2706,14 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, if (lsp) { if (ui_level == ISIS_UI_LEVEL_DETAIL) - lsp_print_detail(lsp, vty, area->dynhostname, - area->isis); + lsp_print_detail(lsp, vty, NULL, + area->dynhostname, area->isis); else - lsp_print(lsp, vty, area->dynhostname, - area->isis); + lsp_print_vty(lsp, vty, area->dynhostname, + area->isis); } else if (sysid_str == NULL) { lsp_count = - lsp_print_all(vty, lspdb, ui_level, + lsp_print_all(vty, NULL, lspdb, ui_level, area->dynhostname, area->isis); vty_out(vty, " %u LSPs\n\n", lsp_count); @@ -2286,7 +2721,43 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, } } -static void show_isis_database_common(struct vty *vty, const char *sysid_str, +static void show_isis_database_json(struct json_object *json, const char *sysid_str, + int ui_level, struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + int level; + struct json_object *tag_area_json,*area_json, *lsp_json, *area_arr_json, *arr_json; + uint8_t area_cnt = 0; + + if (isis->area_list->count == 0) + return; + + area_arr_json = json_object_new_array(); + json_object_object_add(json, "areas", area_arr_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + area_json = json_object_new_object(); + tag_area_json = json_object_new_object(); + json_object_string_add(tag_area_json, "name", + area->area_tag ? area->area_tag + : "null"); + + arr_json = json_object_new_array(); + json_object_object_add(area_json,"area",tag_area_json); + json_object_object_add(area_json,"levels",arr_json); + for (level = 0; level < ISIS_LEVELS; level++) { + lsp_json = json_object_new_object(); + show_isis_database_lspdb_json(lsp_json, area, level, + &area->lspdb[level], + sysid_str, ui_level); + json_object_array_add(arr_json, lsp_json); + } + json_object_array_add(area_arr_json, area_json); + area_cnt++; + } +} + +static void show_isis_database_vty(struct vty *vty, const char *sysid_str, int ui_level, struct isis *isis) { struct listnode *node; @@ -2301,11 +2772,22 @@ static void show_isis_database_common(struct vty *vty, const char *sysid_str, area->area_tag ? area->area_tag : "null"); for (level = 0; level < ISIS_LEVELS; level++) - show_isis_database_lspdb(vty, area, level, + show_isis_database_lspdb_vty(vty, area, level, &area->lspdb[level], sysid_str, ui_level); } } + +static void show_isis_database_common(struct vty *vty, struct json_object *json, const char *sysid_str, + int ui_level, struct isis *isis) +{ + if (json) { + show_isis_database_json(json, sysid_str, ui_level, isis); + } else { + show_isis_database_vty(vty, sysid_str, ui_level, isis); + } +} + /* * This function supports following display options: * [ show isis database [detail] ] @@ -2322,7 +2804,7 @@ static void show_isis_database_common(struct vty *vty, const char *sysid_str, * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ] * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ] */ -static int show_isis_database(struct vty *vty, const char *sysid_str, +static int show_isis_database(struct vty *vty, struct json_object *json, const char *sysid_str, int ui_level, const char *vrf_name, bool all_vrf) { struct listnode *node; @@ -2331,28 +2813,30 @@ static int show_isis_database(struct vty *vty, const char *sysid_str, if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - show_isis_database_common(vty, sysid_str, + show_isis_database_common(vty, json, sysid_str, ui_level, isis); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis) - show_isis_database_common(vty, sysid_str, ui_level, - isis); + show_isis_database_common(vty, json, sysid_str, + ui_level, isis); } return CMD_SUCCESS; } DEFUN(show_database, show_database_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] database [detail] [WORD]", + "show " PROTO_NAME " [vrf <NAME|all>] database [detail] [WORD] [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" "Link state database\n" "Detailed information\n" - "LSP ID\n") + "LSP ID\n" + "json output\n") { + int res = CMD_SUCCESS; int idx = 0; int idx_vrf = 0; const char *vrf_name = VRF_DEFAULT_NAME; @@ -2361,8 +2845,17 @@ DEFUN(show_database, show_database_cmd, ? ISIS_UI_LEVEL_DETAIL : ISIS_UI_LEVEL_BRIEF; char *id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; + bool uj = use_json(argc, argv); + json_object *json = NULL; + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_database(vty, id, uilevel, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + + res = show_isis_database(vty, json, id, uilevel, vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } #ifdef FABRICD diff --git a/isisd/isisd.h b/isisd/isisd.h index 7f8474a5f2..c313fd9ef7 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -89,6 +89,8 @@ struct isis_master { }; #define F_ISIS_UNIT_TEST 0x01 +#define ISIS_DEFAULT_MAX_AREA_ADDRESSES 3 + struct isis { vrf_id_t vrf_id; char *name; @@ -305,9 +307,13 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); -void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, - int level, struct lspdb_head *lspdb, - const char *argv, int ui_level); +void show_isis_database_lspdb_json(struct json_object *json, + struct isis_area *area, int level, + struct lspdb_head *lspdb, const char *argv, + int ui_level); +void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *argv, int ui_level); /* YANG paths */ #define ISIS_INSTANCE "/frr-isisd:isis/instance" diff --git a/lib/json.c b/lib/json.c index 854a3d59d1..d85a21215c 100644 --- a/lib/json.c +++ b/lib/json.c @@ -74,6 +74,19 @@ void json_object_string_addv(struct json_object *obj, const char *key, json_object_object_add(obj, key, json_object_new_stringv(fmt, args)); } +void json_object_object_addv(struct json_object *parent, + struct json_object *child, const char *keyfmt, + va_list args) +{ + char *text, buf[256]; + + text = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), keyfmt, args); + json_object_object_add(parent, text, child); + + if (text != buf) + XFREE(MTYPE_TMP, text); +} + void json_object_int_add(struct json_object *obj, const char *key, int64_t i) { json_object_object_add(obj, key, json_object_new_int64(i)); diff --git a/lib/json.h b/lib/json.h index fcaa84c816..78c3836515 100644 --- a/lib/json.h +++ b/lib/json.h @@ -116,6 +116,28 @@ static inline struct json_object *json_object_new_stringf(const char *fmt, ...) return ret; } +/* NOTE: argument order differs! (due to varargs) + * json_object_object_add(parent, key, child) + * json_object_object_addv(parent, child, key, va) + * json_object_object_addf(parent, child, key, ...) + * (would be weird to have the child inbetween the format string and args) + */ +PRINTFRR(3, 0) +extern void json_object_object_addv(struct json_object *parent, + struct json_object *child, + const char *keyfmt, va_list args); +PRINTFRR(3, 4) +static inline void json_object_object_addf(struct json_object *parent, + struct json_object *child, + const char *keyfmt, ...) +{ + va_list args; + + va_start(args, keyfmt); + json_object_object_addv(parent, child, keyfmt, args); + va_end(args); +} + #define JSON_STR "JavaScript Object Notation\n" /* NOTE: json-c lib has following commit 316da85 which diff --git a/lib/libfrr.c b/lib/libfrr.c index 10b3aad89e..042c9d3704 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -333,6 +333,8 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) umask(0027); + log_args_init(daemon->early_logging); + opt_extend(&os_always); if (!(di->flags & FRR_NO_SPLIT_CONFIG)) opt_extend(&os_cfg); @@ -431,6 +433,8 @@ static int frr_opt(int opt) static int vty_port_set = 0; static int vty_addr_set = 0; struct option_chain *oc; + struct log_arg *log_arg; + size_t arg_len; char *err; switch (opt) { @@ -613,7 +617,10 @@ static int frr_opt(int opt) di->privs->group = optarg; break; case OPTION_LOG: - di->early_logging = optarg; + arg_len = strlen(optarg) + 1; + log_arg = XCALLOC(MTYPE_TMP, sizeof(*log_arg) + arg_len); + memcpy(log_arg->target, optarg, arg_len); + log_args_add_tail(di->early_logging, log_arg); break; case OPTION_LOGLEVEL: di->early_loglevel = optarg; @@ -706,10 +713,12 @@ static struct thread_master *master; struct thread_master *frr_init(void) { struct option_chain *oc; + struct log_arg *log_arg; struct frrmod_runtime *module; struct zprivs_ids_t ids; char p_instance[16] = "", p_pathspace[256] = ""; const char *dir; + dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); @@ -739,7 +748,11 @@ struct thread_master *frr_init(void) zlog_init(di->progname, di->logname, di->instance, ids.uid_normal, ids.gid_normal); - command_setup_early_logging(di->early_logging, di->early_loglevel); + while ((log_arg = log_args_pop(di->early_logging))) { + command_setup_early_logging(log_arg->target, + di->early_loglevel); + XFREE(MTYPE_TMP, log_arg); + } if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len, frr_zclientpath)) { diff --git a/lib/libfrr.h b/lib/libfrr.h index 65c1df9675..69054e4264 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -21,6 +21,7 @@ #ifndef _ZEBRA_FRR_H #define _ZEBRA_FRR_H +#include "typesafe.h" #include "sigevent.h" #include "privs.h" #include "thread.h" @@ -52,6 +53,14 @@ extern "C" { */ #define FRR_DETACH_LATER (1 << 6) +PREDECL_DLIST(log_args); +struct log_arg { + struct log_args_item itm; + + char target[0]; +}; +DECLARE_DLIST(log_args, struct log_arg, itm); + enum frr_cli_mode { FRR_CLI_CLASSIC = 0, FRR_CLI_TRANSACTIONAL, @@ -88,7 +97,7 @@ struct frr_daemon_info { const char *pathspace; bool zpathspace; - const char *early_logging; + struct log_args_head early_logging[1]; const char *early_loglevel; const char *proghelp; diff --git a/lib/log_vty.c b/lib/log_vty.c index 682c9ea372..ef33a39d4a 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -427,6 +427,22 @@ void command_setup_early_logging(const char *dest, const char *level) set_log_file(&zt_file_cmdline, NULL, sep, nlevel); return; } + if (strcmp(type, "monitor") == 0 && sep) { + struct zlog_live_cfg cfg = {}; + unsigned long fd; + char *endp; + + sep++; + fd = strtoul(sep, &endp, 10); + if (!*sep || *endp) { + fprintf(stderr, "invalid monitor fd \"%s\"\n", sep); + exit(1); + } + + zlog_live_open_fd(&cfg, nlevel, fd); + zlog_live_disown(&cfg); + return; + } fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest); exit(1); diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp index 34bb1e4986..e2a6290035 100644 --- a/lib/northbound_grpc.cpp +++ b/lib/northbound_grpc.cpp @@ -1,7 +1,7 @@ // +// Copyright (c) 2021-2022, LabN Consulting, L.L.C // Copyright (C) 2019 NetDEF, Inc. // Renato Westphal -// Copyright (c) 2021, LabN Consulting, L.L.C // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free @@ -50,6 +50,8 @@ static struct thread_master *main_master; static struct frr_pthread *fpt; +static bool grpc_running; + #define grpc_debug(...) \ do { \ if (nb_dbg_client_grpc) \ @@ -96,11 +98,11 @@ class Candidates { char errmsg[BUFSIZ] = {0}; - _cdb.erase(c->id); nb_config_free(c->config); if (c->transaction) nb_candidate_commit_abort(c->transaction, errmsg, sizeof(errmsg)); + _cdb.erase(c->id); } struct candidate *get_candidate(uint32_t id) @@ -116,9 +118,11 @@ class Candidates class RpcStateBase { public: + virtual ~RpcStateBase() = default; virtual CallState doCallback() = 0; virtual void do_request(::frr::Northbound::AsyncService *service, - ::grpc::ServerCompletionQueue *cq) = 0; + ::grpc::ServerCompletionQueue *cq, + bool no_copy) = 0; }; /* @@ -188,17 +192,22 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase } void do_request(::frr::Northbound::AsyncService *service, - ::grpc::ServerCompletionQueue *cq) override + ::grpc::ServerCompletionQueue *cq, + bool no_copy) override { grpc_debug("%s, posting a request for: %s", __func__, name); if (requestf) { NewRpcState<Q, S> *copy = - new NewRpcState(cdb, requestf, callback, name); + no_copy ? this + : new NewRpcState(cdb, requestf, + callback, name); (service->*requestf)(©->ctx, ©->request, ©->responder, cq, cq, copy); } else { NewRpcState<Q, S> *copy = - new NewRpcState(cdb, requestsf, callback, name); + no_copy ? this + : new NewRpcState(cdb, requestsf, + callback, name); (service->*requestsf)(©->ctx, ©->request, ©->async_responder, cq, cq, copy); @@ -227,7 +236,6 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase pthread_mutex_unlock(&_tag->cmux); return; } - NewRpcState<Q, S> *orig; const char *name; grpc::ServerContext ctx; @@ -238,12 +246,12 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase Candidates *cdb; void (*callback)(NewRpcState<Q, S> *); - reqfunc_t requestf; - reqsfunc_t requestsf; + reqfunc_t requestf = NULL; + reqsfunc_t requestsf = NULL; pthread_mutex_t cmux = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; - void *context; + void *context = 0; CallState state = CREATE; }; @@ -268,10 +276,10 @@ static LYD_FORMAT encoding2lyd_format(enum frr::Encoding encoding) } static int yang_dnode_edit(struct lyd_node *dnode, const std::string &path, - const std::string &value) + const char *value) { - LY_ERR err = lyd_new_path(dnode, ly_native_ctx, path.c_str(), - value.c_str(), LYD_NEW_PATH_UPDATE, &dnode); + LY_ERR err = lyd_new_path(dnode, ly_native_ctx, path.c_str(), value, + LYD_NEW_PATH_UPDATE, &dnode); if (err != LY_SUCCESS) { flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed: %s", __func__, ly_errmsg(ly_native_ctx)); @@ -698,8 +706,8 @@ void HandleUnaryEditCandidate( auto pvs = tag->request.update(); for (const frr::PathValue &pv : pvs) { - if (yang_dnode_edit(candidate_tmp->dnode, pv.path(), pv.value()) - != 0) { + if (yang_dnode_edit(candidate_tmp->dnode, pv.path(), + pv.value().c_str()) != 0) { nb_config_free(candidate_tmp); tag->responder.Finish( @@ -1226,7 +1234,7 @@ void HandleUnaryExecute( frr::NAME##Response>( \ (cdb), &frr::Northbound::AsyncService::Request##NAME, \ &HandleUnary##NAME, #NAME); \ - _rpcState->do_request(service, s_cq); \ + _rpcState->do_request(&service, cq.get(), true); \ } while (0) #define REQUEST_NEWRPC_STREAMING(NAME, cdb) \ @@ -1235,7 +1243,7 @@ void HandleUnaryExecute( frr::NAME##Response>( \ (cdb), &frr::Northbound::AsyncService::Request##NAME, \ &HandleStreaming##NAME, #NAME); \ - _rpcState->do_request(service, s_cq); \ + _rpcState->do_request(&service, cq.get(), true); \ } while (0) struct grpc_pthread_attr { @@ -1244,8 +1252,8 @@ struct grpc_pthread_attr { }; // Capture these objects so we can try to shut down cleanly -static std::unique_ptr<grpc::Server> s_server; -static grpc::ServerCompletionQueue *s_cq; +static pthread_mutex_t s_server_lock = PTHREAD_MUTEX_INITIALIZER; +static grpc::Server *s_server; static void *grpc_pthread_start(void *arg) { @@ -1255,18 +1263,22 @@ static void *grpc_pthread_start(void *arg) Candidates candidates; grpc::ServerBuilder builder; std::stringstream server_address; - frr::Northbound::AsyncService *service = - new frr::Northbound::AsyncService(); + frr::Northbound::AsyncService service; frr_pthread_set_name(fpt); server_address << "0.0.0.0:" << port; builder.AddListeningPort(server_address.str(), grpc::InsecureServerCredentials()); - builder.RegisterService(service); - auto cq = builder.AddCompletionQueue(); - s_cq = cq.get(); - s_server = builder.BuildAndStart(); + builder.RegisterService(&service); + builder.AddChannelArgument( + GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, 5000); + std::unique_ptr<grpc::ServerCompletionQueue> cq = + builder.AddCompletionQueue(); + std::unique_ptr<grpc::Server> server = builder.BuildAndStart(); + s_server = server.get(); + + grpc_running = true; /* Schedule all RPC handlers */ REQUEST_NEWRPC(GetCapabilities, NULL); @@ -1287,20 +1299,25 @@ static void *grpc_pthread_start(void *arg) server_address.str().c_str()); /* Process inbound RPCs */ - while (true) { - void *tag; - bool ok; - - s_cq->Next(&tag, &ok); - if (!ok) + bool ok; + void *tag; + while (grpc_running) { + if (!cq->Next(&tag, &ok)) { + grpc_debug("%s: CQ empty exiting", __func__); break; + } - grpc_debug("%s: Got next from CompletionQueue, %p %d", __func__, - tag, ok); + grpc_debug("%s: got next from CQ tag: %p ok: %d", __func__, tag, + ok); + + if (!ok || !grpc_running) { + delete static_cast<RpcStateBase *>(tag); + break; + } RpcStateBase *rpc = static_cast<RpcStateBase *>(tag); CallState state = rpc->doCallback(); - grpc_debug("%s: Callback returned RPC State: %s", __func__, + grpc_debug("%s: callback returned RPC State: %s", __func__, call_states[state]); /* @@ -1310,10 +1327,30 @@ static void *grpc_pthread_start(void *arg) * to be called back once more in the FINISH state (from the * user indicating Finish() for cleanup. */ - if (state == FINISH) - rpc->do_request(service, s_cq); + if (state == FINISH && grpc_running) + rpc->do_request(&service, cq.get(), false); + } + + /* This was probably done for us to get here, but let's be safe */ + pthread_mutex_lock(&s_server_lock); + grpc_running = false; + if (s_server) { + grpc_debug("%s: shutdown server and CQ", __func__); + server->Shutdown(); + s_server = NULL; + } + pthread_mutex_unlock(&s_server_lock); + + grpc_debug("%s: shutting down CQ", __func__); + cq->Shutdown(); + + grpc_debug("%s: draining the CQ", __func__); + while (cq->Next(&tag, &ok)) { + grpc_debug("%s: drain tag %p", __func__, tag); + delete static_cast<RpcStateBase *>(tag); } + zlog_info("%s: exiting from grpc pthread", __func__); return NULL; } @@ -1325,6 +1362,8 @@ static int frr_grpc_init(uint port) .stop = NULL, }; + grpc_debug("%s: entered", __func__); + fpt = frr_pthread_new(&attr, "frr-grpc", "frr-grpc"); fpt->data = reinterpret_cast<void *>((intptr_t)port); @@ -1340,24 +1379,27 @@ static int frr_grpc_init(uint port) static int frr_grpc_finish(void) { - // Shutdown the grpc server - if (s_server) { - s_server->Shutdown(); - s_cq->Shutdown(); - - // And drain the queue - void *ignore; - bool ok; + grpc_debug("%s: entered", __func__); - while (s_cq->Next(&ignore, &ok)) - ; - } + if (!fpt) + return 0; - if (fpt) { - pthread_join(fpt->thread, NULL); - frr_pthread_destroy(fpt); + /* + * Shut the server down here in main thread. This will cause the wait on + * the completion queue (cq.Next()) to exit and cleanup everything else. + */ + pthread_mutex_lock(&s_server_lock); + grpc_running = false; + if (s_server) { + grpc_debug("%s: shutdown server", __func__); + s_server->Shutdown(); + s_server = NULL; } + pthread_mutex_unlock(&s_server_lock); + grpc_debug("%s: joining and destroy grpc thread", __func__); + pthread_join(fpt->thread, NULL); + frr_pthread_destroy(fpt); return 0; } diff --git a/lib/prefix.c b/lib/prefix.c index 90ab48a13b..89c5be8f38 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1071,6 +1071,26 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) return str; } +static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu) +{ + const struct prefix *p = pu.p; + char buf[PREFIX2STR_BUFFER]; + + switch (p->family) { + case AF_INET: + case AF_INET6: + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)); + return bputs(fbuf, buf); + + case AF_ETHERNET: + prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf)); + return bputs(fbuf, buf); + + default: + return bprintfrr(fbuf, "{prefix.af=%dPF}", p->family); + } +} + void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size) { @@ -1458,13 +1478,24 @@ printfrr_ext_autoreg_p("FX", printfrr_pfx); static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { - char cbuf[PREFIX_STRLEN]; + bool host_only = false; + + if (ea->fmt[0] == 'h') { + ea->fmt++; + host_only = true; + } if (!ptr) return bputs(buf, "(null)"); - prefix2str(ptr, cbuf, sizeof(cbuf)); - return bputs(buf, cbuf); + if (host_only) + return prefixhost2str(buf, (struct prefix *)ptr); + else { + char cbuf[PREFIX_STRLEN]; + + prefix2str(ptr, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); + } } printfrr_ext_autoreg_p("PSG4", printfrr_psg); diff --git a/lib/routemap.c b/lib/routemap.c index 7f733c8114..9afe18d10b 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -1799,12 +1799,11 @@ static struct list *route_map_get_index_list(struct route_node **rn, /* * This function returns the route-map index that best matches the prefix. */ -static struct route_map_index *route_map_get_index(struct route_map *map, - const struct prefix *prefix, - void *object, - uint8_t *match_ret) +static struct route_map_index * +route_map_get_index(struct route_map *map, const struct prefix *prefix, + void *object, enum route_map_cmd_result_t *match_ret) { - int ret = 0; + enum route_map_cmd_result_t ret = RMAP_NOMATCH; struct list *candidate_rmap_list = NULL; struct route_node *rn = NULL; struct listnode *ln = NULL, *nn = NULL; @@ -2559,7 +2558,7 @@ route_map_result_t route_map_apply_ext(struct route_map *map, if ((!map->optimization_disabled) && (map->ipv4_prefix_table || map->ipv6_prefix_table)) { index = route_map_get_index(map, prefix, match_object, - (uint8_t *)&match_ret); + &match_ret); if (index) { index->applied++; if (rmap_debug) diff --git a/lib/sockunion.c b/lib/sockunion.c index 006ac142aa..9763b38e28 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -351,21 +351,6 @@ int sockopt_ttl(int family, int sock, int ttl) return 0; } -/* - * This function called setsockopt(.., TCP_CORK,...) - * Which on linux is a no-op since it is enabled by - * default and on BSD it uses TCP_NOPUSH to do - * the same thing( which it was not configured to - * use). This cleanup of the api occurred on 8/1/17 - * I imagine if after more than 1 year of no-one - * complaining, and a major upgrade release we - * can deprecate and remove this function call - */ -int sockopt_cork(int sock, int onoff) -{ - return 0; -} - int sockopt_minttl(int family, int sock, int minttl) { #ifdef IP_MINTTL diff --git a/lib/sockunion.h b/lib/sockunion.h index 9e6719ccf9..8ace3e4781 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -95,7 +95,6 @@ extern int sockunion_bind(int sock, union sockunion *, unsigned short, union sockunion *); extern int sockopt_ttl(int family, int sock, int ttl); extern int sockopt_minttl(int family, int sock, int minttl); -extern int sockopt_cork(int sock, int onoff); extern int sockunion_socket(const union sockunion *su); extern const char *inet_sutop(const union sockunion *su, char *str); extern enum connect_result sockunion_connect(int fd, const union sockunion *su, diff --git a/lib/typerb.c b/lib/typerb.c index e1346df191..fe142ff354 100644 --- a/lib/typerb.c +++ b/lib/typerb.c @@ -468,6 +468,28 @@ struct rb_entry *typed_rb_next(const struct rb_entry *rbe_const) return rbe; } +struct rb_entry *typed_rb_prev(const struct rb_entry *rbe_const) +{ + struct rb_entry *rbe = (struct rb_entry *)rbe_const; + + if (RBE_LEFT(rbe)) { + rbe = RBE_LEFT(rbe); + while (RBE_RIGHT(rbe)) + rbe = RBE_RIGHT(rbe); + } else { + if (RBE_PARENT(rbe) && (rbe == RBE_RIGHT(RBE_PARENT(rbe)))) + rbe = RBE_PARENT(rbe); + else { + while (RBE_PARENT(rbe) + && (rbe == RBE_LEFT(RBE_PARENT(rbe)))) + rbe = RBE_PARENT(rbe); + rbe = RBE_PARENT(rbe); + } + } + + return rbe; +} + struct rb_entry *typed_rb_min(const struct rbt_tree *rbt) { struct rb_entry *rbe = RBH_ROOT(rbt); @@ -481,6 +503,19 @@ struct rb_entry *typed_rb_min(const struct rbt_tree *rbt) return parent; } +struct rb_entry *typed_rb_max(const struct rbt_tree *rbt) +{ + struct rb_entry *rbe = RBH_ROOT(rbt); + struct rb_entry *parent = NULL; + + while (rbe != NULL) { + parent = rbe; + rbe = RBE_RIGHT(rbe); + } + + return parent; +} + bool typed_rb_member(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe) { diff --git a/lib/typerb.h b/lib/typerb.h index 75a1de77b3..8ac1821742 100644 --- a/lib/typerb.h +++ b/lib/typerb.h @@ -62,6 +62,8 @@ const struct typed_rb_entry *typed_rb_find_lt(const struct typed_rb_root *rbt, const struct typed_rb_entry *a, const struct typed_rb_entry *b)); struct typed_rb_entry *typed_rb_min(const struct typed_rb_root *rbt); +struct typed_rb_entry *typed_rb_max(const struct typed_rb_root *rbt); +struct typed_rb_entry *typed_rb_prev(const struct typed_rb_entry *rbe); struct typed_rb_entry *typed_rb_next(const struct typed_rb_entry *rbe); bool typed_rb_member(const struct typed_rb_root *rbt, const struct typed_rb_entry *rbe); @@ -135,12 +137,32 @@ macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ return container_of_null(re, type, field.re); \ } \ TYPESAFE_FIRST_NEXT(prefix, type) \ +macro_pure const type *prefix ## _const_last(const struct prefix##_head *h) \ +{ \ + const struct typed_rb_entry *re; \ + re = typed_rb_max(&h->rr); \ + return container_of_null(re, type, field.re); \ +} \ +macro_pure const type *prefix ## _const_prev(const struct prefix##_head *h, \ + const type *item) \ +{ \ + const struct typed_rb_entry *re; \ + re = typed_rb_prev(&item->field.re); \ + return container_of_null(re, type, field.re); \ +} \ +TYPESAFE_LAST_PREV(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ struct typed_rb_entry *re; \ re = item ? typed_rb_next(&item->field.re) : NULL; \ return container_of_null(re, type, field.re); \ } \ +macro_pure type *prefix ## _prev_safe(struct prefix##_head *h, type *item) \ +{ \ + struct typed_rb_entry *re; \ + re = item ? typed_rb_prev(&item->field.re) : NULL; \ + return container_of_null(re, type, field.re); \ +} \ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->rr.count; \ diff --git a/lib/typesafe.h b/lib/typesafe.h index b284397d98..06fdc52e78 100644 --- a/lib/typesafe.h +++ b/lib/typesafe.h @@ -43,6 +43,22 @@ extern "C" { item; \ item = from, from = prefix##_next_safe(head, from)) +/* reverse direction, only supported by a few containers */ + +#define frr_rev_each(prefix, head, item) \ + for (item = prefix##_last(head); item; \ + item = prefix##_prev(head, item)) +#define frr_rev_each_safe(prefix, head, item) \ + for (typeof(prefix##_prev_safe(head, NULL)) prefix##_safe = \ + prefix##_prev_safe(head, \ + (item = prefix##_last(head))); \ + item; \ + item = prefix##_safe, \ + prefix##_safe = prefix##_prev_safe(head, prefix##_safe)) +#define frr_rev_each_from(prefix, head, item, from) \ + for (item = from, from = prefix##_prev_safe(head, item); \ + item; \ + item = from, from = prefix##_prev_safe(head, from)) /* non-const variants. these wrappers are the same for all the types, so * bundle them together here. @@ -57,6 +73,16 @@ macro_pure type *prefix ## _next(struct prefix##_head *h, type *item) \ return (type *)prefix ## _const_next(h, item); \ } \ /* ... */ +#define TYPESAFE_LAST_PREV(prefix, type) \ +macro_pure type *prefix ## _last(struct prefix##_head *h) \ +{ \ + return (type *)prefix ## _const_last(h); \ +} \ +macro_pure type *prefix ## _prev(struct prefix##_head *h, type *item) \ +{ \ + return (type *)prefix ## _const_prev(h, item); \ +} \ +/* ... */ #define TYPESAFE_FIND(prefix, type) \ macro_inline type *prefix ## _find(struct prefix##_head *h, \ const type *item) \ @@ -398,12 +424,34 @@ macro_pure const type *prefix ## _const_next(const struct prefix##_head *h, \ return container_of(ditem->next, type, field.di); \ } \ TYPESAFE_FIRST_NEXT(prefix, type) \ +macro_pure const type *prefix ## _const_last(const struct prefix##_head *h) \ +{ \ + const struct dlist_item *ditem = h->dh.hitem.prev; \ + if (ditem == &h->dh.hitem) \ + return NULL; \ + return container_of(ditem, type, field.di); \ +} \ +macro_pure const type *prefix ## _const_prev(const struct prefix##_head *h, \ + const type *item) \ +{ \ + const struct dlist_item *ditem = &item->field.di; \ + if (ditem->prev == &h->dh.hitem) \ + return NULL; \ + return container_of(ditem->prev, type, field.di); \ +} \ +TYPESAFE_LAST_PREV(prefix, type) \ macro_pure type *prefix ## _next_safe(struct prefix##_head *h, type *item) \ { \ if (!item) \ return NULL; \ return prefix ## _next(h, item); \ } \ +macro_pure type *prefix ## _prev_safe(struct prefix##_head *h, type *item) \ +{ \ + if (!item) \ + return NULL; \ + return prefix ## _prev(h, item); \ +} \ macro_pure size_t prefix ## _count(const struct prefix##_head *h) \ { \ return h->dh.count; \ diff --git a/lib/zclient.c b/lib/zclient.c index 930adf6a7a..f6c5a8af08 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1924,7 +1924,8 @@ const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, /* * Decode the nexthop-tracking update message */ -bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) +bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, + struct zapi_route *nhr) { uint32_t i; @@ -1932,6 +1933,22 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr) STREAM_GETL(s, nhr->message); STREAM_GETW(s, nhr->safi); + STREAM_GETW(s, match->family); + STREAM_GETC(s, match->prefixlen); + /* + * What we got told to match against + */ + switch (match->family) { + case AF_INET: + STREAM_GET(&match->u.prefix4.s_addr, s, IPV4_MAX_BYTELEN); + break; + case AF_INET6: + STREAM_GET(&match->u.prefix6, s, IPV6_MAX_BYTELEN); + break; + } + /* + * What we matched against + */ STREAM_GETW(s, nhr->prefix.family); STREAM_GETC(s, nhr->prefix.prefixlen); switch (nhr->prefix.family) { diff --git a/lib/zclient.h b/lib/zclient.h index ca62b1afeb..092754f602 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -1111,7 +1111,17 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, const struct nexthop *nh); int zapi_backup_nexthop_from_nexthop(struct zapi_nexthop *znh, const struct nexthop *nh); -extern bool zapi_nexthop_update_decode(struct stream *s, +/* + * match -> is the prefix that the calling daemon asked to be matched + * against. + * nhr->prefix -> is the actual prefix that was matched against in the + * rib itself. + * + * This distinction is made because a LPM can be made if there is a + * covering route. This way the upper level protocol can make a decision + * point about whether or not it wants to use the match or not. + */ +extern bool zapi_nexthop_update_decode(struct stream *s, struct prefix *match, struct zapi_route *nhr); const char *zapi_nexthop2str(const struct zapi_nexthop *znh, char *buf, int bufsize); diff --git a/lib/zlog.c b/lib/zlog.c index 85606d2624..e0bb34a258 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -401,7 +401,7 @@ void zlog_tls_buffer_flush(void) return; rcu_read_lock(); - frr_each (zlog_targets, &zlog_targets, zt) { + frr_each_safe (zlog_targets, &zlog_targets, zt) { if (!zt->logfn) continue; @@ -431,7 +431,7 @@ static void vzlog_notls(const struct xref_logmsg *xref, int prio, msg->stackbufsz = sizeof(stackbuf); rcu_read_lock(); - frr_each (zlog_targets, &zlog_targets, zt) { + frr_each_safe (zlog_targets, &zlog_targets, zt) { if (prio > zt->prio_min) continue; if (!zt->logfn) diff --git a/lib/zlog_live.c b/lib/zlog_live.c index fbe0e5ee49..931aa3461d 100644 --- a/lib/zlog_live.c +++ b/lib/zlog_live.c @@ -22,6 +22,7 @@ #include "frrcu.h" #include "zlog.h" #include "printfrr.h" +#include "network.h" DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target"); @@ -39,6 +40,7 @@ struct zlt_live { struct rcu_head head_self; atomic_uint_fast32_t state; + atomic_uint_fast32_t lost_msgs; }; static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], @@ -63,14 +65,16 @@ static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], for (i = 0; i < nmsgs; i++) { const struct fmt_outpos *argpos; - size_t n_argpos, arghdrlen; + size_t n_argpos, texthdrlen; struct zlog_msg *msg = msgs[i]; int prio = zlog_msg_prio(msg); + const struct xref_logmsg *xref; + intmax_t pid, tid; if (prio > zt->prio_min) continue; - zlog_msg_args(msg, &arghdrlen, &n_argpos, &argpos); + zlog_msg_args(msg, &texthdrlen, &n_argpos, &argpos); mmh->msg_hdr.msg_iov = iov; @@ -89,14 +93,29 @@ static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], iov++; zlog_msg_tsraw(msg, &ts); + zlog_msg_pid(msg, &pid, &tid); + xref = zlog_msg_xref(msg); hdr->ts_sec = ts.tv_sec; hdr->ts_nsec = ts.tv_nsec; - hdr->prio = zlog_msg_prio(msg); + hdr->pid = pid; + hdr->tid = tid; + hdr->lost_msgs = atomic_load_explicit(&zte->lost_msgs, + memory_order_relaxed); + hdr->prio = prio; hdr->flags = 0; hdr->textlen = textlen; - hdr->arghdrlen = arghdrlen; + hdr->texthdrlen = texthdrlen; hdr->n_argpos = n_argpos; + if (xref) { + memcpy(hdr->uid, xref->xref.xrefdata->uid, + sizeof(hdr->uid)); + hdr->ec = xref->ec; + } else { + memset(hdr->uid, 0, sizeof(hdr->uid)); + hdr->ec = 0; + } + hdr->hdrlen = sizeof(*hdr) + sizeof(*argpos) * n_argpos; mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov; mmh++; @@ -109,6 +128,12 @@ static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) { sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0); + if (sent <= 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + atomic_fetch_add_explicit(&zte->lost_msgs, + msgtotal - msgpos, + memory_order_relaxed); + break; + } if (sent <= 0) goto out_err; } @@ -134,7 +159,7 @@ static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, size_t len) { struct zlt_live *zte = container_of(zt, struct zlt_live, zt); - struct zlog_live_hdr hdr[1]; + struct zlog_live_hdr hdr[1] = {}; struct iovec iovs[2], *iov = iovs; struct timespec ts; int fd; @@ -143,14 +168,12 @@ static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, if (fd < 0) return; - clock_gettime(CLOCK_MONOTONIC, &ts); + clock_gettime(CLOCK_REALTIME, &ts); hdr->ts_sec = ts.tv_sec; hdr->ts_nsec = ts.tv_nsec; hdr->prio = LOG_CRIT; - hdr->flags = 0; hdr->textlen = len; - hdr->n_argpos = 0; iov->iov_base = (char *)hdr; iov->iov_len = sizeof(hdr); @@ -166,8 +189,6 @@ static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd) { int sockets[2]; - struct zlt_live *zte; - struct zlog_target *zt; if (cfg->target) zlog_live_close(cfg); @@ -192,12 +213,23 @@ void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd) shutdown(sockets[0], SHUT_RD); *other_fd = sockets[1]; + zlog_live_open_fd(cfg, prio_min, sockets[0]); +} + +void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd) +{ + struct zlt_live *zte; + struct zlog_target *zt; + + if (cfg->target) + zlog_live_close(cfg); zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte)); zte = container_of(zt, struct zlt_live, zt); cfg->target = zte; - zte->fd = sockets[0]; + set_nonblocking(fd); + zte->fd = fd; zte->zt.prio_min = prio_min; zte->zt.logfn = zlog_live; zte->zt.logfn_sigsafe = zlog_live_sigsafe; diff --git a/lib/zlog_live.h b/lib/zlog_live.h index c948baeab1..55e60ae674 100644 --- a/lib/zlog_live.h +++ b/lib/zlog_live.h @@ -20,13 +20,42 @@ #include "printfrr.h" struct zlog_live_hdr { + /* timestamp (CLOCK_REALTIME) */ uint64_t ts_sec; uint32_t ts_nsec; + + /* length of zlog_live_hdr, including variable length bits and + * possible future extensions - aka start of text + */ + uint32_t hdrlen; + + /* process & thread ID, meaning depends on OS */ + int64_t pid; + int64_t tid; + + /* number of lost messages due to best-effort non-blocking mode */ + uint32_t lost_msgs; + /* syslog priority value */ uint32_t prio; + /* flags: currently unused */ uint32_t flags; + /* length of message text - extra data (e.g. future key/value metadata) + * may follow after it + */ uint32_t textlen; + /* length of "[XXXXX-XXXXX][EC 0] " header; consumer may want to skip + * over it if using the raw values below. Note that this text may be + * absent depending on "log error-category" and "log unique-id" + * settings + */ + uint32_t texthdrlen; + + /* xref unique identifier, "XXXXX-XXXXX\0" = 12 bytes */ + char uid[12]; + /* EC value */ + uint32_t ec; - uint32_t arghdrlen; + /* recorded printf formatting argument positions (variable length) */ uint32_t n_argpos; struct fmt_outpos argpos[0]; }; @@ -41,6 +70,7 @@ struct zlog_live_cfg { extern void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd); +extern void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd); static inline bool zlog_live_is_null(struct zlog_live_cfg *cfg) { diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index d618ed86e0..87407245b3 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -689,7 +689,7 @@ DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd, "graceful-restart prepare ipv6 ospf", "Graceful Restart commands\n" "Prepare upcoming graceful restart\n" IPV6_STR - "Prepare to restart the OSPFv3 process") + "Prepare to restart the OSPFv3 process\n") { ospf6_gr_prepare(); diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index e279d0411b..4b37c5bc20 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -166,19 +166,20 @@ static int ospf6_zebra_import_check_update(ZAPI_CALLBACK_ARGS) { struct ospf6 *ospf6; struct zapi_route nhr; + struct prefix matched; ospf6 = ospf6_lookup_by_vrf_id(vrf_id); if (ospf6 == NULL || !IS_OSPF6_ASBR(ospf6)) return 0; - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { zlog_err("%s[%u]: Failure to decode route", __func__, ospf6->vrf_id); return -1; } - if (nhr.prefix.family != AF_INET6 || nhr.prefix.prefixlen != 0 - || nhr.type == ZEBRA_ROUTE_OSPF6) + if (matched.family != AF_INET6 || matched.prefixlen != 0 || + nhr.type == ZEBRA_ROUTE_OSPF6) return 0; ospf6->nssa_default_import_check.status = !!nhr.nexthop_num; diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c index ee1ca256e3..2521f2fce0 100644 --- a/ospfd/ospf_gr.c +++ b/ospfd/ospf_gr.c @@ -730,7 +730,7 @@ DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd, "Graceful Restart commands\n" "Prepare upcoming graceful restart\n" IP_STR - "Prepare to restart the OSPF process") + "Prepare to restart the OSPF process\n") { struct ospf *ospf; struct listnode *node; diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index b480d4072e..f992588729 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -399,17 +399,19 @@ void route_delete(struct pbr_nexthop_group_cache *pnhgc, afi_t afi) static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct zapi_route nhr; + struct prefix matched; uint32_t i; - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { zlog_err("Failure to decode Nexthop update message"); return 0; } if (DEBUG_MODE_CHECK(&pbr_dbg_zebra, DEBUG_MODE_ALL)) { - DEBUGD(&pbr_dbg_zebra, "%s: Received Nexthop update: %pFX", - __func__, &nhr.prefix); + DEBUGD(&pbr_dbg_zebra, + "%s: Received Nexthop update: %pFX against %pFX", + __func__, &matched, &nhr.prefix); DEBUGD(&pbr_dbg_zebra, "%s: (Nexthops(%u)", __func__, nhr.nexthop_num); @@ -423,6 +425,7 @@ static int pbr_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) } } + nhr.prefix = matched; pbr_nht_nexthop_update(&nhr); return 1; } diff --git a/pimd/pim6_cmd.c b/pimd/pim6_cmd.c index 06790d3d63..e3130b1564 100644 --- a/pimd/pim6_cmd.c +++ b/pimd/pim6_cmd.c @@ -210,6 +210,244 @@ DEFPY (no_ipv6_pim_register_suppress, return pim_process_no_register_suppress_cmd(vty); } +DEFPY (interface_ipv6_pim, + interface_ipv6_pim_cmd, + "ipv6 pim", + IPV6_STR + PIM_STR) +{ + return pim_process_ip_pim_cmd(vty); +} + +DEFPY (interface_no_ipv6_pim, + interface_no_ipv6_pim_cmd, + "no ipv6 pim", + NO_STR + IPV6_STR + PIM_STR) +{ + return pim_process_no_ip_pim_cmd(vty); +} + +DEFPY (interface_ipv6_pim_drprio, + interface_ipv6_pim_drprio_cmd, + "ipv6 pim drpriority (1-4294967295)", + IPV6_STR + PIM_STR + "Set the Designated Router Election Priority\n" + "Value of the new DR Priority\n") +{ + return pim_process_ip_pim_drprio_cmd(vty, drpriority_str); +} + +DEFPY (interface_no_ipv6_pim_drprio, + interface_no_ipv6_pim_drprio_cmd, + "no ip pim drpriority [(1-4294967295)]", + NO_STR + IPV6_STR + PIM_STR + "Revert the Designated Router Priority to default\n" + "Old Value of the Priority\n") +{ + return pim_process_no_ip_pim_drprio_cmd(vty); +} + +DEFPY (interface_ipv6_pim_hello, + interface_ipv6_pim_hello_cmd, + "ipv6 pim hello (1-65535) [(1-65535)]$hold", + IPV6_STR + PIM_STR + IFACE_PIM_HELLO_STR + IFACE_PIM_HELLO_TIME_STR + IFACE_PIM_HELLO_HOLD_STR) +{ + return pim_process_ip_pim_hello_cmd(vty, hello_str, hold_str); +} + +DEFPY (interface_no_ipv6_pim_hello, + interface_no_ipv6_pim_hello_cmd, + "no ipv6 pim hello [(1-65535) [(1-65535)]]", + NO_STR + IPV6_STR + PIM_STR + IFACE_PIM_HELLO_STR + IGNORED_IN_NO_STR + IGNORED_IN_NO_STR) +{ + return pim_process_no_ip_pim_hello_cmd(vty); +} + +DEFPY (interface_ipv6_pim_activeactive, + interface_ipv6_pim_activeactive_cmd, + "[no] ipv6 pim active-active", + NO_STR + IPV6_STR + PIM_STR + "Mark interface as Active-Active for MLAG operations\n") +{ + return pim_process_ip_pim_activeactive_cmd(vty, no); +} + +DEFPY_HIDDEN (interface_ipv6_pim_ssm, + interface_ipv6_pim_ssm_cmd, + "ipv6 pim ssm", + IPV6_STR + PIM_STR + IFACE_PIM_STR) +{ + int ret; + + ret = pim_process_ip_pim_cmd(vty); + + if (ret != NB_OK) + return ret; + + vty_out(vty, + "Enabled PIM SM on interface; configure PIM SSM range if needed\n"); + + return NB_OK; +} + +DEFPY_HIDDEN (interface_no_ipv6_pim_ssm, + interface_no_ipv6_pim_ssm_cmd, + "no ipv6 pim ssm", + NO_STR + IPV6_STR + PIM_STR + IFACE_PIM_STR) +{ + return pim_process_no_ip_pim_cmd(vty); +} + +DEFPY_HIDDEN (interface_ipv6_pim_sm, + interface_ipv6_pim_sm_cmd, + "ipv6 pim sm", + IPV6_STR + PIM_STR + IFACE_PIM_SM_STR) +{ + return pim_process_ip_pim_cmd(vty); +} + +DEFPY_HIDDEN (interface_no_ipv6_pim_sm, + interface_no_ipv6_pim_sm_cmd, + "no ipv6 pim sm", + NO_STR + IPV6_STR + PIM_STR + IFACE_PIM_SM_STR) +{ + return pim_process_no_ip_pim_cmd(vty); +} + +/* boundaries */ +DEFPY (interface_ipv6_pim_boundary_oil, + interface_ipv6_pim_boundary_oil_cmd, + "ipv6 multicast boundary oil WORD", + IPV6_STR + "Generic multicast configuration options\n" + "Define multicast boundary\n" + "Filter OIL by group using prefix list\n" + "Prefix list to filter OIL with\n") +{ + return pim_process_ip_pim_boundary_oil_cmd(vty, oil); +} + +DEFPY (interface_no_ipv6_pim_boundary_oil, + interface_no_ipv6_pim_boundary_oil_cmd, + "no ipv6 multicast boundary oil [WORD]", + NO_STR + IPV6_STR + "Generic multicast configuration options\n" + "Define multicast boundary\n" + "Filter OIL by group using prefix list\n" + "Prefix list to filter OIL with\n") +{ + return pim_process_no_ip_pim_boundary_oil_cmd(vty); +} + +DEFPY (interface_ipv6_mroute, + interface_ipv6_mroute_cmd, + "ipv6 mroute INTERFACE X:X::X:X$group [X:X::X:X]$source", + IPV6_STR + "Add multicast route\n" + "Outgoing interface name\n" + "Group address\n" + "Source address\n") +{ + return pim_process_ip_mroute_cmd(vty, interface, group_str, source_str); +} + +DEFPY (interface_no_ipv6_mroute, + interface_no_ipv6_mroute_cmd, + "no ipv6 mroute INTERFACE X:X::X:X$group [X:X::X:X]$source", + NO_STR + IPV6_STR + "Add multicast route\n" + "Outgoing interface name\n" + "Group Address\n" + "Source Address\n") +{ + return pim_process_no_ip_mroute_cmd(vty, interface, group_str, + source_str); +} + +DEFPY (ipv6_pim_rp, + ipv6_pim_rp_cmd, + "ipv6 pim rp X:X::X:X$rp [X:X::X:X/M]$gp", + IPV6_STR + PIM_STR + "Rendezvous Point\n" + "ipv6 address of RP\n" + "Group Address range to cover\n") +{ + const char *group_str = (gp_str) ? gp_str : "FF00::0/8"; + + return pim_process_rp_cmd(vty, rp_str, group_str); +} + +DEFPY (no_ipv6_pim_rp, + no_ipv6_pim_rp_cmd, + "no ipv6 pim rp X:X::X:X$rp [X:X::X:X/M]$gp", + NO_STR + IPV6_STR + PIM_STR + "Rendezvous Point\n" + "ipv6 address of RP\n" + "Group Address range to cover\n") +{ + const char *group_str = (gp_str) ? gp_str : "FF00::0/8"; + + return pim_process_no_rp_cmd(vty, rp_str, group_str); +} + +DEFPY (ipv6_pim_rp_prefix_list, + ipv6_pim_rp_prefix_list_cmd, + "ipv6 pim rp X:X::X:X$rp prefix-list WORD$plist", + IPV6_STR + PIM_STR + "Rendezvous Point\n" + "ipv6 address of RP\n" + "group prefix-list filter\n" + "Name of a prefix-list\n") +{ + return pim_process_rp_plist_cmd(vty, rp_str, plist); +} + +DEFPY (no_ipv6_pim_rp_prefix_list, + no_ipv6_pim_rp_prefix_list_cmd, + "no ipv6 pim rp X:X::X:X$rp prefix-list WORD$plist", + NO_STR + IPV6_STR + PIM_STR + "Rendezvous Point\n" + "ipv6 address of RP\n" + "group prefix-list filter\n" + "Name of a prefix-list\n") +{ + return pim_process_no_rp_plist_cmd(vty, rp_str, plist); +} + void pim_cmd_init(void) { if_cmd_init(pim_interface_config_write); @@ -228,4 +466,29 @@ void pim_cmd_init(void) install_element(CONFIG_NODE, &no_ipv6_pim_rp_keep_alive_cmd); install_element(CONFIG_NODE, &ipv6_pim_register_suppress_cmd); install_element(CONFIG_NODE, &no_ipv6_pim_register_suppress_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_drprio_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_drprio_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_hello_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_hello_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_activeactive_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_ssm_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_ssm_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_pim_sm_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_pim_sm_cmd); + install_element(INTERFACE_NODE, + &interface_ipv6_pim_boundary_oil_cmd); + install_element(INTERFACE_NODE, + &interface_no_ipv6_pim_boundary_oil_cmd); + install_element(INTERFACE_NODE, &interface_ipv6_mroute_cmd); + install_element(INTERFACE_NODE, &interface_no_ipv6_mroute_cmd); + install_element(CONFIG_NODE, &ipv6_pim_rp_cmd); + install_element(VRF_NODE, &ipv6_pim_rp_cmd); + install_element(CONFIG_NODE, &no_ipv6_pim_rp_cmd); + install_element(VRF_NODE, &no_ipv6_pim_rp_cmd); + install_element(CONFIG_NODE, &ipv6_pim_rp_prefix_list_cmd); + install_element(VRF_NODE, &ipv6_pim_rp_prefix_list_cmd); + install_element(CONFIG_NODE, &no_ipv6_pim_rp_prefix_list_cmd); + install_element(VRF_NODE, &no_ipv6_pim_rp_prefix_list_cmd); } diff --git a/pimd/pim6_cmd.h b/pimd/pim6_cmd.h index e49045a1b5..ac5eb3f9bf 100644 --- a/pimd/pim6_cmd.h +++ b/pimd/pim6_cmd.h @@ -33,6 +33,11 @@ #define IFACE_MLD_LAST_MEMBER_QUERY_INTERVAL_STR \ "MLD last member query interval\n" #define IFACE_MLD_LAST_MEMBER_QUERY_COUNT_STR "MLD last member query count\n" +#define IFACE_PIM_STR "Enable PIM SSM operation\n" +#define IFACE_PIM_SM_STR "Enable PIM SM operation\n" +#define IFACE_PIM_HELLO_STR "Hello Interval\n" +#define IFACE_PIM_HELLO_TIME_STR "Time in seconds for Hello Interval\n" +#define IFACE_PIM_HELLO_HOLD_STR "Time in seconds for Hold Interval\n" #define MROUTE_STR "IP multicast routing table\n" #define DEBUG_MLD_STR "MLD protocol activity\n" #define DEBUG_MLD_EVENTS_STR "MLD protocol events\n" diff --git a/pimd/pim6_main.c b/pimd/pim6_main.c index c9e3463969..8c7fca174c 100644 --- a/pimd/pim6_main.c +++ b/pimd/pim6_main.c @@ -185,19 +185,16 @@ int main(int argc, char **argv, char **envp) */ pim_iface_init(); - /* TODO PIM6: next line is temporary since pim_cmd_init is disabled */ - if_cmd_init(NULL); - pim_zebra_init(); #if 0 pim_bfd_init(); pim_mlag_init(); +#endif hook_register(routing_conf_event, routing_control_plane_protocols_name_validate); routing_control_plane_protocols_register_vrf_dependency(); -#endif frr_config_fork(); frr_run(router->master); diff --git a/pimd/pim6_stubs.c b/pimd/pim6_stubs.c index 9f68b7be3d..e689c7aca4 100644 --- a/pimd/pim6_stubs.c +++ b/pimd/pim6_stubs.c @@ -29,40 +29,6 @@ /* * NH lookup / NHT */ -void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient, - struct pim_nexthop_cache *pnc, int command) -{ -} - -int pim_ecmp_nexthop_lookup(struct pim_instance *pim, - struct pim_nexthop *nexthop, struct prefix *src, - struct prefix *grp, int neighbor_needed) -{ - return 0; -} - -int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp, - struct pim_nexthop_cache *out_pnc) -{ - return 0; -} - -void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp) -{ -} - -struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim, - struct pim_rpf *rpf) -{ - return NULL; -} - -void pim_rp_nexthop_del(struct rp_info *rp_info) -{ -} - void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr) { } @@ -133,3 +99,25 @@ void pim_reg_del_on_couldreg_fail(struct interface *ifp) { } +bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp) +{ + return false; +} + +void pim_bsm_proc_free(struct pim_instance *pim) +{ +} + +void pim_bsm_proc_init(struct pim_instance *pim) +{ +} + +struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope, + struct prefix *grp) +{ + return NULL; +} + +void pim_bsm_write_config(struct vty *vty, struct interface *ifp) +{ +} diff --git a/pimd/pim_addr.h b/pimd/pim_addr.h index a278a46593..e422a2e2da 100644 --- a/pimd/pim_addr.h +++ b/pimd/pim_addr.h @@ -33,7 +33,6 @@ typedef struct in_addr pim_addr; #define PIM_AFI AFI_IP #define PIM_MAX_BITLEN IPV4_MAX_BITLEN #define PIM_AF_NAME "ip" -#define FRR_PIM_AF_XPATH_VAL "frr-routing:ipv4" union pimprefixptr { prefixtype(pimprefixptr, struct prefix, p) @@ -53,7 +52,6 @@ typedef struct in6_addr pim_addr; #define PIM_AFI AFI_IP6 #define PIM_MAX_BITLEN IPV6_MAX_BITLEN #define PIM_AF_NAME "ipv6" -#define FRR_PIM_AF_XPATH_VAL "frr-routing:ipv6" union pimprefixptr { prefixtype(pimprefixptr, struct prefix, p) diff --git a/pimd/pim_br.c b/pimd/pim_br.c index 3e64296deb..6ec6b11e7b 100644 --- a/pimd/pim_br.c +++ b/pimd/pim_br.c @@ -30,14 +30,12 @@ struct pim_br { pim_sgaddr sg; - struct in_addr pmbr; + pim_addr pmbr; }; -struct in_addr pim_br_unknown = {.s_addr = 0}; - static struct list *pim_br_list = NULL; -struct in_addr pim_br_get_pmbr(pim_sgaddr *sg) +pim_addr pim_br_get_pmbr(pim_sgaddr *sg) { struct listnode *node; struct pim_br *pim_br; @@ -47,10 +45,10 @@ struct in_addr pim_br_get_pmbr(pim_sgaddr *sg) return pim_br->pmbr; } - return pim_br_unknown; + return PIMADDR_ANY; } -void pim_br_set_pmbr(pim_sgaddr *sg, struct in_addr br) +void pim_br_set_pmbr(pim_sgaddr *sg, pim_addr br) { struct listnode *node, *next; struct pim_br *pim_br; diff --git a/pimd/pim_br.h b/pimd/pim_br.h index ef24ef3c19..7b87c0f1fd 100644 --- a/pimd/pim_br.h +++ b/pimd/pim_br.h @@ -20,13 +20,11 @@ #ifndef PIM_BR_H #define PIM_BR_H -struct in_addr pim_br_get_pmbr(pim_sgaddr *sg); +pim_addr pim_br_get_pmbr(pim_sgaddr *sg); -void pim_br_set_pmbr(pim_sgaddr *sg, struct in_addr value); +void pim_br_set_pmbr(pim_sgaddr *sg, pim_addr value); void pim_br_clear_pmbr(pim_sgaddr *sg); void pim_br_init(void); -extern struct in_addr pim_br_unknown; - #endif diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c index c45823cb87..d2e299a007 100644 --- a/pimd/pim_bsm.c +++ b/pimd/pim_bsm.c @@ -36,6 +36,7 @@ #include "pim_bsm.h" #include "pim_time.h" #include "pim_zebra.h" +#include "pim_util.h" /* Functions forward declaration */ static void pim_bs_timer_start(struct bsm_scope *scope, int bs_timeout); @@ -284,7 +285,7 @@ static void pim_on_g2rp_timer(struct thread *t) struct rp_info *rp_info; struct route_node *rn; uint16_t elapse; - struct in_addr bsrp_addr; + pim_addr bsrp_addr; bsrp = THREAD_ARG(t); THREAD_OFF(bsrp->g2rp_timer); @@ -324,10 +325,9 @@ static void pim_on_g2rp_timer(struct thread *t) if (rp_info->rp_src != RP_SRC_STATIC) { /* If new rp available, change it else delete the existing */ if (bsrp) { - bsrp_addr = bsrp->rp_address; pim_g2rp_timer_start( bsrp, (bsrp->rp_holdtime - bsrp->elapse_time)); - pim_rp_change(pim, bsrp_addr, bsgrp_node->group, + pim_rp_change(pim, bsrp->rp_address, bsgrp_node->group, RP_SRC_BSR); } else { pim_rp_del(pim, bsrp_addr, bsgrp_node->group, NULL, @@ -417,7 +417,7 @@ static void pim_instate_pend_list(struct bsgrp_node *bsgrp_node) pend = bsm_rpinfos_first(bsgrp_node->partial_bsrp_list); - if (!str2prefix("224.0.0.0/4", &group_all)) + if (!pim_get_all_mcast_group(&group_all)) return; rp_all = pim_rp_find_match_group(pim, &group_all); @@ -628,7 +628,7 @@ void pim_bsm_clear(struct pim_instance *pim) pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info); - if (!str2prefix("224.0.0.0/4", &g_all)) + if (!pim_get_all_mcast_group(&g_all)) return; rp_all = pim_rp_find_match_group(pim, &g_all); diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h index a536b50688..fceabef9e6 100644 --- a/pimd/pim_bsm.h +++ b/pimd/pim_bsm.h @@ -115,7 +115,7 @@ struct bsm_rpinfo { uint32_t elapse_time; /* upd at expiry of elected RP node */ uint16_t rp_prio; /* RP priority */ uint16_t rp_holdtime; /* RP holdtime - g2rp timer value */ - struct in_addr rp_address; /* RP Address */ + 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 */ }; diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 76f90cdba7..8906caf00f 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -455,8 +455,7 @@ static void pim_show_membership(struct pim_instance *pim, struct vty *vty, json_object_free(json); } -static void pim_print_ifp_flags(struct vty *vty, struct interface *ifp, - int mloop) +static void pim_print_ifp_flags(struct vty *vty, struct interface *ifp) { vty_out(vty, "Flags\n"); vty_out(vty, "-----\n"); @@ -469,7 +468,6 @@ static void pim_print_ifp_flags(struct vty *vty, struct interface *ifp, vty_out(vty, "Interface Index : %d\n", ifp->ifindex); vty_out(vty, "Multicast : %s\n", if_is_multicast(ifp) ? "yes" : "no"); - vty_out(vty, "Multicast Loop : %d\n", mloop); vty_out(vty, "Promiscuous : %s\n", (ifp->flags & IFF_PROMISC) ? "yes" : "no"); vty_out(vty, "\n"); @@ -576,7 +574,6 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, char other_hhmmss[10]; int found_ifname = 0; int sqi; - int mloop = 0; long gmi_msec; /* Group Membership Interval */ long lmqt_msec; long ohpi_msec; @@ -639,11 +636,6 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, qri_msec = pim_ifp->gm_query_max_response_time_dsec * 100; - if (pim_ifp->pim_sock_fd >= 0) - mloop = pim_socket_mcastloop_get( - pim_ifp->pim_sock_fd); - else - mloop = 0; lmqc = pim_ifp->gm_last_member_query_count; if (uj) { @@ -776,7 +768,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim, vty_out(vty, "\n"); vty_out(vty, "\n"); - pim_print_ifp_flags(vty, ifp, mloop); + pim_print_ifp_flags(vty, ifp); } } } @@ -903,7 +895,6 @@ static void pim_show_interfaces_single(struct pim_instance *pim, char src_str[INET_ADDRSTRLEN]; char stat_uptime[10]; char uptime[10]; - int mloop = 0; int found_ifname = 0; int print_header; json_object *json = NULL; @@ -945,10 +936,6 @@ static void pim_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_hello_period); pim_time_uptime(stat_uptime, sizeof(stat_uptime), now - pim_ifp->pim_ifstat_start); - if (pim_ifp->pim_sock_fd >= 0) - mloop = pim_socket_mcastloop_get(pim_ifp->pim_sock_fd); - else - mloop = 0; if (uj) { char pbuf[PREFIX2STR_BUFFER]; @@ -1096,8 +1083,6 @@ static void pim_show_interfaces_single(struct pim_instance *pim, pim_ifp->pim_ifstat_hello_sendfail); json_object_int_add(json_row, "helloGenerationId", pim_ifp->pim_generation_id); - json_object_int_add(json_row, "flagMulticastLoop", - mloop); json_object_int_add( json_row, "effectivePropagationDelay", @@ -1250,7 +1235,7 @@ static void pim_show_interfaces_single(struct pim_instance *pim, vty_out(vty, "\n"); vty_out(vty, "\n"); - pim_print_ifp_flags(vty, ifp, mloop); + pim_print_ifp_flags(vty, ifp); vty_out(vty, "Join Prune Interval\n"); vty_out(vty, "-------------------\n"); @@ -1305,8 +1290,10 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, FOR_ALL_INTERFACES (pim->vrf, ifp) { struct pim_interface *pim_ifp; - struct listnode *sock_node; + struct listnode *sock_node, *source_node, *group_node; struct gm_sock *igmp; + struct gm_group *group; + struct gm_source *src; pim_ifp = ifp->info; @@ -1316,6 +1303,22 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, if (ifname && strcmp(ifname, ifp->name)) continue; + rx_stats.total_groups += + pim_ifp->gm_group_list + ? listcount(pim_ifp->gm_group_list) + : 0; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, group_node, + group)) { + for (ALL_LIST_ELEMENTS_RO(group->group_source_list, + source_node, src)) { + if (pim_addr_is_any(src->source_addr)) + continue; + + rx_stats.total_source_groups++; + } + } + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) { igmp_stats_add(&rx_stats, &igmp->rx_stats); @@ -1333,7 +1336,7 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, json_object_int_add(json_row, "queryV1", rx_stats.query_v1); json_object_int_add(json_row, "queryV2", rx_stats.query_v2); json_object_int_add(json_row, "queryV3", rx_stats.query_v3); - json_object_int_add(json_row, "leaveV3", rx_stats.leave_v2); + json_object_int_add(json_row, "leaveV2", rx_stats.leave_v2); json_object_int_add(json_row, "reportV1", rx_stats.report_v1); json_object_int_add(json_row, "reportV2", rx_stats.report_v2); json_object_int_add(json_row, "reportV3", rx_stats.report_v3); @@ -1343,6 +1346,10 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, rx_stats.mtrace_req); json_object_int_add(json_row, "unsupported", rx_stats.unsupported); + json_object_int_add(json_row, "totalGroups", + rx_stats.total_groups); + json_object_int_add(json_row, "totalSourceGroups", + rx_stats.total_source_groups); json_object_object_add(json, ifname ? ifname : "global", json_row); vty_json(vty, json); @@ -1350,16 +1357,21 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, vty_out(vty, "IGMP RX statistics\n"); vty_out(vty, "Interface : %s\n", ifname ? ifname : "global"); - vty_out(vty, "V1 query : %u\n", rx_stats.query_v1); - vty_out(vty, "V2 query : %u\n", rx_stats.query_v2); - vty_out(vty, "V3 query : %u\n", rx_stats.query_v3); - vty_out(vty, "V2 leave : %u\n", rx_stats.leave_v2); - vty_out(vty, "V1 report : %u\n", rx_stats.report_v1); - vty_out(vty, "V2 report : %u\n", rx_stats.report_v2); - vty_out(vty, "V3 report : %u\n", rx_stats.report_v3); - vty_out(vty, "mtrace response : %u\n", rx_stats.mtrace_rsp); - vty_out(vty, "mtrace request : %u\n", rx_stats.mtrace_req); - vty_out(vty, "unsupported : %u\n", rx_stats.unsupported); + vty_out(vty, "V1 query : %u\n", rx_stats.query_v1); + vty_out(vty, "V2 query : %u\n", rx_stats.query_v2); + vty_out(vty, "V3 query : %u\n", rx_stats.query_v3); + vty_out(vty, "V2 leave : %u\n", rx_stats.leave_v2); + vty_out(vty, "V1 report : %u\n", rx_stats.report_v1); + vty_out(vty, "V2 report : %u\n", rx_stats.report_v2); + vty_out(vty, "V3 report : %u\n", rx_stats.report_v3); + vty_out(vty, "mtrace response : %u\n", rx_stats.mtrace_rsp); + vty_out(vty, "mtrace request : %u\n", rx_stats.mtrace_req); + vty_out(vty, "unsupported : %u\n", + rx_stats.unsupported); + vty_out(vty, "total groups : %u\n", + rx_stats.total_groups); + vty_out(vty, "total source groups : %u\n", + rx_stats.total_source_groups); } } @@ -5313,39 +5325,56 @@ DEFUN (show_ip_pim_upstream_rpf, DEFUN (show_ip_pim_rp, show_ip_pim_rp_cmd, - "show ip pim [vrf NAME] rp-info [json]", + "show ip pim [vrf NAME] rp-info [A.B.C.D/M] [json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR "PIM RP information\n" + "Multicast Group range\n" JSON_STR) { int idx = 2; struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); bool uj = use_json(argc, argv); + struct prefix *range = NULL; if (!vrf) return CMD_WARNING; - pim_rp_show_information(vrf->info, vty, uj); + if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { + range = prefix_new(); + (void)str2prefix(argv[idx]->arg, range); + apply_mask(range); + } + + pim_rp_show_information(vrf->info, range, vty, uj); return CMD_SUCCESS; } DEFUN (show_ip_pim_rp_vrf_all, show_ip_pim_rp_vrf_all_cmd, - "show ip pim vrf all rp-info [json]", + "show ip pim vrf all rp-info [A.B.C.D/M] [json]", SHOW_STR IP_STR PIM_STR VRF_CMD_HELP_STR "PIM RP information\n" + "Multicast Group range\n" JSON_STR) { + int idx = 0; bool uj = use_json(argc, argv); struct vrf *vrf; bool first = true; + struct prefix *range = NULL; + + if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { + range = prefix_new(); + (void)str2prefix(argv[idx]->arg, range); + apply_mask(range); + } if (uj) vty_out(vty, "{ "); @@ -5357,7 +5386,7 @@ DEFUN (show_ip_pim_rp_vrf_all, first = false; } else vty_out(vty, "VRF: %s\n", vrf->name); - pim_rp_show_information(vrf->info, vty, uj); + pim_rp_show_information(vrf->info, range, vty, uj); } if (uj) vty_out(vty, "}\n"); @@ -6997,98 +7026,36 @@ DEFUN (no_ip_pim_v6_secondary, return nb_cli_apply_changes(vty, NULL); } -DEFUN (ip_pim_rp, +DEFPY (ip_pim_rp, ip_pim_rp_cmd, - "ip pim rp A.B.C.D [A.B.C.D/M]", + "ip pim rp A.B.C.D$rp [A.B.C.D/M]$gp", IP_STR "pim multicast routing\n" "Rendevous Point\n" "ip address of RP\n" "Group Address range to cover\n") { - const char *vrfname; - int idx_rp = 3, idx_group = 4; - char rp_group_xpath[XPATH_MAXLEN]; - int result = 0; - struct prefix group; - struct in_addr rp_addr; - const char *group_str = - (argc == 5) ? argv[idx_group]->arg : "224.0.0.0/4"; - - result = str2prefix(group_str, &group); - if (result) { - struct prefix temp; - - prefix_copy(&temp, &group); - apply_mask(&temp); - if (!prefix_same(&group, &temp)) { - vty_out(vty, "%% Inconsistent address and mask: %s\n", - group_str); - return CMD_WARNING_CONFIG_FAILED; - } - } - - if (!result) { - vty_out(vty, "%% Bad group address specified: %s\n", - group_str); - return CMD_WARNING_CONFIG_FAILED; - } + const char *group_str = (gp_str) ? gp_str : "224.0.0.0/4"; - result = inet_pton(AF_INET, argv[idx_rp]->arg, &rp_addr); - if (result <= 0) { - vty_out(vty, "%% Bad RP address specified: %s\n", - argv[idx_rp]->arg); - return CMD_WARNING_CONFIG_FAILED; - } - - vrfname = pim_cli_get_vrf_name(vty); - if (vrfname == NULL) - return CMD_WARNING_CONFIG_FAILED; - - snprintf(rp_group_xpath, sizeof(rp_group_xpath), - FRR_PIM_STATIC_RP_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", - argv[idx_rp]->arg); - strlcat(rp_group_xpath, "/group-list", sizeof(rp_group_xpath)); - - nb_cli_enqueue_change(vty, rp_group_xpath, NB_OP_CREATE, group_str); - - return nb_cli_apply_changes(vty, NULL); + return pim_process_rp_cmd(vty, rp_str, group_str); } -DEFUN (ip_pim_rp_prefix_list, +DEFPY (ip_pim_rp_prefix_list, ip_pim_rp_prefix_list_cmd, - "ip pim rp A.B.C.D prefix-list WORD", + "ip pim rp A.B.C.D$rp prefix-list WORD$plist", IP_STR "pim multicast routing\n" - "Rendevous Point\n" + "Rendezvous Point\n" "ip address of RP\n" "group prefix-list filter\n" "Name of a prefix-list\n") { - int idx_rp = 3, idx_plist = 5; - const char *vrfname; - char rp_plist_xpath[XPATH_MAXLEN]; - - vrfname = pim_cli_get_vrf_name(vty); - if (vrfname == NULL) - return CMD_WARNING_CONFIG_FAILED; - - snprintf(rp_plist_xpath, sizeof(rp_plist_xpath), - FRR_PIM_STATIC_RP_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", - argv[idx_rp]->arg); - strlcat(rp_plist_xpath, "/prefix-list", sizeof(rp_plist_xpath)); - - nb_cli_enqueue_change(vty, rp_plist_xpath, NB_OP_MODIFY, - argv[idx_plist]->arg); - - return nb_cli_apply_changes(vty, NULL); + return pim_process_rp_plist_cmd(vty, rp_str, plist); } -DEFUN (no_ip_pim_rp, +DEFPY (no_ip_pim_rp, no_ip_pim_rp_cmd, - "no ip pim rp A.B.C.D [A.B.C.D/M]", + "no ip pim rp A.B.C.D$rp [A.B.C.D/M]$gp", NO_STR IP_STR "pim multicast routing\n" @@ -7096,106 +7063,23 @@ DEFUN (no_ip_pim_rp, "ip address of RP\n" "Group Address range to cover\n") { - int idx_rp = 4, idx_group = 5; - const char *group_str = - (argc == 6) ? argv[idx_group]->arg : "224.0.0.0/4"; - char group_list_xpath[XPATH_MAXLEN]; - char group_xpath[XPATH_MAXLEN]; - char rp_xpath[XPATH_MAXLEN]; - int printed; - const char *vrfname; - const struct lyd_node *group_dnode; - - vrfname = pim_cli_get_vrf_name(vty); - if (vrfname == NULL) - return CMD_WARNING_CONFIG_FAILED; - - snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", - argv[idx_rp]->arg); - - - printed = snprintf(group_list_xpath, sizeof(group_list_xpath), - "%s/group-list", rp_xpath); - - if (printed >= (int)(sizeof(group_list_xpath))) { - vty_out(vty, "Xpath too long (%d > %u)", printed + 1, - XPATH_MAXLEN); - return CMD_WARNING_CONFIG_FAILED; - } - - printed = snprintf(group_xpath, sizeof(group_xpath), "%s[.='%s']", - group_list_xpath, group_str); - - if (printed >= (int)(sizeof(group_xpath))) { - vty_out(vty, "Xpath too long (%d > %u)", printed + 1, - XPATH_MAXLEN); - return CMD_WARNING_CONFIG_FAILED; - } - - if (!yang_dnode_exists(vty->candidate_config->dnode, group_xpath)) { - vty_out(vty, "%% Unable to find specified RP\n"); - return NB_OK; - } - - group_dnode = yang_dnode_get(vty->candidate_config->dnode, group_xpath); + const char *group_str = (gp_str) ? gp_str : "224.0.0.0/4"; - if (yang_is_last_list_dnode(group_dnode)) - nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL); - else - nb_cli_enqueue_change(vty, group_list_xpath, NB_OP_DESTROY, - group_str); - - return nb_cli_apply_changes(vty, NULL); + return pim_process_no_rp_cmd(vty, rp_str, group_str); } -DEFUN (no_ip_pim_rp_prefix_list, +DEFPY (no_ip_pim_rp_prefix_list, no_ip_pim_rp_prefix_list_cmd, - "no ip pim rp A.B.C.D prefix-list WORD", + "no ip pim rp A.B.C.D$rp prefix-list WORD$plist", NO_STR IP_STR "pim multicast routing\n" - "Rendevous Point\n" + "Rendezvous Point\n" "ip address of RP\n" "group prefix-list filter\n" "Name of a prefix-list\n") { - int idx_rp = 4; - int idx_plist = 6; - char rp_xpath[XPATH_MAXLEN]; - char plist_xpath[XPATH_MAXLEN]; - const char *vrfname; - const struct lyd_node *plist_dnode; - const char *plist; - - vrfname = pim_cli_get_vrf_name(vty); - if (vrfname == NULL) - return CMD_WARNING_CONFIG_FAILED; - - snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", - argv[idx_rp]->arg); - - snprintf(plist_xpath, sizeof(plist_xpath), FRR_PIM_STATIC_RP_XPATH, - "frr-pim:pimd", "pim", vrfname, "frr-routing:ipv4", - argv[idx_rp]->arg); - strlcat(plist_xpath, "/prefix-list", sizeof(plist_xpath)); - - plist_dnode = yang_dnode_get(vty->candidate_config->dnode, plist_xpath); - if (!plist_dnode) { - vty_out(vty, "%% Unable to find specified RP\n"); - return NB_OK; - } - - plist = yang_dnode_get_string(plist_dnode, plist_xpath); - if (strcmp(argv[idx_plist]->arg, plist)) { - vty_out(vty, "%% Unable to find specified RP\n"); - return NB_OK; - } - - nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, NULL); + return pim_process_no_rp_plist_cmd(vty, rp_str, plist); } DEFUN (ip_pim_ssm_prefix_list, @@ -7959,11 +7843,7 @@ DEFUN (interface_ip_pim_drprio, { int idx_number = 3; - nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_MODIFY, - argv[idx_number]->arg); - - return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_ip_pim_drprio_cmd(vty, argv[idx_number]->arg); } DEFUN (interface_no_ip_pim_drprio, @@ -7975,10 +7855,7 @@ DEFUN (interface_no_ip_pim_drprio, "Revert the Designated Router Priority to default\n" "Old Value of the Priority\n") { - nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_no_ip_pim_drprio_cmd(vty); } DEFPY_HIDDEN (interface_ip_igmp_query_generate, @@ -8069,20 +7946,7 @@ DEFPY (interface_ip_pim_activeactive, PIM_STR "Mark interface as Active-Active for MLAG operations, Hidden because not finished yet\n") { - if (no) - nb_cli_enqueue_change(vty, "./active-active", NB_OP_MODIFY, - "false"); - else { - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, - "true"); - - nb_cli_enqueue_change(vty, "./active-active", NB_OP_MODIFY, - "true"); - } - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_ip_pim_activeactive_cmd(vty, no); } DEFUN_HIDDEN (interface_ip_pim_ssm, @@ -8094,11 +7958,7 @@ DEFUN_HIDDEN (interface_ip_pim_ssm, { int ret; - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, "true"); - - ret = nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + ret = pim_process_ip_pim_cmd(vty); if (ret != NB_OK) return ret; @@ -8116,11 +7976,7 @@ DEFUN_HIDDEN (interface_ip_pim_sm, PIM_STR IFACE_PIM_SM_STR) { - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, "true"); - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_ip_pim_cmd(vty); } DEFUN (interface_ip_pim, @@ -8129,12 +7985,7 @@ DEFUN (interface_ip_pim, IP_STR PIM_STR) { - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, "true"); - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); - + return pim_process_ip_pim_cmd(vty); } DEFUN_HIDDEN (interface_no_ip_pim_ssm, @@ -8145,39 +7996,7 @@ DEFUN_HIDDEN (interface_no_ip_pim_ssm, PIM_STR IFACE_PIM_STR) { - const struct lyd_node *igmp_enable_dnode; - char igmp_if_xpath[XPATH_MAXLEN]; - - int printed = - snprintf(igmp_if_xpath, sizeof(igmp_if_xpath), - "%s/frr-gmp:gmp/address-family[address-family='%s']", - VTY_CURR_XPATH, "frr-routing:ipv4"); - - if (printed >= (int)(sizeof(igmp_if_xpath))) { - vty_out(vty, "Xpath too long (%d > %u)", printed + 1, - XPATH_MAXLEN); - return CMD_WARNING_CONFIG_FAILED; - } - - igmp_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode, - FRR_GMP_ENABLE_XPATH, - VTY_CURR_XPATH, - "frr-routing:ipv4"); - if (!igmp_enable_dnode) { - nb_cli_enqueue_change(vty, igmp_if_xpath, NB_OP_DESTROY, NULL); - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - } else { - if (!yang_dnode_get_bool(igmp_enable_dnode, ".")) { - nb_cli_enqueue_change(vty, igmp_if_xpath, NB_OP_DESTROY, - NULL); - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - } else - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, - "false"); - } - - return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_no_ip_pim_cmd(vty); } DEFUN_HIDDEN (interface_no_ip_pim_sm, @@ -8188,41 +8007,7 @@ DEFUN_HIDDEN (interface_no_ip_pim_sm, PIM_STR IFACE_PIM_SM_STR) { - const struct lyd_node *igmp_enable_dnode; - char igmp_if_xpath[XPATH_MAXLEN]; - - int printed = - snprintf(igmp_if_xpath, sizeof(igmp_if_xpath), - "%s/frr-gmp:gmp/address-family[address-family='%s']", - VTY_CURR_XPATH, "frr-routing:ipv4"); - - if (printed >= (int)(sizeof(igmp_if_xpath))) { - vty_out(vty, "Xpath too long (%d > %u)", printed + 1, - XPATH_MAXLEN); - return CMD_WARNING_CONFIG_FAILED; - } - - igmp_enable_dnode = - yang_dnode_getf(vty->candidate_config->dnode, - FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH, - "frr-routing:ipv4"); - - if (!igmp_enable_dnode) { - nb_cli_enqueue_change(vty, igmp_if_xpath, NB_OP_DESTROY, NULL); - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - } else { - if (!yang_dnode_get_bool(igmp_enable_dnode, ".")) { - nb_cli_enqueue_change(vty, igmp_if_xpath, NB_OP_DESTROY, - NULL); - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - } else - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, - "false"); - } - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_no_ip_pim_cmd(vty); } DEFUN (interface_no_ip_pim, @@ -8232,41 +8017,7 @@ DEFUN (interface_no_ip_pim, IP_STR PIM_STR) { - const struct lyd_node *igmp_enable_dnode; - char igmp_if_xpath[XPATH_MAXLEN]; - - int printed = - snprintf(igmp_if_xpath, sizeof(igmp_if_xpath), - "%s/frr-gmp:gmp/address-family[address-family='%s']", - VTY_CURR_XPATH, "frr-routing:ipv4"); - - if (printed >= (int)(sizeof(igmp_if_xpath))) { - vty_out(vty, "Xpath too long (%d > %u)", printed + 1, - XPATH_MAXLEN); - return CMD_WARNING_CONFIG_FAILED; - } - - igmp_enable_dnode = - yang_dnode_getf(vty->candidate_config->dnode, - FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH, - "frr-routing:ipv4"); - - if (!igmp_enable_dnode) { - nb_cli_enqueue_change(vty, igmp_if_xpath, NB_OP_DESTROY, NULL); - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - } else { - if (!yang_dnode_get_bool(igmp_enable_dnode, ".")) { - nb_cli_enqueue_change(vty, igmp_if_xpath, NB_OP_DESTROY, - NULL); - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - } else - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, - "false"); - } - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_no_ip_pim_cmd(vty); } /* boundaries */ @@ -8279,13 +8030,7 @@ DEFUN(interface_ip_pim_boundary_oil, "Filter OIL by group using prefix list\n" "Prefix list to filter OIL with\n") { - nb_cli_enqueue_change(vty, "./multicast-boundary-oil", NB_OP_MODIFY, - argv[4]->arg); - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); - + return pim_process_ip_pim_boundary_oil_cmd(vty, argv[4]->arg); } DEFUN(interface_no_ip_pim_boundary_oil, @@ -8298,12 +8043,7 @@ DEFUN(interface_no_ip_pim_boundary_oil, "Filter OIL by group using prefix list\n" "Prefix list to filter OIL with\n") { - nb_cli_enqueue_change(vty, "./multicast-boundary-oil", NB_OP_DESTROY, - NULL); - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_no_ip_pim_boundary_oil_cmd(vty); } DEFUN (interface_ip_mroute, @@ -8324,13 +8064,8 @@ DEFUN (interface_ip_mroute, else source_str = argv[idx_ipv4 + 1]->arg; - nb_cli_enqueue_change(vty, "./oif", NB_OP_MODIFY, - argv[idx_interface]->arg); - - return nb_cli_apply_changes(vty, - FRR_PIM_MROUTE_XPATH, - "frr-routing:ipv4", source_str, - argv[idx_ipv4]->arg); + return pim_process_ip_mroute_cmd(vty, argv[idx_interface]->arg, + argv[idx_ipv4]->arg, source_str); } DEFUN (interface_no_ip_mroute, @@ -8343,6 +8078,7 @@ DEFUN (interface_no_ip_mroute, "Group Address\n" "Source Address\n") { + int idx_interface = 3; int idx_ipv4 = 4; const char *source_str; @@ -8351,12 +8087,8 @@ DEFUN (interface_no_ip_mroute, else source_str = argv[idx_ipv4 + 1]->arg; - nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, - FRR_PIM_MROUTE_XPATH, - "frr-routing:ipv4", source_str, - argv[idx_ipv4]->arg); + return pim_process_no_ip_mroute_cmd(vty, argv[idx_interface]->arg, + argv[idx_ipv4]->arg, source_str); } DEFUN (interface_ip_pim_hello, @@ -8370,31 +8102,14 @@ DEFUN (interface_ip_pim_hello, { int idx_time = 3; int idx_hold = 4; - const struct lyd_node *igmp_enable_dnode; - - igmp_enable_dnode = - yang_dnode_getf(vty->candidate_config->dnode, - FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH, - "frr-routing:ipv4"); - if (!igmp_enable_dnode) { - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, - "true"); - } else { - if (!yang_dnode_get_bool(igmp_enable_dnode, ".")) - nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, - "true"); - } - - nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_MODIFY, - argv[idx_time]->arg); if (argc == idx_hold + 1) - nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_MODIFY, - argv[idx_hold]->arg); + return pim_process_ip_pim_hello_cmd(vty, argv[idx_time]->arg, + argv[idx_hold]->arg); - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + else + return pim_process_ip_pim_hello_cmd(vty, argv[idx_time]->arg, + NULL); } DEFUN (interface_no_ip_pim_hello, @@ -8407,12 +8122,7 @@ DEFUN (interface_no_ip_pim_hello, IGNORED_IN_NO_STR IGNORED_IN_NO_STR) { - nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_DESTROY, NULL); - nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_DESTROY, NULL); - - return nb_cli_apply_changes(vty, - FRR_PIM_INTERFACE_XPATH, - "frr-routing:ipv4"); + return pim_process_no_ip_pim_hello_cmd(vty); } DEFUN (debug_igmp, diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c index 6adea54a61..c5d89f8065 100644 --- a/pimd/pim_cmd_common.c +++ b/pimd/pim_cmd_common.c @@ -30,6 +30,7 @@ #include "nexthop.h" #include "vrf.h" #include "ferr.h" +#include "lib/srcdest_table.h" #include "pimd.h" #include "pim_vty.h" @@ -326,3 +327,329 @@ int pim_process_no_register_suppress_cmd(struct vty *vty) return nb_cli_apply_changes(vty, NULL); } + +int pim_process_ip_pim_cmd(struct vty *vty) +{ + nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, "true"); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_no_ip_pim_cmd(struct vty *vty) +{ + const struct lyd_node *mld_enable_dnode; + char mld_if_xpath[XPATH_MAXLEN]; + + int printed = + snprintf(mld_if_xpath, sizeof(mld_if_xpath), + "%s/frr-gmp:gmp/address-family[address-family='%s']", + VTY_CURR_XPATH, FRR_PIM_AF_XPATH_VAL); + + if (printed >= (int)(sizeof(mld_if_xpath))) { + vty_out(vty, "Xpath too long (%d > %u)", printed + 1, + XPATH_MAXLEN); + return CMD_WARNING_CONFIG_FAILED; + } + + mld_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode, + FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH, + FRR_PIM_AF_XPATH_VAL); + + if (!mld_enable_dnode) { + nb_cli_enqueue_change(vty, mld_if_xpath, NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } else { + if (!yang_dnode_get_bool(mld_enable_dnode, ".")) { + nb_cli_enqueue_change(vty, mld_if_xpath, NB_OP_DESTROY, + NULL); + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + } else + nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, + "false"); + } + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_ip_pim_drprio_cmd(struct vty *vty, const char *drpriority_str) +{ + nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_MODIFY, + drpriority_str); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_no_ip_pim_drprio_cmd(struct vty *vty) +{ + nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_ip_pim_hello_cmd(struct vty *vty, const char *hello_str, + const char *hold_str) +{ + const struct lyd_node *mld_enable_dnode; + + mld_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode, + FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH, + FRR_PIM_AF_XPATH_VAL); + + if (!mld_enable_dnode) { + nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, + "true"); + } else { + if (!yang_dnode_get_bool(mld_enable_dnode, ".")) + nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, + "true"); + } + + nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_MODIFY, hello_str); + + if (hold_str) + nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_MODIFY, + hold_str); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_no_ip_pim_hello_cmd(struct vty *vty) +{ + nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_DESTROY, NULL); + nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_ip_pim_activeactive_cmd(struct vty *vty, const char *no) +{ + if (no) + nb_cli_enqueue_change(vty, "./active-active", NB_OP_MODIFY, + "false"); + else { + nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, + "true"); + + nb_cli_enqueue_change(vty, "./active-active", NB_OP_MODIFY, + "true"); + } + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_ip_pim_boundary_oil_cmd(struct vty *vty, const char *oil) +{ + nb_cli_enqueue_change(vty, "./multicast-boundary-oil", NB_OP_MODIFY, + oil); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_no_ip_pim_boundary_oil_cmd(struct vty *vty) +{ + nb_cli_enqueue_change(vty, "./multicast-boundary-oil", NB_OP_DESTROY, + NULL); + + return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH, + FRR_PIM_AF_XPATH_VAL); +} + +int pim_process_ip_mroute_cmd(struct vty *vty, const char *interface, + const char *group_str, const char *source_str) +{ + nb_cli_enqueue_change(vty, "./oif", NB_OP_MODIFY, interface); + + if (!source_str) { + char buf[SRCDEST2STR_BUFFER]; + + inet_ntop(AF_INET6, &in6addr_any, buf, sizeof(buf)); + return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH, + FRR_PIM_AF_XPATH_VAL, buf, + group_str); + } + + return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH, + FRR_PIM_AF_XPATH_VAL, source_str, + group_str); +} + +int pim_process_no_ip_mroute_cmd(struct vty *vty, const char *interface, + const char *group_str, const char *source_str) +{ + nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL); + + if (!source_str) { + char buf[SRCDEST2STR_BUFFER]; + + inet_ntop(AF_INET6, &in6addr_any, buf, sizeof(buf)); + return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH, + FRR_PIM_AF_XPATH_VAL, buf, + group_str); + } + + return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH, + FRR_PIM_AF_XPATH_VAL, source_str, + group_str); +} + +int pim_process_rp_cmd(struct vty *vty, const char *rp_str, + const char *group_str) +{ + const char *vrfname; + char rp_group_xpath[XPATH_MAXLEN]; + int result = 0; + struct prefix group; + pim_addr rp_addr; + + result = str2prefix(group_str, &group); + if (result) { + struct prefix temp; + + prefix_copy(&temp, &group); + apply_mask(&temp); + if (!prefix_same(&group, &temp)) { + vty_out(vty, "%% Inconsistent address and mask: %s\n", + group_str); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (!result) { + vty_out(vty, "%% Bad group address specified: %s\n", group_str); + return CMD_WARNING_CONFIG_FAILED; + } + + result = inet_pton(PIM_AF, rp_str, &rp_addr); + if (result <= 0) { + vty_out(vty, "%% Bad RP address specified: %s\n", rp_str); + return CMD_WARNING_CONFIG_FAILED; + } + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(rp_group_xpath, sizeof(rp_group_xpath), + FRR_PIM_STATIC_RP_XPATH, "frr-pim:pimd", "pim", vrfname, + FRR_PIM_AF_XPATH_VAL, rp_str); + strlcat(rp_group_xpath, "/group-list", sizeof(rp_group_xpath)); + + nb_cli_enqueue_change(vty, rp_group_xpath, NB_OP_CREATE, group_str); + + return nb_cli_apply_changes(vty, NULL); +} + +int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str, + const char *group_str) +{ + char group_list_xpath[XPATH_MAXLEN]; + char group_xpath[XPATH_MAXLEN]; + char rp_xpath[XPATH_MAXLEN]; + int printed; + const char *vrfname; + const struct lyd_node *group_dnode; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH, + "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str); + + printed = snprintf(group_list_xpath, sizeof(group_list_xpath), + "%s/group-list", rp_xpath); + + if (printed >= (int)(sizeof(group_list_xpath))) { + vty_out(vty, "Xpath too long (%d > %u)", printed + 1, + XPATH_MAXLEN); + return CMD_WARNING_CONFIG_FAILED; + } + + printed = snprintf(group_xpath, sizeof(group_xpath), "%s[.='%s']", + group_list_xpath, group_str); + + if (printed >= (int)(sizeof(group_xpath))) { + vty_out(vty, "Xpath too long (%d > %u)", printed + 1, + XPATH_MAXLEN); + return CMD_WARNING_CONFIG_FAILED; + } + + group_dnode = yang_dnode_get(vty->candidate_config->dnode, group_xpath); + if (!group_dnode) { + vty_out(vty, "%% Unable to find specified RP\n"); + return NB_OK; + } + + if (yang_is_last_list_dnode(group_dnode)) + nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL); + else + nb_cli_enqueue_change(vty, group_list_xpath, NB_OP_DESTROY, + group_str); + + return nb_cli_apply_changes(vty, NULL); +} + +int pim_process_rp_plist_cmd(struct vty *vty, const char *rp_str, + const char *prefix_list) +{ + const char *vrfname; + char rp_plist_xpath[XPATH_MAXLEN]; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(rp_plist_xpath, sizeof(rp_plist_xpath), + FRR_PIM_STATIC_RP_XPATH, "frr-pim:pimd", "pim", vrfname, + FRR_PIM_AF_XPATH_VAL, rp_str); + strlcat(rp_plist_xpath, "/prefix-list", sizeof(rp_plist_xpath)); + + nb_cli_enqueue_change(vty, rp_plist_xpath, NB_OP_MODIFY, prefix_list); + + return nb_cli_apply_changes(vty, NULL); +} + +int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str, + const char *prefix_list) +{ + char rp_xpath[XPATH_MAXLEN]; + char plist_xpath[XPATH_MAXLEN]; + const char *vrfname; + const struct lyd_node *plist_dnode; + const char *plist; + + vrfname = pim_cli_get_vrf_name(vty); + if (vrfname == NULL) + return CMD_WARNING_CONFIG_FAILED; + + snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH, + "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str); + + snprintf(plist_xpath, sizeof(plist_xpath), FRR_PIM_STATIC_RP_XPATH, + "frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str); + strlcat(plist_xpath, "/prefix-list", sizeof(plist_xpath)); + + plist_dnode = yang_dnode_get(vty->candidate_config->dnode, plist_xpath); + if (!plist_dnode) { + vty_out(vty, "%% Unable to find specified RP\n"); + return NB_OK; + } + + plist = yang_dnode_get_string(plist_dnode, plist_xpath); + if (strcmp(prefix_list, plist)) { + vty_out(vty, "%% Unable to find specified RP\n"); + return NB_OK; + } + + nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL); + + return nb_cli_apply_changes(vty, NULL); +} diff --git a/pimd/pim_cmd_common.h b/pimd/pim_cmd_common.h index 49fc6bcbeb..b7e6b6ac80 100644 --- a/pimd/pim_cmd_common.h +++ b/pimd/pim_cmd_common.h @@ -35,5 +35,28 @@ int pim_process_rp_kat_cmd(struct vty *vty, const char *rpkat); int pim_process_no_rp_kat_cmd(struct vty *vty); int pim_process_register_suppress_cmd(struct vty *vty, const char *rst); int pim_process_no_register_suppress_cmd(struct vty *vty); +int pim_process_rp_cmd(struct vty *vty, const char *rp_str, + const char *group_str); +int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str, + const char *group_str); +int pim_process_rp_plist_cmd(struct vty *vty, const char *rp_str, + const char *prefix_list); +int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str, + const char *prefix_list); + +int pim_process_ip_pim_cmd(struct vty *vty); +int pim_process_no_ip_pim_cmd(struct vty *vty); +int pim_process_ip_pim_drprio_cmd(struct vty *vty, const char *drpriority_str); +int pim_process_no_ip_pim_drprio_cmd(struct vty *vty); +int pim_process_ip_pim_hello_cmd(struct vty *vty, const char *hello_str, + const char *hold_str); +int pim_process_no_ip_pim_hello_cmd(struct vty *vty); +int pim_process_ip_pim_activeactive_cmd(struct vty *vty, const char *no); +int pim_process_ip_pim_boundary_oil_cmd(struct vty *vty, const char *oil); +int pim_process_no_ip_pim_boundary_oil_cmd(struct vty *vty); +int pim_process_ip_mroute_cmd(struct vty *vty, const char *interface, + const char *group_str, const char *source_str); +int pim_process_no_ip_mroute_cmd(struct vty *vty, const char *interface, + const char *group_str, const char *src_str); #endif /* PIM_CMD_COMMON_H */ diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 5425aec233..d1b4966ec9 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -127,7 +127,6 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, pim_ifp->pim = ifp->vrf->info; pim_ifp->mroute_vif_index = -1; -#if PIM_IPV == 4 pim_ifp->igmp_version = IGMP_DEFAULT_VERSION; pim_ifp->gm_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; @@ -153,10 +152,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, if (pim) PIM_IF_DO_PIM(pim_ifp->options); +#if PIM_IPV == 4 if (igmp) PIM_IF_DO_IGMP(pim_ifp->options); PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); +#endif pim_ifp->gm_join_list = NULL; pim_ifp->pim_neighbor_list = NULL; @@ -186,10 +187,11 @@ struct pim_interface *pim_if_new(struct interface *ifp, bool igmp, bool pim, ifp->info = pim_ifp; +#if PIM_IPV == 4 pim_sock_reset(ifp); +#endif pim_if_add_vif(ifp, ispimreg, is_vxlan_term); -#endif pim_ifp->pim->mcast_if_count++; return pim_ifp; @@ -208,9 +210,12 @@ void pim_if_delete(struct interface *ifp) if (pim_ifp->gm_join_list) { pim_if_igmp_join_del_all(ifp); } +#endif pim_ifchannel_delete_all(ifp); +#if PIM_IPV == 4 igmp_sock_delete_all(ifp); +#endif pim_neighbor_delete_all(ifp, "Interface removed from configuration"); @@ -224,7 +229,6 @@ void pim_if_delete(struct interface *ifp) XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist); XFREE(MTYPE_PIM_INTERFACE, pim_ifp); -#endif ifp->info = NULL; } @@ -512,6 +516,26 @@ void pim_if_addr_add(struct connected *ifc) CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY) ? "secondary" : "primary"); +#if PIM_IPV != 4 + if (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6) || + IN6_IS_ADDR_LOOPBACK(&ifc->address->u.prefix6)) { + if (IN6_IS_ADDR_UNSPECIFIED(&pim_ifp->ll_lowest)) + pim_ifp->ll_lowest = ifc->address->u.prefix6; + else if (IPV6_ADDR_CMP(&ifc->address->u.prefix6, + &pim_ifp->ll_lowest) < 0) + pim_ifp->ll_lowest = ifc->address->u.prefix6; + + if (IPV6_ADDR_CMP(&ifc->address->u.prefix6, + &pim_ifp->ll_highest) > 0) + pim_ifp->ll_highest = ifc->address->u.prefix6; + + if (PIM_DEBUG_ZEBRA) + zlog_debug( + "%s: new link-local %pI6, lowest now %pI6, highest %pI6", + ifc->ifp->name, &ifc->address->u.prefix6, + &pim_ifp->ll_lowest, &pim_ifp->ll_highest); + } +#endif detect_address_change(ifp, 0, __func__); @@ -711,6 +735,43 @@ void pim_if_addr_del(struct connected *ifc, int force_prim_as_any) ? "secondary" : "primary"); +#if PIM_IPV == 6 + struct pim_interface *pim_ifp = ifc->ifp->info; + + if (pim_ifp && + (!IPV6_ADDR_CMP(&ifc->address->u.prefix6, &pim_ifp->ll_lowest) || + !IPV6_ADDR_CMP(&ifc->address->u.prefix6, &pim_ifp->ll_highest))) { + struct listnode *cnode; + struct connected *cc; + + memset(&pim_ifp->ll_lowest, 0xff, sizeof(pim_ifp->ll_lowest)); + memset(&pim_ifp->ll_highest, 0, sizeof(pim_ifp->ll_highest)); + + for (ALL_LIST_ELEMENTS_RO(ifc->ifp->connected, cnode, cc)) { + if (!IN6_IS_ADDR_LINKLOCAL(&cc->address->u.prefix6) && + !IN6_IS_ADDR_LOOPBACK(&cc->address->u.prefix6)) + continue; + + if (IPV6_ADDR_CMP(&cc->address->u.prefix6, + &pim_ifp->ll_lowest) < 0) + pim_ifp->ll_lowest = cc->address->u.prefix6; + if (IPV6_ADDR_CMP(&cc->address->u.prefix6, + &pim_ifp->ll_highest) > 0) + pim_ifp->ll_highest = cc->address->u.prefix6; + } + + if (pim_ifp->ll_lowest.s6_addr[0] == 0xff) + memset(&pim_ifp->ll_lowest, 0, + sizeof(pim_ifp->ll_lowest)); + + if (PIM_DEBUG_ZEBRA) + zlog_debug( + "%s: removed link-local %pI6, lowest now %pI6, highest %pI6", + ifc->ifp->name, &ifc->address->u.prefix6, + &pim_ifp->ll_lowest, &pim_ifp->ll_highest); + } +#endif + detect_address_change(ifp, force_prim_as_any, __func__); pim_if_addr_del_igmp(ifc); @@ -825,17 +886,36 @@ pim_addr pim_find_primary_addr(struct interface *ifp) { struct connected *ifc; struct listnode *node; - int v4_addrs = 0; - int v6_addrs = 0; struct pim_interface *pim_ifp = ifp->info; - if (pim_ifp && !pim_addr_is_any(pim_ifp->update_source)) { + if (pim_ifp && !pim_addr_is_any(pim_ifp->update_source)) return pim_ifp->update_source; - } + +#if PIM_IPV == 6 + if (pim_ifp) + return pim_ifp->ll_highest; + + pim_addr best_addr = PIMADDR_ANY; for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { pim_addr addr; + if (ifc->address->family != AF_INET6) + continue; + + addr = pim_addr_from_prefix(ifc->address); + if (!IN6_IS_ADDR_LINKLOCAL(&addr)) + continue; + if (pim_addr_cmp(addr, best_addr) > 0) + best_addr = addr; + } + + return best_addr; +#else + int v4_addrs = 0; + int v6_addrs = 0; + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { switch (ifc->address->family) { case AF_INET: v4_addrs++; @@ -853,16 +933,9 @@ pim_addr pim_find_primary_addr(struct interface *ifp) if (ifc->address->family != PIM_AF) continue; - addr = pim_addr_from_prefix(ifc->address); - -#if PIM_IPV == 6 - if (!IN6_IS_ADDR_LINKLOCAL(&addr)) - continue; -#endif - return addr; + return pim_addr_from_prefix(ifc->address); } -#if PIM_IPV == 4 /* * If we have no v4_addrs and v6 is configured * We probably are using unnumbered @@ -882,8 +955,8 @@ pim_addr pim_find_primary_addr(struct interface *ifp) if (lo_ifp && (lo_ifp != ifp)) return pim_find_primary_addr(lo_ifp); } -#endif return PIMADDR_ANY; +#endif } static int pim_iface_next_vif_index(struct interface *ifp) @@ -1549,7 +1622,6 @@ static int pim_ifp_create(struct interface *ifp) */ if (pim_ifp) pim_ifp->pim = pim; -#if PIM_IPV == 4 pim_if_addr_add_all(ifp); /* @@ -1561,7 +1633,6 @@ static int pim_ifp_create(struct interface *ifp) * this is a no-op if it's already been done. */ pim_if_create_pimreg(pim); -#endif } #if PIM_IPV == 4 @@ -1599,6 +1670,7 @@ static int pim_ifp_create(struct interface *ifp) static int pim_ifp_up(struct interface *ifp) { + uint32_t table_id; struct pim_interface *pim_ifp; struct pim_instance *pim; @@ -1621,9 +1693,6 @@ static int pim_ifp_up(struct interface *ifp) if (pim_ifp) pim_ifp->pim = pim; -#if PIM_IPV == 4 - uint32_t table_id; - /* pim_if_addr_add_all() suffices for bringing up both IGMP and PIM @@ -1652,7 +1721,6 @@ static int pim_ifp_up(struct interface *ifp) } } } -#endif return 0; } @@ -1666,7 +1734,6 @@ static int pim_ifp_down(struct interface *ifp) ifp->mtu, if_is_operative(ifp)); } -#if PIM_IPV == 4 if (!if_is_operative(ifp)) { pim_ifchannel_delete_all(ifp); /* @@ -1675,6 +1742,7 @@ static int pim_ifp_down(struct interface *ifp) */ pim_if_addr_del_all(ifp); +#if PIM_IPV == 4 /* pim_sock_delete() closes the socket, stops read and timer threads, @@ -1683,13 +1751,15 @@ static int pim_ifp_down(struct interface *ifp) if (ifp->info) { pim_sock_delete(ifp, "link down"); } +#endif } if (ifp->info) { pim_if_del_vif(ifp); +#if PIM_IPV == 4 pim_ifstat_reset(ifp); - } #endif + } return 0; } @@ -1704,12 +1774,12 @@ static int pim_ifp_destroy(struct interface *ifp) ifp->mtu, if_is_operative(ifp)); } -#if PIM_IPV == 4 - struct pim_instance *pim; - if (!if_is_operative(ifp)) pim_if_addr_del_all(ifp); +#if PIM_IPV == 4 + struct pim_instance *pim; + pim = ifp->vrf->info; if (pim && pim->vxlan.term_if == ifp) pim_vxlan_del_term_dev(pim); diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h index 00ec8e7427..a1c946149d 100644 --- a/pimd/pim_iface.h +++ b/pimd/pim_iface.h @@ -96,6 +96,13 @@ struct pim_interface { uint32_t options; /* bit vector */ ifindex_t mroute_vif_index; struct pim_instance *pim; + +#if PIM_IPV == 6 + /* link-locals: MLD uses lowest addr, PIM uses highest... */ + pim_addr ll_lowest; + pim_addr ll_highest; +#endif + pim_addr primary_address; /* remember addr to detect change */ struct list *sec_addr_list; /* list of struct pim_secondary_addr */ pim_addr update_source; /* user can statically set the primary diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 956ab0d67c..f9fb8cf094 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -872,7 +872,7 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, address of the join message is our primary address. */ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { - zlog_warn("%s: Assert Loser recv Join%s from %pI4 on %s", + zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s", __func__, ch->sg_str, &neigh_addr, ifp->name); assert_action_a5(ch); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 5cdefd2828..592cd7ec45 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -37,11 +37,180 @@ #include "pim_str.h" #include "pim_util.h" #include "pim_time.h" -#include "pim_zebra.h" +#include "pim_ssm.h" +#include "pim_tib.h" static void group_timer_off(struct gm_group *group); static void pim_igmp_general_query(struct thread *t); +void igmp_anysource_forward_start(struct pim_instance *pim, + struct gm_group *group) +{ + struct gm_source *source; + struct in_addr src_addr = {.s_addr = 0}; + /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ + assert(group->group_filtermode_isexcl); + assert(listcount(group->group_source_list) < 1); + + source = igmp_get_source_by_addr(group, src_addr, NULL); + if (!source) { + zlog_warn("%s: Failure to create * source", __func__); + return; + } + + igmp_source_forward_start(pim, source); +} + +void igmp_anysource_forward_stop(struct gm_group *group) +{ + struct gm_source *source; + struct in_addr star = {.s_addr = 0}; + + source = igmp_find_source_by_addr(group, star); + if (source) + igmp_source_forward_stop(source); +} + +static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, + struct gm_source *source) +{ + pim_sgaddr sg; + struct gm_group *group = source->source_group; + struct pim_ifchannel *ch; + + if ((source->source_addr.s_addr != INADDR_ANY) || + !IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) + return; + + memset(&sg, 0, sizeof(sg)); + sg.src = source->source_addr; + sg.grp = group->group_addr; + + ch = pim_ifchannel_find(group->interface, &sg); + if (pim_is_grp_ssm(pim, group->group_addr)) { + /* If SSM group withdraw local membership */ + if (ch && + (ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE)) { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug( + "local membership del for %pSG as G is now SSM", + &sg); + pim_ifchannel_local_membership_del(group->interface, + &sg); + } + } else { + /* If ASM group add local membership */ + if (!ch || + (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)) { + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug( + "local membership add for %pSG as G is now ASM", + &sg); + pim_ifchannel_local_membership_add( + group->interface, &sg, false /*is_vxlan*/); + } + } +} + +void igmp_source_forward_reevaluate_all(struct pim_instance *pim) +{ + struct interface *ifp; + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp = ifp->info; + struct listnode *grpnode; + struct gm_group *grp; + struct pim_ifchannel *ch, *ch_temp; + + if (!pim_ifp) + continue; + + /* scan igmp groups */ + for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, + grp)) { + struct listnode *srcnode; + struct gm_source *src; + + /* scan group sources */ + for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, + srcnode, src)) { + igmp_source_forward_reevaluate_one(pim, src); + } /* scan group sources */ + } /* scan igmp groups */ + + RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, + ch_temp) { + if (pim_is_grp_ssm(pim, ch->sg.grp)) { + if (pim_addr_is_any(ch->sg.src)) + pim_ifchannel_delete(ch); + } + } + } /* scan interfaces */ +} + +void igmp_source_forward_start(struct pim_instance *pim, + struct gm_source *source) +{ + struct gm_group *group; + pim_sgaddr sg; + + memset(&sg, 0, sizeof(sg)); + sg.src = source->source_addr; + sg.grp = source->source_group->group_addr; + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg, + source->source_group->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + } + + /* Prevent IGMP interface from installing multicast route multiple + times */ + if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + return; + } + + group = source->source_group; + + if (tib_sg_gm_join(pim, sg, group->interface, + &source->source_channel_oil)) + IGMP_SOURCE_DO_FORWARDING(source->source_flags); +} + +/* + igmp_source_forward_stop: stop fowarding, but keep the source + igmp_source_delete: stop fowarding, and delete the source + */ +void igmp_source_forward_stop(struct gm_source *source) +{ + struct pim_interface *pim_oif; + struct gm_group *group; + pim_sgaddr sg; + + memset(&sg, 0, sizeof(sg)); + sg.src = source->source_addr; + sg.grp = source->source_group->group_addr; + + if (PIM_DEBUG_IGMP_TRACE) { + zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg, + source->source_group->interface->name, + IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); + } + + /* Prevent IGMP interface from removing multicast route multiple + times */ + if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { + return; + } + + group = source->source_group; + pim_oif = group->interface->info; + + tib_sg_gm_prune(pim_oif->pim, sg, group->interface, + &source->source_channel_oil); + IGMP_SOURCE_DONT_FORWARDING(source->source_flags); +} + /* This socket is used for TXing IGMP packets only, IGMP RX happens * in pim_mroute_msg() */ @@ -1016,8 +1185,8 @@ static void pim_igmp_read(struct thread *t) { uint8_t buf[10000]; struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t); - struct sockaddr_in from; - struct sockaddr_in to; + struct sockaddr_storage from; + struct sockaddr_storage to; socklen_t fromlen = sizeof(from); socklen_t tolen = sizeof(to); ifindex_t ifindex = -1; diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 5c35996634..4d7229dcce 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -128,6 +128,15 @@ void pim_igmp_other_querier_timer_off(struct gm_sock *igmp); int igmp_validate_checksum(char *igmp_msg, int igmp_msg_len); #else /* PIM_IPV != 4 */ +static inline void pim_igmp_if_init(struct pim_interface *pim_ifp, + struct interface *ifp) +{ +} + +static inline void pim_igmp_if_fini(struct pim_interface *pim_ifp) +{ +} + static inline void pim_igmp_general_query_on(struct gm_sock *igmp) { } @@ -204,6 +213,17 @@ struct gm_group { }; #if PIM_IPV == 4 +struct pim_instance; + +void igmp_anysource_forward_start(struct pim_instance *pim, + struct gm_group *group); +void igmp_anysource_forward_stop(struct gm_group *group); + +void igmp_source_forward_start(struct pim_instance *pim, + struct gm_source *source); +void igmp_source_forward_stop(struct gm_source *source); +void igmp_source_forward_reevaluate_all(struct pim_instance *pim); + struct gm_group *find_group_by_addr(struct gm_sock *igmp, struct in_addr group_addr); struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp, diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c index 40851a4529..0cf1bb1ec1 100644 --- a/pimd/pim_igmp_stats.c +++ b/pimd/pim_igmp_stats.c @@ -43,4 +43,6 @@ void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b) a->mtrace_rsp += b->mtrace_rsp; a->mtrace_req += b->mtrace_req; a->unsupported += b->unsupported; + a->total_groups += b->total_groups; + a->total_source_groups += b->total_source_groups; } diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h index a70a433557..47167efb3a 100644 --- a/pimd/pim_igmp_stats.h +++ b/pimd/pim_igmp_stats.h @@ -33,6 +33,8 @@ struct igmp_stats { uint32_t mtrace_rsp; uint32_t mtrace_req; uint32_t unsupported; + uint32_t total_groups; + uint32_t total_source_groups; }; #if PIM_IPV == 4 diff --git a/pimd/pim_msdp_packet.c b/pimd/pim_msdp_packet.c index 03284ffa56..4adaca4e78 100644 --- a/pimd/pim_msdp_packet.c +++ b/pimd/pim_msdp_packet.c @@ -214,8 +214,6 @@ void pim_msdp_write(struct thread *thread) return; } - sockopt_cork(mp->fd, 1); - /* Nonblocking write until TCP output buffer is full */ do { int writenum; @@ -280,8 +278,6 @@ void pim_msdp_write(struct thread *thread) } while ((s = stream_fifo_head(mp->obuf)) != NULL); pim_msdp_write_proceed_actions(mp); - sockopt_cork(mp->fd, 0); - if (PIM_DEBUG_MSDP_INTERNAL) { zlog_debug("MSDP peer %s pim_msdp_write wrote %d packets", mp->key_str, work_cnt); diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index 522e94504a..456c356d9f 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -21,6 +21,9 @@ #define PIM_MSG_H #include <netinet/in.h> +#if PIM_IPV == 6 +#include <netinet/ip6.h> +#endif #include "pim_jp_agg.h" @@ -181,6 +184,30 @@ struct pim_jp { struct pim_jp_groups groups[1]; } __attribute__((packed)); +#if PIM_IPV == 4 +static inline pim_sgaddr pim_sgaddr_from_iphdr(const void *iphdr) +{ + const struct ip *ipv4_hdr = iphdr; + pim_sgaddr sg; + + sg.src = ipv4_hdr->ip_src; + sg.grp = ipv4_hdr->ip_dst; + + return sg; +} +#else +static inline pim_sgaddr pim_sgaddr_from_iphdr(const void *iphdr) +{ + const struct ip6_hdr *ipv6_hdr = iphdr; + pim_sgaddr sg; + + sg.src = ipv6_hdr->ip6_src; + sg.grp = ipv6_hdr->ip6_dst; + + return sg; +} +#endif + void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, uint8_t pim_msg_type, bool no_fwd); uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, struct in_addr addr); diff --git a/pimd/pim_nb.h b/pimd/pim_nb.h index 72c96d7d73..273c7e8a61 100644 --- a/pimd/pim_nb.h +++ b/pimd/pim_nb.h @@ -198,6 +198,12 @@ int lib_interface_gmp_address_family_static_group_destroy( int routing_control_plane_protocols_name_validate( struct nb_cb_create_args *args); +#if PIM_IPV == 4 +#define FRR_PIM_AF_XPATH_VAL "frr-routing:ipv4" +#else +#define FRR_PIM_AF_XPATH_VAL "frr-routing:ipv6" +#endif + #define FRR_PIM_VRF_XPATH \ "/frr-routing:routing/control-plane-protocols/" \ "control-plane-protocol[type='%s'][name='%s'][vrf='%s']/" \ diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index 27cac0c1a7..21f57e2d11 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -34,6 +34,7 @@ #include "pim_util.h" #include "log.h" #include "lib_errors.h" +#include "pim_util.h" #if PIM_IPV == 6 #define pim6_msdp_err(funcname, argtype) \ @@ -256,21 +257,17 @@ static int pim_ssm_cmd_worker(struct pim_instance *pim, const char *plist, return ret; } -static int pim_rp_cmd_worker(struct pim_instance *pim, - struct in_addr rp_addr, - struct prefix group, const char *plist, - char *errmsg, size_t errmsg_len) +static int pim_rp_cmd_worker(struct pim_instance *pim, pim_addr rp_addr, + struct prefix group, const char *plist, + char *errmsg, size_t errmsg_len) { - char rp_str[INET_ADDRSTRLEN]; int result; - inet_ntop(AF_INET, &rp_addr, rp_str, sizeof(rp_str)); - result = pim_rp_new(pim, rp_addr, group, plist, RP_SRC_STATIC); if (result == PIM_RP_NO_PATH) { - snprintf(errmsg, errmsg_len, - "No Path to RP address specified: %s", rp_str); + snprintfrr(errmsg, errmsg_len, + "No Path to RP address specified: %pPA", &rp_addr); return NB_ERR_INCONSISTENCY; } @@ -295,16 +292,13 @@ static int pim_rp_cmd_worker(struct pim_instance *pim, return NB_OK; } -static int pim_no_rp_cmd_worker(struct pim_instance *pim, - struct in_addr rp_addr, struct prefix group, - const char *plist, +static int pim_no_rp_cmd_worker(struct pim_instance *pim, pim_addr rp_addr, + struct prefix group, const char *plist, char *errmsg, size_t errmsg_len) { - char rp_str[INET_ADDRSTRLEN]; char group_str[PREFIX2STR_BUFFER]; int result; - inet_ntop(AF_INET, &rp_addr, rp_str, sizeof(rp_str)); prefix2str(&group, group_str, sizeof(group_str)); result = pim_rp_del(pim, rp_addr, group, plist, RP_SRC_STATIC); @@ -316,8 +310,8 @@ static int pim_no_rp_cmd_worker(struct pim_instance *pim, } if (result == PIM_RP_BAD_ADDRESS) { - snprintf(errmsg, errmsg_len, - "Bad RP address specified: %s", rp_str); + snprintfrr(errmsg, errmsg_len, "Bad RP address specified: %pPA", + &rp_addr); return NB_ERR_INCONSISTENCY; } @@ -2340,7 +2334,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp struct vrf *vrf; struct pim_instance *pim; struct prefix group; - struct ipaddr rp_addr; + pim_addr rp_addr; const char *plist; int result = 0; @@ -2352,31 +2346,30 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_ip(&rp_addr, args->dnode, "./rp-address"); + yang_dnode_get_pimaddr(&rp_addr, args->dnode, "./rp-address"); if (yang_dnode_get(args->dnode, "./group-list")) { - yang_dnode_get_ipv4p(&group, args->dnode, - "./group-list"); - apply_mask_ipv4((struct prefix_ipv4 *)&group); - result = pim_no_rp_cmd_worker(pim, rp_addr.ip._v4_addr, - group, NULL, args->errmsg, - args->errmsg_len); + yang_dnode_get_prefix(&group, args->dnode, + "./group-list"); + apply_mask(&group); + result = pim_no_rp_cmd_worker(pim, rp_addr, group, NULL, + args->errmsg, + args->errmsg_len); } else if (yang_dnode_get(args->dnode, "./prefix-list")) { plist = yang_dnode_get_string(args->dnode, "./prefix-list"); - if (!str2prefix("224.0.0.0/4", &group)) { + if (!pim_get_all_mcast_group(&group)) { flog_err( EC_LIB_DEVELOPMENT, "Unable to convert 224.0.0.0/4 to prefix"); return NB_ERR_INCONSISTENCY; } - result = pim_no_rp_cmd_worker(pim, rp_addr.ip._v4_addr, - group, plist, - args->errmsg, - args->errmsg_len); + result = pim_no_rp_cmd_worker(pim, rp_addr, group, + plist, args->errmsg, + args->errmsg_len); } if (result) @@ -2396,7 +2389,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp struct vrf *vrf; struct pim_instance *pim; struct prefix group; - struct ipaddr rp_addr; + pim_addr rp_addr; switch (args->event) { case NB_EV_VALIDATE: @@ -2406,12 +2399,11 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_ip(&rp_addr, args->dnode, "../rp-address"); - yang_dnode_get_ipv4p(&group, args->dnode, NULL); - apply_mask_ipv4((struct prefix_ipv4 *)&group); - - return pim_rp_cmd_worker(pim, rp_addr.ip._v4_addr, group, - NULL, args->errmsg, args->errmsg_len); + yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address"); + yang_dnode_get_prefix(&group, args->dnode, NULL); + apply_mask(&group); + return pim_rp_cmd_worker(pim, rp_addr, group, NULL, + args->errmsg, args->errmsg_len); } return NB_OK; @@ -2423,7 +2415,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp struct vrf *vrf; struct pim_instance *pim; struct prefix group; - struct ipaddr rp_addr; + pim_addr rp_addr; switch (args->event) { case NB_EV_VALIDATE: @@ -2433,13 +2425,12 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_ip(&rp_addr, args->dnode, "../rp-address"); - yang_dnode_get_ipv4p(&group, args->dnode, NULL); - apply_mask_ipv4((struct prefix_ipv4 *)&group); + yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address"); + yang_dnode_get_prefix(&group, args->dnode, NULL); + apply_mask(&group); - return pim_no_rp_cmd_worker(pim, rp_addr.ip._v4_addr, group, - NULL, args->errmsg, - args->errmsg_len); + return pim_no_rp_cmd_worker(pim, rp_addr, group, NULL, + args->errmsg, args->errmsg_len); } return NB_OK; @@ -2454,7 +2445,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp struct vrf *vrf; struct pim_instance *pim; struct prefix group; - struct ipaddr rp_addr; + pim_addr rp_addr; const char *plist; switch (args->event) { @@ -2466,14 +2457,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; plist = yang_dnode_get_string(args->dnode, NULL); - yang_dnode_get_ip(&rp_addr, args->dnode, "../rp-address"); - if (!str2prefix("224.0.0.0/4", &group)) { + yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address"); + if (!pim_get_all_mcast_group(&group)) { flog_err(EC_LIB_DEVELOPMENT, "Unable to convert 224.0.0.0/4 to prefix"); return NB_ERR_INCONSISTENCY; } - return pim_rp_cmd_worker(pim, rp_addr.ip._v4_addr, group, - plist, args->errmsg, args->errmsg_len); + return pim_rp_cmd_worker(pim, rp_addr, group, plist, + args->errmsg, args->errmsg_len); } return NB_OK; @@ -2485,7 +2476,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp struct vrf *vrf; struct pim_instance *pim; struct prefix group; - struct ipaddr rp_addr; + pim_addr rp_addr; const char *plist; switch (args->event) { @@ -2496,16 +2487,15 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_rp case NB_EV_APPLY: vrf = nb_running_get_entry(args->dnode, NULL, true); pim = vrf->info; - yang_dnode_get_ip(&rp_addr, args->dnode, "../rp-address"); + yang_dnode_get_pimaddr(&rp_addr, args->dnode, "../rp-address"); plist = yang_dnode_get_string(args->dnode, NULL); - if (!str2prefix("224.0.0.0/4", &group)) { + if (!pim_get_all_mcast_group(&group)) { flog_err(EC_LIB_DEVELOPMENT, "Unable to convert 224.0.0.0/4 to prefix"); return NB_ERR_INCONSISTENCY; } - return pim_no_rp_cmd_worker(pim, rp_addr.ip._v4_addr, group, - plist, args->errmsg, - args->errmsg_len); + return pim_no_rp_cmd_worker(pim, rp_addr, group, plist, + args->errmsg, args->errmsg_len); break; } diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 48dd565b25..4e7aad99f1 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -162,6 +162,7 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, return 0; } +#if PIM_IPV == 4 void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr) { struct pim_nexthop_cache *pnc; @@ -175,6 +176,7 @@ void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr) pnc->bsr_count++; } +#endif /* PIM_IPV == 4 */ static void pim_nht_drop_maybe(struct pim_instance *pim, struct pim_nexthop_cache *pnc) @@ -244,6 +246,7 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, pim_nht_drop_maybe(pim, pnc); } +#if PIM_IPV == 4 void pim_nht_bsr_del(struct pim_instance *pim, struct in_addr addr) { struct pim_nexthop_cache *pnc = NULL; @@ -398,6 +401,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr_addr, } return false; } +#endif /* PIM_IPV == 4 */ void pim_rp_nexthop_del(struct rp_info *rp_info) { @@ -482,23 +486,13 @@ static int pim_update_upstream_nh(struct pim_instance *pim, uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp) { uint32_t hash_val; - uint32_t s = 0, g = 0; - if ((!src)) + if (!src) return 0; - switch (src->family) { - case AF_INET: { - s = src->u.prefix4.s_addr; - s = s == 0 ? 1 : s; - if (grp) - g = grp->u.prefix4.s_addr; - } break; - default: - break; - } - - hash_val = jhash_2words(g, s, 101); + hash_val = prefix_hash_key(src); + if (grp) + hash_val ^= prefix_hash_key(grp); return hash_val; } @@ -549,9 +543,9 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, break; } - if (curr_route_valid - && !pim_if_connected_to_source(nexthop->interface, - src->u.prefix4)) { + if (curr_route_valid && + !pim_if_connected_to_source(nexthop->interface, + src_addr)) { nbr = pim_neighbor_find_prefix( nexthop->interface, &nexthop->mrib_nexthop_addr); @@ -668,7 +662,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, nh_node->gate.ipv4; #else nexthop->mrib_nexthop_addr.u.prefix6 = - nh_node->gate->ipv6; + nh_node->gate.ipv6; #endif nexthop->mrib_metric_preference = pnc->distance; nexthop->mrib_route_metric = pnc->metric; @@ -708,19 +702,20 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) struct vrf *vrf = vrf_lookup_by_id(vrf_id); struct pim_instance *pim; struct zapi_route nhr; + struct prefix match; if (!vrf) return 0; pim = vrf->info; - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &nhr)) { zlog_err("%s: Decode of nexthop update from zebra failed", __func__); return 0; } if (cmd == ZEBRA_NEXTHOP_UPDATE) { - prefix_copy(&rpf.rpf_addr, &nhr.prefix); + prefix_copy(&rpf.rpf_addr, &match); pnc = pim_nexthop_cache_find(pim, &rpf); if (!pnc) { if (PIM_DEBUG_PIM_NHT) @@ -812,9 +807,9 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: NHT addr %pFX(%s) %d-nhop via %pI4(%s) type %d distance:%u metric:%u ", - __func__, &nhr.prefix, pim->vrf->name, - i + 1, &nexthop->gate.ipv4, - ifp->name, nexthop->type, nhr.distance, + __func__, &match, pim->vrf->name, i + 1, + &nexthop->gate.ipv4, ifp->name, + nexthop->type, nhr.distance, nhr.metric); if (!ifp->info) { @@ -868,7 +863,7 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS) if (PIM_DEBUG_PIM_NHT) zlog_debug( "%s: NHT Update for %pFX(%s) num_nh %d num_pim_nh %d vrf:%u up %ld rp %d", - __func__, &nhr.prefix, pim->vrf->name, nhr.nexthop_num, + __func__, &match, pim->vrf->name, nhr.nexthop_num, pnc->nexthop_num, vrf_id, pnc->upstream_hash->count, listcount(pnc->rp_list)); diff --git a/pimd/pim_oil.c b/pimd/pim_oil.c index a499c884b4..d5e459b44e 100644 --- a/pimd/pim_oil.c +++ b/pimd/pim_oil.c @@ -216,6 +216,10 @@ int pim_channel_del_oif(struct channel_oil *channel_oil, struct interface *oif, pim_ifp = oif->info; + assertf(pim_ifp->mroute_vif_index >= 0, + "trying to del OIF %s with VIF (%d)", oif->name, + pim_ifp->mroute_vif_index); + /* * Don't do anything if we've been asked to remove a source * that is not actually on it. @@ -418,6 +422,10 @@ int pim_channel_add_oif(struct channel_oil *channel_oil, struct interface *oif, pim_ifp = oif->info; + assertf(pim_ifp->mroute_vif_index >= 0, + "trying to add OIF %s with VIF (%d)", oif->name, + pim_ifp->mroute_vif_index); + /* Prevent single protocol from subscribing same interface to channel (S,G) multiple times */ if (channel_oil->oif_flags[pim_ifp->mroute_vif_index] & proto_mask) { diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c index 5cc0d63e31..d3edc5d0fe 100644 --- a/pimd/pim_pim.c +++ b/pimd/pim_pim.c @@ -331,8 +331,8 @@ static void pim_sock_read(struct thread *t) struct interface *ifp, *orig_ifp; struct pim_interface *pim_ifp; int fd; - struct sockaddr_in from; - struct sockaddr_in to; + struct sockaddr_storage from; + struct sockaddr_storage to; socklen_t fromlen = sizeof(from); socklen_t tolen = sizeof(to); uint8_t buf[PIM_PIM_BUFSIZE_READ]; diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 2cc80f957c..8313c8d4f6 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -311,30 +311,26 @@ void pim_null_register_send(struct pim_upstream *up) * } * } */ -int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, - struct in_addr src_addr, uint8_t *tlv_buf, - int tlv_buf_size) +int pim_register_recv(struct interface *ifp, pim_addr dest_addr, + pim_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size) { int sentRegisterStop = 0; - struct ip *ip_hdr; + const void *ip_hdr; pim_sgaddr sg; uint32_t *bits; int i_am_rp = 0; struct pim_interface *pim_ifp = ifp->info; struct pim_instance *pim = pim_ifp->pim; + pim_addr rp_addr; #define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4 - ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); + ip_hdr = (tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); - if (!if_address_is_local(&dest_addr, AF_INET, pim->vrf->vrf_id)) { - if (PIM_DEBUG_PIM_REG) { - char dest[INET_ADDRSTRLEN]; - - pim_inet4_dump("<dst?>", dest_addr, dest, sizeof(dest)); + if (!if_address_is_local(&dest_addr, PIM_AF, pim->vrf->vrf_id)) { + if (PIM_DEBUG_PIM_REG) zlog_debug( - "%s: Received Register message for destination address: %s that I do not own", - __func__, dest); - } + "%s: Received Register message for destination address: %pPA that I do not own", + __func__, &dest_addr); return 0; } @@ -367,18 +363,14 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, * start of the actual Encapsulated data. */ memset(&sg, 0, sizeof(sg)); - sg.src = ip_hdr->ip_src; - sg.grp = ip_hdr->ip_dst; + sg = pim_sgaddr_from_iphdr(ip_hdr); i_am_rp = I_am_RP(pim, sg.grp); - if (PIM_DEBUG_PIM_REG) { - char src_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); - zlog_debug("Received Register message%pSG from %s on %s, rp: %d", - &sg, src_str, ifp->name, i_am_rp); - } + if (PIM_DEBUG_PIM_REG) + zlog_debug( + "Received Register message%pSG from %pPA on %s, rp: %d", + &sg, &src_addr, ifp->name, i_am_rp); if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { if (pim_addr_is_any(sg.src)) { @@ -390,9 +382,8 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, } } - if (i_am_rp - && (dest_addr.s_addr - == ((RP(pim, sg.grp))->rpf_addr.u.prefix4.s_addr))) { + rp_addr = pim_addr_from_prefix(&(RP(pim, sg.grp))->rpf_addr); + if (i_am_rp && (!pim_addr_cmp(dest_addr, rp_addr))) { sentRegisterStop = 0; if (pim->register_plist) { @@ -407,31 +398,25 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, if (prefix_list_apply(plist, &src) == PREFIX_DENY) { pim_register_stop_send(ifp, &sg, dest_addr, src_addr); - if (PIM_DEBUG_PIM_PACKETS) { - char src_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<src?>", src_addr, - src_str, - sizeof(src_str)); + if (PIM_DEBUG_PIM_PACKETS) zlog_debug( - "%s: Sending register-stop to %s for %pSG due to prefix-list denial, dropping packet", - __func__, src_str, &sg); - } + "%s: Sending register-stop to %pPA for %pSG due to prefix-list denial, dropping packet", + __func__, &src_addr, &sg); return 0; } } if (*bits & PIM_REGISTER_BORDER_BIT) { - struct in_addr pimbr = pim_br_get_pmbr(&sg); + pim_addr pimbr = pim_br_get_pmbr(&sg); if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s: Received Register message with Border bit set", __func__); - if (pimbr.s_addr == pim_br_unknown.s_addr) + if (pim_addr_is_any(pimbr)) pim_br_set_pmbr(&sg, src_addr); - else if (src_addr.s_addr != pimbr.s_addr) { + else if (pim_addr_cmp(src_addr, pimbr)) { pim_register_stop_send(ifp, &sg, dest_addr, src_addr); if (PIM_DEBUG_PIM_PACKETS) diff --git a/pimd/pim_register.h b/pimd/pim_register.h index fd4284b802..0ebef40c58 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -32,9 +32,8 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size); -int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, - struct in_addr src_addr, uint8_t *tlv_buf, - int tlv_buf_size); +int pim_register_recv(struct interface *ifp, pim_addr dest_addr, + pim_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size); void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 06b2216072..99727cf837 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -48,6 +48,8 @@ #include "pim_oil.h" #include "pim_zebra.h" #include "pim_bsm.h" +#include "pim_util.h" +#include "pim_ssm.h" /* Cleanup pim->rpf_hash each node data */ void pim_rp_list_hash_clean(void *data) @@ -76,26 +78,21 @@ int pim_rp_list_cmp(void *v1, void *v2) { struct rp_info *rp1 = (struct rp_info *)v1; struct rp_info *rp2 = (struct rp_info *)v2; + int ret; /* * Sort by RP IP address */ - if (rp1->rp.rpf_addr.u.prefix4.s_addr - < rp2->rp.rpf_addr.u.prefix4.s_addr) - return -1; - - if (rp1->rp.rpf_addr.u.prefix4.s_addr - > rp2->rp.rpf_addr.u.prefix4.s_addr) - return 1; + ret = prefix_cmp(&rp1->rp.rpf_addr, &rp2->rp.rpf_addr); + if (ret) + return ret; /* * Sort by group IP address */ - if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr) - return -1; - - if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr) - return 1; + ret = prefix_cmp(&rp1->group, &rp2->group); + if (ret) + return ret; return 0; } @@ -113,15 +110,14 @@ void pim_rp_init(struct pim_instance *pim) rp_info = XCALLOC(MTYPE_PIM_RP, sizeof(*rp_info)); - if (!str2prefix("224.0.0.0/4", &rp_info->group)) { + if (!pim_get_all_mcast_group(&rp_info->group)) { flog_err(EC_LIB_DEVELOPMENT, - "Unable to convert 224.0.0.0/4 to prefix"); + "Unable to convert all-multicast prefix"); list_delete(&pim->rp_list); route_table_finish(pim->rp_table); XFREE(MTYPE_PIM_RP, rp_info); return; } - rp_info->group.family = AF_INET; pim_addr_to_prefix(&rp_info->rp.rpf_addr, PIMADDR_ANY); listnode_add(pim->rp_list, rp_info); @@ -129,9 +125,9 @@ void pim_rp_init(struct pim_instance *pim) rn = route_node_get(pim->rp_table, &rp_info->group); rn->info = rp_info; if (PIM_DEBUG_PIM_TRACE) - zlog_debug( - "Allocated: %p for rp_info: %p(224.0.0.0/4) Lock: %d", - rn, rp_info, route_node_get_lock_count(rn)); + zlog_debug("Allocated: %p for rp_info: %p(%pFX) Lock: %d", rn, + rp_info, &rp_info->group, + route_node_get_lock_count(rn)); } void pim_rp_free(struct pim_instance *pim) @@ -148,15 +144,17 @@ void pim_rp_free(struct pim_instance *pim) * Given an RP's prefix-list, return the RP's rp_info for that prefix-list */ static struct rp_info *pim_rp_find_prefix_list(struct pim_instance *pim, - struct in_addr rp, - const char *plist) + pim_addr rp, const char *plist) { struct listnode *node; struct rp_info *rp_info; + struct prefix rp_prefix; + + pim_addr_to_prefix(&rp_prefix, rp); for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { - if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr - && rp_info->plist && strcmp(rp_info->plist, plist) == 0) { + if (prefix_same(&rp_prefix, &rp_info->rp.rpf_addr) && + rp_info->plist && strcmp(rp_info->plist, plist) == 0) { return rp_info; } } @@ -185,16 +183,17 @@ static int pim_rp_prefix_list_used(struct pim_instance *pim, const char *plist) * Given an RP's address, return the RP's rp_info that is an exact match for * 'group' */ -static struct rp_info *pim_rp_find_exact(struct pim_instance *pim, - struct in_addr rp, +static struct rp_info *pim_rp_find_exact(struct pim_instance *pim, pim_addr rp, const struct prefix *group) { struct listnode *node; struct rp_info *rp_info; + struct prefix rp_prefix; + pim_addr_to_prefix(&rp_prefix, rp); for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { - if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr - && prefix_same(&rp_info->group, group)) + if (prefix_same(&rp_prefix, &rp_info->rp.rpf_addr) && + prefix_same(&rp_info->group, group)) return rp_info; } @@ -238,7 +237,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, bp = NULL; for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { if (rp_info->plist) { - plist = prefix_list_lookup(AFI_IP, rp_info->plist); + plist = prefix_list_lookup(PIM_AFI, rp_info->plist); if (prefix_list_apply_ext(plist, &entry, group, true) == PREFIX_DENY || !entry) @@ -371,7 +370,7 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) up->sg.grp); if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: pim upstream update for old upstream %pI4", + zlog_debug("%s: pim upstream update for old upstream %pPA", __func__, &old_upstream_addr); if (!pim_addr_cmp(old_upstream_addr, new_upstream_addr)) @@ -412,11 +411,10 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) } -int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix group, +int pim_rp_new(struct pim_instance *pim, pim_addr rp_addr, struct prefix group, const char *plist, enum rp_source rp_src_flag) { int result = 0; - char rp[INET_ADDRSTRLEN]; struct rp_info *rp_info; struct rp_info *rp_all; struct prefix group_all; @@ -428,25 +426,20 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g struct pim_upstream *up; bool upstream_updated = false; - if (rp_addr.s_addr == INADDR_ANY) + if (pim_addr_is_any(rp_addr)) return PIM_RP_BAD_ADDRESS; rp_info = XCALLOC(MTYPE_PIM_RP, sizeof(*rp_info)); - rp_info->rp.rpf_addr.family = AF_INET; - rp_info->rp.rpf_addr.prefixlen = IPV4_MAX_BITLEN; - rp_info->rp.rpf_addr.u.prefix4 = rp_addr; + pim_addr_to_prefix(&rp_info->rp.rpf_addr, rp_addr); prefix_copy(&rp_info->group, &group); rp_info->rp_src = rp_src_flag; - inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp, sizeof(rp)); - if (plist) { /* * Return if the prefix-list is already configured for this RP */ - if (pim_rp_find_prefix_list(pim, rp_info->rp.rpf_addr.u.prefix4, - plist)) { + if (pim_rp_find_prefix_list(pim, rp_addr, plist)) { XFREE(MTYPE_PIM_RP, rp_info); return PIM_SUCCESS; } @@ -464,14 +457,14 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g */ for (ALL_LIST_ELEMENTS(pim->rp_list, node, nnode, tmp_rp_info)) { - if (rp_info->rp.rpf_addr.u.prefix4.s_addr - == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) { + if (prefix_same(&rp_info->rp.rpf_addr, + &tmp_rp_info->rp.rpf_addr)) { if (tmp_rp_info->plist) - pim_rp_del_config(pim, rp, NULL, + pim_rp_del_config(pim, rp_addr, NULL, tmp_rp_info->plist); else pim_rp_del_config( - pim, rp, + pim, rp_addr, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), NULL); @@ -481,7 +474,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g rp_info->plist = XSTRDUP(MTYPE_PIM_FILTER_NAME, plist); } else { - if (!str2prefix("224.0.0.0/4", &group_all)) { + if (!pim_get_all_mcast_group(&group_all)) { XFREE(MTYPE_PIM_RP, rp_info); return PIM_GROUP_BAD_ADDRESS; } @@ -500,11 +493,10 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g */ for (ALL_LIST_ELEMENTS(pim->rp_list, node, nnode, tmp_rp_info)) { - if (tmp_rp_info->plist - && rp_info->rp.rpf_addr.u.prefix4.s_addr - == tmp_rp_info->rp.rpf_addr.u.prefix4 - .s_addr) { - pim_rp_del_config(pim, rp, NULL, + if (tmp_rp_info->plist && + prefix_same(&rp_info->rp.rpf_addr, + &tmp_rp_info->rp.rpf_addr)) { + pim_rp_del_config(pim, rp_addr, NULL, tmp_rp_info->plist); } } @@ -519,10 +511,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g XFREE(MTYPE_PIM_RP, rp_info); /* Register addr with Zebra NHT */ - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = - rp_all->rp.rpf_addr.u.prefix4; // RP address + nht_p = rp_all->rp.rpf_addr; if (PIM_DEBUG_PIM_NHT_RP) zlog_debug( "%s: NHT Register rp_all addr %pFX grp %pFX ", @@ -564,8 +553,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g /* * Return if the group is already configured for this RP */ - tmp_rp_info = pim_rp_find_exact( - pim, rp_info->rp.rpf_addr.u.prefix4, &rp_info->group); + tmp_rp_info = pim_rp_find_exact(pim, rp_addr, &rp_info->group); if (tmp_rp_info) { if ((tmp_rp_info->rp_src != rp_src_flag) && (rp_src_flag == RP_SRC_STATIC)) @@ -601,8 +589,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g } result = pim_rp_change( - pim, - rp_info->rp.rpf_addr.u.prefix4, + pim, rp_addr, tmp_rp_info->group, rp_src_flag); XFREE(MTYPE_PIM_RP, rp_info); @@ -643,9 +630,7 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g pim_rp_refresh_group_to_rp_mapping(pim); /* Register addr with Zebra NHT */ - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; if (PIM_DEBUG_PIM_NHT_RP) zlog_debug("%s: NHT Register RP addr %pFX grp %pFX with Zebra ", __func__, &nht_p, &rp_info->group); @@ -657,32 +642,30 @@ int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, struct prefix g return PIM_SUCCESS; } -int pim_rp_del_config(struct pim_instance *pim, const char *rp, - const char *group_range, const char *plist) +void pim_rp_del_config(struct pim_instance *pim, pim_addr rp_addr, + const char *group_range, const char *plist) { struct prefix group; - struct in_addr rp_addr; int result; if (group_range == NULL) - result = str2prefix("224.0.0.0/4", &group); + result = pim_get_all_mcast_group(&group); else result = str2prefix(group_range, &group); - if (!result) - return PIM_GROUP_BAD_ADDRESS; - - result = inet_pton(AF_INET, rp, &rp_addr); - if (result <= 0) - return PIM_RP_BAD_ADDRESS; + if (!result) { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug( + "%s: String to prefix failed for %pPAs group", + __func__, &rp_addr); + return; + } - result = pim_rp_del(pim, rp_addr, group, plist, RP_SRC_STATIC); - return result; + pim_rp_del(pim, rp_addr, group, plist, RP_SRC_STATIC); } -int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, - struct prefix group, const char *plist, - enum rp_source rp_src_flag) +int pim_rp_del(struct pim_instance *pim, pim_addr rp_addr, struct prefix group, + const char *plist, enum rp_source rp_src_flag) { struct prefix g_all; struct rp_info *rp_info; @@ -694,12 +677,8 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, struct pim_upstream *up; struct bsgrp_node *bsgrp = NULL; struct bsm_rpinfo *bsrp = NULL; - char rp_str[INET_ADDRSTRLEN]; bool upstream_updated = false; - if (!inet_ntop(AF_INET, &rp_addr, rp_str, sizeof(rp_str))) - snprintf(rp_str, sizeof(rp_str), "<rp?>"); - if (plist) rp_info = pim_rp_find_prefix_list(pim, rp_addr, plist); else @@ -714,8 +693,8 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, } if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: Delete RP %s for the group %pFX", __func__, - rp_str, &group); + zlog_debug("%s: Delete RP %pPA for the group %pFX", __func__, + &rp_addr, &group); /* While static RP is getting deleted, we need to check if dynamic RP * present for the same group in BSM RP table, then install the dynamic @@ -727,19 +706,11 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, if (bsgrp) { bsrp = bsm_rpinfos_first(bsgrp->bsrp_list); if (bsrp) { - if (PIM_DEBUG_PIM_TRACE) { - char bsrp_str[INET_ADDRSTRLEN]; - - if (!inet_ntop(AF_INET, bsrp, bsrp_str, - sizeof(bsrp_str))) - snprintf(bsrp_str, - sizeof(bsrp_str), - "<bsrp?>"); - + if (PIM_DEBUG_PIM_TRACE) zlog_debug( - "%s: BSM RP %s found for the group %pFX", - __func__, bsrp_str, &group); - } + "%s: BSM RP %pPA found for the group %pFX", + __func__, &bsrp->rp_address, + &group); return pim_rp_change(pim, bsrp->rp_address, group, RP_SRC_BSR); } @@ -752,15 +723,13 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, } /* Deregister addr with Zebra NHT */ - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; if (PIM_DEBUG_PIM_NHT_RP) zlog_debug("%s: Deregister RP addr %pFX with Zebra ", __func__, &nht_p); pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info); - if (!str2prefix("224.0.0.0/4", &g_all)) + if (!pim_get_all_mcast_group(&g_all)) return PIM_RP_BAD_ADDRESS; rp_all = pim_rp_find_match_group(pim, &g_all); @@ -851,7 +820,7 @@ int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, return PIM_SUCCESS; } -int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, +int pim_rp_change(struct pim_instance *pim, pim_addr new_rp_addr, struct prefix group, enum rp_source rp_src_flag) { struct prefix nht_p; @@ -860,6 +829,7 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, struct rp_info *rp_info = NULL; struct pim_upstream *up; bool upstream_updated = false; + pim_addr old_rp_addr; rn = route_node_lookup(pim->rp_table, &group); if (!rn) { @@ -875,7 +845,8 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, return result; } - if (rp_info->rp.rpf_addr.u.prefix4.s_addr == new_rp_addr.s_addr) { + old_rp_addr = pim_addr_from_prefix(&rp_info->rp.rpf_addr); + if (!pim_addr_cmp(new_rp_addr, old_rp_addr)) { if (rp_info->rp_src != rp_src_flag) { rp_info->rp_src = rp_src_flag; route_unlock_node(rn); @@ -883,12 +854,13 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, } } - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; + nht_p.family = PIM_AF; + nht_p.prefixlen = PIM_MAX_BITLEN; /* Deregister old RP addr with Zebra NHT */ - if (rp_info->rp.rpf_addr.u.prefix4.s_addr != INADDR_ANY) { - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + + if (!pim_addr_is_any(old_rp_addr)) { + nht_p = rp_info->rp.rpf_addr; if (PIM_DEBUG_PIM_NHT_RP) zlog_debug("%s: Deregister RP addr %pFX with Zebra ", __func__, &nht_p); @@ -898,7 +870,8 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, pim_rp_nexthop_del(rp_info); listnode_delete(pim->rp_list, rp_info); /* Update the new RP address*/ - rp_info->rp.rpf_addr.u.prefix4 = new_rp_addr; + + pim_addr_to_prefix(&rp_info->rp.rpf_addr, new_rp_addr); rp_info->rp_src = rp_src_flag; rp_info->i_am_rp = 0; @@ -923,7 +896,7 @@ int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, pim_zebra_update_all_interfaces(pim); /* Register new RP addr with Zebra NHT */ - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; if (PIM_DEBUG_PIM_NHT_RP) zlog_debug("%s: NHT Register RP addr %pFX grp %pFX with Zebra ", __func__, &nht_p, &rp_info->group); @@ -954,9 +927,7 @@ void pim_rp_setup(struct pim_instance *pim) if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) continue; - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, @@ -1053,7 +1024,6 @@ void pim_i_am_rp_re_evaluate(struct pim_instance *pim) } } -#if PIM_IPV == 4 /* * I_am_RP(G) is true if the group-to-RP mapping indicates that * this router is the RP for the group. @@ -1066,10 +1036,7 @@ int pim_rp_i_am_rp(struct pim_instance *pim, pim_addr group) struct rp_info *rp_info; memset(&g, 0, sizeof(g)); - g.family = AF_INET; - g.prefixlen = IPV4_MAX_BITLEN; - g.u.prefix4 = group; - + pim_addr_to_prefix(&g, group); rp_info = pim_rp_find_match_group(pim, &g); if (rp_info) @@ -1088,9 +1055,7 @@ struct pim_rpf *pim_rp_g(struct pim_instance *pim, pim_addr group) struct rp_info *rp_info; memset(&g, 0, sizeof(g)); - g.family = AF_INET; - g.prefixlen = IPV4_MAX_BITLEN; - g.u.prefix4 = group; + pim_addr_to_prefix(&g, group); rp_info = pim_rp_find_match_group(pim, &g); @@ -1098,9 +1063,7 @@ struct pim_rpf *pim_rp_g(struct pim_instance *pim, pim_addr group) struct prefix nht_p; /* Register addr with Zebra NHT */ - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; if (PIM_DEBUG_PIM_NHT_RP) zlog_debug( "%s: NHT Register RP addr %pFX grp %pFX with Zebra", @@ -1131,53 +1094,35 @@ int pim_rp_set_upstream_addr(struct pim_instance *pim, pim_addr *up, struct prefix g; memset(&g, 0, sizeof(g)); - g.family = AF_INET; - g.prefixlen = IPV4_MAX_BITLEN; - g.u.prefix4 = group; + + pim_addr_to_prefix(&g, group); rp_info = pim_rp_find_match_group(pim, &g); if (!rp_info || ((pim_rpf_addr_is_inaddr_any(&rp_info->rp)) && - (source.s_addr == INADDR_ANY))) { + (pim_addr_is_any(source)))) { if (PIM_DEBUG_PIM_NHT_RP) zlog_debug("%s: Received a (*,G) with no RP configured", __func__); - up->s_addr = INADDR_ANY; + *up = PIMADDR_ANY; return 0; } - *up = (source.s_addr == INADDR_ANY) ? rp_info->rp.rpf_addr.u.prefix4 - : source; + if (pim_addr_is_any(source)) + *up = pim_addr_from_prefix(&rp_info->rp.rpf_addr); + else + *up = source; return 1; } -#else -CPP_NOTICE("functions stubbed out for IPv6"); - -int pim_rp_i_am_rp(struct pim_instance *pim, pim_addr group) -{ - return 0; -} - -struct pim_rpf *pim_rp_g(struct pim_instance *pim, pim_addr group) -{ - return NULL; -} - -int pim_rp_set_upstream_addr(struct pim_instance *pim, pim_addr *up, - pim_addr source, pim_addr group) -{ - return 0; -} -#endif int pim_rp_config_write(struct pim_instance *pim, struct vty *vty, const char *spaces) { struct listnode *node; struct rp_info *rp_info; - char rp_buffer[32]; int count = 0; + pim_addr rp_addr; for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) @@ -1186,31 +1131,28 @@ int pim_rp_config_write(struct pim_instance *pim, struct vty *vty, if (rp_info->rp_src == RP_SRC_BSR) continue; + rp_addr = pim_addr_from_prefix(&rp_info->rp.rpf_addr); if (rp_info->plist) - vty_out(vty, "%sip pim rp %s prefix-list %s\n", spaces, - inet_ntop(AF_INET, - &rp_info->rp.rpf_addr.u.prefix4, - rp_buffer, 32), - rp_info->plist); + vty_out(vty, + "%s" PIM_AF_NAME + " pim rp %pPA prefix-list %s\n", + spaces, &rp_addr, rp_info->plist); else - vty_out(vty, "%sip pim rp %s %pFX\n", spaces, - inet_ntop(AF_INET, - &rp_info->rp.rpf_addr.u.prefix4, - rp_buffer, 32), - &rp_info->group); + vty_out(vty, "%s" PIM_AF_NAME " pim rp %pPA %pFX\n", + spaces, &rp_addr, &rp_info->group); count++; } return count; } -void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) +void pim_rp_show_information(struct pim_instance *pim, struct prefix *range, + struct vty *vty, bool uj) { struct rp_info *rp_info; struct rp_info *prev_rp_info = NULL; struct listnode *node; char source[7]; - char buf[PREFIX_STRLEN]; json_object *json = NULL; json_object *json_rp_rows = NULL; @@ -1220,112 +1162,105 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) json = json_object_new_object(); else vty_out(vty, - "RP address group/prefix-list OIF I am RP Source\n"); + "RP address group/prefix-list OIF I am RP Source Group-Type\n"); for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { - if (!pim_rpf_addr_is_inaddr_any(&rp_info->rp)) { - char buf[48]; + if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) + continue; - if (rp_info->rp_src == RP_SRC_STATIC) - strlcpy(source, "Static", sizeof(source)); - else if (rp_info->rp_src == RP_SRC_BSR) - strlcpy(source, "BSR", sizeof(source)); - else - strlcpy(source, "None", sizeof(source)); - if (uj) { - /* - * If we have moved on to a new RP then add the - * entry for the previous RP - */ - if (prev_rp_info - && prev_rp_info->rp.rpf_addr.u.prefix4 - .s_addr - != rp_info->rp.rpf_addr.u.prefix4 - .s_addr) { - json_object_object_add( - json, - inet_ntop(AF_INET, - &prev_rp_info->rp - .rpf_addr.u - .prefix4, - buf, sizeof(buf)), - json_rp_rows); - json_rp_rows = NULL; - } +#if PIM_IPV == 4 + pim_addr group = rp_info->group.u.prefix4; +#else + pim_addr group = rp_info->group.u.prefix6; +#endif + const char *group_type = + pim_is_grp_ssm(pim, group) ? "SSM" : "ASM"; - if (!json_rp_rows) - json_rp_rows = json_object_new_array(); - - json_row = json_object_new_object(); - json_object_string_addf( - json_row, "rpAddress", "%pI4", - &rp_info->rp.rpf_addr.u.prefix4); - if (rp_info->rp.source_nexthop.interface) - json_object_string_add( - json_row, "outboundInterface", - rp_info->rp.source_nexthop - .interface->name); - else - json_object_string_add( - json_row, "outboundInterface", - "Unknown"); - if (rp_info->i_am_rp) - json_object_boolean_true_add(json_row, - "iAmRP"); - else - json_object_boolean_false_add(json_row, - "iAmRP"); + if (range && !prefix_same(&rp_info->group, range)) + continue; - if (rp_info->plist) - json_object_string_add(json_row, - "prefixList", - rp_info->plist); - else - json_object_string_addf( - json_row, "group", "%pFX", - &rp_info->group); - json_object_string_add(json_row, "source", - source); + if (rp_info->rp_src == RP_SRC_STATIC) + strlcpy(source, "Static", sizeof(source)); + else if (rp_info->rp_src == RP_SRC_BSR) + strlcpy(source, "BSR", sizeof(source)); + else + strlcpy(source, "None", sizeof(source)); + if (uj) { + /* + * If we have moved on to a new RP then add the + * entry for the previous RP + */ + if (prev_rp_info && + prefix_cmp(&prev_rp_info->rp.rpf_addr, + &rp_info->rp.rpf_addr)) { + json_object_object_addf( + json, json_rp_rows, "%pFXh", + &prev_rp_info->rp.rpf_addr); + json_rp_rows = NULL; + } - json_object_array_add(json_rp_rows, json_row); - } else { - vty_out(vty, "%-15s ", - inet_ntop(AF_INET, - &rp_info->rp.rpf_addr.u - .prefix4, - buf, sizeof(buf))); - - if (rp_info->plist) - vty_out(vty, "%-18s ", rp_info->plist); - else - vty_out(vty, "%-18pFX ", - &rp_info->group); + if (!json_rp_rows) + json_rp_rows = json_object_new_array(); + + json_row = json_object_new_object(); + json_object_string_addf(json_row, "rpAddress", "%pFXh", + &rp_info->rp.rpf_addr); + if (rp_info->rp.source_nexthop.interface) + json_object_string_add( + json_row, "outboundInterface", + rp_info->rp.source_nexthop + .interface->name); + else + json_object_string_add(json_row, + "outboundInterface", + "Unknown"); + if (rp_info->i_am_rp) + json_object_boolean_true_add(json_row, "iAmRP"); + else + json_object_boolean_false_add(json_row, + "iAmRP"); - if (rp_info->rp.source_nexthop.interface) - vty_out(vty, "%-16s ", - rp_info->rp.source_nexthop - .interface->name); - else - vty_out(vty, "%-16s ", "(Unknown)"); + if (rp_info->plist) + json_object_string_add(json_row, "prefixList", + rp_info->plist); + else + json_object_string_addf(json_row, "group", + "%pFX", + &rp_info->group); + json_object_string_add(json_row, "source", source); + json_object_string_add(json_row, "groupType", + group_type); + + json_object_array_add(json_rp_rows, json_row); + } else { + vty_out(vty, "%-15pFXh ", &rp_info->rp.rpf_addr); - if (rp_info->i_am_rp) - vty_out(vty, "yes"); - else - vty_out(vty, "no"); + if (rp_info->plist) + vty_out(vty, "%-18s ", rp_info->plist); + else + vty_out(vty, "%-18pFX ", &rp_info->group); - vty_out(vty, "%14s\n", source); - } - prev_rp_info = rp_info; + if (rp_info->rp.source_nexthop.interface) + vty_out(vty, "%-16s ", + rp_info->rp.source_nexthop + .interface->name); + else + vty_out(vty, "%-16s ", "(Unknown)"); + + if (rp_info->i_am_rp) + vty_out(vty, "yes"); + else + vty_out(vty, "no"); + + vty_out(vty, "%14s", source); + vty_out(vty, "%6s\n", group_type); } + prev_rp_info = rp_info; } if (uj) { if (prev_rp_info && json_rp_rows) - json_object_object_add( - json, - inet_ntop(AF_INET, - &prev_rp_info->rp.rpf_addr.u.prefix4, - buf, sizeof(buf)), - json_rp_rows); + json_object_object_addf(json, json_rp_rows, "%pFXh", + &prev_rp_info->rp.rpf_addr); vty_json(vty, json); } @@ -1343,17 +1278,20 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) continue; - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); if (!pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, &pnc)) continue; for (nh_node = pnc.nexthop; nh_node; nh_node = nh_node->next) { - if (nh_node->gate.ipv4.s_addr != INADDR_ANY) +#if PIM_IPV == 4 + if (!pim_addr_is_any(nh_node->gate.ipv4)) + continue; +#else + if (!pim_addr_is_any(nh_node->gate.ipv6)) continue; +#endif struct interface *ifp1 = if_lookup_by_index( nh_node->ifindex, pim->vrf->vrf_id); @@ -1366,15 +1304,11 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) #else nh_node->gate.ipv6 = nbr->source_addr; #endif - if (PIM_DEBUG_PIM_NHT_RP) { - char str[PREFIX_STRLEN]; - pim_addr_dump("<nht_addr?>", &nht_p, str, - sizeof(str)); + if (PIM_DEBUG_PIM_NHT_RP) zlog_debug( - "%s: addr %s new nexthop addr %pPAs interface %s", - __func__, str, &nbr->source_addr, + "%s: addr %pFXh new nexthop addr %pPAs interface %s", + __func__, &nht_p, &nbr->source_addr, ifp1->name); - } } } } diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h index c223402ddd..04faeb5f26 100644 --- a/pimd/pim_rp.h +++ b/pimd/pim_rp.h @@ -47,15 +47,13 @@ void pim_rp_free(struct pim_instance *pim); void pim_rp_list_hash_clean(void *data); -int pim_rp_new(struct pim_instance *pim, struct in_addr rp_addr, - struct prefix group, const char *plist, - enum rp_source rp_src_flag); -int pim_rp_del_config(struct pim_instance *pim, const char *rp, - const char *group, const char *plist); -int pim_rp_del(struct pim_instance *pim, struct in_addr rp_addr, - struct prefix group, const char *plist, - enum rp_source rp_src_flag); -int pim_rp_change(struct pim_instance *pim, struct in_addr new_rp_addr, +int pim_rp_new(struct pim_instance *pim, pim_addr rp_addr, struct prefix group, + const char *plist, enum rp_source rp_src_flag); +void pim_rp_del_config(struct pim_instance *pim, pim_addr rp_addr, + const char *group, const char *plist); +int pim_rp_del(struct pim_instance *pim, pim_addr rp_addr, struct prefix group, + const char *plist, enum rp_source rp_src_flag); +int pim_rp_change(struct pim_instance *pim, pim_addr new_rp_addr, struct prefix group, enum rp_source rp_src_flag); void pim_rp_prefix_list_update(struct pim_instance *pim, struct prefix_list *plist); @@ -80,8 +78,8 @@ struct pim_rpf *pim_rp_g(struct pim_instance *pim, pim_addr group); #define I_am_RP(P, G) pim_rp_i_am_rp ((P), (G)) #define RP(P, G) pim_rp_g ((P), (G)) -void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, - bool uj); +void pim_rp_show_information(struct pim_instance *pim, struct prefix *range, + struct vty *vty, bool uj); void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr); int pim_rp_list_cmp(void *v1, void *v2); struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c index a99f5536b7..cee542aa13 100644 --- a/pimd/pim_rpf.c +++ b/pimd/pim_rpf.c @@ -71,17 +71,15 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, return false; #endif - if (!pim_addr_cmp(nexthop->last_lookup, addr) - && (nexthop->last_lookup_time > pim->last_route_change_time)) { - if (PIM_DEBUG_PIM_NHT) { - char nexthop_str[PREFIX_STRLEN]; - pim_addr_dump("<nexthop?>", &nexthop->mrib_nexthop_addr, - nexthop_str, sizeof(nexthop_str)); + if ((!pim_addr_cmp(nexthop->last_lookup, addr)) && + (nexthop->last_lookup_time > pim->last_route_change_time)) { + if (PIM_DEBUG_PIM_NHT) zlog_debug( - "%s: Using last lookup for %pPAs at %lld, %" PRId64" addr %s", + "%s: Using last lookup for %pPAs at %lld, %" PRId64 + " addr %pFX", __func__, &addr, nexthop->last_lookup_time, - pim->last_route_change_time, nexthop_str); - } + pim->last_route_change_time, + &nexthop->mrib_nexthop_addr); pim->nexthop_lookups_avoided++; return true; } else { @@ -140,18 +138,13 @@ bool pim_nexthop_lookup(struct pim_instance *pim, struct pim_nexthop *nexthop, } if (found) { - if (PIM_DEBUG_ZEBRA) { - char nexthop_str[PREFIX_STRLEN]; - pim_addr_dump("<nexthop?>", - &nexthop_tab[i].nexthop_addr, nexthop_str, - sizeof(nexthop_str)); + if (PIM_DEBUG_ZEBRA) zlog_debug( - "%s %s: found nexthop %s for address %pPAs: interface %s ifindex=%d metric=%d pref=%d", - __FILE__, __func__, nexthop_str, &addr, - ifp->name, first_ifindex, - nexthop_tab[i].route_metric, + "%s %s: found nexthop %pFX for address %pPAs: interface %s ifindex=%d metric=%d pref=%d", + __FILE__, __func__, + &nexthop_tab[i].nexthop_addr, &addr, ifp->name, + first_ifindex, nexthop_tab[i].route_metric, nexthop_tab[i].protocol_distance); - } /* update nexthop data */ nexthop->interface = ifp; nexthop->mrib_nexthop_addr = nexthop_tab[i].nexthop_addr; @@ -215,7 +208,6 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, bool neigh_needed = true; uint32_t saved_mrib_route_metric; pim_addr rpf_addr; - pim_addr saved_rpf_addr; if (PIM_UPSTREAM_FLAG_TEST_STATIC_IIF(up->flags)) return PIM_RPF_OK; @@ -264,19 +256,14 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, /* detect change in pim_nexthop */ if (nexthop_mismatch(&rpf->source_nexthop, &saved.source_nexthop)) { - if (PIM_DEBUG_ZEBRA) { - char nhaddr_str[PREFIX_STRLEN]; - pim_addr_dump("<addr?>", - &rpf->source_nexthop.mrib_nexthop_addr, - nhaddr_str, sizeof(nhaddr_str)); - zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%s pref=%d metric=%d", + if (PIM_DEBUG_ZEBRA) + zlog_debug("%s(%s): (S,G)=%s source nexthop now is: interface=%s address=%pFX pref=%d metric=%d", __func__, caller, up->sg_str, rpf->source_nexthop.interface ? rpf->source_nexthop.interface->name : "<ifname?>", - nhaddr_str, + &rpf->source_nexthop.mrib_nexthop_addr, rpf->source_nexthop.mrib_metric_preference, rpf->source_nexthop.mrib_route_metric); - } pim_upstream_update_join_desired(pim, up); pim_upstream_update_could_assert(up); @@ -300,10 +287,7 @@ enum pim_rpf_result pim_rpf_update(struct pim_instance *pim, } /* detect change in RPF'(S,G) */ - - saved_rpf_addr = pim_addr_from_prefix(&saved.rpf_addr); - - if (pim_addr_cmp(saved_rpf_addr, rpf_addr) || + if (!prefix_same(&saved.rpf_addr, &rpf->rpf_addr) || saved.source_nexthop.interface != rpf->source_nexthop.interface) { pim_rpf_cost_change(pim, up, saved_mrib_route_metric); return PIM_RPF_CHANGED; @@ -418,7 +402,7 @@ unsigned int pim_rpf_hash_key(const void *arg) { const struct pim_nexthop_cache *r = arg; -#if PIM_IPV == 4 || !defined(PIM_V6_TEMP_BREAK) +#if PIM_IPV == 4 return jhash_1word(r->rpf.rpf_addr.u.prefix4.s_addr, 0); #else return jhash2(r->rpf.rpf_addr.u.prefix6.s6_addr32, diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 05b0f92a4b..92e2d18451 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -34,22 +34,27 @@ #include "vrf.h" #include "sockopt.h" #include "lib_errors.h" +#include "network.h" #include "pimd.h" #include "pim_mroute.h" #include "pim_sock.h" #include "pim_str.h" -/* GLOBAL VARS */ +#if PIM_IPV == 4 +#define setsockopt_iptos setsockopt_ipv4_tos +#define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop +#else +#define setsockopt_iptos setsockopt_ipv6_tclass +#define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop +#endif int pim_socket_raw(int protocol) { int fd; frr_with_privs(&pimd_privs) { - - fd = socket(AF_INET, SOCK_RAW, protocol); - + fd = socket(PIM_AF, SOCK_RAW, protocol); } if (fd < 0) { @@ -66,10 +71,16 @@ void pim_socket_ip_hdr(int fd) const int on = 1; frr_with_privs(&pimd_privs) { - +#if PIM_IPV == 4 if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))) - zlog_err("%s: Could not turn on IP_HDRINCL option: %s", - __func__, safe_strerror(errno)); + zlog_err("%s: Could not turn on IP_HDRINCL option: %m", + __func__); +#else + if (setsockopt(fd, IPPROTO_IPV6, IPV6_HDRINCL, &on, sizeof(on))) + zlog_err( + "%s: Could not turn on IPV6_HDRINCL option: %m", + __func__); +#endif } } @@ -80,248 +91,252 @@ void pim_socket_ip_hdr(int fd) int pim_socket_bind(int fd, struct interface *ifp) { int ret = 0; -#ifdef SO_BINDTODEVICE +#ifdef SO_BINDTODEVICE frr_with_privs(&pimd_privs) { - ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name, strlen(ifp->name)); - } - #endif return ret; } -int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, - uint8_t loop) +#if PIM_IPV == 4 +static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp) { - int rcvbuf = 1024 * 1024 * 8; -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - struct ip_mreqn mreq; -#else - struct ip_mreq mreq; -#endif - int fd; - - fd = pim_socket_raw(protocol); - if (fd < 0) { - zlog_warn("Could not create multicast socket: errno=%d: %s", - errno, safe_strerror(errno)); - return PIM_SOCK_ERR_SOCKET; - } - -#ifdef SO_BINDTODEVICE - int ret; - - ret = pim_socket_bind(fd, ifp); - if (ret) { - close(fd); - zlog_warn( - "Could not set fd: %d for interface: %s to device", - fd, ifp->name); - return PIM_SOCK_ERR_BIND; - } -#else -/* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to - * only use 1 socket for all interfaces? */ -#endif + int one = 1; + int ttl = 1; - /* Needed to obtain destination address from recvmsg() */ - { #if defined(HAVE_IP_PKTINFO) - /* Linux and Solaris IP_PKTINFO */ - int opt = 1; - if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) { - zlog_warn( - "Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - } + /* Linux and Solaris IP_PKTINFO */ + if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one))) + zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd); #elif defined(HAVE_IP_RECVDSTADDR) - /* BSD IP_RECVDSTADDR */ - int opt = 1; - if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, - sizeof(opt))) { - zlog_warn( - "Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - } + /* BSD IP_RECVDSTADDR */ + if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one))) + zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m", + fd); #else - flog_err( - EC_LIB_DEVELOPMENT, - "%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", - __FILE__, __func__); - close(fd); - return PIM_SOCK_ERR_DSTADDR; + flog_err( + EC_LIB_DEVELOPMENT, + "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()"); + close(fd); + return PIM_SOCK_ERR_DSTADDR; #endif - } - - /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. - * Message Formats)*/ + /* Set router alert (RFC 2113) for all IGMP messages (RFC + * 3376 4. Message Formats)*/ if (protocol == IPPROTO_IGMP) { uint8_t ra[4]; + ra[0] = 148; ra[1] = 4; ra[2] = 0; ra[3] = 0; if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) { zlog_warn( - "Could not set Router Alert Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + "Could not set Router Alert Option on socket fd=%d: %m", + fd); close(fd); return PIM_SOCK_ERR_RA; } } - { - int reuse = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse, - sizeof(reuse))) { - zlog_warn( - "Could not set Reuse Address Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_REUSE; - } - } - - { - const int MTTL = 1; - int ttl = MTTL; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, - sizeof(ttl))) { - zlog_warn( - "Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", - MTTL, fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_TTL; - } + if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) { + zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m", + ttl, fd); + close(fd); + return PIM_SOCK_ERR_TTL; } - if (setsockopt_ipv4_multicast_loop(fd, loop)) { + if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) { zlog_warn( - "Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", - loop ? "enable" : "disable", fd, errno, - safe_strerror(errno)); + "Could not set Outgoing Interface Option on socket fd=%d: %m", + fd); close(fd); - return PIM_SOCK_ERR_LOOP; + return PIM_SOCK_ERR_IFACE; } - memset(&mreq, 0, sizeof(mreq)); -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - mreq.imr_ifindex = ifp->ifindex; -#else -/* - * I am not sure what to do here yet for *BSD - */ -// mreq.imr_interface = ifindex; -#endif + return 0; +} +#else /* PIM_IPV != 4 */ +static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp) +{ + int ttl = 1; + struct ipv6_mreq mreq = {}; - if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreq, + setsockopt_ipv6_pktinfo(fd, 1); + setsockopt_ipv6_multicast_hops(fd, ttl); + + mreq.ipv6mr_interface = ifp->ifindex; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq, sizeof(mreq))) { zlog_warn( - "Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); + "Could not set Outgoing Interface Option on socket fd=%d: %m", + fd); close(fd); return PIM_SOCK_ERR_IFACE; } - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf))) - zlog_warn("%s: Failure to set buffer size to %d", __func__, - rcvbuf); + return 0; +} +#endif - { - long flags; +int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp, + uint8_t loop) +{ + int fd; + int ret; - flags = fcntl(fd, F_GETFL, 0); - if (flags < 0) { - zlog_warn( - "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_NONBLOCK_GETFL; - } + fd = pim_socket_raw(protocol); + if (fd < 0) { + zlog_warn("Could not create multicast socket: errno=%d: %s", + errno, safe_strerror(errno)); + return PIM_SOCK_ERR_SOCKET; + } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - zlog_warn( - "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - close(fd); - return PIM_SOCK_ERR_NONBLOCK_SETFL; - } + /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF + * to emulate behaviour? Or change to only use 1 socket for all + * interfaces? */ + ret = pim_socket_bind(fd, ifp); + if (ret) { + close(fd); + zlog_warn("Could not set fd: %d for interface: %s to device", + fd, ifp->name); + return PIM_SOCK_ERR_BIND; } - /* Set Tx socket DSCP byte */ - if (setsockopt_ipv4_tos(fd, IPTOS_PREC_INTERNETCONTROL)) { - zlog_warn("can't set sockopt IP_TOS to PIM/IGMP socket %d: %s", - fd, safe_strerror(errno)); + set_nonblocking(fd); + sockopt_reuseaddr(fd); + setsockopt_so_recvbuf(fd, 8 * 1024 * 1024); + + ret = pim_setsockopt(protocol, fd, ifp); + if (ret) { + zlog_warn("pim_setsockopt failed for interface: %s to device ", + ifp->name); + return ret; + } + + /* leftover common sockopts */ + if (setsockopt_multicast_loop(fd, loop)) { + zlog_warn( + "Could not %s Multicast Loopback Option on socket fd=%d: %m", + loop ? "enable" : "disable", fd); + close(fd); + return PIM_SOCK_ERR_LOOP; } + /* Set Tx socket DSCP byte */ + if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL)) + zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd); + return fd; } -int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr, - ifindex_t ifindex) +int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex) { int ret; -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - struct ip_mreqn opt; +#if PIM_IPV == 4 + ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr, + group.s_addr, ifindex); #else - struct ip_mreq opt; -#endif + struct ipv6_mreq opt; - opt.imr_multiaddr = group; - -#ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX - opt.imr_address = ifaddr; - opt.imr_ifindex = ifindex; -#else - opt.imr_interface = ifaddr; + memcpy(&opt.ipv6mr_multiaddr, &group, 16); + opt.ipv6mr_interface = ifindex; + ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt)); #endif - ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt)); if (ret) { - char group_str[INET_ADDRSTRLEN]; - char ifaddr_str[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str))) - snprintf(group_str, sizeof(group_str), "<group?>"); - if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str, - sizeof(ifaddr_str))) - snprintf(ifaddr_str, sizeof(ifaddr_str), "<ifaddr?>"); - flog_err( EC_LIB_SOCKET, - "Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s", - fd, group_str, ifaddr_str, errno, safe_strerror(errno)); + "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m", + fd, &group, &ifaddr); return ret; } - if (PIM_DEBUG_TRACE) { - char group_str[INET_ADDRSTRLEN]; - char ifaddr_str[INET_ADDRSTRLEN]; - if (!inet_ntop(AF_INET, &group, group_str, sizeof(group_str))) - snprintf(group_str, sizeof(group_str), "<group?>"); - if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str, - sizeof(ifaddr_str))) - snprintf(ifaddr_str, sizeof(ifaddr_str), "<ifaddr?>"); - + if (PIM_DEBUG_TRACE) zlog_debug( - "Socket fd=%d joined group %s on interface address %s", - fd, group_str, ifaddr_str); + "Socket fd=%d joined group %pPAs on interface address %pPAs", + fd, &group, &ifaddr); + return ret; +} + +#if PIM_IPV == 4 +static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst, + ifindex_t *ifindex) +{ + struct cmsghdr *cmsg; + struct sockaddr_in *dst4 = (struct sockaddr_in *)dst; + + for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; + cmsg = CMSG_NXTHDR(mh, cmsg)) { +#ifdef HAVE_IP_PKTINFO + if ((cmsg->cmsg_level == IPPROTO_IP) && + (cmsg->cmsg_type == IP_PKTINFO)) { + struct in_pktinfo *i; + + i = (struct in_pktinfo *)CMSG_DATA(cmsg); + if (dst4) + dst4->sin_addr = i->ipi_addr; + if (ifindex) + *ifindex = i->ipi_ifindex; + + break; + } +#endif + +#ifdef HAVE_IP_RECVDSTADDR + if ((cmsg->cmsg_level == IPPROTO_IP) && + (cmsg->cmsg_type == IP_RECVDSTADDR)) { + struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg); + + if (dst4) + dst4->sin_addr = *i; + + break; + } +#endif + +#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) + if (cmsg->cmsg_type == IP_RECVIF) + if (ifindex) + *ifindex = CMSG_IFINDEX(cmsg); +#endif } +} +#else /* PIM_IPV != 4 */ +static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst, + ifindex_t *ifindex) +{ + struct cmsghdr *cmsg; + struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst; - return ret; + for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL; + cmsg = CMSG_NXTHDR(mh, cmsg)) { + if ((cmsg->cmsg_level == IPPROTO_IPV6) && + (cmsg->cmsg_type == IPV6_PKTINFO)) { + struct in6_pktinfo *i; + + i = (struct in6_pktinfo *)CMSG_DATA(cmsg); + + if (dst6) + dst6->sin6_addr = i->ipi6_addr; + if (ifindex) + *ifindex = i->ipi6_ifindex; + break; + } + } } +#endif /* PIM_IPV != 4 */ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, - struct sockaddr_in *from, socklen_t *fromlen, - struct sockaddr_in *to, socklen_t *tolen, + struct sockaddr_storage *from, socklen_t *fromlen, + struct sockaddr_storage *to, socklen_t *tolen, ifindex_t *ifindex) { struct msghdr msgh; - struct cmsghdr *cmsg; struct iovec iov; char cbuf[1000]; int err; @@ -331,19 +346,12 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, * Use getsockname() to get sin_port. */ if (to) { - struct sockaddr_in si; - socklen_t si_len = sizeof(si); - - memset(&si, 0, sizeof(si)); - to->sin_family = AF_INET; + socklen_t to_len = sizeof(*to); - pim_socket_getsockname(fd, (struct sockaddr *)&si, &si_len); - - to->sin_port = si.sin_port; - to->sin_addr = si.sin_addr; + pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len); if (tolen) - *tolen = sizeof(si); + *tolen = sizeof(*to); } memset(&msgh, 0, sizeof(struct msghdr)); @@ -364,66 +372,11 @@ int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, if (fromlen) *fromlen = msgh.msg_namelen; - for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; - cmsg = CMSG_NXTHDR(&msgh, cmsg)) { - -#ifdef HAVE_IP_PKTINFO - if ((cmsg->cmsg_level == IPPROTO_IP) - && (cmsg->cmsg_type == IP_PKTINFO)) { - struct in_pktinfo *i = - (struct in_pktinfo *)CMSG_DATA(cmsg); - if (to) - to->sin_addr = i->ipi_addr; - if (tolen) - *tolen = sizeof(struct sockaddr_in); - if (ifindex) - *ifindex = i->ipi_ifindex; - - break; - } -#endif - -#ifdef HAVE_IP_RECVDSTADDR - if ((cmsg->cmsg_level == IPPROTO_IP) - && (cmsg->cmsg_type == IP_RECVDSTADDR)) { - struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg); - if (to) - to->sin_addr = *i; - if (tolen) - *tolen = sizeof(struct sockaddr_in); - - break; - } -#endif - -#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX) - if (cmsg->cmsg_type == IP_RECVIF) - if (ifindex) - *ifindex = CMSG_IFINDEX(cmsg); -#endif - - } /* for (cmsg) */ + cmsg_getdstaddr(&msgh, to, ifindex); return err; /* len */ } -int pim_socket_mcastloop_get(int fd) -{ - int loop; - socklen_t loop_len = sizeof(loop); - - if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, &loop_len)) { - int e = errno; - zlog_warn( - "Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s", - fd, errno, safe_strerror(errno)); - errno = e; - return PIM_SOCK_ERR_LOOP; - } - - return loop; -} - int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen) { if (getsockname(fd, name, namelen)) { diff --git a/pimd/pim_sock.h b/pimd/pim_sock.h index 08b0099321..97cbda0c10 100644 --- a/pimd/pim_sock.h +++ b/pimd/pim_sock.h @@ -38,17 +38,15 @@ int pim_socket_bind(int fd, struct interface *ifp); void pim_socket_ip_hdr(int fd); int pim_socket_raw(int protocol); -int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp, +int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp, uint8_t loop); -int pim_socket_join(int fd, struct in_addr group, struct in_addr ifaddr, - ifindex_t ifindex); +int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex); + int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len, - struct sockaddr_in *from, socklen_t *fromlen, - struct sockaddr_in *to, socklen_t *tolen, + struct sockaddr_storage *from, socklen_t *fromlen, + struct sockaddr_storage *to, socklen_t *tolen, ifindex_t *ifindex); -int pim_socket_mcastloop_get(int fd); - int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen); #endif /* PIM_SOCK_H */ diff --git a/pimd/pim_ssm.c b/pimd/pim_ssm.c index 688d38c84c..74310474d4 100644 --- a/pimd/pim_ssm.c +++ b/pimd/pim_ssm.c @@ -28,7 +28,7 @@ #include "pimd.h" #include "pim_ssm.h" -#include "pim_zebra.h" +#include "pim_igmp.h" static void pim_ssm_range_reevaluate(struct pim_instance *pim) { diff --git a/pimd/pim_ssm.h b/pimd/pim_ssm.h index 117713b866..c6b6978218 100644 --- a/pimd/pim_ssm.h +++ b/pimd/pim_ssm.h @@ -34,7 +34,7 @@ struct pim_ssm { void pim_ssm_prefix_list_update(struct pim_instance *pim, struct prefix_list *plist); -int pim_is_grp_ssm(struct pim_instance *pim, pim_addr group_addr); +extern int pim_is_grp_ssm(struct pim_instance *pim, pim_addr group_addr); int pim_ssm_range_set(struct pim_instance *pim, vrf_id_t vrf_id, const char *plist_name); void *pim_ssm_init(void); diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c index 596b06cb38..e43b46604c 100644 --- a/pimd/pim_ssmpingd.c +++ b/pimd/pim_ssmpingd.c @@ -244,8 +244,8 @@ static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf, static int ssmpingd_read_msg(struct ssmpingd_sock *ss) { struct interface *ifp; - struct sockaddr_in from; - struct sockaddr_in to; + struct sockaddr_storage from; + struct sockaddr_storage to; socklen_t fromlen = sizeof(from); socklen_t tolen = sizeof(to); ifindex_t ifindex = -1; @@ -256,13 +256,11 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), &from, &fromlen, &to, &tolen, &ifindex); + if (len < 0) { - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<src?>", ss->source_addr, source_str, - sizeof(source_str)); zlog_warn( - "%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s", - __func__, source_str, ss->sock_fd, errno, + "%s: failure receiving ssmping for source %pI4 on fd=%d: errno=%d: %s", + __func__, &ss->source_addr, ss->sock_fd, errno, safe_strerror(errno)); return -1; } @@ -270,47 +268,31 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss) ifp = if_lookup_by_index(ifindex, ss->pim->vrf->vrf_id); if (buf[0] != PIM_SSMPINGD_REQUEST) { - char source_str[INET_ADDRSTRLEN]; - char from_str[INET_ADDRSTRLEN]; - char to_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<src?>", ss->source_addr, source_str, - sizeof(source_str)); - pim_inet4_dump("<from?>", from.sin_addr, from_str, - sizeof(from_str)); - pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str)); zlog_warn( - "%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", - __func__, buf[0], from_str, ntohs(from.sin_port), - to_str, ntohs(to.sin_port), + "%s: bad ssmping type=%d from %pSUp to %pSUp on interface %s ifindex=%d fd=%d src=%pI4", + __func__, buf[0], &from, &to, ifp ? ifp->name : "<iface?>", ifindex, ss->sock_fd, - source_str); + &ss->source_addr); return 0; } if (PIM_DEBUG_SSMPINGD) { - char source_str[INET_ADDRSTRLEN]; - char from_str[INET_ADDRSTRLEN]; - char to_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<src?>", ss->source_addr, source_str, - sizeof(source_str)); - pim_inet4_dump("<from?>", from.sin_addr, from_str, - sizeof(from_str)); - pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str)); zlog_debug( - "%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", - __func__, from_str, ntohs(from.sin_port), to_str, - ntohs(to.sin_port), ifp ? ifp->name : "<iface?>", - ifindex, ss->sock_fd, source_str); + "%s: recv ssmping from %pSUp, to %pSUp, on interface %s ifindex=%d fd=%d src=%pI4", + __func__, &from, &to, ifp ? ifp->name : "<iface?>", + ifindex, ss->sock_fd, &ss->source_addr); } buf[0] = PIM_SSMPINGD_REPLY; + struct sockaddr_in *from_addr = (struct sockaddr_in *)&from; + /* unicast reply */ - ssmpingd_sendto(ss, buf, len, from); + ssmpingd_sendto(ss, buf, len, *from_addr); /* multicast reply */ - from.sin_addr = ss->pim->ssmpingd_group_addr; - ssmpingd_sendto(ss, buf, len, from); + from_addr->sin_addr = ss->pim->ssmpingd_group_addr; + ssmpingd_sendto(ss, buf, len, *from_addr); return 0; } diff --git a/pimd/pim_tib.c b/pimd/pim_tib.c new file mode 100644 index 0000000000..838f11211e --- /dev/null +++ b/pimd/pim_tib.c @@ -0,0 +1,178 @@ +/* + * TIB (Tree Information Base) - just PIM <> IGMP/MLD glue for now + * Copyright (C) 2022 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "pim_tib.h" + +#include "pimd.h" +#include "pim_iface.h" +#include "pim_upstream.h" +#include "pim_oil.h" +#include "pim_nht.h" + +static struct channel_oil * +tib_sg_oil_setup(struct pim_instance *pim, pim_sgaddr sg, struct interface *oif) +{ + struct pim_interface *pim_oif = oif->info; + int input_iface_vif_index = 0; + pim_addr vif_source; + struct prefix src, grp; + struct pim_nexthop nexthop; + struct pim_upstream *up = NULL; + + if (!pim_rp_set_upstream_addr(pim, &vif_source, sg.src, sg.grp)) { + /* no PIM RP - create a dummy channel oil */ + return pim_channel_oil_add(pim, &sg, __func__); + } + + pim_addr_to_prefix(&src, vif_source); // RP or Src addr + pim_addr_to_prefix(&grp, sg.grp); + + up = pim_upstream_find(pim, &sg); + if (up) { + memcpy(&nexthop, &up->rpf.source_nexthop, + sizeof(struct pim_nexthop)); + pim_ecmp_nexthop_lookup(pim, &nexthop, &src, &grp, 0); + if (nexthop.interface) + input_iface_vif_index = pim_if_find_vifindex_by_ifindex( + pim, nexthop.interface->ifindex); + } else + input_iface_vif_index = + pim_ecmp_fib_lookup_if_vif_index(pim, &src, &grp); + + if (PIM_DEBUG_ZEBRA) + zlog_debug("%s: NHT %pSG vif_source %pPAs vif_index:%d", + __func__, &sg, &vif_source, input_iface_vif_index); + + if (input_iface_vif_index < 1) { + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug( + "%s %s: could not find input interface for %pSG", + __FILE__, __func__, &sg); + + return pim_channel_oil_add(pim, &sg, __func__); + } + + /* + * Protect IGMP against adding looped MFC entries created by both + * source and receiver attached to the same interface. See TODO T22. + * Block only when the intf is non DR DR must create upstream. + */ + if ((input_iface_vif_index == pim_oif->mroute_vif_index) && + !(PIM_I_am_DR(pim_oif))) { + /* ignore request for looped MFC entry */ + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug( + "%s: ignoring request for looped MFC entry (S,G)=%pSG: oif=%s vif_index=%d", + __func__, &sg, oif->name, + input_iface_vif_index); + + return NULL; + } + + return pim_channel_oil_add(pim, &sg, __func__); +} + +bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg, + struct interface *oif, struct channel_oil **oilp) +{ + struct pim_interface *pim_oif = oif->info; + + if (!pim_oif) { + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug("%s: multicast not enabled on oif=%s?", + __func__, oif->name); + return false; + } + + if (!*oilp) + *oilp = tib_sg_oil_setup(pim, sg, oif); + if (!*oilp) + return false; + + if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { + int result; + + result = pim_channel_add_oif(*oilp, oif, + PIM_OIF_FLAG_PROTO_IGMP, __func__); + if (result) { + if (PIM_DEBUG_MROUTE) + zlog_warn("%s: add_oif() failed with return=%d", + __func__, result); + return false; + } + } else { + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug( + "%s: %pSG was received on %s interface but we are not DR for that interface", + __func__, &sg, oif->name); + + return false; + } + /* + Feed IGMPv3-gathered local membership information into PIM + per-interface (S,G) state. + */ + if (!pim_ifchannel_local_membership_add(oif, &sg, false /*is_vxlan*/)) { + if (PIM_DEBUG_MROUTE) + zlog_warn( + "%s: Failure to add local membership for %pSG", + __func__, &sg); + + pim_channel_del_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_IGMP, + __func__); + return false; + } + + return true; +} + +void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg, + struct interface *oif, struct channel_oil **oilp) +{ + int result; + + /* + It appears that in certain circumstances that + igmp_source_forward_stop is called when IGMP forwarding + was not enabled in oif_flags for this outgoing interface. + Possibly because of multiple calls. When that happens, we + enter the below if statement and this function returns early + which in turn triggers the calling function to assert. + Making the call to pim_channel_del_oif and ignoring the return code + fixes the issue without ill effect, similar to + pim_forward_stop below. + */ + result = pim_channel_del_oif(*oilp, oif, PIM_OIF_FLAG_PROTO_IGMP, + __func__); + if (result) { + if (PIM_DEBUG_IGMP_TRACE) + zlog_debug( + "%s: pim_channel_del_oif() failed with return=%d", + __func__, result); + return; + } + + /* + Feed IGMPv3-gathered local membership information into PIM + per-interface (S,G) state. + */ + pim_ifchannel_local_membership_del(oif, &sg); +} diff --git a/pimd/pim_tib.h b/pimd/pim_tib.h new file mode 100644 index 0000000000..b320f4cde0 --- /dev/null +++ b/pimd/pim_tib.h @@ -0,0 +1,33 @@ +/* + * TIB (Tree Information Base) - just PIM <> IGMP/MLD glue for now + * Copyright (C) 2022 David Lamparter for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_PIM_GLUE_H +#define _FRR_PIM_GLUE_H + +#include "pim_addr.h" + +struct pim_instance; +struct channel_oil; + +extern bool tib_sg_gm_join(struct pim_instance *pim, pim_sgaddr sg, + struct interface *oif, struct channel_oil **oilp); +extern void tib_sg_gm_prune(struct pim_instance *pim, pim_sgaddr sg, + struct interface *oif, struct channel_oil **oilp); + +#endif /* _FRR_PIM_GLUE_H */ diff --git a/pimd/pim_util.c b/pimd/pim_util.c index 8232d7205b..4b67dbf1b1 100644 --- a/pimd/pim_util.c +++ b/pimd/pim_util.c @@ -152,3 +152,17 @@ bool pim_is_group_filtered(struct pim_interface *pim_ifp, pim_addr *grp) pl = prefix_list_lookup(PIM_AFI, pim_ifp->boundary_oil_plist); return pl ? prefix_list_apply(pl, &grp_pfx) == PREFIX_DENY : false; } + + +/* This function returns all multicast group */ +int pim_get_all_mcast_group(struct prefix *prefix) +{ +#if PIM_IPV == 4 + if (!str2prefix("224.0.0.0/4", prefix)) + return 0; +#else + if (!str2prefix("FF00::0/8", prefix)) + return 0; +#endif + return 1; +} diff --git a/pimd/pim_util.h b/pimd/pim_util.h index b9c227996e..a4362bef90 100644 --- a/pimd/pim_util.h +++ b/pimd/pim_util.h @@ -36,4 +36,5 @@ void pim_pkt_dump(const char *label, const uint8_t *buf, int size); int pim_is_group_224_0_0_0_24(struct in_addr group_addr); int pim_is_group_224_4(struct in_addr group_addr); bool pim_is_group_filtered(struct pim_interface *pim_ifp, pim_addr *grp); +int pim_get_all_mcast_group(struct prefix *prefix); #endif /* PIM_UTIL_H */ diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 0acd3c0694..7f463715a5 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -55,7 +55,6 @@ struct zclient *zclient; /* Router-id update message from zebra. */ -__attribute__((unused)) static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) { struct prefix router_id; @@ -65,7 +64,6 @@ static int pim_router_id_update_zebra(ZAPI_CALLBACK_ARGS) return 0; } -__attribute__((unused)) static int pim_zebra_interface_vrf_update(ZAPI_CALLBACK_ARGS) { struct interface *ifp; @@ -158,6 +156,10 @@ static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY); } } +#else /* PIM_IPV != 4 */ + if (p->family != PIM_AF) + return 0; +#endif pim_if_addr_add(c); if (pim_ifp) { @@ -178,10 +180,6 @@ static int pim_zebra_if_address_add(ZAPI_CALLBACK_ARGS) pim_if_addr_add_all(ifp); } } -#else /* PIM_IPV != 4 */ - /* unused - for now */ - (void)pim_ifp; -#endif return 0; } @@ -220,8 +218,7 @@ static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) #endif } -#if PIM_IPV == 4 - if (p->family == AF_INET) { + if (p->family == PIM_AF) { struct pim_instance *pim; pim = vrf->info; @@ -229,7 +226,6 @@ static int pim_zebra_if_address_del(ZAPI_CALLBACK_ARGS) pim_rp_setup(pim); pim_i_am_rp_re_evaluate(pim); } -#endif connected_free(&c); return 0; @@ -454,11 +450,12 @@ static void pim_zebra_capabilities(struct zclient_capabilities *cap) static zclient_handler *const pim_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = pim_zebra_if_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del, -#if PIM_IPV == 4 + + [ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update, [ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra, [ZEBRA_INTERFACE_VRF_UPDATE] = pim_zebra_interface_vrf_update, - [ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update, +#if PIM_IPV == 4 [ZEBRA_VXLAN_SG_ADD] = pim_zebra_vxlan_sg_proc, [ZEBRA_VXLAN_SG_DEL] = pim_zebra_vxlan_sg_proc, @@ -485,342 +482,13 @@ void pim_zebra_init(void) zclient_lookup_new(); } -#if PIM_IPV == 4 -void igmp_anysource_forward_start(struct pim_instance *pim, - struct gm_group *group) -{ - struct gm_source *source; - struct in_addr src_addr = {.s_addr = 0}; - /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ - assert(group->group_filtermode_isexcl); - assert(listcount(group->group_source_list) < 1); - - source = igmp_get_source_by_addr(group, src_addr, NULL); - if (!source) { - zlog_warn("%s: Failure to create * source", __func__); - return; - } - - igmp_source_forward_start(pim, source); -} - -void igmp_anysource_forward_stop(struct gm_group *group) -{ - struct gm_source *source; - struct in_addr star = {.s_addr = 0}; - - source = igmp_find_source_by_addr(group, star); - if (source) - igmp_source_forward_stop(source); -} - -static void igmp_source_forward_reevaluate_one(struct pim_instance *pim, - struct gm_source *source) -{ - pim_sgaddr sg; - struct gm_group *group = source->source_group; - struct pim_ifchannel *ch; - - if ((source->source_addr.s_addr != INADDR_ANY) - || !IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) - return; - - memset(&sg, 0, sizeof(sg)); - sg.src = source->source_addr; - sg.grp = group->group_addr; - - ch = pim_ifchannel_find(group->interface, &sg); - if (pim_is_grp_ssm(pim, group->group_addr)) { - /* If SSM group withdraw local membership */ - if (ch - && (ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE)) { - if (PIM_DEBUG_PIM_EVENTS) - zlog_debug("local membership del for %pSG as G is now SSM", - &sg); - pim_ifchannel_local_membership_del(group->interface, - &sg); - } - } else { - /* If ASM group add local membership */ - if (!ch - || (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)) { - if (PIM_DEBUG_PIM_EVENTS) - zlog_debug("local membership add for %pSG as G is now ASM", - &sg); - pim_ifchannel_local_membership_add( - group->interface, &sg, false /*is_vxlan*/); - } - } -} - -void igmp_source_forward_reevaluate_all(struct pim_instance *pim) -{ - struct interface *ifp; - - FOR_ALL_INTERFACES (pim->vrf, ifp) { - struct pim_interface *pim_ifp = ifp->info; - struct listnode *grpnode; - struct gm_group *grp; - struct pim_ifchannel *ch, *ch_temp; - - if (!pim_ifp) - continue; - - /* scan igmp groups */ - for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode, - grp)) { - struct listnode *srcnode; - struct gm_source *src; - - /* scan group sources */ - for (ALL_LIST_ELEMENTS_RO(grp->group_source_list, - srcnode, src)) { - igmp_source_forward_reevaluate_one(pim, src); - } /* scan group sources */ - } /* scan igmp groups */ - - RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, - ch_temp) { - if (pim_is_grp_ssm(pim, ch->sg.grp)) { - if (pim_addr_is_any(ch->sg.src)) - pim_ifchannel_delete(ch); - } - } - } /* scan interfaces */ -} - -void igmp_source_forward_start(struct pim_instance *pim, - struct gm_source *source) -{ - struct pim_interface *pim_oif; - struct gm_group *group; - pim_sgaddr sg; - int result; - int input_iface_vif_index = 0; - - memset(&sg, 0, sizeof(sg)); - sg.src = source->source_addr; - sg.grp = source->source_group->group_addr; - - if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg, - source->source_group->interface->name, - IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); - } - - /* Prevent IGMP interface from installing multicast route multiple - times */ - if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { - return; - } - - group = source->source_group; - pim_oif = group->interface->info; - if (!pim_oif) { - if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug("%s: multicast not enabled on oif=%s ?", - __func__, - source->source_group->interface->name); - } - return; - } - - if (!source->source_channel_oil) { - pim_addr vif_source; - struct prefix src, grp; - struct pim_nexthop nexthop; - struct pim_upstream *up = NULL; - - if (!pim_rp_set_upstream_addr(pim, &vif_source, - source->source_addr, sg.grp)) { - /*Create a dummy channel oil */ - source->source_channel_oil = - pim_channel_oil_add(pim, &sg, __func__); - } - - else { - pim_addr_to_prefix(&src, vif_source); // RP or Src addr - pim_addr_to_prefix(&grp, sg.grp); - - up = pim_upstream_find(pim, &sg); - if (up) { - memcpy(&nexthop, &up->rpf.source_nexthop, - sizeof(struct pim_nexthop)); - pim_ecmp_nexthop_lookup(pim, &nexthop, &src, - &grp, 0); - if (nexthop.interface) - input_iface_vif_index = - pim_if_find_vifindex_by_ifindex( - pim, - nexthop.interface->ifindex); - } else - input_iface_vif_index = - pim_ecmp_fib_lookup_if_vif_index( - pim, &src, &grp); - - if (PIM_DEBUG_ZEBRA) - zlog_debug( - "%s: NHT %pSG vif_source %pPAs vif_index:%d ", - __func__, &sg, &vif_source, - input_iface_vif_index); - - if (input_iface_vif_index < 1) { - if (PIM_DEBUG_IGMP_TRACE) { - char source_str[INET_ADDRSTRLEN]; - pim_inet4_dump("<source?>", - source->source_addr, - source_str, sizeof(source_str)); - zlog_debug( - "%s %s: could not find input interface for source %s", - __FILE__, __func__, source_str); - } - source->source_channel_oil = - pim_channel_oil_add(pim, &sg, __func__); - } - - else { - /* - * Protect IGMP against adding looped MFC - * entries created by both source and receiver - * attached to the same interface. See TODO - * T22. Block only when the intf is non DR - * DR must create upstream. - */ - if ((input_iface_vif_index == - pim_oif->mroute_vif_index) && - !(PIM_I_am_DR(pim_oif))) { - /* ignore request for looped MFC entry - */ - if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug("%s: ignoring request for looped MFC entry (S,G)=%pSG: oif=%s vif_index=%d", - __func__, - &sg, - source->source_group - ->interface->name, - input_iface_vif_index); - } - return; - } - - source->source_channel_oil = - pim_channel_oil_add(pim, &sg, __func__); - if (!source->source_channel_oil) { - if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug("%s %s: could not create OIL for channel (S,G)=%pSG", - __FILE__, __func__, - &sg); - } - return; - } - } - } - } - - if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) { - result = pim_channel_add_oif(source->source_channel_oil, - group->interface, - PIM_OIF_FLAG_PROTO_IGMP, __func__); - if (result) { - if (PIM_DEBUG_MROUTE) { - zlog_warn("%s: add_oif() failed with return=%d", - __func__, result); - } - return; - } - } else { - if (PIM_DEBUG_IGMP_TRACE) - zlog_debug("%s: %pSG was received on %s interface but we are not DR for that interface", - __func__, &sg, - group->interface->name); - - return; - } - /* - Feed IGMPv3-gathered local membership information into PIM - per-interface (S,G) state. - */ - if (!pim_ifchannel_local_membership_add(group->interface, &sg, - false /*is_vxlan*/)) { - if (PIM_DEBUG_MROUTE) - zlog_warn("%s: Failure to add local membership for %pSG", - __func__, &sg); - - pim_channel_del_oif(source->source_channel_oil, - group->interface, PIM_OIF_FLAG_PROTO_IGMP, - __func__); - return; - } - - IGMP_SOURCE_DO_FORWARDING(source->source_flags); -} - -/* - igmp_source_forward_stop: stop fowarding, but keep the source - igmp_source_delete: stop fowarding, and delete the source - */ -void igmp_source_forward_stop(struct gm_source *source) -{ - struct gm_group *group; - pim_sgaddr sg; - int result; - - memset(&sg, 0, sizeof(sg)); - sg.src = source->source_addr; - sg.grp = source->source_group->group_addr; - - if (PIM_DEBUG_IGMP_TRACE) { - zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg, - source->source_group->interface->name, - IGMP_SOURCE_TEST_FORWARDING(source->source_flags)); - } - - /* Prevent IGMP interface from removing multicast route multiple - times */ - if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { - return; - } - - group = source->source_group; - - /* - It appears that in certain circumstances that - igmp_source_forward_stop is called when IGMP forwarding - was not enabled in oif_flags for this outgoing interface. - Possibly because of multiple calls. When that happens, we - enter the below if statement and this function returns early - which in turn triggers the calling function to assert. - Making the call to pim_channel_del_oif and ignoring the return code - fixes the issue without ill effect, similar to - pim_forward_stop below. - */ - result = pim_channel_del_oif(source->source_channel_oil, - group->interface, PIM_OIF_FLAG_PROTO_IGMP, - __func__); - if (result) { - if (PIM_DEBUG_IGMP_TRACE) - zlog_debug( - "%s: pim_channel_del_oif() failed with return=%d", - __func__, result); - return; - } - - /* - Feed IGMPv3-gathered local membership information into PIM - per-interface (S,G) state. - */ - pim_ifchannel_local_membership_del(group->interface, &sg); - - IGMP_SOURCE_DONT_FORWARDING(source->source_flags); -} -#endif /* PIM_IPV == 4 */ - void pim_forward_start(struct pim_ifchannel *ch) { struct pim_upstream *up = ch->upstream; uint32_t mask = 0; if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: (S,G)=%pSG oif=%s (%pI4)", __func__, &ch->sg, + zlog_debug("%s: (S,G)=%pSG oif=%s (%pPA)", __func__, &ch->sg, ch->interface->name, &up->upstream_addr); if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h index 8656d7563d..5879cdefb0 100644 --- a/pimd/pim_zebra.h +++ b/pimd/pim_zebra.h @@ -23,7 +23,6 @@ #include <zebra.h> #include "zclient.h" -#include "pim_igmp.h" #include "pim_ifchannel.h" void pim_zebra_init(void); @@ -32,15 +31,6 @@ void pim_zebra_zclient_update(struct vty *vty); void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index); void pim_scan_oil(struct pim_instance *pim_matcher); -void igmp_anysource_forward_start(struct pim_instance *pim, - struct gm_group *group); -void igmp_anysource_forward_stop(struct gm_group *group); - -void igmp_source_forward_start(struct pim_instance *pim, - struct gm_source *source); -void igmp_source_forward_stop(struct gm_source *source); -void igmp_source_forward_reevaluate_all(struct pim_instance *pim); - void pim_forward_start(struct pim_ifchannel *ch); void pim_forward_stop(struct pim_ifchannel *ch); diff --git a/pimd/subdir.am b/pimd/subdir.am index 0fe40912b1..cda5acb004 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -21,7 +21,6 @@ pim_common = \ pimd/pim_assert.c \ pimd/pim_bfd.c \ pimd/pim_br.c \ - pimd/pim_bsm.c \ pimd/pim_cmd_common.c \ pimd/pim_errors.c \ pimd/pim_hello.c \ @@ -38,6 +37,7 @@ pim_common = \ pimd/pim_nb.c \ pimd/pim_nb_config.c \ pimd/pim_neighbor.c \ + pimd/pim_nht.c \ pimd/pim_oil.c \ pimd/pim_routemap.c \ pimd/pim_rp.c \ @@ -47,6 +47,7 @@ pim_common = \ pimd/pim_ssmpingd.c \ pimd/pim_static.c \ pimd/pim_str.c \ + pimd/pim_tib.c \ pimd/pim_time.c \ pimd/pim_tlv.c \ pimd/pim_upstream.c \ @@ -59,6 +60,7 @@ pim_common = \ pimd_pimd_SOURCES = \ $(pim_common) \ + pimd/pim_bsm.c \ pimd/pim_cmd.c \ pimd/pim_igmp.c \ pimd/pim_igmp_mtrace.c \ @@ -70,7 +72,6 @@ pimd_pimd_SOURCES = \ pimd/pim_msdp.c \ pimd/pim_msdp_packet.c \ pimd/pim_msdp_socket.c \ - pimd/pim_nht.c \ pimd/pim_pim.c \ pimd/pim_register.c \ pimd/pim_signals.c \ @@ -141,6 +142,7 @@ noinst_HEADERS += \ pimd/pim_ssmpingd.h \ pimd/pim_static.h \ pimd/pim_str.h \ + pimd/pim_tib.h \ pimd/pim_time.h \ pimd/pim_tlv.h \ pimd/pim_upstream.h \ diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index 313febd9bb..5304b17f06 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -680,16 +680,17 @@ static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS) { struct sharp_nh_tracker *nht; struct zapi_route nhr; + struct prefix matched; - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { zlog_err("%s: Decode of update failed", __func__); return 0; } - zlog_debug("Received update for %pFX metric: %u", &nhr.prefix, - nhr.metric); + zlog_debug("Received update for %pFX actual match: %pFX metric: %u", + &matched, &nhr.prefix, nhr.metric); - nht = sharp_nh_tracker_get(&nhr.prefix); + nht = sharp_nh_tracker_get(&matched); nht->nhop_num = nhr.nexthop_num; nht->updates++; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index f937492ec2..bd293edebc 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -169,24 +169,25 @@ static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) { struct static_nht_data *nhtd, lookup; struct zapi_route nhr; + struct prefix matched; afi_t afi = AFI_IP; - if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) { + if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { zlog_err("Failure to decode nexthop update message"); return 1; } - if (nhr.prefix.family == AF_INET6) + if (matched.family == AF_INET6) afi = AFI_IP6; if (nhr.type == ZEBRA_ROUTE_CONNECT) { - if (static_nexthop_is_local(vrf_id, &nhr.prefix, - nhr.prefix.family)) + if (static_nexthop_is_local(vrf_id, &matched, + nhr.prefix.family)) nhr.nexthop_num = 0; } memset(&lookup, 0, sizeof(lookup)); - lookup.nh = &nhr.prefix; + lookup.nh = &matched; lookup.nh_vrf_id = vrf_id; nhtd = hash_lookup(static_nht_hash, &lookup); @@ -194,8 +195,8 @@ static int static_zebra_nexthop_update(ZAPI_CALLBACK_ARGS) if (nhtd) { nhtd->nh_num = nhr.nexthop_num; - static_nht_reset_start(&nhr.prefix, afi, nhtd->nh_vrf_id); - static_nht_update(NULL, &nhr.prefix, nhr.nexthop_num, afi, + static_nht_reset_start(&matched, afi, nhtd->nh_vrf_id); + static_nht_update(NULL, &matched, nhr.nexthop_num, afi, nhtd->nh_vrf_id); } else zlog_err("No nhtd?"); diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 97aade6578..8f0b92d0fc 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -108,12 +108,12 @@ static int test(FILE *input, FILE *output) } fprintf(output, "Unpack log:\n%s", log); - const char *s_tlvs = isis_format_tlvs(tlvs); + const char *s_tlvs = isis_format_tlvs(tlvs, NULL); fprintf(output, "Unpacked TLVs:\n%s", s_tlvs); struct isis_item *orig_auth = tlvs->isis_auth.head; tlvs->isis_auth.head = NULL; - s_tlvs = isis_format_tlvs(tlvs); + s_tlvs = isis_format_tlvs(tlvs, NULL); struct isis_tlvs *tlv_copy = isis_copy_tlvs(tlvs); tlvs->isis_auth.head = orig_auth; isis_free_tlvs(tlvs); @@ -133,7 +133,7 @@ static int test(FILE *input, FILE *output) } char *orig_tlvs = XSTRDUP(MTYPE_TMP, s_tlvs); - s_tlvs = isis_format_tlvs(tlvs); + s_tlvs = isis_format_tlvs(tlvs, NULL); if (strcmp(orig_tlvs, s_tlvs)) { fprintf(output, @@ -166,7 +166,7 @@ static int test(FILE *input, FILE *output) fprintf(output, "Could not pack fragment, too large.\n"); assert(0); } - sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs)); + sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs, NULL)); isis_free_tlvs(tlvs); } list_delete(&fragments); diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index a30f33ccad..971aba4c46 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -294,7 +294,7 @@ static int test_run(struct vty *vty, const struct isis_topology *topology, /* Print the LDPDB. */ if (CHECK_FLAG(flags, F_DISPLAY_LSPDB)) - show_isis_database_lspdb(vty, area, level - 1, + show_isis_database_lspdb_vty(vty, area, level - 1, &area->lspdb[level - 1], NULL, ISIS_UI_LEVEL_DETAIL); diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 8f9d637afd..59d08ae82b 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -207,6 +207,24 @@ int main(int argc, char **argv) assert(strcmp(p, "test#5") == 0); XFREE(MTYPE_TMP, p); + struct prefix pfx; + + str2prefix("192.168.1.23/24", &pfx); + printchk("192.168.1.23/24", "%pFX", &pfx); + printchk("192.168.1.23", "%pFXh", &pfx); + + str2prefix("2001:db8::1234/64", &pfx); + printchk("2001:db8::1234/64", "%pFX", &pfx); + printchk("2001:db8::1234", "%pFXh", &pfx); + + pfx.family = AF_UNIX; + printchk("UNK prefix", "%pFX", &pfx); + printchk("{prefix.af=AF_UNIX}", "%pFXh", &pfx); + + str2prefix_eth("02:ca:fe:f0:0d:1e/48", (struct prefix_eth *)&pfx); + printchk("02:ca:fe:f0:0d:1e/48", "%pFX", &pfx); + printchk("02:ca:fe:f0:0d:1e", "%pFXh", &pfx); + struct prefix_sg sg; sg.src.s_addr = INADDR_ANY; sg.grp.s_addr = INADDR_ANY; diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c index 607e29e56b..6e69658490 100644 --- a/tests/lib/test_typelist.c +++ b/tests/lib/test_typelist.c @@ -58,9 +58,10 @@ #define T_HASH (1 << 2) #define T_HEAP (1 << 3) #define T_ATOMIC (1 << 4) +#define T_REVERSE (1 << 5) #define _T_LIST (0) -#define _T_DLIST (0) +#define _T_DLIST (0 | T_REVERSE) #define _T_ATOMLIST (0 | T_ATOMIC) #define _T_HEAP (T_SORTED | T_HEAP) #define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ) @@ -68,8 +69,8 @@ #define _T_HASH (T_SORTED | T_UNIQ | T_HASH) #define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ) #define _T_SKIPLIST_NONUNIQ (T_SORTED) -#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ) -#define _T_RBTREE_NONUNIQ (T_SORTED) +#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ | T_REVERSE) +#define _T_RBTREE_NONUNIQ (T_SORTED | T_REVERSE) #define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC) #define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC) @@ -79,6 +80,7 @@ #define IS_HASH(type) (_T_TYPE(type) & T_HASH) #define IS_HEAP(type) (_T_TYPE(type) & T_HEAP) #define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC) +#define IS_REVERSE(type) (_T_TYPE(type) & T_REVERSE) static struct timeval ref, ref0; diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h index 8261616ed2..e3579c67a2 100644 --- a/tests/lib/test_typelist.h +++ b/tests/lib/test_typelist.h @@ -31,6 +31,11 @@ #define list_const_next concat(TYPE, _const_next) #define list_next concat(TYPE, _next) #define list_next_safe concat(TYPE, _next_safe) +#define list_const_last concat(TYPE, _const_last) +#define list_last concat(TYPE, _last) +#define list_const_prev concat(TYPE, _const_prev) +#define list_prev concat(TYPE, _prev) +#define list_prev_safe concat(TYPE, _prev_safe) #define list_count concat(TYPE, _count) #define list_add concat(TYPE, _add) #define list_add_head concat(TYPE, _add_head) @@ -171,6 +176,9 @@ static void concat(test_, TYPE)(void) list_init(&head); assert(list_first(&head) == NULL); +#if IS_REVERSE(REALTYPE) + assert(list_last(&head) == NULL); +#endif ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119"); @@ -203,6 +211,10 @@ static void concat(test_, TYPE)(void) assert(!list_first(&head)); assert(list_count(&other) == k); assert(list_first(&other) != NULL); +#if IS_REVERSE(REALTYPE) + assert(!list_last(&head)); + assert(list_last(&other) != NULL); +#endif ts_hash_headx( &other, "swap1", "a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838"); @@ -269,13 +281,36 @@ static void concat(test_, TYPE)(void) (void)cprev; #else assert(!cprev || cprev->val < citem->val); +#if IS_REVERSE(REALTYPE) + assert(list_const_prev(chead, citem) == cprev); +#endif #endif cprev = citem; k++; } assert(list_count(chead) == k); +#if IS_REVERSE(REALTYPE) + assert(cprev == list_const_last(chead)); +#endif ts_ref("walk"); +#if IS_REVERSE(REALTYPE) && !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE) + cprev = NULL; + k = 0; + + frr_rev_each(list_const, chead, citem) { + assert(!cprev || cprev->val > citem->val); + assert(list_const_next(chead, citem) == cprev); + + cprev = citem; + k++; + } + assert(list_count(chead) == k); + assert(cprev == list_const_first(chead)); + + ts_ref("reverse-walk"); +#endif + #if IS_UNIQ(REALTYPE) prng_free(prng); prng = prng_new(0); @@ -439,6 +474,9 @@ static void concat(test_, TYPE)(void) } assert(list_count(&head) == k); assert(list_first(&head) != NULL); +#if IS_REVERSE(REALTYPE) + assert(list_last(&head) != NULL); +#endif ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); #if !IS_ATOMIC(REALTYPE) @@ -451,6 +489,10 @@ static void concat(test_, TYPE)(void) assert(!list_first(&head)); assert(list_count(&other) == k); assert(list_first(&other) != NULL); +#if IS_REVERSE(REALTYPE) + assert(!list_last(&head)); + assert(list_last(&other) != NULL); +#endif ts_hash_head( &other, "swap1", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19"); @@ -534,6 +576,21 @@ static void concat(test_, TYPE)(void) } ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d"); #endif +#if IS_REVERSE(REALTYPE) + i = 0; + prev = NULL; + + frr_rev_each (list, &head, item) { + assert(item->scratchpad != 0); + assert(list_next(&head, item) == prev); + + i++; + prev = item; + } + assert(list_first(&head) == prev); + assert(list_count(&head) == i); + ts_hash("reverse-walk", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d"); +#endif while ((item = list_pop(&head))) { assert(item->scratchpad != 0); @@ -746,6 +803,13 @@ static void concat(test_, TYPE)(void) #undef list_first #undef list_next #undef list_next_safe +#undef list_const_first +#undef list_const_next +#undef list_last +#undef list_prev +#undef list_prev_safe +#undef list_const_last +#undef list_const_prev #undef list_count #undef list_add #undef list_add_head diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 94c5faf2e0..014722387f 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -236,6 +236,94 @@ def test_isis_linux_route6_installation(): assert topotest.json_cmp(actual, expected) is None, assertmsg +def test_isis_summary_json(): + "Check json struct in show isis summary json" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking 'show isis summary json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis summary json", isjson=True) + assertmsg = "Test isis summary json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['vrf'] == "default", assertmsg + assert json_output['areas'][0]['area'] == "1", assertmsg + assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + + +def test_isis_interface_json(): + "Check json struct in show isis interface json" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking 'show isis interface json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis interface json", isjson=True) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis interface detail json", isjson=True) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + + +def test_isis_neighbor_json(): + "Check json struct in show isis neighbor json" + + 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() + logger.info("Checking 'show isis neighbor json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor json", isjson=True) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface'] == rname+"-eth0", assertmsg + + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor detail json", isjson=True) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + + +def test_isis_database_json(): + "Check json struct in show isis database json" + + 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() + logger.info("Checking 'show isis database json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis database json", isjson=True) + assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['area']['name'] == "1", assertmsg + assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis database detail json", isjson=True) + assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['area']['name'] == "1", assertmsg + assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 4e5fe4c90b..62b6a8a70e 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -1749,7 +1749,7 @@ class Router(Node): daemon, self.logdir, self.name ) - cmdopt = "{} --log file:{}.log --log-level debug".format( + cmdopt = "{} --command-log-always --log file:{}.log --log-level debug".format( daemon_opts, daemon ) if extra_opts: diff --git a/vrrpd/Makefile b/vrrpd/Makefile index 027c6ee1f8..0abb1a6381 100644 --- a/vrrpd/Makefile +++ b/vrrpd/Makefile @@ -1,7 +1,7 @@ all: ALWAYS - @$(MAKE) -s -C .. vrrp/vrrp + @$(MAKE) -s -C .. vrrpd/vrrpd %: ALWAYS - @$(MAKE) -s -C .. vrrp/$@ + @$(MAKE) -s -C .. vrrpd/$@ Makefile: #nothing diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index f8c6a1fc1d..ed1f1fb5bb 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -73,6 +73,7 @@ struct vtysh_client { struct thread *log_reader; int log_fd; + uint32_t lost_msgs; }; static bool stderr_tty; @@ -3654,6 +3655,15 @@ static void vtysh_log_read(struct thread *thread) if (ret < 0 && ERRNO_IO_RETRY(errno)) return; + if (stderr_stdout_same) { +#ifdef HAVE_RL_CLEAR_VISIBLE_LINE + rl_clear_visible_line(); +#else + puts("\r"); +#endif + fflush(stdout); + } + if (ret <= 0) { struct timespec ts; @@ -3677,17 +3687,17 @@ static void vtysh_log_read(struct thread *thread) buf.hdr.ts_nsec = ts.tv_nsec; buf.hdr.prio = LOG_ERR; buf.hdr.flags = 0; - buf.hdr.arghdrlen = 0; + buf.hdr.texthdrlen = 0; buf.hdr.n_argpos = 0; - } + } else { + int32_t lost_msgs = buf.hdr.lost_msgs - vclient->lost_msgs; - if (stderr_stdout_same) { -#ifdef HAVE_RL_CLEAR_VISIBLE_LINE - rl_clear_visible_line(); -#else - puts("\r"); -#endif - fflush(stdout); + if (lost_msgs > 0) { + vclient->lost_msgs = buf.hdr.lost_msgs; + fprintf(stderr, + "%d log messages from %s lost (vtysh reading too slowly)\n", + lost_msgs, vclient->name); + } } text = buf.text + sizeof(buf.hdr.argpos[0]) * buf.hdr.n_argpos; diff --git a/yang/frr-route-types.yang b/yang/frr-route-types.yang index aeb52a6520..ffc671c99a 100644 --- a/yang/frr-route-types.yang +++ b/yang/frr-route-types.yang @@ -162,9 +162,7 @@ module frr-route-types { typedef ipv6-multicast-group-prefix { type inet:ipv6-prefix { pattern - '(((FF|ff)[0-9a-fA-F]{2}):)([0-9a-fA-F]{0,4}:){0,5}((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))(/((1[6-9])|([2-9][0-9])|(1[0-1][0-9])|(12[0-8])))'; - pattern - '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)(/.+)'; + '(([fF]{2}[0-9a-fA-F]{2}):).*'; } description "This type represents an IPv6 multicast group prefix, diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index db8ee3236c..a75b165270 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1876,6 +1876,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } 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) @@ -1909,9 +1910,21 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); + if (tb[IFLA_PROTO_DOWN]) { + uint8_t protodown; + + protodown = *(uint8_t *)RTA_DATA( + tb[IFLA_PROTO_DOWN]); + netlink_proc_dplane_if_protodown(zif, + !!protodown); + } + 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)) { + 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", @@ -1927,7 +1940,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) zlog_debug( "Intf %s(%u) PTM up, notifying clients", name, ifp->ifindex); - zebra_interface_up_update(ifp); + if_up(ifp, !is_up); /* Update EVPN VNI when SVI MAC change */ @@ -1956,12 +1969,14 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } } else { ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (if_is_operative(ifp)) { + 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); + if_up(ifp, true); if (IS_ZEBRA_IF_BRIDGE(ifp)) chgflags = ZEBRA_BRIDGE_MASTER_UP; @@ -1990,15 +2005,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass); - - if (tb[IFLA_PROTO_DOWN]) { - uint8_t protodown; - - protodown = *(uint8_t *)RTA_DATA( - tb[IFLA_PROTO_DOWN]); - netlink_proc_dplane_if_protodown(ifp->info, - !!protodown); - } } zif = ifp->info; diff --git a/zebra/interface.c b/zebra/interface.c index fbd2aac005..a76f8741e0 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -224,9 +224,13 @@ static int if_zebra_new_hook(struct interface *ifp) static void if_nhg_dependents_check_valid(struct nhg_hash_entry *nhe) { zebra_nhg_check_valid(nhe); - if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) - /* Assuming uninstalled as well here */ - UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) { + /* If we're in shutdown, this interface event needs to clean + * up installed NHGs, so don't clear that flag directly. + */ + if (!zrouter.in_shutdown) + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + } } static void if_down_nhg_dependents(const struct interface *ifp) @@ -517,7 +521,7 @@ void if_flags_update(struct interface *ifp, uint64_t newflags) /* inoperative -> operative? */ ifp->flags = newflags; if (if_is_operative(ifp)) - if_up(ifp); + if_up(ifp, true); } } @@ -1045,7 +1049,7 @@ bool if_nhg_dependents_is_empty(const struct interface *ifp) } /* Interface is up. */ -void if_up(struct interface *ifp) +void if_up(struct interface *ifp, bool install_connected) { struct zebra_if *zif; struct interface *link_if; @@ -1077,7 +1081,8 @@ void if_up(struct interface *ifp) #endif /* Install connected routes to the kernel. */ - if_install_connected(ifp); + if (install_connected) + if_install_connected(ifp); /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces * are checked to see if (remote) neighbor entries need to be installed @@ -2778,7 +2783,7 @@ int if_linkdetect(struct interface *ifp, bool detect) /* Interface may come up after disabling link detection */ if (if_is_operative(ifp) && !if_was_operative) - if_up(ifp); + if_up(ifp, true); } /* FIXME: Will defer status change forwarding if interface does not come down! */ diff --git a/zebra/interface.h b/zebra/interface.h index c19e494860..315a3170d8 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -486,7 +486,7 @@ extern void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, extern void if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(struct interface *ifp); extern void if_delete_update(struct interface *ifp); extern void if_add_update(struct interface *ifp); -extern void if_up(struct interface *); +extern void if_up(struct interface *ifp, bool install_connected); extern void if_down(struct interface *); extern void if_refresh(struct interface *); extern void if_flags_update(struct interface *, uint64_t); diff --git a/zebra/rib.h b/zebra/rib.h index c6f3528cec..c8abfaf023 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -54,8 +54,7 @@ struct rnh { #define ZEBRA_NHT_CONNECTED 0x1 #define ZEBRA_NHT_DELETED 0x2 -#define ZEBRA_NHT_EXACT_MATCH 0x4 -#define ZEBRA_NHT_RESOLVE_VIA_DEFAULT 0x8 +#define ZEBRA_NHT_RESOLVE_VIA_DEFAULT 0x4 /* VRF identifier. */ vrf_id_t vrf_id; diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c index 13b9cc2002..21fb5299bc 100644 --- a/zebra/zebra_evpn.c +++ b/zebra/zebra_evpn.c @@ -344,10 +344,10 @@ int zebra_evpn_add_macip_for_intf(struct interface *ifp, for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { struct ipaddr ip; - memset(&ip, 0, sizeof(struct ipaddr)); if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) continue; + memset(&ip, 0, sizeof(struct ipaddr)); if (c->address->family == AF_INET) { ip.ipa_type = IPADDR_V4; memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index e38766bc6b..74043e521c 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -1141,14 +1141,6 @@ int zebra_evpn_mac_del(struct zebra_evpn *zevpn, struct zebra_mac *mac) sizeof(mac_buf))); } - /* If the MAC is freed before the neigh we will end up - * with a stale pointer against the neigh - */ - if (!list_isempty(mac->neigh_list)) - zlog_warn("%s: MAC %pEA flags 0x%x neigh list not empty %d", - __func__, &mac->macaddr, mac->flags, - listcount(mac->neigh_list)); - /* force de-ref any ES entry linked to the MAC */ zebra_evpn_es_mac_deref_entry(mac); @@ -1161,6 +1153,26 @@ int zebra_evpn_mac_del(struct zebra_evpn *zevpn, struct zebra_mac *mac) /* Cancel auto recovery */ THREAD_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. + * The situation can arise when a MAC is in remote state + * and its associated neigh is local state. + * zebra_evpn_cfg_cleanup() cleans up remote neighs and MACs. + * Instead of deleting remote MAC, if its neigh list is non-empty + * (associated to local neighs), mark the MAC as AUTO. + */ + if (!list_isempty(mac->neigh_list)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "MAC %pEA (flags 0x%x vni %u) has non-empty neigh list " + "count %u, mark MAC as AUTO", + &mac->macaddr, mac->flags, zevpn->vni, + listcount(mac->neigh_list)); + + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + return 0; + } + list_delete(&mac->neigh_list); /* Free the VNI hash entry and allocated memory. */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index f557e66384..01ac7c227f 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -2203,7 +2203,6 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, /* Only advertise in BGP if the knob is enabled */ if (advertise_gw_macip_enabled(zevpn)) { - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); /* Set Router flag (R-bit) */ if (ip->ipa_type == IPADDR_V6) diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 81d610940d..739ba33036 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -136,7 +136,6 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) ns_id_t ns_id = NS_UNKNOWN; int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) + NETLINK_ALIGN(sizeof(struct rtgenmsg)); - int curr_length = offset; void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); struct nlattr *attr; @@ -145,7 +144,6 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) && attr->nla_len >= sizeof(struct nlattr) && attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr); attr += NETLINK_ALIGN(attr->nla_len)) { - curr_length += attr->nla_len; if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { uint32_t *ptr = (uint32_t *)(attr); diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index e1d28e1534..469a94a65b 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1966,7 +1966,7 @@ static int resolve_backup_nexthops(const struct nexthop *nexthop, */ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, const struct prefix *top, int type, uint32_t flags, - uint32_t *pmtu) + uint32_t *pmtu, vrf_id_t vrf_id) { struct prefix p; struct route_table *table; @@ -2061,13 +2061,13 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, return 1; } - if (top - && ((top->family == AF_INET && top->prefixlen == IPV4_MAX_BITLEN - && nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr) - || (top->family == AF_INET6 && top->prefixlen == IPV6_MAX_BITLEN - && memcmp(&nexthop->gate.ipv6, &top->u.prefix6, - IPV6_MAX_BYTELEN) - == 0))) { + if (top && + ((top->family == AF_INET && top->prefixlen == IPV4_MAX_BITLEN && + nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr) || + (top->family == AF_INET6 && top->prefixlen == IPV6_MAX_BITLEN && + memcmp(&nexthop->gate.ipv6, &top->u.prefix6, IPV6_MAX_BYTELEN) == + 0)) && + nexthop->vrf_id == vrf_id) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( " :%s: Attempting to install a max prefixlength route through itself", @@ -2361,6 +2361,7 @@ static unsigned nexthop_active_check(struct route_node *rn, const struct prefix *p, *src_p; struct zebra_vrf *zvrf; uint32_t mtu = 0; + vrf_id_t vrf_id; srcdest_rnode_prefixes(rn, &p, &src_p); @@ -2389,10 +2390,12 @@ static unsigned nexthop_active_check(struct route_node *rn, 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, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -2400,16 +2403,16 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; case NEXTHOP_TYPE_IPV6: family = AFI_IP6; - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -2419,8 +2422,8 @@ static unsigned nexthop_active_check(struct route_node *rn, if (rn->p.family != AF_INET) family = AFI_IP6; - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 68e5c391cf..c28e251e3a 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -350,7 +350,7 @@ DEFUN (no_zebra_ptm_enable_if, if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Bringing up interface %s", __func__, ifp->name); - if_up(ifp); + if_up(ifp, true); } } @@ -553,7 +553,7 @@ static int zebra_ptm_handle_cbl_msg(void *arg, void *in_ctxt, ifp->ptm_status = ZEBRA_PTM_STATUS_UP; if (ifp->ptm_enable && if_is_no_ptm_operative(ifp) && send_linkup) - if_up(ifp); + if_up(ifp, true); } else if (!strcmp(cbl_str, ZEBRA_PTM_FAIL_STR) && (ifp->ptm_status != ZEBRA_PTM_STATUS_DOWN)) { ifp->ptm_status = ZEBRA_PTM_STATUS_DOWN; @@ -1163,7 +1163,7 @@ void zebra_ptm_reset_status(int ptm_disable) zlog_debug( "%s: Bringing up interface %s", __func__, ifp->name); - if_up(ifp); + if_up(ifp, true); } } } diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 8ca25359be..4d5336120d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -1169,15 +1169,17 @@ int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, SET_FLAG(message, ZAPI_MESSAGE_SRTE); stream_putl(s, message); + /* + * Put what we were told to match against + */ stream_putw(s, rnh->safi); stream_putw(s, rn->p.family); + stream_putc(s, rn->p.prefixlen); switch (rn->p.family) { case AF_INET: - stream_putc(s, rn->p.prefixlen); stream_put_in_addr(s, &rn->p.u.prefix4); break; case AF_INET6: - stream_putc(s, rn->p.prefixlen); stream_put(s, &rn->p.u.prefix6, IPV6_MAX_BYTELEN); break; default: @@ -1186,6 +1188,26 @@ int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, __func__, rn->p.family); goto failure; } + + /* + * What we matched against + */ + stream_putw(s, rnh->resolved_route.family); + stream_putc(s, rnh->resolved_route.prefixlen); + switch (rnh->resolved_route.family) { + case AF_INET: + stream_put_in_addr(s, &rnh->resolved_route.u.prefix4); + break; + case AF_INET6: + stream_put(s, &rnh->resolved_route.u.prefix6, IPV6_MAX_BYTELEN); + break; + default: + flog_err(EC_ZEBRA_RNH_UNKNOWN_FAMILY, + "%s: Unknown family (%d) notification attempted", + __func__, rn->p.family); + goto failure; + } + if (srte_color) stream_putl(s, srte_color); diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c index 7933ef66b1..c0f18dd091 100644 --- a/zebra/zebra_srte.c +++ b/zebra/zebra_srte.c @@ -117,16 +117,28 @@ static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, stream_putl(s, message); stream_putw(s, SAFI_UNICAST); + /* + * The prefix is copied twice because the ZEBRA_NEXTHOP_UPDATE + * code was modified to send back both the matched against + * as well as the actual matched. There does not appear to + * be an equivalent here so just send the same thing twice. + */ switch (policy->endpoint.ipa_type) { case IPADDR_V4: stream_putw(s, AF_INET); stream_putc(s, IPV4_MAX_BITLEN); stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); break; case IPADDR_V6: stream_putw(s, AF_INET6); stream_putc(s, IPV6_MAX_BITLEN); stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); break; default: flog_warn(EC_LIB_DEVELOPMENT, |
