Annotation of embedaddon/pimd/route.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: route.c,v 1.39 2003/02/12 21:56:55 pavlin Exp $
! 32: */
! 33:
! 34:
! 35: #include "defs.h"
! 36:
! 37:
! 38: /* Marian Stagarescu : 07/31/01:
! 39: *
! 40: * Administrative scoped multicast filtering im PIMD. This allows an
! 41: * interface to be configured as an administrative boundary for the
! 42: * specified scoped address. Packets belonging to the scoped address will
! 43: * not be forwarded.
! 44: *
! 45: * Please note the in order to minimize the search for the matching groups
! 46: * the implementation is limited to:
! 47: *
! 48: * Packets are stopped from being forwarded by installing a NULL outgoing
! 49: * interface; the user space (pimd) is not up-call-ed any more for
! 50: * these packets which are dropped by kernel (nil oif) except for
! 51: * when we de-install the route are re-create it (timer 3 minute).
! 52: * uses the VIF acl that was installed via config scoped statements.
! 53: *
! 54: * this is not an all-purpose packet filtering mechanism.
! 55: * we tried here to achieve the filtering with minimal processing
! 56: * (inspect (g) when we are about to install a route for it).
! 57: *
! 58: * to use it edit pimd.conf and compile with -DSCOPED_ACL
! 59: */
! 60:
! 61: static void process_cache_miss (struct igmpmsg *igmpctl);
! 62: static void process_wrong_iif (struct igmpmsg *igmpctl);
! 63: static void process_whole_pkt (char *buf);
! 64:
! 65: #ifdef SCOPED_ACL
! 66: /* from mrouted. Contributed by Marian Stagarescu <marian@bile.cidera.com>*/
! 67: static int scoped_addr(vifi_t vifi, uint32_t addr)
! 68: {
! 69: struct vif_acl *acl;
! 70:
! 71: for (acl = uvifs[vifi].uv_acl; acl; acl = acl->acl_next) {
! 72: if ((addr & acl->acl_mask) == acl->acl_addr)
! 73: return 1;
! 74: }
! 75:
! 76: return 0;
! 77: }
! 78:
! 79: /* Contributed by Marian Stagarescu <marian@bile.cidera.com>
! 80: * adapted from mrouted: check for scoped multicast addresses
! 81: * install null oif if matched
! 82: */
! 83: #define APPLY_SCOPE(g, mp) { \
! 84: vifi_t i; \
! 85: for (i = 0; i < numvifs; i++) \
! 86: if (scoped_addr(i, g)) \
! 87: (mp)->oifs = 0; \
! 88: }
! 89: #endif /* SCOPED_ACL */
! 90:
! 91: /* Return the iif for given address */
! 92: vifi_t get_iif(uint32_t address)
! 93: {
! 94: struct rpfctl rpfc;
! 95:
! 96: k_req_incoming(address, &rpfc);
! 97: if (rpfc.rpfneighbor.s_addr == INADDR_ANY_N)
! 98: return NO_VIF;
! 99:
! 100: return rpfc.iif;
! 101: }
! 102:
! 103: /* Return the PIM neighbor toward a source */
! 104: /* If route not found or if a local source or if a directly connected source,
! 105: * but is not PIM router, or if the first hop router is not a PIM router,
! 106: * then return NULL.
! 107: */
! 108: pim_nbr_entry_t *find_pim_nbr(uint32_t source)
! 109: {
! 110: struct rpfctl rpfc;
! 111: pim_nbr_entry_t *nbr;
! 112: uint32_t addr;
! 113:
! 114: if (local_address(source) != NO_VIF)
! 115: return NULL;
! 116: k_req_incoming(source, &rpfc);
! 117:
! 118: if ((rpfc.rpfneighbor.s_addr == INADDR_ANY_N) || (rpfc.iif == NO_VIF))
! 119: return NULL;
! 120:
! 121: /* Figure out the nexthop neighbor by checking the reverse path */
! 122: addr = rpfc.rpfneighbor.s_addr;
! 123: for (nbr = uvifs[rpfc.iif].uv_pim_neighbors; nbr; nbr = nbr->next)
! 124: if (nbr->address == addr)
! 125: return nbr;
! 126:
! 127: return NULL;
! 128: }
! 129:
! 130:
! 131: /* TODO: check again the exact setup if the source is local or directly
! 132: * connected!!!
! 133: */
! 134: /* TODO: XXX: change the metric and preference for all (S,G) entries per
! 135: * source or RP?
! 136: */
! 137: /* TODO - If possible, this would be the place to correct set the
! 138: * source's preference and metric to that obtained from the kernel
! 139: * and/or unicast routing protocol. For now, set it to the configured
! 140: * default for local pref/metric.
! 141: */
! 142:
! 143: /*
! 144: * Set the iif, upstream router, preference and metric for the route
! 145: * toward the source. Return TRUE is the route was found, othewise FALSE.
! 146: * If type==PIM_IIF_SOURCE and if the source is directly connected
! 147: * then the "upstream" is set to NULL. If srcentry==PIM_IIF_RP, then
! 148: * "upstream" in case of directly connected "source" will be that "source"
! 149: * (if it is also PIM router).,
! 150: */
! 151: int set_incoming(srcentry_t *src, int type)
! 152: {
! 153: struct rpfctl rpfc;
! 154: uint32_t src_addr = src->address;
! 155: uint32_t nbr_addr;
! 156: struct uvif *vif;
! 157: pim_nbr_entry_t *nbr;
! 158:
! 159: /* Preference will be 0 if directly connected */
! 160: src->metric = 0;
! 161: src->preference = 0;
! 162:
! 163: /* The source is a local address */
! 164: src->incoming = local_address(src_addr);
! 165: if (src->incoming != NO_VIF) {
! 166: /* iif of (*,G) at RP has to be register_if */
! 167: if (type == PIM_IIF_RP)
! 168: src->incoming = reg_vif_num;
! 169:
! 170: /* TODO: set the upstream to myself? */
! 171: src->upstream = NULL;
! 172: return TRUE;
! 173: }
! 174:
! 175: src->incoming = find_vif_direct(src_addr);
! 176: if (src->incoming != NO_VIF) {
! 177: /* The source is directly connected. Check whether we are
! 178: * looking for real source or RP */
! 179: if (type == PIM_IIF_SOURCE) {
! 180: src->upstream = NULL;
! 181: return TRUE;
! 182: }
! 183:
! 184: /* PIM_IIF_RP */
! 185: nbr_addr = src_addr;
! 186: } else {
! 187: /* TODO: probably need to check the case if the iif is disabled */
! 188: /* Use the lastest resource: the kernel unicast routing table */
! 189: k_req_incoming(src_addr, &rpfc);
! 190: if ((rpfc.iif == NO_VIF) || rpfc.rpfneighbor.s_addr == INADDR_ANY_N) {
! 191: /* couldn't find a route */
! 192: if (!IN_LINK_LOCAL_RANGE(src_addr)) {
! 193: IF_DEBUG(DEBUG_PIM_MRT | DEBUG_RPF)
! 194: logit(LOG_DEBUG, 0, "NO ROUTE found for %s", inet_fmt(src_addr, s1, sizeof(s1)));
! 195: }
! 196: return FALSE;
! 197: }
! 198:
! 199: src->incoming = rpfc.iif;
! 200: nbr_addr = rpfc.rpfneighbor.s_addr;
! 201:
! 202: /* set the preference for sources that aren't directly connected. */
! 203: vif = &uvifs[src->incoming];
! 204: src->preference = vif->uv_local_pref;
! 205: src->metric = vif->uv_local_metric;
! 206: }
! 207:
! 208: /* The upstream router must be a (PIM router) neighbor, otherwise we
! 209: * are in big trouble ;-) */
! 210: vif = &uvifs[src->incoming];
! 211: for (nbr = vif->uv_pim_neighbors; nbr; nbr = nbr->next) {
! 212: if (ntohl(nbr_addr) < ntohl(nbr->address))
! 213: continue;
! 214:
! 215: if (nbr_addr == nbr->address) {
! 216: /* The upstream router is found in the list of neighbors.
! 217: * We are safe! */
! 218: src->upstream = nbr;
! 219: IF_DEBUG(DEBUG_RPF)
! 220: logit(LOG_DEBUG, 0, "For src %s, iif is %d, next hop router is %s",
! 221: inet_fmt(src_addr, s1, sizeof(s1)), src->incoming,
! 222: inet_fmt(nbr_addr, s2, sizeof(s2)));
! 223:
! 224: return TRUE;
! 225: }
! 226:
! 227: break;
! 228: }
! 229:
! 230: /* TODO: control the number of messages! */
! 231: logit(LOG_INFO, 0, "For src %s, iif is %d, next hop router is %s: NOT A PIM ROUTER",
! 232: inet_fmt(src_addr, s1, sizeof(s1)), src->incoming,
! 233: inet_fmt(nbr_addr, s2, sizeof(s2)));
! 234: src->upstream = NULL;
! 235:
! 236: return FALSE;
! 237: }
! 238:
! 239:
! 240: /*
! 241: * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
! 242: * we have source-specific Join/Prune.
! 243: */
! 244: void add_leaf(vifi_t vifi, uint32_t source, uint32_t group)
! 245: {
! 246: mrtentry_t *mrt;
! 247: mrtentry_t *srcs;
! 248: vifbitmap_t old_oifs;
! 249: vifbitmap_t new_oifs;
! 250: vifbitmap_t new_leaves;
! 251:
! 252: /* Don't create routing entries for the LAN scoped addresses */
! 253: if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP) { /* group <= 224.0.0.255? */
! 254: IF_DEBUG(DEBUG_IGMP)
! 255: logit(LOG_DEBUG, 0, "Not creating routing entry for LAN scoped group %s",
! 256: inet_fmt(group, s1, sizeof(s1)));
! 257: return;
! 258: }
! 259:
! 260: /*
! 261: * XXX: only if I am a DR, the IGMP Join should result in creating
! 262: * a PIM MRT state.
! 263: * XXX: Each router must know if it has local members, i.e., whether
! 264: * it is a last-hop router as well. This info is needed so it will
! 265: * know whether is allowed to initiate a SPT switch by sending
! 266: * a PIM (S,G) Join to the high datarate source.
! 267: * However, if a non-DR last-hop router has not received
! 268: * a PIM Join, it should not create a PIM state, otherwise later
! 269: * this state may incorrectly trigger PIM joins.
! 270: * There is a design flow in pimd, so without making major changes
! 271: * the best we can do is that the non-DR last-hop router will
! 272: * record the local members only after it receives PIM Join from the DR
! 273: * (i.e. after the second or third IGMP Join by the local member).
! 274: * The downside is that a last-hop router may delay the initiation
! 275: * of the SPT switch. Sigh...
! 276: */
! 277: if (IN_PIM_SSM_RANGE(group))
! 278: mrt = find_route(source, group, MRTF_SG, CREATE);
! 279: else if (uvifs[vifi].uv_flags & VIFF_DR)
! 280: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
! 281: else
! 282: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
! 283:
! 284: if (!mrt)
! 285: return;
! 286:
! 287: IF_DEBUG(DEBUG_MRT)
! 288: logit(LOG_DEBUG, 0, "Adding vif %d for group %s", vifi, inet_fmt(group, s1, sizeof(s1)));
! 289:
! 290: if (VIFM_ISSET(vifi, mrt->leaves))
! 291: return; /* Already a leaf */
! 292:
! 293: calc_oifs(mrt, &old_oifs);
! 294: VIFM_COPY(mrt->leaves, new_leaves);
! 295: VIFM_SET(vifi, new_leaves); /* Add the leaf */
! 296: change_interfaces(mrt,
! 297: mrt->incoming,
! 298: mrt->joined_oifs,
! 299: mrt->pruned_oifs,
! 300: new_leaves,
! 301: mrt->asserted_oifs, 0);
! 302: calc_oifs(mrt, &new_oifs);
! 303:
! 304: /* Only if I am the DR for that subnet, eventually initiate a Join */
! 305: if (!(uvifs[vifi].uv_flags & VIFF_DR))
! 306: return;
! 307:
! 308: if ((mrt->flags & MRTF_NEW) || (VIFM_ISEMPTY(old_oifs) && (!VIFM_ISEMPTY(new_oifs)))) {
! 309: /* A new created entry or the oifs have changed
! 310: * from NULL to non-NULL. */
! 311: mrt->flags &= ~MRTF_NEW;
! 312: FIRE_TIMER(mrt->jp_timer); /* Timeout the Join/Prune timer */
! 313:
! 314: /* TODO: explicitly call the function below?
! 315: send_pim_join_prune(mrt->upstream->vifi,
! 316: mrt->upstream,
! 317: PIM_JOIN_PRUNE_HOLDTIME);
! 318: */
! 319: }
! 320:
! 321: /* Check all (S,G) entries and set the inherited "leaf" flag.
! 322: * TODO: XXX: This won't work for IGMPv3, because there we don't know
! 323: * whether the (S,G) leaf oif was inherited from the (*,G) entry or
! 324: * was created by source specific IGMP join.
! 325: */
! 326: for (srcs = mrt->group->mrtlink; srcs; srcs = srcs->grpnext) {
! 327: VIFM_COPY(srcs->leaves, new_leaves);
! 328: VIFM_SET(vifi, new_leaves);
! 329: change_interfaces(srcs,
! 330: srcs->incoming,
! 331: srcs->joined_oifs,
! 332: srcs->pruned_oifs,
! 333: new_leaves,
! 334: srcs->asserted_oifs, 0);
! 335: }
! 336: }
! 337:
! 338:
! 339: /*
! 340: * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where
! 341: * we have source-specific joins/prunes.
! 342: */
! 343: void delete_leaf(vifi_t vifi, uint32_t source, uint32_t group)
! 344: {
! 345: mrtentry_t *mrt;
! 346: mrtentry_t *srcs;
! 347: vifbitmap_t new_oifs;
! 348: vifbitmap_t old_oifs;
! 349: vifbitmap_t new_leaves;
! 350:
! 351: if (IN_PIM_SSM_RANGE(group))
! 352: mrt = find_route(source, group, MRTF_SG, DONT_CREATE);
! 353: else
! 354: mrt = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
! 355:
! 356: if (!mrt)
! 357: return;
! 358:
! 359: if (!VIFM_ISSET(vifi, mrt->leaves))
! 360: return; /* This interface wasn't leaf */
! 361:
! 362: IF_DEBUG(DEBUG_MRT)
! 363: logit(LOG_DEBUG, 0, "Deleting vif %d for group %s", vifi, inet_fmt(group, s1, sizeof(s1)));
! 364:
! 365: calc_oifs(mrt, &old_oifs);
! 366:
! 367: /* For SSM, source must match */
! 368: if (!IN_PIM_SSM_RANGE(group) || (mrt->source->address==source)) {
! 369: VIFM_COPY(mrt->leaves, new_leaves);
! 370: VIFM_CLR(vifi, new_leaves);
! 371: change_interfaces(mrt,
! 372: mrt->incoming,
! 373: mrt->joined_oifs,
! 374: mrt->pruned_oifs,
! 375: new_leaves,
! 376: mrt->asserted_oifs, 0);
! 377: }
! 378: calc_oifs(mrt, &new_oifs);
! 379:
! 380: if ((!VIFM_ISEMPTY(old_oifs)) && VIFM_ISEMPTY(new_oifs)) {
! 381: /* The result oifs have changed from non-NULL to NULL */
! 382: FIRE_TIMER(mrt->jp_timer); /* Timeout the Join/Prune timer */
! 383:
! 384: /* TODO: explicitly call the function below?
! 385: send_pim_join_prune(mrt->upstream->vifi,
! 386: mrt->upstream,
! 387: PIM_JOIN_PRUNE_HOLDTIME);
! 388: */
! 389: }
! 390:
! 391: /* Check all (S,G) entries and clear the inherited "leaf" flag.
! 392: * TODO: XXX: This won't work for IGMPv3, because there we don't know
! 393: * whether the (S,G) leaf oif was inherited from the (*,G) entry or
! 394: * was created by source specific IGMP join.
! 395: */
! 396: for (srcs = mrt->group->mrtlink; srcs; srcs = srcs->grpnext) {
! 397: VIFM_COPY(srcs->leaves, new_leaves);
! 398: VIFM_CLR(vifi, new_leaves);
! 399: change_interfaces(srcs,
! 400: srcs->incoming,
! 401: srcs->joined_oifs,
! 402: srcs->pruned_oifs,
! 403: new_leaves,
! 404: srcs->asserted_oifs, 0);
! 405: }
! 406: }
! 407:
! 408:
! 409: void calc_oifs(mrtentry_t *mrt, vifbitmap_t *oifs_ptr)
! 410: {
! 411: vifbitmap_t oifs;
! 412: mrtentry_t *grp;
! 413: mrtentry_t *mrp;
! 414:
! 415: /*
! 416: * oifs =
! 417: * (((copied_outgoing + my_join) - my_prune) + my_leaves)
! 418: * - my_asserted_oifs - incoming_interface,
! 419: * i.e. `leaves` have higher priority than `prunes`, but lower priority
! 420: * than `asserted`. The incoming interface is always deleted from the oifs
! 421: */
! 422:
! 423: if (!mrt) {
! 424: VIFM_CLRALL(*oifs_ptr);
! 425: return;
! 426: }
! 427:
! 428: VIFM_CLRALL(oifs);
! 429: if (!(mrt->flags & MRTF_PMBR)) {
! 430: /* Either (*,G) or (S,G). Merge with the oifs from the (*,*,RP) */
! 431: mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
! 432: if (mrp) {
! 433: VIFM_MERGE(oifs, mrp->joined_oifs, oifs);
! 434: VIFM_CLR_MASK(oifs, mrp->pruned_oifs);
! 435: VIFM_MERGE(oifs, mrp->leaves, oifs);
! 436: VIFM_CLR_MASK(oifs, mrp->asserted_oifs);
! 437: }
! 438: }
! 439: if (mrt->flags & MRTF_SG) {
! 440: /* (S,G) entry. Merge with the oifs from (*,G) */
! 441: grp = mrt->group->grp_route;
! 442: if (grp) {
! 443: VIFM_MERGE(oifs, grp->joined_oifs, oifs);
! 444: VIFM_CLR_MASK(oifs, grp->pruned_oifs);
! 445: VIFM_MERGE(oifs, grp->leaves, oifs);
! 446: VIFM_CLR_MASK(oifs, grp->asserted_oifs);
! 447: }
! 448: }
! 449:
! 450: /* Calculate my own stuff */
! 451: VIFM_MERGE(oifs, mrt->joined_oifs, oifs);
! 452: VIFM_CLR_MASK(oifs, mrt->pruned_oifs);
! 453: VIFM_MERGE(oifs, mrt->leaves, oifs);
! 454: VIFM_CLR_MASK(oifs, mrt->asserted_oifs);
! 455:
! 456: VIFM_COPY(oifs, *oifs_ptr);
! 457: }
! 458:
! 459: /*
! 460: * Set the iif, join/prune/leaves/asserted interfaces. Calculate and
! 461: * set the oifs.
! 462: * Return 1 if oifs change from NULL to not-NULL.
! 463: * Return -1 if oifs change from non-NULL to NULL
! 464: * else return 0
! 465: * If the iif change or if the oifs change from NULL to non-NULL
! 466: * or vice-versa, then schedule that mrtentry join/prune timer to
! 467: * timeout immediately.
! 468: */
! 469: int change_interfaces(mrtentry_t *mrt,
! 470: vifi_t new_iif,
! 471: vifbitmap_t new_joined_oifs_,
! 472: vifbitmap_t new_pruned_oifs,
! 473: vifbitmap_t new_leaves_,
! 474: vifbitmap_t new_asserted_oifs,
! 475: uint16_t flags)
! 476: {
! 477: vifbitmap_t new_joined_oifs; /* The oifs for that particular mrtentry */
! 478: vifbitmap_t old_joined_oifs __attribute__ ((unused));
! 479: vifbitmap_t old_pruned_oifs __attribute__ ((unused));
! 480: vifbitmap_t old_leaves __attribute__ ((unused));
! 481: vifbitmap_t new_leaves;
! 482: vifbitmap_t old_asserted_oifs __attribute__ ((unused));
! 483: vifbitmap_t new_real_oifs; /* The result oifs */
! 484: vifbitmap_t old_real_oifs;
! 485: vifi_t old_iif;
! 486: rpentry_t *rp;
! 487: cand_rp_t *cand_rp;
! 488: kernel_cache_t *kc;
! 489: rp_grp_entry_t *rp_grp;
! 490: grpentry_t *grp;
! 491: mrtentry_t *srcs;
! 492: mrtentry_t *mwc;
! 493: mrtentry_t *mrp;
! 494: int delete_mrt_flag;
! 495: int result;
! 496: int fire_timer_flag;
! 497:
! 498: if (!mrt)
! 499: return 0;
! 500:
! 501: VIFM_COPY(new_joined_oifs_, new_joined_oifs);
! 502: VIFM_COPY(new_leaves_, new_leaves);
! 503:
! 504: old_iif = mrt->incoming;
! 505: VIFM_COPY(mrt->joined_oifs, old_joined_oifs);
! 506: VIFM_COPY(mrt->leaves, old_leaves);
! 507: VIFM_COPY(mrt->pruned_oifs, old_pruned_oifs);
! 508: VIFM_COPY(mrt->asserted_oifs, old_asserted_oifs);
! 509:
! 510: VIFM_COPY(mrt->oifs, old_real_oifs);
! 511:
! 512: mrt->incoming = new_iif;
! 513: VIFM_COPY(new_joined_oifs, mrt->joined_oifs);
! 514: VIFM_COPY(new_pruned_oifs, mrt->pruned_oifs);
! 515: VIFM_COPY(new_leaves, mrt->leaves);
! 516: VIFM_COPY(new_asserted_oifs, mrt->asserted_oifs);
! 517: calc_oifs(mrt, &new_real_oifs);
! 518:
! 519: if (VIFM_ISEMPTY(old_real_oifs)) {
! 520: if (VIFM_ISEMPTY(new_real_oifs))
! 521: result = 0;
! 522: else
! 523: result = 1;
! 524: } else {
! 525: if (VIFM_ISEMPTY(new_real_oifs))
! 526: result = -1;
! 527: else
! 528: result = 0;
! 529: }
! 530:
! 531: if ((VIFM_SAME(new_real_oifs, old_real_oifs))
! 532: && (new_iif == old_iif)
! 533: && !(flags & MFC_UPDATE_FORCE))
! 534: return 0; /* Nothing to change */
! 535:
! 536: if ((result != 0) || (new_iif != old_iif) || (flags & MFC_UPDATE_FORCE)) {
! 537: FIRE_TIMER(mrt->jp_timer);
! 538: }
! 539: VIFM_COPY(new_real_oifs, mrt->oifs);
! 540:
! 541: if (mrt->flags & MRTF_PMBR) {
! 542: /* (*,*,RP) entry */
! 543: rp = mrt->source;
! 544: if (!rp)
! 545: return 0; /* Shouldn't happen */
! 546:
! 547: rp->incoming = new_iif;
! 548: cand_rp = rp->cand_rp;
! 549:
! 550: if (VIFM_ISEMPTY(new_real_oifs)) {
! 551: delete_mrt_flag = TRUE;
! 552: } else {
! 553: delete_mrt_flag = FALSE;
! 554: #ifdef RSRR
! 555: rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK);
! 556: #endif /* RSRR */
! 557: }
! 558:
! 559: if (mrt->flags & MRTF_KERNEL_CACHE) {
! 560: /* Update the kernel MFC entries */
! 561: if (delete_mrt_flag == TRUE) {
! 562: /* XXX: no need to send RSRR message. Will do it when
! 563: * delete the mrtentry.
! 564: */
! 565: for (kc = mrt->kernel_cache; kc; kc = kc->next)
! 566: delete_mrtentry_all_kernel_cache(mrt);
! 567: } else {
! 568: /* here mrt->source->address is the RP address */
! 569: for (kc = mrt->kernel_cache; kc; kc = kc->next)
! 570: k_chg_mfc(igmp_socket, kc->source,
! 571: kc->group, new_iif,
! 572: new_real_oifs, mrt->source->address);
! 573: }
! 574: }
! 575:
! 576: /*
! 577: * Update all (*,G) entries associated with this RP.
! 578: * The particular (*,G) outgoing are not changed, but the change
! 579: * in the (*,*,RP) oifs may have affect the real oifs.
! 580: */
! 581: fire_timer_flag = FALSE;
! 582: for (rp_grp = cand_rp->rp_grp_next; rp_grp; rp_grp = rp_grp->rp_grp_next) {
! 583: for (grp = rp_grp->grplink; grp; grp = grp->rpnext) {
! 584: if (grp->grp_route) {
! 585: if (change_interfaces(grp->grp_route, new_iif,
! 586: grp->grp_route->joined_oifs,
! 587: grp->grp_route->pruned_oifs,
! 588: grp->grp_route->leaves,
! 589: grp->grp_route->asserted_oifs,
! 590: flags))
! 591: fire_timer_flag = TRUE;
! 592: } else {
! 593: /* Change all (S,G) entries if no (*,G) */
! 594: for (srcs = grp->mrtlink; srcs; srcs = srcs->grpnext) {
! 595: if (srcs->flags & MRTF_RP) {
! 596: if (change_interfaces(srcs, new_iif,
! 597: srcs->joined_oifs,
! 598: srcs->pruned_oifs,
! 599: srcs->leaves,
! 600: srcs->asserted_oifs,
! 601: flags))
! 602: fire_timer_flag = TRUE;
! 603: } else {
! 604: if (change_interfaces(srcs,
! 605: srcs->incoming,
! 606: srcs->joined_oifs,
! 607: srcs->pruned_oifs,
! 608: srcs->leaves,
! 609: srcs->asserted_oifs,
! 610: flags))
! 611: fire_timer_flag = TRUE;
! 612: }
! 613: }
! 614: }
! 615: }
! 616: }
! 617: if (fire_timer_flag == TRUE)
! 618: FIRE_TIMER(mrt->jp_timer);
! 619: if (delete_mrt_flag == TRUE) {
! 620: /* TODO: XXX: trigger a Prune message? Don't delete now, it will
! 621: * be automatically timed out. If want to delete now, don't
! 622: * reference to it anymore!
! 623: delete_mrtentry(mrt);
! 624: */
! 625: }
! 626:
! 627: return result; /* (*,*,RP) */
! 628: }
! 629:
! 630: if (mrt->flags & MRTF_WC) {
! 631: /* (*,G) entry */
! 632: if (VIFM_ISEMPTY(new_real_oifs)) {
! 633: delete_mrt_flag = TRUE;
! 634: } else {
! 635: delete_mrt_flag = FALSE;
! 636: #ifdef RSRR
! 637: rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK);
! 638: #endif /* RSRR */
! 639: }
! 640:
! 641: if (mrt->flags & MRTF_KERNEL_CACHE) {
! 642: if (delete_mrt_flag == TRUE) {
! 643: delete_mrtentry_all_kernel_cache(mrt);
! 644: } else {
! 645: for (kc = mrt->kernel_cache; kc; kc = kc->next)
! 646: k_chg_mfc(igmp_socket, kc->source,
! 647: kc->group, new_iif,
! 648: new_real_oifs, mrt->group->rpaddr);
! 649: }
! 650: }
! 651:
! 652: /* Update all (S,G) entries for this group.
! 653: * For the (S,G)RPbit entries the iif is the iif toward the RP;
! 654: * The particular (S,G) oifs are not changed, but the change in the
! 655: * (*,G) oifs may affect the real oifs.
! 656: */
! 657: fire_timer_flag = FALSE;
! 658: for (srcs = mrt->group->mrtlink; srcs; srcs = srcs->grpnext) {
! 659: if (srcs->flags & MRTF_RP) {
! 660: if (change_interfaces(srcs, new_iif,
! 661: srcs->joined_oifs,
! 662: srcs->pruned_oifs,
! 663: srcs->leaves,
! 664: srcs->asserted_oifs, flags))
! 665: fire_timer_flag = TRUE;
! 666: } else {
! 667: if (change_interfaces(srcs, srcs->incoming,
! 668: srcs->joined_oifs,
! 669: srcs->pruned_oifs,
! 670: srcs->leaves,
! 671: srcs->asserted_oifs, flags))
! 672: fire_timer_flag = TRUE;
! 673: }
! 674: }
! 675:
! 676: if (fire_timer_flag == TRUE)
! 677: FIRE_TIMER(mrt->jp_timer);
! 678:
! 679: if (delete_mrt_flag == TRUE) {
! 680: /* TODO: XXX: the oifs are NULL. Send a Prune message? */
! 681: }
! 682:
! 683: return result; /* (*,G) */
! 684: }
! 685:
! 686: /* (S,G) entry */
! 687: if (mrt->flags & MRTF_SG) {
! 688: mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
! 689: mwc = mrt->group->grp_route;
! 690:
! 691: #ifdef KERNEL_MFC_WC_G
! 692: mrtentry_t *tmp;
! 693:
! 694: /* Check whether (*,*,RP) or (*,G) have different (iif,oifs) from
! 695: * the (S,G). If "yes", then forbid creating (*,G) MFC. */
! 696: for (tmp = mrp; 1; tmp = mwc) {
! 697: while (1) {
! 698: vifbitmap_t oifs;
! 699:
! 700: if (!tmp)
! 701: break;
! 702:
! 703: if (tmp->flags & MRTF_MFC_CLONE_SG)
! 704: break;
! 705:
! 706: if (tmp->incoming != mrt->incoming) {
! 707: delete_single_kernel_cache_addr(tmp, INADDR_ANY_N, mrt->group->group);
! 708: tmp->flags |= MRTF_MFC_CLONE_SG;
! 709: break;
! 710: }
! 711:
! 712: calc_oifs(tmp, &oifs);
! 713: if (!(VIFM_SAME(new_real_oifs, oifs)))
! 714: tmp->flags |= MRTF_MFC_CLONE_SG;
! 715:
! 716: break;
! 717: }
! 718:
! 719: if (tmp == mwc)
! 720: break;
! 721: }
! 722: #endif /* KERNEL_MFC_WC_G */
! 723:
! 724: if (VIFM_ISEMPTY(new_real_oifs)) {
! 725: delete_mrt_flag = TRUE;
! 726: } else {
! 727: delete_mrt_flag = FALSE;
! 728: #ifdef RSRR
! 729: rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK);
! 730: #endif
! 731: }
! 732:
! 733: if (mrt->flags & MRTF_KERNEL_CACHE) {
! 734: if (delete_mrt_flag == TRUE)
! 735: delete_mrtentry_all_kernel_cache(mrt);
! 736: else
! 737: k_chg_mfc(igmp_socket, mrt->source->address,
! 738: mrt->group->group, new_iif, new_real_oifs,
! 739: mrt->group->rpaddr);
! 740: }
! 741:
! 742: if (old_iif != new_iif) {
! 743: if (new_iif == mrt->source->incoming) {
! 744: /* For example, if this was (S,G)RPbit with iif toward the RP,
! 745: * and now switch to the Shortest Path.
! 746: * The setup of MRTF_SPT flag must be
! 747: * done by the external calling function (triggered only
! 748: * by receiving of a data from the source.)
! 749: */
! 750: mrt->flags &= ~MRTF_RP;
! 751: /* TODO: XXX: delete? Check again where will be the best
! 752: * place to set it.
! 753: mrt->flags |= MRTF_SPT;
! 754: */
! 755: }
! 756:
! 757: if ((mwc && mwc->incoming == new_iif) ||
! 758: (mrp && mrp->incoming == new_iif)) {
! 759: /* If the new iif points toward the RP, reset the SPT flag.
! 760: * (PIM-SM-spec-10.ps pp. 11, 2.10, last sentence of first
! 761: * paragraph. */
! 762:
! 763: /* TODO: XXX: check again! */
! 764: mrt->flags &= ~MRTF_SPT;
! 765: mrt->flags |= MRTF_RP;
! 766: }
! 767: }
! 768:
! 769: /* TODO: XXX: if this is (S,G)RPbit entry and the oifs==(*,G)oifs,
! 770: * then delete the (S,G) entry?? The same if we have (*,*,RP) ? */
! 771: if (delete_mrt_flag == TRUE) {
! 772: /* TODO: XXX: the oifs are NULL. Send a Prune message ? */
! 773: }
! 774:
! 775: /* TODO: XXX: have the feeling something is missing.... */
! 776: return result; /* (S,G) */
! 777: }
! 778:
! 779: return result;
! 780: }
! 781:
! 782:
! 783: /* TODO: implement it. Required to allow changing of the physical interfaces
! 784: * configuration without need to restart pimd.
! 785: */
! 786: int delete_vif_from_mrt(vifi_t vifi __attribute__((unused)))
! 787: {
! 788: return TRUE;
! 789: }
! 790:
! 791:
! 792: void process_kernel_call(void)
! 793: {
! 794: struct igmpmsg *igmpctl = (struct igmpmsg *)igmp_recv_buf;
! 795:
! 796: switch (igmpctl->im_msgtype) {
! 797: case IGMPMSG_NOCACHE:
! 798: process_cache_miss(igmpctl);
! 799: break;
! 800:
! 801: case IGMPMSG_WRONGVIF:
! 802: process_wrong_iif(igmpctl);
! 803: break;
! 804:
! 805: case IGMPMSG_WHOLEPKT:
! 806: process_whole_pkt(igmp_recv_buf);
! 807: break;
! 808:
! 809: default:
! 810: IF_DEBUG(DEBUG_KERN)
! 811: logit(LOG_DEBUG, 0, "Unknown IGMP message type from kernel: %d", igmpctl->im_msgtype);
! 812: break;
! 813: }
! 814: }
! 815:
! 816:
! 817: /*
! 818: * TODO: when cache miss, check the iif, because probably ASSERTS
! 819: * shoult take place
! 820: */
! 821: static void process_cache_miss(struct igmpmsg *igmpctl)
! 822: {
! 823: uint32_t source, mfc_source;
! 824: uint32_t group;
! 825: uint32_t rp_addr;
! 826: vifi_t iif;
! 827: mrtentry_t *mrt;
! 828: mrtentry_t *mrp;
! 829:
! 830: /* When there is a cache miss, we check only the header of the packet
! 831: * (and only it should be sent up by the kernel. */
! 832:
! 833: group = igmpctl->im_dst.s_addr;
! 834: source = mfc_source = igmpctl->im_src.s_addr;
! 835: iif = igmpctl->im_vif;
! 836:
! 837: IF_DEBUG(DEBUG_MRT)
! 838: logit(LOG_DEBUG, 0, "Cache miss, src %s, dst %s, iif %d",
! 839: inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), iif);
! 840:
! 841: /* TODO: XXX: check whether the kernel generates cache miss for the LAN scoped addresses */
! 842: if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP)
! 843: return; /* Don't create routing entries for the LAN scoped addresses */
! 844:
! 845: /* TODO: check if correct in case the source is one of my addresses */
! 846: /* If I am the DR for this source, create (S,G) and add the register_vif
! 847: * to the oifs. */
! 848:
! 849: if ((uvifs[iif].uv_flags & VIFF_DR) && (find_vif_direct_local(source) == iif)) {
! 850: mrt = find_route(source, group, MRTF_SG, CREATE);
! 851: if (!mrt)
! 852: return;
! 853:
! 854: mrt->flags &= ~MRTF_NEW;
! 855: /* set reg_vif_num as outgoing interface ONLY if I am not the RP */
! 856: if (mrt->group->rpaddr != my_cand_rp_address)
! 857: VIFM_SET(reg_vif_num, mrt->joined_oifs);
! 858: change_interfaces(mrt,
! 859: mrt->incoming,
! 860: mrt->joined_oifs,
! 861: mrt->pruned_oifs,
! 862: mrt->leaves,
! 863: mrt->asserted_oifs, 0);
! 864: } else {
! 865: mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
! 866: switch_shortest_path(source, group);
! 867: if (!mrt)
! 868: return;
! 869: }
! 870:
! 871: /* TODO: if there are too many cache miss for the same (S,G),
! 872: * install negative cache entry in the kernel (oif==NULL) to prevent
! 873: * too many upcalls. */
! 874:
! 875: if (mrt->incoming == iif) {
! 876: if (!VIFM_ISEMPTY(mrt->oifs)) {
! 877: if (mrt->flags & MRTF_SG) {
! 878: /* TODO: check that the RPbit is not set? */
! 879: /* TODO: XXX: TIMER implem. dependency! */
! 880: if (mrt->timer < PIM_DATA_TIMEOUT)
! 881: SET_TIMER(mrt->timer, PIM_DATA_TIMEOUT);
! 882:
! 883: if (!(mrt->flags & MRTF_SPT)) {
! 884: mrp = mrt->group->grp_route;
! 885: if (!mrp)
! 886: mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
! 887:
! 888: if (mrp) {
! 889: /* Check if the (S,G) iif is different from
! 890: * the (*,G) or (*,*,RP) iif */
! 891: if ((mrt->incoming != mrp->incoming) ||
! 892: (mrt->upstream != mrp->upstream)) {
! 893: mrt->flags |= MRTF_SPT;
! 894: mrt->flags &= ~MRTF_RP;
! 895: }
! 896: }
! 897: }
! 898: }
! 899:
! 900: if (mrt->flags & MRTF_PMBR)
! 901: rp_addr = mrt->source->address;
! 902: else
! 903: rp_addr = mrt->group->rpaddr;
! 904:
! 905: mfc_source = source;
! 906: #ifdef KERNEL_MFC_WC_G
! 907: if (mrt->flags & (MRTF_WC | MRTF_PMBR))
! 908: if (!(mrt->flags & MRTF_MFC_CLONE_SG))
! 909: mfc_source = INADDR_ANY_N;
! 910: #endif /* KERNEL_MFC_WC_G */
! 911:
! 912: add_kernel_cache(mrt, mfc_source, group, MFC_MOVE_FORCE);
! 913:
! 914: #ifdef SCOPED_ACL
! 915: APPLY_SCOPE(group, mrt);
! 916: #endif
! 917: k_chg_mfc(igmp_socket, mfc_source, group, iif, mrt->oifs, rp_addr);
! 918:
! 919: /* No need for RSRR message, because nothing has changed. */
! 920: }
! 921:
! 922: return; /* iif match */
! 923: }
! 924:
! 925: /* The iif doesn't match */
! 926: if (mrt->flags & MRTF_SG) {
! 927: /* Arrived on wrong interface */
! 928: if (mrt->flags & MRTF_SPT)
! 929: return;
! 930:
! 931: mrp = mrt->group->grp_route;
! 932: if (!mrp)
! 933: mrp = mrt->group->active_rp_grp->rp->rpentry->mrtlink;
! 934:
! 935: if (mrp) {
! 936: /* Forward on (*,G) or (*,*,RP) */
! 937: if (mrp->incoming == iif) {
! 938: #ifdef KERNEL_MFC_WC_G
! 939: if (!(mrp->flags & MRTF_MFC_CLONE_SG))
! 940: mfc_source = INADDR_ANY_N;
! 941: #endif /* KERNEL_MFC_WC_G */
! 942:
! 943: add_kernel_cache(mrp, mfc_source, group, 0);
! 944:
! 945: #ifdef SCOPED_ACL
! 946: /* marian: not sure if we reach here with our scoped traffic? */
! 947: APPLY_SCOPE(group, mrt);
! 948: #endif
! 949: k_chg_mfc(igmp_socket, mfc_source, group, iif,
! 950: mrp->oifs, mrt->group->rpaddr);
! 951: #ifdef RSRR
! 952: rsrr_cache_send(mrp, RSRR_NOTIFICATION_OK);
! 953: #endif /* RSRR */
! 954: }
! 955: }
! 956: }
! 957: }
! 958:
! 959:
! 960: /*
! 961: * A multicast packet has been received on wrong iif by the kernel.
! 962: * Check for a matching entry. If there is (S,G) with reset SPTbit and
! 963: * the packet was received on the iif toward the source, this completes
! 964: * the switch to the shortest path and triggers (S,G) prune toward the RP
! 965: * (unless I am the RP).
! 966: * Otherwise, if the packet's iif is in the oiflist of the routing entry,
! 967: * trigger an Assert.
! 968: */
! 969: static void process_wrong_iif(struct igmpmsg *igmpctl)
! 970: {
! 971: uint32_t source;
! 972: uint32_t group;
! 973: vifi_t iif;
! 974: mrtentry_t *mrt;
! 975:
! 976: group = igmpctl->im_dst.s_addr;
! 977: source = igmpctl->im_src.s_addr;
! 978: iif = igmpctl->im_vif;
! 979:
! 980: IF_DEBUG(DEBUG_MRT)
! 981: logit(LOG_DEBUG, 0, "Wrong iif: src %s, dst %s, iif %d",
! 982: inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)), iif);
! 983:
! 984: /* Don't create routing entries for the LAN scoped addresses */
! 985: if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP)
! 986: return;
! 987:
! 988: /* Ignore if it comes on register vif. register vif is neither SPT iif,
! 989: * neither is used to send asserts out.
! 990: */
! 991: if (uvifs[iif].uv_flags & VIFF_REGISTER)
! 992: return;
! 993:
! 994: mrt = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE);
! 995: if (!mrt)
! 996: return;
! 997:
! 998: /*
! 999: * TODO: check again!
! 1000: */
! 1001: if (mrt->flags & MRTF_SG) {
! 1002: if (!(mrt->flags & MRTF_SPT)) {
! 1003: if (mrt->source->incoming == iif) {
! 1004: /* Switch to the Shortest Path */
! 1005: mrt->flags |= MRTF_SPT;
! 1006: mrt->flags &= ~MRTF_RP;
! 1007: add_kernel_cache(mrt, source, group, MFC_MOVE_FORCE);
! 1008: k_chg_mfc(igmp_socket, source, group, iif,
! 1009: mrt->oifs, mrt->group->rpaddr);
! 1010: FIRE_TIMER(mrt->jp_timer);
! 1011: #ifdef RSRR
! 1012: rsrr_cache_send(mrt, RSRR_NOTIFICATION_OK);
! 1013: #endif /* RSRR */
! 1014:
! 1015: return;
! 1016: }
! 1017: }
! 1018: }
! 1019:
! 1020: /* Trigger an Assert */
! 1021: if (VIFM_ISSET(iif, mrt->oifs))
! 1022: send_pim_assert(source, group, iif, mrt);
! 1023: }
! 1024:
! 1025: /*
! 1026: * Receives whole packets from the register vif entries
! 1027: * in the kernel, and calls the send_pim_register procedure to
! 1028: * encapsulate the packets and unicasts them to the RP.
! 1029: */
! 1030: static void process_whole_pkt(char *buf)
! 1031: {
! 1032: send_pim_register((char *)(buf + sizeof(struct igmpmsg)));
! 1033: }
! 1034:
! 1035:
! 1036: mrtentry_t *switch_shortest_path(uint32_t source, uint32_t group)
! 1037: {
! 1038: mrtentry_t *mrt;
! 1039:
! 1040: IF_DEBUG(DEBUG_MRT)
! 1041: logit(LOG_DEBUG, 0, "Switch shortest path (SPT): src %s, group %s",
! 1042: inet_fmt(source, s1, sizeof(s1)), inet_fmt(group, s2, sizeof(s2)));
! 1043:
! 1044: /* TODO: XXX: prepare and send immediately the (S,G) join? */
! 1045: mrt = find_route(source, group, MRTF_SG, CREATE);
! 1046: if (mrt) {
! 1047: if (mrt->flags & MRTF_NEW) {
! 1048: mrt->flags &= ~MRTF_NEW;
! 1049: } else if (mrt->flags & MRTF_RP || IN_PIM_SSM_RANGE(group)) {
! 1050: /* (S,G)RPbit with iif toward RP. Reset to (S,G) with iif
! 1051: * toward S. Delete the kernel cache (if any), because
! 1052: * change_interfaces() will reset it with iif toward S
! 1053: * and no data will arrive from RP before the switch
! 1054: * really occurs.
! 1055: * For SSM, (S,G)RPbit entry does not exist but switch to
! 1056: * SPT must be allowed right away.
! 1057: */
! 1058: mrt->flags &= ~MRTF_RP;
! 1059: mrt->incoming = mrt->source->incoming;
! 1060: mrt->upstream = mrt->source->upstream;
! 1061: delete_mrtentry_all_kernel_cache(mrt);
! 1062: change_interfaces(mrt,
! 1063: mrt->incoming,
! 1064: mrt->joined_oifs,
! 1065: mrt->pruned_oifs,
! 1066: mrt->leaves,
! 1067: mrt->asserted_oifs, 0);
! 1068: }
! 1069:
! 1070: SET_TIMER(mrt->timer, PIM_DATA_TIMEOUT);
! 1071: FIRE_TIMER(mrt->jp_timer);
! 1072: }
! 1073:
! 1074: return mrt;
! 1075: }
! 1076:
! 1077: /**
! 1078: * Local Variables:
! 1079: * version-control: t
! 1080: * indent-tabs-mode: t
! 1081: * c-file-style: "ellemtel"
! 1082: * c-basic-offset: 4
! 1083: * End:
! 1084: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>