summaryrefslogtreecommitdiff
path: root/pimd/pim_igmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_igmp.c')
-rw-r--r--pimd/pim_igmp.c257
1 files changed, 217 insertions, 40 deletions
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 5cdefd2828..aa6b30c624 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -37,11 +37,180 @@
#include "pim_str.h"
#include "pim_util.h"
#include "pim_time.h"
-#include "pim_zebra.h"
+#include "pim_ssm.h"
+#include "pim_tib.h"
static void group_timer_off(struct gm_group *group);
static void pim_igmp_general_query(struct thread *t);
+void igmp_anysource_forward_start(struct pim_instance *pim,
+ struct gm_group *group)
+{
+ struct gm_source *source;
+ struct in_addr src_addr = {.s_addr = 0};
+ /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
+ assert(group->group_filtermode_isexcl);
+ assert(listcount(group->group_source_list) < 1);
+
+ source = igmp_get_source_by_addr(group, src_addr, NULL);
+ if (!source) {
+ zlog_warn("%s: Failure to create * source", __func__);
+ return;
+ }
+
+ igmp_source_forward_start(pim, source);
+}
+
+void igmp_anysource_forward_stop(struct gm_group *group)
+{
+ struct gm_source *source;
+ struct in_addr star = {.s_addr = 0};
+
+ source = igmp_find_source_by_addr(group, star);
+ if (source)
+ igmp_source_forward_stop(source);
+}
+
+static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
+ struct gm_source *source)
+{
+ pim_sgaddr sg;
+ struct gm_group *group = source->source_group;
+ struct pim_ifchannel *ch;
+
+ if ((source->source_addr.s_addr != INADDR_ANY) ||
+ !IGMP_SOURCE_TEST_FORWARDING(source->source_flags))
+ return;
+
+ memset(&sg, 0, sizeof(sg));
+ sg.src = source->source_addr;
+ sg.grp = group->group_addr;
+
+ ch = pim_ifchannel_find(group->interface, &sg);
+ if (pim_is_grp_ssm(pim, group->group_addr)) {
+ /* If SSM group withdraw local membership */
+ if (ch &&
+ (ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE)) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug(
+ "local membership del for %pSG as G is now SSM",
+ &sg);
+ pim_ifchannel_local_membership_del(group->interface,
+ &sg);
+ }
+ } else {
+ /* If ASM group add local membership */
+ if (!ch ||
+ (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug(
+ "local membership add for %pSG as G is now ASM",
+ &sg);
+ pim_ifchannel_local_membership_add(
+ group->interface, &sg, false /*is_vxlan*/);
+ }
+ }
+}
+
+void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (pim->vrf, ifp) {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct listnode *grpnode;
+ struct gm_group *grp;
+ struct pim_ifchannel *ch, *ch_temp;
+
+ if (!pim_ifp)
+ continue;
+
+ /* scan igmp groups */
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
+ grp)) {
+ struct listnode *srcnode;
+ struct gm_source *src;
+
+ /* scan group sources */
+ for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
+ srcnode, src)) {
+ igmp_source_forward_reevaluate_one(pim, src);
+ } /* scan group sources */
+ } /* scan igmp groups */
+
+ RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
+ ch_temp) {
+ if (pim_is_grp_ssm(pim, ch->sg.grp)) {
+ if (pim_addr_is_any(ch->sg.src))
+ pim_ifchannel_delete(ch);
+ }
+ }
+ } /* scan interfaces */
+}
+
+void igmp_source_forward_start(struct pim_instance *pim,
+ struct gm_source *source)
+{
+ struct gm_group *group;
+ pim_sgaddr sg;
+
+ memset(&sg, 0, sizeof(sg));
+ sg.src = source->source_addr;
+ sg.grp = source->source_group->group_addr;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
+ source->source_group->interface->name,
+ IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+ }
+
+ /* Prevent IGMP interface from installing multicast route multiple
+ times */
+ if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+ return;
+ }
+
+ group = source->source_group;
+
+ if (tib_sg_gm_join(pim, sg, group->interface,
+ &source->source_channel_oil))
+ IGMP_SOURCE_DO_FORWARDING(source->source_flags);
+}
+
+/*
+ igmp_source_forward_stop: stop fowarding, but keep the source
+ igmp_source_delete: stop fowarding, and delete the source
+ */
+void igmp_source_forward_stop(struct gm_source *source)
+{
+ struct pim_interface *pim_oif;
+ struct gm_group *group;
+ pim_sgaddr sg;
+
+ memset(&sg, 0, sizeof(sg));
+ sg.src = source->source_addr;
+ sg.grp = source->source_group->group_addr;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
+ source->source_group->interface->name,
+ IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
+ }
+
+ /* Prevent IGMP interface from removing multicast route multiple
+ times */
+ if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
+ return;
+ }
+
+ group = source->source_group;
+ pim_oif = group->interface->info;
+
+ tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
+ &source->source_channel_oil);
+ IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
+}
+
/* This socket is used for TXing IGMP packets only, IGMP RX happens
* in pim_mroute_msg()
*/
@@ -51,6 +220,7 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
int fd;
int join = 0;
struct in_addr group;
+ struct pim_interface *pim_ifp = ifp->info;
fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
@@ -59,7 +229,8 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
if (inet_aton(PIM_ALL_ROUTERS, &group)) {
- if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
+ if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
+ pim_ifp))
++join;
} else {
zlog_warn(
@@ -75,7 +246,7 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
IGMP routers must receive general queries for querier election.
*/
if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
- if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
+ if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
++join;
} else {
zlog_warn(
@@ -85,7 +256,8 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
}
if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
- if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) {
+ if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
+ pim_ifp)) {
++join;
}
} else {
@@ -320,16 +492,16 @@ static int igmp_recv_query(struct gm_sock *igmp, int query_version,
/* Collecting IGMP Rx stats */
switch (query_version) {
case 1:
- igmp->rx_stats.query_v1++;
+ igmp->igmp_stats.query_v1++;
break;
case 2:
- igmp->rx_stats.query_v2++;
+ igmp->igmp_stats.query_v2++;
break;
case 3:
- igmp->rx_stats.query_v3++;
+ igmp->igmp_stats.query_v3++;
break;
default:
- igmp->rx_stats.unsupported++;
+ igmp->igmp_stats.unsupported++;
}
/*
@@ -461,7 +633,7 @@ static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
}
/* Collecting IGMP Rx stats */
- igmp->rx_stats.report_v1++;
+ igmp->igmp_stats.report_v1++;
if (PIM_DEBUG_IGMP_TRACE) {
zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
@@ -613,7 +785,7 @@ int pim_igmp_packet(struct gm_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++;
+ igmp->igmp_stats.unsupported++;
return -1;
}
@@ -737,13 +909,10 @@ static void pim_igmp_general_query(struct thread *t)
querier_str, dst_str, igmp->interface->name);
}
- igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, igmp->fd,
- igmp->interface->name, query_buf, sizeof(query_buf),
- 0 /* num_sources */, dst_addr, group_addr,
- pim_ifp->gm_query_max_response_time_dsec,
- 1 /* s_flag: always set for general queries */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
+ sizeof(query_buf), 0 /* num_sources */, dst_addr,
+ group_addr, pim_ifp->gm_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */, igmp);
pim_igmp_general_query_on(igmp);
}
@@ -808,6 +977,8 @@ static void igmp_group_free(struct gm_group *group)
static void igmp_group_count_incr(struct pim_interface *pim_ifp)
{
+ uint32_t group_count = listcount(pim_ifp->gm_group_list);
+
++pim_ifp->pim->igmp_group_count;
if (pim_ifp->pim->igmp_group_count
== pim_ifp->pim->igmp_watermark_limit) {
@@ -816,6 +987,9 @@ static void igmp_group_count_incr(struct pim_interface *pim_ifp)
pim_ifp->pim->igmp_group_count,
VRF_LOGNAME(pim_ifp->pim->vrf));
}
+
+ if (pim_ifp->igmp_peak_group_count < group_count)
+ pim_ifp->igmp_peak_group_count = group_count;
}
static void igmp_group_count_decr(struct pim_interface *pim_ifp)
@@ -990,7 +1164,7 @@ static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
pim_ifp->gm_default_robustness_variable;
igmp->sock_creation = pim_time_monotonic_sec();
- igmp_stats_init(&igmp->rx_stats);
+ igmp_stats_init(&igmp->igmp_stats);
if (mtrace_only) {
igmp->mtrace_only = mtrace_only;
@@ -1016,8 +1190,8 @@ static void pim_igmp_read(struct thread *t)
{
uint8_t buf[10000];
struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
- struct sockaddr_in from;
- struct sockaddr_in to;
+ struct sockaddr_storage from;
+ struct sockaddr_storage to;
socklen_t fromlen = sizeof(from);
socklen_t tolen = sizeof(to);
ifindex_t ifindex = -1;
@@ -1282,23 +1456,29 @@ struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
return group;
}
-void igmp_send_query(int igmp_version, struct gm_group *group, int fd,
- const char *ifname, char *query_buf, int query_buf_size,
- int num_sources, struct in_addr dst_addr,
- struct in_addr group_addr,
+void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
+ int query_buf_size, int num_sources,
+ struct in_addr dst_addr, struct in_addr group_addr,
int query_max_response_time_dsec, uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval)
+ struct gm_sock *igmp)
{
+ if (pim_addr_is_any(group_addr) &&
+ ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
+ igmp->igmp_stats.general_queries_sent++;
+ else if (group)
+ igmp->igmp_stats.group_queries_sent++;
+
if (igmp_version == 3) {
- igmp_v3_send_query(group, fd, ifname, query_buf, query_buf_size,
- num_sources, dst_addr, group_addr,
+ igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
+ query_buf, query_buf_size, num_sources,
+ dst_addr, group_addr,
query_max_response_time_dsec, s_flag,
- querier_robustness_variable,
- querier_query_interval);
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
} else if (igmp_version == 2) {
- igmp_v2_send_query(group, fd, ifname, query_buf, dst_addr,
- group_addr, query_max_response_time_dsec);
+ igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
+ query_buf, dst_addr, group_addr,
+ query_max_response_time_dsec);
}
}
@@ -1329,13 +1509,10 @@ void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
char query_buf[query_buf_size];
- igmp_send_query(igmp_ver, 0 /* igmp_group */, igmp->fd,
- igmp->interface->name, query_buf,
- sizeof(query_buf), 0 /* num_sources */,
- dst_addr, group_addr,
- pim_ifp->gm_query_max_response_time_dsec,
- 1 /* s_flag: always set for general queries */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query(
+ igmp_ver, 0 /* igmp_group */, query_buf,
+ sizeof(query_buf), 0 /* num_sources */, dst_addr,
+ group_addr, pim_ifp->gm_query_max_response_time_dsec,
+ 1 /* s_flag: always set for general queries */, igmp);
}
}