summaryrefslogtreecommitdiff
path: root/pimd/pim_cmd_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_cmd_common.c')
-rw-r--r--pimd/pim_cmd_common.c2886
1 files changed, 2886 insertions, 0 deletions
diff --git a/pimd/pim_cmd_common.c b/pimd/pim_cmd_common.c
index c5d89f8065..d69b94ab12 100644
--- a/pimd/pim_cmd_common.c
+++ b/pimd/pim_cmd_common.c
@@ -31,13 +31,28 @@
#include "vrf.h"
#include "ferr.h"
#include "lib/srcdest_table.h"
+#include "lib/linklist.h"
#include "pimd.h"
#include "pim_vty.h"
#include "lib/northbound_cli.h"
#include "pim_errors.h"
#include "pim_nb.h"
+#include "pim_mroute.h"
+#include "pim_cmd.h"
+#include "pim6_cmd.h"
#include "pim_cmd_common.h"
+#include "pim_time.h"
+#include "pim_zebra.h"
+#include "pim_zlookup.h"
+#include "pim_iface.h"
+#include "pim_macro.h"
+#include "pim_neighbor.h"
+#include "pim_nht.h"
+#include "pim_sock.h"
+#include "pim_ssm.h"
+#include "pim_addr.h"
+#include "pim_static.h"
/**
* Get current node VRF name.
@@ -653,3 +668,2874 @@ int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str,
return nb_cli_apply_changes(vty, NULL);
}
+
+bool pim_sgaddr_match(pim_sgaddr item, pim_sgaddr match)
+{
+ return (pim_addr_is_any(match.grp) ||
+ !pim_addr_cmp(match.grp, item.grp)) &&
+ (pim_addr_is_any(match.src) ||
+ !pim_addr_cmp(match.src, item.src));
+}
+
+void json_object_pim_ifp_add(struct json_object *json, struct interface *ifp)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ifp->info;
+ json_object_string_add(json, "name", ifp->name);
+ json_object_string_add(json, "state", if_is_up(ifp) ? "up" : "down");
+ json_object_string_addf(json, "address", "%pPA",
+ &pim_ifp->primary_address);
+ json_object_int_add(json, "index", ifp->ifindex);
+
+ if (if_is_multicast(ifp))
+ json_object_boolean_true_add(json, "flagMulticast");
+
+ if (if_is_broadcast(ifp))
+ json_object_boolean_true_add(json, "flagBroadcast");
+
+ if (ifp->flags & IFF_ALLMULTI)
+ json_object_boolean_true_add(json, "flagAllMulticast");
+
+ if (ifp->flags & IFF_PROMISC)
+ json_object_boolean_true_add(json, "flagPromiscuous");
+
+ if (PIM_IF_IS_DELETED(ifp))
+ json_object_boolean_true_add(json, "flagDeleted");
+
+ if (pim_if_lan_delay_enabled(ifp))
+ json_object_boolean_true_add(json, "lanDelayEnabled");
+}
+
+void pim_print_ifp_flags(struct vty *vty, struct interface *ifp)
+{
+ vty_out(vty, "Flags\n");
+ vty_out(vty, "-----\n");
+ vty_out(vty, "All Multicast : %s\n",
+ (ifp->flags & IFF_ALLMULTI) ? "yes" : "no");
+ vty_out(vty, "Broadcast : %s\n",
+ if_is_broadcast(ifp) ? "yes" : "no");
+ vty_out(vty, "Deleted : %s\n",
+ PIM_IF_IS_DELETED(ifp) ? "yes" : "no");
+ vty_out(vty, "Interface Index : %d\n", ifp->ifindex);
+ vty_out(vty, "Multicast : %s\n",
+ if_is_multicast(ifp) ? "yes" : "no");
+ vty_out(vty, "Promiscuous : %s\n",
+ (ifp->flags & IFF_PROMISC) ? "yes" : "no");
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+}
+
+void json_object_pim_upstream_add(json_object *json, struct pim_upstream *up)
+{
+ json_object_boolean_add(
+ json, "drJoinDesired",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED));
+ json_object_boolean_add(
+ json, "drJoinDesiredUpdated",
+ CHECK_FLAG(up->flags,
+ PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED));
+ json_object_boolean_add(
+ json, "firstHopRouter",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_FHR));
+ json_object_boolean_add(
+ json, "sourceIgmp",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP));
+ json_object_boolean_add(
+ json, "sourcePim",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_PIM));
+ json_object_boolean_add(
+ json, "sourceStream",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_STREAM));
+ /* XXX: need to print ths flag in the plain text display as well */
+ json_object_boolean_add(
+ json, "sourceMsdp",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_MSDP));
+ json_object_boolean_add(
+ json, "sendSGRptPrune",
+ CHECK_FLAG(up->flags,
+ PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE));
+ json_object_boolean_add(
+ json, "lastHopRouter",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_LHR));
+ json_object_boolean_add(
+ json, "disableKATExpiry",
+ CHECK_FLAG(up->flags,
+ PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY));
+ json_object_boolean_add(
+ json, "staticIncomingInterface",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_STATIC_IIF));
+ json_object_boolean_add(
+ json, "allowIncomingInterfaceinOil",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL));
+ json_object_boolean_add(
+ json, "noPimRegistrationData",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA));
+ json_object_boolean_add(
+ json, "forcePimRegistration",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG));
+ json_object_boolean_add(
+ json, "sourceVxlanOrigination",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG));
+ json_object_boolean_add(
+ json, "sourceVxlanTermination",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM));
+ json_object_boolean_add(
+ json, "mlagVxlan",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN));
+ json_object_boolean_add(
+ json, "mlagNonDesignatedForwarder",
+ CHECK_FLAG(up->flags, PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF));
+}
+
+static const char *
+pim_upstream_state2brief_str(enum pim_upstream_state join_state,
+ char *state_str, size_t state_str_len)
+{
+ switch (join_state) {
+ case PIM_UPSTREAM_NOTJOINED:
+ strlcpy(state_str, "NotJ", state_str_len);
+ break;
+ case PIM_UPSTREAM_JOINED:
+ strlcpy(state_str, "J", state_str_len);
+ break;
+ default:
+ strlcpy(state_str, "Unk", state_str_len);
+ }
+ return state_str;
+}
+
+static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state,
+ char *state_str,
+ size_t state_str_len)
+{
+ switch (reg_state) {
+ case PIM_REG_NOINFO:
+ strlcpy(state_str, "RegNI", state_str_len);
+ break;
+ case PIM_REG_JOIN:
+ strlcpy(state_str, "RegJ", state_str_len);
+ break;
+ case PIM_REG_JOIN_PENDING:
+ case PIM_REG_PRUNE:
+ strlcpy(state_str, "RegP", state_str_len);
+ break;
+ }
+ return state_str;
+}
+
+void pim_show_rpf_refresh_stats(struct vty *vty, struct pim_instance *pim,
+ time_t now, json_object *json)
+{
+ char refresh_uptime[10];
+
+ pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now,
+ pim->rpf_cache_refresh_last);
+
+ if (json) {
+ json_object_int_add(json, "rpfCacheRefreshDelayMsecs",
+ router->rpf_cache_refresh_delay_msec);
+ json_object_int_add(
+ json, "rpfCacheRefreshTimer",
+ pim_time_timer_remain_msec(pim->rpf_cache_refresher));
+ json_object_int_add(json, "rpfCacheRefreshRequests",
+ pim->rpf_cache_refresh_requests);
+ json_object_int_add(json, "rpfCacheRefreshEvents",
+ pim->rpf_cache_refresh_events);
+ json_object_string_add(json, "rpfCacheRefreshLast",
+ refresh_uptime);
+ json_object_int_add(json, "nexthopLookups",
+ pim->nexthop_lookups);
+ json_object_int_add(json, "nexthopLookupsAvoided",
+ pim->nexthop_lookups_avoided);
+ } else {
+ vty_out(vty,
+ "RPF Cache Refresh Delay: %ld msecs\n"
+ "RPF Cache Refresh Timer: %ld msecs\n"
+ "RPF Cache Refresh Requests: %lld\n"
+ "RPF Cache Refresh Events: %lld\n"
+ "RPF Cache Refresh Last: %s\n"
+ "Nexthop Lookups: %lld\n"
+ "Nexthop Lookups Avoided: %lld\n",
+ router->rpf_cache_refresh_delay_msec,
+ pim_time_timer_remain_msec(pim->rpf_cache_refresher),
+ (long long)pim->rpf_cache_refresh_requests,
+ (long long)pim->rpf_cache_refresh_events,
+ refresh_uptime, (long long)pim->nexthop_lookups,
+ (long long)pim->nexthop_lookups_avoided);
+ }
+}
+
+void pim_show_rpf(struct pim_instance *pim, struct vty *vty, json_object *json)
+{
+ struct pim_upstream *up;
+ time_t now = pim_time_monotonic_sec();
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ pim_show_rpf_refresh_stats(vty, pim, now, json);
+
+ if (!json) {
+ vty_out(vty, "\n");
+ vty_out(vty,
+ "Source Group RpfIface RpfAddress RibNextHop Metric Pref\n");
+ }
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ char rpf_addr_str[PREFIX_STRLEN];
+ const char *rpf_ifname;
+ struct pim_rpf *rpf = &up->rpf;
+
+ pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str,
+ sizeof(rpf_addr_str));
+
+ rpf_ifname =
+ rpf->source_nexthop.interface ? rpf->source_nexthop
+ .interface->name
+ : "<ifname?>";
+
+ if (json) {
+ char grp_str[PIM_ADDRSTRLEN];
+ char src_str[PIM_ADDRSTRLEN];
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
+ &up->sg.grp);
+ snprintfrr(src_str, sizeof(src_str), "%pPAs",
+ &up->sg.src);
+
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str,
+ json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "rpfInterface",
+ rpf_ifname);
+ json_object_string_add(json_row, "rpfAddress",
+ rpf_addr_str);
+ json_object_string_addf(
+ json_row, "ribNexthop", "%pPAs",
+ &rpf->source_nexthop.mrib_nexthop_addr);
+ json_object_int_add(
+ json_row, "routeMetric",
+ rpf->source_nexthop.mrib_route_metric);
+ json_object_int_add(
+ json_row, "routePreference",
+ rpf->source_nexthop.mrib_metric_preference);
+ json_object_object_add(json_group, src_str, json_row);
+
+ } else {
+ vty_out(vty,
+ "%-15pPAs %-15pPAs %-16s %-15s %-15pPAs %6d %4d\n",
+ &up->sg.src, &up->sg.grp, rpf_ifname,
+ rpf_addr_str,
+ &rpf->source_nexthop.mrib_nexthop_addr,
+ rpf->source_nexthop.mrib_route_metric,
+ rpf->source_nexthop.mrib_metric_preference);
+ }
+ }
+}
+
+void pim_show_neighbors_secondary(struct pim_instance *pim, struct vty *vty)
+{
+ struct interface *ifp;
+
+ vty_out(vty,
+ "Interface Address Neighbor Secondary \n");
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp;
+ pim_addr ifaddr;
+ struct listnode *neighnode;
+ struct pim_neighbor *neigh;
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ ifaddr = pim_ifp->primary_address;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode,
+ neigh)) {
+ struct listnode *prefix_node;
+ struct prefix *p;
+
+ if (!neigh->prefix_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list,
+ prefix_node, p))
+ vty_out(vty,
+ "%-16s %-15pPAs %-15pPAs %-15pFX\n",
+ ifp->name, &ifaddr, &neigh->source_addr,
+ p);
+ }
+ }
+}
+
+void pim_show_state(struct pim_instance *pim, struct vty *vty,
+ const char *src_or_group, const char *group,
+ json_object *json)
+{
+ struct channel_oil *c_oil;
+ json_object *json_group = NULL;
+ json_object *json_ifp_in = NULL;
+ json_object *json_ifp_out = NULL;
+ json_object *json_source = NULL;
+ time_t now;
+ int first_oif;
+
+ now = pim_time_monotonic_sec();
+
+ if (!json) {
+ vty_out(vty,
+ "Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted");
+ vty_out(vty,
+ "\nActive Source Group RPT IIF OIL\n");
+ }
+
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
+ char src_str[PIM_ADDRSTRLEN];
+ char grp_str[PIM_ADDRSTRLEN];
+ char in_ifname[INTERFACE_NAMSIZ + 1];
+ char out_ifname[INTERFACE_NAMSIZ + 1];
+ int oif_vif_index;
+ struct interface *ifp_in;
+ bool isRpt;
+
+ first_oif = 1;
+
+ if ((c_oil->up &&
+ PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) ||
+ pim_addr_is_any(*oil_origin(c_oil)))
+ isRpt = true;
+ else
+ isRpt = false;
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
+ oil_mcastgrp(c_oil));
+ snprintfrr(src_str, sizeof(src_str), "%pPAs",
+ oil_origin(c_oil));
+ ifp_in = pim_if_find_by_vif_index(pim, *oil_parent(c_oil));
+
+ if (ifp_in)
+ strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname));
+ else
+ strlcpy(in_ifname, "<iif?>", sizeof(in_ifname));
+
+ if (src_or_group) {
+ if (strcmp(src_or_group, src_str) &&
+ strcmp(src_or_group, grp_str))
+ continue;
+
+ if (group && strcmp(group, grp_str))
+ continue;
+ }
+
+ if (json) {
+
+ /* Find the group, create it if it doesn't exist */
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str,
+ json_group);
+ }
+
+ /* Find the source nested under the group, create it if
+ * it doesn't exist
+ */
+ json_object_object_get_ex(json_group, src_str,
+ &json_source);
+
+ if (!json_source) {
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, src_str,
+ json_source);
+ }
+
+ /* Find the inbound interface nested under the source,
+ * create it if it doesn't exist
+ */
+ json_object_object_get_ex(json_source, in_ifname,
+ &json_ifp_in);
+
+ if (!json_ifp_in) {
+ json_ifp_in = json_object_new_object();
+ json_object_object_add(json_source, in_ifname,
+ json_ifp_in);
+ json_object_int_add(json_source, "Installed",
+ c_oil->installed);
+ json_object_int_add(json_source, "installed",
+ c_oil->installed);
+ json_object_boolean_add(json_source, "isRpt",
+ 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 %-15pPAs %-15pPAs %-3s %-16s ",
+ c_oil->installed, oil_origin(c_oil),
+ oil_mcastgrp(c_oil), isRpt ? "y" : "n",
+ in_ifname);
+
+ for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
+ ++oif_vif_index) {
+ struct interface *ifp_out;
+ char oif_uptime[10];
+ int ttl;
+
+ ttl = oil_if_has(c_oil, oif_vif_index);
+ if (ttl < 1)
+ continue;
+
+ ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index);
+ pim_time_uptime(
+ oif_uptime, sizeof(oif_uptime),
+ now - c_oil->oif_creation[oif_vif_index]);
+
+ if (ifp_out)
+ strlcpy(out_ifname, ifp_out->name,
+ sizeof(out_ifname));
+ else
+ strlcpy(out_ifname, "<oif?>",
+ sizeof(out_ifname));
+
+ if (json) {
+ json_ifp_out = json_object_new_object();
+ json_object_string_add(json_ifp_out, "source",
+ src_str);
+ json_object_string_add(json_ifp_out, "group",
+ grp_str);
+ json_object_string_add(json_ifp_out,
+ "inboundInterface",
+ in_ifname);
+ json_object_string_add(json_ifp_out,
+ "outboundInterface",
+ out_ifname);
+ json_object_int_add(json_ifp_out, "installed",
+ c_oil->installed);
+
+ json_object_object_add(json_ifp_in, out_ifname,
+ json_ifp_out);
+ } else {
+ if (first_oif) {
+ first_oif = 0;
+ vty_out(vty, "%s(%c%c%c%c%c)",
+ out_ifname,
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_GM)
+ ? 'I'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_PIM)
+ ? 'J'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_VXLAN)
+ ? 'V'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_STAR)
+ ? '*'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_MUTE)
+ ? 'M'
+ : ' ');
+ } else
+ vty_out(vty, ", %s(%c%c%c%c%c)",
+ out_ifname,
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_GM)
+ ? 'I'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_PIM)
+ ? 'J'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_VXLAN)
+ ? 'V'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_STAR)
+ ? '*'
+ : ' ',
+ (c_oil->oif_flags
+ [oif_vif_index] &
+ PIM_OIF_FLAG_MUTE)
+ ? 'M'
+ : ' ');
+ }
+ }
+
+ if (!json)
+ vty_out(vty, "\n");
+ }
+
+ if (!json)
+ vty_out(vty, "\n");
+}
+
+/* pim statistics - just adding only bsm related now.
+ * We can continue to add all pim related stats here.
+ */
+void pim_show_statistics(struct pim_instance *pim, struct vty *vty,
+ const char *ifname, bool uj)
+{
+ json_object *json = NULL;
+ struct interface *ifp;
+
+ if (uj) {
+ json = json_object_new_object();
+ json_object_int_add(json, "bsmRx", pim->bsm_rcvd);
+ json_object_int_add(json, "bsmTx", pim->bsm_sent);
+ json_object_int_add(json, "bsmDropped", pim->bsm_dropped);
+ } else {
+ vty_out(vty, "BSM Statistics :\n");
+ vty_out(vty, "----------------\n");
+ vty_out(vty, "Number of Received BSMs : %" PRIu64 "\n",
+ pim->bsm_rcvd);
+ vty_out(vty, "Number of Forwared BSMs : %" PRIu64 "\n",
+ pim->bsm_sent);
+ vty_out(vty, "Number of Dropped BSMs : %" PRIu64 "\n",
+ pim->bsm_dropped);
+ }
+
+ vty_out(vty, "\n");
+
+ /* scan interfaces */
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp = ifp->info;
+
+ if (ifname && strcmp(ifname, ifp->name))
+ continue;
+
+ if (!pim_ifp)
+ continue;
+
+ if (!uj) {
+ vty_out(vty, "Interface : %s\n", ifp->name);
+ vty_out(vty, "-------------------\n");
+ vty_out(vty,
+ "Number of BSMs dropped due to config miss : %u\n",
+ pim_ifp->pim_ifstat_bsm_cfg_miss);
+ vty_out(vty, "Number of unicast BSMs dropped : %u\n",
+ pim_ifp->pim_ifstat_ucast_bsm_cfg_miss);
+ vty_out(vty,
+ "Number of BSMs dropped due to invalid scope zone : %u\n",
+ pim_ifp->pim_ifstat_bsm_invalid_sz);
+ } else {
+
+ json_object *json_row = NULL;
+
+ json_row = json_object_new_object();
+
+ json_object_string_add(json_row, "If Name", ifp->name);
+ json_object_int_add(json_row, "bsmDroppedConfig",
+ pim_ifp->pim_ifstat_bsm_cfg_miss);
+ json_object_int_add(
+ json_row, "bsmDroppedUnicast",
+ pim_ifp->pim_ifstat_ucast_bsm_cfg_miss);
+ json_object_int_add(json_row,
+ "bsmDroppedInvalidScopeZone",
+ pim_ifp->pim_ifstat_bsm_invalid_sz);
+ json_object_object_add(json, ifp->name, json_row);
+ }
+ vty_out(vty, "\n");
+ }
+
+ if (uj)
+ vty_json(vty, json);
+}
+
+void pim_show_upstream(struct pim_instance *pim, struct vty *vty,
+ pim_sgaddr *sg, json_object *json)
+{
+ struct pim_upstream *up;
+ time_t now;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ now = pim_time_monotonic_sec();
+
+ if (!json)
+ vty_out(vty,
+ "Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt\n");
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ char uptime[10];
+ char join_timer[10];
+ char rs_timer[10];
+ char ka_timer[10];
+ char msdp_reg_timer[10];
+ char state_str[PIM_REG_STATE_STR_LEN];
+
+ if (!pim_sgaddr_match(up->sg, *sg))
+ continue;
+
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - up->state_transition);
+ pim_time_timer_to_hhmmss(join_timer, sizeof(join_timer),
+ up->t_join_timer);
+
+ /*
+ * If the upstream is not dummy and it has a J/P timer for the
+ * neighbor display that
+ */
+ if (!up->t_join_timer && up->rpf.source_nexthop.interface) {
+ struct pim_neighbor *nbr;
+
+ nbr = pim_neighbor_find_prefix(
+ up->rpf.source_nexthop.interface,
+ &up->rpf.rpf_addr);
+ if (nbr)
+ pim_time_timer_to_hhmmss(join_timer,
+ sizeof(join_timer),
+ nbr->jp_timer);
+ }
+
+ pim_time_timer_to_hhmmss(rs_timer, sizeof(rs_timer),
+ up->t_rs_timer);
+ pim_time_timer_to_hhmmss(ka_timer, sizeof(ka_timer),
+ up->t_ka_timer);
+ pim_time_timer_to_hhmmss(msdp_reg_timer, sizeof(msdp_reg_timer),
+ up->t_msdp_reg_timer);
+
+ pim_upstream_state2brief_str(up->join_state, state_str,
+ sizeof(state_str));
+ if (up->reg_state != PIM_REG_NOINFO) {
+ char tmp_str[PIM_REG_STATE_STR_LEN];
+ char tmp[sizeof(state_str) + 1];
+
+ snprintf(tmp, sizeof(tmp), ",%s",
+ pim_reg_state2brief_str(up->reg_state, tmp_str,
+ sizeof(tmp_str)));
+ strlcat(state_str, tmp, sizeof(state_str));
+ }
+
+ if (json) {
+ char grp_str[PIM_ADDRSTRLEN];
+ char src_str[PIM_ADDRSTRLEN];
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
+ &up->sg.grp);
+ snprintfrr(src_str, sizeof(src_str), "%pPAs",
+ &up->sg.src);
+
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str,
+ json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(
+ json_row, "inboundInterface",
+ up->rpf.source_nexthop.interface
+ ? up->rpf.source_nexthop.interface->name
+ : "Unknown");
+
+ /*
+ * The RPF address we use is slightly different
+ * based upon what we are looking up.
+ * If we have a S, list that unless
+ * we are the FHR, else we just put
+ * the RP as the rpfAddress
+ */
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR ||
+ pim_addr_is_any(up->sg.src)) {
+ struct pim_rpf *rpg;
+
+ rpg = RP(pim, up->sg.grp);
+ json_object_string_addf(json_row, "rpfAddress",
+ "%pFX", &rpg->rpf_addr);
+ } else {
+ json_object_string_add(json_row, "rpfAddress",
+ src_str);
+ }
+
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "state", state_str);
+ json_object_string_add(
+ json_row, "joinState",
+ pim_upstream_state2str(up->join_state));
+ json_object_string_add(
+ json_row, "regState",
+ pim_reg_state2str(up->reg_state, state_str,
+ sizeof(state_str)));
+ json_object_string_add(json_row, "upTime", uptime);
+ json_object_string_add(json_row, "joinTimer",
+ join_timer);
+ json_object_string_add(json_row, "resetTimer",
+ rs_timer);
+ json_object_string_add(json_row, "keepaliveTimer",
+ ka_timer);
+ json_object_string_add(json_row, "msdpRegTimer",
+ msdp_reg_timer);
+ json_object_int_add(json_row, "refCount",
+ up->ref_count);
+ json_object_int_add(json_row, "sptBit", up->sptbit);
+ json_object_object_add(json_group, src_str, json_row);
+ } else {
+ vty_out(vty,
+ "%-16s%-15pPAs %-15pPAs %-11s %-8s %-9s %-9s %-9s %6d\n",
+ up->rpf.source_nexthop.interface
+ ? up->rpf.source_nexthop.interface->name
+ : "Unknown",
+ &up->sg.src, &up->sg.grp, state_str, uptime,
+ join_timer, rs_timer, ka_timer, up->ref_count);
+ }
+ }
+}
+
+static void pim_show_join_desired_helper(struct pim_instance *pim,
+ struct vty *vty,
+ struct pim_upstream *up,
+ json_object *json, bool uj)
+{
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ if (uj) {
+ char grp_str[PIM_ADDRSTRLEN];
+ char src_str[PIM_ADDRSTRLEN];
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &up->sg.grp);
+ snprintfrr(src_str, sizeof(src_str), "%pPAs", &up->sg.src);
+
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+
+ if (pim_upstream_evaluate_join_desired(pim, up))
+ json_object_boolean_true_add(json_row,
+ "evaluateJoinDesired");
+
+ json_object_object_add(json_group, src_str, json_row);
+
+ } else {
+ vty_out(vty, "%-15pPAs %-15pPAs %-6s\n", &up->sg.src,
+ &up->sg.grp,
+ pim_upstream_evaluate_join_desired(pim, up) ? "yes"
+ : "no");
+ }
+}
+
+void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, bool uj)
+{
+ struct pim_upstream *up;
+
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty, "Source Group EvalJD\n");
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ /* scan all interfaces */
+ pim_show_join_desired_helper(pim, vty, up, json, uj);
+ }
+
+ if (uj)
+ vty_json(vty, json);
+}
+
+void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, bool uj)
+{
+ struct pim_upstream *up;
+ json_object *json = NULL;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "Source Group RpfIface RibNextHop RpfAddress \n");
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ char rpf_addr_str[PREFIX_STRLEN];
+ struct pim_rpf *rpf;
+ const char *rpf_ifname;
+
+ rpf = &up->rpf;
+
+ pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str,
+ sizeof(rpf_addr_str));
+
+ rpf_ifname =
+ rpf->source_nexthop.interface ? rpf->source_nexthop
+ .interface->name
+ : "<ifname?>";
+
+ if (uj) {
+ char grp_str[PIM_ADDRSTRLEN];
+ char src_str[PIM_ADDRSTRLEN];
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
+ &up->sg.grp);
+ snprintfrr(src_str, sizeof(src_str), "%pPAs",
+ &up->sg.src);
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str,
+ json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "source", src_str);
+ json_object_string_add(json_row, "group", grp_str);
+ json_object_string_add(json_row, "rpfInterface",
+ rpf_ifname);
+ json_object_string_addf(
+ json_row, "ribNexthop", "%pPAs",
+ &rpf->source_nexthop.mrib_nexthop_addr);
+ json_object_string_add(json_row, "rpfAddress",
+ rpf_addr_str);
+ json_object_object_add(json_group, src_str, json_row);
+ } else {
+ vty_out(vty, "%-15pPAs %-15pPAs %-16s %-15pPA %-15s\n",
+ &up->sg.src, &up->sg.grp, rpf_ifname,
+ &rpf->source_nexthop.mrib_nexthop_addr,
+ rpf_addr_str);
+ }
+ }
+
+ if (uj)
+ vty_json(vty, json);
+}
+
+static void pim_show_join_helper(struct vty *vty, struct pim_interface *pim_ifp,
+ struct pim_ifchannel *ch, json_object *json,
+ time_t now)
+{
+ json_object *json_iface = NULL;
+ json_object *json_row = NULL;
+ json_object *json_grp = NULL;
+ pim_addr ifaddr;
+ char uptime[10];
+ char expire[10];
+ char prune[10];
+
+ ifaddr = pim_ifp->primary_address;
+
+ pim_time_uptime_begin(uptime, sizeof(uptime), now, ch->ifjoin_creation);
+ pim_time_timer_to_mmss(expire, sizeof(expire),
+ ch->t_ifjoin_expiry_timer);
+ pim_time_timer_to_mmss(prune, sizeof(prune),
+ ch->t_ifjoin_prune_pending_timer);
+
+ if (json) {
+ char ch_grp_str[PIM_ADDRSTRLEN];
+
+ json_object_object_get_ex(json, ch->interface->name,
+ &json_iface);
+
+ if (!json_iface) {
+ json_iface = json_object_new_object();
+ json_object_pim_ifp_add(json_iface, ch->interface);
+ json_object_object_add(json, ch->interface->name,
+ json_iface);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_addf(json_row, "source", "%pPAs",
+ &ch->sg.src);
+ json_object_string_addf(json_row, "group", "%pPAs",
+ &ch->sg.grp);
+ json_object_string_add(json_row, "upTime", uptime);
+ json_object_string_add(json_row, "expire", expire);
+ json_object_string_add(json_row, "prune", prune);
+ 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 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))
+ json_object_int_add(json_row, "protocolIgmp", 1);
+ snprintfrr(ch_grp_str, sizeof(ch_grp_str), "%pPAs",
+ &ch->sg.grp);
+ json_object_object_get_ex(json_iface, ch_grp_str, &json_grp);
+ if (!json_grp) {
+ json_grp = json_object_new_object();
+ json_object_object_addf(json_grp, json_row, "%pPAs",
+ &ch->sg.src);
+ json_object_object_addf(json_iface, json_grp, "%pPAs",
+ &ch->sg.grp);
+ } else
+ json_object_object_addf(json_grp, json_row, "%pPAs",
+ &ch->sg.src);
+ } else {
+ vty_out(vty,
+ "%-16s %-15pPAs %-15pPAs %-15pPAs %-10s %8s %-6s %5s\n",
+ ch->interface->name, &ifaddr, &ch->sg.src, &ch->sg.grp,
+ pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
+ uptime, expire, prune);
+ }
+}
+
+void pim_show_join(struct pim_instance *pim, struct vty *vty, pim_sgaddr *sg,
+ json_object *json)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+ struct interface *ifp;
+ time_t now;
+
+ now = pim_time_monotonic_sec();
+
+ if (!json)
+ vty_out(vty,
+ "Interface Address Source Group State Uptime Expire Prune\n");
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
+ if (!pim_sgaddr_match(ch->sg, *sg))
+ continue;
+
+ pim_show_join_helper(vty, pim_ifp, ch, json, now);
+ } /* scan interface channels */
+ }
+}
+
+static void pim_show_jp_agg_helper(struct vty *vty, struct interface *ifp,
+ struct pim_neighbor *neigh,
+ struct pim_upstream *up, int is_join)
+{
+ vty_out(vty, "%-16s %-15pPAs %-15pPAs %-15pPAs %5s\n", ifp->name,
+ &neigh->source_addr, &up->sg.src, &up->sg.grp,
+ is_join ? "J" : "P");
+}
+
+void pim_show_jp_agg_list(struct pim_instance *pim, struct vty *vty)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct listnode *n_node;
+ struct pim_neighbor *neigh;
+ struct listnode *jag_node;
+ struct pim_jp_agg_group *jag;
+ struct listnode *js_node;
+ struct pim_jp_sources *js;
+
+ vty_out(vty,
+ "Interface RPF Nbr Source Group State\n");
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, n_node,
+ neigh)) {
+ for (ALL_LIST_ELEMENTS_RO(neigh->upstream_jp_agg,
+ jag_node, jag)) {
+ for (ALL_LIST_ELEMENTS_RO(jag->sources, js_node,
+ js)) {
+ pim_show_jp_agg_helper(vty, ifp, neigh,
+ js->up,
+ js->is_join);
+ }
+ }
+ }
+ }
+}
+
+static void pim_show_membership_helper(struct vty *vty,
+ struct pim_interface *pim_ifp,
+ struct pim_ifchannel *ch,
+ struct json_object *json)
+{
+ json_object *json_iface = NULL;
+ json_object *json_row = NULL;
+
+ json_object_object_get_ex(json, ch->interface->name, &json_iface);
+ if (!json_iface) {
+ json_iface = json_object_new_object();
+ json_object_pim_ifp_add(json_iface, ch->interface);
+ json_object_object_add(json, ch->interface->name, json_iface);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_addf(json_row, "source", "%pPAs", &ch->sg.src);
+ json_object_string_addf(json_row, "group", "%pPAs", &ch->sg.grp);
+ json_object_string_add(json_row, "localMembership",
+ ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO
+ ? "NOINFO"
+ : "INCLUDE");
+ json_object_object_addf(json_iface, json_row, "%pPAs", &ch->sg.grp);
+}
+
+void pim_show_membership(struct pim_instance *pim, struct vty *vty, bool uj)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+ struct interface *ifp;
+ enum json_type type;
+ json_object *json = NULL;
+ json_object *json_tmp = NULL;
+
+ json = json_object_new_object();
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+ RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
+ pim_show_membership_helper(vty, pim_ifp, ch, json);
+ } /* scan interface channels */
+ }
+
+ if (uj) {
+ vty_json(vty, json);
+ } else {
+ vty_out(vty,
+ "Interface Address Source Group Membership\n");
+
+ /*
+ * Example of the json data we are traversing
+ *
+ * {
+ * "swp3":{
+ * "name":"swp3",
+ * "state":"up",
+ * "address":"10.1.20.1",
+ * "index":5,
+ * "flagMulticast":true,
+ * "flagBroadcast":true,
+ * "lanDelayEnabled":true,
+ * "226.10.10.10":{
+ * "source":"*",
+ * "group":"226.10.10.10",
+ * "localMembership":"INCLUDE"
+ * }
+ * }
+ * }
+ */
+
+ /* foreach interface */
+ json_object_object_foreach(json, key, val)
+ {
+
+ /* Find all of the keys where the val is an object. In
+ * the example
+ * above the only one is 226.10.10.10
+ */
+ json_object_object_foreach(val, if_field_key,
+ if_field_val)
+ {
+ type = json_object_get_type(if_field_val);
+
+ if (type == json_type_object) {
+ vty_out(vty, "%-16s ", key);
+
+ json_object_object_get_ex(
+ val, "address", &json_tmp);
+ vty_out(vty, "%-15s ",
+ json_object_get_string(
+ json_tmp));
+
+ json_object_object_get_ex(if_field_val,
+ "source",
+ &json_tmp);
+ vty_out(vty, "%-15s ",
+ json_object_get_string(
+ json_tmp));
+
+ /* Group */
+ vty_out(vty, "%-15s ", if_field_key);
+
+ json_object_object_get_ex(
+ if_field_val, "localMembership",
+ &json_tmp);
+ vty_out(vty, "%-10s\n",
+ json_object_get_string(
+ json_tmp));
+ }
+ }
+ }
+ json_object_free(json);
+ }
+}
+
+static void pim_show_channel_helper(struct pim_instance *pim, struct vty *vty,
+ struct pim_interface *pim_ifp,
+ struct pim_ifchannel *ch, json_object *json,
+ bool uj)
+{
+ struct pim_upstream *up = ch->upstream;
+ json_object *json_group = NULL;
+ json_object *json_row = NULL;
+
+ if (uj) {
+ char grp_str[PIM_ADDRSTRLEN];
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &up->sg.grp);
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str, json_group);
+ }
+
+ json_row = json_object_new_object();
+ json_object_pim_upstream_add(json_row, up);
+ json_object_string_add(json_row, "interface",
+ ch->interface->name);
+ json_object_string_addf(json_row, "source", "%pPAs",
+ &up->sg.src);
+ json_object_string_addf(json_row, "group", "%pPAs",
+ &up->sg.grp);
+
+ if (pim_macro_ch_lost_assert(ch))
+ json_object_boolean_true_add(json_row, "lostAssert");
+
+ if (pim_macro_chisin_joins(ch))
+ json_object_boolean_true_add(json_row, "joins");
+
+ if (pim_macro_chisin_pim_include(ch))
+ json_object_boolean_true_add(json_row, "pimInclude");
+
+ if (pim_upstream_evaluate_join_desired(pim, up))
+ json_object_boolean_true_add(json_row,
+ "evaluateJoinDesired");
+
+ json_object_object_addf(json_group, json_row, "%pPAs",
+ &up->sg.src);
+
+ } else {
+ vty_out(vty,
+ "%-16s %-15pPAs %-15pPAs %-10s %-5s %-10s %-11s %-6s\n",
+ ch->interface->name, &up->sg.src, &up->sg.grp,
+ pim_macro_ch_lost_assert(ch) ? "yes" : "no",
+ pim_macro_chisin_joins(ch) ? "yes" : "no",
+ pim_macro_chisin_pim_include(ch) ? "yes" : "no",
+ PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(up->flags)
+ ? "yes"
+ : "no",
+ pim_upstream_evaluate_join_desired(pim, up) ? "yes"
+ : "no");
+ }
+}
+
+void pim_show_channel(struct pim_instance *pim, struct vty *vty, bool uj)
+{
+ struct pim_interface *pim_ifp;
+ struct pim_ifchannel *ch;
+ struct interface *ifp;
+
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+ else
+ vty_out(vty,
+ "Interface Source Group LostAssert Joins PimInclude JoinDesired EvalJD\n");
+
+ /* scan per-interface (S,G) state */
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+ if (!pim_ifp)
+ continue;
+
+
+ RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
+ /* scan all interfaces */
+ pim_show_channel_helper(pim, vty, pim_ifp, ch, json,
+ uj);
+ }
+ }
+
+ if (uj)
+ vty_json(vty, json);
+}
+
+void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, bool mlag,
+ json_object *json)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up;
+ int fhr = 0;
+ int pim_nbrs = 0;
+ int pim_ifchannels = 0;
+ bool uj = true;
+ json_object *json_row = NULL;
+ json_object *json_tmp;
+
+ if (!json) {
+ uj = false;
+ json = json_object_new_object();
+ }
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (mlag == true && pim_ifp->activeactive == false)
+ continue;
+
+ pim_nbrs = pim_ifp->pim_neighbor_list->count;
+ pim_ifchannels = pim_if_ifchannel_count(pim_ifp);
+ fhr = 0;
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up)
+ if (ifp == up->rpf.source_nexthop.interface)
+ if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
+ fhr++;
+
+ json_row = json_object_new_object();
+ json_object_pim_ifp_add(json_row, ifp);
+ json_object_int_add(json_row, "pimNeighbors", pim_nbrs);
+ json_object_int_add(json_row, "pimIfChannels", pim_ifchannels);
+ json_object_int_add(json_row, "firstHopRouterCount", fhr);
+ json_object_string_addf(json_row, "pimDesignatedRouter",
+ "%pPAs", &pim_ifp->pim_dr_addr);
+
+ if (pim_addr_cmp(pim_ifp->pim_dr_addr,
+ pim_ifp->primary_address))
+ json_object_boolean_true_add(
+ json_row, "pimDesignatedRouterLocal");
+
+ json_object_object_add(json, ifp->name, json_row);
+ }
+
+ if (!uj) {
+ vty_out(vty,
+ "Interface State Address PIM Nbrs PIM DR FHR IfChannels\n");
+
+ json_object_object_foreach(json, key, val)
+ {
+ vty_out(vty, "%-16s ", key);
+
+ json_object_object_get_ex(val, "state", &json_tmp);
+ vty_out(vty, "%5s ", json_object_get_string(json_tmp));
+
+ json_object_object_get_ex(val, "address", &json_tmp);
+ vty_out(vty, "%15s ",
+ json_object_get_string(json_tmp));
+
+ json_object_object_get_ex(val, "pimNeighbors",
+ &json_tmp);
+ vty_out(vty, "%8d ", json_object_get_int(json_tmp));
+
+ if (json_object_object_get_ex(
+ val, "pimDesignatedRouterLocal",
+ &json_tmp)) {
+ vty_out(vty, "%15s ", "local");
+ } else {
+ json_object_object_get_ex(
+ val, "pimDesignatedRouter", &json_tmp);
+ vty_out(vty, "%15s ",
+ json_object_get_string(json_tmp));
+ }
+
+ json_object_object_get_ex(val, "firstHopRouter",
+ &json_tmp);
+ vty_out(vty, "%3d ", json_object_get_int(json_tmp));
+
+ json_object_object_get_ex(val, "pimIfChannels",
+ &json_tmp);
+ vty_out(vty, "%9d\n", json_object_get_int(json_tmp));
+ }
+ }
+}
+
+void pim_show_interfaces_single(struct pim_instance *pim, struct vty *vty,
+ const char *ifname, bool mlag,
+ json_object *json)
+{
+ pim_addr ifaddr;
+ struct interface *ifp;
+ struct listnode *neighnode;
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+ struct pim_upstream *up;
+ time_t now;
+ char dr_str[PIM_ADDRSTRLEN];
+ char dr_uptime[10];
+ char expire[10];
+ char grp_str[PIM_ADDRSTRLEN];
+ char hello_period[10];
+ char hello_timer[10];
+ char neigh_src_str[PIM_ADDRSTRLEN];
+ char src_str[PIM_ADDRSTRLEN];
+ char stat_uptime[10];
+ char uptime[10];
+ int found_ifname = 0;
+ int print_header;
+ json_object *json_row = NULL;
+ json_object *json_pim_neighbor = NULL;
+ json_object *json_pim_neighbors = NULL;
+ json_object *json_group = NULL;
+ json_object *json_group_source = NULL;
+ json_object *json_fhr_sources = NULL;
+ struct pim_secondary_addr *sec_addr;
+ struct listnode *sec_node;
+
+ now = pim_time_monotonic_sec();
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (mlag == true && pim_ifp->activeactive == false)
+ continue;
+
+ if (strcmp(ifname, "detail") && strcmp(ifname, ifp->name))
+ continue;
+
+ found_ifname = 1;
+ ifaddr = pim_ifp->primary_address;
+ snprintfrr(dr_str, sizeof(dr_str), "%pPAs",
+ &pim_ifp->pim_dr_addr);
+ pim_time_uptime_begin(dr_uptime, sizeof(dr_uptime), now,
+ pim_ifp->pim_dr_election_last);
+ pim_time_timer_to_hhmmss(hello_timer, sizeof(hello_timer),
+ pim_ifp->t_pim_hello_timer);
+ pim_time_mmss(hello_period, sizeof(hello_period),
+ pim_ifp->pim_hello_period);
+ pim_time_uptime(stat_uptime, sizeof(stat_uptime),
+ now - pim_ifp->pim_ifstat_start);
+
+ if (json) {
+ json_row = json_object_new_object();
+ json_object_pim_ifp_add(json_row, ifp);
+
+ if (!pim_addr_is_any(pim_ifp->update_source)) {
+ json_object_string_addf(
+ json_row, "useSource", "%pPAs",
+ &pim_ifp->update_source);
+ }
+ if (pim_ifp->sec_addr_list) {
+ json_object *sec_list = NULL;
+
+ sec_list = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(
+ pim_ifp->sec_addr_list, sec_node,
+ sec_addr)) {
+ json_object_array_add(
+ sec_list,
+ json_object_new_stringf(
+ "%pFXh",
+ &sec_addr->addr));
+ }
+ json_object_object_add(json_row,
+ "secondaryAddressList",
+ sec_list);
+ }
+
+ /* PIM neighbors */
+ if (pim_ifp->pim_neighbor_list->count) {
+ json_pim_neighbors = json_object_new_object();
+
+ for (ALL_LIST_ELEMENTS_RO(
+ pim_ifp->pim_neighbor_list,
+ neighnode, neigh)) {
+ json_pim_neighbor =
+ json_object_new_object();
+ snprintfrr(neigh_src_str,
+ sizeof(neigh_src_str),
+ "%pPAs",
+ &neigh->source_addr);
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - neigh->creation);
+ pim_time_timer_to_hhmmss(
+ expire, sizeof(expire),
+ neigh->t_expire_timer);
+
+ json_object_string_add(
+ json_pim_neighbor, "address",
+ neigh_src_str);
+ json_object_string_add(
+ json_pim_neighbor, "upTime",
+ uptime);
+ json_object_string_add(
+ json_pim_neighbor, "holdtime",
+ expire);
+
+ json_object_object_add(
+ json_pim_neighbors,
+ neigh_src_str,
+ json_pim_neighbor);
+ }
+
+ json_object_object_add(json_row, "neighbors",
+ json_pim_neighbors);
+ }
+
+ json_object_string_add(json_row, "drAddress", dr_str);
+ json_object_int_add(json_row, "drPriority",
+ pim_ifp->pim_dr_priority);
+ json_object_string_add(json_row, "drUptime", dr_uptime);
+ json_object_int_add(json_row, "drElections",
+ pim_ifp->pim_dr_election_count);
+ json_object_int_add(json_row, "drChanges",
+ pim_ifp->pim_dr_election_changes);
+
+ /* FHR */
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ if (ifp != up->rpf.source_nexthop.interface)
+ continue;
+
+ if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_FHR))
+ continue;
+
+ if (!json_fhr_sources)
+ json_fhr_sources =
+ json_object_new_object();
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
+ &up->sg.grp);
+ snprintfrr(src_str, sizeof(src_str), "%pPAs",
+ &up->sg.src);
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - up->state_transition);
+
+ /*
+ * Does this group live in json_fhr_sources?
+ * If not create it.
+ */
+ json_object_object_get_ex(json_fhr_sources,
+ grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json_fhr_sources,
+ grp_str,
+ json_group);
+ }
+
+ json_group_source = json_object_new_object();
+ json_object_string_add(json_group_source,
+ "source", src_str);
+ json_object_string_add(json_group_source,
+ "group", grp_str);
+ json_object_string_add(json_group_source,
+ "upTime", uptime);
+ json_object_object_add(json_group, src_str,
+ json_group_source);
+ }
+
+ if (json_fhr_sources) {
+ json_object_object_add(json_row,
+ "firstHopRouter",
+ json_fhr_sources);
+ }
+
+ json_object_int_add(json_row, "helloPeriod",
+ pim_ifp->pim_hello_period);
+ json_object_int_add(json_row, "holdTime",
+ PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+ json_object_string_add(json_row, "helloTimer",
+ hello_timer);
+ json_object_string_add(json_row, "helloStatStart",
+ stat_uptime);
+ json_object_int_add(json_row, "helloReceived",
+ pim_ifp->pim_ifstat_hello_recv);
+ json_object_int_add(json_row, "helloReceivedFailed",
+ pim_ifp->pim_ifstat_hello_recvfail);
+ json_object_int_add(json_row, "helloSend",
+ pim_ifp->pim_ifstat_hello_sent);
+ json_object_int_add(json_row, "hellosendFailed",
+ pim_ifp->pim_ifstat_hello_sendfail);
+ json_object_int_add(json_row, "helloGenerationId",
+ pim_ifp->pim_generation_id);
+
+ json_object_int_add(
+ json_row, "effectivePropagationDelay",
+ pim_if_effective_propagation_delay_msec(ifp));
+ json_object_int_add(
+ json_row, "effectiveOverrideInterval",
+ pim_if_effective_override_interval_msec(ifp));
+ json_object_int_add(
+ json_row, "joinPruneOverrideInterval",
+ pim_if_jp_override_interval_msec(ifp));
+
+ json_object_int_add(
+ json_row, "propagationDelay",
+ pim_ifp->pim_propagation_delay_msec);
+ json_object_int_add(
+ json_row, "propagationDelayHighest",
+ pim_ifp->pim_neighbors_highest_propagation_delay_msec);
+ json_object_int_add(
+ json_row, "overrideInterval",
+ pim_ifp->pim_override_interval_msec);
+ json_object_int_add(
+ json_row, "overrideIntervalHighest",
+ pim_ifp->pim_neighbors_highest_override_interval_msec);
+ if (pim_ifp->bsm_enable)
+ json_object_boolean_true_add(json_row,
+ "bsmEnabled");
+ if (pim_ifp->ucast_bsm_accept)
+ json_object_boolean_true_add(json_row,
+ "ucastBsmEnabled");
+ json_object_object_add(json, ifp->name, json_row);
+
+ } else {
+ vty_out(vty, "Interface : %s\n", ifp->name);
+ vty_out(vty, "State : %s\n",
+ if_is_up(ifp) ? "up" : "down");
+ if (!pim_addr_is_any(pim_ifp->update_source)) {
+ vty_out(vty, "Use Source : %pPAs\n",
+ &pim_ifp->update_source);
+ }
+ if (pim_ifp->sec_addr_list) {
+ vty_out(vty, "Address : %pPAs (primary)\n",
+ &ifaddr);
+ for (ALL_LIST_ELEMENTS_RO(
+ pim_ifp->sec_addr_list, sec_node,
+ sec_addr))
+ vty_out(vty, " %pFX\n",
+ &sec_addr->addr);
+ } else {
+ vty_out(vty, "Address : %pPAs\n", &ifaddr);
+ }
+ vty_out(vty, "\n");
+
+ /* PIM neighbors */
+ print_header = 1;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list,
+ neighnode, neigh)) {
+
+ if (print_header) {
+ vty_out(vty, "PIM Neighbors\n");
+ vty_out(vty, "-------------\n");
+ print_header = 0;
+ }
+
+ snprintfrr(neigh_src_str, sizeof(neigh_src_str),
+ "%pPAs", &neigh->source_addr);
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - neigh->creation);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire),
+ neigh->t_expire_timer);
+ vty_out(vty,
+ "%-15s : up for %s, holdtime expires in %s\n",
+ neigh_src_str, uptime, expire);
+ }
+
+ if (!print_header) {
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+ }
+
+ vty_out(vty, "Designated Router\n");
+ vty_out(vty, "-----------------\n");
+ vty_out(vty, "Address : %s\n", dr_str);
+ vty_out(vty, "Priority : %u(%d)\n",
+ pim_ifp->pim_dr_priority,
+ pim_ifp->pim_dr_num_nondrpri_neighbors);
+ vty_out(vty, "Uptime : %s\n", dr_uptime);
+ vty_out(vty, "Elections : %d\n",
+ pim_ifp->pim_dr_election_count);
+ vty_out(vty, "Changes : %d\n",
+ pim_ifp->pim_dr_election_changes);
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+
+ /* FHR */
+ print_header = 1;
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ if (!up->rpf.source_nexthop.interface)
+ continue;
+
+ if (strcmp(ifp->name,
+ up->rpf.source_nexthop
+ .interface->name) != 0)
+ continue;
+
+ if (!(up->flags & PIM_UPSTREAM_FLAG_MASK_FHR))
+ continue;
+
+ if (print_header) {
+ vty_out(vty,
+ "FHR - First Hop Router\n");
+ vty_out(vty,
+ "----------------------\n");
+ print_header = 0;
+ }
+
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - up->state_transition);
+ vty_out(vty,
+ "%pPAs : %pPAs is a source, uptime is %s\n",
+ &up->sg.grp, &up->sg.src, uptime);
+ }
+
+ if (!print_header) {
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+ }
+
+ vty_out(vty, "Hellos\n");
+ vty_out(vty, "------\n");
+ vty_out(vty, "Period : %d\n",
+ pim_ifp->pim_hello_period);
+ vty_out(vty, "HoldTime : %d\n",
+ PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
+ vty_out(vty, "Timer : %s\n", hello_timer);
+ vty_out(vty, "StatStart : %s\n", stat_uptime);
+ vty_out(vty, "Receive : %d\n",
+ pim_ifp->pim_ifstat_hello_recv);
+ vty_out(vty, "Receive Failed : %d\n",
+ pim_ifp->pim_ifstat_hello_recvfail);
+ vty_out(vty, "Send : %d\n",
+ pim_ifp->pim_ifstat_hello_sent);
+ vty_out(vty, "Send Failed : %d\n",
+ pim_ifp->pim_ifstat_hello_sendfail);
+ vty_out(vty, "Generation ID : %08x\n",
+ pim_ifp->pim_generation_id);
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+
+ pim_print_ifp_flags(vty, ifp);
+
+ vty_out(vty, "Join Prune Interval\n");
+ vty_out(vty, "-------------------\n");
+ vty_out(vty, "LAN Delay : %s\n",
+ pim_if_lan_delay_enabled(ifp) ? "yes" : "no");
+ vty_out(vty, "Effective Propagation Delay : %d msec\n",
+ pim_if_effective_propagation_delay_msec(ifp));
+ vty_out(vty, "Effective Override Interval : %d msec\n",
+ pim_if_effective_override_interval_msec(ifp));
+ vty_out(vty, "Join Prune Override Interval : %d msec\n",
+ pim_if_jp_override_interval_msec(ifp));
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+
+ vty_out(vty, "LAN Prune Delay\n");
+ vty_out(vty, "---------------\n");
+ vty_out(vty, "Propagation Delay : %d msec\n",
+ pim_ifp->pim_propagation_delay_msec);
+ vty_out(vty, "Propagation Delay (Highest) : %d msec\n",
+ pim_ifp->pim_neighbors_highest_propagation_delay_msec);
+ vty_out(vty, "Override Interval : %d msec\n",
+ pim_ifp->pim_override_interval_msec);
+ vty_out(vty, "Override Interval (Highest) : %d msec\n",
+ pim_ifp->pim_neighbors_highest_override_interval_msec);
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+
+ vty_out(vty, "BSM Status\n");
+ vty_out(vty, "----------\n");
+ vty_out(vty, "Bsm Enabled : %s\n",
+ pim_ifp->bsm_enable ? "yes" : "no");
+ vty_out(vty, "Unicast Bsm Enabled : %s\n",
+ pim_ifp->ucast_bsm_accept ? "yes" : "no");
+ vty_out(vty, "\n");
+ vty_out(vty, "\n");
+ }
+ }
+
+ if (!found_ifname)
+ vty_out(vty, "%% No such interface\n");
+}
+
+void ip_pim_ssm_show_group_range(struct pim_instance *pim, struct vty *vty,
+ bool uj)
+{
+ struct pim_ssm *ssm = pim->ssm_info;
+ const char *range_str =
+ ssm->plist_name ? ssm->plist_name : PIM_SSM_STANDARD_RANGE;
+
+ if (uj) {
+ json_object *json;
+
+ json = json_object_new_object();
+ json_object_string_add(json, "ssmGroups", range_str);
+ vty_json(vty, json);
+ } else
+ vty_out(vty, "SSM group range : %s\n", range_str);
+}
+
+struct pnc_cache_walk_data {
+ struct vty *vty;
+ struct pim_instance *pim;
+};
+
+static int pim_print_pnc_cache_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct pim_nexthop_cache *pnc = bucket->data;
+ struct pnc_cache_walk_data *cwd = arg;
+ struct vty *vty = cwd->vty;
+ struct pim_instance *pim = cwd->pim;
+ struct nexthop *nh_node = NULL;
+ ifindex_t first_ifindex;
+ struct interface *ifp = NULL;
+ char buf[PREFIX_STRLEN];
+
+ for (nh_node = pnc->nexthop; nh_node; nh_node = nh_node->next) {
+ first_ifindex = nh_node->ifindex;
+ ifp = if_lookup_by_index(first_ifindex, pim->vrf->vrf_id);
+
+ vty_out(vty, "%-15s ",
+ inet_ntop(AF_INET, &pnc->rpf.rpf_addr.u.prefix4, buf,
+ sizeof(buf)));
+ vty_out(vty, "%-16s ", ifp ? ifp->name : "NULL");
+ vty_out(vty, "%pI4 ", &nh_node->gate.ipv4);
+ vty_out(vty, "\n");
+ }
+ return CMD_SUCCESS;
+}
+
+void pim_show_nexthop(struct pim_instance *pim, struct vty *vty)
+{
+ struct pnc_cache_walk_data cwd;
+
+ cwd.vty = vty;
+ cwd.pim = pim;
+ vty_out(vty, "Number of registered addresses: %lu\n",
+ pim->rpf_hash->count);
+ vty_out(vty, "Address Interface Nexthop\n");
+ vty_out(vty, "---------------------------------------------\n");
+
+ hash_walk(pim->rpf_hash, pim_print_pnc_cache_walkcb, &cwd);
+}
+
+void pim_show_neighbors_single(struct pim_instance *pim, struct vty *vty,
+ const char *neighbor, json_object *json)
+{
+ struct listnode *neighnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+ time_t now;
+ int found_neighbor = 0;
+ int option_address_list;
+ int option_dr_priority;
+ int option_generation_id;
+ int option_holdtime;
+ int option_lan_prune_delay;
+ int option_t_bit;
+ char uptime[10];
+ char expire[10];
+ char neigh_src_str[PIM_ADDRSTRLEN];
+
+ json_object *json_ifp = NULL;
+ json_object *json_row = NULL;
+
+ now = pim_time_monotonic_sec();
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode,
+ neigh)) {
+ snprintfrr(neigh_src_str, sizeof(neigh_src_str),
+ "%pPAs", &neigh->source_addr);
+
+ /*
+ * The user can specify either the interface name or the
+ * PIM neighbor IP.
+ * If this pim_ifp matches neither then skip.
+ */
+ if (strcmp(neighbor, "detail") &&
+ strcmp(neighbor, ifp->name) &&
+ strcmp(neighbor, neigh_src_str))
+ continue;
+
+ found_neighbor = 1;
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - neigh->creation);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire),
+ neigh->t_expire_timer);
+
+ option_address_list = 0;
+ option_dr_priority = 0;
+ option_generation_id = 0;
+ option_holdtime = 0;
+ option_lan_prune_delay = 0;
+ option_t_bit = 0;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_ADDRESS_LIST))
+ option_address_list = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_DR_PRIORITY))
+ option_dr_priority = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_GENERATION_ID))
+ option_generation_id = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_HOLDTIME))
+ option_holdtime = 1;
+
+ if (PIM_OPTION_IS_SET(neigh->hello_options,
+ PIM_OPTION_MASK_LAN_PRUNE_DELAY))
+ option_lan_prune_delay = 1;
+
+ if (PIM_OPTION_IS_SET(
+ neigh->hello_options,
+ PIM_OPTION_MASK_CAN_DISABLE_JOIN_SUPPRESSION))
+ option_t_bit = 1;
+
+ if (json) {
+
+ /* Does this ifp live in json? If not create it
+ */
+ json_object_object_get_ex(json, ifp->name,
+ &json_ifp);
+
+ if (!json_ifp) {
+ json_ifp = json_object_new_object();
+ json_object_pim_ifp_add(json_ifp, ifp);
+ json_object_object_add(json, ifp->name,
+ json_ifp);
+ }
+
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "interface",
+ ifp->name);
+ json_object_string_add(json_row, "address",
+ neigh_src_str);
+ json_object_string_add(json_row, "upTime",
+ uptime);
+ json_object_string_add(json_row, "holdtime",
+ expire);
+ json_object_int_add(json_row, "drPriority",
+ neigh->dr_priority);
+ json_object_int_add(json_row, "generationId",
+ neigh->generation_id);
+
+ if (option_address_list)
+ json_object_boolean_true_add(
+ json_row,
+ "helloOptionAddressList");
+
+ if (option_dr_priority)
+ json_object_boolean_true_add(
+ json_row,
+ "helloOptionDrPriority");
+
+ if (option_generation_id)
+ json_object_boolean_true_add(
+ json_row,
+ "helloOptionGenerationId");
+
+ if (option_holdtime)
+ json_object_boolean_true_add(
+ json_row,
+ "helloOptionHoldtime");
+
+ if (option_lan_prune_delay)
+ json_object_boolean_true_add(
+ json_row,
+ "helloOptionLanPruneDelay");
+
+ if (option_t_bit)
+ json_object_boolean_true_add(
+ json_row, "helloOptionTBit");
+
+ json_object_object_add(json_ifp, neigh_src_str,
+ json_row);
+
+ } else {
+ vty_out(vty, "Interface : %s\n", ifp->name);
+ vty_out(vty, "Neighbor : %s\n", neigh_src_str);
+ vty_out(vty,
+ " Uptime : %s\n",
+ uptime);
+ vty_out(vty,
+ " Holdtime : %s\n",
+ expire);
+ vty_out(vty,
+ " DR Priority : %d\n",
+ neigh->dr_priority);
+ vty_out(vty,
+ " Generation ID : %08x\n",
+ neigh->generation_id);
+ vty_out(vty,
+ " Override Interval (msec) : %d\n",
+ neigh->override_interval_msec);
+ vty_out(vty,
+ " Propagation Delay (msec) : %d\n",
+ neigh->propagation_delay_msec);
+ vty_out(vty,
+ " Hello Option - Address List : %s\n",
+ option_address_list ? "yes" : "no");
+ vty_out(vty,
+ " Hello Option - DR Priority : %s\n",
+ option_dr_priority ? "yes" : "no");
+ vty_out(vty,
+ " Hello Option - Generation ID : %s\n",
+ option_generation_id ? "yes" : "no");
+ vty_out(vty,
+ " Hello Option - Holdtime : %s\n",
+ option_holdtime ? "yes" : "no");
+ vty_out(vty,
+ " Hello Option - LAN Prune Delay : %s\n",
+ option_lan_prune_delay ? "yes" : "no");
+ vty_out(vty,
+ " Hello Option - T-bit : %s\n",
+ option_t_bit ? "yes" : "no");
+ bfd_sess_show(vty, json_ifp,
+ neigh->bfd_session);
+ vty_out(vty, "\n");
+ }
+ }
+ }
+
+ if (!found_neighbor)
+ vty_out(vty, "%% No such interface or neighbor\n");
+}
+
+void pim_show_neighbors(struct pim_instance *pim, struct vty *vty,
+ json_object *json)
+{
+ struct listnode *neighnode;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_neighbor *neigh;
+ time_t now;
+ char uptime[10];
+ char expire[10];
+ char neigh_src_str[PIM_ADDRSTRLEN];
+ json_object *json_ifp_rows = NULL;
+ json_object *json_row = NULL;
+
+ now = pim_time_monotonic_sec();
+
+ if (!json) {
+ vty_out(vty,
+ "Interface Neighbor Uptime Holdtime DR Pri\n");
+ }
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ if (pim_ifp->pim_sock_fd < 0)
+ continue;
+
+ if (json)
+ json_ifp_rows = json_object_new_object();
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode,
+ neigh)) {
+ snprintfrr(neigh_src_str, sizeof(neigh_src_str),
+ "%pPAs", &neigh->source_addr);
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - neigh->creation);
+ pim_time_timer_to_hhmmss(expire, sizeof(expire),
+ neigh->t_expire_timer);
+
+ if (json) {
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "interface",
+ ifp->name);
+ json_object_string_add(json_row, "neighbor",
+ neigh_src_str);
+ json_object_string_add(json_row, "upTime",
+ uptime);
+ json_object_string_add(json_row, "holdTime",
+ expire);
+ json_object_int_add(json_row, "holdTimeMax",
+ neigh->holdtime);
+ json_object_int_add(json_row, "drPriority",
+ neigh->dr_priority);
+ json_object_object_add(json_ifp_rows,
+ neigh_src_str, json_row);
+
+ } else {
+ vty_out(vty, "%-16s %15s %8s %8s %6d\n",
+ ifp->name, neigh_src_str, uptime,
+ expire, neigh->dr_priority);
+ }
+ }
+
+ if (json) {
+ json_object_object_add(json, ifp->name, json_ifp_rows);
+ json_ifp_rows = NULL;
+ }
+ }
+}
+
+int gm_process_query_max_response_time_cmd(struct vty *vty,
+ const char *qmrt_str)
+{
+ const struct lyd_node *pim_enable_dnode;
+
+ pim_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
+ FRR_PIM_ENABLE_XPATH, VTY_CURR_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+
+ if (!pim_enable_dnode) {
+ nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY, "true");
+ } else {
+ if (!yang_dnode_get_bool(pim_enable_dnode, "."))
+ nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY,
+ "true");
+ }
+
+ nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_MODIFY,
+ qmrt_str);
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
+int gm_process_no_query_max_response_time_cmd(struct vty *vty)
+{
+ nb_cli_enqueue_change(vty, "./query-max-response-time", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
+int gm_process_last_member_query_count_cmd(struct vty *vty,
+ const char *lmqc_str)
+{
+ const struct lyd_node *pim_enable_dnode;
+
+ pim_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
+ FRR_PIM_ENABLE_XPATH, VTY_CURR_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+ if (!pim_enable_dnode) {
+ nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY, "true");
+ } else {
+ if (!yang_dnode_get_bool(pim_enable_dnode, "."))
+ nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY,
+ "true");
+ }
+
+ nb_cli_enqueue_change(vty, "./robustness-variable", NB_OP_MODIFY,
+ lmqc_str);
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
+int gm_process_no_last_member_query_count_cmd(struct vty *vty)
+{
+ nb_cli_enqueue_change(vty, "./robustness-variable", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
+int gm_process_last_member_query_interval_cmd(struct vty *vty,
+ const char *lmqi_str)
+{
+ const struct lyd_node *pim_enable_dnode;
+
+ pim_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
+ FRR_PIM_ENABLE_XPATH, VTY_CURR_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+ if (!pim_enable_dnode) {
+ nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY, "true");
+ } else {
+ if (!yang_dnode_get_bool(pim_enable_dnode, "."))
+ nb_cli_enqueue_change(vty, "./enable", NB_OP_MODIFY,
+ "true");
+ }
+
+ nb_cli_enqueue_change(vty, "./last-member-query-interval", NB_OP_MODIFY,
+ lmqi_str);
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
+int gm_process_no_last_member_query_interval_cmd(struct vty *vty)
+{
+ nb_cli_enqueue_change(vty, "./last-member-query-interval",
+ NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
+ FRR_PIM_AF_XPATH_VAL);
+}
+
+int pim_process_ssmpingd_cmd(struct vty *vty, enum nb_operation operation,
+ const char *src_str)
+{
+ const char *vrfname;
+ char ssmpingd_ip_xpath[XPATH_MAXLEN];
+
+ vrfname = pim_cli_get_vrf_name(vty);
+ if (vrfname == NULL)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ snprintf(ssmpingd_ip_xpath, sizeof(ssmpingd_ip_xpath),
+ FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
+ FRR_PIM_AF_XPATH_VAL);
+ strlcat(ssmpingd_ip_xpath, "/ssm-pingd-source-ip",
+ sizeof(ssmpingd_ip_xpath));
+
+ nb_cli_enqueue_change(vty, ssmpingd_ip_xpath, operation, src_str);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static void show_scan_oil_stats(struct pim_instance *pim, struct vty *vty,
+ time_t now)
+{
+ char uptime_scan_oil[10];
+ char uptime_mroute_add[10];
+ char uptime_mroute_del[10];
+
+ pim_time_uptime_begin(uptime_scan_oil, sizeof(uptime_scan_oil), now,
+ pim->scan_oil_last);
+ pim_time_uptime_begin(uptime_mroute_add, sizeof(uptime_mroute_add), now,
+ pim->mroute_add_last);
+ pim_time_uptime_begin(uptime_mroute_del, sizeof(uptime_mroute_del), now,
+ pim->mroute_del_last);
+
+ vty_out(vty,
+ "Scan OIL - Last: %s Events: %lld\n"
+ "MFC Add - Last: %s Events: %lld\n"
+ "MFC Del - Last: %s Events: %lld\n",
+ uptime_scan_oil, (long long)pim->scan_oil_events,
+ uptime_mroute_add, (long long)pim->mroute_add_events,
+ uptime_mroute_del, (long long)pim->mroute_del_events);
+}
+
+void show_multicast_interfaces(struct pim_instance *pim, struct vty *vty,
+ json_object *json)
+{
+ struct interface *ifp;
+ json_object *json_row = NULL;
+
+ vty_out(vty, "\n");
+
+ if (!json)
+ vty_out(vty,
+ "Interface Address ifi Vif PktsIn PktsOut BytesIn BytesOut\n");
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp;
+#if PIM_IPV == 4
+ struct sioc_vif_req vreq;
+#else
+ struct sioc_mif_req6 vreq;
+#endif
+
+ pim_ifp = ifp->info;
+
+ if (!pim_ifp)
+ continue;
+
+ memset(&vreq, 0, sizeof(vreq));
+#if PIM_IPV == 4
+ vreq.vifi = pim_ifp->mroute_vif_index;
+ if (ioctl(pim->mroute_socket, SIOCGETVIFCNT, &vreq)) {
+ zlog_warn(
+ "ioctl(SIOCGETVIFCNT=%lu) failure for interface %s vif_index=%d: errno=%d: %s",
+ (unsigned long)SIOCGETVIFCNT, ifp->name,
+ pim_ifp->mroute_vif_index, errno,
+ safe_strerror(errno));
+ }
+#else
+ vreq.mifi = pim_ifp->mroute_vif_index;
+ if (ioctl(pim->mroute_socket, SIOCGETMIFCNT_IN6, &vreq)) {
+ zlog_warn(
+ "ioctl(SIOCGETMIFCNT_IN6=%lu) failure for interface %s vif_index=%d: errno=%d: %s",
+ (unsigned long)SIOCGETMIFCNT_IN6, ifp->name,
+ pim_ifp->mroute_vif_index, errno,
+ safe_strerror(errno));
+ }
+#endif
+
+ if (json) {
+ json_row = json_object_new_object();
+ json_object_string_add(json_row, "name", ifp->name);
+ json_object_string_add(json_row, "state",
+ if_is_up(ifp) ? "up" : "down");
+ json_object_string_addf(json_row, "address", "%pPA",
+ &pim_ifp->primary_address);
+ json_object_int_add(json_row, "ifIndex", ifp->ifindex);
+ json_object_int_add(json_row, "vif",
+ pim_ifp->mroute_vif_index);
+ json_object_int_add(json_row, "pktsIn",
+ (unsigned long)vreq.icount);
+ json_object_int_add(json_row, "pktsOut",
+ (unsigned long)vreq.ocount);
+ json_object_int_add(json_row, "bytesIn",
+ (unsigned long)vreq.ibytes);
+ json_object_int_add(json_row, "bytesOut",
+ (unsigned long)vreq.obytes);
+ json_object_object_add(json, ifp->name, json_row);
+ } else {
+ vty_out(vty,
+ "%-16s %-15pPAs %3d %3d %7lu %7lu %10lu %10lu\n",
+ ifp->name, &pim_ifp->primary_address,
+ ifp->ifindex, pim_ifp->mroute_vif_index,
+ (unsigned long)vreq.icount,
+ (unsigned long)vreq.ocount,
+ (unsigned long)vreq.ibytes,
+ (unsigned long)vreq.obytes);
+ }
+ }
+}
+
+void pim_cmd_show_ip_multicast_helper(struct pim_instance *pim, struct vty *vty)
+{
+ struct vrf *vrf = pim->vrf;
+ time_t now = pim_time_monotonic_sec();
+ char uptime[10];
+ char mlag_role[80];
+
+ pim = vrf->info;
+
+ vty_out(vty, "Router MLAG Role: %s\n",
+ mlag_role2str(router->mlag_role, mlag_role, sizeof(mlag_role)));
+ vty_out(vty, "Mroute socket descriptor:");
+
+ vty_out(vty, " %d(%s)\n", pim->mroute_socket, vrf->name);
+
+ pim_time_uptime(uptime, sizeof(uptime),
+ now - pim->mroute_socket_creation);
+ vty_out(vty, "Mroute socket uptime: %s\n", uptime);
+
+ vty_out(vty, "\n");
+
+ pim_zebra_zclient_update(vty);
+#if PIM_IPV == 4
+ pim_zlookup_show_ip_multicast(vty);
+#else
+ /* TBD */
+#endif
+
+ vty_out(vty, "\n");
+ vty_out(vty, "Maximum highest VifIndex: %d\n", PIM_MAX_USABLE_VIFS);
+
+ vty_out(vty, "\n");
+ vty_out(vty, "Upstream Join Timer: %d secs\n", router->t_periodic);
+ vty_out(vty, "Join/Prune Holdtime: %d secs\n", PIM_JP_HOLDTIME);
+ vty_out(vty, "PIM ECMP: %s\n", pim->ecmp_enable ? "Enable" : "Disable");
+ vty_out(vty, "PIM ECMP Rebalance: %s\n",
+ pim->ecmp_rebalance_enable ? "Enable" : "Disable");
+
+ vty_out(vty, "\n");
+
+ pim_show_rpf_refresh_stats(vty, pim, now, NULL);
+
+ vty_out(vty, "\n");
+
+ show_scan_oil_stats(pim, vty, now);
+
+ show_multicast_interfaces(pim, vty, NULL);
+}
+
+void show_mroute(struct pim_instance *pim, struct vty *vty, pim_sgaddr *sg,
+ bool fill, json_object *json)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ struct static_route *s_route;
+ time_t now;
+ json_object *json_group = NULL;
+ json_object *json_source = NULL;
+ json_object *json_oil = NULL;
+ json_object *json_ifp_out = NULL;
+ int found_oif;
+ int first;
+ char grp_str[PIM_ADDRSTRLEN];
+ char src_str[PIM_ADDRSTRLEN];
+ char in_ifname[INTERFACE_NAMSIZ + 1];
+ char out_ifname[INTERFACE_NAMSIZ + 1];
+ int oif_vif_index;
+ struct interface *ifp_in;
+ char proto[100];
+ char state_str[PIM_REG_STATE_STR_LEN];
+ char mroute_uptime[10];
+
+ if (!json) {
+ vty_out(vty, "IP Multicast Routing Table\n");
+ vty_out(vty, "Flags: S - Sparse, C - Connected, P - Pruned\n");
+ vty_out(vty,
+ " R - SGRpt Pruned, F - Register flag, T - SPT-bit set\n");
+ vty_out(vty,
+ "\nSource Group Flags Proto Input Output TTL Uptime\n");
+ }
+
+ now = pim_time_monotonic_sec();
+
+ /* print list of PIM and IGMP routes */
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
+ found_oif = 0;
+ first = 1;
+ if (!c_oil->installed)
+ continue;
+
+ if (!pim_addr_is_any(sg->grp) &&
+ pim_addr_cmp(sg->grp, *oil_mcastgrp(c_oil)))
+ continue;
+ if (!pim_addr_is_any(sg->src) &&
+ pim_addr_cmp(sg->src, *oil_origin(c_oil)))
+ continue;
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
+ oil_mcastgrp(c_oil));
+ snprintfrr(src_str, sizeof(src_str), "%pPAs",
+ oil_origin(c_oil));
+
+ strlcpy(state_str, "S", sizeof(state_str));
+ /* When a non DR receives a igmp join, it creates a (*,G)
+ * channel_oil without any upstream creation
+ */
+ if (c_oil->up) {
+ if (PIM_UPSTREAM_FLAG_TEST_SRC_IGMP(c_oil->up->flags))
+ strlcat(state_str, "C", sizeof(state_str));
+ if (pim_upstream_is_sg_rpt(c_oil->up))
+ strlcat(state_str, "R", sizeof(state_str));
+ if (PIM_UPSTREAM_FLAG_TEST_FHR(c_oil->up->flags))
+ strlcat(state_str, "F", sizeof(state_str));
+ if (c_oil->up->sptbit == PIM_UPSTREAM_SPTBIT_TRUE)
+ strlcat(state_str, "T", sizeof(state_str));
+ }
+ if (pim_channel_oil_empty(c_oil))
+ strlcat(state_str, "P", sizeof(state_str));
+
+ ifp_in = pim_if_find_by_vif_index(pim, *oil_parent(c_oil));
+
+ if (ifp_in)
+ strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname));
+ else
+ strlcpy(in_ifname, "<iif?>", sizeof(in_ifname));
+
+
+ pim_time_uptime(mroute_uptime, sizeof(mroute_uptime),
+ now - c_oil->mroute_creation);
+
+ if (json) {
+
+ /* Find the group, create it if it doesn't exist */
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str,
+ json_group);
+ }
+
+ /* Find the source nested under the group, create it if
+ * it doesn't exist
+ */
+ json_object_object_get_ex(json_group, src_str,
+ &json_source);
+
+ if (!json_source) {
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, src_str,
+ json_source);
+ }
+
+ /* Find the inbound interface nested under the source,
+ * create it if it doesn't exist
+ */
+ json_object_string_add(json_source, "source", src_str);
+ json_object_string_add(json_source, "group", grp_str);
+ json_object_int_add(json_source, "installed",
+ c_oil->installed);
+ json_object_int_add(json_source, "refCount",
+ c_oil->oil_ref_count);
+ json_object_int_add(json_source, "oilSize",
+ 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);
+ json_oil = NULL;
+ }
+
+ for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
+ ++oif_vif_index) {
+ struct interface *ifp_out;
+ int ttl;
+
+ ttl = oil_if_has(c_oil, oif_vif_index);
+ if (ttl < 1)
+ continue;
+
+ /* do not display muted OIFs */
+ if (c_oil->oif_flags[oif_vif_index] & PIM_OIF_FLAG_MUTE)
+ continue;
+
+ if (*oil_parent(c_oil) == oif_vif_index &&
+ !pim_mroute_allow_iif_in_oil(c_oil, oif_vif_index))
+ continue;
+
+ ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index);
+ found_oif = 1;
+
+ if (ifp_out)
+ strlcpy(out_ifname, ifp_out->name,
+ sizeof(out_ifname));
+ else
+ strlcpy(out_ifname, "<oif?>",
+ sizeof(out_ifname));
+
+ if (json) {
+ json_ifp_out = json_object_new_object();
+ json_object_string_add(json_ifp_out, "source",
+ src_str);
+ json_object_string_add(json_ifp_out, "group",
+ grp_str);
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_PIM)
+ json_object_boolean_true_add(
+ json_ifp_out, "protocolPim");
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_GM)
+#if PIM_IPV == 4
+ json_object_boolean_true_add(
+ json_ifp_out, "protocolIgmp");
+#else
+ json_object_boolean_true_add(
+ json_ifp_out, "protocolMld");
+#endif
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_VXLAN)
+ json_object_boolean_true_add(
+ json_ifp_out, "protocolVxlan");
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_STAR)
+ json_object_boolean_true_add(
+ json_ifp_out,
+ "protocolInherited");
+
+ json_object_string_add(json_ifp_out,
+ "inboundInterface",
+ in_ifname);
+ json_object_int_add(json_ifp_out, "iVifI",
+ *oil_parent(c_oil));
+ json_object_string_add(json_ifp_out,
+ "outboundInterface",
+ out_ifname);
+ json_object_int_add(json_ifp_out, "oVifI",
+ oif_vif_index);
+ json_object_int_add(json_ifp_out, "ttl", ttl);
+ json_object_string_add(json_ifp_out, "upTime",
+ mroute_uptime);
+ json_object_string_add(json_source, "flags",
+ state_str);
+ if (!json_oil) {
+ json_oil = json_object_new_object();
+ json_object_object_add(json_source,
+ "oil", json_oil);
+ }
+ json_object_object_add(json_oil, out_ifname,
+ json_ifp_out);
+ } else {
+ proto[0] = '\0';
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_PIM) {
+ strlcpy(proto, "PIM", sizeof(proto));
+ }
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_GM) {
+#if PIM_IPV == 4
+ strlcpy(proto, "IGMP", sizeof(proto));
+#else
+ strlcpy(proto, "MLD", sizeof(proto));
+#endif
+ }
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_VXLAN) {
+ strlcpy(proto, "VxLAN", sizeof(proto));
+ }
+
+ if (c_oil->oif_flags[oif_vif_index] &
+ PIM_OIF_FLAG_PROTO_STAR) {
+ strlcpy(proto, "STAR", sizeof(proto));
+ }
+
+ vty_out(vty,
+ "%-15pPAs %-15pPAs %-8s %-6s %-16s %-16s %-3d %8s\n",
+ oil_origin(c_oil), oil_mcastgrp(c_oil),
+ state_str, proto, in_ifname, out_ifname,
+ ttl, mroute_uptime);
+
+ if (first) {
+ src_str[0] = '\0';
+ grp_str[0] = '\0';
+ in_ifname[0] = '\0';
+ state_str[0] = '\0';
+ mroute_uptime[0] = '\0';
+ first = 0;
+ }
+ }
+ }
+
+ if (!json && !found_oif) {
+ vty_out(vty,
+ "%-15pPAs %-15pPAs %-8s %-6s %-16s %-16s %-3d %8s\n",
+ oil_origin(c_oil), oil_mcastgrp(c_oil),
+ state_str, "none", in_ifname, "none", 0,
+ "--:--:--");
+ }
+ }
+
+ /* Print list of static routes */
+ for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
+ first = 1;
+
+ if (!s_route->c_oil.installed)
+ continue;
+
+ snprintfrr(grp_str, sizeof(grp_str), "%pPAs", &s_route->group);
+ snprintfrr(src_str, sizeof(src_str), "%pPAs", &s_route->source);
+ ifp_in = pim_if_find_by_vif_index(pim, s_route->iif);
+ found_oif = 0;
+
+ if (ifp_in)
+ strlcpy(in_ifname, ifp_in->name, sizeof(in_ifname));
+ else
+ strlcpy(in_ifname, "<iif?>", sizeof(in_ifname));
+
+ if (json) {
+
+ /* Find the group, create it if it doesn't exist */
+ json_object_object_get_ex(json, grp_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, grp_str,
+ json_group);
+ }
+
+ /* Find the source nested under the group, create it if
+ * it doesn't exist
+ */
+ json_object_object_get_ex(json_group, src_str,
+ &json_source);
+
+ if (!json_source) {
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, src_str,
+ json_source);
+ }
+
+ json_object_string_add(json_source, "iif", in_ifname);
+ json_oil = NULL;
+ } else {
+ strlcpy(proto, "STATIC", sizeof(proto));
+ }
+
+ for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
+ ++oif_vif_index) {
+ struct interface *ifp_out;
+ char oif_uptime[10];
+ int ttl;
+
+ ttl = s_route->oif_ttls[oif_vif_index];
+ if (ttl < 1)
+ continue;
+
+ ifp_out = pim_if_find_by_vif_index(pim, oif_vif_index);
+ pim_time_uptime(
+ oif_uptime, sizeof(oif_uptime),
+ now - s_route->c_oil
+ .oif_creation[oif_vif_index]);
+ found_oif = 1;
+
+ if (ifp_out)
+ strlcpy(out_ifname, ifp_out->name,
+ sizeof(out_ifname));
+ else
+ strlcpy(out_ifname, "<oif?>",
+ sizeof(out_ifname));
+
+ if (json) {
+ json_ifp_out = json_object_new_object();
+ json_object_string_add(json_ifp_out, "source",
+ src_str);
+ json_object_string_add(json_ifp_out, "group",
+ grp_str);
+ json_object_boolean_true_add(json_ifp_out,
+ "protocolStatic");
+ json_object_string_add(json_ifp_out,
+ "inboundInterface",
+ in_ifname);
+ json_object_int_add(
+ json_ifp_out, "iVifI",
+ *oil_parent(&s_route->c_oil));
+ json_object_string_add(json_ifp_out,
+ "outboundInterface",
+ out_ifname);
+ json_object_int_add(json_ifp_out, "oVifI",
+ oif_vif_index);
+ json_object_int_add(json_ifp_out, "ttl", ttl);
+ json_object_string_add(json_ifp_out, "upTime",
+ oif_uptime);
+ if (!json_oil) {
+ json_oil = json_object_new_object();
+ json_object_object_add(json_source,
+ "oil", json_oil);
+ }
+ json_object_object_add(json_oil, out_ifname,
+ json_ifp_out);
+ } else {
+ vty_out(vty,
+ "%-15pPAs %-15pPAs %-8s %-6s %-16s %-16s %-3d %8s\n",
+ &s_route->source, &s_route->group, "-",
+ proto, in_ifname, out_ifname, ttl,
+ oif_uptime);
+ if (first && !fill) {
+ src_str[0] = '\0';
+ grp_str[0] = '\0';
+ in_ifname[0] = '\0';
+ first = 0;
+ }
+ }
+ }
+
+ if (!json && !found_oif) {
+ vty_out(vty,
+ "%-15pPAs %-15pPAs %-8s %-6s %-16s %-16s %-3d %8s\n",
+ &s_route->source, &s_route->group, "-", proto,
+ in_ifname, "none", 0, "--:--:--");
+ }
+ }
+}
+
+static void show_mroute_count_per_channel_oil(struct channel_oil *c_oil,
+ json_object *json,
+ struct vty *vty)
+{
+ json_object *json_group = NULL;
+ json_object *json_source = NULL;
+
+ if (!c_oil->installed)
+ return;
+
+ pim_mroute_update_counters(c_oil);
+
+ if (json) {
+ char group_str[PIM_ADDRSTRLEN];
+ char source_str[PIM_ADDRSTRLEN];
+
+ snprintfrr(group_str, sizeof(group_str), "%pPAs",
+ oil_mcastgrp(c_oil));
+ snprintfrr(source_str, sizeof(source_str), "%pPAs",
+ oil_origin(c_oil));
+
+ json_object_object_get_ex(json, group_str, &json_group);
+
+ if (!json_group) {
+ json_group = json_object_new_object();
+ json_object_object_add(json, group_str, json_group);
+ }
+
+ json_source = json_object_new_object();
+ json_object_object_add(json_group, source_str, json_source);
+ json_object_int_add(json_source, "lastUsed",
+ c_oil->cc.lastused / 100);
+ json_object_int_add(json_source, "packets", c_oil->cc.pktcnt);
+ json_object_int_add(json_source, "bytes", c_oil->cc.bytecnt);
+ json_object_int_add(json_source, "wrongIf", c_oil->cc.wrong_if);
+
+ } else {
+ vty_out(vty, "%-15pPAs %-15pPAs %-8llu %-7ld %-10ld %-7ld\n",
+ oil_origin(c_oil), oil_mcastgrp(c_oil),
+ c_oil->cc.lastused / 100,
+ c_oil->cc.pktcnt - c_oil->cc.origpktcnt,
+ c_oil->cc.bytecnt - c_oil->cc.origbytecnt,
+ c_oil->cc.wrong_if - c_oil->cc.origwrong_if);
+ }
+}
+
+void show_mroute_count(struct pim_instance *pim, struct vty *vty,
+ json_object *json)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ struct static_route *sr;
+
+ if (!json) {
+ vty_out(vty, "\n");
+
+ vty_out(vty,
+ "Source Group LastUsed Packets Bytes WrongIf \n");
+ }
+
+ /* Print PIM and IGMP route counts */
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil)
+ show_mroute_count_per_channel_oil(c_oil, json, vty);
+
+ for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr))
+ show_mroute_count_per_channel_oil(&sr->c_oil, json, vty);
+}
+
+void show_mroute_summary(struct pim_instance *pim, struct vty *vty,
+ json_object *json)
+{
+ struct listnode *node;
+ struct channel_oil *c_oil;
+ struct static_route *s_route;
+ uint32_t starg_sw_mroute_cnt = 0;
+ uint32_t sg_sw_mroute_cnt = 0;
+ uint32_t starg_hw_mroute_cnt = 0;
+ uint32_t sg_hw_mroute_cnt = 0;
+ json_object *json_starg = NULL;
+ json_object *json_sg = NULL;
+
+ if (!json)
+ vty_out(vty, "Mroute Type Installed/Total\n");
+
+ frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
+ if (!c_oil->installed) {
+ if (pim_addr_is_any(*oil_origin(c_oil)))
+ starg_sw_mroute_cnt++;
+ else
+ sg_sw_mroute_cnt++;
+ } else {
+ if (pim_addr_is_any(*oil_origin(c_oil)))
+ starg_hw_mroute_cnt++;
+ else
+ sg_hw_mroute_cnt++;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, s_route)) {
+ if (!s_route->c_oil.installed) {
+ if (pim_addr_is_any(*oil_origin(&s_route->c_oil)))
+ starg_sw_mroute_cnt++;
+ else
+ sg_sw_mroute_cnt++;
+ } else {
+ if (pim_addr_is_any(*oil_origin(&s_route->c_oil)))
+ starg_hw_mroute_cnt++;
+ else
+ sg_hw_mroute_cnt++;
+ }
+ }
+
+ if (!json) {
+ vty_out(vty, "%-20s %u/%u\n", "(*, G)", starg_hw_mroute_cnt,
+ starg_sw_mroute_cnt + starg_hw_mroute_cnt);
+ vty_out(vty, "%-20s %u/%u\n", "(S, G)", sg_hw_mroute_cnt,
+ sg_sw_mroute_cnt + sg_hw_mroute_cnt);
+ vty_out(vty, "------\n");
+ vty_out(vty, "%-20s %u/%u\n", "Total",
+ (starg_hw_mroute_cnt + sg_hw_mroute_cnt),
+ (starg_sw_mroute_cnt + starg_hw_mroute_cnt +
+ sg_sw_mroute_cnt + sg_hw_mroute_cnt));
+ } else {
+ /* (*,G) route details */
+ json_starg = json_object_new_object();
+ json_object_object_add(json, "wildcardGroup", json_starg);
+
+ json_object_int_add(json_starg, "installed",
+ starg_hw_mroute_cnt);
+ json_object_int_add(json_starg, "total",
+ starg_sw_mroute_cnt + starg_hw_mroute_cnt);
+
+ /* (S, G) route details */
+ json_sg = json_object_new_object();
+ json_object_object_add(json, "sourceGroup", json_sg);
+
+ json_object_int_add(json_sg, "installed", sg_hw_mroute_cnt);
+ json_object_int_add(json_sg, "total",
+ sg_sw_mroute_cnt + sg_hw_mroute_cnt);
+
+ json_object_int_add(json, "totalNumOfInstalledMroutes",
+ starg_hw_mroute_cnt + sg_hw_mroute_cnt);
+ json_object_int_add(json, "totalNumOfMroutes",
+ starg_sw_mroute_cnt + starg_hw_mroute_cnt +
+ sg_sw_mroute_cnt +
+ sg_hw_mroute_cnt);
+ }
+}