frr/pimd/pim_cmd_common.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

5972 lines
154 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PIM for IPv6 FRR
* Copyright (C) 2022 Vmware, Inc.
* Mobashshera Rasool <mrasool@vmware.com>
*/
#include <zebra.h>
#include <sys/ioctl.h>
#include "lib/json.h"
#include "command.h"
#include "if.h"
#include "prefix.h"
#include "zclient.h"
#include "plist.h"
#include "hash.h"
#include "nexthop.h"
#include "vrf.h"
#include "ferr.h"
#include "lib/srcdest_table.h"
#include "lib/linklist.h"
#include "termtable.h"
#include "pimd.h"
#include "pim_instance.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_static.h"
#include "pim_addr.h"
#include "pim_static.h"
#include "pim_util.h"
#include "pim6_mld.h"
/**
* Get current node VRF name.
*
* NOTE:
* In case of failure it will print error message to user.
*
* \returns name or NULL if failed to get VRF.
*/
const char *pim_cli_get_vrf_name(struct vty *vty)
{
const struct lyd_node *vrf_node;
/* Not inside any VRF context. */
if (vty->xpath_index == 0)
return VRF_DEFAULT_NAME;
vrf_node = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH);
if (vrf_node == NULL) {
vty_out(vty, "%% Failed to get vrf dnode in configuration\n");
return NULL;
}
return yang_dnode_get_string(vrf_node, "name");
}
int pim_process_join_prune_cmd(struct vty *vty, const char *jpi_str)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(xpath, "/join-prune-interval", sizeof(xpath));
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, jpi_str);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_join_prune_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(xpath, "/join-prune-interval", sizeof(xpath));
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_spt_switchover_infinity_cmd(struct vty *vty)
{
char spt_plist_xpath[XPATH_MAXLEN + 40];
char spt_action_xpath[XPATH_MAXLEN + 26];
snprintf(spt_plist_xpath, sizeof(spt_plist_xpath),
"%s/spt-switchover/spt-infinity-prefix-list", VTY_CURR_XPATH);
snprintf(spt_action_xpath, sizeof(spt_action_xpath),
"%s/spt-switchover/spt-action", VTY_CURR_XPATH);
if (yang_dnode_exists(vty->candidate_config->dnode, spt_plist_xpath))
nb_cli_enqueue_change(vty, spt_plist_xpath, NB_OP_DESTROY,
NULL);
nb_cli_enqueue_change(vty, spt_action_xpath, NB_OP_MODIFY,
"PIM_SPT_INFINITY");
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_spt_switchover_prefixlist_cmd(struct vty *vty,
const char *plist)
{
char spt_plist_xpath[XPATH_MAXLEN];
char spt_action_xpath[XPATH_MAXLEN];
snprintf(spt_plist_xpath, sizeof(spt_plist_xpath),
"./spt-switchover/spt-infinity-prefix-list");
snprintf(spt_action_xpath, sizeof(spt_action_xpath),
"./spt-switchover/spt-action");
nb_cli_enqueue_change(vty, spt_action_xpath, NB_OP_MODIFY,
"PIM_SPT_INFINITY");
nb_cli_enqueue_change(vty, spt_plist_xpath, NB_OP_MODIFY, plist);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_spt_switchover_cmd(struct vty *vty)
{
char spt_plist_xpath[XPATH_MAXLEN];
char spt_action_xpath[XPATH_MAXLEN];
snprintf(spt_plist_xpath, sizeof(spt_plist_xpath),
"./spt-switchover/spt-infinity-prefix-list");
snprintf(spt_action_xpath, sizeof(spt_action_xpath),
"./spt-switchover/spt-action");
nb_cli_enqueue_change(vty, spt_plist_xpath, NB_OP_DESTROY, NULL);
nb_cli_enqueue_change(vty, spt_action_xpath, NB_OP_MODIFY,
"PIM_SPT_IMMEDIATE");
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_pim_packet_cmd(struct vty *vty, const char *packet)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(xpath, "/packets", sizeof(xpath));
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, packet);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_pim_packet_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(xpath, "/packets", sizeof(xpath));
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_keepalivetimer_cmd(struct vty *vty, const char *kat)
{
char ka_timer_xpath[XPATH_MAXLEN];
snprintf(ka_timer_xpath, sizeof(ka_timer_xpath), "./keep-alive-timer");
nb_cli_enqueue_change(vty, ka_timer_xpath, NB_OP_MODIFY, kat);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_keepalivetimer_cmd(struct vty *vty)
{
char ka_timer_xpath[XPATH_MAXLEN];
snprintf(ka_timer_xpath, sizeof(ka_timer_xpath), "./keep-alive-timer");
nb_cli_enqueue_change(vty, ka_timer_xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_rp_kat_cmd(struct vty *vty, const char *rpkat)
{
char rp_ka_timer_xpath[XPATH_MAXLEN];
snprintf(rp_ka_timer_xpath, sizeof(rp_ka_timer_xpath),
"./rp-keep-alive-timer");
nb_cli_enqueue_change(vty, rp_ka_timer_xpath, NB_OP_MODIFY, rpkat);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_rp_kat_cmd(struct vty *vty)
{
char rp_ka_timer[6];
char rp_ka_timer_xpath[XPATH_MAXLEN];
uint v;
char rs_timer_xpath[XPATH_MAXLEN];
snprintf(rs_timer_xpath, sizeof(rs_timer_xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(rs_timer_xpath, "/register-suppress-time",
sizeof(rs_timer_xpath));
/* RFC4601 */
/* Check if register suppress time is configured or assigned
* the default register suppress time.
*/
if (yang_dnode_exists(vty->candidate_config->dnode, rs_timer_xpath))
v = yang_dnode_get_uint16(vty->candidate_config->dnode, "%s",
rs_timer_xpath);
else
v = PIM_REGISTER_SUPPRESSION_TIME_DEFAULT;
v = 3 * v + PIM_REGISTER_PROBE_TIME_DEFAULT;
if (v > UINT16_MAX)
v = UINT16_MAX;
snprintf(rp_ka_timer, sizeof(rp_ka_timer), "%u", v);
snprintf(rp_ka_timer_xpath, sizeof(rp_ka_timer_xpath),
"./rp-keep-alive-timer");
nb_cli_enqueue_change(vty, rp_ka_timer_xpath, NB_OP_MODIFY, rp_ka_timer);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_register_suppress_cmd(struct vty *vty, const char *rst)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(xpath, "/register-suppress-time", sizeof(xpath));
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, rst);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_register_suppress_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), FRR_PIM_ROUTER_XPATH,
FRR_PIM_AF_XPATH_VAL);
strlcat(xpath, "/register-suppress-time", sizeof(xpath));
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_ip_pim_cmd(struct vty *vty)
{
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY, "true");
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_pim_passive_cmd(struct vty *vty, bool enable)
{
if (enable)
nb_cli_enqueue_change(vty, "./pim-passive-enable", NB_OP_MODIFY,
"true");
else
nb_cli_enqueue_change(vty, "./pim-passive-enable", NB_OP_MODIFY,
"false");
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_no_ip_pim_cmd(struct vty *vty)
{
const struct lyd_node *mld_enable_dnode;
char mld_if_xpath[XPATH_MAXLEN];
int printed =
snprintf(mld_if_xpath, sizeof(mld_if_xpath),
"%s/frr-gmp:gmp/address-family[address-family='%s']",
VTY_CURR_XPATH, FRR_PIM_AF_XPATH_VAL);
if (printed >= (int)(sizeof(mld_if_xpath))) {
vty_out(vty, "Xpath too long (%d > %u)", printed + 1,
XPATH_MAXLEN);
return CMD_WARNING_CONFIG_FAILED;
}
mld_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH,
FRR_PIM_AF_XPATH_VAL);
if (!mld_enable_dnode) {
nb_cli_enqueue_change(vty, mld_if_xpath, NB_OP_DESTROY, NULL);
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
} else {
if (!yang_dnode_get_bool(mld_enable_dnode, ".")) {
nb_cli_enqueue_change(vty, mld_if_xpath, NB_OP_DESTROY,
NULL);
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
} else
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"false");
}
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_pim_drprio_cmd(struct vty *vty, const char *drpriority_str)
{
nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_MODIFY,
drpriority_str);
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_no_ip_pim_drprio_cmd(struct vty *vty)
{
nb_cli_enqueue_change(vty, "./dr-priority", NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_pim_hello_cmd(struct vty *vty, const char *hello_str,
const char *hold_str)
{
const struct lyd_node *mld_enable_dnode;
mld_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH,
FRR_PIM_AF_XPATH_VAL);
if (!mld_enable_dnode) {
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
} else {
if (!yang_dnode_get_bool(mld_enable_dnode, "."))
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
}
nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_MODIFY, hello_str);
if (hold_str)
nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_MODIFY,
hold_str);
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_no_ip_pim_hello_cmd(struct vty *vty)
{
nb_cli_enqueue_change(vty, "./hello-interval", NB_OP_DESTROY, NULL);
nb_cli_enqueue_change(vty, "./hello-holdtime", NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_pim_activeactive_cmd(struct vty *vty, const char *no)
{
if (no)
nb_cli_enqueue_change(vty, "./active-active", NB_OP_MODIFY,
"false");
else {
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
nb_cli_enqueue_change(vty, "./active-active", NB_OP_MODIFY,
"true");
}
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_pim_boundary_oil_cmd(struct vty *vty, const char *oil)
{
nb_cli_enqueue_change(vty, "./multicast-boundary-oil", NB_OP_MODIFY,
oil);
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_no_ip_pim_boundary_oil_cmd(struct vty *vty)
{
nb_cli_enqueue_change(vty, "./multicast-boundary-oil", NB_OP_DESTROY,
NULL);
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_gmp_proxy_cmd(struct vty *vty, bool enable)
{
if (enable)
nb_cli_enqueue_change(vty, "./proxy", NB_OP_MODIFY, "true");
else
nb_cli_enqueue_change(vty, "./proxy", NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, FRR_GMP_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_ip_mroute_cmd(struct vty *vty, const char *interface,
const char *group_str, const char *source_str)
{
nb_cli_enqueue_change(vty, "./oif", NB_OP_MODIFY, interface);
if (!source_str) {
char buf[SRCDEST2STR_BUFFER];
inet_ntop(AF_INET6, &in6addr_any, buf, sizeof(buf));
return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH,
FRR_PIM_AF_XPATH_VAL, buf,
group_str);
}
return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH,
FRR_PIM_AF_XPATH_VAL, source_str,
group_str);
}
int pim_process_no_ip_mroute_cmd(struct vty *vty, const char *interface,
const char *group_str, const char *source_str)
{
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
if (!source_str) {
char buf[SRCDEST2STR_BUFFER];
inet_ntop(AF_INET6, &in6addr_any, buf, sizeof(buf));
return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH,
FRR_PIM_AF_XPATH_VAL, buf,
group_str);
}
return nb_cli_apply_changes(vty, FRR_PIM_MROUTE_XPATH,
FRR_PIM_AF_XPATH_VAL, source_str,
group_str);
}
int pim_process_rp_cmd(struct vty *vty, const char *rp_str,
const char *group_str)
{
char group_xpath[XPATH_MAXLEN];
int printed;
int result = 0;
struct prefix group;
pim_addr rp_addr;
result = str2prefix(group_str, &group);
if (result) {
struct prefix temp;
prefix_copy(&temp, &group);
apply_mask(&temp);
if (!prefix_same(&group, &temp)) {
vty_out(vty, "%% Inconsistent address and mask: %s\n",
group_str);
return CMD_WARNING_CONFIG_FAILED;
}
}
if (!result) {
vty_out(vty, "%% Bad group address specified: %s\n", group_str);
return CMD_WARNING_CONFIG_FAILED;
}
result = inet_pton(PIM_AF, rp_str, &rp_addr);
if (result <= 0) {
vty_out(vty, "%% Bad RP address specified: %s\n", rp_str);
return CMD_WARNING_CONFIG_FAILED;
}
if (pim_addr_is_any(rp_addr) || pim_addr_is_multicast(rp_addr)) {
vty_out(vty, "%% Bad RP address specified: %s\n", rp_str);
return CMD_WARNING_CONFIG_FAILED;
}
#if PIM_IPV == 6
if (IN6_IS_ADDR_LINKLOCAL(&rp_addr)) {
vty_out(vty, "%% Bad RP address specified: %s\n", rp_str);
return CMD_WARNING_CONFIG_FAILED;
}
#endif
printed = snprintf(group_xpath, sizeof(group_xpath),
"./" FRR_PIM_STATIC_RP_XPATH "/group-list[.='%s']",
rp_str, group_str);
if (printed >= (int)(sizeof(group_xpath))) {
vty_out(vty, "Xpath too long (%d > %u)", printed + 1,
XPATH_MAXLEN);
return CMD_WARNING_CONFIG_FAILED;
}
nb_cli_enqueue_change(vty, group_xpath, NB_OP_CREATE, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_rp_cmd(struct vty *vty, const char *rp_str,
const char *group_str)
{
char group_xpath[XPATH_MAXLEN];
char rp_xpath[XPATH_MAXLEN + 47];
int printed;
const struct lyd_node *group_dnode;
snprintf(rp_xpath, sizeof(rp_xpath), "%s/" FRR_PIM_STATIC_RP_XPATH,
VTY_CURR_XPATH, rp_str);
printed = snprintf(group_xpath, sizeof(group_xpath),
"%s/group-list[.='%s']", rp_xpath, group_str);
if (printed >= (int)(sizeof(group_xpath))) {
vty_out(vty, "Xpath too long (%d > %u)", printed + 1,
XPATH_MAXLEN);
return CMD_WARNING_CONFIG_FAILED;
}
group_dnode = yang_dnode_get(vty->candidate_config->dnode, group_xpath);
if (!group_dnode) {
vty_out(vty, "%% Unable to find specified RP\n");
return NB_OK;
}
if (yang_is_last_list_dnode(group_dnode))
nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL);
else
nb_cli_enqueue_change(vty, group_xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_rp_plist_cmd(struct vty *vty, const char *rp_str,
const char *prefix_list)
{
char rp_plist_xpath[XPATH_MAXLEN];
snprintf(rp_plist_xpath, sizeof(rp_plist_xpath),
"./" FRR_PIM_STATIC_RP_XPATH, rp_str);
strlcat(rp_plist_xpath, "/prefix-list", sizeof(rp_plist_xpath));
nb_cli_enqueue_change(vty, rp_plist_xpath, NB_OP_MODIFY, prefix_list);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_rp_plist_cmd(struct vty *vty, const char *rp_str,
const char *prefix_list)
{
char rp_xpath[XPATH_MAXLEN + 47];
char plist_xpath[XPATH_MAXLEN + 1070];
const struct lyd_node *plist_dnode;
const char *plist;
snprintf(rp_xpath, sizeof(rp_xpath), "%s/" FRR_PIM_STATIC_RP_XPATH,
VTY_CURR_XPATH, rp_str);
snprintf(plist_xpath, sizeof(plist_xpath), "%s", rp_xpath);
strlcat(plist_xpath, "/prefix-list", sizeof(plist_xpath));
plist_dnode = yang_dnode_get(vty->candidate_config->dnode, plist_xpath);
if (!plist_dnode) {
vty_out(vty, "%% Unable to find specified RP\n");
return NB_OK;
}
plist = yang_dnode_get_string(plist_dnode, NULL);
if (strcmp(prefix_list, plist)) {
vty_out(vty, "%% Unable to find specified RP\n");
return NB_OK;
}
nb_cli_enqueue_change(vty, rp_xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
pimd,yang: Implement AutoRP CLI and NB config path New CLI commands added: router pim [vrf NAME] autorp discovery autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} autorp discovery Enables Auto RP discovery for learning dynamic RP information using the AutoRP protocol. autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] Enable announcements of a candidate RP with the given group range, or prefix list of group ranges, to an AutoRP mapping agent. autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} Configure the parameters of the AutoRP announcement messages. The scope sets the packet TTL. The interval sets the time between TX of announcements. The holdtime sets the hold time in the message, the time the mapping agent should wait before invalidating the candidate RP information. debug pim autorp Enable debug logging of the AutoRP protocol show ip pim [vrf NAME] autorp [json] Show details of the AutoRP protocol. To view learned RP info, use the existing command 'show ip pim rp-info' Extend pim yang for new configuration: augment /frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-pim:pim/frr-pim:address-family: +--rw rp +--rw auto-rp +--rw discovery-enabled? boolean +--rw announce-scope? uint8 +--rw announce-interval? uint16 +--rw announce-holdtime? uint16 +--rw candidate-rp-list* [rp-address] +--rw rp-address inet:ip-address +--rw (group-or-prefix-list)? +--:(group) | +--rw group? frr-route-types:ip-multicast-group-prefix +--:(prefix-list) +--rw prefix-list? plist-ref Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
2024-09-17 04:10:03 +02:00
int pim_process_autorp_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH,
"discovery-enabled");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY, "true");
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_no_autorp_cmd(struct vty *vty)
{
char xpath[XPATH_MAXLEN];
snprintf(xpath, sizeof(xpath), "%s/%s", FRR_PIM_AUTORP_XPATH,
"discovery-enabled");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_autorp_candidate_rp_cmd(struct vty *vty, bool no,
const char *rpaddr_str,
const struct prefix_ipv4 *grp,
const char *plist)
{
char xpath[XPATH_MAXLEN];
char grpstr[64];
if (no) {
if ((grp && !is_default_prefix((const struct prefix *)grp)) || plist) {
pimd,yang: Implement AutoRP CLI and NB config path New CLI commands added: router pim [vrf NAME] autorp discovery autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} autorp discovery Enables Auto RP discovery for learning dynamic RP information using the AutoRP protocol. autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] Enable announcements of a candidate RP with the given group range, or prefix list of group ranges, to an AutoRP mapping agent. autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} Configure the parameters of the AutoRP announcement messages. The scope sets the packet TTL. The interval sets the time between TX of announcements. The holdtime sets the hold time in the message, the time the mapping agent should wait before invalidating the candidate RP information. debug pim autorp Enable debug logging of the AutoRP protocol show ip pim [vrf NAME] autorp [json] Show details of the AutoRP protocol. To view learned RP info, use the existing command 'show ip pim rp-info' Extend pim yang for new configuration: augment /frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-pim:pim/frr-pim:address-family: +--rw rp +--rw auto-rp +--rw discovery-enabled? boolean +--rw announce-scope? uint8 +--rw announce-interval? uint16 +--rw announce-holdtime? uint16 +--rw candidate-rp-list* [rp-address] +--rw rp-address inet:ip-address +--rw (group-or-prefix-list)? +--:(group) | +--rw group? frr-route-types:ip-multicast-group-prefix +--:(prefix-list) +--rw prefix-list? plist-ref Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
2024-09-17 04:10:03 +02:00
/* If any single values are set, only destroy those */
if (grp && !is_default_prefix((const struct prefix *)grp)) {
pimd,yang: Implement AutoRP CLI and NB config path New CLI commands added: router pim [vrf NAME] autorp discovery autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} autorp discovery Enables Auto RP discovery for learning dynamic RP information using the AutoRP protocol. autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] Enable announcements of a candidate RP with the given group range, or prefix list of group ranges, to an AutoRP mapping agent. autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} Configure the parameters of the AutoRP announcement messages. The scope sets the packet TTL. The interval sets the time between TX of announcements. The holdtime sets the hold time in the message, the time the mapping agent should wait before invalidating the candidate RP information. debug pim autorp Enable debug logging of the AutoRP protocol show ip pim [vrf NAME] autorp [json] Show details of the AutoRP protocol. To view learned RP info, use the existing command 'show ip pim rp-info' Extend pim yang for new configuration: augment /frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-pim:pim/frr-pim:address-family: +--rw rp +--rw auto-rp +--rw discovery-enabled? boolean +--rw announce-scope? uint8 +--rw announce-interval? uint16 +--rw announce-holdtime? uint16 +--rw candidate-rp-list* [rp-address] +--rw rp-address inet:ip-address +--rw (group-or-prefix-list)? +--:(group) | +--rw group? frr-route-types:ip-multicast-group-prefix +--:(prefix-list) +--rw prefix-list? plist-ref Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
2024-09-17 04:10:03 +02:00
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/group",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
if (plist) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/prefix-list",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
} else {
/* No values set, remove the entire RP */
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
}
} else {
if ((grp && !is_default_prefix((const struct prefix *)grp)) || plist) {
pimd,yang: Implement AutoRP CLI and NB config path New CLI commands added: router pim [vrf NAME] autorp discovery autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} autorp discovery Enables Auto RP discovery for learning dynamic RP information using the AutoRP protocol. autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] Enable announcements of a candidate RP with the given group range, or prefix list of group ranges, to an AutoRP mapping agent. autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} Configure the parameters of the AutoRP announcement messages. The scope sets the packet TTL. The interval sets the time between TX of announcements. The holdtime sets the hold time in the message, the time the mapping agent should wait before invalidating the candidate RP information. debug pim autorp Enable debug logging of the AutoRP protocol show ip pim [vrf NAME] autorp [json] Show details of the AutoRP protocol. To view learned RP info, use the existing command 'show ip pim rp-info' Extend pim yang for new configuration: augment /frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-pim:pim/frr-pim:address-family: +--rw rp +--rw auto-rp +--rw discovery-enabled? boolean +--rw announce-scope? uint8 +--rw announce-interval? uint16 +--rw announce-holdtime? uint16 +--rw candidate-rp-list* [rp-address] +--rw rp-address inet:ip-address +--rw (group-or-prefix-list)? +--:(group) | +--rw group? frr-route-types:ip-multicast-group-prefix +--:(prefix-list) +--rw prefix-list? plist-ref Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
2024-09-17 04:10:03 +02:00
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
if (grp && !is_default_prefix((const struct prefix *)grp)) {
pimd,yang: Implement AutoRP CLI and NB config path New CLI commands added: router pim [vrf NAME] autorp discovery autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} autorp discovery Enables Auto RP discovery for learning dynamic RP information using the AutoRP protocol. autorp announce RP-ADDR [GROUP | group-list PREFIX-LIST] Enable announcements of a candidate RP with the given group range, or prefix list of group ranges, to an AutoRP mapping agent. autorp announce {scope (1-255) | interval (1-65535) | holdtime (0-65535)} Configure the parameters of the AutoRP announcement messages. The scope sets the packet TTL. The interval sets the time between TX of announcements. The holdtime sets the hold time in the message, the time the mapping agent should wait before invalidating the candidate RP information. debug pim autorp Enable debug logging of the AutoRP protocol show ip pim [vrf NAME] autorp [json] Show details of the AutoRP protocol. To view learned RP info, use the existing command 'show ip pim rp-info' Extend pim yang for new configuration: augment /frr-rt:routing/frr-rt:control-plane-protocols/frr-rt:control-plane-protocol/frr-pim:pim/frr-pim:address-family: +--rw rp +--rw auto-rp +--rw discovery-enabled? boolean +--rw announce-scope? uint8 +--rw announce-interval? uint16 +--rw announce-holdtime? uint16 +--rw candidate-rp-list* [rp-address] +--rw rp-address inet:ip-address +--rw (group-or-prefix-list)? +--:(group) | +--rw group? frr-route-types:ip-multicast-group-prefix +--:(prefix-list) +--rw prefix-list? plist-ref Signed-off-by: Nathan Bahr <nbahr@atcorp.com>
2024-09-17 04:10:03 +02:00
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/group",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
prefix2str(grp, grpstr,
sizeof(grpstr)));
}
if (plist) {
snprintfrr(xpath, sizeof(xpath),
"%s/candidate-rp-list[rp-address='%s']/prefix-list",
FRR_PIM_AUTORP_XPATH, rpaddr_str);
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
plist);
}
} else {
return CMD_WARNING_CONFIG_FAILED;
}
}
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_autorp_announce_scope_int_cmd(struct vty *vty, bool no,
const char *scope,
const char *interval,
const char *holdtime)
{
char xpath[XPATH_MAXLEN];
if (no) {
if (scope || interval || holdtime) {
/* If any single values are set, only destroy those */
if (scope) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-scope");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
if (interval) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-interval");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
if (holdtime) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-holdtime");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY,
NULL);
}
} else {
/* No values set, remove all */
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH, "announce-scope");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH, "announce-interval");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH, "announce-holdtime");
nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
}
} else {
if (scope || interval || holdtime) {
if (scope) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-scope");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
scope);
}
if (interval) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-interval");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
interval);
}
if (holdtime) {
snprintfrr(xpath, sizeof(xpath), "%s/%s",
FRR_PIM_AUTORP_XPATH,
"announce-holdtime");
nb_cli_enqueue_change(vty, xpath, NB_OP_MODIFY,
holdtime);
}
} else {
return CMD_WARNING_CONFIG_FAILED;
}
}
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();
struct ttable *tt = NULL;
char *table = NULL;
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");
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Source|Group|RpfIface|RpfAddress|RibNextHop|Metric|Pref");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
const char *rpf_ifname;
struct pim_rpf *rpf = &up->rpf;
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_addf(json_row, "rpfAddress", "%pPA",
&rpf->rpf_addr);
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 {
ttable_add_row(
tt, "%pPAs|%pPAs|%s|%pPA|%pPAs|%d|%d",
&up->sg.src, &up->sg.grp, rpf_ifname,
&rpf->rpf_addr,
&rpf->source_nexthop.mrib_nexthop_addr,
rpf->source_nexthop.mrib_route_metric,
rpf->source_nexthop.mrib_metric_preference);
}
}
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
void pim_show_neighbors_secondary(struct pim_instance *pim, struct vty *vty)
{
struct interface *ifp;
struct ttable *tt = NULL;
char *table = NULL;
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Interface|Address|Neighbor|Secondary");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
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))
ttable_add_row(tt, "%s|%pPAs|%pPAs|%pFX",
ifp->name, &ifaddr,
&neigh->source_addr, p);
}
}
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
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;
#if PIM_IPV != 4
struct ttable *tt = NULL;
char *table = NULL;
#endif
char flag[50];
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 -> " GM " Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted\n");
#if PIM_IPV == 4
vty_out(vty,
"Active Source Group RPT IIF OIL\n");
#else
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Active|Source|Group|RPT|IIF|OIL");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
#endif
}
frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
char src_str[PIM_ADDRSTRLEN];
char grp_str[PIM_ADDRSTRLEN];
char in_ifname[IFNAMSIZ + 1];
char out_ifname[IFNAMSIZ + 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_incoming_vif(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_boolean_add(json_source, "isRpt",
isRpt);
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, "oilRescan",
c_oil->oil_inherited_rescan);
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, "byteCount",
c_oil->cc.bytecnt);
json_object_int_add(json_source,
"wrongInterface",
c_oil->cc.wrong_if);
}
pim6d: "show ipv6 pim state" not displaying when OIL is empty Problem: ------- The cli "show ipv6 pim state" is not displaying when outgoing interface list is empty. This is fixed now. Before Fix: ---------- frr# show ipv6 pim state json { "ffaa::5":{ "1100::10":{ "ens224":{ }, "installed":1, "isRpt":false, "refCount":1, "oilListSize":0, "oilRescan":0, "lastUsed":0, "packetCount":40, "byteCount":3080, "wrongInterface":1 } } } frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL After fix: --------- Case 1: - "show ipv6 pim state" output for 1 mroute with 1 oil. frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 ens256( J ) Case 2: - "show ipv6 pim state" output for 1 mroute with multiple oil. frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 ens192( J ) ens256( J ) Case 3: - "show ipv6 pim state" output for 1 mroute with no oil frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 Case 4: - "show ipv6 pim state" output for multiple mroute with multiple oil frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 * ff05::2 y ens224 pim6reg(I ) ens192(I ) 1 * ffaa::5 y ens224 pim6reg(I ) ens192(I ) Issue: #13070 Signed-off-by: Sarita Patra <saritap@vmware.com>
2023-06-07 14:44:19 +02:00
} else
#if PIM_IPV == 4
vty_out(vty, "%-6d %-15pPAs %-15pPAs %-3s %-16s ",
c_oil->installed, oil_origin(c_oil),
oil_mcastgrp(c_oil), isRpt ? "y" : "n",
in_ifname);
pim6d: "show ipv6 pim state" not displaying when OIL is empty Problem: ------- The cli "show ipv6 pim state" is not displaying when outgoing interface list is empty. This is fixed now. Before Fix: ---------- frr# show ipv6 pim state json { "ffaa::5":{ "1100::10":{ "ens224":{ }, "installed":1, "isRpt":false, "refCount":1, "oilListSize":0, "oilRescan":0, "lastUsed":0, "packetCount":40, "byteCount":3080, "wrongInterface":1 } } } frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL After fix: --------- Case 1: - "show ipv6 pim state" output for 1 mroute with 1 oil. frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 ens256( J ) Case 2: - "show ipv6 pim state" output for 1 mroute with multiple oil. frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 ens192( J ) ens256( J ) Case 3: - "show ipv6 pim state" output for 1 mroute with no oil frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 Case 4: - "show ipv6 pim state" output for multiple mroute with multiple oil frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 * ff05::2 y ens224 pim6reg(I ) ens192(I ) 1 * ffaa::5 y ens224 pim6reg(I ) ens192(I ) Issue: #13070 Signed-off-by: Sarita Patra <saritap@vmware.com>
2023-06-07 14:44:19 +02:00
#else
/* Add a new row for c_oil with no OIF */
ttable_add_row(tt, "%d|%pPAs|%pPAs|%s|%s|%c",
c_oil->installed, oil_origin(c_oil),
oil_mcastgrp(c_oil), isRpt ? "y" : "n",
in_ifname, ' ');
#endif
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 {
flag[0] = '\0';
snprintf(flag, sizeof(flag), "(%c%c%c%c%c)",
(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 (first_oif) {
first_oif = 0;
#if PIM_IPV == 4
vty_out(vty, "%s%s", out_ifname, flag);
#else
pim6d: "show ipv6 pim state" not displaying when OIL is empty Problem: ------- The cli "show ipv6 pim state" is not displaying when outgoing interface list is empty. This is fixed now. Before Fix: ---------- frr# show ipv6 pim state json { "ffaa::5":{ "1100::10":{ "ens224":{ }, "installed":1, "isRpt":false, "refCount":1, "oilListSize":0, "oilRescan":0, "lastUsed":0, "packetCount":40, "byteCount":3080, "wrongInterface":1 } } } frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL After fix: --------- Case 1: - "show ipv6 pim state" output for 1 mroute with 1 oil. frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 ens256( J ) Case 2: - "show ipv6 pim state" output for 1 mroute with multiple oil. frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 ens192( J ) ens256( J ) Case 3: - "show ipv6 pim state" output for 1 mroute with no oil frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 1100::10 ffaa::5 n ens224 Case 4: - "show ipv6 pim state" output for multiple mroute with multiple oil frr# show ipv6 pim state Codes: J -> Pim Join, I -> MLD Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted Active Source Group RPT IIF OIL 1 * ff05::2 y ens224 pim6reg(I ) ens192(I ) 1 * ffaa::5 y ens224 pim6reg(I ) ens192(I ) Issue: #13070 Signed-off-by: Sarita Patra <saritap@vmware.com>
2023-06-07 14:44:19 +02:00
/* OIF found.
* Delete the existing row for c_oil,
* with no OIF.
* Add a new row for c_oil with OIF and
* flag.
*/
ttable_del_row(tt, tt->nrows - 1);
ttable_add_row(
tt, "%d|%pPAs|%pPAs|%s|%s|%s%s",
c_oil->installed,
oil_origin(c_oil),
oil_mcastgrp(c_oil),
isRpt ? "y" : "n", in_ifname,
out_ifname, flag);
#endif
} else {
#if PIM_IPV == 4
vty_out(vty, ", %s%s", out_ifname,
flag);
#else
ttable_add_row(tt,
"%c|%c|%c|%c|%c|%s%s",
' ', ' ', ' ', ' ', ' ',
out_ifname, flag);
#endif
}
}
}
#if PIM_IPV == 4
if (!json)
vty_out(vty, "\n");
#endif
}
/* Dump the generated table. */
if (!json) {
#if PIM_IPV == 4
vty_out(vty, "\n");
#else
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
#endif
}
}
/* 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;
struct ttable *tt = NULL;
char *table = NULL;
time_t now;
json_object *json_group = NULL;
json_object *json_row = NULL;
now = pim_time_monotonic_sec();
if (!json) {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Iif|Source|Group|State|Uptime|JoinTimer|RSTimer|KATimer|RefCnt");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
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(
up->rpf.source_nexthop.interface,
up->rpf.rpf_addr, false);
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",
"%pPA", &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 {
ttable_add_row(tt,
"%s|%pPAs|%pPAs|%s|%s|%s|%s|%s|%d",
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);
}
}
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
static void pim_show_join_desired_helper(struct pim_instance *pim,
struct vty *vty,
struct pim_upstream *up,
json_object *json, bool uj,
struct ttable *tt)
{
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 {
ttable_add_row(tt, "%pPAs|%pPAs|%s", &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;
struct ttable *tt = NULL;
char *table = NULL;
json_object *json = NULL;
if (uj)
json = json_object_new_object();
else {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Source|Group|EvalJD");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
/* scan all interfaces */
pim_show_join_desired_helper(pim, vty, up, json, uj, tt);
}
if (uj)
vty_json(vty, json);
else {
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, bool uj)
{
struct pim_upstream *up;
struct ttable *tt = NULL;
char *table = NULL;
json_object *json = NULL;
json_object *json_group = NULL;
json_object *json_row = NULL;
if (uj)
json = json_object_new_object();
else {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt,
"Source|Group|RpfIface|RibNextHop|RpfAddress");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
struct pim_rpf *rpf;
const char *rpf_ifname;
rpf = &up->rpf;
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_addf(json_row, "rpfAddress", "%pPA",
&rpf->rpf_addr);
json_object_object_add(json_group, src_str, json_row);
} else {
ttable_add_row(tt, "%pPAs|%pPAs|%s|%pPA|%pPA",
&up->sg.src, &up->sg.grp, rpf_ifname,
&rpf->source_nexthop.mrib_nexthop_addr,
&rpf->rpf_addr);
}
}
if (uj)
vty_json(vty, json);
else {
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
static void pim_show_join_helper(struct pim_interface *pim_ifp,
struct pim_ifchannel *ch, json_object *json,
time_t now, struct ttable *tt)
{
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))
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 {
ttable_add_row(
tt, "%s|%pPAs|%pPAs|%pPAs|%s|%s|%s|%s",
ch->interface->name, &ifaddr, &ch->sg.src, &ch->sg.grp,
pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
uptime, expire, prune);
}
}
int pim_show_join_cmd_helper(const char *vrf, struct vty *vty, pim_addr s_or_g,
pim_addr g, const char *json)
{
pim_sgaddr sg = {};
struct vrf *v;
struct pim_instance *pim;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v) {
vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
return CMD_WARNING;
}
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (!pim_addr_is_any(s_or_g)) {
if (!pim_addr_is_any(g)) {
sg.src = s_or_g;
sg.grp = g;
} else
sg.grp = s_or_g;
}
if (json)
json_parent = json_object_new_object();
pim_show_join(pim, vty, &sg, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_join_vrf_all_cmd_helper(struct vty *vty, const char *json)
{
pim_sgaddr sg = {0};
struct vrf *vrf_struct;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf_struct, vrf_name_head, &vrfs_by_name) {
if (!json_parent)
vty_out(vty, "VRF: %s\n", vrf_struct->name);
else
json_vrf = json_object_new_object();
pim_show_join(vrf_struct->info, vty, &sg, json_vrf);
if (json)
json_object_object_add(json_parent, vrf_struct->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_WARNING;
}
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;
struct ttable *tt = NULL;
char *table = NULL;
now = pim_time_monotonic_sec();
if (!json) {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Interface|Address|Source|Group|State|Uptime|Expire|Prune");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
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(pim_ifp, ch, json, now, tt);
} /* scan interface channels */
}
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
static void pim_show_jp_agg_helper(struct interface *ifp,
struct pim_neighbor *neigh,
struct pim_upstream *up, int is_join,
struct ttable *tt)
{
ttable_add_row(tt, "%s|%pPAs|%pPAs|%pPAs|%s", ifp->name,
&neigh->source_addr, &up->sg.src, &up->sg.grp,
is_join ? "J" : "P");
}
int pim_show_jp_agg_list_cmd_helper(const char *vrf, struct vty *vty)
{
struct vrf *v;
struct pim_instance *pim;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v) {
vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
return CMD_WARNING;
}
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_jp_agg_list(pim, vty);
return CMD_SUCCESS;
}
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;
struct ttable *tt;
char *table;
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Interface|RPF Nbr|Source|Group|State");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
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(ifp, neigh,
js->up,
js->is_join, tt);
}
}
}
}
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
int pim_show_membership_cmd_helper(const char *vrf, struct vty *vty, bool uj)
{
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim_show_membership(v->info, vty, uj);
return CMD_SUCCESS;
}
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;
struct ttable *tt = NULL;
char *table = 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 {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Interface|Address|Source|Group|Membership");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
/*
* 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) {
const char *address, *source,
*localMembership;
json_object_object_get_ex(
val, "address", &json_tmp);
address = json_object_get_string(
json_tmp);
json_object_object_get_ex(if_field_val,
"source",
&json_tmp);
source = json_object_get_string(
json_tmp);
json_object_object_get_ex(
if_field_val, "localMembership",
&json_tmp);
localMembership =
json_object_get_string(
json_tmp);
ttable_add_row(tt, "%s|%s|%s|%s|%s",
key, address, source,
if_field_key,
localMembership);
}
}
}
json_object_free(json);
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
static void pim_show_channel_helper(struct pim_instance *pim,
struct pim_interface *pim_ifp,
struct pim_ifchannel *ch, json_object *json,
bool uj, struct ttable *tt)
{
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 {
ttable_add_row(tt, "%s|%pPAs|%pPAs|%s|%s|%s|%s|%s",
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;
struct ttable *tt = NULL;
json_object *json = NULL;
char *table = NULL;
if (uj)
json = json_object_new_object();
else {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Interface|Source|Group|LostAssert|Joins|PimInclude|JoinDesired|EvalJD");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
/* 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, pim_ifp, ch, json, uj, tt);
}
}
if (uj)
vty_json(vty, json);
else {
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
int pim_show_channel_cmd_helper(const char *vrf, struct vty *vty, bool uj)
{
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim_show_channel(v->info, vty, uj);
return CMD_SUCCESS;
}
int pim_show_interface_cmd_helper(const char *vrf, struct vty *vty, bool uj,
bool mlag, const char *interface)
{
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
if (uj)
json_parent = json_object_new_object();
if (interface)
pim_show_interfaces_single(v->info, vty, interface, mlag,
json_parent);
else
pim_show_interfaces(v->info, vty, mlag, json_parent);
if (uj)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_interface_vrf_all_cmd_helper(struct vty *vty, bool uj, bool mlag,
const char *interface)
{
struct vrf *v;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (uj)
json_parent = json_object_new_object();
RB_FOREACH (v, vrf_name_head, &vrfs_by_name) {
if (!uj)
vty_out(vty, "VRF: %s\n", v->name);
else
json_vrf = json_object_new_object();
if (interface)
pim_show_interfaces_single(v->info, vty, interface,
mlag, json_vrf);
else
pim_show_interfaces(v->info, vty, mlag, json_vrf);
if (uj)
json_object_object_add(json_parent, v->name, json_vrf);
}
if (uj)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
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;
struct ttable *tt = NULL;
char *table = NULL;
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) {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Interface|State|Address|PIM Nbrs|PIM DR|FHR|IfChannels");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
json_object_object_foreach(json, key, val)
{
const char *state, *address, *pimdr;
int neighbors, firsthpr, pimifchnl;
json_object_object_get_ex(val, "state", &json_tmp);
state = json_object_get_string(json_tmp);
json_object_object_get_ex(val, "address", &json_tmp);
address = json_object_get_string(json_tmp);
json_object_object_get_ex(val, "pimNeighbors",
&json_tmp);
neighbors = json_object_get_int(json_tmp);
if (json_object_object_get_ex(
val, "pimDesignatedRouterLocal",
&json_tmp)) {
pimdr = "local";
} else {
json_object_object_get_ex(
val, "pimDesignatedRouter", &json_tmp);
pimdr = json_object_get_string(json_tmp);
}
json_object_object_get_ex(val, "firstHopRouter",
&json_tmp);
firsthpr = json_object_get_int(json_tmp);
json_object_object_get_ex(val, "pimIfChannels",
&json_tmp);
pimifchnl = json_object_get_int(json_tmp);
ttable_add_row(tt, "%s|%s|%s|%d|%s|%d|%d", key, state,
address, neighbors, pimdr, firsthpr,
pimifchnl);
}
pimd, pim6d: deleting the memory malloced for json. Problem Statement: ================= 2022-08-18 04:57:32,376 - ERROR: Found memory leak in daemon pimd 2022-08-18 04:57:32,377 - ERROR: ==12100== 18,978 (144 direct, 18,834 indirect) bytes in 2 blocks are definitely lost in loss record 45 of 45 2022-08-18 04:57:32,377 - ERROR: ==12100== at 0x4C31FAC: calloc (vg_replace_malloc.c:762) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x532F51F: json_object_new_object (in /lib/x86_64-linux-gnu/libjson-c.so.3.0.1) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x128B5C: pim_show_interfaces (pim_cmd_common.c:3395) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x15A86B: show_ip_pim_interface_magic (pim_cmd.c:3127) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x15A86B: show_ip_pim_interface (pim_cmd_clippy.c:324) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4E672B2: cmd_execute_command_real.isra.2 (command.c:1005) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4E696FE: cmd_execute_command (command.c:1064) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4E698F1: cmd_execute (command.c:1227) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4EBC814: vty_command (vty.c:530) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4EBC9EF: vty_execute (vty.c:1295) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4EBF1F7: vtysh_read (vty.c:2142) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4EB739B: thread_call (thread.c:1692) 2022-08-18 04:57:32,377 - ERROR: ==12100== by 0x4E85B17: frr_run (libfrr.c:1068) 2022-08-18 04:57:32,377 - ERROR: ==12100== Signed-off-by: Abhishek N R <abnr@vmware.com>
2022-08-29 13:50:26 +02:00
json_object_free(json);
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
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);
}
if (pim_ifp->pim_passive_enable)
json_object_boolean_true_add(json_row,
"passive");
/* 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);
}
if (pim_ifp->pim_passive_enable)
vty_out(vty, "Passive : %s\n",
(pim_ifp->pim_passive_enable) ? "yes"
: "no");
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 && !json)
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 vty_pnc_cache_walk_data {
struct vty *vty;
struct pim_instance *pim;
};
struct json_pnc_cache_walk_data {
json_object *json_obj;
struct pim_instance *pim;
};
static int pim_print_vty_pnc_cache_walkcb(struct hash_bucket *bucket, void *arg)
{
struct pim_nexthop_cache *pnc = bucket->data;
struct vty_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;
struct ttable *tt = NULL;
char *table = NULL;
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Address|Interface|Nexthop");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
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);
#if PIM_IPV == 4
ttable_add_row(tt, "%pPA|%s|%pI4", &pnc->rpf.rpf_addr,
ifp ? ifp->name : "NULL", &nh_node->gate.ipv4);
#else
ttable_add_row(tt, "%pPA|%s|%pI6", &pnc->rpf.rpf_addr,
ifp ? ifp->name : "NULL", &nh_node->gate.ipv6);
#endif
}
/* Dump the generated table. */
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
return CMD_SUCCESS;
}
static int pim_print_json_pnc_cache_walkcb(struct hash_bucket *backet,
void *arg)
{
struct pim_nexthop_cache *pnc = backet->data;
struct json_pnc_cache_walk_data *cwd = arg;
struct pim_instance *pim = cwd->pim;
struct nexthop *nh_node = NULL;
ifindex_t first_ifindex;
struct interface *ifp = NULL;
char addr_str[PIM_ADDRSTRLEN];
json_object *json_row = NULL;
json_object *json_ifp = NULL;
json_object *json_arr = NULL;
struct pim_interface *pim_ifp = NULL;
bool pim_enable = false;
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);
snprintfrr(addr_str, sizeof(addr_str), "%pPA",
&pnc->rpf.rpf_addr);
json_object_object_get_ex(cwd->json_obj, addr_str, &json_row);
if (!json_row) {
json_row = json_object_new_object();
json_object_string_addf(json_row, "address", "%pPA",
&pnc->rpf.rpf_addr);
json_object_object_addf(cwd->json_obj, json_row, "%pPA",
&pnc->rpf.rpf_addr);
json_arr = json_object_new_array();
json_object_object_add(json_row, "nexthops", json_arr);
}
json_ifp = json_object_new_object();
json_object_string_add(json_ifp, "interface",
ifp ? ifp->name : "NULL");
if (ifp)
pim_ifp = ifp->info;
if (pim_ifp && pim_ifp->pim_enable)
pim_enable = true;
json_object_boolean_add(json_ifp, "pimEnabled", pim_enable);
#if PIM_IPV == 4
json_object_string_addf(json_ifp, "nexthop", "%pI4",
&nh_node->gate.ipv4);
#else
json_object_string_addf(json_ifp, "nexthop", "%pI6",
&nh_node->gate.ipv6);
#endif
json_object_array_add(json_arr, json_ifp);
}
return CMD_SUCCESS;
}
int pim_show_nexthop_lookup_cmd_helper(const char *vrf, struct vty *vty,
pim_addr source, pim_addr group)
{
int result = 0;
pim_addr vif_source;
struct prefix grp;
struct pim_nexthop nexthop;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
#if PIM_IPV == 4
if (pim_is_group_224_4(source)) {
vty_out(vty,
"Invalid argument. Expected Valid Source Address.\n");
return CMD_WARNING;
}
if (!pim_is_group_224_4(group)) {
vty_out(vty,
"Invalid argument. Expected Valid Multicast Group Address.\n");
return CMD_WARNING;
}
#endif
if (!pim_rp_set_upstream_addr(v->info, &vif_source, source, group))
return CMD_SUCCESS;
pim_addr_to_prefix(&grp, group);
memset(&nexthop, 0, sizeof(nexthop));
result =
pim_ecmp_nexthop_lookup(v->info, &nexthop, vif_source, &grp, 0);
if (!result) {
vty_out(vty,
"Nexthop Lookup failed, no usable routes returned.\n");
return CMD_SUCCESS;
}
vty_out(vty, "Group %pFXh --- Nexthop %pPAs Interface %s\n", &grp,
&nexthop.mrib_nexthop_addr, nexthop.interface->name);
return CMD_SUCCESS;
}
int pim_show_nexthop_cmd_helper(const char *vrf, struct vty *vty, bool uj)
{
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim_show_nexthop(v->info, vty, uj);
return CMD_SUCCESS;
}
void pim_show_nexthop(struct pim_instance *pim, struct vty *vty, bool uj)
{
struct vty_pnc_cache_walk_data cwd;
struct json_pnc_cache_walk_data jcwd;
cwd.vty = vty;
cwd.pim = pim;
jcwd.pim = pim;
if (uj) {
jcwd.json_obj = json_object_new_object();
} else {
vty_out(vty, "Number of registered addresses: %lu\n",
pim->rpf_hash->count);
}
if (uj) {
hash_walk(pim->rpf_hash, pim_print_json_pnc_cache_walkcb,
&jcwd);
vty_json(vty, jcwd.json_obj);
} else
hash_walk(pim->rpf_hash, pim_print_vty_pnc_cache_walkcb, &cwd);
}
int pim_show_neighbors_cmd_helper(const char *vrf, struct vty *vty,
const char *json, const char *interface)
{
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
if (json)
json_parent = json_object_new_object();
if (interface)
pim_show_neighbors_single(v->info, vty, interface, json_parent);
else
pim_show_neighbors(v->info, vty, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_neighbors_vrf_all_cmd_helper(struct vty *vty, const char *json,
const char *interface)
{
struct vrf *v;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (v, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", v->name);
else
json_vrf = json_object_new_object();
if (interface)
pim_show_neighbors_single(v->info, vty, interface,
json_vrf);
else
pim_show_neighbors(v->info, vty, json_vrf);
if (json)
json_object_object_add(json_parent, v->name, json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
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 && !json)
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;
struct ttable *tt = NULL;
char *table = NULL;
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) {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Interface|Neighbor|Uptime|Holdtime|DR Pri");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
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 {
ttable_add_row(tt, "%s|%pPAs|%s|%s|%d",
ifp->name, &neigh->source_addr,
uptime, expire,
neigh->dr_priority);
}
}
if (json) {
json_object_object_add(json, ifp->name, json_ifp_rows);
json_ifp_rows = NULL;
}
}
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
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)
{
char ssmpingd_src_ip_xpath[XPATH_MAXLEN];
int printed;
printed = snprintf(ssmpingd_src_ip_xpath, sizeof(ssmpingd_src_ip_xpath),
"./ssm-pingd-source-ip[.='%s']", src_str);
if (printed >= (int)sizeof(ssmpingd_src_ip_xpath)) {
vty_out(vty, "Xpath too long (%d > %u)", printed + 1,
XPATH_MAXLEN);
return CMD_WARNING_CONFIG_FAILED;
}
nb_cli_enqueue_change(vty, ssmpingd_src_ip_xpath, operation, NULL);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_bsm_cmd(struct vty *vty)
{
const struct lyd_node *gm_enable_dnode;
gm_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH,
FRR_PIM_AF_XPATH_VAL);
if (!gm_enable_dnode)
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
else {
if (!yang_dnode_get_bool(gm_enable_dnode, "."))
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
}
nb_cli_enqueue_change(vty, "./bsm", NB_OP_MODIFY, "true");
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_no_bsm_cmd(struct vty *vty)
{
nb_cli_enqueue_change(vty, "./bsm", NB_OP_MODIFY, "false");
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_unicast_bsm_cmd(struct vty *vty)
{
const struct lyd_node *gm_enable_dnode;
gm_enable_dnode = yang_dnode_getf(vty->candidate_config->dnode,
FRR_GMP_ENABLE_XPATH, VTY_CURR_XPATH,
FRR_PIM_AF_XPATH_VAL);
if (!gm_enable_dnode)
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
else {
if (!yang_dnode_get_bool(gm_enable_dnode, "."))
nb_cli_enqueue_change(vty, "./pim-enable", NB_OP_MODIFY,
"true");
}
nb_cli_enqueue_change(vty, "./unicast-bsm", NB_OP_MODIFY, "true");
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
int pim_process_no_unicast_bsm_cmd(struct vty *vty)
{
nb_cli_enqueue_change(vty, "./unicast-bsm", NB_OP_MODIFY, "false");
return nb_cli_apply_changes(vty, FRR_PIM_INTERFACE_XPATH,
FRR_PIM_AF_XPATH_VAL);
}
/* helper for bsr/rp candidate commands*/
int pim_process_bsr_candidate_cmd(struct vty *vty, const char *cand_str,
bool no, bool is_rp, bool any,
const char *ifname, const char *addr,
const char *prio, const char *interval)
{
if (no)
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
else {
nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
if (any)
nb_cli_enqueue_change(vty, "./if-any", NB_OP_CREATE,
NULL);
else if (ifname)
nb_cli_enqueue_change(vty, "./interface", NB_OP_CREATE,
ifname);
else if (addr)
nb_cli_enqueue_change(vty, "./address", NB_OP_CREATE,
addr);
else
nb_cli_enqueue_change(vty, "./if-loopback",
NB_OP_CREATE, NULL);
if (prio)
nb_cli_enqueue_change(vty,
(is_rp ? "./rp-priority"
: "./bsr-priority"),
NB_OP_MODIFY, prio);
/* only valid for rp candidate case*/
if (is_rp && interval)
nb_cli_enqueue_change(vty, "./advertisement-interval",
NB_OP_MODIFY, interval);
}
return nb_cli_apply_changes(vty, "%s", cand_str);
}
int pim_process_bsr_crp_grp_cmd(struct vty *vty, const char *grp, bool no)
{
if (no)
nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, grp);
else
nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, grp);
return nb_cli_apply_changes(vty, "%s/group-list", FRR_PIM_CAND_RP_XPATH);
}
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;
struct ttable *tt = NULL;
char *table = NULL;
json_object *json_row = NULL;
vty_out(vty, "\n");
if (!json) {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Interface|Address|ifi|Vif|PktsIn|PktsOut|BytesIn|BytesOut");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
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 {
ttable_add_row(tt, "%s|%pPAs|%d|%d|%lu|%lu|%lu|%lu",
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);
}
}
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
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);
vty_out(vty, "PIM Register socket descriptor:");
vty_out(vty, " %d(%s)\n", pim->reg_sock, 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);
pim_zlookup_show_ip_multicast(vty);
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;
struct ttable *tt = NULL;
char *table = NULL;
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[IFNAMSIZ + 1];
char out_ifname[IFNAMSIZ + 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");
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt, "Source|Group|Flags|Proto|Input|Output|TTL|Uptime");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
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_incoming_vif(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_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_incoming_vif(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_incoming_vif(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));
}
ttable_add_row(tt, "%s|%s|%s|%s|%s|%s|%d|%s",
src_str, grp_str, 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) {
ttable_add_row(tt, "%pPAs|%pPAs|%s|%s|%s|%s|%d|%s",
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_incoming_vif(
&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 {
ttable_add_row(
tt, "%pPAs|%pPAs|%s|%s|%s|%s|%d|%s",
&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) {
ttable_add_row(tt, "%pPAs|%pPAs|%s|%s|%s|%s|%d|%s",
&s_route->source, &s_route->group, "-",
proto, in_ifname, "none", 0, "--:--:--");
}
}
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
static void show_mroute_count_per_channel_oil(struct channel_oil *c_oil,
json_object *json,
struct ttable *tt)
{
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 {
ttable_add_row(tt, "%pPAs|%pPAs|%llu|%ld|%ld|%ld",
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;
struct ttable *tt = NULL;
char *table = NULL;
if (!json) {
vty_out(vty, "\n");
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt,
"Source|Group|LastUsed|Packets|Bytes|WrongIf");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
/* 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, tt);
for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr))
show_mroute_count_per_channel_oil(&sr->c_oil, json, tt);
/* Dump the generated table. */
if (!json) {
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
}
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);
}
}
int clear_ip_mroute_count_command(struct vty *vty, const char *name)
{
struct listnode *node;
struct channel_oil *c_oil;
struct static_route *sr;
struct vrf *v = pim_cmd_lookup(vty, name);
struct pim_instance *pim;
if (!v)
return CMD_WARNING;
pim = v->info;
frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
if (!c_oil->installed)
continue;
pim_mroute_update_counters(c_oil);
c_oil->cc.origpktcnt = c_oil->cc.pktcnt;
c_oil->cc.origbytecnt = c_oil->cc.bytecnt;
c_oil->cc.origwrong_if = c_oil->cc.wrong_if;
}
for (ALL_LIST_ELEMENTS_RO(pim->static_routes, node, sr)) {
if (!sr->c_oil.installed)
continue;
pim_mroute_update_counters(&sr->c_oil);
sr->c_oil.cc.origpktcnt = sr->c_oil.cc.pktcnt;
sr->c_oil.cc.origbytecnt = sr->c_oil.cc.bytecnt;
sr->c_oil.cc.origwrong_if = sr->c_oil.cc.wrong_if;
}
return CMD_SUCCESS;
}
struct vrf *pim_cmd_lookup(struct vty *vty, const char *name)
{
struct vrf *vrf;
if (name)
vrf = vrf_lookup_by_name(name);
else
vrf = vrf_lookup_by_id(VRF_DEFAULT);
if (!vrf)
vty_out(vty, "Specified VRF: %s does not exist\n", name);
return vrf;
}
struct vrf *pim_cmd_lookup_vrf(struct vty *vty, struct cmd_token *argv[],
const int argc, int *idx, bool uj)
{
struct vrf *vrf;
if (argv_find(argv, argc, "NAME", idx))
vrf = vrf_lookup_by_name(argv[*idx]->arg);
else
vrf = vrf_lookup_by_id(VRF_DEFAULT);
if (!vrf) {
if (uj)
vty_json_empty(vty, NULL);
else
vty_out(vty, "Specified VRF: %s does not exist\n",
argv[*idx]->arg);
}
return vrf;
}
void clear_mroute(struct pim_instance *pim)
{
struct pim_upstream *up;
struct interface *ifp;
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
struct pim_ifchannel *ch;
if (!pim_ifp)
continue;
/* deleting all ifchannels */
while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
pim_ifchannel_delete(ch);
}
#if PIM_IPV == 4
/* clean up all igmp groups */
struct gm_group *grp;
if (pim_ifp->gm_group_list) {
while (pim_ifp->gm_group_list->count) {
grp = listnode_head(pim_ifp->gm_group_list);
igmp_group_delete(grp);
}
}
#else
struct gm_if *gm_ifp;
gm_ifp = pim_ifp->mld;
if (gm_ifp)
gm_group_delete(gm_ifp);
#endif
}
/* clean up all upstreams*/
while ((up = rb_pim_upstream_first(&pim->upstream_head)))
pim_upstream_del(pim, up, __func__);
}
void clear_pim_statistics(struct pim_instance *pim)
{
struct interface *ifp;
pim->bsm_rcvd = 0;
pim->bsm_sent = 0;
pim->bsm_dropped = 0;
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
if (!pim_ifp)
continue;
pim_ifp->pim_ifstat_bsm_cfg_miss = 0;
pim_ifp->pim_ifstat_ucast_bsm_cfg_miss = 0;
pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
}
}
int clear_pim_interface_traffic(const char *vrf, struct vty *vty)
{
struct interface *ifp = NULL;
struct pim_interface *pim_ifp = NULL;
struct vrf *v = pim_cmd_lookup(vty, vrf);
if (!v)
return CMD_WARNING;
FOR_ALL_INTERFACES (v, ifp) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
pim_ifp->pim_ifstat_hello_recv = 0;
pim_ifp->pim_ifstat_hello_sent = 0;
pim_ifp->pim_ifstat_join_recv = 0;
pim_ifp->pim_ifstat_join_send = 0;
pim_ifp->pim_ifstat_prune_recv = 0;
pim_ifp->pim_ifstat_prune_send = 0;
pim_ifp->pim_ifstat_reg_recv = 0;
pim_ifp->pim_ifstat_reg_send = 0;
pim_ifp->pim_ifstat_reg_stop_recv = 0;
pim_ifp->pim_ifstat_reg_stop_send = 0;
pim_ifp->pim_ifstat_assert_recv = 0;
pim_ifp->pim_ifstat_assert_send = 0;
pim_ifp->pim_ifstat_bsm_rx = 0;
pim_ifp->pim_ifstat_bsm_tx = 0;
#if PIM_IPV == 4
pim_ifp->igmp_ifstat_joins_sent = 0;
pim_ifp->igmp_ifstat_joins_failed = 0;
pim_ifp->igmp_peak_group_count = 0;
#endif
}
return CMD_SUCCESS;
}
int pim_debug_pim_cmd(void)
{
PIM_DO_DEBUG_PIM_EVENTS;
PIM_DO_DEBUG_PIM_PACKETS;
PIM_DO_DEBUG_PIM_TRACE;
PIM_DO_DEBUG_MSDP_EVENTS;
PIM_DO_DEBUG_MSDP_PACKETS;
PIM_DO_DEBUG_BSM;
PIM_DO_DEBUG_VXLAN;
return CMD_SUCCESS;
}
int pim_no_debug_pim_cmd(void)
{
PIM_DONT_DEBUG_PIM_EVENTS;
PIM_DONT_DEBUG_PIM_PACKETS;
PIM_DONT_DEBUG_PIM_TRACE;
PIM_DONT_DEBUG_MSDP_EVENTS;
PIM_DONT_DEBUG_MSDP_PACKETS;
PIM_DONT_DEBUG_PIM_PACKETDUMP_SEND;
PIM_DONT_DEBUG_PIM_PACKETDUMP_RECV;
PIM_DONT_DEBUG_BSM;
PIM_DONT_DEBUG_VXLAN;
return CMD_SUCCESS;
}
int pim_debug_pim_packets_cmd(const char *hello, const char *joins,
const char *registers, struct vty *vty)
{
if (hello) {
PIM_DO_DEBUG_PIM_HELLO;
vty_out(vty, "PIM Hello debugging is on\n");
} else if (joins) {
PIM_DO_DEBUG_PIM_J_P;
vty_out(vty, "PIM Join/Prune debugging is on\n");
} else if (registers) {
PIM_DO_DEBUG_PIM_REG;
vty_out(vty, "PIM Register debugging is on\n");
} else {
PIM_DO_DEBUG_PIM_PACKETS;
vty_out(vty, "PIM Packet debugging is on\n");
}
return CMD_SUCCESS;
}
int pim_no_debug_pim_packets_cmd(const char *hello, const char *joins,
const char *registers, struct vty *vty)
{
if (hello) {
PIM_DONT_DEBUG_PIM_HELLO;
vty_out(vty, "PIM Hello debugging is off\n");
} else if (joins) {
PIM_DONT_DEBUG_PIM_J_P;
vty_out(vty, "PIM Join/Prune debugging is off\n");
} else if (registers) {
PIM_DONT_DEBUG_PIM_REG;
vty_out(vty, "PIM Register debugging is off\n");
} else {
PIM_DONT_DEBUG_PIM_PACKETS;
vty_out(vty, "PIM Packet debugging is off\n");
}
return CMD_SUCCESS;
}
int pim_show_rpf_helper(const char *vrf, struct vty *vty, bool json)
{
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
pim_show_rpf(pim, vty, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_rpf_vrf_all_helper(struct vty *vty, bool json)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
pim_show_rpf(vrf->info, vty, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_rp_helper(const char *vrf, struct vty *vty, const char *group_str,
const struct prefix *group, bool json)
{
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
struct prefix *range = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (group_str) {
range = prefix_new();
prefix_copy(range, group);
apply_mask(range);
}
if (json)
json_parent = json_object_new_object();
pim_rp_show_information(pim, range, vty, json_parent);
if (json)
vty_json(vty, json_parent);
prefix_free(&range);
return CMD_SUCCESS;
}
int pim_show_rp_vrf_all_helper(struct vty *vty, const char *group_str,
const struct prefix *group, bool json)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
struct prefix *range = NULL;
if (group_str) {
range = prefix_new();
prefix_copy(range, group);
apply_mask(range);
}
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
pim_rp_show_information(vrf->info, range, vty, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
prefix_free(&range);
return CMD_SUCCESS;
}
int pim_show_secondary_helper(const char *vrf, struct vty *vty)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_neighbors_secondary(pim, vty);
return CMD_SUCCESS;
}
int pim_show_statistics_helper(const char *vrf, struct vty *vty,
const char *word, bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (word)
pim_show_statistics(pim, vty, word, uj);
else
pim_show_statistics(pim, vty, NULL, uj);
return CMD_SUCCESS;
}
int pim_show_upstream_helper(const char *vrf, struct vty *vty, pim_addr s_or_g,
pim_addr g, bool json)
{
pim_sgaddr sg = {0};
struct vrf *v;
struct pim_instance *pim;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v) {
vty_out(vty, "%% Vrf specified: %s does not exist\n", vrf);
return CMD_WARNING;
}
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
if (!pim_addr_is_any(s_or_g)) {
if (!pim_addr_is_any(g)) {
sg.src = s_or_g;
sg.grp = g;
} else
sg.grp = s_or_g;
}
pim_show_upstream(pim, vty, &sg, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_upstream_vrf_all_helper(struct vty *vty, bool json)
{
pim_sgaddr sg = {0};
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
pim_show_upstream(vrf->info, vty, &sg, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_upstream_join_desired_helper(const char *vrf, struct vty *vty,
bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_join_desired(pim, vty, uj);
return CMD_SUCCESS;
}
int pim_show_upstream_rpf_helper(const char *vrf, struct vty *vty, bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_upstream_rpf(pim, vty, uj);
return CMD_SUCCESS;
}
int pim_show_state_helper(const char *vrf, struct vty *vty,
const char *s_or_g_str, const char *g_str, bool json)
{
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
pim_show_state(pim, vty, s_or_g_str, g_str, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_state_vrf_all_helper(struct vty *vty, const char *s_or_g_str,
const char *g_str, bool json)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
pim_show_state(vrf->info, vty, s_or_g_str, g_str, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_multicast_helper(const char *vrf, struct vty *vty)
{
struct vrf *v;
struct pim_instance *pim;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_cmd_show_ip_multicast_helper(pim, vty);
return CMD_SUCCESS;
}
int pim_show_multicast_vrf_all_helper(struct vty *vty)
{
struct vrf *vrf;
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
vty_out(vty, "VRF: %s\n", vrf->name);
pim_cmd_show_ip_multicast_helper(vrf->info, vty);
}
return CMD_SUCCESS;
}
int pim_show_multicast_count_helper(const char *vrf, struct vty *vty, bool json)
{
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
show_multicast_interfaces(pim, vty, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_multicast_count_vrf_all_helper(struct vty *vty, bool json)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
show_multicast_interfaces(vrf->info, vty, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_mroute_helper(const char *vrf, struct vty *vty, pim_addr s_or_g,
pim_addr g, bool fill, bool json)
{
pim_sgaddr sg = {0};
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
if (!pim_addr_is_any(s_or_g)) {
if (!pim_addr_is_any(g)) {
sg.src = s_or_g;
sg.grp = g;
} else
sg.grp = s_or_g;
}
show_mroute(pim, vty, &sg, fill, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_mroute_vrf_all_helper(struct vty *vty, bool fill, bool json)
{
pim_sgaddr sg = {0};
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
show_mroute(vrf->info, vty, &sg, fill, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_mroute_count_helper(const char *vrf, struct vty *vty, bool json)
{
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
show_mroute_count(pim, vty, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_mroute_count_vrf_all_helper(struct vty *vty, bool json)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
show_mroute_count(vrf->info, vty, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_mroute_summary_helper(const char *vrf, struct vty *vty, bool json)
{
struct pim_instance *pim;
struct vrf *v;
json_object *json_parent = NULL;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (json)
json_parent = json_object_new_object();
show_mroute_summary(pim, vty, json_parent);
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
int pim_show_mroute_summary_vrf_all_helper(struct vty *vty, bool json)
{
struct vrf *vrf;
json_object *json_parent = NULL;
json_object *json_vrf = NULL;
if (json)
json_parent = json_object_new_object();
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
if (!json)
vty_out(vty, "VRF: %s\n", vrf->name);
else
json_vrf = json_object_new_object();
show_mroute_summary(vrf->info, vty, json_vrf);
if (json)
json_object_object_add(json_parent, vrf->name,
json_vrf);
}
if (json)
vty_json(vty, json_parent);
return CMD_SUCCESS;
}
void pim_show_interface_traffic(struct pim_instance *pim, struct vty *vty,
bool uj)
{
struct interface *ifp = NULL;
struct pim_interface *pim_ifp = NULL;
json_object *json = NULL;
json_object *json_row = NULL;
if (uj)
json = json_object_new_object();
else {
vty_out(vty, "\n");
vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n",
"Interface", " HELLO", " JOIN",
" PRUNE", " REGISTER", "REGISTER-STOP",
" ASSERT", " BSM");
vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n", "",
" Rx/Tx", " Rx/Tx", " Rx/Tx",
" Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx");
vty_out(vty,
"---------------------------------------------------------------------------------------------------------------\n");
}
FOR_ALL_INTERFACES (pim->vrf, ifp) {
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
if (uj) {
json_row = json_object_new_object();
json_object_pim_ifp_add(json_row, ifp);
json_object_int_add(json_row, "helloRx",
pim_ifp->pim_ifstat_hello_recv);
json_object_int_add(json_row, "helloTx",
pim_ifp->pim_ifstat_hello_sent);
json_object_int_add(json_row, "joinRx",
pim_ifp->pim_ifstat_join_recv);
json_object_int_add(json_row, "joinTx",
pim_ifp->pim_ifstat_join_send);
json_object_int_add(json_row, "pruneRx",
pim_ifp->pim_ifstat_prune_recv);
json_object_int_add(json_row, "pruneTx",
pim_ifp->pim_ifstat_prune_send);
json_object_int_add(json_row, "registerRx",
pim_ifp->pim_ifstat_reg_recv);
json_object_int_add(json_row, "registerTx",
pim_ifp->pim_ifstat_reg_send);
json_object_int_add(json_row, "registerStopRx",
pim_ifp->pim_ifstat_reg_stop_recv);
json_object_int_add(json_row, "registerStopTx",
pim_ifp->pim_ifstat_reg_stop_send);
json_object_int_add(json_row, "assertRx",
pim_ifp->pim_ifstat_assert_recv);
json_object_int_add(json_row, "assertTx",
pim_ifp->pim_ifstat_assert_send);
json_object_int_add(json_row, "bsmRx",
pim_ifp->pim_ifstat_bsm_rx);
json_object_int_add(json_row, "bsmTx",
pim_ifp->pim_ifstat_bsm_tx);
json_object_object_add(json, ifp->name, json_row);
} else {
vty_out(vty,
"%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7" PRIu64
"/%-7" PRIu64 "\n",
ifp->name, pim_ifp->pim_ifstat_hello_recv,
pim_ifp->pim_ifstat_hello_sent,
pim_ifp->pim_ifstat_join_recv,
pim_ifp->pim_ifstat_join_send,
pim_ifp->pim_ifstat_prune_recv,
pim_ifp->pim_ifstat_prune_send,
pim_ifp->pim_ifstat_reg_recv,
pim_ifp->pim_ifstat_reg_send,
pim_ifp->pim_ifstat_reg_stop_recv,
pim_ifp->pim_ifstat_reg_stop_send,
pim_ifp->pim_ifstat_assert_recv,
pim_ifp->pim_ifstat_assert_send,
pim_ifp->pim_ifstat_bsm_rx,
pim_ifp->pim_ifstat_bsm_tx);
}
}
if (uj)
vty_json(vty, json);
}
void pim_show_interface_traffic_single(struct pim_instance *pim,
struct vty *vty, const char *ifname,
bool uj)
{
struct interface *ifp = NULL;
struct pim_interface *pim_ifp = NULL;
json_object *json = NULL;
json_object *json_row = NULL;
uint8_t found_ifname = 0;
if (uj)
json = json_object_new_object();
else {
vty_out(vty, "\n");
vty_out(vty, "%-16s%-17s%-17s%-17s%-17s%-17s%-17s%-17s\n",
"Interface", " HELLO", " JOIN", " PRUNE",
" REGISTER", " REGISTER-STOP", " ASSERT",
" BSM");
vty_out(vty, "%-14s%-18s%-17s%-17s%-17s%-17s%-17s%-17s\n", "",
" Rx/Tx", " Rx/Tx", " Rx/Tx", " Rx/Tx",
" Rx/Tx", " Rx/Tx", " Rx/Tx");
vty_out(vty,
"-------------------------------------------------------------------------------------------------------------------------------\n");
}
FOR_ALL_INTERFACES (pim->vrf, ifp) {
if (strcmp(ifname, ifp->name))
continue;
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
found_ifname = 1;
if (uj) {
json_row = json_object_new_object();
json_object_pim_ifp_add(json_row, ifp);
json_object_int_add(json_row, "helloRx",
pim_ifp->pim_ifstat_hello_recv);
json_object_int_add(json_row, "helloTx",
pim_ifp->pim_ifstat_hello_sent);
json_object_int_add(json_row, "joinRx",
pim_ifp->pim_ifstat_join_recv);
json_object_int_add(json_row, "joinTx",
pim_ifp->pim_ifstat_join_send);
json_object_int_add(json_row, "pruneRx",
pim_ifp->pim_ifstat_prune_recv);
json_object_int_add(json_row, "pruneTx",
pim_ifp->pim_ifstat_prune_send);
json_object_int_add(json_row, "registerRx",
pim_ifp->pim_ifstat_reg_recv);
json_object_int_add(json_row, "registerTx",
pim_ifp->pim_ifstat_reg_send);
json_object_int_add(json_row, "registerStopRx",
pim_ifp->pim_ifstat_reg_stop_recv);
json_object_int_add(json_row, "registerStopTx",
pim_ifp->pim_ifstat_reg_stop_send);
json_object_int_add(json_row, "assertRx",
pim_ifp->pim_ifstat_assert_recv);
json_object_int_add(json_row, "assertTx",
pim_ifp->pim_ifstat_assert_send);
json_object_int_add(json_row, "bsmRx",
pim_ifp->pim_ifstat_bsm_rx);
json_object_int_add(json_row, "bsmTx",
pim_ifp->pim_ifstat_bsm_tx);
json_object_object_add(json, ifp->name, json_row);
} else {
vty_out(vty,
"%-16s %8u/%-8u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7u/%-7u %7" PRIu64
"/%-7" PRIu64 "\n",
ifp->name, pim_ifp->pim_ifstat_hello_recv,
pim_ifp->pim_ifstat_hello_sent,
pim_ifp->pim_ifstat_join_recv,
pim_ifp->pim_ifstat_join_send,
pim_ifp->pim_ifstat_prune_recv,
pim_ifp->pim_ifstat_prune_send,
pim_ifp->pim_ifstat_reg_recv,
pim_ifp->pim_ifstat_reg_send,
pim_ifp->pim_ifstat_reg_stop_recv,
pim_ifp->pim_ifstat_reg_stop_send,
pim_ifp->pim_ifstat_assert_recv,
pim_ifp->pim_ifstat_assert_send,
pim_ifp->pim_ifstat_bsm_rx,
pim_ifp->pim_ifstat_bsm_tx);
}
}
if (uj)
vty_json(vty, json);
else if (!found_ifname)
vty_out(vty, "%% No such interface\n");
}
int pim_show_interface_traffic_helper(const char *vrf, const char *if_name,
struct vty *vty, bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
if (if_name)
pim_show_interface_traffic_single(v->info, vty, if_name, uj);
else
pim_show_interface_traffic(v->info, vty, uj);
return CMD_SUCCESS;
}
void clear_pim_interfaces(struct pim_instance *pim)
{
struct interface *ifp;
FOR_ALL_INTERFACES (pim->vrf, ifp) {
if (ifp->info)
pim_neighbor_delete_all(ifp, "interface cleared");
}
}
void pim_show_bsr(struct pim_instance *pim, struct vty *vty, bool uj)
{
char uptime[10];
char last_bsm_seen[10];
time_t now;
char bsr_state[20];
json_object *json = NULL;
if (pim_addr_is_any(pim->global_scope.current_bsr)) {
pim_time_uptime(uptime, sizeof(uptime),
pim->global_scope.current_bsr_first_ts);
pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen),
pim->global_scope.current_bsr_last_ts);
}
else {
now = pim_time_monotonic_sec();
pim_time_uptime(uptime, sizeof(uptime),
(now - pim->global_scope.current_bsr_first_ts));
pim_time_uptime(last_bsm_seen, sizeof(last_bsm_seen),
now - pim->global_scope.current_bsr_last_ts);
}
switch (pim->global_scope.state) {
case NO_INFO:
strlcpy(bsr_state, "NO_INFO", sizeof(bsr_state));
break;
case ACCEPT_ANY:
strlcpy(bsr_state, "ACCEPT_ANY", sizeof(bsr_state));
break;
case ACCEPT_PREFERRED:
strlcpy(bsr_state, "ACCEPT_PREFERRED", sizeof(bsr_state));
break;
case BSR_PENDING:
strlcpy(bsr_state, "BSR_PENDING", sizeof(bsr_state));
break;
case BSR_ELECTED:
strlcpy(bsr_state, "BSR_ELECTED", sizeof(bsr_state));
break;
default:
strlcpy(bsr_state, "", sizeof(bsr_state));
}
if (uj) {
json = json_object_new_object();
json_object_string_addf(json, "bsr", "%pPA",
&pim->global_scope.current_bsr);
json_object_int_add(json, "priority",
pim->global_scope.current_bsr_prio);
json_object_int_add(json, "fragmentTag",
pim->global_scope.bsm_frag_tag);
json_object_string_add(json, "state", bsr_state);
json_object_string_add(json, "upTime", uptime);
json_object_string_add(json, "lastBsmSeen", last_bsm_seen);
}
else {
vty_out(vty, "PIMv2 Bootstrap Router information\n");
vty_out(vty, "Current preferred BSR address: %pPA\n",
&pim->global_scope.current_bsr);
vty_out(vty,
"Priority Fragment-Tag State UpTime\n");
vty_out(vty, " %-12d %-12d %-13s %7s\n",
pim->global_scope.current_bsr_prio,
pim->global_scope.bsm_frag_tag, bsr_state, uptime);
vty_out(vty, "Last BSM seen: %s\n", last_bsm_seen);
}
if (uj)
vty_json(vty, json);
}
int pim_show_bsr_helper(const char *vrf, struct vty *vty, bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = pim_get_pim_instance(v->vrf_id);
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_bsr(v->info, vty, uj);
return CMD_SUCCESS;
}
/*Display the group-rp mappings */
static void pim_show_group_rp_mappings_info(struct pim_instance *pim,
struct vty *vty, bool uj)
{
struct bsgrp_node *bsgrp;
struct bsm_rpinfo *bsm_rp;
struct route_node *rn;
json_object *json = NULL;
json_object *json_group = NULL;
json_object *json_row = NULL;
struct ttable *tt = NULL;
if (uj) {
json = json_object_new_object();
json_object_string_addf(json, "BSR Address", "%pPA",
&pim->global_scope.current_bsr);
} else
vty_out(vty, "BSR Address %pPA\n",
&pim->global_scope.current_bsr);
for (rn = route_top(pim->global_scope.bsrp_table); rn;
rn = route_next(rn)) {
bsgrp = (struct bsgrp_node *)rn->info;
if (!bsgrp)
continue;
char grp_str[PREFIX_STRLEN];
prefix2str(&bsgrp->group, grp_str, sizeof(grp_str));
if (uj) {
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);
}
} else {
vty_out(vty, "Group Address %pFX\n", &bsgrp->group);
vty_out(vty, "--------------------------\n");
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(tt, "Rp Address|priority|Holdtime|Hash");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
ttable_add_row(tt, "%s|%c|%c|%c", "(ACTIVE)", ' ', ' ',
' ');
}
frr_each (bsm_rpinfos, bsgrp->bsrp_list, bsm_rp) {
if (uj) {
json_row = json_object_new_object();
json_object_string_addf(json_row, "Rp Address",
"%pPA",
&bsm_rp->rp_address);
json_object_int_add(json_row, "Rp HoldTime",
bsm_rp->rp_holdtime);
json_object_int_add(json_row, "Rp Priority",
bsm_rp->rp_prio);
json_object_int_add(json_row, "Hash Val",
bsm_rp->hash);
json_object_object_addf(json_group, json_row,
"%pPA",
&bsm_rp->rp_address);
} else {
ttable_add_row(
tt, "%pPA|%u|%u|%u",
&bsm_rp->rp_address, bsm_rp->rp_prio,
bsm_rp->rp_holdtime, bsm_rp->hash);
}
}
/* Dump the generated table. */
if (tt) {
char *table = NULL;
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
tt = NULL;
}
if (!bsm_rpinfos_count(bsgrp->bsrp_list) && !uj)
vty_out(vty, "Active List is empty.\n");
if (uj) {
json_object_int_add(json_group, "Pending RP count",
bsgrp->pend_rp_cnt);
} else {
vty_out(vty, "(PENDING)\n");
vty_out(vty, "Pending RP count :%d\n",
bsgrp->pend_rp_cnt);
if (bsgrp->pend_rp_cnt) {
/* Prepare table. */
tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
ttable_add_row(
tt,
"Rp Address|priority|Holdtime|Hash");
tt->style.cell.rpad = 2;
tt->style.corner = '+';
ttable_restyle(tt);
}
}
frr_each (bsm_rpinfos, bsgrp->partial_bsrp_list, bsm_rp) {
if (uj) {
json_row = json_object_new_object();
json_object_string_addf(json_row, "Rp Address",
"%pPA",
&bsm_rp->rp_address);
json_object_int_add(json_row, "Rp HoldTime",
bsm_rp->rp_holdtime);
json_object_int_add(json_row, "Rp Priority",
bsm_rp->rp_prio);
json_object_int_add(json_row, "Hash Val",
bsm_rp->hash);
json_object_object_addf(json_group, json_row,
"%pPA",
&bsm_rp->rp_address);
} else {
ttable_add_row(
tt, "%pPA|%u|%u|%u",
&bsm_rp->rp_address, bsm_rp->rp_prio,
bsm_rp->rp_holdtime, bsm_rp->hash);
}
}
/* Dump the generated table. */
if (tt) {
char *table = NULL;
table = ttable_dump(tt, "\n");
vty_out(vty, "%s\n", table);
XFREE(MTYPE_TMP_TTABLE, table);
ttable_del(tt);
}
if (!bsm_rpinfos_count(bsgrp->partial_bsrp_list) && !uj)
vty_out(vty, "Partial List is empty\n");
if (!uj)
vty_out(vty, "\n");
}
if (uj)
vty_json(vty, json);
}
int pim_show_group_rp_mappings_info_helper(const char *vrf, struct vty *vty,
bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_group_rp_mappings_info(v->info, vty, uj);
return CMD_SUCCESS;
}
int pim_show_bsr_cand_rp(const struct vrf *vrf, struct vty *vty, bool uj)
{
struct pim_instance *pim;
struct bsm_scope *scope;
json_object *jsondata = NULL;
if (!vrf || !vrf->info)
return CMD_WARNING;
pim = (struct pim_instance *)vrf->info;
scope = &pim->global_scope;
if (!scope->cand_rp_addrsel.run) {
if (!!uj)
vty_out(vty, "{}\n");
else
vty_out(vty,
"This router is not currently operating as Candidate RP\n");
return CMD_SUCCESS;
}
if (!!uj) {
jsondata = json_object_new_object();
json_object_string_addf(jsondata, "address", "%pPA",
&scope->cand_rp_addrsel.run_addr);
json_object_int_add(jsondata, "priority", scope->cand_rp_prio);
json_object_int_add(jsondata, "nextAdvertisementMsec",
event_timer_remain_msec(
scope->cand_rp_adv_timer));
vty_json(vty, jsondata);
return CMD_SUCCESS;
}
vty_out(vty, "Candidate-RP\nAddress: %pPA\nPriority: %u\n\n",
&scope->cand_rp_addrsel.run_addr, scope->cand_rp_prio);
vty_out(vty, "Next adv.: %lu msec\n",
event_timer_remain_msec(scope->cand_rp_adv_timer));
return CMD_SUCCESS;
}
int pim_show_bsr_cand_bsr(const struct vrf *vrf, struct vty *vty, bool uj)
{
struct pim_instance *pim;
struct bsm_scope *scope;
json_object *jsondata = NULL;
if (!vrf || !vrf->info)
return CMD_WARNING;
pim = (struct pim_instance *)vrf->info;
scope = &pim->global_scope;
if (!scope->bsr_addrsel.cfg_enable) {
if (!!uj)
vty_out(vty, "{}\n");
else
vty_out(vty,
"This router is not currently operating as Candidate BSR\n");
return CMD_SUCCESS;
}
if (uj) {
char buf[INET_ADDRSTRLEN];
jsondata = json_object_new_object();
inet_ntop(AF_INET, &scope->bsr_addrsel.run_addr, buf,
sizeof(buf));
json_object_string_add(jsondata, "address", buf);
json_object_int_add(jsondata, "priority", scope->cand_bsr_prio);
json_object_boolean_add(jsondata, "elected",
pim->global_scope.state == BSR_ELECTED);
vty_json(vty, jsondata);
return CMD_SUCCESS;
}
vty_out(vty,
"Candidate-BSR\nAddress: %pPA\nPriority: %u\nElected: %s\n",
&scope->bsr_addrsel.run_addr, scope->cand_bsr_prio,
(pim->global_scope.state == BSR_ELECTED) ? " Yes" : " No");
if (!pim_addr_cmp(scope->bsr_addrsel.run_addr, PIMADDR_ANY))
vty_out(vty,
"\nThis router is not currently operating as Candidate BSR\n"
"Configure a BSR address to enable this feature\n\n");
return CMD_SUCCESS;
}
/* Display the bsm database details */
static void pim_show_bsm_db(struct pim_instance *pim, struct vty *vty, bool uj)
{
int count = 0;
int fragment = 1;
struct bsm_frag *bsfrag;
json_object *json = NULL;
json_object *json_group = NULL;
json_object *json_row = NULL;
count = bsm_frags_count(pim->global_scope.bsm_frags);
if (uj) {
json = json_object_new_object();
json_object_int_add(json, "Number of the fragments", count);
} else {
vty_out(vty, "Scope Zone: Global\n");
vty_out(vty, "Number of the fragments: %d\n", count);
vty_out(vty, "\n");
}
frr_each (bsm_frags, pim->global_scope.bsm_frags, bsfrag) {
char grp_str[PREFIX_STRLEN];
struct bsmmsg_grpinfo *group;
struct bsmmsg_rpinfo *bsm_rpinfo;
struct prefix grp;
struct bsm_hdr *hdr;
pim_addr bsr_addr;
uint32_t offset = 0;
uint8_t *buf;
uint32_t len = 0;
uint32_t frag_rp_cnt = 0;
buf = bsfrag->data;
len = bsfrag->size;
/* skip pim header */
buf += PIM_MSG_HEADER_LEN;
len -= PIM_MSG_HEADER_LEN;
hdr = (struct bsm_hdr *)buf;
/* NB: bshdr->bsr_addr.addr is packed/unaligned => memcpy */
memcpy(&bsr_addr, &hdr->bsr_addr.addr, sizeof(bsr_addr));
/* BSM starts with bsr header */
buf += sizeof(struct bsm_hdr);
len -= sizeof(struct bsm_hdr);
if (uj) {
json_object_string_addf(json, "BSR address", "%pPA",
&bsr_addr);
json_object_int_add(json, "BSR priority",
hdr->bsr_prio);
json_object_int_add(json, "Hashmask Length",
hdr->hm_len);
json_object_int_add(json, "Fragment Tag",
ntohs(hdr->frag_tag));
} else {
vty_out(vty, "BSM Fragment : %d\n", fragment);
vty_out(vty, "------------------\n");
vty_out(vty, "%-15s %-15s %-15s %-15s\n", "BSR-Address",
"BSR-Priority", "Hashmask-len", "Fragment-Tag");
vty_out(vty, "%-15pPA %-15d %-15d %-15d\n", &bsr_addr,
hdr->bsr_prio, hdr->hm_len,
ntohs(hdr->frag_tag));
}
vty_out(vty, "\n");
while (offset < len) {
group = (struct bsmmsg_grpinfo *)buf;
if (group->group.family == PIM_MSG_ADDRESS_FAMILY_IPV4)
grp.family = AF_INET;
else if (group->group.family ==
PIM_MSG_ADDRESS_FAMILY_IPV6)
grp.family = AF_INET6;
grp.prefixlen = group->group.mask;
#if PIM_IPV == 4
grp.u.prefix4 = group->group.addr;
#else
grp.u.prefix6 = group->group.addr;
#endif
prefix2str(&grp, grp_str, sizeof(grp_str));
buf += sizeof(struct bsmmsg_grpinfo);
offset += sizeof(struct bsmmsg_grpinfo);
if (uj) {
json_object_object_get_ex(json, grp_str,
&json_group);
if (!json_group) {
json_group = json_object_new_object();
json_object_int_add(json_group,
"Rp Count",
group->rp_count);
json_object_int_add(
json_group, "Fragment Rp count",
group->frag_rp_count);
json_object_object_add(json, grp_str,
json_group);
}
} else {
vty_out(vty, "Group : %s\n", grp_str);
vty_out(vty, "-------------------\n");
vty_out(vty, "Rp Count:%d\n", group->rp_count);
vty_out(vty, "Fragment Rp Count : %d\n",
group->frag_rp_count);
}
frag_rp_cnt = group->frag_rp_count;
if (!frag_rp_cnt)
continue;
if (!uj)
vty_out(vty,
"RpAddress HoldTime Priority\n");
while (frag_rp_cnt--) {
pim_addr rp_addr;
bsm_rpinfo = (struct bsmmsg_rpinfo *)buf;
/* unaligned, again */
memcpy(&rp_addr, &bsm_rpinfo->rpaddr.addr,
sizeof(rp_addr));
buf += sizeof(struct bsmmsg_rpinfo);
offset += sizeof(struct bsmmsg_rpinfo);
if (uj) {
json_row = json_object_new_object();
json_object_string_addf(
json_row, "Rp Address", "%pPA",
&rp_addr);
json_object_int_add(
json_row, "Rp HoldTime",
ntohs(bsm_rpinfo->rp_holdtime));
json_object_int_add(json_row,
"Rp Priority",
bsm_rpinfo->rp_pri);
json_object_object_addf(
json_group, json_row, "%pPA",
&rp_addr);
} else {
vty_out(vty, "%-15pPA %-12d %d\n",
&rp_addr,
ntohs(bsm_rpinfo->rp_holdtime),
bsm_rpinfo->rp_pri);
}
}
vty_out(vty, "\n");
}
fragment++;
}
if (uj)
vty_json(vty, json);
}
int pim_show_bsm_db_helper(const char *vrf, struct vty *vty, bool uj)
{
struct pim_instance *pim;
struct vrf *v;
v = vrf_lookup_by_name(vrf ? vrf : VRF_DEFAULT_NAME);
if (!v)
return CMD_WARNING;
pim = v->info;
if (!pim) {
vty_out(vty, "%% Unable to find pim instance\n");
return CMD_WARNING;
}
pim_show_bsm_db(v->info, vty, uj);
return CMD_SUCCESS;
}
int pim_router_config_write(struct vty *vty)
{
struct vrf *vrf;
struct pim_instance *pim;
int writes = 0;
char framestr[64] = { 0 };
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
pim = vrf->info;
if (!pim)
continue;
snprintfrr(framestr, sizeof(framestr), "router %s",
PIM_AF_ROUTER);
if (vrf->vrf_id != VRF_DEFAULT) {
strlcat(framestr, " vrf ", sizeof(framestr));
strlcat(framestr, vrf->name, sizeof(framestr));
}
vty_frame(vty, "%s\n", framestr);
++writes;
writes += pim_global_config_write_worker(pim, vty);
vty_endframe(vty, "exit\n");
++writes;
}
return writes;
}