Exemplo n.º 1
0
Arquivo: route.c Projeto: F0rth/pimd
/*
 * TODO: XXX: currently `source` is not used. To be used with IGMPv3 where
 * we have source-specific joins/prunes.
 */
void delete_leaf(vifi_t vifi, u_int32 source __attribute__((unused)), u_int32 group)
{
    mrtentry_t *mrtentry_ptr;
    mrtentry_t *mrtentry_srcs;
    vifbitmap_t new_oifs;
    vifbitmap_t old_oifs;
    vifbitmap_t new_leaves;

    mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);
    if (mrtentry_ptr == (mrtentry_t *)NULL)
        return;

    if (!VIFM_ISSET(vifi, mrtentry_ptr->leaves))
        return;      /* This interface wasn't leaf */

    calc_oifs(mrtentry_ptr, &old_oifs);
    VIFM_COPY(mrtentry_ptr->leaves, new_leaves);
    VIFM_CLR(vifi, new_leaves);
    change_interfaces(mrtentry_ptr,
                      mrtentry_ptr->incoming,
                      mrtentry_ptr->joined_oifs,
                      mrtentry_ptr->pruned_oifs,
                      new_leaves,
                      mrtentry_ptr->asserted_oifs, 0);
    calc_oifs(mrtentry_ptr, &new_oifs);
    if ((!VIFM_ISEMPTY(old_oifs)) && VIFM_ISEMPTY(new_oifs)) {
        /* The result oifs have changed from non-NULL to NULL */
        FIRE_TIMER(mrtentry_ptr->jp_timer);  /* Timeout the Join/Prune timer */
        /* TODO: explicitly call the function?
           send_pim_join_prune(mrtentry_ptr->upstream->vifi,
           mrtentry_ptr->upstream, PIM_JOIN_PRUNE_HOLDTIME);
        */
    }
    /* Check all (S,G) entries and clear the inherited "leaf" flag.
     * TODO: XXX: This won't work for IGMPv3, because there we don't know
     * whether the (S,G) leaf oif was inherited from the (*,G) entry or
     * was created by source specific IGMP join.
     */
    for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
         mrtentry_srcs != (mrtentry_t *)NULL;
         mrtentry_srcs = mrtentry_srcs->grpnext) {
        VIFM_COPY(mrtentry_srcs->leaves, new_leaves);
        VIFM_CLR(vifi, new_leaves);
        change_interfaces(mrtentry_srcs,
                          mrtentry_srcs->incoming,
                          mrtentry_srcs->joined_oifs,
                          mrtentry_srcs->pruned_oifs,
                          new_leaves,
                          mrtentry_srcs->asserted_oifs, 0);
    }

}
Exemplo n.º 2
0
/*
 * Initialize the children and leaf bits for route 'r', along with the
 * associated dominant, subordinate, and leaf timing data structures.
 * Return TRUE if this changes the value of either the children or
 * leaf bitmaps for 'r'.
 */
