diff --git a/pimd/COMMANDS b/pimd/COMMANDS index 6f2e020bd8..141ec62aeb 100644 --- a/pimd/COMMANDS +++ b/pimd/COMMANDS @@ -24,6 +24,7 @@ verification commands: show ip igmp groups retransmissions IGMP group retransmission show ip igmp sources IGMP sources information show ip igmp sources retransmissions IGMP source retransmission + show ip igmp statistics IGMP statistics information show ip pim address PIM interface address show ip pim assert PIM interface assert show ip pim assert-internal PIM interface internal assert state diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index 35514a85da..81191eb96c 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -1294,6 +1294,76 @@ static void pim_show_interfaces_single(struct pim_instance *pim, } } +static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty, + const char *ifname, uint8_t uj) +{ + struct interface *ifp; + struct igmp_stats rx_stats; + + igmp_stats_init(&rx_stats); + + FOR_ALL_INTERFACES (pim->vrf, ifp) { + struct pim_interface *pim_ifp; + struct listnode *sock_node; + struct igmp_sock *igmp; + + pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (ifname && strcmp(ifname, ifp->name)) + continue; + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, + igmp)) { + igmp_stats_add(&rx_stats, &igmp->rx_stats); + } + } + if (uj) { + json_object *json = NULL; + json_object *json_row = NULL; + + json = json_object_new_object(); + json_row = json_object_new_object(); + + json_object_string_add(json_row, "name", ifname ? ifname : + "global"); + json_object_int_add(json_row, "queryV1", rx_stats.query_v1); + json_object_int_add(json_row, "queryV2", rx_stats.query_v2); + json_object_int_add(json_row, "queryV3", rx_stats.query_v3); + json_object_int_add(json_row, "leaveV3", rx_stats.leave_v2); + json_object_int_add(json_row, "reportV1", rx_stats.report_v1); + json_object_int_add(json_row, "reportV2", rx_stats.report_v2); + json_object_int_add(json_row, "reportV3", rx_stats.report_v3); + json_object_int_add(json_row, "mtraceResponse", + rx_stats.mtrace_rsp); + json_object_int_add(json_row, "mtraceRequest", + rx_stats.mtrace_req); + json_object_int_add(json_row, "unsupported", + rx_stats.unsupported); + json_object_object_add(json, ifname ? ifname : "global", + json_row); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + vty_out(vty, "IGMP RX statistics\n"); + vty_out(vty, "Interface : %s\n", + ifname ? ifname : "global"); + vty_out(vty, "V1 query : %u\n", rx_stats.query_v1); + vty_out(vty, "V2 query : %u\n", rx_stats.query_v2); + vty_out(vty, "V3 query : %u\n", rx_stats.query_v3); + vty_out(vty, "V2 leave : %u\n", rx_stats.leave_v2); + vty_out(vty, "V1 report : %u\n", rx_stats.report_v1); + vty_out(vty, "V2 report : %u\n", rx_stats.report_v2); + vty_out(vty, "V3 report : %u\n", rx_stats.report_v3); + vty_out(vty, "mtrace response : %u\n", rx_stats.mtrace_rsp); + vty_out(vty, "mtrace request : %u\n", rx_stats.mtrace_req); + vty_out(vty, "unsupported : %u\n", rx_stats.unsupported); + } +} + static void pim_show_interfaces(struct pim_instance *pim, struct vty *vty, uint8_t uj) { @@ -3527,6 +3597,33 @@ DEFUN (show_ip_igmp_sources_retransmissions, return CMD_SUCCESS; } +DEFUN (show_ip_igmp_statistics, + show_ip_igmp_statistics_cmd, + "show ip igmp [vrf NAME] statistics [interface WORD] [json]", + SHOW_STR + IP_STR + IGMP_STR + VRF_CMD_HELP_STR + "IGMP statistics\n" + "interface\n" + "IGMP interface\n" + JSON_STR) +{ + int idx = 2; + struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx); + uint8_t uj = use_json(argc, argv); + + if (!vrf) + return CMD_WARNING; + + if (argv_find(argv, argc, "WORD", &idx)) + igmp_show_statistics(vrf->info, vty, argv[idx]->arg, uj); + else + igmp_show_statistics(vrf->info, vty, NULL, uj); + + return CMD_SUCCESS; +} + DEFUN (show_ip_pim_assert, show_ip_pim_assert_cmd, "show ip pim [vrf NAME] assert", @@ -8644,6 +8741,7 @@ void pim_cmd_init(void) install_element(VIEW_NODE, &show_ip_igmp_groups_retransmissions_cmd); install_element(VIEW_NODE, &show_ip_igmp_sources_cmd); install_element(VIEW_NODE, &show_ip_igmp_sources_retransmissions_cmd); + install_element(VIEW_NODE, &show_ip_igmp_statistics_cmd); install_element(VIEW_NODE, &show_ip_pim_assert_cmd); install_element(VIEW_NODE, &show_ip_pim_assert_internal_cmd); install_element(VIEW_NODE, &show_ip_pim_assert_metric_cmd); diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 5e1aecc3a3..c980f5fcba 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -303,6 +303,21 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version, return -1; } + /* Collecting IGMP Rx stats */ + switch (query_version) { + case 1: + igmp->rx_stats.query_v1++; + break; + case 2: + igmp->rx_stats.query_v2++; + break; + case 3: + igmp->rx_stats.query_v3++; + break; + default: + igmp->rx_stats.unsupported++; + } + /* * RFC 3376 defines some guidelines on operating in backwards * compatibility with older versions of IGMP but there are some gaps in @@ -400,6 +415,9 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.report_v1++; + if (PIM_DEBUG_IGMP_TRACE) { zlog_warn("%s %s: FIXME WRITEME", __FILE__, __PRETTY_FUNCTION__); @@ -524,6 +542,9 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len) zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); + /* Collecting IGMP Rx stats */ + igmp->rx_stats.unsupported++; + return -1; } @@ -867,6 +888,8 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr, pim_ifp->igmp_default_robustness_variable; igmp->sock_creation = pim_time_monotonic_sec(); + igmp_stats_init(&igmp->rx_stats); + if (mtrace_only) { igmp->mtrace_only = mtrace_only; return igmp; diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 561a127d0f..c8b880ddd7 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -25,6 +25,7 @@ #include #include "vty.h" #include "linklist.h" +#include "pim_igmp_stats.h" /* The following sizes are likely to support @@ -94,6 +95,8 @@ struct igmp_sock { struct list *igmp_group_list; /* list of struct igmp_group */ struct hash *igmp_group_hash; + + struct igmp_stats rx_stats; }; struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list, diff --git a/pimd/pim_igmp_mtrace.c b/pimd/pim_igmp_mtrace.c index d3ae185709..673e2ca5b8 100644 --- a/pimd/pim_igmp_mtrace.c +++ b/pimd/pim_igmp_mtrace.c @@ -671,6 +671,9 @@ int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.mtrace_req++; + if (PIM_DEBUG_MTRACE) mtrace_debug(pim_ifp, mtracep, igmp_msg_len); @@ -881,6 +884,9 @@ int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr, mtracep->checksum = checksum; + /* Collecting IGMP Rx stats */ + igmp->rx_stats.mtrace_rsp++; + if (PIM_DEBUG_MTRACE) mtrace_debug(pim_ifp, mtracep, igmp_msg_len); diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c new file mode 100644 index 0000000000..428816e1f0 --- /dev/null +++ b/pimd/pim_igmp_stats.c @@ -0,0 +1,42 @@ +/* + * PIM for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * 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 "pim_igmp_stats.h" + +void igmp_stats_init(struct igmp_stats *stats) +{ + memset(stats, 0, sizeof(struct igmp_stats)); +} + +void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b) +{ + if (!a || !b) + return; + + a->query_v1 += b->query_v1; + a->query_v2 += b->query_v2; + a->query_v3 += b->query_v3; + a->report_v1 += b->report_v1; + a->report_v2 += b->report_v2; + a->report_v3 += b->report_v3; + a->leave_v2 += b->leave_v2; + a->mtrace_rsp += b->mtrace_rsp; + a->mtrace_req += b->mtrace_req; + a->unsupported += b->unsupported; +} diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h new file mode 100644 index 0000000000..57b5cc62f4 --- /dev/null +++ b/pimd/pim_igmp_stats.h @@ -0,0 +1,41 @@ +/* + * PIM for FRRouting + * Copyright (C) 2018 Mladen Sablic + * + * 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 + */ + +#ifndef PIM_IGMP_STATS_H +#define PIM_IGMP_STATS_H + +#include + +struct igmp_stats { + uint32_t query_v1; + uint32_t query_v2; + uint32_t query_v3; + uint32_t report_v1; + uint32_t report_v2; + uint32_t report_v3; + uint32_t leave_v2; + uint32_t mtrace_rsp; + uint32_t mtrace_req; + uint32_t unsupported; +}; + +void igmp_stats_init(struct igmp_stats *stats); +void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b); + +#endif /* PIM_IGMP_STATS_H */ diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c index dbbe83a965..19c3768813 100644 --- a/pimd/pim_igmpv2.c +++ b/pimd/pim_igmpv2.c @@ -121,6 +121,9 @@ int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.report_v2++; + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); if (PIM_DEBUG_IGMP_PACKETS) { @@ -167,6 +170,9 @@ int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.leave_v2++; + memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); if (PIM_DEBUG_IGMP_PACKETS) { diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index 3360e36b4a..5ccad39b33 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -1900,6 +1900,9 @@ int igmp_v3_recv_report(struct igmp_sock *igmp, struct in_addr from, return -1; } + /* Collecting IGMP Rx stats */ + igmp->rx_stats.report_v3++; + num_groups = ntohs( *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET)); if (num_groups < 1) { diff --git a/pimd/subdir.am b/pimd/subdir.am index 2254362221..0696d9b1e8 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -20,6 +20,7 @@ pimd_libpim_a_SOURCES = \ pimd/pim_ifchannel.c \ pimd/pim_igmp.c \ pimd/pim_igmp_mtrace.c \ + pimd/pim_igmp_stats.c \ pimd/pim_igmpv2.c \ pimd/pim_igmpv3.c \ pimd/pim_instance.c \ @@ -69,6 +70,7 @@ noinst_HEADERS += \ pimd/pim_igmp.h \ pimd/pim_igmp_join.h \ pimd/pim_igmp_mtrace.h \ + pimd/pim_igmp_stats.h \ pimd/pim_igmpv2.h \ pimd/pim_igmpv3.h \ pimd/pim_instance.h \