Annotation of embedaddon/pimdd/igmp_proto.c, revision 1.1.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>