static int
init_children_and_leaves(struct rtentry *r, vifi_t parent)
{
    register vifi_t vifi;
    register struct uvif *v;
    vifbitmap_t old_children, old_leaves;

    VIFM_COPY(r->rt_children, old_children);
    VIFM_COPY(r->rt_leaves,   old_leaves  );

    VIFM_CLRALL(r->rt_children);
    VIFM_CLRALL(r->rt_leaves);
    r->rt_flags &= ~RTF_LEAF_TIMING;

    for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
        r->rt_dominants   [vifi] = 0;
        r->rt_subordinates[vifi] = 0;

        if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
            VIFM_SET(vifi, r->rt_children);
            if (v->uv_neighbors == NULL) {
                VIFM_SET(vifi, r->rt_leaves);
                r->rt_leaf_timers[vifi] = 0;
            }
            else {
                r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
                r->rt_flags |= RTF_LEAF_TIMING;
            }
        }
        else {
            r->rt_leaf_timers[vifi] = 0;
        }
    }

    return (!VIFM_SAME(r->rt_children, old_children) ||
            !VIFM_SAME(r->rt_leaves,   old_leaves));
}
Exemplo n.º 3
0
Arquivo: route.c Projeto: F0rth/pimd
void calc_oifs(mrtentry_t *mrtentry_ptr, vifbitmap_t *oifs_ptr)
{
    vifbitmap_t oifs;
    mrtentry_t *grp_route;
    mrtentry_t *rp_route;

    /*
     * oifs =
     * (((copied_outgoing + my_join) - my_prune) + my_leaves)
     *              - my_asserted_oifs - incoming_interface,
     * i.e. `leaves` have higher priority than `prunes`, but lower priority
     * than `asserted`. The incoming interface is always deleted from the oifs
     */

    if (mrtentry_ptr == (mrtentry_t *)NULL) {
        VIFM_CLRALL(*oifs_ptr);
        return;
    }
    VIFM_CLRALL(oifs);
    if (!(mrtentry_ptr->flags & MRTF_PMBR)) {
        /* Either (*,G) or (S,G). Merge with the oifs from the (*,*,RP) */
        if ((rp_route =
             mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink)
            != (mrtentry_t *)NULL) {
            VIFM_MERGE(oifs, rp_route->joined_oifs, oifs);
            VIFM_CLR_MASK(oifs, rp_route->pruned_oifs);
            VIFM_MERGE(oifs, rp_route->leaves, oifs);
            VIFM_CLR_MASK(oifs, rp_route->asserted_oifs);
        }
    }
    if (mrtentry_ptr->flags & MRTF_SG) {
        /* (S,G) entry. Merge with the oifs from (*,G) */
        if ((grp_route = mrtentry_ptr->group->grp_route)
            != (mrtentry_t *)NULL) {
            VIFM_MERGE(oifs, grp_route->joined_oifs, oifs);
            VIFM_CLR_MASK(oifs, grp_route->pruned_oifs);
            VIFM_MERGE(oifs, grp_route->leaves, oifs);
            VIFM_CLR_MASK(oifs, grp_route->asserted_oifs);
        }
    }

    /* Calculate my own stuff */
    VIFM_MERGE(oifs, mrtentry_ptr->joined_oifs, oifs);
    VIFM_CLR_MASK(oifs, mrtentry_ptr->pruned_oifs);
    VIFM_MERGE(oifs, mrtentry_ptr->leaves, oifs);
    VIFM_CLR_MASK(oifs, mrtentry_ptr->asserted_oifs);

    VIFM_COPY(oifs, *oifs_ptr);
}
Exemplo n.º 4
0
Arquivo: route.c Projeto: F0rth/pimd
/*
 * Set the iif, join/prune/leaves/asserted interfaces. Calculate and
 * set the oifs.
 * Return 1 if oifs change from NULL to not-NULL.
 * Return -1 if oifs change from non-NULL to NULL
 *  else return 0
 * If the iif change or if the oifs change from NULL to non-NULL
 * or vice-versa, then schedule that mrtentry join/prune timer to
 * timeout immediately.
 */
