summaryrefslogtreecommitdiff
path: root/pimd/pim_igmpv3.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_igmpv3.c')
-rw-r--r--pimd/pim_igmpv3.c563
1 files changed, 391 insertions, 172 deletions
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c
index bdaf2bb270..f7a6cbd0ce 100644
--- a/pimd/pim_igmpv3.c
+++ b/pimd/pim_igmpv3.c
@@ -46,8 +46,8 @@ static void on_trace(const char *label,
int num_sources, struct in_addr *sources)
{
if (PIM_DEBUG_IGMP_TRACE) {
- char from_str[100];
- char group_str[100];
+ char from_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
@@ -57,50 +57,6 @@ static void on_trace(const char *label,
}
}
-int igmp_group_compat_mode(const struct igmp_sock *igmp,
- const struct igmp_group *group)
-{
- struct pim_interface *pim_ifp;
- int64_t now_dsec;
- long older_host_present_interval_dsec;
-
- zassert(igmp);
- zassert(igmp->interface);
- zassert(igmp->interface->info);
-
- pim_ifp = igmp->interface->info;
-
- /*
- RFC 3376: 8.13. Older Host Present Interval
-
- This value MUST be ((the Robustness Variable) times (the Query
- Interval)) plus (one Query Response Interval).
-
- older_host_present_interval_dsec = \
- igmp->querier_robustness_variable * \
- 10 * igmp->querier_query_interval + \
- pim_ifp->query_max_response_time_dsec;
- */
- older_host_present_interval_dsec =
- PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
-
- now_dsec = pim_time_monotonic_dsec();
- if (now_dsec < 1) {
- /* broken timer logged by pim_time_monotonic_dsec() */
- return 3;
- }
-
- if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
- return 1; /* IGMPv1 */
-
- if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
- return 2; /* IGMPv2 */
-
- return 3; /* IGMPv3 */
-}
-
void igmp_group_reset_gmi(struct igmp_group *group)
{
long group_membership_interval_msec;
@@ -132,7 +88,7 @@ void igmp_group_reset_gmi(struct igmp_group *group)
pim_ifp->igmp_query_max_response_time_dsec);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
group_str,
@@ -158,15 +114,13 @@ static int igmp_source_timer(struct thread *t)
struct igmp_source *source;
struct igmp_group *group;
- zassert(t);
source = THREAD_ARG(t);
- zassert(source);
group = source->source_group;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("%s: Source timer expired for group %s source %s on %s",
@@ -176,7 +130,7 @@ static int igmp_source_timer(struct thread *t)
}
zassert(source->t_source_timer);
- source->t_source_timer = 0;
+ source->t_source_timer = NULL;
/*
RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
@@ -230,8 +184,8 @@ static void source_timer_off(struct igmp_group *group,
return;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("Cancelling TIMER event for group %s source %s on %s",
@@ -250,8 +204,8 @@ static void igmp_source_timer_on(struct igmp_group *group,
source_timer_off(group, source);
if (PIM_DEBUG_IGMP_EVENTS) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
@@ -291,8 +245,8 @@ void igmp_source_reset_gmi(struct igmp_sock *igmp,
pim_ifp->igmp_query_max_response_time_dsec);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
@@ -383,7 +337,7 @@ static void source_channel_oil_detach(struct igmp_source *source)
{
if (source->source_channel_oil) {
pim_channel_oil_del(source->source_channel_oil);
- source->source_channel_oil = 0;
+ source->source_channel_oil = NULL;
}
}
@@ -398,8 +352,8 @@ void igmp_source_delete(struct igmp_source *source)
group = source->source_group;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
@@ -413,8 +367,8 @@ void igmp_source_delete(struct igmp_source *source)
/* sanity check that forwarding has been disabled */
if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
@@ -483,8 +437,8 @@ source_new (struct igmp_group *group,
struct igmp_source *src;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
@@ -493,9 +447,9 @@ source_new (struct igmp_group *group,
group->group_igmp_sock->interface->name);
}
- src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
+ src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
if (!src) {
- zlog_warn("%s %s: XMALLOC() failure",
+ zlog_warn("%s %s: XCALLOC() failure",
__FILE__, __PRETTY_FUNCTION__);
return 0; /* error, not found, could not create */
}
@@ -646,8 +600,10 @@ static void isex_excl(struct igmp_group *group,
struct in_addr star = { .s_addr = INADDR_ANY };
source = igmp_find_source_by_addr (group, star);
if (source)
- IGMP_SOURCE_DONT_DELETE(source->source_flags);
- igmp_source_reset_gmi (group->group_igmp_sock, group, source);
+ {
+ IGMP_SOURCE_DONT_DELETE(source->source_flags);
+ igmp_source_reset_gmi (group->group_igmp_sock, group, source);
+ }
}
/* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
@@ -702,7 +658,8 @@ static void isex_incl(struct igmp_group *group,
void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
+ int num_sources, struct in_addr *sources,
+ int from_igmp_v2_report)
{
struct interface *ifp = igmp->interface;
struct igmp_group *group;
@@ -716,6 +673,10 @@ void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
return;
}
+ /* So we can display how we learned the group in our show command output */
+ if (from_igmp_v2_report)
+ group->igmp_version = 2;
+
if (group->group_filtermode_isexcl) {
/* EXCLUDE mode */
isex_excl(group, num_sources, sources);
@@ -932,6 +893,16 @@ static void toex_excl(struct igmp_group *group,
/* clear off SEND flag from all known sources (X,Y) */
source_clear_send_flag(group->group_source_list);
+ if (num_sources == 0)
+ {
+ struct igmp_source *source;
+ struct in_addr any = { .s_addr = INADDR_ANY };
+
+ source = igmp_find_source_by_addr (group, any);
+ if (source)
+ IGMP_SOURCE_DONT_DELETE(source->source_flags);
+ }
+
/* scan received sources (A) */
for (i = 0; i < num_sources; ++i) {
struct igmp_source *source;
@@ -1037,17 +1008,25 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
*/
static void group_retransmit_group(struct igmp_group *group)
{
- char query_buf[PIM_IGMP_BUFSIZE_WRITE];
struct igmp_sock *igmp;
struct pim_interface *pim_ifp;
long lmqc; /* Last Member Query Count */
long lmqi_msec; /* Last Member Query Interval */
long lmqt_msec; /* Last Member Query Time */
int s_flag;
+ int query_buf_size;
igmp = group->group_igmp_sock;
pim_ifp = igmp->interface->info;
+ if (pim_ifp->igmp_version == 3) {
+ query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
+ } else {
+ query_buf_size = IGMP_V12_MSG_SIZE;
+ }
+
+ char query_buf[query_buf_size];
+
lmqc = igmp->querier_robustness_variable;
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
lmqt_msec = lmqc * lmqi_msec;
@@ -1062,7 +1041,7 @@ static void group_retransmit_group(struct igmp_group *group)
s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
group_str, igmp->interface->name, s_flag,
@@ -1077,18 +1056,19 @@ static void group_retransmit_group(struct igmp_group *group)
interest.
*/
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf,
- sizeof(query_buf),
- 0 /* num_sources_tosend */,
- group->group_addr /* dst_addr */,
- group->group_addr /* group_addr */,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- s_flag,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
+ igmp_send_query(pim_ifp->igmp_version,
+ group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf,
+ sizeof(query_buf),
+ 0 /* num_sources_tosend */,
+ group->group_addr /* dst_addr */,
+ group->group_addr /* group_addr */,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ s_flag,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
}
/*
@@ -1123,9 +1103,6 @@ static int group_retransmit_sources(struct igmp_group *group,
struct igmp_source *src;
int num_retransmit_sources_left = 0;
- query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
- query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
-
source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
@@ -1163,7 +1140,7 @@ static int group_retransmit_sources(struct igmp_group *group,
num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
group_str, igmp->interface->name,
@@ -1183,7 +1160,7 @@ static int group_retransmit_sources(struct igmp_group *group,
query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
if (num_sources_tosend1 > query_buf1_max_sources) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
__PRETTY_FUNCTION__, group_str, igmp->interface->name,
@@ -1198,19 +1175,19 @@ static int group_retransmit_sources(struct igmp_group *group,
interest.
*/
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf1,
- sizeof(query_buf1),
- num_sources_tosend1,
- group->group_addr,
- group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 1 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
-
+ igmp_send_query(pim_ifp->igmp_version,
+ group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf1,
+ sizeof(query_buf1),
+ num_sources_tosend1,
+ group->group_addr,
+ group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ 1 /* s_flag */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
}
} /* send_with_sflag_set */
@@ -1225,7 +1202,7 @@ static int group_retransmit_sources(struct igmp_group *group,
query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
if (num_sources_tosend2 > query_buf2_max_sources) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
__PRETTY_FUNCTION__, group_str, igmp->interface->name,
@@ -1240,19 +1217,19 @@ static int group_retransmit_sources(struct igmp_group *group,
interest.
*/
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf2,
- sizeof(query_buf2),
- num_sources_tosend2,
- group->group_addr,
- group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 0 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
-
+ igmp_send_query(pim_ifp->igmp_version,
+ group,
+ igmp->fd,
+ igmp->interface->name,
+ query_buf2,
+ sizeof(query_buf2),
+ num_sources_tosend2,
+ group->group_addr,
+ group->group_addr,
+ pim_ifp->igmp_specific_query_max_response_time_dsec,
+ 0 /* s_flag */,
+ igmp->querier_robustness_variable,
+ igmp->querier_query_interval);
}
}
@@ -1265,12 +1242,10 @@ static int igmp_group_retransmit(struct thread *t)
int num_retransmit_sources_left;
int send_with_sflag_set; /* boolean */
- zassert(t);
group = THREAD_ARG(t);
- zassert(group);
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("group_retransmit_timer: group %s on %s",
group_str, group->group_igmp_sock->interface->name);
@@ -1300,7 +1275,7 @@ static int igmp_group_retransmit(struct thread *t)
num_retransmit_sources_left = group_retransmit_sources(group,
send_with_sflag_set);
- group->t_group_query_retransmit_timer = 0;
+ group->t_group_query_retransmit_timer = NULL;
/*
Keep group retransmit timer running if there is any retransmit
@@ -1336,7 +1311,7 @@ static void group_retransmit_timer_on(struct igmp_group *group)
lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
lmqi_msec / 1000,
@@ -1565,7 +1540,7 @@ void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
__PRETTY_FUNCTION__,
@@ -1600,8 +1575,8 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
+ char group_str[INET_ADDRSTRLEN];
+ char source_str[INET_ADDRSTRLEN];
pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
@@ -1613,32 +1588,19 @@ void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
igmp_source_timer_on(group, source, lmqt_msec);
}
-/*
- Copy sources to message:
-
- struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
- if (num_sources > 0) {
- struct listnode *node;
- struct igmp_source *src;
- int i = 0;
-
- for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
- sources[i++] = src->source_addr;
- }
- }
-*/
-void pim_igmp_send_membership_query(struct igmp_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,
- int query_max_response_time_dsec,
- uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval)
+void
+igmp_v3_send_query (struct igmp_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,
+ int query_max_response_time_dsec,
+ uint8_t s_flag,
+ uint8_t querier_robustness_variable,
+ uint16_t querier_query_interval)
{
ssize_t msg_size;
uint8_t max_resp_code;
@@ -1678,7 +1640,7 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
query_buf[1] = max_resp_code;
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = 0; /* for computing checksum */
memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
query_buf[8] = (s_flag << 3) | querier_robustness_variable;
@@ -1686,18 +1648,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
*(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
checksum = in_cksum(query_buf, msg_size);
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
+ *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
if (PIM_DEBUG_IGMP_PACKETS) {
- char dst_str[100];
- char group_str[100];
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, num_sources,
- msg_size, s_flag, querier_robustness_variable,
- querier_query_interval, qqic, checksum);
+ zlog_debug("Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
+ dst_str, ifname, group_str,
+ num_sources, msg_size, s_flag, querier_robustness_variable,
+ querier_query_interval, qqic);
}
memset(&to, 0, sizeof(to));
@@ -1708,22 +1669,17 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != (ssize_t) msg_size) {
- int e = errno;
- char dst_str[100];
- char group_str[100];
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
if (sent < 0) {
- zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, msg_size,
- e, safe_strerror(e));
+ zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
+ dst_str, ifname, group_str, msg_size, errno, safe_strerror(errno));
}
else {
- zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str,
- msg_size, sent);
+ zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifname, group_str, msg_size, sent);
}
return;
}
@@ -1742,8 +1698,8 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
if (!s_flag) {
/* general query? */
if (PIM_INADDR_IS_ANY(group_addr)) {
- char dst_str[100];
- char group_str[100];
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
@@ -1751,5 +1707,268 @@ void pim_igmp_send_membership_query(struct igmp_group *group,
dst_str, ifname, group_str, num_sources);
}
}
+}
+
+void
+igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct in_addr group_addr;
+ uint8_t resv_s_qrv = 0;
+ uint8_t s_flag = 0;
+ uint8_t qrv = 0;
+ int i;
+
+ memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+
+ /*
+ * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
+ *
+ * Routers adopt the QRV value from the most recently received Query
+ * as their own [Robustness Variable] value, unless that most
+ * recently received QRV was zero, in which case the receivers use
+ * the default [Robustness Variable] value specified in section 8.1
+ * or a statically configured value.
+ */
+ resv_s_qrv = igmp_msg[8];
+ qrv = 7 & resv_s_qrv;
+ igmp->querier_robustness_variable = qrv ? qrv : pim_ifp->igmp_default_robustness_variable;
+
+ /*
+ * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
+ *
+ * Multicast routers that are not the current querier adopt the QQI
+ * value from the most recently received Query as their own [Query
+ * Interval] value, unless that most recently received QQI was zero,
+ * in which case the receiving routers use the default.
+ */
+ if (igmp->t_other_querier_timer) {
+ /* other querier present */
+ uint8_t qqic;
+ uint16_t qqi;
+ qqic = igmp_msg[9];
+ qqi = igmp_msg_decode8to16(qqic);
+ igmp->querier_query_interval = qqi ? qqi : pim_ifp->igmp_default_query_interval;
+
+ if (PIM_DEBUG_IGMP_TRACE) {
+ char ifaddr_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
+ zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
+ ifaddr_str,
+ qqi ? "recv-non-default" : "default",
+ igmp->querier_query_interval,
+ qqic,
+ from_str);
+ }
+ }
+
+ /*
+ * RFC 3376: 6.6.1. Timer Updates
+ *
+ * When a router sends or receives a query with a clear Suppress
+ * Router-Side Processing flag, it must update its timers to reflect
+ * the correct timeout values for the group or sources being queried.
+ *
+ * General queries don't trigger timer update.
+ */
+ s_flag = (1 << 3) & resv_s_qrv;
+
+ if (!s_flag) {
+ /* s_flag is clear */
+
+ if (PIM_INADDR_IS_ANY(group_addr)) {
+ /* this is a general query */
+ /* log that general query should have the s_flag set */
+ zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
+ from_str, ifp->name);
+ } else {
+ struct igmp_group *group;
+
+ /* this is a non-general query: perform timer updates */
+
+ group = find_group_by_addr(igmp, group_addr);
+ if (group) {
+ int recv_num_sources = ntohs(*(uint16_t *)(igmp_msg + IGMP_V3_NUMSOURCES_OFFSET));
+
+ /*
+ * RFC 3376: 6.6.1. Timer Updates
+ * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
+ * Query Q(G): Group Timer is lowered to LMQT
+ */
+ if (recv_num_sources < 1) {
+ /* Query Q(G): Group Timer is lowered to LMQT */
+
+ igmp_group_timer_lower_to_lmqt(group);
+ } else {
+ /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
+
+ /* Scan sources in query and lower their timers to LMQT */
+ struct in_addr *sources = (struct in_addr *)(igmp_msg + IGMP_V3_SOURCES_OFFSET);
+ for (i = 0; i < recv_num_sources; ++i) {
+ struct in_addr src_addr;
+ struct igmp_source *src;
+ memcpy(&src_addr, sources + i, sizeof(struct in_addr));
+ src = igmp_find_source_by_addr(group, src_addr);
+ if (src) {
+ igmp_source_timer_lower_to_lmqt(src);
+ }
+ }
+ }
+ } else {
+ char group_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
+ zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
+ from_str, ifp->name, group_str);
+ }
+ }
+ } /* s_flag is clear: timer updates */
+}
+
+int
+igmp_v3_recv_report (struct igmp_sock *igmp,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ uint16_t recv_checksum;
+ uint16_t checksum;
+ int num_groups;
+ uint8_t *group_record;
+ uint8_t *report_pastend = (uint8_t *) igmp_msg + igmp_msg_len;
+ struct interface *ifp = igmp->interface;
+ int i;
+ int local_ncb = 0;
+
+ if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
+ from_str, ifp->name, igmp_msg_len, IGMP_V3_MSG_MIN_SIZE);
+ return -1;
+ }
+
+ recv_checksum = *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET);
+
+ /* for computing checksum */
+ *(uint16_t *) (igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+ if (checksum != recv_checksum) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+ num_groups = ntohs(*(uint16_t *) (igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
+ if (num_groups < 1) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
+ from_str, ifp->name, igmp_msg_len, checksum, num_groups);
+ }
+
+ group_record = (uint8_t *) igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
+
+ /* Scan groups */
+ for (i = 0; i < num_groups; ++i) {
+ struct in_addr rec_group;
+ uint8_t *sources;
+ uint8_t *src;
+ int rec_type;
+ int rec_auxdatalen;
+ int rec_num_sources;
+ int j;
+ struct prefix lncb;
+ struct prefix g;
+
+ if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
+ rec_auxdatalen = group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
+ rec_num_sources = ntohs(* (uint16_t *) (group_record + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
+
+ memcpy(&rec_group, group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET, sizeof(struct in_addr));
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
+ from_str, ifp->name, i, rec_type, rec_auxdatalen, rec_num_sources, inet_ntoa(rec_group));
+ }
+
+ /* Scan sources */
+
+ sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
+
+ for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
+
+ if ((src + 4) > report_pastend) {
+ zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
+ from_str, ifp->name);
+ return -1;
+ }
+
+ if (PIM_DEBUG_IGMP_PACKETS) {
+ char src_str[200];
+
+ if (!inet_ntop(AF_INET, src, src_str , sizeof(src_str)))
+ sprintf(src_str, "<source?>");
+
+ zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
+ from_str, ifp->name, i, inet_ntoa(rec_group), src_str);
+ }
+ } /* for (sources) */
+
+
+ lncb.family = AF_INET;
+ lncb.u.prefix4.s_addr = 0x000000E0;
+ lncb.prefixlen = 24;
+
+ g.family = AF_INET;
+ g.u.prefix4 = rec_group;
+ g.prefixlen = 32;
+ /*
+ * If we receive a igmp report with the group in 224.0.0.0/24
+ * then we should ignore it
+ */
+ if (prefix_match(&lncb, &g))
+ local_ncb = 1;
+
+ if (!local_ncb)
+ switch (rec_type) {
+ case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
+ igmpv3_report_isin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
+ igmpv3_report_isex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources, 0);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
+ igmpv3_report_toin(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
+ igmpv3_report_toex(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
+ igmpv3_report_allow(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
+ igmpv3_report_block(igmp, from, rec_group, rec_num_sources, (struct in_addr *) sources);
+ break;
+ default:
+ zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
+ from_str, ifp->name, rec_type);
+ }
+
+ group_record += 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
+ local_ncb = 0;
+
+ } /* for (group records) */
+
+ return 0;
}