Annotation of embedaddon/pimd/timer.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1998-2001
! 3: * University of Southern California/Information Sciences Institute.
! 4: * All rights reserved.
! 5: *
! 6: * Redistribution and use in source and binary forms, with or without
! 7: * modification, are permitted provided that the following conditions
! 8: * are met:
! 9: * 1. Redistributions of source code must retain the above copyright
! 10: * notice, this list of conditions and the following disclaimer.
! 11: * 2. Redistributions in binary form must reproduce the above copyright
! 12: * notice, this list of conditions and the following disclaimer in the
! 13: * documentation and/or other materials provided with the distribution.
! 14: * 3. Neither the name of the project nor the names of its contributors
! 15: * may be used to endorse or promote products derived from this software
! 16: * without specific prior written permission.
! 17: *
! 18: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
! 19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 21: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
! 22: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 28: * SUCH DAMAGE.
! 29: */
! 30: /*
! 31: * $Id: timer.c,v 1.31 2001/09/10 20:31:37 pavlin Exp $
! 32: */
! 33:
! 34:
! 35: #include "defs.h"
! 36:
! 37: /*
! 38: * Global variables
! 39: */
! 40:
! 41: /* To account for header overhead, we apx 1 byte/s = 10 bits/s (bps)
! 42: * Note, in the new spt_threshold setting the rate is in kbps as well! */
! 43: spt_threshold_t spt_threshold = {
! 44: .mode = SPT_THRESHOLD_DEFAULT_MODE,
! 45: .bytes = SPT_THRESHOLD_DEFAULT_RATE * SPT_THRESHOLD_DEFAULT_INTERVAL / 10 * 1000,
! 46: .packets = SPT_THRESHOLD_DEFAULT_PACKETS,
! 47: .interval = SPT_THRESHOLD_DEFAULT_INTERVAL,
! 48: };
! 49:
! 50: /*
! 51: * Local variables
! 52: */
! 53: uint16_t unicast_routing_interval = UCAST_ROUTING_CHECK_INTERVAL;
! 54: uint16_t unicast_routing_timer; /* Used to check periodically for any
! 55: * change in the unicast routing. */
! 56: uint8_t ucast_flag;
! 57:
! 58: uint16_t pim_spt_threshold_timer; /* Used for periodic check of spt-threshold
! 59: * for the RP or the lasthop router. */
! 60: uint8_t rate_flag;
! 61:
! 62: /*
! 63: * TODO: XXX: the timers below are not used. Instead, the data rate timer is used.
! 64: */
! 65: uint16_t kernel_cache_timer; /* Used to timeout the kernel cache
! 66: * entries for idle sources */
! 67: uint16_t kernel_cache_interval;
! 68:
! 69: /* to request and compare any route changes */
! 70: srcentry_t srcentry_save;
! 71: rpentry_t rpentry_save;
! 72:
! 73: /*
! 74: * Init some timers
! 75: */
! 76: void init_timers(void)
! 77: {
! 78: SET_TIMER(unicast_routing_timer, unicast_routing_interval);
! 79: SET_TIMER(pim_spt_threshold_timer, spt_threshold.interval);
! 80:
! 81: /* Initialize the srcentry and rpentry used to save the old routes
! 82: * during unicast routing change discovery process. */
! 83: srcentry_save.prev = NULL;
! 84: srcentry_save.next = NULL;
! 85: srcentry_save.address = INADDR_ANY_N;
! 86: srcentry_save.mrtlink = NULL;
! 87: srcentry_save.incoming = NO_VIF;
! 88: srcentry_save.upstream = NULL;
! 89: srcentry_save.metric = ~0;
! 90: srcentry_save.preference = ~0;
! 91: RESET_TIMER(srcentry_save.timer);
! 92: srcentry_save.cand_rp = NULL;
! 93:
! 94: rpentry_save.prev = NULL;
! 95: rpentry_save.next = NULL;
! 96: rpentry_save.address = INADDR_ANY_N;
! 97: rpentry_save.mrtlink = NULL;
! 98: rpentry_save.incoming = NO_VIF;
! 99: rpentry_save.upstream = NULL;
! 100: rpentry_save.metric = ~0;
! 101: rpentry_save.preference = ~0;
! 102: RESET_TIMER(rpentry_save.timer);
! 103: rpentry_save.cand_rp = NULL;
! 104: }
! 105:
! 106: /*
! 107: * On every timer interrupt, advance (i.e. decrease) the timer for each
! 108: * neighbor and group entry for each vif.
! 109: */
! 110: void age_vifs(void)
! 111: {
! 112: vifi_t vifi;
! 113: struct uvif *v;
! 114: pim_nbr_entry_t *next, *curr;
! 115:
! 116: /* XXX: TODO: currently, sending to qe* interface which is DOWN
! 117: * doesn't return error (ENETDOWN) on my Solaris machine,
! 118: * so have to check periodically the
! 119: * interfaces status. If this is fixed, just remove the defs around
! 120: * the "if (vifs_down)" line.
! 121: */
! 122:
! 123: #if (!((defined SunOS) && (SunOS >= 50)))
! 124: if (vifs_down)
! 125: #endif /* Solaris */
! 126: check_vif_state();
! 127:
! 128: /* Age many things */
! 129: for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
! 130: if (v->uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER))
! 131: continue;
! 132:
! 133: /* Timeout neighbors */
! 134: for (curr = v->uv_pim_neighbors; curr; curr = next) {
! 135: next = curr->next;
! 136:
! 137: /* Never timeout neighbors with holdtime = 0xffff.
! 138: * This may be used with ISDN lines to avoid keeping the
! 139: * link up with periodic Hello messages.
! 140: */
! 141: /* TODO: XXX: TIMER implem. dependency! */
! 142: if (PIM_HELLO_HOLDTIME_FOREVER == curr->timer)
! 143: continue;
! 144: IF_NOT_TIMEOUT(curr->timer)
! 145: continue;
! 146:
! 147: logit(LOG_INFO, 0, "Delete PIM neighbor %s on %s (holdtime timeout)",
! 148: inet_fmt(curr->address, s2, sizeof(s2)), v->uv_name);
! 149:
! 150: delete_pim_nbr(curr);
! 151: }
! 152:
! 153: /* PIM_HELLO periodic */
! 154: IF_TIMEOUT(v->uv_hello_timer)
! 155: send_pim_hello(v, pim_timer_hello_holdtime);
! 156:
! 157: #ifdef TOBE_DELETED
! 158: /* PIM_JOIN_PRUNE periodic */
! 159: /* TODO: XXX: TIMER implem. dependency! */
! 160: if (v->uv_jp_timer <= TIMER_INTERVAL)
! 161: /* TODO: need to scan the whole routing table,
! 162: * because different entries have different Join/Prune timer.
! 163: * Probably don't need the Join/Prune timer per vif.
! 164: */
! 165: send_pim_join_prune(vifi, NULL, PIM_JOIN_PRUNE_HOLDTIME);
! 166: else
! 167: /* TODO: XXX: TIMER implem. dependency! */
! 168: v->uv_jp_timer -= TIMER_INTERVAL;
! 169: #endif /* TOBE_DELETED */
! 170:
! 171: /* IGMP query periodic */
! 172: IF_TIMEOUT(v->uv_gq_timer)
! 173: query_groups(v);
! 174:
! 175: if (v->uv_querier &&
! 176: (v->uv_querier->al_timer += TIMER_INTERVAL) > igmp_querier_timeout) {
! 177: /*
! 178: * The current querier has timed out. We must become the
! 179: * querier.
! 180: */
! 181: IF_DEBUG(DEBUG_IGMP) {
! 182: logit(LOG_DEBUG, 0, "IGMP Querier %s timed out.",
! 183: inet_fmt(v->uv_querier->al_addr, s1, sizeof(s1)));
! 184: }
! 185: free(v->uv_querier);
! 186: v->uv_querier = NULL;
! 187: v->uv_flags |= VIFF_QUERIER;
! 188: query_groups(v);
! 189: }
! 190: }
! 191:
! 192: IF_DEBUG(DEBUG_IF) {
! 193: fputs("\n", stderr);
! 194: dump_vifs(stderr);
! 195: }
! 196: }
! 197:
! 198: #define MRT_IS_LASTHOP(mrt) VIFM_LASTHOP_ROUTER(mrt->leaves, mrt->oifs)
! 199: #define MRT_IS_RP(mrt) mrt->incoming == reg_vif_num
! 200:
! 201: static void try_switch_to_spt(mrtentry_t *mrt, kernel_cache_t *kc)
! 202: {
! 203: if (MRT_IS_LASTHOP(mrt) || MRT_IS_RP(mrt)) {
! 204: #ifdef KERNEL_MFC_WC_G
! 205: if (kc->source == INADDR_ANY_N) {
! 206: delete_single_kernel_cache(mrt, kc);
! 207: mrt->flags |= MRTF_MFC_CLONE_SG;
! 208: return;
! 209: }
! 210: #endif /* KERNEL_MFC_WC_G */
! 211:
! 212: switch_shortest_path(kc->source, kc->group);
! 213: }
! 214: }
! 215:
! 216: /*
! 217: * Check the SPT threshold for a given (*,*,RP) or (*,G) entry
! 218: *
! 219: * XXX: the spec says to start monitoring first the total traffic for
! 220: * all senders for particular (*,*,RP) or (*,G) and if the total traffic
! 221: * exceeds some predefined threshold, then start monitoring the data
! 222: * traffic for each particular sender for this group: (*,G) or
! 223: * (*,*,RP). However, because the kernel cache/traffic info is of the
! 224: * form (S,G), it is easier if we are simply collecting (S,G) traffic
! 225: * all the time.
! 226: *
! 227: * For (*,*,RP) if the number of bytes received between the last check
! 228: * and now exceeds some precalculated value (based on interchecking
! 229: * period and datarate threshold AND if there are directly connected
! 230: * members (i.e. we are their last hop(e) router), then create (S,G) and
! 231: * start initiating (S,G) Join toward the source. The same applies for
! 232: * (*,G). The spec does not say that if the datarate goes below a given
! 233: * threshold, then will switch back to the shared tree, hence after a
! 234: * switch to the source-specific tree occurs, a source with low
! 235: * datarate, but periodically sending will keep the (S,G) states.
! 236: *
! 237: * If a source with kernel cache entry has been idle after the last time
! 238: * a check of the datarate for the whole routing table, then delete its
! 239: * kernel cache entry.
! 240: */
! 241: static void check_spt_threshold(mrtentry_t *mrt)
! 242: {
! 243: int status;
! 244: uint32_t prev_bytecnt, prev_pktcnt;
! 245: kernel_cache_t *kc, *kc_next;
! 246:
! 247: /* XXX: TODO: When we add group-list support to spt-threshold we need
! 248: * to move this infinity check to inside the for-loop ... obviously. */
! 249: if (!rate_flag || spt_threshold.mode == SPT_INF)
! 250: return;
! 251:
! 252: for (kc = mrt->kernel_cache; kc; kc = kc_next) {
! 253: kc_next = kc->next;
! 254:
! 255: prev_bytecnt = kc->sg_count.bytecnt;
! 256: prev_pktcnt = kc->sg_count.pktcnt;
! 257:
! 258: status = k_get_sg_cnt(udp_socket, kc->source, kc->group, &kc->sg_count);
! 259: if (status || prev_bytecnt == kc->sg_count.bytecnt) {
! 260: /* Either (for whatever reason) there is no such routing
! 261: * entry, or that particular (S,G) was idle. Delete the
! 262: * routing entry from the kernel. */
! 263: delete_single_kernel_cache(mrt, kc);
! 264: continue;
! 265: }
! 266:
! 267: // TODO: Why is this needed?
! 268: try_switch_to_spt(mrt, kc);
! 269:
! 270: /* Check spt-threshold for forwarder and RP, should we switch to
! 271: * source specific tree (SPT). Need to check only when we have
! 272: * (S,G)RPbit in the forwarder or the RP itself. */
! 273: switch (spt_threshold.mode) {
! 274: case SPT_RATE:
! 275: if (prev_bytecnt + spt_threshold.bytes < kc->sg_count.bytecnt)
! 276: try_switch_to_spt(mrt, kc);
! 277: break;
! 278:
! 279: case SPT_PACKETS:
! 280: if (prev_pktcnt + spt_threshold.packets < kc->sg_count.pktcnt)
! 281: try_switch_to_spt(mrt, kc);
! 282: break;
! 283:
! 284: default:
! 285: ; /* INF not handled here yet. */
! 286: }
! 287:
! 288: /* XXX: currently the spec doesn't say to switch back to the
! 289: * shared tree if low datarate, but if needed to implement, the
! 290: * check must be done here. Don't forget to check whether I am a
! 291: * forwarder for that source. */
! 292: }
! 293: }
! 294:
! 295:
! 296: /*
! 297: * Scan the whole routing table and timeout a bunch of timers:
! 298: * - oifs timers
! 299: * - Join/Prune timer
! 300: * - routing entry
! 301: * - Assert timer
! 302: * - Register-Suppression timer
! 303: *
! 304: * - If the global timer for checking the unicast routing has expired, perform
! 305: * also iif/upstream router change verification
! 306: * - If the global timer for checking the data rate has expired, check the
! 307: * number of bytes forwarded after the lastest timeout. If bigger than
! 308: * a given threshold, then switch to the shortest path.
! 309: * If `number_of_bytes == 0`, then delete the kernel cache entry.
! 310: *
! 311: * Only the entries which have the Join/Prune timer expired are sent.
! 312: * In the special case when we have ~(S,G)RPbit Prune entry, we must
! 313: * include any (*,G) or (*,*,RP) XXX: ???? what and why?
! 314: *
! 315: * Below is a table which summarizes the segmantic rules.
! 316: *
! 317: * On the left side is "if A must be included in the J/P message".
! 318: * On the top is "shall/must include B?"
! 319: * "Y" means "MUST include"
! 320: * "SY" means "SHOULD include"
! 321: * "N" means "NO NEED to include"
! 322: * (G is a group that matches to RP)
! 323: *
! 324: * -----------||-----------||-----------
! 325: * || (*,*,RP) || (*,G) || (S,G) ||
! 326: * ||-----------||-----------||-----------||
! 327: * || J | P || J | P || J | P ||
! 328: * ==================================================||
! 329: * J || n/a | n/a || N | Y || N | Y ||
! 330: * (*,*,RP) -----------------------------------------||
! 331: * P || n/a | n/a || SY | N || SY | N ||
! 332: * ==================================================||
! 333: * J || N | N || n/a | n/a || N | Y ||
! 334: * (*,G) -----------------------------------------||
! 335: * P || N | N || n/a | n/a || SY | N ||
! 336: * ==================================================||
! 337: * J || N | N || N | N || n/a | n/a ||
! 338: * (S,G) -----------------------------------------||
! 339: * P || N | N || N | N || n/a | n/a ||
! 340: * ==================================================
! 341: *
! 342: */
! 343: void age_routes(void)
! 344: {
! 345: cand_rp_t *cand_rp;
! 346: grpentry_t *grp;
! 347: grpentry_t *grp_next;
! 348: mrtentry_t *mrt_grp;
! 349: mrtentry_t *mrt_rp;
! 350: mrtentry_t *mrt_wide;
! 351: mrtentry_t *mrt_srcs;
! 352: mrtentry_t *mrt_srcs_next;
! 353: rp_grp_entry_t *rp_grp;
! 354: struct uvif *v;
! 355: vifi_t vifi;
! 356: pim_nbr_entry_t *nbr;
! 357: int change_flag;
! 358: int rp_action, grp_action, src_action = PIM_ACTION_NOTHING, src_action_rp = PIM_ACTION_NOTHING;
! 359: int dont_calc_action;
! 360: rpentry_t *rp;
! 361: int update_rp_iif;
! 362: int update_src_iif;
! 363: vifbitmap_t new_pruned_oifs;
! 364: int assert_timer_expired = 0;
! 365:
! 366: /*
! 367: * Timing out of the global `unicast_routing_timer`
! 368: * and `data_rate_timer`
! 369: */
! 370: IF_TIMEOUT(unicast_routing_timer) {
! 371: ucast_flag = TRUE;
! 372: SET_TIMER(unicast_routing_timer, unicast_routing_interval);
! 373: }
! 374: ELSE {
! 375: ucast_flag = FALSE;
! 376: }
! 377:
! 378: IF_TIMEOUT(pim_spt_threshold_timer) {
! 379: rate_flag = TRUE;
! 380: SET_TIMER(pim_spt_threshold_timer, spt_threshold.interval);
! 381: }
! 382: ELSE {
! 383: rate_flag = FALSE;
! 384: }
! 385:
! 386: /* Scan the (*,*,RP) entries */
! 387: for (cand_rp = cand_rp_list; cand_rp; cand_rp = cand_rp->next) {
! 388: rp = cand_rp->rpentry;
! 389:
! 390: /* Need to save only `incoming` and `upstream` to discover
! 391: * unicast route changes. `metric` and `preference` are not
! 392: * interesting for us.
! 393: */
! 394: rpentry_save.incoming = rp->incoming;
! 395: rpentry_save.upstream = rp->upstream;
! 396:
! 397: update_rp_iif = FALSE;
! 398: if ((ucast_flag == TRUE) && (rp->address != my_cand_rp_address)) {
! 399: /* I am not the RP. If I was the RP, then the iif is
! 400: * register_vif and no need to reset it. */
! 401: if (set_incoming(rp, PIM_IIF_RP) != TRUE) {
! 402: /* TODO: XXX: no route to that RP. Panic? There is a high
! 403: * probability the network is partitioning so immediately
! 404: * remapping to other RP is not a good idea. Better wait
! 405: * the Bootstrap mechanism to take care of it and provide
! 406: * me with correct Cand-RP-Set. */
! 407: }
! 408: else {
! 409: if ((rpentry_save.upstream != rp->upstream) ||
! 410: (rpentry_save.incoming != rp->incoming)) {
! 411: /* Routing change has occur. Update all (*,G)
! 412: * and (S,G)RPbit iifs mapping to that RP */
! 413: update_rp_iif = TRUE;
! 414: }
! 415: }
! 416: }
! 417:
! 418: rp_action = PIM_ACTION_NOTHING;
! 419: mrt_rp = cand_rp->rpentry->mrtlink;
! 420: if (mrt_rp) {
! 421: /* outgoing interfaces timers */
! 422: change_flag = FALSE;
! 423: for (vifi = 0; vifi < numvifs; vifi++) {
! 424: if (VIFM_ISSET(vifi, mrt_rp->joined_oifs)) {
! 425: IF_TIMEOUT(mrt_rp->vif_timers[vifi]) {
! 426: VIFM_CLR(vifi, mrt_rp->joined_oifs);
! 427: change_flag = TRUE;
! 428: }
! 429: }
! 430: }
! 431: if ((change_flag == TRUE) || (update_rp_iif == TRUE)) {
! 432: change_interfaces(mrt_rp,
! 433: rp->incoming,
! 434: mrt_rp->joined_oifs,
! 435: mrt_rp->pruned_oifs,
! 436: mrt_rp->leaves,
! 437: mrt_rp->asserted_oifs, 0);
! 438: mrt_rp->upstream = rp->upstream;
! 439: }
! 440:
! 441: /* Check the activity for this entry */
! 442: check_spt_threshold(mrt_rp);
! 443:
! 444: /* Join/Prune timer */
! 445: IF_TIMEOUT(mrt_rp->jp_timer) {
! 446: rp_action = join_or_prune(mrt_rp, mrt_rp->upstream);
! 447:
! 448: if (rp_action != PIM_ACTION_NOTHING)
! 449: add_jp_entry(mrt_rp->upstream,
! 450: PIM_JOIN_PRUNE_HOLDTIME,
! 451: htonl(CLASSD_PREFIX),
! 452: STAR_STAR_RP_MSKLEN,
! 453: mrt_rp->source->address,
! 454: SINGLE_SRC_MSKLEN,
! 455: MRTF_RP | MRTF_WC,
! 456: rp_action);
! 457:
! 458: SET_TIMER(mrt_rp->jp_timer, PIM_JOIN_PRUNE_PERIOD);
! 459: }
! 460:
! 461: /* Assert timer */
! 462: if (mrt_rp->flags & MRTF_ASSERTED) {
! 463: IF_TIMEOUT(mrt_rp->assert_timer) {
! 464: /* TODO: XXX: reset the upstream router now */
! 465: mrt_rp->flags &= ~MRTF_ASSERTED;
! 466: }
! 467: }
! 468:
! 469: /* Register-Suppression timer */
! 470: /* TODO: to reduce the kernel calls, if the timer is running,
! 471: * install a negative cache entry in the kernel?
! 472: */
! 473: /* TODO: can we have Register-Suppression timer for (*,*,RP)?
! 474: * Currently no...
! 475: */
! 476: IF_TIMEOUT(mrt_rp->rs_timer) {}
! 477:
! 478: /* routing entry */
! 479: if ((TIMEOUT(mrt_rp->timer)) && (VIFM_ISEMPTY(mrt_rp->leaves)))
! 480: delete_mrtentry(mrt_rp);
! 481: } /* if (mrt_rp) */
! 482:
! 483: /* Just in case if that (*,*,RP) was deleted */
! 484: mrt_rp = cand_rp->rpentry->mrtlink;
! 485:
! 486: /* Check the (*,G) and (S,G) entries */
! 487: for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
! 488: for (grp = rp_grp->grplink; grp; grp = grp_next) {
! 489: grp_next = grp->rpnext;
! 490: grp_action = PIM_ACTION_NOTHING;
! 491: mrt_grp = grp->grp_route;
! 492: mrt_srcs = grp->mrtlink;
! 493:
! 494: if (mrt_grp) {
! 495: /* The (*,G) entry */
! 496: /* outgoing interfaces timers */
! 497: change_flag = FALSE;
! 498: assert_timer_expired = 0;
! 499: if (mrt_grp->flags & MRTF_ASSERTED)
! 500: assert_timer_expired = TIMEOUT(mrt_grp->assert_timer);
! 501:
! 502: for (vifi = 0; vifi < numvifs; vifi++) {
! 503: if (VIFM_ISSET(vifi, mrt_grp->joined_oifs)) {
! 504: IF_TIMEOUT(mrt_grp->vif_timers[vifi]) {
! 505: VIFM_CLR(vifi, mrt_grp->joined_oifs);
! 506: change_flag = TRUE;
! 507: }
! 508: }
! 509:
! 510: if (assert_timer_expired) {
! 511: VIFM_CLR(vifi, mrt_grp->asserted_oifs);
! 512: change_flag = TRUE;
! 513: mrt_grp->flags &= ~MRTF_ASSERTED;
! 514: }
! 515: }
! 516:
! 517: if ((change_flag == TRUE) || (update_rp_iif == TRUE)) {
! 518: change_interfaces(mrt_grp,
! 519: rp->incoming,
! 520: mrt_grp->joined_oifs,
! 521: mrt_grp->pruned_oifs,
! 522: mrt_grp->leaves,
! 523: mrt_grp->asserted_oifs, 0);
! 524: mrt_grp->upstream = rp->upstream;
! 525: }
! 526:
! 527: /* Check the sources activity */
! 528: check_spt_threshold(mrt_grp);
! 529:
! 530: dont_calc_action = FALSE;
! 531: if (rp_action != PIM_ACTION_NOTHING) {
! 532: dont_calc_action = TRUE;
! 533:
! 534: grp_action = join_or_prune(mrt_grp, mrt_grp->upstream);
! 535: if (((rp_action == PIM_ACTION_JOIN) && (grp_action == PIM_ACTION_PRUNE)) ||
! 536: ((rp_action == PIM_ACTION_PRUNE) && (grp_action == PIM_ACTION_JOIN)))
! 537: FIRE_TIMER(mrt_grp->jp_timer);
! 538: }
! 539:
! 540:
! 541: /* Join/Prune timer */
! 542: IF_TIMEOUT(mrt_grp->jp_timer) {
! 543: if (dont_calc_action != TRUE)
! 544: grp_action = join_or_prune(mrt_grp, mrt_grp->upstream);
! 545:
! 546: if (grp_action != PIM_ACTION_NOTHING)
! 547: add_jp_entry(mrt_grp->upstream,
! 548: PIM_JOIN_PRUNE_HOLDTIME,
! 549: mrt_grp->group->group,
! 550: SINGLE_GRP_MSKLEN,
! 551: cand_rp->rpentry->address,
! 552: SINGLE_SRC_MSKLEN,
! 553: MRTF_RP | MRTF_WC,
! 554: grp_action);
! 555: SET_TIMER(mrt_grp->jp_timer, PIM_JOIN_PRUNE_PERIOD);
! 556: }
! 557:
! 558: /* Register-Suppression timer */
! 559: /* TODO: to reduce the kernel calls, if the timer
! 560: * is running, install a negative cache entry in
! 561: * the kernel?
! 562: */
! 563: /* TODO: currently cannot have Register-Suppression
! 564: * timer for (*,G) entry, but keep this around.
! 565: */
! 566: IF_TIMEOUT(mrt_grp->rs_timer) {}
! 567:
! 568: /* routing entry */
! 569: if ((TIMEOUT(mrt_grp->timer)) && (VIFM_ISEMPTY(mrt_grp->leaves)))
! 570: delete_mrtentry(mrt_grp);
! 571: } /* if (mrt_grp) */
! 572:
! 573:
! 574: /* For all (S,G) for this group */
! 575: /* XXX: mrt_srcs was set before */
! 576: for (; mrt_srcs; mrt_srcs = mrt_srcs_next) {
! 577: /* routing entry */
! 578: mrt_srcs_next = mrt_srcs->grpnext;
! 579:
! 580: /* outgoing interfaces timers */
! 581: change_flag = FALSE;
! 582: assert_timer_expired = 0;
! 583: if (mrt_srcs->flags & MRTF_ASSERTED)
! 584: assert_timer_expired = TIMEOUT(mrt_srcs->assert_timer);
! 585:
! 586: for (vifi = 0; vifi < numvifs; vifi++) {
! 587: if (VIFM_ISSET(vifi, mrt_srcs->joined_oifs)) {
! 588: /* TODO: checking for reg_num_vif is slow! */
! 589: if (vifi != reg_vif_num) {
! 590: IF_TIMEOUT(mrt_srcs->vif_timers[vifi]) {
! 591: VIFM_CLR(vifi, mrt_srcs->joined_oifs);
! 592: change_flag = TRUE;
! 593: }
! 594: }
! 595: }
! 596:
! 597: if (assert_timer_expired) {
! 598: VIFM_CLR(vifi, mrt_srcs->asserted_oifs);
! 599: change_flag = TRUE;
! 600: mrt_srcs->flags &= ~MRTF_ASSERTED;
! 601: }
! 602: }
! 603:
! 604: update_src_iif = FALSE;
! 605: if (ucast_flag == TRUE) {
! 606: if (!(mrt_srcs->flags & MRTF_RP)) {
! 607: /* iif toward the source */
! 608: srcentry_save.incoming = mrt_srcs->source->incoming;
! 609: srcentry_save.upstream = mrt_srcs->source->upstream;
! 610: if (set_incoming(mrt_srcs->source, PIM_IIF_SOURCE) != TRUE) {
! 611: /* XXX: not in the spec!
! 612: * Cannot find route toward that source.
! 613: * This is bad. Delete the entry.
! 614: */
! 615: delete_mrtentry(mrt_srcs);
! 616: continue;
! 617: }
! 618:
! 619: /* iif info found */
! 620: if ((srcentry_save.incoming != mrt_srcs->incoming) ||
! 621: (srcentry_save.upstream != mrt_srcs->upstream)) {
! 622: /* Route change has occur */
! 623: update_src_iif = TRUE;
! 624: mrt_srcs->incoming = mrt_srcs->source->incoming;
! 625: mrt_srcs->upstream = mrt_srcs->source->upstream;
! 626: }
! 627: } else {
! 628: /* (S,G)RPBit with iif toward RP */
! 629: if ((rpentry_save.upstream != mrt_srcs->upstream) ||
! 630: (rpentry_save.incoming != mrt_srcs->incoming)) {
! 631: update_src_iif = TRUE; /* XXX: a hack */
! 632: /* XXX: setup the iif now! */
! 633: mrt_srcs->incoming = rp->incoming;
! 634: mrt_srcs->upstream = rp->upstream;
! 635: }
! 636: }
! 637: }
! 638:
! 639: if ((change_flag == TRUE) || (update_src_iif == TRUE))
! 640: /* Flush the changes */
! 641: change_interfaces(mrt_srcs,
! 642: mrt_srcs->incoming,
! 643: mrt_srcs->joined_oifs,
! 644: mrt_srcs->pruned_oifs,
! 645: mrt_srcs->leaves,
! 646: mrt_srcs->asserted_oifs, 0);
! 647:
! 648: check_spt_threshold(mrt_srcs);
! 649:
! 650: mrt_wide = mrt_srcs->group->grp_route;
! 651: if (!mrt_wide)
! 652: mrt_wide = mrt_rp;
! 653:
! 654: dont_calc_action = FALSE;
! 655: if ((rp_action != PIM_ACTION_NOTHING) ||
! 656: (grp_action != PIM_ACTION_NOTHING)) {
! 657: src_action_rp = join_or_prune(mrt_srcs, rp->upstream);
! 658: src_action = src_action_rp;
! 659: dont_calc_action = TRUE;
! 660:
! 661: if (src_action_rp == PIM_ACTION_JOIN) {
! 662: if ((grp_action == PIM_ACTION_PRUNE) ||
! 663: (rp_action == PIM_ACTION_PRUNE))
! 664: FIRE_TIMER(mrt_srcs->jp_timer);
! 665: } else if (src_action_rp == PIM_ACTION_PRUNE) {
! 666: if ((grp_action == PIM_ACTION_JOIN) ||
! 667: (rp_action == PIM_ACTION_JOIN))
! 668: FIRE_TIMER(mrt_srcs->jp_timer);
! 669: }
! 670: }
! 671:
! 672: /* Join/Prune timer */
! 673: IF_TIMEOUT(mrt_srcs->jp_timer) {
! 674: if ((dont_calc_action != TRUE) || (rp->upstream != mrt_srcs->upstream))
! 675: src_action = join_or_prune(mrt_srcs, mrt_srcs->upstream);
! 676:
! 677: if (src_action != PIM_ACTION_NOTHING)
! 678: add_jp_entry(mrt_srcs->upstream,
! 679: PIM_JOIN_PRUNE_HOLDTIME,
! 680: mrt_srcs->group->group,
! 681: SINGLE_GRP_MSKLEN,
! 682: mrt_srcs->source->address,
! 683: SINGLE_SRC_MSKLEN,
! 684: mrt_srcs->flags & MRTF_RP,
! 685: src_action);
! 686:
! 687: if (mrt_wide) {
! 688: /* Have both (S,G) and (*,G) (or (*,*,RP)).
! 689: * Check if need to send (S,G) PRUNE toward RP */
! 690: if (mrt_srcs->upstream != mrt_wide->upstream) {
! 691: if (dont_calc_action != TRUE)
! 692: src_action_rp = join_or_prune(mrt_srcs, mrt_wide->upstream);
! 693:
! 694: /* XXX: TODO: do error check if
! 695: * src_action == PIM_ACTION_JOIN, which
! 696: * should be an error. */
! 697: if (src_action_rp == PIM_ACTION_PRUNE)
! 698: add_jp_entry(mrt_wide->upstream,
! 699: PIM_JOIN_PRUNE_HOLDTIME,
! 700: mrt_srcs->group->group,
! 701: SINGLE_GRP_MSKLEN,
! 702: mrt_srcs->source->address,
! 703: SINGLE_SRC_MSKLEN,
! 704: MRTF_RP,
! 705: src_action_rp);
! 706: }
! 707: }
! 708: SET_TIMER(mrt_srcs->jp_timer, PIM_JOIN_PRUNE_PERIOD);
! 709: }
! 710:
! 711: /* Register-Suppression timer */
! 712: /* TODO: to reduce the kernel calls, if the timer
! 713: * is running, install a negative cache entry in
! 714: * the kernel? */
! 715: IF_TIMER_SET(mrt_srcs->rs_timer) {
! 716: IF_TIMEOUT(mrt_srcs->rs_timer) {
! 717: /* Start encapsulating the packets */
! 718: VIFM_COPY(mrt_srcs->pruned_oifs, new_pruned_oifs);
! 719: VIFM_CLR(reg_vif_num, new_pruned_oifs);
! 720: change_interfaces(mrt_srcs,
! 721: mrt_srcs->incoming,
! 722: mrt_srcs->joined_oifs,
! 723: new_pruned_oifs,
! 724: mrt_srcs->leaves,
! 725: mrt_srcs->asserted_oifs, 0);
! 726: }
! 727: ELSE {
! 728: /* The register suppression timer is running. Check
! 729: * whether it is time to send PIM_NULL_REGISTER.
! 730: */
! 731: /* TODO: XXX: TIMER implem. dependency! */
! 732: if (mrt_srcs->rs_timer <= PIM_REGISTER_PROBE_TIME)
! 733: /* Time to send a PIM_NULL_REGISTER */
! 734: /* XXX: a (bad) hack! This will be sending
! 735: * periodically NULL_REGISTERS between
! 736: * PIM_REGISTER_PROBE_TIME and 0. Well,
! 737: * because PROBE_TIME is 5 secs, it will
! 738: * happen only once, so it helps to avoid
! 739: * adding a flag to the routing entry whether
! 740: * a NULL_REGISTER was sent.
! 741: */
! 742: send_pim_null_register(mrt_srcs);
! 743: }
! 744: }
! 745:
! 746: /* routing entry */
! 747: if (TIMEOUT(mrt_srcs->timer)) {
! 748: if (VIFM_ISEMPTY(mrt_srcs->leaves)) {
! 749: delete_mrtentry(mrt_srcs);
! 750: continue;
! 751: }
! 752: /* XXX: if DR, Register suppressed,
! 753: * and leaf oif inherited from (*,G), the
! 754: * directly connected source is not active anymore,
! 755: * this (S,G) entry won't timeout. Check if the leaf
! 756: * oifs are inherited from (*,G); if true. delete the
! 757: * (S,G) entry.
! 758: */
! 759: if (mrt_srcs->group->grp_route) {
! 760: if (!((mrt_srcs->group->grp_route->leaves & mrt_srcs->leaves) ^ mrt_srcs->leaves)) {
! 761: delete_mrtentry(mrt_srcs);
! 762: continue;
! 763: }
! 764: }
! 765: }
! 766: } /* End of (S,G) loop */
! 767: } /* End of (*,G) loop */
! 768: }
! 769: } /* For all cand RPs */
! 770:
! 771: /* TODO: check again! */
! 772: for (vifi = 0, v = &uvifs[0]; vifi < numvifs; vifi++, v++) {
! 773: /* Send all pending Join/Prune messages */
! 774: for (nbr = v->uv_pim_neighbors; nbr; nbr = nbr->next)
! 775: pack_and_send_jp_message(nbr);
! 776: }
! 777:
! 778: IF_DEBUG(DEBUG_PIM_MRT) {
! 779: fputs("\n", stderr);
! 780: dump_pim_mrt(stderr);
! 781: }
! 782: }
! 783:
! 784:
! 785: /*
! 786: * TODO: timeout the RP-group mapping entries during the scan of the
! 787: * whole routing table?
! 788: */
! 789: void age_misc(void)
! 790: {
! 791: rp_grp_entry_t *rp;
! 792: rp_grp_entry_t *rp_next;
! 793: grp_mask_t *grp;
! 794: grp_mask_t *grp_next;
! 795:
! 796: /* Timeout the Cand-RP-set entries */
! 797: for (grp = grp_mask_list; grp; grp = grp_next) {
! 798: /* If we timeout an entry, the grp entry might be removed */
! 799: grp_next = grp->next;
! 800: for (rp = grp->grp_rp_next; rp; rp = rp_next) {
! 801: rp_next = rp->grp_rp_next;
! 802:
! 803: if (rp->holdtime < 60000) {
! 804: IF_TIMEOUT(rp->holdtime) {
! 805: if (rp->group!=NULL) {
! 806: logit(LOG_INFO, 0, "Delete RP group entry for group %s (holdtime timeout)",
! 807: inet_fmt(rp->group->group_addr, s2, sizeof(s2)));
! 808: }
! 809: delete_rp_grp_entry(&cand_rp_list, &grp_mask_list, rp);
! 810: }
! 811: }
! 812: }
! 813: }
! 814:
! 815: /* Cand-RP-Adv timer */
! 816: if (cand_rp_flag == TRUE) {
! 817: IF_TIMEOUT(pim_cand_rp_adv_timer) {
! 818: send_pim_cand_rp_adv();
! 819: SET_TIMER(pim_cand_rp_adv_timer, my_cand_rp_adv_period);
! 820: }
! 821: }
! 822:
! 823: /* bootstrap-timer */
! 824: IF_TIMEOUT(pim_bootstrap_timer) {
! 825: if (cand_bsr_flag == FALSE) {
! 826: /* If I am not Cand-BSR, start accepting Bootstrap messages from anyone.
! 827: * XXX: Even if the BSR has timeout, the existing Cand-RP-Set is kept. */
! 828: SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT);
! 829: curr_bsr_fragment_tag = 0;
! 830: curr_bsr_priority = 0; /* Lowest priority */
! 831: curr_bsr_address = INADDR_ANY_N; /* Lowest priority */
! 832: MASKLEN_TO_MASK(RP_DEFAULT_IPV4_HASHMASKLEN, curr_bsr_hash_mask);
! 833: } else {
! 834: /* I am Cand-BSR, so set the current BSR to me */
! 835: if (curr_bsr_address == my_bsr_address) {
! 836: SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_PERIOD);
! 837: send_pim_bootstrap();
! 838: } else {
! 839: /* Short delay before becoming the BSR and start sending
! 840: * of the Cand-RP set (to reduce the transient control
! 841: * overhead). */
! 842: SET_TIMER(pim_bootstrap_timer, bootstrap_initial_delay());
! 843: curr_bsr_fragment_tag = RANDOM();
! 844: curr_bsr_priority = my_bsr_priority;
! 845: curr_bsr_address = my_bsr_address;
! 846: curr_bsr_hash_mask = my_bsr_hash_mask;
! 847: }
! 848: }
! 849: }
! 850:
! 851: IF_DEBUG(DEBUG_PIM_BOOTSTRAP | DEBUG_PIM_CAND_RP)
! 852: dump_rp_set(stderr);
! 853: /* TODO: XXX: anything else to timeout */
! 854: }
! 855:
! 856: /**
! 857: * Local Variables:
! 858: * version-control: t
! 859: * indent-tabs-mode: t
! 860: * c-file-style: "ellemtel"
! 861: * c-basic-offset: 4
! 862: * End:
! 863: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>