Annotation of src/sys/netinet6/mld6.c, Revision 1.38
1.38 ! christos 1: /* $NetBSD: mld6.c,v 1.37 2007/03/04 06:03:27 christos 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.38 ! christos 105: __KERNEL_RCSID(0, "$NetBSD: mld6.c,v 1.37 2007/03/04 06:03:27 christos 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;
1.37 christos 184: bcopy((void *)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
1.2 itojun 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
1.38 ! christos 192: mld_starttimer(struct in6_multi *in6m)
1.31 rpaulo 193: {
194: struct timeval now;
195:
196: microtime(&now);
197: in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
198: in6m->in6m_timer_expire.tv_usec = now.tv_usec +
199: (in6m->in6m_timer % hz) * (1000000 / hz);
200: if (in6m->in6m_timer_expire.tv_usec > 1000000) {
201: in6m->in6m_timer_expire.tv_sec++;
202: in6m->in6m_timer_expire.tv_usec -= 1000000;
203: }
204:
205: /* start or restart the timer */
206: callout_reset(in6m->in6m_timer_ch, in6m->in6m_timer,
207: (void (*) __P((void *)))mld_timeo, in6m);
208: }
209:
210: static void
1.38 ! christos 211: mld_stoptimer(struct in6_multi *in6m)
1.31 rpaulo 212: {
213: if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
214: return;
215:
216: callout_stop(in6m->in6m_timer_ch);
217:
218: in6m->in6m_timer = IN6M_TIMER_UNDEF;
219: }
220:
221: static void
1.38 ! christos 222: mld_timeo(struct in6_multi *in6m)
1.31 rpaulo 223: {
224: int s = splsoftnet();
225:
226: in6m->in6m_timer = IN6M_TIMER_UNDEF;
227:
228: callout_stop(in6m->in6m_timer_ch);
229:
230: switch (in6m->in6m_state) {
231: case MLD_REPORTPENDING:
232: mld_start_listening(in6m);
233: break;
234: default:
235: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
236: break;
237: }
238:
239: splx(s);
240: }
241:
242: static u_long
1.38 ! christos 243: mld_timerresid(struct in6_multi *in6m)
1.31 rpaulo 244: {
245: struct timeval now, diff;
246:
247: microtime(&now);
248:
249: if (now.tv_sec > in6m->in6m_timer_expire.tv_sec ||
250: (now.tv_sec == in6m->in6m_timer_expire.tv_sec &&
251: now.tv_usec > in6m->in6m_timer_expire.tv_usec)) {
252: return (0);
253: }
254: diff = in6m->in6m_timer_expire;
255: diff.tv_sec -= now.tv_sec;
256: diff.tv_usec -= now.tv_usec;
257: if (diff.tv_usec < 0) {
258: diff.tv_sec--;
259: diff.tv_usec += 1000000;
260: }
261:
262: /* return the remaining time in milliseconds */
263: return (((u_long)(diff.tv_sec * 1000000 + diff.tv_usec)) / 1000);
264: }
265:
266: static void
1.38 ! christos 267: mld_start_listening(struct in6_multi *in6m)
1.2 itojun 268: {
1.29 rpaulo 269: struct in6_addr all_in6;
270:
1.2 itojun 271: /*
1.11 itojun 272: * RFC2710 page 10:
1.2 itojun 273: * The node never sends a Report or Done for the link-scope all-nodes
274: * address.
275: * MLD messages are never sent for multicast addresses whose scope is 0
276: * (reserved) or 1 (node-local).
277: */
1.29 rpaulo 278: all_in6 = in6addr_linklocal_allnodes;
279: if (in6_setscope(&all_in6, in6m->in6m_ifp, NULL)) {
280: /* XXX: this should not happen! */
281: in6m->in6m_timer = 0;
282: in6m->in6m_state = MLD_OTHERLISTENER;
283: }
284: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
1.2 itojun 285: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
1.31 rpaulo 286: in6m->in6m_timer = IN6M_TIMER_UNDEF;
1.22 itojun 287: in6m->in6m_state = MLD_OTHERLISTENER;
1.2 itojun 288: } else {
1.31 rpaulo 289: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
290: in6m->in6m_timer = arc4random() %
291: (MLD_UNSOLICITED_REPORT_INTERVAL * hz);
1.22 itojun 292: in6m->in6m_state = MLD_IREPORTEDLAST;
1.31 rpaulo 293:
294: mld_starttimer(in6m);
1.2 itojun 295: }
296: }
297:
1.31 rpaulo 298: static void
1.38 ! christos 299: mld_stop_listening(struct in6_multi *in6m)
1.2 itojun 300: {
1.29 rpaulo 301: struct in6_addr allnode, allrouter;
302:
303: allnode = in6addr_linklocal_allnodes;
304: if (in6_setscope(&allnode, in6m->in6m_ifp, NULL)) {
305: /* XXX: this should not happen! */
306: return;
307: }
308: allrouter = in6addr_linklocal_allrouters;
309: if (in6_setscope(&allrouter, in6m->in6m_ifp, NULL)) {
310: /* XXX impossible */
311: return;
312: }
1.2 itojun 313:
1.22 itojun 314: if (in6m->in6m_state == MLD_IREPORTEDLAST &&
1.29 rpaulo 315: (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode)) &&
316: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
317: IPV6_ADDR_SCOPE_INTFACELOCAL) {
1.31 rpaulo 318: mld_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
1.29 rpaulo 319: }
1.2 itojun 320: }
321:
322: void
1.38 ! christos 323: mld_input(struct mbuf *m, int off)
1.2 itojun 324: {
325: struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
1.22 itojun 326: struct mld_hdr *mldh;
1.2 itojun 327: struct ifnet *ifp = m->m_pkthdr.rcvif;
1.31 rpaulo 328: struct in6_multi *in6m = NULL;
1.29 rpaulo 329: struct in6_addr mld_addr, all_in6;
1.2 itojun 330: struct in6_ifaddr *ia;
1.31 rpaulo 331: int timer = 0; /* timer value in the MLD query header */
1.2 itojun 332:
1.22 itojun 333: IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
1.13 itojun 334: if (mldh == NULL) {
335: icmp6stat.icp6s_tooshort++;
336: return;
337: }
338:
1.2 itojun 339: /* source address validation */
1.13 itojun 340: ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
1.2 itojun 341: if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
1.31 rpaulo 342: /*
343: * RFC3590 allows the IPv6 unspecified address as the source
344: * address of MLD report and done messages. However, as this
345: * same document says, this special rule is for snooping
346: * switches and the RFC requires routers to discard MLD packets
347: * with the unspecified source address. The RFC only talks
348: * about hosts receiving an MLD query or report in Security
349: * Considerations, but this is probably the correct intention.
350: * RFC3590 does not talk about other cases than link-local and
351: * the unspecified source addresses, but we believe the same
352: * rule should be applied.
353: * As a result, we only allow link-local addresses as the
354: * source address; otherwise, simply discard the packet.
355: */
1.18 itojun 356: #if 0
1.31 rpaulo 357: /*
358: * XXX: do not log in an input path to avoid log flooding,
359: * though RFC3590 says "SHOULD log" if the source of a query
360: * is the unspecified address.
361: */
362: log(LOG_INFO,
1.22 itojun 363: "mld_input: src %s is not link-local (grp=%s)\n",
1.31 rpaulo 364: ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&mldh->mld_addr));
1.18 itojun 365: #endif
1.11 itojun 366: m_freem(m);
1.2 itojun 367: return;
368: }
369:
370: /*
1.29 rpaulo 371: * make a copy for local work (in6_setscope() may modify the 1st arg)
372: */
373: mld_addr = mldh->mld_addr;
374: if (in6_setscope(&mld_addr, ifp, NULL)) {
375: /* XXX: this should not happen! */
376: m_free(m);
377: return;
378: }
379:
380: /*
1.31 rpaulo 381: * In the MLD specification, there are 3 states and a flag.
1.2 itojun 382: *
383: * In Non-Listener state, we simply don't have a membership record.
384: * In Delaying Listener state, our timer is running (in6m->in6m_timer)
1.31 rpaulo 385: * In Idle Listener state, our timer is not running
386: * (in6m->in6m_timer==IN6M_TIMER_UNDEF)
1.2 itojun 387: *
1.22 itojun 388: * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
389: * we have heard a report from another member, or MLD_IREPORTEDLAST
1.2 itojun 390: * if we sent the last report.
391: */
1.22 itojun 392: switch (mldh->mld_type) {
393: case MLD_LISTENER_QUERY:
1.7 itojun 394: if (ifp->if_flags & IFF_LOOPBACK)
395: break;
396:
1.29 rpaulo 397: if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
398: !IN6_IS_ADDR_MULTICAST(&mld_addr))
1.7 itojun 399: break; /* print error or log stat? */
1.29 rpaulo 400:
401: all_in6 = in6addr_linklocal_allnodes;
402: if (in6_setscope(&all_in6, ifp, NULL)) {
403: /* XXX: this should not happen! */
404: break;
405: }
1.2 itojun 406:
1.7 itojun 407: /*
1.11 itojun 408: * - Start the timers in all of our membership records
409: * that the query applies to for the interface on
410: * which the query arrived excl. those that belong
411: * to the "all-nodes" group (ff02::1).
412: * - Restart any timer that is already running but has
1.30 rpaulo 413: * a value longer than the requested timeout.
1.11 itojun 414: * - Use the value specified in the query message as
415: * the maximum timeout.
416: */
1.31 rpaulo 417: timer = ntohs(mldh->mld_maxdelay);
418:
1.7 itojun 419: IFP_TO_IA6(ifp, ia);
420: if (ia == NULL)
421: break;
1.2 itojun 422:
1.35 dyoung 423: LIST_FOREACH(in6m, &ia->ia6_multiaddrs, in6m_entry) {
1.29 rpaulo 424: if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
1.7 itojun 425: IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
426: IPV6_ADDR_SCOPE_LINKLOCAL)
427: continue;
1.2 itojun 428:
1.31 rpaulo 429: if (in6m->in6m_state == MLD_REPORTPENDING)
430: continue; /* we are not yet ready */
431:
432: if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
433: !IN6_ARE_ADDR_EQUAL(&mld_addr, &in6m->in6m_addr))
434: continue;
435:
436: if (timer == 0) {
437: /* send a report immediately */
438: mld_stoptimer(in6m);
439: mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
440: in6m->in6m_state = MLD_IREPORTEDLAST;
441: } else if (in6m->in6m_timer == IN6M_TIMER_UNDEF ||
442: mld_timerresid(in6m) > (u_long)timer) {
443: in6m->in6m_timer = arc4random() %
444: (int)(((long)timer * hz) / 1000);
445: mld_starttimer(in6m);
1.7 itojun 446: }
447: }
1.29 rpaulo 448: break;
1.2 itojun 449:
1.22 itojun 450: case MLD_LISTENER_REPORT:
1.2 itojun 451: /*
1.11 itojun 452: * For fast leave to work, we have to know that we are the
453: * last person to send a report for this group. Reports
454: * can potentially get looped back if we are a multicast
455: * router, so discard reports sourced by me.
456: * Note that it is impossible to check IFF_LOOPBACK flag of
457: * ifp for this purpose, since ip6_mloopback pass the physical
458: * interface to looutput.
459: */
1.7 itojun 460: if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
461: break;
462:
1.22 itojun 463: if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
1.7 itojun 464: break;
465:
466: /*
1.11 itojun 467: * If we belong to the group being reported, stop
468: * our timer for that group.
469: */
1.29 rpaulo 470: IN6_LOOKUP_MULTI(mld_addr, ifp, in6m);
1.7 itojun 471: if (in6m) {
1.31 rpaulo 472: mld_stoptimer(in6m); /* transit to idle state */
1.22 itojun 473: in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
1.7 itojun 474: }
475: break;
476: default: /* this is impossible */
1.18 itojun 477: #if 0
1.19 itojun 478: /*
479: * this case should be impossible because of filtering in
480: * icmp6_input(). But we explicitly disabled this part
481: * just in case.
482: */
1.31 rpaulo 483: log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
1.18 itojun 484: #endif
1.7 itojun 485: break;
1.2 itojun 486: }
1.11 itojun 487:
488: m_freem(m);
1.2 itojun 489: }
490:
491: static void
1.38 ! christos 492: mld_sendpkt(struct in6_multi *in6m, int type,
! 493: const struct in6_addr *dst)
1.2 itojun 494: {
1.31 rpaulo 495: struct mbuf *mh;
1.22 itojun 496: struct mld_hdr *mldh;
1.31 rpaulo 497: struct ip6_hdr *ip6 = NULL;
1.2 itojun 498: struct ip6_moptions im6o;
1.31 rpaulo 499: struct in6_ifaddr *ia = NULL;
1.2 itojun 500: struct ifnet *ifp = in6m->in6m_ifp;
1.19 itojun 501: int ignflags;
1.2 itojun 502:
503: /*
504: * At first, find a link local address on the outgoing interface
505: * to use as the source address of the MLD packet.
1.19 itojun 506: * We do not reject tentative addresses for MLD report to deal with
507: * the case where we first join a link-local address.
1.2 itojun 508: */
1.19 itojun 509: ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
510: if ((ia = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL)
1.2 itojun 511: return;
1.19 itojun 512: if ((ia->ia6_flags & IN6_IFF_TENTATIVE))
513: ia = NULL;
1.2 itojun 514:
1.31 rpaulo 515: /* Allocate two mbufs to store IPv6 header and MLD header */
516: mldh = mld_allocbuf(&mh, sizeof(struct mld_hdr), in6m, type);
517: if (mldh == NULL)
1.2 itojun 518: return;
519:
1.31 rpaulo 520: /* fill src/dst here */
521: ip6 = mtod(mh, struct ip6_hdr *);
522: ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
523: ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
1.2 itojun 524:
1.22 itojun 525: mldh->mld_addr = in6m->in6m_addr;
1.29 rpaulo 526: in6_clearscope(&mldh->mld_addr); /* XXX */
1.22 itojun 527: mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
528: sizeof(struct mld_hdr));
1.2 itojun 529:
530: /* construct multicast option */
1.31 rpaulo 531: memset(&im6o, 0, sizeof(im6o));
1.2 itojun 532: im6o.im6o_multicast_ifp = ifp;
533: im6o.im6o_multicast_hlim = 1;
534:
535: /*
536: * Request loopback of the report if we are acting as a multicast
537: * router, so that the process-level routing daemon can hear it.
538: */
539: im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
540:
541: /* increment output statictics */
542: icmp6stat.icp6s_outhist[type]++;
1.15 itojun 543: icmp6_ifstat_inc(ifp, ifs6_out_msg);
1.17 itojun 544: switch (type) {
1.22 itojun 545: case MLD_LISTENER_QUERY:
1.15 itojun 546: icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
547: break;
1.22 itojun 548: case MLD_LISTENER_REPORT:
1.15 itojun 549: icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
550: break;
1.22 itojun 551: case MLD_LISTENER_DONE:
1.15 itojun 552: icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
553: break;
1.7 itojun 554: }
1.19 itojun 555:
1.27 perry 556: ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC,
1.25 itojun 557: &im6o, (struct socket *)NULL, NULL);
1.2 itojun 558: }
1.31 rpaulo 559:
560: static struct mld_hdr *
1.34 christos 561: mld_allocbuf(struct mbuf **mh, int len, struct in6_multi *in6m,
1.33 christos 562: int type)
1.31 rpaulo 563: {
564: struct mbuf *md;
565: struct mld_hdr *mldh;
566: struct ip6_hdr *ip6;
567:
568: /*
569: * Allocate mbufs to store ip6 header and MLD header.
570: * We allocate 2 mbufs and make chain in advance because
571: * it is more convenient when inserting the hop-by-hop option later.
572: */
573: MGETHDR(*mh, M_DONTWAIT, MT_HEADER);
574: if (*mh == NULL)
575: return NULL;
576: MGET(md, M_DONTWAIT, MT_DATA);
577: if (md == NULL) {
578: m_free(*mh);
579: *mh = NULL;
580: return NULL;
581: }
582: (*mh)->m_next = md;
583: md->m_next = NULL;
584:
585: (*mh)->m_pkthdr.rcvif = NULL;
586: (*mh)->m_pkthdr.len = sizeof(struct ip6_hdr) + len;
587: (*mh)->m_len = sizeof(struct ip6_hdr);
588: MH_ALIGN(*mh, sizeof(struct ip6_hdr));
589:
590: /* fill in the ip6 header */
591: ip6 = mtod(*mh, struct ip6_hdr *);
592: memset(ip6, 0, sizeof(*ip6));
593: ip6->ip6_flow = 0;
594: ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
595: ip6->ip6_vfc |= IPV6_VERSION;
596: /* ip6_plen will be set later */
597: ip6->ip6_nxt = IPPROTO_ICMPV6;
598: /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
599: /* ip6_src/dst will be set by mld_sendpkt() or mld_sendbuf() */
600:
601: /* fill in the MLD header as much as possible */
602: md->m_len = len;
603: mldh = mtod(md, struct mld_hdr *);
604: memset(mldh, 0, len);
605: mldh->mld_type = type;
606: return mldh;
607: }
608:
609: /*
610: * Add an address to the list of IP6 multicast addresses for a given interface.
611: */
612: struct in6_multi *
1.38 ! christos 613: in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp,
! 614: int *errorp, int timer)
1.31 rpaulo 615: {
616: struct in6_ifaddr *ia;
617: struct in6_ifreq ifr;
618: struct in6_multi *in6m;
619: int s = splsoftnet();
620:
621: *errorp = 0;
622:
623: /*
624: * See if address already in list.
625: */
626: IN6_LOOKUP_MULTI(*maddr6, ifp, in6m);
627: if (in6m != NULL) {
628: /*
629: * Found it; just increment the refrence count.
630: */
631: in6m->in6m_refcount++;
632: } else {
633: /*
634: * New address; allocate a new multicast record
635: * and link it into the interface's multicast list.
636: */
637: in6m = (struct in6_multi *)
638: malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
639: if (in6m == NULL) {
640: splx(s);
641: *errorp = ENOBUFS;
642: return (NULL);
643: }
644:
645: memset(in6m, 0, sizeof(*in6m));
646: in6m->in6m_addr = *maddr6;
647: in6m->in6m_ifp = ifp;
648: in6m->in6m_refcount = 1;
649: in6m->in6m_timer = IN6M_TIMER_UNDEF;
650: in6m->in6m_timer_ch =
651: malloc(sizeof(*in6m->in6m_timer_ch), M_IPMADDR, M_NOWAIT);
652: if (in6m->in6m_timer_ch == NULL) {
653: free(in6m, M_IPMADDR);
654: splx(s);
655: return (NULL);
656: }
657: IFP_TO_IA6(ifp, ia);
658: if (ia == NULL) {
1.36 dyoung 659: /* leaks in6m_timer_ch */
1.31 rpaulo 660: free(in6m, M_IPMADDR);
661: splx(s);
662: *errorp = EADDRNOTAVAIL; /* appropriate? */
663: return (NULL);
664: }
665: in6m->in6m_ia = ia;
666: IFAREF(&ia->ia_ifa); /* gain a reference */
667: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
668:
669: /*
670: * Ask the network driver to update its multicast reception
671: * filter appropriately for the new address.
672: */
673: memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6));
674: ifr.ifr_addr.sin6_family = AF_INET6;
675: ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
676: ifr.ifr_addr.sin6_addr = *maddr6;
677: if (ifp->if_ioctl == NULL)
678: *errorp = ENXIO; /* XXX: appropriate? */
679: else
680: *errorp = (*ifp->if_ioctl)(ifp, SIOCADDMULTI,
1.37 christos 681: (void *)&ifr);
1.31 rpaulo 682: if (*errorp) {
683: LIST_REMOVE(in6m, in6m_entry);
1.36 dyoung 684: /* leaks in6m_timer_ch */
1.31 rpaulo 685: free(in6m, M_IPMADDR);
686: IFAFREE(&ia->ia_ifa);
687: splx(s);
688: return (NULL);
689: }
690:
691: callout_init(in6m->in6m_timer_ch);
1.32 rpaulo 692: in6m->in6m_timer = timer;
1.31 rpaulo 693: if (in6m->in6m_timer > 0) {
694: in6m->in6m_state = MLD_REPORTPENDING;
695: mld_starttimer(in6m);
696:
697: splx(s);
698: return (in6m);
699: }
700:
701: /*
702: * Let MLD6 know that we have joined a new IP6 multicast
703: * group.
704: */
705: mld_start_listening(in6m);
706: }
707: splx(s);
708: return (in6m);
709: }
710:
711: /*
712: * Delete a multicast address record.
713: */
714: void
1.38 ! christos 715: in6_delmulti(struct in6_multi *in6m)
1.31 rpaulo 716: {
717: struct in6_ifreq ifr;
718: struct in6_ifaddr *ia;
719: int s = splsoftnet();
720:
721: mld_stoptimer(in6m);
722:
723: if (--in6m->in6m_refcount == 0) {
724: /*
725: * No remaining claims to this record; let MLD6 know
726: * that we are leaving the multicast group.
727: */
728: mld_stop_listening(in6m);
729:
730: /*
731: * Unlink from list.
732: */
733: LIST_REMOVE(in6m, in6m_entry);
1.35 dyoung 734: if (in6m->in6m_ia != NULL) {
1.31 rpaulo 735: IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
1.35 dyoung 736: in6m->in6m_ia = NULL;
1.31 rpaulo 737: }
738:
739: /*
740: * Delete all references of this multicasting group from
741: * the membership arrays
742: */
743: for (ia = in6_ifaddr; ia; ia = ia->ia_next) {
744: struct in6_multi_mship *imm;
1.35 dyoung 745: LIST_FOREACH(imm, &ia->ia6_memberships, i6mm_chain) {
1.31 rpaulo 746: if (imm->i6mm_maddr == in6m)
747: imm->i6mm_maddr = NULL;
748: }
749: }
750:
751: /*
752: * Notify the network driver to update its multicast
753: * reception filter.
754: */
755: memset(&ifr.ifr_addr, 0, sizeof(struct sockaddr_in6));
756: ifr.ifr_addr.sin6_family = AF_INET6;
757: ifr.ifr_addr.sin6_len = sizeof(struct sockaddr_in6);
758: ifr.ifr_addr.sin6_addr = in6m->in6m_addr;
759: (*in6m->in6m_ifp->if_ioctl)(in6m->in6m_ifp,
1.37 christos 760: SIOCDELMULTI, (void *)&ifr);
1.31 rpaulo 761: free(in6m->in6m_timer_ch, M_IPMADDR);
762: free(in6m, M_IPMADDR);
763: }
764: splx(s);
765: }
766:
767:
768: struct in6_multi_mship *
1.38 ! christos 769: in6_joingroup(struct ifnet *ifp, struct in6_addr *addr,
! 770: int *errorp, int timer)
1.31 rpaulo 771: {
772: struct in6_multi_mship *imm;
773:
774: imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT);
775: if (!imm) {
776: *errorp = ENOBUFS;
777: return NULL;
778: }
779:
780: memset(imm, 0, sizeof(*imm));
1.32 rpaulo 781: imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer);
1.31 rpaulo 782: if (!imm->i6mm_maddr) {
1.36 dyoung 783: /* *errorp is already set */
1.31 rpaulo 784: free(imm, M_IPMADDR);
785: return NULL;
786: }
787: return imm;
788: }
789:
790: int
1.38 ! christos 791: in6_leavegroup(struct in6_multi_mship *imm)
1.31 rpaulo 792: {
793:
794: if (imm->i6mm_maddr) {
795: in6_delmulti(imm->i6mm_maddr);
796: }
797: free(imm, M_IPMADDR);
798: return 0;
799: }
800:
801:
802: /*
803: * Multicast address kludge:
804: * If there were any multicast addresses attached to this interface address,
805: * either move them to another address on this interface, or save them until
806: * such time as this interface is reconfigured for IPv6.
807: */
808: void
1.38 ! christos 809: in6_savemkludge(struct in6_ifaddr *oia)
1.31 rpaulo 810: {
811: struct in6_ifaddr *ia;
1.36 dyoung 812: struct in6_multi *in6m;
1.31 rpaulo 813:
814: IFP_TO_IA6(oia->ia_ifp, ia);
815: if (ia) { /* there is another address */
1.36 dyoung 816: KASSERT(ia != oia);
817: while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) {
818: LIST_REMOVE(in6m, in6m_entry);
819: IFAREF(&ia->ia_ifa);
1.31 rpaulo 820: IFAFREE(&in6m->in6m_ia->ia_ifa);
821: in6m->in6m_ia = ia;
822: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
823: }
824: } else { /* last address on this if deleted, save */
825: struct multi6_kludge *mk;
826:
1.35 dyoung 827: LIST_FOREACH(mk, &in6_mk, mk_entry) {
1.31 rpaulo 828: if (mk->mk_ifp == oia->ia_ifp)
829: break;
830: }
831: if (mk == NULL) /* this should not happen! */
832: panic("in6_savemkludge: no kludge space");
833:
1.36 dyoung 834: while ((in6m = LIST_FIRST(&oia->ia6_multiaddrs)) != NULL) {
835: LIST_REMOVE(in6m, in6m_entry);
1.31 rpaulo 836: IFAFREE(&in6m->in6m_ia->ia_ifa); /* release reference */
837: in6m->in6m_ia = NULL;
838: LIST_INSERT_HEAD(&mk->mk_head, in6m, in6m_entry);
839: }
840: }
841: }
842:
843: /*
844: * Continuation of multicast address hack:
845: * If there was a multicast group list previously saved for this interface,
846: * then we re-attach it to the first address configured on the i/f.
847: */
848: void
1.38 ! christos 849: in6_restoremkludge(struct in6_ifaddr *ia, struct ifnet *ifp)
1.31 rpaulo 850: {
851: struct multi6_kludge *mk;
1.36 dyoung 852: struct in6_multi *in6m;
1.31 rpaulo 853:
1.35 dyoung 854: LIST_FOREACH(mk, &in6_mk, mk_entry) {
855: if (mk->mk_ifp == ifp)
1.31 rpaulo 856: break;
857: }
1.35 dyoung 858: if (mk == NULL)
859: return;
1.36 dyoung 860: while ((in6m = LIST_FIRST(&mk->mk_head)) != NULL) {
861: LIST_REMOVE(in6m, in6m_entry);
1.35 dyoung 862: in6m->in6m_ia = ia;
863: IFAREF(&ia->ia_ifa);
864: LIST_INSERT_HEAD(&ia->ia6_multiaddrs, in6m, in6m_entry);
865: }
1.31 rpaulo 866: }
867:
868: /*
869: * Allocate space for the kludge at interface initialization time.
870: * Formerly, we dynamically allocated the space in in6_savemkludge() with
871: * malloc(M_WAITOK). However, it was wrong since the function could be called
872: * under an interrupt context (software timer on address lifetime expiration).
873: * Also, we cannot just give up allocating the strucutre, since the group
874: * membership structure is very complex and we need to keep it anyway.
875: * Of course, this function MUST NOT be called under an interrupt context.
876: * Specifically, it is expected to be called only from in6_ifattach(), though
877: * it is a global function.
878: */
879: void
1.38 ! christos 880: in6_createmkludge(struct ifnet *ifp)
1.31 rpaulo 881: {
882: struct multi6_kludge *mk;
883:
1.35 dyoung 884: LIST_FOREACH(mk, &in6_mk, mk_entry) {
1.31 rpaulo 885: /* If we've already had one, do not allocate. */
886: if (mk->mk_ifp == ifp)
887: return;
888: }
889:
890: mk = malloc(sizeof(*mk), M_IPMADDR, M_WAITOK);
891:
892: memset(mk, 0, sizeof(*mk));
893: LIST_INIT(&mk->mk_head);
894: mk->mk_ifp = ifp;
895: LIST_INSERT_HEAD(&in6_mk, mk, mk_entry);
896: }
897:
898: void
1.38 ! christos 899: in6_purgemkludge(struct ifnet *ifp)
1.31 rpaulo 900: {
901: struct multi6_kludge *mk;
1.36 dyoung 902: struct in6_multi *in6m, *next;
1.31 rpaulo 903:
1.35 dyoung 904: LIST_FOREACH(mk, &in6_mk, mk_entry) {
905: if (mk->mk_ifp == ifp)
906: break;
907: }
908: if (mk == NULL)
909: return;
910:
911: /* leave from all multicast groups joined */
1.36 dyoung 912: for (in6m = LIST_FIRST(&mk->mk_head); in6m != NULL; in6m = next) {
913: next = LIST_NEXT(in6m, in6m_entry);
1.35 dyoung 914: in6_delmulti(in6m);
1.31 rpaulo 915: }
1.35 dyoung 916: LIST_REMOVE(mk, mk_entry);
917: free(mk, M_IPMADDR);
1.31 rpaulo 918: }
CVSweb <webmaster@jp.NetBSD.org>