mld6.c revision 1.83 1 /* $NetBSD: mld6.c,v 1.83 2017/02/23 07:57:10 ozaki-r Exp $ */
2 /* $KAME: mld6.c,v 1.25 2001/01/16 14:14:18 itojun Exp $ */
3
4 /*
5 * Copyright (C) 1998 WIDE Project.
6 * All rights reserved.
7 *
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.
19 *
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.
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.
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 */
103
104 #include <sys/cdefs.h>
105 __KERNEL_RCSID(0, "$NetBSD: mld6.c,v 1.83 2017/02/23 07:57:10 ozaki-r Exp $");
106
107 #ifdef _KERNEL_OPT
108 #include "opt_inet.h"
109 #include "opt_net_mpsafe.h"
110 #endif
111
112 #include <sys/param.h>
113 #include <sys/systm.h>
114 #include <sys/mbuf.h>
115 #include <sys/socket.h>
116 #include <sys/socketvar.h>
117 #include <sys/syslog.h>
118 #include <sys/sysctl.h>
119 #include <sys/kernel.h>
120 #include <sys/callout.h>
121 #include <sys/cprng.h>
122
123 #include <net/if.h>
124
125 #include <netinet/in.h>
126 #include <netinet/in_var.h>
127 #include <netinet6/in6_var.h>
128 #include <netinet/ip6.h>
129 #include <netinet6/ip6_var.h>
130 #include <netinet6/scope6_var.h>
131 #include <netinet/icmp6.h>
132 #include <netinet6/icmp6_private.h>
133 #include <netinet6/mld6_var.h>
134
135 #include <net/net_osdep.h>
136
137
138 /*
139 * Protocol constants
140 */
141
142 /*
143 * time between repetitions of a node's initial report of interest in a
144 * multicast address(in seconds)
145 */
146 #define MLD_UNSOLICITED_REPORT_INTERVAL 10
147
148 static struct ip6_pktopts ip6_opts;
149
150 static void mld_start_listening(struct in6_multi *);
151 static void mld_stop_listening(struct in6_multi *);
152
153 static struct mld_hdr * mld_allocbuf(struct mbuf **, int, struct in6_multi *,
154 int);
155 static void mld_sendpkt(struct in6_multi *, int, const struct in6_addr *);
156 static void mld_starttimer(struct in6_multi *);
157 static void mld_stoptimer(struct in6_multi *);
158 static u_long mld_timerresid(struct in6_multi *);
159
160 void
161 mld_init(void)
162 {
163 static u_int8_t hbh_buf[8];
164 struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
165 u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
166
167 /* ip6h_nxt will be fill in later */
168 hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
169
170 /* XXX: grotty hard coding... */
171 hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
172 hbh_buf[3] = 0;
173 hbh_buf[4] = IP6OPT_RTALERT;
174 hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
175 memcpy(&hbh_buf[6], (void *)&rtalert_code, sizeof(u_int16_t));
176
177 ip6_opts.ip6po_hbh = hbh;
178 /* We will specify the hoplimit by a multicast option. */
179 ip6_opts.ip6po_hlim = -1;
180 ip6_opts.ip6po_prefer_tempaddr = IP6PO_TEMPADDR_NOTPREFER;
181 }
182
183 static void
184 mld_starttimer(struct in6_multi *in6m)
185 {
186 struct timeval now;
187
188 KASSERT(in6m->in6m_timer != IN6M_TIMER_UNDEF);
189
190 microtime(&now);
191 in6m->in6m_timer_expire.tv_sec = now.tv_sec + in6m->in6m_timer / hz;
192 in6m->in6m_timer_expire.tv_usec = now.tv_usec +
193 (in6m->in6m_timer % hz) * (1000000 / hz);
194 if (in6m->in6m_timer_expire.tv_usec > 1000000) {
195 in6m->in6m_timer_expire.tv_sec++;
196 in6m->in6m_timer_expire.tv_usec -= 1000000;
197 }
198
199 /* start or restart the timer */
200 callout_schedule(&in6m->in6m_timer_ch, in6m->in6m_timer);
201 }
202
203 static void
204 mld_stoptimer(struct in6_multi *in6m)
205 {
206 if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
207 return;
208
209 callout_stop(&in6m->in6m_timer_ch);
210
211 in6m->in6m_timer = IN6M_TIMER_UNDEF;
212 }
213
214 static void
215 mld_timeo(void *arg)
216 {
217 struct in6_multi *in6m = arg;
218
219 /* XXX NOMPSAFE still need softnet_lock */
220 mutex_enter(softnet_lock);
221 KERNEL_LOCK(1, NULL);
222
223 if (in6m->in6m_timer == IN6M_TIMER_UNDEF)
224 goto out;
225
226 in6m->in6m_timer = IN6M_TIMER_UNDEF;
227
228 switch (in6m->in6m_state) {
229 case MLD_REPORTPENDING:
230 mld_start_listening(in6m);
231 break;
232 default:
233 mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
234 break;
235 }
236
237 out:
238 KERNEL_UNLOCK_ONE(NULL);
239 mutex_exit(softnet_lock);
240 }
241
242 static u_long
243 mld_timerresid(struct in6_multi *in6m)
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 diff.tv_sec * 1000 + diff.tv_usec / 1000;
264 }
265
266 static void
267 mld_start_listening(struct in6_multi *in6m)
268 {
269 struct in6_addr all_in6;
270
271 /*
272 * RFC2710 page 10:
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 */
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) ||
285 IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
286 in6m->in6m_timer = IN6M_TIMER_UNDEF;
287 in6m->in6m_state = MLD_OTHERLISTENER;
288 } else {
289 mld_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
290 in6m->in6m_timer = cprng_fast32() %
291 (MLD_UNSOLICITED_REPORT_INTERVAL * hz);
292 in6m->in6m_state = MLD_IREPORTEDLAST;
293
294 mld_starttimer(in6m);
295 }
296 }
297
298 static void
299 mld_stop_listening(struct in6_multi *in6m)
300 {
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 }
313
314 if (in6m->in6m_state == MLD_IREPORTEDLAST &&
315 (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &allnode)) &&
316 IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
317 IPV6_ADDR_SCOPE_INTFACELOCAL) {
318 mld_sendpkt(in6m, MLD_LISTENER_DONE, &allrouter);
319 }
320 }
321
322 void
323 mld_input(struct mbuf *m, int off)
324 {
325 struct ip6_hdr *ip6;
326 struct mld_hdr *mldh;
327 struct ifnet *ifp;
328 struct in6_multi *in6m = NULL;
329 struct in6_addr mld_addr, all_in6;
330 u_long timer = 0; /* timer value in the MLD query header */
331 struct psref psref;
332
333 ifp = m_get_rcvif_psref(m, &psref);
334 if (__predict_false(ifp == NULL))
335 goto out;
336 IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
337 if (mldh == NULL) {
338 ICMP6_STATINC(ICMP6_STAT_TOOSHORT);
339 goto out_nodrop;
340 }
341
342 /* source address validation */
343 ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
344 if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
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 */
359 #if 0
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 char ip6bufs[INET6_ADDRSTRLEN];
366 char ip6bufm[INET6_ADDRSTRLEN];
367 log(LOG_INFO,
368 "mld_input: src %s is not link-local (grp=%s)\n",
369 IN6_PRINT(ip6bufs,&ip6->ip6_src),
370 IN6_PRINT(ip6bufm, &mldh->mld_addr));
371 #endif
372 goto out;
373 }
374
375 /*
376 * make a copy for local work (in6_setscope() may modify the 1st arg)
377 */
378 mld_addr = mldh->mld_addr;
379 if (in6_setscope(&mld_addr, ifp, NULL)) {
380 /* XXX: this should not happen! */
381 goto out;
382 }
383
384 /*
385 * In the MLD specification, there are 3 states and a flag.
386 *
387 * In Non-Listener state, we simply don't have a membership record.
388 * In Delaying Listener state, our timer is running (in6m->in6m_timer)
389 * In Idle Listener state, our timer is not running
390 * (in6m->in6m_timer==IN6M_TIMER_UNDEF)
391 *
392 * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
393 * we have heard a report from another member, or MLD_IREPORTEDLAST
394 * if we sent the last report.
395 */
396 switch (mldh->mld_type) {
397 case MLD_LISTENER_QUERY: {
398 if (ifp->if_flags & IFF_LOOPBACK)
399 break;
400
401 if (!IN6_IS_ADDR_UNSPECIFIED(&mld_addr) &&
402 !IN6_IS_ADDR_MULTICAST(&mld_addr))
403 break; /* print error or log stat? */
404
405 all_in6 = in6addr_linklocal_allnodes;
406 if (in6_setscope(&all_in6, ifp, NULL)) {
407 /* XXX: this should not happen! */
408 break;
409 }
410
411 /*
412 * - Start the timers in all of our membership records
413 * that the query applies to for the interface on
414 * which the query arrived excl. those that belong
415 * to the "all-nodes" group (ff02::1).
416 * - Restart any timer that is already running but has
417 * a value longer than the requested timeout.
418 * - Use the value specified in the query message as
419 * the maximum timeout.
420 */
421 timer = ntohs(mldh->mld_maxdelay);
422
423 LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
424 if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_in6) ||
425 IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
426 IPV6_ADDR_SCOPE_LINKLOCAL)
427 continue;
428
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) > timer) {
443 in6m->in6m_timer =
444 1 + (cprng_fast32() % timer) * hz / 1000;
445 mld_starttimer(in6m);
446 }
447 }
448 break;
449 }
450
451 case MLD_LISTENER_REPORT:
452 /*
453 * For fast leave to work, we have to know that we are the
454 * last person to send a report for this group. Reports
455 * can potentially get looped back if we are a multicast
456 * router, so discard reports sourced by me.
457 * Note that it is impossible to check IFF_LOOPBACK flag of
458 * ifp for this purpose, since ip6_mloopback pass the physical
459 * interface to looutput.
460 */
461 if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
462 break;
463
464 if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
465 break;
466
467 /*
468 * If we belong to the group being reported, stop
469 * our timer for that group.
470 */
471 in6m = in6_lookup_multi(&mld_addr, ifp);
472 if (in6m) {
473 mld_stoptimer(in6m); /* transit to idle state */
474 in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
475 }
476 break;
477 default: /* this is impossible */
478 #if 0
479 /*
480 * this case should be impossible because of filtering in
481 * icmp6_input(). But we explicitly disabled this part
482 * just in case.
483 */
484 log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
485 #endif
486 break;
487 }
488
489 out:
490 m_freem(m);
491 out_nodrop:
492 m_put_rcvif_psref(ifp, &psref);
493 }
494
495 static void
496 mld_sendpkt(struct in6_multi *in6m, int type,
497 const struct in6_addr *dst)
498 {
499 struct mbuf *mh;
500 struct mld_hdr *mldh;
501 struct ip6_hdr *ip6 = NULL;
502 struct ip6_moptions im6o;
503 struct in6_ifaddr *ia = NULL;
504 struct ifnet *ifp = in6m->in6m_ifp;
505 int ignflags;
506 struct psref psref;
507 int bound;
508
509 /*
510 * At first, find a link local address on the outgoing interface
511 * to use as the source address of the MLD packet.
512 * We do not reject tentative addresses for MLD report to deal with
513 * the case where we first join a link-local address.
514 */
515 ignflags = (IN6_IFF_NOTREADY|IN6_IFF_ANYCAST) & ~IN6_IFF_TENTATIVE;
516 bound = curlwp_bind();
517 ia = in6ifa_ifpforlinklocal_psref(ifp, ignflags, &psref);
518 if (ia == NULL) {
519 curlwp_bindx(bound);
520 return;
521 }
522 if ((ia->ia6_flags & IN6_IFF_TENTATIVE)) {
523 ia6_release(ia, &psref);
524 ia = NULL;
525 }
526
527 /* Allocate two mbufs to store IPv6 header and MLD header */
528 mldh = mld_allocbuf(&mh, sizeof(struct mld_hdr), in6m, type);
529 if (mldh == NULL) {
530 ia6_release(ia, &psref);
531 curlwp_bindx(bound);
532 return;
533 }
534
535 /* fill src/dst here */
536 ip6 = mtod(mh, struct ip6_hdr *);
537 ip6->ip6_src = ia ? ia->ia_addr.sin6_addr : in6addr_any;
538 ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
539 ia6_release(ia, &psref);
540 curlwp_bindx(bound);
541
542 mldh->mld_addr = in6m->in6m_addr;
543 in6_clearscope(&mldh->mld_addr); /* XXX */
544 mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
545 sizeof(struct mld_hdr));
546
547 /* construct multicast option */
548 memset(&im6o, 0, sizeof(im6o));
549 im6o.im6o_multicast_if_index = if_get_index(ifp);
550 im6o.im6o_multicast_hlim = 1;
551
552 /*
553 * Request loopback of the report if we are acting as a multicast
554 * router, so that the process-level routing daemon can hear it.
555 */
556 im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
557
558 /* increment output statictics */
559 ICMP6_STATINC(ICMP6_STAT_OUTHIST + type);
560 icmp6_ifstat_inc(ifp, ifs6_out_msg);
561 switch (type) {
562 case MLD_LISTENER_QUERY:
563 icmp6_ifstat_inc(ifp, ifs6_out_mldquery);
564 break;
565 case MLD_LISTENER_REPORT:
566 icmp6_ifstat_inc(ifp, ifs6_out_mldreport);
567 break;
568 case MLD_LISTENER_DONE:
569 icmp6_ifstat_inc(ifp, ifs6_out_mlddone);
570 break;
571 }
572
573 ip6_output(mh, &ip6_opts, NULL, ia ? 0 : IPV6_UNSPECSRC,
574 &im6o, NULL, NULL);
575 }
576
577 static struct mld_hdr *
578 mld_allocbuf(struct mbuf **mh, int len, struct in6_multi *in6m,
579 int type)
580 {
581 struct mbuf *md;
582 struct mld_hdr *mldh;
583 struct ip6_hdr *ip6;
584
585 /*
586 * Allocate mbufs to store ip6 header and MLD header.
587 * We allocate 2 mbufs and make chain in advance because
588 * it is more convenient when inserting the hop-by-hop option later.
589 */
590 MGETHDR(*mh, M_DONTWAIT, MT_HEADER);
591 if (*mh == NULL)
592 return NULL;
593 MGET(md, M_DONTWAIT, MT_DATA);
594 if (md == NULL) {
595 m_free(*mh);
596 *mh = NULL;
597 return NULL;
598 }
599 (*mh)->m_next = md;
600 md->m_next = NULL;
601
602 m_reset_rcvif((*mh));
603 (*mh)->m_pkthdr.len = sizeof(struct ip6_hdr) + len;
604 (*mh)->m_len = sizeof(struct ip6_hdr);
605 MH_ALIGN(*mh, sizeof(struct ip6_hdr));
606
607 /* fill in the ip6 header */
608 ip6 = mtod(*mh, struct ip6_hdr *);
609 memset(ip6, 0, sizeof(*ip6));
610 ip6->ip6_flow = 0;
611 ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
612 ip6->ip6_vfc |= IPV6_VERSION;
613 /* ip6_plen will be set later */
614 ip6->ip6_nxt = IPPROTO_ICMPV6;
615 /* ip6_hlim will be set by im6o.im6o_multicast_hlim */
616 /* ip6_src/dst will be set by mld_sendpkt() or mld_sendbuf() */
617
618 /* fill in the MLD header as much as possible */
619 md->m_len = len;
620 mldh = mtod(md, struct mld_hdr *);
621 memset(mldh, 0, len);
622 mldh->mld_type = type;
623 return mldh;
624 }
625
626 /*
627 * Add an address to the list of IP6 multicast addresses for a given interface.
628 */
629 struct in6_multi *
630 in6_addmulti(struct in6_addr *maddr6, struct ifnet *ifp,
631 int *errorp, int timer)
632 {
633 struct sockaddr_in6 sin6;
634 struct in6_multi *in6m;
635 int s = splsoftnet();
636
637 *errorp = 0;
638
639 /*
640 * See if address already in list.
641 */
642 in6m = in6_lookup_multi(maddr6, ifp);
643 if (in6m != NULL) {
644 /*
645 * Found it; just increment the refrence count.
646 */
647 in6m->in6m_refcount++;
648 } else {
649 /*
650 * New address; allocate a new multicast record
651 * and link it into the interface's multicast list.
652 */
653 in6m = (struct in6_multi *)
654 malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT|M_ZERO);
655 if (in6m == NULL) {
656 splx(s);
657 *errorp = ENOBUFS;
658 return (NULL);
659 }
660
661 in6m->in6m_addr = *maddr6;
662 in6m->in6m_ifp = ifp;
663 in6m->in6m_refcount = 1;
664 in6m->in6m_timer = IN6M_TIMER_UNDEF;
665 callout_init(&in6m->in6m_timer_ch, CALLOUT_MPSAFE);
666 callout_setfunc(&in6m->in6m_timer_ch, mld_timeo, in6m);
667
668 /* FIXME NOMPSAFE: need to lock */
669 LIST_INSERT_HEAD(&ifp->if_multiaddrs, in6m, in6m_entry);
670
671 /*
672 * Ask the network driver to update its multicast reception
673 * filter appropriately for the new address.
674 */
675 sockaddr_in6_init(&sin6, maddr6, 0, 0, 0);
676 *errorp = if_mcast_op(ifp, SIOCADDMULTI, sin6tosa(&sin6));
677 if (*errorp) {
678 callout_destroy(&in6m->in6m_timer_ch);
679 LIST_REMOVE(in6m, in6m_entry);
680 free(in6m, M_IPMADDR);
681 splx(s);
682 return (NULL);
683 }
684
685 in6m->in6m_timer = timer;
686 if (in6m->in6m_timer > 0) {
687 in6m->in6m_state = MLD_REPORTPENDING;
688 mld_starttimer(in6m);
689
690 splx(s);
691 return (in6m);
692 }
693
694 /*
695 * Let MLD6 know that we have joined a new IP6 multicast
696 * group.
697 */
698 mld_start_listening(in6m);
699 }
700 splx(s);
701 return (in6m);
702 }
703
704 /*
705 * Delete a multicast address record.
706 */
707 void
708 in6_delmulti(struct in6_multi *in6m)
709 {
710 struct sockaddr_in6 sin6;
711 struct in6_ifaddr *ia;
712 int s = splsoftnet();
713
714 mld_stoptimer(in6m);
715
716 if (--in6m->in6m_refcount == 0) {
717 int _s;
718
719 /*
720 * No remaining claims to this record; let MLD6 know
721 * that we are leaving the multicast group.
722 */
723 mld_stop_listening(in6m);
724
725 /*
726 * Unlink from list.
727 */
728 /* FIXME NOMPSAFE: need to lock */
729 LIST_REMOVE(in6m, in6m_entry);
730
731 /*
732 * Delete all references of this multicasting group from
733 * the membership arrays
734 */
735 _s = pserialize_read_enter();
736 IN6_ADDRLIST_READER_FOREACH(ia) {
737 struct in6_multi_mship *imm;
738 LIST_FOREACH(imm, &ia->ia6_memberships, i6mm_chain) {
739 if (imm->i6mm_maddr == in6m)
740 imm->i6mm_maddr = NULL;
741 }
742 }
743 pserialize_read_exit(_s);
744
745 /*
746 * Notify the network driver to update its multicast
747 * reception filter.
748 */
749 sockaddr_in6_init(&sin6, &in6m->in6m_addr, 0, 0, 0);
750 if_mcast_op(in6m->in6m_ifp, SIOCDELMULTI, sin6tosa(&sin6));
751
752 /* Tell mld_timeo we're halting the timer */
753 in6m->in6m_timer = IN6M_TIMER_UNDEF;
754 if (mutex_owned(softnet_lock))
755 callout_halt(&in6m->in6m_timer_ch, softnet_lock);
756 else
757 callout_halt(&in6m->in6m_timer_ch, NULL);
758 callout_destroy(&in6m->in6m_timer_ch);
759
760 free(in6m, M_IPMADDR);
761 }
762 splx(s);
763 }
764
765 /*
766 * Look up the in6_multi record for a given IP6 multicast address
767 * on a given interface. If no matching record is found, "in6m"
768 * returns NULL.
769 */
770 struct in6_multi *
771 in6_lookup_multi(const struct in6_addr *addr, const struct ifnet *ifp)
772 {
773 struct in6_multi *in6m;
774
775 /* XXX NOMPSAFE */
776 LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
777 if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, addr))
778 break;
779 }
780 return in6m;
781 }
782
783 /*
784 * Purge in6_multi records associated to the interface.
785 */
786 void
787 in6_purge_multi(struct ifnet *ifp)
788 {
789 struct in6_multi *in6m, *next;
790
791 /* XXX NOMPSAFE */
792 LIST_FOREACH_SAFE(in6m, &ifp->if_multiaddrs, in6m_entry, next) {
793 in6_delmulti(in6m);
794 }
795 }
796
797 struct in6_multi_mship *
798 in6_joingroup(struct ifnet *ifp, struct in6_addr *addr,
799 int *errorp, int timer)
800 {
801 struct in6_multi_mship *imm;
802
803 imm = malloc(sizeof(*imm), M_IPMADDR, M_NOWAIT|M_ZERO);
804 if (imm == NULL) {
805 *errorp = ENOBUFS;
806 return NULL;
807 }
808
809 imm->i6mm_maddr = in6_addmulti(addr, ifp, errorp, timer);
810 if (!imm->i6mm_maddr) {
811 /* *errorp is already set */
812 free(imm, M_IPMADDR);
813 return NULL;
814 }
815 return imm;
816 }
817
818 int
819 in6_leavegroup(struct in6_multi_mship *imm)
820 {
821
822 if (imm->i6mm_maddr) {
823 in6_delmulti(imm->i6mm_maddr);
824 }
825 free(imm, M_IPMADDR);
826 return 0;
827 }
828
829 /*
830 * DEPRECATED: keep it just to avoid breaking old sysctl users.
831 */
832 static int
833 in6_mkludge_sysctl(SYSCTLFN_ARGS)
834 {
835
836 if (namelen != 1)
837 return EINVAL;
838 *oldlenp = 0;
839 return 0;
840 }
841
842 static int
843 in6_multicast_sysctl(SYSCTLFN_ARGS)
844 {
845 struct ifnet *ifp;
846 struct ifaddr *ifa;
847 struct in6_ifaddr *ia6;
848 struct in6_multi *in6m;
849 uint32_t tmp;
850 int error;
851 size_t written;
852 struct psref psref, psref_ia;
853 int bound, s;
854
855 if (namelen != 1)
856 return EINVAL;
857
858 bound = curlwp_bind();
859 ifp = if_get_byindex(name[0], &psref);
860 if (ifp == NULL) {
861 curlwp_bindx(bound);
862 return ENODEV;
863 }
864
865 if (oldp == NULL) {
866 *oldlenp = 0;
867 s = pserialize_read_enter();
868 IFADDR_READER_FOREACH(ifa, ifp) {
869 LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
870 *oldlenp += 2 * sizeof(struct in6_addr) +
871 sizeof(uint32_t);
872 }
873 }
874 pserialize_read_exit(s);
875 if_put(ifp, &psref);
876 curlwp_bindx(bound);
877 return 0;
878 }
879
880 error = 0;
881 written = 0;
882 s = pserialize_read_enter();
883 IFADDR_READER_FOREACH(ifa, ifp) {
884 if (ifa->ifa_addr->sa_family != AF_INET6)
885 continue;
886
887 ifa_acquire(ifa, &psref_ia);
888 pserialize_read_exit(s);
889
890 ia6 = ifatoia6(ifa);
891 LIST_FOREACH(in6m, &ifp->if_multiaddrs, in6m_entry) {
892 if (written + 2 * sizeof(struct in6_addr) +
893 sizeof(uint32_t) > *oldlenp)
894 goto done;
895 /*
896 * XXX return the first IPv6 address to keep backward
897 * compatibility, however now multicast addresses
898 * don't belong to any IPv6 addresses so it should be
899 * unnecessary.
900 */
901 error = sysctl_copyout(l, &ia6->ia_addr.sin6_addr,
902 oldp, sizeof(struct in6_addr));
903 if (error)
904 goto done;
905 oldp = (char *)oldp + sizeof(struct in6_addr);
906 written += sizeof(struct in6_addr);
907 error = sysctl_copyout(l, &in6m->in6m_addr,
908 oldp, sizeof(struct in6_addr));
909 if (error)
910 goto done;
911 oldp = (char *)oldp + sizeof(struct in6_addr);
912 written += sizeof(struct in6_addr);
913 tmp = in6m->in6m_refcount;
914 error = sysctl_copyout(l, &tmp, oldp, sizeof(tmp));
915 if (error)
916 goto done;
917 oldp = (char *)oldp + sizeof(tmp);
918 written += sizeof(tmp);
919 }
920
921 s = pserialize_read_enter();
922 ifa_release(ifa, &psref_ia);
923
924 break;
925 }
926 pserialize_read_exit(s);
927 done:
928 ifa_release(ifa, &psref_ia);
929 if_put(ifp, &psref);
930 curlwp_bindx(bound);
931 *oldlenp = written;
932 return error;
933 }
934
935 void
936 in6_sysctl_multicast_setup(struct sysctllog **clog)
937 {
938
939 sysctl_createv(clog, 0, NULL, NULL,
940 CTLFLAG_PERMANENT,
941 CTLTYPE_NODE, "inet6", NULL,
942 NULL, 0, NULL, 0,
943 CTL_NET, PF_INET6, CTL_EOL);
944
945 sysctl_createv(clog, 0, NULL, NULL,
946 CTLFLAG_PERMANENT,
947 CTLTYPE_NODE, "multicast",
948 SYSCTL_DESCR("Multicast information"),
949 in6_multicast_sysctl, 0, NULL, 0,
950 CTL_NET, PF_INET6, CTL_CREATE, CTL_EOL);
951
952 sysctl_createv(clog, 0, NULL, NULL,
953 CTLFLAG_PERMANENT,
954 CTLTYPE_NODE, "multicast_kludge",
955 SYSCTL_DESCR("multicast kludge information"),
956 in6_mkludge_sysctl, 0, NULL, 0,
957 CTL_NET, PF_INET6, CTL_CREATE, CTL_EOL);
958 }
959