Annotation of embedaddon/pimdd/pim_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: 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>