mld6.c revision 126603
1/*	$FreeBSD: head/sys/netinet6/mld6.c 126603 2004-03-04 15:07:42Z ume $	*/
2/*	$KAME: mld6.c,v 1.27 2001/04/04 05:17:30 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) 1988 Stephen Deering.
35 * Copyright (c) 1992, 1993
36 *	The Regents of the University of California.  All rights reserved.
37 *
38 * This code is derived from software contributed to Berkeley by
39 * Stephen Deering of Stanford University.
40 *
41 * Redistribution and use in source and binary forms, with or without
42 * modification, are permitted provided that the following conditions
43 * are met:
44 * 1. Redistributions of source code must retain the above copyright
45 *    notice, this list of conditions and the following disclaimer.
46 * 2. Redistributions in binary form must reproduce the above copyright
47 *    notice, this list of conditions and the following disclaimer in the
48 *    documentation and/or other materials provided with the distribution.
49 * 3. All advertising materials mentioning features or use of this software
50 *    must display the following acknowledgement:
51 *	This product includes software developed by the University of
52 *	California, Berkeley and its contributors.
53 * 4. Neither the name of the University nor the names of its contributors
54 *    may be used to endorse or promote products derived from this software
55 *    without specific prior written permission.
56 *
57 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
58 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
59 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
60 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
61 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
62 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
63 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
64 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
65 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
66 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
67 * SUCH DAMAGE.
68 *
69 *	@(#)igmp.c	8.1 (Berkeley) 7/19/93
70 */
71
72#include "opt_inet.h"
73#include "opt_inet6.h"
74
75#include <sys/param.h>
76#include <sys/systm.h>
77#include <sys/mbuf.h>
78#include <sys/socket.h>
79#include <sys/protosw.h>
80#include <sys/syslog.h>
81#include <sys/malloc.h>
82
83#include <net/if.h>
84
85#include <netinet/in.h>
86#include <netinet/in_var.h>
87#include <netinet/ip6.h>
88#include <netinet6/ip6_var.h>
89#include <netinet/icmp6.h>
90#include <netinet6/mld6_var.h>
91
92#include <net/net_osdep.h>
93
94/*
95 * Protocol constants
96 */
97
98/* denotes that the MLD max response delay field specifies time in milliseconds */
99#define MLD_TIMER_SCALE	1000
100/*
101 * time between repetitions of a node's initial report of interest in a
102 * multicast address(in seconds)
103 */
104#define MLD_UNSOLICITED_REPORT_INTERVAL	10
105
106static struct ip6_pktopts ip6_opts;
107static int mld6_timers_are_running;
108/* XXX: These are necessary for KAME's link-local hack */
109static struct in6_addr mld6_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
110static struct in6_addr mld6_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
111
112static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
113
114void
115mld6_init()
116{
117	static u_int8_t hbh_buf[8];
118	struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
119	u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
120
121	mld6_timers_are_running = 0;
122
123	/* ip6h_nxt will be fill in later */
124	hbh->ip6h_len = 0;	/* (8 >> 3) - 1 */
125
126	/* XXX: grotty hard coding... */
127	hbh_buf[2] = IP6OPT_PADN;	/* 2 byte padding */
128	hbh_buf[3] = 0;
129	hbh_buf[4] = IP6OPT_ROUTER_ALERT;
130	hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
131	bcopy((caddr_t)&rtalert_code, &hbh_buf[6], sizeof(u_int16_t));
132
133	init_ip6pktopts(&ip6_opts);
134	ip6_opts.ip6po_hbh = hbh;
135}
136
137void
138mld6_start_listening(in6m)
139	struct in6_multi *in6m;
140{
141	int s = splnet();
142
143	/*
144	 * RFC2710 page 10:
145	 * The node never sends a Report or Done for the link-scope all-nodes
146	 * address.
147	 * MLD messages are never sent for multicast addresses whose scope is 0
148	 * (reserved) or 1 (node-local).
149	 */
150	mld6_all_nodes_linklocal.s6_addr16[1] =
151		htons(in6m->in6m_ifp->if_index); /* XXX */
152	if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal) ||
153	    IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < IPV6_ADDR_SCOPE_LINKLOCAL) {
154		in6m->in6m_timer = 0;
155		in6m->in6m_state = MLD_OTHERLISTENER;
156	} else {
157		mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
158		in6m->in6m_timer =
159			MLD_RANDOM_DELAY(MLD_UNSOLICITED_REPORT_INTERVAL *
160					 PR_FASTHZ);
161		in6m->in6m_state = MLD_IREPORTEDLAST;
162		mld6_timers_are_running = 1;
163	}
164	splx(s);
165}
166
167void
168mld6_stop_listening(in6m)
169	struct in6_multi *in6m;
170{
171	mld6_all_nodes_linklocal.s6_addr16[1] =
172		htons(in6m->in6m_ifp->if_index); /* XXX */
173	mld6_all_routers_linklocal.s6_addr16[1] =
174		htons(in6m->in6m_ifp->if_index); /* XXX: necessary when mrouting */
175
176	if (in6m->in6m_state == MLD_IREPORTEDLAST &&
177	    (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld6_all_nodes_linklocal)) &&
178	    IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > IPV6_ADDR_SCOPE_INTFACELOCAL)
179		mld6_sendpkt(in6m, MLD_LISTENER_DONE,
180			     &mld6_all_routers_linklocal);
181}
182
183void
184mld6_input(m, off)
185	struct mbuf *m;
186	int off;
187{
188	struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
189	struct mld_hdr *mldh;
190	struct ifnet *ifp = m->m_pkthdr.rcvif;
191	struct in6_multi *in6m;
192	struct in6_ifaddr *ia;
193	struct ifmultiaddr *ifma;
194	int timer;		/* timer value in the MLD query header */
195
196#ifndef PULLDOWN_TEST
197	IP6_EXTHDR_CHECK(m, off, sizeof(*mldh),);
198	mldh = (struct mld_hdr *)(mtod(m, caddr_t) + off);
199#else
200	IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
201	if (mldh == NULL) {
202		icmp6stat.icp6s_tooshort++;
203		return;
204	}
205#endif
206
207	/* source address validation */
208	ip6 = mtod(m, struct ip6_hdr *); /* in case mpullup */
209	if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
210		log(LOG_ERR,
211		    "mld6_input: src %s is not link-local (grp=%s)\n",
212		    ip6_sprintf(&ip6->ip6_src),
213		    ip6_sprintf(&mldh->mld_addr));
214		/*
215		 * spec (RFC2710) does not explicitly
216		 * specify to discard the packet from a non link-local
217		 * source address. But we believe it's expected to do so.
218		 * XXX: do we have to allow :: as source?
219		 */
220		m_freem(m);
221		return;
222	}
223
224	/*
225	 * In the MLD6 specification, there are 3 states and a flag.
226	 *
227	 * In Non-Listener state, we simply don't have a membership record.
228	 * In Delaying Listener state, our timer is running (in6m->in6m_timer)
229	 * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
230	 *
231	 * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
232	 * we have heard a report from another member, or MLD_IREPORTEDLAST
233	 * if we sent the last report.
234	 */
235	switch(mldh->mld_type) {
236	case MLD_LISTENER_QUERY:
237		if (ifp->if_flags & IFF_LOOPBACK)
238			break;
239
240		if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
241		    !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
242			break;	/* print error or log stat? */
243		if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
244			mldh->mld_addr.s6_addr16[1] =
245				htons(ifp->if_index); /* XXX */
246
247		/*
248		 * - Start the timers in all of our membership records
249		 *   that the query applies to for the interface on
250		 *   which the query arrived excl. those that belong
251		 *   to the "all-nodes" group (ff02::1).
252		 * - Restart any timer that is already running but has
253		 *   A value longer than the requested timeout.
254		 * - Use the value specified in the query message as
255		 *   the maximum timeout.
256		 */
257		IFP_TO_IA6(ifp, ia);
258		if (ia == NULL)
259			break;
260
261		/*
262		 * XXX: System timer resolution is too low to handle Max
263		 * Response Delay, so set 1 to the internal timer even if
264		 * the calculated value equals to zero when Max Response
265		 * Delay is positive.
266		 */
267		timer = ntohs(mldh->mld_maxdelay) * PR_FASTHZ / MLD_TIMER_SCALE;
268		if (timer == 0 && mldh->mld_maxdelay)
269			timer = 1;
270		mld6_all_nodes_linklocal.s6_addr16[1] =
271			htons(ifp->if_index); /* XXX */
272
273		TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
274			if (ifma->ifma_addr->sa_family != AF_INET6)
275				continue;
276			in6m = (struct in6_multi *)ifma->ifma_protospec;
277
278			if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
279					&mld6_all_nodes_linklocal) ||
280			    IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
281			    IPV6_ADDR_SCOPE_LINKLOCAL)
282				continue;
283
284			if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
285			    IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
286						&in6m->in6m_addr))
287			{
288				if (timer == 0) {
289					/* send a report immediately */
290					mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
291						NULL);
292					in6m->in6m_timer = 0; /* reset timer */
293					in6m->in6m_state = MLD_IREPORTEDLAST;
294				}
295				else if (in6m->in6m_timer == 0 || /*idle state*/
296					in6m->in6m_timer > timer) {
297					in6m->in6m_timer =
298						MLD_RANDOM_DELAY(timer);
299					mld6_timers_are_running = 1;
300				}
301			}
302		}
303
304		if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
305			mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
306		break;
307
308	case MLD_LISTENER_REPORT:
309		/*
310		 * For fast leave to work, we have to know that we are the
311		 * last person to send a report for this group.  Reports
312		 * can potentially get looped back if we are a multicast
313		 * router, so discard reports sourced by me.
314		 * Note that it is impossible to check IFF_LOOPBACK flag of
315		 * ifp for this purpose, since ip6_mloopback pass the physical
316		 * interface to looutput.
317		 */
318		if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
319			break;
320
321		if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
322			break;
323
324		if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
325			mldh->mld_addr.s6_addr16[1] =
326				htons(ifp->if_index); /* XXX */
327		/*
328		 * If we belong to the group being reported, stop
329		 * our timer for that group.
330		 */
331		IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
332		if (in6m) {
333			in6m->in6m_timer = 0; /* transit to idle state */
334			in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
335		}
336
337		if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
338			mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
339		break;
340	default:		/* this is impossible */
341		log(LOG_ERR, "mld6_input: illegal type(%d)", mldh->mld_type);
342		break;
343	}
344
345	m_freem(m);
346}
347
348void
349mld6_fasttimeo()
350{
351	struct in6_multi *in6m;
352	struct in6_multistep step;
353	int s;
354
355	/*
356	 * Quick check to see if any work needs to be done, in order
357	 * to minimize the overhead of fasttimo processing.
358	 */
359	if (!mld6_timers_are_running)
360		return;
361
362	s = splnet();
363
364	mld6_timers_are_running = 0;
365	IN6_FIRST_MULTI(step, in6m);
366	while (in6m != NULL) {
367		if (in6m->in6m_timer == 0) {
368			/* do nothing */
369		} else if (--in6m->in6m_timer == 0) {
370			mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
371			in6m->in6m_state = MLD_IREPORTEDLAST;
372		} else {
373			mld6_timers_are_running = 1;
374		}
375		IN6_NEXT_MULTI(step, in6m);
376	}
377
378	splx(s);
379}
380
381static void
382mld6_sendpkt(in6m, type, dst)
383	struct in6_multi *in6m;
384	int type;
385	const struct in6_addr *dst;
386{
387	struct mbuf *mh, *md;
388	struct mld_hdr *mldh;
389	struct ip6_hdr *ip6;
390	struct ip6_moptions im6o;
391	struct in6_ifaddr *ia;
392	struct ifnet *ifp = in6m->in6m_ifp;
393	struct ifnet *outif = NULL;
394
395	/*
396	 * At first, find a link local address on the outgoing interface
397	 * to use as the source address of the MLD packet.
398	 */
399	if ((ia = in6ifa_ifpforlinklocal(ifp, IN6_IFF_NOTREADY|IN6_IFF_ANYCAST))
400	    == NULL)
401		return;
402
403	/*
404	 * Allocate mbufs to store ip6 header and MLD header.
405	 * We allocate 2 mbufs and make chain in advance because
406	 * it is more convenient when inserting the hop-by-hop option later.
407	 */
408	MGETHDR(mh, M_DONTWAIT, MT_HEADER);
409	if (mh == NULL)
410		return;
411	MGET(md, M_DONTWAIT, MT_DATA);
412	if (md == NULL) {
413		m_free(mh);
414		return;
415	}
416	mh->m_next = md;
417
418	mh->m_pkthdr.rcvif = NULL;
419	mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
420	mh->m_len = sizeof(struct ip6_hdr);
421	MH_ALIGN(mh, sizeof(struct ip6_hdr));
422
423	/* fill in the ip6 header */
424	ip6 = mtod(mh, struct ip6_hdr *);
425	ip6->ip6_flow = 0;
426	ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
427	ip6->ip6_vfc |= IPV6_VERSION;
428	/* ip6_plen will be set later */
429	ip6->ip6_nxt = IPPROTO_ICMPV6;
430	/* ip6_hlim will be set by im6o.im6o_multicast_hlim */
431	ip6->ip6_src = ia->ia_addr.sin6_addr;
432	ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
433
434	/* fill in the MLD header */
435	md->m_len = sizeof(struct mld_hdr);
436	mldh = mtod(md, struct mld_hdr *);
437	mldh->mld_type = type;
438	mldh->mld_code = 0;
439	mldh->mld_cksum = 0;
440	/* XXX: we assume the function will not be called for query messages */
441	mldh->mld_maxdelay = 0;
442	mldh->mld_reserved = 0;
443	mldh->mld_addr = in6m->in6m_addr;
444	if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
445		mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
446	mldh->mld_cksum = in6_cksum(mh, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
447				    sizeof(struct mld_hdr));
448
449	/* construct multicast option */
450	bzero(&im6o, sizeof(im6o));
451	im6o.im6o_multicast_ifp = ifp;
452	im6o.im6o_multicast_hlim = 1;
453
454	/*
455	 * Request loopback of the report if we are acting as a multicast
456	 * router, so that the process-level routing daemon can hear it.
457	 */
458	im6o.im6o_multicast_loop = (ip6_mrouter != NULL);
459
460	/* increment output statictics */
461	icmp6stat.icp6s_outhist[type]++;
462
463	ip6_output(mh, &ip6_opts, NULL, 0, &im6o, &outif, NULL);
464	if (outif) {
465		icmp6_ifstat_inc(outif, ifs6_out_msg);
466		switch (type) {
467		case MLD_LISTENER_QUERY:
468			icmp6_ifstat_inc(outif, ifs6_out_mldquery);
469			break;
470		case MLD_LISTENER_REPORT:
471			icmp6_ifstat_inc(outif, ifs6_out_mldreport);
472			break;
473		case MLD_LISTENER_DONE:
474			icmp6_ifstat_inc(outif, ifs6_out_mlddone);
475			break;
476		}
477	}
478}
479
480/*
481 * Add an address to the list of IP6 multicast addresses for a given interface.
482 * Add source addresses to the list also, if upstream router is MLDv2 capable
483 * and the number of source is not 0.
484 */
485struct	in6_multi *
486in6_addmulti(maddr6, ifp, errorp)
487	struct in6_addr *maddr6;
488	struct ifnet *ifp;
489	int *errorp;
490{
491	struct in6_multi *in6m;
492	struct ifmultiaddr *ifma;
493	struct sockaddr_in6 sa6;
494	int	s = splnet();
495
496	*errorp = 0;
497
498	/*
499	 * Call generic routine to add membership or increment
500	 * refcount.  It wants addresses in the form of a sockaddr,
501	 * so we build one here (being careful to zero the unused bytes).
502	 */
503	bzero(&sa6, sizeof(sa6));
504	sa6.sin6_family = AF_INET6;
505	sa6.sin6_len = sizeof(struct sockaddr_in6);
506	sa6.sin6_addr = *maddr6;
507	*errorp = if_addmulti(ifp, (struct sockaddr *)&sa6, &ifma);
508	if (*errorp) {
509		splx(s);
510		return 0;
511	}
512
513	/*
514	 * If ifma->ifma_protospec is null, then if_addmulti() created
515	 * a new record.  Otherwise, we are done.
516	 */
517	if (ifma->ifma_protospec != 0) {
518		splx(s);
519		return ifma->ifma_protospec;
520	}
521
522	/* XXX - if_addmulti uses M_WAITOK.  Can this really be called
523	   at interrupt time?  If so, need to fix if_addmulti. XXX */
524	in6m = (struct in6_multi *)malloc(sizeof(*in6m), M_IPMADDR, M_NOWAIT);
525	if (in6m == NULL) {
526		splx(s);
527		return (NULL);
528	}
529
530	bzero(in6m, sizeof *in6m);
531	in6m->in6m_addr = *maddr6;
532	in6m->in6m_ifp = ifp;
533	in6m->in6m_refcount = 1;
534	in6m->in6m_ifma = ifma;
535	ifma->ifma_protospec = in6m;
536	LIST_INSERT_HEAD(&in6_multihead, in6m, in6m_entry);
537
538	/*
539	 * Let MLD6 know that we have joined a new IPv6 multicast
540	 * group.
541	 */
542	mld6_start_listening(in6m);
543	splx(s);
544	return (in6m);
545}
546
547/*
548 * Delete a multicast address record.
549 */
550void
551in6_delmulti(in6m)
552	struct in6_multi *in6m;
553{
554	struct ifmultiaddr *ifma = in6m->in6m_ifma;
555	int	s = splnet();
556
557	if (ifma->ifma_refcount == 1) {
558		/*
559		 * No remaining claims to this record; let MLD6 know
560		 * that we are leaving the multicast group.
561		 */
562		mld6_stop_listening(in6m);
563		ifma->ifma_protospec = 0;
564		LIST_REMOVE(in6m, in6m_entry);
565		free(in6m, M_IPMADDR);
566	}
567	/* XXX - should be separate API for when we have an ifma? */
568	if_delmulti(ifma->ifma_ifp, ifma->ifma_addr);
569	splx(s);
570}
571