summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--bgpd/bgp_labelpool.c10
-rw-r--r--bgpd/bgp_nht.c30
-rw-r--r--bgpd/bgp_packet.c3
-rw-r--r--bgpd/bgp_route.c7
-rw-r--r--debian/.gitignore1
-rwxr-xr-xdebian/rules2
-rw-r--r--doc/developer/frr-release-procedure.rst15
-rw-r--r--doc/developer/lists.rst186
-rw-r--r--doc/user/pim.rst4
-rw-r--r--ldpd/ldp_vty_exec.c4
-rw-r--r--lib/log_vty.c1
-rw-r--r--lib/routemap.c34
-rw-r--r--lib/sockunion.c15
-rw-r--r--lib/sockunion.h1
-rw-r--r--lib/typerb.c35
-rw-r--r--lib/typerb.h22
-rw-r--r--lib/typesafe.h48
-rw-r--r--lib/zclient.c19
-rw-r--r--lib/zclient.h12
-rw-r--r--ospf6d/ospf6_gr_helper.c19
-rw-r--r--ospf6d/ospf6_lsa.c8
-rw-r--r--ospf6d/ospf6_top.c15
-rw-r--r--ospf6d/ospf6_zebra.c7
-rw-r--r--ospfd/ospf_ldp_sync.c7
-rw-r--r--ospfd/ospf_vty.c63
-rw-r--r--pbrd/pbr_zebra.c9
-rw-r--r--pimd/pim_cmd.c169
-rw-r--r--pimd/pim_cmd_common.c5
-rw-r--r--pimd/pim_iface.c140
-rw-r--r--pimd/pim_iface.h10
-rw-r--r--pimd/pim_igmp.c198
-rw-r--r--pimd/pim_igmp.h22
-rw-r--r--pimd/pim_igmp_mtrace.c4
-rw-r--r--pimd/pim_igmp_stats.c4
-rw-r--r--pimd/pim_igmp_stats.h4
-rw-r--r--pimd/pim_igmpv2.c4
-rw-r--r--pimd/pim_igmpv3.c2
-rw-r--r--pimd/pim_msdp_packet.c4
-rw-r--r--pimd/pim_nht.c13
-rw-r--r--pimd/pim_oil.c8
-rw-r--r--pimd/pim_pim.c8
-rw-r--r--pimd/pim_rp.c22
-rw-r--r--pimd/pim_rp.h4
-rw-r--r--pimd/pim_sock.c440
-rw-r--r--pimd/pim_sock.h12
-rw-r--r--pimd/pim_ssm.c2
-rw-r--r--pimd/pim_ssm.h2
-rw-r--r--pimd/pim_ssmpingd.c50
-rw-r--r--pimd/pim_tib.c178
-rw-r--r--pimd/pim_tib.h33
-rw-r--r--pimd/pim_zebra.c345
-rw-r--r--pimd/pim_zebra.h10
-rw-r--r--pimd/subdir.am2
-rw-r--r--sharpd/sharp_zebra.c9
-rw-r--r--staticd/static_zebra.c15
-rw-r--r--tests/lib/test_typelist.c8
-rw-r--r--tests/lib/test_typelist.h64
-rw-r--r--tests/topotests/lib/topotest.py2
-rw-r--r--tools/coccinelle/json_object_add_camel_case.cocci19
-rw-r--r--zebra/rib.h3
-rw-r--r--zebra/zebra_evpn_neigh.c28
-rw-r--r--zebra/zebra_rnh.c26
-rw-r--r--zebra/zebra_srte.c12
-rw-r--r--zebra/zebra_vxlan.c17
65 files changed, 1509 insertions, 969 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_labelpool.c b/bgpd/bgp_labelpool.c
index 1bc7b62304..8772afd736 100644
--- a/bgpd/bgp_labelpool.c
+++ b/bgpd/bgp_labelpool.c
@@ -631,13 +631,23 @@ DEFUN(show_bgp_labelpool_summary, show_bgp_labelpool_summary_cmd,
if (uj) {
json = json_object_new_object();
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
json_object_int_add(json, "Ledger", skiplist_count(lp->ledger));
+ json_object_int_add(json, "ledger", skiplist_count(lp->ledger));
json_object_int_add(json, "InUse", skiplist_count(lp->inuse));
+ json_object_int_add(json, "inUse", skiplist_count(lp->inuse));
json_object_int_add(json, "Requests",
lp_fifo_count(&lp->requests));
+ json_object_int_add(json, "requests",
+ lp_fifo_count(&lp->requests));
json_object_int_add(json, "LabelChunks", listcount(lp->chunks));
+ json_object_int_add(json, "labelChunks", listcount(lp->chunks));
json_object_int_add(json, "Pending", lp->pending_count);
+ json_object_int_add(json, "pending", lp->pending_count);
json_object_int_add(json, "Reconnects", lp->reconnect_count);
+ json_object_int_add(json, "reconnects", lp->reconnect_count);
vty_json(vty, json);
} else {
vty_out(vty, "Labelpool Summary\n");
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 485495924a..78e4964d9f 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -9529,6 +9529,8 @@ void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
} else {
json_object_string_add(json_nexthop, "Error",
"Unsupported address-family");
+ json_object_string_add(json_nexthop, "error",
+ "Unsupported address-family");
}
}
@@ -9898,9 +9900,12 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
if (tag_buf[0] != '\0')
vty_out(vty, " VNI %s", tag_buf);
} else {
- if (tag_buf[0])
+ if (tag_buf[0]) {
json_object_string_add(json_path, "VNI",
tag_buf);
+ json_object_string_add(json_path, "vni",
+ tag_buf);
+ }
}
}
diff --git a/debian/.gitignore b/debian/.gitignore
index 0b267c6f5c..d95d33a610 100644
--- a/debian/.gitignore
+++ b/debian/.gitignore
@@ -11,3 +11,4 @@
/files
/frr.init
/frr.service
+/frr@.service
diff --git a/debian/rules b/debian/rules
index 0fa9c3a3b0..7a719b7c60 100755
--- a/debian/rules
+++ b/debian/rules
@@ -72,6 +72,7 @@ override_dh_auto_install:
# let dh_systemd_* and dh_installinit do their thing automatically
cp tools/frr.service debian/frr.service
+ cp tools/frr@.service debian/frr@.service
cp tools/frrinit.sh debian/frr.init
-rm -f debian/tmp/usr/lib/frr/frr
@@ -112,3 +113,4 @@ override_dh_auto_clean:
if test -f Makefile; then make redistclean; fi
-rm -f debian/frr.init
-rm -f debian/frr.service
+ -rm -f debian/frr@.service
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/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/ldpd/ldp_vty_exec.c b/ldpd/ldp_vty_exec.c
index 7bad1dca7c..c4053f5374 100644
--- a/ldpd/ldp_vty_exec.c
+++ b/ldpd/ldp_vty_exec.c
@@ -1472,7 +1472,11 @@ show_l2vpn_pw_msg_json(struct imsg *imsg, struct show_params *params,
json_pw = json_object_new_object();
json_object_string_addf(json_pw, "peerId", "%pI4", &pw->lsr_id);
json_object_int_add(json_pw, "vcId", pw->pwid);
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
json_object_string_add(json_pw, "VpnName", pw->l2vpn_name);
+ json_object_string_add(json_pw, "vpnName", pw->l2vpn_name);
if (pw->status == PW_FORWARDING)
json_object_string_add(json_pw, "status", "up");
else
diff --git a/lib/log_vty.c b/lib/log_vty.c
index ef33a39d4a..81280f302f 100644
--- a/lib/log_vty.c
+++ b/lib/log_vty.c
@@ -159,6 +159,7 @@ void zlog_rotate(void)
{
zlog_file_rotate(&zt_file);
zlog_file_rotate(&zt_filterfile.parent);
+ zlog_file_rotate(&zt_file_cmdline);
hook_call(zlog_rotate);
}
diff --git a/lib/routemap.c b/lib/routemap.c
index 9afe18d10b..46161fd817 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -100,6 +100,7 @@ static void route_map_del_plist_entries(afi_t afi,
struct prefix_list_entry *entry);
static struct hash *route_map_get_dep_hash(route_map_event_t event);
+static void route_map_free_map(struct route_map *map);
struct route_map_match_set_hooks rmap_match_set_hook;
@@ -566,15 +567,8 @@ static bool route_map_hash_cmp(const void *p1, const void *p2)
const struct route_map *map1 = p1;
const struct route_map *map2 = p2;
- if (map1->deleted == map2->deleted) {
- if (map1->name && map2->name) {
- if (!strcmp(map1->name, map2->name)) {
- return true;
- }
- } else if (!map1->name && !map2->name) {
- return true;
- }
- }
+ if (!strcmp(map1->name, map2->name))
+ return true;
return false;
}
@@ -636,13 +630,25 @@ static struct route_map *route_map_new(const char *name)
/* Add new name to route_map. */
static struct route_map *route_map_add(const char *name)
{
- struct route_map *map;
+ struct route_map *map, *exist;
struct route_map_list *list;
map = route_map_new(name);
list = &route_map_master;
- /* Add map to the hash */
+ /*
+ * Add map to the hash
+ *
+ * If the map already exists in the hash, then we know that
+ * FRR is now in a sequence of delete/create.
+ * All FRR needs to do here is set the to_be_processed
+ * bit (to inherit from the old one
+ */
+ exist = hash_release(route_map_master_hash, map);
+ if (exist) {
+ map->to_be_processed = exist->to_be_processed;
+ route_map_free_map(exist);
+ }
hash_get(route_map_master_hash, map, hash_alloc_intern);
/* Add new entry to the head of the list to match how it is added in the
@@ -752,11 +758,15 @@ struct route_map *route_map_lookup_by_name(const char *name)
if (!name)
return NULL;
- // map.deleted is 0 via memset
+ // map.deleted is false via memset
memset(&tmp_map, 0, sizeof(struct route_map));
tmp_map.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
map = hash_lookup(route_map_master_hash, &tmp_map);
XFREE(MTYPE_ROUTE_MAP_NAME, tmp_map.name);
+
+ if (map && map->deleted)
+ return NULL;
+
return map;
}
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/ospf6d/ospf6_gr_helper.c b/ospf6d/ospf6_gr_helper.c
index 5f9b7f0294..7b5ffc920b 100644
--- a/ospf6d/ospf6_gr_helper.c
+++ b/ospf6d/ospf6_gr_helper.c
@@ -960,13 +960,22 @@ static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6,
json, "supportedGracePeriod",
ospf6->ospf6_helper_cfg.supported_grace_time);
- if (ospf6->ospf6_helper_cfg.last_exit_reason
- != OSPF6_GR_HELPER_EXIT_NONE)
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
+ if (ospf6->ospf6_helper_cfg.last_exit_reason !=
+ OSPF6_GR_HELPER_EXIT_NONE) {
json_object_string_add(
json, "LastExitReason",
ospf6_exit_reason_desc
[ospf6->ospf6_helper_cfg
.last_exit_reason]);
+ json_object_string_add(
+ json, "lastExitReason",
+ ospf6_exit_reason_desc
+ [ospf6->ospf6_helper_cfg
+ .last_exit_reason]);
+ }
if (OSPF6_HELPER_ENABLE_RTR_COUNT(ospf6)) {
struct json_object *json_rid_array =
@@ -995,12 +1004,18 @@ static void show_ospf6_gr_helper_details(struct vty *vty, struct ospf6 *ospf6,
json_object_object_get_ex(
json, "Neighbors",
&json_neighbors);
+ json_object_object_get_ex(
+ json, "neighbors",
+ &json_neighbors);
if (!json_neighbors) {
json_neighbors =
json_object_new_object();
json_object_object_add(
json, "Neighbors",
json_neighbors);
+ json_object_object_add(
+ json, "neighbors",
+ json_neighbors);
}
}
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
index 740a94ba84..779076f387 100644
--- a/ospf6d/ospf6_lsa.c
+++ b/ospf6d/ospf6_lsa.c
@@ -85,9 +85,13 @@ static int ospf6_unknown_lsa_show(struct vty *vty, struct ospf6_lsa *lsa,
start = (uint8_t *)lsa->header + sizeof(struct ospf6_lsa_header);
end = (uint8_t *)lsa->header + ntohs(lsa->header->length);
- if (use_json)
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
+ if (use_json) {
json_object_string_add(json_obj, "LsaType", "unknown");
- else {
+ json_object_string_add(json_obj, "lsaType", "unknown");
+ } else {
vty_out(vty, " Unknown contents:\n");
for (current = start; current < end; current++) {
if ((current - start) % 16 == 0)
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index e64bf24b66..2a6d7cd099 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -2017,6 +2017,9 @@ ospf6_show_vrf_name(struct vty *vty, struct ospf6 *ospf6,
}
}
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys containing whitespaces")
+#endif
static int
ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6,
json_object *json,
@@ -2037,7 +2040,9 @@ ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6,
ospf6_show_vrf_name(vty, ospf6, json_vrf);
json_object_int_add(json_vrf, "aggregation delay interval",
- ospf6->aggr_delay_interval);
+ ospf6->aggr_delay_interval);
+ json_object_int_add(json_vrf, "aggregationDelayInterval",
+ ospf6->aggr_delay_interval);
}
@@ -2062,12 +2067,18 @@ ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6,
json_object_string_add(json_aggr,
"Summary address",
buf);
+ json_object_string_add(json_aggr, "summaryAddress",
+ buf);
json_object_string_add(
json_aggr, "Metric-type",
(aggr->mtype == DEFAULT_METRIC_TYPE)
? "E2"
: "E1");
+ json_object_string_add(
+ json_aggr, "metricType",
+ (aggr->mtype == DEFAULT_METRIC_TYPE) ? "E2"
+ : "E1");
json_object_int_add(json_aggr, "Metric",
(aggr->metric != -1)
@@ -2080,6 +2091,8 @@ ospf6_show_summary_address(struct vty *vty, struct ospf6 *ospf6,
json_object_int_add(json_aggr,
"External route count",
OSPF6_EXTERNAL_RT_COUNT(aggr));
+ json_object_int_add(json_aggr, "externalRouteCount",
+ OSPF6_EXTERNAL_RT_COUNT(aggr));
if (OSPF6_EXTERNAL_RT_COUNT(aggr) && detail) {
json_object_int_add(json_aggr, "ID",
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_ldp_sync.c b/ospfd/ospf_ldp_sync.c
index 9b3498bc1e..f6c1b43610 100644
--- a/ospfd/ospf_ldp_sync.c
+++ b/ospfd/ospf_ldp_sync.c
@@ -508,10 +508,17 @@ void ospf_ldp_sync_show_info(struct vty *vty, struct ospf *ospf,
if (CHECK_FLAG(ospf->ldp_sync_cmd.flags, LDP_SYNC_FLAG_ENABLE)) {
if (use_json) {
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
json_object_boolean_true_add(json_vrf,
"MplsLdpIgpSyncEnabled");
+ json_object_boolean_true_add(json_vrf,
+ "mplsLdpIgpSyncEnabled");
json_object_int_add(json_vrf, "MplsLdpIgpSyncHolddown",
ospf->ldp_sync_cmd.holddown);
+ json_object_int_add(json_vrf, "mplsLdpIgpSyncHolddown",
+ ospf->ldp_sync_cmd.holddown);
} else {
vty_out(vty, " MPLS LDP-IGP Sync is enabled\n");
if (ospf->ldp_sync_cmd.holddown == 0)
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 780521bfe4..da90435c5b 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -10185,10 +10185,17 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf,
json_object_int_add(json_vrf, "supportedGracePeriod",
ospf->supported_grace_time);
- if (ospf->last_exit_reason != OSPF_GR_HELPER_EXIT_NONE)
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
+ if (ospf->last_exit_reason != OSPF_GR_HELPER_EXIT_NONE) {
json_object_string_add(
json_vrf, "LastExitReason",
ospf_exit_reason2str(ospf->last_exit_reason));
+ json_object_string_add(
+ json_vrf, "lastExitReason",
+ ospf_exit_reason2str(ospf->last_exit_reason));
+ }
if (ospf->active_restarter_cnt)
json_object_int_add(json_vrf, "activeRestarterCnt",
@@ -10211,12 +10218,17 @@ static int ospf_show_gr_helper_details(struct vty *vty, struct ospf *ospf,
if (uj) {
json_object_object_get_ex(json_vrf, "Neighbors",
&json_neighbors);
+ json_object_object_get_ex(json_vrf, "neighbors",
+ &json_neighbors);
if (!json_neighbors) {
json_neighbors =
json_object_new_object();
json_object_object_add(json_vrf,
"Neighbors",
json_neighbors);
+ json_object_object_add(json_vrf,
+ "neighbors",
+ json_neighbors);
}
}
@@ -10514,6 +10526,9 @@ static void config_write_stub_router(struct vty *vty, struct ospf *ospf)
return;
}
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys containing whitespaces")
+#endif
static void show_ip_ospf_route_network(struct vty *vty, struct ospf *ospf,
struct route_table *rt,
json_object *json)
@@ -10621,6 +10636,12 @@ static void show_ip_ospf_route_network(struct vty *vty, struct ospf *ospf,
ifindex2ifname(
path->ifindex,
ospf->vrf_id));
+ json_object_string_add(
+ json_nexthop,
+ "directlyAttachedTo",
+ ifindex2ifname(
+ path->ifindex,
+ ospf->vrf_id));
} else {
vty_out(vty,
"%24s directly attached to %s\n",
@@ -10706,9 +10727,12 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf,
json_object_string_addf(json_route, "area",
"%pI4",
&or->u.std.area_id);
- if (or->path_type == OSPF_PATH_INTER_AREA)
+ if (or->path_type == OSPF_PATH_INTER_AREA) {
json_object_boolean_true_add(json_route,
"IA");
+ json_object_boolean_true_add(json_route,
+ "ia");
+ }
if (or->u.std.flags & ROUTER_LSA_BORDER)
json_object_string_add(json_route,
"routerType",
@@ -10760,6 +10784,12 @@ static void show_ip_ospf_route_router(struct vty *vty, struct ospf *ospf,
ifindex2ifname(
path->ifindex,
ospf->vrf_id));
+ json_object_string_add(
+ json_nexthop,
+ "directlyAttachedTo",
+ ifindex2ifname(
+ path->ifindex,
+ ospf->vrf_id));
} else {
vty_out(vty,
"%24s directly attached to %s\n",
@@ -10886,6 +10916,12 @@ static void show_ip_ospf_route_external(struct vty *vty, struct ospf *ospf,
ifindex2ifname(
path->ifindex,
ospf->vrf_id));
+ json_object_string_add(
+ json_nexthop,
+ "directlyAttachedTo",
+ ifindex2ifname(
+ path->ifindex,
+ ospf->vrf_id));
} else {
vty_out(vty,
"%24s directly attached to %s\n",
@@ -11408,12 +11444,15 @@ static int ospf_show_summary_address(struct vty *vty, struct ospf *ospf,
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
- if (!uj)
+ if (!uj) {
vty_out(vty, "aggregation delay interval :%u(in seconds)\n\n",
ospf->aggr_delay_interval);
- else
+ } else {
json_object_int_add(json_vrf, "aggregation delay interval",
ospf->aggr_delay_interval);
+ json_object_int_add(json_vrf, "aggregationDelayInterval",
+ ospf->aggr_delay_interval);
+ }
for (rn = route_top(ospf->rt_aggr_tbl); rn; rn = route_next(rn))
if (rn->info) {
@@ -11432,21 +11471,37 @@ static int ospf_show_summary_address(struct vty *vty, struct ospf *ospf,
json_object_string_add(json_aggr,
"Summary address", buf);
+ json_object_string_add(json_aggr,
+ "summaryAddress", buf);
json_object_string_add(
json_aggr, "Metric-type",
(mtype == EXTERNAL_METRIC_TYPE_1)
? "E1"
: "E2");
+ json_object_string_add(
+ json_aggr, "metricType",
+ (mtype == EXTERNAL_METRIC_TYPE_1)
+ ? "E1"
+ : "E2");
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
json_object_int_add(json_aggr, "Metric", mval);
+ json_object_int_add(json_aggr, "metric", mval);
json_object_int_add(json_aggr, "Tag",
aggr->tag);
+ json_object_int_add(json_aggr, "tag",
+ aggr->tag);
json_object_int_add(
json_aggr, "External route count",
OSPF_EXTERNAL_RT_COUNT(aggr));
+ json_object_int_add(
+ json_aggr, "externalRouteCount",
+ OSPF_EXTERNAL_RT_COUNT(aggr));
if (OSPF_EXTERNAL_RT_COUNT(aggr) && detail) {
hash_walk(
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/pim_cmd.c b/pimd/pim_cmd.c
index 3b3d06e791..1c7fcd62e0 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");
@@ -1299,14 +1284,16 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
const char *ifname, bool uj)
{
struct interface *ifp;
- struct igmp_stats rx_stats;
+ struct igmp_stats igmp_stats;
- igmp_stats_init(&rx_stats);
+ igmp_stats_init(&igmp_stats);
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,9 +1303,27 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
if (ifname && strcmp(ifname, ifp->name))
continue;
+ igmp_stats.joins_failed += pim_ifp->igmp_ifstat_joins_failed;
+ igmp_stats.joins_sent += pim_ifp->igmp_ifstat_joins_sent;
+ igmp_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;
+
+ igmp_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);
+ igmp_stats_add(&igmp_stats, &igmp->igmp_stats);
}
}
if (uj) {
@@ -1330,36 +1335,58 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
json_object_string_add(json_row, "name", ifname ? ifname :
"global");
- 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, "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);
+ json_object_int_add(json_row, "queryV1", igmp_stats.query_v1);
+ json_object_int_add(json_row, "queryV2", igmp_stats.query_v2);
+ json_object_int_add(json_row, "queryV3", igmp_stats.query_v3);
+ json_object_int_add(json_row, "leaveV2", igmp_stats.leave_v2);
+ json_object_int_add(json_row, "reportV1", igmp_stats.report_v1);
+ json_object_int_add(json_row, "reportV2", igmp_stats.report_v2);
+ json_object_int_add(json_row, "reportV3", igmp_stats.report_v3);
json_object_int_add(json_row, "mtraceResponse",
- rx_stats.mtrace_rsp);
+ igmp_stats.mtrace_rsp);
json_object_int_add(json_row, "mtraceRequest",
- rx_stats.mtrace_req);
+ igmp_stats.mtrace_req);
json_object_int_add(json_row, "unsupported",
- rx_stats.unsupported);
+ igmp_stats.unsupported);
+ json_object_int_add(json_row, "totalGroups",
+ igmp_stats.total_groups);
+ json_object_int_add(json_row, "totalSourceGroups",
+ igmp_stats.total_source_groups);
+ json_object_int_add(json_row, "joinsFailed",
+ igmp_stats.joins_failed);
+ json_object_int_add(json_row, "joinsSent",
+ igmp_stats.joins_sent);
json_object_object_add(json, ifname ? ifname : "global",
json_row);
vty_json(vty, json);
} else {
- vty_out(vty, "IGMP RX statistics\n");
- vty_out(vty, "Interface : %s\n",
+ vty_out(vty, "IGMP 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", igmp_stats.query_v1);
+ vty_out(vty, "V2 query : %u\n", igmp_stats.query_v2);
+ vty_out(vty, "V3 query : %u\n", igmp_stats.query_v3);
+ vty_out(vty, "V2 leave : %u\n", igmp_stats.leave_v2);
+ vty_out(vty, "V1 report : %u\n",
+ igmp_stats.report_v1);
+ vty_out(vty, "V2 report : %u\n",
+ igmp_stats.report_v2);
+ vty_out(vty, "V3 report : %u\n",
+ igmp_stats.report_v3);
+ vty_out(vty, "mtrace response : %u\n",
+ igmp_stats.mtrace_rsp);
+ vty_out(vty, "mtrace request : %u\n",
+ igmp_stats.mtrace_req);
+ vty_out(vty, "unsupported : %u\n",
+ igmp_stats.unsupported);
+ vty_out(vty, "joins failed : %u\n",
+ igmp_stats.joins_failed);
+ vty_out(vty, "joins sent : %u\n",
+ igmp_stats.joins_sent);
+ vty_out(vty, "total groups : %u\n",
+ igmp_stats.total_groups);
+ vty_out(vty, "total source groups : %u\n",
+ igmp_stats.total_source_groups);
}
}
@@ -1686,8 +1713,13 @@ static void pim_show_join_helper(struct vty *vty, struct pim_interface *pim_ifp,
json_object_string_add(
json_row, "channelJoinName",
pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
- if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags))
+ if (PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
json_object_int_add(json_row, "SGRpt", 1);
+ json_object_int_add(json_row, "sgRpt", 1);
+ }
if (PIM_IF_FLAG_TEST_PROTO_PIM(ch->flags))
json_object_int_add(json_row, "protocolPim", 1);
if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags))
@@ -2037,6 +2069,8 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
json_ifp_in);
json_object_int_add(json_source, "Installed",
c_oil->installed);
+ json_object_int_add(json_source, "installed",
+ c_oil->installed);
if (isRpt)
json_object_boolean_true_add(
json_source, "isRpt");
@@ -2045,20 +2079,36 @@ static void pim_show_state(struct pim_instance *pim, struct vty *vty,
json_source, "isRpt");
json_object_int_add(json_source, "RefCount",
c_oil->oil_ref_count);
+ json_object_int_add(json_source, "refCount",
+ c_oil->oil_ref_count);
json_object_int_add(json_source, "OilListSize",
c_oil->oil_size);
+ json_object_int_add(json_source, "oilListSize",
+ c_oil->oil_size);
json_object_int_add(
json_source, "OilRescan",
c_oil->oil_inherited_rescan);
+ json_object_int_add(
+ json_source, "oilRescan",
+ c_oil->oil_inherited_rescan);
json_object_int_add(json_source, "LastUsed",
c_oil->cc.lastused);
+ json_object_int_add(json_source, "lastUsed",
+ c_oil->cc.lastused);
json_object_int_add(json_source, "PacketCount",
c_oil->cc.pktcnt);
+ json_object_int_add(json_source, "packetCount",
+ c_oil->cc.pktcnt);
json_object_int_add(json_source, "ByteCount",
c_oil->cc.bytecnt);
+ json_object_int_add(json_source, "byteCount",
+ c_oil->cc.bytecnt);
json_object_int_add(json_source,
"WrongInterface",
c_oil->cc.wrong_if);
+ json_object_int_add(json_source,
+ "wrongInterface",
+ c_oil->cc.wrong_if);
}
} else {
vty_out(vty, "%-6d %-15s %-15s %-3s %-16s ",
@@ -3983,6 +4033,8 @@ DEFUN (clear_ip_pim_interface_traffic,
pim_ifp->pim_ifstat_assert_send = 0;
pim_ifp->pim_ifstat_bsm_rx = 0;
pim_ifp->pim_ifstat_bsm_tx = 0;
+ pim_ifp->igmp_ifstat_joins_sent = 0;
+ pim_ifp->igmp_ifstat_joins_failed = 0;
}
return CMD_SUCCESS;
@@ -5313,39 +5365,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 +5426,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");
@@ -5960,6 +6029,8 @@ static void show_mroute(struct pim_instance *pim, struct vty *vty,
c_oil->oil_size);
json_object_int_add(json_source, "OilInheritedRescan",
c_oil->oil_inherited_rescan);
+ json_object_int_add(json_source, "oilInheritedRescan",
+ c_oil->oil_inherited_rescan);
json_object_string_add(json_source, "iif", in_ifname);
json_object_string_add(json_source, "upTime",
mroute_uptime);
diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c
index 442760fdfe..c5d89f8065 100644
--- a/pimd/pim_cmd_common.c
+++ b/pimd/pim_cmd_common.c
@@ -582,13 +582,12 @@ int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str,
return CMD_WARNING_CONFIG_FAILED;
}
- if (!yang_dnode_exists(vty->candidate_config->dnode, group_xpath)) {
+ 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;
}
- group_dnode = yang_dnode_get(vty->candidate_config->dnode, group_xpath);
-
if (yang_is_last_list_dnode(group_dnode))
nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL);
else
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index 5425aec233..98fa4c4882 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -53,8 +53,8 @@
#if PIM_IPV == 4
static void pim_if_igmp_join_del_all(struct interface *ifp);
static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
- struct in_addr group_addr,
- struct in_addr source_addr);
+ struct in_addr group_addr, struct in_addr source_addr,
+ struct pim_interface *pim_ifp);
#endif
void pim_if_init(struct pim_instance *pim)
@@ -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__);
@@ -552,7 +576,7 @@ void pim_if_addr_add(struct connected *ifc)
close(ij->sock_fd);
join_fd = igmp_join_sock(
ifp->name, ifp->ifindex, ij->group_addr,
- ij->source_addr);
+ ij->source_addr, pim_ifp);
if (join_fd < 0) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
@@ -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)
@@ -1175,12 +1248,16 @@ static struct gm_join *igmp_join_find(struct list *join_list,
}
static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
- struct in_addr group_addr, struct in_addr source_addr)
+ struct in_addr group_addr, struct in_addr source_addr,
+ struct pim_interface *pim_ifp)
{
int join_fd;
+ pim_ifp->igmp_ifstat_joins_sent++;
+
join_fd = pim_socket_raw(IPPROTO_IGMP);
if (join_fd < 0) {
+ pim_ifp->igmp_ifstat_joins_failed++;
return -1;
}
@@ -1196,6 +1273,8 @@ static int igmp_join_sock(const char *ifname, ifindex_t ifindex,
__func__, join_fd, group_str, source_str, ifindex,
ifname, errno, safe_strerror(errno));
+ pim_ifp->igmp_ifstat_joins_failed++;
+
close(join_fd);
return -2;
}
@@ -1215,7 +1294,7 @@ static struct gm_join *igmp_join_new(struct interface *ifp,
assert(pim_ifp);
join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr,
- source_addr);
+ source_addr, pim_ifp);
if (join_fd < 0) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
@@ -1549,7 +1628,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 +1639,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 +1676,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 +1699,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 +1727,6 @@ static int pim_ifp_up(struct interface *ifp)
}
}
}
-#endif
return 0;
}
@@ -1666,7 +1740,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 +1748,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 +1757,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 +1780,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..244e1aedd1 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
@@ -182,6 +189,9 @@ struct pim_interface {
bool bsm_enable; /* bsm processing enable */
bool ucast_bsm_accept; /* ucast bsm processing */
+ uint32_t igmp_ifstat_joins_sent;
+ uint32_t igmp_ifstat_joins_failed;
+
struct {
bool enabled;
uint32_t min_rx;
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 5cdefd2828..fcb335a5b3 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()
*/
@@ -51,6 +220,7 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
int fd;
int join = 0;
struct in_addr group;
+ struct pim_interface *pim_ifp = ifp->info;
fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
@@ -59,7 +229,8 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
if (inet_aton(PIM_ALL_ROUTERS, &group)) {
- if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
+ if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
+ pim_ifp))
++join;
} else {
zlog_warn(
@@ -75,7 +246,7 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
IGMP routers must receive general queries for querier election.
*/
if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
- if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
+ if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
++join;
} else {
zlog_warn(
@@ -85,7 +256,8 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
}
if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
- if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) {
+ if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
+ pim_ifp)) {
++join;
}
} else {
@@ -320,16 +492,16 @@ static int igmp_recv_query(struct gm_sock *igmp, int query_version,
/* Collecting IGMP Rx stats */
switch (query_version) {
case 1:
- igmp->rx_stats.query_v1++;
+ igmp->igmp_stats.query_v1++;
break;
case 2:
- igmp->rx_stats.query_v2++;
+ igmp->igmp_stats.query_v2++;
break;
case 3:
- igmp->rx_stats.query_v3++;
+ igmp->igmp_stats.query_v3++;
break;
default:
- igmp->rx_stats.unsupported++;
+ igmp->igmp_stats.unsupported++;
}
/*
@@ -461,7 +633,7 @@ static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
}
/* Collecting IGMP Rx stats */
- igmp->rx_stats.report_v1++;
+ igmp->igmp_stats.report_v1++;
if (PIM_DEBUG_IGMP_TRACE) {
zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
@@ -613,7 +785,7 @@ int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
/* Collecting IGMP Rx stats */
- igmp->rx_stats.unsupported++;
+ igmp->igmp_stats.unsupported++;
return -1;
}
@@ -990,7 +1162,7 @@ static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
pim_ifp->gm_default_robustness_variable;
igmp->sock_creation = pim_time_monotonic_sec();
- igmp_stats_init(&igmp->rx_stats);
+ igmp_stats_init(&igmp->igmp_stats);
if (mtrace_only) {
igmp->mtrace_only = mtrace_only;
@@ -1016,8 +1188,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..4160dcb118 100644
--- a/pimd/pim_igmp.h
+++ b/pimd/pim_igmp.h
@@ -100,7 +100,7 @@ struct gm_sock {
bool mtrace_only;
- struct igmp_stats rx_stats;
+ struct igmp_stats igmp_stats;
};
struct pim_interface;
@@ -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_mtrace.c b/pimd/pim_igmp_mtrace.c
index d8210168e2..11bb2db7eb 100644
--- a/pimd/pim_igmp_mtrace.c
+++ b/pimd/pim_igmp_mtrace.c
@@ -626,7 +626,7 @@ int igmp_mtrace_recv_qry_req(struct gm_sock *igmp, struct ip *ip_hdr,
}
/* Collecting IGMP Rx stats */
- igmp->rx_stats.mtrace_req++;
+ igmp->igmp_stats.mtrace_req++;
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
@@ -843,7 +843,7 @@ int igmp_mtrace_recv_response(struct gm_sock *igmp, struct ip *ip_hdr,
mtracep->checksum = checksum;
/* Collecting IGMP Rx stats */
- igmp->rx_stats.mtrace_rsp++;
+ igmp->igmp_stats.mtrace_rsp++;
if (PIM_DEBUG_MTRACE)
mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c
index 40851a4529..e1eb166b65 100644
--- a/pimd/pim_igmp_stats.c
+++ b/pimd/pim_igmp_stats.c
@@ -43,4 +43,8 @@ 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;
+ a->joins_sent += b->joins_sent;
+ a->joins_failed += b->joins_failed;
}
diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h
index a70a433557..42c0c9ee31 100644
--- a/pimd/pim_igmp_stats.h
+++ b/pimd/pim_igmp_stats.h
@@ -33,6 +33,10 @@ struct igmp_stats {
uint32_t mtrace_rsp;
uint32_t mtrace_req;
uint32_t unsupported;
+ uint32_t total_groups;
+ uint32_t total_source_groups;
+ uint32_t joins_sent;
+ uint32_t joins_failed;
};
#if PIM_IPV == 4
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
index a7c7c99ebf..09a82069a2 100644
--- a/pimd/pim_igmpv2.c
+++ b/pimd/pim_igmpv2.c
@@ -130,7 +130,7 @@ int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
}
/* Collecting IGMP Rx stats */
- igmp->rx_stats.report_v2++;
+ igmp->igmp_stats.report_v2++;
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
@@ -221,7 +221,7 @@ int igmp_v2_recv_leave(struct gm_sock *igmp, struct ip *ip_hdr,
}
/* Collecting IGMP Rx stats */
- igmp->rx_stats.leave_v2++;
+ igmp->igmp_stats.leave_v2++;
/*
* RFC 3376
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
index 87554bc8ba..1ce5fdc4b0 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -1854,7 +1854,7 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
}
/* Collecting IGMP Rx stats */
- igmp->rx_stats.report_v3++;
+ igmp->igmp_stats.report_v3++;
num_groups = ntohs(
*(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
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_nht.c b/pimd/pim_nht.c
index 80d214b2f7..4e7aad99f1 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -702,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)
@@ -806,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) {
@@ -862,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..3980e4828d 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];
@@ -428,7 +428,7 @@ static int pim_sock_open(struct interface *ifp)
return -1;
if (pim_socket_join(fd, qpim_all_pim_routers_addr,
- pim_ifp->primary_address, ifp->ifindex)) {
+ pim_ifp->primary_address, ifp->ifindex, pim_ifp)) {
close(fd);
return -2;
}
@@ -467,6 +467,8 @@ void pim_ifstat_reset(struct interface *ifp)
pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
+ pim_ifp->igmp_ifstat_joins_sent = 0;
+ pim_ifp->igmp_ifstat_joins_failed = 0;
}
void pim_sock_reset(struct interface *ifp)
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index 00a1e1b58c..99727cf837 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -49,6 +49,7 @@
#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)
@@ -1145,7 +1146,8 @@ int pim_rp_config_write(struct pim_instance *pim, struct vty *vty,
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;
@@ -1160,11 +1162,22 @@ 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))
continue;
+#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 (range && !prefix_same(&rp_info->group, range))
+ continue;
+
if (rp_info->rp_src == RP_SRC_STATIC)
strlcpy(source, "Static", sizeof(source));
else if (rp_info->rp_src == RP_SRC_BSR)
@@ -1214,6 +1227,8 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj)
"%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 {
@@ -1236,7 +1251,8 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj)
else
vty_out(vty, "no");
- vty_out(vty, "%14s\n", source);
+ vty_out(vty, "%14s", source);
+ vty_out(vty, "%6s\n", group_type);
}
prev_rp_info = rp_info;
}
diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h
index 29834f8e5e..04faeb5f26 100644
--- a/pimd/pim_rp.h
+++ b/pimd/pim_rp.h
@@ -78,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_sock.c b/pimd/pim_sock.c
index 05b0f92a4b..8619cc3f83 100644
--- a/pimd/pim_sock.c
+++ b/pimd/pim_sock.c
@@ -34,22 +34,28 @@
#include "vrf.h"
#include "sockopt.h"
#include "lib_errors.h"
+#include "network.h"
#include "pimd.h"
#include "pim_mroute.h"
+#include "pim_iface.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 +72,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 +92,256 @@ 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,
+ struct pim_interface *pim_ifp)
{
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?>");
+ pim_ifp->igmp_ifstat_joins_sent++;
+ if (ret) {
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);
+ pim_ifp->igmp_ifstat_joins_failed++;
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 +351,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;
-
- pim_socket_getsockname(fd, (struct sockaddr *)&si, &si_len);
+ socklen_t to_len = sizeof(*to);
- 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 +377,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..2e9c043e84 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,
+ struct pim_interface *pim_ifp);
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_zebra.c b/pimd/pim_zebra.c
index 526fdcbc27..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;
@@ -456,10 +452,10 @@ static zclient_handler *const pim_handlers[] = {
[ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del,
[ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update,
-#if PIM_IPV == 4
[ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra,
[ZEBRA_INTERFACE_VRF_UPDATE] = pim_zebra_interface_vrf_update,
+#if PIM_IPV == 4
[ZEBRA_VXLAN_SG_ADD] = pim_zebra_vxlan_sg_proc,
[ZEBRA_VXLAN_SG_DEL] = pim_zebra_vxlan_sg_proc,
@@ -486,335 +482,6 @@ 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;
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 d617be3cb7..cda5acb004 100644
--- a/pimd/subdir.am
+++ b/pimd/subdir.am
@@ -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 \
@@ -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/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/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/tools/coccinelle/json_object_add_camel_case.cocci b/tools/coccinelle/json_object_add_camel_case.cocci
new file mode 100644
index 0000000000..279ba213ac
--- /dev/null
+++ b/tools/coccinelle/json_object_add_camel_case.cocci
@@ -0,0 +1,19 @@
+// Catch whitespaces in JSON keys
+
+@r@
+identifier json;
+constant key;
+identifier func =~ "json_object_";
+position p;
+@@
+
+func(json, key, ...)@p
+
+@script:python@
+fmt << r.key;
+p << r.p;
+@@
+if " " in str(fmt):
+ print("Whitespace detected in JSON keys %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt))
+if str(fmt)[1].isupper():
+ print("Capital first detected in JSON keys %s:%s:%s:%s" % (p[0].file, p[0].line, p[0].column, fmt))
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_neigh.c b/zebra/zebra_evpn_neigh.c
index 01ac7c227f..ed224151ba 100644
--- a/zebra/zebra_evpn_neigh.c
+++ b/zebra/zebra_evpn_neigh.c
@@ -1311,14 +1311,7 @@ int zebra_evpn_local_neigh_update(struct zebra_evpn *zevpn,
if (!n) {
/* New neighbor - create */
n = zebra_evpn_neigh_add(zevpn, ip, macaddr, zmac, 0);
- if (!n) {
- flog_err(
- EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add neighbor %pIA MAC %pEA intf %s(%u) -> VNI %u",
- ip, macaddr, ifp->name, ifp->ifindex,
- zevpn->vni);
- return -1;
- }
+
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
n->ifindex = ifp->ifindex;
@@ -2070,14 +2063,6 @@ void zebra_evpn_neigh_remote_macip_add(struct zebra_evpn *zevpn,
if (!n) {
n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr,
mac, 0);
- if (!n) {
- zlog_warn(
- "Failed to add Neigh %pIA MAC %pEA VNI %u Remote VTEP %pI4",
- ipaddr, &mac->macaddr, zevpn->vni,
- &vtep_ip);
- return;
- }
-
} else {
/* When host moves but changes its (MAC,IP)
* binding, BGP may install a MACIP entry that
@@ -2182,17 +2167,8 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp,
assert(mac);
n = zebra_evpn_neigh_lookup(zevpn, ip);
- if (!n) {
+ if (!n)
n = zebra_evpn_neigh_add(zevpn, ip, &mac->macaddr, mac, 0);
- if (!n) {
- flog_err(
- EC_ZEBRA_MAC_ADD_FAILED,
- "Failed to add neighbor %pIA MAC %pEA intf %s(%u) -> VNI %u",
- ip, &mac->macaddr,
- ifp->name, ifp->ifindex, zevpn->vni);
- return -1;
- }
- }
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
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,
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index b6da445e38..d01d8158de 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -2124,13 +2124,6 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
return 0;
zevpn = zebra_evpn_add(vni);
- if (!zevpn) {
- flog_err(EC_ZEBRA_VNI_ADD_FAILED,
- "Adding L2-VNI - Failed to add VNI hash, VNI %u",
- vni);
-
- return -1;
- }
/* Find bridge interface for the VNI */
vlan_if = zvni_map_to_svi(vxl->access_vlan,
@@ -5171,16 +5164,8 @@ int zebra_vxlan_if_add(struct interface *ifp)
/* Create or update EVPN hash. */
zevpn = zebra_evpn_lookup(vni);
- if (!zevpn) {
+ if (!zevpn)
zevpn = zebra_evpn_add(vni);
- if (!zevpn) {
- flog_err(
- EC_ZEBRA_VNI_ADD_FAILED,
- "Failed to add EVPN hash, IF %s(%u) VNI %u",
- ifp->name, ifp->ifindex, vni);
- return -1;
- }
- }
if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr ||
zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) {