diff options
Diffstat (limited to 'pimd/pim_cmd_common.c')
| -rw-r--r-- | pimd/pim_cmd_common.c | 2886 |
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); + } +} |
