FreeBSD/Linux Kernel Cross Reference
sys/netinet6/mld6.c
1 /* $OpenBSD: mld6.c,v 1.61 2022/09/08 10:22:07 kn 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 int mld6_timers_are_running; /* [N] shortcut for fast timer */
88
89 void mld6_checktimer(struct ifnet *);
90 static void mld6_sendpkt(struct in6_multi *, int, const struct in6_addr *);
91
92 void
93 mld6_init(void)
94 {
95 static u_int8_t hbh_buf[8];
96 struct ip6_hbh *hbh = (struct ip6_hbh *)hbh_buf;
97 u_int16_t rtalert_code = htons((u_int16_t)IP6OPT_RTALERT_MLD);
98
99 mld6_timers_are_running = 0;
100
101 /* ip6h_nxt will be fill in later */
102 hbh->ip6h_len = 0; /* (8 >> 3) - 1 */
103
104 /* XXX: grotty hard coding... */
105 hbh_buf[2] = IP6OPT_PADN; /* 2 byte padding */
106 hbh_buf[3] = 0;
107 hbh_buf[4] = IP6OPT_ROUTER_ALERT;
108 hbh_buf[5] = IP6OPT_RTALERT_LEN - 2;
109 memcpy(&hbh_buf[6], (caddr_t)&rtalert_code, sizeof(u_int16_t));
110
111 ip6_initpktopts(&ip6_opts);
112 ip6_opts.ip6po_hbh = hbh;
113 }
114
115 void
116 mld6_start_listening(struct in6_multi *in6m)
117 {
118 /* XXX: These are necessary for KAME's link-local hack */
119 struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
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 all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx);
129 if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) ||
130 __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
131 __IPV6_ADDR_SCOPE_LINKLOCAL) {
132 in6m->in6m_timer = 0;
133 in6m->in6m_state = MLD_OTHERLISTENER;
134 } else {
135 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
136 in6m->in6m_timer =
137 MLD_RANDOM_DELAY(MLD_V1_MAX_RI *
138 PR_FASTHZ);
139 in6m->in6m_state = MLD_IREPORTEDLAST;
140 mld6_timers_are_running = 1;
141 }
142 }
143
144 void
145 mld6_stop_listening(struct in6_multi *in6m)
146 {
147 /* XXX: These are necessary for KAME's link-local hack */
148 struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
149 struct in6_addr all_routers = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT;
150
151 all_nodes.s6_addr16[1] = htons(in6m->in6m_ifidx);
152 /* XXX: necessary when mrouting */
153 all_routers.s6_addr16[1] = htons(in6m->in6m_ifidx);
154
155 if (in6m->in6m_state == MLD_IREPORTEDLAST &&
156 (!IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes)) &&
157 __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) >
158 __IPV6_ADDR_SCOPE_INTFACELOCAL)
159 mld6_sendpkt(in6m, MLD_LISTENER_DONE, &all_routers);
160 }
161
162 void
163 mld6_input(struct mbuf *m, int off)
164 {
165 struct ip6_hdr *ip6;
166 struct mld_hdr *mldh;
167 struct ifnet *ifp;
168 struct in6_multi *in6m;
169 struct ifmaddr *ifma;
170 int timer; /* timer value in the MLD query header */
171 /* XXX: These are necessary for KAME's link-local hack */
172 struct in6_addr all_nodes = IN6ADDR_LINKLOCAL_ALLNODES_INIT;
173
174 IP6_EXTHDR_GET(mldh, struct mld_hdr *, m, off, sizeof(*mldh));
175 if (mldh == NULL) {
176 icmp6stat_inc(icp6s_tooshort);
177 return;
178 }
179
180 /* source address validation */
181 ip6 = mtod(m, struct ip6_hdr *);/* in case mpullup */
182 if (!IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_src)) {
183 #if 0
184 char src[INET6_ADDRSTRLEN], grp[INET6_ADDRSTRLEN];
185
186 log(LOG_ERR,
187 "mld_input: src %s is not link-local (grp=%s)\n",
188 inet_ntop(AF_INET6, &ip6->ip6_src, src, sizeof(src)),
189 inet_ntop(AF_INET6, &mldh->mld_addr, grp, sizeof(grp)));
190 #endif
191 /*
192 * spec (RFC2710) does not explicitly
193 * specify to discard the packet from a non link-local
194 * source address. But we believe it's expected to do so.
195 */
196 m_freem(m);
197 return;
198 }
199
200 ifp = if_get(m->m_pkthdr.ph_ifidx);
201 if (ifp == NULL) {
202 m_freem(m);
203 return;
204 }
205
206 /*
207 * In the MLD6 specification, there are 3 states and a flag.
208 *
209 * In Non-Listener state, we simply don't have a membership record.
210 * In Delaying Listener state, our timer is running (in6m->in6m_timer)
211 * In Idle Listener state, our timer is not running (in6m->in6m_timer==0)
212 *
213 * The flag is in6m->in6m_state, it is set to MLD_OTHERLISTENER if
214 * we have heard a report from another member, or MLD_IREPORTEDLAST
215 * if we sent the last report.
216 */
217 switch(mldh->mld_type) {
218 case MLD_LISTENER_QUERY:
219 if (ifp->if_flags & IFF_LOOPBACK)
220 break;
221
222 if (!IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) &&
223 !IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
224 break; /* print error or log stat? */
225 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
226 mldh->mld_addr.s6_addr16[1] =
227 htons(ifp->if_index); /* XXX */
228
229 /*
230 * - Start the timers in all of our membership records
231 * that the query applies to for the interface on
232 * which the query arrived excl. those that belong
233 * to the "all-nodes" group (ff02::1).
234 * - Restart any timer that is already running but has
235 * A value longer than the requested timeout.
236 * - Use the value specified in the query message as
237 * the maximum timeout.
238 */
239
240 /*
241 * XXX: System timer resolution is too low to handle Max
242 * Response Delay, so set 1 to the internal timer even if
243 * the calculated value equals to zero when Max Response
244 * Delay is positive.
245 */
246 timer = ntohs(mldh->mld_maxdelay)*PR_FASTHZ/MLD_TIMER_SCALE;
247 if (timer == 0 && mldh->mld_maxdelay)
248 timer = 1;
249 all_nodes.s6_addr16[1] = htons(ifp->if_index);
250
251 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
252 if (ifma->ifma_addr->sa_family != AF_INET6)
253 continue;
254 in6m = ifmatoin6m(ifma);
255 if (IN6_ARE_ADDR_EQUAL(&in6m->in6m_addr, &all_nodes) ||
256 __IPV6_ADDR_MC_SCOPE(&in6m->in6m_addr) <
257 __IPV6_ADDR_SCOPE_LINKLOCAL)
258 continue;
259
260 if (IN6_IS_ADDR_UNSPECIFIED(&mldh->mld_addr) ||
261 IN6_ARE_ADDR_EQUAL(&mldh->mld_addr,
262 &in6m->in6m_addr))
263 {
264 if (timer == 0) {
265 /* send a report immediately */
266 mld6_sendpkt(in6m, MLD_LISTENER_REPORT,
267 NULL);
268 in6m->in6m_timer = 0; /* reset timer */
269 in6m->in6m_state = MLD_IREPORTEDLAST;
270 } else if (in6m->in6m_timer == 0 || /* idle */
271 in6m->in6m_timer > timer) {
272 in6m->in6m_timer =
273 MLD_RANDOM_DELAY(timer);
274 mld6_timers_are_running = 1;
275 }
276 }
277 }
278
279 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
280 mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
281 break;
282 case MLD_LISTENER_REPORT:
283 /*
284 * For fast leave to work, we have to know that we are the
285 * last person to send a report for this group. Reports
286 * can potentially get looped back if we are a multicast
287 * router, so discard reports sourced by me.
288 * Note that it is impossible to check IFF_LOOPBACK flag of
289 * ifp for this purpose, since ip6_mloopback pass the physical
290 * interface to if_input_local().
291 */
292 if (m->m_flags & M_LOOP) /* XXX: grotty flag, but efficient */
293 break;
294
295 if (!IN6_IS_ADDR_MULTICAST(&mldh->mld_addr))
296 break;
297
298 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
299 mldh->mld_addr.s6_addr16[1] =
300 htons(ifp->if_index); /* XXX */
301 /*
302 * If we belong to the group being reported, stop
303 * our timer for that group.
304 */
305 IN6_LOOKUP_MULTI(mldh->mld_addr, ifp, in6m);
306 if (in6m) {
307 in6m->in6m_timer = 0; /* transit to idle state */
308 in6m->in6m_state = MLD_OTHERLISTENER; /* clear flag */
309 }
310
311 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
312 mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
313 break;
314 default: /* this is impossible */
315 #if 0
316 /*
317 * this case should be impossible because of filtering in
318 * icmp6_input(). But we explicitly disabled this part
319 * just in case.
320 */
321 log(LOG_ERR, "mld_input: illegal type(%d)", mldh->mld_type);
322 #endif
323 break;
324 }
325 if_put(ifp);
326
327 m_freem(m);
328 }
329
330 void
331 mld6_fasttimeo(void)
332 {
333 struct ifnet *ifp;
334
335 /*
336 * Quick check to see if any work needs to be done, in order
337 * to minimize the overhead of fasttimo processing.
338 * Variable mld6_timers_are_running is read atomically, but without
339 * lock intentionally. In case it is not set due to MP races, we may
340 * miss to check the timers. Then run the loop at next fast timeout.
341 */
342 if (!mld6_timers_are_running)
343 return;
344
345 NET_LOCK();
346
347 mld6_timers_are_running = 0;
348 TAILQ_FOREACH(ifp, &ifnetlist, if_list)
349 mld6_checktimer(ifp);
350
351 NET_UNLOCK();
352 }
353
354 void
355 mld6_checktimer(struct ifnet *ifp)
356 {
357 struct in6_multi *in6m;
358 struct ifmaddr *ifma;
359
360 NET_ASSERT_LOCKED();
361
362 TAILQ_FOREACH(ifma, &ifp->if_maddrlist, ifma_list) {
363 if (ifma->ifma_addr->sa_family != AF_INET6)
364 continue;
365 in6m = ifmatoin6m(ifma);
366 if (in6m->in6m_timer == 0) {
367 /* do nothing */
368 } else if (--in6m->in6m_timer == 0) {
369 mld6_sendpkt(in6m, MLD_LISTENER_REPORT, NULL);
370 in6m->in6m_state = MLD_IREPORTEDLAST;
371 } else {
372 mld6_timers_are_running = 1;
373 }
374 }
375 }
376
377 static void
378 mld6_sendpkt(struct in6_multi *in6m, int type, const struct in6_addr *dst)
379 {
380 struct mbuf *mh, *md;
381 struct mld_hdr *mldh;
382 struct ip6_hdr *ip6;
383 struct ip6_moptions im6o;
384 struct in6_ifaddr *ia6;
385 struct ifnet *ifp;
386 int ignflags;
387
388 ifp = if_get(in6m->in6m_ifidx);
389 if (ifp == NULL)
390 return;
391
392 /*
393 * At first, find a link local address on the outgoing interface
394 * to use as the source address of the MLD packet.
395 * We do not reject tentative addresses for MLD report to deal with
396 * the case where we first join a link-local address.
397 */
398 ignflags = IN6_IFF_DUPLICATED|IN6_IFF_ANYCAST;
399 if ((ia6 = in6ifa_ifpforlinklocal(ifp, ignflags)) == NULL) {
400 if_put(ifp);
401 return;
402 }
403 if ((ia6->ia6_flags & IN6_IFF_TENTATIVE))
404 ia6 = NULL;
405
406 /*
407 * Allocate mbufs to store ip6 header and MLD header.
408 * We allocate 2 mbufs and make chain in advance because
409 * it is more convenient when inserting the hop-by-hop option later.
410 */
411 MGETHDR(mh, M_DONTWAIT, MT_HEADER);
412 if (mh == NULL) {
413 if_put(ifp);
414 return;
415 }
416 MGET(md, M_DONTWAIT, MT_DATA);
417 if (md == NULL) {
418 m_free(mh);
419 if_put(ifp);
420 return;
421 }
422 mh->m_next = md;
423
424 mh->m_pkthdr.ph_ifidx = 0;
425 mh->m_pkthdr.ph_rtableid = ifp->if_rdomain;
426 mh->m_pkthdr.len = sizeof(struct ip6_hdr) + sizeof(struct mld_hdr);
427 mh->m_len = sizeof(struct ip6_hdr);
428 m_align(mh, sizeof(struct ip6_hdr));
429
430 /* fill in the ip6 header */
431 ip6 = mtod(mh, struct ip6_hdr *);
432 ip6->ip6_flow = 0;
433 ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
434 ip6->ip6_vfc |= IPV6_VERSION;
435 /* ip6_plen will be set later */
436 ip6->ip6_nxt = IPPROTO_ICMPV6;
437 /* ip6_hlim will be set by im6o.im6o_hlim */
438 ip6->ip6_src = ia6 ? ia6->ia_addr.sin6_addr : in6addr_any;
439 ip6->ip6_dst = dst ? *dst : in6m->in6m_addr;
440
441 /* fill in the MLD header */
442 md->m_len = sizeof(struct mld_hdr);
443 mldh = mtod(md, struct mld_hdr *);
444 mldh->mld_type = type;
445 mldh->mld_code = 0;
446 mldh->mld_cksum = 0;
447 /* XXX: we assume the function will not be called for query messages */
448 mldh->mld_maxdelay = 0;
449 mldh->mld_reserved = 0;
450 mldh->mld_addr = in6m->in6m_addr;
451 if (IN6_IS_ADDR_MC_LINKLOCAL(&mldh->mld_addr))
452 mldh->mld_addr.s6_addr16[1] = 0; /* XXX */
453 mh->m_pkthdr.csum_flags |= M_ICMP_CSUM_OUT;
454
455 /* construct multicast option */
456 bzero(&im6o, sizeof(im6o));
457 im6o.im6o_ifidx = ifp->if_index;
458 im6o.im6o_hlim = 1;
459
460 /*
461 * Request loopback of the report if we are acting as a multicast
462 * router, so that the process-level routing daemon can hear it.
463 */
464 #ifdef MROUTING
465 im6o.im6o_loop = (ip6_mrouter[ifp->if_rdomain] != NULL);
466 #endif
467 if_put(ifp);
468
469 icmp6stat_inc(icp6s_outhist + type);
470 ip6_output(mh, &ip6_opts, NULL, ia6 ? 0 : IPV6_UNSPECSRC, &im6o,
471 NULL);
472 }
Cache object: 400142a22515cb5233f29b26e3a6044d
|