1 /* $NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv Exp $ */
2
3 /*
4 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Internet Group Management Protocol (IGMP) routines.
34 *
35 * Written by Steve Deering, Stanford, May 1988.
36 * Modified by Rosen Sharma, Stanford, Aug 1994.
37 * Modified by Bill Fenner, Xerox PARC, Feb 1995.
38 *
39 * MULTICAST Revision: 1.3
40 */
41
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: igmp.c,v 1.70 2020/05/15 06:34:34 maxv Exp $");
44
45 #ifdef _KERNEL_OPT
46 #include "opt_mrouting.h"
47 #include "opt_net_mpsafe.h"
48 #endif
49
50 #include <sys/param.h>
51 #include <sys/mbuf.h>
52 #include <sys/socket.h>
53 #include <sys/socketvar.h>
54 #include <sys/systm.h>
55 #include <sys/cprng.h>
56 #include <sys/sysctl.h>
57
58 #include <net/if.h>
59 #include <net/net_stats.h>
60
61 #include <netinet/in.h>
62 #include <netinet/in_var.h>
63 #include <netinet/in_systm.h>
64 #include <netinet/ip.h>
65 #include <netinet/ip_var.h>
66 #include <netinet/igmp.h>
67 #include <netinet/igmp_var.h>
68
69 /*
70 * Per-interface router version information.
71 */
72 typedef struct router_info {
73 LIST_ENTRY(router_info) rti_link;
74 ifnet_t * rti_ifp;
75 int rti_type; /* type of router on this interface */
76 int rti_age; /* time since last v1 query */
77 } router_info_t;
78
79 /*
80 * The router-info list and the timer flag are protected by in_multilock.
81 *
82 * Lock order:
83 *
84 * softnet_lock ->
85 * in_multilock
86 */
87 static struct pool igmp_rti_pool __cacheline_aligned;
88 static LIST_HEAD(, router_info) rti_head __cacheline_aligned;
89 static int igmp_timers_on __cacheline_aligned;
90 static percpu_t * igmpstat_percpu __read_mostly;
91
92 #define IGMP_STATINC(x) _NET_STATINC(igmpstat_percpu, x)
93
94 static void igmp_sendpkt(struct in_multi *, int);
95 static int rti_fill(struct in_multi *);
96 static router_info_t * rti_find(struct ifnet *);
97 static void rti_delete(struct ifnet *);
98 static void sysctl_net_inet_igmp_setup(struct sysctllog **);
99
100 /*
101 * rti_fill: associate router information with the given multicast group;
102 * if there is no router information for the interface, then create it.
103 */
104 static int
rti_fill(struct in_multi * inm)105 rti_fill(struct in_multi *inm)
106 {
107 router_info_t *rti;
108
109 KASSERT(in_multi_lock_held());
110
111 LIST_FOREACH(rti, &rti_head, rti_link) {
112 if (rti->rti_ifp == inm->inm_ifp) {
113 inm->inm_rti = rti;
114 return rti->rti_type == IGMP_v1_ROUTER ?
115 IGMP_v1_HOST_MEMBERSHIP_REPORT :
116 IGMP_v2_HOST_MEMBERSHIP_REPORT;
117 }
118 }
119 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
120 if (rti == NULL) {
121 return 0;
122 }
123 rti->rti_ifp = inm->inm_ifp;
124 rti->rti_type = IGMP_v2_ROUTER;
125 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
126 inm->inm_rti = rti;
127 return IGMP_v2_HOST_MEMBERSHIP_REPORT;
128 }
129
130 /*
131 * rti_find: lookup or create router information for the given interface.
132 */
133 static router_info_t *
rti_find(ifnet_t * ifp)134 rti_find(ifnet_t *ifp)
135 {
136 router_info_t *rti;
137
138 KASSERT(in_multi_lock_held());
139
140 LIST_FOREACH(rti, &rti_head, rti_link) {
141 if (rti->rti_ifp == ifp)
142 return rti;
143 }
144 rti = pool_get(&igmp_rti_pool, PR_NOWAIT);
145 if (rti == NULL) {
146 return NULL;
147 }
148 rti->rti_ifp = ifp;
149 rti->rti_type = IGMP_v2_ROUTER;
150 LIST_INSERT_HEAD(&rti_head, rti, rti_link);
151 return rti;
152 }
153
154 /*
155 * rti_delete: remove and free the router information entry for the
156 * given interface.
157 */
158 static void
rti_delete(ifnet_t * ifp)159 rti_delete(ifnet_t *ifp)
160 {
161 router_info_t *rti;
162
163 KASSERT(in_multi_lock_held());
164
165 LIST_FOREACH(rti, &rti_head, rti_link) {
166 if (rti->rti_ifp == ifp) {
167 LIST_REMOVE(rti, rti_link);
168 pool_put(&igmp_rti_pool, rti);
169 break;
170 }
171 }
172 }
173
174 void
igmp_init(void)175 igmp_init(void)
176 {
177 pool_init(&igmp_rti_pool, sizeof(router_info_t), 0, 0, 0,
178 "igmppl", NULL, IPL_SOFTNET);
179 igmpstat_percpu = percpu_alloc(sizeof(uint64_t) * IGMP_NSTATS);
180 sysctl_net_inet_igmp_setup(NULL);
181 LIST_INIT(&rti_head);
182 }
183
184 void
igmp_input(struct mbuf * m,int off,int proto)185 igmp_input(struct mbuf *m, int off, int proto)
186 {
187 ifnet_t *ifp;
188 struct ip *ip = mtod(m, struct ip *);
189 struct igmp *igmp;
190 u_int minlen, timer;
191 struct in_multi *inm;
192 struct in_ifaddr *ia;
193 int ip_len, iphlen;
194 struct psref psref;
195
196 iphlen = off;
197
198 IGMP_STATINC(IGMP_STAT_RCV_TOTAL);
199
200 /*
201 * Validate lengths
202 */
203 minlen = iphlen + IGMP_MINLEN;
204 ip_len = ntohs(ip->ip_len);
205 if (ip_len < minlen) {
206 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
207 m_freem(m);
208 return;
209 }
210 if (((m->m_flags & M_EXT) && (ip->ip_src.s_addr & IN_CLASSA_NET) == 0)
211 || m->m_len < minlen) {
212 if ((m = m_pullup(m, minlen)) == NULL) {
213 IGMP_STATINC(IGMP_STAT_RCV_TOOSHORT);
214 return;
215 }
216 ip = mtod(m, struct ip *);
217 }
218
219 /*
220 * Validate checksum
221 */
222 m->m_data += iphlen;
223 m->m_len -= iphlen;
224 igmp = mtod(m, struct igmp *);
225 /* No need to assert alignment here. */
226 if (in_cksum(m, ip_len - iphlen)) {
227 IGMP_STATINC(IGMP_STAT_RCV_BADSUM);
228 m_freem(m);
229 return;
230 }
231 m->m_data -= iphlen;
232 m->m_len += iphlen;
233
234 ifp = m_get_rcvif_psref(m, &psref);
235 if (__predict_false(ifp == NULL))
236 goto drop;
237
238 switch (igmp->igmp_type) {
239
240 case IGMP_HOST_MEMBERSHIP_QUERY:
241 IGMP_STATINC(IGMP_STAT_RCV_QUERIES);
242
243 if (ifp->if_flags & IFF_LOOPBACK)
244 break;
245
246 if (igmp->igmp_code == 0) {
247 struct in_multistep step;
248 router_info_t *rti;
249
250 if (ip->ip_dst.s_addr != INADDR_ALLHOSTS_GROUP) {
251 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
252 goto drop;
253 }
254
255 in_multi_lock(RW_WRITER);
256 rti = rti_find(ifp);
257 if (rti == NULL) {
258 in_multi_unlock();
259 break;
260 }
261 rti->rti_type = IGMP_v1_ROUTER;
262 rti->rti_age = 0;
263
264 /*
265 * Start the timers in all of our membership records
266 * for the interface on which the query arrived,
267 * except those that are already running and those
268 * that belong to a "local" group (224.0.0.X).
269 */
270
271 inm = in_first_multi(&step);
272 while (inm != NULL) {
273 if (inm->inm_ifp == ifp &&
274 inm->inm_timer == 0 &&
275 !IN_LOCAL_GROUP(inm->inm_addr.s_addr)) {
276 inm->inm_state = IGMP_DELAYING_MEMBER;
277 inm->inm_timer = IGMP_RANDOM_DELAY(
278 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
279 igmp_timers_on = true;
280 }
281 inm = in_next_multi(&step);
282 }
283 in_multi_unlock();
284 } else {
285 struct in_multistep step;
286
287 if (!IN_MULTICAST(ip->ip_dst.s_addr)) {
288 IGMP_STATINC(IGMP_STAT_RCV_BADQUERIES);
289 goto drop;
290 }
291
292 timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE;
293 if (timer == 0)
294 timer = 1;
295
296 /*
297 * Start the timers in all of our membership records
298 * for the interface on which the query arrived,
299 * except those that are already running and those
300 * that belong to a "local" group (224.0.0.X). For
301 * timers already running, check if they need to be
302 * reset.
303 */
304 in_multi_lock(RW_WRITER);
305 inm = in_first_multi(&step);
306 while (inm != NULL) {
307 if (inm->inm_ifp == ifp &&
308 !IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
309 (ip->ip_dst.s_addr == INADDR_ALLHOSTS_GROUP ||
310 in_hosteq(ip->ip_dst, inm->inm_addr))) {
311 switch (inm->inm_state) {
312 case IGMP_DELAYING_MEMBER:
313 if (inm->inm_timer <= timer)
314 break;
315 /* FALLTHROUGH */
316 case IGMP_IDLE_MEMBER:
317 case IGMP_LAZY_MEMBER:
318 case IGMP_AWAKENING_MEMBER:
319 inm->inm_state =
320 IGMP_DELAYING_MEMBER;
321 inm->inm_timer =
322 IGMP_RANDOM_DELAY(timer);
323 igmp_timers_on = true;
324 break;
325 case IGMP_SLEEPING_MEMBER:
326 inm->inm_state =
327 IGMP_AWAKENING_MEMBER;
328 break;
329 }
330 }
331 inm = in_next_multi(&step);
332 }
333 in_multi_unlock();
334 }
335
336 break;
337
338 case IGMP_v1_HOST_MEMBERSHIP_REPORT:
339 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
340
341 if (ifp->if_flags & IFF_LOOPBACK)
342 break;
343
344 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
345 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
346 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
347 goto drop;
348 }
349
350 /*
351 * KLUDGE: if the IP source address of the report has an
352 * unspecified (i.e., zero) subnet number, as is allowed for
353 * a booting host, replace it with the correct subnet number
354 * so that a process-level multicast routing daemon can
355 * determine which subnet it arrived from. This is necessary
356 * to compensate for the lack of any way for a process to
357 * determine the arrival interface of an incoming packet.
358 */
359 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
360 int s = pserialize_read_enter();
361 ia = in_get_ia_from_ifp(ifp); /* XXX */
362 if (ia)
363 ip->ip_src.s_addr = ia->ia_subnet;
364 pserialize_read_exit(s);
365 }
366
367 /*
368 * If we belong to the group being reported, stop
369 * our timer for that group.
370 */
371 in_multi_lock(RW_WRITER);
372 inm = in_lookup_multi(igmp->igmp_group, ifp);
373 if (inm != NULL) {
374 inm->inm_timer = 0;
375 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
376
377 switch (inm->inm_state) {
378 case IGMP_IDLE_MEMBER:
379 case IGMP_LAZY_MEMBER:
380 case IGMP_AWAKENING_MEMBER:
381 case IGMP_SLEEPING_MEMBER:
382 inm->inm_state = IGMP_SLEEPING_MEMBER;
383 break;
384 case IGMP_DELAYING_MEMBER:
385 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
386 inm->inm_state = IGMP_LAZY_MEMBER;
387 else
388 inm->inm_state = IGMP_SLEEPING_MEMBER;
389 break;
390 }
391 }
392 in_multi_unlock();
393 break;
394
395 case IGMP_v2_HOST_MEMBERSHIP_REPORT: {
396 int s = pserialize_read_enter();
397 #ifdef MROUTING
398 /*
399 * Make sure we don't hear our own membership report. Fast
400 * leave requires knowing that we are the only member of a
401 * group.
402 */
403 ia = in_get_ia_from_ifp(ifp); /* XXX */
404 if (ia && in_hosteq(ip->ip_src, ia->ia_addr.sin_addr)) {
405 pserialize_read_exit(s);
406 break;
407 }
408 #endif
409
410 IGMP_STATINC(IGMP_STAT_RCV_REPORTS);
411
412 if (ifp->if_flags & IFF_LOOPBACK) {
413 pserialize_read_exit(s);
414 break;
415 }
416
417 if (!IN_MULTICAST(igmp->igmp_group.s_addr) ||
418 !in_hosteq(igmp->igmp_group, ip->ip_dst)) {
419 IGMP_STATINC(IGMP_STAT_RCV_BADREPORTS);
420 pserialize_read_exit(s);
421 goto drop;
422 }
423
424 /*
425 * KLUDGE: if the IP source address of the report has an
426 * unspecified (i.e., zero) subnet number, as is allowed for
427 * a booting host, replace it with the correct subnet number
428 * so that a process-level multicast routing daemon can
429 * determine which subnet it arrived from. This is necessary
430 * to compensate for the lack of any way for a process to
431 * determine the arrival interface of an incoming packet.
432 */
433 if ((ip->ip_src.s_addr & IN_CLASSA_NET) == 0) {
434 #ifndef MROUTING
435 ia = in_get_ia_from_ifp(ifp); /* XXX */
436 #endif
437 if (ia)
438 ip->ip_src.s_addr = ia->ia_subnet;
439 }
440 pserialize_read_exit(s);
441
442 /*
443 * If we belong to the group being reported, stop
444 * our timer for that group.
445 */
446 in_multi_lock(RW_WRITER);
447 inm = in_lookup_multi(igmp->igmp_group, ifp);
448 if (inm != NULL) {
449 inm->inm_timer = 0;
450 IGMP_STATINC(IGMP_STAT_RCV_OURREPORTS);
451
452 switch (inm->inm_state) {
453 case IGMP_DELAYING_MEMBER:
454 case IGMP_IDLE_MEMBER:
455 case IGMP_AWAKENING_MEMBER:
456 inm->inm_state = IGMP_LAZY_MEMBER;
457 break;
458 case IGMP_LAZY_MEMBER:
459 case IGMP_SLEEPING_MEMBER:
460 break;
461 }
462 }
463 in_multi_unlock();
464 break;
465 }
466 }
467 m_put_rcvif_psref(ifp, &psref);
468
469 /*
470 * Pass all valid IGMP packets up to any process(es) listening
471 * on a raw IGMP socket.
472 */
473 /*
474 * Currently, igmp_input() is always called holding softnet_lock
475 * by ipintr()(!NET_MPSAFE) or PR_INPUT_WRAP()(NET_MPSAFE).
476 */
477 KASSERT(mutex_owned(softnet_lock));
478 rip_input(m, iphlen, proto);
479 return;
480
481 drop:
482 m_put_rcvif_psref(ifp, &psref);
483 m_freem(m);
484 return;
485 }
486
487 int
igmp_joingroup(struct in_multi * inm)488 igmp_joingroup(struct in_multi *inm)
489 {
490 KASSERT(in_multi_lock_held());
491 inm->inm_state = IGMP_IDLE_MEMBER;
492
493 if (!IN_LOCAL_GROUP(inm->inm_addr.s_addr) &&
494 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0) {
495 int report_type;
496
497 report_type = rti_fill(inm);
498 if (report_type == 0) {
499 return ENOMEM;
500 }
501 igmp_sendpkt(inm, report_type);
502 inm->inm_state = IGMP_DELAYING_MEMBER;
503 inm->inm_timer = IGMP_RANDOM_DELAY(
504 IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ);
505 igmp_timers_on = true;
506 } else
507 inm->inm_timer = 0;
508
509 return 0;
510 }
511
512 void
igmp_leavegroup(struct in_multi * inm)513 igmp_leavegroup(struct in_multi *inm)
514 {
515 KASSERT(in_multi_lock_held());
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 (inm->inm_ifp->if_flags & IFF_LOOPBACK) == 0)
522 if (inm->inm_rti->rti_type != IGMP_v1_ROUTER)
523 igmp_sendpkt(inm, IGMP_HOST_LEAVE_MESSAGE);
524 break;
525 case IGMP_LAZY_MEMBER:
526 case IGMP_AWAKENING_MEMBER:
527 case IGMP_SLEEPING_MEMBER:
528 break;
529 }
530 }
531
532 void
igmp_fasttimo(void)533 igmp_fasttimo(void)
534 {
535 struct in_multi *inm;
536 struct in_multistep step;
537
538 /*
539 * Quick check to see if any work needs to be done, in order
540 * to minimize the overhead of fasttimo processing.
541 */
542 if (!igmp_timers_on) {
543 return;
544 }
545
546 /* XXX: Needed for ip_output(). */
547 SOFTNET_LOCK_UNLESS_NET_MPSAFE();
548
549 in_multi_lock(RW_WRITER);
550 igmp_timers_on = false;
551 inm = in_first_multi(&step);
552 while (inm != NULL) {
553 if (inm->inm_timer == 0) {
554 /* do nothing */
555 } else if (--inm->inm_timer == 0) {
556 if (inm->inm_state == IGMP_DELAYING_MEMBER) {
557 if (inm->inm_rti->rti_type == IGMP_v1_ROUTER)
558 igmp_sendpkt(inm,
559 IGMP_v1_HOST_MEMBERSHIP_REPORT);
560 else
561 igmp_sendpkt(inm,
562 IGMP_v2_HOST_MEMBERSHIP_REPORT);
563 inm->inm_state = IGMP_IDLE_MEMBER;
564 }
565 } else {
566 igmp_timers_on = true;
567 }
568 inm = in_next_multi(&step);
569 }
570 in_multi_unlock();
571 SOFTNET_UNLOCK_UNLESS_NET_MPSAFE();
572 }
573
574 void
igmp_slowtimo(void)575 igmp_slowtimo(void)
576 {
577 router_info_t *rti;
578
579 in_multi_lock(RW_WRITER);
580 LIST_FOREACH(rti, &rti_head, rti_link) {
581 if (rti->rti_type == IGMP_v1_ROUTER &&
582 ++rti->rti_age >= IGMP_AGE_THRESHOLD) {
583 rti->rti_type = IGMP_v2_ROUTER;
584 }
585 }
586 in_multi_unlock();
587 }
588
589 /*
590 * igmp_sendpkt: construct an IGMP packet, given the multicast structure
591 * and the type, and send the datagram.
592 */
593 static void
igmp_sendpkt(struct in_multi * inm,int type)594 igmp_sendpkt(struct in_multi *inm, int type)
595 {
596 struct mbuf *m;
597 struct igmp *igmp;
598 struct ip *ip;
599 struct ip_moptions imo;
600
601 KASSERT(in_multi_lock_held());
602
603 MGETHDR(m, M_DONTWAIT, MT_HEADER);
604 if (m == NULL)
605 return;
606 KASSERT(max_linkhdr + sizeof(struct ip) + IGMP_MINLEN <= MHLEN);
607
608 m->m_data += max_linkhdr;
609 m->m_len = sizeof(struct ip) + IGMP_MINLEN;
610 m->m_pkthdr.len = sizeof(struct ip) + IGMP_MINLEN;
611
612 ip = mtod(m, struct ip *);
613 ip->ip_tos = 0;
614 ip->ip_len = htons(sizeof(struct ip) + IGMP_MINLEN);
615 ip->ip_off = htons(0);
616 ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL;
617 ip->ip_p = IPPROTO_IGMP;
618 ip->ip_src = zeroin_addr;
619 ip->ip_dst = inm->inm_addr;
620
621 m->m_data += sizeof(struct ip);
622 m->m_len -= sizeof(struct ip);
623 igmp = mtod(m, struct igmp *);
624 igmp->igmp_type = type;
625 igmp->igmp_code = 0;
626 igmp->igmp_group = inm->inm_addr;
627 igmp->igmp_cksum = 0;
628 igmp->igmp_cksum = in_cksum(m, IGMP_MINLEN);
629 m->m_data -= sizeof(struct ip);
630 m->m_len += sizeof(struct ip);
631
632 imo.imo_multicast_if_index = if_get_index(inm->inm_ifp);
633 imo.imo_multicast_ttl = 1;
634
635 /*
636 * Request loopback of the report if we are acting as a multicast
637 * router, so that the process-level routing demon can hear it.
638 */
639 #ifdef MROUTING
640 extern struct socket *ip_mrouter;
641 imo.imo_multicast_loop = (ip_mrouter != NULL);
642 #else
643 imo.imo_multicast_loop = 0;
644 #endif
645
646 /*
647 * Note: IP_IGMP_MCAST indicates that in_multilock is held.
648 * The caller must still acquire softnet_lock for ip_output().
649 */
650 #ifndef NET_MPSAFE
651 KASSERT(mutex_owned(softnet_lock));
652 #endif
653 ip_output(m, NULL, NULL, IP_IGMP_MCAST, &imo, NULL);
654 IGMP_STATINC(IGMP_STAT_SND_REPORTS);
655 }
656
657 void
igmp_purgeif(ifnet_t * ifp)658 igmp_purgeif(ifnet_t *ifp)
659 {
660 in_multi_lock(RW_WRITER);
661 rti_delete(ifp);
662 in_multi_unlock();
663 }
664
665 static int
sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)666 sysctl_net_inet_igmp_stats(SYSCTLFN_ARGS)
667 {
668 return NETSTAT_SYSCTL(igmpstat_percpu, IGMP_NSTATS);
669 }
670
671 static void
sysctl_net_inet_igmp_setup(struct sysctllog ** clog)672 sysctl_net_inet_igmp_setup(struct sysctllog **clog)
673 {
674 sysctl_createv(clog, 0, NULL, NULL,
675 CTLFLAG_PERMANENT,
676 CTLTYPE_NODE, "inet", NULL,
677 NULL, 0, NULL, 0,
678 CTL_NET, PF_INET, CTL_EOL);
679 sysctl_createv(clog, 0, NULL, NULL,
680 CTLFLAG_PERMANENT,
681 CTLTYPE_NODE, "igmp",
682 SYSCTL_DESCR("Internet Group Management Protocol"),
683 NULL, 0, NULL, 0,
684 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_EOL);
685 sysctl_createv(clog, 0, NULL, NULL,
686 CTLFLAG_PERMANENT,
687 CTLTYPE_STRUCT, "stats",
688 SYSCTL_DESCR("IGMP statistics"),
689 sysctl_net_inet_igmp_stats, 0, NULL, 0,
690 CTL_NET, PF_INET, IPPROTO_IGMP, CTL_CREATE, CTL_EOL);
691 }
692