Line data Source code
1 : /* $OpenBSD: igmp.c,v 1.72 2017/11/20 10:35:24 mpi 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 *igmpctl_vars[IGMPCTL_MAXID] = IGMPCTL_VARS;
100 :
101 : int igmp_timers_are_running;
102 : static struct router_info *rti_head;
103 : static struct mbuf *router_alert;
104 : struct cpumem *igmpcounters;
105 :
106 : void igmp_checktimer(struct ifnet *);
107 : void igmp_sendpkt(struct ifnet *, struct in_multi *, int, in_addr_t);
108 : int rti_fill(struct in_multi *);
109 : struct router_info * rti_find(struct ifnet *);
110 : int igmp_input_if(struct ifnet *, struct mbuf **, int *, int, int);
111 : int igmp_sysctl_igmpstat(void *, size_t *, void *);
112 :
113 : void
114 0 : igmp_init(void)
115 : {
116 : struct ipoption *ra;
117 :
118 0 : igmp_timers_are_running = 0;
119 0 : rti_head = 0;
120 :
121 0 : igmpcounters = counters_alloc(igps_ncounters);
122 0 : router_alert = m_get(M_DONTWAIT, MT_DATA);
123 0 : if (router_alert == NULL) {
124 0 : printf("%s: no mbuf\n", __func__);
125 0 : return;
126 : }
127 :
128 : /*
129 : * Construct a Router Alert option (RAO) to use in report
130 : * messages as required by RFC2236. This option has the
131 : * following format:
132 : *
133 : * | 10010100 | 00000100 | 2 octet value |
134 : *
135 : * where a value of "0" indicates that routers shall examine
136 : * the packet.
137 : */
138 0 : ra = mtod(router_alert, struct ipoption *);
139 0 : ra->ipopt_dst.s_addr = INADDR_ANY;
140 0 : ra->ipopt_list[0] = IPOPT_RA;
141 0 : ra->ipopt_list[1] = 0x04;
142 0 : ra->ipopt_list[2] = 0x00;
143 0 : ra->ipopt_list[3] = 0x00;
144 0 : router_alert->m_len = sizeof(ra->ipopt_dst) + ra->ipopt_list[1];
145 0 : }
146 :
147 : /* Return -1 for error. */
148 : int
149 0 : rti_fill(struct in_multi *inm)
150 : {
151 : struct router_info *rti;
152 :
153 0 : for (rti = rti_head; rti != 0; rti = rti->rti_next) {
154 0 : if (rti->rti_ifidx == inm->inm_ifidx) {
155 0 : inm->inm_rti = rti;
156 0 : if (rti->rti_type == IGMP_v1_ROUTER)
157 0 : return (IGMP_v1_HOST_MEMBERSHIP_REPORT);
158 : else
159 0 : return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
160 : }
161 : }
162 :
163 0 : rti = (struct router_info *)malloc(sizeof(struct router_info),
164 : M_MRTABLE, M_NOWAIT);
165 0 : if (rti == NULL)
166 0 : return (-1);
167 0 : rti->rti_ifidx = inm->inm_ifidx;
168 0 : rti->rti_type = IGMP_v2_ROUTER;
169 0 : rti->rti_next = rti_head;
170 0 : rti_head = rti;
171 0 : inm->inm_rti = rti;
172 0 : return (IGMP_v2_HOST_MEMBERSHIP_REPORT);
173 0 : }
174 :
175 : struct router_info *
176 0 : rti_find(struct ifnet *ifp)
177 : {
178 : struct router_info *rti;
179 :
180 0 : KERNEL_ASSERT_LOCKED();
181 0 : for (rti = rti_head; rti != 0; rti = rti->rti_next) {
182 0 : if (rti->rti_ifidx == ifp->if_index)
183 0 : return (rti);
184 : }
185 :
186 0 : rti = (struct router_info *)malloc(sizeof(struct router_info),
187 : M_MRTABLE, M_NOWAIT);
188 0 : if (rti == NULL)
189 0 : return (NULL);
190 0 : rti->rti_ifidx = ifp->if_index;
191 0 : rti->rti_type = IGMP_v2_ROUTER;
192 0 : rti->rti_next = rti_head;
193 0 : rti_head = rti;
194 0 : return (rti);
195 0 : }
196 :
197 : void
198 0 : rti_delete(struct ifnet *ifp)
199 : {
200 : struct router_info *rti, **prti = &rti_head;
201 :
202 0 : for (rti = rti_head; rti != 0; rti = rti->rti_next) {
203 0 : if (rti->rti_ifidx == ifp->if_index) {
204 0 : *prti = rti->rti_next;
205 0 : free(rti, M_MRTABLE, sizeof(*rti));
206 0 : break;
207 : }
208 : prti = &rti->rti_next;
209 : }
210 0 : }
211 :
212 : int
213 0 : igmp_input(struct mbuf **mp, int *offp, int proto, int af)
214 : {
215 : struct ifnet *ifp;
216 :
217 0 : igmpstat_inc(igps_rcv_total);
218 :
219 0 : ifp = if_get((*mp)->m_pkthdr.ph_ifidx);
220 0 : if (ifp == NULL) {
221 0 : m_freemp(mp);
222 0 : return IPPROTO_DONE;
223 : }
224 :
225 0 : KERNEL_LOCK();
226 0 : proto = igmp_input_if(ifp, mp, offp, proto, af);
227 0 : KERNEL_UNLOCK();
228 0 : if_put(ifp);
229 0 : return proto;
230 0 : }
231 :
232 : int
233 0 : igmp_input_if(struct ifnet *ifp, struct mbuf **mp, int *offp, int proto, int af)
234 : {
235 0 : struct mbuf *m = *mp;
236 0 : int iphlen = *offp;
237 0 : struct ip *ip = mtod(m, struct ip *);
238 : struct igmp *igmp;
239 : int igmplen;
240 : int minlen;
241 : struct ifmaddr *ifma;
242 : struct in_multi *inm;
243 : struct router_info *rti;
244 : struct in_ifaddr *ia;
245 : int timer;
246 :
247 0 : igmplen = ntohs(ip->ip_len) - iphlen;
248 :
249 : /*
250 : * Validate lengths
251 : */
252 0 : if (igmplen < IGMP_MINLEN) {
253 0 : igmpstat_inc(igps_rcv_tooshort);
254 0 : m_freem(m);
255 0 : return IPPROTO_DONE;
256 : }
257 0 : minlen = iphlen + IGMP_MINLEN;
258 0 : if ((m->m_flags & M_EXT || m->m_len < minlen) &&
259 0 : (m = *mp = m_pullup(m, minlen)) == NULL) {
260 0 : igmpstat_inc(igps_rcv_tooshort);
261 0 : return IPPROTO_DONE;
262 : }
263 :
264 : /*
265 : * Validate checksum
266 : */
267 0 : m->m_data += iphlen;
268 0 : m->m_len -= iphlen;
269 0 : igmp = mtod(m, struct igmp *);
270 0 : if (in_cksum(m, igmplen)) {
271 0 : igmpstat_inc(igps_rcv_badsum);
272 0 : m_freem(m);
273 0 : return IPPROTO_DONE;
274 : }
275 0 : m->m_data -= iphlen;
276 0 : m->m_len += iphlen;
277 0 : ip = mtod(m, struct ip *);
278 :
279 0 : switch (igmp->igmp_type) {
280 :
281 : case IGMP_HOST_MEMBERSHIP_QUERY:
282 0 : igmpstat_inc(igps_rcv_queries);
283 :
284 0 : if (ifp->if_flags & IFF_LOOPBACK)
285 : break;
286 :
287 0 : if (igmp->igmp_code == 0) {
288 0 : rti = rti_find(ifp);
289 0 : if (rti == NULL) {
290 0 : m_freem(m);
291 0 : return IPPROTO_DONE;
292 : }
293 0 : rti->rti_type = IGMP_v1_ROUTER;
294 0 : rti->rti_age = 0;
295 :
296 0 : if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
297 0 : igmpstat_inc(igps_rcv_badqueries);
298 0 : m_freem(m);
299 0 : return IPPROTO_DONE;
300 : }
301 :
302 : /*
303 : * Start the timers in all of our membership records
304 : * for the interface on which the query arrived,
305 : * except those that are already running and those
306 : * that belong to a "local" group (224.0.0.X).
307 : */
308 0 : TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
309 0 : if (ifma->ifma_addr->sa_family != AF_INET)
310 : continue;
311 0 : inm = ifmatoinm(ifma);
312 0 : if (inm->inm_timer == 0 &&
313 0 : !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
314 0 : inm->inm_state = IGMP_DELAYING_MEMBER;
315 0 : inm->inm_timer = IGMP_RANDOM_DELAY(
316 : IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
317 0 : igmp_timers_are_running = 1;
318 0 : }
319 : }
320 : } else {
321 0 : if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
322 0 : igmpstat_inc(igps_rcv_badqueries);
323 0 : m_freem(m);
324 0 : return IPPROTO_DONE;
325 : }
326 :
327 0 : timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
328 0 : if (timer == 0)
329 : timer = 1;
330 :
331 : /*
332 : * Start the timers in all of our membership records
333 : * for the interface on which the query arrived,
334 : * except those that are already running and those
335 : * that belong to a "local" group (224.0.0.X). For
336 : * timers already running, check if they need to be
337 : * reset.
338 : */
339 0 : TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
340 0 : if (ifma->ifma_addr->sa_family != AF_INET)
341 : continue;
342 0 : inm = ifmatoinm(ifma);
343 0 : if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
344 0 : (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
345 0 : ip->ip_dst.s_addr == inm->inm_addr.s_addr)) {
346 0 : switch (inm->inm_state) {
347 : case IGMP_DELAYING_MEMBER:
348 0 : if (inm->inm_timer <= timer)
349 : break;
350 : /* FALLTHROUGH */
351 : case IGMP_IDLE_MEMBER:
352 : case IGMP_LAZY_MEMBER:
353 : case IGMP_AWAKENING_MEMBER:
354 0 : inm->inm_state =
355 : IGMP_DELAYING_MEMBER;
356 0 : inm->inm_timer =
357 0 : IGMP_RANDOM_DELAY(timer);
358 0 : igmp_timers_are_running = 1;
359 0 : break;
360 : case IGMP_SLEEPING_MEMBER:
361 0 : inm->inm_state =
362 : IGMP_AWAKENING_MEMBER;
363 0 : break;
364 : }
365 : }
366 : }
367 : }
368 :
369 : break;
370 :
371 : case IGMP_v1_HOST_MEMBERSHIP_REPORT:
372 0 : igmpstat_inc(igps_rcv_reports);
373 :
374 0 : if (ifp->if_flags & IFF_LOOPBACK)
375 : break;
376 :
377 0 : if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
378 0 : igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
379 0 : igmpstat_inc(igps_rcv_badreports);
380 0 : m_freem(m);
381 0 : return IPPROTO_DONE;
382 : }
383 :
384 : /*
385 : * KLUDGE: if the IP source address of the report has an
386 : * unspecified (i.e., zero) subnet number, as is allowed for
387 : * a booting host, replace it with the correct subnet number
388 : * so that a process-level multicast routing daemon can
389 : * determine which subnet it arrived from. This is necessary
390 : * to compensate for the lack of any way for a process to
391 : * determine the arrival interface of an incoming packet.
392 : */
393 0 : if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
394 0 : IFP_TO_IA(ifp, ia);
395 0 : if (ia)
396 0 : ip->ip_src.s_addr = ia->ia_net;
397 : }
398 :
399 : /*
400 : * If we belong to the group being reported, stop
401 : * our timer for that group.
402 : */
403 0 : IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
404 0 : if (inm != NULL) {
405 0 : inm->inm_timer = 0;
406 0 : igmpstat_inc(igps_rcv_ourreports);
407 :
408 0 : switch (inm->inm_state) {
409 : case IGMP_IDLE_MEMBER:
410 : case IGMP_LAZY_MEMBER:
411 : case IGMP_AWAKENING_MEMBER:
412 : case IGMP_SLEEPING_MEMBER:
413 0 : inm->inm_state = IGMP_SLEEPING_MEMBER;
414 0 : break;
415 : case IGMP_DELAYING_MEMBER:
416 0 : if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
417 0 : inm->inm_state = IGMP_LAZY_MEMBER;
418 : else
419 0 : inm->inm_state = IGMP_SLEEPING_MEMBER;
420 : break;
421 : }
422 : }
423 :
424 : break;
425 :
426 : case IGMP_v2_HOST_MEMBERSHIP_REPORT:
427 : #ifdef MROUTING
428 : /*
429 : * Make sure we don't hear our own membership report. Fast
430 : * leave requires knowing that we are the only member of a
431 : * group.
432 : */
433 0 : IFP_TO_IA(ifp, ia);
434 0 : if (ia && ip->ip_src.s_addr == ia->ia_addr.sin_addr.s_addr)
435 : break;
436 : #endif
437 :
438 0 : igmpstat_inc(igps_rcv_reports);
439 :
440 0 : if (ifp->if_flags & IFF_LOOPBACK)
441 : break;
442 :
443 0 : if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
444 0 : igmp->igmp_group.s_addr != ip->ip_dst.s_addr) {
445 0 : igmpstat_inc(igps_rcv_badreports);
446 0 : m_freem(m);
447 0 : return IPPROTO_DONE;
448 : }
449 :
450 : /*
451 : * KLUDGE: if the IP source address of the report has an
452 : * unspecified (i.e., zero) subnet number, as is allowed for
453 : * a booting host, replace it with the correct subnet number
454 : * so that a process-level multicast routing daemon can
455 : * determine which subnet it arrived from. This is necessary
456 : * to compensate for the lack of any way for a process to
457 : * determine the arrival interface of an incoming packet.
458 : */
459 0 : if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
460 : #ifndef MROUTING
461 : IFP_TO_IA(ifp, ia);
462 : #endif
463 : if (ia)
464 0 : ip->ip_src.s_addr = ia->ia_net;
465 : }
466 :
467 : /*
468 : * If we belong to the group being reported, stop
469 : * our timer for that group.
470 : */
471 0 : IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm);
472 0 : if (inm != NULL) {
473 0 : inm->inm_timer = 0;
474 0 : igmpstat_inc(igps_rcv_ourreports);
475 :
476 0 : switch (inm->inm_state) {
477 : case IGMP_DELAYING_MEMBER:
478 : case IGMP_IDLE_MEMBER:
479 : case IGMP_AWAKENING_MEMBER:
480 0 : inm->inm_state = IGMP_LAZY_MEMBER;
481 0 : break;
482 : case IGMP_LAZY_MEMBER:
483 : case IGMP_SLEEPING_MEMBER:
484 : break;
485 : }
486 : }
487 :
488 : break;
489 :
490 : }
491 :
492 : /*
493 : * Pass all valid IGMP packets up to any process(es) listening
494 : * on a raw IGMP socket.
495 : */
496 0 : return rip_input(mp, offp, proto, af);
497 0 : }
498 :
499 : void
500 0 : igmp_joingroup(struct in_multi *inm)
501 : {
502 : struct ifnet* ifp;
503 : int i;
504 :
505 0 : ifp = if_get(inm->inm_ifidx);
506 :
507 0 : inm->inm_state = IGMP_IDLE_MEMBER;
508 :
509 0 : if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
510 0 : ifp && (ifp->if_flags & IFF_LOOPBACK) == 0) {
511 0 : if ((i = rti_fill(inm)) == -1)
512 : goto out;
513 :
514 0 : igmp_sendpkt(ifp, inm, i, 0);
515 0 : inm->inm_state = IGMP_DELAYING_MEMBER;
516 0 : inm->inm_timer = IGMP_RANDOM_DELAY(
517 : IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
518 0 : igmp_timers_are_running = 1;
519 0 : } else
520 0 : inm->inm_timer = 0;
521 :
522 : out:
523 0 : if_put(ifp);
524 0 : }
525 :
526 : void
527 0 : igmp_leavegroup(struct in_multi *inm)
528 : {
529 : struct ifnet* ifp;
530 :
531 0 : ifp = if_get(inm->inm_ifidx);
532 :
533 0 : switch (inm->inm_state) {
534 : case IGMP_DELAYING_MEMBER:
535 : case IGMP_IDLE_MEMBER:
536 0 : if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
537 0 : ifp && (ifp->if_flags & IFF_LOOPBACK) == 0)
538 0 : if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
539 0 : igmp_sendpkt(ifp, inm,
540 : IGMP_HOST_LEAVE_MESSAGE,
541 : INADDR_ALLROUTERS_GROUP);
542 : break;
543 : case IGMP_LAZY_MEMBER:
544 : case IGMP_AWAKENING_MEMBER:
545 : case IGMP_SLEEPING_MEMBER:
546 : break;
547 : }
548 0 : if_put(ifp);
549 0 : }
550 :
551 : void
552 0 : igmp_fasttimo(void)
553 : {
554 : struct ifnet *ifp;
555 :
556 0 : NET_LOCK();
557 :
558 : /*
559 : * Quick check to see if any work needs to be done, in order
560 : * to minimize the overhead of fasttimo processing.
561 : */
562 0 : if (!igmp_timers_are_running)
563 : goto out;
564 :
565 0 : igmp_timers_are_running = 0;
566 0 : TAILQ_FOREACH(ifp, &ifnet, if_list)
567 0 : igmp_checktimer(ifp);
568 :
569 : out:
570 0 : NET_UNLOCK();
571 0 : }
572 :
573 :
574 : void
575 0 : igmp_checktimer(struct ifnet *ifp)
576 : {
577 : struct in_multi *inm;
578 : struct ifmaddr *ifma;
579 :
580 0 : NET_ASSERT_LOCKED();
581 :
582 0 : TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
583 0 : if (ifma->ifma_addr->sa_family != AF_INET)
584 : continue;
585 0 : inm = ifmatoinm(ifma);
586 0 : if (inm->inm_timer == 0) {
587 : /* do nothing */
588 0 : } else if (--inm->inm_timer == 0) {
589 0 : if (inm->inm_state == IGMP_DELAYING_MEMBER) {
590 0 : if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
591 0 : igmp_sendpkt(ifp, inm,
592 : IGMP_v1_HOST_MEMBERSHIP_REPORT, 0);
593 : else
594 0 : igmp_sendpkt(ifp, inm,
595 : IGMP_v2_HOST_MEMBERSHIP_REPORT, 0);
596 0 : inm->inm_state = IGMP_IDLE_MEMBER;
597 0 : }
598 : } else {
599 0 : igmp_timers_are_running = 1;
600 : }
601 : }
602 0 : }
603 :
604 : void
605 0 : igmp_slowtimo(void)
606 : {
607 : struct router_info *rti;
608 :
609 0 : NET_LOCK();
610 :
611 0 : for (rti = rti_head; rti != 0; rti = rti->rti_next) {
612 0 : if (rti->rti_type == IGMP_v1_ROUTER &&
613 0 : ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
614 0 : rti->rti_type = IGMP_v2_ROUTER;
615 0 : }
616 : }
617 :
618 0 : NET_UNLOCK();
619 0 : }
620 :
621 : void
622 0 : igmp_sendpkt(struct ifnet *ifp, struct in_multi *inm, int type,
623 : in_addr_t addr)
624 : {
625 : struct mbuf *m;
626 : struct igmp *igmp;
627 : struct ip *ip;
628 0 : struct ip_moptions imo;
629 :
630 0 : MGETHDR(m, M_DONTWAIT, MT_HEADER);
631 0 : if (m == NULL)
632 0 : return;
633 :
634 : /*
635 : * Assume max_linkhdr + sizeof(struct ip) + IGMP_MINLEN
636 : * is smaller than mbuf size returned by MGETHDR.
637 : */
638 0 : m->m_data += max_linkhdr;
639 0 : m->m_len = sizeof(struct ip) + IGMP_MINLEN;
640 0 : m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
641 :
642 0 : ip = mtod(m, struct ip *);
643 0 : ip->ip_tos = 0;
644 0 : ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
645 0 : ip->ip_off = 0;
646 0 : ip->ip_p = IPPROTO_IGMP;
647 0 : ip->ip_src.s_addr = INADDR_ANY;
648 0 : if (addr) {
649 0 : ip->ip_dst.s_addr = addr;
650 0 : } else {
651 0 : ip->ip_dst = inm->inm_addr;
652 : }
653 :
654 0 : m->m_data += sizeof(struct ip);
655 0 : m->m_len -= sizeof(struct ip);
656 0 : igmp = mtod(m, struct igmp *);
657 0 : igmp->igmp_type = type;
658 0 : igmp->igmp_code = 0;
659 0 : igmp->igmp_group = inm->inm_addr;
660 0 : igmp->igmp_cksum = 0;
661 0 : igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
662 0 : m->m_data -= sizeof(struct ip);
663 0 : m->m_len += sizeof(struct ip);
664 :
665 0 : m->m_pkthdr.ph_rtableid = ifp->if_rdomain;
666 0 : imo.imo_ifidx = inm->inm_ifidx;
667 0 : imo.imo_ttl = 1;
668 :
669 : /*
670 : * Request loopback of the report if we are acting as a multicast
671 : * router, so that the process-level routing daemon can hear it.
672 : */
673 : #ifdef MROUTING
674 0 : imo.imo_loop = (ip_mrouter[ifp->if_rdomain] != NULL);
675 : #else
676 : imo.imo_loop = 0;
677 : #endif /* MROUTING */
678 :
679 0 : ip_output(m, router_alert, NULL, IP_MULTICASTOPTS, &imo, NULL, 0);
680 :
681 0 : igmpstat_inc(igps_snd_reports);
682 0 : }
683 :
684 : /*
685 : * Sysctl for igmp variables.
686 : */
687 : int
688 0 : igmp_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
689 : void *newp, size_t newlen)
690 : {
691 : int error;
692 :
693 : /* All sysctl names at this level are terminal. */
694 0 : if (namelen != 1)
695 0 : return (ENOTDIR);
696 :
697 0 : switch (name[0]) {
698 : case IGMPCTL_STATS:
699 0 : if (newp != NULL)
700 0 : return (EPERM);
701 0 : return (igmp_sysctl_igmpstat(oldp, oldlenp, newp));
702 : default:
703 0 : if (name[0] < IGMPCTL_MAXID) {
704 0 : NET_LOCK();
705 0 : error = sysctl_int_arr(igmpctl_vars, name, namelen,
706 : oldp, oldlenp, newp, newlen);
707 0 : NET_UNLOCK();
708 0 : return (error);
709 : }
710 0 : return (ENOPROTOOPT);
711 : }
712 : /* NOTREACHED */
713 0 : }
714 :
715 : int
716 0 : igmp_sysctl_igmpstat(void *oldp, size_t *oldlenp, void *newp)
717 : {
718 0 : uint64_t counters[igps_ncounters];
719 0 : struct igmpstat igmpstat;
720 0 : u_long *words = (u_long *)&igmpstat;
721 : int i;
722 :
723 : CTASSERT(sizeof(igmpstat) == (nitems(counters) * sizeof(u_long)));
724 0 : memset(&igmpstat, 0, sizeof igmpstat);
725 0 : counters_read(igmpcounters, counters, nitems(counters));
726 :
727 0 : for (i = 0; i < nitems(counters); i++)
728 0 : words[i] = (u_long)counters[i];
729 :
730 0 : return (sysctl_rdstruct(oldp, oldlenp, newp,
731 : &igmpstat, sizeof(igmpstat)));
732 0 : }
|