2 * Copyright (C) 1998 WIDE Project.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the project nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * Copyright (c) 1998 by the University of Southern California.
31 * All rights reserved.
33 * Permission to use, copy, modify, and distribute this software and
34 * its documentation in source and binary forms for lawful
35 * purposes and without fee is hereby granted, provided
36 * that the above copyright notice appear in all copies and that both
37 * the copyright notice and this permission notice appear in supporting
38 * documentation, and that any documentation, advertising materials,
39 * and other materials related to such distribution and use acknowledge
40 * that the software was developed by the University of Southern
41 * California and/or Information Sciences Institute.
42 * The name of the University of Southern California may not
43 * be used to endorse or promote products derived from this software
44 * without specific prior written permission.
46 * THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY REPRESENTATIONS
47 * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS
48 * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
49 * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
50 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
53 * IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
54 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
55 * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
56 * THE USE OR PERFORMANCE OF THIS SOFTWARE.
58 * Other copyrights might apply to parts of this software and are so
59 * noted when applicable.
64 * Questions concerning this software should be directed to
65 * Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
69 * This program has been derived from pim6dd.
70 * The pim6dd program is covered by the license in the accompanying file
71 * named "LICENSE.pim6dd".
74 * This program has been derived from pimd.
75 * The pimd program is covered by the license in the accompanying file
76 * named "LICENSE.pimd".
80 * Part of this program has been derived from mrouted.
81 * The mrouted program is covered by the license in the accompanying file
82 * named "LICENSE.mrouted".
84 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
85 * Leland Stanford Junior University.
89 * Part of this program has been derived from mrouted.
90 * The mrouted program is covered by the license in the accompanying file
91 * named "LICENSE.mrouted".
93 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
94 * Leland Stanford Junior University.
98 #include <sys/param.h>
99 #include <sys/types.h>
100 #include <sys/time.h>
101 #include <sys/socket.h>
102 #include <net/route.h>
103 #include <netinet/in.h>
104 #include <netinet6/ip6_mroute.h>
105 #include <netinet/icmp6.h>
116 extern struct in6_addr in6addr_any;
127 * Forward declarations.
129 static void DelVif __P((void *arg));
130 static int SetTimer __P((int mifi, struct listaddr * g));
131 static int DeleteTimer __P((int id));
132 static void SendQuery __P((void *arg));
133 static int SetQueryTimer
134 __P((struct listaddr * g, int mifi, int to_expire,
138 * Send group membership queries on that interface if I am querier.
142 register struct uvif *v;
144 register struct listaddr *g;
146 v->uv_gq_timer = MLD6_QUERY_INTERVAL;
147 if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) {
148 send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
149 NULL, (struct in6_addr *)&in6addr_any, v->uv_ifindex,
150 MLD6_QUERY_RESPONSE_INTERVAL, 0, 1);
151 v->uv_out_mld_query++;
155 * Decrement the old-hosts-present timer for each active group on that
158 for (g = v->uv_groups; g != NULL; g = g->al_next)
159 if (g->al_old > timer_interval)
160 g->al_old -= timer_interval;
167 * Process an incoming host membership query
170 accept_listener_query(src, dst, group, tmo)
171 struct sockaddr_in6 *src;
172 struct in6_addr *dst,
177 register struct uvif *v;
178 struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
180 /* Ignore my own membership query */
181 if (local_address(src) != NO_VIF)
184 if ((mifi = find_vif_direct(src)) == NO_VIF)
188 "accept_listener_query: can't find a mif");
194 "accepting multicast listener query: "
195 "src %s, dst %s, grp %s",
196 inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
200 v->uv_in_mld_query++;
202 if (v->uv_querier == NULL || inet6_equal(&v->uv_querier->al_addr, src))
205 * This might be: - A query from a new querier, with a lower source
206 * address than the current querier (who might be me) - A query from
207 * a new router that just started up and doesn't know who the querier
208 * is. - A query from the current querier
211 if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr
212 : &v->uv_linklocal->pa_addr)))
215 log(LOG_DEBUG, 0, "new querier %s (was %s) "
217 inet6_fmt(&src->sin6_addr),
219 inet6_fmt(&v->uv_querier->al_addr.sin6_addr) :
223 v->uv_querier = (struct listaddr *)malloc(sizeof(struct listaddr));
224 v->uv_querier->al_next = (struct listaddr *) NULL;
225 v->uv_querier->al_timer = 0;
226 v->uv_querier->al_genid = 0;
227 v->uv_querier->al_pv = 0;
228 v->uv_querier->al_mv = 0;
229 v->uv_querier->al_old = 0;
230 v->uv_querier->al_index = 0;
231 v->uv_querier->al_timerid = 0;
232 v->uv_querier->al_query = 0;
233 v->uv_querier->al_flags = 0;
235 v->uv_flags &= ~VIFF_QUERIER;
237 v->uv_querier->al_addr = *src;
238 time(&v->uv_querier->al_ctime);
243 * Reset the timer since we've received a query.
245 if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr))
246 v->uv_querier->al_timer = 0;
249 * If this is a Group-Specific query which we did not source, we must set
250 * our membership timer to [Last Member Query Count] * the [Max Response
251 * Time] in the packet.
253 if (!IN6_IS_ADDR_UNSPECIFIED(group) &&
254 inet6_equal(src, &v->uv_linklocal->pa_addr))
256 register struct listaddr *g;
260 "%s for %s from %s on mif %d, timer %d",
261 "Group-specific membership query",
263 inet6_fmt(&src->sin6_addr), mifi, tmo);
265 group_sa.sin6_addr = *group;
266 group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
267 for (g = v->uv_groups; g != NULL; g = g->al_next)
269 if (inet6_equal(&group_sa, &g->al_addr)
272 /* setup a timeout to remove the group membership */
274 g->al_timerid = DeleteTimer(g->al_timerid);
275 g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT *
276 tmo / MLD6_TIMER_SCALE;
278 * use al_query to record our presence in last-member state
281 g->al_timerid = SetTimer(mifi, g);
284 "timer for grp %s on mif %d "
296 * Process an incoming group membership report.
299 accept_listener_report(src, dst, group)
300 struct sockaddr_in6 *src;
301 struct in6_addr *dst,
304 register mifi_t mifi;
305 register struct uvif *v;
306 register struct listaddr *g;
307 struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
309 if (IN6_IS_ADDR_MC_LINKLOCAL(group)) {
312 "accept_listener_report: group(%s) has the "
313 "link-local scope. discard", inet6_fmt(group));
317 if ((mifi = find_vif_direct_local(src)) == NO_VIF)
321 "accept_listener_report: can't find a mif");
327 "accepting multicast listener report: "
328 "src %s,dst %s, grp %s",
329 inet6_fmt(&src->sin6_addr),inet6_fmt(dst),
333 v->uv_in_mld_report++;
336 * Look for the group in our group list; if found, reset its timer.
339 group_sa.sin6_addr = *group;
340 group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
342 for (g = v->uv_groups; g != NULL; g = g->al_next)
344 if (inet6_equal(&group_sa, &g->al_addr))
348 "The group already exist");
350 g->al_reporter = *src;
352 /* delete old timers, set a timer for expiration */
354 g->al_timer = MLD6_LISTENER_INTERVAL;
356 g->al_query = DeleteTimer(g->al_query);
358 g->al_timerid = DeleteTimer(g->al_timerid);
359 g->al_timerid = SetTimer(mifi, g);
360 add_leaf(mifi, NULL, &group_sa);
366 * If not found, add it to the list and update kernel cache.
372 "The group don't exist , trying to add it");
374 g = (struct listaddr *) malloc(sizeof(struct listaddr));
376 log(LOG_ERR, 0, "ran out of memory"); /* fatal */
378 g->al_addr = group_sa;
381 /** set a timer for expiration **/
383 g->al_timer = MLD6_LISTENER_INTERVAL;
384 g->al_reporter = *src;
385 g->al_timerid = SetTimer(mifi, g);
386 g->al_next = v->uv_groups;
390 add_leaf(mifi, NULL, &group_sa);
395 /* TODO: send PIM prune message if the last member? */
397 accept_listener_done(src, dst, group)
398 struct sockaddr_in6 *src;
399 struct in6_addr *dst,
402 register mifi_t mifi;
403 register struct uvif *v;
404 register struct listaddr *g;
405 struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
407 /* Don't create routing entries for the LAN scoped addresses */
409 if (IN6_IS_ADDR_MC_NODELOCAL(group)) /* sanity? */
413 "accept_listener_done: address multicast node local(%s),"
414 " ignore it...", inet6_fmt(group));
418 if (IN6_IS_ADDR_MC_LINKLOCAL(group))
422 "accept_listener_done: address multicast link local(%s), "
423 "ignore it ...", inet6_fmt(group));
427 if ((mifi = find_vif_direct_local(src)) == NO_VIF)
431 "accept_listener_done: can't find a mif");
437 "accepting listener done message: src %s, dst% s, grp %s",
438 inet6_fmt(&src->sin6_addr),
439 inet6_fmt(dst), inet6_fmt(group));
444 if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
448 * Look for the group in our group list in order to set up a
449 * short-timeout query.
451 group_sa.sin6_addr = *group;
452 group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
453 for (g = v->uv_groups; g != NULL; g = g->al_next)
455 if (inet6_equal(&group_sa, &g->al_addr))
459 "[accept_done_message] %d %d \n",
460 g->al_old, g->al_query);
463 * Ignore the done message if there are old hosts present
469 * still waiting for a reply to a query, ignore the done
474 /** delete old timer set a timer for expiration **/
476 g->al_timerid = DeleteTimer(g->al_timerid);
478 /** send a group specific querry **/
479 g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE) *
480 (MLD6_LAST_LISTENER_QUERY_COUNT + 1);
481 if (v->uv_flags & VIFF_QUERIER &&
482 (v->uv_flags & VIFF_NOLISTENER) == 0) {
483 send_mld6(MLD6_LISTENER_QUERY, 0,
484 &v->uv_linklocal->pa_addr, NULL,
485 &g->al_addr.sin6_addr,
487 MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1);
488 v->uv_out_mld_query++;
490 g->al_query = SetQueryTimer(g, mifi,
491 MLD6_LAST_LISTENER_QUERY_INTERVAL / MLD6_TIMER_SCALE,
492 MLD6_LAST_LISTENER_QUERY_INTERVAL);
493 g->al_timerid = SetTimer(mifi, g);
501 * Time out record of a group membership on a vif
507 cbk_t *cbk = (cbk_t *) arg;
508 mifi_t mifi = cbk->mifi;
509 struct uvif *v = &uvifs[mifi];
515 * Group has expired delete all kernel cache entries with this group
518 DeleteTimer(g->al_query);
520 delete_leaf(mifi, NULL, &g->al_addr);
522 /* increment statistics */
523 v->uv_listener_timo++;
525 anp = &(v->uv_groups);
526 while ((a = *anp) != NULL)
544 * Set a timer to delete the record of a group membership on a vif.
553 cbk = (cbk_t *) malloc(sizeof(cbk_t));
556 return timer_setTimer(g->al_timer, DelVif, cbk);
561 * Delete a timer that was set above.
567 timer_clearTimer(id);
573 * Send a group-specific query.
579 cbk_t *cbk = (cbk_t *) arg;
580 register struct uvif *v = &uvifs[cbk->mifi];
582 if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0) {
583 send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
584 NULL, &cbk->g->al_addr.sin6_addr, v->uv_ifindex,
586 v->uv_out_mld_query++;
588 cbk->g->al_query = 0;
594 * Set a timer to send a group-specific query.
597 SetQueryTimer(g, mifi, to_expire, q_time)
605 cbk = (cbk_t *) malloc(sizeof(cbk_t));
607 cbk->q_time = q_time;
609 return timer_setTimer(to_expire, SendQuery, cbk);
613 * Checks for MLD listener: returns TRUE if there is a receiver for the group
614 * on the given uvif, or returns FALSE otherwise.
617 check_multicast_listener(v, group)
619 struct sockaddr_in6 *group;
621 register struct listaddr *g;
624 * Look for the group in our listener list;
626 for (g = v->uv_groups; g != NULL; g = g->al_next)
628 if (inet6_equal(group, &g->al_addr))