File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / pimdd / igmp_proto.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jun 12 07:58:55 2017 UTC (7 years ago) by misho
Branches: pimdd, MAIN
CVS tags: v0_2_1p0, v0_2_1, HEAD
pimdd-dense 0.2.1.0_2

    1: /*
    2:  *  Copyright (c) 1998 by the University of Oregon.
    3:  *  All rights reserved.
    4:  *
    5:  *  Permission to use, copy, modify, and distribute this software and
    6:  *  its documentation in source and binary forms for lawful
    7:  *  purposes and without fee is hereby granted, provided
    8:  *  that the above copyright notice appear in all copies and that both
    9:  *  the copyright notice and this permission notice appear in supporting
   10:  *  documentation, and that any documentation, advertising materials,
   11:  *  and other materials related to such distribution and use acknowledge
   12:  *  that the software was developed by the University of Oregon.
   13:  *  The name of the University of Oregon may not be used to endorse or 
   14:  *  promote products derived from this software without specific prior 
   15:  *  written permission.
   16:  *
   17:  *  THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
   18:  *  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
   19:  *  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
   20:  *  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
   21:  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND 
   22:  *  NON-INFRINGEMENT.
   23:  *
   24:  *  IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
   25:  *  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
   26:  *  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
   27:  *  THE USE OR PERFORMANCE OF THIS SOFTWARE.
   28:  *
   29:  *  Other copyrights might apply to parts of this software and are so
   30:  *  noted when applicable.
   31:  */
   32: /*
   33:  *  Questions concerning this software should be directed to 
   34:  *  Kurt Windisch (kurtw@antc.uoregon.edu)
   35:  *
   36:  *  $Id: igmp_proto.c,v 1.1.1.1 2017/06/12 07:58:55 misho Exp $
   37:  */
   38: /*
   39:  * Part of this program has been derived from PIM sparse-mode pimd.
   40:  * The pimd program is covered by the license in the accompanying file
   41:  * named "LICENSE.pimd".
   42:  *  
   43:  * The pimd program is COPYRIGHT 1998 by University of Southern California.
   44:  *
   45:  * Part of this program has been derived from mrouted.
   46:  * The mrouted program is covered by the license in the accompanying file
   47:  * named "LICENSE.mrouted".
   48:  * 
   49:  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
   50:  * Leland Stanford Junior University.
   51:  *
   52:  */
   53: 
   54: #include "defs.h" 
   55: 
   56: 
   57: typedef struct {
   58:     vifi_t  vifi;
   59:     struct listaddr *g;
   60:     int    q_time;
   61: } cbk_t;
   62: 
   63: 
   64: /*
   65:  * Forward declarations.
   66:  */
   67: static void DelVif __P((void *arg));
   68: static int SetTimer __P((int vifi, struct listaddr *g));
   69: static int DeleteTimer __P((int id));
   70: static void SendQuery __P((void *arg));
   71: static int SetQueryTimer __P((struct listaddr *g, vifi_t vifi, int to_expire,
   72: 			      int q_time));
   73: 
   74: 
   75: /*
   76:  * Send group membership queries on that interface if I am querier.
   77:  */
   78: void
   79: query_groups(v)
   80:     register struct uvif *v;
   81: {
   82:     register struct listaddr *g;
   83:     
   84:     v->uv_gq_timer = IGMP_QUERY_INTERVAL;
   85:     if (v->uv_flags & VIFF_QUERIER)
   86: 	send_igmp(igmp_send_buf, v->uv_lcl_addr, allhosts_group,
   87: 		  IGMP_MEMBERSHIP_QUERY, 
   88: 		  (v->uv_flags & VIFF_IGMPV1) ? 0 :
   89: 		  IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
   90:     /*
   91:      * Decrement the old-hosts-present timer for each
   92:      * active group on that vif.
   93:      */
   94:     for (g = v->uv_groups; g != NULL; g = g->al_next)
   95: 	if (g->al_old > TIMER_INTERVAL)
   96: 	    g->al_old -= TIMER_INTERVAL;
   97: 	else
   98: 	    g->al_old = 0;
   99: }
  100: 
  101: 
  102: /*
  103:  * Process an incoming host membership query
  104:  */
  105: void
  106: accept_membership_query(src, dst, group, tmo)
  107:     u_int32 src, dst, group;
  108:     int  tmo;
  109: {
  110:     register vifi_t vifi;
  111:     register struct uvif *v;
  112:     
  113:     /* Ignore my own membership query */
  114:     if (local_address(src) != NO_VIF)
  115: 	return;
  116: 
  117:     /* TODO: modify for DVMRP?? */
  118:     if ((vifi = find_vif_direct(src)) == NO_VIF) {
  119: 	IF_DEBUG(DEBUG_IGMP)
  120: 	    log(LOG_INFO, 0,
  121: 		"ignoring group membership query from non-adjacent host %s",
  122: 		inet_fmt(src, s1));
  123: 	return;
  124:     }
  125:     
  126:     v = &uvifs[vifi];
  127:     
  128:     if ((tmo == 0 && !(v->uv_flags & VIFF_IGMPV1)) ||
  129:         (tmo != 0 &&  (v->uv_flags & VIFF_IGMPV1))) {
  130:         int i;
  131: 	
  132:         /*
  133:          * Exponentially back-off warning rate
  134:          */
  135:         i = ++v->uv_igmpv1_warn;
  136:         while (i && !(i & 1))
  137:             i >>= 1;
  138:         if (i == 1)
  139:             log(LOG_WARNING, 0, "%s %s on vif %d, %s",
  140:                 tmo == 0 ? "Received IGMPv1 report from"
  141:                          : "Received IGMPv2 report from",
  142:                 inet_fmt(src, s1),
  143:                 vifi,
  144:                 tmo == 0 ? "please configure vif for IGMPv1"
  145:                          : "but I am configured for IGMPv1");
  146:     }
  147:     
  148:     if (v->uv_querier == NULL || v->uv_querier->al_addr != src) {
  149:         /*
  150:          * This might be:
  151:          * - A query from a new querier, with a lower source address
  152:          *   than the current querier (who might be me)
  153:          * - A query from a new router that just started up and doesn't
  154:          *   know who the querier is.
  155:          * - A query from the current querier
  156:          */
  157:         if (ntohl(src) < (v->uv_querier ? ntohl(v->uv_querier->al_addr)
  158:                                    : ntohl(v->uv_lcl_addr))) {
  159:             IF_DEBUG(DEBUG_IGMP)
  160: 		log(LOG_DEBUG, 0, "new querier %s (was %s) on vif %d",
  161: 		    inet_fmt(src, s1),
  162: 		    v->uv_querier ?
  163: 		    inet_fmt(v->uv_querier->al_addr, s2) :
  164: 		    "me", vifi);
  165:             if (!v->uv_querier) {
  166:                 v->uv_querier = (struct listaddr *)
  167: 		    malloc(sizeof(struct listaddr));
  168: 		v->uv_querier->al_next = (struct listaddr *)NULL;
  169: 		v->uv_querier->al_timer = 0;
  170: 		v->uv_querier->al_genid = 0;
  171: 		/* TODO: write the protocol version */
  172: 		v->uv_querier->al_pv = 0;
  173: 		v->uv_querier->al_mv = 0;
  174: 		v->uv_querier->al_old = 0;
  175: 		v->uv_querier->al_index = 0;
  176: 		v->uv_querier->al_timerid = 0;
  177: 		v->uv_querier->al_query = 0;
  178: 		v->uv_querier->al_flags = 0;
  179: 		
  180:                 v->uv_flags &= ~VIFF_QUERIER;
  181:             }
  182:             v->uv_querier->al_addr = src;
  183:             time(&v->uv_querier->al_ctime);
  184:         }
  185:     }
  186:     
  187:     /*
  188:      * Reset the timer since we've received a query.
  189:      */
  190:     if (v->uv_querier && src == v->uv_querier->al_addr)
  191:         v->uv_querier->al_timer = 0;
  192:     
  193:     /*
  194:      * If this is a Group-Specific query which we did not source,
  195:      * we must set our membership timer to [Last Member Query Count] *
  196:      * the [Max Response Time] in the packet.
  197:      */
  198:     if (!(v->uv_flags & VIFF_IGMPV1) && group != 0 &&
  199: 	src != v->uv_lcl_addr) {
  200:         register struct listaddr *g;
  201: 	
  202:         IF_DEBUG(DEBUG_IGMP)
  203: 	    log(LOG_DEBUG, 0,
  204: 		"%s for %s from %s on vif %d, timer %d",
  205: 		"Group-specific membership query",
  206: 		inet_fmt(group, s2), inet_fmt(src, s1), vifi, tmo);
  207:         
  208:         for (g = v->uv_groups; g != NULL; g = g->al_next) {
  209:             if (group == g->al_addr && g->al_query == 0) {
  210:                 /* setup a timeout to remove the group membership */
  211:                 if (g->al_timerid)
  212:                     g->al_timerid = DeleteTimer(g->al_timerid);
  213:                 g->al_timer = IGMP_LAST_MEMBER_QUERY_COUNT *
  214: 		                        tmo / IGMP_TIMER_SCALE;
  215:                 /* use al_query to record our presence in last-member state */
  216:                 g->al_query = -1;
  217:                 g->al_timerid = SetTimer(vifi, g);
  218:                 IF_DEBUG(DEBUG_IGMP)
  219: 		    log(LOG_DEBUG, 0,
  220: 			"timer for grp %s on vif %d set to %d",
  221: 			inet_fmt(group, s2), vifi, g->al_timer);
  222:                 break;
  223:             }
  224:         }
  225:     }
  226: }
  227: 
  228: 
  229: /*
  230:  * Process an incoming group membership report.
  231:  */
  232: void
  233: accept_group_report(src, dst, group, igmp_report_type)
  234:     u_int32 src, dst, group;
  235:     int  igmp_report_type;
  236: {
  237:     register vifi_t vifi;
  238:     register struct uvif *v;
  239:     register struct listaddr *g;
  240: 
  241:     if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
  242: 	IF_DEBUG(DEBUG_IGMP)
  243: 	    log(LOG_INFO, 0,
  244: 		"ignoring group membership report from non-adjacent host %s",
  245: 		inet_fmt(src, s1));
  246: 	return;
  247:     }
  248:     
  249:     IF_DEBUG(DEBUG_IGMP)
  250: 	log(LOG_INFO, 0,
  251: 	    "accepting IGMP group membership report: src %s, dst% s, grp %s",
  252: 	    inet_fmt(src, s1), inet_fmt(dst, s2), inet_fmt(group, s3));
  253:     
  254:     v = &uvifs[vifi];
  255:     
  256:     /*
  257:      * Look for the group in our group list; if found, reset its timer.
  258:      */
  259:     for (g = v->uv_groups; g != NULL; g = g->al_next) {
  260:         if (group == g->al_addr) {
  261:             if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT)
  262:                 g->al_old = DVMRP_OLD_AGE_THRESHOLD;
  263: 	    
  264:             g->al_reporter = src;
  265: 	    
  266:             /** delete old timers, set a timer for expiration **/
  267:             g->al_timer = IGMP_GROUP_MEMBERSHIP_INTERVAL;
  268:             if (g->al_query)
  269:                 g->al_query = DeleteTimer(g->al_query);
  270:             if (g->al_timerid)
  271:                 g->al_timerid = DeleteTimer(g->al_timerid);
  272:             g->al_timerid = SetTimer(vifi, g);
  273: 	    add_leaf(vifi, INADDR_ANY_N, group);
  274:             break;
  275:         }
  276:     }
  277:     
  278:     /*
  279:      * If not found, add it to the list and update kernel cache.
  280:      */
  281:     if (g == NULL) {
  282: 	g = (struct listaddr *)malloc(sizeof(struct listaddr));
  283:         if (g == NULL)
  284:             log(LOG_ERR, 0, "ran out of memory");    /* fatal */
  285: 	
  286:         g->al_addr   = group;
  287:         if (igmp_report_type == IGMP_V1_MEMBERSHIP_REPORT)
  288:             g->al_old = DVMRP_OLD_AGE_THRESHOLD;
  289:         else
  290:             g->al_old = 0;
  291: 	
  292:         /** set a timer for expiration **/
  293:         g->al_query     = 0;
  294:         g->al_timer     = IGMP_GROUP_MEMBERSHIP_INTERVAL;
  295:         g->al_reporter  = src;
  296:         g->al_timerid   = SetTimer(vifi, g);
  297:         g->al_next      = v->uv_groups;
  298:         v->uv_groups    = g;
  299:         time(&g->al_ctime);
  300: 
  301: 	add_leaf(vifi, INADDR_ANY_N, group);
  302:     }
  303: }
  304: 
  305: 
  306: /* TODO: send PIM prune message if the last member? */
  307: void
  308: accept_leave_message(src, dst, group)
  309:     u_int32 src, dst, group;
  310: {
  311:     register vifi_t vifi;
  312:     register struct uvif *v;
  313:     register struct listaddr *g;
  314: 
  315:     /* TODO: modify for DVMRP ??? */    
  316:     if ((vifi = find_vif_direct_local(src)) == NO_VIF) {
  317: 	IF_DEBUG(DEBUG_IGMP)
  318:             log(LOG_INFO, 0,
  319:                 "ignoring group leave report from non-adjacent host %s",
  320:                 inet_fmt(src, s1));
  321:         return;
  322:     }
  323:     
  324:     IF_DEBUG(DEBUG_IGMP)
  325: 	log(LOG_INFO, 0,
  326: 	    "accepting IGMP leave message: src %s, dst% s, grp %s",
  327: 	    inet_fmt(src, s1), inet_fmt(dst, s2), inet_fmt(group, s3));
  328: 
  329:     v = &uvifs[vifi];
  330:     
  331:     if (!(v->uv_flags & (VIFF_QUERIER | VIFF_DR))
  332: 	|| (v->uv_flags & VIFF_IGMPV1))
  333:         return;
  334:     
  335:     /*
  336:      * Look for the group in our group list in order to set up a short-timeout
  337:      * query.
  338:      */
  339:     for (g = v->uv_groups; g != NULL; g = g->al_next) {
  340:         if (group == g->al_addr) {
  341:             IF_DEBUG(DEBUG_IGMP)
  342: 		log(LOG_DEBUG, 0,
  343: 		    "[vif.c, _accept_leave_message] %d %d \n",
  344: 		    g->al_old, g->al_query);
  345: 	    
  346:             /* Ignore the leave message if there are old hosts present */
  347:             if (g->al_old)
  348:                 return;
  349: 	    
  350:             /* still waiting for a reply to a query, ignore the leave */
  351:             if (g->al_query)
  352:                 return;
  353: 	    
  354:             /** delete old timer set a timer for expiration **/
  355:             if (g->al_timerid)
  356:                 g->al_timerid = DeleteTimer(g->al_timerid);
  357: 	    
  358: #if IGMP_LAST_MEMBER_QUERY_COUNT != 2
  359: /*
  360: This code needs to be updated to keep a counter of the number
  361: of queries remaining.
  362: */
  363: #endif
  364: 	    /** send a group specific querry **/
  365: 	    g->al_timer = IGMP_LAST_MEMBER_QUERY_INTERVAL *
  366: 		(IGMP_LAST_MEMBER_QUERY_COUNT + 1);
  367: 	    if (v->uv_flags & VIFF_QUERIER)
  368: 		send_igmp(igmp_send_buf, v->uv_lcl_addr, g->al_addr,
  369: 			  IGMP_MEMBERSHIP_QUERY, 
  370: 			  IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE,
  371: 			  g->al_addr, 0);
  372: 	    g->al_query = SetQueryTimer(g, vifi,
  373: 					IGMP_LAST_MEMBER_QUERY_INTERVAL,
  374: 					IGMP_LAST_MEMBER_QUERY_INTERVAL * IGMP_TIMER_SCALE);
  375: 	    g->al_timerid = SetTimer(vifi, g);
  376:             break;
  377:         }
  378:     }
  379: }
  380: 
  381: 
  382: /*
  383:  * Time out record of a group membership on a vif
  384:  */
  385: static void
  386: DelVif(arg)
  387:     void *arg;
  388: {
  389:     cbk_t *cbk = (cbk_t *)arg;
  390:     vifi_t vifi = cbk->vifi;
  391:     struct uvif *v = &uvifs[vifi];
  392:     struct listaddr *a, **anp, *g = cbk->g;
  393: 
  394:     /*
  395:      * Group has expired
  396:      * delete all kernel cache entries with this group
  397:      */
  398:     if (g->al_query)
  399:         DeleteTimer(g->al_query);
  400: 
  401:     delete_leaf(vifi, INADDR_ANY_N, g->al_addr);
  402: 
  403:     anp = &(v->uv_groups);
  404:     while ((a = *anp) != NULL) {
  405:         if (a == g) {
  406:             *anp = a->al_next;
  407:             free((char *)a);
  408:         } else {
  409:             anp = &a->al_next;
  410:         }
  411:     }
  412: 
  413:     free(cbk);
  414: }
  415: 
  416: 
  417: /*
  418:  * Set a timer to delete the record of a group membership on a vif.
  419:  */
  420: static int
  421: SetTimer(vifi, g)
  422:     vifi_t vifi;
  423:     struct listaddr *g;
  424: {
  425:     cbk_t *cbk;
  426:     
  427:     cbk = (cbk_t *) malloc(sizeof(cbk_t));
  428:     cbk->vifi = vifi;
  429:     cbk->g = g;
  430:     return timer_setTimer(g->al_timer, DelVif, cbk);
  431: }
  432: 
  433: 
  434: /*
  435:  * Delete a timer that was set above.
  436:  */
  437: static int
  438: DeleteTimer(id)
  439:     int id;
  440: {
  441:     timer_clearTimer(id);
  442:     return 0;
  443: }
  444: 
  445: 
  446: /*
  447:  * Send a group-specific query.
  448:  */
  449: static void
  450: SendQuery(arg)
  451:     void *arg;
  452: {
  453:     cbk_t *cbk = (cbk_t *)arg;
  454:     register struct uvif *v = &uvifs[cbk->vifi];
  455: 
  456:     if (v->uv_flags & VIFF_QUERIER)
  457: 	send_igmp(igmp_send_buf, v->uv_lcl_addr, cbk->g->al_addr,
  458: 		  IGMP_MEMBERSHIP_QUERY,
  459: 		  cbk->q_time, cbk->g->al_addr, 0);
  460:     cbk->g->al_query = 0;
  461:     free(cbk);
  462: }
  463: 
  464: 
  465: /*
  466:  * Set a timer to send a group-specific query.
  467:  */
  468: static int
  469: SetQueryTimer(g, vifi, to_expire, q_time)
  470:     struct listaddr *g;
  471:     vifi_t vifi;
  472:     int to_expire;
  473:     int q_time;
  474: {
  475:     cbk_t *cbk;
  476: 
  477:     cbk = (cbk_t *) malloc(sizeof(cbk_t));
  478:     cbk->g = g;
  479:     cbk->q_time = q_time;
  480:     cbk->vifi = vifi;
  481:     return timer_setTimer(to_expire, SendQuery, cbk);
  482: }
  483: 
  484: /* Checks for IGMP group membership: returns TRUE if there is a receiver for the
  485:  * group on the given vif, or returns FALSE otherwise.
  486:  */
  487: int check_grp_membership(v, group)
  488:     struct uvif *v;
  489:     u_int32 group;
  490: {
  491:     register struct listaddr *g;
  492: 
  493:     /*
  494:      * Look for the group in our group list;
  495:      */
  496:     for (g = v->uv_groups; g != NULL; g = g->al_next) {
  497:         if (group == g->al_addr) 
  498:         	return TRUE;
  499:     }
  500: 	return FALSE;
  501: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>