Annotation of embedaddon/pimdd/igmp_proto.c, revision 1.1

1.1     ! misho       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.4 1998/05/29 21:58:22 kurtw 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>