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