FreeBSD/Linux Kernel Cross Reference
sys/netinet6/mld6.c
1 /*-
2 * Copyright (C) 1998 WIDE Project.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $KAME: mld6.c,v 1.27 2001/04/04 05:17:30 itojun Exp $
30 */
31
32 /*-
33 * Copyright (c) 1988 Stephen Deering.
34 * Copyright (c) 1992, 1993
35 * The Regents of the University of California. All rights reserved.
36 *
37 * This code is derived from software contributed to Berkeley by
38 * Stephen Deering of Stanford University.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 * notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)igmp.c 8.1 (Berkeley) 7/19/93
65 */
66
67 #include <sys/cdefs.h>
68 __FBSDID("$FreeBSD$");
69
70 #include "opt_inet.h"
71 #include "opt_inet6.h"
72
73 #include <sys/param.h>
74 #include <sys/systm.h>
75 #include <sys/mbuf.h>
76 #include <sys/socket.h>
77 #include <sys/protosw.h>
78 #include <sys/syslog.h>
79 #include <sys/kernel.h>
80 #include <sys/callout.h>
81 #include <sys/malloc.h>
82
83 #include <net/if.h>
84
85 #include <netinet/in.h>
86 #include <netinet/in_var.h>
87 #include <netinet6/in6_var.h>
88 #include <netinet/ip6.h>
89 #include <netinet6/ip6_var.h>
90 #include <netinet6/scope6_var.h>
91 #include <netinet/icmp6.h>
92 #include <netinet6/mld6_var.h>
93
94 #include <net/net_osdep.h>
95
96 /*
97 * Protocol constants
98 */
99
100 /* denotes that the MLD max response delay field specifies time in milliseconds */
101 #define MLD_TIMER_SCALE 1000
102 /*
103 * time between repetitions of a node's initial report of interest in a
104 * multicast address(in seconds)
105 */
106 #define MLD_UNSOLICITED_REPORT_INTERVAL 10
107
108 static struct ip6_pktopts ip6_opts;
109
110 static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
111 static void mld_starttimer(struct in6_multi *);
112 static void mld_stoptimer(struct in6_multi *);
113 static void mld_timeo(struct in6_multi *);
114 static u_long mld_timerresid(struct in6_multi *);
115
116 void
117 mld6_init()
118 {
119 static u_int8_t hbh_buf[8];
120 struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
121 u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
122
123 /* ip6h_nxt will be fill in later */
124 hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
125
126 /* XXX: grotty hard coding... */
127 hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
128 hbh_buf[3] = 0;
129 hbh_buf[4] = IP6OPT_ROUTER_ALERT;
130 hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
131 bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
132
133 ip6_initpktopts(&ip6_opts);
134 ip6_opts.ip6po_hbh = hbh;
135 }
136
137 static void
138 mld_starttimer(in6m)
139 struct in6_multi *in6m;
140 {
141 struct timeval now;
142
143 microtime(&now);
144 in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
145 in6m->in6m_timer_expire.tv_usec = now.tv_usec +
146 (in6m->in6m_timer % hz) * (1000000 / hz);
147 if (in6m->in6m_timer_expire.tv_usec > 1000000) {
148 in6m->in6m_timer_expire.tv_sec++;
149 in6m->in6m_timer_expire.tv_usec -= 1000000;
150 }
151
152 /* start or restart the timer */
153 callout_reset(in6m->in6m_timer_ch, in6m->in6m_timer,
154 (void (*)(void *))mld_timeo, in6m);
155 }
156
157 static void
158 mld_stoptimer(in6m)
159 struct in6_multi *in6m;
160 {
161 if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
162 return;
163
164 callout_stop(in6m->in6m_timer_ch);
165 in6m->in6m_timer = IN6M_TIMER_UNDEF;
166 }
167
168 static void
169 mld_timeo(in6m)
170 struct in6_multi *in6m;
171 {
172 int s = splnet();
173
174 in6m->in6m_timer = IN6M_TIMER_UNDEF;
175
176 callout_stop(in6m->in6m_timer_ch);
177
178 switch (in6m->in6m_state) {
179 case MLD_REPORTPENDING:
180 mld6_start_listening(in6m);
181 break;
182 default:
183 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
184 break;
185 }
186
187 splx(s);
188 }
189
190 static u_long
191 mld_timerresid(in6m)
192 struct in6_multi *in6m;
193 {
194 struct timeval now, diff;
195
196 microtime(&now);
197
198 if (now.tv_sec > in6m->in6m_timer_expire.tv_sec ||
199 (now.tv_sec == in6m->in6m_timer_expire.tv_sec &&
200 now.tv_usec > in6m->in6m_timer_expire.tv_usec)) {
201 return (0);
202 }
203 diff = in6m->in6m_timer_expire;
204 diff.tv_sec -= now.tv_sec;
205 diff.tv_usec -= now.tv_usec;
206 if (diff.tv_usec < 0) {
207 diff.tv_sec--;
208 diff.tv_usec += 1000000;
209 }
210
211 /* return the remaining time in milliseconds */
212 return (((u_long)(diff.tv_sec * 1000000 + diff.tv_usec)) / 1000);
213 }
214
215 void
216 mld6_start_listening(in6m)
217 struct in6_multi *in6m;
218 {
219 struct in6_addr all_in6;
220 int s = splnet();
221
222 /*
223 * RFC2710 page 10:
224 * The node never sends a Report or Done for the link-scope all-nodes
225 * address.
226 * MLD messages are never sent for multicast addresses whose scope is 0
227 * (reserved) or 1 (node-local).
228 */
229 all_in6 = in6addr_linklocal_allnodes;
230 if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
231 /* XXX: this should not happen! */
232 in6m->in6m_timer = 0;
233 in6m->in6m_state = MLD_OTHERLISTENER;
234 }
235 if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
236 IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
237 IPV6_ADDR_SCOPE_LINKLOCAL) {
238 in6m->in6m_timer = 0;
239 in6m->in6m_state = MLD_OTHERLISTENER;
240 } else {
241 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
242 in6m->in6m_timer = arc4random() %
243 MLD_UNSOLICITED_REPORT_INTERVAL * hz;
244 in6m->in6m_state = MLD_IREPORTEDLAST;
245
246 mld_starttimer(in6m);
247 }
248 splx(s);
249 }
250
251 void
252 mld6_stop_listening(in6m)
253 struct in6_multi *in6m;
254 {
255 struct in6_addr allnode, allrouter;
256
257 allnode = in6addr_linklocal_allnodes;
258 if (in6_setscope(&allnode, in6m->in6m_ifp, NULL)) {
259 /* XXX: this should not happen! */
260 return;
261 }
262 allrouter = in6addr_linklocal_allrouters;
263 if (in6_setscope(&allrouter, in6m->in6m_ifp, NULL)) {
264 /* XXX impossible */
265 return;
266 }
267 if (in6m->in6m_state == MLD_IREPORTEDLAST &&
268 !IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode) &&
269 IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
270 IPV6_ADDR_SCOPE_INTFACELOCAL) {
271 mld6_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
272 }
273 }
274
275 void
276 mld6_input(m, off)
277 struct mbuf *m;
278 int off;
279 {
280 struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
281 struct mld_hdr *mldh;
282 struct ifnet *ifp = m->m_pkthdr.rcvif;
283 struct in6_multi *in6m;
284 struct in6_addr mld_addr, all_in6;
285 struct in6_ifaddr *ia;
286 struct ifmultiaddr *ifma;
287 u_long timer; /* timer value in the MLD query header */
288
289 #ifndef PULLDOWN_TEST
290 IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),);
291 mldh = (struct mld_hdr *)(mtod(m, caddr_t) + off);
292 #else
293 IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
294 if (mldh == NULL) {
295 icmp6stat.icp6s_tooshort++;
296 return;
297 }
298 #endif
299
300 /* source address validation */
301 ip6 = mtod(m, struct ip6_hdr *); /* in case mpullup */
302 if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
303 log(LOG_ERR,
304 "mld6_input: src %s is not link-local (grp=%s)\n",
305 ip6_sprintf(&ip6->ip6_src),
306 ip6_sprintf(&mldh->mld_addr));
307 /*
308 * spec (RFC2710) does not explicitly
309 * specify to discard the packet from a non link-local
310 * source address. But we believe it's expected to do so.
311 * XXX: do we have to allow :: as source?
312 */
313 m_freem(m);
314 return;
315 }
316
317 /*
318 * make a copy for local work (in6_setscope() may modify the 1st arg)
319 */
320 mld_addr = mldh->mld_addr;
321 if (in6_setscope(&mld_addr, ifp, NULL)) {
322 /* XXX: this should not happen! */
323 m_free(m);
324 return;
325 }
326
327 /*
328 * In the MLD6 specification, there are 3 states and a flag.
329 *
330 * In Non-Listener state, we simply don't have a membership record.
331 * In Delaying Listener state, our timer is running (in6m->in6m_timer)
332 * In Idle Listener state, our timer is not running
333 * (in6m->in6m_timer==IN6M_TIMER_UNDEF)
334 *
335 * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
336 * we have heard a report from another member, or MLD_IREPORTEDLAST
337 * if we sent the last report.
338 */
339 switch(mldh->mld_type) {
340 case MLD_LISTENER_QUERY:
341 if (ifp->if_flags & IFF_LOOPBACK)
342 break;
343
344 if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
345 !IN6_IS_ADDR_MULTICAST(&mld_addr))
346 break; /* print error or log stat? */
347
348 all_in6 = in6addr_linklocal_allnodes;
349 if (in6_setscope(&all_in6, ifp, NULL)) {
350 /* XXX: this should not happen! */
351 break;
352 }
353
354 /*
355 * - Start the timers in all of our membership records
356 * that the query applies to for the interface on
357 * which the query arrived excl. those that belong
358 * to the "all-nodes" group (ff02::1).
359 * - Restart any timer that is already running but has
360 * A value longer than the requested timeout.
361 * - Use the value specified in the query message as
362 * the maximum timeout.
363 */
364 timer = ntohs(mldh->mld_maxdelay);
365
366 IFP_TO_IA6(ifp, ia);
367 if (ia == NULL)
368 break;
369
370 /*
371 * XXX: System timer resolution is too low to handle Max
372 * Response Delay, so set 1 to the internal timer even if
373 * the calculated value equals to zero when Max Response
374 * Delay is positive.
375 */
376 timer = ntohs(mldh->mld_maxdelay) * PR_FASTHZ / MLD_TIMER_SCALE;
377 if (timer == 0 && mldh->mld_maxdelay)
378 timer = 1;
379
380 IF_ADDR_LOCK(ifp);
381 TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
382 if (ifma->ifma_addr->sa_family != AF_INET6)
383 continue;
384 in6m = (struct in6_multi *)ifma->ifma_protospec;
385
386 if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
387 IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
388 IPV6_ADDR_SCOPE_LINKLOCAL)
389 continue;
390
391 if (IN6_IS_ADDR_UNSPECIFIED(&mld_addr) ||
392 IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr)) {
393 if (timer == 0) {
394 /* send a report immediately */
395 mld_stoptimer(in6m);
396 mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
397 NULL);
398 in6m->in6m_timer = 0; /* reset timer */
399 in6m->in6m_state = MLD_IREPORTEDLAST;
400 }
401 else if (in6m->in6m_timer == IN6M_TIMER_UNDEF ||
402 mld_timerresid(in6m) > timer) {
403 in6m->in6m_timer =
404 1 + (arc4random() % timer) * hz / 1000;
405 mld_starttimer(in6m);
406 }
407 }
408 }
409 IF_ADDR_UNLOCK(ifp);
410 break;
411
412 case MLD_LISTENER_REPORT:
413 /*
414 * For fast leave to work, we have to know that we are the
415 * last person to send a report for this group. Reports
416 * can potentially get looped back if we are a multicast
417 * router, so discard reports sourced by me.
418 * Note that it is impossible to check IFF_LOOPBACK flag of
419 * ifp for this purpose, since ip6_mloopback pass the physical
420 * interface to looutput.
421 */
422 if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
423 break;
424
425 if (!IN6_IS_ADDR_MULTICAST(&mld_addr))
426 break;
427
428 /*
429 * If we belong to the group being reported, stop
430 * our timer for that group.
431 */
432 IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
433 if (in6m) {
434 in6m->in6m_timer = 0; /* transit to idle state */
435 in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
436 }
437 break;
438 default: /* this is impossible */
439 log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld_type);
440 break;
441 }
442
443 m_freem(m);
444 }
445
446 static void
447 mld6_sendpkt(in6m, type, dst)
448 struct in6_multi *in6m;
449 int type;
450 const struct in6_addr *dst;
451 {
452 struct mbuf *mh, *md;
453 struct mld_hdr *mldh;
454 struct ip6_hdr *ip6;
455 struct ip6_moptions im6o;
456 struct in6_ifaddr *ia;
457 struct ifnet *ifp = in6m->in6m_ifp;
458 struct ifnet *outif = NULL;
459
460 /*
461 * At first, find a link local address on the outgoing interface
462 * to use as the source address of the MLD packet.
463 */
464 if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST))
465 == NULL)
466 return;
467
468 /*
469 * Allocate mbufs to store ip6 header and MLD header.
470 * We allocate 2 mbufs and make chain in advance because
471 * it is more convenient when inserting the hop-by-hop option later.
472 */
473 MGETHDR(mh, M_DONTWAIT, MT_HEADER);
474 if (mh == NULL)
475 return;
476 MGET(md, M_DONTWAIT, MT_DATA);
477 if (md == NULL) {
478 m_free(mh);
479 return;
480 }
481 mh->m_next = md;
482
483 mh->m_pkthdr.rcvif = NULL;
484 mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
485 mh->m_len = sizeof(struct ip6_hdr);
486 MH_ALIGN(mh, sizeof(struct ip6_hdr));
487
488 /* fill in the ip6 header */
489 ip6 = mtod(mh, struct ip6_hdr *);
490 ip6->ip6_flow = 0;
491 ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
492 ip6->ip6_vfc |= IPV6_VERSION;
493 /* ip6_plen will be set later */
494 ip6->ip6_nxt = IPPROTO_ICMPV6;
495 /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
496 ip6->ip6_src = ia->ia_addr.sin6_addr;
497 ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
498
499 /* fill in the MLD header */
500 md->m_len = sizeof(struct mld_hdr);
501 mldh = mtod(md, struct mld_hdr *);
502 mldh->mld_type = type;
503 mldh->mld_code = 0;
504 mldh->mld_cksum = 0;
505 /* XXX: we assume the function will not be called for query messages */
506 mldh->mld_maxdelay = 0;
507 mldh->mld_reserved = 0;
508 mldh->mld_addr = in6m->in6m_addr;
509 in6_clearscope(&mldh->mld_addr); /* XXX */
510 mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
511 sizeof(struct mld_hdr));
512
513 /* construct multicast option */
514 bzero(&im6o, sizeof(im6o));
515 im6o.im6o_multicast_ifp = ifp;
516 im6o.im6o_multicast_hlim = 1;
517
518 /*
519 * Request loopback of the report if we are acting as a multicast
520 * router, so that the process-level routing daemon can hear it.
521 */
522 im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
523
524 /* increment output statictics */
525 icmp6stat.icp6s_outhist[type]++;
526
527 ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif, NULL);
528 if (outif) {
529 icmp6_ifstat_inc(outif, ifs6_out_msg);
530 switch (type) {
531 case MLD_LISTENER_QUERY:
532 icmp6_ifstat_inc(outif, ifs6_out_mldquery);
533 break;
534 case MLD_LISTENER_REPORT:
535 icmp6_ifstat_inc(outif, ifs6_out_mldreport);
536 break;
537 case MLD_LISTENER_DONE:
538 icmp6_ifstat_inc(outif, ifs6_out_mlddone);
539 break;
540 }
541 }
542 }
543
544 /*
545 * Add an address to the list of IP6 multicast addresses for a given interface.
546 * Add source addresses to the list also, if upstream router is MLDv2 capable
547 * and the number of source is not 0.
548 */
549 struct in6_multi *
550 in6_addmulti(maddr6, ifp, errorp, delay)
551 struct in6_addr *maddr6;
552 struct ifnet *ifp;
553 int *errorp, delay;
554 {
555 struct in6_multi *in6m;
556 struct ifmultiaddr *ifma;
557 struct sockaddr_in6 sa6;
558 int s = splnet();
559
560 *errorp = 0;
561
562 /*
563 * Call generic routine to add membership or increment
564 * refcount. It wants addresses in the form of a sockaddr,
565 * so we build one here (being careful to zero the unused bytes).
566 */
567 bzero(&sa6, sizeof(sa6));
568 sa6.sin6_family = AF_INET6;
569 sa6.sin6_len = sizeof(struct sockaddr_in6);
570 sa6.sin6_addr = *maddr6;
571 *errorp = if_addmulti(ifp, (struct sockaddr *)&sa6, &ifma);
572 if (*errorp) {
573 splx(s);
574 return 0;
575 }
576
577 /*
578 * If ifma->ifma_protospec is null, then if_addmulti() created
579 * a new record. Otherwise, we are done.
580 */
581 if (ifma->ifma_protospec != NULL) {
582 splx(s);
583 return ifma->ifma_protospec;
584 }
585
586 /* XXX - if_addmulti uses M_WAITOK. Can this really be called
587 at interrupt time? If so, need to fix if_addmulti. XXX */
588 in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IP6MADDR, M_NOWAIT);
589 if (in6m == NULL) {
590 splx(s);
591 return (NULL);
592 }
593
594 bzero(in6m, sizeof *in6m);
595 in6m->in6m_addr = *maddr6;
596 in6m->in6m_ifp = ifp;
597 in6m->in6m_refcount = 1;
598 in6m->in6m_ifma = ifma;
599 ifma->ifma_protospec = in6m;
600 in6m->in6m_timer_ch = malloc(sizeof(*in6m->in6m_timer_ch), M_IP6MADDR,
601 M_NOWAIT);
602 if (in6m->in6m_timer_ch == NULL) {
603 free(in6m, M_IP6MADDR);
604 splx(s);
605 return (NULL);
606 }
607 LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
608
609 callout_init(in6m->in6m_timer_ch, 0);
610 in6m->in6m_timer = delay;
611 if (in6m->in6m_timer > 0) {
612 in6m->in6m_state = MLD_REPORTPENDING;
613 mld_starttimer(in6m);
614
615 splx(s);
616 return (in6m);
617 }
618
619 /*
620 * Let MLD6 know that we have joined a new IPv6 multicast
621 * group.
622 */
623 mld6_start_listening(in6m);
624 splx(s);
625 return (in6m);
626 }
627
628 /*
629 * Delete a multicast address record.
630 */
631 void
632 in6_delmulti(in6m)
633 struct in6_multi *in6m;
634 {
635 struct ifmultiaddr *ifma = in6m->in6m_ifma;
636 int s = splnet();
637
638 if (ifma->ifma_refcount == 1) {
639 /*
640 * No remaining claims to this record; let MLD6 know
641 * that we are leaving the multicast group.
642 */
643 mld_stoptimer(in6m);
644 mld6_stop_listening(in6m);
645 ifma->ifma_protospec = NULL;
646 LIST_REMOVE(in6m, in6m_entry);
647 free(in6m->in6m_timer_ch, M_IP6MADDR);
648 free(in6m, M_IP6MADDR);
649 }
650 /* XXX - should be separate API for when we have an ifma? */
651 if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
652 splx(s);
653 }
Cache object: 7a4e260e984e7ede7c92c4a6c4febedc
|