]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pim6dd/mld6_proto.c
This commit was generated by cvs2svn to compensate for changes in r69836,
[FreeBSD/FreeBSD.git] / usr.sbin / pim6dd / mld6_proto.c
1 /*
2  * Copyright (C) 1998 WIDE Project.
3  * All rights reserved.
4  * 
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  * 
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
27  * SUCH DAMAGE.
28  */
29
30 /*
31  *  Copyright (c) 1998 by the University of Oregon.
32  *  All rights reserved.
33  *
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 
44  *  written permission.
45  *
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 
51  *  NON-INFRINGEMENT.
52  *
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.
57  *
58  *  Other copyrights might apply to parts of this software and are so
59  *  noted when applicable.
60  */
61 /*
62  *  Questions concerning this software should be directed to 
63  *  Kurt Windisch (kurtw@antc.uoregon.edu)
64  *
65  *  $Id: mld6_proto.c,v 1.5 2000/10/05 22:20:38 itojun Exp $
66  */
67 /*
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".
71  *  
72  * The pimd program is COPYRIGHT 1998 by University of Southern California.
73  *
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".
77  * 
78  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
79  * Leland Stanford Junior University.
80  *
81  * $FreeBSD$
82  */
83
84 #include "defs.h"
85
86 extern struct in6_addr in6addr_any;
87
88 typedef struct {
89         mifi_t  mifi;
90         struct listaddr *g;
91         int    q_time;
92 } cbk_t;
93
94
95 /*
96  * Forward declarations.
97  */
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,
103                               int q_time));
104
105 /*
106  * Send group membership queries on that interface if I am querier.
107  */
108 void
109 query_groups(v)
110         register struct uvif *v;
111 {
112         register struct listaddr *g;
113     
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);
119
120         /*
121          * Decrement the old-hosts-present timer for each
122          * active group on that vif.
123          */
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;
127                 else
128                         g->al_old = 0;
129 }
130
131
132 /*
133  * Process an incoming host membership query
134  */
135 void
136 accept_listener_query(src, dst, group, tmo)
137         struct sockaddr_in6 *src;
138         struct in6_addr *dst, *group;
139         int tmo;
140 {
141         register int mifi;
142         register struct uvif *v;
143         struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
144
145         /* Ignore my own membership query */
146         if (local_address(src) != NO_VIF)
147                 return;
148
149         if ((mifi = find_vif_direct(src)) == NO_VIF) {
150                 IF_DEBUG(DEBUG_MLD)
151                         log(LOG_INFO, 0,
152                             "accept_listener_query: can't find a mif");
153                 return;
154         }
155
156         v = &uvifs[mifi];
157
158         if (v->uv_querier == NULL || !inet6_equal(&v->uv_querier->al_addr, src))
159         {
160                 /*
161                  * This might be:
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
167                  */
168                 if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr
169                                          : &v->uv_linklocal->pa_addr))) {
170                         IF_DEBUG(DEBUG_MLD)
171                                 log(LOG_DEBUG, 0, "new querier %s (was %s) "
172                                     "on mif %d",
173                                     inet6_fmt(&src->sin6_addr),
174                                     v->uv_querier ?
175                                     inet6_fmt(&v->uv_querier->al_addr.sin6_addr) :
176                                     "me", mifi);
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));
182                         }
183                         v->uv_flags &= ~VIFF_QUERIER;
184                         v->uv_querier->al_addr = *src;
185                         time(&v->uv_querier->al_ctime);
186                 }
187         }
188     
189         /*
190          * Reset the timer since we've received a query.
191          */
192         if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr))
193                 v->uv_querier->al_timer = MLD6_OTHER_QUERIER_PRESENT_INTERVAL;
194
195         /*
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.
199          */
200         if (!IN6_IS_ADDR_UNSPECIFIED(group) &&
201             inet6_equal(src, &v->uv_linklocal->pa_addr)) {
202                 register struct listaddr *g;
203
204                 IF_DEBUG(DEBUG_MLD)
205                         log(LOG_DEBUG, 0,
206                             "%s for %s from %s on mif %d, timer %d",
207                             "Group-specific membership query",
208                             inet6_fmt(group),
209                             inet6_fmt(&src->sin6_addr), mifi, tmo);
210
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 */
217                                 if (g->al_timerid)
218                                         g->al_timerid = DeleteTimer(g->al_timerid);
219                                 g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT *
220                                         tmo / MLD6_TIMER_SCALE;
221                                 /*
222                                  * use al_query to record our presence
223                                  * in last-member state
224                                  */
225                                 g->al_query = -1;
226                                 g->al_timerid = SetTimer(mifi, g);
227                                 IF_DEBUG(DEBUG_MLD)
228                                         log(LOG_DEBUG, 0,
229                                             "timer for grp %s on mif %d "
230                                             "set to %ld",
231                                             inet6_fmt(group),
232                                             mifi, g->al_timer);
233                                 break;
234                         }
235                 }
236         }
237 }
238
239
240 /*
241  * Process an incoming group membership report.
242  */
243 void
244 accept_listener_report(src, dst, group)
245         struct sockaddr_in6 *src;
246         struct in6_addr *dst, *group;
247 {
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};
252
253         if (IN6_IS_ADDR_MC_LINKLOCAL(group)) {
254                 IF_DEBUG(DEBUG_MLD)
255                         log(LOG_DEBUG, 0,
256                             "accept_listener_report: group(%s) has the "
257                             "link-local scope. discard", inet6_fmt(group));
258                 return;
259         }
260
261         if ((mifi = find_vif_direct_local(src)) == NO_VIF) {
262                 IF_DEBUG(DEBUG_MLD)
263                         log(LOG_INFO, 0,
264                             "accept_listener_report: can't find a mif");
265                 return;
266         }
267     
268         IF_DEBUG(DEBUG_MLD)
269                 log(LOG_INFO, 0,
270                     "accepting multicast listener report: "
271                     "src %s, dst %s, grp %s",
272                     inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
273                     inet6_fmt(group));
274
275         v = &uvifs[mifi];
276     
277         /*
278          * Look for the group in our group list; if found, reset its timer.
279          */
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;
285
286                         /* delete old timers, set a timer for expiration */
287                         g->al_timer = MLD6_LISTENER_INTERVAL;
288                         if (g->al_query)
289                                 g->al_query = DeleteTimer(g->al_query);
290                         if (g->al_timerid)
291                                 g->al_timerid = DeleteTimer(g->al_timerid);
292                         g->al_timerid = SetTimer(mifi, g);
293                         add_leaf(mifi, NULL, &group_sa);
294                         break;
295                 }
296         }
297
298         /*
299          * If not found, add it to the list and update kernel cache.
300          */
301         if (g == NULL) {
302                 g = (struct listaddr *)malloc(sizeof(struct listaddr));
303                 if (g == NULL)
304                         log(LOG_ERR, 0, "ran out of memory");    /* fatal */
305
306                 g->al_addr   = group_sa;
307                 g->al_old = 0;
308
309                 /** set a timer for expiration **/
310                 g->al_query     = 0;
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;
315                 v->uv_groups    = g;
316                 time(&g->al_ctime);
317
318                 add_leaf(mifi, NULL, &group_sa);
319         }
320 }
321
322
323 /* TODO: send PIM prune message if the last member? */
324 void
325 accept_listener_done(src, dst, group)
326         struct sockaddr_in6 *src;
327         struct in6_addr *dst, *group;
328 {
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};
333
334         if ((mifi = find_vif_direct_local(src)) == NO_VIF) {
335                 IF_DEBUG(DEBUG_MLD)
336                         log(LOG_INFO, 0,
337                             "accept_listener_done: can't find a mif");
338                 return;
339         }
340
341         IF_DEBUG(DEBUG_MLD)
342                 log(LOG_INFO, 0,
343                     "accepting listener done message: src %s, dst %s, grp %s",
344                     inet6_fmt(&src->sin6_addr),
345                     inet6_fmt(dst), inet6_fmt(group));
346
347         v = &uvifs[mifi];
348     
349         if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
350                 return;
351
352         /*
353          * Look for the group in our group list in order to set up a
354          * short-timeout query.
355          */
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)) {
360                         IF_DEBUG(DEBUG_MLD)
361                                 log(LOG_DEBUG, 0,
362                                     "[accept_done_message] %d %ld\n",
363                                     g->al_old, g->al_query);
364
365                         /*
366                          * Ignore the done message if there are old
367                          * hosts present
368                          */
369                         if (g->al_old)
370                                 return;
371             
372                         /*
373                          * still waiting for a reply to a query,
374                          * ignore the done
375                          */
376                         if (g->al_query)
377                                 return;
378             
379                         /** delete old timer set a timer for expiration **/
380                         if (g->al_timerid)
381                                 g->al_timerid = DeleteTimer(g->al_timerid);
382
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,
391                                           v->uv_ifindex,
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);
397                         break;
398                 }
399         }
400 }
401
402
403 /*
404  * Time out record of a group membership on a vif
405  */
406 static void
407 DelVif(arg)
408         void *arg;
409 {
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;
414
415         /*
416          * Group has expired
417          * delete all kernel cache entries with this group
418          */
419         if (g->al_query)
420                 DeleteTimer(g->al_query);
421
422         delete_leaf(mifi, NULL, &g->al_addr);
423
424         anp = &(v->uv_groups);
425         while ((a = *anp) != NULL) {
426                 if (a == g) {
427                         *anp = a->al_next;
428                         free((char *)a);
429                 } else {
430                         anp = &a->al_next;
431                 }
432         }
433
434         free(cbk);
435 }
436
437
438 /*
439  * Set a timer to delete the record of a group membership on a vif.
440  */
441 static int
442 SetTimer(mifi, g)
443         mifi_t mifi;
444         struct listaddr *g;
445 {
446         cbk_t *cbk;
447     
448         cbk = (cbk_t *) malloc(sizeof(cbk_t));
449         cbk->mifi = mifi;
450         cbk->g = g;
451         return timer_setTimer(g->al_timer, DelVif, cbk);
452 }
453
454
455 /*
456  * Delete a timer that was set above.
457  */
458 static int
459 DeleteTimer(id)
460         int id;
461 {
462         timer_clearTimer(id);
463         return 0;
464 }
465
466
467 /*
468  * Send a group-specific query.
469  */
470 static void
471 SendQuery(arg)
472         void *arg;
473 {
474         cbk_t *cbk = (cbk_t *)arg;
475         register struct uvif *v = &uvifs[cbk->mifi];
476
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;
482         free(cbk);
483 }
484
485
486 /*
487  * Set a timer to send a group-specific query.
488  */
489 static int
490 SetQueryTimer(g, mifi, to_expire, q_time)
491         struct listaddr *g;
492         mifi_t mifi;
493         int to_expire;
494         int q_time;
495 {
496         cbk_t *cbk;
497
498         cbk = (cbk_t *) malloc(sizeof(cbk_t));
499         cbk->g = g;
500         cbk->q_time = q_time;
501         cbk->mifi = mifi;
502         return timer_setTimer(to_expire, SendQuery, cbk);
503 }
504
505 /* Checks for MLD listener: returns TRUE if there is a receiver for the
506  * group on the given uvif, or returns FALSE otherwise.
507  */
508 int
509 check_multicast_listener(v, group)
510         struct uvif *v;
511         struct sockaddr_in6 *group;
512 {
513         register struct listaddr *g;
514
515         /*
516          * Look for the group in our listener list;
517          */
518         for (g = v->uv_groups; g != NULL; g = g->al_next) {
519                 if (inet6_equal(group, &g->al_addr)) 
520                         return TRUE;
521         }
522         return FALSE;
523 }