xref: /openbsd/sys/netinet/igmp.c (revision 572e76f6)
1 /*	$OpenBSD: igmp.c,v 1.85 2024/08/20 07:46:27 mvs Exp $	*/
2 /*	$NetBSD: igmp.c,v 1.15 1996/02/13 23:41:25 christos Exp $	*/
3 
4 /*
5  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the project nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * Copyright (c) 1988 Stephen Deering.
35  * Copyright (c) 1992, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed to Berkeley by
39  * Stephen Deering of Stanford University.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  *
65  *	@(#)igmp.c	8.2 (Berkeley) 5/3/95
66  */
67 
68 /*
69  * Internet Group Management Protocol (IGMP) routines.
70  *
71  * Written by Steve Deering, Stanford, May 1988.
72  * Modified by Rosen Sharma, Stanford, Aug 1994.
73  * Modified by Bill Fenner, Xerox PARC, Feb 1995.
74  *
75  * MULTICAST Revision: 1.3
76  */
77 
78 #include <sys/param.h>
79 #include <sys/mbuf.h>
80 #include <sys/systm.h>
81 #include <sys/socket.h>
82 #include <sys/protosw.h>
83 #include <sys/sysctl.h>
84 
85 #include <net/if.h>
86 #include <net/if_var.h>
87 
88 #include <netinet/in.h>
89 #include <netinet/in_var.h>
90 #include <netinet/ip.h>
91 #include <netinet/ip_var.h>
92 #include <netinet/igmp.h>
93 #include <netinet/igmp_var.h>
94 
95 #include <sys/stdarg.h>
96 
97 #define IP_MULTICASTOPTS	0
98 
99 int	igmp_timers_are_running;	/* [a] shortcut for fast timer */
100 static LIST_HEAD(, router_info) rti_head;
101 static struct mbuf *router_alert;
102 struct cpumem *igmpcounters;
103 
104 int igmp_checktimer(struct ifnet *);
105 void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t);
106 int rti_fill(struct in_multi *);
107 struct router_info * rti_find(struct ifnet *);
108 int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
109 int igmp_sysctl_igmpstat(void *, size_t *, void *);
110 
111 void
igmp_init(void)112 igmp_init(void)
113 {
114 	struct ipoption *ra;
115 
116 	igmp_timers_are_running = 0;
117 	LIST_INIT(&rti_head);
118 
119 	igmpcounters = counters_alloc(igps_ncounters);
120 	router_alert = m_get(M_WAIT, MT_DATA);
121 
122 	/*
123 	 * Construct a Router Alert option (RAO) to use in report
124 	 * messages as required by RFC2236.  This option has the
125 	 * following format:
126 	 *
127 	 *	| 10010100 | 00000100 |  2 octet value  |
128 	 *
129 	 * where a value of "0" indicates that routers shall examine
130 	 * the packet.
131 	 */
132 	ra = mtod(router_alert, struct ipoption *);
133 	ra->ipopt_dst.s_addr = INADDR_ANY;
134 	ra->ipopt_list[0] = IPOPT_RA;
135 	ra->ipopt_list[1] = 0x04;
136 	ra->ipopt_list[2] = 0x00;
137 	ra->ipopt_list[3] = 0x00;
138 	router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
139 }
140 
141 int
rti_fill(struct in_multi * inm)142 rti_fill(struct in_multi *inm)
143 {
144 	struct router_info *rti;
145 
146 	LIST_FOREACH(rti, &rti_head, rti_list) {
147 		if (rti->rti_ifidx == inm->inm_ifidx) {
148 			inm->inm_rti = rti;
149 			if (rti->rti_type == IGMP_v1_ROUTER)
150 				return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
151 			else
152 				return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
153 		}
154 	}
155 
156 	rti = malloc(sizeof(*rti), M_MRTABLE, M_WAITOK);
157 	rti->rti_ifidx = inm->inm_ifidx;
158 	rti->rti_type = IGMP_v2_ROUTER;
159 	LIST_INSERT_HEAD(&rti_head, rti, rti_list);
160 	inm->inm_rti = rti;
161 	return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
162 }
163 
164 struct router_info *
rti_find(struct ifnet * ifp)165 rti_find(struct ifnet *ifp)
166 {
167 	struct router_info *rti;
168 
169 	KERNEL_ASSERT_LOCKED();
170 	LIST_FOREACH(rti, &rti_head, rti_list) {
171 		if (rti->rti_ifidx == ifp->if_index)
172 			return (rti);
173 	}
174 
175 	rti = malloc(sizeof(*rti), M_MRTABLE, M_NOWAIT);
176 	if (rti == NULL)
177 		return (NULL);
178 	rti->rti_ifidx = ifp->if_index;
179 	rti->rti_type = IGMP_v2_ROUTER;
180 	LIST_INSERT_HEAD(&rti_head, rti, rti_list);
181 	return (rti);
182 }
183 
184 void
rti_delete(struct ifnet * ifp)185 rti_delete(struct ifnet *ifp)
186 {
187 	struct router_info *rti, *trti;
188 
189 	LIST_FOREACH_SAFE(rti, &rti_head, rti_list, trti) {
190 		if (rti->rti_ifidx == ifp->if_index) {
191 			LIST_REMOVE(rti, rti_list);
192 			free(rti, M_MRTABLE, sizeof(*rti));
193 			break;
194 		}
195 	}
196 }
197 
198 int
igmp_input(struct mbuf ** mp,int * offp,int proto,int af)199 igmp_input(struct mbuf **mp, int *offp, int proto, int af)
200 {
201 	struct ifnet *ifp;
202 
203 	igmpstat_inc(igps_rcv_total);
204 
205 	ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
206 	if (ifp == NULL) {
207 		m_freemp(mp);
208 		return IPPROTO_DONE;
209 	}
210 
211 	KERNEL_LOCK();
212 	proto = igmp_input_if(ifp, mp, offp, proto, af);
213 	KERNEL_UNLOCK();
214 	if_put(ifp);
215 	return proto;
216 }
217 
218 int
igmp_input_if(struct ifnet * ifp,struct mbuf ** mp,int * offp,int proto,int af)219 igmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
220 {
221 	struct mbuf *m = *mp;
222 	int iphlen = *offp;
223 	struct ip *ip = mtod(m, struct ip *);
224 	struct igmp *igmp;
225 	int igmplen;
226 	int minlen;
227 	struct ifmaddr *ifma;
228 	struct in_multi *inm;
229 	struct router_info *rti;
230 	struct in_ifaddr *ia;
231 	int timer, running = 0;
232 
233 	igmplen = ntohs(ip->ip_len) - iphlen;
234 
235 	/*
236 	 * Validate lengths
237 	 */
238 	if (igmplen < IGMP_MINLEN) {
239 		igmpstat_inc(igps_rcv_tooshort);
240 		m_freem(m);
241 		return IPPROTO_DONE;
242 	}
243 	minlen = iphlen + IGMP_MINLEN;
244 	if ((m->m_flags & M_EXT || m->m_len < minlen) &&
245 	    (m = *mp = m_pullup(m, minlen)) == NULL) {
246 		igmpstat_inc(igps_rcv_tooshort);
247 		return IPPROTO_DONE;
248 	}
249 
250 	/*
251 	 * Validate checksum
252 	 */
253 	m->m_data += iphlen;
254 	m->m_len -= iphlen;
255 	igmp = mtod(m, struct igmp *);
256 	if (in_cksum(m, igmplen)) {
257 		igmpstat_inc(igps_rcv_badsum);
258 		m_freem(m);
259 		return IPPROTO_DONE;
260 	}
261 	m->m_data -= iphlen;
262 	m->m_len += iphlen;
263 	ip = mtod(m, struct ip *);
264 
265 	switch (igmp->igmp_type) {
266 
267 	case IGMP_HOST_MEMBERSHIP_QUERY:
268 		igmpstat_inc(igps_rcv_queries);
269 
270 		if (ifp->if_flags & IFF_LOOPBACK)
271 			break;
272 
273 		if (igmp->igmp_code == 0) {
274 			rti = rti_find(ifp);
275 			if (rti == NULL) {
276 				m_freem(m);
277 				return IPPROTO_DONE;
278 			}
279 			rti->rti_type = IGMP_v1_ROUTER;
280 			rti->rti_age = 0;
281 
282 			if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
283 				igmpstat_inc(igps_rcv_badqueries);
284 				m_freem(m);
285 				return IPPROTO_DONE;
286 			}
287 
288 			/*
289 			 * Start the timers in all of our membership records
290 			 * for the interface on which the query arrived,
291 			 * except those that are already running and those
292 			 * that belong to a "local" group (224.0.0.X).
293 			 */
294 			TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
295 				if (ifma->ifma_addr->sa_family != AF_INET)
296 					continue;
297 				inm = ifmatoinm(ifma);
298 				if (inm->inm_timer == 0 &&
299 				    !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
300 					inm->inm_state = IGMP_DELAYING_MEMBER;
301 					inm->inm_timer = IGMP_RANDOM_DELAY(
302 					    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
303 					running = 1;
304 				}
305 			}
306 		} else {
307 			if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
308 				igmpstat_inc(igps_rcv_badqueries);
309 				m_freem(m);
310 				return IPPROTO_DONE;
311 			}
312 
313 			timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
314 			if (timer == 0)
315 				timer = 1;
316 
317 			/*
318 			 * Start the timers in all of our membership records
319 			 * for the interface on which the query arrived,
320 			 * except those that are already running and those
321 			 * that belong to a "local" group (224.0.0.X).  For
322 			 * timers already running, check if they need to be
323 			 * reset.
324 			 */
325 			TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
326 				if (ifma->ifma_addr->sa_family != AF_INET)
327 					continue;
328 				inm = ifmatoinm(ifma);
329 				if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
330 				    (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
331 				     ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
332 					switch (inm->inm_state) {
333 					case IGMP_DELAYING_MEMBER:
334 						if (inm->inm_timer <= timer)
335 							break;
336 						/* FALLTHROUGH */
337 					case IGMP_IDLE_MEMBER:
338 					case IGMP_LAZY_MEMBER:
339 					case IGMP_AWAKENING_MEMBER:
340 						inm->inm_state =
341 						    IGMP_DELAYING_MEMBER;
342 						inm->inm_timer =
343 						    IGMP_RANDOM_DELAY(timer);
344 						running = 1;
345 						break;
346 					case IGMP_SLEEPING_MEMBER:
347 						inm->inm_state =
348 						    IGMP_AWAKENING_MEMBER;
349 						break;
350 					}
351 				}
352 			}
353 		}
354 
355 		break;
356 
357 	case IGMP_v1_HOST_MEMBERSHIP_REPORT:
358 		igmpstat_inc(igps_rcv_reports);
359 
360 		if (ifp->if_flags & IFF_LOOPBACK)
361 			break;
362 
363 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
364 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
365 			igmpstat_inc(igps_rcv_badreports);
366 			m_freem(m);
367 			return IPPROTO_DONE;
368 		}
369 
370 		/*
371 		 * KLUDGE: if the IP source address of the report has an
372 		 * unspecified (i.e., zero) subnet number, as is allowed for
373 		 * a booting host, replace it with the correct subnet number
374 		 * so that a process-level multicast routing daemon can
375 		 * determine which subnet it arrived from.  This is necessary
376 		 * to compensate for the lack of any way for a process to
377 		 * determine the arrival interface of an incoming packet.
378 		 */
379 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
380 			IFP_TO_IA(ifp, ia);
381 			if (ia)
382 				ip->ip_src.s_addr = ia->ia_net;
383 		}
384 
385 		/*
386 		 * If we belong to the group being reported, stop
387 		 * our timer for that group.
388 		 */
389 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
390 		if (inm != NULL) {
391 			inm->inm_timer = 0;
392 			igmpstat_inc(igps_rcv_ourreports);
393 
394 			switch (inm->inm_state) {
395 			case IGMP_IDLE_MEMBER:
396 			case IGMP_LAZY_MEMBER:
397 			case IGMP_AWAKENING_MEMBER:
398 			case IGMP_SLEEPING_MEMBER:
399 				inm->inm_state = IGMP_SLEEPING_MEMBER;
400 				break;
401 			case IGMP_DELAYING_MEMBER:
402 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
403 					inm->inm_state = IGMP_LAZY_MEMBER;
404 				else
405 					inm->inm_state = IGMP_SLEEPING_MEMBER;
406 				break;
407 			}
408 		}
409 
410 		break;
411 
412 	case IGMP_v2_HOST_MEMBERSHIP_REPORT:
413 #ifdef MROUTING
414 		/*
415 		 * Make sure we don't hear our own membership report.  Fast
416 		 * leave requires knowing that we are the only member of a
417 		 * group.
418 		 */
419 		IFP_TO_IA(ifp, ia);
420 		if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
421 			break;
422 #endif
423 
424 		igmpstat_inc(igps_rcv_reports);
425 
426 		if (ifp->if_flags & IFF_LOOPBACK)
427 			break;
428 
429 		if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
430 		    igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
431 			igmpstat_inc(igps_rcv_badreports);
432 			m_freem(m);
433 			return IPPROTO_DONE;
434 		}
435 
436 		/*
437 		 * KLUDGE: if the IP source address of the report has an
438 		 * unspecified (i.e., zero) subnet number, as is allowed for
439 		 * a booting host, replace it with the correct subnet number
440 		 * so that a process-level multicast routing daemon can
441 		 * determine which subnet it arrived from.  This is necessary
442 		 * to compensate for the lack of any way for a process to
443 		 * determine the arrival interface of an incoming packet.
444 		 */
445 		if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
446 #ifndef MROUTING
447 			IFP_TO_IA(ifp, ia);
448 #endif
449 			if (ia)
450 				ip->ip_src.s_addr = ia->ia_net;
451 		}
452 
453 		/*
454 		 * If we belong to the group being reported, stop
455 		 * our timer for that group.
456 		 */
457 		IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
458 		if (inm != NULL) {
459 			inm->inm_timer = 0;
460 			igmpstat_inc(igps_rcv_ourreports);
461 
462 			switch (inm->inm_state) {
463 			case IGMP_DELAYING_MEMBER:
464 			case IGMP_IDLE_MEMBER:
465 			case IGMP_AWAKENING_MEMBER:
466 				inm->inm_state = IGMP_LAZY_MEMBER;
467 				break;
468 			case IGMP_LAZY_MEMBER:
469 			case IGMP_SLEEPING_MEMBER:
470 				break;
471 			}
472 		}
473 
474 		break;
475 
476 	}
477 
478 	if (running) {
479 		membar_producer();
480 		atomic_store_int(&igmp_timers_are_running, running);
481 	}
482 
483 	/*
484 	 * Pass all valid IGMP packets up to any process(es) listening
485 	 * on a raw IGMP socket.
486 	 */
487 	return rip_input(mp, offp, proto, af);
488 }
489 
490 void
igmp_joingroup(struct in_multi * inm,struct ifnet * ifp)491 igmp_joingroup(struct in_multi *inm, struct ifnet *ifp)
492 {
493 	int i, running = 0;
494 
495 	inm->inm_state = IGMP_IDLE_MEMBER;
496 
497 	if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
498 	    (ifp->if_flags & IFF_LOOPBACK) == 0) {
499 		i = rti_fill(inm);
500 		igmp_sendpkt(ifp, inm, i, 0);
501 		inm->inm_state = IGMP_DELAYING_MEMBER;
502 		inm->inm_timer = IGMP_RANDOM_DELAY(
503 		    IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
504 		running = 1;
505 	} else
506 		inm->inm_timer = 0;
507 
508 	if (running) {
509 		membar_producer();
510 		atomic_store_int(&igmp_timers_are_running, running);
511 	}
512 }
513 
514 void
igmp_leavegroup(struct in_multi * inm,struct ifnet * ifp)515 igmp_leavegroup(struct in_multi *inm, struct ifnet *ifp)
516 {
517 	switch (inm->inm_state) {
518 	case IGMP_DELAYING_MEMBER:
519 	case IGMP_IDLE_MEMBER:
520 		if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
521 		    (ifp->if_flags & IFF_LOOPBACK) == 0)
522 			if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
523 				igmp_sendpkt(ifp, inm,
524 				    IGMP_HOST_LEAVE_MESSAGE,
525 				    INADDR_ALLROUTERS_GROUP);
526 		break;
527 	case IGMP_LAZY_MEMBER:
528 	case IGMP_AWAKENING_MEMBER:
529 	case IGMP_SLEEPING_MEMBER:
530 		break;
531 	}
532 }
533 
534 void
igmp_fasttimo(void)535 igmp_fasttimo(void)
536 {
537 	struct ifnet *ifp;
538 	int running = 0;
539 
540 	/*
541 	 * Quick check to see if any work needs to be done, in order
542 	 * to minimize the overhead of fasttimo processing.
543 	 * Variable igmp_timers_are_running is read atomically, but without
544 	 * lock intentionally.  In case it is not set due to MP races, we may
545 	 * miss to check the timers.  Then run the loop at next fast timeout.
546 	 */
547 	if (!atomic_load_int(&igmp_timers_are_running))
548 		return;
549 	membar_consumer();
550 
551 	NET_LOCK();
552 
553 	TAILQ_FOREACH(ifp, &ifnetlist, if_list) {
554 		if (igmp_checktimer(ifp))
555 			running = 1;
556 	}
557 
558 	membar_producer();
559 	atomic_store_int(&igmp_timers_are_running, running);
560 
561 	NET_UNLOCK();
562 }
563 
564 int
igmp_checktimer(struct ifnet * ifp)565 igmp_checktimer(struct ifnet *ifp)
566 {
567 	struct in_multi *inm;
568 	struct ifmaddr *ifma;
569 	int running = 0;
570 
571 	NET_ASSERT_LOCKED();
572 
573 	TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
574 		if (ifma->ifma_addr->sa_family != AF_INET)
575 			continue;
576 		inm = ifmatoinm(ifma);
577 		if (inm->inm_timer == 0) {
578 			/* do nothing */
579 		} else if (--inm->inm_timer == 0) {
580 			if (inm->inm_state == IGMP_DELAYING_MEMBER) {
581 				if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
582 					igmp_sendpkt(ifp, inm,
583 					    IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
584 				else
585 					igmp_sendpkt(ifp, inm,
586 					    IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
587 				inm->inm_state = IGMP_IDLE_MEMBER;
588 			}
589 		} else {
590 			running = 1;
591 		}
592 	}
593 
594 	return (running);
595 }
596 
597 void
igmp_slowtimo(void)598 igmp_slowtimo(void)
599 {
600 	struct router_info *rti;
601 
602 	NET_LOCK();
603 
604 	LIST_FOREACH(rti, &rti_head, rti_list) {
605 		if (rti->rti_type == IGMP_v1_ROUTER &&
606 		    ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
607 			rti->rti_type = IGMP_v2_ROUTER;
608 		}
609 	}
610 
611 	NET_UNLOCK();
612 }
613 
614 void
igmp_sendpkt(struct ifnet * ifp,struct in_multi * inm,int type,in_addr_t addr)615 igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type,
616     in_addr_t addr)
617 {
618 	struct mbuf *m;
619 	struct igmp *igmp;
620 	struct ip *ip;
621 	struct ip_moptions imo;
622 
623 	MGETHDR(m, M_DONTWAIT, MT_HEADER);
624 	if (m == NULL)
625 		return;
626 
627 	/*
628 	 * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
629 	 * is smaller than mbuf size returned by MGETHDR.
630 	 */
631 	m->m_data += max_linkhdr;
632 	m->m_len = sizeof(struct ip) + IGMP_MINLEN;
633 	m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
634 
635 	ip = mtod(m, struct ip *);
636 	ip->ip_tos = 0;
637 	ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
638 	ip->ip_off = 0;
639 	ip->ip_p = IPPROTO_IGMP;
640 	ip->ip_src.s_addr = INADDR_ANY;
641 	if (addr) {
642 		ip->ip_dst.s_addr = addr;
643 	} else {
644 		ip->ip_dst = inm->inm_addr;
645 	}
646 
647 	m->m_data += sizeof(struct ip);
648 	m->m_len -= sizeof(struct ip);
649 	igmp = mtod(m, struct igmp *);
650 	igmp->igmp_type = type;
651 	igmp->igmp_code = 0;
652 	igmp->igmp_group = inm->inm_addr;
653 	igmp->igmp_cksum = 0;
654 	igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
655 	m->m_data -= sizeof(struct ip);
656 	m->m_len += sizeof(struct ip);
657 
658 	m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
659 	imo.imo_ifidx = inm->inm_ifidx;
660 	imo.imo_ttl = 1;
661 
662 	/*
663 	 * Request loopback of the report if we are acting as a multicast
664 	 * router, so that the process-level routing daemon can hear it.
665 	 */
666 #ifdef MROUTING
667 	imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL);
668 #else
669 	imo.imo_loop = 0;
670 #endif /* MROUTING */
671 
672 	ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
673 
674 	igmpstat_inc(igps_snd_reports);
675 }
676 
677 /*
678  * Sysctl for igmp variables.
679  */
680 int
igmp_sysctl(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)681 igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
682     void *newp, size_t newlen)
683 {
684 	/* All sysctl names at this level are terminal. */
685 	if (namelen != 1)
686 		return (ENOTDIR);
687 
688 	switch (name[0]) {
689 	case IGMPCTL_STATS:
690 		return (igmp_sysctl_igmpstat(oldp, oldlenp, newp));
691 	default:
692 		return (EOPNOTSUPP);
693 	}
694 	/* NOTREACHED */
695 }
696 
697 int
igmp_sysctl_igmpstat(void * oldp,size_t * oldlenp,void * newp)698 igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp)
699 {
700 	uint64_t counters[igps_ncounters];
701 	struct igmpstat igmpstat;
702 	u_long *words = (u_long *)&igmpstat;
703 	int i;
704 
705 	CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long)));
706 	memset(&igmpstat, 0, sizeof igmpstat);
707 	counters_read(igmpcounters, counters, nitems(counters), NULL);
708 
709 	for (i = 0; i < nitems(counters); i++)
710 		words[i] = (u_long)counters[i];
711 
712 	return (sysctl_rdstruct(oldp, oldlenp, newp,
713 	    &igmpstat, sizeof(igmpstat)));
714 }
715