diff options
Diffstat (limited to 'pimd/pim_igmpv3.c')
| -rw-r--r-- | pimd/pim_igmpv3.c | 3451 | 
1 files changed, 1759 insertions, 1692 deletions
diff --git a/pimd/pim_igmpv3.c b/pimd/pim_igmpv3.c index ad37ad8762..880d840eac 100644 --- a/pimd/pim_igmpv3.c +++ b/pimd/pim_igmpv3.c @@ -39,269 +39,270 @@ static void group_query_send(struct igmp_group *group);  static void source_query_send_by_flag(struct igmp_group *group,  				      int num_sources_tosend); -static void on_trace(const char *label, -		     struct interface *ifp, struct in_addr from, -		     struct in_addr group_addr, +static void on_trace(const char *label, struct interface *ifp, +		     struct in_addr from, struct in_addr group_addr,  		     int num_sources, struct in_addr *sources)  { -  if (PIM_DEBUG_IGMP_TRACE) { -    char from_str[INET_ADDRSTRLEN]; -    char group_str[INET_ADDRSTRLEN]; +	if (PIM_DEBUG_IGMP_TRACE) { +		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)); +		pim_inet4_dump("<from?>", from, from_str, sizeof(from_str)); +		pim_inet4_dump("<group?>", group_addr, group_str, +			       sizeof(group_str)); -    zlog_debug("%s: from %s on %s: group=%s sources=%d", -	       label, from_str, ifp->name, group_str, num_sources); -  } +		zlog_debug("%s: from %s on %s: group=%s sources=%d", label, +			   from_str, ifp->name, group_str, num_sources); +	}  }  void igmp_group_reset_gmi(struct igmp_group *group)  { -  long group_membership_interval_msec; -  struct pim_interface *pim_ifp; -  struct igmp_sock *igmp; -  struct interface *ifp; - -  igmp = group->group_igmp_sock; -  ifp = igmp->interface; -  pim_ifp = ifp->info; - -  /* -    RFC 3376: 8.4. Group Membership Interval - -    The Group Membership Interval is the amount of time that must pass -    before a multicast router decides there are no more members of a -    group or a particular source on a network. - -    This value MUST be ((the Robustness Variable) times (the Query -    Interval)) plus (one Query Response Interval). - -    group_membership_interval_msec = querier_robustness_variable * -                                     (1000 * querier_query_interval) + -                                     100 * query_response_interval_dsec; -  */ -  group_membership_interval_msec = -    PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, -		      igmp->querier_query_interval, -		      pim_ifp->igmp_query_max_response_time_dsec); - -  if (PIM_DEBUG_IGMP_TRACE) { -    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, -	       group_membership_interval_msec / 1000, -	       group_membership_interval_msec % 1000, -	       ifp->name); -  } - -  /* -    RFC 3376: 6.2.2. Definition of Group Timers - -    The group timer is only used when a group is in EXCLUDE mode and -    it represents the time for the *filter-mode* of the group to -    expire and switch to INCLUDE mode. -  */ -  zassert(group->group_filtermode_isexcl); - -  igmp_group_timer_on(group, group_membership_interval_msec, ifp->name); +	long group_membership_interval_msec; +	struct pim_interface *pim_ifp; +	struct igmp_sock *igmp; +	struct interface *ifp; + +	igmp = group->group_igmp_sock; +	ifp = igmp->interface; +	pim_ifp = ifp->info; + +	/* +	  RFC 3376: 8.4. Group Membership Interval + +	  The Group Membership Interval is the amount of time that must pass +	  before a multicast router decides there are no more members of a +	  group or a particular source on a network. + +	  This value MUST be ((the Robustness Variable) times (the Query +	  Interval)) plus (one Query Response Interval). + +	  group_membership_interval_msec = querier_robustness_variable * +					   (1000 * querier_query_interval) + +					   100 * query_response_interval_dsec; +	*/ +	group_membership_interval_msec = PIM_IGMP_GMI_MSEC( +		igmp->querier_robustness_variable, igmp->querier_query_interval, +		pim_ifp->igmp_query_max_response_time_dsec); + +	if (PIM_DEBUG_IGMP_TRACE) { +		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, group_membership_interval_msec / 1000, +			group_membership_interval_msec % 1000, ifp->name); +	} + +	/* +	  RFC 3376: 6.2.2. Definition of Group Timers + +	  The group timer is only used when a group is in EXCLUDE mode and +	  it represents the time for the *filter-mode* of the group to +	  expire and switch to INCLUDE mode. +	*/ +	zassert(group->group_filtermode_isexcl); + +	igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);  }  static int igmp_source_timer(struct thread *t)  { -  struct igmp_source *source; -  struct igmp_group *group; - -  source = THREAD_ARG(t); - -  group = source->source_group; - -  if (PIM_DEBUG_IGMP_TRACE) { -    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", -	       __PRETTY_FUNCTION__, -	       group_str, source_str, -	       group->group_igmp_sock->interface->name); -  } - -  /* -    RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules - -    Group -    Filter-Mode    Source Timer Value    Action -    -----------    ------------------    ------ -    INCLUDE        TIMER == 0            Suggest to stop forwarding -                                         traffic from source and -                                         remove source record.  If -                                         there are no more source -                                         records for the group, delete -                                         group record. - -    EXCLUDE        TIMER == 0            Suggest to not forward -                                         traffic from source -                                         (DO NOT remove record) - -    Source timer switched from (T > 0) to (T == 0): disable forwarding. -   */ - -  if (group->group_filtermode_isexcl) { -    /* EXCLUDE mode */ - -    igmp_source_forward_stop(source); -  } -  else { -    /* INCLUDE mode */ - -    /* igmp_source_delete() will stop forwarding source */ -    igmp_source_delete(source); - -    /* -      If there are no more source records for the group, delete group -      record. -    */ -    if (!listcount(group->group_source_list)) { -      igmp_group_delete_empty_include(group); -    } -  } - -  return 0; +	struct igmp_source *source; +	struct igmp_group *group; + +	source = THREAD_ARG(t); + +	group = source->source_group; + +	if (PIM_DEBUG_IGMP_TRACE) { +		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", +			__PRETTY_FUNCTION__, group_str, source_str, +			group->group_igmp_sock->interface->name); +	} + +	/* +	  RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules + +	  Group +	  Filter-Mode    Source Timer Value    Action +	  -----------    ------------------    ------ +	  INCLUDE        TIMER == 0            Suggest to stop forwarding +					       traffic from source and +					       remove source record.  If +					       there are no more source +					       records for the group, delete +					       group record. + +	  EXCLUDE        TIMER == 0            Suggest to not forward +					       traffic from source +					       (DO NOT remove record) + +	  Source timer switched from (T > 0) to (T == 0): disable forwarding. +	 */ + +	if (group->group_filtermode_isexcl) { +		/* EXCLUDE mode */ + +		igmp_source_forward_stop(source); +	} else { +		/* INCLUDE mode */ + +		/* igmp_source_delete() will stop forwarding source */ +		igmp_source_delete(source); + +		/* +		  If there are no more source records for the group, delete +		  group +		  record. +		*/ +		if (!listcount(group->group_source_list)) { +			igmp_group_delete_empty_include(group); +		} +	} + +	return 0;  }  static void source_timer_off(struct igmp_group *group,  			     struct igmp_source *source)  { -  if (!source->t_source_timer) -    return; -   -  if (PIM_DEBUG_IGMP_TRACE) { -    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", -	       group_str, source_str, -	       group->group_igmp_sock->interface->name); -  } - -  THREAD_OFF(source->t_source_timer); +	if (!source->t_source_timer) +		return; + +	if (PIM_DEBUG_IGMP_TRACE) { +		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", +			group_str, source_str, +			group->group_igmp_sock->interface->name); +	} + +	THREAD_OFF(source->t_source_timer);  }  static void igmp_source_timer_on(struct igmp_group *group, -				 struct igmp_source *source, -				 long interval_msec) +				 struct igmp_source *source, long interval_msec)  { -  source_timer_off(group, source); - -  if (PIM_DEBUG_IGMP_EVENTS) { -    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", -	       interval_msec / 1000, -	       interval_msec % 1000, -	       group_str, source_str, -	       group->group_igmp_sock->interface->name); -  } - -  thread_add_timer_msec(master, igmp_source_timer, source, interval_msec, -                        &source->t_source_timer); - -  /* -    RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules -     -    Source timer switched from (T == 0) to (T > 0): enable forwarding. -  */ -  igmp_source_forward_start(source); +	source_timer_off(group, source); + +	if (PIM_DEBUG_IGMP_EVENTS) { +		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", +			interval_msec / 1000, interval_msec % 1000, group_str, +			source_str, group->group_igmp_sock->interface->name); +	} + +	thread_add_timer_msec(master, igmp_source_timer, source, interval_msec, +			      &source->t_source_timer); + +	/* +	  RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules + +	  Source timer switched from (T == 0) to (T > 0): enable forwarding. +	*/ +	igmp_source_forward_start(source);  } -void igmp_source_reset_gmi(struct igmp_sock *igmp, -			   struct igmp_group *group, +void igmp_source_reset_gmi(struct igmp_sock *igmp, struct igmp_group *group,  			   struct igmp_source *source)  { -  long group_membership_interval_msec; -  struct pim_interface *pim_ifp; -  struct interface *ifp; - -  ifp = igmp->interface; -  pim_ifp = ifp->info; - -  group_membership_interval_msec = -    PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable, -		      igmp->querier_query_interval, -		      pim_ifp->igmp_query_max_response_time_dsec); - -  if (PIM_DEBUG_IGMP_TRACE) { -    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("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s", -	       source_str, -	       group_membership_interval_msec / 1000, -	       group_membership_interval_msec % 1000, -	       group_str, -	       ifp->name); -  } - -  igmp_source_timer_on(group, source, -		       group_membership_interval_msec); +	long group_membership_interval_msec; +	struct pim_interface *pim_ifp; +	struct interface *ifp; + +	ifp = igmp->interface; +	pim_ifp = ifp->info; + +	group_membership_interval_msec = PIM_IGMP_GMI_MSEC( +		igmp->querier_robustness_variable, igmp->querier_query_interval, +		pim_ifp->igmp_query_max_response_time_dsec); + +	if (PIM_DEBUG_IGMP_TRACE) { +		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( +			"Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s", +			source_str, group_membership_interval_msec / 1000, +			group_membership_interval_msec % 1000, group_str, +			ifp->name); +	} + +	igmp_source_timer_on(group, source, group_membership_interval_msec);  }  static void source_mark_delete_flag(struct igmp_group *group)  { -  struct listnode    *src_node; -  struct igmp_source *src; +	struct listnode *src_node; +	struct igmp_source *src; -  for (ALL_LIST_ELEMENTS_RO (group->group_source_list, src_node, src)) { -    IGMP_SOURCE_DO_DELETE(src->source_flags); -  } +	for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { +		IGMP_SOURCE_DO_DELETE(src->source_flags); +	}  } -static void source_mark_send_flag (struct igmp_group *group) +static void source_mark_send_flag(struct igmp_group *group)  { -  struct listnode    *src_node; -  struct igmp_source *src; +	struct listnode *src_node; +	struct igmp_source *src; -  for (ALL_LIST_ELEMENTS_RO (group->group_source_list, src_node, src)) { -    IGMP_SOURCE_DO_SEND(src->source_flags); -  } +	for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { +		IGMP_SOURCE_DO_SEND(src->source_flags); +	}  } -static int source_mark_send_flag_by_timer (struct igmp_group *group) +static int source_mark_send_flag_by_timer(struct igmp_group *group)  { -  struct listnode    *src_node; -  struct igmp_source *src; -  int                 num_marked_sources = 0; - -  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { -    /* Is source timer running? */ -    if (src->t_source_timer) { -      IGMP_SOURCE_DO_SEND(src->source_flags); -      ++num_marked_sources; -    } -    else { -      IGMP_SOURCE_DONT_SEND(src->source_flags); -    } -  } - -  return num_marked_sources; +	struct listnode *src_node; +	struct igmp_source *src; +	int num_marked_sources = 0; + +	for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { +		/* Is source timer running? */ +		if (src->t_source_timer) { +			IGMP_SOURCE_DO_SEND(src->source_flags); +			++num_marked_sources; +		} else { +			IGMP_SOURCE_DONT_SEND(src->source_flags); +		} +	} + +	return num_marked_sources;  }  static void source_clear_send_flag(struct list *source_list)  { -  struct listnode    *src_node; -  struct igmp_source *src; +	struct listnode *src_node; +	struct igmp_source *src; -  for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { -    IGMP_SOURCE_DONT_SEND(src->source_flags); -  } +	for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) { +		IGMP_SOURCE_DONT_SEND(src->source_flags); +	}  }  /* @@ -309,27 +310,27 @@ static void source_clear_send_flag(struct list *source_list)  */  static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)  { -  zassert(group->group_filtermode_isexcl); +	zassert(group->group_filtermode_isexcl); -  if (listcount(group->group_source_list) < 1) { -    igmp_anysource_forward_start(group); -  } +	if (listcount(group->group_source_list) < 1) { +		igmp_anysource_forward_start(group); +	}  }  void igmp_source_free(struct igmp_source *source)  { -  /* make sure there is no source timer running */ -  zassert(!source->t_source_timer); +	/* make sure there is no source timer running */ +	zassert(!source->t_source_timer); -  XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source); +	XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);  }  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 = NULL; -  } +	if (source->source_channel_oil) { +		pim_channel_oil_del(source->source_channel_oil); +		source->source_channel_oil = NULL; +	}  }  /* @@ -338,662 +339,659 @@ static void source_channel_oil_detach(struct igmp_source *source)  */  void igmp_source_delete(struct igmp_source *source)  { -  struct igmp_group *group; -  struct in_addr src; - -  group = source->source_group; - -  if (PIM_DEBUG_IGMP_TRACE) { -    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 c_oil ref_count %d", -	       source_str, group_str, -	       group->group_igmp_sock->fd, -	       group->group_igmp_sock->interface->name, -               source->source_channel_oil ? source->source_channel_oil->oil_ref_count : 0); -  } - -  source_timer_off(group, source); -  igmp_source_forward_stop(source); - -  /* sanity check that forwarding has been disabled */ -  if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { -    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", -	      __PRETTY_FUNCTION__, -	      source_str, group_str, -	      group->group_igmp_sock->fd, -	      group->group_igmp_sock->interface->name); -    /* warning only */ -  } - -  source_channel_oil_detach(source); - -  /* -    notice that listnode_delete() can't be moved -    into igmp_source_free() because the later is -    called by list_delete_all_node() -  */ -  listnode_delete(group->group_source_list, source); - -  src.s_addr = source->source_addr.s_addr; -  igmp_source_free(source); - -  /* Group source list is empty and current source is * then -   *,G group going away so do not trigger start */ -  if (group->group_filtermode_isexcl && -      (listcount (group->group_source_list) != 0) && -      src.s_addr != INADDR_ANY) -    { -      group_exclude_fwd_anysrc_ifempty (group); -    } +	struct igmp_group *group; +	struct in_addr src; + +	group = source->source_group; + +	if (PIM_DEBUG_IGMP_TRACE) { +		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 c_oil ref_count %d", +			source_str, group_str, group->group_igmp_sock->fd, +			group->group_igmp_sock->interface->name, +			source->source_channel_oil +				? source->source_channel_oil->oil_ref_count +				: 0); +	} + +	source_timer_off(group, source); +	igmp_source_forward_stop(source); + +	/* sanity check that forwarding has been disabled */ +	if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) { +		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", +			__PRETTY_FUNCTION__, source_str, group_str, +			group->group_igmp_sock->fd, +			group->group_igmp_sock->interface->name); +		/* warning only */ +	} + +	source_channel_oil_detach(source); + +	/* +	  notice that listnode_delete() can't be moved +	  into igmp_source_free() because the later is +	  called by list_delete_all_node() +	*/ +	listnode_delete(group->group_source_list, source); + +	src.s_addr = source->source_addr.s_addr; +	igmp_source_free(source); + +	/* Group source list is empty and current source is * then +	 *,G group going away so do not trigger start */ +	if (group->group_filtermode_isexcl +	    && (listcount(group->group_source_list) != 0) +	    && src.s_addr != INADDR_ANY) { +		group_exclude_fwd_anysrc_ifempty(group); +	}  }  static void source_delete_by_flag(struct list *source_list)  { -  struct listnode    *src_node; -  struct listnode    *src_nextnode; -  struct igmp_source *src; -   -  for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) -    if (IGMP_SOURCE_TEST_DELETE(src->source_flags)) -      igmp_source_delete(src); +	struct listnode *src_node; +	struct listnode *src_nextnode; +	struct igmp_source *src; + +	for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) +		if (IGMP_SOURCE_TEST_DELETE(src->source_flags)) +			igmp_source_delete(src);  }  void igmp_source_delete_expired(struct list *source_list)  { -  struct listnode    *src_node; -  struct listnode    *src_nextnode; -  struct igmp_source *src; -   -  for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) -    if (!src->t_source_timer) -      igmp_source_delete(src); +	struct listnode *src_node; +	struct listnode *src_nextnode; +	struct igmp_source *src; + +	for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src)) +		if (!src->t_source_timer) +			igmp_source_delete(src);  }  struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,  					     struct in_addr src_addr)  { -  struct listnode    *src_node; -  struct igmp_source *src; +	struct listnode *src_node; +	struct igmp_source *src; -  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) -    if (src_addr.s_addr == src->source_addr.s_addr) -      return src; +	for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) +		if (src_addr.s_addr == src->source_addr.s_addr) +			return src; -  return 0; +	return 0;  } -struct igmp_source * -source_new (struct igmp_group *group, -	    struct in_addr src_addr) +struct igmp_source *source_new(struct igmp_group *group, +			       struct in_addr src_addr)  { -  struct igmp_source *src; - -  if (PIM_DEBUG_IGMP_TRACE) { -    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", -	       source_str, group_str, -	       group->group_igmp_sock->fd, -	       group->group_igmp_sock->interface->name); -  } - -  src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); -  if (!src) { -    zlog_warn("%s %s: XCALLOC() failure", -	      __FILE__, __PRETTY_FUNCTION__); -    return 0; /* error, not found, could not create */ -  } -   -  src->t_source_timer                = NULL; -  src->source_group                  = group; /* back pointer */ -  src->source_addr                   = src_addr; -  src->source_creation               = pim_time_monotonic_sec(); -  src->source_flags                  = 0; -  src->source_query_retransmit_count = 0; -  src->source_channel_oil            = NULL; - -  listnode_add(group->group_source_list, src); - -  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ -  igmp_anysource_forward_stop(group); - -  return src; +	struct igmp_source *src; + +	if (PIM_DEBUG_IGMP_TRACE) { +		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", +			source_str, group_str, group->group_igmp_sock->fd, +			group->group_igmp_sock->interface->name); +	} + +	src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src)); +	if (!src) { +		zlog_warn("%s %s: XCALLOC() failure", __FILE__, +			  __PRETTY_FUNCTION__); +		return 0; /* error, not found, could not create */ +	} + +	src->t_source_timer = NULL; +	src->source_group = group; /* back pointer */ +	src->source_addr = src_addr; +	src->source_creation = pim_time_monotonic_sec(); +	src->source_flags = 0; +	src->source_query_retransmit_count = 0; +	src->source_channel_oil = NULL; + +	listnode_add(group->group_source_list, src); + +	/* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ +	igmp_anysource_forward_stop(group); + +	return src;  }  static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,  					      struct igmp_group *group,  					      struct in_addr src_addr)  { -  struct igmp_source *src; +	struct igmp_source *src; -  src = igmp_find_source_by_addr(group, src_addr); -  if (src) { -    return src; -  } +	src = igmp_find_source_by_addr(group, src_addr); +	if (src) { +		return src; +	} -  src = source_new(group, src_addr); -  if (!src) { -    return 0; -  } +	src = source_new(group, src_addr); +	if (!src) { +		return 0; +	} -  return src; +	return src;  }  static void allow(struct igmp_sock *igmp, struct in_addr from, -		  struct in_addr group_addr, -		  int num_sources, struct in_addr *sources) +		  struct in_addr group_addr, int num_sources, +		  struct in_addr *sources)  { -  struct igmp_source *source; -  struct igmp_group *group; -  int    i; - -  /* non-existant group is created as INCLUDE {empty} */ -  group = igmp_add_group_by_addr(igmp, group_addr); -  if (!group) { -    return; -  } - -  /* scan received sources */ -  for (i = 0; i < num_sources; ++i) { -    struct in_addr     *src_addr; - -    src_addr = sources + i; - -    source = add_source_by_addr(igmp, group, *src_addr); -    if (!source) { -      continue; -    } - -    /* -      RFC 3376: 6.4.1. Reception of Current-State Records - -      When receiving IS_IN reports for groups in EXCLUDE mode is -      sources should be moved from set with (timers = 0) to set with -      (timers > 0). - -      igmp_source_reset_gmi() below, resetting the source timers to -      GMI, accomplishes this. -    */ -    igmp_source_reset_gmi(igmp, group, source); - -  } /* scan received sources */ - -  if ((num_sources == 0) && -      (group->group_filtermode_isexcl) && -      (listcount (group->group_source_list) == 1)) -    { -      struct in_addr star = { .s_addr = INADDR_ANY }; - -      source = igmp_find_source_by_addr (group, star); -      if (source) -	igmp_source_reset_gmi (igmp, group, source); -    } +	struct igmp_source *source; +	struct igmp_group *group; +	int i; + +	/* non-existant group is created as INCLUDE {empty} */ +	group = igmp_add_group_by_addr(igmp, group_addr); +	if (!group) { +		return; +	} + +	/* scan received sources */ +	for (i = 0; i < num_sources; ++i) { +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		source = add_source_by_addr(igmp, group, *src_addr); +		if (!source) { +			continue; +		} + +		/* +		  RFC 3376: 6.4.1. Reception of Current-State Records + +		  When receiving IS_IN reports for groups in EXCLUDE mode is +		  sources should be moved from set with (timers = 0) to set with +		  (timers > 0). + +		  igmp_source_reset_gmi() below, resetting the source timers to +		  GMI, accomplishes this. +		*/ +		igmp_source_reset_gmi(igmp, group, source); + +	} /* scan received sources */ + +	if ((num_sources == 0) && (group->group_filtermode_isexcl) +	    && (listcount(group->group_source_list) == 1)) { +		struct in_addr star = {.s_addr = INADDR_ANY}; + +		source = igmp_find_source_by_addr(group, star); +		if (source) +			igmp_source_reset_gmi(igmp, group, source); +	}  }  void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from, -			struct in_addr group_addr, -			int num_sources, struct in_addr *sources) +			struct in_addr group_addr, int num_sources, +			struct in_addr *sources)  { -  on_trace(__PRETTY_FUNCTION__, -	   igmp->interface, from, group_addr, num_sources, sources); +	on_trace(__PRETTY_FUNCTION__, igmp->interface, from, group_addr, +		 num_sources, sources); -  allow(igmp, from, group_addr, num_sources, sources); +	allow(igmp, from, group_addr, num_sources, sources);  } -static void isex_excl(struct igmp_group *group, -		      int num_sources, struct in_addr *sources) +static void isex_excl(struct igmp_group *group, int num_sources, +		      struct in_addr *sources)  { -  struct igmp_source *source; -  int     i; - -  /* EXCLUDE mode */ -  zassert(group->group_filtermode_isexcl); -   -  /* E.1: set deletion flag for known sources (X,Y) */ -  source_mark_delete_flag (group); - -  /* scan received sources (A) */ -  for (i = 0; i < num_sources; ++i) { -    struct in_addr     *src_addr; - -    src_addr = sources + i; - -    /* E.2: lookup reported source from (A) in (X,Y) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ -      IGMP_SOURCE_DONT_DELETE(source->source_flags); -    } -    else { -      /* E.4: if not found, create source with timer=GMI: (A-X-Y) */ -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } -      zassert(!source->t_source_timer); /* timer == 0 */ -      igmp_source_reset_gmi(group->group_igmp_sock, group, source); -      zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ -    } - -  } /* scan received sources */ - -  /* -   * If we are in isexcl mode and num_sources == 0 -   * than that means we have a *,g entry that -   * needs to be handled -   */ -  if (group->group_filtermode_isexcl && num_sources == 0) -    { -       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); -         } -    } - -  /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */ -  source_delete_by_flag(group->group_source_list); +	struct igmp_source *source; +	int i; + +	/* EXCLUDE mode */ +	zassert(group->group_filtermode_isexcl); + +	/* E.1: set deletion flag for known sources (X,Y) */ +	source_mark_delete_flag(group); + +	/* scan received sources (A) */ +	for (i = 0; i < num_sources; ++i) { +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* E.2: lookup reported source from (A) in (X,Y) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			/* E.3: if found, clear deletion flag: (X*A) or (Y*A) */ +			IGMP_SOURCE_DONT_DELETE(source->source_flags); +		} else { +			/* E.4: if not found, create source with timer=GMI: +			 * (A-X-Y) */ +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} +			zassert(!source->t_source_timer); /* timer == 0 */ +			igmp_source_reset_gmi(group->group_igmp_sock, group, +					      source); +			zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ +		} + +	} /* scan received sources */ + +	/* +	 * If we are in isexcl mode and num_sources == 0 +	 * than that means we have a *,g entry that +	 * needs to be handled +	 */ +	if (group->group_filtermode_isexcl && num_sources == 0) { +		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); +		} +	} + +	/* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */ +	source_delete_by_flag(group->group_source_list);  } -static void isex_incl(struct igmp_group *group, -		      int num_sources, struct in_addr *sources) +static void isex_incl(struct igmp_group *group, int num_sources, +		      struct in_addr *sources)  { -  int i; - -  /* INCLUDE mode */ -  zassert(!group->group_filtermode_isexcl); -   -  /* I.1: set deletion flag for known sources (A) */ -  source_mark_delete_flag (group); - -  /* scan received sources (B) */ -  for (i = 0; i < num_sources; ++i) { -    struct igmp_source *source; -    struct in_addr     *src_addr; - -    src_addr = sources + i; - -    /* I.2: lookup reported source (B) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      /* I.3: if found, clear deletion flag (A*B) */ -      IGMP_SOURCE_DONT_DELETE(source->source_flags); -    } -    else { -      /* I.4: if not found, create source with timer=0 (B-A) */ -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } -      zassert(!source->t_source_timer); /* (B-A) timer=0 */ -    } - -  } /* scan received sources */ - -  /* I.5: delete all sources marked with deletion flag (A-B) */ -  source_delete_by_flag(group->group_source_list); - -  group->group_filtermode_isexcl = 1; /* boolean=true */ - -  zassert(group->group_filtermode_isexcl); - -  group_exclude_fwd_anysrc_ifempty(group); +	int i; + +	/* INCLUDE mode */ +	zassert(!group->group_filtermode_isexcl); + +	/* I.1: set deletion flag for known sources (A) */ +	source_mark_delete_flag(group); + +	/* scan received sources (B) */ +	for (i = 0; i < num_sources; ++i) { +		struct igmp_source *source; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* I.2: lookup reported source (B) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			/* I.3: if found, clear deletion flag (A*B) */ +			IGMP_SOURCE_DONT_DELETE(source->source_flags); +		} else { +			/* I.4: if not found, create source with timer=0 (B-A) +			 */ +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} +			zassert(!source->t_source_timer); /* (B-A) timer=0 */ +		} + +	} /* scan received sources */ + +	/* I.5: delete all sources marked with deletion flag (A-B) */ +	source_delete_by_flag(group->group_source_list); + +	group->group_filtermode_isexcl = 1; /* boolean=true */ + +	zassert(group->group_filtermode_isexcl); + +	group_exclude_fwd_anysrc_ifempty(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 from_igmp_v2_report) +			struct in_addr group_addr, int num_sources, +			struct in_addr *sources, int from_igmp_v2_report)  { -  struct interface *ifp = igmp->interface; -  struct igmp_group *group; - -  on_trace(__PRETTY_FUNCTION__, -	   ifp, from, group_addr, num_sources, sources); - -  /* non-existant group is created as INCLUDE {empty} */ -  group = igmp_add_group_by_addr(igmp, group_addr); -  if (!group) { -    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); -  } -  else { -    /* INCLUDE mode */ -    isex_incl(group, num_sources, sources); -    zassert(group->group_filtermode_isexcl); -  } - -  zassert(group->group_filtermode_isexcl); - -  igmp_group_reset_gmi(group); +	struct interface *ifp = igmp->interface; +	struct igmp_group *group; + +	on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, +		 sources); + +	/* non-existant group is created as INCLUDE {empty} */ +	group = igmp_add_group_by_addr(igmp, group_addr); +	if (!group) { +		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); +	} else { +		/* INCLUDE mode */ +		isex_incl(group, num_sources, sources); +		zassert(group->group_filtermode_isexcl); +	} + +	zassert(group->group_filtermode_isexcl); + +	igmp_group_reset_gmi(group);  } -static void toin_incl(struct igmp_group *group, -		      int num_sources, struct in_addr *sources) +static void toin_incl(struct igmp_group *group, int num_sources, +		      struct in_addr *sources)  { -  struct igmp_sock *igmp = group->group_igmp_sock; -  int num_sources_tosend = listcount(group->group_source_list); -  int i; - -  /* Set SEND flag for all known sources (A) */ -  source_mark_send_flag (group); - -  /* Scan received sources (B) */ -  for (i = 0; i < num_sources; ++i) { -    struct igmp_source *source; -    struct in_addr     *src_addr; - -    src_addr = sources + i; - -    /* Lookup reported source (B) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      /* If found, clear SEND flag (A*B) */ -      IGMP_SOURCE_DONT_SEND(source->source_flags); -      --num_sources_tosend; -    } -    else { -      /* If not found, create new source */ -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } -    } - -    /* (B)=GMI */ -    igmp_source_reset_gmi(igmp, group, source); -  } - -  /* Send sources marked with SEND flag: Q(G,A-B) */ -  if (num_sources_tosend > 0) { -    source_query_send_by_flag(group, num_sources_tosend); -  } +	struct igmp_sock *igmp = group->group_igmp_sock; +	int num_sources_tosend = listcount(group->group_source_list); +	int i; + +	/* Set SEND flag for all known sources (A) */ +	source_mark_send_flag(group); + +	/* Scan received sources (B) */ +	for (i = 0; i < num_sources; ++i) { +		struct igmp_source *source; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* Lookup reported source (B) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			/* If found, clear SEND flag (A*B) */ +			IGMP_SOURCE_DONT_SEND(source->source_flags); +			--num_sources_tosend; +		} else { +			/* If not found, create new source */ +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} +		} + +		/* (B)=GMI */ +		igmp_source_reset_gmi(igmp, group, source); +	} + +	/* Send sources marked with SEND flag: Q(G,A-B) */ +	if (num_sources_tosend > 0) { +		source_query_send_by_flag(group, num_sources_tosend); +	}  } -static void toin_excl(struct igmp_group *group, -		      int num_sources, struct in_addr *sources) +static void toin_excl(struct igmp_group *group, int num_sources, +		      struct in_addr *sources)  { -  struct igmp_sock *igmp = group->group_igmp_sock; -  int num_sources_tosend; -  int i; - -  /* Set SEND flag for X (sources with timer > 0) */ -  num_sources_tosend = source_mark_send_flag_by_timer (group); - -  /* Scan received sources (A) */ -  for (i = 0; i < num_sources; ++i) { -    struct igmp_source *source; -    struct in_addr     *src_addr; - -    src_addr = sources + i; - -    /* Lookup reported source (A) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      if (source->t_source_timer) { -	/* If found and timer running, clear SEND flag (X*A) */ -	IGMP_SOURCE_DONT_SEND(source->source_flags); -	--num_sources_tosend; -      } -    } -    else { -      /* If not found, create new source */ -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } -    } - -    /* (A)=GMI */ -    igmp_source_reset_gmi(igmp, group, source); -  } - -  /* Send sources marked with SEND flag: Q(G,X-A) */ -  if (num_sources_tosend > 0) { -    source_query_send_by_flag(group, num_sources_tosend); -  } - -  /* Send Q(G) */ -  group_query_send(group); +	struct igmp_sock *igmp = group->group_igmp_sock; +	int num_sources_tosend; +	int i; + +	/* Set SEND flag for X (sources with timer > 0) */ +	num_sources_tosend = source_mark_send_flag_by_timer(group); + +	/* Scan received sources (A) */ +	for (i = 0; i < num_sources; ++i) { +		struct igmp_source *source; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* Lookup reported source (A) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			if (source->t_source_timer) { +				/* If found and timer running, clear SEND flag +				 * (X*A) */ +				IGMP_SOURCE_DONT_SEND(source->source_flags); +				--num_sources_tosend; +			} +		} else { +			/* If not found, create new source */ +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} +		} + +		/* (A)=GMI */ +		igmp_source_reset_gmi(igmp, group, source); +	} + +	/* Send sources marked with SEND flag: Q(G,X-A) */ +	if (num_sources_tosend > 0) { +		source_query_send_by_flag(group, num_sources_tosend); +	} + +	/* Send Q(G) */ +	group_query_send(group);  }  void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from, -			struct in_addr group_addr, -			int num_sources, struct in_addr *sources) +			struct in_addr group_addr, int num_sources, +			struct in_addr *sources)  { -  struct interface *ifp = igmp->interface; -  struct igmp_group *group; - -  on_trace(__PRETTY_FUNCTION__, -	   ifp, from, group_addr, num_sources, sources); - -  /* -   * If the requested filter mode is INCLUDE *and* the requested source -   * list is empty, then the entry corresponding to the requested -   * interface and multicast address is deleted if present.  If no such -   * entry is present, the request is ignored. -   */ -  if (num_sources) -    { -      /* non-existant group is created as INCLUDE {empty} */ -      group = igmp_add_group_by_addr(igmp, group_addr); -      if (!group) { -	return; -      } -    } -  else -    { -      group = find_group_by_addr (igmp, group_addr); -      if (!group) -	return; -    } - -  if (group->group_filtermode_isexcl) { -    /* EXCLUDE mode */ -    toin_excl(group, num_sources, sources); -  } -  else { -    /* INCLUDE mode */ -    toin_incl(group, num_sources, sources); -  } +	struct interface *ifp = igmp->interface; +	struct igmp_group *group; + +	on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, +		 sources); + +	/* +	 * If the requested filter mode is INCLUDE *and* the requested source +	 * list is empty, then the entry corresponding to the requested +	 * interface and multicast address is deleted if present.  If no such +	 * entry is present, the request is ignored. +	 */ +	if (num_sources) { +		/* non-existant group is created as INCLUDE {empty} */ +		group = igmp_add_group_by_addr(igmp, group_addr); +		if (!group) { +			return; +		} +	} else { +		group = find_group_by_addr(igmp, group_addr); +		if (!group) +			return; +	} + +	if (group->group_filtermode_isexcl) { +		/* EXCLUDE mode */ +		toin_excl(group, num_sources, sources); +	} else { +		/* INCLUDE mode */ +		toin_incl(group, num_sources, sources); +	}  } -static void toex_incl(struct igmp_group *group, -		      int num_sources, struct in_addr *sources) +static void toex_incl(struct igmp_group *group, int num_sources, +		      struct in_addr *sources)  { -  int num_sources_tosend = 0; -  int i; - -  zassert(!group->group_filtermode_isexcl); - -  /* Set DELETE flag for all known sources (A) */ -  source_mark_delete_flag (group); - -  /* Clear off SEND flag from all known sources (A) */ -  source_clear_send_flag(group->group_source_list); - -  /* Scan received sources (B) */ -  for (i = 0; i < num_sources; ++i) { -    struct igmp_source *source; -    struct in_addr     *src_addr; - -    src_addr = sources + i; - -    /* Lookup reported source (B) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      /* If found, clear deletion flag: (A*B) */ -      IGMP_SOURCE_DONT_DELETE(source->source_flags); -      /* and set SEND flag (A*B) */ -      IGMP_SOURCE_DO_SEND(source->source_flags); -      ++num_sources_tosend; -    } -    else { -      /* If source not found, create source with timer=0: (B-A)=0 */ -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } -      zassert(!source->t_source_timer); /* (B-A) timer=0 */ -    } - -  } /* Scan received sources (B) */ - -  group->group_filtermode_isexcl = 1; /* boolean=true */ - -  /* Delete all sources marked with DELETE flag (A-B) */ -  source_delete_by_flag(group->group_source_list); - -  /* Send sources marked with SEND flag: Q(G,A*B) */ -  if (num_sources_tosend > 0) { -    source_query_send_by_flag(group, num_sources_tosend); -  } - -  zassert(group->group_filtermode_isexcl); - -  group_exclude_fwd_anysrc_ifempty(group); +	int num_sources_tosend = 0; +	int i; + +	zassert(!group->group_filtermode_isexcl); + +	/* Set DELETE flag for all known sources (A) */ +	source_mark_delete_flag(group); + +	/* Clear off SEND flag from all known sources (A) */ +	source_clear_send_flag(group->group_source_list); + +	/* Scan received sources (B) */ +	for (i = 0; i < num_sources; ++i) { +		struct igmp_source *source; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* Lookup reported source (B) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			/* If found, clear deletion flag: (A*B) */ +			IGMP_SOURCE_DONT_DELETE(source->source_flags); +			/* and set SEND flag (A*B) */ +			IGMP_SOURCE_DO_SEND(source->source_flags); +			++num_sources_tosend; +		} else { +			/* If source not found, create source with timer=0: +			 * (B-A)=0 */ +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} +			zassert(!source->t_source_timer); /* (B-A) timer=0 */ +		} + +	} /* Scan received sources (B) */ + +	group->group_filtermode_isexcl = 1; /* boolean=true */ + +	/* Delete all sources marked with DELETE flag (A-B) */ +	source_delete_by_flag(group->group_source_list); + +	/* Send sources marked with SEND flag: Q(G,A*B) */ +	if (num_sources_tosend > 0) { +		source_query_send_by_flag(group, num_sources_tosend); +	} + +	zassert(group->group_filtermode_isexcl); + +	group_exclude_fwd_anysrc_ifempty(group);  } -static void toex_excl(struct igmp_group *group, -		      int num_sources, struct in_addr *sources) +static void toex_excl(struct igmp_group *group, int num_sources, +		      struct in_addr *sources)  { -  int num_sources_tosend = 0; -  int i; - -  /* set DELETE flag for all known sources (X,Y) */ -  source_mark_delete_flag (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; -    struct in_addr     *src_addr; -     -    src_addr = sources + i; -     -    /* lookup reported source (A) in known sources (X,Y) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      /* if found, clear off DELETE flag from reported source (A) */ -      IGMP_SOURCE_DONT_DELETE(source->source_flags); -    } -    else { -      /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */ -      long group_timer_msec; -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } - -      zassert(!source->t_source_timer); /* timer == 0 */ -      group_timer_msec = igmp_group_timer_remain_msec(group); -      igmp_source_timer_on(group, source, group_timer_msec); -      zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ - -      /* make sure source is created with DELETE flag unset */ -      zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); -    } - -    /* make sure reported source has DELETE flag unset */ -    zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); - -    if (source->t_source_timer) { -      /* if source timer>0 mark SEND flag: Q(G,A-Y) */ -      IGMP_SOURCE_DO_SEND(source->source_flags); -      ++num_sources_tosend; -    } - -  } /* scan received sources (A) */ - -  /* -    delete all sources marked with DELETE flag: -    Delete (X-A) -    Delete (Y-A) -  */ -  source_delete_by_flag(group->group_source_list); -  -  /* send sources marked with SEND flag: Q(G,A-Y) */ -  if (num_sources_tosend > 0) { -    source_query_send_by_flag(group, num_sources_tosend); -  } +	int num_sources_tosend = 0; +	int i; + +	/* set DELETE flag for all known sources (X,Y) */ +	source_mark_delete_flag(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; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* lookup reported source (A) in known sources (X,Y) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			/* if found, clear off DELETE flag from reported source +			 * (A) */ +			IGMP_SOURCE_DONT_DELETE(source->source_flags); +		} else { +			/* if not found, create source with Group Timer: +			 * (A-X-Y)=Group Timer */ +			long group_timer_msec; +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} + +			zassert(!source->t_source_timer); /* timer == 0 */ +			group_timer_msec = igmp_group_timer_remain_msec(group); +			igmp_source_timer_on(group, source, group_timer_msec); +			zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ + +			/* make sure source is created with DELETE flag unset */ +			zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); +		} + +		/* make sure reported source has DELETE flag unset */ +		zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags)); + +		if (source->t_source_timer) { +			/* if source timer>0 mark SEND flag: Q(G,A-Y) */ +			IGMP_SOURCE_DO_SEND(source->source_flags); +			++num_sources_tosend; +		} + +	} /* scan received sources (A) */ + +	/* +	  delete all sources marked with DELETE flag: +	  Delete (X-A) +	  Delete (Y-A) +	*/ +	source_delete_by_flag(group->group_source_list); + +	/* send sources marked with SEND flag: Q(G,A-Y) */ +	if (num_sources_tosend > 0) { +		source_query_send_by_flag(group, num_sources_tosend); +	}  }  void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from, -			struct in_addr group_addr, -			int num_sources, struct in_addr *sources) +			struct in_addr group_addr, int num_sources, +			struct in_addr *sources)  { -  struct interface *ifp = igmp->interface; -  struct igmp_group *group; - -  on_trace(__PRETTY_FUNCTION__, -	   ifp, from, group_addr, num_sources, sources); - -  /* non-existant group is created as INCLUDE {empty} */ -  group = igmp_add_group_by_addr(igmp, group_addr); -  if (!group) { -    return; -  } - -  if (group->group_filtermode_isexcl) { -    /* EXCLUDE mode */ -    toex_excl(group, num_sources, sources); -  } -  else { -    /* INCLUDE mode */ -    toex_incl(group, num_sources, sources); -    zassert(group->group_filtermode_isexcl); -  } -  zassert(group->group_filtermode_isexcl); - -  /* Group Timer=GMI */ -  igmp_group_reset_gmi(group); +	struct interface *ifp = igmp->interface; +	struct igmp_group *group; + +	on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, +		 sources); + +	/* non-existant group is created as INCLUDE {empty} */ +	group = igmp_add_group_by_addr(igmp, group_addr); +	if (!group) { +		return; +	} + +	if (group->group_filtermode_isexcl) { +		/* EXCLUDE mode */ +		toex_excl(group, num_sources, sources); +	} else { +		/* INCLUDE mode */ +		toex_incl(group, num_sources, sources); +		zassert(group->group_filtermode_isexcl); +	} +	zassert(group->group_filtermode_isexcl); + +	/* Group Timer=GMI */ +	igmp_group_reset_gmi(group);  }  void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from, -			 struct in_addr group_addr, -			 int num_sources, struct in_addr *sources) +			 struct in_addr group_addr, int num_sources, +			 struct in_addr *sources)  { -  on_trace(__PRETTY_FUNCTION__, -	   igmp->interface, from, group_addr, num_sources, sources); +	on_trace(__PRETTY_FUNCTION__, igmp->interface, from, group_addr, +		 num_sources, sources); -  allow(igmp, from, group_addr, num_sources, sources); +	allow(igmp, from, group_addr, num_sources, sources);  }  /* @@ -1005,67 +1003,64 @@ void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,  */  static void group_retransmit_group(struct igmp_group *group)  { -  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; - -  /* -    RFC3376: 6.6.3.1. Building and Sending Group Specific Queries -     -    When transmitting a group specific query, if the group timer is -    larger than LMQT, the "Suppress Router-Side Processing" bit is set -    in the query message. -  */ -  s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec; - -  if (PIM_DEBUG_IGMP_TRACE) { -    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, -	       group->group_specific_query_retransmit_count); -  } - -  /* -    RFC3376: 4.1.12. IP Destination Addresses for Queries - -    Group-Specific and Group-and-Source-Specific Queries are sent with -    an IP destination address equal to the multicast address of -    interest. -  */ - -  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); +	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; + +	/* +	  RFC3376: 6.6.3.1. Building and Sending Group Specific Queries + +	  When transmitting a group specific query, if the group timer is +	  larger than LMQT, the "Suppress Router-Side Processing" bit is set +	  in the query message. +	*/ +	s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec; + +	if (PIM_DEBUG_IGMP_TRACE) { +		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, +			group->group_specific_query_retransmit_count); +	} + +	/* +	  RFC3376: 4.1.12. IP Destination Addresses for Queries + +	  Group-Specific and Group-and-Source-Specific Queries are sent with +	  an IP destination address equal to the multicast address of +	  interest. +	*/ + +	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);  }  /* @@ -1083,205 +1078,214 @@ static void group_retransmit_group(struct igmp_group *group)  static int group_retransmit_sources(struct igmp_group *group,  				    int send_with_sflag_set)  { -  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 */ -  char                  query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */ -  char                  query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */ -  int                   query_buf1_max_sources; -  int                   query_buf2_max_sources; -  struct in_addr       *source_addr1; -  struct in_addr       *source_addr2; -  int                   num_sources_tosend1; -  int                   num_sources_tosend2; -  struct listnode      *src_node; -  struct igmp_source   *src; -  int                   num_retransmit_sources_left = 0; -   -  source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); -  source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); - -  igmp = group->group_igmp_sock; -  pim_ifp = igmp->interface->info; - -  lmqc      = igmp->querier_robustness_variable; -  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; -  lmqt_msec = lmqc * lmqi_msec; - -  /* Scan all group sources */ -  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { - -    /* Source has retransmission state? */ -    if (src->source_query_retransmit_count < 1) -      continue; - -    if (--src->source_query_retransmit_count > 0) { -      ++num_retransmit_sources_left; -    } - -    /* Copy source address into appropriate query buffer */ -    if (igmp_source_timer_remain_msec(src) > lmqt_msec) { -      *source_addr1 = src->source_addr; -      ++source_addr1; -    } -    else { -      *source_addr2 = src->source_addr; -      ++source_addr2; -    } - -  } -   -  num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); -  num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); - -  if (PIM_DEBUG_IGMP_TRACE) { -    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, -	       num_sources_tosend1, -	       num_sources_tosend2, -	       send_with_sflag_set, -	       num_retransmit_sources_left); -  } - -  if (num_sources_tosend1 > 0) { -    /* -      Send group-and-source-specific query with s_flag set and all -      sources with timers greater than LMQT. -    */ - -    if (send_with_sflag_set) { - -      query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2; -      if (num_sources_tosend1 > query_buf1_max_sources) { -	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, -		  num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources); -      } -      else { -	/* -	  RFC3376: 4.1.12. IP Destination Addresses for Queries -       -	  Group-Specific and Group-and-Source-Specific Queries are sent with -	  an IP destination address equal to the multicast address of -	  interest. -	*/ -     -        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 */ - -  } - -  if (num_sources_tosend2 > 0) { -    /* -      Send group-and-source-specific query with s_flag clear and all -      sources with timers lower or equal to LMQT. -    */ -   -    query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; -    if (num_sources_tosend2 > query_buf2_max_sources) { -      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, -		num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources); -    } -    else { -      /* -	RFC3376: 4.1.12. IP Destination Addresses for Queries - -	Group-Specific and Group-and-Source-Specific Queries are sent with -	an IP destination address equal to the multicast address of -	interest. -      */ - -      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); -    } -  } - -  return num_retransmit_sources_left; +	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 */ +	char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */ +	char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */ +	int query_buf1_max_sources; +	int query_buf2_max_sources; +	struct in_addr *source_addr1; +	struct in_addr *source_addr2; +	int num_sources_tosend1; +	int num_sources_tosend2; +	struct listnode *src_node; +	struct igmp_source *src; +	int num_retransmit_sources_left = 0; + +	source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); +	source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); + +	igmp = group->group_igmp_sock; +	pim_ifp = igmp->interface->info; + +	lmqc = igmp->querier_robustness_variable; +	lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; +	lmqt_msec = lmqc * lmqi_msec; + +	/* Scan all group sources */ +	for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { + +		/* Source has retransmission state? */ +		if (src->source_query_retransmit_count < 1) +			continue; + +		if (--src->source_query_retransmit_count > 0) { +			++num_retransmit_sources_left; +		} + +		/* Copy source address into appropriate query buffer */ +		if (igmp_source_timer_remain_msec(src) > lmqt_msec) { +			*source_addr1 = src->source_addr; +			++source_addr1; +		} else { +			*source_addr2 = src->source_addr; +			++source_addr2; +		} +	} + +	num_sources_tosend1 = +		source_addr1 +		- (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET); +	num_sources_tosend2 = +		source_addr2 +		- (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET); + +	if (PIM_DEBUG_IGMP_TRACE) { +		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, num_sources_tosend1, +			num_sources_tosend2, send_with_sflag_set, +			num_retransmit_sources_left); +	} + +	if (num_sources_tosend1 > 0) { +		/* +		  Send group-and-source-specific query with s_flag set and all +		  sources with timers greater than LMQT. +		*/ + +		if (send_with_sflag_set) { + +			query_buf1_max_sources = +				(sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) +				>> 2; +			if (num_sources_tosend1 > query_buf1_max_sources) { +				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, +					num_sources_tosend1, sizeof(query_buf1), +					query_buf1_max_sources); +			} else { +				/* +				  RFC3376: 4.1.12. IP Destination Addresses for +				  Queries + +				  Group-Specific and Group-and-Source-Specific +				  Queries are sent with +				  an IP destination address equal to the +				  multicast address of +				  interest. +				*/ + +				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 */ +	} + +	if (num_sources_tosend2 > 0) { +		/* +		  Send group-and-source-specific query with s_flag clear and all +		  sources with timers lower or equal to LMQT. +		*/ + +		query_buf2_max_sources = +			(sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2; +		if (num_sources_tosend2 > query_buf2_max_sources) { +			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, num_sources_tosend2, +				sizeof(query_buf2), query_buf2_max_sources); +		} else { +			/* +			  RFC3376: 4.1.12. IP Destination Addresses for Queries + +			  Group-Specific and Group-and-Source-Specific Queries +			  are sent with +			  an IP destination address equal to the multicast +			  address of +			  interest. +			*/ + +			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); +		} +	} + +	return num_retransmit_sources_left;  }  static int igmp_group_retransmit(struct thread *t)  { -  struct igmp_group *group; -  int num_retransmit_sources_left; -  int send_with_sflag_set; /* boolean */ - -  group = THREAD_ARG(t); - -  if (PIM_DEBUG_IGMP_TRACE) { -    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); -  } - -  /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ -  if (group->group_specific_query_retransmit_count > 0) { - -    /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */ -    group_retransmit_group(group); -    --group->group_specific_query_retransmit_count; - -    /* -      RFC3376: 6.6.3.2 -      If a group specific query is scheduled to be transmitted at the -      same time as a group and source specific query for the same group, -      then transmission of the group and source specific message with the -      "Suppress Router-Side Processing" bit set may be suppressed. -    */ -    send_with_sflag_set = 0; /* boolean=false */ -  } -  else { -    send_with_sflag_set = 1; /* boolean=true */ -  } - -  /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */ -  num_retransmit_sources_left = group_retransmit_sources(group, -							 send_with_sflag_set); - -  /* -    Keep group retransmit timer running if there is any retransmit -    counter pending -  */ -  if ((num_retransmit_sources_left > 0) || -      (group->group_specific_query_retransmit_count > 0)) { -    group_retransmit_timer_on(group); -  } - -  return 0; +	struct igmp_group *group; +	int num_retransmit_sources_left; +	int send_with_sflag_set; /* boolean */ + +	group = THREAD_ARG(t); + +	if (PIM_DEBUG_IGMP_TRACE) { +		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); +	} + +	/* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */ +	if (group->group_specific_query_retransmit_count > 0) { + +		/* Retransmit group-specific queries (RFC3376: 6.6.3.1) */ +		group_retransmit_group(group); +		--group->group_specific_query_retransmit_count; + +		/* +		  RFC3376: 6.6.3.2 +		  If a group specific query is scheduled to be transmitted at +		  the +		  same time as a group and source specific query for the same +		  group, +		  then transmission of the group and source specific message +		  with the +		  "Suppress Router-Side Processing" bit set may be suppressed. +		*/ +		send_with_sflag_set = 0; /* boolean=false */ +	} else { +		send_with_sflag_set = 1; /* boolean=true */ +	} + +	/* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */ +	num_retransmit_sources_left = +		group_retransmit_sources(group, send_with_sflag_set); + +	/* +	  Keep group retransmit timer running if there is any retransmit +	  counter pending +	*/ +	if ((num_retransmit_sources_left > 0) +	    || (group->group_specific_query_retransmit_count > 0)) { +		group_retransmit_timer_on(group); +	} + +	return 0;  }  /* @@ -1291,42 +1295,42 @@ static int igmp_group_retransmit(struct thread *t)  */  static void group_retransmit_timer_on(struct igmp_group *group)  { -  struct igmp_sock     *igmp; -  struct pim_interface *pim_ifp; -  long                  lmqi_msec; /* Last Member Query Interval */ - -  /* if group retransmit timer is running, do nothing */ -  if (group->t_group_query_retransmit_timer) { -    return; -  } - -  igmp = group->group_igmp_sock; -  pim_ifp = igmp->interface->info; - -  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; - -  if (PIM_DEBUG_IGMP_TRACE) { -    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, -	       lmqi_msec % 1000, -	       group_str, -	       igmp->interface->name); -  } - -  thread_add_timer_msec(master, igmp_group_retransmit, group, lmqi_msec, -                        &group->t_group_query_retransmit_timer); +	struct igmp_sock *igmp; +	struct pim_interface *pim_ifp; +	long lmqi_msec; /* Last Member Query Interval */ + +	/* if group retransmit timer is running, do nothing */ +	if (group->t_group_query_retransmit_timer) { +		return; +	} + +	igmp = group->group_igmp_sock; +	pim_ifp = igmp->interface->info; + +	lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; + +	if (PIM_DEBUG_IGMP_TRACE) { +		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, lmqi_msec % 1000, group_str, +			igmp->interface->name); +	} + +	thread_add_timer_msec(master, igmp_group_retransmit, group, lmqi_msec, +			      &group->t_group_query_retransmit_timer);  }  static long igmp_group_timer_remain_msec(struct igmp_group *group)  { -  return pim_time_timer_remain_msec(group->t_group_timer); +	return pim_time_timer_remain_msec(group->t_group_timer);  }  static long igmp_source_timer_remain_msec(struct igmp_source *source)  { -  return pim_time_timer_remain_msec(source->t_source_timer); +	return pim_time_timer_remain_msec(source->t_source_timer);  }  /* @@ -1334,21 +1338,22 @@ static long igmp_source_timer_remain_msec(struct igmp_source *source)  */  static void group_query_send(struct igmp_group *group)  { -  long              lmqc;    /* Last Member Query Count */ +	long lmqc; /* Last Member Query Count */ -  lmqc = group->group_igmp_sock->querier_robustness_variable; +	lmqc = group->group_igmp_sock->querier_robustness_variable; -  /* lower group timer to lmqt */ -  igmp_group_timer_lower_to_lmqt(group); +	/* lower group timer to lmqt */ +	igmp_group_timer_lower_to_lmqt(group); -  /* reset retransmission counter */ -  group->group_specific_query_retransmit_count = lmqc; +	/* reset retransmission counter */ +	group->group_specific_query_retransmit_count = lmqc; -  /* immediately send group specific query (decrease retransmit counter by 1)*/ -  group_retransmit_group(group); +	/* immediately send group specific query (decrease retransmit counter by +	 * 1)*/ +	group_retransmit_group(group); -  /* make sure group retransmit timer is running */ -  group_retransmit_timer_on(group); +	/* make sure group retransmit timer is running */ +	group_retransmit_timer_on(group);  }  /* @@ -1357,612 +1362,674 @@ static void group_query_send(struct igmp_group *group)  static void source_query_send_by_flag(struct igmp_group *group,  				      int num_sources_tosend)  { -  struct igmp_sock     *igmp; -  struct pim_interface *pim_ifp; -  struct listnode      *src_node; -  struct igmp_source   *src; -  long                  lmqc;      /* Last Member Query Count */ -  long                  lmqi_msec; /* Last Member Query Interval */ -  long                  lmqt_msec; /* Last Member Query Time */ - -  zassert(num_sources_tosend > 0); - -  igmp = group->group_igmp_sock; -  pim_ifp = igmp->interface->info; - -  lmqc      = igmp->querier_robustness_variable; -  lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; -  lmqt_msec = lmqc * lmqi_msec; - -  /* -    RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries - -    (...) for each of the sources in X of group G, with source timer larger -    than LMQT: -    o Set number of retransmissions for each source to [Last Member -    Query Count]. -    o Lower source timer to LMQT. -  */ -  for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { -    if (IGMP_SOURCE_TEST_SEND(src->source_flags)) { -      /* source "src" in X of group G */ -      if (igmp_source_timer_remain_msec(src) > lmqt_msec) { -	src->source_query_retransmit_count = lmqc; -	igmp_source_timer_lower_to_lmqt(src); -      } -    } -  } - -  /* send group-and-source specific queries */ -  group_retransmit_sources(group, 1 /* send_with_sflag_set=true */); - -  /* make sure group retransmit timer is running */ -  group_retransmit_timer_on(group); +	struct igmp_sock *igmp; +	struct pim_interface *pim_ifp; +	struct listnode *src_node; +	struct igmp_source *src; +	long lmqc;      /* Last Member Query Count */ +	long lmqi_msec; /* Last Member Query Interval */ +	long lmqt_msec; /* Last Member Query Time */ + +	zassert(num_sources_tosend > 0); + +	igmp = group->group_igmp_sock; +	pim_ifp = igmp->interface->info; + +	lmqc = igmp->querier_robustness_variable; +	lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec; +	lmqt_msec = lmqc * lmqi_msec; + +	/* +	  RFC3376: 6.6.3.2. Building and Sending Group and Source Specific +	  Queries + +	  (...) for each of the sources in X of group G, with source timer +	  larger +	  than LMQT: +	  o Set number of retransmissions for each source to [Last Member +	  Query Count]. +	  o Lower source timer to LMQT. +	*/ +	for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) { +		if (IGMP_SOURCE_TEST_SEND(src->source_flags)) { +			/* source "src" in X of group G */ +			if (igmp_source_timer_remain_msec(src) > lmqt_msec) { +				src->source_query_retransmit_count = lmqc; +				igmp_source_timer_lower_to_lmqt(src); +			} +		} +	} + +	/* send group-and-source specific queries */ +	group_retransmit_sources(group, 1 /* send_with_sflag_set=true */); + +	/* make sure group retransmit timer is running */ +	group_retransmit_timer_on(group);  } -static void block_excl(struct igmp_group *group, -		       int num_sources, struct in_addr *sources) +static void block_excl(struct igmp_group *group, int num_sources, +		       struct in_addr *sources)  { -  int num_sources_tosend = 0; -  int i; - -  /* 1. clear off SEND flag from all known sources (X,Y) */ -  source_clear_send_flag(group->group_source_list); - -  /* 2. scan received sources (A) */ -  for (i = 0; i < num_sources; ++i) { -    struct igmp_source *source; -    struct in_addr     *src_addr; -     -    src_addr = sources + i; -     -    /* lookup reported source (A) in known sources (X,Y) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (!source) { -      /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */ -      long group_timer_msec; -      source = source_new(group, *src_addr); -      if (!source) { -	/* ugh, internal malloc failure, skip source */ -	continue; -      } - -      zassert(!source->t_source_timer); /* timer == 0 */ -      group_timer_msec = igmp_group_timer_remain_msec(group); -      igmp_source_timer_on(group, source, group_timer_msec); -      zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ -    } - -    if (source->t_source_timer) { -      /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */ -      IGMP_SOURCE_DO_SEND(source->source_flags); -      ++num_sources_tosend; -    } -  } -  -  /* 5. send sources marked with SEND flag: Q(G,A-Y) */ -  if (num_sources_tosend > 0) { -    source_query_send_by_flag(group, num_sources_tosend); -  } +	int num_sources_tosend = 0; +	int i; + +	/* 1. clear off SEND flag from all known sources (X,Y) */ +	source_clear_send_flag(group->group_source_list); + +	/* 2. scan received sources (A) */ +	for (i = 0; i < num_sources; ++i) { +		struct igmp_source *source; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* lookup reported source (A) in known sources (X,Y) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (!source) { +			/* 3: if not found, create source with Group Timer: +			 * (A-X-Y)=Group Timer */ +			long group_timer_msec; +			source = source_new(group, *src_addr); +			if (!source) { +				/* ugh, internal malloc failure, skip source */ +				continue; +			} + +			zassert(!source->t_source_timer); /* timer == 0 */ +			group_timer_msec = igmp_group_timer_remain_msec(group); +			igmp_source_timer_on(group, source, group_timer_msec); +			zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */ +		} + +		if (source->t_source_timer) { +			/* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */ +			IGMP_SOURCE_DO_SEND(source->source_flags); +			++num_sources_tosend; +		} +	} + +	/* 5. send sources marked with SEND flag: Q(G,A-Y) */ +	if (num_sources_tosend > 0) { +		source_query_send_by_flag(group, num_sources_tosend); +	}  } -static void block_incl(struct igmp_group *group, -		       int num_sources, struct in_addr *sources) +static void block_incl(struct igmp_group *group, int num_sources, +		       struct in_addr *sources)  { -  int num_sources_tosend = 0; -  int i; - -  /* 1. clear off SEND flag from all known sources (B) */ -  source_clear_send_flag(group->group_source_list); - -  /* 2. scan received sources (A) */ -  for (i = 0; i < num_sources; ++i) { -    struct igmp_source *source; -    struct in_addr     *src_addr; -     -    src_addr = sources + i; -     -    /* lookup reported source (A) in known sources (B) */ -    source = igmp_find_source_by_addr(group, *src_addr); -    if (source) { -      /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */ -      IGMP_SOURCE_DO_SEND(source->source_flags); -      ++num_sources_tosend; -    } -  }  -  -  /* 4. send sources marked with SEND flag: Q(G,A*B) */ -  if (num_sources_tosend > 0) { -    source_query_send_by_flag(group, num_sources_tosend); -  } +	int num_sources_tosend = 0; +	int i; + +	/* 1. clear off SEND flag from all known sources (B) */ +	source_clear_send_flag(group->group_source_list); + +	/* 2. scan received sources (A) */ +	for (i = 0; i < num_sources; ++i) { +		struct igmp_source *source; +		struct in_addr *src_addr; + +		src_addr = sources + i; + +		/* lookup reported source (A) in known sources (B) */ +		source = igmp_find_source_by_addr(group, *src_addr); +		if (source) { +			/* 3. if found (A*B), mark SEND flag: Q(G,A*B) */ +			IGMP_SOURCE_DO_SEND(source->source_flags); +			++num_sources_tosend; +		} +	} + +	/* 4. send sources marked with SEND flag: Q(G,A*B) */ +	if (num_sources_tosend > 0) { +		source_query_send_by_flag(group, num_sources_tosend); +	}  }  void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from, -			 struct in_addr group_addr, -			 int num_sources, struct in_addr *sources) +			 struct in_addr group_addr, int num_sources, +			 struct in_addr *sources)  { -  struct interface *ifp = igmp->interface; -  struct igmp_group *group; - -  on_trace(__PRETTY_FUNCTION__, -	   ifp, from, group_addr, num_sources, sources); - -  /* non-existant group is created as INCLUDE {empty} */ -  group = igmp_add_group_by_addr(igmp, group_addr); -  if (!group) { -    return; -  } - -  if (group->group_filtermode_isexcl) { -    /* EXCLUDE mode */ -    block_excl(group, num_sources, sources); -  } -  else { -    /* INCLUDE mode */ -    block_incl(group, num_sources, sources); -  } +	struct interface *ifp = igmp->interface; +	struct igmp_group *group; + +	on_trace(__PRETTY_FUNCTION__, ifp, from, group_addr, num_sources, +		 sources); + +	/* non-existant group is created as INCLUDE {empty} */ +	group = igmp_add_group_by_addr(igmp, group_addr); +	if (!group) { +		return; +	} + +	if (group->group_filtermode_isexcl) { +		/* EXCLUDE mode */ +		block_excl(group, num_sources, sources); +	} else { +		/* INCLUDE mode */ +		block_incl(group, num_sources, sources); +	}  }  void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)  { -  struct igmp_sock     *igmp; -  struct interface     *ifp; -  struct pim_interface *pim_ifp; -  char                 *ifname; -  int   lmqi_dsec; /* Last Member Query Interval */ -  int   lmqc;      /* Last Member Query Count */ -  int   lmqt_msec; /* Last Member Query Time */ - -  /* -    RFC 3376: 6.2.2. Definition of Group Timers -     -    The group timer is only used when a group is in EXCLUDE mode and -    it represents the time for the *filter-mode* of the group to -    expire and switch to INCLUDE mode. -  */ -  if (!group->group_filtermode_isexcl) { -    return; -  } - -  igmp    = group->group_igmp_sock; -  ifp     = igmp->interface; -  pim_ifp = ifp->info; -  ifname  = ifp->name; - -  lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; -  lmqc      = igmp->querier_robustness_variable; -  lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ - -  if (PIM_DEBUG_IGMP_TRACE) { -    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__, -	       group_str, ifname, -	       lmqc, lmqi_dsec, lmqt_msec); -  } - -  zassert(group->group_filtermode_isexcl); - -  igmp_group_timer_on(group, lmqt_msec, ifname); +	struct igmp_sock *igmp; +	struct interface *ifp; +	struct pim_interface *pim_ifp; +	char *ifname; +	int lmqi_dsec; /* Last Member Query Interval */ +	int lmqc;      /* Last Member Query Count */ +	int lmqt_msec; /* Last Member Query Time */ + +	/* +	  RFC 3376: 6.2.2. Definition of Group Timers + +	  The group timer is only used when a group is in EXCLUDE mode and +	  it represents the time for the *filter-mode* of the group to +	  expire and switch to INCLUDE mode. +	*/ +	if (!group->group_filtermode_isexcl) { +		return; +	} + +	igmp = group->group_igmp_sock; +	ifp = igmp->interface; +	pim_ifp = ifp->info; +	ifname = ifp->name; + +	lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; +	lmqc = igmp->querier_robustness_variable; +	lmqt_msec = PIM_IGMP_LMQT_MSEC( +		lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ + +	if (PIM_DEBUG_IGMP_TRACE) { +		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__, group_str, ifname, lmqc, lmqi_dsec, +			lmqt_msec); +	} + +	zassert(group->group_filtermode_isexcl); + +	igmp_group_timer_on(group, lmqt_msec, ifname);  }  void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)  { -  struct igmp_group    *group; -  struct igmp_sock     *igmp; -  struct interface     *ifp; -  struct pim_interface *pim_ifp; -  char                 *ifname; -  int   lmqi_dsec; /* Last Member Query Interval */ -  int   lmqc;      /* Last Member Query Count */ -  int   lmqt_msec; /* Last Member Query Time */ - -  group   = source->source_group; -  igmp    = group->group_igmp_sock; -  ifp     = igmp->interface; -  pim_ifp = ifp->info; -  ifname  = ifp->name; - -  lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; -  lmqc      = igmp->querier_robustness_variable; -  lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ - -  if (PIM_DEBUG_IGMP_TRACE) { -    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", -	       __PRETTY_FUNCTION__, -	       group_str, source_str, ifname, -	       lmqc, lmqi_dsec, lmqt_msec); -  } - -  igmp_source_timer_on(group, source, lmqt_msec); +	struct igmp_group *group; +	struct igmp_sock *igmp; +	struct interface *ifp; +	struct pim_interface *pim_ifp; +	char *ifname; +	int lmqi_dsec; /* Last Member Query Interval */ +	int lmqc;      /* Last Member Query Count */ +	int lmqt_msec; /* Last Member Query Time */ + +	group = source->source_group; +	igmp = group->group_igmp_sock; +	ifp = igmp->interface; +	pim_ifp = ifp->info; +	ifname = ifp->name; + +	lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec; +	lmqc = igmp->querier_robustness_variable; +	lmqt_msec = PIM_IGMP_LMQT_MSEC( +		lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */ + +	if (PIM_DEBUG_IGMP_TRACE) { +		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", +			__PRETTY_FUNCTION__, group_str, source_str, ifname, +			lmqc, lmqi_dsec, lmqt_msec); +	} + +	igmp_source_timer_on(group, source, lmqt_msec);  } -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) +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; -  uint8_t             qqic; -  ssize_t             sent; -  struct sockaddr_in  to; -  socklen_t           tolen; -  uint16_t            checksum; - -  zassert(num_sources >= 0); - -  msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2); -  if (msg_size > query_buf_size) { -    zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d", -	     __FILE__, __PRETTY_FUNCTION__, -	     msg_size, query_buf_size); -    return; -  } - -  s_flag = PIM_FORCE_BOOLEAN(s_flag); -  zassert((s_flag == 0) || (s_flag == 1)); - -  max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); -  qqic          = igmp_msg_encode16to8(querier_query_interval); - -  /* -    RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) - -    If non-zero, the QRV field contains the [Robustness Variable] -    value used by the querier, i.e., the sender of the Query.  If the -    querier's [Robustness Variable] exceeds 7, the maximum value of -    the QRV field, the QRV is set to zero. -  */ -  if (querier_robustness_variable > 7) { -    querier_robustness_variable = 0; -  } - -  query_buf[0]                                         = PIM_IGMP_MEMBERSHIP_QUERY; -  query_buf[1]                                         = max_resp_code; -  *(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; -  query_buf[9]                                         = qqic; -  *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources); - -  checksum = in_cksum(query_buf, msg_size); -  *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum; - -  if (PIM_DEBUG_IGMP_PACKETS) { -    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("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)); -  to.sin_family = AF_INET; -  to.sin_addr = dst_addr; -  tolen = sizeof(to); - -  sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, -                (struct sockaddr *)&to, tolen); -  if (sent != (ssize_t) msg_size) { -    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("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("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; -  } - -  /* -    s_flag sanity test: s_flag must be set for general queries - -    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. -  */ -  if (!s_flag) { -    /* general query? */ -    if (PIM_INADDR_IS_ANY(group_addr)) { -      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!", -		__PRETTY_FUNCTION__, -		dst_str, ifname, group_str, num_sources); -    } -  } +	ssize_t msg_size; +	uint8_t max_resp_code; +	uint8_t qqic; +	ssize_t sent; +	struct sockaddr_in to; +	socklen_t tolen; +	uint16_t checksum; + +	zassert(num_sources >= 0); + +	msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2); +	if (msg_size > query_buf_size) { +		zlog_err( +			"%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d", +			__FILE__, __PRETTY_FUNCTION__, msg_size, +			query_buf_size); +		return; +	} + +	s_flag = PIM_FORCE_BOOLEAN(s_flag); +	zassert((s_flag == 0) || (s_flag == 1)); + +	max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec); +	qqic = igmp_msg_encode16to8(querier_query_interval); + +	/* +	  RFC 3376: 4.1.6. QRV (Querier's Robustness Variable) + +	  If non-zero, the QRV field contains the [Robustness Variable] +	  value used by the querier, i.e., the sender of the Query.  If the +	  querier's [Robustness Variable] exceeds 7, the maximum value of +	  the QRV field, the QRV is set to zero. +	*/ +	if (querier_robustness_variable > 7) { +		querier_robustness_variable = 0; +	} + +	query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY; +	query_buf[1] = max_resp_code; +	*(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; +	query_buf[9] = qqic; +	*(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = +		htons(num_sources); + +	checksum = in_cksum(query_buf, msg_size); +	*(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum; + +	if (PIM_DEBUG_IGMP_PACKETS) { +		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( +			"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)); +	to.sin_family = AF_INET; +	to.sin_addr = dst_addr; +	tolen = sizeof(to); + +	sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, +		      (struct sockaddr *)&to, tolen); +	if (sent != (ssize_t)msg_size) { +		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( +				"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( +				"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; +	} + +	/* +	  s_flag sanity test: s_flag must be set for general queries + +	  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. +	*/ +	if (!s_flag) { +		/* general query? */ +		if (PIM_INADDR_IS_ANY(group_addr)) { +			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!", +				__PRETTY_FUNCTION__, dst_str, ifname, group_str, +				num_sources); +		} +	}  } -void -igmp_v3_recv_query (struct igmp_sock *igmp, const char *from_str, char *igmp_msg) +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 */ +	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) +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; +	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;  }  | 
