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

    1: /*
    2:  *  Copyright (c) 1998 by the University of Oregon.
    3:  *  All rights reserved.
    4:  *
    5:  *  Permission to use, copy, modify, and distribute this software and
    6:  *  its documentation in source and binary forms for lawful
    7:  *  purposes and without fee is hereby granted, provided
    8:  *  that the above copyright notice appear in all copies and that both
    9:  *  the copyright notice and this permission notice appear in supporting
   10:  *  documentation, and that any documentation, advertising materials,
   11:  *  and other materials related to such distribution and use acknowledge
   12:  *  that the software was developed by the University of Oregon.
   13:  *  The name of the University of Oregon may not be used to endorse or 
   14:  *  promote products derived from this software without specific prior 
   15:  *  written permission.
   16:  *
   17:  *  THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS
   18:  *  ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE.  THIS SOFTWARE IS
   19:  *  PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
   20:  *  INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
   21:  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND 
   22:  *  NON-INFRINGEMENT.
   23:  *
   24:  *  IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY
   25:  *  SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT,
   26:  *  TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH,
   27:  *  THE USE OR PERFORMANCE OF THIS SOFTWARE.
   28:  *
   29:  *  Other copyrights might apply to parts of this software and are so
   30:  *  noted when applicable.
   31:  */
   32: /*
   33:  *  Questions concerning this software should be directed to 
   34:  *  Kurt Windisch (kurtw@antc.uoregon.edu)
   35:  *
   36:  *  $Id: pim_proto.c,v 1.1.1.1 2017/06/12 07:58:55 misho Exp $
   37:  */
   38: /*
   39:  * Part of this program has been derived from PIM sparse-mode pimd.
   40:  * The pimd program is covered by the license in the accompanying file
   41:  * named "LICENSE.pimd".
   42:  *  
   43:  * The pimd program is COPYRIGHT 1998 by University of Southern California.
   44:  *
   45:  * Part of this program has been derived from mrouted.
   46:  * The mrouted program is covered by the license in the accompanying file
   47:  * named "LICENSE.mrouted".
   48:  * 
   49:  * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
   50:  * Leland Stanford Junior University.
   51:  *
   52:  */
   53: 
   54: #include "defs.h"
   55: 
   56: /*
   57:  * 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>