]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pim6sd/mld6_proto.c
This commit was generated by cvs2svn to compensate for changes in r57416,
[FreeBSD/FreeBSD.git] / usr.sbin / pim6sd / 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  *  Copyright (c) 1998 by the University of Southern California.
31  *  All rights reserved.
32  *
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.
45  *
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
51  *  NON-INFRINGEMENT.
52  *
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.
57  *
58  *  Other copyrights might apply to parts of this software and are so
59  *  noted when applicable.
60  *
61  * $FreeBSD$
62  */
63 /*
64  *  Questions concerning this software should be directed to
65  *  Mickael Hoerdt (hoerdt@clarinet.u-strasbg.fr) LSIIT Strasbourg.
66  *
67  */
68 /*
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".
72  */
73 /*
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".
77  *
78  */
79 /*
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".
83  *
84  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
85  * Leland Stanford Junior University.
86  *
87  */
88 /*
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".
92  *
93  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
94  * Leland Stanford Junior University.
95  *
96  */
97
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>
106 #include <syslog.h>
107 #include <stdlib.h>
108 #include "mld6.h"
109 #include "vif.h"
110 #include "debug.h"
111 #include "inet6.h"
112 #include "route.h"
113 #include "callout.h"
114 #include "timer.h"
115
116 extern struct in6_addr in6addr_any;
117
118 typedef struct
119 {
120     mifi_t          mifi;
121     struct listaddr *g;
122     int             q_time;
123 }               cbk_t;
124
125
126 /*
127  * Forward declarations.
128  */
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,
135      int q_time));
136
137 /*
138  * Send group membership queries on that interface if I am querier.
139  */
140 void
141 query_groups(v)
142         register struct uvif *v;
143 {
144     register struct listaddr *g;
145
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++;
152     }
153
154     /*
155      * Decrement the old-hosts-present timer for each active group on that
156      * vif.
157      */
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;
161         else
162             g->al_old = 0;
163 }
164
165
166 /*
167  * Process an incoming host membership query
168  */
169 void
170 accept_listener_query(src, dst, group, tmo)
171     struct sockaddr_in6 *src;
172     struct in6_addr *dst,
173                    *group;
174     int             tmo;
175 {
176     register int    mifi;
177     register struct uvif *v;
178     struct sockaddr_in6 group_sa = {sizeof(group_sa), AF_INET6};
179
180     /* Ignore my own membership query */
181     if (local_address(src) != NO_VIF)
182         return;
183
184     if ((mifi = find_vif_direct(src)) == NO_VIF)
185     {
186         IF_DEBUG(DEBUG_MLD)
187             log(LOG_INFO, 0,
188                 "accept_listener_query: can't find a mif");
189         return;
190     }
191
192     IF_DEBUG(DEBUG_MLD)
193         log(LOG_DEBUG, 0,
194             "accepting multicast listener query: "
195             "src %s, dst %s, grp %s",
196             inet6_fmt(&src->sin6_addr), inet6_fmt(dst),
197             inet6_fmt(group));
198
199     v = &uvifs[mifi];
200     v->uv_in_mld_query++;
201
202     if (v->uv_querier == NULL || inet6_equal(&v->uv_querier->al_addr, src))
203     {
204         /*
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
209          */
210
211         if (inet6_lessthan(src, (v->uv_querier ? &v->uv_querier->al_addr
212                                  : &v->uv_linklocal->pa_addr)))
213         {
214             IF_DEBUG(DEBUG_MLD)
215                 log(LOG_DEBUG, 0, "new querier %s (was %s) "
216                     "on mif %d",
217                     inet6_fmt(&src->sin6_addr),
218                     v->uv_querier ?
219                     inet6_fmt(&v->uv_querier->al_addr.sin6_addr) :
220                     "me", mifi);
221             if (!v->uv_querier)
222             {
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;
234
235                 v->uv_flags &= ~VIFF_QUERIER;
236             }
237             v->uv_querier->al_addr = *src;
238             time(&v->uv_querier->al_ctime);
239         }
240     }
241
242     /*
243      * Reset the timer since we've received a query.
244      */
245     if (v->uv_querier && inet6_equal(src, &v->uv_querier->al_addr))
246         v->uv_querier->al_timer = 0;
247
248     /*
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.
252      */
253     if (!IN6_IS_ADDR_UNSPECIFIED(group) &&
254         inet6_equal(src, &v->uv_linklocal->pa_addr))
255     {
256         register struct listaddr *g;
257
258         IF_DEBUG(DEBUG_MLD)
259             log(LOG_DEBUG, 0,
260                 "%s for %s from %s on mif %d, timer %d",
261                 "Group-specific membership query",
262                 inet6_fmt(group),
263                 inet6_fmt(&src->sin6_addr), mifi, tmo);
264
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)
268         {
269             if (inet6_equal(&group_sa, &g->al_addr)
270                 && g->al_query == 0)
271             {
272                 /* setup a timeout to remove the group membership */
273                 if (g->al_timerid)
274                     g->al_timerid = DeleteTimer(g->al_timerid);
275                 g->al_timer = MLD6_LAST_LISTENER_QUERY_COUNT *
276                     tmo / MLD6_TIMER_SCALE;
277                 /*
278                  * use al_query to record our presence in last-member state
279                  */
280                 g->al_query = -1;
281                 g->al_timerid = SetTimer(mifi, g);
282                 IF_DEBUG(DEBUG_MLD)
283                     log(LOG_DEBUG, 0,
284                         "timer for grp %s on mif %d "
285                         "set to %d",
286                         inet6_fmt(group),
287                         mifi, g->al_timer);
288                 break;
289             }
290         }
291     }
292 }
293
294
295 /*
296  * Process an incoming group membership report.
297  */
298 void
299 accept_listener_report(src, dst, group)
300     struct sockaddr_in6 *src;
301     struct in6_addr *dst,
302                    *group;
303 {
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};
308
309     if (IN6_IS_ADDR_MC_LINKLOCAL(group)) {
310             IF_DEBUG(DEBUG_MLD)
311                     log(LOG_DEBUG, 0,
312                         "accept_listener_report: group(%s) has the "
313                         "link-local scope. discard", inet6_fmt(group));
314             return;
315     }
316
317     if ((mifi = find_vif_direct_local(src)) == NO_VIF)
318     {
319         IF_DEBUG(DEBUG_MLD)
320             log(LOG_INFO, 0,
321                 "accept_listener_report: can't find a mif");
322         return;
323     }
324
325     IF_DEBUG(DEBUG_MLD)
326         log(LOG_DEBUG, 0,
327             "accepting multicast listener report: "
328             "src %s,dst %s, grp %s",
329             inet6_fmt(&src->sin6_addr),inet6_fmt(dst),
330             inet6_fmt(group));
331
332     v = &uvifs[mifi];
333     v->uv_in_mld_report++;
334
335     /*
336      * Look for the group in our group list; if found, reset its timer.
337      */
338
339     group_sa.sin6_addr = *group;
340     group_sa.sin6_scope_id = inet6_uvif2scopeid(&group_sa, v);
341
342     for (g = v->uv_groups; g != NULL; g = g->al_next)
343     {
344         if (inet6_equal(&group_sa, &g->al_addr))
345         {
346             IF_DEBUG(DEBUG_MLD)
347                 log(LOG_DEBUG,0,
348                 "The group already exist");
349
350             g->al_reporter = *src;
351
352             /* delete old timers, set a timer for expiration */
353
354             g->al_timer = MLD6_LISTENER_INTERVAL;
355             if (g->al_query)
356                 g->al_query = DeleteTimer(g->al_query);
357             if (g->al_timerid)
358                 g->al_timerid = DeleteTimer(g->al_timerid);
359             g->al_timerid = SetTimer(mifi, g);
360             add_leaf(mifi, NULL, &group_sa);
361             break;
362         }
363     }
364
365     /*
366      * If not found, add it to the list and update kernel cache.
367      */
368     if (g == NULL)
369     {
370         IF_DEBUG(DEBUG_MLD)
371             log(LOG_DEBUG,0,
372             "The group don't exist , trying to add it");
373
374         g = (struct listaddr *) malloc(sizeof(struct listaddr));
375         if (g == NULL)
376             log(LOG_ERR, 0, "ran out of memory");       /* fatal */
377
378         g->al_addr = group_sa;
379         g->al_old = 0;
380
381         /** set a timer for expiration **/
382         g->al_query = 0;
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;
387         v->uv_groups = g;
388         time(&g->al_ctime);
389
390         add_leaf(mifi, NULL, &group_sa);
391     }
392 }
393
394
395 /* TODO: send PIM prune message if the last member? */
396 void
397 accept_listener_done(src, dst, group)
398     struct sockaddr_in6 *src;
399     struct in6_addr *dst,
400                    *group;
401 {
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};
406
407             /* Don't create routing entries for the LAN scoped addresses */
408
409     if (IN6_IS_ADDR_MC_NODELOCAL(group)) /* sanity? */
410     {
411     IF_DEBUG(DEBUG_MLD)
412         log(LOG_DEBUG, 0,
413             "accept_listener_done: address multicast node local(%s),"
414             " ignore it...", inet6_fmt(group));
415     return;
416     }
417
418     if (IN6_IS_ADDR_MC_LINKLOCAL(group))
419     {
420     IF_DEBUG(DEBUG_MLD)
421         log(LOG_DEBUG, 0,
422             "accept_listener_done: address multicast link local(%s), "
423             "ignore it ...", inet6_fmt(group));
424     return;
425     }
426
427     if ((mifi = find_vif_direct_local(src)) == NO_VIF)
428     {
429         IF_DEBUG(DEBUG_MLD)
430             log(LOG_INFO, 0,
431                 "accept_listener_done: can't find a mif");
432         return;
433     }
434
435     IF_DEBUG(DEBUG_MLD)
436         log(LOG_INFO, 0,
437             "accepting listener done message: src %s, dst% s, grp %s",
438             inet6_fmt(&src->sin6_addr),
439             inet6_fmt(dst), inet6_fmt(group));
440
441     v = &uvifs[mifi];
442     v->uv_in_mld_done++;
443
444     if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR)))
445         return;
446
447     /*
448      * Look for the group in our group list in order to set up a
449      * short-timeout query.
450      */
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)
454     {
455         if (inet6_equal(&group_sa, &g->al_addr))
456         {
457             IF_DEBUG(DEBUG_MLD)
458                 log(LOG_DEBUG, 0,
459                     "[accept_done_message] %d %d \n",
460                     g->al_old, g->al_query);
461
462             /*
463              * Ignore the done message if there are old hosts present
464              */
465             if (g->al_old)
466                 return;
467
468             /*
469              * still waiting for a reply to a query, ignore the done
470              */
471             if (g->al_query)
472                 return;
473
474             /** delete old timer set a timer for expiration **/
475             if (g->al_timerid)
476                 g->al_timerid = DeleteTimer(g->al_timerid);
477
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,
486                               v->uv_ifindex,
487                               MLD6_LAST_LISTENER_QUERY_INTERVAL, 0, 1);
488                     v->uv_out_mld_query++;
489             }
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);
494             break;
495         }
496     }
497 }
498
499
500 /*
501  * Time out record of a group membership on a vif
502  */
503 static void
504 DelVif(arg)
505     void           *arg;
506 {
507     cbk_t          *cbk = (cbk_t *) arg;
508     mifi_t          mifi = cbk->mifi;
509     struct uvif    *v = &uvifs[mifi];
510     struct listaddr *a,
511                   **anp,
512                    *g = cbk->g;
513
514     /*
515      * Group has expired delete all kernel cache entries with this group
516      */
517     if (g->al_query)
518         DeleteTimer(g->al_query);
519
520     delete_leaf(mifi, NULL, &g->al_addr);
521
522     /* increment statistics */
523     v->uv_listener_timo++;
524
525     anp = &(v->uv_groups);
526     while ((a = *anp) != NULL)
527     {
528         if (a == g)
529         {
530             *anp = a->al_next;
531             free((char *) a);
532         }
533         else
534         {
535             anp = &a->al_next;
536         }
537     }
538
539     free(cbk);
540 }
541
542
543 /*
544  * Set a timer to delete the record of a group membership on a vif.
545  */
546 static int
547 SetTimer(mifi, g)
548     mifi_t          mifi;
549     struct listaddr *g;
550 {
551     cbk_t          *cbk;
552
553     cbk = (cbk_t *) malloc(sizeof(cbk_t));
554     cbk->mifi = mifi;
555     cbk->g = g;
556     return timer_setTimer(g->al_timer, DelVif, cbk);
557 }
558
559
560 /*
561  * Delete a timer that was set above.
562  */
563 static int
564 DeleteTimer(id)
565     int             id;
566 {
567     timer_clearTimer(id);
568     return 0;
569 }
570
571
572 /*
573  * Send a group-specific query.
574  */
575 static void
576 SendQuery(arg)
577     void           *arg;
578 {
579     cbk_t          *cbk = (cbk_t *) arg;
580     register struct uvif *v = &uvifs[cbk->mifi];
581
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,
585                   cbk->q_time, 0, 1);
586         v->uv_out_mld_query++;
587     }
588     cbk->g->al_query = 0;
589     free(cbk);
590 }
591
592
593 /*
594  * Set a timer to send a group-specific query.
595  */
596 static int
597 SetQueryTimer(g, mifi, to_expire, q_time)
598     struct listaddr *g;
599     mifi_t          mifi;
600     int             to_expire;
601     int             q_time;
602 {
603     cbk_t          *cbk;
604
605     cbk = (cbk_t *) malloc(sizeof(cbk_t));
606     cbk->g = g;
607     cbk->q_time = q_time;
608     cbk->mifi = mifi;
609     return timer_setTimer(to_expire, SendQuery, cbk);
610 }
611
612 /*
613  * Checks for MLD listener: returns TRUE if there is a receiver for the group
614  * on the given uvif, or returns FALSE otherwise.
615  */
616 int
617 check_multicast_listener(v, group)
618     struct uvif    *v;
619     struct sockaddr_in6 *group;
620 {
621     register struct listaddr *g;
622
623     /*
624      * Look for the group in our listener list;
625      */
626     for (g = v->uv_groups; g != NULL; g = g->al_next)
627     {
628         if (inet6_equal(group, &g->al_addr))
629             return TRUE;
630     }
631     return FALSE;
632 }