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
31 * Copyright (c) 1998 by the University of Oregon.
32 * All rights reserved.
34 * Permission to use, copy, modify, and distribute this software and
35 * its documentation in source and binary forms for lawful
36 * purposes and without fee is hereby granted, provided
37 * that the above copyright notice appear in all copies and that both
38 * the copyright notice and this permission notice appear in supporting
39 * documentation, and that any documentation, advertising materials,
40 * and other materials related to such distribution and use acknowledge
41 * that the software was developed by the University of Oregon.
42 * The name of the University of Oregon may not be used to endorse or
43 * promote products derived from this software without specific prior
46 * THE UNIVERSITY OF OREGON 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 UO, 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.
62 * Questions concerning this software should be directed to
63 * Kurt Windisch (kurtw@antc.uoregon.edu)
65 * $Id: mld6_proto.c,v 1.5 2000/10/05 22:20:38 itojun Exp $
68 * Part of this program has been derived from PIM sparse-mode pimd.
69 * The pimd program is covered by the license in the accompanying file
70 * named "LICENSE.pimd".
72 * The pimd program is COPYRIGHT 1998 by University of Southern California.
74 * Part of this program has been derived from mrouted.
75 * The mrouted program is covered by the license in the accompanying file
76 * named "LICENSE.mrouted".
78 * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
79 * Leland Stanford Junior University.
86 extern struct in6_addr in6addr_any;
96 * Forward declarations.
98 static void DelVif __P((void *arg));
99 static int SetTimer __P((int mifi, struct listaddr *g));
100 static int DeleteTimer __P((int id));
101 static void SendQuery __P((void *arg));
102 static int SetQueryTimer __P((struct listaddr *g, int mifi, int to_expire,
106 * Send group membership queries on that interface if I am querier.
110 register struct uvif *v;
112 register struct listaddr *g;
114 v->uv_gq_timer = MLD6_QUERY_INTERVAL;
115 if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0)
116 send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
117 NULL, (struct in6_addr *)&in6addr_any,
118 v->uv_ifindex, MLD6_QUERY_RESPONSE_INTERVAL, 0, 1);
121 * Decrement the old-hosts-present timer for each
122 * active group on that vif.
124 for (g = v->uv_groups; g != NULL; g = g->al_next)
125 if (g->al_old > TIMER_INTERVAL)
126 g->al_old -= TIMER_INTERVAL;
133 * Process an incoming host membership query
136 accept_listener_query(src, dst, group, tmo)
137 struct sockaddr_in6 *src;
138 struct in6_addr *dst, *group;
142 register struct uvif *v;
143 struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
145 /* Ignore my own membership query */
146 if (local_address(src) != NO_VIF)
149 if ((mifi = find_vif_direct(src)) == NO_VIF) {
152 "accept_listener_query: can't find a mif");
158 if (v->uv_querier == NULL || !inet6_equal(&v->uv_querier->al_addr, src))
162 * - A query from a new querier, with a lower source address
163 * than the current querier (who might be me)
164 * - A query from a new router that just started up and doesn't
165 * know who the querier is.
166 * - A query from the current querier
168 if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr
169 : &v->uv_linklocal->pa_addr))) {
171 log(LOG_DEBUG, 0, "new querier %s (was %s) "
173 inet6_fmt(&src->sin6_addr),
175 inet6_fmt(&v->uv_querier->al_addr.sin6_addr) :
177 if (!v->uv_querier) {
178 v->uv_querier = (struct listaddr *)
179 malloc(sizeof(struct listaddr));
180 memset(v->uv_querier, 0,
181 sizeof(struct listaddr));
183 v->uv_flags &= ~VIFF_QUERIER;
184 v->uv_querier->al_addr = *src;
185 time(&v->uv_querier->al_ctime);
190 * Reset the timer since we've received a query.
192 if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr))
193 v->uv_querier->al_timer = MLD6_OTHER_QUERIER_PRESENT_INTERVAL;
196 * If this is a Group-Specific query which we did not source,
197 * we must set our membership timer to [Last Member Query Count] *
198 * the [Max Response Time] in the packet.
200 if (!IN6_IS_ADDR_UNSPECIFIED(group) &&
201 inet6_equal(src, &v->uv_linklocal->pa_addr)) {
202 register struct listaddr *g;
206 "%s for %s from %s on mif %d, timer %d",
207 "Group-specific membership query",
209 inet6_fmt(&src->sin6_addr), mifi, tmo);
211 group_sa.sin6_addr = *group;
212 group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
213 for (g = v->uv_groups; g != NULL; g = g->al_next) {
214 if (inet6_equal(&group_sa, &g->al_addr)
215 && g->al_query == 0) {
216 /* setup a timeout to remove the group membership */
218 g->al_timerid = DeleteTimer(g->al_timerid);
219 g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT *
220 tmo / MLD6_TIMER_SCALE;
222 * use al_query to record our presence
223 * in last-member state
226 g->al_timerid = SetTimer(mifi, g);
229 "timer for grp %s on mif %d "
241 * Process an incoming group membership report.
244 accept_listener_report(src, dst, group)
245 struct sockaddr_in6 *src;
246 struct in6_addr *dst, *group;
248 register mifi_t mifi;
249 register struct uvif *v;
250 register struct listaddr *g;
251 struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
253 if (IN6_IS_ADDR_MC_LINKLOCAL(group)) {
256 "accept_listener_report: group(%s) has the "
257 "link-local scope. discard", inet6_fmt(group));
261 if ((mifi = find_vif_direct_local(src)) == NO_VIF) {
264 "accept_listener_report: can't find a mif");
270 "accepting multicast listener report: "
271 "src %s, dst %s, grp %s",
272 inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
278 * Look for the group in our group list; if found, reset its timer.
280 group_sa.sin6_addr = *group;
281 group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
282 for (g = v->uv_groups; g != NULL; g = g->al_next) {
283 if (inet6_equal(&group_sa, &g->al_addr)) {
284 g->al_reporter = *src;
286 /* delete old timers, set a timer for expiration */
287 g->al_timer = MLD6_LISTENER_INTERVAL;
289 g->al_query = DeleteTimer(g->al_query);
291 g->al_timerid = DeleteTimer(g->al_timerid);
292 g->al_timerid = SetTimer(mifi, g);
293 add_leaf(mifi, NULL, &group_sa);
299 * If not found, add it to the list and update kernel cache.
302 g = (struct listaddr *)malloc(sizeof(struct listaddr));
304 log(LOG_ERR, 0, "ran out of memory"); /* fatal */
306 g->al_addr = group_sa;
309 /** set a timer for expiration **/
311 g->al_timer = MLD6_LISTENER_INTERVAL;
312 g->al_reporter = *src;
313 g->al_timerid = SetTimer(mifi, g);
314 g->al_next = v->uv_groups;
318 add_leaf(mifi, NULL, &group_sa);
323 /* TODO: send PIM prune message if the last member? */
325 accept_listener_done(src, dst, group)
326 struct sockaddr_in6 *src;
327 struct in6_addr *dst, *group;
329 register mifi_t mifi;
330 register struct uvif *v;
331 register struct listaddr *g;
332 struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
334 if ((mifi = find_vif_direct_local(src)) == NO_VIF) {
337 "accept_listener_done: can't find a mif");
343 "accepting listener done message: src %s, dst %s, grp %s",
344 inet6_fmt(&src->sin6_addr),
345 inet6_fmt(dst), inet6_fmt(group));
349 if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
353 * Look for the group in our group list in order to set up a
354 * short-timeout query.
356 group_sa.sin6_addr = *group;
357 group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
358 for (g = v->uv_groups; g != NULL; g = g->al_next) {
359 if (inet6_equal(&group_sa, &g->al_addr)) {
362 "[accept_done_message] %d %ld\n",
363 g->al_old, g->al_query);
366 * Ignore the done message if there are old
373 * still waiting for a reply to a query,
379 /** delete old timer set a timer for expiration **/
381 g->al_timerid = DeleteTimer(g->al_timerid);
383 /** send a group specific querry **/
384 g->al_timer = (MLD6_LAST_LISTENER_QUERY_INTERVAL/MLD6_TIMER_SCALE) *
385 (MLD6_LAST_LISTENER_QUERY_COUNT + 1);
386 if (v->uv_flags & VIFF_QUERIER &&
387 (v->uv_flags & VIFF_NOLISTENER) == 0)
388 send_mld6(MLD6_LISTENER_QUERY, 0,
389 &v->uv_linklocal->pa_addr, NULL,
390 &g->al_addr.sin6_addr,
392 MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1);
393 g->al_query = SetQueryTimer(g, mifi,
394 MLD6_LAST_LISTENER_QUERY_INTERVAL/MLD6_TIMER_SCALE,
395 MLD6_LAST_LISTENER_QUERY_INTERVAL);
396 g->al_timerid = SetTimer(mifi, g);
404 * Time out record of a group membership on a vif
410 cbk_t *cbk = (cbk_t *)arg;
411 mifi_t mifi = cbk->mifi;
412 struct uvif *v = &uvifs[mifi];
413 struct listaddr *a, **anp, *g = cbk->g;
417 * delete all kernel cache entries with this group
420 DeleteTimer(g->al_query);
422 delete_leaf(mifi, NULL, &g->al_addr);
424 anp = &(v->uv_groups);
425 while ((a = *anp) != NULL) {
439 * Set a timer to delete the record of a group membership on a vif.
448 cbk = (cbk_t *) malloc(sizeof(cbk_t));
451 return timer_setTimer(g->al_timer, DelVif, cbk);
456 * Delete a timer that was set above.
462 timer_clearTimer(id);
468 * Send a group-specific query.
474 cbk_t *cbk = (cbk_t *)arg;
475 register struct uvif *v = &uvifs[cbk->mifi];
477 if (v->uv_flags & VIFF_QUERIER && (v->uv_flags & VIFF_NOLISTENER) == 0)
478 send_mld6(MLD6_LISTENER_QUERY, 0, &v->uv_linklocal->pa_addr,
479 NULL, &cbk->g->al_addr.sin6_addr,
480 v->uv_ifindex, cbk->q_time, 0, 1);
481 cbk->g->al_query = 0;
487 * Set a timer to send a group-specific query.
490 SetQueryTimer(g, mifi, to_expire, q_time)
498 cbk = (cbk_t *) malloc(sizeof(cbk_t));
500 cbk->q_time = q_time;
502 return timer_setTimer(to_expire, SendQuery, cbk);
505 /* Checks for MLD listener: returns TRUE if there is a receiver for the
506 * group on the given uvif, or returns FALSE otherwise.
509 check_multicast_listener(v, group)
511 struct sockaddr_in6 *group;
513 register struct listaddr *g;
516 * Look for the group in our listener list;
518 for (g = v->uv_groups; g != NULL; g = g->al_next) {
519 if (inet6_equal(group, &g->al_addr))