mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-14 04:26:12 +00:00
pim6d: add some show commands for MLD
Just some basic show commands to get going. Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
This commit is contained in:
parent
5afe22f5bc
commit
d2951219a1
549
pimd/pim6_mld.c
549
pimd/pim6_mld.c
@ -2237,6 +2237,9 @@ void gm_ifp_update(struct interface *ifp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CLI (show commands only)
|
||||
*/
|
||||
|
||||
#include "lib/command.h"
|
||||
|
||||
@ -2244,6 +2247,548 @@ void gm_ifp_update(struct interface *ifp)
|
||||
#include "pimd/pim6_mld_clippy.c"
|
||||
#endif
|
||||
|
||||
#define MLD_STR "Multicast Listener Discovery\n"
|
||||
|
||||
static struct vrf *gm_cmd_vrf_lookup(struct vty *vty, const char *vrf_str,
|
||||
int *err)
|
||||
{
|
||||
struct vrf *ret;
|
||||
|
||||
if (!vrf_str)
|
||||
return vrf_lookup_by_id(VRF_DEFAULT);
|
||||
if (!strcmp(vrf_str, "all"))
|
||||
return NULL;
|
||||
ret = vrf_lookup_by_name(vrf_str);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vty_out(vty, "%% VRF %pSQq does not exist\n", vrf_str);
|
||||
*err = CMD_WARNING;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void gm_show_if_one_detail(struct vty *vty, struct interface *ifp)
|
||||
{
|
||||
struct pim_interface *pim_ifp = (struct pim_interface *)ifp->info;
|
||||
struct gm_if *gm_ifp;
|
||||
bool querier;
|
||||
size_t i;
|
||||
|
||||
if (!pim_ifp) {
|
||||
vty_out(vty, "Interface %s: no PIM/MLD config\n\n", ifp->name);
|
||||
return;
|
||||
}
|
||||
|
||||
gm_ifp = pim_ifp->mld;
|
||||
if (!gm_ifp) {
|
||||
vty_out(vty, "Interface %s: MLD not running\n\n", ifp->name);
|
||||
return;
|
||||
}
|
||||
|
||||
querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest);
|
||||
|
||||
vty_out(vty, "Interface %s: MLD running\n", ifp->name);
|
||||
vty_out(vty, " Uptime: %pTVMs\n", &gm_ifp->started);
|
||||
vty_out(vty, " MLD version: %d\n", gm_ifp->cur_version);
|
||||
vty_out(vty, " Querier: %pPA%s\n", &gm_ifp->querier,
|
||||
querier ? " (this system)" : "");
|
||||
vty_out(vty, " Query timer: %pTH\n", gm_ifp->t_query);
|
||||
vty_out(vty, " Other querier timer: %pTH\n",
|
||||
gm_ifp->t_other_querier);
|
||||
vty_out(vty, " Robustness value: %u\n", gm_ifp->cur_qrv);
|
||||
vty_out(vty, " Query interval: %ums\n",
|
||||
gm_ifp->cur_query_intv);
|
||||
vty_out(vty, " Query response timer: %ums\n", gm_ifp->cur_max_resp);
|
||||
vty_out(vty, " Last member query intv.: %ums\n",
|
||||
gm_ifp->cur_query_intv_trig);
|
||||
vty_out(vty, " %u expiry timers from general queries:\n",
|
||||
gm_ifp->n_pending);
|
||||
for (i = 0; i < gm_ifp->n_pending; i++) {
|
||||
struct gm_general_pending *p = &gm_ifp->pending[i];
|
||||
|
||||
vty_out(vty, " %9pTVMs ago (query) -> %9pTVMu (expiry)\n",
|
||||
&p->query, &p->expiry);
|
||||
}
|
||||
vty_out(vty, " %zu expiry timers from *,G queries\n",
|
||||
gm_grp_pends_count(gm_ifp->grp_pends));
|
||||
vty_out(vty, " %zu expiry timers from S,G queries\n",
|
||||
gm_gsq_pends_count(gm_ifp->gsq_pends));
|
||||
vty_out(vty, " %zu total *,G/S,G from %zu hosts in %zu bundles\n",
|
||||
gm_sgs_count(gm_ifp->sgs),
|
||||
gm_subscribers_count(gm_ifp->subscribers),
|
||||
gm_packet_expires_count(gm_ifp->expires));
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
|
||||
static void gm_show_if_one(struct vty *vty, struct interface *ifp,
|
||||
json_object *js_if)
|
||||
{
|
||||
struct pim_interface *pim_ifp = (struct pim_interface *)ifp->info;
|
||||
struct gm_if *gm_ifp = pim_ifp->mld;
|
||||
bool querier;
|
||||
|
||||
if (!gm_ifp) {
|
||||
if (js_if)
|
||||
json_object_string_add(js_if, "state", "down");
|
||||
else
|
||||
vty_out(vty, "%-16s %5s\n", ifp->name, "down");
|
||||
return;
|
||||
}
|
||||
|
||||
querier = IPV6_ADDR_SAME(&gm_ifp->querier, &pim_ifp->ll_lowest);
|
||||
|
||||
if (js_if) {
|
||||
json_object_string_add(js_if, "state", "up");
|
||||
json_object_string_addf(js_if, "version", "%d",
|
||||
gm_ifp->cur_version);
|
||||
json_object_string_addf(js_if, "upTime", "%pTVMs",
|
||||
&gm_ifp->started);
|
||||
json_object_boolean_add(js_if, "querier", querier);
|
||||
json_object_string_addf(js_if, "querierIp", "%pPA",
|
||||
&gm_ifp->querier);
|
||||
if (querier)
|
||||
json_object_string_addf(js_if, "queryTimer", "%pTH",
|
||||
gm_ifp->t_query);
|
||||
else
|
||||
json_object_string_addf(js_if, "otherQuerierTimer",
|
||||
"%pTH",
|
||||
gm_ifp->t_other_querier);
|
||||
} else {
|
||||
vty_out(vty, "%-16s %-5s %d %-25pPA %-5s %11pTH %pTVMs\n",
|
||||
ifp->name, "up", gm_ifp->cur_version, &gm_ifp->querier,
|
||||
querier ? "query" : "other",
|
||||
querier ? gm_ifp->t_query : gm_ifp->t_other_querier,
|
||||
&gm_ifp->started);
|
||||
}
|
||||
}
|
||||
|
||||
static void gm_show_if_vrf(struct vty *vty, struct vrf *vrf, const char *ifname,
|
||||
bool detail, json_object *js)
|
||||
{
|
||||
struct interface *ifp;
|
||||
json_object *js_vrf;
|
||||
|
||||
if (js) {
|
||||
js_vrf = json_object_new_object();
|
||||
json_object_object_add(js, vrf->name, js_vrf);
|
||||
}
|
||||
|
||||
FOR_ALL_INTERFACES (vrf, ifp) {
|
||||
json_object *js_if = NULL;
|
||||
|
||||
if (ifname && strcmp(ifp->name, ifname))
|
||||
continue;
|
||||
if (detail && !js) {
|
||||
gm_show_if_one_detail(vty, ifp);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ifp->info)
|
||||
continue;
|
||||
if (js) {
|
||||
js_if = json_object_new_object();
|
||||
json_object_object_add(js_vrf, ifp->name, js_if);
|
||||
}
|
||||
|
||||
gm_show_if_one(vty, ifp, js_if);
|
||||
}
|
||||
}
|
||||
|
||||
static void gm_show_if(struct vty *vty, struct vrf *vrf, const char *ifname,
|
||||
bool detail, json_object *js)
|
||||
{
|
||||
if (!js && !detail)
|
||||
vty_out(vty, "%-16s %-5s V %-25s %-18s %s\n", "Interface",
|
||||
"State", "Querier", "Timer", "Uptime");
|
||||
|
||||
if (vrf)
|
||||
gm_show_if_vrf(vty, vrf, ifname, detail, js);
|
||||
else
|
||||
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
|
||||
gm_show_if_vrf(vty, vrf, ifname, detail, js);
|
||||
}
|
||||
|
||||
DEFPY(gm_show_interface,
|
||||
gm_show_interface_cmd,
|
||||
"show ipv6 mld [vrf <VRF|all>$vrf_str] interface [IFNAME] [detail$detail|json$json]",
|
||||
DEBUG_STR
|
||||
SHOW_STR
|
||||
IPV6_STR
|
||||
MLD_STR
|
||||
VRF_FULL_CMD_HELP_STR
|
||||
"MLD interface information\n"
|
||||
"Detailed output\n"
|
||||
JSON_STR)
|
||||
{
|
||||
int ret = CMD_SUCCESS;
|
||||
struct vrf *vrf;
|
||||
json_object *js = NULL;
|
||||
|
||||
vrf = gm_cmd_vrf_lookup(vty, vrf_str, &ret);
|
||||
if (ret != CMD_SUCCESS)
|
||||
return ret;
|
||||
|
||||
if (json)
|
||||
js = json_object_new_object();
|
||||
gm_show_if(vty, vrf, ifname, !!detail, js);
|
||||
return vty_json(vty, js);
|
||||
}
|
||||
|
||||
static void gm_show_stats_one(struct vty *vty, struct gm_if *gm_ifp,
|
||||
json_object *js_if)
|
||||
{
|
||||
struct gm_if_stats *stats = &gm_ifp->stats;
|
||||
/* clang-format off */
|
||||
struct {
|
||||
const char *text;
|
||||
const char *js_key;
|
||||
uint64_t *val;
|
||||
} *item, items[] = {
|
||||
{ "v2 reports received", "rxV2Reports", &stats->rx_new_report },
|
||||
{ "v1 reports received", "rxV1Reports", &stats->rx_old_report },
|
||||
{ "v1 done received", "rxV1Done", &stats->rx_old_leave },
|
||||
|
||||
{ "v2 *,* queries received", "rxV2QueryGeneral", &stats->rx_query_new_general },
|
||||
{ "v2 *,G queries received", "rxV2QueryGroup", &stats->rx_query_new_group },
|
||||
{ "v2 S,G queries received", "rxV2QueryGroupSource", &stats->rx_query_new_groupsrc },
|
||||
{ "v2 S-bit queries received", "rxV2QuerySBit", &stats->rx_query_new_sbit },
|
||||
{ "v1 *,* queries received", "rxV1QueryGeneral", &stats->rx_query_old_general },
|
||||
{ "v1 *,G queries received", "rxV1QueryGroup", &stats->rx_query_old_group },
|
||||
|
||||
{ "v2 *,* queries sent", "txV2QueryGeneral", &stats->tx_query_new_general },
|
||||
{ "v2 *,G queries sent", "txV2QueryGroup", &stats->tx_query_new_group },
|
||||
{ "v2 S,G queries sent", "txV2QueryGroupSource", &stats->tx_query_new_groupsrc },
|
||||
{ "v1 *,* queries sent", "txV1QueryGeneral", &stats->tx_query_old_general },
|
||||
{ "v1 *,G queries sent", "txV1QueryGroup", &stats->tx_query_old_group },
|
||||
{ "TX errors", "txErrors", &stats->tx_query_fail },
|
||||
|
||||
{ "RX system errors", "rxErrorSys", &stats->rx_drop_sys },
|
||||
{ "RX dropped (checksum error)", "rxDropChecksum", &stats->rx_drop_csum },
|
||||
{ "RX dropped (invalid source)", "rxDropSrcAddr", &stats->rx_drop_srcaddr },
|
||||
{ "RX dropped (invalid dest.)", "rxDropDstAddr", &stats->rx_drop_dstaddr },
|
||||
{ "RX dropped (missing alert)", "rxDropRtrAlert", &stats->rx_drop_ra },
|
||||
{ "RX dropped (malformed pkt.)", "rxDropMalformed", &stats->rx_drop_malformed },
|
||||
{ "RX truncated reports", "rxTruncatedRep", &stats->rx_trunc_report },
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
for (item = items; item < items + array_size(items); item++) {
|
||||
if (js_if)
|
||||
json_object_int_add(js_if, item->js_key, *item->val);
|
||||
else
|
||||
vty_out(vty, " %-30s %" PRIu64 "\n", item->text,
|
||||
*item->val);
|
||||
}
|
||||
}
|
||||
|
||||
static void gm_show_stats_vrf(struct vty *vty, struct vrf *vrf,
|
||||
const char *ifname, json_object *js)
|
||||
{
|
||||
struct interface *ifp;
|
||||
json_object *js_vrf;
|
||||
|
||||
if (js) {
|
||||
js_vrf = json_object_new_object();
|
||||
json_object_object_add(js, vrf->name, js_vrf);
|
||||
}
|
||||
|
||||
FOR_ALL_INTERFACES (vrf, ifp) {
|
||||
struct pim_interface *pim_ifp;
|
||||
struct gm_if *gm_ifp;
|
||||
json_object *js_if = NULL;
|
||||
|
||||
if (ifname && strcmp(ifp->name, ifname))
|
||||
continue;
|
||||
|
||||
if (!ifp->info)
|
||||
continue;
|
||||
pim_ifp = ifp->info;
|
||||
if (!pim_ifp->mld)
|
||||
continue;
|
||||
gm_ifp = pim_ifp->mld;
|
||||
|
||||
if (js) {
|
||||
js_if = json_object_new_object();
|
||||
json_object_object_add(js_vrf, ifp->name, js_if);
|
||||
} else {
|
||||
vty_out(vty, "Interface: %s\n", ifp->name);
|
||||
}
|
||||
gm_show_stats_one(vty, gm_ifp, js_if);
|
||||
if (!js)
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
DEFPY(gm_show_interface_stats,
|
||||
gm_show_interface_stats_cmd,
|
||||
"show ipv6 mld [vrf <VRF|all>$vrf_str] statistics [interface IFNAME] [json$json]",
|
||||
SHOW_STR
|
||||
IPV6_STR
|
||||
MLD_STR
|
||||
VRF_FULL_CMD_HELP_STR
|
||||
"MLD statistics\n"
|
||||
INTERFACE_STR
|
||||
"Interface name\n"
|
||||
JSON_STR)
|
||||
{
|
||||
int ret = CMD_SUCCESS;
|
||||
struct vrf *vrf;
|
||||
json_object *js = NULL;
|
||||
|
||||
vrf = gm_cmd_vrf_lookup(vty, vrf_str, &ret);
|
||||
if (ret != CMD_SUCCESS)
|
||||
return ret;
|
||||
|
||||
if (json)
|
||||
js = json_object_new_object();
|
||||
|
||||
if (vrf)
|
||||
gm_show_stats_vrf(vty, vrf, ifname, js);
|
||||
else
|
||||
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
|
||||
gm_show_stats_vrf(vty, vrf, ifname, js);
|
||||
return vty_json(vty, js);
|
||||
}
|
||||
|
||||
static void gm_show_joins_one(struct vty *vty, struct gm_if *gm_ifp,
|
||||
const struct prefix_ipv6 *groups,
|
||||
const struct prefix_ipv6 *sources, bool detail,
|
||||
json_object *js_if)
|
||||
{
|
||||
struct gm_sg *sg, *sg_start;
|
||||
json_object *js_group = NULL;
|
||||
pim_addr js_grpaddr = PIMADDR_ANY;
|
||||
struct gm_subscriber sub_ref = {}, *sub_untracked;
|
||||
|
||||
if (groups) {
|
||||
struct gm_sg sg_ref = {};
|
||||
|
||||
sg_ref.sgaddr.grp = pim_addr_from_prefix(groups);
|
||||
sg_start = gm_sgs_find_gteq(gm_ifp->sgs, &sg_ref);
|
||||
} else
|
||||
sg_start = gm_sgs_first(gm_ifp->sgs);
|
||||
|
||||
sub_ref.addr = gm_dummy_untracked;
|
||||
sub_untracked = gm_subscribers_find(gm_ifp->subscribers, &sub_ref);
|
||||
/* NB: sub_untracked may be NULL if no untracked joins exist */
|
||||
|
||||
frr_each_from (gm_sgs, gm_ifp->sgs, sg, sg_start) {
|
||||
struct timeval *recent = NULL, *untracked = NULL;
|
||||
json_object *js_src;
|
||||
|
||||
if (groups) {
|
||||
struct prefix grp_p;
|
||||
|
||||
pim_addr_to_prefix(&grp_p, sg->sgaddr.grp);
|
||||
if (!prefix_match(groups, &grp_p))
|
||||
break;
|
||||
}
|
||||
|
||||
if (sources) {
|
||||
struct prefix src_p;
|
||||
|
||||
pim_addr_to_prefix(&src_p, sg->sgaddr.src);
|
||||
if (!prefix_match(sources, &src_p))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sg->most_recent) {
|
||||
struct gm_packet_state *packet;
|
||||
|
||||
packet = gm_packet_sg2state(sg->most_recent);
|
||||
recent = &packet->received;
|
||||
}
|
||||
|
||||
if (sub_untracked) {
|
||||
struct gm_packet_state *packet;
|
||||
struct gm_packet_sg *item;
|
||||
|
||||
item = gm_packet_sg_find(sg, GM_SUB_POS, sub_untracked);
|
||||
if (item) {
|
||||
packet = gm_packet_sg2state(item);
|
||||
untracked = &packet->received;
|
||||
}
|
||||
}
|
||||
|
||||
if (!js_if) {
|
||||
FMT_NSTD_BEGIN; /* %.0p */
|
||||
vty_out(vty,
|
||||
"%-30pPA %-30pPAs %-16s %10.0pTVMs %10.0pTVMs %10.0pTVMs\n",
|
||||
&sg->sgaddr.grp, &sg->sgaddr.src,
|
||||
gm_states[sg->state], recent, untracked,
|
||||
&sg->created);
|
||||
|
||||
if (!detail)
|
||||
continue;
|
||||
|
||||
struct gm_packet_sg *item;
|
||||
struct gm_packet_state *packet;
|
||||
|
||||
frr_each (gm_packet_sg_subs, sg->subs_positive, item) {
|
||||
packet = gm_packet_sg2state(item);
|
||||
|
||||
if (packet->subscriber == sub_untracked)
|
||||
continue;
|
||||
vty_out(vty, " %-58pPA %-16s %10.0pTVMs\n",
|
||||
&packet->subscriber->addr, "(JOIN)",
|
||||
&packet->received);
|
||||
}
|
||||
frr_each (gm_packet_sg_subs, sg->subs_negative, item) {
|
||||
packet = gm_packet_sg2state(item);
|
||||
|
||||
if (packet->subscriber == sub_untracked)
|
||||
continue;
|
||||
vty_out(vty, " %-58pPA %-16s %10.0pTVMs\n",
|
||||
&packet->subscriber->addr, "(PRUNE)",
|
||||
&packet->received);
|
||||
}
|
||||
FMT_NSTD_END; /* %.0p */
|
||||
continue;
|
||||
}
|
||||
/* if (js_if) */
|
||||
|
||||
if (!js_group || pim_addr_cmp(js_grpaddr, sg->sgaddr.grp)) {
|
||||
js_group = json_object_new_object();
|
||||
json_object_object_addf(js_if, js_group, "%pPA",
|
||||
&sg->sgaddr.grp);
|
||||
js_grpaddr = sg->sgaddr.grp;
|
||||
}
|
||||
|
||||
js_src = json_object_new_object();
|
||||
json_object_object_addf(js_group, js_src, "%pPA",
|
||||
&sg->sgaddr.src);
|
||||
|
||||
json_object_string_add(js_src, "state", gm_states[sg->state]);
|
||||
json_object_string_addf(js_src, "created", "%pTVMs",
|
||||
&sg->created);
|
||||
json_object_string_addf(js_src, "lastSeen", "%pTVMs", recent);
|
||||
|
||||
if (untracked)
|
||||
json_object_string_addf(js_src, "untrackedLastSeen",
|
||||
"%pTVMs", untracked);
|
||||
if (!detail)
|
||||
continue;
|
||||
|
||||
json_object *js_subs;
|
||||
struct gm_packet_sg *item;
|
||||
struct gm_packet_state *packet;
|
||||
|
||||
js_subs = json_object_new_object();
|
||||
json_object_object_add(js_src, "joinedBy", js_subs);
|
||||
frr_each (gm_packet_sg_subs, sg->subs_positive, item) {
|
||||
packet = gm_packet_sg2state(item);
|
||||
if (packet->subscriber == sub_untracked)
|
||||
continue;
|
||||
|
||||
json_object *js_sub;
|
||||
|
||||
js_sub = json_object_new_object();
|
||||
json_object_object_addf(js_subs, js_sub, "%pPA",
|
||||
&packet->subscriber->addr);
|
||||
json_object_string_addf(js_sub, "lastSeen", "%pTVMs",
|
||||
&packet->received);
|
||||
}
|
||||
|
||||
js_subs = json_object_new_object();
|
||||
json_object_object_add(js_src, "prunedBy", js_subs);
|
||||
frr_each (gm_packet_sg_subs, sg->subs_negative, item) {
|
||||
packet = gm_packet_sg2state(item);
|
||||
if (packet->subscriber == sub_untracked)
|
||||
continue;
|
||||
|
||||
json_object *js_sub;
|
||||
|
||||
js_sub = json_object_new_object();
|
||||
json_object_object_addf(js_subs, js_sub, "%pPA",
|
||||
&packet->subscriber->addr);
|
||||
json_object_string_addf(js_sub, "lastSeen", "%pTVMs",
|
||||
&packet->received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gm_show_joins_vrf(struct vty *vty, struct vrf *vrf,
|
||||
const char *ifname,
|
||||
const struct prefix_ipv6 *groups,
|
||||
const struct prefix_ipv6 *sources, bool detail,
|
||||
json_object *js)
|
||||
{
|
||||
struct interface *ifp;
|
||||
json_object *js_vrf;
|
||||
|
||||
if (js) {
|
||||
js_vrf = json_object_new_object();
|
||||
json_object_object_add(js, vrf->name, js_vrf);
|
||||
}
|
||||
|
||||
FOR_ALL_INTERFACES (vrf, ifp) {
|
||||
struct pim_interface *pim_ifp;
|
||||
struct gm_if *gm_ifp;
|
||||
json_object *js_if = NULL;
|
||||
|
||||
if (ifname && strcmp(ifp->name, ifname))
|
||||
continue;
|
||||
|
||||
if (!ifp->info)
|
||||
continue;
|
||||
pim_ifp = ifp->info;
|
||||
if (!pim_ifp->mld)
|
||||
continue;
|
||||
gm_ifp = pim_ifp->mld;
|
||||
|
||||
if (js) {
|
||||
js_if = json_object_new_object();
|
||||
json_object_object_add(js_vrf, ifp->name, js_if);
|
||||
}
|
||||
|
||||
if (!js && !ifname)
|
||||
vty_out(vty, "\nOn interface %s:\n", ifp->name);
|
||||
|
||||
gm_show_joins_one(vty, gm_ifp, groups, sources, detail, js_if);
|
||||
}
|
||||
}
|
||||
|
||||
DEFPY(gm_show_interface_joins,
|
||||
gm_show_interface_joins_cmd,
|
||||
"show ipv6 mld [vrf <VRF|all>$vrf_str] joins [{interface IFNAME|groups X:X::X:X/M|sources X:X::X:X/M|detail$detail}] [json$json]",
|
||||
SHOW_STR
|
||||
IPV6_STR
|
||||
MLD_STR
|
||||
VRF_FULL_CMD_HELP_STR
|
||||
"MLD joined groups & sources\n"
|
||||
INTERFACE_STR
|
||||
"Interface name\n"
|
||||
"Limit output to group range\n"
|
||||
"Show groups covered by this prefix\n"
|
||||
"Limit output to source range\n"
|
||||
"Show sources covered by this prefix\n"
|
||||
"Show details, including tracked receivers\n"
|
||||
JSON_STR)
|
||||
{
|
||||
int ret = CMD_SUCCESS;
|
||||
struct vrf *vrf;
|
||||
json_object *js = NULL;
|
||||
|
||||
vrf = gm_cmd_vrf_lookup(vty, vrf_str, &ret);
|
||||
if (ret != CMD_SUCCESS)
|
||||
return ret;
|
||||
|
||||
if (json)
|
||||
js = json_object_new_object();
|
||||
else
|
||||
vty_out(vty, "%-30s %-30s %-16s %10s %10s %10s\n", "Group",
|
||||
"Source", "State", "LastSeen", "NonTrkSeen", "Created");
|
||||
|
||||
if (vrf)
|
||||
gm_show_joins_vrf(vty, vrf, ifname, groups, sources, !!detail,
|
||||
js);
|
||||
else
|
||||
RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
|
||||
gm_show_joins_vrf(vty, vrf, ifname, groups, sources,
|
||||
!!detail, js);
|
||||
return vty_json(vty, js);
|
||||
}
|
||||
|
||||
DEFPY(gm_debug_show,
|
||||
gm_debug_show_cmd,
|
||||
"debug show mld interface IFNAME",
|
||||
@ -2416,6 +2961,10 @@ void gm_cli_init(void);
|
||||
|
||||
void gm_cli_init(void)
|
||||
{
|
||||
install_element(VIEW_NODE, &gm_show_interface_cmd);
|
||||
install_element(VIEW_NODE, &gm_show_interface_stats_cmd);
|
||||
install_element(VIEW_NODE, &gm_show_interface_joins_cmd);
|
||||
|
||||
install_element(VIEW_NODE, &gm_debug_show_cmd);
|
||||
install_element(INTERFACE_NODE, &gm_debug_iface_cfg_cmd);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user