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