int change_interfaces(mrtentry_t *mrtentry_ptr,
                      vifi_t new_iif,
                      vifbitmap_t new_joined_oifs_,
                      vifbitmap_t new_pruned_oifs,
                      vifbitmap_t new_leaves_,
                      vifbitmap_t new_asserted_oifs,
                      u_int16 flags)
{
    vifbitmap_t new_joined_oifs;  /* The oifs for that particular mrtentry */
    vifbitmap_t old_joined_oifs;
    vifbitmap_t old_pruned_oifs;
    vifbitmap_t old_leaves;
    vifbitmap_t new_leaves;
    vifbitmap_t old_asserted_oifs;
    vifbitmap_t new_real_oifs;    /* The result oifs */
    vifbitmap_t old_real_oifs;
    vifi_t      old_iif;
    rpentry_t   *rpentry_ptr;
    cand_rp_t   *cand_rp_ptr;
    kernel_cache_t *kernel_cache_ptr;
    rp_grp_entry_t *rp_grp_entry_ptr;
    grpentry_t     *grpentry_ptr;
    mrtentry_t     *mrtentry_srcs;
    mrtentry_t     *mrtentry_wc;
    mrtentry_t     *mrtentry_rp;
    int delete_mrtentry_flag;
    int return_value;
    int fire_timer_flag;

    if (mrtentry_ptr == (mrtentry_t *)NULL)
        return 0;

    VIFM_COPY(new_joined_oifs_, new_joined_oifs);
    VIFM_COPY(new_leaves_, new_leaves);

    old_iif = mrtentry_ptr->incoming;
    VIFM_COPY(mrtentry_ptr->joined_oifs, old_joined_oifs);
    VIFM_COPY(mrtentry_ptr->leaves, old_leaves);
    VIFM_COPY(mrtentry_ptr->pruned_oifs, old_pruned_oifs);
    VIFM_COPY(mrtentry_ptr->asserted_oifs, old_asserted_oifs);

    VIFM_COPY(mrtentry_ptr->oifs, old_real_oifs);

    mrtentry_ptr->incoming = new_iif;
    VIFM_COPY(new_joined_oifs, mrtentry_ptr->joined_oifs);
    VIFM_COPY(new_pruned_oifs, mrtentry_ptr->pruned_oifs);
    VIFM_COPY(new_leaves, mrtentry_ptr->leaves);
    VIFM_COPY(new_asserted_oifs, mrtentry_ptr->asserted_oifs);
    calc_oifs(mrtentry_ptr, &new_real_oifs);

    if (VIFM_ISEMPTY(old_real_oifs)) {
        if (VIFM_ISEMPTY(new_real_oifs))
            return_value = 0;
        else
            return_value = 1;
    } else {
        if (VIFM_ISEMPTY(new_real_oifs))
            return_value = -1;
        else
            return_value = 0;
    }

    if ((VIFM_SAME(new_real_oifs, old_real_oifs))
        && (new_iif == old_iif)
        && !(flags & MFC_UPDATE_FORCE))
        return 0;                  /* Nothing to change */

    if ((return_value != 0) || (new_iif != old_iif)
        || (flags & MFC_UPDATE_FORCE))
        FIRE_TIMER(mrtentry_ptr->jp_timer);

    VIFM_COPY(new_real_oifs, mrtentry_ptr->oifs);

    if (mrtentry_ptr->flags & MRTF_PMBR) {
        /* (*,*,RP) entry */
        rpentry_ptr = mrtentry_ptr->source;
        if (rpentry_ptr == (rpentry_t *)NULL)
            return (0);    /* Shoudn't happen */
        rpentry_ptr->incoming = new_iif;
        cand_rp_ptr = rpentry_ptr->cand_rp;

        if (VIFM_ISEMPTY(new_real_oifs)) {
            delete_mrtentry_flag = TRUE;
        }
        else {
            delete_mrtentry_flag = FALSE;
#ifdef RSRR
            rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
#endif /* RSRR */
        }

        if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE) {
            /* Update the kernel MFC entries */
            if (delete_mrtentry_flag == TRUE)
                /* XXX: no need to send RSRR message. Will do it when
                 * delete the mrtentry.
                 */
                for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
                     kernel_cache_ptr != (kernel_cache_t *)NULL;
                     kernel_cache_ptr = kernel_cache_ptr->next)
                    delete_mrtentry_all_kernel_cache(mrtentry_ptr);
            else {
                for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
                     kernel_cache_ptr != (kernel_cache_t *)NULL;
                     kernel_cache_ptr = kernel_cache_ptr->next)
                    /* here mrtentry_ptr->source->address is the RP address */
                    k_chg_mfc(igmp_socket, kernel_cache_ptr->source,
                              kernel_cache_ptr->group, new_iif,
                              new_real_oifs, mrtentry_ptr->source->address);
            }
        }

        /*
         * Update all (*,G) entries associated with this RP.
         * The particular (*,G) outgoing are not changed, but the change
         * in the (*,*,RP) oifs may have affect the real oifs.
         */
        fire_timer_flag = FALSE;
        for (rp_grp_entry_ptr = cand_rp_ptr->rp_grp_next;
             rp_grp_entry_ptr != (rp_grp_entry_t *)NULL;
             rp_grp_entry_ptr = rp_grp_entry_ptr->rp_grp_next) {
            for (grpentry_ptr = rp_grp_entry_ptr->grplink;
                 grpentry_ptr != (grpentry_t *)NULL;
                 grpentry_ptr = grpentry_ptr->rpnext) {
                if (grpentry_ptr->grp_route != (mrtentry_t *)NULL) {
                    if (change_interfaces(grpentry_ptr->grp_route, new_iif,
                                          grpentry_ptr->grp_route->joined_oifs,
                                          grpentry_ptr->grp_route->pruned_oifs,
                                          grpentry_ptr->grp_route->leaves,
                                          grpentry_ptr->grp_route->asserted_oifs,
                                          flags))
                        fire_timer_flag = TRUE;
                } else {
                    /* Change all (S,G) entries if no (*,G) */
                    for (mrtentry_srcs = grpentry_ptr->mrtlink;
                         mrtentry_srcs != (mrtentry_t *)NULL;
                         mrtentry_srcs = mrtentry_srcs->grpnext) {
                        if (mrtentry_srcs->flags & MRTF_RP) {
                            if (change_interfaces(mrtentry_srcs, new_iif,
                                                  mrtentry_srcs->joined_oifs,
                                                  mrtentry_srcs->pruned_oifs,
                                                  mrtentry_srcs->leaves,
                                                  mrtentry_srcs->asserted_oifs,
                                                  flags))
                                fire_timer_flag = TRUE;
                        } else {
                            if (change_interfaces(mrtentry_srcs,
                                                  mrtentry_srcs->incoming,
                                                  mrtentry_srcs->joined_oifs,
                                                  mrtentry_srcs->pruned_oifs,
                                                  mrtentry_srcs->leaves,
                                                  mrtentry_srcs->asserted_oifs,
                                                  flags))
                                fire_timer_flag = TRUE;
                        }
                    }
                }
            }
        }
        if (fire_timer_flag == TRUE)
            FIRE_TIMER(mrtentry_ptr->jp_timer);
        if (delete_mrtentry_flag == TRUE) {
            /* TODO: XXX: trigger a Prune message? Don't delete now, it will
             * be automatically timed out. If want to delete now, don't
             * reference to it anymore!
             delete_mrtentry(mrtentry_ptr);
            */
        }

        return return_value;   /* (*,*,RP) */
    }

    if (mrtentry_ptr->flags & MRTF_WC) {
        /* (*,G) entry */
        if (VIFM_ISEMPTY(new_real_oifs))
            delete_mrtentry_flag = TRUE;
        else {
            delete_mrtentry_flag = FALSE;
#ifdef RSRR
            rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
#endif /* RSRR */
        }
        if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE) {
            if (delete_mrtentry_flag == TRUE)
                delete_mrtentry_all_kernel_cache(mrtentry_ptr);
            else {
                for (kernel_cache_ptr = mrtentry_ptr->kernel_cache;
                     kernel_cache_ptr != (kernel_cache_t *)NULL;
                     kernel_cache_ptr = kernel_cache_ptr->next)
                    k_chg_mfc(igmp_socket, kernel_cache_ptr->source,
                              kernel_cache_ptr->group, new_iif,
                              new_real_oifs, mrtentry_ptr->group->rpaddr);
            }
        }
        /* Update all (S,G) entries for this group.
         * For the (S,G)RPbit entries the iif is the iif toward the RP;
         * The particular (S,G) oifs are not changed, but the change in the
         * (*,G) oifs may affect the real oifs.
         */
        fire_timer_flag = FALSE;
        for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
             mrtentry_srcs != (mrtentry_t *)NULL;
             mrtentry_srcs = mrtentry_srcs->grpnext) {
            if (mrtentry_srcs->flags & MRTF_RP) {
                if (change_interfaces(mrtentry_srcs, new_iif,
                                      mrtentry_srcs->joined_oifs,
                                      mrtentry_srcs->pruned_oifs,
                                      mrtentry_srcs->leaves,
                                      mrtentry_srcs->asserted_oifs, flags))
                    fire_timer_flag = TRUE;
            } else {
                if (change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming,
                                      mrtentry_srcs->joined_oifs,
                                      mrtentry_srcs->pruned_oifs,
                                      mrtentry_srcs->leaves,
                                      mrtentry_srcs->asserted_oifs, flags))
                    fire_timer_flag = TRUE;
            }
        }

        if (fire_timer_flag == TRUE)
            FIRE_TIMER(mrtentry_ptr->jp_timer);
        if (delete_mrtentry_flag == TRUE) {
            /* TODO: XXX: the oifs are NULL. Send a Prune message? */
        }
        return return_value;   /* (*,G) */
    }

    if (mrtentry_ptr->flags & MRTF_SG) {
        /* (S,G) entry */
#ifdef KERNEL_MFC_WC_G
        vifbitmap_t tmp_oifs;
        mrtentry_t *mrtentry_tmp;
#endif /* KERNEL_MFC_WC_G */

        mrtentry_rp = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink;
        mrtentry_wc = mrtentry_ptr->group->grp_route;
#ifdef KERNEL_MFC_WC_G
        /* Check whether (*,*,RP) or (*,G) have different (iif,oifs) from
         * the (S,G). If "yes", then forbid creating (*,G) MFC.
         */
        for (mrtentry_tmp = mrtentry_rp; 1; mrtentry_tmp = mrtentry_wc) {
            for ( ; 1; ) {
                if (mrtentry_tmp == (mrtentry_t *)NULL)
                    break;
                if (mrtentry_tmp->flags & MRTF_MFC_CLONE_SG)
                    break;
                if (mrtentry_tmp->incoming != mrtentry_ptr->incoming) {
                    delete_single_kernel_cache_addr(mrtentry_tmp, INADDR_ANY_N,
                                                    mrtentry_ptr->group->group);
                    mrtentry_tmp->flags |= MRTF_MFC_CLONE_SG;
                    break;
                }
                calc_oifs(mrtentry_tmp, &tmp_oifs);
                if (!(VIFM_SAME(new_real_oifs, tmp_oifs)))
                    mrtentry_tmp->flags |= MRTF_MFC_CLONE_SG;
                break;
            }
            if (mrtentry_tmp == mrtentry_wc)
                break;
        }
#endif /* KERNEL_MFC_WC_G */

        if (VIFM_ISEMPTY(new_real_oifs))
            delete_mrtentry_flag = TRUE;
        else {
            delete_mrtentry_flag = FALSE;
#ifdef RSRR
            rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK);
#endif /* RSRR */
        }
        if (mrtentry_ptr->flags & MRTF_KERNEL_CACHE) {
            if (delete_mrtentry_flag == TRUE)
                delete_mrtentry_all_kernel_cache(mrtentry_ptr);
            else {
                k_chg_mfc(igmp_socket, mrtentry_ptr->source->address,
                          mrtentry_ptr->group->group, new_iif, new_real_oifs,
                          mrtentry_ptr->group->rpaddr);
            }
        }
        if (old_iif != new_iif) {
            if (new_iif == mrtentry_ptr->source->incoming) {
                /* For example, if this was (S,G)RPbit with iif toward the RP,
                 * and now switch to the Shortest Path.
                 * The setup of MRTF_SPT flag must be
                 * done by the external calling function (triggered only
                 * by receiving of a data from the source.)
                 */
                mrtentry_ptr->flags &= ~MRTF_RP;
                /* TODO: XXX: delete? Check again where will be the best
                 * place to set it.
                 mrtentry_ptr->flags |= MRTF_SPT;
                */
            }
            if (((mrtentry_wc != (mrtentry_t *)NULL)
                 && (mrtentry_wc->incoming == new_iif))
                || ((mrtentry_rp != (mrtentry_t *)NULL)
                    && (mrtentry_rp->incoming == new_iif))) {
                /* If the new iif points toward the RP, reset the SPT flag.
                 * (PIM-SM-spec-10.ps pp. 11, 2.10, last sentence of first
                 * paragraph.
                 */
                /* TODO: XXX: check again! */
                mrtentry_ptr->flags &= ~MRTF_SPT;
                mrtentry_ptr->flags |= MRTF_RP;
            }
        }
        /* TODO: XXX: if this is (S,G)RPbit entry and the oifs==(*,G)oifs,
         * then delete the (S,G) entry?? The same if we have (*,*,RP) ?
         */
        if (delete_mrtentry_flag == TRUE) {
            /* TODO: XXX: the oifs are NULL. Send a Prune message ? */
        }

        /* TODO: XXX: have the feeling something is missing.... */
        return return_value;  /* (S,G) */
    }

    return return_value;
}
Exemplo n.º 5
0
Arquivo: route.c Projeto: F0rth/pimd
/*
 * TODO: XXX: currently `source` is not used. Will be used with IGMPv3 where
 * we have source-specific Join/Prune.
 */
