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