Line data Source code
1 : /* $OpenBSD: mld6.c,v 1.55 2017/10/29 14:56:36 florian Exp $ */
2 : /* $KAME: mld6.c,v 1.26 2001/02/16 14:50:35 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. Neither the name of the University nor the names of its contributors
50 : * may be used to endorse or promote products derived from this software
51 : * without specific prior written permission.
52 : *
53 : * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 : * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 : * SUCH DAMAGE.
64 : *
65 : * @(#)igmp.c 8.1 (Berkeley) 7/19/93
66 : */
67 :
68 : #include <sys/param.h>
69 : #include <sys/systm.h>
70 : #include <sys/mbuf.h>
71 : #include <sys/socket.h>
72 : #include <sys/protosw.h>
73 : #include <sys/syslog.h>
74 :
75 : #include <net/if.h>
76 : #include <net/if_var.h>
77 :
78 : #include <netinet/in.h>
79 : #include <netinet6/in6_var.h>
80 : #include <netinet/ip6.h>
81 : #include <netinet6/ip6_var.h>
82 : #include <netinet/icmp6.h>
83 : #include <netinet6/mld6.h>
84 : #include <netinet6/mld6_var.h>
85 :
86 : static struct ip6_pktopts ip6_opts;
87 : static int mld_timers_are_running;
88 : /* XXX: These are necessary for KAME's link-local hack */
89 : static struct in6_addr mld_all_nodes_linklocal = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
90 : static struct in6_addr mld_all_routers_linklocal = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
91 :
92 : void mld6_checktimer(struct ifnet *);
93 : static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
94 :
95 : void
96 0 : mld6_init(void)
97 : {
98 : static u_int8_t hbh_buf[8];
99 : struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
100 : u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
101 :
102 0 : mld_timers_are_running = 0;
103 :
104 : /* ip6h_nxt will be fill in later */
105 0 : hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
106 :
107 : /* XXX: grotty hard coding... */
108 0 : hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
109 0 : hbh_buf[3] = 0;
110 0 : hbh_buf[4] = IP6OPT_ROUTER_ALERT;
111 0 : hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
112 0 : memcpy(&hbh_buf[6], (caddr_t)&rtalert_code, sizeof(u_int16_t));
113 :
114 0 : ip6_initpktopts(&ip6_opts);
115 0 : ip6_opts.ip6po_hbh = hbh;
116 0 : }
117 :
118 : void
119 0 : mld6_start_listening(struct in6_multi *in6m)
120 : {
121 : /*
122 : * RFC2710 page 10:
123 : * The node never sends a Report or Done for the link-scope all-nodes
124 : * address.
125 : * MLD messages are never sent for multicast addresses whose scope is 0
126 : * (reserved) or 1 (node-local).
127 : */
128 0 : mld_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifidx);/* XXX */
129 0 : if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal) ||
130 0 : __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) < __IPV6_ADDR_SCOPE_LINKLOCAL) {
131 0 : in6m->in6m_timer = 0;
132 0 : in6m->in6m_state = MLD_OTHERLISTENER;
133 0 : } else {
134 0 : mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
135 0 : in6m->in6m_timer =
136 0 : MLD_RANDOM_DELAY(MLD_V1_MAX_RI *
137 : PR_FASTHZ);
138 0 : in6m->in6m_state = MLD_IREPORTEDLAST;
139 0 : mld_timers_are_running = 1;
140 : }
141 0 : }
142 :
143 : void
144 0 : mld6_stop_listening(struct in6_multi *in6m)
145 : {
146 0 : mld_all_nodes_linklocal.s6_addr16[1] = htons(in6m->in6m_ifidx);/* XXX */
147 0 : mld_all_routers_linklocal.s6_addr16[1] =
148 0 : htons(in6m->in6m_ifidx); /* XXX: necessary when mrouting */
149 :
150 0 : if (in6m->in6m_state == MLD_IREPORTEDLAST &&
151 0 : (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &mld_all_nodes_linklocal)) &&
152 0 : __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) > __IPV6_ADDR_SCOPE_INTFACELOCAL)
153 0 : mld6_sendpkt(in6m, MLD_LISTENER_DONE,
154 : &mld_all_routers_linklocal);
155 0 : }
156 :
157 : void
158 0 : mld6_input(struct mbuf *m, int off)
159 : {
160 : struct ip6_hdr *ip6;
161 : struct mld_hdr *mldh;
162 : struct ifnet *ifp;
163 : struct in6_multi *in6m;
164 : struct ifmaddr *ifma;
165 : int timer; /* timer value in the MLD query header */
166 :
167 0 : IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
168 0 : if (mldh == NULL) {
169 0 : icmp6stat_inc(icp6s_tooshort);
170 0 : return;
171 : }
172 :
173 : /* source address validation */
174 0 : ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
175 0 : if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
176 : #if 0
177 : char src[INET6_ADDRSTRLEN], grp[INET6_ADDRSTRLEN];
178 :
179 : log(LOG_ERR,
180 : "mld_input: src %s is not link-local (grp=%s)\n",
181 : inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)),
182 : inet_ntop(AF_INET6, &mldh->mld_addr, grp, sizeof(grp)));
183 : #endif
184 : /*
185 : * spec (RFC2710) does not explicitly
186 : * specify to discard the packet from a non link-local
187 : * source address. But we believe it's expected to do so.
188 : */
189 0 : m_freem(m);
190 0 : return;
191 : }
192 :
193 0 : ifp = if_get(m->m_pkthdr.ph_ifidx);
194 0 : if (ifp == NULL) {
195 0 : m_freem(m);
196 0 : return;
197 : }
198 :
199 : /*
200 : * In the MLD6 specification, there are 3 states and a flag.
201 : *
202 : * In Non-Listener state, we simply don't have a membership record.
203 : * In Delaying Listener state, our timer is running (in6m->in6m_timer)
204 : * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
205 : *
206 : * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
207 : * we have heard a report from another member, or MLD_IREPORTEDLAST
208 : * if we sent the last report.
209 : */
210 0 : switch(mldh->mld_type) {
211 : case MLD_LISTENER_QUERY:
212 0 : if (ifp->if_flags & IFF_LOOPBACK)
213 : break;
214 :
215 0 : if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
216 0 : !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
217 : break; /* print error or log stat? */
218 0 : if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
219 0 : mldh->mld_addr.s6_addr16[1] =
220 0 : htons(ifp->if_index); /* XXX */
221 :
222 : /*
223 : * - Start the timers in all of our membership records
224 : * that the query applies to for the interface on
225 : * which the query arrived excl. those that belong
226 : * to the "all-nodes" group (ff02::1).
227 : * - Restart any timer that is already running but has
228 : * A value longer than the requested timeout.
229 : * - Use the value specified in the query message as
230 : * the maximum timeout.
231 : */
232 :
233 : /*
234 : * XXX: System timer resolution is too low to handle Max
235 : * Response Delay, so set 1 to the internal timer even if
236 : * the calculated value equals to zero when Max Response
237 : * Delay is positive.
238 : */
239 0 : timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD_TIMER_SCALE;
240 0 : if (timer == 0 && mldh->mld_maxdelay)
241 0 : timer = 1;
242 0 : mld_all_nodes_linklocal.s6_addr16[1] =
243 0 : htons(ifp->if_index); /* XXX */
244 :
245 0 : TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
246 0 : if (ifma->ifma_addr->sa_family != AF_INET6)
247 : continue;
248 0 : in6m = ifmatoin6m(ifma);
249 0 : if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr,
250 0 : &mld_all_nodes_linklocal) ||
251 0 : __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
252 : __IPV6_ADDR_SCOPE_LINKLOCAL)
253 : continue;
254 :
255 0 : if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
256 0 : IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
257 : &in6m->in6m_addr))
258 : {
259 0 : if (timer == 0) {
260 : /* send a report immediately */
261 0 : mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
262 : NULL);
263 0 : in6m->in6m_timer = 0; /* reset timer */
264 0 : in6m->in6m_state = MLD_IREPORTEDLAST;
265 0 : } else if (in6m->in6m_timer == 0 || /* idle */
266 0 : in6m->in6m_timer > timer) {
267 0 : in6m->in6m_timer =
268 0 : MLD_RANDOM_DELAY(timer);
269 0 : mld_timers_are_running = 1;
270 0 : }
271 : }
272 : }
273 :
274 0 : if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
275 0 : mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
276 : break;
277 : case MLD_LISTENER_REPORT:
278 : /*
279 : * For fast leave to work, we have to know that we are the
280 : * last person to send a report for this group. Reports
281 : * can potentially get looped back if we are a multicast
282 : * router, so discard reports sourced by me.
283 : * Note that it is impossible to check IFF_LOOPBACK flag of
284 : * ifp for this purpose, since ip6_mloopback pass the physical
285 : * interface to if_input_local().
286 : */
287 0 : if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
288 : break;
289 :
290 0 : if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
291 : break;
292 :
293 0 : if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
294 0 : mldh->mld_addr.s6_addr16[1] =
295 0 : htons(ifp->if_index); /* XXX */
296 : /*
297 : * If we belong to the group being reported, stop
298 : * our timer for that group.
299 : */
300 0 : IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
301 0 : if (in6m) {
302 0 : in6m->in6m_timer = 0; /* transit to idle state */
303 0 : in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
304 0 : }
305 :
306 0 : if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
307 0 : mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
308 : break;
309 : default: /* this is impossible */
310 : #if 0
311 : /*
312 : * this case should be impossible because of filtering in
313 : * icmp6_input(). But we explicitly disabled this part
314 : * just in case.
315 : */
316 : log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
317 : #endif
318 : break;
319 : }
320 0 : if_put(ifp);
321 :
322 0 : m_freem(m);
323 0 : }
324 :
325 : void
326 0 : mld6_fasttimeo(void)
327 : {
328 : struct ifnet *ifp;
329 :
330 0 : NET_LOCK();
331 :
332 : /*
333 : * Quick check to see if any work needs to be done, in order
334 : * to minimize the overhead of fasttimo processing.
335 : */
336 0 : if (!mld_timers_are_running)
337 : goto out;
338 :
339 0 : mld_timers_are_running = 0;
340 0 : TAILQ_FOREACH(ifp, &ifnet, if_list)
341 0 : mld6_checktimer(ifp);
342 :
343 : out:
344 0 : NET_UNLOCK();
345 0 : }
346 :
347 : void
348 0 : mld6_checktimer(struct ifnet *ifp)
349 : {
350 : struct in6_multi *in6m;
351 : struct ifmaddr *ifma;
352 :
353 0 : NET_ASSERT_LOCKED();
354 :
355 0 : TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
356 0 : if (ifma->ifma_addr->sa_family != AF_INET6)
357 : continue;
358 0 : in6m = ifmatoin6m(ifma);
359 0 : if (in6m->in6m_timer == 0) {
360 : /* do nothing */
361 0 : } else if (--in6m->in6m_timer == 0) {
362 0 : mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
363 0 : in6m->in6m_state = MLD_IREPORTEDLAST;
364 0 : } else {
365 0 : mld_timers_are_running = 1;
366 : }
367 : }
368 0 : }
369 :
370 : static void
371 0 : mld6_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst)
372 : {
373 : struct mbuf *mh, *md;
374 : struct mld_hdr *mldh;
375 : struct ip6_hdr *ip6;
376 0 : struct ip6_moptions im6o;
377 : struct in6_ifaddr *ia6;
378 : struct ifnet *ifp;
379 : int ignflags;
380 :
381 0 : ifp = if_get(in6m->in6m_ifidx);
382 0 : if (ifp == NULL)
383 0 : return;
384 :
385 : /*
386 : * At first, find a link local address on the outgoing interface
387 : * to use as the source address of the MLD packet.
388 : * We do not reject tentative addresses for MLD report to deal with
389 : * the case where we first join a link-local address.
390 : */
391 : ignflags = IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST;
392 0 : if ((ia6 = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) {
393 0 : if_put(ifp);
394 0 : return;
395 : }
396 0 : if ((ia6->ia6_flags & IN6_IFF_TENTATIVE))
397 0 : ia6 = NULL;
398 :
399 : /*
400 : * Allocate mbufs to store ip6 header and MLD header.
401 : * We allocate 2 mbufs and make chain in advance because
402 : * it is more convenient when inserting the hop-by-hop option later.
403 : */
404 0 : MGETHDR(mh, M_DONTWAIT, MT_HEADER);
405 0 : if (mh == NULL) {
406 0 : if_put(ifp);
407 0 : return;
408 : }
409 0 : MGET(md, M_DONTWAIT, MT_DATA);
410 0 : if (md == NULL) {
411 0 : m_free(mh);
412 0 : if_put(ifp);
413 0 : return;
414 : }
415 0 : mh->m_next = md;
416 :
417 0 : mh->m_pkthdr.ph_ifidx = 0;
418 0 : mh->m_pkthdr.ph_rtableid = ifp->if_rdomain;
419 0 : mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
420 0 : mh->m_len = sizeof(struct ip6_hdr);
421 0 : MH_ALIGN(mh, sizeof(struct ip6_hdr));
422 :
423 : /* fill in the ip6 header */
424 0 : ip6 = mtod(mh, struct ip6_hdr *);
425 0 : ip6->ip6_flow = 0;
426 0 : ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
427 0 : ip6->ip6_vfc |= IPV6_VERSION;
428 : /* ip6_plen will be set later */
429 0 : ip6->ip6_nxt = IPPROTO_ICMPV6;
430 : /* ip6_hlim will be set by im6o.im6o_hlim */
431 0 : ip6->ip6_src = ia6 ? ia6->ia_addr.sin6_addr : in6addr_any;
432 0 : ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
433 :
434 : /* fill in the MLD header */
435 0 : md->m_len = sizeof(struct mld_hdr);
436 0 : mldh = mtod(md, struct mld_hdr *);
437 0 : mldh->mld_type = type;
438 0 : mldh->mld_code = 0;
439 0 : mldh->mld_cksum = 0;
440 : /* XXX: we assume the function will not be called for query messages */
441 0 : mldh->mld_maxdelay = 0;
442 0 : mldh->mld_reserved = 0;
443 0 : mldh->mld_addr = in6m->in6m_addr;
444 0 : if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
445 0 : mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
446 0 : mh->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
447 :
448 : /* construct multicast option */
449 0 : bzero(&im6o, sizeof(im6o));
450 0 : im6o.im6o_ifidx = ifp->if_index;
451 0 : im6o.im6o_hlim = 1;
452 :
453 : /*
454 : * Request loopback of the report if we are acting as a multicast
455 : * router, so that the process-level routing daemon can hear it.
456 : */
457 : #ifdef MROUTING
458 0 : im6o.im6o_loop = (ip6_mrouter[ifp->if_rdomain] != NULL);
459 : #endif
460 0 : if_put(ifp);
461 :
462 0 : icmp6stat_inc(icp6s_outhist + type);
463 0 : ip6_output(mh, &ip6_opts, NULL, ia6 ? 0 : IPV6_UNSPECSRC, &im6o,
464 : NULL);
465 0 : }
|