summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format3
-rw-r--r--bgpd/bgp_errors.c6
-rw-r--r--bgpd/bgp_errors.h1
-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--bgpd/bgp_vty.c2
-rw-r--r--bgpd/bgp_zebra.c6
-rw-r--r--bgpd/bgpd.c2
-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/developer/logging.rst4
-rw-r--r--doc/developer/topotests.rst3
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/pim.rst4
-rw-r--r--doc/user/pimv6.rst288
-rw-r--r--doc/user/subdir.am1
-rw-r--r--ldpd/ldp_vty_exec.c4
-rw-r--r--lib/json.c13
-rw-r--r--lib/json.h22
-rw-r--r--lib/log_vty.c1
-rw-r--r--lib/northbound_grpc.cpp829
-rw-r--r--lib/prefix.c37
-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/wheel.c1
-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/pim6_main.c3
-rw-r--r--pimd/pim_cmd.c218
-rw-r--r--pimd/pim_cmd_common.c5
-rw-r--r--pimd/pim_iface.c140
-rw-r--r--pimd/pim_iface.h11
-rw-r--r--pimd/pim_ifchannel.c2
-rw-r--r--pimd/pim_igmp.c257
-rw-r--r--pimd/pim_igmp.h32
-rw-r--r--pimd/pim_igmp_mtrace.c4
-rw-r--r--pimd/pim_igmp_stats.c10
-rw-r--r--pimd/pim_igmp_stats.h28
-rw-r--r--pimd/pim_igmpv2.c25
-rw-r--r--pimd/pim_igmpv3.c103
-rw-r--r--pimd/pim_join.c49
-rw-r--r--pimd/pim_msdp_packet.c4
-rw-r--r--pimd/pim_nht.c23
-rw-r--r--pimd/pim_oil.c8
-rw-r--r--pimd/pim_pim.c27
-rw-r--r--pimd/pim_register.c106
-rw-r--r--pimd/pim_rp.c239
-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_tlv.c32
-rw-r--r--pimd/pim_upstream.c13
-rw-r--r--pimd/pim_zebra.c347
-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/.gitignore2
-rw-r--r--tests/lib/test_grpc.py10
-rw-r--r--tests/lib/test_printfrr.c18
-rw-r--r--tests/lib/test_typelist.c8
-rw-r--r--tests/lib/test_typelist.h64
-rwxr-xr-xtests/topotests/analyze.py7
l---------tests/topotests/grpc_basic/lib1
-rw-r--r--tests/topotests/grpc_basic/r1/zebra.conf8
-rw-r--r--tests/topotests/grpc_basic/r2/zebra.conf8
-rw-r--r--tests/topotests/grpc_basic/test_basic_grpc.py179
-rwxr-xr-xtests/topotests/lib/grpc-query.py155
-rw-r--r--tests/topotests/lib/topotest.py2
-rw-r--r--tools/coccinelle/json_object_add_camel_case.cocci19
-rw-r--r--vrrpd/Makefile4
-rw-r--r--zebra/dplane_fpm_nl.c3
-rw-r--r--zebra/if_netlink.c4
-rw-r--r--zebra/rib.h3
-rw-r--r--zebra/zebra_evpn_neigh.c29
-rw-r--r--zebra/zebra_rnh.c26
-rw-r--r--zebra/zebra_srte.c12
-rw-r--r--zebra/zebra_vty.c1
-rw-r--r--zebra/zebra_vxlan.c23
99 files changed, 3055 insertions, 1770 deletions
diff --git a/.clang-format b/.clang-format
index a620b5c2c0..b01157b051 100644
--- a/.clang-format
+++ b/.clang-format
@@ -28,6 +28,9 @@ ForEachMacros:
- frr_each
- frr_each_safe
- frr_each_from
+ - frr_rev_each
+ - frr_rev_each_safe
+ - frr_rev_each_from
- frr_with_mutex
- frr_with_privs
- LIST_FOREACH
diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c
index f11717b41f..193c96a169 100644
--- a/bgpd/bgp_errors.c
+++ b/bgpd/bgp_errors.c
@@ -475,6 +475,12 @@ static struct log_ref ferr_bgp_err[] = {
.suggestion = "Get log files from router and open an issue",
},
{
+ .code = EC_BGP_NO_LL_ADDRESS_AVAILABLE,
+ .title = "BGP v6 peer with no LL address on outgoing interface",
+ .description = "BGP when using a v6 peer requires a v6 LL address to be configured on the outgoing interface as per RFC 4291 section 2.1",
+ .suggestion = "Add a v6 LL address to the outgoing interfaces as per RFC",
+ },
+ {
.code = END_FERR,
}
};
diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h
index 20056d382a..0b71af3fc6 100644
--- a/bgpd/bgp_errors.h
+++ b/bgpd/bgp_errors.h
@@ -101,6 +101,7 @@ enum bgp_log_refs {
EC_BGP_ROUTER_ID_SAME,
EC_BGP_INVALID_BGP_INSTANCE,
EC_BGP_INVALID_ROUTE,
+ EC_BGP_NO_LL_ADDRESS_AVAILABLE,
};
extern void bgp_error_init(void);
diff --git a/bgpd/bgp_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/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index d21e257cb5..dea1433f6d 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -1486,7 +1486,7 @@ DEFUN (no_router_bgp,
}
if (bgp->l3vni) {
- vty_out(vty, "%% Please unconfigure l3vni %u",
+ vty_out(vty, "%% Please unconfigure l3vni %u\n",
bgp->l3vni);
return CMD_WARNING_CONFIG_FAILED;
}
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index c0a9a38773..78eaac7806 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -882,6 +882,12 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote,
*/
if (!v6_ll_avail && if_is_loopback(ifp))
v6_ll_avail = true;
+ else {
+ flog_warn(
+ EC_BGP_NO_LL_ADDRESS_AVAILABLE,
+ "Interface: %s does not have a v6 LL address associated with it, waiting until one is created for it",
+ ifp->name);
+ }
} else
/* Link-local address. */
{
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 16488eb4a4..38a106359e 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1223,7 +1223,7 @@ int bgp_global_gr_init(struct bgp *bgp)
{
/*Event -> */
/*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
- GLOBAL_INVALID, GLOBAL_HELPER,
+ GLOBAL_GR, GLOBAL_HELPER,
/*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
GLOBAL_DISABLE, GLOBAL_INVALID
},
diff --git a/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/developer/logging.rst b/doc/developer/logging.rst
index 4e6fc04206..7046361204 100644
--- a/doc/developer/logging.rst
+++ b/doc/developer/logging.rst
@@ -163,6 +163,10 @@ Networking data types
- :c:union:`prefixptr` (dereference to get :c:struct:`prefix`)
- :c:union:`prefixconstptr` (dereference to get :c:struct:`prefix`)
+ Options:
+
+ ``%pFXh``: (address only) :frrfmtout:`1.2.3.0` / :frrfmtout:`fe80::1234`
+
.. frrfmt:: %pPSG4 (struct prefix_sg *)
:frrfmtout:`(*,1.2.3.4)`
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index b41181f4e9..6c1d9148d1 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -35,6 +35,9 @@ Installing Topotest Requirements
python2 -m pip install 'exabgp<4.0.0'
useradd -d /var/run/exabgp/ -s /bin/false exabgp
+ # To enable the gRPC topotest install:
+ python3 -m pip install grpcio grpcio-tools
+
Enable Coredumps
""""""""""""""""
diff --git a/doc/user/index.rst b/doc/user/index.rst
index cadf4cb9cf..5a018a5583 100644
--- a/doc/user/index.rst
+++ b/doc/user/index.rst
@@ -54,6 +54,7 @@ Protocols
ospf6d
pathd
pim
+ pimv6
pbr
ripd
ripngd
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index 1c3a0110ac..30363dfdf6 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -505,10 +505,12 @@ cause great confusion.
Display information about a S,G pair and how the RPF would be chosen. This
is especially useful if there are ECMP's available from the RPF lookup.
-.. clicmd:: show ip pim rp-info
+.. clicmd:: show ip pim [vrf NAME] rp-info [A.B.C.D/M] [json]
Display information about RP's that are configured on this router.
+ You can filter the output by specifying an arbitrary group.
+
.. clicmd:: show ip pim rpf
Display information about currently being used S,G's and their RPF lookup
diff --git a/doc/user/pimv6.rst b/doc/user/pimv6.rst
new file mode 100644
index 0000000000..e71cf4631c
--- /dev/null
+++ b/doc/user/pimv6.rst
@@ -0,0 +1,288 @@
+.. _pimv6:
+
+*****
+PIMv6
+*****
+
+PIMv6 -- Protocol Independent Multicast for IPv6
+
+*pim6d* supports pim-sm as well as MLD v1 and v2. PIMv6 is
+vrf aware and can work within the context of vrf's in order to
+do S,G mrouting.
+
+.. _starting-and-stopping-pim6d:
+
+Starting and Stopping pim6d
+===========================
+
+The default configuration file name of *pim6d*'s is :file:`pim6d.conf`. When
+invoked *pim6d* searches directory |INSTALL_PREFIX_ETC|. If
+:file:`pim6d.conf` is not there then next search current directory.
+
+*pim6d* requires zebra for proper operation. Additionally *pim6d* depends on
+routing properly setup and working in the network that it is working on.
+
+::
+
+ # zebra -d
+ # pim6d -d
+
+
+Please note that *zebra* must be invoked before *pim6d*.
+
+To stop *pim6d* please use::
+
+ kill `cat /var/run/pim6d.pid`
+
+Certain signals have special meanings to *pim6d*.
+
++---------+---------------------------------------------------------------------+
+| Signal | Meaning |
++=========+=====================================================================+
+| SIGUSR1 | Rotate the *pim6d* logfile |
++---------+---------------------------------------------------------------------+
+| SIGINT | *pim6d* sweeps all installed PIM mroutes then terminates gracefully.|
+| SIGTERM | |
++---------+---------------------------------------------------------------------+
+
+*pim6d* invocation options. Common options that can be specified
+(:ref:`common-invocation-options`).
+
+.. clicmd:: ipv6 pim rp X:X::X:X Y:Y::Y:Y/M
+
+ In order to use pimv6, it is necessary to configure a RP for join messages to
+ be sent to. Currently the only methodology to do this is via static rp
+ commands. All routers in the pimv6 network must agree on these values. The
+ first ipv6 address is the RP's address and the second value is the matching
+ prefix of group ranges covered. This command is vrf aware, to configure for
+ a vrf, enter the vrf submode.
+
+.. clicmd:: ipv6 pim rp X:X::X:X prefix-list WORD
+
+ This CLI helps in configuring RP address for a range of groups specified
+ by the prefix-list.
+
+.. clicmd:: ipv6 pim rp keep-alive-timer (1-65535)
+
+ Modify the time out value for a S,G flow from 1-65535 seconds at RP.
+ The normal keepalive period for the KAT(S,G) defaults to 210 seconds.
+ However, at the RP, the keepalive period must be at least the
+ Register_Suppression_Time, or the RP may time out the (S,G) state
+ before the next Null-Register arrives. Thus, the KAT(S,G) is set to
+ max(Keepalive_Period, RP_Keepalive_Period) when a Register-Stop is sent.
+ If choosing a value below 31 seconds be aware that some hardware platforms
+ cannot see data flowing in better than 30 second chunks. This command is
+ vrf aware, to configure for a vrf, enter the vrf submode.
+
+.. clicmd:: ipv6 pim spt-switchover infinity-and-beyond [prefix-list PLIST]
+
+ On the last hop router if it is desired to not switch over to the SPT tree
+ configure this command. Optional parameter prefix-list can be use to control
+ which groups to switch or not switch. If a group is PERMIT as per the
+ PLIST, then the SPT switchover does not happen for it and if it is DENY,
+ then the SPT switchover happens.
+ This command is vrf aware, to configure for a vrf,
+ enter the vrf submode.
+
+.. clicmd:: ipv6 pim join-prune-interval (1-65535)
+
+ Modify the join/prune interval that pim uses to the new value. Time is
+ specified in seconds. This command is vrf aware, to configure for a vrf,
+ enter the vrf submode. The default time is 60 seconds. If you enter
+ a value smaller than 60 seconds be aware that this can and will affect
+ convergence at scale.
+
+.. clicmd:: ipv6 pim keep-alive-timer (1-65535)
+
+ Modify the time out value for a S,G flow from 1-65535 seconds. If choosing
+ a value below 31 seconds be aware that some hardware platforms cannot see data
+ flowing in better than 30 second chunks. This command is vrf aware, to
+ configure for a vrf, enter the vrf submode.
+
+.. clicmd:: ipv6 pim packets (1-255)
+
+ When processing packets from a neighbor process the number of packets
+ incoming at one time before moving on to the next task. The default value is
+ 3 packets. This command is only useful at scale when you can possibly have
+ a large number of pim control packets flowing. This command is vrf aware, to
+ configure for a vrf, enter the vrf submode.
+
+.. clicmd:: ipv6 pim register-suppress-time (1-65535)
+
+ Modify the time that pim will register suppress a FHR will send register
+ notifications to the kernel. This command is vrf aware, to configure for a
+ vrf, enter the vrf submode.
+
+.. _pimv6-interface-configuration:
+
+PIMv6 Interface Configuration
+=============================
+
+PIMv6 interface commands allow you to configure an interface as either a Receiver
+or a interface that you would like to form pimv6 neighbors on. If the interface
+is in a vrf, enter the interface command with the vrf keyword at the end.
+
+.. clicmd:: ipv6 pim active-active
+
+ Turn on pim active-active configuration for a Vxlan interface. This
+ command will not do anything if you do not have the underlying ability
+ of a mlag implementation.
+
+.. clicmd:: ipv6 pim drpriority (1-4294967295)
+
+ Set the DR Priority for the interface. This command is useful to allow the
+ user to influence what node becomes the DR for a lan segment.
+
+.. clicmd:: ipv6 pim hello (1-65535) (1-65535)
+
+ Set the pim hello and hold interval for a interface.
+
+.. clicmd:: ipv6 pim
+
+ Tell pim that we would like to use this interface to form pim neighbors
+ over. Please note that this command does not enable the reception of MLD
+ reports on the interface. Refer to the next ``ipv6 mld`` command for MLD
+ management.
+
+.. clicmd:: ipv6 pim use-source X:X::X:X
+
+ If you have multiple addresses configured on a particular interface
+ and would like pim to use a specific source address associated with
+ that interface.
+
+.. clicmd:: ipv6 mld
+
+ Tell pim to receive MLD reports and Query on this interface. The default
+ version is v2. This command is useful on a LHR.
+
+.. clicmd:: ipv6 mld join X:X::X:X [Y:Y::Y:Y]
+
+ Join multicast group or source-group on an interface.
+
+.. clicmd:: ipv6 mld query-interval (1-65535)
+
+ Set the MLD query interval that PIM will use.
+
+.. clicmd:: ipv6 mld query-max-response-time (1-65535)
+
+ Set the MLD query response timeout value. If an report is not returned in
+ the specified time we will assume the S,G or \*,G has timed out.
+
+.. clicmd:: ipv6 mld version (1-2)
+
+ Set the MLD version used on this interface. The default value is 2.
+
+.. clicmd:: ipv6 multicast boundary oil WORD
+
+ Set a PIMv6 multicast boundary, based upon the WORD prefix-list. If a PIMv6
+ join or MLD report is received on this interface and the Group is denied by
+ the prefix-list, PIMv6 will ignore the join or report.
+
+.. clicmd:: ipv6 mld last-member-query-count (1-255)
+
+ Set the MLD last member query count. The default value is 2. 'no' form of
+ this command is used to configure back to the default value.
+
+.. clicmd:: ipv6 MLD last-member-query-interval (1-65535)
+
+ Set the MLD last member query interval in deciseconds. The default value is
+ 10 deciseconds. 'no' form of this command is used to to configure back to the
+ default value.
+
+.. clicmd:: ipv6 mroute INTERFACE X:X::X:X [Y:Y::Y:Y]
+
+ Set a static multicast route for a traffic coming on the current interface to
+ be forwarded on the given interface if the traffic matches the group address
+ and optionally the source address.
+
+.. _show-pimv6-information:
+
+Show PIMv6 Information
+======================
+
+All PIMv6 show commands are vrf aware and typically allow you to insert a
+specified vrf command if information is desired about a specific vrf. If no
+vrf is specified then the default vrf is assumed. Finally the special keyword
+'all' allows you to look at all vrfs for the command. Naming a vrf 'all' will
+cause great confusion.
+
+.. clicmd:: show ipv6 pim [vrf NAME] group-type [json]
+
+ Display SSM group ranges.
+
+.. clicmd:: show ipv6 pim interface
+
+ Display information about interfaces PIM is using.
+
+.. clicmd:: show ipv6 pim [vrf NAME] join [X:X::X:X [X:X::X:X]] [json]
+.. clicmd:: show ipv6 pim vrf all join [json]
+
+ Display information about PIM joins received. If one address is specified
+ then we assume it is the Group we are interested in displaying data on.
+ If the second address is specified then it is Source Group.
+
+.. clicmd:: show ipv6 pim [vrf NAME] local-membership [json]
+
+ Display information about PIM interface local-membership.
+
+.. clicmd:: show ipv6 pim [vrf NAME] neighbor [detail|WORD] [json]
+.. clicmd:: show ipv6 pim vrf all neighbor [detail|WORD] [json]
+
+ Display information about PIM neighbors.
+
+.. clicmd:: show ipv6 pim [vrf NAME] nexthop
+
+ Display information about pim nexthops that are being used.
+
+.. clicmd:: show ipv6 pim [vrf NAME] nexthop-lookup X:X::X:X X:X::X:X
+
+ Display information about a S,G pair and how the RPF would be chosen. This
+ is especially useful if there are ECMP's available from the RPF lookup.
+
+.. clicmd:: show ipv6 pim [vrf NAME] rp-info [json]
+.. clicmd:: show ipv6 pim vrf all rp-info [json]
+
+ Display information about RP's that are configured on this router.
+
+.. clicmd:: show ipv6 pim [vrf NAME] rpf [json]
+.. clicmd:: show ipv6 pim vrf all rpf [json]
+
+ Display information about currently being used S,G's and their RPF lookup
+ information. Additionally display some statistics about what has been
+ happening on the router.
+
+.. clicmd:: show ipv6 pim [vrf NAME] secondary
+
+ Display information about an interface and all the secondary addresses
+ associated with it.
+
+.. clicmd:: show ipv6 pim [vrf NAME] state [X:X::X:X [X:X::X:X]] [json]
+.. clicmd:: show ipv6 pim vrf all state [X:X::X:X [X:X::X:X]] [json]
+
+ Display information about known S,G's and incoming interface as well as the
+ OIL and how they were chosen.
+
+.. clicmd:: show ipv6 pim [vrf NAME] upstream [X:X::X:X [Y:Y::Y:Y]] [json]
+.. clicmd:: show ipv6 pim vrf all upstream [json]
+
+ Display upstream information about a S,G mroute. Allow the user to
+ specify sub Source and Groups that we are interested in.
+
+.. clicmd:: show ipv6 pim [vrf NAME] upstream-join-desired [json]
+
+ Display upstream information for S,G's and if we desire to
+ join the multicast tree
+
+.. clicmd:: show ipv6 pim [vrf NAME] upstream-rpf [json]
+
+ Display upstream information for S,G's and the RPF data associated with them.
+
+PIMv6 Debug Commands
+====================
+
+The debugging subsystem for PIMv6 behaves in accordance with how FRR handles
+debugging. You can specify debugging at the enable CLI mode as well as the
+configure CLI mode. If you specify debug commands in the configuration cli
+mode, the debug commands can be persistent across restarts of the FRR pim6d if
+the config was written out.
+
diff --git a/doc/user/subdir.am b/doc/user/subdir.am
index 31158cb5f7..14ace2c856 100644
--- a/doc/user/subdir.am
+++ b/doc/user/subdir.am
@@ -30,6 +30,7 @@ user_RSTFILES = \
doc/user/packet-dumps.rst \
doc/user/pathd.rst \
doc/user/pim.rst \
+ doc/user/pimv6.rst \
doc/user/ripd.rst \
doc/user/pbr.rst \
doc/user/ripngd.rst \
diff --git a/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/json.c b/lib/json.c
index 854a3d59d1..d85a21215c 100644
--- a/lib/json.c
+++ b/lib/json.c
@@ -74,6 +74,19 @@ void json_object_string_addv(struct json_object *obj, const char *key,
json_object_object_add(obj, key, json_object_new_stringv(fmt, args));
}
+void json_object_object_addv(struct json_object *parent,
+ struct json_object *child, const char *keyfmt,
+ va_list args)
+{
+ char *text, buf[256];
+
+ text = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), keyfmt, args);
+ json_object_object_add(parent, text, child);
+
+ if (text != buf)
+ XFREE(MTYPE_TMP, text);
+}
+
void json_object_int_add(struct json_object *obj, const char *key, int64_t i)
{
json_object_object_add(obj, key, json_object_new_int64(i));
diff --git a/lib/json.h b/lib/json.h
index fcaa84c816..78c3836515 100644
--- a/lib/json.h
+++ b/lib/json.h
@@ -116,6 +116,28 @@ static inline struct json_object *json_object_new_stringf(const char *fmt, ...)
return ret;
}
+/* NOTE: argument order differs! (due to varargs)
+ * json_object_object_add(parent, key, child)
+ * json_object_object_addv(parent, child, key, va)
+ * json_object_object_addf(parent, child, key, ...)
+ * (would be weird to have the child inbetween the format string and args)
+ */
+PRINTFRR(3, 0)
+extern void json_object_object_addv(struct json_object *parent,
+ struct json_object *child,
+ const char *keyfmt, va_list args);
+PRINTFRR(3, 4)
+static inline void json_object_object_addf(struct json_object *parent,
+ struct json_object *child,
+ const char *keyfmt, ...)
+{
+ va_list args;
+
+ va_start(args, keyfmt);
+ json_object_object_addv(parent, child, keyfmt, args);
+ va_end(args);
+}
+
#define JSON_STR "JavaScript Object Notation\n"
/* NOTE: json-c lib has following commit 316da85 which
diff --git a/lib/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/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index e2a6290035..dda1756214 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -39,6 +39,11 @@
#define GRPC_DEFAULT_PORT 50051
+
+// ------------------------------------------------------
+// File Local Variables
+// ------------------------------------------------------
+
/*
* NOTE: we can't use the FRR debugging infrastructure here since it uses
* atomics and C++ has a different atomics API. Enable gRPC debugging
@@ -78,7 +83,7 @@ class Candidates
{
// Delete candidates.
for (auto it = _cdb.begin(); it != _cdb.end(); it++)
- delete_candidate(&it->second);
+ delete_candidate(it->first);
}
struct candidate *create_candidate(void)
@@ -94,8 +99,14 @@ class Candidates
return c;
}
- void delete_candidate(struct candidate *c)
+ bool contains(uint64_t candidate_id)
{
+ return _cdb.count(candidate_id) > 0;
+ }
+
+ void delete_candidate(uint64_t candidate_id)
+ {
+ struct candidate *c = &_cdb[candidate_id];
char errmsg[BUFSIZ] = {0};
nb_config_free(c->config);
@@ -105,119 +116,89 @@ class Candidates
_cdb.erase(c->id);
}
- struct candidate *get_candidate(uint32_t id)
+ struct candidate *get_candidate(uint64_t id)
{
return _cdb.count(id) == 0 ? NULL : &_cdb[id];
}
private:
uint64_t _next_id = 0;
- std::map<uint32_t, struct candidate> _cdb;
+ std::map<uint64_t, struct candidate> _cdb;
};
+/*
+ * RpcStateBase is the common base class used to track a gRPC RPC.
+ */
class RpcStateBase
{
public:
- virtual ~RpcStateBase() = default;
- virtual CallState doCallback() = 0;
virtual void do_request(::frr::Northbound::AsyncService *service,
::grpc::ServerCompletionQueue *cq,
bool no_copy) = 0;
-};
-/*
- * The RPC state class is used to track the execution of an RPC.
- */
-template <typename Q, typename S> class NewRpcState : RpcStateBase
-{
- typedef void (frr::Northbound::AsyncService::*reqfunc_t)(
- ::grpc::ServerContext *, Q *,
- ::grpc::ServerAsyncResponseWriter<S> *,
- ::grpc::CompletionQueue *, ::grpc::ServerCompletionQueue *,
- void *);
- typedef void (frr::Northbound::AsyncService::*reqsfunc_t)(
- ::grpc::ServerContext *, Q *, ::grpc::ServerAsyncWriter<S> *,
- ::grpc::CompletionQueue *, ::grpc::ServerCompletionQueue *,
- void *);
+ RpcStateBase(const char *name) : name(name){};
- public:
- NewRpcState(Candidates *cdb, reqfunc_t rfunc,
- void (*cb)(NewRpcState<Q, S> *), const char *name)
- : requestf(rfunc), callback(cb), responder(&ctx),
- async_responder(&ctx), name(name), cdb(cdb){};
- NewRpcState(Candidates *cdb, reqsfunc_t rfunc,
- void (*cb)(NewRpcState<Q, S> *), const char *name)
- : requestsf(rfunc), callback(cb), responder(&ctx),
- async_responder(&ctx), name(name), cdb(cdb){};
-
- CallState doCallback() override
+ virtual ~RpcStateBase() = default;
+
+ CallState get_state() const
+ {
+ return state;
+ }
+
+ bool is_initial_process() const
+ {
+ /* Will always be true for Unary */
+ return entered_state == CREATE;
+ }
+
+ // Returns "more" status, if false caller can delete
+ bool run(frr::Northbound::AsyncService *service,
+ grpc::ServerCompletionQueue *cq)
{
- CallState enter_state = this->state;
- CallState new_state;
- if (enter_state == FINISH) {
- grpc_debug("%s RPC FINISH -> DELETED", name);
- new_state = FINISH;
- } else {
- grpc_debug("%s RPC: %s -> PROCESS", name,
- call_states[this->state]);
- new_state = PROCESS;
- }
/*
- * We are either in state CREATE, MORE or FINISH. If CREATE or
- * MORE move back to PROCESS, otherwise we are cleaning up
- * (FINISH) so leave it in that state. Run the callback on the
- * main threadmaster/pthread; and wait for expected transition
- * from main thread. If transition is to FINISH->DELETED.
- * delete us.
- *
- * We update the state prior to scheduling the callback which
- * may then update the state in the master pthread. Then we
- * obtain the lock in the condvar-check-loop as the callback
- * will be modifying updating the state value.
+ * We enter in either CREATE or MORE state, and transition to
+ * PROCESS state.
+ */
+ this->entered_state = this->state;
+ this->state = PROCESS;
+ grpc_debug("%s RPC: %s -> %s on grpc-io-thread", name,
+ call_states[this->entered_state],
+ call_states[this->state]);
+ /*
+ * We schedule the callback on the main pthread, and wait for
+ * the state to transition out of the PROCESS state. The new
+ * state will either be MORE or FINISH. It will always be FINISH
+ * for Unary RPCs.
*/
- this->state = new_state;
thread_add_event(main_master, c_callback, (void *)this, 0,
NULL);
+
pthread_mutex_lock(&this->cmux);
- while (this->state == new_state)
+ while (this->state == PROCESS)
pthread_cond_wait(&this->cond, &this->cmux);
pthread_mutex_unlock(&this->cmux);
- if (this->state == DELETED) {
- grpc_debug("%s RPC: -> [DELETED]", name);
- delete this;
- return DELETED;
- }
- return this->state;
- }
-
- void do_request(::frr::Northbound::AsyncService *service,
- ::grpc::ServerCompletionQueue *cq,
- bool no_copy) override
- {
- grpc_debug("%s, posting a request for: %s", __func__, name);
- if (requestf) {
- NewRpcState<Q, S> *copy =
- no_copy ? this
- : new NewRpcState(cdb, requestf,
- callback, name);
- (service->*requestf)(&copy->ctx, &copy->request,
- &copy->responder, cq, cq, copy);
- } else {
- NewRpcState<Q, S> *copy =
- no_copy ? this
- : new NewRpcState(cdb, requestsf,
- callback, name);
- (service->*requestsf)(&copy->ctx, &copy->request,
- &copy->async_responder, cq, cq,
- copy);
+ grpc_debug("%s RPC in %s on grpc-io-thread", name,
+ call_states[this->state]);
+
+ if (this->state == FINISH) {
+ /*
+ * Server is done (FINISH) so prep to receive a new
+ * request of this type. We could do this earlier but
+ * that would mean we could be handling multiple same
+ * type requests in parallel without limit.
+ */
+ this->do_request(service, cq, false);
}
+ return true;
}
+ protected:
+ virtual CallState run_mainthread(struct thread *thread) = 0;
static void c_callback(struct thread *thread)
{
- auto _tag = static_cast<NewRpcState<Q, S> *>(thread->arg);
+ auto _tag = static_cast<RpcStateBase *>(thread->arg);
/*
* We hold the lock until the callback finishes and has updated
* _tag->state, then we signal done and release.
@@ -225,11 +206,12 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase
pthread_mutex_lock(&_tag->cmux);
CallState enter_state = _tag->state;
- grpc_debug("%s RPC running on main thread", _tag->name);
+ grpc_debug("%s RPC: running %s on main thread", _tag->name,
+ call_states[enter_state]);
- _tag->callback(_tag);
+ _tag->state = _tag->run_mainthread(thread);
- grpc_debug("%s RPC: %s -> %s", _tag->name,
+ grpc_debug("%s RPC: %s -> %s [main thread]", _tag->name,
call_states[enter_state], call_states[_tag->state]);
pthread_cond_signal(&_tag->cond);
@@ -237,23 +219,118 @@ template <typename Q, typename S> class NewRpcState : RpcStateBase
return;
}
- const char *name;
grpc::ServerContext ctx;
+ pthread_mutex_t cmux = PTHREAD_MUTEX_INITIALIZER;
+ pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+ CallState state = CREATE;
+ CallState entered_state;
+
+ public:
+ const char *name;
+};
+
+/*
+ * The UnaryRpcState class is used to track the execution of a Unary RPC.
+ *
+ * Template Args:
+ * Q - the request type for a given unary RPC
+ * S - the response type for a given unary RPC
+ */
+template <typename Q, typename S> class UnaryRpcState : public RpcStateBase
+{
+ public:
+ typedef void (frr::Northbound::AsyncService::*reqfunc_t)(
+ ::grpc::ServerContext *, Q *,
+ ::grpc::ServerAsyncResponseWriter<S> *,
+ ::grpc::CompletionQueue *, ::grpc::ServerCompletionQueue *,
+ void *);
+
+ UnaryRpcState(Candidates *cdb, reqfunc_t rfunc,
+ grpc::Status (*cb)(UnaryRpcState<Q, S> *),
+ const char *name)
+ : RpcStateBase(name), cdb(cdb), requestf(rfunc), callback(cb),
+ responder(&ctx){};
+
+ void do_request(::frr::Northbound::AsyncService *service,
+ ::grpc::ServerCompletionQueue *cq,
+ bool no_copy) override
+ {
+ grpc_debug("%s, posting a request for: %s", __func__, name);
+ auto copy = no_copy ? this
+ : new UnaryRpcState(cdb, requestf, callback,
+ name);
+ (service->*requestf)(&copy->ctx, &copy->request,
+ &copy->responder, cq, cq, copy);
+ }
+
+ CallState run_mainthread(struct thread *thread) override
+ {
+ // Unary RPC are always finished, see "Unary" :)
+ grpc::Status status = this->callback(this);
+ responder.Finish(response, status, this);
+ return FINISH;
+ }
+
+ Candidates *cdb;
+
Q request;
S response;
grpc::ServerAsyncResponseWriter<S> responder;
- grpc::ServerAsyncWriter<S> async_responder;
- Candidates *cdb;
- void (*callback)(NewRpcState<Q, S> *);
+ grpc::Status (*callback)(UnaryRpcState<Q, S> *);
reqfunc_t requestf = NULL;
- reqsfunc_t requestsf = NULL;
+};
- pthread_mutex_t cmux = PTHREAD_MUTEX_INITIALIZER;
- pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- void *context = 0;
+/*
+ * The StreamRpcState class is used to track the execution of a Streaming RPC.
+ *
+ * Template Args:
+ * Q - the request type for a given streaming RPC
+ * S - the response type for a given streaming RPC
+ * X - the type used to track the streaming state
+ */
+template <typename Q, typename S, typename X>
+class StreamRpcState : public RpcStateBase
+{
+ public:
+ typedef void (frr::Northbound::AsyncService::*reqsfunc_t)(
+ ::grpc::ServerContext *, Q *, ::grpc::ServerAsyncWriter<S> *,
+ ::grpc::CompletionQueue *, ::grpc::ServerCompletionQueue *,
+ void *);
- CallState state = CREATE;
+ StreamRpcState(reqsfunc_t rfunc, bool (*cb)(StreamRpcState<Q, S, X> *),
+ const char *name)
+ : RpcStateBase(name), requestsf(rfunc), callback(cb),
+ async_responder(&ctx){};
+
+ void do_request(::frr::Northbound::AsyncService *service,
+ ::grpc::ServerCompletionQueue *cq,
+ bool no_copy) override
+ {
+ grpc_debug("%s, posting a request for: %s", __func__, name);
+ auto copy =
+ no_copy ? this
+ : new StreamRpcState(requestsf, callback, name);
+ (service->*requestsf)(&copy->ctx, &copy->request,
+ &copy->async_responder, cq, cq, copy);
+ }
+
+ CallState run_mainthread(struct thread *thread) override
+ {
+ if (this->callback(this))
+ return MORE;
+ else
+ return FINISH;
+ }
+
+ Q request;
+ S response;
+ grpc::ServerAsyncWriter<S> async_responder;
+
+ bool (*callback)(StreamRpcState<Q, S, X> *);
+ reqsfunc_t requestsf = NULL;
+
+ X context;
};
// ------------------------------------------------------
@@ -472,15 +549,11 @@ static grpc::Status get_path(frr::DataTree *dt, const std::string &path,
// RPC Callback Functions: run on main thread
// ------------------------------------------------------
-void HandleUnaryGetCapabilities(NewRpcState<frr::GetCapabilitiesRequest,
- frr::GetCapabilitiesResponse> *tag)
+grpc::Status HandleUnaryGetCapabilities(
+ UnaryRpcState<frr::GetCapabilitiesRequest, frr::GetCapabilitiesResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
// Response: string frr_version = 1;
tag->response.set_frr_version(FRR_VERSION);
@@ -506,30 +579,24 @@ void HandleUnaryGetCapabilities(NewRpcState<frr::GetCapabilitiesRequest,
tag->response.add_supported_encodings(frr::JSON);
tag->response.add_supported_encodings(frr::XML);
- /* Should we do this in the async process call? */
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
-
- /* Indicate we are done. */
- tag->state = FINISH;
+ return grpc::Status::OK;
}
-void HandleStreamingGet(NewRpcState<frr::GetRequest, frr::GetResponse> *tag)
-{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
+// Define the context variable type for this streaming handler
+typedef std::list<std::string> GetContextType;
- if (tag->state == FINISH) {
- delete static_cast<std::list<std::string> *>(tag->context);
- tag->state = DELETED;
- return;
- }
+bool HandleStreamingGet(
+ StreamRpcState<frr::GetRequest, frr::GetResponse, GetContextType> *tag)
+{
+ grpc_debug("%s: entered", __func__);
- if (!tag->context) {
- /* Creating, first time called for this RPC */
- auto mypaths = new std::list<std::string>();
- tag->context = mypaths;
+ auto mypathps = &tag->context;
+ if (tag->is_initial_process()) {
+ // Fill our context container first time through
+ grpc_debug("%s: initialize streaming state", __func__);
auto paths = tag->request.path();
for (const std::string &path : paths) {
- mypaths->push_back(std::string(path));
+ mypathps->push_back(std::string(path));
}
}
@@ -540,11 +607,9 @@ void HandleStreamingGet(NewRpcState<frr::GetRequest, frr::GetResponse> *tag)
// Request: bool with_defaults = 3;
bool with_defaults = tag->request.with_defaults();
- auto mypathps = static_cast<std::list<std::string> *>(tag->context);
if (mypathps->empty()) {
tag->async_responder.Finish(grpc::Status::OK, tag);
- tag->state = FINISH;
- return;
+ return false;
}
frr::GetResponse response;
@@ -562,86 +627,57 @@ void HandleStreamingGet(NewRpcState<frr::GetRequest, frr::GetResponse> *tag)
if (!status.ok()) {
tag->async_responder.WriteAndFinish(
response, grpc::WriteOptions(), status, tag);
- tag->state = FINISH;
- return;
+ return false;
}
mypathps->pop_back();
if (mypathps->empty()) {
tag->async_responder.WriteAndFinish(
response, grpc::WriteOptions(), grpc::Status::OK, tag);
- tag->state = FINISH;
+ return false;
} else {
tag->async_responder.Write(response, tag);
- tag->state = MORE;
+ return true;
}
}
-void HandleUnaryCreateCandidate(NewRpcState<frr::CreateCandidateRequest,
- frr::CreateCandidateResponse> *tag)
+grpc::Status HandleUnaryCreateCandidate(
+ UnaryRpcState<frr::CreateCandidateRequest, frr::CreateCandidateResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
struct candidate *candidate = tag->cdb->create_candidate();
- if (!candidate) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::RESOURCE_EXHAUSTED,
- "Can't create candidate configuration"),
- tag);
- } else {
- tag->response.set_candidate_id(candidate->id);
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- }
-
- tag->state = FINISH;
+ if (!candidate)
+ return grpc::Status(grpc::StatusCode::RESOURCE_EXHAUSTED,
+ "Can't create candidate configuration");
+ tag->response.set_candidate_id(candidate->id);
+ return grpc::Status::OK;
}
-void HandleUnaryDeleteCandidate(NewRpcState<frr::DeleteCandidateRequest,
- frr::DeleteCandidateResponse> *tag)
+grpc::Status HandleUnaryDeleteCandidate(
+ UnaryRpcState<frr::DeleteCandidateRequest, frr::DeleteCandidateResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
- // Request: uint32 candidate_id = 1;
uint32_t candidate_id = tag->request.candidate_id();
grpc_debug("%s(candidate_id: %u)", __func__, candidate_id);
- struct candidate *candidate = tag->cdb->get_candidate(candidate_id);
- if (!candidate) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::NOT_FOUND,
- "candidate configuration not found"),
- tag);
- } else {
- tag->cdb->delete_candidate(candidate);
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- }
- tag->state = FINISH;
+ if (!tag->cdb->contains(candidate_id))
+ return grpc::Status(grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+ tag->cdb->delete_candidate(candidate_id);
+ return grpc::Status::OK;
}
-void HandleUnaryUpdateCandidate(NewRpcState<frr::UpdateCandidateRequest,
- frr::UpdateCandidateResponse> *tag)
+grpc::Status HandleUnaryUpdateCandidate(
+ UnaryRpcState<frr::UpdateCandidateRequest, frr::UpdateCandidateResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
- // Request: uint32 candidate_id = 1;
uint32_t candidate_id = tag->request.candidate_id();
grpc_debug("%s(candidate_id: %u)", __func__, candidate_id);
@@ -649,58 +685,33 @@ void HandleUnaryUpdateCandidate(NewRpcState<frr::UpdateCandidateRequest,
struct candidate *candidate = tag->cdb->get_candidate(candidate_id);
if (!candidate)
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::NOT_FOUND,
- "candidate configuration not found"),
- tag);
- else if (candidate->transaction)
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::FAILED_PRECONDITION,
- "candidate is in the middle of a transaction"),
- tag);
- else if (nb_candidate_update(candidate->config) != NB_OK)
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::INTERNAL,
- "failed to update candidate configuration"),
- tag);
-
- else
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
+ return grpc::Status(grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
+ if (candidate->transaction)
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "candidate is in the middle of a transaction");
+ if (nb_candidate_update(candidate->config) != NB_OK)
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "failed to update candidate configuration");
- tag->state = FINISH;
+ return grpc::Status::OK;
}
-void HandleUnaryEditCandidate(
- NewRpcState<frr::EditCandidateRequest, frr::EditCandidateResponse> *tag)
+grpc::Status HandleUnaryEditCandidate(
+ UnaryRpcState<frr::EditCandidateRequest, frr::EditCandidateResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
- // Request: uint32 candidate_id = 1;
uint32_t candidate_id = tag->request.candidate_id();
grpc_debug("%s(candidate_id: %u)", __func__, candidate_id);
struct candidate *candidate = tag->cdb->get_candidate(candidate_id);
-
- if (!candidate) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::NOT_FOUND,
- "candidate configuration not found"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!candidate)
+ return grpc::Status(grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
struct nb_config *candidate_tmp = nb_config_dup(candidate->config);
@@ -710,15 +721,9 @@ void HandleUnaryEditCandidate(
pv.value().c_str()) != 0) {
nb_config_free(candidate_tmp);
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Failed to update \"" + pv.path()
- + "\""),
- tag);
-
- tag->state = FINISH;
- return;
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Failed to update \"" + pv.path() +
+ "\"");
}
}
@@ -726,36 +731,23 @@ void HandleUnaryEditCandidate(
for (const frr::PathValue &pv : pvs) {
if (yang_dnode_delete(candidate_tmp->dnode, pv.path()) != 0) {
nb_config_free(candidate_tmp);
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Failed to remove \"" + pv.path()
- + "\""),
- tag);
- tag->state = FINISH;
- return;
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Failed to remove \"" + pv.path() +
+ "\"");
}
}
// No errors, accept all changes.
nb_config_replace(candidate->config, candidate_tmp, false);
-
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
-
- tag->state = FINISH;
+ return grpc::Status::OK;
}
-void HandleUnaryLoadToCandidate(NewRpcState<frr::LoadToCandidateRequest,
- frr::LoadToCandidateResponse> *tag)
+grpc::Status HandleUnaryLoadToCandidate(
+ UnaryRpcState<frr::LoadToCandidateRequest, frr::LoadToCandidateResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
- // Request: uint32 candidate_id = 1;
uint32_t candidate_id = tag->request.candidate_id();
grpc_debug("%s(candidate_id: %u)", __func__, candidate_id);
@@ -765,59 +757,31 @@ void HandleUnaryLoadToCandidate(NewRpcState<frr::LoadToCandidateRequest,
// Request: DataTree config = 3;
auto config = tag->request.config();
-
struct candidate *candidate = tag->cdb->get_candidate(candidate_id);
-
- if (!candidate) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::NOT_FOUND,
- "candidate configuration not found"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!candidate)
+ return grpc::Status(grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
struct lyd_node *dnode = dnode_from_data_tree(&config, true);
- if (!dnode) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INTERNAL,
- "Failed to parse the configuration"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!dnode)
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "Failed to parse the configuration");
struct nb_config *loaded_config = nb_config_new(dnode);
-
if (load_type == frr::LoadToCandidateRequest::REPLACE)
nb_config_replace(candidate->config, loaded_config, false);
- else if (nb_config_merge(candidate->config, loaded_config, false)
- != NB_OK) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::INTERNAL,
- "Failed to merge the loaded configuration"),
- tag);
- tag->state = FINISH;
- return;
- }
+ else if (nb_config_merge(candidate->config, loaded_config, false) !=
+ NB_OK)
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "Failed to merge the loaded configuration");
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- tag->state = FINISH;
+ return grpc::Status::OK;
}
-void HandleUnaryCommit(
- NewRpcState<frr::CommitRequest, frr::CommitResponse> *tag)
+grpc::Status
+HandleUnaryCommit(UnaryRpcState<frr::CommitRequest, frr::CommitResponse> *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
// Request: uint32 candidate_id = 1;
uint32_t candidate_id = tag->request.candidate_id();
@@ -831,15 +795,9 @@ void HandleUnaryCommit(
// Find candidate configuration.
struct candidate *candidate = tag->cdb->get_candidate(candidate_id);
- if (!candidate) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::NOT_FOUND,
- "candidate configuration not found"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!candidate)
+ return grpc::Status(grpc::StatusCode::NOT_FOUND,
+ "candidate configuration not found");
int ret = NB_OK;
uint32_t transaction_id = 0;
@@ -848,29 +806,17 @@ void HandleUnaryCommit(
switch (phase) {
case frr::CommitRequest::PREPARE:
case frr::CommitRequest::ALL:
- if (candidate->transaction) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::FAILED_PRECONDITION,
- "candidate is in the middle of a transaction"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (candidate->transaction)
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "candidate is in the middle of a transaction");
break;
case frr::CommitRequest::ABORT:
case frr::CommitRequest::APPLY:
- if (!candidate->transaction) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::FAILED_PRECONDITION,
- "no transaction in progress"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!candidate->transaction)
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "no transaction in progress");
break;
default:
break;
@@ -950,53 +896,30 @@ void HandleUnaryCommit(
if (strlen(errmsg) > 0)
tag->response.set_error_message(errmsg);
- tag->responder.Finish(tag->response, status, tag);
- tag->state = FINISH;
+ return status;
}
-void HandleUnaryLockConfig(
- NewRpcState<frr::LockConfigRequest, frr::LockConfigResponse> *tag)
+grpc::Status HandleUnaryLockConfig(
+ UnaryRpcState<frr::LockConfigRequest, frr::LockConfigResponse> *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
- if (nb_running_lock(NB_CLIENT_GRPC, NULL)) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
- "running configuration is locked already"),
- tag);
- } else {
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- }
- tag->state = FINISH;
+ if (nb_running_lock(NB_CLIENT_GRPC, NULL))
+ return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
+ "running configuration is locked already");
+ return grpc::Status::OK;
}
-void HandleUnaryUnlockConfig(
- NewRpcState<frr::UnlockConfigRequest, frr::UnlockConfigResponse> *tag)
+grpc::Status HandleUnaryUnlockConfig(
+ UnaryRpcState<frr::UnlockConfigRequest, frr::UnlockConfigResponse> *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
- if (nb_running_unlock(NB_CLIENT_GRPC, NULL)) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(
- grpc::StatusCode::FAILED_PRECONDITION,
- "failed to unlock the running configuration"),
- tag);
- } else {
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- }
- tag->state = FINISH;
+ if (nb_running_unlock(NB_CLIENT_GRPC, NULL))
+ return grpc::Status(
+ grpc::StatusCode::FAILED_PRECONDITION,
+ "failed to unlock the running configuration");
+ return grpc::Status::OK;
}
static void list_transactions_cb(void *arg, int transaction_id,
@@ -1010,45 +933,34 @@ static void list_transactions_cb(void *arg, int transaction_id,
std::string(date), std::string(comment)));
}
-void HandleStreamingListTransactions(
- NewRpcState<frr::ListTransactionsRequest, frr::ListTransactionsResponse>
- *tag)
-{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- delete static_cast<std::list<std::tuple<
- int, std::string, std::string, std::string>> *>(
- tag->context);
- tag->state = DELETED;
- return;
- }
+// Define the context variable type for this streaming handler
+typedef std::list<std::tuple<int, std::string, std::string, std::string>>
+ ListTransactionsContextType;
- if (!tag->context) {
- /* Creating, first time called for this RPC */
- auto new_list =
- new std::list<std::tuple<int, std::string, std::string,
- std::string>>();
- tag->context = new_list;
- nb_db_transactions_iterate(list_transactions_cb, tag->context);
+bool HandleStreamingListTransactions(
+ StreamRpcState<frr::ListTransactionsRequest,
+ frr::ListTransactionsResponse,
+ ListTransactionsContextType> *tag)
+{
+ grpc_debug("%s: entered", __func__);
- new_list->push_back(std::make_tuple(
+ auto list = &tag->context;
+ if (tag->is_initial_process()) {
+ grpc_debug("%s: initialize streaming state", __func__);
+ // Fill our context container first time through
+ nb_db_transactions_iterate(list_transactions_cb, list);
+ list->push_back(std::make_tuple(
0xFFFF, std::string("fake client"),
std::string("fake date"), std::string("fake comment")));
- new_list->push_back(
- std::make_tuple(0xFFFE, std::string("fake client2"),
- std::string("fake date"),
- std::string("fake comment2")));
+ list->push_back(std::make_tuple(0xFFFE,
+ std::string("fake client2"),
+ std::string("fake date"),
+ std::string("fake comment2")));
}
- auto list = static_cast<std::list<
- std::tuple<int, std::string, std::string, std::string>> *>(
- tag->context);
-
if (list->empty()) {
tag->async_responder.Finish(grpc::Status::OK, tag);
- tag->state = FINISH;
- return;
+ return false;
}
auto item = list->back();
@@ -1071,22 +983,18 @@ void HandleStreamingListTransactions(
if (list->empty()) {
tag->async_responder.WriteAndFinish(
response, grpc::WriteOptions(), grpc::Status::OK, tag);
- tag->state = FINISH;
+ return false;
} else {
tag->async_responder.Write(response, tag);
- tag->state = MORE;
+ return true;
}
}
-void HandleUnaryGetTransaction(NewRpcState<frr::GetTransactionRequest,
- frr::GetTransactionResponse> *tag)
+grpc::Status HandleUnaryGetTransaction(
+ UnaryRpcState<frr::GetTransactionRequest, frr::GetTransactionResponse>
+ *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
// Request: uint32 transaction_id = 1;
uint32_t transaction_id = tag->request.transaction_id();
@@ -1102,15 +1010,9 @@ void HandleUnaryGetTransaction(NewRpcState<frr::GetTransactionRequest,
// Load configuration from the transactions database.
nb_config = nb_db_transaction_load(transaction_id);
- if (!nb_config) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Transaction not found"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!nb_config)
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Transaction not found");
// Response: DataTree config = 1;
auto config = tag->response.mutable_config();
@@ -1121,29 +1023,19 @@ void HandleUnaryGetTransaction(NewRpcState<frr::GetTransactionRequest,
encoding2lyd_format(encoding), with_defaults)
!= 0) {
nb_config_free(nb_config);
- tag->responder.Finish(tag->response,
- grpc::Status(grpc::StatusCode::INTERNAL,
- "Failed to dump data"),
- tag);
- tag->state = FINISH;
- return;
+ return grpc::Status(grpc::StatusCode::INTERNAL,
+ "Failed to dump data");
}
nb_config_free(nb_config);
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- tag->state = FINISH;
+ return grpc::Status::OK;
}
-void HandleUnaryExecute(
- NewRpcState<frr::ExecuteRequest, frr::ExecuteResponse> *tag)
+grpc::Status HandleUnaryExecute(
+ UnaryRpcState<frr::ExecuteRequest, frr::ExecuteResponse> *tag)
{
- grpc_debug("%s: state: %s", __func__, call_states[tag->state]);
-
- if (tag->state == FINISH) {
- tag->state = DELETED;
- return;
- }
+ grpc_debug("%s: entered", __func__);
struct nb_node *nb_node;
struct list *input_list;
@@ -1158,26 +1050,14 @@ void HandleUnaryExecute(
grpc_debug("%s(path: \"%s\")", __func__, xpath);
- if (tag->request.path().empty()) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Data path is empty"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (tag->request.path().empty())
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Data path is empty");
nb_node = nb_node_find(xpath);
- if (!nb_node) {
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
- "Unknown data path"),
- tag);
- tag->state = FINISH;
- return;
- }
+ if (!nb_node)
+ return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
+ "Unknown data path");
input_list = yang_data_list_new();
output_list = yang_data_list_new();
@@ -1199,12 +1079,7 @@ void HandleUnaryExecute(
list_delete(&input_list);
list_delete(&output_list);
- tag->responder.Finish(
- tag->response,
- grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed"),
- tag);
- tag->state = FINISH;
- return;
+ return grpc::Status(grpc::StatusCode::INTERNAL, "RPC failed");
}
// Process output parameters.
@@ -1219,8 +1094,7 @@ void HandleUnaryExecute(
list_delete(&input_list);
list_delete(&output_list);
- tag->responder.Finish(tag->response, grpc::Status::OK, tag);
- tag->state = FINISH;
+ return grpc::Status::OK;
}
// ------------------------------------------------------
@@ -1230,18 +1104,19 @@ void HandleUnaryExecute(
#define REQUEST_NEWRPC(NAME, cdb) \
do { \
- auto _rpcState = new NewRpcState<frr::NAME##Request, \
- frr::NAME##Response>( \
+ auto _rpcState = new UnaryRpcState<frr::NAME##Request, \
+ frr::NAME##Response>( \
(cdb), &frr::Northbound::AsyncService::Request##NAME, \
&HandleUnary##NAME, #NAME); \
_rpcState->do_request(&service, cq.get(), true); \
} while (0)
-#define REQUEST_NEWRPC_STREAMING(NAME, cdb) \
+#define REQUEST_NEWRPC_STREAMING(NAME) \
do { \
- auto _rpcState = new NewRpcState<frr::NAME##Request, \
- frr::NAME##Response>( \
- (cdb), &frr::Northbound::AsyncService::Request##NAME, \
+ auto _rpcState = new StreamRpcState<frr::NAME##Request, \
+ frr::NAME##Response, \
+ NAME##ContextType>( \
+ &frr::Northbound::AsyncService::Request##NAME, \
&HandleStreaming##NAME, #NAME); \
_rpcState->do_request(&service, cq.get(), true); \
} while (0)
@@ -1280,7 +1155,7 @@ static void *grpc_pthread_start(void *arg)
grpc_running = true;
- /* Schedule all RPC handlers */
+ /* Schedule unary RPC handlers */
REQUEST_NEWRPC(GetCapabilities, NULL);
REQUEST_NEWRPC(CreateCandidate, &candidates);
REQUEST_NEWRPC(DeleteCandidate, &candidates);
@@ -1292,8 +1167,10 @@ static void *grpc_pthread_start(void *arg)
REQUEST_NEWRPC(LockConfig, NULL);
REQUEST_NEWRPC(UnlockConfig, NULL);
REQUEST_NEWRPC(Execute, NULL);
- REQUEST_NEWRPC_STREAMING(Get, NULL);
- REQUEST_NEWRPC_STREAMING(ListTransactions, NULL);
+
+ /* Schedule streaming RPC handlers */
+ REQUEST_NEWRPC_STREAMING(Get);
+ REQUEST_NEWRPC_STREAMING(ListTransactions);
zlog_notice("gRPC server listening on %s",
server_address.str().c_str());
@@ -1301,7 +1178,7 @@ static void *grpc_pthread_start(void *arg)
/* Process inbound RPCs */
bool ok;
void *tag;
- while (grpc_running) {
+ while (true) {
if (!cq->Next(&tag, &ok)) {
grpc_debug("%s: CQ empty exiting", __func__);
break;
@@ -1310,25 +1187,18 @@ static void *grpc_pthread_start(void *arg)
grpc_debug("%s: got next from CQ tag: %p ok: %d", __func__, tag,
ok);
- if (!ok || !grpc_running) {
+ if (!ok) {
delete static_cast<RpcStateBase *>(tag);
break;
}
RpcStateBase *rpc = static_cast<RpcStateBase *>(tag);
- CallState state = rpc->doCallback();
- grpc_debug("%s: callback returned RPC State: %s", __func__,
- call_states[state]);
-
- /*
- * Our side is done (FINISH) receive new requests of this type
- * We could do this earlier but that would mean we could be
- * handling multiple same type requests in parallel. We expect
- * to be called back once more in the FINISH state (from the
- * user indicating Finish() for cleanup.
- */
- if (state == FINISH && grpc_running)
- rpc->do_request(&service, cq.get(), false);
+ if (rpc->get_state() != FINISH)
+ rpc->run(&service, cq.get());
+ else {
+ grpc_debug("%s RPC FINISH -> [delete]", rpc->name);
+ delete rpc;
+ }
}
/* This was probably done for us to get here, but let's be safe */
@@ -1400,6 +1270,11 @@ static int frr_grpc_finish(void)
grpc_debug("%s: joining and destroy grpc thread", __func__);
pthread_join(fpt->thread, NULL);
frr_pthread_destroy(fpt);
+
+ // Fix protobuf 'memory leaks' during shutdown.
+ // https://groups.google.com/g/protobuf/c/4y_EmQiCGgs
+ google::protobuf::ShutdownProtobufLibrary();
+
return 0;
}
diff --git a/lib/prefix.c b/lib/prefix.c
index 90ab48a13b..89c5be8f38 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -1071,6 +1071,26 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
return str;
}
+static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu)
+{
+ const struct prefix *p = pu.p;
+ char buf[PREFIX2STR_BUFFER];
+
+ switch (p->family) {
+ case AF_INET:
+ case AF_INET6:
+ inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf));
+ return bputs(fbuf, buf);
+
+ case AF_ETHERNET:
+ prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf));
+ return bputs(fbuf, buf);
+
+ default:
+ return bprintfrr(fbuf, "{prefix.af=%dPF}", p->family);
+ }
+}
+
void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr,
char *buf, int buf_size)
{
@@ -1458,13 +1478,24 @@ printfrr_ext_autoreg_p("FX", printfrr_pfx);
static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea,
const void *ptr)
{
- char cbuf[PREFIX_STRLEN];
+ bool host_only = false;
+
+ if (ea->fmt[0] == 'h') {
+ ea->fmt++;
+ host_only = true;
+ }
if (!ptr)
return bputs(buf, "(null)");
- prefix2str(ptr, cbuf, sizeof(cbuf));
- return bputs(buf, cbuf);
+ if (host_only)
+ return prefixhost2str(buf, (struct prefix *)ptr);
+ else {
+ char cbuf[PREFIX_STRLEN];
+
+ prefix2str(ptr, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
+ }
}
printfrr_ext_autoreg_p("PSG4", printfrr_psg);
diff --git a/lib/routemap.c b/lib/routemap.c
index 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/wheel.c b/lib/wheel.c
index 463410bea4..cdf738a137 100644
--- a/lib/wheel.c
+++ b/lib/wheel.c
@@ -40,7 +40,6 @@ static void wheel_timer_thread_helper(struct thread *t)
void *data;
wheel = THREAD_ARG(t);
- THREAD_OFF(wheel->timer);
wheel->curr_slot += wheel->slots_to_skip;
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/pim6_main.c b/pimd/pim6_main.c
index 8c7fca174c..ed53924616 100644
--- a/pimd/pim6_main.c
+++ b/pimd/pim6_main.c
@@ -178,8 +178,7 @@ int main(int argc, char **argv, char **envp)
pim_route_map_init();
#endif
- /* pim_init(); */
- pim_cmd_init();
+ pim_init();
/*
* Initialize zclient "update" and "lookup" sockets
*/
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 3b3d06e791..2a6d03fb91 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,21 @@ 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;
+ bool found_ifname = false;
+ json_object *json = NULL;
- igmp_stats_init(&rx_stats);
+ igmp_stats_init(&igmp_stats);
+
+ if (uj)
+ json = json_object_new_object();
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,50 +1308,119 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
if (ifname && strcmp(ifname, ifp->name))
continue;
+ found_ifname = true;
+
+ 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;
+ igmp_stats.peak_groups += pim_ifp->igmp_peak_group_count;
+
+
+ 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) {
- json_object *json = NULL;
- json_object *json_row = NULL;
- json = json_object_new_object();
- json_row = json_object_new_object();
+ if (!found_ifname) {
+ if (uj)
+ vty_json(vty, json);
+ else
+ vty_out(vty, "%% No such interface\n");
+ return;
+ }
- 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);
+ if (uj) {
+ json_object *json_row = json_object_new_object();
+
+ json_object_string_add(json_row, "name",
+ ifname ? ifname : "global");
+ 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, "totalReceivedMessages",
+ igmp_stats.total_recv_messages);
+ json_object_int_add(json_row, "peakGroups",
+ igmp_stats.peak_groups);
+ 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_int_add(json_row, "generalQueriesSent",
+ igmp_stats.general_queries_sent);
+ json_object_int_add(json_row, "groupQueriesSent",
+ igmp_stats.group_queries_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, "total received messages : %u\n",
+ igmp_stats.total_recv_messages);
+ 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, "general queries sent : %u\n",
+ igmp_stats.general_queries_sent);
+ vty_out(vty, "group queries sent : %u\n",
+ igmp_stats.group_queries_sent);
+ vty_out(vty, "peak groups : %u\n",
+ igmp_stats.peak_groups);
+ 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 +1747,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 +2103,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 +2113,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 +4067,9 @@ 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;
+ pim_ifp->igmp_peak_group_count = 0;
}
return CMD_SUCCESS;
@@ -5313,39 +5400,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 +5461,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 +6064,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..bab73eae86 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,10 @@ 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;
+ uint32_t igmp_peak_group_count;
+
struct {
bool enabled;
uint32_t min_rx;
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index 956ab0d67c..f9fb8cf094 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -872,7 +872,7 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr,
address of the join message is our primary address.
*/
if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
- zlog_warn("%s: Assert Loser recv Join%s from %pI4 on %s",
+ zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
__func__, ch->sg_str, &neigh_addr, ifp->name);
assert_action_a5(ch);
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 5cdefd2828..aa6b30c624 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;
}
@@ -737,13 +909,10 @@ static void pim_igmp_general_query(struct thread *t)
querier_str, dst_str, igmp->interface->name);
}
- igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, igmp->fd,
- igmp->interface->name, query_buf, sizeof(query_buf),
- 0 /* num_sources */, dst_addr, group_addr,
- pim_ifp->gm_query_max_response_time_dsec,
- 1 /* s_flag: always set for general queries */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
+ sizeof(query_buf), 0 /* num_sources */, dst_addr,
+ group_addr, pim_ifp->gm_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */, igmp);
pim_igmp_general_query_on(igmp);
}
@@ -808,6 +977,8 @@ static void igmp_group_free(struct gm_group *group)
static void igmp_group_count_incr(struct pim_interface *pim_ifp)
{
+ uint32_t group_count = listcount(pim_ifp->gm_group_list);
+
++pim_ifp->pim->igmp_group_count;
if (pim_ifp->pim->igmp_group_count
== pim_ifp->pim->igmp_watermark_limit) {
@@ -816,6 +987,9 @@ static void igmp_group_count_incr(struct pim_interface *pim_ifp)
pim_ifp->pim->igmp_group_count,
VRF_LOGNAME(pim_ifp->pim->vrf));
}
+
+ if (pim_ifp->igmp_peak_group_count < group_count)
+ pim_ifp->igmp_peak_group_count = group_count;
}
static void igmp_group_count_decr(struct pim_interface *pim_ifp)
@@ -990,7 +1164,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 +1190,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;
@@ -1282,23 +1456,29 @@ struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
return group;
}
-void igmp_send_query(int igmp_version, struct gm_group *group, int fd,
- const char *ifname, char *query_buf, int query_buf_size,
- int num_sources, struct in_addr dst_addr,
- struct in_addr group_addr,
+void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
+ int query_buf_size, int num_sources,
+ struct in_addr dst_addr, struct in_addr group_addr,
int query_max_response_time_dsec, uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval)
+ struct gm_sock *igmp)
{
+ if (pim_addr_is_any(group_addr) &&
+ ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
+ igmp->igmp_stats.general_queries_sent++;
+ else if (group)
+ igmp->igmp_stats.group_queries_sent++;
+
if (igmp_version == 3) {
- igmp_v3_send_query(group, fd, ifname, query_buf, query_buf_size,
- num_sources, dst_addr, group_addr,
+ igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
+ query_buf, query_buf_size, num_sources,
+ dst_addr, group_addr,
query_max_response_time_dsec, s_flag,
- querier_robustness_variable,
- querier_query_interval);
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
} else if (igmp_version == 2) {
- igmp_v2_send_query(group, fd, ifname, query_buf, dst_addr,
- group_addr, query_max_response_time_dsec);
+ igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
+ query_buf, dst_addr, group_addr,
+ query_max_response_time_dsec);
}
}
@@ -1329,13 +1509,10 @@ void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
char query_buf[query_buf_size];
- igmp_send_query(igmp_ver, 0 /* igmp_group */, igmp->fd,
- igmp->interface->name, query_buf,
- sizeof(query_buf), 0 /* num_sources */,
- dst_addr, group_addr,
- pim_ifp->gm_query_max_response_time_dsec,
- 1 /* s_flag: always set for general queries */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query(
+ igmp_ver, 0 /* igmp_group */, query_buf,
+ sizeof(query_buf), 0 /* num_sources */, dst_addr,
+ group_addr, pim_ifp->gm_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */, igmp);
}
}
diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h
index 5c35996634..a642469f27 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,
@@ -220,13 +240,11 @@ void igmp_startup_mode_on(struct gm_sock *igmp);
void igmp_group_timer_on(struct gm_group *group, long interval_msec,
const char *ifname);
-void igmp_send_query(int igmp_version, struct gm_group *group, int fd,
- const char *ifname, char *query_buf, int query_buf_size,
- int num_sources, struct in_addr dst_addr,
- struct in_addr group_addr,
+void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
+ int query_buf_size, int num_sources,
+ struct in_addr dst_addr, struct in_addr group_addr,
int query_max_response_time_dsec, uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval);
+ struct gm_sock *igmp);
void igmp_group_delete(struct gm_group *group);
void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver);
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..1d51104687 100644
--- a/pimd/pim_igmp_stats.c
+++ b/pimd/pim_igmp_stats.c
@@ -43,4 +43,14 @@ 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->peak_groups += b->peak_groups;
+ 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;
+ a->general_queries_sent += b->general_queries_sent;
+ a->group_queries_sent += b->group_queries_sent;
+ a->total_recv_messages += b->query_v1 + b->query_v2 + b->query_v3 +
+ b->report_v1 + b->report_v2 + b->report_v3 +
+ b->leave_v2 + b->mtrace_rsp + b->mtrace_req;
}
diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h
index a70a433557..8c66986e03 100644
--- a/pimd/pim_igmp_stats.h
+++ b/pimd/pim_igmp_stats.h
@@ -23,16 +23,24 @@
#include <zebra.h>
struct igmp_stats {
- uint32_t query_v1;
- uint32_t query_v2;
- uint32_t query_v3;
- uint32_t report_v1;
- uint32_t report_v2;
- uint32_t report_v3;
- uint32_t leave_v2;
- uint32_t mtrace_rsp;
- uint32_t mtrace_req;
- uint32_t unsupported;
+ uint32_t query_v1;
+ uint32_t query_v2;
+ uint32_t query_v3;
+ uint32_t report_v1;
+ uint32_t report_v2;
+ uint32_t report_v3;
+ uint32_t leave_v2;
+ uint32_t mtrace_rsp;
+ uint32_t mtrace_req;
+ uint32_t unsupported;
+ uint32_t peak_groups;
+ uint32_t total_groups;
+ uint32_t total_source_groups;
+ uint32_t joins_sent;
+ uint32_t joins_failed;
+ uint32_t general_queries_sent;
+ uint32_t group_queries_sent;
+ uint32_t total_recv_messages;
};
#if PIM_IPV == 4
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
index a7c7c99ebf..34cda25963 100644
--- a/pimd/pim_igmpv2.c
+++ b/pimd/pim_igmpv2.c
@@ -24,6 +24,7 @@
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
+#include "pim_ssm.h"
#include "pim_str.h"
#include "pim_time.h"
#include "pim_util.h"
@@ -107,10 +108,13 @@ int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
{
struct interface *ifp = igmp->interface;
struct in_addr group_addr;
+ struct pim_interface *pim_ifp;
char group_str[INET_ADDRSTRLEN];
on_trace(__func__, igmp->interface, from);
+ pim_ifp = ifp->info;
+
if (igmp->mtrace_only)
return 0;
@@ -130,7 +134,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));
@@ -142,6 +146,23 @@ int igmp_v2_recv_report(struct gm_sock *igmp, struct in_addr from,
}
/*
+ * RFC 4604
+ * section 2.2.1
+ * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
+ * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
+ * the SSM range.
+ */
+ if (pim_is_grp_ssm(pim_ifp->pim, group_addr)) {
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug(
+ "Ignoring IGMPv2 group record %pI4 from %s on %s exclude mode in SSM range",
+ &group_addr.s_addr, from_str, ifp->name);
+ }
+ return -1;
+ }
+
+
+ /*
* RFC 3376
* 7.3.2. In the Presence of Older Version Group Members
*
@@ -221,7 +242,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..b6114f9ead 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -32,6 +32,7 @@
#include "pim_time.h"
#include "pim_zebra.h"
#include "pim_oil.h"
+#include "pim_ssm.h"
static void group_retransmit_timer_on(struct gm_group *group);
static long igmp_group_timer_remain_msec(struct gm_group *group);
@@ -984,12 +985,10 @@ static void igmp_send_query_group(struct gm_group *group, char *query_buf,
for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
igmp_send_query(
- pim_ifp->igmp_version, group, igmp->fd, ifp->name,
- query_buf, query_buf_size, num_sources,
- group->group_addr, group->group_addr,
+ pim_ifp->igmp_version, group, query_buf, query_buf_size,
+ num_sources, group->group_addr, group->group_addr,
pim_ifp->gm_specific_query_max_response_time_dsec,
- s_flag, igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ s_flag, igmp);
}
}
@@ -1822,6 +1821,64 @@ void igmp_v3_recv_query(struct gm_sock *igmp, const char *from_str,
} /* s_flag is clear: timer updates */
}
+static bool igmp_pkt_grp_addr_ok(struct interface *ifp, const char *from_str,
+ struct in_addr grp, int rec_type)
+{
+ struct pim_interface *pim_ifp;
+ struct in_addr grp_addr;
+
+ pim_ifp = ifp->info;
+
+ /* determine filtering status for group */
+ if (pim_is_group_filtered(pim_ifp, &grp)) {
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug(
+ "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
+ &grp.s_addr, from_str, ifp->name,
+ pim_ifp->boundary_oil_plist);
+ }
+ return false;
+ }
+
+ /*
+ * If we receive a igmp report with the group in 224.0.0.0/24
+ * then we should ignore it
+ */
+
+ grp_addr.s_addr = ntohl(grp.s_addr);
+
+ if (pim_is_group_224_0_0_0_24(grp_addr)) {
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug(
+ "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24",
+ &grp.s_addr, from_str, ifp->name);
+ }
+ return false;
+ }
+
+ /*
+ * RFC 4604
+ * section 2.2.1
+ * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
+ * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
+ * the SSM range.
+ */
+ if (pim_is_grp_ssm(pim_ifp->pim, grp)) {
+ switch (rec_type) {
+ case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug(
+ "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range",
+ &grp.s_addr, from_str, ifp->name);
+ }
+ return false;
+ }
+ }
+
+ return true;
+}
+
int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
const char *from_str, char *igmp_msg, int igmp_msg_len)
{
@@ -1830,14 +1887,10 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
struct interface *ifp = igmp->interface;
int i;
- int local_ncb = 0;
- struct pim_interface *pim_ifp;
if (igmp->mtrace_only)
return 0;
- pim_ifp = igmp->interface->info;
-
if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
zlog_warn(
"Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
@@ -1854,7 +1907,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));
@@ -1882,9 +1935,6 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
int rec_auxdatalen;
int rec_num_sources;
int j;
- struct prefix lncb;
- struct prefix g;
- bool filtered = false;
if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
> report_pastend) {
@@ -1942,31 +1992,7 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
} /* for (sources) */
- lncb.family = AF_INET;
- lncb.u.prefix4.s_addr = 0x000000E0;
- lncb.prefixlen = 24;
-
- g.family = AF_INET;
- g.u.prefix4 = rec_group;
- g.prefixlen = IPV4_MAX_BITLEN;
-
- /* determine filtering status for group */
- filtered = pim_is_group_filtered(ifp->info, &rec_group);
-
- if (PIM_DEBUG_IGMP_PACKETS && filtered)
- zlog_debug(
- "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
- &rec_group, from_str, ifp->name,
- pim_ifp->boundary_oil_plist);
-
- /*
- * If we receive a igmp report with the group in 224.0.0.0/24
- * then we should ignore it
- */
- if (prefix_match(&lncb, &g))
- local_ncb = 1;
-
- if (!local_ncb && !filtered)
+ if (igmp_pkt_grp_addr_ok(ifp, from_str, rec_group, rec_type))
switch (rec_type) {
case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
igmpv3_report_isin(igmp, from, rec_group,
@@ -2006,7 +2032,6 @@ int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
group_record +=
8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
- local_ncb = 0;
} /* for (group records) */
diff --git a/pimd/pim_join.c b/pimd/pim_join.c
index 929beea26b..2c11d5d13f 100644
--- a/pimd/pim_join.c
+++ b/pimd/pim_join.c
@@ -43,14 +43,10 @@
#include "pim_util.h"
#include "pim_ssm.h"
-static void on_trace(const char *label, struct interface *ifp,
- struct in_addr src)
+static void on_trace(const char *label, struct interface *ifp, pim_addr src)
{
- if (PIM_DEBUG_PIM_TRACE) {
- char src_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
- zlog_debug("%s: from %s on %s", label, src_str, ifp->name);
- }
+ if (PIM_DEBUG_PIM_TRACE)
+ zlog_debug("%s: from %pPA on %s", label, &src, ifp->name);
}
static void recv_join(struct interface *ifp, struct pim_neighbor *neigh,
@@ -422,6 +418,7 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
size_t packet_left = 0;
size_t packet_size = 0;
size_t group_size = 0;
+ pim_addr rpf_addr;
if (rpf->source_nexthop.interface)
pim_ifp = rpf->source_nexthop.interface->info;
@@ -430,8 +427,9 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
return -1;
}
- on_trace(__func__, rpf->source_nexthop.interface,
- rpf->rpf_addr.u.prefix4);
+ rpf_addr = pim_addr_from_prefix(&rpf->rpf_addr);
+
+ on_trace(__func__, rpf->source_nexthop.interface, rpf_addr);
if (!pim_ifp) {
zlog_warn("%s: multicast not enabled on interface %s", __func__,
@@ -439,15 +437,12 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
return -1;
}
- if (rpf->rpf_addr.u.prefix4.s_addr == INADDR_ANY) {
- if (PIM_DEBUG_PIM_J_P) {
- char dst_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4,
- dst_str, sizeof(dst_str));
- zlog_debug("%s: upstream=%s is myself on interface %s",
- __func__, dst_str,
- rpf->source_nexthop.interface->name);
- }
+ if (pim_addr_is_any(rpf_addr)) {
+ if (PIM_DEBUG_PIM_J_P)
+ zlog_debug(
+ "%s: upstream=%pPA is myself on interface %s",
+ __func__, &rpf_addr,
+ rpf->source_nexthop.interface->name);
return 0;
}
@@ -468,8 +463,8 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
memset(msg, 0, sizeof(*msg));
- pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg->addr,
- rpf->rpf_addr.u.prefix4);
+ pim_msg_addr_encode_ucast((uint8_t *)&msg->addr,
+ rpf_addr);
msg->reserved = 0;
msg->holdtime = htons(PIM_JP_HOLDTIME);
@@ -485,15 +480,11 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
packet_left = rpf->source_nexthop.interface->mtu - 24;
packet_left -= packet_size;
}
- if (PIM_DEBUG_PIM_J_P) {
- char dst_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4,
- dst_str, sizeof(dst_str));
+ if (PIM_DEBUG_PIM_J_P)
zlog_debug(
- "%s: sending (G)=%pPAs to upstream=%s on interface %s",
- __func__, &group->group, dst_str,
+ "%s: sending (G)=%pPAs to upstream=%pPA on interface %s",
+ __func__, &group->group, &rpf_addr,
rpf->source_nexthop.interface->name);
- }
group_size = pim_msg_get_jp_group_size(group->sources);
if (group_size > packet_left) {
@@ -513,8 +504,8 @@ int pim_joinprune_send(struct pim_rpf *rpf, struct list *groups)
msg = (struct pim_jp *)pim_msg;
memset(msg, 0, sizeof(*msg));
- pim_msg_addr_encode_ipv4_ucast((uint8_t *)&msg->addr,
- rpf->rpf_addr.u.prefix4);
+ pim_msg_addr_encode_ucast((uint8_t *)&msg->addr,
+ rpf_addr);
msg->reserved = 0;
msg->holdtime = htons(PIM_JP_HOLDTIME);
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..35c8469090 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));
@@ -992,12 +993,14 @@ int pim_ecmp_nexthop_lookup(struct pim_instance *pim,
if (!nbr && !if_is_loopback(ifp)) {
if (i == mod_val)
mod_val++;
- i++;
if (PIM_DEBUG_PIM_NHT)
zlog_debug(
- "%s: NBR not found on input interface %s(%s) (RPF for source %pPA)",
- __func__, ifp->name,
- pim->vrf->name, &src_addr);
+ "%s: NBR (%pFXh) not found on input interface %s(%s) (RPF for source %pPA)",
+ __func__,
+ &nexthop_tab[i].nexthop_addr,
+ ifp->name, pim->vrf->name,
+ &src_addr);
+ i++;
continue;
}
}
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..535448f013 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,9 @@ 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;
+ pim_ifp->igmp_peak_group_count = 0;
}
void pim_sock_reset(struct interface *ifp)
@@ -521,7 +524,8 @@ static uint16_t ip_id = 0;
static int pim_msg_send_frame(int fd, char *buf, size_t len,
- struct sockaddr *dst, size_t salen)
+ struct sockaddr *dst, size_t salen,
+ const char *ifname)
{
struct ip *ip = (struct ip *)buf;
@@ -537,8 +541,8 @@ static int pim_msg_send_frame(int fd, char *buf, size_t len,
ip->ip_len = htons(sendlen);
ip->ip_off = htons(offset | IP_MF);
- if (pim_msg_send_frame(fd, buf, sendlen, dst, salen)
- == 0) {
+ if (pim_msg_send_frame(fd, buf, sendlen, dst, salen,
+ ifname) == 0) {
struct ip *ip2 = (struct ip *)(buf + newlen1);
size_t newlen2 = len - sendlen;
sendlen = newlen2 + hdrsize;
@@ -547,7 +551,8 @@ static int pim_msg_send_frame(int fd, char *buf, size_t len,
ip2->ip_len = htons(sendlen);
ip2->ip_off = htons(offset + (newlen1 >> 3));
return pim_msg_send_frame(fd, (char *)ip2,
- sendlen, dst, salen);
+ sendlen, dst, salen,
+ ifname);
}
}
@@ -557,9 +562,9 @@ static int pim_msg_send_frame(int fd, char *buf, size_t len,
pim_inet4_dump("<dst?>", ip->ip_dst, dst_str,
sizeof(dst_str));
zlog_warn(
- "%s: sendto() failure to %s: fd=%d msg_size=%zd: errno=%d: %s",
- __func__, dst_str, fd, len, errno,
- safe_strerror(errno));
+ "%s: sendto() failure to %s: iface=%s fd=%d msg_size=%zd: errno=%d: %s",
+ __func__, dst_str, ifname, fd, len,
+ errno, safe_strerror(errno));
}
return -1;
}
@@ -641,7 +646,7 @@ int pim_msg_send(int fd, pim_addr src, pim_addr dst, uint8_t *pim_msg,
}
pim_msg_send_frame(fd, (char *)buffer, sendlen, (struct sockaddr *)&to,
- tolen);
+ tolen, ifname);
return 0;
}
diff --git a/pimd/pim_register.c b/pimd/pim_register.c
index 8313c8d4f6..7fa36e5a44 100644
--- a/pimd/pim_register.c
+++ b/pimd/pim_register.c
@@ -61,7 +61,7 @@ void pim_register_join(struct pim_upstream *up)
pim_channel_add_oif(up->channel_oil, pim->regiface,
PIM_OIF_FLAG_PROTO_PIM, __func__);
up->reg_state = PIM_REG_JOIN;
- pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
+ pim_vxlan_update_sg_reg_state(pim, up, true);
}
void pim_register_stop_send(struct interface *ifp, pim_sgaddr *sg,
@@ -108,13 +108,39 @@ void pim_register_stop_send(struct interface *ifp, pim_sgaddr *sg,
++pinfo->pim_ifstat_reg_stop_send;
}
+static void pim_reg_stop_upstream(struct pim_instance *pim,
+ struct pim_upstream *up)
+{
+ switch (up->reg_state) {
+ case PIM_REG_NOINFO:
+ case PIM_REG_PRUNE:
+ return;
+ case PIM_REG_JOIN:
+ up->reg_state = PIM_REG_PRUNE;
+ pim_channel_del_oif(up->channel_oil, pim->regiface,
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
+ pim_upstream_start_register_stop_timer(up, 0);
+ pim_vxlan_update_sg_reg_state(pim, up, false);
+ break;
+ case PIM_REG_JOIN_PENDING:
+ up->reg_state = PIM_REG_PRUNE;
+ pim_upstream_start_register_stop_timer(up, 0);
+ return;
+ }
+}
+
int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size)
{
struct pim_interface *pim_ifp = ifp->info;
struct pim_instance *pim = pim_ifp->pim;
- struct pim_upstream *upstream = NULL;
+ struct pim_upstream *up = NULL;
+ struct pim_rpf *rp;
+ pim_addr rpf_addr;
pim_sgaddr sg;
+ struct listnode *up_node;
+ struct pim_upstream *child;
bool wrong_af = false;
+ bool handling_star = false;
int l;
++pim_ifp->pim_ifstat_reg_stop_recv;
@@ -127,33 +153,65 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size)
if (wrong_af) {
zlog_err("invalid AF in Register-Stop on %s", ifp->name);
- return 0;
+ return -1;
}
- upstream = pim_upstream_find(pim, &sg);
- if (!upstream) {
- return 0;
- }
if (PIM_DEBUG_PIM_REG)
- zlog_debug("Received Register stop for %s", upstream->sg_str);
+ zlog_debug("Received Register stop for %pSG", &sg);
+
+ rp = RP(pim_ifp->pim, sg.grp);
+ if (rp) {
+ rpf_addr = pim_addr_from_prefix(&rp->rpf_addr);
+ if (pim_addr_cmp(sg.src, rpf_addr) == 0) {
+ handling_star = true;
+ sg.src = PIMADDR_ANY;
+ }
+ }
- switch (upstream->reg_state) {
- case PIM_REG_NOINFO:
- case PIM_REG_PRUNE:
- return 0;
- case PIM_REG_JOIN:
- upstream->reg_state = PIM_REG_PRUNE;
- pim_channel_del_oif(upstream->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM, __func__);
- pim_upstream_start_register_stop_timer(upstream, 0);
- pim_vxlan_update_sg_reg_state(pim, upstream,
- false/*reg_join*/);
- break;
- case PIM_REG_JOIN_PENDING:
- upstream->reg_state = PIM_REG_PRUNE;
- pim_upstream_start_register_stop_timer(upstream, 0);
- return 0;
+ /*
+ * RFC 7761 Sec 4.4.1
+ * Handling Register-Stop(*,G) Messages at the DR:
+ * A Register-Stop(*,G) should be treated as a
+ * Register-Stop(S,G) for all (S,G) Register state
+ * machines that are not in the NoInfo state.
+ */
+ up = pim_upstream_find(pim, &sg);
+ if (up) {
+ /*
+ * If the upstream find actually found a particular
+ * S,G then we *know* that the following for loop
+ * is not going to execute and this is ok
+ */
+ for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("Executing Reg stop for %s",
+ child->sg_str);
+
+ pim_reg_stop_upstream(pim, child);
+ }
+
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("Executing Reg stop for %s", up->sg_str);
+ pim_reg_stop_upstream(pim, up);
+ } else {
+ if (!handling_star)
+ return 0;
+ /*
+ * Unfortunately pim was unable to find a *,G
+ * but pim may still actually have individual
+ * S,G's that need to be processed. In that
+ * case pim must do the expensive walk to find
+ * and stop
+ */
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ if (pim_addr_cmp(up->sg.grp, sg.grp) == 0) {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("Executing Reg stop for %s",
+ up->sg_str);
+ pim_reg_stop_upstream(pim, up);
+ }
+ }
}
return 0;
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index 25eabe6743..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)
@@ -77,26 +78,21 @@ int pim_rp_list_cmp(void *v1, void *v2)
{
struct rp_info *rp1 = (struct rp_info *)v1;
struct rp_info *rp2 = (struct rp_info *)v2;
+ int ret;
/*
* Sort by RP IP address
*/
- if (rp1->rp.rpf_addr.u.prefix4.s_addr
- < rp2->rp.rpf_addr.u.prefix4.s_addr)
- return -1;
-
- if (rp1->rp.rpf_addr.u.prefix4.s_addr
- > rp2->rp.rpf_addr.u.prefix4.s_addr)
- return 1;
+ ret = prefix_cmp(&rp1->rp.rpf_addr, &rp2->rp.rpf_addr);
+ if (ret)
+ return ret;
/*
* Sort by group IP address
*/
- if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr)
- return -1;
-
- if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr)
- return 1;
+ ret = prefix_cmp(&rp1->group, &rp2->group);
+ if (ret)
+ return ret;
return 0;
}
@@ -116,13 +112,12 @@ void pim_rp_init(struct pim_instance *pim)
if (!pim_get_all_mcast_group(&rp_info->group)) {
flog_err(EC_LIB_DEVELOPMENT,
- "Unable to convert 224.0.0.0/4 to prefix");
+ "Unable to convert all-multicast prefix");
list_delete(&pim->rp_list);
route_table_finish(pim->rp_table);
XFREE(MTYPE_PIM_RP, rp_info);
return;
}
- rp_info->group.family = AF_INET;
pim_addr_to_prefix(&rp_info->rp.rpf_addr, PIMADDR_ANY);
listnode_add(pim->rp_list, rp_info);
@@ -130,9 +125,9 @@ void pim_rp_init(struct pim_instance *pim)
rn = route_node_get(pim->rp_table, &rp_info->group);
rn->info = rp_info;
if (PIM_DEBUG_PIM_TRACE)
- zlog_debug(
- "Allocated: %p for rp_info: %p(224.0.0.0/4) Lock: %d",
- rn, rp_info, route_node_get_lock_count(rn));
+ zlog_debug("Allocated: %p for rp_info: %p(%pFX) Lock: %d", rn,
+ rp_info, &rp_info->group,
+ route_node_get_lock_count(rn));
}
void pim_rp_free(struct pim_instance *pim)
@@ -375,7 +370,7 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up)
up->sg.grp);
if (PIM_DEBUG_PIM_TRACE)
- zlog_debug("%s: pim upstream update for old upstream %pI4",
+ zlog_debug("%s: pim upstream update for old upstream %pPA",
__func__, &old_upstream_addr);
if (!pim_addr_cmp(old_upstream_addr, new_upstream_addr))
@@ -932,9 +927,7 @@ void pim_rp_setup(struct pim_instance *pim)
if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
continue;
- nht_p.family = AF_INET;
- nht_p.prefixlen = IPV4_MAX_BITLEN;
- nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4;
+ nht_p = rp_info->rp.rpf_addr;
pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL);
if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop,
@@ -1153,13 +1146,13 @@ 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;
struct listnode *node;
char source[7];
- char buf[PREFIX_STRLEN];
json_object *json = NULL;
json_object *json_rp_rows = NULL;
@@ -1169,112 +1162,105 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj)
json = json_object_new_object();
else
vty_out(vty,
- "RP address group/prefix-list OIF I am RP Source\n");
+ "RP address group/prefix-list OIF I am RP Source Group-Type\n");
for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) {
- if (!pim_rpf_addr_is_inaddr_any(&rp_info->rp)) {
- char buf[48];
+ if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
+ continue;
- if (rp_info->rp_src == RP_SRC_STATIC)
- strlcpy(source, "Static", sizeof(source));
- else if (rp_info->rp_src == RP_SRC_BSR)
- strlcpy(source, "BSR", sizeof(source));
- else
- strlcpy(source, "None", sizeof(source));
- if (uj) {
- /*
- * If we have moved on to a new RP then add the
- * entry for the previous RP
- */
- if (prev_rp_info
- && prev_rp_info->rp.rpf_addr.u.prefix4
- .s_addr
- != rp_info->rp.rpf_addr.u.prefix4
- .s_addr) {
- json_object_object_add(
- json,
- inet_ntop(AF_INET,
- &prev_rp_info->rp
- .rpf_addr.u
- .prefix4,
- buf, sizeof(buf)),
- json_rp_rows);
- json_rp_rows = NULL;
- }
+#if PIM_IPV == 4
+ pim_addr group = rp_info->group.u.prefix4;
+#else
+ pim_addr group = rp_info->group.u.prefix6;
+#endif
+ const char *group_type =
+ pim_is_grp_ssm(pim, group) ? "SSM" : "ASM";
- if (!json_rp_rows)
- json_rp_rows = json_object_new_array();
-
- json_row = json_object_new_object();
- json_object_string_addf(
- json_row, "rpAddress", "%pI4",
- &rp_info->rp.rpf_addr.u.prefix4);
- if (rp_info->rp.source_nexthop.interface)
- json_object_string_add(
- json_row, "outboundInterface",
- rp_info->rp.source_nexthop
- .interface->name);
- else
- json_object_string_add(
- json_row, "outboundInterface",
- "Unknown");
- if (rp_info->i_am_rp)
- json_object_boolean_true_add(json_row,
- "iAmRP");
- else
- json_object_boolean_false_add(json_row,
- "iAmRP");
+ if (range && !prefix_same(&rp_info->group, range))
+ continue;
- if (rp_info->plist)
- json_object_string_add(json_row,
- "prefixList",
- rp_info->plist);
- else
- json_object_string_addf(
- json_row, "group", "%pFX",
- &rp_info->group);
- json_object_string_add(json_row, "source",
- source);
+ if (rp_info->rp_src == RP_SRC_STATIC)
+ strlcpy(source, "Static", sizeof(source));
+ else if (rp_info->rp_src == RP_SRC_BSR)
+ strlcpy(source, "BSR", sizeof(source));
+ else
+ strlcpy(source, "None", sizeof(source));
+ if (uj) {
+ /*
+ * If we have moved on to a new RP then add the
+ * entry for the previous RP
+ */
+ if (prev_rp_info &&
+ prefix_cmp(&prev_rp_info->rp.rpf_addr,
+ &rp_info->rp.rpf_addr)) {
+ json_object_object_addf(
+ json, json_rp_rows, "%pFXh",
+ &prev_rp_info->rp.rpf_addr);
+ json_rp_rows = NULL;
+ }
- json_object_array_add(json_rp_rows, json_row);
- } else {
- vty_out(vty, "%-15s ",
- inet_ntop(AF_INET,
- &rp_info->rp.rpf_addr.u
- .prefix4,
- buf, sizeof(buf)));
-
- if (rp_info->plist)
- vty_out(vty, "%-18s ", rp_info->plist);
- else
- vty_out(vty, "%-18pFX ",
- &rp_info->group);
+ if (!json_rp_rows)
+ json_rp_rows = json_object_new_array();
+
+ json_row = json_object_new_object();
+ json_object_string_addf(json_row, "rpAddress", "%pFXh",
+ &rp_info->rp.rpf_addr);
+ if (rp_info->rp.source_nexthop.interface)
+ json_object_string_add(
+ json_row, "outboundInterface",
+ rp_info->rp.source_nexthop
+ .interface->name);
+ else
+ json_object_string_add(json_row,
+ "outboundInterface",
+ "Unknown");
+ if (rp_info->i_am_rp)
+ json_object_boolean_true_add(json_row, "iAmRP");
+ else
+ json_object_boolean_false_add(json_row,
+ "iAmRP");
- if (rp_info->rp.source_nexthop.interface)
- vty_out(vty, "%-16s ",
- rp_info->rp.source_nexthop
- .interface->name);
- else
- vty_out(vty, "%-16s ", "(Unknown)");
+ if (rp_info->plist)
+ json_object_string_add(json_row, "prefixList",
+ rp_info->plist);
+ else
+ json_object_string_addf(json_row, "group",
+ "%pFX",
+ &rp_info->group);
+ json_object_string_add(json_row, "source", source);
+ json_object_string_add(json_row, "groupType",
+ group_type);
+
+ json_object_array_add(json_rp_rows, json_row);
+ } else {
+ vty_out(vty, "%-15pFXh ", &rp_info->rp.rpf_addr);
- if (rp_info->i_am_rp)
- vty_out(vty, "yes");
- else
- vty_out(vty, "no");
+ if (rp_info->plist)
+ vty_out(vty, "%-18s ", rp_info->plist);
+ else
+ vty_out(vty, "%-18pFX ", &rp_info->group);
- vty_out(vty, "%14s\n", source);
- }
- prev_rp_info = rp_info;
+ if (rp_info->rp.source_nexthop.interface)
+ vty_out(vty, "%-16s ",
+ rp_info->rp.source_nexthop
+ .interface->name);
+ else
+ vty_out(vty, "%-16s ", "(Unknown)");
+
+ if (rp_info->i_am_rp)
+ vty_out(vty, "yes");
+ else
+ vty_out(vty, "no");
+
+ vty_out(vty, "%14s", source);
+ vty_out(vty, "%6s\n", group_type);
}
+ prev_rp_info = rp_info;
}
if (uj) {
if (prev_rp_info && json_rp_rows)
- json_object_object_add(
- json,
- inet_ntop(AF_INET,
- &prev_rp_info->rp.rpf_addr.u.prefix4,
- buf, sizeof(buf)),
- json_rp_rows);
+ json_object_object_addf(json, json_rp_rows, "%pFXh",
+ &prev_rp_info->rp.rpf_addr);
vty_json(vty, json);
}
@@ -1292,17 +1278,20 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr)
if (pim_rpf_addr_is_inaddr_any(&rp_info->rp))
continue;
- nht_p.family = AF_INET;
- nht_p.prefixlen = IPV4_MAX_BITLEN;
- nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4;
+ nht_p = rp_info->rp.rpf_addr;
memset(&pnc, 0, sizeof(struct pim_nexthop_cache));
if (!pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info,
&pnc))
continue;
for (nh_node = pnc.nexthop; nh_node; nh_node = nh_node->next) {
- if (nh_node->gate.ipv4.s_addr != INADDR_ANY)
+#if PIM_IPV == 4
+ if (!pim_addr_is_any(nh_node->gate.ipv4))
continue;
+#else
+ if (!pim_addr_is_any(nh_node->gate.ipv6))
+ continue;
+#endif
struct interface *ifp1 = if_lookup_by_index(
nh_node->ifindex, pim->vrf->vrf_id);
@@ -1315,15 +1304,11 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr)
#else
nh_node->gate.ipv6 = nbr->source_addr;
#endif
- if (PIM_DEBUG_PIM_NHT_RP) {
- char str[PREFIX_STRLEN];
- pim_addr_dump("<nht_addr?>", &nht_p, str,
- sizeof(str));
+ if (PIM_DEBUG_PIM_NHT_RP)
zlog_debug(
- "%s: addr %s new nexthop addr %pPAs interface %s",
- __func__, str, &nbr->source_addr,
+ "%s: addr %pFXh new nexthop addr %pPAs interface %s",
+ __func__, &nht_p, &nbr->source_addr,
ifp1->name);
- }
}
}
}
diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h
index 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_tlv.c b/pimd/pim_tlv.c
index 86403dd54a..028514659b 100644
--- a/pimd/pim_tlv.c
+++ b/pimd/pim_tlv.c
@@ -127,11 +127,7 @@ int pim_encode_addr_ucast(uint8_t *buf, pim_addr addr)
{
uint8_t *start = buf;
-#if PIM_IPV == 4
- *buf++ = PIM_MSG_ADDRESS_FAMILY_IPV4;
-#else
- *buf++ = PIM_MSG_ADDRESS_FAMILY_IPV6;
-#endif
+ *buf++ = PIM_MSG_ADDRESS_FAMILY;
*buf++ = 0;
memcpy(buf, &addr, sizeof(addr));
buf += sizeof(addr);
@@ -624,16 +620,15 @@ int pim_parse_addr_source(pim_sgaddr *sg, uint8_t *flags, const uint8_t *buf,
}
switch (family) {
- case PIM_MSG_ADDRESS_FAMILY_IPV4:
- if ((addr + sizeof(struct in_addr)) > pastend) {
+ case PIM_MSG_ADDRESS_FAMILY:
+ if ((addr + sizeof(sg->src)) > pastend) {
zlog_warn(
- "%s: IPv4 source address overflow: left=%td needed=%zu",
- __func__, pastend - addr,
- sizeof(struct in_addr));
+ "%s: IP source address overflow: left=%td needed=%zu",
+ __func__, pastend - addr, sizeof(sg->src));
return -3;
}
- memcpy(&sg->src, addr, sizeof(struct in_addr));
+ memcpy(&sg->src, addr, sizeof(sg->src));
/*
RFC 4601: 4.9.1 Encoded Source and Group Address Formats
@@ -642,27 +637,24 @@ int pim_parse_addr_source(pim_sgaddr *sg, uint8_t *flags, const uint8_t *buf,
The mask length MUST be equal to the mask length in bits for
the given Address Family and Encoding Type (32 for IPv4
- native
- and 128 for IPv6 native). A router SHOULD ignore any
- messages
- received with any other mask length.
+ native and 128 for IPv6 native). A router SHOULD ignore any
+ messages received with any other mask length.
*/
- if (mask_len != IPV4_MAX_BITLEN) {
- zlog_warn("%s: IPv4 bad source address mask: %d",
+ if (mask_len != PIM_MAX_BITLEN) {
+ zlog_warn("%s: IP bad source address mask: %d",
__func__, mask_len);
return -4;
}
- addr += sizeof(struct in_addr);
+ addr += sizeof(sg->src);
break;
- default: {
+ default:
zlog_warn(
"%s: unknown source address encoding family=%d: %02x%02x%02x%02x",
__func__, family, buf[0], buf[1], buf[2], buf[3]);
return -5;
}
- }
return addr - buf;
}
diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c
index 24833f5a63..571117ac0a 100644
--- a/pimd/pim_upstream.c
+++ b/pimd/pim_upstream.c
@@ -178,6 +178,14 @@ static void upstream_channel_oil_detach(struct pim_upstream *up)
}
+static void pim_upstream_timers_stop(struct pim_upstream *up)
+{
+ THREAD_OFF(up->t_ka_timer);
+ THREAD_OFF(up->t_rs_timer);
+ THREAD_OFF(up->t_msdp_reg_timer);
+ THREAD_OFF(up->t_join_timer);
+}
+
struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
struct pim_upstream *up, const char *name)
{
@@ -207,9 +215,7 @@ struct pim_upstream *pim_upstream_del(struct pim_instance *pim,
if (pim_up_mlag_is_local(up))
pim_mlag_up_local_del(pim, up);
- THREAD_OFF(up->t_ka_timer);
- THREAD_OFF(up->t_rs_timer);
- THREAD_OFF(up->t_msdp_reg_timer);
+ pim_upstream_timers_stop(up);
if (up->join_state == PIM_UPSTREAM_JOINED) {
pim_jp_agg_single_upstream_send(&up->rpf, up, 0);
@@ -1945,6 +1951,7 @@ void pim_upstream_terminate(struct pim_instance *pim)
while ((up = rb_pim_upstream_first(&pim->upstream_head))) {
pim_upstream_del(pim, up, __func__);
+ pim_upstream_timers_stop(up);
}
rb_pim_upstream_fini(&pim->upstream_head);
diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c
index 3f418c0870..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,342 +482,13 @@ void pim_zebra_init(void)
zclient_lookup_new();
}
-#if PIM_IPV == 4
-void igmp_anysource_forward_start(struct pim_instance *pim,
- struct gm_group *group)
-{
- struct gm_source *source;
- struct in_addr src_addr = {.s_addr = 0};
- /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
- assert(group->group_filtermode_isexcl);
- assert(listcount(group->group_source_list) < 1);
-
- source = igmp_get_source_by_addr(group, src_addr, NULL);
- if (!source) {
- zlog_warn("%s: Failure to create * source", __func__);
- return;
- }
-
- igmp_source_forward_start(pim, source);
-}
-
-void igmp_anysource_forward_stop(struct gm_group *group)
-{
- struct gm_source *source;
- struct in_addr star = {.s_addr = 0};
-
- source = igmp_find_source_by_addr(group, star);
- if (source)
- igmp_source_forward_stop(source);
-}
-
-static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
- struct gm_source *source)
-{
- pim_sgaddr sg;
- struct gm_group *group = source->source_group;
- struct pim_ifchannel *ch;
-
- if ((source->source_addr.s_addr != INADDR_ANY)
- || !IGMP_SOURCE_TEST_FORWARDING(source->source_flags))
- return;
-
- memset(&sg, 0, sizeof(sg));
- sg.src = source->source_addr;
- sg.grp = group->group_addr;
-
- ch = pim_ifchannel_find(group->interface, &sg);
- if (pim_is_grp_ssm(pim, group->group_addr)) {
- /* If SSM group withdraw local membership */
- if (ch
- && (ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE)) {
- if (PIM_DEBUG_PIM_EVENTS)
- zlog_debug("local membership del for %pSG as G is now SSM",
- &sg);
- pim_ifchannel_local_membership_del(group->interface,
- &sg);
- }
- } else {
- /* If ASM group add local membership */
- if (!ch
- || (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)) {
- if (PIM_DEBUG_PIM_EVENTS)
- zlog_debug("local membership add for %pSG as G is now ASM",
- &sg);
- pim_ifchannel_local_membership_add(
- group->interface, &sg, false /*is_vxlan*/);
- }
- }
-}
-
-void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
-{
- struct interface *ifp;
-
- FOR_ALL_INTERFACES (pim->vrf, ifp) {
- struct pim_interface *pim_ifp = ifp->info;
- struct listnode *grpnode;
- struct gm_group *grp;
- struct pim_ifchannel *ch, *ch_temp;
-
- if (!pim_ifp)
- continue;
-
- /* scan igmp groups */
- for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
- grp)) {
- struct listnode *srcnode;
- struct gm_source *src;
-
- /* scan group sources */
- for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
- srcnode, src)) {
- igmp_source_forward_reevaluate_one(pim, src);
- } /* scan group sources */
- } /* scan igmp groups */
-
- RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
- ch_temp) {
- if (pim_is_grp_ssm(pim, ch->sg.grp)) {
- if (pim_addr_is_any(ch->sg.src))
- pim_ifchannel_delete(ch);
- }
- }
- } /* scan interfaces */
-}
-
-void igmp_source_forward_start(struct pim_instance *pim,
- struct gm_source *source)
-{
- struct pim_interface *pim_oif;
- struct gm_group *group;
- pim_sgaddr sg;
- int result;
- int input_iface_vif_index = 0;
-
- memset(&sg, 0, sizeof(sg));
- sg.src = source->source_addr;
- sg.grp = source->source_group->group_addr;
-
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
- source->source_group->interface->name,
- IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
- }
-
- /* Prevent IGMP interface from installing multicast route multiple
- times */
- if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
- return;
- }
-
- group = source->source_group;
- pim_oif = group->interface->info;
- if (!pim_oif) {
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug("%s: multicast not enabled on oif=%s ?",
- __func__,
- source->source_group->interface->name);
- }
- return;
- }
-
- if (!source->source_channel_oil) {
- pim_addr vif_source;
- struct prefix src, grp;
- struct pim_nexthop nexthop;
- struct pim_upstream *up = NULL;
-
- if (!pim_rp_set_upstream_addr(pim, &vif_source,
- source->source_addr, sg.grp)) {
- /*Create a dummy channel oil */
- source->source_channel_oil =
- pim_channel_oil_add(pim, &sg, __func__);
- }
-
- else {
- pim_addr_to_prefix(&src, vif_source); // RP or Src addr
- pim_addr_to_prefix(&grp, sg.grp);
-
- up = pim_upstream_find(pim, &sg);
- if (up) {
- memcpy(&nexthop, &up->rpf.source_nexthop,
- sizeof(struct pim_nexthop));
- pim_ecmp_nexthop_lookup(pim, &nexthop, &src,
- &grp, 0);
- if (nexthop.interface)
- input_iface_vif_index =
- pim_if_find_vifindex_by_ifindex(
- pim,
- nexthop.interface->ifindex);
- } else
- input_iface_vif_index =
- pim_ecmp_fib_lookup_if_vif_index(
- pim, &src, &grp);
-
- if (PIM_DEBUG_ZEBRA)
- zlog_debug(
- "%s: NHT %pSG vif_source %pPAs vif_index:%d ",
- __func__, &sg, &vif_source,
- input_iface_vif_index);
-
- if (input_iface_vif_index < 1) {
- if (PIM_DEBUG_IGMP_TRACE) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<source?>",
- source->source_addr,
- source_str, sizeof(source_str));
- zlog_debug(
- "%s %s: could not find input interface for source %s",
- __FILE__, __func__, source_str);
- }
- source->source_channel_oil =
- pim_channel_oil_add(pim, &sg, __func__);
- }
-
- else {
- /*
- * Protect IGMP against adding looped MFC
- * entries created by both source and receiver
- * attached to the same interface. See TODO
- * T22. Block only when the intf is non DR
- * DR must create upstream.
- */
- if ((input_iface_vif_index ==
- pim_oif->mroute_vif_index) &&
- !(PIM_I_am_DR(pim_oif))) {
- /* ignore request for looped MFC entry
- */
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug("%s: ignoring request for looped MFC entry (S,G)=%pSG: oif=%s vif_index=%d",
- __func__,
- &sg,
- source->source_group
- ->interface->name,
- input_iface_vif_index);
- }
- return;
- }
-
- source->source_channel_oil =
- pim_channel_oil_add(pim, &sg, __func__);
- if (!source->source_channel_oil) {
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug("%s %s: could not create OIL for channel (S,G)=%pSG",
- __FILE__, __func__,
- &sg);
- }
- return;
- }
- }
- }
- }
-
- if (PIM_I_am_DR(pim_oif) || PIM_I_am_DualActive(pim_oif)) {
- result = pim_channel_add_oif(source->source_channel_oil,
- group->interface,
- PIM_OIF_FLAG_PROTO_IGMP, __func__);
- if (result) {
- if (PIM_DEBUG_MROUTE) {
- zlog_warn("%s: add_oif() failed with return=%d",
- __func__, result);
- }
- return;
- }
- } else {
- if (PIM_DEBUG_IGMP_TRACE)
- zlog_debug("%s: %pSG was received on %s interface but we are not DR for that interface",
- __func__, &sg,
- group->interface->name);
-
- return;
- }
- /*
- Feed IGMPv3-gathered local membership information into PIM
- per-interface (S,G) state.
- */
- if (!pim_ifchannel_local_membership_add(group->interface, &sg,
- false /*is_vxlan*/)) {
- if (PIM_DEBUG_MROUTE)
- zlog_warn("%s: Failure to add local membership for %pSG",
- __func__, &sg);
-
- pim_channel_del_oif(source->source_channel_oil,
- group->interface, PIM_OIF_FLAG_PROTO_IGMP,
- __func__);
- return;
- }
-
- IGMP_SOURCE_DO_FORWARDING(source->source_flags);
-}
-
-/*
- igmp_source_forward_stop: stop fowarding, but keep the source
- igmp_source_delete: stop fowarding, and delete the source
- */
-void igmp_source_forward_stop(struct gm_source *source)
-{
- struct gm_group *group;
- pim_sgaddr sg;
- int result;
-
- memset(&sg, 0, sizeof(sg));
- sg.src = source->source_addr;
- sg.grp = source->source_group->group_addr;
-
- if (PIM_DEBUG_IGMP_TRACE) {
- zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
- source->source_group->interface->name,
- IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
- }
-
- /* Prevent IGMP interface from removing multicast route multiple
- times */
- if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
- return;
- }
-
- group = source->source_group;
-
- /*
- It appears that in certain circumstances that
- igmp_source_forward_stop is called when IGMP forwarding
- was not enabled in oif_flags for this outgoing interface.
- Possibly because of multiple calls. When that happens, we
- enter the below if statement and this function returns early
- which in turn triggers the calling function to assert.
- Making the call to pim_channel_del_oif and ignoring the return code
- fixes the issue without ill effect, similar to
- pim_forward_stop below.
- */
- result = pim_channel_del_oif(source->source_channel_oil,
- group->interface, PIM_OIF_FLAG_PROTO_IGMP,
- __func__);
- if (result) {
- if (PIM_DEBUG_IGMP_TRACE)
- zlog_debug(
- "%s: pim_channel_del_oif() failed with return=%d",
- __func__, result);
- return;
- }
-
- /*
- Feed IGMPv3-gathered local membership information into PIM
- per-interface (S,G) state.
- */
- pim_ifchannel_local_membership_del(group->interface, &sg);
-
- IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
-}
-#endif /* PIM_IPV == 4 */
-
void pim_forward_start(struct pim_ifchannel *ch)
{
struct pim_upstream *up = ch->upstream;
uint32_t mask = 0;
if (PIM_DEBUG_PIM_TRACE)
- zlog_debug("%s: (S,G)=%pSG oif=%s (%pI4)", __func__, &ch->sg,
+ zlog_debug("%s: (S,G)=%pSG oif=%s (%pPA)", __func__, &ch->sg,
ch->interface->name, &up->upstream_addr);
if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags))
diff --git a/pimd/pim_zebra.h b/pimd/pim_zebra.h
index 8656d7563d..5879cdefb0 100644
--- a/pimd/pim_zebra.h
+++ b/pimd/pim_zebra.h
@@ -23,7 +23,6 @@
#include <zebra.h>
#include "zclient.h"
-#include "pim_igmp.h"
#include "pim_ifchannel.h"
void pim_zebra_init(void);
@@ -32,15 +31,6 @@ void pim_zebra_zclient_update(struct vty *vty);
void pim_scan_individual_oil(struct channel_oil *c_oil, int in_vif_index);
void pim_scan_oil(struct pim_instance *pim_matcher);
-void igmp_anysource_forward_start(struct pim_instance *pim,
- struct gm_group *group);
-void igmp_anysource_forward_stop(struct gm_group *group);
-
-void igmp_source_forward_start(struct pim_instance *pim,
- struct gm_source *source);
-void igmp_source_forward_stop(struct gm_source *source);
-void igmp_source_forward_reevaluate_all(struct pim_instance *pim);
-
void pim_forward_start(struct pim_ifchannel *ch);
void pim_forward_stop(struct pim_ifchannel *ch);
diff --git a/pimd/subdir.am b/pimd/subdir.am
index 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/.gitignore b/tests/.gitignore
index 70d0ef6e0a..f00177abd8 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,6 +1,8 @@
*.log
*.sum
*.xml
+frr-northbound.proto
+frr_northbound*
.pytest_cache
/bgpd/test_aspath
/bgpd/test_bgp_table
diff --git a/tests/lib/test_grpc.py b/tests/lib/test_grpc.py
index 06ae6c05dd..2e292fadc9 100644
--- a/tests/lib/test_grpc.py
+++ b/tests/lib/test_grpc.py
@@ -1,8 +1,10 @@
import inspect
import os
import subprocess
-import pytest
+
import frrtest
+import pytest
+
class TestGRPC(object):
program = "./test_grpc"
@@ -15,9 +17,13 @@ class TestGRPC(object):
basedir = os.path.dirname(inspect.getsourcefile(type(self)))
program = os.path.join(basedir, self.program)
proc = subprocess.Popen(
- [frrtest.binpath(program)], stdin=subprocess.PIPE, stdout=subprocess.PIPE
+ [frrtest.binpath(program)],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
)
output, _ = proc.communicate()
self.exitcode = proc.wait()
if self.exitcode != 0:
+ print("OUTPUT:\n" + output.decode("ascii"))
raise frrtest.TestExitNonzero(self)
diff --git a/tests/lib/test_printfrr.c b/tests/lib/test_printfrr.c
index 8f9d637afd..59d08ae82b 100644
--- a/tests/lib/test_printfrr.c
+++ b/tests/lib/test_printfrr.c
@@ -207,6 +207,24 @@ int main(int argc, char **argv)
assert(strcmp(p, "test#5") == 0);
XFREE(MTYPE_TMP, p);
+ struct prefix pfx;
+
+ str2prefix("192.168.1.23/24", &pfx);
+ printchk("192.168.1.23/24", "%pFX", &pfx);
+ printchk("192.168.1.23", "%pFXh", &pfx);
+
+ str2prefix("2001:db8::1234/64", &pfx);
+ printchk("2001:db8::1234/64", "%pFX", &pfx);
+ printchk("2001:db8::1234", "%pFXh", &pfx);
+
+ pfx.family = AF_UNIX;
+ printchk("UNK prefix", "%pFX", &pfx);
+ printchk("{prefix.af=AF_UNIX}", "%pFXh", &pfx);
+
+ str2prefix_eth("02:ca:fe:f0:0d:1e/48", (struct prefix_eth *)&pfx);
+ printchk("02:ca:fe:f0:0d:1e/48", "%pFX", &pfx);
+ printchk("02:ca:fe:f0:0d:1e", "%pFXh", &pfx);
+
struct prefix_sg sg;
sg.src.s_addr = INADDR_ANY;
sg.grp.s_addr = INADDR_ANY;
diff --git a/tests/lib/test_typelist.c b/tests/lib/test_typelist.c
index 607e29e56b..6e69658490 100644
--- a/tests/lib/test_typelist.c
+++ b/tests/lib/test_typelist.c
@@ -58,9 +58,10 @@
#define T_HASH (1 << 2)
#define T_HEAP (1 << 3)
#define T_ATOMIC (1 << 4)
+#define T_REVERSE (1 << 5)
#define _T_LIST (0)
-#define _T_DLIST (0)
+#define _T_DLIST (0 | T_REVERSE)
#define _T_ATOMLIST (0 | T_ATOMIC)
#define _T_HEAP (T_SORTED | T_HEAP)
#define _T_SORTLIST_UNIQ (T_SORTED | T_UNIQ)
@@ -68,8 +69,8 @@
#define _T_HASH (T_SORTED | T_UNIQ | T_HASH)
#define _T_SKIPLIST_UNIQ (T_SORTED | T_UNIQ)
#define _T_SKIPLIST_NONUNIQ (T_SORTED)
-#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ)
-#define _T_RBTREE_NONUNIQ (T_SORTED)
+#define _T_RBTREE_UNIQ (T_SORTED | T_UNIQ | T_REVERSE)
+#define _T_RBTREE_NONUNIQ (T_SORTED | T_REVERSE)
#define _T_ATOMSORT_UNIQ (T_SORTED | T_UNIQ | T_ATOMIC)
#define _T_ATOMSORT_NONUNIQ (T_SORTED | T_ATOMIC)
@@ -79,6 +80,7 @@
#define IS_HASH(type) (_T_TYPE(type) & T_HASH)
#define IS_HEAP(type) (_T_TYPE(type) & T_HEAP)
#define IS_ATOMIC(type) (_T_TYPE(type) & T_ATOMIC)
+#define IS_REVERSE(type) (_T_TYPE(type) & T_REVERSE)
static struct timeval ref, ref0;
diff --git a/tests/lib/test_typelist.h b/tests/lib/test_typelist.h
index 8261616ed2..e3579c67a2 100644
--- a/tests/lib/test_typelist.h
+++ b/tests/lib/test_typelist.h
@@ -31,6 +31,11 @@
#define list_const_next concat(TYPE, _const_next)
#define list_next concat(TYPE, _next)
#define list_next_safe concat(TYPE, _next_safe)
+#define list_const_last concat(TYPE, _const_last)
+#define list_last concat(TYPE, _last)
+#define list_const_prev concat(TYPE, _const_prev)
+#define list_prev concat(TYPE, _prev)
+#define list_prev_safe concat(TYPE, _prev_safe)
#define list_count concat(TYPE, _count)
#define list_add concat(TYPE, _add)
#define list_add_head concat(TYPE, _add_head)
@@ -171,6 +176,9 @@ static void concat(test_, TYPE)(void)
list_init(&head);
assert(list_first(&head) == NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(list_last(&head) == NULL);
+#endif
ts_hash("init", "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119");
@@ -203,6 +211,10 @@ static void concat(test_, TYPE)(void)
assert(!list_first(&head));
assert(list_count(&other) == k);
assert(list_first(&other) != NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(!list_last(&head));
+ assert(list_last(&other) != NULL);
+#endif
ts_hash_headx(
&other, "swap1",
"a538546a6e6ab0484e925940aa8dd02fd934408bbaed8cb66a0721841584d838");
@@ -269,13 +281,36 @@ static void concat(test_, TYPE)(void)
(void)cprev;
#else
assert(!cprev || cprev->val < citem->val);
+#if IS_REVERSE(REALTYPE)
+ assert(list_const_prev(chead, citem) == cprev);
+#endif
#endif
cprev = citem;
k++;
}
assert(list_count(chead) == k);
+#if IS_REVERSE(REALTYPE)
+ assert(cprev == list_const_last(chead));
+#endif
ts_ref("walk");
+#if IS_REVERSE(REALTYPE) && !IS_HASH(REALTYPE) && !IS_HEAP(REALTYPE)
+ cprev = NULL;
+ k = 0;
+
+ frr_rev_each(list_const, chead, citem) {
+ assert(!cprev || cprev->val > citem->val);
+ assert(list_const_next(chead, citem) == cprev);
+
+ cprev = citem;
+ k++;
+ }
+ assert(list_count(chead) == k);
+ assert(cprev == list_const_first(chead));
+
+ ts_ref("reverse-walk");
+#endif
+
#if IS_UNIQ(REALTYPE)
prng_free(prng);
prng = prng_new(0);
@@ -439,6 +474,9 @@ static void concat(test_, TYPE)(void)
}
assert(list_count(&head) == k);
assert(list_first(&head) != NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(list_last(&head) != NULL);
+#endif
ts_hash("fill / add_tail", "eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
#if !IS_ATOMIC(REALTYPE)
@@ -451,6 +489,10 @@ static void concat(test_, TYPE)(void)
assert(!list_first(&head));
assert(list_count(&other) == k);
assert(list_first(&other) != NULL);
+#if IS_REVERSE(REALTYPE)
+ assert(!list_last(&head));
+ assert(list_last(&other) != NULL);
+#endif
ts_hash_head(
&other, "swap1",
"eabfcf1413936daaf20965abced95762f45110a6619b84aac7d38481bce4ea19");
@@ -534,6 +576,21 @@ static void concat(test_, TYPE)(void)
}
ts_hash("member", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
#endif
+#if IS_REVERSE(REALTYPE)
+ i = 0;
+ prev = NULL;
+
+ frr_rev_each (list, &head, item) {
+ assert(item->scratchpad != 0);
+ assert(list_next(&head, item) == prev);
+
+ i++;
+ prev = item;
+ }
+ assert(list_first(&head) == prev);
+ assert(list_count(&head) == i);
+ ts_hash("reverse-walk", "42b8950c880535b2d2e0c980f9845f7841ecf675c0fb9801aec4170d2036349d");
+#endif
while ((item = list_pop(&head))) {
assert(item->scratchpad != 0);
@@ -746,6 +803,13 @@ static void concat(test_, TYPE)(void)
#undef list_first
#undef list_next
#undef list_next_safe
+#undef list_const_first
+#undef list_const_next
+#undef list_last
+#undef list_prev
+#undef list_prev_safe
+#undef list_const_last
+#undef list_const_prev
#undef list_count
#undef list_add
#undef list_add_head
diff --git a/tests/topotests/analyze.py b/tests/topotests/analyze.py
index 888e706339..bdb2e56ee1 100755
--- a/tests/topotests/analyze.py
+++ b/tests/topotests/analyze.py
@@ -198,9 +198,12 @@ def main():
logging.critical("%s doesn't exist", args.results)
sys.exit(1)
ttfiles = [args.results]
+ elif os.path.exists("/tmp/topotests/topotests.xml"):
+ ttfiles.append("/tmp/topotests/topotests.xml")
- if not ttfiles and os.path.exists("/tmp/topotests.xml"):
- ttfiles.append("/tmp/topotests.xml")
+ if not ttfiles:
+ if os.path.exists("/tmp/topotests.xml"):
+ ttfiles.append("/tmp/topotests.xml")
for f in ttfiles:
m = re.match(r"tt-group-(\d+)/topotests.xml", f)
diff --git a/tests/topotests/grpc_basic/lib b/tests/topotests/grpc_basic/lib
new file mode 120000
index 0000000000..dc598c56dc
--- /dev/null
+++ b/tests/topotests/grpc_basic/lib
@@ -0,0 +1 @@
+../lib \ No newline at end of file
diff --git a/tests/topotests/grpc_basic/r1/zebra.conf b/tests/topotests/grpc_basic/r1/zebra.conf
new file mode 100644
index 0000000000..49a0911c53
--- /dev/null
+++ b/tests/topotests/grpc_basic/r1/zebra.conf
@@ -0,0 +1,8 @@
+log record-priority
+log timestamp precision 6
+log extended extlog
+ destination file ext-log.txt create
+ timestamp precision 6
+ structured-data code-location
+interface r1-eth0
+ ip address 192.168.1.1/24 \ No newline at end of file
diff --git a/tests/topotests/grpc_basic/r2/zebra.conf b/tests/topotests/grpc_basic/r2/zebra.conf
new file mode 100644
index 0000000000..20da1885d4
--- /dev/null
+++ b/tests/topotests/grpc_basic/r2/zebra.conf
@@ -0,0 +1,8 @@
+log record-priority
+log timestamp precision 6
+log extended extlog
+ destination file ext-log.txt create
+ timestamp precision 6
+ structured-data code-location
+interface r2-eth0
+ ip address 192.168.1.2/24
diff --git a/tests/topotests/grpc_basic/test_basic_grpc.py b/tests/topotests/grpc_basic/test_basic_grpc.py
new file mode 100644
index 0000000000..b6812a5afc
--- /dev/null
+++ b/tests/topotests/grpc_basic/test_basic_grpc.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# February 21 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free 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
+#
+"""
+test_basic_grpc.py: Test Basic gRPC.
+"""
+
+import logging
+import os
+import sys
+
+import pytest
+
+from lib.common_config import step
+from lib.micronet import commander
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+GRPCP_ZEBRA = 50051
+GRPCP_STATICD = 50052
+GRPCP_BFDD = 50053
+GRPCP_ISISD = 50054
+GRPCP_OSPFD = 50055
+GRPCP_PIMD = 50056
+
+pytestmark = [
+ # pytest.mark.mgmtd -- Need a new non-protocol marker
+ # pytest.mark.bfdd,
+ # pytest.mark.isisd,
+ # pytest.mark.ospfd,
+ # pytest.mark.pimd,
+ pytest.mark.staticd,
+]
+
+script_path = os.path.realpath(os.path.join(CWD, "../lib/grpc-query.py"))
+
+try:
+ commander.cmd_raises([script_path, "--check"])
+except Exception:
+ pytest.skip(
+ "skipping; cannot create or import gRPC proto modules", allow_module_level=True
+ )
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, request.module.__name__)
+
+ tgen.start_topology()
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf", f"-M grpc:{GRPCP_ZEBRA}")
+ router.load_config(TopoRouter.RD_STATIC, None, f"-M grpc:{GRPCP_STATICD}")
+ # router.load_config(TopoRouter.RD_BFD, None, f"-M grpc:{GRPCP_BFDD}")
+ # router.load_config(TopoRouter.RD_ISIS, None, f"-M grpc:{GRPCP_ISISD}")
+ # router.load_config(TopoRouter.RD_OSPF, None, f"-M grpc:{GRPCP_OSPFD}")
+ # router.load_config(TopoRouter.RD_PIM, None, f"-M grpc:{GRPCP_PIMD}")
+
+ tgen.start_router()
+ yield tgen
+
+ logging.info("Stopping all routers (no assert on error)")
+ tgen.stop_topology()
+
+
+# Let's not do this so we catch errors
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+# ===================
+# The tests functions
+# ===================
+
+
+def run_grpc_client(r, port, commands):
+ if not isinstance(commands, str):
+ commands = "\n".join(commands) + "\n"
+ if not commands.endswith("\n"):
+ commands += "\n"
+ return r.cmd_raises([script_path, f"--port={port}"], stdin=commands)
+
+
+def test_connectivity(tgen):
+ r1 = tgen.gears["r1"]
+ output = r1.cmd_raises("ping -c1 192.168.1.2")
+ logging.info("ping output: %s", output)
+
+
+def test_capabilities(tgen):
+ r1 = tgen.gears["r1"]
+ output = run_grpc_client(r1, GRPCP_ZEBRA, "GETCAP")
+ logging.info("grpc output: %s", output)
+
+
+def test_get_config(tgen):
+ nrepeat = 5
+ r1 = tgen.gears["r1"]
+
+ step("'GET' inteface config 10 times, once per invocation")
+
+ for i in range(0, nrepeat):
+ output = run_grpc_client(r1, GRPCP_ZEBRA, "GET,/frr-interface:lib")
+ logging.info("[iteration %s]: grpc GET output: %s", i, output)
+
+ step(f"'GET' YANG {nrepeat} times in one invocation")
+ commands = ["GET,/frr-interface:lib" for _ in range(0, 10)]
+ output = run_grpc_client(r1, GRPCP_ZEBRA, commands)
+ logging.info("grpc GET*{%d} output: %s", nrepeat, output)
+
+
+def test_get_vrf_config(tgen):
+ r1 = tgen.gears["r1"]
+
+ step("'GET' get VRF config")
+
+ output = run_grpc_client(r1, GRPCP_ZEBRA, "GET,/frr-vrf:lib")
+ logging.info("grpc GET /frr-vrf:lib output: %s", output)
+
+
+def test_shutdown_checks(tgen):
+ # Start a process rnuning that will fetch bunches of data then shut the routers down
+ # and check for cores.
+ nrepeat = 100
+ r1 = tgen.gears["r1"]
+ commands = ["GET,/frr-interface:lib" for _ in range(0, nrepeat)]
+ p = r1.popen([script_path, f"--port={GRPCP_ZEBRA}"] + commands)
+ import time
+
+ time.sleep(1)
+ try:
+ for r in tgen.routers().values():
+ r.net.stopRouter()
+ r.net.checkRouterCores()
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+# Memory leak test template
+# Not compatible with the shutdown check above
+def _test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/lib/grpc-query.py b/tests/topotests/lib/grpc-query.py
new file mode 100755
index 0000000000..61f01c36bb
--- /dev/null
+++ b/tests/topotests/lib/grpc-query.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# February 22 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import argparse
+import logging
+import os
+import sys
+
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+# This is painful but works if you have installed grpc and grpc_tools would be *way*
+# better if we actually built and installed these but ... python packaging.
+try:
+ import grpc
+ import grpc_tools
+
+ from micronet import commander
+
+ commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
+ commander.cmd_raises(
+ f"python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . frr-northbound.proto"
+ )
+except Exception as error:
+ logging.error("can't create proto definition modules %s", error)
+ raise
+
+try:
+ sys.path[0:0] = "."
+ import frr_northbound_pb2
+ import frr_northbound_pb2_grpc
+
+ # Would be nice if compiling the modules internally from the source worked
+ # # import grpc_tools.protoc
+ # # proto_include = pkg_resources.resource_filename("grpc_tools", "_proto")
+ # from grpc_tools.protoc import _proto_file_to_module_name, _protos_and_services
+ # try:
+ # frr_northbound_pb2, frr_northbound_pb2_grpc = _protos_and_services(
+ # "frr_northbound.proto"
+ # )
+ # finally:
+ # os.chdir(CWD)
+except Exception as error:
+ logging.error("can't import proto definition modules %s", error)
+ raise
+
+
+class GRPCClient:
+ def __init__(self, server, port):
+ self.channel = grpc.insecure_channel("{}:{}".format(server, port))
+ self.stub = frr_northbound_pb2_grpc.NorthboundStub(self.channel)
+
+ def get_capabilities(self):
+ request = frr_northbound_pb2.GetCapabilitiesRequest()
+ response = "NONE"
+ try:
+ response = self.stub.GetCapabilities(request)
+ except Exception as error:
+ logging.error("Got exception from stub: %s", error)
+
+ logging.debug("GRPC Capabilities: %s", response)
+ return response
+
+ def get(self, xpath):
+ request = frr_northbound_pb2.GetRequest()
+ request.path.append(xpath)
+ request.type = frr_northbound_pb2.GetRequest.ALL
+ request.encoding = frr_northbound_pb2.XML
+ xml = ""
+ for r in self.stub.Get(request):
+ logging.info('GRPC Get path: "%s" value: %s', request.path, r)
+ xml += str(r.data.data)
+ return xml
+
+
+def next_action(action_list=None):
+ "Get next action from list or STDIN"
+ if action_list:
+ for action in action_list:
+ yield action
+ else:
+ while True:
+ try:
+ action = input("")
+ if not action:
+ break
+ yield action.strip()
+ except EOFError:
+ break
+
+
+def main(*args):
+ parser = argparse.ArgumentParser(description="gRPC Client")
+ parser.add_argument(
+ "-s", "--server", default="localhost", help="gRPC Server Address"
+ )
+ parser.add_argument(
+ "-p", "--port", type=int, default=50051, help="gRPC Server TCP Port"
+ )
+ parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ parser.add_argument("--check", action="store_true", help="check runable")
+ parser.add_argument("actions", nargs="*", help="GETCAP|GET,xpath")
+ args = parser.parse_args(*args)
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level,
+ format="%(asctime)s %(levelname)s: GRPC-CLI-CLIENT: %(name)s %(message)s",
+ )
+
+ if args.check:
+ sys.exit(0)
+
+ c = GRPCClient(args.server, args.port)
+
+ for action in next_action(args.actions):
+ action = action.casefold()
+ logging.info("GOT ACTION: %s", action)
+ if action == "getcap":
+ caps = c.get_capabilities()
+ print("Capabilities:", caps)
+ elif action.startswith("get,"):
+ # Print Interface State and Config
+ _, xpath = action.split(",", 1)
+ print("Get XPath: ", xpath)
+ xml = c.get(xpath)
+ print("{}: {}".format(xpath, xml))
+ # for _ in range(0, 1):
+
+
+if __name__ == "__main__":
+ main()
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/vrrpd/Makefile b/vrrpd/Makefile
index 027c6ee1f8..0abb1a6381 100644
--- a/vrrpd/Makefile
+++ b/vrrpd/Makefile
@@ -1,7 +1,7 @@
all: ALWAYS
- @$(MAKE) -s -C .. vrrp/vrrp
+ @$(MAKE) -s -C .. vrrpd/vrrpd
%: ALWAYS
- @$(MAKE) -s -C .. vrrp/$@
+ @$(MAKE) -s -C .. vrrpd/$@
Makefile:
#nothing
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index 8c8004190b..4cd2bef307 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -1232,7 +1232,8 @@ static void fpm_process_queue(struct thread *t)
* the output data in the STREAM_WRITEABLE
* check above, so we can ignore the return
*/
- (void)fpm_nl_enqueue(fnc, ctx);
+ if (fnc->socket != -1)
+ (void)fpm_nl_enqueue(fnc, ctx);
/* Account the processed entries. */
processed_contexts++;
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index a75b165270..e3506ecb14 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -1834,7 +1834,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ZEBRA_INTERFACE_VRF_LOOPBACK);
/* Update link. */
- zebra_if_update_link(ifp, link_ifindex, ns_id);
+ zebra_if_update_link(ifp, link_ifindex, link_nsid);
ifp->ll_type =
netlink_to_zebra_link_type(ifi->ifi_type);
@@ -1904,7 +1904,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memcpy(old_hw_addr, ifp->hw_addr, INTERFACE_HWADDR_MAX);
/* Update link. */
- zebra_if_update_link(ifp, link_ifindex, ns_id);
+ zebra_if_update_link(ifp, link_ifindex, link_nsid);
ifp->ll_type =
netlink_to_zebra_link_type(ifi->ifi_type);
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 f557e66384..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);
@@ -2203,7 +2179,6 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp,
/* Only advertise in BGP if the knob is enabled */
if (advertise_gw_macip_enabled(zevpn)) {
- SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
/* Set Router flag (R-bit) */
if (ip->ipa_type == IPADDR_V6)
diff --git a/zebra/zebra_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_vty.c b/zebra/zebra_vty.c
index bb16232118..cc1ba3d8f8 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -3976,6 +3976,7 @@ DEFUN (show_zebra,
ttable_rowseps(table, 0, BOTTOM, true, '-');
ttable_add_row(table, "OS|%s(%s)", cmd_system_get(), cmd_release_get());
+ ttable_add_row(table, "ECMP Maximum|%d", zrouter.multipath_num);
ttable_add_row(table, "v4 Forwarding|%s", ipforward() ? "On" : "Off");
ttable_add_row(table, "v6 Forwarding|%s",
ipforward_ipv6() ? "On" : "Off");
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index fdf21a135a..6a3b1bfbe3 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -826,8 +826,7 @@ static int zvni_map_to_svi_ns(struct ns *ns,
struct interface **p_ifp = (struct interface **)_p_ifp;
struct zebra_if *zif;
- if (!in_param)
- return NS_WALK_STOP;
+ assert(in_param && p_ifp);
/* TODO: Optimize with a hash. */
for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
@@ -842,8 +841,7 @@ static int zvni_map_to_svi_ns(struct ns *ns,
vl = (struct zebra_l2info_vlan *)&zif->l2info.vl;
if (vl->vid == in_param->vid) {
- if (p_ifp)
- *p_ifp = tmp_if;
+ *p_ifp = tmp_if;
return NS_WALK_STOP;
}
}
@@ -2122,13 +2120,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,
@@ -5169,16 +5160,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) {