frr/pimd/pim_cmd_common.c

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

1528 lines
42 KiB
C
Raw Normal View History

/*
* PIM for IPv6 FRR
* Copyright (C) 2022 Vmware, Inc.
* Mobashshera Rasool <mrasool@vmware.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "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 "pimd.h"
#include "pim_vty.h"
#include "lib/northbound_cli.h"
#include "pim_errors.h"
#include "pim_nb.h"
#include "pim_cmd_common.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 "lib/linklist.h"
#include "pim_neighbor.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)
{
const char *vrfname;
char spt_plist_xpath[XPATH_MAXLEN];
char spt_action_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(spt_plist_xpath, sizeof(spt_plist_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(spt_plist_xpath, "/spt-switchover/spt-infinity-prefix-list",
sizeof(spt_plist_xpath));
snprintf(spt_action_xpath, sizeof(spt_action_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(spt_action_xpath, "/spt-switchover/spt-action",
sizeof(spt_action_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)
{
const char *vrfname;
char spt_plist_xpath[XPATH_MAXLEN];
char spt_action_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(spt_plist_xpath, sizeof(spt_plist_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(spt_plist_xpath, "/spt-switchover/spt-infinity-prefix-list",
sizeof(spt_plist_xpath));
snprintf(spt_action_xpath, sizeof(spt_action_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(spt_action_xpath, "/spt-switchover/spt-action",
sizeof(spt_action_xpath));
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)
{
const char *vrfname;
char spt_plist_xpath[XPATH_MAXLEN];
char spt_action_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(spt_plist_xpath, sizeof(spt_plist_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(spt_plist_xpath, "/spt-switchover/spt-infinity-prefix-list",
sizeof(spt_plist_xpath));
snprintf(spt_action_xpath, sizeof(spt_action_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(spt_action_xpath, "/spt-switchover/spt-action",
sizeof(spt_action_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_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)
{
const char *vrfname;
char ka_timer_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(ka_timer_xpath, sizeof(ka_timer_xpath), FRR_PIM_VRF_XPATH,
"frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
strlcat(ka_timer_xpath, "/keep-alive-timer", sizeof(ka_timer_xpath));
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)
{
const char *vrfname;
char ka_timer_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(ka_timer_xpath, sizeof(ka_timer_xpath), FRR_PIM_VRF_XPATH,
"frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL);
strlcat(ka_timer_xpath, "/keep-alive-timer", sizeof(ka_timer_xpath));
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)
{
const char *vrfname;
char rp_ka_timer_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(rp_ka_timer_xpath, sizeof(rp_ka_timer_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(rp_ka_timer_xpath, "/rp-keep-alive-timer",
sizeof(rp_ka_timer_xpath));
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)
{
const char *vrfname;
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 */
v = yang_dnode_get_uint16(vty->candidate_config->dnode,
rs_timer_xpath);
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);
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(rp_ka_timer_xpath, sizeof(rp_ka_timer_xpath),
FRR_PIM_VRF_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL);
strlcat(rp_ka_timer_xpath, "/rp-keep-alive-timer",
sizeof(rp_ka_timer_xpath));
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_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_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)
{
const char *vrfname;
char rp_group_xpath[XPATH_MAXLEN];
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;
}
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(rp_group_xpath, sizeof(rp_group_xpath),
FRR_PIM_STATIC_RP_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL, rp_str);
strlcat(rp_group_xpath, "/group-list", sizeof(rp_group_xpath));
nb_cli_enqueue_change(vty, rp_group_xpath, NB_OP_CREATE, group_str);
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_list_xpath[XPATH_MAXLEN];
char group_xpath[XPATH_MAXLEN];
char rp_xpath[XPATH_MAXLEN];
int printed;
const char *vrfname;
const struct lyd_node *group_dnode;
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH,
"frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str);
printed = snprintf(group_list_xpath, sizeof(group_list_xpath),
"%s/group-list", rp_xpath);
if (printed >= (int)(sizeof(group_list_xpath))) {
vty_out(vty, "Xpath too long (%d > %u)", printed + 1,
XPATH_MAXLEN);
return CMD_WARNING_CONFIG_FAILED;
}
printed = snprintf(group_xpath, sizeof(group_xpath), "%s[.='%s']",
group_list_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_list_xpath, NB_OP_DESTROY,
group_str);
return nb_cli_apply_changes(vty, NULL);
}
int pim_process_rp_plist_cmd(struct vty *vty, const char *rp_str,
const char *prefix_list)
{
const char *vrfname;
char rp_plist_xpath[XPATH_MAXLEN];
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(rp_plist_xpath, sizeof(rp_plist_xpath),
FRR_PIM_STATIC_RP_XPATH, "frr-pim:pimd", "pim", vrfname,
FRR_PIM_AF_XPATH_VAL, 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];
char plist_xpath[XPATH_MAXLEN];
const char *vrfname;
const struct lyd_node *plist_dnode;
const char *plist;
vrfname = pim_cli_get_vrf_name(vty);
if (vrfname == NULL)
return CMD_WARNING_CONFIG_FAILED;
snprintf(rp_xpath, sizeof(rp_xpath), FRR_PIM_STATIC_RP_XPATH,
"frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str);
snprintf(plist_xpath, sizeof(plist_xpath), FRR_PIM_STATIC_RP_XPATH,
"frr-pim:pimd", "pim", vrfname, FRR_PIM_AF_XPATH_VAL, rp_str);
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, plist_xpath);
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);
}
void json_object_pim_upstream_add(json_object *json, struct pim_upstream *up)
{
if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
json_object_boolean_true_add(json, "drJoinDesired");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
json_object_boolean_true_add(json, "drJoinDesiredUpdated");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_FHR)
json_object_boolean_true_add(json, "firstHopRouter");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
json_object_boolean_true_add(json, "sourceIgmp");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
json_object_boolean_true_add(json, "sourcePim");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_STREAM)
json_object_boolean_true_add(json, "sourceStream");
/* XXX: need to print ths flag in the plain text display as well */
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_MSDP)
json_object_boolean_true_add(json, "sourceMsdp");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SEND_SG_RPT_PRUNE)
json_object_boolean_true_add(json, "sendSGRptPrune");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)
json_object_boolean_true_add(json, "lastHopRouter");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_DISABLE_KAT_EXPIRY)
json_object_boolean_true_add(json, "disableKATExpiry");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_STATIC_IIF)
json_object_boolean_true_add(json, "staticIncomingInterface");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_ALLOW_IIF_IN_OIL)
json_object_boolean_true_add(json,
"allowIncomingInterfaceinOil");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_NO_PIMREG_DATA)
json_object_boolean_true_add(json, "noPimRegistrationData");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_FORCE_PIMREG)
json_object_boolean_true_add(json, "forcePimRegistration");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_ORIG)
json_object_boolean_true_add(json, "sourceVxlanOrigination");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)
json_object_boolean_true_add(json, "sourceVxlanTermination");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
json_object_boolean_true_add(json, "mlagVxlan");
if (up->flags & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
json_object_boolean_true_add(json,
"mlagNonDesignatedForwarder");
}
static const char *
pim_upstream_state2brief_str(enum pim_upstream_state join_state,
char *state_str, size_t state_str_len)
{
switch (join_state) {
case PIM_UPSTREAM_NOTJOINED:
strlcpy(state_str, "NotJ", state_str_len);
break;
case PIM_UPSTREAM_JOINED:
strlcpy(state_str, "J", state_str_len);
break;
default:
strlcpy(state_str, "Unk", state_str_len);
}
return state_str;
}
static const char *pim_reg_state2brief_str(enum pim_reg_state reg_state,
char *state_str,
size_t state_str_len)
{
switch (reg_state) {
case PIM_REG_NOINFO:
strlcpy(state_str, "RegNI", state_str_len);
break;
case PIM_REG_JOIN:
strlcpy(state_str, "RegJ", state_str_len);
break;
case PIM_REG_JOIN_PENDING:
case PIM_REG_PRUNE:
strlcpy(state_str, "RegP", state_str_len);
break;
}
return state_str;
}
void pim_show_rpf_refresh_stats(struct vty *vty, struct pim_instance *pim,
time_t now, json_object *json)
{
char refresh_uptime[10];
pim_time_uptime_begin(refresh_uptime, sizeof(refresh_uptime), now,
pim->rpf_cache_refresh_last);
if (json) {
json_object_int_add(json, "rpfCacheRefreshDelayMsecs",
router->rpf_cache_refresh_delay_msec);
json_object_int_add(
json, "rpfCacheRefreshTimer",
pim_time_timer_remain_msec(pim->rpf_cache_refresher));
json_object_int_add(json, "rpfCacheRefreshRequests",
pim->rpf_cache_refresh_requests);
json_object_int_add(json, "rpfCacheRefreshEvents",
pim->rpf_cache_refresh_events);
json_object_string_add(json, "rpfCacheRefreshLast",
refresh_uptime);
json_object_int_add(json, "nexthopLookups",
pim->nexthop_lookups);
json_object_int_add(json, "nexthopLookupsAvoided",
pim->nexthop_lookups_avoided);
} else {
vty_out(vty,
"RPF Cache Refresh Delay: %ld msecs\n"
"RPF Cache Refresh Timer: %ld msecs\n"
"RPF Cache Refresh Requests: %lld\n"
"RPF Cache Refresh Events: %lld\n"
"RPF Cache Refresh Last: %s\n"
"Nexthop Lookups: %lld\n"
"Nexthop Lookups Avoided: %lld\n",
router->rpf_cache_refresh_delay_msec,
pim_time_timer_remain_msec(pim->rpf_cache_refresher),
(long long)pim->rpf_cache_refresh_requests,
(long long)pim->rpf_cache_refresh_events,
refresh_uptime, (long long)pim->nexthop_lookups,
(long long)pim->nexthop_lookups_avoided);
}
}
void pim_show_rpf(struct pim_instance *pim, struct vty *vty, json_object *json)
{
struct pim_upstream *up;
time_t now = pim_time_monotonic_sec();
json_object *json_group = NULL;
json_object *json_row = NULL;
pim_show_rpf_refresh_stats(vty, pim, now, json);
if (!json) {
vty_out(vty, "\n");
vty_out(vty,
"Source Group RpfIface RpfAddress RibNextHop Metric Pref\n");
}
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
char rpf_addr_str[PREFIX_STRLEN];
char rib_nexthop_str[PREFIX_STRLEN];
const char *rpf_ifname;
struct pim_rpf *rpf = &up->rpf;
pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str,
sizeof(rpf_addr_str));
pim_addr_dump("<nexthop?>",
&rpf->source_nexthop.mrib_nexthop_addr,
rib_nexthop_str, sizeof(rib_nexthop_str));
rpf_ifname =
rpf->source_nexthop.interface ? rpf->source_nexthop
.interface->name
: "<ifname?>";
if (json) {
char grp_str[PIM_ADDRSTRLEN];
char src_str[PIM_ADDRSTRLEN];
snprintfrr(grp_str, sizeof(grp_str), "%pPAs",
&up->sg.grp);
snprintfrr(src_str, sizeof(src_str), "%pPAs",
&up->sg.src);
json_object_object_get_ex(json, grp_str, &json_group);
if (!json_group) {
json_group = json_object_new_object();
json_object_object_add(json, grp_str,
json_group);
}
json_row = json_object_new_object();
json_object_string_add(json_row, "source", src_str);
json_object_string_add(json_row, "group", grp_str);
json_object_string_add(json_row, "rpfInterface",
rpf_ifname);
json_object_string_add(json_row, "rpfAddress",
rpf_addr_str);
json_object_string_add(json_row, "ribNexthop",
rib_nexthop_str);
json_object_int_add(
json_row, "routeMetric",
rpf->source_nexthop.mrib_route_metric);
json_object_int_add(
json_row, "routePreference",
rpf->source_nexthop.mrib_metric_preference);
json_object_object_add(json_group, src_str, json_row);
} else {
vty_out(vty,
"%-15pPAs %-15pPAs %-16s %-15s %-15s %6d %4d\n",
&up->sg.src, &up->sg.grp, rpf_ifname,
rpf_addr_str, rib_nexthop_str,
rpf->source_nexthop.mrib_route_metric,
rpf->source_nexthop.mrib_metric_preference);
}
}
}
void pim_show_neighbors_secondary(struct pim_instance *pim, struct vty *vty)
{
struct interface *ifp;
vty_out(vty,
"Interface Address Neighbor Secondary \n");
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp;
pim_addr ifaddr;
struct listnode *neighnode;
struct pim_neighbor *neigh;
pim_ifp = ifp->info;
if (!pim_ifp)
continue;
if (pim_ifp->pim_sock_fd < 0)
continue;
ifaddr = pim_ifp->primary_address;
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, neighnode,
neigh)) {
struct listnode *prefix_node;
struct prefix *p;
if (!neigh->prefix_list)
continue;
for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list,
prefix_node, p))
vty_out(vty,
"%-16s %-15pPAs %-15pPAs %-15pFX\n",
ifp->name, &ifaddr, &neigh->source_addr,
p);
}
}
}
void pim_show_state(struct pim_instance *pim, struct vty *vty,
const char *src_or_group, const char *group, bool uj)
{
struct channel_oil *c_oil;
json_object *json = NULL;
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 (uj) {
json = json_object_new_object();
} else {
vty_out(vty,
"Codes: J -> Pim Join, I -> IGMP Report, S -> Source, * -> Inherited from (*,G), V -> VxLAN, M -> Muted");
vty_out(vty,
"\nActive Source Group RPT IIF OIL\n");
}
frr_each (rb_pim_oil, &pim->channel_oil_head, c_oil) {
char grp_str[INET_ADDRSTRLEN];
char src_str[INET_ADDRSTRLEN];
char in_ifname[INTERFACE_NAMSIZ + 1];
char out_ifname[INTERFACE_NAMSIZ + 1];
int oif_vif_index;
struct interface *ifp_in;
bool isRpt;
first_oif = 1;
if ((c_oil->up &&
PIM_UPSTREAM_FLAG_TEST_USE_RPT(c_oil->up->flags)) ||
c_oil->oil.mfcc_origin.s_addr == INADDR_ANY)
isRpt = true;
else
isRpt = false;
pim_inet4_dump("<group?>", c_oil->oil.mfcc_mcastgrp, grp_str,
sizeof(grp_str));
pim_inet4_dump("<source?>", c_oil->oil.mfcc_origin, src_str,
sizeof(src_str));
ifp_in = pim_if_find_by_vif_index(pim, c_oil->oil.mfcc_parent);
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 (uj) {
/* Find the group, create it if it doesn't exist */
json_object_object_get_ex(json, grp_str, &json_group);
if (!json_group) {
json_group = json_object_new_object();
json_object_object_add(json, grp_str,
json_group);
}
/* Find the source nested under the group, create it if
* it doesn't exist
*/
json_object_object_get_ex(json_group, src_str,
&json_source);
if (!json_source) {
json_source = json_object_new_object();
json_object_object_add(json_group, src_str,
json_source);
}
/* Find the inbound interface nested under the source,
* create it if it doesn't exist
*/
json_object_object_get_ex(json_source, in_ifname,
&json_ifp_in);
if (!json_ifp_in) {
json_ifp_in = json_object_new_object();
json_object_object_add(json_source, in_ifname,
json_ifp_in);
json_object_int_add(json_source, "Installed",
c_oil->installed);
json_object_int_add(json_source, "installed",
c_oil->installed);
if (isRpt)
json_object_boolean_true_add(
json_source, "isRpt");
else
json_object_boolean_false_add(
json_source, "isRpt");
json_object_int_add(json_source, "RefCount",
c_oil->oil_ref_count);
json_object_int_add(json_source, "refCount",
c_oil->oil_ref_count);
json_object_int_add(json_source, "OilListSize",
c_oil->oil_size);
json_object_int_add(json_source, "oilListSize",
c_oil->oil_size);
json_object_int_add(
json_source, "OilRescan",
c_oil->oil_inherited_rescan);
json_object_int_add(
json_source, "oilRescan",
c_oil->oil_inherited_rescan);
json_object_int_add(json_source, "LastUsed",
c_oil->cc.lastused);
json_object_int_add(json_source, "lastUsed",
c_oil->cc.lastused);
json_object_int_add(json_source, "PacketCount",
c_oil->cc.pktcnt);
json_object_int_add(json_source, "packetCount",
c_oil->cc.pktcnt);
json_object_int_add(json_source, "ByteCount",
c_oil->cc.bytecnt);
json_object_int_add(json_source, "byteCount",
c_oil->cc.bytecnt);
json_object_int_add(json_source,
"WrongInterface",
c_oil->cc.wrong_if);
json_object_int_add(json_source,
"wrongInterface",
c_oil->cc.wrong_if);
}
} else {
vty_out(vty, "%-6d %-15s %-15s %-3s %-16s ",
c_oil->installed, src_str, grp_str,
isRpt ? "y" : "n", in_ifname);
}
for (oif_vif_index = 0; oif_vif_index < MAXVIFS;
++oif_vif_index) {
struct interface *ifp_out;
char oif_uptime[10];
int ttl;
ttl = c_oil->oil.mfcc_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 - 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 (uj) {
json_ifp_out = json_object_new_object();
json_object_string_add(json_ifp_out, "source",
src_str);
json_object_string_add(json_ifp_out, "group",
grp_str);
json_object_string_add(json_ifp_out,
"inboundInterface",
in_ifname);
json_object_string_add(json_ifp_out,
"outboundInterface",
out_ifname);
json_object_int_add(json_ifp_out, "installed",
c_oil->installed);
json_object_object_add(json_ifp_in, out_ifname,
json_ifp_out);
} else {
if (first_oif) {
first_oif = 0;
vty_out(vty, "%s(%c%c%c%c%c)",
out_ifname,
(c_oil->oif_flags
[oif_vif_index] &
PIM_OIF_FLAG_PROTO_IGMP)
? 'I'
: ' ',
(c_oil->oif_flags
[oif_vif_index] &
PIM_OIF_FLAG_PROTO_PIM)
? 'J'
: ' ',
(c_oil->oif_flags
[oif_vif_index] &
PIM_OIF_FLAG_PROTO_VXLAN)
? 'V'
: ' ',
(c_oil->oif_flags
[oif_vif_index] &
PIM_OIF_FLAG_PROTO_STAR)
? '*'
: ' ',
(c_oil->oif_flags
[oif_vif_index] &
PIM_OIF_FLAG_MUTE)
? 'M'
: ' ');
} else
vty_out(vty, ", %s(%c%c%c%c%c)",
out_ifname,
(c_oil->oif_flags
[oif_vif_index] &
PIM_OIF_FLAG_PROTO_IGMP)
? '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 (!uj)
vty_out(vty, "\n");
}
if (uj)
vty_json(vty, json);
else
vty_out(vty, "\n");
}
/* pim statistics - just adding only bsm related now.
* We can continue to add all pim related stats here.
*/
void pim_show_statistics(struct pim_instance *pim, struct vty *vty,
const char *ifname, bool uj)
{
json_object *json = NULL;
struct interface *ifp;
if (uj) {
json = json_object_new_object();
json_object_int_add(json, "bsmRx", pim->bsm_rcvd);
json_object_int_add(json, "bsmTx", pim->bsm_sent);
json_object_int_add(json, "bsmDropped", pim->bsm_dropped);
} else {
vty_out(vty, "BSM Statistics :\n");
vty_out(vty, "----------------\n");
vty_out(vty, "Number of Received BSMs : %" PRIu64 "\n",
pim->bsm_rcvd);
vty_out(vty, "Number of Forwared BSMs : %" PRIu64 "\n",
pim->bsm_sent);
vty_out(vty, "Number of Dropped BSMs : %" PRIu64 "\n",
pim->bsm_dropped);
}
vty_out(vty, "\n");
/* scan interfaces */
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp = ifp->info;
if (ifname && strcmp(ifname, ifp->name))
continue;
if (!pim_ifp)
continue;
if (!uj) {
vty_out(vty, "Interface : %s\n", ifp->name);
vty_out(vty, "-------------------\n");
vty_out(vty,
"Number of BSMs dropped due to config miss : %u\n",
pim_ifp->pim_ifstat_bsm_cfg_miss);
vty_out(vty, "Number of unicast BSMs dropped : %u\n",
pim_ifp->pim_ifstat_ucast_bsm_cfg_miss);
vty_out(vty,
"Number of BSMs dropped due to invalid scope zone : %u\n",
pim_ifp->pim_ifstat_bsm_invalid_sz);
} else {
json_object *json_row = NULL;
json_row = json_object_new_object();
json_object_string_add(json_row, "If Name", ifp->name);
json_object_int_add(json_row, "bsmDroppedConfig",
pim_ifp->pim_ifstat_bsm_cfg_miss);
json_object_int_add(
json_row, "bsmDroppedUnicast",
pim_ifp->pim_ifstat_ucast_bsm_cfg_miss);
json_object_int_add(json_row,
"bsmDroppedInvalidScopeZone",
pim_ifp->pim_ifstat_bsm_invalid_sz);
json_object_object_add(json, ifp->name, json_row);
}
vty_out(vty, "\n");
}
if (uj)
vty_json(vty, json);
}
void pim_show_upstream(struct pim_instance *pim, struct vty *vty,
pim_sgaddr *sg, bool uj)
{
struct pim_upstream *up;
time_t now;
json_object *json = NULL;
json_object *json_group = NULL;
json_object *json_row = NULL;
now = pim_time_monotonic_sec();
if (uj)
json = json_object_new_object();
else
vty_out(vty,
"Iif Source Group State Uptime JoinTimer RSTimer KATimer RefCnt\n");
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
char src_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
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 (sg->grp.s_addr != INADDR_ANY &&
sg->grp.s_addr != up->sg.grp.s_addr)
continue;
if (sg->src.s_addr != INADDR_ANY &&
sg->src.s_addr != up->sg.src.s_addr)
continue;
pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
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.u.prefix4);
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 (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);
}
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 ||
up->sg.src.s_addr == INADDR_ANY) {
char rpf[PREFIX_STRLEN];
struct pim_rpf *rpg;
rpg = RP(pim, up->sg.grp);
pim_inet4_dump("<rpf?>",
rpg->rpf_addr.u.prefix4, rpf,
sizeof(rpf));
json_object_string_add(json_row, "rpfAddress",
rpf);
} else {
json_object_string_add(json_row, "rpfAddress",
src_str);
}
json_object_string_add(json_row, "source", src_str);
json_object_string_add(json_row, "group", grp_str);
json_object_string_add(json_row, "state", state_str);
json_object_string_add(
json_row, "joinState",
pim_upstream_state2str(up->join_state));
json_object_string_add(
json_row, "regState",
pim_reg_state2str(up->reg_state, state_str,
sizeof(state_str)));
json_object_string_add(json_row, "upTime", uptime);
json_object_string_add(json_row, "joinTimer",
join_timer);
json_object_string_add(json_row, "resetTimer",
rs_timer);
json_object_string_add(json_row, "keepaliveTimer",
ka_timer);
json_object_string_add(json_row, "msdpRegTimer",
msdp_reg_timer);
json_object_int_add(json_row, "refCount",
up->ref_count);
json_object_int_add(json_row, "sptBit", up->sptbit);
json_object_object_add(json_group, src_str, json_row);
} else {
vty_out(vty,
"%-16s%-15s %-15s %-11s %-8s %-9s %-9s %-9s %6d\n",
up->rpf.source_nexthop.interface
? up->rpf.source_nexthop.interface->name
: "Unknown",
src_str, grp_str, state_str, uptime, join_timer,
rs_timer, ka_timer, up->ref_count);
}
}
if (uj)
vty_json(vty, json);
}
static void pim_show_join_desired_helper(struct pim_instance *pim,
struct vty *vty,
struct pim_upstream *up,
json_object *json, bool uj)
{
json_object *json_group = NULL;
char src_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
json_object *json_row = NULL;
pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->sg.grp, 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);
}
json_row = json_object_new_object();
json_object_pim_upstream_add(json_row, up);
json_object_string_add(json_row, "source", src_str);
json_object_string_add(json_row, "group", grp_str);
if (pim_upstream_evaluate_join_desired(pim, up))
json_object_boolean_true_add(json_row,
"evaluateJoinDesired");
json_object_object_add(json_group, src_str, json_row);
} else {
vty_out(vty, "%-15s %-15s %-6s\n", src_str, grp_str,
pim_upstream_evaluate_join_desired(pim, up) ? "yes"
: "no");
}
}
void pim_show_join_desired(struct pim_instance *pim, struct vty *vty, bool uj)
{
struct pim_upstream *up;
json_object *json = NULL;
if (uj)
json = json_object_new_object();
else
vty_out(vty, "Source Group EvalJD\n");
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
/* scan all interfaces */
pim_show_join_desired_helper(pim, vty, up, json, uj);
}
if (uj)
vty_json(vty, json);
}
void pim_show_upstream_rpf(struct pim_instance *pim, struct vty *vty, bool uj)
{
struct pim_upstream *up;
json_object *json = NULL;
json_object *json_group = NULL;
json_object *json_row = NULL;
if (uj)
json = json_object_new_object();
else
vty_out(vty,
"Source Group RpfIface RibNextHop RpfAddress \n");
frr_each (rb_pim_upstream, &pim->upstream_head, up) {
char src_str[INET_ADDRSTRLEN];
char grp_str[INET_ADDRSTRLEN];
char rpf_nexthop_str[PREFIX_STRLEN];
char rpf_addr_str[PREFIX_STRLEN];
struct pim_rpf *rpf;
const char *rpf_ifname;
rpf = &up->rpf;
pim_inet4_dump("<src?>", up->sg.src, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
pim_addr_dump("<nexthop?>",
&rpf->source_nexthop.mrib_nexthop_addr,
rpf_nexthop_str, sizeof(rpf_nexthop_str));
pim_addr_dump("<rpf?>", &rpf->rpf_addr, rpf_addr_str,
sizeof(rpf_addr_str));
rpf_ifname =
rpf->source_nexthop.interface ? rpf->source_nexthop
.interface->name
: "<ifname?>";
if (uj) {
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_add(json_row, "ribNexthop",
rpf_nexthop_str);
json_object_string_add(json_row, "rpfAddress",
rpf_addr_str);
json_object_object_add(json_group, src_str, json_row);
} else {
vty_out(vty, "%-15s %-15s %-16s %-15s %-15s\n", src_str,
grp_str, rpf_ifname, rpf_nexthop_str,
rpf_addr_str);
}
}
if (uj)
vty_json(vty, json);
}