1 /*
2  * PIM for Quagga
3  * Copyright (C) 2008  Everton da Silva Marques
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; see the file COPYING; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <zebra.h>
21 
22 #include "memory.h"
23 #include "prefix.h"
24 #include "if.h"
25 #include "hash.h"
26 #include "jhash.h"
27 #include "lib_errors.h"
28 
29 #include "pimd.h"
30 #include "pim_igmp.h"
31 #include "pim_igmpv2.h"
32 #include "pim_igmpv3.h"
33 #include "pim_igmp_mtrace.h"
34 #include "pim_iface.h"
35 #include "pim_sock.h"
36 #include "pim_mroute.h"
37 #include "pim_str.h"
38 #include "pim_util.h"
39 #include "pim_time.h"
40 #include "pim_zebra.h"
41 
42 static void group_timer_off(struct igmp_group *group);
43 static int pim_igmp_general_query(struct thread *t);
44 
45 /* This socket is used for TXing IGMP packets only, IGMP RX happens
46  * in pim_mroute_msg()
47  */
igmp_sock_open(struct in_addr ifaddr,struct interface * ifp,uint32_t pim_options)48 static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
49 			  uint32_t pim_options)
50 {
51 	int fd;
52 	int join = 0;
53 	struct in_addr group;
54 
55 	fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
56 
57 	if (fd < 0)
58 		return -1;
59 
60 	if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
61 		if (inet_aton(PIM_ALL_ROUTERS, &group)) {
62 			if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
63 				++join;
64 		} else {
65 			zlog_warn(
66 				"%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
67 				__FILE__, __func__, fd, inet_ntoa(ifaddr),
68 				PIM_ALL_ROUTERS, errno, safe_strerror(errno));
69 		}
70 	}
71 
72 	/*
73 	  IGMP routers periodically send IGMP general queries to
74 	  AllSystems=224.0.0.1
75 	  IGMP routers must receive general queries for querier election.
76 	*/
77 	if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
78 		if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex))
79 			++join;
80 	} else {
81 		zlog_warn(
82 			"%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
83 			__FILE__, __func__, fd, inet_ntoa(ifaddr),
84 			PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
85 	}
86 
87 	if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
88 		if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex)) {
89 			++join;
90 		}
91 	} else {
92 		zlog_warn(
93 			"%s %s: IGMP socket fd=%d interface %s: could not solve %s to group address: errno=%d: %s",
94 			__FILE__, __func__, fd, inet_ntoa(ifaddr),
95 			PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
96 	}
97 
98 	if (!join) {
99 		flog_err_sys(
100 			EC_LIB_SOCKET,
101 			"IGMP socket fd=%d could not join any group on interface address %s",
102 			fd, inet_ntoa(ifaddr));
103 		close(fd);
104 		fd = -1;
105 	}
106 
107 	return fd;
108 }
109 
110 #undef IGMP_SOCK_DUMP
111 
112 #ifdef IGMP_SOCK_DUMP
igmp_sock_dump(array_t * igmp_sock_array)113 static void igmp_sock_dump(array_t *igmp_sock_array)
114 {
115 	int size = array_size(igmp_sock_array);
116 	for (int i = 0; i < size; ++i) {
117 
118 		struct igmp_sock *igmp = array_get(igmp_sock_array, i);
119 
120 		zlog_debug("%s %s: [%d/%d] igmp_addr=%s fd=%d", __FILE__,
121 			   __func__, i, size, inet_ntoa(igmp->ifaddr),
122 			   igmp->fd);
123 	}
124 }
125 #endif
126 
pim_igmp_sock_lookup_ifaddr(struct list * igmp_sock_list,struct in_addr ifaddr)127 struct igmp_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
128 					      struct in_addr ifaddr)
129 {
130 	struct listnode *sock_node;
131 	struct igmp_sock *igmp;
132 
133 #ifdef IGMP_SOCK_DUMP
134 	igmp_sock_dump(igmp_sock_list);
135 #endif
136 
137 	for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
138 		if (ifaddr.s_addr == igmp->ifaddr.s_addr)
139 			return igmp;
140 
141 	return NULL;
142 }
143 
igmp_sock_lookup_by_fd(struct list * igmp_sock_list,int fd)144 struct igmp_sock *igmp_sock_lookup_by_fd(struct list *igmp_sock_list, int fd)
145 {
146 	struct listnode *sock_node;
147 	struct igmp_sock *igmp;
148 
149 	for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
150 		if (fd == igmp->fd)
151 			return igmp;
152 
153 	return NULL;
154 }
155 
pim_igmp_other_querier_expire(struct thread * t)156 static int pim_igmp_other_querier_expire(struct thread *t)
157 {
158 	struct igmp_sock *igmp;
159 
160 	igmp = THREAD_ARG(t);
161 
162 	zassert(!igmp->t_igmp_query_timer);
163 
164 	if (PIM_DEBUG_IGMP_TRACE) {
165 		char ifaddr_str[INET_ADDRSTRLEN];
166 		pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
167 			       sizeof(ifaddr_str));
168 		zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
169 	}
170 
171 	/*
172 	  We are the current querier, then
173 	  re-start sending general queries.
174 	  RFC 2236 - sec 7 Other Querier
175 	  present timer expired (Send General
176 	  Query, Set Gen. Query. timer)
177 	*/
178 	pim_igmp_general_query(t);
179 
180 	return 0;
181 }
182 
pim_igmp_other_querier_timer_on(struct igmp_sock * igmp)183 void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp)
184 {
185 	long other_querier_present_interval_msec;
186 	struct pim_interface *pim_ifp;
187 
188 	zassert(igmp);
189 	zassert(igmp->interface);
190 	zassert(igmp->interface->info);
191 
192 	pim_ifp = igmp->interface->info;
193 
194 	if (igmp->t_other_querier_timer) {
195 		/*
196 		  There is other querier present already,
197 		  then reset the other-querier-present timer.
198 		*/
199 
200 		if (PIM_DEBUG_IGMP_TRACE) {
201 			char ifaddr_str[INET_ADDRSTRLEN];
202 			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
203 				       sizeof(ifaddr_str));
204 			zlog_debug(
205 				"Querier %s resetting TIMER event for Other-Querier-Present",
206 				ifaddr_str);
207 		}
208 		THREAD_OFF(igmp->t_other_querier_timer);
209 	} else {
210 		/*
211 		  We are the current querier, then stop sending general queries:
212 		  igmp->t_igmp_query_timer = NULL;
213 		*/
214 		pim_igmp_general_query_off(igmp);
215 	}
216 
217 	/*
218 	  Since this socket is starting the other-querier-present timer,
219 	  there should not be periodic query timer for this socket.
220 	 */
221 	zassert(!igmp->t_igmp_query_timer);
222 
223 	/*
224 	  RFC 3376: 8.5. Other Querier Present Interval
225 
226 	  The Other Querier Present Interval is the length of time that must
227 	  pass before a multicast router decides that there is no longer
228 	  another multicast router which should be the querier.  This value
229 	  MUST be ((the Robustness Variable) times (the Query Interval)) plus
230 	  (one half of one Query Response Interval).
231 
232 	  other_querier_present_interval_msec = \
233 	    igmp->querier_robustness_variable * \
234 	    1000 * igmp->querier_query_interval + \
235 	    100 * (pim_ifp->query_max_response_time_dsec >> 1);
236 	*/
237 	other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC(
238 		igmp->querier_robustness_variable, igmp->querier_query_interval,
239 		pim_ifp->igmp_query_max_response_time_dsec);
240 
241 	if (PIM_DEBUG_IGMP_TRACE) {
242 		char ifaddr_str[INET_ADDRSTRLEN];
243 		pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
244 			       sizeof(ifaddr_str));
245 		zlog_debug(
246 			"Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
247 			ifaddr_str, other_querier_present_interval_msec / 1000,
248 			other_querier_present_interval_msec % 1000);
249 	}
250 
251 	thread_add_timer_msec(router->master, pim_igmp_other_querier_expire,
252 			      igmp, other_querier_present_interval_msec,
253 			      &igmp->t_other_querier_timer);
254 }
255 
pim_igmp_other_querier_timer_off(struct igmp_sock * igmp)256 void pim_igmp_other_querier_timer_off(struct igmp_sock *igmp)
257 {
258 	zassert(igmp);
259 
260 	if (PIM_DEBUG_IGMP_TRACE) {
261 		if (igmp->t_other_querier_timer) {
262 			char ifaddr_str[INET_ADDRSTRLEN];
263 			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
264 				       sizeof(ifaddr_str));
265 			zlog_debug(
266 				"IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
267 				ifaddr_str, igmp->fd, igmp->interface->name);
268 		}
269 	}
270 	THREAD_OFF(igmp->t_other_querier_timer);
271 }
272 
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)273 static int igmp_recv_query(struct igmp_sock *igmp, int query_version,
274 			   int max_resp_code, struct in_addr from,
275 			   const char *from_str, char *igmp_msg,
276 			   int igmp_msg_len)
277 {
278 	struct interface *ifp;
279 	struct pim_interface *pim_ifp;
280 	struct in_addr group_addr;
281 	uint16_t recv_checksum;
282 	uint16_t checksum;
283 
284 	if (igmp->mtrace_only)
285 		return 0;
286 
287 	memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
288 
289 	ifp = igmp->interface;
290 	pim_ifp = ifp->info;
291 
292 	recv_checksum = *(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET);
293 
294 	/* for computing checksum */
295 	*(uint16_t *)(igmp_msg + IGMP_CHECKSUM_OFFSET) = 0;
296 
297 	checksum = in_cksum(igmp_msg, igmp_msg_len);
298 	if (checksum != recv_checksum) {
299 		zlog_warn(
300 			"Recv IGMP query v%d from %s on %s: checksum mismatch: received=%x computed=%x",
301 			query_version, from_str, ifp->name, recv_checksum,
302 			checksum);
303 		return -1;
304 	}
305 
306 	if (!pim_if_connected_to_source(ifp, from)) {
307 		if (PIM_DEBUG_IGMP_PACKETS)
308 			zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
309 				   ifp->name, from_str);
310 		return 0;
311 	}
312 
313 	if (if_lookup_exact_address(&from, AF_INET, ifp->vrf_id)) {
314 		if (PIM_DEBUG_IGMP_PACKETS)
315 			zlog_debug("Recv IGMP query on interface: %s from ourself %s",
316 				   ifp->name, from_str);
317 		return 0;
318 	}
319 
320 	/* Collecting IGMP Rx stats */
321 	switch (query_version) {
322 	case 1:
323 		igmp->rx_stats.query_v1++;
324 		break;
325 	case 2:
326 		igmp->rx_stats.query_v2++;
327 		break;
328 	case 3:
329 		igmp->rx_stats.query_v3++;
330 		break;
331 	default:
332 		igmp->rx_stats.unsupported++;
333 	}
334 
335 	/*
336 	 * RFC 3376 defines some guidelines on operating in backwards
337 	 * compatibility with older versions of IGMP but there are some gaps in
338 	 * the logic:
339 	 *
340 	 * - once we drop from say version 3 to version 2 we will never go back
341 	 *   to version 3 even if the node that TXed an IGMP v2 query upgrades
342 	 *   to v3
343 	 *
344 	 * - The node with the lowest IP is the querier so we will only know to
345 	 *   drop from v3 to v2 if the node that is the querier is also the one
346 	 *   that is running igmp v2.  If a non-querier only supports igmp v2
347 	 *   we will have no way of knowing.
348 	 *
349 	 * For now we will simplify things and inform the user that they need to
350 	 * configure all PIM routers to use the same version of IGMP.
351 	 */
352 	if (query_version != pim_ifp->igmp_version) {
353 		zlog_warn(
354 			"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",
355 			query_version, from_str, ifp->name,
356 			pim_ifp->igmp_version);
357 		return 0;
358 	}
359 
360 	if (PIM_DEBUG_IGMP_PACKETS) {
361 		char group_str[INET_ADDRSTRLEN];
362 		pim_inet4_dump("<group?>", group_addr, group_str,
363 			       sizeof(group_str));
364 		zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
365 			   query_version, from_str, ifp->name, group_str);
366 	}
367 
368 	/*
369 	  RFC 3376: 6.6.2. Querier Election
370 
371 	  When a router receives a query with a lower IP address, it sets
372 	  the Other-Querier-Present timer to Other Querier Present Interval
373 	  and ceases to send queries on the network if it was the previously
374 	  elected querier.
375 	 */
376 	if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
377 
378 		if (PIM_DEBUG_IGMP_TRACE) {
379 			char ifaddr_str[INET_ADDRSTRLEN];
380 			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
381 				       sizeof(ifaddr_str));
382 			zlog_debug(
383 				"%s: local address %s (%u) lost querier election to %s (%u)",
384 				ifp->name, ifaddr_str,
385 				ntohl(igmp->ifaddr.s_addr), from_str,
386 				ntohl(from.s_addr));
387 		}
388 
389 		pim_igmp_other_querier_timer_on(igmp);
390 	}
391 
392 	/* IGMP version 3 is the only one where we process the RXed query */
393 	if (query_version == 3) {
394 		igmp_v3_recv_query(igmp, from_str, igmp_msg);
395 	}
396 
397 	return 0;
398 }
399 
on_trace(const char * label,struct interface * ifp,struct in_addr from)400 static void on_trace(const char *label, struct interface *ifp,
401 		     struct in_addr from)
402 {
403 	if (PIM_DEBUG_IGMP_TRACE) {
404 		char from_str[INET_ADDRSTRLEN];
405 		pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
406 		zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
407 	}
408 }
409 
igmp_v1_recv_report(struct igmp_sock * igmp,struct in_addr from,const char * from_str,char * igmp_msg,int igmp_msg_len)410 static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from,
411 			       const char *from_str, char *igmp_msg,
412 			       int igmp_msg_len)
413 {
414 	struct interface *ifp = igmp->interface;
415 	struct igmp_group *group;
416 	struct in_addr group_addr;
417 
418 	on_trace(__func__, igmp->interface, from);
419 
420 	if (igmp->mtrace_only)
421 		return 0;
422 
423 	if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
424 		zlog_warn(
425 			"Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
426 			from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
427 		return -1;
428 	}
429 
430 	/* Collecting IGMP Rx stats */
431 	igmp->rx_stats.report_v1++;
432 
433 	if (PIM_DEBUG_IGMP_TRACE) {
434 		zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
435 	}
436 
437 	memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
438 
439 	if (pim_is_group_filtered(ifp->info, &group_addr))
440 		return -1;
441 
442 	/* non-existant group is created as INCLUDE {empty} */
443 	group = igmp_add_group_by_addr(igmp, group_addr);
444 	if (!group) {
445 		return -1;
446 	}
447 
448 	group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
449 
450 	return 0;
451 }
452 
pim_igmp_packet(struct igmp_sock * igmp,char * buf,size_t len)453 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
454 {
455 	struct ip *ip_hdr;
456 	size_t ip_hlen; /* ip header length in bytes */
457 	char *igmp_msg;
458 	int igmp_msg_len;
459 	int msg_type;
460 	char from_str[INET_ADDRSTRLEN];
461 	char to_str[INET_ADDRSTRLEN];
462 
463 	if (len < sizeof(*ip_hdr)) {
464 		zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
465 			  sizeof(*ip_hdr));
466 		return -1;
467 	}
468 
469 	ip_hdr = (struct ip *)buf;
470 
471 	pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
472 	pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
473 
474 	ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
475 
476 	if (ip_hlen > len) {
477 		zlog_warn(
478 			"IGMP packet header claims size %zu, but we only have %zu bytes",
479 			ip_hlen, len);
480 		return -1;
481 	}
482 
483 	igmp_msg = buf + ip_hlen;
484 	igmp_msg_len = len - ip_hlen;
485 
486 	if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
487 		zlog_warn("IGMP message size=%d shorter than minimum=%d",
488 			  igmp_msg_len, PIM_IGMP_MIN_LEN);
489 		return -1;
490 	}
491 
492 	msg_type = *igmp_msg;
493 
494 	if (PIM_DEBUG_IGMP_PACKETS) {
495 		zlog_debug(
496 			"Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
497 			from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
498 			msg_type, igmp_msg_len);
499 	}
500 
501 	switch (msg_type) {
502 	case PIM_IGMP_MEMBERSHIP_QUERY: {
503 		int max_resp_code = igmp_msg[1];
504 		int query_version;
505 
506 		/*
507 		  RFC 3376: 7.1. Query Version Distinctions
508 		  IGMPv1 Query: length = 8 octets AND Max Resp Code field is
509 		  zero
510 		  IGMPv2 Query: length = 8 octets AND Max Resp Code field is
511 		  non-zero
512 		  IGMPv3 Query: length >= 12 octets
513 		*/
514 
515 		if (igmp_msg_len == 8) {
516 			query_version = max_resp_code ? 2 : 1;
517 		} else if (igmp_msg_len >= 12) {
518 			query_version = 3;
519 		} else {
520 			zlog_warn("Unknown IGMP query version");
521 			return -1;
522 		}
523 
524 		return igmp_recv_query(igmp, query_version, max_resp_code,
525 				       ip_hdr->ip_src, from_str, igmp_msg,
526 				       igmp_msg_len);
527 	}
528 
529 	case PIM_IGMP_V3_MEMBERSHIP_REPORT:
530 		return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
531 					   igmp_msg, igmp_msg_len);
532 
533 	case PIM_IGMP_V2_MEMBERSHIP_REPORT:
534 		return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
535 					   igmp_msg, igmp_msg_len);
536 
537 	case PIM_IGMP_V1_MEMBERSHIP_REPORT:
538 		return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
539 					   igmp_msg, igmp_msg_len);
540 
541 	case PIM_IGMP_V2_LEAVE_GROUP:
542 		return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
543 					  igmp_msg, igmp_msg_len);
544 
545 	case PIM_IGMP_MTRACE_RESPONSE:
546 		return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
547 						 from_str, igmp_msg,
548 						 igmp_msg_len);
549 	case PIM_IGMP_MTRACE_QUERY_REQUEST:
550 		return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
551 						from_str, igmp_msg,
552 						igmp_msg_len);
553 	}
554 
555 	zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
556 
557 	/* Collecting IGMP Rx stats */
558 	igmp->rx_stats.unsupported++;
559 
560 	return -1;
561 }
562 
pim_igmp_general_query_on(struct igmp_sock * igmp)563 void pim_igmp_general_query_on(struct igmp_sock *igmp)
564 {
565 	struct pim_interface *pim_ifp;
566 	int startup_mode;
567 	int query_interval;
568 
569 	/*
570 	  Since this socket is starting as querier,
571 	  there should not exist a timer for other-querier-present.
572 	 */
573 	zassert(!igmp->t_other_querier_timer);
574 	pim_ifp = igmp->interface->info;
575 	zassert(pim_ifp);
576 
577 	/*
578 	  RFC 3376: 8.6. Startup Query Interval
579 
580 	  The Startup Query Interval is the interval between General Queries
581 	  sent by a Querier on startup.  Default: 1/4 the Query Interval.
582 	  The first one should be sent out immediately instead of 125/4
583 	  seconds from now.
584 	*/
585 	startup_mode = igmp->startup_query_count > 0;
586 	if (startup_mode) {
587 		/*
588 		 * If this is the first time we are sending a query on a
589 		 * newly configured igmp interface send it out in 1 second
590 		 * just to give the entire world a tiny bit of time to settle
591 		 * else the query interval is:
592 		 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
593 		 */
594 		if (igmp->startup_query_count
595 		    == igmp->querier_robustness_variable)
596 			query_interval = 1;
597 		else
598 			query_interval = PIM_IGMP_SQI(
599 				pim_ifp->igmp_default_query_interval);
600 
601 		--igmp->startup_query_count;
602 	} else {
603 		query_interval = igmp->querier_query_interval;
604 	}
605 
606 	if (PIM_DEBUG_IGMP_TRACE) {
607 		char ifaddr_str[INET_ADDRSTRLEN];
608 		pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
609 			       sizeof(ifaddr_str));
610 		zlog_debug(
611 			"Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
612 			ifaddr_str, query_interval,
613 			startup_mode ? "startup" : "non-startup", igmp->fd);
614 	}
615 	igmp->t_igmp_query_timer = NULL;
616 	thread_add_timer(router->master, pim_igmp_general_query, igmp,
617 			 query_interval, &igmp->t_igmp_query_timer);
618 }
619 
pim_igmp_general_query_off(struct igmp_sock * igmp)620 void pim_igmp_general_query_off(struct igmp_sock *igmp)
621 {
622 	zassert(igmp);
623 
624 	if (PIM_DEBUG_IGMP_TRACE) {
625 		if (igmp->t_igmp_query_timer) {
626 			char ifaddr_str[INET_ADDRSTRLEN];
627 			pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
628 				       sizeof(ifaddr_str));
629 			zlog_debug(
630 				"IGMP querier %s fd=%d cancelling query TIMER event on %s",
631 				ifaddr_str, igmp->fd, igmp->interface->name);
632 		}
633 	}
634 	THREAD_OFF(igmp->t_igmp_query_timer);
635 }
636 
637 /* Issue IGMP general query */
pim_igmp_general_query(struct thread * t)638 static int pim_igmp_general_query(struct thread *t)
639 {
640 	struct igmp_sock *igmp;
641 	struct in_addr dst_addr;
642 	struct in_addr group_addr;
643 	struct pim_interface *pim_ifp;
644 	int query_buf_size;
645 
646 	igmp = THREAD_ARG(t);
647 
648 	zassert(igmp->interface);
649 	zassert(igmp->interface->info);
650 
651 	pim_ifp = igmp->interface->info;
652 
653 	if (pim_ifp->igmp_version == 3) {
654 		query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
655 	} else {
656 		query_buf_size = IGMP_V12_MSG_SIZE;
657 	}
658 
659 	char query_buf[query_buf_size];
660 
661 	/*
662 	  RFC3376: 4.1.12. IP Destination Addresses for Queries
663 
664 	  In IGMPv3, General Queries are sent with an IP destination address
665 	  of 224.0.0.1, the all-systems multicast address.  Group-Specific
666 	  and Group-and-Source-Specific Queries are sent with an IP
667 	  destination address equal to the multicast address of interest.
668 	*/
669 
670 	dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
671 	group_addr.s_addr = PIM_NET_INADDR_ANY;
672 
673 	if (PIM_DEBUG_IGMP_TRACE) {
674 		char querier_str[INET_ADDRSTRLEN];
675 		char dst_str[INET_ADDRSTRLEN];
676 		pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
677 			       sizeof(querier_str));
678 		pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
679 		zlog_debug("Querier %s issuing IGMP general query to %s on %s",
680 			   querier_str, dst_str, igmp->interface->name);
681 	}
682 
683 	igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, igmp->fd,
684 			igmp->interface->name, query_buf, sizeof(query_buf),
685 			0 /* num_sources */, dst_addr, group_addr,
686 			pim_ifp->igmp_query_max_response_time_dsec,
687 			1 /* s_flag: always set for general queries */,
688 			igmp->querier_robustness_variable,
689 			igmp->querier_query_interval);
690 
691 	pim_igmp_general_query_on(igmp);
692 
693 	return 0;
694 }
695 
sock_close(struct igmp_sock * igmp)696 static void sock_close(struct igmp_sock *igmp)
697 {
698 	pim_igmp_other_querier_timer_off(igmp);
699 	pim_igmp_general_query_off(igmp);
700 
701 	if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
702 		if (igmp->t_igmp_read) {
703 			zlog_debug(
704 				"Cancelling READ event on IGMP socket %s fd=%d on interface %s",
705 				inet_ntoa(igmp->ifaddr), igmp->fd,
706 				igmp->interface->name);
707 		}
708 	}
709 	THREAD_OFF(igmp->t_igmp_read);
710 
711 	if (close(igmp->fd)) {
712 		flog_err(
713 			EC_LIB_SOCKET,
714 			"Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
715 			inet_ntoa(igmp->ifaddr), igmp->fd,
716 			igmp->interface->name, errno, safe_strerror(errno));
717 	}
718 
719 	if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
720 		zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
721 			   inet_ntoa(igmp->ifaddr), igmp->fd,
722 			   igmp->interface->name);
723 	}
724 }
725 
igmp_startup_mode_on(struct igmp_sock * igmp)726 void igmp_startup_mode_on(struct igmp_sock *igmp)
727 {
728 	struct pim_interface *pim_ifp;
729 
730 	pim_ifp = igmp->interface->info;
731 
732 	/*
733 	  RFC 3376: 8.7. Startup Query Count
734 
735 	  The Startup Query Count is the number of Queries sent out on
736 	  startup, separated by the Startup Query Interval.  Default: the
737 	  Robustness Variable.
738 	*/
739 	igmp->startup_query_count = igmp->querier_robustness_variable;
740 
741 	/*
742 	  Since we're (re)starting, reset QQI to default Query Interval
743 	*/
744 	igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
745 }
746 
igmp_group_free(struct igmp_group * group)747 static void igmp_group_free(struct igmp_group *group)
748 {
749 	list_delete(&group->group_source_list);
750 
751 	XFREE(MTYPE_PIM_IGMP_GROUP, group);
752 }
753 
igmp_group_count_incr(struct igmp_sock * igmp)754 static void igmp_group_count_incr(struct igmp_sock *igmp)
755 {
756 	struct pim_interface *pim_ifp = igmp->interface->info;
757 
758 	if (!pim_ifp)
759 		return;
760 
761 	++pim_ifp->pim->igmp_group_count;
762 	if (pim_ifp->pim->igmp_group_count
763 	    == pim_ifp->pim->igmp_watermark_limit) {
764 		zlog_warn(
765 			"IGMP group count reached watermark limit: %u(vrf: %s)",
766 			pim_ifp->pim->igmp_group_count,
767 			VRF_LOGNAME(pim_ifp->pim->vrf));
768 	}
769 }
770 
igmp_group_count_decr(struct igmp_sock * igmp)771 static void igmp_group_count_decr(struct igmp_sock *igmp)
772 {
773 	struct pim_interface *pim_ifp = igmp->interface->info;
774 
775 	if (!pim_ifp)
776 		return;
777 
778 	if (pim_ifp->pim->igmp_group_count == 0) {
779 		zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
780 			  VRF_LOGNAME(pim_ifp->pim->vrf));
781 		return;
782 	}
783 
784 	--pim_ifp->pim->igmp_group_count;
785 }
786 
igmp_group_delete(struct igmp_group * group)787 void igmp_group_delete(struct igmp_group *group)
788 {
789 	struct listnode *src_node;
790 	struct listnode *src_nextnode;
791 	struct igmp_source *src;
792 
793 	if (PIM_DEBUG_IGMP_TRACE) {
794 		char group_str[INET_ADDRSTRLEN];
795 		pim_inet4_dump("<group?>", group->group_addr, group_str,
796 			       sizeof(group_str));
797 		zlog_debug("Deleting IGMP group %s from socket %d interface %s",
798 			   group_str, group->group_igmp_sock->fd,
799 			   group->group_igmp_sock->interface->name);
800 	}
801 
802 	for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
803 			       src)) {
804 		igmp_source_delete(src);
805 	}
806 
807 	if (group->t_group_query_retransmit_timer) {
808 		THREAD_OFF(group->t_group_query_retransmit_timer);
809 	}
810 
811 	group_timer_off(group);
812 	igmp_group_count_decr(group->group_igmp_sock);
813 	listnode_delete(group->group_igmp_sock->igmp_group_list, group);
814 	hash_release(group->group_igmp_sock->igmp_group_hash, group);
815 
816 	igmp_group_free(group);
817 }
818 
igmp_group_delete_empty_include(struct igmp_group * group)819 void igmp_group_delete_empty_include(struct igmp_group *group)
820 {
821 	zassert(!group->group_filtermode_isexcl);
822 	zassert(!listcount(group->group_source_list));
823 
824 	igmp_group_delete(group);
825 }
826 
igmp_sock_free(struct igmp_sock * igmp)827 void igmp_sock_free(struct igmp_sock *igmp)
828 {
829 	zassert(!igmp->t_igmp_read);
830 	zassert(!igmp->t_igmp_query_timer);
831 	zassert(!igmp->t_other_querier_timer);
832 	zassert(igmp->igmp_group_list);
833 	zassert(!listcount(igmp->igmp_group_list));
834 
835 	list_delete(&igmp->igmp_group_list);
836 	hash_free(igmp->igmp_group_hash);
837 
838 	XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
839 }
840 
igmp_sock_delete(struct igmp_sock * igmp)841 void igmp_sock_delete(struct igmp_sock *igmp)
842 {
843 	struct pim_interface *pim_ifp;
844 	struct listnode *grp_node;
845 	struct listnode *grp_nextnode;
846 	struct igmp_group *grp;
847 
848 	for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode,
849 			       grp)) {
850 		igmp_group_delete(grp);
851 	}
852 
853 	sock_close(igmp);
854 
855 	pim_ifp = igmp->interface->info;
856 
857 	listnode_delete(pim_ifp->igmp_socket_list, igmp);
858 
859 	igmp_sock_free(igmp);
860 }
861 
igmp_sock_delete_all(struct interface * ifp)862 void igmp_sock_delete_all(struct interface *ifp)
863 {
864 	struct pim_interface *pim_ifp;
865 	struct listnode *igmp_node, *igmp_nextnode;
866 	struct igmp_sock *igmp;
867 
868 	pim_ifp = ifp->info;
869 
870 	for (ALL_LIST_ELEMENTS(pim_ifp->igmp_socket_list, igmp_node,
871 			       igmp_nextnode, igmp)) {
872 		igmp_sock_delete(igmp);
873 	}
874 }
875 
igmp_group_hash_key(const void * arg)876 static unsigned int igmp_group_hash_key(const void *arg)
877 {
878 	const struct igmp_group *group = arg;
879 
880 	return jhash_1word(group->group_addr.s_addr, 0);
881 }
882 
igmp_group_hash_equal(const void * arg1,const void * arg2)883 static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
884 {
885 	const struct igmp_group *g1 = (const struct igmp_group *)arg1;
886 	const struct igmp_group *g2 = (const struct igmp_group *)arg2;
887 
888 	if (g1->group_addr.s_addr == g2->group_addr.s_addr)
889 		return true;
890 
891 	return false;
892 }
893 
igmp_sock_new(int fd,struct in_addr ifaddr,struct interface * ifp,int mtrace_only)894 static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
895 				       struct interface *ifp, int mtrace_only)
896 {
897 	struct pim_interface *pim_ifp;
898 	struct igmp_sock *igmp;
899 	char hash_name[64];
900 
901 	pim_ifp = ifp->info;
902 
903 	if (PIM_DEBUG_IGMP_TRACE) {
904 		zlog_debug(
905 			"Creating IGMP socket fd=%d for address %s on interface %s",
906 			fd, inet_ntoa(ifaddr), ifp->name);
907 	}
908 
909 	igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
910 
911 	igmp->igmp_group_list = list_new();
912 	igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
913 
914 	snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
915 	igmp->igmp_group_hash = hash_create(igmp_group_hash_key,
916 					    igmp_group_hash_equal, hash_name);
917 
918 	igmp->fd = fd;
919 	igmp->interface = ifp;
920 	igmp->ifaddr = ifaddr;
921 	igmp->t_igmp_read = NULL;
922 	igmp->t_igmp_query_timer = NULL;
923 	igmp->t_other_querier_timer = NULL; /* no other querier present */
924 	igmp->querier_robustness_variable =
925 		pim_ifp->igmp_default_robustness_variable;
926 	igmp->sock_creation = pim_time_monotonic_sec();
927 
928 	igmp_stats_init(&igmp->rx_stats);
929 
930 	if (mtrace_only) {
931 		igmp->mtrace_only = mtrace_only;
932 		return igmp;
933 	}
934 
935 	igmp->mtrace_only = false;
936 
937 	/*
938 	  igmp_startup_mode_on() will reset QQI:
939 
940 	  igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
941 	*/
942 	igmp_startup_mode_on(igmp);
943 	pim_igmp_general_query_on(igmp);
944 
945 	return igmp;
946 }
947 
948 static void igmp_read_on(struct igmp_sock *igmp);
949 
pim_igmp_read(struct thread * t)950 static int pim_igmp_read(struct thread *t)
951 {
952 	uint8_t buf[10000];
953 	struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t);
954 	struct sockaddr_in from;
955 	struct sockaddr_in to;
956 	socklen_t fromlen = sizeof(from);
957 	socklen_t tolen = sizeof(to);
958 	ifindex_t ifindex = -1;
959 	int len;
960 
961 	while (1) {
962 		len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
963 					    &fromlen, &to, &tolen, &ifindex);
964 		if (len < 0) {
965 			if (errno == EINTR)
966 				continue;
967 			if (errno == EWOULDBLOCK || errno == EAGAIN)
968 				break;
969 
970 			goto done;
971 		}
972 	}
973 
974 done:
975 	igmp_read_on(igmp);
976 	return 0;
977 }
978 
igmp_read_on(struct igmp_sock * igmp)979 static void igmp_read_on(struct igmp_sock *igmp)
980 {
981 
982 	if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
983 		zlog_debug("Scheduling READ event on IGMP socket fd=%d",
984 			   igmp->fd);
985 	}
986 	igmp->t_igmp_read = NULL;
987 	thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
988 			&igmp->t_igmp_read);
989 }
990 
pim_igmp_sock_add(struct list * igmp_sock_list,struct in_addr ifaddr,struct interface * ifp,bool mtrace_only)991 struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
992 				    struct in_addr ifaddr,
993 				    struct interface *ifp,
994 				    bool mtrace_only)
995 {
996 	struct pim_interface *pim_ifp;
997 	struct igmp_sock *igmp;
998 	struct sockaddr_in sin;
999 	int fd;
1000 
1001 	pim_ifp = ifp->info;
1002 
1003 	fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
1004 	if (fd < 0) {
1005 		zlog_warn("Could not open IGMP socket for %s on %s",
1006 			  inet_ntoa(ifaddr), ifp->name);
1007 		return NULL;
1008 	}
1009 
1010 	sin.sin_family = AF_INET;
1011 	sin.sin_addr = ifaddr;
1012 	sin.sin_port = 0;
1013 	if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
1014 		zlog_warn("Could not bind IGMP socket for %s on %s",
1015 			  inet_ntoa(ifaddr), ifp->name);
1016 		close(fd);
1017 
1018 		return NULL;
1019 	}
1020 
1021 	igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
1022 
1023 	igmp_read_on(igmp);
1024 
1025 	listnode_add(igmp_sock_list, igmp);
1026 
1027 #ifdef IGMP_SOCK_DUMP
1028 	igmp_sock_dump(igmp_sock_array);
1029 #endif
1030 
1031 	return igmp;
1032 }
1033 
1034 /*
1035   RFC 3376: 6.5. Switching Router Filter-Modes
1036 
1037   When a router's filter-mode for a group is EXCLUDE and the group
1038   timer expires, the router filter-mode for the group transitions to
1039   INCLUDE.
1040 
1041   A router uses source records with running source timers as its state
1042   for the switch to a filter-mode of INCLUDE.  If there are any source
1043   records with source timers greater than zero (i.e., requested to be
1044   forwarded), a router switches to filter-mode of INCLUDE using those
1045   source records.  Source records whose timers are zero (from the
1046   previous EXCLUDE mode) are deleted.
1047  */
igmp_group_timer(struct thread * t)1048 static int igmp_group_timer(struct thread *t)
1049 {
1050 	struct igmp_group *group;
1051 
1052 	group = THREAD_ARG(t);
1053 
1054 	if (PIM_DEBUG_IGMP_TRACE) {
1055 		char group_str[INET_ADDRSTRLEN];
1056 		pim_inet4_dump("<group?>", group->group_addr, group_str,
1057 			       sizeof(group_str));
1058 		zlog_debug("%s: Timer for group %s on interface %s", __func__,
1059 			   group_str, group->group_igmp_sock->interface->name);
1060 	}
1061 
1062 	zassert(group->group_filtermode_isexcl);
1063 
1064 	group->group_filtermode_isexcl = 0;
1065 
1066 	/* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1067 	igmp_anysource_forward_stop(group);
1068 
1069 	igmp_source_delete_expired(group->group_source_list);
1070 
1071 	zassert(!group->group_filtermode_isexcl);
1072 
1073 	/*
1074 	  RFC 3376: 6.2.2. Definition of Group Timers
1075 
1076 	  If there are no more source records for the group, delete group
1077 	  record.
1078 	*/
1079 	if (listcount(group->group_source_list) < 1) {
1080 		igmp_group_delete_empty_include(group);
1081 	}
1082 
1083 	return 0;
1084 }
1085 
group_timer_off(struct igmp_group * group)1086 static void group_timer_off(struct igmp_group *group)
1087 {
1088 	if (!group->t_group_timer)
1089 		return;
1090 
1091 	if (PIM_DEBUG_IGMP_TRACE) {
1092 		char group_str[INET_ADDRSTRLEN];
1093 		pim_inet4_dump("<group?>", group->group_addr, group_str,
1094 			       sizeof(group_str));
1095 		zlog_debug("Cancelling TIMER event for group %s on %s",
1096 			   group_str, group->group_igmp_sock->interface->name);
1097 	}
1098 	THREAD_OFF(group->t_group_timer);
1099 }
1100 
igmp_group_timer_on(struct igmp_group * group,long interval_msec,const char * ifname)1101 void igmp_group_timer_on(struct igmp_group *group, long interval_msec,
1102 			 const char *ifname)
1103 {
1104 	group_timer_off(group);
1105 
1106 	if (PIM_DEBUG_IGMP_EVENTS) {
1107 		char group_str[INET_ADDRSTRLEN];
1108 		pim_inet4_dump("<group?>", group->group_addr, group_str,
1109 			       sizeof(group_str));
1110 		zlog_debug(
1111 			"Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1112 			interval_msec / 1000, interval_msec % 1000, group_str,
1113 			ifname);
1114 	}
1115 
1116 	/*
1117 	  RFC 3376: 6.2.2. Definition of Group Timers
1118 
1119 	  The group timer is only used when a group is in EXCLUDE mode and
1120 	  it represents the time for the *filter-mode* of the group to
1121 	  expire and switch to INCLUDE mode.
1122 	*/
1123 	zassert(group->group_filtermode_isexcl);
1124 
1125 	thread_add_timer_msec(router->master, igmp_group_timer, group,
1126 			      interval_msec, &group->t_group_timer);
1127 }
1128 
find_group_by_addr(struct igmp_sock * igmp,struct in_addr group_addr)1129 struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
1130 				      struct in_addr group_addr)
1131 {
1132 	struct igmp_group lookup;
1133 
1134 	lookup.group_addr.s_addr = group_addr.s_addr;
1135 
1136 	return hash_lookup(igmp->igmp_group_hash, &lookup);
1137 }
1138 
igmp_add_group_by_addr(struct igmp_sock * igmp,struct in_addr group_addr)1139 struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
1140 					  struct in_addr group_addr)
1141 {
1142 	struct igmp_group *group;
1143 
1144 	group = find_group_by_addr(igmp, group_addr);
1145 	if (group) {
1146 		return group;
1147 	}
1148 
1149 	if (!pim_is_group_224_4(group_addr)) {
1150 		zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1151 			  __func__);
1152 		return NULL;
1153 	}
1154 
1155 	if (pim_is_group_224_0_0_0_24(group_addr)) {
1156 		if (PIM_DEBUG_IGMP_TRACE)
1157 			zlog_debug(
1158 				"%s: Group specified %s is part of 224.0.0.0/24",
1159 				__func__, inet_ntoa(group_addr));
1160 		return NULL;
1161 	}
1162 	/*
1163 	  Non-existant group is created as INCLUDE {empty}:
1164 
1165 	  RFC 3376 - 5.1. Action on Change of Interface State
1166 
1167 	  If no interface state existed for that multicast address before
1168 	  the change (i.e., the change consisted of creating a new
1169 	  per-interface record), or if no state exists after the change
1170 	  (i.e., the change consisted of deleting a per-interface record),
1171 	  then the "non-existent" state is considered to have a filter mode
1172 	  of INCLUDE and an empty source list.
1173 	*/
1174 
1175 	group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1176 
1177 	group->group_source_list = list_new();
1178 	group->group_source_list->del = (void (*)(void *))igmp_source_free;
1179 
1180 	group->t_group_timer = NULL;
1181 	group->t_group_query_retransmit_timer = NULL;
1182 	group->group_specific_query_retransmit_count = 0;
1183 	group->group_addr = group_addr;
1184 	group->group_igmp_sock = igmp;
1185 	group->last_igmp_v1_report_dsec = -1;
1186 	group->last_igmp_v2_report_dsec = -1;
1187 	group->group_creation = pim_time_monotonic_sec();
1188 	group->igmp_version = IGMP_DEFAULT_VERSION;
1189 
1190 	/* initialize new group as INCLUDE {empty} */
1191 	group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1192 
1193 	listnode_add(igmp->igmp_group_list, group);
1194 	group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern);
1195 
1196 	if (PIM_DEBUG_IGMP_TRACE) {
1197 		char group_str[INET_ADDRSTRLEN];
1198 		pim_inet4_dump("<group?>", group->group_addr, group_str,
1199 			       sizeof(group_str));
1200 		zlog_debug(
1201 			"Creating new IGMP group %s on socket %d interface %s",
1202 			group_str, igmp->fd, igmp->interface->name);
1203 	}
1204 
1205 	igmp_group_count_incr(igmp);
1206 
1207 	/*
1208 	  RFC 3376: 6.2.2. Definition of Group Timers
1209 
1210 	  The group timer is only used when a group is in EXCLUDE mode and
1211 	  it represents the time for the *filter-mode* of the group to
1212 	  expire and switch to INCLUDE mode.
1213 	*/
1214 	zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1215 	zassert(!group->t_group_timer);		  /* group timer == 0 */
1216 
1217 	/* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1218 	igmp_anysource_forward_stop(group);
1219 
1220 	return group;
1221 }
1222 
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)1223 void igmp_send_query(int igmp_version, struct igmp_group *group, int fd,
1224 		     const char *ifname, char *query_buf, int query_buf_size,
1225 		     int num_sources, struct in_addr dst_addr,
1226 		     struct in_addr group_addr,
1227 		     int query_max_response_time_dsec, uint8_t s_flag,
1228 		     uint8_t querier_robustness_variable,
1229 		     uint16_t querier_query_interval)
1230 {
1231 	if (igmp_version == 3) {
1232 		igmp_v3_send_query(group, fd, ifname, query_buf, query_buf_size,
1233 				   num_sources, dst_addr, group_addr,
1234 				   query_max_response_time_dsec, s_flag,
1235 				   querier_robustness_variable,
1236 				   querier_query_interval);
1237 	} else if (igmp_version == 2) {
1238 		igmp_v2_send_query(group, fd, ifname, query_buf, dst_addr,
1239 				   group_addr, query_max_response_time_dsec);
1240 	}
1241 }
1242 
igmp_send_query_on_intf(struct interface * ifp,int igmp_ver)1243 void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1244 {
1245 	struct pim_interface *pim_ifp = ifp->info;
1246 	struct listnode *sock_node = NULL;
1247 	struct igmp_sock *igmp = NULL;
1248 	struct in_addr dst_addr;
1249 	struct in_addr group_addr;
1250 	int query_buf_size;
1251 
1252 	if (!igmp_ver)
1253 		igmp_ver = 2;
1254 
1255 	if (igmp_ver == 3)
1256 		query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1257 	else
1258 		query_buf_size = IGMP_V12_MSG_SIZE;
1259 
1260 	dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1261 	group_addr.s_addr = PIM_NET_INADDR_ANY;
1262 
1263 	if (PIM_DEBUG_IGMP_TRACE)
1264 		zlog_debug("Issuing general query on request on %s",
1265 				ifp->name);
1266 
1267 	for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
1268 
1269 		char query_buf[query_buf_size];
1270 
1271 		igmp_send_query(igmp_ver, 0 /* igmp_group */, igmp->fd,
1272 				igmp->interface->name, query_buf,
1273 				sizeof(query_buf), 0 /* num_sources */,
1274 				dst_addr, group_addr,
1275 				pim_ifp->igmp_query_max_response_time_dsec,
1276 				1 /* s_flag: always set for general queries */,
1277 				igmp->querier_robustness_variable,
1278 				igmp->querier_query_interval);
1279 	}
1280 }
1281