Annotation of embedaddon/pimdd/pim_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: pim_proto.c,v 1.37 1998/12/22 21:50:18 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:  * Local functions definitions.
        !            58:  */
        !            59: static int parse_pim_hello         __P((char *pktPtr,
        !            60:                                        int datalen,
        !            61:                                        u_int32 src,
        !            62:                                        u_int16 *holdtime));
        !            63: static int compare_metrics         __P((u_int32 local_preference,
        !            64:                                        u_int32 local_metric,
        !            65:                                        u_int32 local_address,
        !            66:                                        u_int32 remote_preference,
        !            67:                                        u_int32 remote_metric,
        !            68:                                        u_int32 remote_address));
        !            69: 
        !            70: vifbitmap_t nbr_vifs;    /* Vifs that have one or more neighbors attached */
        !            71: 
        !            72: /************************************************************************
        !            73:  *                        PIM_HELLO
        !            74:  ************************************************************************/
        !            75: int
        !            76: receive_pim_hello(src, dst, pim_message, datalen)
        !            77:     u_int32 src, dst;
        !            78:     register char *pim_message;
        !            79:     int datalen;
        !            80: {
        !            81:     vifi_t vifi;
        !            82:     struct uvif *v;
        !            83:     register pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr;
        !            84:     u_int16 holdtime;
        !            85:     u_int8  *data_ptr;
        !            86:     int state_change;
        !            87:     srcentry_t *srcentry_ptr;
        !            88:     srcentry_t *srcentry_ptr_next;
        !            89:     mrtentry_t *mrtentry_ptr;
        !            90: 
        !            91:     /* Checksum */
        !            92:     if (inet_cksum((u_int16 *)pim_message, datalen))
        !            93:        return(FALSE);
        !            94: 
        !            95:     if ((vifi = find_vif_direct(src)) == NO_VIF) {
        !            96:        /* Either a local vif or somehow received PIM_HELLO from
        !            97:         * non-directly connected router. Ignore it.
        !            98:         */
        !            99:        if (local_address(src) == NO_VIF)
        !           100:            log(LOG_INFO, 0, "Ignoring PIM_HELLO from non-neighbor router %s",
        !           101:                inet_fmt(src, s1));
        !           102:        return(FALSE);
        !           103:     }
        !           104: 
        !           105:     v = &uvifs[vifi];
        !           106:     if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED))
        !           107:        return(FALSE);    /* Shoudn't come on this interface */
        !           108:     data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
        !           109: 
        !           110:     /* Get the Holdtime (in seconds) from the message. Return if error. */
        !           111:     if (parse_pim_hello(pim_message, datalen, src, &holdtime) == FALSE)
        !           112:        return(FALSE);
        !           113:     IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER)
        !           114:        log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u",
        !           115:            inet_fmt(src, s1), holdtime);
        !           116:     
        !           117:     for (prev_nbr = (pim_nbr_entry_t *)NULL, nbr = v->uv_pim_neighbors;
        !           118:         nbr != (pim_nbr_entry_t *)NULL;
        !           119:         prev_nbr = nbr, nbr = nbr->next) {
        !           120:        /* The PIM neighbors are sorted in decreasing order of the
        !           121:         * network addresses (note that to be able to compare them
        !           122:         * correctly we must translate the addresses in host order.
        !           123:         */
        !           124:        if (ntohl(src) < ntohl(nbr->address))
        !           125:            continue;
        !           126:        if (src == nbr->address) {
        !           127:            /* We already have an entry for this host */
        !           128:            if (0 == holdtime) {
        !           129:                /* Looks like we have a nice neighbor who is going down
        !           130:                 * and wants to inform us by sending "holdtime=0". Thanks
        !           131:                 * buddy and see you again!
        !           132:                 */
        !           133:                log(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down",
        !           134:                    inet_fmt(src, s1));
        !           135:                delete_pim_nbr(nbr);
        !           136:                return(TRUE);
        !           137:            }
        !           138:            SET_TIMER(nbr->timer, holdtime);
        !           139:            return(TRUE);
        !           140:        }
        !           141:        else
        !           142:            /*
        !           143:             * No entry for this neighbor. Exit the loop and create an
        !           144:             * entry for it.
        !           145:             */
        !           146:            break;
        !           147:     }
        !           148:     
        !           149:     /*
        !           150:      * This is a new neighbor. Create a new entry for it.
        !           151:      * It must be added right after `prev_nbr`
        !           152:      */
        !           153:     new_nbr = (pim_nbr_entry_t *)malloc(sizeof(pim_nbr_entry_t));
        !           154:     new_nbr->address          = src;
        !           155:     new_nbr->vifi             = vifi;
        !           156:     SET_TIMER(new_nbr->timer, holdtime);
        !           157:     new_nbr->next             = nbr;
        !           158:     new_nbr->prev             = prev_nbr;
        !           159:     
        !           160:     if (prev_nbr != (pim_nbr_entry_t *)NULL)
        !           161:        prev_nbr->next  = new_nbr;
        !           162:     else
        !           163:        v->uv_pim_neighbors = new_nbr;
        !           164:     if (new_nbr->next != (pim_nbr_entry_t *)NULL)
        !           165:        new_nbr->next->prev = new_nbr;
        !           166: 
        !           167:     v->uv_flags &= ~VIFF_NONBRS;
        !           168:     v->uv_flags |= VIFF_PIM_NBR;
        !           169:     VIFM_SET(vifi, nbr_vifs);
        !           170: 
        !           171:     /* Elect a new DR */
        !           172:     if (ntohl(v->uv_lcl_addr) <
        !           173:        ntohl(v->uv_pim_neighbors->address)) {
        !           174:        /* The first address is the new potential remote
        !           175:         * DR address and it wins (is >) over the local address.
        !           176:         */
        !           177:        v->uv_flags &= ~VIFF_DR;
        !           178:        v->uv_flags &= ~VIFF_QUERIER;
        !           179:     }
        !           180: 
        !           181:     /* Since a new neighbour has come up, let it know your existence */
        !           182:     /* XXX: TODO: not in the spec,
        !           183:      * but probably should send the message after a short random period?
        !           184:      */
        !           185:     send_pim_hello(v, PIM_TIMER_HELLO_HOLDTIME);
        !           186:     
        !           187:     /* Update the source entries */
        !           188:     for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL;
        !           189:         srcentry_ptr = srcentry_ptr_next) {
        !           190:        srcentry_ptr_next = srcentry_ptr->next;
        !           191:        
        !           192:        if (srcentry_ptr->incoming == vifi)
        !           193:            continue;
        !           194:        
        !           195:        for (mrtentry_ptr = srcentry_ptr->mrtlink;
        !           196:             mrtentry_ptr != (mrtentry_t *)NULL;
        !           197:             mrtentry_ptr = mrtentry_ptr->srcnext) {
        !           198: 
        !           199:            if(!(VIFM_ISSET(vifi, mrtentry_ptr->oifs))) {
        !           200:                state_change = 
        !           201:                    change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
        !           202:                                      mrtentry_ptr->pruned_oifs,
        !           203:                                      mrtentry_ptr->leaves);
        !           204:                if(state_change == 1)
        !           205:                    trigger_join_alert(mrtentry_ptr);
        !           206:            }
        !           207:        }
        !           208:     }
        !           209:     
        !           210:     IF_DEBUG(DEBUG_PIM_HELLO)
        !           211:        dump_vifs(stderr);      /* Show we got a new neighbor */
        !           212:     return(TRUE);
        !           213: }
        !           214: 
        !           215: 
        !           216: void
        !           217: delete_pim_nbr(nbr_delete)
        !           218:     pim_nbr_entry_t *nbr_delete;
        !           219: {
        !           220:     srcentry_t *srcentry_ptr;
        !           221:     srcentry_t *srcentry_ptr_next;
        !           222:     mrtentry_t *mrtentry_ptr;
        !           223:     struct uvif *v;
        !           224:     int state_change;
        !           225: 
        !           226:     v = &uvifs[nbr_delete->vifi];
        !           227:     
        !           228:     /* Delete the entry from the pim_nbrs chain */
        !           229:     if (nbr_delete->prev != (pim_nbr_entry_t *)NULL)
        !           230:        nbr_delete->prev->next = nbr_delete->next;
        !           231:     else
        !           232:        v->uv_pim_neighbors = nbr_delete->next;
        !           233:     if (nbr_delete->next != (pim_nbr_entry_t *)NULL)
        !           234:        nbr_delete->next->prev = nbr_delete->prev;
        !           235:     
        !           236:     if (v->uv_pim_neighbors == (pim_nbr_entry_t *)NULL) {
        !           237:        /* This was our last neighbor. */
        !           238:        v->uv_flags &= ~VIFF_PIM_NBR;
        !           239:        v->uv_flags |= (VIFF_NONBRS | VIFF_DR | VIFF_QUERIER);
        !           240:        VIFM_CLR(nbr_delete->vifi, nbr_vifs);
        !           241:     }
        !           242:     else {
        !           243:        if (ntohl(v->uv_lcl_addr) >
        !           244:            ntohl(v->uv_pim_neighbors->address)) {
        !           245:            /* The first address is the new potential remote
        !           246:             * DR address, but the local address is the winner.
        !           247:             */
        !           248:            v->uv_flags |= VIFF_DR;
        !           249:            v->uv_flags |= VIFF_QUERIER;
        !           250:        }
        !           251:     }
        !           252:     
        !           253:     /* Update the source entries:
        !           254:      * If the deleted nbr was my upstream, then reset incoming and
        !           255:      * update all (S,G) entries for sources reachable through it.
        !           256:      * If the deleted nbr was the last on a non-iif vif, then recalcuate
        !           257:      * outgoing interfaces.
        !           258:      */
        !           259:     for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL;
        !           260:         srcentry_ptr = srcentry_ptr_next) {
        !           261:        srcentry_ptr_next = srcentry_ptr->next;
        !           262:        
        !           263:        /* The only time we don't need to scan all mrtentries is when the nbr
        !           264:         * was on the iif, but not the upstream nbr! 
        !           265:         */
        !           266:        if (nbr_delete->vifi == srcentry_ptr->incoming &&
        !           267:            srcentry_ptr->upstream != nbr_delete)
        !           268:            continue;
        !           269:        
        !           270:        /* Reset the next hop (PIM) router */
        !           271:        if(srcentry_ptr->upstream == nbr_delete) 
        !           272:            if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) {
        !           273:                /* Coudn't reset it. Sorry, the hext hop router toward that
        !           274:                 * source is probably not a PIM router, or cannot find route
        !           275:                 * at all, hence I cannot handle this source and have to
        !           276:                 * delete it.
        !           277:                 */
        !           278:                delete_srcentry(srcentry_ptr);
        !           279:                free((char *)nbr_delete);
        !           280:                return;
        !           281:            }
        !           282: 
        !           283:        for (mrtentry_ptr = srcentry_ptr->mrtlink;
        !           284:             mrtentry_ptr != (mrtentry_t *)NULL;
        !           285:             mrtentry_ptr = mrtentry_ptr->srcnext) {
        !           286:            mrtentry_ptr->incoming   = srcentry_ptr->incoming;
        !           287:            mrtentry_ptr->upstream   = srcentry_ptr->upstream;
        !           288:            mrtentry_ptr->metric     = srcentry_ptr->metric;
        !           289:            mrtentry_ptr->preference = srcentry_ptr->preference;
        !           290:            state_change = 
        !           291:                change_interfaces(mrtentry_ptr, srcentry_ptr->incoming,
        !           292:                                  mrtentry_ptr->pruned_oifs,
        !           293:                                  mrtentry_ptr->leaves);
        !           294:            if(state_change == -1) {
        !           295:                trigger_prune_alert(mrtentry_ptr);
        !           296:            } else if(state_change == 1) {
        !           297:                trigger_join_alert(mrtentry_ptr);
        !           298:            }
        !           299:        }
        !           300:     }
        !           301:     
        !           302:     free((char *)nbr_delete);
        !           303: }
        !           304: 
        !           305: 
        !           306: /* TODO: simplify it! */
        !           307: static int
        !           308: parse_pim_hello(pim_message, datalen, src, holdtime)
        !           309:     char *pim_message;
        !           310:     int datalen;
        !           311:     u_int32 src;
        !           312:     u_int16 *holdtime;
        !           313: {
        !           314:     u_int8 *pim_hello_message;
        !           315:     u_int8 *data_ptr;
        !           316:     u_int16 option_type;
        !           317:     u_int16 option_length;
        !           318:     int holdtime_received_ok = FALSE;
        !           319:     int option_total_length;
        !           320: 
        !           321:     pim_hello_message = (u_int8 *)(pim_message + sizeof(pim_header_t));
        !           322:     datalen -= sizeof(pim_header_t);
        !           323:     for ( ; datalen >= sizeof(pim_hello_t); ) {
        !           324:        /* Ignore any data if shorter than (pim_hello header) */
        !           325:        data_ptr = pim_hello_message;
        !           326:        GET_HOSTSHORT(option_type, data_ptr);
        !           327:        GET_HOSTSHORT(option_length, data_ptr);
        !           328:        switch (option_type) {
        !           329:        case PIM_MESSAGE_HELLO_HOLDTIME:
        !           330:            if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length) {
        !           331:                IF_DEBUG(DEBUG_PIM_HELLO)
        !           332:                    log(LOG_DEBUG, 0,
        !           333:                       "PIM HELLO Holdtime from %s: invalid OptionLength = %u",
        !           334:                        inet_fmt(src, s1), option_length);
        !           335:                return (FALSE);
        !           336:            }
        !           337:            GET_HOSTSHORT(*holdtime, data_ptr);
        !           338:            holdtime_received_ok = TRUE;
        !           339:            break;
        !           340:        default:
        !           341:            /* Ignore any unknown options */
        !           342:            break;
        !           343:        }
        !           344: 
        !           345:        /* Move to the next option */
        !           346:         /* XXX: TODO: If we are padding to the end of the 32 bit boundary,
        !           347:          * use the first method to move to the next option, otherwise
        !           348:          * simply (sizeof(pim_hello_t) + option_length).
        !           349:          */
        !           350: #ifdef BOUNDARY_32_BIT
        !           351:        option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) +
        !           352:                               ((option_length & 0x3) ? 4 : 0));
        !           353: #else
        !           354:        option_total_length = (sizeof(pim_hello_t) + option_length);
        !           355: #endif /* BOUNDARY_32_BIT */
        !           356:        datalen -= option_total_length;
        !           357:        pim_hello_message += option_total_length;
        !           358:     }
        !           359:     return (holdtime_received_ok);
        !           360: }
        !           361: 
        !           362: 
        !           363: int
        !           364: send_pim_hello(v, holdtime)
        !           365:     struct uvif *v;
        !           366:     u_int16 holdtime;
        !           367: {
        !           368:     char   *buf;
        !           369:     u_int8 *data_ptr;
        !           370: 
        !           371:     int datalen;
        !           372: 
        !           373:     buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t);
        !           374:     data_ptr = (u_int8 *)buf;
        !           375:     PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr);
        !           376:     PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr);
        !           377:     PUT_HOSTSHORT(holdtime, data_ptr);
        !           378:     
        !           379:     datalen = data_ptr - (u_int8 *)buf;
        !           380: 
        !           381:     send_pim(pim_send_buf, v->uv_lcl_addr, allpimrouters_group, PIM_HELLO,
        !           382:             datalen);
        !           383:     SET_TIMER(v->uv_pim_hello_timer, PIM_TIMER_HELLO_PERIOD);
        !           384:     return(TRUE);
        !           385: }
        !           386: 
        !           387: 
        !           388: /************************************************************************
        !           389:  *                        PIM_JOIN_PRUNE
        !           390:  ************************************************************************/
        !           391: 
        !           392: typedef struct {
        !           393:     u_int32 source;
        !           394:     u_int32 group;
        !           395:     u_int32 target;
        !           396: } join_delay_cbk_t;
        !           397: 
        !           398: typedef struct {
        !           399:     vifi_t vifi;
        !           400:     u_int32 source;
        !           401:     u_int32 group;
        !           402:     u_int16 holdtime;
        !           403: } prune_delay_cbk_t;
        !           404:     
        !           405: static void 
        !           406: delayed_join_job(arg)
        !           407:      void *arg;
        !           408: {
        !           409:     mrtentry_t *mrtentry_ptr;
        !           410: 
        !           411:     join_delay_cbk_t *cbk = (join_delay_cbk_t *)arg;
        !           412: 
        !           413:     mrtentry_ptr = find_route(cbk->source, cbk->group, MRTF_SG, DONT_CREATE);
        !           414:     if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !           415:        return;
        !           416:     
        !           417:     if(mrtentry_ptr->join_delay_timerid)
        !           418:        timer_clearTimer(mrtentry_ptr->join_delay_timerid);
        !           419: 
        !           420:     if(mrtentry_ptr->upstream)
        !           421:        send_pim_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming,
        !           422:                    mrtentry_ptr->upstream->address, 0);
        !           423: 
        !           424:     free(cbk);
        !           425: }
        !           426: 
        !           427: static void 
        !           428: schedule_delayed_join(mrtentry_ptr, target) 
        !           429:      mrtentry_t *mrtentry_ptr;
        !           430:      u_int32 target;
        !           431: {
        !           432:     u_long random_delay;
        !           433:     join_delay_cbk_t *cbk;
        !           434:     
        !           435:     /* Delete existing timer */
        !           436:     if(mrtentry_ptr->join_delay_timerid) 
        !           437:        timer_clearTimer(mrtentry_ptr->join_delay_timerid);
        !           438: 
        !           439: #ifdef SYSV
        !           440:     random_delay = lrand48() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT;
        !           441: #else
        !           442:     random_delay = random() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT;
        !           443: #endif
        !           444:     
        !           445:     IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           446:        log(LOG_DEBUG, 0, "Scheduling join for src %s, grp %s, delay %d",
        !           447:            inet_fmt(mrtentry_ptr->source->address, s1), 
        !           448:            inet_fmt(mrtentry_ptr->group->group, s2),
        !           449:            random_delay);
        !           450: 
        !           451:     if(random_delay == 0 && mrtentry_ptr->upstream) {
        !           452:        send_pim_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming,
        !           453:                    mrtentry_ptr->upstream->address, 0);
        !           454:        return;
        !           455:     }
        !           456: 
        !           457:     cbk = (join_delay_cbk_t *)malloc(sizeof(join_delay_cbk_t));
        !           458:     cbk->source = mrtentry_ptr->source->address;
        !           459:     cbk->group = mrtentry_ptr->group->group;
        !           460:     cbk->target = target;
        !           461: 
        !           462:     mrtentry_ptr->join_delay_timerid = 
        !           463:        timer_setTimer(random_delay, delayed_join_job, cbk);
        !           464: }
        !           465: 
        !           466: 
        !           467: static void 
        !           468: delayed_prune_job(arg)
        !           469:      void *arg;
        !           470: {
        !           471:     mrtentry_t *mrtentry_ptr;
        !           472:     vifbitmap_t new_pruned_oifs;
        !           473:     int state_change;
        !           474: 
        !           475:     prune_delay_cbk_t *cbk = (prune_delay_cbk_t *)arg;
        !           476: 
        !           477:     mrtentry_ptr = find_route(cbk->source, cbk->group, MRTF_SG, DONT_CREATE);
        !           478:     if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !           479:        return;
        !           480:     
        !           481:     if(mrtentry_ptr->prune_delay_timerids[cbk->vifi])
        !           482:        timer_clearTimer(mrtentry_ptr->prune_delay_timerids[cbk->vifi]);
        !           483: 
        !           484:     if(VIFM_ISSET(cbk->vifi, mrtentry_ptr->oifs)) {
        !           485:        IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           486:            log(LOG_DEBUG, 0, "Deleting pruned vif %d for src %s, grp %s",
        !           487:                cbk->vifi, 
        !           488:                inet_fmt(cbk->source, s1), 
        !           489:                inet_fmt(cbk->group, s2));
        !           490: 
        !           491:        VIFM_COPY(mrtentry_ptr->pruned_oifs, new_pruned_oifs);
        !           492:        VIFM_SET(cbk->vifi, new_pruned_oifs);
        !           493:        SET_TIMER(mrtentry_ptr->prune_timers[cbk->vifi], cbk->holdtime);
        !           494: 
        !           495:        state_change = 
        !           496:            change_interfaces(mrtentry_ptr,
        !           497:                              mrtentry_ptr->incoming,
        !           498:                              new_pruned_oifs,
        !           499:                              mrtentry_ptr->leaves);
        !           500:                        
        !           501:        /* Handle transition to negative cache */
        !           502:        if(state_change == -1)
        !           503:            trigger_prune_alert(mrtentry_ptr);
        !           504:     }
        !           505:        
        !           506:     free(cbk);
        !           507: }
        !           508: 
        !           509: static void 
        !           510: schedule_delayed_prune(mrtentry_ptr, vifi, holdtime) 
        !           511:      mrtentry_t *mrtentry_ptr;
        !           512:      vifi_t vifi;
        !           513:      u_int16 holdtime;
        !           514: {
        !           515:     prune_delay_cbk_t *cbk;
        !           516:     
        !           517:     /* Delete existing timer */
        !           518:     if(mrtentry_ptr->prune_delay_timerids[vifi]) 
        !           519:        timer_clearTimer(mrtentry_ptr->prune_delay_timerids[vifi]);
        !           520: 
        !           521:     cbk = (prune_delay_cbk_t *)malloc(sizeof(prune_delay_cbk_t));
        !           522:     cbk->vifi = vifi;
        !           523:     cbk->source = mrtentry_ptr->source->address;
        !           524:     cbk->group = mrtentry_ptr->group->group;
        !           525:     cbk->holdtime = holdtime;
        !           526: 
        !           527:     mrtentry_ptr->prune_delay_timerids[vifi] = 
        !           528:        timer_setTimer((u_int16)PIM_RANDOM_DELAY_JOIN_TIMEOUT+1, 
        !           529:                       delayed_prune_job, cbk);
        !           530: }
        !           531: 
        !           532: 
        !           533: /* TODO: when parsing, check if we go beyong message size */
        !           534: int
        !           535: receive_pim_join_prune(src, dst, pim_message, datalen)
        !           536:     u_int32 src, dst;
        !           537:     char *pim_message;
        !           538:     register int datalen;
        !           539: {
        !           540:     vifi_t vifi;
        !           541:     struct uvif *v;
        !           542:     pim_encod_uni_addr_t uni_target_addr;
        !           543:     pim_encod_grp_addr_t encod_group;
        !           544:     pim_encod_src_addr_t encod_src;
        !           545:     u_int8 *data_ptr;
        !           546:     u_int8 num_groups;
        !           547:     u_int16 holdtime;
        !           548:     u_int16 num_j_srcs;
        !           549:     u_int16 num_p_srcs;
        !           550:     u_int32 source;
        !           551:     u_int32 group;
        !           552:     u_int32 s_mask;
        !           553:     u_int32 g_mask;
        !           554:     u_int8 s_flags;
        !           555:     u_int8 reserved;
        !           556:     mrtentry_t *mrtentry_ptr;
        !           557:     pim_nbr_entry_t *upstream_router;
        !           558:     vifbitmap_t new_pruned_oifs;
        !           559:     int state_change;
        !           560: 
        !           561:     if ((vifi = find_vif_direct(src)) == NO_VIF) {
        !           562:         /* Either a local vif or somehow received PIM_JOIN_PRUNE from
        !           563:          * non-directly connected router. Ignore it.
        !           564:          */
        !           565:         if (local_address(src) == NO_VIF)
        !           566:             log(LOG_INFO, 0,
        !           567:                 "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s",
        !           568:                 inet_fmt(src, s1));
        !           569:         return(FALSE);
        !           570:     }
        !           571: 
        !           572:     /* Checksum */
        !           573:     if (inet_cksum((u_int16 *)pim_message, datalen))
        !           574:         return(FALSE);
        !           575:  
        !           576:     v = &uvifs[vifi];
        !           577:     if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
        !           578:         return(FALSE);    /* Shoudn't come on this interface */
        !           579:     data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
        !           580: 
        !           581:     /* Get the target address */
        !           582:     GET_EUADDR(&uni_target_addr, data_ptr);
        !           583:     GET_BYTE(reserved, data_ptr);
        !           584:     GET_BYTE(num_groups, data_ptr);
        !           585:     if (num_groups == 0)
        !           586:         return (FALSE);    /* No indication for groups in the message */
        !           587:     GET_HOSTSHORT(holdtime, data_ptr);
        !           588: 
        !           589:     IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           590:        log(LOG_DEBUG, 0,
        !           591:            "PIM Join/Prune received from %s : target %s, holdtime %d",
        !           592:            inet_fmt(src, s1), inet_fmt(uni_target_addr.unicast_addr, s2), 
        !           593:            holdtime);
        !           594:     
        !           595:     if ((uni_target_addr.unicast_addr != v->uv_lcl_addr) && 
        !           596:        (uni_target_addr.unicast_addr != INADDR_ANY_N))  {
        !           597:         /* if I am not the target of the join or prune message */
        !           598:         /* Join Suppression: when receiving a join not addressed to me,
        !           599:         * if I am delaying a join for this (S,G) then cancel the delayed 
        !           600:         * join.
        !           601:         * Prune Soliticiting Joins: when receiving a prune not addressed to
        !           602:         * me on a LAN, schedule delayed join if I have downstream receivers.
        !           603:          */
        !           604: 
        !           605:         upstream_router = find_pim_nbr(uni_target_addr.unicast_addr);
        !           606:         if (upstream_router == (pim_nbr_entry_t *)NULL)
        !           607:             return (FALSE);   /* I have no such neighbor */
        !           608:         while (num_groups--) {
        !           609:             GET_EGADDR(&encod_group, data_ptr);
        !           610:             GET_HOSTSHORT(num_j_srcs, data_ptr);
        !           611:             GET_HOSTSHORT(num_p_srcs, data_ptr);
        !           612:             MASKLEN_TO_MASK(encod_group.masklen, g_mask);
        !           613:             group = encod_group.mcast_addr;
        !           614:             if (!IN_MULTICAST(ntohl(group))) {
        !           615:                 data_ptr +=
        !           616:                     (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
        !           617:                 continue; /* Ignore this group and jump to the next */
        !           618:             }
        !           619: 
        !           620:             while (num_j_srcs--) {
        !           621:                 GET_ESADDR(&encod_src, data_ptr);
        !           622:                 source = encod_src.src_addr;
        !           623:                 if (!inet_valid_host(source))
        !           624:                     continue;
        !           625:                 s_flags = encod_src.flags;
        !           626:                 MASKLEN_TO_MASK(encod_src.masklen, s_mask);
        !           627: 
        !           628:                 /* (S,G) Join suppresion */
        !           629:                 mrtentry_ptr = find_route(source, group, MRTF_SG,
        !           630:                                           DONT_CREATE);
        !           631:                if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !           632:                    continue;
        !           633: 
        !           634:                IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           635:                    log(LOG_DEBUG, 0,
        !           636:                        "\tJOIN src %s, group %s - canceling delayed join",
        !           637:                        inet_fmt(source, s1), inet_fmt(group, s2));
        !           638:                
        !           639:                /* Cancel the delayed join */
        !           640:                if(mrtentry_ptr->join_delay_timerid) {
        !           641:                    timer_clearTimer(mrtentry_ptr->join_delay_timerid);
        !           642:                    mrtentry_ptr->join_delay_timerid = 0;
        !           643:                }
        !           644:            } 
        !           645: 
        !           646:             while (num_p_srcs--) {
        !           647:                 GET_ESADDR(&encod_src, data_ptr);
        !           648:                 source = encod_src.src_addr;
        !           649:                 if (!inet_valid_host(source))
        !           650:                     continue;
        !           651:                 s_flags = encod_src.flags;
        !           652: 
        !           653:                /* if P2P link (not addressed to me) ignore 
        !           654:                 */
        !           655:                if(uvifs[vifi].uv_flags & VIFF_POINT_TO_POINT) 
        !           656:                    continue;
        !           657: 
        !           658:                /* if non-null oiflist then schedule delayed join
        !           659:                 */
        !           660:                 mrtentry_ptr = find_route(source, group, MRTF_SG,
        !           661:                                           DONT_CREATE);
        !           662:                if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !           663:                    continue;
        !           664: 
        !           665: 
        !           666:                if(!(VIFM_ISEMPTY(mrtentry_ptr->oifs))) {
        !           667:                    IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           668:                        log(LOG_DEBUG, 0,
        !           669:                            "\tPRUNE src %s, group %s - scheduling delayed join",
        !           670:                            inet_fmt(source, s1), inet_fmt(group, s2));
        !           671:                    
        !           672:                    schedule_delayed_join(mrtentry_ptr, uni_target_addr);
        !           673:                }
        !           674:            }
        !           675: 
        !           676:        } /* while groups */
        !           677: 
        !           678:        return(TRUE);
        !           679:     } /* if not unicast target */
        !           680: 
        !           681:     /* I am the target of this join/prune:
        !           682:      * For joins, cancel delayed prunes that I have scheduled.
        !           683:      * For prunes, echo the prune and schedule delayed prunes on LAN or 
        !           684:      * prune immediately on point-to-point links.
        !           685:      */
        !           686:     else {
        !           687:        while (num_groups--) {
        !           688:            GET_EGADDR(&encod_group, data_ptr);
        !           689:            GET_HOSTSHORT(num_j_srcs, data_ptr);
        !           690:            GET_HOSTSHORT(num_p_srcs, data_ptr);
        !           691:            MASKLEN_TO_MASK(encod_group.masklen, g_mask);
        !           692:            group = encod_group.mcast_addr;
        !           693:            if (!IN_MULTICAST(ntohl(group))) {
        !           694:                data_ptr +=
        !           695:                    (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
        !           696:                continue; /* Ignore this group and jump to the next */
        !           697:            }
        !           698:            
        !           699:            while (num_j_srcs--) {
        !           700:                GET_ESADDR(&encod_src, data_ptr);
        !           701:                source = encod_src.src_addr;
        !           702:                if (!inet_valid_host(source))
        !           703:                    continue;
        !           704:                s_flags = encod_src.flags;
        !           705:                MASKLEN_TO_MASK(encod_src.masklen, s_mask);
        !           706:            
        !           707:                 mrtentry_ptr = find_route(source, group, MRTF_SG,
        !           708:                                           DONT_CREATE);
        !           709:                if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !           710:                    continue;
        !           711: 
        !           712:                IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           713:                    log(LOG_DEBUG, 0,
        !           714:                        "\tJOIN src %s, group %s - canceling delayed prune",
        !           715:                        inet_fmt(source, s1), inet_fmt(group, s2));
        !           716:                
        !           717:                /* Cancel the delayed prune */
        !           718:                if(mrtentry_ptr->prune_delay_timerids[vifi]) {
        !           719:                    timer_clearTimer(mrtentry_ptr->prune_delay_timerids[vifi]);
        !           720:                    mrtentry_ptr->prune_delay_timerids[vifi] = 0;
        !           721:                }           
        !           722:            }
        !           723: 
        !           724:             while (num_p_srcs--) {
        !           725:                 GET_ESADDR(&encod_src, data_ptr);
        !           726:                 source = encod_src.src_addr;
        !           727:                 if (!inet_valid_host(source))
        !           728:                     continue;
        !           729:                 s_flags = encod_src.flags;
        !           730: 
        !           731: 
        !           732:                 mrtentry_ptr = find_route(source, group, MRTF_SG,
        !           733:                                           DONT_CREATE);
        !           734:                if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !           735:                    continue;
        !           736: 
        !           737:                /* if P2P link (addressed to me) prune immediately
        !           738:                 */
        !           739:                if(uvifs[vifi].uv_flags & VIFF_POINT_TO_POINT) {
        !           740:                    if(VIFM_ISSET(vifi, mrtentry_ptr->pruned_oifs)) {
        !           741: 
        !           742:                        IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           743:                            log(LOG_DEBUG, 0,
        !           744:                                "\tPRUNE(P2P) src %s, group %s - pruning vif",
        !           745:                                inet_fmt(source, s1), inet_fmt(group, s2));
        !           746:                
        !           747:                        IF_DEBUG(DEBUG_MRT)
        !           748:                            log(LOG_DEBUG, 0, "Deleting pruned vif %d for src %s, grp %s",
        !           749:                                vifi, 
        !           750:                                inet_fmt(source, s1), inet_fmt(group, s2));
        !           751: 
        !           752:                        VIFM_COPY(mrtentry_ptr->pruned_oifs, new_pruned_oifs);
        !           753:                        VIFM_SET(vifi, new_pruned_oifs);
        !           754:                        SET_TIMER(mrtentry_ptr->prune_timers[vifi], holdtime);
        !           755: 
        !           756:                        state_change = 
        !           757:                            change_interfaces(mrtentry_ptr,
        !           758:                                              mrtentry_ptr->incoming,
        !           759:                                              new_pruned_oifs,
        !           760:                                              mrtentry_ptr->leaves);
        !           761:                        
        !           762:                        /* Handle transition to negative cache */
        !           763:                        if(state_change == -1)
        !           764:                            trigger_prune_alert(mrtentry_ptr);
        !           765:                    } /* if is pruned */
        !           766:                } /* if p2p */
        !           767: 
        !           768:                /* if LAN link, echo the prune and schedule delayed
        !           769:                 * oif deletion
        !           770:                 */
        !           771:                else {
        !           772:                    IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           773:                        log(LOG_DEBUG, 0,
        !           774:                            "\tPRUNE(LAN) src %s, group %s - scheduling delayed prune",
        !           775:                            inet_fmt(source, s1), inet_fmt(group, s2));
        !           776:                    
        !           777:                    send_pim_jp(mrtentry_ptr, PIM_ACTION_PRUNE, vifi,
        !           778:                                uni_target_addr.unicast_addr, holdtime);
        !           779:                    schedule_delayed_prune(mrtentry_ptr, vifi, holdtime);
        !           780:                }
        !           781:            }
        !           782:        } /* while groups */
        !           783:     } /* else I am unicast target */
        !           784: 
        !           785:     return(TRUE);
        !           786: }
        !           787: 
        !           788: 
        !           789: int
        !           790: send_pim_jp(mrtentry_ptr, action, vifi, target_addr, holdtime)
        !           791:      mrtentry_t *mrtentry_ptr;
        !           792:      int action;           /* PIM_ACTION_JOIN or PIM_ACTION_PRUNE */
        !           793:      vifi_t vifi;          /* vif to send join/prune on */
        !           794:      u_int32 target_addr;  /* encoded unicast target neighbor */
        !           795:      u_int16 holdtime;     /* holdtime */
        !           796: {
        !           797:     u_int8 *data_ptr, *data_start_ptr;
        !           798:        
        !           799:     data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip)
        !           800:                          + sizeof(pim_header_t));
        !           801:     data_start_ptr = data_ptr;
        !           802: 
        !           803:     if(mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) {
        !           804:        /* No upstream neighbor - don't send */
        !           805:        return(FALSE);
        !           806:     }
        !           807:     
        !           808:     IF_DEBUG(DEBUG_PIM_JOIN_PRUNE)
        !           809:        log(LOG_DEBUG, 0,
        !           810:            "Sending %s:  vif %s, src %s, group %s, target %s, holdtime %d",
        !           811:            action==PIM_ACTION_JOIN ? "JOIN" : "PRUNE",
        !           812:            inet_fmt(uvifs[vifi].uv_lcl_addr, s1),
        !           813:            inet_fmt(mrtentry_ptr->source->address, s2), 
        !           814:            inet_fmt(mrtentry_ptr->group->group, s3),
        !           815:            inet_fmt(target_addr, s4), holdtime);
        !           816: 
        !           817:     PUT_EUADDR(target_addr, data_ptr);   /* encoded unicast target addr */
        !           818:     PUT_BYTE(0, data_ptr);                  /* Reserved */
        !           819:     *data_ptr++ = (u_int8)1;             /* number of groups */
        !           820:     PUT_HOSTSHORT(holdtime, data_ptr);   /* holdtime */
        !           821:     
        !           822:     /* data_ptr points at the first, and only encoded mcast group */
        !           823:     PUT_EGADDR(mrtentry_ptr->group->group, SINGLE_GRP_MSKLEN, 0, data_ptr);
        !           824:     
        !           825:     /* set the number of join and prune sources */
        !           826:     if(action == PIM_ACTION_JOIN) {
        !           827:        PUT_HOSTSHORT(1, data_ptr);
        !           828:        PUT_HOSTSHORT(0, data_ptr);
        !           829:     } else if(action == PIM_ACTION_PRUNE) {
        !           830:        PUT_HOSTSHORT(0, data_ptr);
        !           831:        PUT_HOSTSHORT(1, data_ptr);
        !           832:     }  
        !           833:     
        !           834:     PUT_ESADDR(mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN,
        !           835:                0, data_ptr);
        !           836:     
        !           837:     /* Cancel active graft */
        !           838:     delete_pim_graft_entry(mrtentry_ptr);
        !           839:     
        !           840:     send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
        !           841:              PIM_JOIN_PRUNE, data_ptr - data_start_ptr);
        !           842:     
        !           843:     return(TRUE);
        !           844: }
        !           845: 
        !           846: 
        !           847: /************************************************************************
        !           848:  *                        PIM_ASSERT
        !           849:  ************************************************************************/
        !           850: 
        !           851: /* Notes on assert prefs/metrics
        !           852:  *   - For downstream routers, compare pref/metric previously received from
        !           853:  *     winner against those in message.
        !           854:  *     ==> store assert winner's pref/metric in mrtentry
        !           855:  *   - For upstream router compare my actualy pref/metric for the source
        !           856:  *     against those received in message.
        !           857:  *     ==> store my actual pref/metric in srcentry
        !           858:  */
        !           859: 
        !           860: int
        !           861: receive_pim_assert(src, dst, pim_message, datalen)
        !           862:     u_int32 src, dst;
        !           863:     register char *pim_message;
        !           864:     int datalen;
        !           865: {
        !           866:     vifi_t vifi;
        !           867:     pim_encod_uni_addr_t eusaddr;
        !           868:     pim_encod_grp_addr_t egaddr;
        !           869:     u_int32 source, group;
        !           870:     mrtentry_t *mrtentry_ptr;
        !           871:     u_int8 *data_ptr;
        !           872:     struct uvif *v;
        !           873:     u_int32 assert_preference;
        !           874:     u_int32 assert_metric;
        !           875:     u_int32 local_metric;
        !           876:     u_int32 local_preference;
        !           877:     u_int8  local_wins;
        !           878:     vifbitmap_t new_pruned_oifs;
        !           879:     int state_change;
        !           880:     
        !           881:     if ((vifi = find_vif_direct(src)) == NO_VIF) {
        !           882:         /* Either a local vif or somehow received PIM_ASSERT from
        !           883:          * non-directly connected router. Ignore it.
        !           884:          */
        !           885:         if (local_address(src) == NO_VIF)
        !           886:             log(LOG_INFO, 0,
        !           887:                 "Ignoring PIM_ASSERT from non-neighbor router %s",
        !           888:                 inet_fmt(src, s1));
        !           889:         return(FALSE);
        !           890:     }
        !           891:     
        !           892:     /* Checksum */
        !           893:     if (inet_cksum((u_int16 *)pim_message, datalen))
        !           894:         return(FALSE);
        !           895:     
        !           896:     v = &uvifs[vifi];
        !           897:     if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
        !           898:         return(FALSE);    /* Shoudn't come on this interface */
        !           899:     data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
        !           900: 
        !           901:     /* Get the group and source addresses */
        !           902:     GET_EGADDR(&egaddr, data_ptr);
        !           903:     GET_EUADDR(&eusaddr, data_ptr);
        !           904:     
        !           905:     /* Get the metric related info */
        !           906:     GET_HOSTLONG(assert_preference, data_ptr);
        !           907:     GET_HOSTLONG(assert_metric, data_ptr);
        !           908: 
        !           909:     source = eusaddr.unicast_addr;
        !           910:     group = egaddr.mcast_addr;
        !           911:  
        !           912:     IF_DEBUG(DEBUG_PIM_ASSERT)
        !           913:        log(LOG_DEBUG, 0,
        !           914:            "PIM Assert received from : src %s, grp %s, pref %d, metric %d",
        !           915:            inet_fmt(src, s1), inet_fmt(source, s2), inet_fmt(group, s3),
        !           916:            assert_preference, assert_metric);
        !           917:  
        !           918:     mrtentry_ptr = find_route(source, group, MRTF_SG, CREATE);
        !           919:     if(mrtentry_ptr->flags & MRTF_NEW) {
        !           920:        /* For some reason, it's possible for asserts to be processed
        !           921:         * before the data alerts a cache miss.  Therefore, when an
        !           922:         * assert is received, create (S,G) state and continue, since
        !           923:         * we know by the assert that there are upstream forwarders. 
        !           924:         */
        !           925:        IF_DEBUG(DEBUG_PIM_ASSERT)
        !           926:            log(LOG_DEBUG, 0, "\tNo MRT entry - creating...");
        !           927: 
        !           928:        mrtentry_ptr->flags &= ~MRTF_NEW;
        !           929:        
        !           930:        /* Set oifs */  
        !           931:        set_leaves(mrtentry_ptr);
        !           932:        calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs));
        !           933:        
        !           934:        /* Add it to the kernel */
        !           935:        k_chg_mfc(igmp_socket, source, group, mrtentry_ptr->incoming,
        !           936:                  mrtentry_ptr->oifs);
        !           937:        
        !           938: #ifdef RSRR
        !           939:        rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
        !           940: #endif /* RSRR */
        !           941:        
        !           942:        /* No need to call change_interfaces, but check for NULL oiflist */
        !           943:        if(VIFM_ISEMPTY(mrtentry_ptr->oifs))
        !           944:            trigger_prune_alert(mrtentry_ptr);  
        !           945:     }
        !           946: 
        !           947:     /* If arrived on iif, I'm downstream of the asserted LAN.
        !           948:      * If arrived on oif, I'm upstream of the asserted LAN.
        !           949:      */
        !           950:     if (vifi == mrtentry_ptr->incoming) {
        !           951:        /* assert arrived on iif ==> I'm a downstream router */
        !           952: 
        !           953:        /* Determine local (really that of upstream nbr!) pref/metric */
        !           954:        local_metric = mrtentry_ptr->metric;
        !           955:        local_preference = mrtentry_ptr->preference;
        !           956:  
        !           957:        if(mrtentry_ptr->upstream &&
        !           958:           mrtentry_ptr->upstream->address == src &&
        !           959:           assert_preference == local_preference &&
        !           960:           assert_metric == local_metric)
        !           961:           
        !           962:           /* if assert from previous winner w/ same pref/metric, 
        !           963:            * then assert sender wins again */
        !           964:            local_wins = FALSE;
        !           965: 
        !           966:        else
        !           967:            /* assert from someone else or something changed */
        !           968:            local_wins = compare_metrics(local_preference, local_metric,
        !           969:                                         mrtentry_ptr->upstream->address,
        !           970:                                         assert_preference, assert_metric, 
        !           971:                                         src);
        !           972:        
        !           973:        /* This is between the assert sender and previous winner or rpf 
        !           974:         * (who is the "local" in this case).
        !           975:         */
        !           976:        if(local_wins == TRUE) {
        !           977:            /* the assert-sender loses, so discard the assert */
        !           978:            IF_DEBUG(DEBUG_PIM_ASSERT)
        !           979:                log(LOG_DEBUG, 0,
        !           980:                    "\tAssert sender %s loses", inet_fmt(src, s1));
        !           981:            return(TRUE);
        !           982:        }
        !           983:        
        !           984:        /* The assert sender wins: upstream must be changed to the winner */
        !           985:        IF_DEBUG(DEBUG_PIM_ASSERT)
        !           986:            log(LOG_DEBUG, 0,
        !           987:                "\tAssert sender %s wins", inet_fmt(src, s1));
        !           988:        if(mrtentry_ptr->upstream->address != src) {
        !           989:            IF_DEBUG(DEBUG_PIM_ASSERT)
        !           990:                log(LOG_DEBUG, 0,
        !           991:                    "\tChanging upstream nbr to %s", inet_fmt(src, s1));
        !           992:            mrtentry_ptr->preference = assert_preference;
        !           993:            mrtentry_ptr->metric = assert_metric;
        !           994:            mrtentry_ptr->upstream = find_pim_nbr(src);
        !           995:        }
        !           996:        SET_TIMER(mrtentry_ptr->assert_timer, PIM_ASSERT_TIMEOUT);
        !           997:        mrtentry_ptr->flags |= MRTF_ASSERTED;
        !           998: 
        !           999:        /* Send a join for the S,G if oiflist is non-empty */
        !          1000:        if(!(VIFM_ISEMPTY(mrtentry_ptr->oifs))) 
        !          1001:            send_pim_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming,
        !          1002:                        src, 0);
        !          1003:        
        !          1004:     } /* if assert on iif */
        !          1005: 
        !          1006:     /* If the assert arrived on an oif: */
        !          1007:     else {
        !          1008:        if(!(VIFM_ISSET(vifi, mrtentry_ptr->oifs))) 
        !          1009:            return(FALSE);
        !          1010:        /* assert arrived on oif ==> I'm a upstream router */
        !          1011: 
        !          1012:        /* Determine local pref/metric */
        !          1013:        local_metric = mrtentry_ptr->source->metric;
        !          1014:        local_preference = mrtentry_ptr->source->preference;
        !          1015: 
        !          1016:         local_wins = compare_metrics(local_preference, local_metric,
        !          1017:                                     v->uv_lcl_addr, 
        !          1018:                                     assert_preference, assert_metric, src);
        !          1019: 
        !          1020:        if(local_wins == FALSE) {
        !          1021: 
        !          1022:            /* Assert sender wins - prune the interface */
        !          1023: 
        !          1024:            IF_DEBUG(DEBUG_PIM_ASSERT)
        !          1025:                log(LOG_DEBUG, 0,
        !          1026:                    "\tAssert sender %s wins - pruning...", inet_fmt(src, s1));
        !          1027:            
        !          1028:            VIFM_COPY(mrtentry_ptr->pruned_oifs, new_pruned_oifs);
        !          1029:            VIFM_SET(vifi, new_pruned_oifs);
        !          1030:            SET_TIMER(mrtentry_ptr->prune_timers[vifi], 
        !          1031:                      PIM_JOIN_PRUNE_HOLDTIME);
        !          1032: 
        !          1033:            state_change = 
        !          1034:                change_interfaces(mrtentry_ptr,
        !          1035:                                  mrtentry_ptr->incoming,
        !          1036:                                  new_pruned_oifs,
        !          1037:                                  mrtentry_ptr->leaves);
        !          1038:            
        !          1039:            /* Handle transition to negative cache */
        !          1040:            if(state_change == -1)
        !          1041:                trigger_prune_alert(mrtentry_ptr);
        !          1042: 
        !          1043:        } /* assert sender wins */
        !          1044: 
        !          1045:        else {
        !          1046: 
        !          1047:            /* Local wins (assert sender loses):
        !          1048:             * send assert and schedule prune
        !          1049:             */
        !          1050: 
        !          1051:            IF_DEBUG(DEBUG_PIM_ASSERT)
        !          1052:                log(LOG_DEBUG, 0,
        !          1053:                    "\tAssert sender %s loses - sending assert and scheuling prune", 
        !          1054:                    inet_fmt(src, s1));
        !          1055: 
        !          1056:            if(!(VIFM_ISSET(vifi, mrtentry_ptr->leaves))) {
        !          1057:                /* No directly connected receivers - delay prune */
        !          1058:                send_pim_jp(mrtentry_ptr, PIM_ACTION_PRUNE, vifi, 
        !          1059:                            v->uv_lcl_addr, PIM_JOIN_PRUNE_HOLDTIME);
        !          1060:                schedule_delayed_prune(mrtentry_ptr, vifi, 
        !          1061:                                       PIM_JOIN_PRUNE_HOLDTIME);
        !          1062:            }
        !          1063:            send_pim_assert(source, group, vifi, 
        !          1064:                            mrtentry_ptr->source->preference,
        !          1065:                            mrtentry_ptr->source->metric);
        !          1066:        }
        !          1067: 
        !          1068:     } /* if assert on oif */
        !          1069:        
        !          1070:     return(TRUE);
        !          1071: }
        !          1072: 
        !          1073: 
        !          1074: int 
        !          1075: send_pim_assert(source, group, vifi, local_preference, local_metric)
        !          1076:     u_int32 source;
        !          1077:     u_int32 group;
        !          1078:     vifi_t vifi;
        !          1079:     u_int32 local_preference;
        !          1080:     u_int32 local_metric;
        !          1081: {
        !          1082:     u_int8 *data_ptr;
        !          1083:     u_int8 *data_start_ptr;
        !          1084: 
        !          1085:     data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip)
        !          1086:                           + sizeof(pim_header_t));
        !          1087:     data_start_ptr = data_ptr;
        !          1088:     PUT_EGADDR(group, SINGLE_GRP_MSKLEN, 0, data_ptr);
        !          1089:     PUT_EUADDR(source, data_ptr);
        !          1090: 
        !          1091:     PUT_HOSTLONG(local_preference, data_ptr);
        !          1092:     PUT_HOSTLONG(local_metric, data_ptr);
        !          1093:     send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group,
        !          1094:              PIM_ASSERT, data_ptr - data_start_ptr);
        !          1095: 
        !          1096:     return(TRUE);
        !          1097: }
        !          1098: 
        !          1099: 
        !          1100: /* Return TRUE if the local win, otherwise FALSE */
        !          1101: static int
        !          1102: compare_metrics(local_preference, local_metric, local_address,
        !          1103:                remote_preference, remote_metric, remote_address)
        !          1104:     u_int32 local_preference;
        !          1105:     u_int32 local_metric;
        !          1106:     u_int32 local_address;
        !          1107:     u_int32 remote_preference;
        !          1108:     u_int32 remote_metric;
        !          1109:     u_int32 remote_address;
        !          1110: {
        !          1111:     /* Now lets see who has a smaller gun (aka "asserts war") */
        !          1112:     /* FYI, the smaller gun...err metric wins, but if the same
        !          1113:      * caliber, then the bigger network address wins. The order of
        !          1114:      * treatment is: preference, metric, address.
        !          1115:      */
        !          1116:     /* The RPT bits are already included as the most significant bits
        !          1117:      * of the preferences.
        !          1118:      */
        !          1119:     if (remote_preference > local_preference)
        !          1120:        return TRUE;
        !          1121:     if (remote_preference < local_preference)
        !          1122:        return FALSE;
        !          1123:     if (remote_metric > local_metric)
        !          1124:        return TRUE;
        !          1125:     if (remote_metric < local_metric)
        !          1126:        return FALSE;
        !          1127:     if (ntohl(local_address) > ntohl(remote_address))
        !          1128:        return TRUE;
        !          1129:     return FALSE;
        !          1130: }
        !          1131: 
        !          1132: 
        !          1133: 
        !          1134: 
        !          1135: /************************************************************************
        !          1136:  *                        PIM_GRAFT
        !          1137:  ************************************************************************/
        !          1138: 
        !          1139: 
        !          1140: u_long              graft_retrans_timer;   /* Graft retransmission timer */
        !          1141: pim_graft_entry_t   *graft_list;           /* Active grafting entries */
        !          1142: 
        !          1143: 
        !          1144: void
        !          1145: delete_pim_graft_entry(mrtentry_ptr)
        !          1146:      mrtentry_t *mrtentry_ptr;
        !          1147: {
        !          1148:     pim_graft_entry_t *graft_entry;
        !          1149: 
        !          1150:     if(mrtentry_ptr->graft == (pim_graft_entry_t *)NULL)
        !          1151:        return;
        !          1152:     graft_entry = mrtentry_ptr->graft;
        !          1153:     
        !          1154:     if(graft_entry->prev)
        !          1155:        graft_entry->prev->next = graft_entry->next;
        !          1156:     else 
        !          1157:        graft_list = graft_entry->next;
        !          1158:     if(graft_entry->next)
        !          1159:        graft_entry->next->prev = graft_entry->prev;
        !          1160:     mrtentry_ptr->graft = (pim_graft_entry_t *)NULL;
        !          1161:     free(graft_entry);
        !          1162: 
        !          1163:     /* Stop the timer if there are no more entries */
        !          1164:     if(!graft_list) {
        !          1165:        timer_clearTimer(graft_retrans_timer);
        !          1166:        RESET_TIMER(graft_retrans_timer);
        !          1167:     }
        !          1168: }
        !          1169: 
        !          1170: 
        !          1171: int retransmit_pim_graft(mrtentry_ptr)
        !          1172:      mrtentry_t *mrtentry_ptr;
        !          1173: {
        !          1174:     u_int8 *data_ptr, *data_start_ptr;
        !          1175:        
        !          1176:     data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip)
        !          1177:                          + sizeof(pim_header_t));
        !          1178:     data_start_ptr = data_ptr;
        !          1179: 
        !          1180:     if(mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) {
        !          1181:        /* No upstream neighbor - don't send */
        !          1182:        return(FALSE);
        !          1183:     }
        !          1184:     
        !          1185:     IF_DEBUG(DEBUG_PIM_GRAFT)
        !          1186:        log(LOG_DEBUG, 0,
        !          1187:            "Sending GRAFT:  vif %s, src %s, grp %s, dst %s",
        !          1188:            inet_fmt(uvifs[mrtentry_ptr->incoming].uv_lcl_addr, s1),
        !          1189:            inet_fmt(mrtentry_ptr->source->address, s2), 
        !          1190:            inet_fmt(mrtentry_ptr->group->group, s3),
        !          1191:            inet_fmt(mrtentry_ptr->upstream->address, s4));
        !          1192: 
        !          1193:     
        !          1194:     PUT_EUADDR(mrtentry_ptr->upstream->address, data_ptr); /* unicast target */
        !          1195:     PUT_BYTE(0, data_ptr);              /* Reserved */
        !          1196:     *data_ptr++ = (u_int8)1;             /* number of groups */
        !          1197:     PUT_HOSTSHORT(0, data_ptr);          /* no holdtime */
        !          1198:     
        !          1199:     /* data_ptr points at the first, and only encoded mcast group */
        !          1200:     PUT_EGADDR(mrtentry_ptr->group->group, SINGLE_GRP_MSKLEN, 0, data_ptr);
        !          1201:     
        !          1202:     /* set the number of join(graft) and prune sources */
        !          1203:     PUT_HOSTSHORT(1, data_ptr);
        !          1204:     PUT_HOSTSHORT(0, data_ptr);
        !          1205: 
        !          1206:     PUT_ESADDR(mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN,
        !          1207:                0, data_ptr);
        !          1208:     
        !          1209:     send_pim(pim_send_buf, uvifs[mrtentry_ptr->incoming].uv_lcl_addr, 
        !          1210:             mrtentry_ptr->upstream->address,
        !          1211:             PIM_GRAFT, data_ptr - data_start_ptr);
        !          1212:     
        !          1213:     return(TRUE);
        !          1214: }
        !          1215: 
        !          1216: 
        !          1217: static void
        !          1218: retransmit_all_pim_grafts(arg)
        !          1219:      void *arg; /* UNUSED */
        !          1220: {
        !          1221:     pim_graft_entry_t *graft_ptr;
        !          1222: 
        !          1223:     IF_DEBUG(DEBUG_PIM_GRAFT)
        !          1224:        log(LOG_DEBUG, 0, "Retransmitting all pending PIM-Grafts");
        !          1225:   
        !          1226: 
        !          1227:     for(graft_ptr = graft_list; 
        !          1228:        graft_ptr != NULL; 
        !          1229:        graft_ptr = graft_ptr->next) {
        !          1230: 
        !          1231:        IF_DEBUG(DEBUG_PIM_GRAFT)
        !          1232:            log(LOG_DEBUG, 0, "\tGRAFT src %s, grp %s",
        !          1233:                inet_fmt(graft_ptr->mrtlink->source->address, s1),
        !          1234:                inet_fmt(graft_ptr->mrtlink->group->group, s2));
        !          1235: 
        !          1236:        retransmit_pim_graft(graft_ptr->mrtlink);
        !          1237:     }
        !          1238: 
        !          1239:     if(graft_list)
        !          1240:        timer_setTimer(PIM_GRAFT_RETRANS_PERIOD, retransmit_all_pim_grafts, 
        !          1241:                       (void *)NULL);
        !          1242: }
        !          1243: 
        !          1244: 
        !          1245: int
        !          1246: receive_pim_graft(src, dst, pim_message, datalen, pimtype)
        !          1247:     u_int32 src, dst;
        !          1248:     register char *pim_message;
        !          1249:     int datalen;
        !          1250:     int pimtype;
        !          1251: {
        !          1252:     vifi_t vifi;
        !          1253:     struct uvif *v;
        !          1254:     pim_encod_uni_addr_t uni_target_addr;
        !          1255:     pim_encod_grp_addr_t encod_group;
        !          1256:     pim_encod_src_addr_t encod_src;
        !          1257:     u_int8 *data_ptr;
        !          1258:     u_int8 num_groups;
        !          1259:     u_int16 holdtime;
        !          1260:     u_int16 num_j_srcs;
        !          1261:     u_int16 num_p_srcs;
        !          1262:     u_int32 source;
        !          1263:     u_int32 group;
        !          1264:     u_int32 s_mask;
        !          1265:     u_int32 g_mask;
        !          1266:     u_int8 s_flags;
        !          1267:     u_int8 reserved;
        !          1268:     mrtentry_t *mrtentry_ptr;
        !          1269:     int state_change;
        !          1270: 
        !          1271:     if ((vifi = find_vif_direct(src)) == NO_VIF) {
        !          1272:         /* Either a local vif or somehow received PIM_GRAFT from
        !          1273:          * non-directly connected router. Ignore it.
        !          1274:          */
        !          1275:         if (local_address(src) == NO_VIF)
        !          1276:             log(LOG_INFO, 0,
        !          1277:                 "Ignoring PIM_GRAFT from non-neighbor router %s",
        !          1278:                 inet_fmt(src, s1));
        !          1279:         return(FALSE);
        !          1280:     }
        !          1281: 
        !          1282:     /* Checksum */
        !          1283:     if (inet_cksum((u_int16 *)pim_message, datalen))
        !          1284:         return(FALSE);
        !          1285:  
        !          1286:     v = &uvifs[vifi];
        !          1287:     if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS))
        !          1288:         return(FALSE);    /* Shoudn't come on this interface */
        !          1289:     data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t));
        !          1290: 
        !          1291:     /* Get the target address */
        !          1292:     GET_EUADDR(&uni_target_addr, data_ptr);
        !          1293:     GET_BYTE(reserved, data_ptr);
        !          1294:     GET_BYTE(num_groups, data_ptr);
        !          1295:     if (num_groups == 0)
        !          1296:         return (FALSE);    /* No indication for groups in the message */
        !          1297:     GET_HOSTSHORT(holdtime, data_ptr);
        !          1298: 
        !          1299:     IF_DEBUG(DEBUG_PIM_GRAFT)
        !          1300:        log(LOG_DEBUG, 0,
        !          1301:            "PIM %s received from %s on vif %s",
        !          1302:            pimtype == PIM_GRAFT ? "GRAFT" : "GRAFT-ACK",
        !          1303:            inet_fmt(src, s1), inet_fmt(uvifs[vifi].uv_lcl_addr, s2));
        !          1304:     
        !          1305:     while (num_groups--) {
        !          1306:        GET_EGADDR(&encod_group, data_ptr);
        !          1307:        GET_HOSTSHORT(num_j_srcs, data_ptr);
        !          1308:        GET_HOSTSHORT(num_p_srcs, data_ptr);
        !          1309:        MASKLEN_TO_MASK(encod_group.masklen, g_mask);
        !          1310:        group = encod_group.mcast_addr;
        !          1311:        if (!IN_MULTICAST(ntohl(group))) {
        !          1312:            data_ptr +=
        !          1313:                (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t);
        !          1314:            continue; /* Ignore this group and jump to the next */
        !          1315:        }
        !          1316:        
        !          1317:        while (num_j_srcs--) {
        !          1318:            GET_ESADDR(&encod_src, data_ptr);
        !          1319:            source = encod_src.src_addr;
        !          1320:            if (!inet_valid_host(source))
        !          1321:                continue;
        !          1322:            s_flags = encod_src.flags;
        !          1323:            MASKLEN_TO_MASK(encod_src.masklen, s_mask);
        !          1324:            
        !          1325:            mrtentry_ptr = find_route(source, group, MRTF_SG,
        !          1326:                                      DONT_CREATE);
        !          1327:            if(mrtentry_ptr == (mrtentry_t *)NULL) 
        !          1328:                continue;
        !          1329: 
        !          1330:            if(pimtype == PIM_GRAFT) {
        !          1331:                /* Graft */
        !          1332:                IF_DEBUG(DEBUG_PIM_GRAFT)
        !          1333:                    log(LOG_DEBUG, 0,
        !          1334:                        "\tGRAFT src %s, group %s - forward data on vif %d",
        !          1335:                        inet_fmt(source, s1), inet_fmt(group, s2), vifi);
        !          1336:                
        !          1337:                /* Cancel any delayed prune */
        !          1338:                if(mrtentry_ptr->prune_delay_timerids[vifi]) {
        !          1339:                    timer_clearTimer(mrtentry_ptr->prune_delay_timerids[vifi]);
        !          1340:                    mrtentry_ptr->prune_delay_timerids[vifi] = 0;
        !          1341:                }           
        !          1342:                
        !          1343:                /* Add to oiflist (unprune) */
        !          1344:                if (VIFM_ISSET(vifi, mrtentry_ptr->pruned_oifs)) {
        !          1345:                    VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs);
        !          1346:                    SET_TIMER(mrtentry_ptr->prune_timers[vifi], 0);
        !          1347:                    state_change = 
        !          1348:                        change_interfaces(mrtentry_ptr,
        !          1349:                                          mrtentry_ptr->incoming,
        !          1350:                                          mrtentry_ptr->pruned_oifs,
        !          1351:                                          mrtentry_ptr->leaves);
        !          1352:                    if(state_change == 1)
        !          1353:                        trigger_join_alert(mrtentry_ptr);
        !          1354:                }
        !          1355:            }  /* Graft */
        !          1356:            else {
        !          1357:                /* Graft-Ack */
        !          1358:                if(mrtentry_ptr->graft)
        !          1359:                    delete_pim_graft_entry(mrtentry_ptr);
        !          1360:            }
        !          1361:        }
        !          1362:        /* Ignore anything in the prune portion of the message! */
        !          1363:     }
        !          1364: 
        !          1365:     /* Respond to graft with a graft-ack */
        !          1366:     if(pimtype == PIM_GRAFT) {
        !          1367:        IF_DEBUG(DEBUG_PIM_GRAFT)
        !          1368:            log(LOG_DEBUG, 0, "Sending GRAFT-ACK: vif %s, dst %s",
        !          1369:                inet_fmt(uvifs[vifi].uv_lcl_addr, s1), inet_fmt(src, s2));
        !          1370:        bcopy(pim_message, pim_send_buf + sizeof(struct ip), datalen);
        !          1371:        send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, 
        !          1372:                 src, PIM_GRAFT_ACK, datalen - sizeof(pim_header_t));
        !          1373:     }
        !          1374:        
        !          1375:     return(TRUE);
        !          1376: }
        !          1377: 
        !          1378: int 
        !          1379: send_pim_graft(mrtentry_ptr)
        !          1380:      mrtentry_t *mrtentry_ptr;
        !          1381: {
        !          1382:     pim_graft_entry_t *new_graft;
        !          1383:     int was_sent = 0;
        !          1384: 
        !          1385:     if(mrtentry_ptr->graft != (pim_graft_entry_t *)NULL)
        !          1386:        /* Already sending grafts */
        !          1387:        return(FALSE);
        !          1388: 
        !          1389:     /* Send the first graft */
        !          1390:     was_sent = retransmit_pim_graft(mrtentry_ptr);
        !          1391:     if(!was_sent)
        !          1392:        return(FALSE);
        !          1393: 
        !          1394:     /* Set up retransmission */
        !          1395:     new_graft = (pim_graft_entry_t *)malloc(sizeof(pim_graft_entry_t));
        !          1396:     if (new_graft == (pim_graft_entry_t *)NULL) {
        !          1397:        log(LOG_WARNING, 0, 
        !          1398:            "Memory allocation error for graft entry src %s, grp %s",
        !          1399:            inet_fmt(mrtentry_ptr->source->address, s1),
        !          1400:            inet_fmt(mrtentry_ptr->group->group, s2));
        !          1401:        return(FALSE);
        !          1402:     }
        !          1403:     new_graft->next = graft_list;
        !          1404:     new_graft->prev = (pim_graft_entry_t *)NULL;
        !          1405:     new_graft->mrtlink = mrtentry_ptr;
        !          1406:     if(graft_list == (pim_graft_entry_t *)NULL)
        !          1407:        graft_list = new_graft;
        !          1408:     mrtentry_ptr->graft = new_graft;
        !          1409:        
        !          1410:     /* Set up timer if not running */
        !          1411:     if(!graft_retrans_timer) 
        !          1412:        SET_TIMER(graft_retrans_timer,
        !          1413:                  timer_setTimer(PIM_GRAFT_RETRANS_PERIOD,
        !          1414:                                 retransmit_all_pim_grafts, 
        !          1415:                                 (void *)NULL));
        !          1416: 
        !          1417:     return(TRUE);
        !          1418: }

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