void add_leaf(vifi_t vifi, u_int32 source __attribute__((unused)), u_int32 group)
{
    mrtentry_t *mrtentry_ptr;
    mrtentry_t *mrtentry_srcs;
    vifbitmap_t old_oifs;
    vifbitmap_t new_oifs;
    vifbitmap_t new_leaves;

    if (ntohl(group) <= INADDR_MAX_LOCAL_GROUP)
        return; /* Don't create routing entries for the LAN scoped addresses */

    /*
     * XXX: only if I am a DR, the IGMP Join should result in creating
     * a PIM MRT state.
     * XXX: Each router must know if it has local members, i.e., whether
     * it is a last-hop router as well. This info is needed so it will
     * know whether is allowed to initiate a SPT switch by sending
     * a PIM (S,G) Join to the high datarate source.
     * However, if a non-DR last-hop router has not received
     * a PIM Join, it should not create a PIM state, otherwise later
     * this state may incorrectly trigger PIM joins.
     * There is a design flow in pimd, so without making major changes
     * the best we can do is that the non-DR last-hop router will
     * record the local members only after it receives PIM Join from the DR
     * (i.e.  after the second or third IGMP Join by the local member).
     * The downside is that a last-hop router may delay the initiation
     * of the SPT switch. Sigh...
     */
    if (uvifs[vifi].uv_flags & VIFF_DR)
        mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE);
    else
        mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE);

    if (mrtentry_ptr == (mrtentry_t *)NULL)
        return;

    IF_DEBUG(DEBUG_MRT)
        logit(LOG_DEBUG, 0, "Adding vif %d for group %s", vifi,
              inet_fmt(group, s1, sizeof(s1)));

    if (VIFM_ISSET(vifi, mrtentry_ptr->leaves))
        return;     /* Already a leaf */
    calc_oifs(mrtentry_ptr, &old_oifs);
    VIFM_COPY(mrtentry_ptr->leaves, new_leaves);
    VIFM_SET(vifi, new_leaves);    /* Add the leaf */
    change_interfaces(mrtentry_ptr,
                      mrtentry_ptr->incoming,
                      mrtentry_ptr->joined_oifs,
                      mrtentry_ptr->pruned_oifs,
                      new_leaves,
                      mrtentry_ptr->asserted_oifs, 0);
    calc_oifs(mrtentry_ptr, &new_oifs);

    /* Only if I am the DR for that subnet, eventually initiate a Join */
    if (!(uvifs[vifi].uv_flags & VIFF_DR))
        return;

    if ((mrtentry_ptr->flags & MRTF_NEW)
        || (VIFM_ISEMPTY(old_oifs) && (!VIFM_ISEMPTY(new_oifs)))) {
        /* A new created entry or the oifs have changed
         * from NULL to non-NULL.
         */
        mrtentry_ptr->flags &= ~MRTF_NEW;
        FIRE_TIMER(mrtentry_ptr->jp_timer); /* Timeout the Join/Prune timer */
        /* TODO: explicitly call the function below?
           send_pim_join_prune(mrtentry_ptr->upstream->vifi,
           mrtentry_ptr->upstream,
           PIM_JOIN_PRUNE_HOLDTIME);
        */
    }

    /* Check all (S,G) entries and set the inherited "leaf" flag.
     * TODO: XXX: This won't work for IGMPv3, because there we don't know
     * whether the (S,G) leaf oif was inherited from the (*,G) entry or
     * was created by source specific IGMP join.
     */
    for (mrtentry_srcs = mrtentry_ptr->group->mrtlink;
         mrtentry_srcs != (mrtentry_t *)NULL;
         mrtentry_srcs = mrtentry_srcs->grpnext) {
        VIFM_COPY(mrtentry_srcs->leaves, new_leaves);
        VIFM_SET(vifi, new_leaves);
        change_interfaces(mrtentry_srcs,
                          mrtentry_srcs->incoming,
                          mrtentry_srcs->joined_oifs,
                          mrtentry_srcs->pruned_oifs,
                          new_leaves,
                          mrtentry_srcs->asserted_oifs, 0);
    }
}