Annotation of src/sys/netinet6/mld6.c, Revision 1.27.4.2
1.27.4.2! yamt 1: /* $NetBSD: mld6.c,v 1.27.4.1 2006/06/21 15:11:09 yamt 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.27.4.2! yamt 105: __KERNEL_RCSID(0, "$NetBSD: mld6.c,v 1.27.4.1 2006/06/21 15:11:09 yamt 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.27.4.1 yamt 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.27.4.1 yamt 123: #include <netinet6/in6_var.h>
1.10 itojun 124: #include <netinet/ip6.h>
1.2 itojun 125: #include <netinet6/ip6_var.h>
1.27.4.1 yamt 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.27.4.1 yamt 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.27.4.1 yamt 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.27.4.1 yamt 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.27.4.1 yamt 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)
1.2 itojun 247: struct in6_multi *in6m;
248: {
1.27.4.1 yamt 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)
272: struct in6_multi *in6m;
273: {
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.27.4.1 yamt 283: all_in6 = in6addr_linklocal_allnodes;
284: if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
285: /* XXX: this should not happen! */
1.2 itojun 286: in6m->in6m_timer = 0;
1.22 itojun 287: in6m->in6m_state = MLD_OTHERLISTENER;
1.27.4.1 yamt 288: }
289: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
290: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
291: in6m->in6m_timer = IN6M_TIMER_UNDEF;
292: in6m->in6m_state = MLD_OTHERLISTENER;
1.2 itojun 293: } else {
1.27.4.1 yamt 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.27.4.1 yamt 298:
299: mld_starttimer(in6m);
1.2 itojun 300: }
301: }
302:
1.27.4.1 yamt 303: static void
304: mld_stop_listening(in6m)
1.2 itojun 305: struct in6_multi *in6m;
306: {
1.27.4.1 yamt 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.27.4.1 yamt 321: (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode)) &&
322: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
323: IPV6_ADDR_SCOPE_INTFACELOCAL) {
324: mld_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
325: }
1.2 itojun 326: }
327:
328: void
1.27.4.1 yamt 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.27.4.1 yamt 336: struct in6_multi *in6m = NULL;
337: struct in6_addr mld_addr, all_in6;
1.2 itojun 338: struct in6_ifaddr *ia;
1.27.4.1 yamt 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.27.4.1 yamt 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.2 itojun 365: /*
1.27.4.1 yamt 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.
1.2 itojun 369: */
1.27.4.1 yamt 370: log(LOG_INFO,
371: "mld_input: src %s is not link-local (grp=%s)\n",
372: ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&mldh->mld_addr));
373: #endif
1.11 itojun 374: m_freem(m);
1.2 itojun 375: return;
376: }
377:
378: /*
1.27.4.1 yamt 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: /*
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.27.4.1 yamt 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.27.4.1 yamt 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.27.4.1 yamt 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.27.4.1 yamt 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.27.4.1 yamt 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.27.4.2! yamt 431: LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) {
1.27.4.1 yamt 432: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
1.7 itojun 433: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
434: IPV6_ADDR_SCOPE_LINKLOCAL)
435: continue;
1.2 itojun 436:
1.27.4.1 yamt 437: if (in6m->in6m_state == MLD_REPORTPENDING)
438: continue; /* we are not yet ready */
439:
440: if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
441: !IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr))
442: continue;
443:
444: if (timer == 0) {
445: /* send a report immediately */
446: mld_stoptimer(in6m);
447: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
448: in6m->in6m_state = MLD_IREPORTEDLAST;
449: } else if (in6m->in6m_timer == IN6M_TIMER_UNDEF ||
450: mld_timerresid(in6m) > (u_long)timer) {
451: in6m->in6m_timer = arc4random() %
452: (int)(((long)timer * hz) / 1000);
453: mld_starttimer(in6m);
1.7 itojun 454: }
455: }
456: break;
1.27.4.1 yamt 457:
1.22 itojun 458: case MLD_LISTENER_REPORT:
1.2 itojun 459: /*
1.11 itojun 460: * For fast leave to work, we have to know that we are the
461: * last person to send a report for this group. Reports
462: * can potentially get looped back if we are a multicast
463: * router, so discard reports sourced by me.
464: * Note that it is impossible to check IFF_LOOPBACK flag of
465: * ifp for this purpose, since ip6_mloopback pass the physical
466: * interface to looutput.
467: */
1.7 itojun 468: if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
469: break;
470:
1.22 itojun 471: if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
1.7 itojun 472: break;
473:
474: /*
1.11 itojun 475: * If we belong to the group being reported, stop
476: * our timer for that group.
477: */
1.27.4.1 yamt 478: IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
1.7 itojun 479: if (in6m) {
1.27.4.1 yamt 480: mld_stoptimer(in6m); /* transit to idle state */
1.22 itojun 481: in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
1.7 itojun 482: }
483: break;
484: default: /* this is impossible */
1.18 itojun 485: #if 0
1.19 itojun 486: /*
487: * this case should be impossible because of filtering in
488: * icmp6_input(). But we explicitly disabled this part
489: * just in case.
490: */
1.27.4.1 yamt 491: log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
1.18 itojun 492: #endif
1.7 itojun 493: break;
1.2 itojun 494: }
1.11 itojun 495:
496: m_freem(m);
1.2 itojun 497: }
498:
499: static void
1.27.4.1 yamt 500: mld_sendpkt(in6m, type, dst)
1.2 itojun 501: struct in6_multi *in6m;
502: int type;
503: const struct in6_addr *dst;
504: {
1.27.4.1 yamt 505: struct mbuf *mh;
1.22 itojun 506: struct mld_hdr *mldh;
1.27.4.1 yamt 507: struct ip6_hdr *ip6 = NULL;
1.2 itojun 508: struct ip6_moptions im6o;
1.27.4.1 yamt 509: struct in6_ifaddr *ia = NULL;
1.2 itojun 510: struct ifnet *ifp = in6m->in6m_ifp;
1.19 itojun 511: int ignflags;
1.2 itojun 512:
513: /*
514: * At first, find a link local address on the outgoing interface
515: * to use as the source address of the MLD packet.
1.19 itojun 516: * We do not reject tentative addresses for MLD report to deal with
517: * the case where we first join a link-local address.
1.2 itojun 518: */
1.19 itojun 519: ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
520: if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL)
1.2 itojun 521: return;
1.19 itojun 522: if ((ia->ia6_flags & IN6_IFF_TENTATIVE))
523: ia = NULL;
1.2 itojun 524:
1.27.4.1 yamt 525: /* Allocate two mbufs to store IPv6 header and MLD header */
526: mldh = mld_allocbuf(&mh, sizeof(struct mld_hdr), in6m, type);
527: if (mldh == NULL)
1.2 itojun 528: return;
529:
1.27.4.1 yamt 530: /* fill src/dst here */
531: ip6 = mtod(mh, struct ip6_hdr *);
532: ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
533: ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
1.2 itojun 534:
1.22 itojun 535: mldh->mld_addr = in6m->in6m_addr;
1.27.4.1 yamt 536: in6_clearscope(&mldh->mld_addr); /* XXX */
1.22 itojun 537: mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
538: sizeof(struct mld_hdr));
1.2 itojun 539:
540: /* construct multicast option */
1.27.4.1 yamt 541: memset(&im6o, 0, sizeof(im6o));
1.2 itojun 542: im6o.im6o_multicast_ifp = ifp;
543: im6o.im6o_multicast_hlim = 1;
544:
545: /*
546: * Request loopback of the report if we are acting as a multicast
547: * router, so that the process-level routing daemon can hear it.
548: */
549: im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
550:
551: /* increment output statictics */
552: icmp6stat.icp6s_outhist[type]++;
1.15 itojun 553: icmp6_ifstat_inc(ifp, ifs6_out_msg);
1.17 itojun 554: switch (type) {
1.22 itojun 555: case MLD_LISTENER_QUERY:
1.15 itojun 556: icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
557: break;
1.22 itojun 558: case MLD_LISTENER_REPORT:
1.15 itojun 559: icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
560: break;
1.22 itojun 561: case MLD_LISTENER_DONE:
1.15 itojun 562: icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
563: break;
1.7 itojun 564: }
1.19 itojun 565:
1.27 perry 566: ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC,
1.25 itojun 567: &im6o, (struct socket *)NULL, NULL);
1.2 itojun 568: }
1.27.4.1 yamt 569:
570: static struct mld_hdr *
1.27.4.2! yamt 571: mld_allocbuf(struct mbuf **mh, int len, struct in6_multi *in6m,
! 572: int type)
1.27.4.1 yamt 573: {
574: struct mbuf *md;
575: struct mld_hdr *mldh;
576: struct ip6_hdr *ip6;
577:
578: /*
579: * Allocate mbufs to store ip6 header and MLD header.
580: * We allocate 2 mbufs and make chain in advance because
581: * it is more convenient when inserting the hop-by-hop option later.
582: */
583: MGETHDR(*mh, M_DONTWAIT, MT_HEADER);
584: if (*mh == NULL)
585: return NULL;
586: MGET(md, M_DONTWAIT, MT_DATA);
587: if (md == NULL) {
588: m_free(*mh);
589: *mh = NULL;
590: return NULL;
591: }
592: (*mh)->m_next = md;
593: md->m_next = NULL;
594:
595: (*mh)->m_pkthdr.rcvif = NULL;
596: (*mh)->m_pkthdr.len = sizeof(struct ip6_hdr) + len;
597: (*mh)->m_len = sizeof(struct ip6_hdr);
598: MH_ALIGN(*mh, sizeof(struct ip6_hdr));
599:
600: /* fill in the ip6 header */
601: ip6 = mtod(*mh, struct ip6_hdr *);
602: memset(ip6, 0, sizeof(*ip6));
603: ip6->ip6_flow = 0;
604: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
605: ip6->ip6_vfc |= IPV6_VERSION;
606: /* ip6_plen will be set later */
607: ip6->ip6_nxt = IPPROTO_ICMPV6;
608: /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
609: /* ip6_src/dst will be set by mld_sendpkt() or mld_sendbuf() */
610:
611: /* fill in the MLD header as much as possible */
612: md->m_len = len;
613: mldh = mtod(md, struct mld_hdr *);
614: memset(mldh, 0, len);
615: mldh->mld_type = type;
616: return mldh;
617: }
618:
619: /*
620: * Add an address to the list of IP6 multicast addresses for a given interface.
621: */
622: struct in6_multi *
623: in6_addmulti(maddr6, ifp, errorp, timer)
624: struct in6_addr *maddr6;
625: struct ifnet *ifp;
626: int *errorp, timer;
627: {
628: struct in6_ifaddr *ia;
629: struct in6_ifreq ifr;
630: struct in6_multi *in6m;
631: int s = splsoftnet();
632:
633: *errorp = 0;
634:
635: /*
636: * See if address already in list.
637: */
638: IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
639: if (in6m != NULL) {
640: /*
641: * Found it; just increment the refrence count.
642: */
643: in6m->in6m_refcount++;
644: } else {
645: /*
646: * New address; allocate a new multicast record
647: * and link it into the interface's multicast list.
648: */
649: in6m = (struct in6_multi *)
650: malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
651: if (in6m == NULL) {
652: splx(s);
653: *errorp = ENOBUFS;
654: return (NULL);
655: }
656:
657: memset(in6m, 0, sizeof(*in6m));
658: in6m->in6m_addr = *maddr6;
659: in6m->in6m_ifp = ifp;
660: in6m->in6m_refcount = 1;
661: in6m->in6m_timer = IN6M_TIMER_UNDEF;
662: in6m->in6m_timer_ch =
663: malloc(sizeof(*in6m->in6m_timer_ch), M_IPMADDR, M_NOWAIT);
664: if (in6m->in6m_timer_ch == NULL) {
665: free(in6m, M_IPMADDR);
666: splx(s);
667: return (NULL);
668: }
669: IFP_TO_IA6(ifp, ia);
670: if (ia == NULL) {
1.27.4.2! yamt 671: /* leaks in6m_timer_ch */
1.27.4.1 yamt 672: free(in6m, M_IPMADDR);
673: splx(s);
674: *errorp = EADDRNOTAVAIL; /* appropriate? */
675: return (NULL);
676: }
677: in6m->in6m_ia = ia;
678: IFAREF(&ia->ia_ifa); /* gain a reference */
679: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
680:
681: /*
682: * Ask the network driver to update its multicast reception
683: * filter appropriately for the new address.
684: */
685: memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6));
686: ifr.ifr_addr.sin6_family = AF_INET6;
687: ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
688: ifr.ifr_addr.sin6_addr = *maddr6;
689: if (ifp->if_ioctl == NULL)
690: *errorp = ENXIO; /* XXX: appropriate? */
691: else
692: *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI,
693: (caddr_t)&ifr);
694: if (*errorp) {
695: LIST_REMOVE(in6m, in6m_entry);
1.27.4.2! yamt 696: /* leaks in6m_timer_ch */
1.27.4.1 yamt 697: free(in6m, M_IPMADDR);
698: IFAFREE(&ia->ia_ifa);
699: splx(s);
700: return (NULL);
701: }
702:
703: callout_init(in6m->in6m_timer_ch);
704: in6m->in6m_timer = timer;
705: if (in6m->in6m_timer > 0) {
706: in6m->in6m_state = MLD_REPORTPENDING;
707: mld_starttimer(in6m);
708:
709: splx(s);
710: return (in6m);
711: }
712:
713: /*
714: * Let MLD6 know that we have joined a new IP6 multicast
715: * group.
716: */
717: mld_start_listening(in6m);
718: }
719: splx(s);
720: return (in6m);
721: }
722:
723: /*
724: * Delete a multicast address record.
725: */
726: void
727: in6_delmulti(in6m)
728: struct in6_multi *in6m;
729: {
730: struct in6_ifreq ifr;
731: struct in6_ifaddr *ia;
732: int s = splsoftnet();
733:
734: mld_stoptimer(in6m);
735:
736: if (--in6m->in6m_refcount == 0) {
737: /*
738: * No remaining claims to this record; let MLD6 know
739: * that we are leaving the multicast group.
740: */
741: mld_stop_listening(in6m);
742:
743: /*
744: * Unlink from list.
745: */
746: LIST_REMOVE(in6m, in6m_entry);
1.27.4.2! yamt 747: if (in6m->in6m_ia != NULL) {
1.27.4.1 yamt 748: IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
1.27.4.2! yamt 749: in6m->in6m_ia = NULL;
1.27.4.1 yamt 750: }
751:
752: /*
753: * Delete all references of this multicasting group from
754: * the membership arrays
755: */
756: for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
757: struct in6_multi_mship *imm;
1.27.4.2! yamt 758: LIST_FOREACH(imm, &ia->ia6_memberships, i6mm_chain) {
1.27.4.1 yamt 759: if (imm->i6mm_maddr == in6m)
760: imm->i6mm_maddr = NULL;
761: }
762: }
763:
764: /*
765: * Notify the network driver to update its multicast
766: * reception filter.
767: */
768: memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6));
769: ifr.ifr_addr.sin6_family = AF_INET6;
770: ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
771: ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
772: (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp,
773: SIOCDELMULTI, (caddr_t)&ifr);
774: free(in6m->in6m_timer_ch, M_IPMADDR);
775: free(in6m, M_IPMADDR);
776: }
777: splx(s);
778: }
779:
780:
781: struct in6_multi_mship *
782: in6_joingroup(ifp, addr, errorp, timer)
783: struct ifnet *ifp;
784: struct in6_addr *addr;
785: int *errorp, timer;
786: {
787: struct in6_multi_mship *imm;
788:
789: imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT);
790: if (!imm) {
791: *errorp = ENOBUFS;
792: return NULL;
793: }
794:
795: memset(imm, 0, sizeof(*imm));
796: imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer);
797: if (!imm->i6mm_maddr) {
1.27.4.2! yamt 798: /* *errorp is already set */
1.27.4.1 yamt 799: free(imm, M_IPMADDR);
800: return NULL;
801: }
802: return imm;
803: }
804:
805: int
806: in6_leavegroup(imm)
807: struct in6_multi_mship *imm;
808: {
809:
810: if (imm->i6mm_maddr) {
811: in6_delmulti(imm->i6mm_maddr);
812: }
813: free(imm, M_IPMADDR);
814: return 0;
815: }
816:
817:
818: /*
819: * Multicast address kludge:
820: * If there were any multicast addresses attached to this interface address,
821: * either move them to another address on this interface, or save them until
822: * such time as this interface is reconfigured for IPv6.
823: */
824: void
825: in6_savemkludge(oia)
826: struct in6_ifaddr *oia;
827: {
828: struct in6_ifaddr *ia;
1.27.4.2! yamt 829: struct in6_multi *in6m;
1.27.4.1 yamt 830:
831: IFP_TO_IA6(oia->ia_ifp, ia);
832: if (ia) { /* there is another address */
1.27.4.2! yamt 833: KASSERT(ia != oia);
! 834: while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) {
! 835: LIST_REMOVE(in6m, in6m_entry);
1.27.4.1 yamt 836: IFAREF(&ia->ia_ifa);
1.27.4.2! yamt 837: IFAFREE(&in6m->in6m_ia->ia_ifa);
1.27.4.1 yamt 838: in6m->in6m_ia = ia;
839: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
840: }
841: } else { /* last address on this if deleted, save */
842: struct multi6_kludge *mk;
843:
1.27.4.2! yamt 844: LIST_FOREACH(mk, &in6_mk, mk_entry) {
1.27.4.1 yamt 845: if (mk->mk_ifp == oia->ia_ifp)
846: break;
847: }
848: if (mk == NULL) /* this should not happen! */
849: panic("in6_savemkludge: no kludge space");
850:
1.27.4.2! yamt 851: while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) {
! 852: LIST_REMOVE(in6m, in6m_entry);
1.27.4.1 yamt 853: IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
854: in6m->in6m_ia = NULL;
855: LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry);
856: }
857: }
858: }
859:
860: /*
861: * Continuation of multicast address hack:
862: * If there was a multicast group list previously saved for this interface,
863: * then we re-attach it to the first address configured on the i/f.
864: */
865: void
866: in6_restoremkludge(ia, ifp)
867: struct in6_ifaddr *ia;
868: struct ifnet *ifp;
869: {
870: struct multi6_kludge *mk;
1.27.4.2! yamt 871: struct in6_multi *in6m;
1.27.4.1 yamt 872:
1.27.4.2! yamt 873: LIST_FOREACH(mk, &in6_mk, mk_entry) {
! 874: if (mk->mk_ifp == ifp)
1.27.4.1 yamt 875: break;
1.27.4.2! yamt 876: }
! 877: if (mk == NULL)
! 878: return;
! 879: while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) {
! 880: LIST_REMOVE(in6m, in6m_entry);
! 881: in6m->in6m_ia = ia;
! 882: IFAREF(&ia->ia_ifa);
! 883: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
1.27.4.1 yamt 884: }
885: }
886:
887: /*
888: * Allocate space for the kludge at interface initialization time.
889: * Formerly, we dynamically allocated the space in in6_savemkludge() with
890: * malloc(M_WAITOK). However, it was wrong since the function could be called
891: * under an interrupt context (software timer on address lifetime expiration).
892: * Also, we cannot just give up allocating the strucutre, since the group
893: * membership structure is very complex and we need to keep it anyway.
894: * Of course, this function MUST NOT be called under an interrupt context.
895: * Specifically, it is expected to be called only from in6_ifattach(), though
896: * it is a global function.
897: */
898: void
899: in6_createmkludge(ifp)
900: struct ifnet *ifp;
901: {
902: struct multi6_kludge *mk;
903:
1.27.4.2! yamt 904: LIST_FOREACH(mk, &in6_mk, mk_entry) {
1.27.4.1 yamt 905: /* If we've already had one, do not allocate. */
906: if (mk->mk_ifp == ifp)
907: return;
908: }
909:
910: mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK);
911:
912: memset(mk, 0, sizeof(*mk));
913: LIST_INIT(&mk->mk_head);
914: mk->mk_ifp = ifp;
915: LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
916: }
917:
918: void
919: in6_purgemkludge(ifp)
920: struct ifnet *ifp;
921: {
922: struct multi6_kludge *mk;
1.27.4.2! yamt 923: struct in6_multi *in6m, *next;
1.27.4.1 yamt 924:
1.27.4.2! yamt 925: LIST_FOREACH(mk, &in6_mk, mk_entry) {
! 926: if (mk->mk_ifp == ifp)
! 927: break;
! 928: }
! 929: if (mk == NULL)
! 930: return;
! 931:
! 932: /* leave from all multicast groups joined */
! 933: for (in6m = LIST_FIRST(&mk->mk_head); in6m != NULL; in6m = next) {
! 934: next = LIST_NEXT(in6m, in6m_entry);
! 935: in6_delmulti(in6m);
1.27.4.1 yamt 936: }
1.27.4.2! yamt 937: LIST_REMOVE(mk, mk_entry);
! 938: free(mk, M_IPMADDR);
1.27.4.1 yamt 939: }
CVSweb <webmaster@jp.NetBSD.org>