/* * Copyright (C) 1998 WIDE Project. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1998 by the University of Oregon. * All rights reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation in source and binary forms for lawful * purposes and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both * the copyright notice and this permission notice appear in supporting * documentation, and that any documentation, advertising materials, * and other materials related to such distribution and use acknowledge * that the software was developed by the University of Oregon. * The name of the University of Oregon may not be used to endorse or * promote products derived from this software without specific prior * written permission. * * THE UNIVERSITY OF OREGON DOES NOT MAKE ANY REPRESENTATIONS * ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY PURPOSE. THIS SOFTWARE IS * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND * NON-INFRINGEMENT. * * IN NO EVENT SHALL UO, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, * TORT, OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, * THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Other copyrights might apply to parts of this software and are so * noted when applicable. */ /* * Questions concerning this software should be directed to * Kurt Windisch (kurtw@antc.uoregon.edu) * * $Id: pim6_proto.c,v 1.6 2000/10/05 22:20:38 itojun Exp $ */ /* * Part of this program has been derived from PIM sparse-mode pimd. * The pimd program is covered by the license in the accompanying file * named "LICENSE.pimd". * * The pimd program is COPYRIGHT 1998 by University of Southern California. * * Part of this program has been derived from mrouted. * The mrouted program is covered by the license in the accompanying file * named "LICENSE.mrouted". * * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of * Leland Stanford Junior University. * * $FreeBSD$ */ #include "defs.h" /* * Local functions definitions. */ static int parse_pim6_hello __P((char *pktPtr, int datalen, struct sockaddr_in6 *src, u_int16 *holdtime)); static void delayed_join_job __P((void *)); static void schedule_delayed_join __P((mrtentry_t *, struct sockaddr_in6 *)); static void delayed_prune_job __P((void *)); static void schedule_delayed_prune __P((mrtentry_t *, mifi_t, u_int16)); static int compare_metrics __P((u_int32 local_preference, u_int32 local_metric, struct sockaddr_in6 *local_address, u_int32 remote_preference, u_int32 remote_metric, struct sockaddr_in6 *remote_address)); static int retransmit_pim6_graft __P((mrtentry_t *)); static void retransmit_all_pim6_grafts __P((void *)); if_set nbr_mifs; /* Mifs that have one or more neighbors attached */ /************************************************************************ * PIM_HELLO ************************************************************************/ int receive_pim6_hello(src, pim_message, datalen) struct sockaddr_in6 *src; register char *pim_message; int datalen; { mifi_t mifi; struct uvif *v; register pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr; u_int16 holdtime; u_int8 *data_ptr; int state_change; srcentry_t *srcentry_ptr; srcentry_t *srcentry_ptr_next; mrtentry_t *mrtentry_ptr; u_long random_delay; if ((mifi = find_vif_direct(src)) == NO_VIF) { /* Either a local vif or somehow received PIM_HELLO from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) log(LOG_INFO, 0, "Ignoring PIM_HELLO from non-neighbor router %s", inet6_fmt(&src->sin6_addr)); return(FALSE); } v = &uvifs[mifi]; if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED)) return(FALSE); /* Shoudn't come on this interface */ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); /* Get the Holdtime (in seconds) from the message. Return if error. */ if (parse_pim6_hello(pim_message, datalen, src, &holdtime) == FALSE) return(FALSE); IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) log(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u", inet6_fmt(&src->sin6_addr), holdtime); for (prev_nbr = (pim_nbr_entry_t *)NULL, nbr = v->uv_pim_neighbors; nbr != (pim_nbr_entry_t *)NULL; prev_nbr = nbr, nbr = nbr->next) { /* The PIM neighbors are sorted in decreasing order of the * network addresses (note that to be able to compare them * correctly we must translate the addresses in host order. */ if (inet6_lessthan(src, &nbr->address)) continue; if (inet6_equal(src, &nbr->address)) { /* We already have an entry for this host */ if (0 == holdtime) { /* * Looks like we have a nice neighbor who is * going down and wants to inform us by sending * "holdtime=0". Thanks buddy and see you again! */ log(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down", inet6_fmt(&src->sin6_addr)); delete_pim6_nbr(nbr); return(TRUE); } /* Set the timer */ nbr->timer = holdtime; return(TRUE); } else /* * No entry for this neighbor. Exit the loop and create an * entry for it. */ break; } /* * This is a new neighbor. Create a new entry for it. * It must be added right after `prev_nbr` */ new_nbr = (pim_nbr_entry_t *)malloc(sizeof(pim_nbr_entry_t)); new_nbr->address = *src; new_nbr->vifi = mifi; new_nbr->timer = holdtime; new_nbr->next = nbr; new_nbr->prev = prev_nbr; if (prev_nbr != (pim_nbr_entry_t *)NULL) prev_nbr->next = new_nbr; else v->uv_pim_neighbors = new_nbr; if (new_nbr->next != (pim_nbr_entry_t *)NULL) new_nbr->next->prev = new_nbr; v->uv_flags &= ~VIFF_NONBRS; v->uv_flags |= VIFF_PIM_NBR; IF_SET(mifi, &nbr_mifs); /* Elect a new DR */ if (inet6_lessthan(&v->uv_linklocal->pa_addr, &v->uv_pim_neighbors->address)) { /* The first address is the new potential remote * DR address and it wins (is >) over the local address. */ v->uv_flags &= ~VIFF_DR; } /* * Since a new neighbour has come up, let it know your existence ASAP; * compute a random value, and reset the value to the hello timer * if it's smaller than the rest of the timer. * XXX: not in the spec... */ random_delay = 1 + (random() % (long)(PIM_TIMER_HELLO_PERIOD - 1)); if (random_delay < v->uv_pim_hello_timer) v->uv_pim_hello_timer = random_delay; /* Update the source entries */ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL; srcentry_ptr = srcentry_ptr_next) { srcentry_ptr_next = srcentry_ptr->next; if (srcentry_ptr->incoming == mifi) continue; for (mrtentry_ptr = srcentry_ptr->mrtlink; mrtentry_ptr != (mrtentry_t *)NULL; mrtentry_ptr = mrtentry_ptr->srcnext) { if(!(IF_ISSET(mifi, &mrtentry_ptr->oifs))) { state_change = change_interfaces(mrtentry_ptr, srcentry_ptr->incoming, &mrtentry_ptr->pruned_oifs, &mrtentry_ptr->leaves, &mrtentry_ptr->asserted_oifs); if(state_change == 1) trigger_join_alert(mrtentry_ptr); } } } IF_DEBUG(DEBUG_PIM_HELLO) dump_vifs(stderr); /* Show we got a new neighbor */ return(TRUE); } void delete_pim6_nbr(nbr_delete) pim_nbr_entry_t *nbr_delete; { srcentry_t *srcentry_ptr; srcentry_t *srcentry_ptr_next; mrtentry_t *mrtentry_ptr; struct uvif *v; int state_change; v = &uvifs[nbr_delete->vifi]; /* Delete the entry from the pim_nbrs chain */ if (nbr_delete->prev != (pim_nbr_entry_t *)NULL) nbr_delete->prev->next = nbr_delete->next; else v->uv_pim_neighbors = nbr_delete->next; if (nbr_delete->next != (pim_nbr_entry_t *)NULL) nbr_delete->next->prev = nbr_delete->prev; if (v->uv_pim_neighbors == (pim_nbr_entry_t *)NULL) { /* This was our last neighbor. */ v->uv_flags &= ~VIFF_PIM_NBR; v->uv_flags |= (VIFF_NONBRS | VIFF_DR | VIFF_QUERIER); IF_CLR(nbr_delete->vifi, &nbr_mifs); } else { if (inet6_greaterthan(&v->uv_linklocal->pa_addr, &v->uv_pim_neighbors->address)) { /* The first address is the new potential remote * DR address, but the local address is the winner. */ v->uv_flags |= VIFF_DR; } } /* Update the source entries: * If the deleted nbr was my upstream, then reset incoming and * update all (S,G) entries for sources reachable through it. * If the deleted nbr was the last on a non-iif vif, then recalcuate * outgoing interfaces. */ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL; srcentry_ptr = srcentry_ptr_next) { srcentry_ptr_next = srcentry_ptr->next; /* The only time we don't need to scan all mrtentries is * when the nbr was on the iif, but not the upstream nbr! */ if (nbr_delete->vifi == srcentry_ptr->incoming && srcentry_ptr->upstream != nbr_delete) continue; /* Reset the next hop (PIM) router */ if(srcentry_ptr->upstream == nbr_delete) if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) { /* * Couldn't reset it. Sorry, the next hop router * toward that source is probably not * a PIM router, or cannot find route at all, * hence I cannot handle this source and have to * delete it. */ delete_srcentry(srcentry_ptr); free((char *)nbr_delete); return; } for (mrtentry_ptr = srcentry_ptr->mrtlink; mrtentry_ptr != (mrtentry_t *)NULL; mrtentry_ptr = mrtentry_ptr->srcnext) { mrtentry_ptr->incoming = srcentry_ptr->incoming; mrtentry_ptr->upstream = srcentry_ptr->upstream; mrtentry_ptr->metric = srcentry_ptr->metric; mrtentry_ptr->preference = srcentry_ptr->preference; state_change = change_interfaces(mrtentry_ptr, srcentry_ptr->incoming, &mrtentry_ptr->pruned_oifs, &mrtentry_ptr->leaves, &mrtentry_ptr->asserted_oifs); if(state_change == -1) { trigger_prune_alert(mrtentry_ptr); } else if(state_change == 1) { trigger_join_alert(mrtentry_ptr); } } } free((char *)nbr_delete); } /* TODO: simplify it! */ static int parse_pim6_hello(pim_message, datalen, src, holdtime) char *pim_message; int datalen; struct sockaddr_in6 *src; u_int16 *holdtime; { u_int8 *pim_hello_message; u_int8 *data_ptr; u_int16 option_type; u_int16 option_length; int holdtime_received_ok = FALSE; int option_total_length; pim_hello_message = (u_int8 *)(pim_message + sizeof(struct pim)); datalen -= sizeof(struct pim); for ( ; datalen >= sizeof(pim_hello_t); ) { /* Ignore any data if shorter than (pim_hello header) */ data_ptr = pim_hello_message; GET_HOSTSHORT(option_type, data_ptr); GET_HOSTSHORT(option_length, data_ptr); switch (option_type) { case PIM_MESSAGE_HELLO_HOLDTIME: if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length) { IF_DEBUG(DEBUG_PIM_HELLO) log(LOG_DEBUG, 0, "PIM HELLO Holdtime from %s: " "invalid OptionLength = %u", inet6_fmt(&src->sin6_addr), option_length); return (FALSE); } GET_HOSTSHORT(*holdtime, data_ptr); holdtime_received_ok = TRUE; break; default: /* Ignore any unknown options */ break; } /* Move to the next option */ /* XXX: TODO: If we are padding to the end of the 32 bit boundary, * use the first method to move to the next option, otherwise * simply (sizeof(pim_hello_t) + option_length). */ #ifdef BOUNDARY_32_BIT option_total_length = (sizeof(pim_hello_t) + (option_length & ~0x3) + ((option_length & 0x3) ? 4 : 0)); #else option_total_length = (sizeof(pim_hello_t) + option_length); #endif /* BOUNDARY_32_BIT */ datalen -= option_total_length; pim_hello_message += option_total_length; } return (holdtime_received_ok); } int send_pim6_hello(v, holdtime) struct uvif *v; u_int16 holdtime; { char *buf; u_int8 *data_ptr; int datalen; buf = pim6_send_buf + sizeof(struct pim); data_ptr = (u_int8 *)buf; PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME, data_ptr); PUT_HOSTSHORT(PIM_MESSAGE_HELLO_HOLDTIME_LENGTH, data_ptr); PUT_HOSTSHORT(holdtime, data_ptr); datalen = data_ptr - (u_int8 *)buf; send_pim6(pim6_send_buf, &v->uv_linklocal->pa_addr, &allpim6routers_group, PIM_HELLO, datalen); v->uv_pim_hello_timer = PIM_TIMER_HELLO_PERIOD; return(TRUE); } /************************************************************************ * PIM_JOIN_PRUNE ************************************************************************/ typedef struct { struct sockaddr_in6 source; struct sockaddr_in6 group; struct sockaddr_in6 target; } join_delay_cbk_t; typedef struct { mifi_t mifi; struct sockaddr_in6 source; struct sockaddr_in6 group; u_int16 holdtime; } prune_delay_cbk_t; static void delayed_join_job(arg) void *arg; { mrtentry_t *mrtentry_ptr; join_delay_cbk_t *cbk = (join_delay_cbk_t *)arg; mrtentry_ptr = find_route(&cbk->source, &cbk->group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) return; if(mrtentry_ptr->join_delay_timerid) timer_clearTimer(mrtentry_ptr->join_delay_timerid); if(mrtentry_ptr->upstream) send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming, &mrtentry_ptr->upstream->address, 0, 0); free(cbk); } static void schedule_delayed_join(mrtentry_ptr, target) mrtentry_t *mrtentry_ptr; struct sockaddr_in6 *target; { u_long random_delay; join_delay_cbk_t *cbk; /* Delete existing timer */ if(mrtentry_ptr->join_delay_timerid) timer_clearTimer(mrtentry_ptr->join_delay_timerid); #ifdef SYSV random_delay = lrand48() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT; #else random_delay = random() % (long)PIM_RANDOM_DELAY_JOIN_TIMEOUT; #endif IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "Scheduling join for src %s, grp %s, delay %ld", inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), random_delay); if(random_delay == 0 && mrtentry_ptr->upstream) { send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming, &mrtentry_ptr->upstream->address, 0, 0); return; } cbk = (join_delay_cbk_t *)malloc(sizeof(join_delay_cbk_t)); cbk->source = mrtentry_ptr->source->address; cbk->group = mrtentry_ptr->group->group; cbk->target = *target; mrtentry_ptr->join_delay_timerid = timer_setTimer(random_delay, delayed_join_job, cbk); } static void delayed_prune_job(arg) void *arg; { mrtentry_t *mrtentry_ptr; if_set new_pruned_oifs; int state_change; prune_delay_cbk_t *cbk = (prune_delay_cbk_t *)arg; mrtentry_ptr = find_route(&cbk->source, &cbk->group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) return; if(mrtentry_ptr->prune_delay_timerids[cbk->mifi]) timer_clearTimer(mrtentry_ptr->prune_delay_timerids[cbk->mifi]); if(IF_ISSET(cbk->mifi, &mrtentry_ptr->oifs)) { IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "Deleting pruned mif %d for src %s, grp %s", cbk->mifi, inet6_fmt(&cbk->source.sin6_addr), inet6_fmt(&cbk->group.sin6_addr)); IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs); IF_SET(cbk->mifi, &new_pruned_oifs); SET_TIMER(mrtentry_ptr->prune_timers[cbk->mifi], cbk->holdtime); state_change = change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, &new_pruned_oifs, &mrtentry_ptr->leaves, &mrtentry_ptr->asserted_oifs); /* Handle transition to negative cache */ if(state_change == -1) trigger_prune_alert(mrtentry_ptr); } free(cbk); } static void schedule_delayed_prune(mrtentry_ptr, mifi, holdtime) mrtentry_t *mrtentry_ptr; mifi_t mifi; u_int16 holdtime; { prune_delay_cbk_t *cbk; /* Delete existing timer */ if(mrtentry_ptr->prune_delay_timerids[mifi]) timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]); cbk = (prune_delay_cbk_t *)malloc(sizeof(prune_delay_cbk_t)); cbk->mifi = mifi; cbk->source = mrtentry_ptr->source->address; cbk->group = mrtentry_ptr->group->group; cbk->holdtime = holdtime; mrtentry_ptr->prune_delay_timerids[mifi] = timer_setTimer((u_int16)PIM_RANDOM_DELAY_JOIN_TIMEOUT, delayed_prune_job, cbk); } /* TODO: when parsing, check if we go beyong message size */ int receive_pim6_join_prune(src, pim_message, datalen) struct sockaddr_in6 *src; char *pim_message; register int datalen; { mifi_t mifi; struct uvif *v; pim6_encod_uni_addr_t uni_target_addr; pim6_encod_grp_addr_t encod_group; pim6_encod_src_addr_t encod_src; u_int8 *data_ptr; u_int8 num_groups; u_int16 holdtime; u_int16 num_j_srcs, num_p_srcs; struct sockaddr_in6 source, group, target; struct in6_addr s_mask, g_mask; u_int8 s_flags; u_int8 reserved; mrtentry_t *mrtentry_ptr; pim_nbr_entry_t *upstream_router; if_set new_pruned_oifs; int state_change; if ((mifi = find_vif_direct(src)) == NO_VIF) { /* * Either a local vif or somehow received PIM_JOIN_PRUNE from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) log(LOG_INFO, 0, "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s", inet6_fmt(&src->sin6_addr)); return(FALSE); } v = &uvifs[mifi]; if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS)) return(FALSE); /* Shoudn't come on this interface */ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); /* Get the target address */ GET_EUADDR6(&uni_target_addr, data_ptr); GET_BYTE(reserved, data_ptr); GET_BYTE(num_groups, data_ptr); if (num_groups == 0) return (FALSE); /* No indication for groups in the message */ GET_HOSTSHORT(holdtime, data_ptr); target.sin6_len = sizeof(target); target.sin6_family = AF_INET6; target.sin6_addr = uni_target_addr.unicast_addr; target.sin6_scope_id = inet6_uvif2scopeid(&target, v); IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "PIM Join/Prune received from %s : target %s, holdtime %d", inet6_fmt(&src->sin6_addr), inet6_fmt(&target.sin6_addr), holdtime); if (!inet6_localif_address(&target, v) && !IN6_IS_ADDR_UNSPECIFIED(&uni_target_addr.unicast_addr)) { /* if I am not the target of the join or prune message */ /* * Join Suppression: when receiving a join not addressed to me, * if I am delaying a join for this (S,G) then cancel the delayed * join. * Prune Soliticiting Joins: when receiving a prune not * addressed to me on a LAN, schedule delayed join if I have * downstream receivers. */ upstream_router = find_pim6_nbr(&target); if (upstream_router == (pim_nbr_entry_t *)NULL) return (FALSE); /* I have no such neighbor */ group.sin6_len = sizeof(group); group.sin6_family = AF_INET6; source.sin6_len = sizeof(source); source.sin6_family = AF_INET6; while (num_groups--) { GET_EGADDR6(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); if (encod_group.masklen > (sizeof(struct in6_addr) << 3)) continue; MASKLEN_TO_MASK6(encod_group.masklen, g_mask); group.sin6_addr = encod_group.mcast_addr; group.sin6_scope_id = inet6_uvif2scopeid(&group, v); if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) { data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t); continue; /* Ignore this group and jump to the next */ } while (num_j_srcs--) { GET_ESADDR6(&encod_src, data_ptr); source.sin6_addr = encod_src.src_addr; source.sin6_scope_id = inet6_uvif2scopeid(&source, v); /* sanity checks */ if (!inet6_valid_host(&source)) continue; if (encod_src.masklen > (sizeof(struct in6_addr) << 3)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK6(encod_src.masklen, s_mask); /* (S,G) Join suppresion */ mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) continue; IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "\tJOIN src %s, group %s - canceling " "delayed join", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); /* Cancel the delayed join */ if(mrtentry_ptr->join_delay_timerid) { timer_clearTimer(mrtentry_ptr->join_delay_timerid); mrtentry_ptr->join_delay_timerid = 0; } } while (num_p_srcs--) { GET_ESADDR6(&encod_src, data_ptr); source.sin6_addr = encod_src.src_addr; source.sin6_scope_id = inet6_uvif2scopeid(&source, v); /* sanity checks */ if (!inet6_valid_host(&source)) continue; if (encod_src.masklen > (sizeof(struct in6_addr) << 3)) continue; s_flags = encod_src.flags; /* if P2P link (not addressed to me) ignore */ if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) continue; /* * if non-null oiflist then schedule delayed join. */ mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) continue; if(!(IF_ISEMPTY(&mrtentry_ptr->oifs))) { IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "\tPRUNE src %s, group %s " "- scheduling delayed join", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); schedule_delayed_join(mrtentry_ptr, &target); } } } /* while groups */ return(TRUE); } /* if not unicast target */ /* I am the target of this join/prune: * For joins, cancel delayed prunes that I have scheduled. * For prunes, echo the prune and schedule delayed prunes on LAN or * prune immediately on point-to-point links. */ else { while (num_groups--) { GET_EGADDR6(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "PIM Join/Prune received: grp: %s plen: %d, " "%d jsrc, %d psrc", inet6_fmt(&encod_group.mcast_addr), encod_group.masklen, num_j_srcs, num_p_srcs); if (encod_group.masklen > (sizeof(struct in6_addr) << 3)) continue; /* Ignore this group */ MASKLEN_TO_MASK6(encod_group.masklen, g_mask); group.sin6_addr = encod_group.mcast_addr; group.sin6_scope_id = inet6_uvif2scopeid(&group, v); if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) { data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t); continue; /* Ignore this group and jump to the next */ } while (num_j_srcs--) { GET_ESADDR6(&encod_src, data_ptr); source.sin6_addr = encod_src.src_addr; source.sin6_scope_id = inet6_uvif2scopeid(&source, v); if (!inet6_valid_host(&source)) continue; if (encod_src.masklen > (sizeof(struct in6_addr) << 3)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK6(encod_src.masklen, s_mask); mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) continue; IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "\tJOIN src %s, group %s - canceling " "delayed prune", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); /* Cancel the delayed prune */ if(mrtentry_ptr->prune_delay_timerids[mifi]) { timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]); mrtentry_ptr->prune_delay_timerids[mifi] = 0; } } while (num_p_srcs--) { GET_ESADDR6(&encod_src, data_ptr); source.sin6_addr = encod_src.src_addr; source.sin6_scope_id = inet6_uvif2scopeid(&source, v); if (!inet6_valid_host(&source)) continue; s_flags = encod_src.flags; mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) continue; /* if P2P link (addressed to me) prune immediately */ if(uvifs[mifi].uv_flags & VIFF_POINT_TO_POINT) { if(IF_ISSET(mifi, &mrtentry_ptr->oifs)) { IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "\tPRUNE(P2P) src %s," "group %s - pruning " "mif", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); IF_DEBUG(DEBUG_MRT) log(LOG_DEBUG, 0, "Deleting pruned mif %d for src %s, grp %s", mifi, inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs); IF_SET(mifi, &new_pruned_oifs); SET_TIMER(mrtentry_ptr->prune_timers[mifi], holdtime); state_change = change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, &new_pruned_oifs, &mrtentry_ptr->leaves, &mrtentry_ptr->asserted_oifs); /* Handle transition to negative cache */ if(state_change == -1) trigger_prune_alert(mrtentry_ptr); } /* if is pruned */ } /* if p2p */ /* if LAN link, echo the prune and schedule delayed * oif deletion */ else { IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "\tPRUNE(LAN) src %s, group " "%s - scheduling delayed prune", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mifi, &target, holdtime, 1); schedule_delayed_prune(mrtentry_ptr, mifi, holdtime); } } } /* while groups */ } /* else I am unicast target */ return(TRUE); } int send_pim6_jp(mrtentry_ptr, action, mifi, target_addr, holdtime, echo) mrtentry_t *mrtentry_ptr; int action; /* PIM_ACTION_JOIN or PIM_ACTION_PRUNE */ mifi_t mifi; /* vif to send join/prune on */ struct sockaddr_in6 *target_addr; /* encoded unicast target neighbor */ u_int16 holdtime; /* holdtime */ int echo; { u_int8 *data_ptr, *data_start_ptr; data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim)); data_start_ptr = data_ptr; if(echo == 0 && mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) { /* No upstream neighbor - don't send */ return(FALSE); } IF_DEBUG(DEBUG_PIM_JOIN_PRUNE) log(LOG_DEBUG, 0, "Sending %s: vif %s, src %s, group %s, " "target %s, holdtime %d", action==PIM_ACTION_JOIN ? "JOIN" : "PRUNE", inet6_fmt(&uvifs[mifi].uv_linklocal->pa_addr.sin6_addr), inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), inet6_fmt(&target_addr->sin6_addr), holdtime); PUT_EUADDR6(target_addr->sin6_addr, data_ptr); /* encoded unicast target addr */ PUT_BYTE(0, data_ptr); /* Reserved */ *data_ptr++ = (u_int8)1; /* number of groups */ PUT_HOSTSHORT(holdtime, data_ptr); /* holdtime */ /* data_ptr points at the first, and only encoded mcast group */ PUT_EGADDR6(mrtentry_ptr->group->group.sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr); /* set the number of join and prune sources */ if(action == PIM_ACTION_JOIN) { PUT_HOSTSHORT(1, data_ptr); PUT_HOSTSHORT(0, data_ptr); } else if(action == PIM_ACTION_PRUNE) { PUT_HOSTSHORT(0, data_ptr); PUT_HOSTSHORT(1, data_ptr); } PUT_ESADDR6(mrtentry_ptr->source->address.sin6_addr, SINGLE_SRC_MSK6LEN, 0, data_ptr); /* Cancel active graft */ if (echo == 0) delete_pim6_graft_entry(mrtentry_ptr); send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr, &allpim6routers_group, PIM_JOIN_PRUNE, data_ptr - data_start_ptr); return(TRUE); } /************************************************************************ * PIM_ASSERT ************************************************************************/ /* Notes on assert prefs/metrics * - For downstream routers, compare pref/metric previously received from * winner against those in message. * ==> store assert winner's pref/metric in mrtentry * - For upstream router compare my actualy pref/metric for the source * against those received in message. * ==> store my actual pref/metric in srcentry */ int receive_pim6_assert(src, pim_message, datalen) struct sockaddr_in6 *src; register char *pim_message; int datalen; { mifi_t mifi; pim6_encod_uni_addr_t eusaddr; pim6_encod_grp_addr_t egaddr; struct sockaddr_in6 source, group; mrtentry_t *mrtentry_ptr; u_int8 *data_ptr; struct uvif *v; u_int32 assert_preference; u_int32 assert_metric; u_int32 local_metric; u_int32 local_preference; u_int8 local_wins; if_set new_pruned_oifs, new_leaves; int state_change; if ((mifi = find_vif_direct(src)) == NO_VIF) { /* Either a local vif or somehow received PIM_ASSERT from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) log(LOG_INFO, 0, "Ignoring PIM_ASSERT from non-neighbor router %s", inet6_fmt(&src->sin6_addr)); return(FALSE); } v = &uvifs[mifi]; if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS)) return(FALSE); /* Shoudn't come on this interface */ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); /* Get the group and source addresses */ GET_EGADDR6(&egaddr, data_ptr); GET_EUADDR6(&eusaddr, data_ptr); /* Get the metric related info */ GET_HOSTLONG(assert_preference, data_ptr); GET_HOSTLONG(assert_metric, data_ptr); source.sin6_addr = eusaddr.unicast_addr; source.sin6_scope_id = inet6_uvif2scopeid(&source, v); group.sin6_addr = egaddr.mcast_addr; group.sin6_scope_id = inet6_uvif2scopeid(&group, v); IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "PIM Assert received from %s: src %s, grp %s, " "pref %d, metric %d", inet6_fmt(&src->sin6_addr), inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr), assert_preference, assert_metric); if ((mrtentry_ptr = find_route(&source, &group, MRTF_SG, CREATE)) == NULL) { IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_INFO, 0, "\tFailed to create a mrtentry src:%s grp:%s", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr)); return(FALSE); } if(mrtentry_ptr->flags & MRTF_NEW) { /* For some reason, it's possible for asserts to be processed * before the data alerts a cache miss. Therefore, when an * assert is received, create (S,G) state and continue, since * we know by the assert that there are upstream forwarders. */ IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "\tNo MRT entry - creating..."); mrtentry_ptr->flags &= ~MRTF_NEW; /* Set oifs */ set_leaves(mrtentry_ptr); calc_oifs(mrtentry_ptr, &(mrtentry_ptr->oifs)); /* Add it to the kernel */ k_chg_mfc(mld6_socket, &source, &group, mrtentry_ptr->incoming, &mrtentry_ptr->oifs); #ifdef RSRR rsrr_cache_send(mrtentry_ptr, RSRR_NOTIFICATION_OK); #endif /* RSRR */ /* No need to call change_interfaces, but check for NULL oiflist */ if(IF_ISEMPTY(&mrtentry_ptr->oifs)) trigger_prune_alert(mrtentry_ptr); } /* If arrived on iif, I'm downstream of the asserted LAN. * If arrived on oif, I'm upstream of the asserted LAN. */ if (mifi == mrtentry_ptr->incoming) { /* assert arrived on iif ==> I'm a downstream router */ /* Determine local (really that of upstream nbr!) pref/metric */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; if(mrtentry_ptr->upstream && inet6_equal(&mrtentry_ptr->upstream->address, src) && assert_preference == local_preference && assert_metric == local_metric) /* if assert from previous winner w/ same pref/metric, * then assert sender wins again */ local_wins = FALSE; else /* assert from someone else or something changed */ local_wins = compare_metrics(local_preference, local_metric, &mrtentry_ptr->upstream->address, assert_preference, assert_metric, src); /* * This is between the assert sender and previous winner or rpf * (who is the "local" in this case). */ if(local_wins == TRUE) { /* the assert-sender loses, so discard the assert */ IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "\tAssert sender %s loses", inet6_fmt(&src->sin6_addr)); return(TRUE); } /* The assert sender wins: upstream must be changed to the winner */ IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "\tAssert sender %s wins", inet6_fmt(&src->sin6_addr)); if(inet6_equal(&mrtentry_ptr->upstream->address, src)) { IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "\tChanging upstream nbr to %s", inet6_fmt(&src->sin6_addr)); mrtentry_ptr->preference = assert_preference; mrtentry_ptr->metric = assert_metric; mrtentry_ptr->upstream = find_pim6_nbr(src); } SET_TIMER(mrtentry_ptr->assert_timer, PIM_ASSERT_TIMEOUT); mrtentry_ptr->flags |= MRTF_ASSERTED; /* Send a join for the S,G if oiflist is non-empty */ if(!(IF_ISEMPTY(&mrtentry_ptr->oifs))) send_pim6_jp(mrtentry_ptr, PIM_ACTION_JOIN, mrtentry_ptr->incoming, src, 0, 0); } /* if assert on iif */ /* If the assert arrived on an oif: */ else { if(!(IF_ISSET(mifi, &mrtentry_ptr->oifs))) return(FALSE); /* assert arrived on oif ==> I'm a upstream router */ /* Determine local pref/metric */ local_metric = mrtentry_ptr->source->metric; local_preference = mrtentry_ptr->source->preference; local_wins = compare_metrics(local_preference, local_metric, &v->uv_linklocal->pa_addr, assert_preference, assert_metric, src); if(local_wins == FALSE) { /* Assert sender wins - prune the interface */ IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "\tAssert sender %s wins - pruning...", inet6_fmt(&src->sin6_addr)); IF_COPY(&mrtentry_ptr->pruned_oifs, &new_pruned_oifs); IF_SET(mifi, &new_pruned_oifs); IF_SET(mifi, &mrtentry_ptr->asserted_oifs); SET_TIMER(mrtentry_ptr->prune_timers[mifi], PIM_JOIN_PRUNE_HOLDTIME); if (IF_ISSET(mifi, &mrtentry_ptr->leaves)) { IF_COPY(&mrtentry_ptr->leaves, &new_leaves); IF_CLR(mifi, &new_leaves); state_change = change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, &new_pruned_oifs, &mrtentry_ptr->leaves, &new_leaves); } else { state_change = change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, &new_pruned_oifs, &mrtentry_ptr->leaves, &mrtentry_ptr->asserted_oifs); } /* Handle transition to negative cache */ if(state_change == -1) trigger_prune_alert(mrtentry_ptr); } /* assert sender wins */ else { /* Local wins (assert sender loses): * send assert and schedule prune */ IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "\tAssert sender %s loses - " "sending assert and scheuling prune", inet6_fmt(&src->sin6_addr)); if(!(IF_ISSET(mifi, &mrtentry_ptr->leaves))) { /* * No directly connected receivers - delay prune */ send_pim6_jp(mrtentry_ptr, PIM_ACTION_PRUNE, mifi, &v->uv_linklocal->pa_addr, PIM_JOIN_PRUNE_HOLDTIME, 0); schedule_delayed_prune(mrtentry_ptr, mifi, PIM_JOIN_PRUNE_HOLDTIME); } send_pim6_assert(&source, &group, mifi, mrtentry_ptr); } } /* if assert on oif */ return(TRUE); } int send_pim6_assert(source, group, mifi, mrtentry_ptr) struct sockaddr_in6 *source; struct sockaddr_in6 *group; mifi_t mifi; mrtentry_t *mrtentry_ptr; { u_int8 *data_ptr; u_int8 *data_start_ptr; u_int32 local_preference; u_int32 local_metric; data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim)); data_start_ptr = data_ptr; PUT_EGADDR6(group->sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr); PUT_EUADDR6(source->sin6_addr, data_ptr); local_metric = mrtentry_ptr->source->metric; local_preference = mrtentry_ptr->source->preference; PUT_HOSTLONG(local_preference, data_ptr); PUT_HOSTLONG(local_metric, data_ptr); IF_DEBUG(DEBUG_PIM_ASSERT) log(LOG_DEBUG, 0, "PIM Assert sending: src %s, grp %s, " "pref %d, metric %d", inet6_fmt(&source->sin6_addr), inet6_fmt(&group->sin6_addr), local_metric, local_preference); send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr, &allpim6routers_group, PIM_ASSERT, data_ptr - data_start_ptr); return(TRUE); } /* Return TRUE if the local win, otherwise FALSE */ static int compare_metrics(local_preference, local_metric, local_address, remote_preference, remote_metric, remote_address) u_int32 local_preference; u_int32 local_metric; struct sockaddr_in6 *local_address; u_int32 remote_preference; u_int32 remote_metric; struct sockaddr_in6 *remote_address; { /* Now lets see who has a smaller gun (aka "asserts war") */ /* FYI, the smaller gun...err metric wins, but if the same * caliber, then the bigger network address wins. The order of * treatment is: preference, metric, address. */ /* The RPT bits are already included as the most significant bits * of the preferences. */ if (remote_preference > local_preference) return TRUE; if (remote_preference < local_preference) return FALSE; if (remote_metric > local_metric) return TRUE; if (remote_metric < local_metric) return FALSE; if (inet6_greaterthan(local_address, remote_address)) return TRUE; return FALSE; } /************************************************************************ * PIM_GRAFT ************************************************************************/ u_long graft_retrans_timer; /* Graft retransmission timer */ pim_graft_entry_t *graft_list; /* Active grafting entries */ void delete_pim6_graft_entry(mrtentry_ptr) mrtentry_t *mrtentry_ptr; { pim_graft_entry_t *graft_entry; if(mrtentry_ptr->graft == (pim_graft_entry_t *)NULL) return; graft_entry = mrtentry_ptr->graft; if(graft_entry->prev) graft_entry->prev->next = graft_entry->next; else graft_list = graft_entry->next; if(graft_entry->next) graft_entry->next->prev = graft_entry->prev; mrtentry_ptr->graft = (pim_graft_entry_t *)NULL; free(graft_entry); /* Stop the timer if there are no more entries */ if(!graft_list) { timer_clearTimer(graft_retrans_timer); graft_retrans_timer = 0; } } static int retransmit_pim6_graft(mrtentry_ptr) mrtentry_t *mrtentry_ptr; { u_int8 *data_ptr, *data_start_ptr; data_ptr = (u_int8 *)(pim6_send_buf + sizeof(struct pim)); data_start_ptr = data_ptr; if (mrtentry_ptr->upstream == (pim_nbr_entry_t *)NULL) { /* No upstream neighbor - don't send */ return(FALSE); } IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "Sending GRAFT: vif %s, src %s, grp %s, dst %s", inet6_fmt(&uvifs[mrtentry_ptr->incoming].uv_linklocal->pa_addr.sin6_addr), inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), inet6_fmt(&mrtentry_ptr->group->group.sin6_addr), inet6_fmt(&mrtentry_ptr->upstream->address.sin6_addr)); /* unicast target */ PUT_EUADDR6(mrtentry_ptr->upstream->address.sin6_addr, data_ptr); PUT_BYTE(0, data_ptr); /* Reserved */ *data_ptr++ = (u_int8)1; /* number of groups */ PUT_HOSTSHORT(0, data_ptr); /* no holdtime */ /* data_ptr points at the first, and only encoded mcast group */ PUT_EGADDR6(mrtentry_ptr->group->group.sin6_addr, SINGLE_GRP_MSK6LEN, 0, data_ptr); /* set the number of join(graft) and prune sources */ PUT_HOSTSHORT(1, data_ptr); PUT_HOSTSHORT(0, data_ptr); PUT_ESADDR6(mrtentry_ptr->source->address.sin6_addr, SINGLE_SRC_MSK6LEN, 0, data_ptr); send_pim6(pim6_send_buf, &uvifs[mrtentry_ptr->incoming].uv_linklocal->pa_addr, &mrtentry_ptr->upstream->address, PIM_GRAFT, data_ptr - data_start_ptr); return(TRUE); } static void retransmit_all_pim6_grafts(arg) void *arg; /* UNUSED */ { pim_graft_entry_t *graft_ptr; IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "Retransmitting all pending PIM-Grafts"); for(graft_ptr = graft_list; graft_ptr != NULL; graft_ptr = graft_ptr->next) { IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "\tGRAFT src %s, grp %s", inet6_fmt(&graft_ptr->mrtlink->source->address.sin6_addr), inet6_fmt(&graft_ptr->mrtlink->group->group.sin6_addr)); retransmit_pim6_graft(graft_ptr->mrtlink); } if (graft_list) timer_setTimer(PIM_GRAFT_RETRANS_PERIOD, retransmit_all_pim6_grafts, (void *)NULL); } int receive_pim6_graft(src, pim_message, datalen, pimtype) struct sockaddr_in6 *src; register char *pim_message; int datalen; int pimtype; { mifi_t mifi; struct uvif *v; pim6_encod_uni_addr_t uni_target_addr; pim6_encod_grp_addr_t encod_group; pim6_encod_src_addr_t encod_src; u_int8 *data_ptr; u_int8 num_groups; u_int16 holdtime; u_int16 num_j_srcs; u_int16 num_p_srcs; struct sockaddr_in6 source, group; struct in6_addr s_mask, g_mask; u_int8 s_flags; u_int8 reserved; mrtentry_t *mrtentry_ptr; int state_change; if ((mifi = find_vif_direct(src)) == NO_VIF) { /* Either a local vif or somehow received PIM_GRAFT from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) log(LOG_INFO, 0, "Ignoring PIM_GRAFT from non-neighbor router %s", inet6_fmt(&src->sin6_addr)); return(FALSE); } v = &uvifs[mifi]; if (uvifs[mifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS)) return(FALSE); /* Shoudn't come on this interface */ data_ptr = (u_int8 *)(pim_message + sizeof(struct pim)); /* Get the target address */ GET_EUADDR6(&uni_target_addr, data_ptr); GET_BYTE(reserved, data_ptr); GET_BYTE(num_groups, data_ptr); if (num_groups == 0) return (FALSE); /* No indication for groups in the message */ GET_HOSTSHORT(holdtime, data_ptr); IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "PIM %s received from %s on mif %d, grps: %d", pimtype == PIM_GRAFT ? "GRAFT" : "GRAFT-ACK", inet6_fmt(&src->sin6_addr), mifi, num_groups); group.sin6_len = sizeof(group); group.sin6_family = AF_INET6; source.sin6_len = sizeof(source); source.sin6_family = AF_INET6; while (num_groups--) { GET_EGADDR6(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, " PIM graft: grp: %s, plen: %d, %d jsrcs, %d psrcs", inet6_fmt(&encod_group.mcast_addr), encod_group.masklen, num_j_srcs, num_p_srcs); if (encod_group.masklen > (sizeof(struct in6_addr) << 3)) continue; MASKLEN_TO_MASK6(encod_group.masklen, g_mask); group.sin6_addr = encod_group.mcast_addr; group.sin6_scope_id = inet6_uvif2scopeid(&group, v); if (!IN6_IS_ADDR_MULTICAST(&group.sin6_addr)) { data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim6_encod_src_addr_t); continue; /* Ignore this group and jump to the next */ } while (num_j_srcs--) { GET_ESADDR6(&encod_src, data_ptr); if (encod_src.masklen > (sizeof(struct in6_addr) << 3)) continue; source.sin6_addr = encod_src.src_addr; source.sin6_scope_id = inet6_uvif2scopeid(&source, v); if (!inet6_valid_host(&source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK6(encod_src.masklen, s_mask); mrtentry_ptr = find_route(&source, &group, MRTF_SG, DONT_CREATE); if(mrtentry_ptr == (mrtentry_t *)NULL) continue; if(pimtype == PIM_GRAFT) { /* Graft */ IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "\tGRAFT src %s, group %s - " "forward data on mif %d", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr), mifi); /* Cancel any delayed prune */ if(mrtentry_ptr->prune_delay_timerids[mifi]) { timer_clearTimer(mrtentry_ptr->prune_delay_timerids[mifi]); mrtentry_ptr->prune_delay_timerids[mifi] = 0; } /* Add to oiflist (unprune) */ if (IF_ISSET(mifi, &mrtentry_ptr->pruned_oifs)) { IF_CLR(mifi, &mrtentry_ptr->pruned_oifs); IF_CLR(mifi, &mrtentry_ptr->asserted_oifs); SET_TIMER(mrtentry_ptr->prune_timers[mifi], 0); state_change = change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, &mrtentry_ptr->pruned_oifs, &mrtentry_ptr->leaves, &mrtentry_ptr->asserted_oifs); if(state_change == 1) trigger_join_alert(mrtentry_ptr); } } /* Graft */ else { /* Graft-Ack */ IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "\tGRAFT-ACK src %s, group %s - " "forward data on mif %d", inet6_fmt(&source.sin6_addr), inet6_fmt(&group.sin6_addr), mifi); if(mrtentry_ptr->graft) delete_pim6_graft_entry(mrtentry_ptr); } } /* Ignore anything in the prune portion of the message! */ } /* Respond to graft with a graft-ack */ if(pimtype == PIM_GRAFT) { IF_DEBUG(DEBUG_PIM_GRAFT) log(LOG_DEBUG, 0, "Sending GRAFT-ACK: mif %s, dst %s", inet6_fmt(&uvifs[mifi].uv_linklocal->pa_addr.sin6_addr), inet6_fmt(&src->sin6_addr)); bcopy(pim_message, pim6_send_buf, datalen); send_pim6(pim6_send_buf, &uvifs[mifi].uv_linklocal->pa_addr, src, PIM_GRAFT_ACK, datalen - sizeof(struct pim)); } return(TRUE); } int send_pim6_graft(mrtentry_ptr) mrtentry_t *mrtentry_ptr; { pim_graft_entry_t *new_graft; int was_sent = 0; if(mrtentry_ptr->graft != (pim_graft_entry_t *)NULL) /* Already sending grafts */ return(FALSE); /* Send the first graft */ was_sent = retransmit_pim6_graft(mrtentry_ptr); if(!was_sent) return(FALSE); /* Set up retransmission */ new_graft = (pim_graft_entry_t *)malloc(sizeof(pim_graft_entry_t)); if (new_graft == (pim_graft_entry_t *)NULL) { log(LOG_WARNING, 0, "Memory allocation error for graft entry src %s, grp %s", inet6_fmt(&mrtentry_ptr->source->address.sin6_addr), inet6_fmt(&mrtentry_ptr->group->group.sin6_addr)); return(FALSE); } new_graft->next = graft_list; new_graft->prev = (pim_graft_entry_t *)NULL; new_graft->mrtlink = mrtentry_ptr; if(graft_list) graft_list->prev = new_graft; graft_list = new_graft; mrtentry_ptr->graft = new_graft; /* Set up timer if not running */ if(!graft_retrans_timer) graft_retrans_timer = timer_setTimer(PIM_GRAFT_RETRANS_PERIOD, retransmit_all_pim6_grafts, (void *)NULL); return(TRUE); }