diff options
Diffstat (limited to 'pimd/pim_igmp.c')
| -rw-r--r-- | pimd/pim_igmp.c | 1807 | 
1 files changed, 905 insertions, 902 deletions
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index ae5f365b82..c693f30ac2 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -43,62 +43,66 @@ static int pim_igmp_general_query(struct thread *t);  /* This socket is used for TXing IGMP packets only, IGMP RX happens   * in pim_mroute_msg()   */ -static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, uint32_t pim_options) +static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, +			  uint32_t pim_options)  { -  int fd; -  int join = 0; -  struct in_addr group; - -  fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1); - -  if (fd < 0) -    return -1; - -  if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) { -    if (inet_aton(PIM_ALL_ROUTERS, &group)) { -      if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) -       ++join; -    } -    else { -      zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", -               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), -               PIM_ALL_ROUTERS, errno, safe_strerror(errno)); -    } -  } - -  /* -    IGMP routers periodically send IGMP general queries to AllSystems=224.0.0.1 -    IGMP routers must receive general queries for querier election. -  */ -  if (inet_aton(PIM_ALL_SYSTEMS, &group)) { -    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) -      ++join; -  } -  else { -    zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", -             __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), -             PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); -  } - -  if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) { -    if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) { -      ++join; -    } -  } -  else { -      zlog_warn("%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", -               __FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), -               PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); -  }     - -  if (!join) { -    zlog_err("IGMP socket fd=%d could not join any group on interface address %s", -            fd, inet_ntoa(ifaddr)); -    close(fd); -    fd = -1; -  } - -  return fd; +	int fd; +	int join = 0; +	struct in_addr group; + +	fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1); + +	if (fd < 0) +		return -1; + +	if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) { +		if (inet_aton(PIM_ALL_ROUTERS, &group)) { +			if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) +				++join; +		} else { +			zlog_warn( +				"%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", +				__FILE__, __PRETTY_FUNCTION__, fd, +				inet_ntoa(ifaddr), PIM_ALL_ROUTERS, errno, +				safe_strerror(errno)); +		} +	} + +	/* +	  IGMP routers periodically send IGMP general queries to +	  AllSystems=224.0.0.1 +	  IGMP routers must receive general queries for querier election. +	*/ +	if (inet_aton(PIM_ALL_SYSTEMS, &group)) { +		if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) +			++join; +	} else { +		zlog_warn( +			"%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", +			__FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), +			PIM_ALL_SYSTEMS, errno, safe_strerror(errno)); +	} + +	if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) { +		if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) { +			++join; +		} +	} else { +		zlog_warn( +			"%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s", +			__FILE__, __PRETTY_FUNCTION__, fd, inet_ntoa(ifaddr), +			PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno)); +	} + +	if (!join) { +		zlog_err( +			"IGMP socket fd=%d could not join any group on interface address %s", +			fd, inet_ntoa(ifaddr)); +		close(fd); +		fd = -1; +	} + +	return fd;  }  #undef IGMP_SOCK_DUMP @@ -106,828 +110,834 @@ static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp, uint32_t  #ifdef IGMP_SOCK_DUMP  static void igmp_sock_dump(array_t *igmp_sock_array)  { -  int size = array_size(igmp_sock_array); -  for (int i = 0; i < size; ++i) { -     -    struct igmp_sock *igmp = array_get(igmp_sock_array, i); -     -    zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", -	       __FILE__, __PRETTY_FUNCTION__, -	       i, size, -	       inet_ntoa(igmp->ifaddr), -	       igmp->fd); -  } +	int size = array_size(igmp_sock_array); +	for (int i = 0; i < size; ++i) { + +		struct igmp_sock *igmp = array_get(igmp_sock_array, i); + +		zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__, +			   __PRETTY_FUNCTION__, i, size, +			   inet_ntoa(igmp->ifaddr), igmp->fd); +	}  }  #endif  struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,  					      struct in_addr ifaddr)  { -  struct listnode  *sock_node; -  struct igmp_sock *igmp; +	struct listnode *sock_node; +	struct igmp_sock *igmp;  #ifdef IGMP_SOCK_DUMP -  igmp_sock_dump(igmp_sock_list); +	igmp_sock_dump(igmp_sock_list);  #endif -  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) -    if (ifaddr.s_addr == igmp->ifaddr.s_addr) -      return igmp; +	for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) +		if (ifaddr.s_addr == igmp->ifaddr.s_addr) +			return igmp; -  return 0; +	return 0;  } -struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, -					 int fd) +struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd)  { -  struct listnode  *sock_node; -  struct igmp_sock *igmp; +	struct listnode *sock_node; +	struct igmp_sock *igmp; -  for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) -    if (fd == igmp->fd) -      return igmp; +	for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp)) +		if (fd == igmp->fd) +			return igmp; -  return 0; +	return 0;  }  static int pim_igmp_other_querier_expire(struct thread *t)  { -  struct igmp_sock *igmp; - -  igmp = THREAD_ARG(t); - -  zassert(!igmp->t_igmp_query_timer); - -  if (PIM_DEBUG_IGMP_TRACE) { -    char ifaddr_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); -    zlog_debug("%s: Querier %s resuming", -	       __PRETTY_FUNCTION__, -	       ifaddr_str); -  } - -  /* -    We are the current querier, then -    re-start sending general queries. -    RFC 2236 - sec 7 Other Querier -    present timer expired (Send General -    Query, Set Gen. Query. timer) -  */ -  pim_igmp_general_query(t); - -  return 0; +	struct igmp_sock *igmp; + +	igmp = THREAD_ARG(t); + +	zassert(!igmp->t_igmp_query_timer); + +	if (PIM_DEBUG_IGMP_TRACE) { +		char ifaddr_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, +			       sizeof(ifaddr_str)); +		zlog_debug("%s: Querier %s resuming", __PRETTY_FUNCTION__, +			   ifaddr_str); +	} + +	/* +	  We are the current querier, then +	  re-start sending general queries. +	  RFC 2236 - sec 7 Other Querier +	  present timer expired (Send General +	  Query, Set Gen. Query. timer) +	*/ +	pim_igmp_general_query(t); + +	return 0;  }  void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)  { -  long other_querier_present_interval_msec; -  struct pim_interface *pim_ifp; - -  zassert(igmp); -  zassert(igmp->interface); -  zassert(igmp->interface->info); - -  pim_ifp = igmp->interface->info; - -  if (igmp->t_other_querier_timer) { -    /* -      There is other querier present already, -      then reset the other-querier-present timer. -    */ - -    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 resetting TIMER event for Other-Querier-Present", -		 ifaddr_str); -    } -    THREAD_OFF(igmp->t_other_querier_timer); -  } -  else { -    /* -      We are the current querier, then stop sending general queries: -      igmp->t_igmp_query_timer = NULL; -    */ -    pim_igmp_general_query_off(igmp); -  } - -  /* -    Since this socket is starting the other-querier-present timer, -    there should not be periodic query timer for this socket. -   */ -  zassert(!igmp->t_igmp_query_timer); - -  /* -    RFC 3376: 8.5. Other Querier Present Interval - -    The Other Querier Present Interval is the length of time that must -    pass before a multicast router decides that there is no longer -    another multicast router which should be the querier.  This value -    MUST be ((the Robustness Variable) times (the Query Interval)) plus -    (one half of one Query Response Interval). - -    other_querier_present_interval_msec = \ -      igmp->querier_robustness_variable * \ -      1000 * igmp->querier_query_interval + \ -      100 * (pim_ifp->query_max_response_time_dsec >> 1); -  */ -  other_querier_present_interval_msec = -    PIM_IGMP_OQPI_MSEC(igmp->querier_robustness_variable, -		       igmp->querier_query_interval, -		       pim_ifp->igmp_query_max_response_time_dsec); -   -  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 scheduling %ld.%03ld sec TIMER event for Other-Querier-Present", -	       ifaddr_str, -	       other_querier_present_interval_msec / 1000, -	       other_querier_present_interval_msec % 1000); -  } -   -  thread_add_timer_msec(master, pim_igmp_other_querier_expire, igmp, -                        other_querier_present_interval_msec, -                        &igmp->t_other_querier_timer); +	long other_querier_present_interval_msec; +	struct pim_interface *pim_ifp; + +	zassert(igmp); +	zassert(igmp->interface); +	zassert(igmp->interface->info); + +	pim_ifp = igmp->interface->info; + +	if (igmp->t_other_querier_timer) { +		/* +		  There is other querier present already, +		  then reset the other-querier-present timer. +		*/ + +		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 resetting TIMER event for Other-Querier-Present", +				ifaddr_str); +		} +		THREAD_OFF(igmp->t_other_querier_timer); +	} else { +		/* +		  We are the current querier, then stop sending general queries: +		  igmp->t_igmp_query_timer = NULL; +		*/ +		pim_igmp_general_query_off(igmp); +	} + +	/* +	  Since this socket is starting the other-querier-present timer, +	  there should not be periodic query timer for this socket. +	 */ +	zassert(!igmp->t_igmp_query_timer); + +	/* +	  RFC 3376: 8.5. Other Querier Present Interval + +	  The Other Querier Present Interval is the length of time that must +	  pass before a multicast router decides that there is no longer +	  another multicast router which should be the querier.  This value +	  MUST be ((the Robustness Variable) times (the Query Interval)) plus +	  (one half of one Query Response Interval). + +	  other_querier_present_interval_msec = \ +	    igmp->querier_robustness_variable * \ +	    1000 * igmp->querier_query_interval + \ +	    100 * (pim_ifp->query_max_response_time_dsec >> 1); +	*/ +	other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC( +		igmp->querier_robustness_variable, igmp->querier_query_interval, +		pim_ifp->igmp_query_max_response_time_dsec); + +	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 scheduling %ld.%03ld sec TIMER event for Other-Querier-Present", +			ifaddr_str, other_querier_present_interval_msec / 1000, +			other_querier_present_interval_msec % 1000); +	} + +	thread_add_timer_msec(master, pim_igmp_other_querier_expire, igmp, +			      other_querier_present_interval_msec, +			      &igmp->t_other_querier_timer);  }  void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)  { -  zassert(igmp); - -  if (PIM_DEBUG_IGMP_TRACE) { -    if (igmp->t_other_querier_timer) { -      char ifaddr_str[INET_ADDRSTRLEN]; -      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); -      zlog_debug("IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s", -		 ifaddr_str, igmp->fd, igmp->interface->name); -    } -  } -  THREAD_OFF(igmp->t_other_querier_timer); +	zassert(igmp); + +	if (PIM_DEBUG_IGMP_TRACE) { +		if (igmp->t_other_querier_timer) { +			char ifaddr_str[INET_ADDRSTRLEN]; +			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, +				       sizeof(ifaddr_str)); +			zlog_debug( +				"IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s", +				ifaddr_str, igmp->fd, igmp->interface->name); +		} +	} +	THREAD_OFF(igmp->t_other_querier_timer);  } -static int -igmp_recv_query(struct igmp_sock *igmp, int query_version, -                int max_resp_code, -                struct in_addr from, const char *from_str, -                char *igmp_msg, int igmp_msg_len) +static int igmp_recv_query(struct igmp_sock *igmp, int query_version, +			   int max_resp_code, struct in_addr from, +			   const char *from_str, char *igmp_msg, +			   int igmp_msg_len)  { -  struct interface     *ifp; -  struct pim_interface *pim_ifp; -  struct in_addr        group_addr; -  uint16_t              recv_checksum; -  uint16_t              checksum; - -  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); - -  ifp = igmp->interface; -  pim_ifp = ifp->info; - -  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 query v%d from %s on %s: checksum mismatch: received=%x computed=%x", -	      query_version, from_str, ifp->name, recv_checksum, checksum); -    return -1; -  } - -  /* RFC 3376 defines some guidelines on operating in backwards compatibility -   * with older versions of IGMP but there are some gaps in the logic: -   * -   * - once we drop from say version 3 to version 2 we will never go back to -   *   version 3 even if the node that TXed an IGMP v2 query upgrades to v3 -   * -   * - The node with the lowest IP is the querier so we will only know to drop -   *   from v3 to v2 if the node that is the querier is also the one that is -   *   running igmp v2.  If a non-querier only supports igmp v2 we will have -   *   no way of knowing. -   * -   * For now we will simplify things and inform the user that they need to -   * configure all PIM routers to use the same version of IGMP. -   */ -  if (query_version != pim_ifp->igmp_version) { -    zlog_warn("Recv IGMP query v%d from %s on %s but we are using v%d, please " -              "configure all PIM routers on this subnet to use the same " -              "IGMP version", -              query_version, from_str, ifp->name, pim_ifp->igmp_version); -    return 0; -  } - -  if (PIM_DEBUG_IGMP_PACKETS) { -    char group_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str)); -    zlog_debug("Recv IGMP query v%d from %s on %s for group %s", -	       query_version, from_str, ifp->name, group_str); -  } - -  /* -    RFC 3376: 6.6.2. Querier Election - -    When a router receives a query with a lower IP address, it sets -    the Other-Querier-Present timer to Other Querier Present Interval -    and ceases to send queries on the network if it was the previously -    elected querier. -   */ -  if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) { - -    if (PIM_DEBUG_IGMP_TRACE) { -      char ifaddr_str[INET_ADDRSTRLEN]; -      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); -      zlog_debug("%s: local address %s (%u) lost querier election to %s (%u)", -		 ifp->name, -		 ifaddr_str, ntohl(igmp->ifaddr.s_addr), -		 from_str, ntohl(from.s_addr)); -    } - -    pim_igmp_other_querier_timer_on(igmp); -  } - -  /* IGMP version 3 is the only one where we process the RXed query */ -  if (query_version == 3) { -    igmp_v3_recv_query(igmp, from_str, igmp_msg); -  } - -  return 0; +	struct interface *ifp; +	struct pim_interface *pim_ifp; +	struct in_addr group_addr; +	uint16_t recv_checksum; +	uint16_t checksum; + +	memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); + +	ifp = igmp->interface; +	pim_ifp = ifp->info; + +	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 query v%d from %s on %s: checksum mismatch: received=%x computed=%x", +			query_version, from_str, ifp->name, recv_checksum, +			checksum); +		return -1; +	} + +	/* RFC 3376 defines some guidelines on operating in backwards +	 * compatibility +	 * with older versions of IGMP but there are some gaps in the logic: +	 * +	 * - once we drop from say version 3 to version 2 we will never go back +	 * to +	 *   version 3 even if the node that TXed an IGMP v2 query upgrades to +	 * v3 +	 * +	 * - The node with the lowest IP is the querier so we will only know to +	 * drop +	 *   from v3 to v2 if the node that is the querier is also the one that +	 * is +	 *   running igmp v2.  If a non-querier only supports igmp v2 we will +	 * have +	 *   no way of knowing. +	 * +	 * For now we will simplify things and inform the user that they need to +	 * configure all PIM routers to use the same version of IGMP. +	 */ +	if (query_version != pim_ifp->igmp_version) { +		zlog_warn( +			"Recv IGMP query v%d from %s on %s but we are using v%d, please " +			"configure all PIM routers on this subnet to use the same " +			"IGMP version", +			query_version, from_str, ifp->name, +			pim_ifp->igmp_version); +		return 0; +	} + +	if (PIM_DEBUG_IGMP_PACKETS) { +		char group_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<group?>", group_addr, group_str, +			       sizeof(group_str)); +		zlog_debug("Recv IGMP query v%d from %s on %s for group %s", +			   query_version, from_str, ifp->name, group_str); +	} + +	/* +	  RFC 3376: 6.6.2. Querier Election + +	  When a router receives a query with a lower IP address, it sets +	  the Other-Querier-Present timer to Other Querier Present Interval +	  and ceases to send queries on the network if it was the previously +	  elected querier. +	 */ +	if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) { + +		if (PIM_DEBUG_IGMP_TRACE) { +			char ifaddr_str[INET_ADDRSTRLEN]; +			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, +				       sizeof(ifaddr_str)); +			zlog_debug( +				"%s: local address %s (%u) lost querier election to %s (%u)", +				ifp->name, ifaddr_str, +				ntohl(igmp->ifaddr.s_addr), from_str, +				ntohl(from.s_addr)); +		} + +		pim_igmp_other_querier_timer_on(igmp); +	} + +	/* IGMP version 3 is the only one where we process the RXed query */ +	if (query_version == 3) { +		igmp_v3_recv_query(igmp, from_str, igmp_msg); +	} + +	return 0;  } -static void on_trace(const char *label, -		     struct interface *ifp, struct in_addr from) +static void on_trace(const char *label, struct interface *ifp, +		     struct in_addr from)  { -  if (PIM_DEBUG_IGMP_TRACE) { -    char from_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<from?>", from, from_str, sizeof(from_str)); -    zlog_debug("%s: from %s on %s", -	       label, from_str, ifp->name); -  } +	if (PIM_DEBUG_IGMP_TRACE) { +		char from_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<from?>", from, from_str, sizeof(from_str)); +		zlog_debug("%s: from %s on %s", label, from_str, ifp->name); +	}  } -static int -igmp_v1_recv_report (struct igmp_sock *igmp, -                     struct in_addr from, const char *from_str, -                     char *igmp_msg, int igmp_msg_len) +static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from, +			       const char *from_str, char *igmp_msg, +			       int igmp_msg_len)  { -  struct interface *ifp = igmp->interface; -  struct igmp_group *group; -  struct in_addr group_addr; +	struct interface *ifp = igmp->interface; +	struct igmp_group *group; +	struct in_addr group_addr; -  on_trace(__PRETTY_FUNCTION__, igmp->interface, from); +	on_trace(__PRETTY_FUNCTION__, igmp->interface, from); -  if (igmp_msg_len != IGMP_V12_MSG_SIZE) { -    zlog_warn("Recv IGMP report v1 from %s on %s: size=%d other than correct=%d", -	      from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); -    return -1; -  } +	if (igmp_msg_len != IGMP_V12_MSG_SIZE) { +		zlog_warn( +			"Recv IGMP report v1 from %s on %s: size=%d other than correct=%d", +			from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE); +		return -1; +	} -  if (PIM_DEBUG_IGMP_TRACE) { -    zlog_warn("%s %s: FIXME WRITEME", -	      __FILE__, __PRETTY_FUNCTION__); -  } +	if (PIM_DEBUG_IGMP_TRACE) { +		zlog_warn("%s %s: FIXME WRITEME", __FILE__, +			  __PRETTY_FUNCTION__); +	} -  memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); +	memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr)); -  /* non-existant group is created as INCLUDE {empty} */ -  group = igmp_add_group_by_addr(igmp, group_addr); -  if (!group) { -    return -1; -  } +	/* non-existant group is created as INCLUDE {empty} */ +	group = igmp_add_group_by_addr(igmp, group_addr); +	if (!group) { +		return -1; +	} -  group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec(); +	group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec(); -  return 0; +	return 0;  }  int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)  { -  struct ip *ip_hdr; -  size_t ip_hlen; /* ip header length in bytes */ -  char *igmp_msg; -  int igmp_msg_len; -  int msg_type; -  char from_str[INET_ADDRSTRLEN]; -  char to_str[INET_ADDRSTRLEN]; -     -  if (len < sizeof(*ip_hdr)) { -    zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", -	      len, sizeof(*ip_hdr)); -    return -1; -  } - -  ip_hdr = (struct ip *) buf; - -  pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str , sizeof(from_str)); -  pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str , sizeof(to_str)); - -  ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ - -  if (PIM_DEBUG_IGMP_PACKETS) { -    zlog_debug("Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", -	       from_str, to_str, igmp->interface->name, len, ip_hlen, ip_hdr->ip_p); -  } - -  igmp_msg = buf + ip_hlen; -  msg_type = *igmp_msg; -  igmp_msg_len = len - ip_hlen; - -  if (PIM_DEBUG_IGMP_PACKETS) { -    zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d", -	       from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type, -	       igmp_msg_len); -  } - -  if (igmp_msg_len < PIM_IGMP_MIN_LEN) { -    zlog_warn("IGMP message size=%d shorter than minimum=%d", -	      igmp_msg_len, PIM_IGMP_MIN_LEN); -    return -1; -  } - -  switch (msg_type) { -  case PIM_IGMP_MEMBERSHIP_QUERY: -    { -      int max_resp_code = igmp_msg[1]; -      int query_version; - -      /* -	RFC 3376: 7.1. Query Version Distinctions -	IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero -	IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero -	IGMPv3 Query: length >= 12 octets -      */ - -      if (igmp_msg_len == 8) { -	query_version = max_resp_code ? 2 : 1; -      } -      else if (igmp_msg_len >= 12) { -	query_version = 3; -      } -      else { -	zlog_warn("Unknown IGMP query version"); -	return -1; -      } +	struct ip *ip_hdr; +	size_t ip_hlen; /* ip header length in bytes */ +	char *igmp_msg; +	int igmp_msg_len; +	int msg_type; +	char from_str[INET_ADDRSTRLEN]; +	char to_str[INET_ADDRSTRLEN]; + +	if (len < sizeof(*ip_hdr)) { +		zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len, +			  sizeof(*ip_hdr)); +		return -1; +	} + +	ip_hdr = (struct ip *)buf; + +	pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str)); +	pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str)); + +	ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */ -      return igmp_recv_query(igmp, query_version, max_resp_code, -			     ip_hdr->ip_src, from_str, -			     igmp_msg, igmp_msg_len); -    } +	if (PIM_DEBUG_IGMP_PACKETS) { +		zlog_debug( +			"Recv IP packet from %s to %s on %s: size=%zu ip_header_size=%zu ip_proto=%d", +			from_str, to_str, igmp->interface->name, len, ip_hlen, +			ip_hdr->ip_p); +	} + +	igmp_msg = buf + ip_hlen; +	msg_type = *igmp_msg; +	igmp_msg_len = len - ip_hlen; + +	if (PIM_DEBUG_IGMP_PACKETS) { +		zlog_debug( +			"Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d", +			from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, +			msg_type, igmp_msg_len); +	} -  case PIM_IGMP_V3_MEMBERSHIP_REPORT: -    return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str, -                               igmp_msg, igmp_msg_len); +	if (igmp_msg_len < PIM_IGMP_MIN_LEN) { +		zlog_warn("IGMP message size=%d shorter than minimum=%d", +			  igmp_msg_len, PIM_IGMP_MIN_LEN); +		return -1; +	} + +	switch (msg_type) { +	case PIM_IGMP_MEMBERSHIP_QUERY: { +		int max_resp_code = igmp_msg[1]; +		int query_version; + +		/* +		  RFC 3376: 7.1. Query Version Distinctions +		  IGMPv1 Query: length = 8 octets AND Max Resp Code field is +		  zero +		  IGMPv2 Query: length = 8 octets AND Max Resp Code field is +		  non-zero +		  IGMPv3 Query: length >= 12 octets +		*/ + +		if (igmp_msg_len == 8) { +			query_version = max_resp_code ? 2 : 1; +		} else if (igmp_msg_len >= 12) { +			query_version = 3; +		} else { +			zlog_warn("Unknown IGMP query version"); +			return -1; +		} + +		return igmp_recv_query(igmp, query_version, max_resp_code, +				       ip_hdr->ip_src, from_str, igmp_msg, +				       igmp_msg_len); +	} -  case PIM_IGMP_V2_MEMBERSHIP_REPORT: -    return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str, -                               igmp_msg, igmp_msg_len); +	case PIM_IGMP_V3_MEMBERSHIP_REPORT: +		return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str, +					   igmp_msg, igmp_msg_len); -  case PIM_IGMP_V1_MEMBERSHIP_REPORT: -    return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str, -                               igmp_msg, igmp_msg_len); +	case PIM_IGMP_V2_MEMBERSHIP_REPORT: +		return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str, +					   igmp_msg, igmp_msg_len); -  case PIM_IGMP_V2_LEAVE_GROUP: -    return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str, -                              igmp_msg, igmp_msg_len); -  } +	case PIM_IGMP_V1_MEMBERSHIP_REPORT: +		return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str, +					   igmp_msg, igmp_msg_len); -  zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); +	case PIM_IGMP_V2_LEAVE_GROUP: +		return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str, +					  igmp_msg, igmp_msg_len); +	} -  return -1; +	zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type); + +	return -1;  }  void pim_igmp_general_query_on(struct igmp_sock *igmp)  { -  struct pim_interface *pim_ifp; -  int startup_mode; -  int query_interval; - -  /* -    Since this socket is starting as querier, -    there should not exist a timer for other-querier-present. -   */ -  zassert(!igmp->t_other_querier_timer); -  pim_ifp = igmp->interface->info; -  zassert(pim_ifp); - -  /* -    RFC 3376: 8.6. Startup Query Interval - -    The Startup Query Interval is the interval between General Queries -    sent by a Querier on startup.  Default: 1/4 the Query Interval. -    The first one should be sent out immediately instead of 125/4 -    seconds from now. -  */ -  startup_mode = igmp->startup_query_count > 0; -  if (startup_mode) { -    /* -     * If this is the first time we are sending a query on a -     * newly configured igmp interface send it out in 1 second -     * just to give the entire world a tiny bit of time to settle -     * else the query interval is: -     * query_interval = pim_ifp->igmp_default_query_interval >> 2; -     */ -    if (igmp->startup_query_count == igmp->querier_robustness_variable) -      query_interval = 1; -    else -      query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval); - -    --igmp->startup_query_count; -  } -  else { -    query_interval = igmp->querier_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 scheduling %d-second (%s) TIMER event for IGMP query on fd=%d", -	       ifaddr_str, -	       query_interval, -	       startup_mode ? "startup" : "non-startup", -	       igmp->fd); -  } -  igmp->t_igmp_query_timer = NULL; -  thread_add_timer(master, pim_igmp_general_query, igmp, query_interval, -                   &igmp->t_igmp_query_timer); +	struct pim_interface *pim_ifp; +	int startup_mode; +	int query_interval; + +	/* +	  Since this socket is starting as querier, +	  there should not exist a timer for other-querier-present. +	 */ +	zassert(!igmp->t_other_querier_timer); +	pim_ifp = igmp->interface->info; +	zassert(pim_ifp); + +	/* +	  RFC 3376: 8.6. Startup Query Interval + +	  The Startup Query Interval is the interval between General Queries +	  sent by a Querier on startup.  Default: 1/4 the Query Interval. +	  The first one should be sent out immediately instead of 125/4 +	  seconds from now. +	*/ +	startup_mode = igmp->startup_query_count > 0; +	if (startup_mode) { +		/* +		 * If this is the first time we are sending a query on a +		 * newly configured igmp interface send it out in 1 second +		 * just to give the entire world a tiny bit of time to settle +		 * else the query interval is: +		 * query_interval = pim_ifp->igmp_default_query_interval >> 2; +		 */ +		if (igmp->startup_query_count +		    == igmp->querier_robustness_variable) +			query_interval = 1; +		else +			query_interval = PIM_IGMP_SQI( +				pim_ifp->igmp_default_query_interval); + +		--igmp->startup_query_count; +	} else { +		query_interval = igmp->querier_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 scheduling %d-second (%s) TIMER event for IGMP query on fd=%d", +			ifaddr_str, query_interval, +			startup_mode ? "startup" : "non-startup", igmp->fd); +	} +	igmp->t_igmp_query_timer = NULL; +	thread_add_timer(master, pim_igmp_general_query, igmp, query_interval, +			 &igmp->t_igmp_query_timer);  }  void pim_igmp_general_query_off(struct igmp_sock *igmp)  { -  zassert(igmp); - -  if (PIM_DEBUG_IGMP_TRACE) { -    if (igmp->t_igmp_query_timer) { -      char ifaddr_str[INET_ADDRSTRLEN]; -      pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str)); -      zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s", -		 ifaddr_str, igmp->fd, igmp->interface->name); -    } -  } -  THREAD_OFF(igmp->t_igmp_query_timer); +	zassert(igmp); + +	if (PIM_DEBUG_IGMP_TRACE) { +		if (igmp->t_igmp_query_timer) { +			char ifaddr_str[INET_ADDRSTRLEN]; +			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, +				       sizeof(ifaddr_str)); +			zlog_debug( +				"IGMP querier %s fd=%d cancelling query TIMER event on %s", +				ifaddr_str, igmp->fd, igmp->interface->name); +		} +	} +	THREAD_OFF(igmp->t_igmp_query_timer);  }  /* Issue IGMP general query */  static int pim_igmp_general_query(struct thread *t)  { -  struct igmp_sock *igmp; -  struct in_addr dst_addr; -  struct in_addr group_addr; -  struct pim_interface *pim_ifp; -  int    query_buf_size; - -  igmp = THREAD_ARG(t); - -  zassert(igmp->interface); -  zassert(igmp->interface->info); - -  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]; - -  /* -    RFC3376: 4.1.12. IP Destination Addresses for Queries - -    In IGMPv3, General Queries are sent with an IP destination address -    of 224.0.0.1, the all-systems multicast address.  Group-Specific -    and Group-and-Source-Specific Queries are sent with an IP -    destination address equal to the multicast address of interest. -  */ - -  dst_addr.s_addr   = htonl(INADDR_ALLHOSTS_GROUP); -  group_addr.s_addr = PIM_NET_INADDR_ANY; - -  if (PIM_DEBUG_IGMP_TRACE) { -    char querier_str[INET_ADDRSTRLEN]; -    char dst_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str, -		   sizeof(querier_str)); -    pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str)); -    zlog_debug("Querier %s issuing IGMP general query to %s on %s", -	       querier_str, dst_str, igmp->interface->name); -  } - -  igmp_send_query (pim_ifp->igmp_version, -                   0 /* igmp_group */, -                   igmp->fd, -                   igmp->interface->name, -                   query_buf, -                   sizeof(query_buf), -                   0 /* num_sources */, -                   dst_addr, -                   group_addr, -                   pim_ifp->igmp_query_max_response_time_dsec, -                   1 /* s_flag: always set for general queries */, -                   igmp->querier_robustness_variable, -                   igmp->querier_query_interval); - -  pim_igmp_general_query_on(igmp); - -  return 0; +	struct igmp_sock *igmp; +	struct in_addr dst_addr; +	struct in_addr group_addr; +	struct pim_interface *pim_ifp; +	int query_buf_size; + +	igmp = THREAD_ARG(t); + +	zassert(igmp->interface); +	zassert(igmp->interface->info); + +	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]; + +	/* +	  RFC3376: 4.1.12. IP Destination Addresses for Queries + +	  In IGMPv3, General Queries are sent with an IP destination address +	  of 224.0.0.1, the all-systems multicast address.  Group-Specific +	  and Group-and-Source-Specific Queries are sent with an IP +	  destination address equal to the multicast address of interest. +	*/ + +	dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); +	group_addr.s_addr = PIM_NET_INADDR_ANY; + +	if (PIM_DEBUG_IGMP_TRACE) { +		char querier_str[INET_ADDRSTRLEN]; +		char dst_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str, +			       sizeof(querier_str)); +		pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str)); +		zlog_debug("Querier %s issuing IGMP general query to %s on %s", +			   querier_str, dst_str, igmp->interface->name); +	} + +	igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, igmp->fd, +			igmp->interface->name, query_buf, sizeof(query_buf), +			0 /* num_sources */, dst_addr, group_addr, +			pim_ifp->igmp_query_max_response_time_dsec, +			1 /* s_flag: always set for general queries */, +			igmp->querier_robustness_variable, +			igmp->querier_query_interval); + +	pim_igmp_general_query_on(igmp); + +	return 0;  }  static void sock_close(struct igmp_sock *igmp)  { -  pim_igmp_other_querier_timer_off(igmp); -  pim_igmp_general_query_off(igmp); - -  if (PIM_DEBUG_IGMP_TRACE_DETAIL) { -    if (igmp->t_igmp_read) { -      zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s", -		 inet_ntoa(igmp->ifaddr), igmp->fd, -		 igmp->interface->name); -    } -  } -  THREAD_OFF(igmp->t_igmp_read); -   -  if (close(igmp->fd)) { -    zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s", -	     inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name, -	     errno, safe_strerror(errno)); -  } -   -  if (PIM_DEBUG_IGMP_TRACE_DETAIL) { -    zlog_debug("Deleted IGMP socket %s fd=%d on interface %s", -	       inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name); -  } +	pim_igmp_other_querier_timer_off(igmp); +	pim_igmp_general_query_off(igmp); + +	if (PIM_DEBUG_IGMP_TRACE_DETAIL) { +		if (igmp->t_igmp_read) { +			zlog_debug( +				"Cancelling READ event on IGMP socket %s fd=%d on interface %s", +				inet_ntoa(igmp->ifaddr), igmp->fd, +				igmp->interface->name); +		} +	} +	THREAD_OFF(igmp->t_igmp_read); + +	if (close(igmp->fd)) { +		zlog_err( +			"Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s", +			inet_ntoa(igmp->ifaddr), igmp->fd, +			igmp->interface->name, errno, safe_strerror(errno)); +	} + +	if (PIM_DEBUG_IGMP_TRACE_DETAIL) { +		zlog_debug("Deleted IGMP socket %s fd=%d on interface %s", +			   inet_ntoa(igmp->ifaddr), igmp->fd, +			   igmp->interface->name); +	}  }  void igmp_startup_mode_on(struct igmp_sock *igmp)  { -  struct pim_interface *pim_ifp; +	struct pim_interface *pim_ifp; -  pim_ifp = igmp->interface->info; +	pim_ifp = igmp->interface->info; -  /* -    RFC 3376: 8.7. Startup Query Count +	/* +	  RFC 3376: 8.7. Startup Query Count -    The Startup Query Count is the number of Queries sent out on -    startup, separated by the Startup Query Interval.  Default: the -    Robustness Variable. -  */ -  igmp->startup_query_count = igmp->querier_robustness_variable; +	  The Startup Query Count is the number of Queries sent out on +	  startup, separated by the Startup Query Interval.  Default: the +	  Robustness Variable. +	*/ +	igmp->startup_query_count = igmp->querier_robustness_variable; -  /* -    Since we're (re)starting, reset QQI to default Query Interval -  */ -  igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; +	/* +	  Since we're (re)starting, reset QQI to default Query Interval +	*/ +	igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;  }  static void igmp_group_free(struct igmp_group *group)  { -  list_free(group->group_source_list); +	list_free(group->group_source_list); -  XFREE(MTYPE_PIM_IGMP_GROUP, group); +	XFREE(MTYPE_PIM_IGMP_GROUP, group);  }  static void igmp_group_delete(struct igmp_group *group)  { -  struct listnode *src_node; -  struct listnode *src_nextnode; -  struct igmp_source *src; - -  if (PIM_DEBUG_IGMP_TRACE) { -    char group_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); -    zlog_debug("Deleting IGMP group %s from socket %d interface %s", -	       group_str, -	       group->group_igmp_sock->fd, -	       group->group_igmp_sock->interface->name); -  } - -  for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) { -    igmp_source_delete(src); -  } - -  if (group->t_group_query_retransmit_timer) { -    THREAD_OFF(group->t_group_query_retransmit_timer); -  } - -  group_timer_off(group); -  listnode_delete(group->group_igmp_sock->igmp_group_list, group); -  hash_release (group->group_igmp_sock->igmp_group_hash, group); - -  igmp_group_free(group); +	struct listnode *src_node; +	struct listnode *src_nextnode; +	struct igmp_source *src; + +	if (PIM_DEBUG_IGMP_TRACE) { +		char group_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<group?>", group->group_addr, group_str, +			       sizeof(group_str)); +		zlog_debug("Deleting IGMP group %s from socket %d interface %s", +			   group_str, group->group_igmp_sock->fd, +			   group->group_igmp_sock->interface->name); +	} + +	for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, +			       src)) { +		igmp_source_delete(src); +	} + +	if (group->t_group_query_retransmit_timer) { +		THREAD_OFF(group->t_group_query_retransmit_timer); +	} + +	group_timer_off(group); +	listnode_delete(group->group_igmp_sock->igmp_group_list, group); +	hash_release(group->group_igmp_sock->igmp_group_hash, group); + +	igmp_group_free(group);  }  void igmp_group_delete_empty_include(struct igmp_group *group)  { -  zassert(!group->group_filtermode_isexcl); -  zassert(!listcount(group->group_source_list)); +	zassert(!group->group_filtermode_isexcl); +	zassert(!listcount(group->group_source_list)); -  igmp_group_delete(group); +	igmp_group_delete(group);  }  void igmp_sock_free(struct igmp_sock *igmp)  { -  zassert(!igmp->t_igmp_read); -  zassert(!igmp->t_igmp_query_timer); -  zassert(!igmp->t_other_querier_timer); -  zassert(igmp->igmp_group_list); -  zassert(!listcount(igmp->igmp_group_list)); +	zassert(!igmp->t_igmp_read); +	zassert(!igmp->t_igmp_query_timer); +	zassert(!igmp->t_other_querier_timer); +	zassert(igmp->igmp_group_list); +	zassert(!listcount(igmp->igmp_group_list)); -  list_free(igmp->igmp_group_list); -  hash_free(igmp->igmp_group_hash); +	list_free(igmp->igmp_group_list); +	hash_free(igmp->igmp_group_hash); -  XFREE(MTYPE_PIM_IGMP_SOCKET, igmp); +	XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);  }  void igmp_sock_delete(struct igmp_sock *igmp)  { -  struct pim_interface *pim_ifp; -  struct listnode      *grp_node; -  struct listnode      *grp_nextnode; -  struct igmp_group    *grp; - -  for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) { -    igmp_group_delete(grp); -  } +	struct pim_interface *pim_ifp; +	struct listnode *grp_node; +	struct listnode *grp_nextnode; +	struct igmp_group *grp; + +	for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, +			       grp)) { +		igmp_group_delete(grp); +	} -  sock_close(igmp); +	sock_close(igmp); -  pim_ifp = igmp->interface->info; +	pim_ifp = igmp->interface->info; -  listnode_delete(pim_ifp->igmp_socket_list, igmp); +	listnode_delete(pim_ifp->igmp_socket_list, igmp); -  igmp_sock_free(igmp); +	igmp_sock_free(igmp);  } -void -igmp_sock_delete_all (struct interface *ifp) +void igmp_sock_delete_all(struct interface *ifp)  { -  struct pim_interface *pim_ifp; -  struct listnode *igmp_node, *igmp_nextnode; -  struct igmp_sock *igmp; +	struct pim_interface *pim_ifp; +	struct listnode *igmp_node, *igmp_nextnode; +	struct igmp_sock *igmp; -  pim_ifp = ifp->info; +	pim_ifp = ifp->info; -  for (ALL_LIST_ELEMENTS (pim_ifp->igmp_socket_list, igmp_node, -			  igmp_nextnode, igmp)) -    { -      igmp_sock_delete(igmp); -    } +	for (ALL_LIST_ELEMENTS(pim_ifp->igmp_socket_list, igmp_node, +			       igmp_nextnode, igmp)) { +		igmp_sock_delete(igmp); +	}  } -static unsigned int -igmp_group_hash_key (void *arg) +static unsigned int igmp_group_hash_key(void *arg)  { -  struct igmp_group *group = (struct igmp_group *)arg; +	struct igmp_group *group = (struct igmp_group *)arg; -  return jhash_1word(group->group_addr.s_addr, 0); +	return jhash_1word(group->group_addr.s_addr, 0);  } -static int -igmp_group_hash_equal (const void *arg1, const void *arg2) +static int igmp_group_hash_equal(const void *arg1, const void *arg2)  { -  const struct igmp_group *g1 = (const struct igmp_group *)arg1; -  const struct igmp_group *g2 = (const struct igmp_group *)arg2; +	const struct igmp_group *g1 = (const struct igmp_group *)arg1; +	const struct igmp_group *g2 = (const struct igmp_group *)arg2; -  if (g1->group_addr.s_addr == g2->group_addr.s_addr) -    return 1; +	if (g1->group_addr.s_addr == g2->group_addr.s_addr) +		return 1; -  return 0; +	return 0;  } -static struct igmp_sock *igmp_sock_new(int fd, -				       struct in_addr ifaddr, +static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,  				       struct interface *ifp)  { -  struct pim_interface *pim_ifp; -  struct igmp_sock *igmp; - -  pim_ifp = ifp->info; - -  if (PIM_DEBUG_IGMP_TRACE) { -    zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s", -	       fd, inet_ntoa(ifaddr), ifp->name); -  } - -  igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); -  if (!igmp) { -    zlog_warn("%s %s: XCALLOC() failure", -              __FILE__, __PRETTY_FUNCTION__); -    return 0; -  } - -  igmp->igmp_group_list = list_new(); -  if (!igmp->igmp_group_list) { -    zlog_err("%s %s: failure: igmp_group_list = list_new()", -	     __FILE__, __PRETTY_FUNCTION__); -    return 0; -  } -  igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free; - -  igmp->igmp_group_hash = hash_create (igmp_group_hash_key, -                                       igmp_group_hash_equal, NULL); - -  igmp->fd                          = fd; -  igmp->interface                   = ifp; -  igmp->ifaddr                      = ifaddr; -  igmp->t_igmp_read                 = NULL; -  igmp->t_igmp_query_timer          = NULL; -  igmp->t_other_querier_timer       = NULL; /* no other querier present */ -  igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable; -  igmp->sock_creation               = pim_time_monotonic_sec(); - -  /* -    igmp_startup_mode_on() will reset QQI: - -    igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; -  */ -  igmp_startup_mode_on(igmp); -  pim_igmp_general_query_on(igmp); - -  return igmp; +	struct pim_interface *pim_ifp; +	struct igmp_sock *igmp; + +	pim_ifp = ifp->info; + +	if (PIM_DEBUG_IGMP_TRACE) { +		zlog_debug( +			"Creating IGMP socket fd=%d for address %s on interface %s", +			fd, inet_ntoa(ifaddr), ifp->name); +	} + +	igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp)); +	if (!igmp) { +		zlog_warn("%s %s: XCALLOC() failure", __FILE__, +			  __PRETTY_FUNCTION__); +		return 0; +	} + +	igmp->igmp_group_list = list_new(); +	if (!igmp->igmp_group_list) { +		zlog_err("%s %s: failure: igmp_group_list = list_new()", +			 __FILE__, __PRETTY_FUNCTION__); +		return 0; +	} +	igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free; + +	igmp->igmp_group_hash = +		hash_create(igmp_group_hash_key, igmp_group_hash_equal, NULL); + +	igmp->fd = fd; +	igmp->interface = ifp; +	igmp->ifaddr = ifaddr; +	igmp->t_igmp_read = NULL; +	igmp->t_igmp_query_timer = NULL; +	igmp->t_other_querier_timer = NULL; /* no other querier present */ +	igmp->querier_robustness_variable = +		pim_ifp->igmp_default_robustness_variable; +	igmp->sock_creation = pim_time_monotonic_sec(); + +	/* +	  igmp_startup_mode_on() will reset QQI: + +	  igmp->querier_query_interval = pim_ifp->igmp_default_query_interval; +	*/ +	igmp_startup_mode_on(igmp); +	pim_igmp_general_query_on(igmp); + +	return igmp;  } -static void igmp_read_on (struct igmp_sock *igmp); +static void igmp_read_on(struct igmp_sock *igmp); -static int -pim_igmp_read (struct thread *t) +static int pim_igmp_read(struct thread *t)  { -  uint8_t buf[10000]; -  struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t); -  struct sockaddr_in from; -  struct sockaddr_in to; -  socklen_t fromlen = sizeof(from); -  socklen_t tolen = sizeof(to); -  ifindex_t ifindex = -1; -  int cont = 1; -  int len; - -  while (cont) -    { -      len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), -				  &from, &fromlen, -				  &to, &tolen, -				  &ifindex); -      if (len < 0) -	{ -	  if (errno == EINTR) -	    continue; -	  if (errno == EWOULDBLOCK || errno == EAGAIN) -	    break; - -	  goto done; +	uint8_t buf[10000]; +	struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t); +	struct sockaddr_in from; +	struct sockaddr_in to; +	socklen_t fromlen = sizeof(from); +	socklen_t tolen = sizeof(to); +	ifindex_t ifindex = -1; +	int cont = 1; +	int len; + +	while (cont) { +		len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from, +					    &fromlen, &to, &tolen, &ifindex); +		if (len < 0) { +			if (errno == EINTR) +				continue; +			if (errno == EWOULDBLOCK || errno == EAGAIN) +				break; + +			goto done; +		}  	} -    } - done: -  igmp_read_on(igmp); -  return 0; +done: +	igmp_read_on(igmp); +	return 0;  } -static void -igmp_read_on (struct igmp_sock *igmp) +static void igmp_read_on(struct igmp_sock *igmp)  { -  if (PIM_DEBUG_IGMP_TRACE_DETAIL) { -    zlog_debug("Scheduling READ event on IGMP socket fd=%d", -	       igmp->fd); -  } -  igmp->t_igmp_read = NULL; -  thread_add_read(master, pim_igmp_read, igmp, igmp->fd, &igmp->t_igmp_read); - +	if (PIM_DEBUG_IGMP_TRACE_DETAIL) { +		zlog_debug("Scheduling READ event on IGMP socket fd=%d", +			   igmp->fd); +	} +	igmp->t_igmp_read = NULL; +	thread_add_read(master, pim_igmp_read, igmp, igmp->fd, +			&igmp->t_igmp_read);  }  struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,  				    struct in_addr ifaddr,  				    struct interface *ifp)  { -  struct pim_interface *pim_ifp; -  struct igmp_sock *igmp; -  int fd; +	struct pim_interface *pim_ifp; +	struct igmp_sock *igmp; +	int fd; -  pim_ifp = ifp->info; +	pim_ifp = ifp->info; -  fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options); -  if (fd < 0) { -    zlog_warn("Could not open IGMP socket for %s on %s", -	      inet_ntoa(ifaddr), ifp->name); -    return 0; -  } +	fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options); +	if (fd < 0) { +		zlog_warn("Could not open IGMP socket for %s on %s", +			  inet_ntoa(ifaddr), ifp->name); +		return 0; +	} -  igmp = igmp_sock_new(fd, ifaddr, ifp); -  if (!igmp) { -    zlog_err("%s %s: igmp_sock_new() failure", -	     __FILE__, __PRETTY_FUNCTION__); -    close(fd); -    return 0; -  } +	igmp = igmp_sock_new(fd, ifaddr, ifp); +	if (!igmp) { +		zlog_err("%s %s: igmp_sock_new() failure", __FILE__, +			 __PRETTY_FUNCTION__); +		close(fd); +		return 0; +	} -  igmp_read_on (igmp); +	igmp_read_on(igmp); -  listnode_add(igmp_sock_list, igmp); +	listnode_add(igmp_sock_list, igmp);  #ifdef IGMP_SOCK_DUMP -  igmp_sock_dump(igmp_sock_array); +	igmp_sock_dump(igmp_sock_array);  #endif -  return igmp; +	return igmp;  }  /* @@ -946,210 +956,203 @@ struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,   */  static int igmp_group_timer(struct thread *t)  { -  struct igmp_group *group; +	struct igmp_group *group; -  group = THREAD_ARG(t); +	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("%s: Timer for group %s on interface %s", -	       __PRETTY_FUNCTION__, -	       group_str, group->group_igmp_sock->interface->name); -  } +	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: Timer for group %s on interface %s", +			   __PRETTY_FUNCTION__, group_str, +			   group->group_igmp_sock->interface->name); +	} -  zassert(group->group_filtermode_isexcl); +	zassert(group->group_filtermode_isexcl); -  group->group_filtermode_isexcl = 0; +	group->group_filtermode_isexcl = 0; -  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ -  igmp_anysource_forward_stop(group); +	/* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ +	igmp_anysource_forward_stop(group); -  igmp_source_delete_expired(group->group_source_list); +	igmp_source_delete_expired(group->group_source_list); -  zassert(!group->group_filtermode_isexcl); +	zassert(!group->group_filtermode_isexcl); -  /* -    RFC 3376: 6.2.2. Definition of Group Timers +	/* +	  RFC 3376: 6.2.2. Definition of Group Timers -    If there are no more source records for the group, delete group -    record. -  */ -  if (listcount(group->group_source_list) < 1) { -    igmp_group_delete_empty_include(group); -  } +	  If there are no more source records for the group, delete group +	  record. +	*/ +	if (listcount(group->group_source_list) < 1) { +		igmp_group_delete_empty_include(group); +	} -  return 0; +	return 0;  }  static void group_timer_off(struct igmp_group *group)  { -  if (!group->t_group_timer) -    return; - -  if (PIM_DEBUG_IGMP_TRACE) { -    char group_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); -    zlog_debug("Cancelling TIMER event for group %s on %s", -	       group_str, group->group_igmp_sock->interface->name); -  } -  THREAD_OFF(group->t_group_timer); +	if (!group->t_group_timer) +		return; + +	if (PIM_DEBUG_IGMP_TRACE) { +		char group_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<group?>", group->group_addr, group_str, +			       sizeof(group_str)); +		zlog_debug("Cancelling TIMER event for group %s on %s", +			   group_str, group->group_igmp_sock->interface->name); +	} +	THREAD_OFF(group->t_group_timer);  } -void igmp_group_timer_on(struct igmp_group *group, -			 long interval_msec, const char *ifname) +void igmp_group_timer_on(struct igmp_group *group, long interval_msec, +			 const char *ifname)  { -  group_timer_off(group); - -  if (PIM_DEBUG_IGMP_EVENTS) { -    char group_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); -    zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s", -	       interval_msec / 1000, -	       interval_msec % 1000, -	       group_str, ifname); -  } - -  /* -    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); - -  thread_add_timer_msec(master, igmp_group_timer, group, interval_msec, -                        &group->t_group_timer); +	group_timer_off(group); + +	if (PIM_DEBUG_IGMP_EVENTS) { +		char group_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<group?>", group->group_addr, group_str, +			       sizeof(group_str)); +		zlog_debug( +			"Scheduling %ld.%03ld sec TIMER event for group %s on %s", +			interval_msec / 1000, interval_msec % 1000, group_str, +			ifname); +	} + +	/* +	  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); + +	thread_add_timer_msec(master, igmp_group_timer, group, interval_msec, +			      &group->t_group_timer);  } -struct igmp_group * -find_group_by_addr (struct igmp_sock *igmp, -		   struct in_addr group_addr) +struct igmp_group *find_group_by_addr(struct igmp_sock *igmp, +				      struct in_addr group_addr)  { -  struct igmp_group lookup; +	struct igmp_group lookup; -  lookup.group_addr.s_addr = group_addr.s_addr; +	lookup.group_addr.s_addr = group_addr.s_addr; -  return hash_lookup(igmp->igmp_group_hash, &lookup); +	return hash_lookup(igmp->igmp_group_hash, &lookup);  }  struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,  					  struct in_addr group_addr)  { -  struct igmp_group *group; - -  group = find_group_by_addr(igmp, group_addr); -  if (group) { -    return group; -  } - -  if (!pim_is_group_224_4 (group_addr)) -    { -      zlog_warn("%s: Group Specified is not part of 224.0.0.0/4", -		__PRETTY_FUNCTION__); -      return NULL; -    } - -  if (pim_is_group_224_0_0_0_24 (group_addr)) -    { -      zlog_warn("%s: Group specified is part of 224.0.0.0/24", -		__PRETTY_FUNCTION__); -      return NULL; -    } -  /* -    Non-existant group is created as INCLUDE {empty}: - -    RFC 3376 - 5.1. Action on Change of Interface State - -    If no interface state existed for that multicast address before -    the change (i.e., the change consisted of creating a new -    per-interface record), or if no state exists after the change -    (i.e., the change consisted of deleting a per-interface record), -    then the "non-existent" state is considered to have a filter mode -    of INCLUDE and an empty source list. -  */ - -  group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group)); -  if (!group) { -    zlog_warn("%s %s: XCALLOC() failure", -	      __FILE__, __PRETTY_FUNCTION__); -    return NULL; /* error, not found, could not create */ -  } - -  group->group_source_list = list_new(); -  if (!group->group_source_list) { -    zlog_warn("%s %s: list_new() failure", -	      __FILE__, __PRETTY_FUNCTION__); -    XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */ -    return NULL; /* error, not found, could not initialize */ -  } -  group->group_source_list->del = (void (*)(void *)) igmp_source_free; - -  group->t_group_timer                         = NULL; -  group->t_group_query_retransmit_timer        = NULL; -  group->group_specific_query_retransmit_count = 0; -  group->group_addr                            = group_addr; -  group->group_igmp_sock                       = igmp; -  group->last_igmp_v1_report_dsec              = -1; -  group->last_igmp_v2_report_dsec              = -1; -  group->group_creation                        = pim_time_monotonic_sec(); -  group->igmp_version                          = IGMP_DEFAULT_VERSION; - -  /* initialize new group as INCLUDE {empty} */ -  group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ - -  listnode_add(igmp->igmp_group_list, group); -  group = hash_get (igmp->igmp_group_hash, group, hash_alloc_intern); - -  if (PIM_DEBUG_IGMP_TRACE) { -    char group_str[INET_ADDRSTRLEN]; -    pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str)); -    zlog_debug("Creating new IGMP group %s on socket %d interface %s", -	       group_str, igmp->fd, igmp->interface->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); /* INCLUDE mode */ -  zassert(!group->t_group_timer); /* group timer == 0 */ - -  /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ -  igmp_anysource_forward_stop(group); - -  return group; +	struct igmp_group *group; + +	group = find_group_by_addr(igmp, group_addr); +	if (group) { +		return group; +	} + +	if (!pim_is_group_224_4(group_addr)) { +		zlog_warn("%s: Group Specified is not part of 224.0.0.0/4", +			  __PRETTY_FUNCTION__); +		return NULL; +	} + +	if (pim_is_group_224_0_0_0_24(group_addr)) { +		zlog_warn("%s: Group specified is part of 224.0.0.0/24", +			  __PRETTY_FUNCTION__); +		return NULL; +	} +	/* +	  Non-existant group is created as INCLUDE {empty}: + +	  RFC 3376 - 5.1. Action on Change of Interface State + +	  If no interface state existed for that multicast address before +	  the change (i.e., the change consisted of creating a new +	  per-interface record), or if no state exists after the change +	  (i.e., the change consisted of deleting a per-interface record), +	  then the "non-existent" state is considered to have a filter mode +	  of INCLUDE and an empty source list. +	*/ + +	group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group)); +	if (!group) { +		zlog_warn("%s %s: XCALLOC() failure", __FILE__, +			  __PRETTY_FUNCTION__); +		return NULL; /* error, not found, could not create */ +	} + +	group->group_source_list = list_new(); +	if (!group->group_source_list) { +		zlog_warn("%s %s: list_new() failure", __FILE__, +			  __PRETTY_FUNCTION__); +		XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */ +		return NULL; /* error, not found, could not initialize */ +	} +	group->group_source_list->del = (void (*)(void *))igmp_source_free; + +	group->t_group_timer = NULL; +	group->t_group_query_retransmit_timer = NULL; +	group->group_specific_query_retransmit_count = 0; +	group->group_addr = group_addr; +	group->group_igmp_sock = igmp; +	group->last_igmp_v1_report_dsec = -1; +	group->last_igmp_v2_report_dsec = -1; +	group->group_creation = pim_time_monotonic_sec(); +	group->igmp_version = IGMP_DEFAULT_VERSION; + +	/* initialize new group as INCLUDE {empty} */ +	group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */ + +	listnode_add(igmp->igmp_group_list, group); +	group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern); + +	if (PIM_DEBUG_IGMP_TRACE) { +		char group_str[INET_ADDRSTRLEN]; +		pim_inet4_dump("<group?>", group->group_addr, group_str, +			       sizeof(group_str)); +		zlog_debug( +			"Creating new IGMP group %s on socket %d interface %s", +			group_str, igmp->fd, igmp->interface->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); /* INCLUDE mode */ +	zassert(!group->t_group_timer);		  /* group timer == 0 */ + +	/* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */ +	igmp_anysource_forward_stop(group); + +	return group;  } -void -igmp_send_query (int igmp_version, -                 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_send_query(int igmp_version, 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)  { -  if (igmp_version == 3) { -    igmp_v3_send_query (group, fd, ifname, query_buf, -                        query_buf_size, num_sources, -                        dst_addr, group_addr, -                        query_max_response_time_dsec, s_flag, -                        querier_robustness_variable, -                        querier_query_interval); -  } else if (igmp_version == 2) { -    igmp_v2_send_query (group, fd, ifname, query_buf, -                        dst_addr, group_addr, -                        query_max_response_time_dsec); -  } +	if (igmp_version == 3) { +		igmp_v3_send_query(group, fd, ifname, query_buf, query_buf_size, +				   num_sources, dst_addr, group_addr, +				   query_max_response_time_dsec, s_flag, +				   querier_robustness_variable, +				   querier_query_interval); +	} else if (igmp_version == 2) { +		igmp_v2_send_query(group, fd, ifname, query_buf, dst_addr, +				   group_addr, query_max_response_time_dsec); +	}  }  | 
