Annotation of src/sys/netinet6/mld6.c, Revision 1.32
1.32 ! rpaulo 1: /* $NetBSD: mld6.c,v 1.31 2006/03/05 23:47:08 rpaulo Exp $ */
1.13 itojun 2: /* $KAME: mld6.c,v 1.25 2001/01/16 14:14:18 itojun Exp $ */
1.3 thorpej 3:
1.2 itojun 4: /*
5: * Copyright (C) 1998 WIDE Project.
6: * All rights reserved.
1.13 itojun 7: *
1.2 itojun 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.
1.13 itojun 19: *
1.2 itojun 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) 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.
1.23 agc 48: * 3. 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: /*
68: * Copyright (c) 1988 Stephen Deering.
69: *
70: * This code is derived from software contributed to Berkeley by
71: * Stephen Deering of Stanford University.
72: *
73: * Redistribution and use in source and binary forms, with or without
74: * modification, are permitted provided that the following conditions
75: * are met:
76: * 1. Redistributions of source code must retain the above copyright
77: * notice, this list of conditions and the following disclaimer.
78: * 2. Redistributions in binary form must reproduce the above copyright
79: * notice, this list of conditions and the following disclaimer in the
80: * documentation and/or other materials provided with the distribution.
1.2 itojun 81: * 3. All advertising materials mentioning features or use of this software
82: * must display the following acknowledgement:
83: * This product includes software developed by the University of
84: * California, Berkeley and its contributors.
85: * 4. Neither the name of the University nor the names of its contributors
86: * may be used to endorse or promote products derived from this software
87: * without specific prior written permission.
88: *
89: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
90: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
91: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
92: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
93: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
94: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
95: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
96: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
97: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
98: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
99: * SUCH DAMAGE.
100: *
101: * @(#)igmp.c 8.1 (Berkeley) 7/19/93
102: */
1.16 lukem 103:
104: #include <sys/cdefs.h>
1.32 ! rpaulo 105: __KERNEL_RCSID(0, "$NetBSD: mld6.c,v 1.31 2006/03/05 23:47:08 rpaulo Exp $");
1.2 itojun 106:
107: #include "opt_inet.h"
108:
109: #include <sys/param.h>
110: #include <sys/systm.h>
111: #include <sys/mbuf.h>
112: #include <sys/socket.h>
113: #include <sys/protosw.h>
114: #include <sys/syslog.h>
1.31 rpaulo 115: #include <sys/sysctl.h>
116: #include <sys/kernel.h>
117: #include <sys/callout.h>
1.2 itojun 118:
119: #include <net/if.h>
120:
121: #include <netinet/in.h>
122: #include <netinet/in_var.h>
1.31 rpaulo 123: #include <netinet6/in6_var.h>
1.10 itojun 124: #include <netinet/ip6.h>
1.2 itojun 125: #include <netinet6/ip6_var.h>
1.29 rpaulo 126: #include <netinet6/scope6_var.h>
1.10 itojun 127: #include <netinet/icmp6.h>
1.2 itojun 128: #include <netinet6/mld6_var.h>
129:
1.7 itojun 130: #include <net/net_osdep.h>
131:
1.31 rpaulo 132:
133: /*
134: * This structure is used to keep track of in6_multi chains which belong to
135: * deleted interface addresses.
136: */
137: static LIST_HEAD(, multi6_kludge) in6_mk; /* XXX BSS initialization */
138:
139: struct multi6_kludge {
140: LIST_ENTRY(multi6_kludge) mk_entry;
141: struct ifnet *mk_ifp;
142: struct in6_multihead mk_head;
143: };
144:
145:
1.2 itojun 146: /*
147: * Protocol constants
148: */
149:
150: /*
151: * time between repetitions of a node's initial report of interest in a
152: * multicast address(in seconds)
153: */
1.22 itojun 154: #define MLD_UNSOLICITED_REPORT_INTERVAL 10
1.2 itojun 155:
156: static struct ip6_pktopts ip6_opts;
157:
1.31 rpaulo 158: static void mld_start_listening(struct in6_multi *);
159: static void mld_stop_listening(struct in6_multi *);
160:
161: static struct mld_hdr * mld_allocbuf(struct mbuf **, int, struct in6_multi *,
162: int);
163: static void mld_sendpkt(struct in6_multi *, int, const struct in6_addr *);
164: static void mld_starttimer(struct in6_multi *);
165: static void mld_stoptimer(struct in6_multi *);
166: static void mld_timeo(struct in6_multi *);
167: static u_long mld_timerresid(struct in6_multi *);
1.2 itojun 168:
169: void
1.31 rpaulo 170: mld_init()
1.2 itojun 171: {
172: static u_int8_t hbh_buf[8];
173: struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
174: u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
175:
176: /* ip6h_nxt will be fill in later */
1.11 itojun 177: hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
1.2 itojun 178:
179: /* XXX: grotty hard coding... */
180: hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
181: hbh_buf[3] = 0;
182: hbh_buf[4] = IP6OPT_RTALERT;
183: hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
184: bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
185:
186: ip6_opts.ip6po_hbh = hbh;
187: /* We will specify the hoplimit by a multicast option. */
188: ip6_opts.ip6po_hlim = -1;
189: }
190:
1.31 rpaulo 191: static void
192: mld_starttimer(in6m)
193: struct in6_multi *in6m;
194: {
195: struct timeval now;
196:
197: microtime(&now);
198: in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
199: in6m->in6m_timer_expire.tv_usec = now.tv_usec +
200: (in6m->in6m_timer % hz) * (1000000 / hz);
201: if (in6m->in6m_timer_expire.tv_usec > 1000000) {
202: in6m->in6m_timer_expire.tv_sec++;
203: in6m->in6m_timer_expire.tv_usec -= 1000000;
204: }
205:
206: /* start or restart the timer */
207: callout_reset(in6m->in6m_timer_ch, in6m->in6m_timer,
208: (void (*) __P((void *)))mld_timeo, in6m);
209: }
210:
211: static void
212: mld_stoptimer(in6m)
213: struct in6_multi *in6m;
214: {
215: if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
216: return;
217:
218: callout_stop(in6m->in6m_timer_ch);
219:
220: in6m->in6m_timer = IN6M_TIMER_UNDEF;
221: }
222:
223: static void
224: mld_timeo(in6m)
225: struct in6_multi *in6m;
226: {
227: int s = splsoftnet();
228:
229: in6m->in6m_timer = IN6M_TIMER_UNDEF;
230:
231: callout_stop(in6m->in6m_timer_ch);
232:
233: switch (in6m->in6m_state) {
234: case MLD_REPORTPENDING:
235: mld_start_listening(in6m);
236: break;
237: default:
238: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
239: break;
240: }
241:
242: splx(s);
243: }
244:
245: static u_long
246: mld_timerresid(in6m)
247: struct in6_multi *in6m;
248: {
249: struct timeval now, diff;
250:
251: microtime(&now);
252:
253: if (now.tv_sec > in6m->in6m_timer_expire.tv_sec ||
254: (now.tv_sec == in6m->in6m_timer_expire.tv_sec &&
255: now.tv_usec > in6m->in6m_timer_expire.tv_usec)) {
256: return (0);
257: }
258: diff = in6m->in6m_timer_expire;
259: diff.tv_sec -= now.tv_sec;
260: diff.tv_usec -= now.tv_usec;
261: if (diff.tv_usec < 0) {
262: diff.tv_sec--;
263: diff.tv_usec += 1000000;
264: }
265:
266: /* return the remaining time in milliseconds */
267: return (((u_long)(diff.tv_sec * 1000000 + diff.tv_usec)) / 1000);
268: }
269:
270: static void
271: mld_start_listening(in6m)
1.2 itojun 272: struct in6_multi *in6m;
273: {
1.29 rpaulo 274: struct in6_addr all_in6;
275:
1.2 itojun 276: /*
1.11 itojun 277: * RFC2710 page 10:
1.2 itojun 278: * The node never sends a Report or Done for the link-scope all-nodes
279: * address.
280: * MLD messages are never sent for multicast addresses whose scope is 0
281: * (reserved) or 1 (node-local).
282: */
1.29 rpaulo 283: all_in6 = in6addr_linklocal_allnodes;
284: if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
285: /* XXX: this should not happen! */
286: in6m->in6m_timer = 0;
287: in6m->in6m_state = MLD_OTHERLISTENER;
288: }
289: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
1.2 itojun 290: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
1.31 rpaulo 291: in6m->in6m_timer = IN6M_TIMER_UNDEF;
1.22 itojun 292: in6m->in6m_state = MLD_OTHERLISTENER;
1.2 itojun 293: } else {
1.31 rpaulo 294: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
295: in6m->in6m_timer = arc4random() %
296: (MLD_UNSOLICITED_REPORT_INTERVAL * hz);
1.22 itojun 297: in6m->in6m_state = MLD_IREPORTEDLAST;
1.31 rpaulo 298:
299: mld_starttimer(in6m);
1.2 itojun 300: }
301: }
302:
1.31 rpaulo 303: static void
304: mld_stop_listening(in6m)
1.2 itojun 305: struct in6_multi *in6m;
306: {
1.29 rpaulo 307: struct in6_addr allnode, allrouter;
308:
309: allnode = in6addr_linklocal_allnodes;
310: if (in6_setscope(&allnode, in6m->in6m_ifp, NULL)) {
311: /* XXX: this should not happen! */
312: return;
313: }
314: allrouter = in6addr_linklocal_allrouters;
315: if (in6_setscope(&allrouter, in6m->in6m_ifp, NULL)) {
316: /* XXX impossible */
317: return;
318: }
1.2 itojun 319:
1.22 itojun 320: if (in6m->in6m_state == MLD_IREPORTEDLAST &&
1.29 rpaulo 321: (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode)) &&
322: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
323: IPV6_ADDR_SCOPE_INTFACELOCAL) {
1.31 rpaulo 324: mld_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
1.29 rpaulo 325: }
1.2 itojun 326: }
327:
328: void
1.31 rpaulo 329: mld_input(m, off)
1.2 itojun 330: struct mbuf *m;
331: int off;
332: {
333: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
1.22 itojun 334: struct mld_hdr *mldh;
1.2 itojun 335: struct ifnet *ifp = m->m_pkthdr.rcvif;
1.31 rpaulo 336: struct in6_multi *in6m = NULL;
1.29 rpaulo 337: struct in6_addr mld_addr, all_in6;
1.2 itojun 338: struct in6_ifaddr *ia;
1.31 rpaulo 339: int timer = 0; /* timer value in the MLD query header */
1.2 itojun 340:
1.22 itojun 341: IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
1.13 itojun 342: if (mldh == NULL) {
343: icmp6stat.icp6s_tooshort++;
344: return;
345: }
346:
1.2 itojun 347: /* source address validation */
1.13 itojun 348: ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
1.2 itojun 349: if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
1.31 rpaulo 350: /*
351: * RFC3590 allows the IPv6 unspecified address as the source
352: * address of MLD report and done messages. However, as this
353: * same document says, this special rule is for snooping
354: * switches and the RFC requires routers to discard MLD packets
355: * with the unspecified source address. The RFC only talks
356: * about hosts receiving an MLD query or report in Security
357: * Considerations, but this is probably the correct intention.
358: * RFC3590 does not talk about other cases than link-local and
359: * the unspecified source addresses, but we believe the same
360: * rule should be applied.
361: * As a result, we only allow link-local addresses as the
362: * source address; otherwise, simply discard the packet.
363: */
1.18 itojun 364: #if 0
1.31 rpaulo 365: /*
366: * XXX: do not log in an input path to avoid log flooding,
367: * though RFC3590 says "SHOULD log" if the source of a query
368: * is the unspecified address.
369: */
370: log(LOG_INFO,
1.22 itojun 371: "mld_input: src %s is not link-local (grp=%s)\n",
1.31 rpaulo 372: ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&mldh->mld_addr));
1.18 itojun 373: #endif
1.11 itojun 374: m_freem(m);
1.2 itojun 375: return;
376: }
377:
378: /*
1.29 rpaulo 379: * make a copy for local work (in6_setscope() may modify the 1st arg)
380: */
381: mld_addr = mldh->mld_addr;
382: if (in6_setscope(&mld_addr, ifp, NULL)) {
383: /* XXX: this should not happen! */
384: m_free(m);
385: return;
386: }
387:
388: /*
1.31 rpaulo 389: * In the MLD specification, there are 3 states and a flag.
1.2 itojun 390: *
391: * In Non-Listener state, we simply don't have a membership record.
392: * In Delaying Listener state, our timer is running (in6m->in6m_timer)
1.31 rpaulo 393: * In Idle Listener state, our timer is not running
394: * (in6m->in6m_timer==IN6M_TIMER_UNDEF)
1.2 itojun 395: *
1.22 itojun 396: * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
397: * we have heard a report from another member, or MLD_IREPORTEDLAST
1.2 itojun 398: * if we sent the last report.
399: */
1.22 itojun 400: switch (mldh->mld_type) {
401: case MLD_LISTENER_QUERY:
1.7 itojun 402: if (ifp->if_flags & IFF_LOOPBACK)
403: break;
404:
1.29 rpaulo 405: if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
406: !IN6_IS_ADDR_MULTICAST(&mld_addr))
1.7 itojun 407: break; /* print error or log stat? */
1.29 rpaulo 408:
409: all_in6 = in6addr_linklocal_allnodes;
410: if (in6_setscope(&all_in6, ifp, NULL)) {
411: /* XXX: this should not happen! */
412: break;
413: }
1.2 itojun 414:
1.7 itojun 415: /*
1.11 itojun 416: * - Start the timers in all of our membership records
417: * that the query applies to for the interface on
418: * which the query arrived excl. those that belong
419: * to the "all-nodes" group (ff02::1).
420: * - Restart any timer that is already running but has
1.30 rpaulo 421: * a value longer than the requested timeout.
1.11 itojun 422: * - Use the value specified in the query message as
423: * the maximum timeout.
424: */
1.31 rpaulo 425: timer = ntohs(mldh->mld_maxdelay);
426:
1.7 itojun 427: IFP_TO_IA6(ifp, ia);
428: if (ia == NULL)
429: break;
1.2 itojun 430:
1.7 itojun 431: for (in6m = ia->ia6_multiaddrs.lh_first;
432: in6m;
433: in6m = in6m->in6m_entry.le_next)
434: {
1.29 rpaulo 435: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
1.7 itojun 436: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
437: IPV6_ADDR_SCOPE_LINKLOCAL)
438: continue;
1.2 itojun 439:
1.31 rpaulo 440: if (in6m->in6m_state == MLD_REPORTPENDING)
441: continue; /* we are not yet ready */
442:
443: if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
444: !IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr))
445: continue;
446:
447: if (timer == 0) {
448: /* send a report immediately */
449: mld_stoptimer(in6m);
450: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
451: in6m->in6m_state = MLD_IREPORTEDLAST;
452: } else if (in6m->in6m_timer == IN6M_TIMER_UNDEF ||
453: mld_timerresid(in6m) > (u_long)timer) {
454: in6m->in6m_timer = arc4random() %
455: (int)(((long)timer * hz) / 1000);
456: mld_starttimer(in6m);
1.7 itojun 457: }
458: }
1.29 rpaulo 459: break;
1.2 itojun 460:
1.22 itojun 461: case MLD_LISTENER_REPORT:
1.2 itojun 462: /*
1.11 itojun 463: * For fast leave to work, we have to know that we are the
464: * last person to send a report for this group. Reports
465: * can potentially get looped back if we are a multicast
466: * router, so discard reports sourced by me.
467: * Note that it is impossible to check IFF_LOOPBACK flag of
468: * ifp for this purpose, since ip6_mloopback pass the physical
469: * interface to looutput.
470: */
1.7 itojun 471: if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
472: break;
473:
1.22 itojun 474: if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
1.7 itojun 475: break;
476:
477: /*
1.11 itojun 478: * If we belong to the group being reported, stop
479: * our timer for that group.
480: */
1.29 rpaulo 481: IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
1.7 itojun 482: if (in6m) {
1.31 rpaulo 483: mld_stoptimer(in6m); /* transit to idle state */
1.22 itojun 484: in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
1.7 itojun 485: }
486: break;
487: default: /* this is impossible */
1.18 itojun 488: #if 0
1.19 itojun 489: /*
490: * this case should be impossible because of filtering in
491: * icmp6_input(). But we explicitly disabled this part
492: * just in case.
493: */
1.31 rpaulo 494: log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
1.18 itojun 495: #endif
1.7 itojun 496: break;
1.2 itojun 497: }
1.11 itojun 498:
499: m_freem(m);
1.2 itojun 500: }
501:
502: static void
1.31 rpaulo 503: mld_sendpkt(in6m, type, dst)
1.2 itojun 504: struct in6_multi *in6m;
505: int type;
506: const struct in6_addr *dst;
507: {
1.31 rpaulo 508: struct mbuf *mh;
1.22 itojun 509: struct mld_hdr *mldh;
1.31 rpaulo 510: struct ip6_hdr *ip6 = NULL;
1.2 itojun 511: struct ip6_moptions im6o;
1.31 rpaulo 512: struct in6_ifaddr *ia = NULL;
1.2 itojun 513: struct ifnet *ifp = in6m->in6m_ifp;
1.19 itojun 514: int ignflags;
1.2 itojun 515:
516: /*
517: * At first, find a link local address on the outgoing interface
518: * to use as the source address of the MLD packet.
1.19 itojun 519: * We do not reject tentative addresses for MLD report to deal with
520: * the case where we first join a link-local address.
1.2 itojun 521: */
1.19 itojun 522: ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
523: if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL)
1.2 itojun 524: return;
1.19 itojun 525: if ((ia->ia6_flags & IN6_IFF_TENTATIVE))
526: ia = NULL;
1.2 itojun 527:
1.31 rpaulo 528: /* Allocate two mbufs to store IPv6 header and MLD header */
529: mldh = mld_allocbuf(&mh, sizeof(struct mld_hdr), in6m, type);
530: if (mldh == NULL)
1.2 itojun 531: return;
532:
1.31 rpaulo 533: /* fill src/dst here */
534: ip6 = mtod(mh, struct ip6_hdr *);
535: ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
536: ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
1.2 itojun 537:
1.22 itojun 538: mldh->mld_addr = in6m->in6m_addr;
1.29 rpaulo 539: in6_clearscope(&mldh->mld_addr); /* XXX */
1.22 itojun 540: mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
541: sizeof(struct mld_hdr));
1.2 itojun 542:
543: /* construct multicast option */
1.31 rpaulo 544: memset(&im6o, 0, sizeof(im6o));
1.2 itojun 545: im6o.im6o_multicast_ifp = ifp;
546: im6o.im6o_multicast_hlim = 1;
547:
548: /*
549: * Request loopback of the report if we are acting as a multicast
550: * router, so that the process-level routing daemon can hear it.
551: */
552: im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
553:
554: /* increment output statictics */
555: icmp6stat.icp6s_outhist[type]++;
1.15 itojun 556: icmp6_ifstat_inc(ifp, ifs6_out_msg);
1.17 itojun 557: switch (type) {
1.22 itojun 558: case MLD_LISTENER_QUERY:
1.15 itojun 559: icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
560: break;
1.22 itojun 561: case MLD_LISTENER_REPORT:
1.15 itojun 562: icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
563: break;
1.22 itojun 564: case MLD_LISTENER_DONE:
1.15 itojun 565: icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
566: break;
1.7 itojun 567: }
1.19 itojun 568:
1.27 perry 569: ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC,
1.25 itojun 570: &im6o, (struct socket *)NULL, NULL);
1.2 itojun 571: }
1.31 rpaulo 572:
573: static struct mld_hdr *
574: mld_allocbuf(mh, len, in6m, type)
575: struct mbuf **mh;
576: int len;
577: struct in6_multi *in6m;
578: int type;
579: {
580: struct mbuf *md;
581: struct mld_hdr *mldh;
582: struct ip6_hdr *ip6;
583:
584: /*
585: * Allocate mbufs to store ip6 header and MLD header.
586: * We allocate 2 mbufs and make chain in advance because
587: * it is more convenient when inserting the hop-by-hop option later.
588: */
589: MGETHDR(*mh, M_DONTWAIT, MT_HEADER);
590: if (*mh == NULL)
591: return NULL;
592: MGET(md, M_DONTWAIT, MT_DATA);
593: if (md == NULL) {
594: m_free(*mh);
595: *mh = NULL;
596: return NULL;
597: }
598: (*mh)->m_next = md;
599: md->m_next = NULL;
600:
601: (*mh)->m_pkthdr.rcvif = NULL;
602: (*mh)->m_pkthdr.len = sizeof(struct ip6_hdr) + len;
603: (*mh)->m_len = sizeof(struct ip6_hdr);
604: MH_ALIGN(*mh, sizeof(struct ip6_hdr));
605:
606: /* fill in the ip6 header */
607: ip6 = mtod(*mh, struct ip6_hdr *);
608: memset(ip6, 0, sizeof(*ip6));
609: ip6->ip6_flow = 0;
610: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
611: ip6->ip6_vfc |= IPV6_VERSION;
612: /* ip6_plen will be set later */
613: ip6->ip6_nxt = IPPROTO_ICMPV6;
614: /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
615: /* ip6_src/dst will be set by mld_sendpkt() or mld_sendbuf() */
616:
617: /* fill in the MLD header as much as possible */
618: md->m_len = len;
619: mldh = mtod(md, struct mld_hdr *);
620: memset(mldh, 0, len);
621: mldh->mld_type = type;
622: return mldh;
623: }
624:
625: /*
626: * Add an address to the list of IP6 multicast addresses for a given interface.
627: */
628: struct in6_multi *
1.32 ! rpaulo 629: in6_addmulti(maddr6, ifp, errorp, timer)
1.31 rpaulo 630: struct in6_addr *maddr6;
631: struct ifnet *ifp;
1.32 ! rpaulo 632: int *errorp, timer;
1.31 rpaulo 633: {
634: struct in6_ifaddr *ia;
635: struct in6_ifreq ifr;
636: struct in6_multi *in6m;
637: int s = splsoftnet();
638:
639: *errorp = 0;
640:
641: /*
642: * See if address already in list.
643: */
644: IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
645: if (in6m != NULL) {
646: /*
647: * Found it; just increment the refrence count.
648: */
649: in6m->in6m_refcount++;
650: } else {
651: /*
652: * New address; allocate a new multicast record
653: * and link it into the interface's multicast list.
654: */
655: in6m = (struct in6_multi *)
656: malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
657: if (in6m == NULL) {
658: splx(s);
659: *errorp = ENOBUFS;
660: return (NULL);
661: }
662:
663: memset(in6m, 0, sizeof(*in6m));
664: in6m->in6m_addr = *maddr6;
665: in6m->in6m_ifp = ifp;
666: in6m->in6m_refcount = 1;
667: in6m->in6m_timer = IN6M_TIMER_UNDEF;
668: in6m->in6m_timer_ch =
669: malloc(sizeof(*in6m->in6m_timer_ch), M_IPMADDR, M_NOWAIT);
670: if (in6m->in6m_timer_ch == NULL) {
671: free(in6m, M_IPMADDR);
672: splx(s);
673: return (NULL);
674: }
675: IFP_TO_IA6(ifp, ia);
676: if (ia == NULL) {
677: free(in6m, M_IPMADDR);
678: splx(s);
679: *errorp = EADDRNOTAVAIL; /* appropriate? */
680: return (NULL);
681: }
682: in6m->in6m_ia = ia;
683: IFAREF(&ia->ia_ifa); /* gain a reference */
684: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
685:
686: /*
687: * Ask the network driver to update its multicast reception
688: * filter appropriately for the new address.
689: */
690: memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6));
691: ifr.ifr_addr.sin6_family = AF_INET6;
692: ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
693: ifr.ifr_addr.sin6_addr = *maddr6;
694: if (ifp->if_ioctl == NULL)
695: *errorp = ENXIO; /* XXX: appropriate? */
696: else
697: *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI,
698: (caddr_t)&ifr);
699: if (*errorp) {
700: LIST_REMOVE(in6m, in6m_entry);
701: free(in6m, M_IPMADDR);
702: IFAFREE(&ia->ia_ifa);
703: splx(s);
704: return (NULL);
705: }
706:
707: callout_init(in6m->in6m_timer_ch);
1.32 ! rpaulo 708: in6m->in6m_timer = timer;
1.31 rpaulo 709: if (in6m->in6m_timer > 0) {
710: in6m->in6m_state = MLD_REPORTPENDING;
711: mld_starttimer(in6m);
712:
713: splx(s);
714: return (in6m);
715: }
716:
717: /*
718: * Let MLD6 know that we have joined a new IP6 multicast
719: * group.
720: */
721: mld_start_listening(in6m);
722: }
723: splx(s);
724: return (in6m);
725: }
726:
727: /*
728: * Delete a multicast address record.
729: */
730: void
731: in6_delmulti(in6m)
732: struct in6_multi *in6m;
733: {
734: struct in6_ifreq ifr;
735: struct in6_ifaddr *ia;
736: int s = splsoftnet();
737:
738: mld_stoptimer(in6m);
739:
740: if (--in6m->in6m_refcount == 0) {
741: /*
742: * No remaining claims to this record; let MLD6 know
743: * that we are leaving the multicast group.
744: */
745: mld_stop_listening(in6m);
746:
747: /*
748: * Unlink from list.
749: */
750: LIST_REMOVE(in6m, in6m_entry);
751: if (in6m->in6m_ia) {
752: IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
753: }
754:
755: /*
756: * Delete all references of this multicasting group from
757: * the membership arrays
758: */
759: for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
760: struct in6_multi_mship *imm;
761: LIST_FOREACH(imm, &ia->ia6_memberships,
762: i6mm_chain) {
763: if (imm->i6mm_maddr == in6m)
764: imm->i6mm_maddr = NULL;
765: }
766: }
767:
768: /*
769: * Notify the network driver to update its multicast
770: * reception filter.
771: */
772: memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6));
773: ifr.ifr_addr.sin6_family = AF_INET6;
774: ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
775: ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
776: (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp,
777: SIOCDELMULTI, (caddr_t)&ifr);
778: free(in6m->in6m_timer_ch, M_IPMADDR);
779: free(in6m, M_IPMADDR);
780: }
781: splx(s);
782: }
783:
784:
785: struct in6_multi_mship *
1.32 ! rpaulo 786: in6_joingroup(ifp, addr, errorp, timer)
1.31 rpaulo 787: struct ifnet *ifp;
788: struct in6_addr *addr;
1.32 ! rpaulo 789: int *errorp, timer;
1.31 rpaulo 790: {
791: struct in6_multi_mship *imm;
792:
793: imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT);
794: if (!imm) {
795: *errorp = ENOBUFS;
796: return NULL;
797: }
798:
799: memset(imm, 0, sizeof(*imm));
1.32 ! rpaulo 800: imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer);
1.31 rpaulo 801: if (!imm->i6mm_maddr) {
802: /* *errorp is alrady set */
803: free(imm, M_IPMADDR);
804: return NULL;
805: }
806: return imm;
807: }
808:
809: int
810: in6_leavegroup(imm)
811: struct in6_multi_mship *imm;
812: {
813:
814: if (imm->i6mm_maddr) {
815: in6_delmulti(imm->i6mm_maddr);
816: }
817: free(imm, M_IPMADDR);
818: return 0;
819: }
820:
821:
822: /*
823: * Multicast address kludge:
824: * If there were any multicast addresses attached to this interface address,
825: * either move them to another address on this interface, or save them until
826: * such time as this interface is reconfigured for IPv6.
827: */
828: void
829: in6_savemkludge(oia)
830: struct in6_ifaddr *oia;
831: {
832: struct in6_ifaddr *ia;
833: struct in6_multi *in6m, *next;
834:
835: IFP_TO_IA6(oia->ia_ifp, ia);
836: if (ia) { /* there is another address */
837: for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next) {
838: next = in6m->in6m_entry.le_next;
839: IFAFREE(&in6m->in6m_ia->ia_ifa);
840: IFAREF(&ia->ia_ifa);
841: in6m->in6m_ia = ia;
842: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
843: }
844: } else { /* last address on this if deleted, save */
845: struct multi6_kludge *mk;
846:
847: for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
848: if (mk->mk_ifp == oia->ia_ifp)
849: break;
850: }
851: if (mk == NULL) /* this should not happen! */
852: panic("in6_savemkludge: no kludge space");
853:
854: for (in6m = oia->ia6_multiaddrs.lh_first; in6m; in6m = next) {
855: next = in6m->in6m_entry.le_next;
856: IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
857: in6m->in6m_ia = NULL;
858: LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry);
859: }
860: }
861: }
862:
863: /*
864: * Continuation of multicast address hack:
865: * If there was a multicast group list previously saved for this interface,
866: * then we re-attach it to the first address configured on the i/f.
867: */
868: void
869: in6_restoremkludge(ia, ifp)
870: struct in6_ifaddr *ia;
871: struct ifnet *ifp;
872: {
873: struct multi6_kludge *mk;
874:
875: for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
876: if (mk->mk_ifp == ifp) {
877: struct in6_multi *in6m, *next;
878:
879: for (in6m = mk->mk_head.lh_first; in6m; in6m = next) {
880: next = in6m->in6m_entry.le_next;
881: in6m->in6m_ia = ia;
882: IFAREF(&ia->ia_ifa);
883: LIST_INSERT_HEAD(&ia->ia6_multiaddrs,
884: in6m, in6m_entry);
885: }
886: LIST_INIT(&mk->mk_head);
887: break;
888: }
889: }
890: }
891:
892: /*
893: * Allocate space for the kludge at interface initialization time.
894: * Formerly, we dynamically allocated the space in in6_savemkludge() with
895: * malloc(M_WAITOK). However, it was wrong since the function could be called
896: * under an interrupt context (software timer on address lifetime expiration).
897: * Also, we cannot just give up allocating the strucutre, since the group
898: * membership structure is very complex and we need to keep it anyway.
899: * Of course, this function MUST NOT be called under an interrupt context.
900: * Specifically, it is expected to be called only from in6_ifattach(), though
901: * it is a global function.
902: */
903: void
904: in6_createmkludge(ifp)
905: struct ifnet *ifp;
906: {
907: struct multi6_kludge *mk;
908:
909: for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
910: /* If we've already had one, do not allocate. */
911: if (mk->mk_ifp == ifp)
912: return;
913: }
914:
915: mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK);
916:
917: memset(mk, 0, sizeof(*mk));
918: LIST_INIT(&mk->mk_head);
919: mk->mk_ifp = ifp;
920: LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
921: }
922:
923: void
924: in6_purgemkludge(ifp)
925: struct ifnet *ifp;
926: {
927: struct multi6_kludge *mk;
928: struct in6_multi *in6m;
929:
930: for (mk = in6_mk.lh_first; mk; mk = mk->mk_entry.le_next) {
931: if (mk->mk_ifp != ifp)
932: continue;
933:
934: /* leave from all multicast groups joined */
935: while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) {
936: in6_delmulti(in6m);
937: }
938: LIST_REMOVE(mk, mk_entry);
939: free(mk, M_IPMADDR);
940: break;
941: }
942: }
CVSweb <webmaster@jp.NetBSD.org>