/* * 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); } }
/* * 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)); }
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); }
/* * 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; }
/* * 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); } }