/* * Copyright (c) 1998-2001 * University of Southern California/Information Sciences Institute. * 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. */ /* * $Id: pim_proto.c,v 1.47 2003/05/28 22:57:16 pavlin Exp $ */ #include #include "defs.h" /* * Local functions definitions. */ static int parse_pim_hello (char *pim_message, size_t datalen, u_int32 src, u_int16 *holdtime); static int send_pim_register_stop (u_int32 reg_src, u_int32 reg_dst, u_int32 inner_source, u_int32 inner_grp); static build_jp_message_t *get_jp_working_buff (void); static void return_jp_working_buff (pim_nbr_entry_t *pim_nbr); static void pack_jp_message (pim_nbr_entry_t *pim_nbr); static void send_jp_message (pim_nbr_entry_t *pim_nbr); static int compare_metrics (u_int32 local_preference, u_int32 local_metric, u_int32 local_address, u_int32 remote_preference, u_int32 remote_metric, u_int32 remote_address); build_jp_message_t *build_jp_message_pool; int build_jp_message_pool_counter; /************************************************************************ * PIM_HELLO ************************************************************************/ int receive_pim_hello(u_int32 src, u_int32 dst __attribute__((unused)), char *pim_message, size_t datalen) { vifi_t vifi; struct uvif *v; pim_nbr_entry_t *nbr, *prev_nbr, *new_nbr; u_int16 holdtime = 0; int bsr_length; u_int8 *data_ptr __attribute__((unused)); srcentry_t *srcentry_ptr; mrtentry_t *mrtentry_ptr; /* Checksum */ if (inet_cksum((u_int16 *)pim_message, datalen)) return FALSE; if ((vifi = 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) logit(LOG_INFO, 0, "Ignoring PIM_HELLO from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); return FALSE; } v = &uvifs[vifi]; if (v->uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_REGISTER)) return FALSE; /* Shoudn't come on this interface */ data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t)); /* Get the Holdtime (in seconds) from the message. Return if error. */ if (parse_pim_hello(pim_message, datalen, src, &holdtime) == FALSE) return FALSE; IF_DEBUG(DEBUG_PIM_HELLO | DEBUG_PIM_TIMER) { logit(LOG_DEBUG, 0, "PIM HELLO holdtime from %s is %u", inet_fmt(src, s1, sizeof(s1)), 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 (ntohl(src) < ntohl(nbr->address)) continue; if (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! */ logit(LOG_INFO, 0, "PIM HELLO received: neighbor %s going down", inet_fmt(src, s1, sizeof(s1))); delete_pim_nbr(nbr); return TRUE; } SET_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 *)calloc(1, sizeof(pim_nbr_entry_t)); if (!new_nbr) logit(LOG_ERR, 0, "Ran out of memory in receive_pim_hello()"); new_nbr->address = src; new_nbr->vifi = vifi; SET_TIMER(new_nbr->timer, holdtime); new_nbr->build_jp_message = (build_jp_message_t *)NULL; 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; /* Since a new neighbour has come up, let it know your existence */ /* XXX: TODO: not in the spec, * but probably should send the message after a short random period? */ send_pim_hello(v, PIM_TIMER_HELLO_HOLDTIME); if (v->uv_flags & VIFF_DR) { /* * If I am the current DR on that interface, so * send an RP-Set message to the new neighbor. */ if ((bsr_length = create_pim_bootstrap_message(pim_send_buf))) send_pim_unicast(pim_send_buf, v->uv_lcl_addr, src, PIM_BOOTSTRAP, bsr_length); /* The router with highest network address is the elected DR */ if (ntohl(v->uv_lcl_addr) < ntohl(src)) { /* I was the DR, but not anymore. Remove all register_vif from * oif list for all directly connected sources (for vifi). */ /* TODO: XXX: first entry is not used! */ for (srcentry_ptr = srclist->next; srcentry_ptr != (srcentry_t *)NULL; srcentry_ptr = srcentry_ptr->next) { /* If not directly connected source for vifi */ if ((srcentry_ptr->incoming != vifi) || (srcentry_ptr->upstream != (pim_nbr_entry_t *)NULL)) continue; for (mrtentry_ptr = srcentry_ptr->mrtlink; mrtentry_ptr != (mrtentry_t *)NULL; mrtentry_ptr = mrtentry_ptr->srcnext) { if (!(mrtentry_ptr->flags & MRTF_SG)) continue; /* This is not (S,G) entry */ /* Remove the register oif */ VIFM_CLR(reg_vif_num, mrtentry_ptr->joined_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } } v->uv_flags &= ~VIFF_DR; } } /* * TODO: XXX: does a new neighbor change any routing entries info? * Need to trigger joins? */ IF_DEBUG(DEBUG_PIM_HELLO) dump_vifs(stderr); /* Show we got a new neighbor */ return TRUE; } void delete_pim_nbr(pim_nbr_entry_t *nbr_delete) { srcentry_t *srcentry_ptr; srcentry_t *srcentry_ptr_next; mrtentry_t *mrtentry_ptr; mrtentry_t *mrtentry_srcs; grpentry_t *grpentry_ptr; pim_nbr_entry_t *new_nbr __attribute__((unused)); cand_rp_t *cand_rp_ptr; rp_grp_entry_t *rp_grp_entry_ptr; rpentry_t *rpentry_ptr; struct uvif *v; 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; return_jp_working_buff(nbr_delete); 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); } else { if (ntohl(v->uv_lcl_addr) > ntohl(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 */ for (srcentry_ptr = srclist; srcentry_ptr != (srcentry_t *)NULL; srcentry_ptr = srcentry_ptr_next) { srcentry_ptr_next = srcentry_ptr->next; if (srcentry_ptr->upstream != nbr_delete) continue; /* Reset the next hop (PIM) router */ if (set_incoming(srcentry_ptr, PIM_IIF_SOURCE) == FALSE) { /* Coudn't reset it. Sorry, the hext 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); } else if (srcentry_ptr->upstream != (pim_nbr_entry_t *)NULL) { /* Ignore the local or directly connected sources */ /* Browse all MRT entries for this source and reset the * upstream router. Note that the upstream router is not always * toward the source: it could be toward the RP for example. */ new_nbr = srcentry_ptr->upstream; for (mrtentry_ptr = srcentry_ptr->mrtlink; mrtentry_ptr != (mrtentry_t *)NULL; mrtentry_ptr = mrtentry_ptr->srcnext) { if (!(mrtentry_ptr->flags & MRTF_RP)) { mrtentry_ptr->upstream = srcentry_ptr->upstream; mrtentry_ptr->metric = srcentry_ptr->metric; mrtentry_ptr->preference = srcentry_ptr->preference; change_interfaces(mrtentry_ptr, srcentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } } } } /* Update the RP entries */ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr != (cand_rp_t *)NULL; cand_rp_ptr = cand_rp_ptr->next) { if (cand_rp_ptr->rpentry->upstream != nbr_delete) continue; rpentry_ptr = cand_rp_ptr->rpentry; /* Reset the RP entry iif */ /* TODO: check if error setting the iif! */ if (local_address(rpentry_ptr->address) == NO_VIF) set_incoming(rpentry_ptr, PIM_IIF_RP); else { rpentry_ptr->incoming = reg_vif_num; rpentry_ptr->upstream = (pim_nbr_entry_t *)NULL; } mrtentry_ptr = rpentry_ptr->mrtlink; if (mrtentry_ptr != (mrtentry_t *)NULL) { mrtentry_ptr->upstream = rpentry_ptr->upstream; mrtentry_ptr->metric = rpentry_ptr->metric; mrtentry_ptr->preference = rpentry_ptr->preference; change_interfaces(mrtentry_ptr, rpentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } /* Update the group entries for this RP */ 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) { mrtentry_ptr = grpentry_ptr->grp_route; if (mrtentry_ptr != (mrtentry_t *)NULL) { mrtentry_ptr->upstream = rpentry_ptr->upstream; mrtentry_ptr->metric = rpentry_ptr->metric; mrtentry_ptr->preference = rpentry_ptr->preference; change_interfaces(mrtentry_ptr, rpentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } /* Update only the (S,G)RPbit entries for this group */ for (mrtentry_srcs = grpentry_ptr->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) { if (mrtentry_srcs->flags & MRTF_RP) { mrtentry_ptr->upstream = rpentry_ptr->upstream; mrtentry_ptr->metric = rpentry_ptr->metric; mrtentry_ptr->preference = rpentry_ptr->preference; change_interfaces(mrtentry_srcs, rpentry_ptr->incoming, mrtentry_srcs->joined_oifs, mrtentry_srcs->pruned_oifs, mrtentry_srcs->leaves, mrtentry_srcs->asserted_oifs, 0); } } } } } free((char *)nbr_delete); } /* TODO: simplify it! */ static int parse_pim_hello(char *pim_message, size_t datalen, u_int32 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(pim_header_t)); datalen -= sizeof(pim_header_t); 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); if (option_type == PIM_MESSAGE_HELLO_HOLDTIME) { if (PIM_MESSAGE_HELLO_HOLDTIME_LENGTH != option_length) { IF_DEBUG(DEBUG_PIM_HELLO) { logit(LOG_DEBUG, 0, "PIM HELLO Holdtime from %s: invalid OptionLength = %u", inet_fmt(src, s1, sizeof(s1)), option_length); } return FALSE; } GET_HOSTSHORT(*holdtime, data_ptr); holdtime_received_ok = TRUE; } else { /* Ignore any unknown options */ } /* 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_pim_hello(struct uvif *v, u_int16 holdtime) { char *buf; u_int8 *data_ptr; int datalen; buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t); 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_pim(pim_send_buf, v->uv_lcl_addr, allpimrouters_group, PIM_HELLO, datalen); SET_TIMER(v->uv_pim_hello_timer, PIM_TIMER_HELLO_PERIOD); return TRUE; } /************************************************************************ * PIM_REGISTER ************************************************************************/ /* TODO: XXX: IF THE BORDER BIT IS SET, THEN * FORWARD THE WHOLE PACKET FROM USER SPACE * AND AT THE SAME TIME IGNORE ANY CACHE_MISS * SIGNALS FROM THE KERNEL. */ int receive_pim_register(u_int32 reg_src, u_int32 reg_dst, char *pim_message, size_t datalen) { u_int32 inner_src, inner_grp; pim_register_t *register_p; struct ip *ip; u_int32 borderBit, nullRegisterBit; mrtentry_t *mrtentry_ptr; mrtentry_t *mrtentry_ptr2; vifbitmap_t oifs; /* * Message length validation. * This is suppose to be done in the kernel, but some older kernel * versions do not pefrorm the check for the NULL register messages. */ if (datalen < sizeof(pim_header_t) + sizeof(pim_register_t) + sizeof(struct ip)) { IF_DEBUG(DEBUG_PIM_REGISTER) { logit(LOG_INFO, 0, "PIM register: short packet (len = %d) from %s", datalen, inet_fmt(reg_src, s1, sizeof(s1))); } return FALSE; } /* * XXX: For PIM_REGISTER the checksum does not include * the inner IP packet. However, some older routers might * create the checksum over the whole packet. Hence, * verify the checksum over the first 8 bytes, and if fails, * then over the whole Register */ if ((inet_cksum((u_int16 *)pim_message, sizeof(pim_header_t) + sizeof(pim_register_t))) && (inet_cksum((u_int16 *)pim_message, datalen))) { IF_DEBUG(DEBUG_PIM_REGISTER) { logit(LOG_DEBUG, 0, "PIM REGISTER from DR %s: invalid PIM header checksum", inet_fmt(reg_src, s1, sizeof(s1))); } return FALSE; } register_p = (pim_register_t *)(pim_message + sizeof(pim_header_t)); borderBit = ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_BORDER_BIT; nullRegisterBit = ntohl(register_p->reg_flags) & PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT; /* initialize the pointer to the encapsulated packet */ ip = (struct ip *)(register_p + 1); /* check the IP version (especially for the NULL register...see above) */ if (ip->ip_v != IPVERSION && (! nullRegisterBit)) { IF_DEBUG(DEBUG_PIM_REGISTER) logit(LOG_INFO, 0, "PIM register: incorrect IP version (%d) of the inner packet from %s", ip->ip_v, inet_fmt(reg_src, s1, sizeof(s1))); return FALSE; } /* We are keeping all addresses in network order, so no need for ntohl()*/ inner_src = ip->ip_src.s_addr; inner_grp = ip->ip_dst.s_addr; /* * inner_src and inner_grp must be valid IP unicast and multicast address * respectively. XXX: not in the spec. */ if ((!inet_valid_host(inner_src)) || (!IN_MULTICAST(ntohl(inner_grp)))) { if (!inet_valid_host(inner_src)) { logit(LOG_WARNING, 0, "Inner source address of register message by %s is invalid: %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_src, s2, sizeof(s2))); } if (!IN_MULTICAST(ntohl(inner_grp))) { logit(LOG_WARNING, 0, "Inner group address of register message by %s is invalid: %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2))); } send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return FALSE; } mrtentry_ptr = find_route(inner_src, inner_grp, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); if (!mrtentry_ptr) { /* No routing entry. Send REGISTER_STOP and return. */ IF_DEBUG(DEBUG_PIM_REGISTER) { logit(LOG_DEBUG, 0, "No routing entry for source %s and/or group %s" , inet_fmt(inner_src, s1, sizeof(s1)), inet_fmt(inner_grp, s2, sizeof(s2))); } /* TODO: XXX: shoudn't be inner_src=INADDR_ANY? Not in the spec. */ send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } /* XXX: not in the spec: check if I am the RP for that group */ if ((local_address(reg_dst) == NO_VIF) || (check_mrtentry_rp(mrtentry_ptr, reg_dst) == FALSE)) { send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } if (mrtentry_ptr->flags & MRTF_SG) { /* (S,G) found */ /* TODO: check the timer again */ SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT); /* restart timer */ if (!(mrtentry_ptr->flags & MRTF_SPT)) { /* The SPT bit is not set */ if (!nullRegisterBit) { calc_oifs(mrtentry_ptr, &oifs); if (VIFM_ISEMPTY(oifs) && (mrtentry_ptr->incoming == reg_vif_num)) { send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } /* * TODO: XXX: BUG!!! * The data will be forwarded by the kernel MFC!!! * Need to set a special flag for this routing entry so after * a cache miss occur, the multicast packet will be forwarded * from user space and won't install entry in the kernel MFC. * The problem is that the kernel MFC doesn't know the * PMBR address and simply sets the multicast forwarding * cache to accept/forward all data coming from the * register_vif. */ if (borderBit) { if (mrtentry_ptr->pmbr_addr != reg_src) { send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } } return TRUE; } /* TODO: XXX: if NULL_REGISTER and has (S,G) with SPT=0, then..?*/ return TRUE; } else { /* The SPT bit is set */ send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } } if (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR)) { if (borderBit) { /* Create (S,G) state. The oifs will be the copied from the * existing (*,G) or (*,*,RP) entry. */ mrtentry_ptr2 = find_route(inner_src, inner_grp, MRTF_SG, CREATE); if (mrtentry_ptr2) { mrtentry_ptr2->pmbr_addr = reg_src; /* Clear the SPT flag */ mrtentry_ptr2->flags &= ~(MRTF_SPT | MRTF_NEW); SET_TIMER(mrtentry_ptr2->timer, PIM_DATA_TIMEOUT); /* TODO: explicitly call the Join/Prune send function? */ FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Send the Join immediately */ /* TODO: explicitly call this function? send_pim_join_prune(mrtentry_ptr2->upstream->vifi, mrtentry_ptr2->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } } } if (mrtentry_ptr->flags & MRTF_WC) { /* (*,G) entry */ calc_oifs(mrtentry_ptr, &oifs); if (VIFM_ISEMPTY(oifs)) { send_pim_register_stop(reg_dst, reg_src, inner_grp, INADDR_ANY_N); return FALSE; } /* XXX: TODO: check with the spec again */ else { if (!nullRegisterBit) { /* Install cache entry in the kernel */ /* TODO: XXX: probably redundant here, because the * decapsulated mcast packet in the kernel will * result in CACHE_MISS */ u_int32 mfc_source = inner_src; #ifdef KERNEL_MFC_WC_G if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG)) mfc_source = INADDR_ANY_N; #endif /* KERNEL_MFC_WC_G */ add_kernel_cache(mrtentry_ptr, mfc_source, inner_grp, 0); k_chg_mfc(igmp_socket, mfc_source, inner_grp, mrtentry_ptr->incoming, mrtentry_ptr->oifs, mrtentry_ptr->group->rpaddr); return TRUE; } } return TRUE; } if (mrtentry_ptr->flags & MRTF_PMBR) { /* (*,*,RP) entry */ if (!nullRegisterBit) { u_int32 mfc_source = inner_src; /* XXX: have to create either (S,G) or (*,G). * The choice below is (*,G) */ mrtentry_ptr2 = find_route(INADDR_ANY_N, inner_grp, MRTF_WC, CREATE); if (!mrtentry_ptr2) return FALSE; if (mrtentry_ptr2->flags & MRTF_NEW) { /* TODO: something else? Have the feeling sth is missing */ mrtentry_ptr2->flags &= ~MRTF_NEW; /* TODO: XXX: copy the timer from the (*,*,RP) entry? */ COPY_TIMER(mrtentry_ptr->timer, mrtentry_ptr2->timer); } /* Install cache entry in the kernel */ #ifdef KERNEL_MFC_WC_G if (!(mrtentry_ptr->flags & MRTF_MFC_CLONE_SG)) mfc_source = INADDR_ANY_N; #endif /* KERNEL_MFC_WC_G */ add_kernel_cache(mrtentry_ptr, mfc_source, inner_grp, 0); k_chg_mfc(igmp_socket, mfc_source, inner_grp, mrtentry_ptr->incoming, mrtentry_ptr->oifs, mrtentry_ptr2->group->rpaddr); return TRUE; } } /* Shoudn't happen: invalid routing entry? */ /* XXX: TODO: shoudn't be inner_src=INADDR_ANY? Not in the spec. */ send_pim_register_stop(reg_dst, reg_src, inner_grp, inner_src); return TRUE; } int send_pim_register(char *packet) { struct ip *ip; u_int32 source, group; vifi_t vifi; rpentry_t *rpentry_ptr; mrtentry_t *mrtentry_ptr; mrtentry_t *mrtentry_ptr2; u_int32 reg_src, reg_dst; int pktlen = 0; char *buf; ip = (struct ip *)packet; source = ip->ip_src.s_addr; group = ip->ip_dst.s_addr; if ((vifi = find_vif_direct_local(source)) == NO_VIF) return FALSE; if (!(uvifs[vifi].uv_flags & VIFF_DR)) return FALSE; /* I am not the DR for that subnet */ rpentry_ptr = rp_match(group); if (!rpentry_ptr) return FALSE; /* No RP for this group */ if (local_address(rpentry_ptr->address) != NO_VIF) { /* TODO: XXX: not sure it is working! */ return FALSE; /* I am the RP for this group */ } mrtentry_ptr = find_route(source, group, MRTF_SG, CREATE); if (!mrtentry_ptr) return FALSE; /* Cannot create (S,G) state */ if (mrtentry_ptr->flags & MRTF_NEW) { /* A new entry */ mrtentry_ptr->flags &= ~MRTF_NEW; RESET_TIMER(mrtentry_ptr->rs_timer); /* Reset the Register-Suppression timer */ mrtentry_ptr2 = mrtentry_ptr->group->grp_route; if (!mrtentry_ptr2) mrtentry_ptr2 = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink; if (mrtentry_ptr2) { FIRE_TIMER(mrtentry_ptr2->jp_timer); /* Timeout the Join/Prune timer */ /* TODO: explicitly call this function? send_pim_join_prune(mrtentry_ptr2->upstream->vifi, mrtentry_ptr2->upstream, PIM_JOIN_PRUNE_HOLDTIME); */ } } /* Restart the (S,G) Entry-timer */ SET_TIMER(mrtentry_ptr->timer, PIM_DATA_TIMEOUT); IF_TIMER_NOT_SET(mrtentry_ptr->rs_timer) { /* The Register-Suppression Timer is not running. * Encapsulate the data and send to the RP. */ #ifdef PIM_OLD_KERNEL /* TODO: verify again! */ /* recompute the checksum; as checksum may fail when decapsulating * at the RP if we don't do this. */ #ifdef old_Linux ip->ip_csum = 0; #else ip->ip_sum = 0; #endif /* old_Linux */ #ifndef RAW_INPUT_IS_RAW /* TODO: htons??? */ ip->ip_len = htons(ip->ip_len); ip->ip_off = htons(ip->ip_off); #endif /* !RAW_INPUT_IS_RAW */ #ifdef old_Linux ip->ip_csum = inet_cksum((u_int16 *)ip, ip->ip_hl << 2); #else ip->ip_sum = inet_cksum((u_int16 *)ip, ip->ip_hl << 2); #endif /* old_Linux */ #endif /* PIM_OLD_KERNEL */ buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t); memset(buf, 0, sizeof(pim_register_t)); /* No flags set */ buf += sizeof(pim_register_t); /* Copy the data packet at the back of the register packet */ /* TODO: check pktlen. ntohs? */ pktlen = ntohs(ip->ip_len); memcpy(buf, ip, pktlen); pktlen += sizeof(pim_register_t); reg_src = uvifs[vifi].uv_lcl_addr; reg_dst = mrtentry_ptr->group->rpaddr; send_pim_unicast(pim_send_buf, reg_src, reg_dst, PIM_REGISTER, pktlen); return TRUE; } return TRUE; } int send_pim_null_register(mrtentry_t *mrtentry_ptr) { struct ip *ip; pim_register_t *pim_register; int pktlen =0; vifi_t vifi; u_int32 reg_source, dest; /* No directly connected source; no local address */ if ((vifi = find_vif_direct_local(mrtentry_ptr->source->address))== NO_VIF) return FALSE; pim_register = (pim_register_t *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t)); memset(pim_register, 0, sizeof(pim_register_t)); pim_register->reg_flags = htonl(pim_register->reg_flags | PIM_MESSAGE_REGISTER_NULL_REGISTER_BIT); ip = (struct ip *)(pim_register + 1); /* set src/dst in dummy hdr */ ip->ip_v = IPVERSION; ip->ip_hl = (sizeof(struct ip) >> 2); ip->ip_tos = 0; ip->ip_id = 0; ip->ip_off = 0; ip->ip_p = IPPROTO_UDP; /* XXX: bogus */ ip->ip_len = htons(sizeof(struct ip)); ip->ip_ttl = MINTTL; /* TODO: XXX: check whether need to setup the ttl */ ip->ip_src.s_addr = mrtentry_ptr->source->address; ip->ip_dst.s_addr = mrtentry_ptr->group->group; #ifdef old_Linux ip->ip_csum = 0; ip->ip_csum = inet_cksum((u_int16 *)ip, sizeof(struct ip)); #else ip->ip_sum = 0; ip->ip_sum = inet_cksum((u_int16 *)ip, sizeof(struct ip)); #endif /* old_Linux */ /* include the dummy ip header */ pktlen = sizeof(pim_register_t) + sizeof(struct ip); dest = mrtentry_ptr->group->rpaddr; reg_source = uvifs[vifi].uv_lcl_addr; send_pim_unicast(pim_send_buf, reg_source, dest, PIM_REGISTER, pktlen); return TRUE; } /************************************************************************ * PIM_REGISTER_STOP ************************************************************************/ int receive_pim_register_stop(u_int32 reg_src, u_int32 reg_dst, char *pim_message, size_t datalen) { pim_register_stop_t *pim_regstop_p; pim_encod_grp_addr_t encod_grp; pim_encod_uni_addr_t encod_unisrc; u_int8 *data_ptr; mrtentry_t *mrtentry_ptr; vifbitmap_t pruned_oifs; /* Checksum */ if (inet_cksum((u_int16 *)pim_message, datalen)) return FALSE; pim_regstop_p = (pim_register_stop_t *)(pim_message + sizeof(pim_header_t)); data_ptr = (u_int8 *)&pim_regstop_p->encod_grp; GET_EGADDR(&encod_grp, data_ptr); GET_EUADDR(&encod_unisrc, data_ptr); IF_DEBUG(DEBUG_PIM_REGISTER) { logit(LOG_DEBUG, 0, "Received PIM_REGISTER_STOP from RP %s to %s for src = %s and group = %s", inet_fmt(reg_src, s1, sizeof(s1)), inet_fmt(reg_dst, s2, sizeof(s2)), inet_fmt(encod_unisrc.unicast_addr, s3, sizeof(s3)), inet_fmt(encod_grp.mcast_addr, s4, sizeof(s4))); } /* TODO: apply the group mask and do register_stop for all grp addresses */ /* TODO: check for SourceAddress == 0 */ mrtentry_ptr = find_route(encod_unisrc.unicast_addr, encod_grp.mcast_addr, MRTF_SG, DONT_CREATE); if (!mrtentry_ptr) return FALSE; /* XXX: not in the spec: check if the PIM_REGISTER_STOP originator is * really the RP */ if (check_mrtentry_rp(mrtentry_ptr, reg_src) == FALSE) return FALSE; /* restart the Register-Suppression timer */ SET_TIMER(mrtentry_ptr->rs_timer, (0.5 * PIM_REGISTER_SUPPRESSION_TIMEOUT) + (RANDOM() % (PIM_REGISTER_SUPPRESSION_TIMEOUT + 1))); /* Prune the register_vif from the outgoing list */ VIFM_COPY(mrtentry_ptr->pruned_oifs, pruned_oifs); VIFM_SET(reg_vif_num, pruned_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); return TRUE; } /* TODO: optional rate limiting is not implemented yet */ /* Unicasts a REGISTER_STOP message to the DR */ static int send_pim_register_stop(u_int32 reg_src, u_int32 reg_dst, u_int32 inner_grp, u_int32 inner_src) { char *buf; u_int8 *data_ptr; buf = pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t); data_ptr = (u_int8 *)buf; PUT_EGADDR(inner_grp, SINGLE_GRP_MSKLEN, 0, data_ptr); PUT_EUADDR(inner_src, data_ptr); send_pim_unicast(pim_send_buf, reg_src, reg_dst, PIM_REGISTER_STOP, data_ptr - (u_int8 *)buf); return TRUE; } /************************************************************************ * PIM_JOIN_PRUNE ************************************************************************/ int join_or_prune(mrtentry_t *mrtentry_ptr, pim_nbr_entry_t *upstream_router) { vifbitmap_t entry_oifs; mrtentry_t *mrtentry_grp; if (!mrtentry_ptr || !upstream_router) return PIM_ACTION_NOTHING; calc_oifs(mrtentry_ptr, &entry_oifs); if (mrtentry_ptr->flags & (MRTF_PMBR | MRTF_WC)) { /* (*,*,RP) or (*,G) entry */ /* The (*,*,RP) or (*,G) J/P messages are sent only toward the RP */ if (upstream_router != mrtentry_ptr->upstream) return PIM_ACTION_NOTHING; /* TODO: XXX: Can we have (*,*,RP) prune? */ if (VIFM_ISEMPTY(entry_oifs)) { /* NULL oifs */ if (!(uvifs[mrtentry_ptr->incoming].uv_flags & VIFF_DR)) /* I am not the DR for that subnet. */ return PIM_ACTION_PRUNE; if (VIFM_ISSET(mrtentry_ptr->incoming, mrtentry_ptr->leaves)) /* I am the DR and have local leaves */ return PIM_ACTION_JOIN; /* Probably the last local member hast timeout */ return PIM_ACTION_PRUNE; } return PIM_ACTION_JOIN; } if (mrtentry_ptr->flags & MRTF_SG) { /* (S,G) entry */ /* TODO: check again */ if (mrtentry_ptr->upstream == upstream_router) { if (!(mrtentry_ptr->flags & MRTF_RP)) { /* Upstream router toward S */ if (VIFM_ISEMPTY(entry_oifs)) { if (mrtentry_ptr->group->active_rp_grp && mrtentry_ptr->group->rpaddr == my_cand_rp_address) { /* (S,G) at the RP. Don't send Join/Prune * (see the end of Section 3.3.2) */ return PIM_ACTION_NOTHING; } return PIM_ACTION_PRUNE; } else { return PIM_ACTION_JOIN; } } else { /* Upstream router toward RP */ if (VIFM_ISEMPTY(entry_oifs)) return PIM_ACTION_PRUNE; } } /* Looks like the case when the upstream router toward S is * different from the upstream router toward RP */ if (!mrtentry_ptr->group->active_rp_grp) return PIM_ACTION_NOTHING; mrtentry_grp = mrtentry_ptr->group->grp_route; if (!mrtentry_grp) { mrtentry_grp = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink; if (!mrtentry_grp) return PIM_ACTION_NOTHING; } if (mrtentry_grp->upstream != upstream_router) return PIM_ACTION_NOTHING; /* XXX: shoudn't happen */ if (!(mrtentry_ptr->flags & MRTF_RP) && (mrtentry_ptr->flags & MRTF_SPT)) return PIM_ACTION_PRUNE; } return PIM_ACTION_NOTHING; } /* TODO: when parsing, check if we go beyong message size */ /* TODO: too long, simplify it! */ #define PIM_JOIN_PRUNE_MINLEN (4 + PIM_ENCODE_UNI_ADDR_LEN + 4) int receive_pim_join_prune(u_int32 src, u_int32 dst __attribute__((unused)), char *pim_message, int datalen) { vifi_t vifi; struct uvif *v; pim_encod_uni_addr_t uni_target_addr; pim_encod_grp_addr_t encod_group; pim_encod_src_addr_t encod_src; u_int8 *data_ptr; u_int8 *data_ptr_start; u_int8 *data_ptr_group_end; u_int8 num_groups; u_int8 num_groups_tmp; int star_star_rp_found; u_int16 holdtime; u_int16 num_j_srcs; u_int16 num_j_srcs_tmp; u_int16 num_p_srcs; u_int32 source; u_int32 group; u_int32 s_mask; u_int32 g_mask; u_int8 s_flags; u_int8 reserved __attribute__((unused)); rpentry_t *rpentry_ptr; mrtentry_t *mrtentry_ptr; mrtentry_t *mrtentry_srcs; mrtentry_t *mrtentry_rp; grpentry_t *grpentry_ptr; u_int16 jp_value; pim_nbr_entry_t *upstream_router; int my_action; int ignore_group; rp_grp_entry_t *rp_grp_entry_ptr; u_int8 *data_ptr_group_j_start; u_int8 *data_ptr_group_p_start; if ((vifi = 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) { logit(LOG_INFO, 0, "Ignoring PIM_JOIN_PRUNE from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); } return FALSE; } /* Checksum */ if (inet_cksum((u_int16 *)pim_message, datalen)) return FALSE; v = &uvifs[vifi]; if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER)) return FALSE; /* Shoudn't come on this interface */ /* sanity check for the minimum length */ if (datalen < PIM_JOIN_PRUNE_MINLEN) { logit(LOG_NOTICE, 0, "receive_pim_join_prune: Join/Prune message size(%u) is too short from %s on %s", datalen, inet_fmt(src, s1, sizeof(s1)), v->uv_name); return FALSE; } /* * TODO: Sanity check for the message length through all the groups * (see Kame's pim6sd). */ data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t)); /* Get the target address */ GET_EUADDR(&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 (uni_target_addr.unicast_addr != v->uv_lcl_addr) { /* if I am not the targer of the join message */ /* Join/Prune suppression code. This either modifies the J/P timers * or triggers an overriding Join. */ /* Note that if we have (S,G) prune and (*,G) Join, we must send * them in the same message. We don't bother to modify both timers * here. The Join/Prune sending function will take care of that. */ upstream_router = find_pim_nbr(uni_target_addr.unicast_addr); if (!upstream_router) return FALSE; /* I have no such neighbor */ while (num_groups--) { GET_EGADDR(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); MASKLEN_TO_MASK(encod_group.masklen, g_mask); group = encod_group.mcast_addr; if (!IN_MULTICAST(ntohl(group))) { data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; /* Ignore this group and jump to the next */ } if ((ntohl(group) == CLASSD_PREFIX) && (encod_group.masklen == STAR_STAR_RP_MSKLEN)) { /* (*,*,RP) Join suppression */ while(num_j_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* This is the RP address. */ rpentry_ptr = rp_find(source); if (!rpentry_ptr) continue; /* Don't have such RP. Ignore */ mrtentry_rp = rpentry_ptr->mrtlink; my_action = join_or_prune(mrtentry_rp, upstream_router); if (my_action != PIM_ACTION_JOIN) continue; /* Check the holdtime */ /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_rp->jp_timer > holdtime) continue; if ((mrtentry_rp->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr))) continue; /* Set the Join/Prune suppression timer for this * routing entry by increasing the current * Join/Prune timer. */ jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_rp->jp_timer < jp_value) SET_TIMER(mrtentry_rp->jp_timer, jp_value); } } /* num_j_srcs */ while (num_p_srcs--) { /* TODO: XXX: Can we have (*,*,RP) prune message? * Not in the spec, but anyway, the code below * can handle them: either suppress * the local (*,*,RP) prunes or override the prunes by * sending (*,*,RP) and/or (*,G) and/or (S,G) Join. */ GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* This is the RP address. */ rpentry_ptr = rp_find(source); if (!rpentry_ptr) continue; /* Don't have such RP. Ignore */ mrtentry_rp = rpentry_ptr->mrtlink; my_action = join_or_prune(mrtentry_rp, upstream_router); if (my_action == PIM_ACTION_PRUNE) { /* TODO: XXX: TIMER implem. dependency! */ if ((mrtentry_rp->jp_timer < holdtime) || ((mrtentry_rp->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))) { /* Suppress the Prune */ jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrtentry_rp->jp_timer < jp_value) SET_TIMER(mrtentry_rp->jp_timer, jp_value); } } else if (my_action == PIM_ACTION_JOIN) { /* Override the Prune by scheduling a Join */ jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_rp->jp_timer > jp_value) SET_TIMER(mrtentry_rp->jp_timer, jp_value); } /* Check all (*,G) and (S,G) matching to this RP. * If my_action == JOIN, then send a Join and override * the (*,*,RP) Prune. */ for (grpentry_ptr = rpentry_ptr->cand_rp->rp_grp_next->grplink; grpentry_ptr != (grpentry_t *)NULL; grpentry_ptr = grpentry_ptr->rpnext) { my_action = join_or_prune(grpentry_ptr->grp_route, upstream_router); if (my_action == PIM_ACTION_JOIN) { jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (grpentry_ptr->grp_route->jp_timer > jp_value) SET_TIMER(grpentry_ptr->grp_route->jp_timer, jp_value); } for (mrtentry_srcs = grpentry_ptr->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) { my_action = join_or_prune(mrtentry_srcs, upstream_router); if (my_action == PIM_ACTION_JOIN) { jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_srcs->jp_timer > jp_value) SET_TIMER(mrtentry_srcs->jp_timer, jp_value); } } /* For all (S,G) */ } /* For all (*,G) */ } } /* num_p_srcs */ continue; /* This was (*,*,RP) suppression */ } /* (*,G) or (S,G) suppression */ /* TODO: XXX: currently, accumulated groups * (i.e. group_masklen < group_address_lengt) are not * implemented. Just need to create a loop and apply the * procedure below for all groups matching the prefix. */ while (num_j_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* (*,G) JOIN_REQUEST (toward the RP) */ mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (!mrtentry_ptr) continue; my_action = join_or_prune(mrtentry_ptr, upstream_router); if (my_action != PIM_ACTION_JOIN) continue; /* (*,G) Join suppresion */ if (source != mrtentry_ptr->group->active_rp_grp->rp->rpentry->address) continue; /* The RP address doesn't match. Ignore. */ /* Check the holdtime */ /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->jp_timer > holdtime) continue; if ((mrtentry_ptr->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr))) continue; jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrtentry_ptr->jp_timer < jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); continue; } /* End of (*,G) Join suppression */ /* (S,G) Join suppresion */ mrtentry_ptr = find_route(source, group, MRTF_SG, DONT_CREATE); if (!mrtentry_ptr) continue; my_action = join_or_prune(mrtentry_ptr, upstream_router); if (my_action != PIM_ACTION_JOIN) continue; /* Check the holdtime */ /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->jp_timer > holdtime) continue; if ((mrtentry_ptr->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr))) continue; jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrtentry_ptr->jp_timer < jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); continue; } /* Prunes suppression */ while (num_p_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* (*,G) prune suppression */ rpentry_ptr = rp_match(group); if (!rpentry_ptr || (rpentry_ptr->address != source)) continue; /* No such RP or it is different. Ignore */ mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (!mrtentry_ptr) continue; my_action = join_or_prune(mrtentry_ptr, upstream_router); if (my_action == PIM_ACTION_PRUNE) { /* TODO: XXX: TIMER implem. dependency! */ if ((mrtentry_ptr->jp_timer < holdtime) || ((mrtentry_ptr->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))) { /* Suppress the Prune */ jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrtentry_ptr->jp_timer < jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); } } else if (my_action == PIM_ACTION_JOIN) { /* Override the Prune by scheduling a Join */ jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->jp_timer > jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); } /* Check all (S,G) entries for this group. * If my_action == JOIN, then send the Join and override * the (*,G) Prune. */ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) { my_action = join_or_prune(mrtentry_srcs, upstream_router); if (my_action == PIM_ACTION_JOIN) { jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->jp_timer > jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); } } /* For all (S,G) */ continue; /* End of (*,G) prune suppression */ } /* (S,G) prune suppression */ mrtentry_ptr = find_route(source, group, MRTF_SG, DONT_CREATE); if (mrtentry_ptr == NULL) continue; my_action = join_or_prune(mrtentry_ptr, upstream_router); if (my_action == PIM_ACTION_PRUNE) { /* Suppress the (S,G) Prune */ /* TODO: XXX: TIMER implem. dependency! */ if ((mrtentry_ptr->jp_timer < holdtime) || ((mrtentry_ptr->jp_timer == holdtime) && (ntohl(src) > ntohl(v->uv_lcl_addr)))) { jp_value = PIM_JOIN_PRUNE_PERIOD + 0.5 * (RANDOM() % PIM_JOIN_PRUNE_PERIOD); if (mrtentry_ptr->jp_timer < jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); } } else if (my_action == PIM_ACTION_JOIN) { /* Override the Prune by scheduling a Join */ jp_value = (RANDOM() % (int)(10 * PIM_RANDOM_DELAY_JOIN_TIMEOUT)) / 10; /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->jp_timer > jp_value) SET_TIMER(mrtentry_ptr->jp_timer, jp_value); } } /* while (num_p_srcs--) */ } /* while (num_groups--) */ return TRUE; } /* End of Join/Prune suppression code */ /* I am the target of this join, so process the message */ /* The spec says that if there is (*,G) Join, it has priority over * old existing ~(S,G) prunes in the routing table. * However, if the (*,G) Join and the ~(S,G) prune are in * the same message, ~(S,G) has the priority. The spec doesn't say it, * but I think the same is true for (*,*,RP) and ~(S,G) prunes. * * The code below do: * (1) Check the whole message for (*,*,RP) Joins. * (1.1) If found, clean all pruned_oifs for all (*,G) and all (S,G) * for each RP in the list, but do not update the kernel cache. * Then go back to the beginning of the message and start * processing for each group: * (2) Check for Prunes. If no prunes, process the Joins. * (3) If there are Prunes: * (3.1) Scan the Join part for existing (*,G) Join. * (3.1.1) If there is (*,G) Join, clear join interface from * the pruned_oifs for all (S,G), but DO NOT flush the * change to the kernel (by using change_interfaces() * for example) * (3.2) After the pruned_oifs are eventually cleared in (3.1.1), * process the Prune part of the message normally * (setting the prune_oifs and flashing the changes to the (kernel). * (3.3) After the Prune part is processed, process the Join part * normally (by applying any changes to the kernel) * (4) If there were (*,*,RP) Join/Prune, process them. * * If the Join/Prune list is too long, it may result in long processing * overhead. The idea above is not to place any wrong info in the * kernel, because it may result in short-time existing traffic * forwarding on wrong interface. * Hopefully, in the future will find a better way to implement it. */ num_groups_tmp = num_groups; data_ptr_start = data_ptr; star_star_rp_found = FALSE; /* Indicating whether we have (*,*,RP) join */ while (num_groups_tmp--) { /* Search for (*,*,RP) Join */ GET_EGADDR(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); group = encod_group.mcast_addr; if ((ntohl(group) != CLASSD_PREFIX) || (encod_group.masklen != STAR_STAR_RP_MSKLEN)) { /* This is not (*,*,RP). Jump to the next group. */ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } /* (*,*,RP) found. For each RP and each (*,G) and each (S,G) clear * the pruned oif, but do not update the kernel. */ star_star_rp_found = TRUE; while (num_j_srcs--) { GET_ESADDR(&encod_src, data_ptr); rpentry_ptr = rp_find(encod_src.src_addr); if (rpentry_ptr == (rpentry_t *)NULL) continue; for (rp_grp_entry_ptr = rpentry_ptr->cand_rp->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) VIFM_CLR(vifi, grpentry_ptr->grp_route->pruned_oifs); for (mrtentry_ptr = grpentry_ptr->mrtlink; mrtentry_ptr != (mrtentry_t *)NULL; mrtentry_ptr = mrtentry_ptr->grpnext) VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs); } } } data_ptr += (num_p_srcs) * sizeof(pim_encod_src_addr_t); } /* * Start processing the groups. If this is (*,*,RP), skip it, but process * it at the end. */ data_ptr = data_ptr_start; num_groups_tmp = num_groups; while (num_groups_tmp--) { GET_EGADDR(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); group = encod_group.mcast_addr; if (!IN_MULTICAST(ntohl(group))) { data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; /* Ignore this group and jump to the next one */ } if ((ntohl(group) == CLASSD_PREFIX) && (encod_group.masklen == STAR_STAR_RP_MSKLEN)) { /* This is (*,*,RP). Jump to the next group. */ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } rpentry_ptr = rp_match(group); if (rpentry_ptr == (rpentry_t *)NULL) { data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } data_ptr_group_j_start = data_ptr; data_ptr_group_p_start = data_ptr + num_j_srcs * sizeof(pim_encod_src_addr_t); data_ptr_group_end = data_ptr + (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); /* Scan the Join part for (*,G) Join and then clear the * particular interface from pruned_oifs for all (S,G). * If the RP address in the Join message is different from * the local match, ignore the whole group. */ num_j_srcs_tmp = num_j_srcs; ignore_group = FALSE; while (num_j_srcs_tmp--) { GET_ESADDR(&encod_src, data_ptr); if ((encod_src.flags & USADDR_RP_BIT) && (encod_src.flags & USADDR_WC_BIT)) { /* This is the RP address, i.e. (*,G) Join. * Check if the RP-mapping is consistent and if "yes", * then Reset the pruned_oifs for all (S,G) entries. */ if (rpentry_ptr->address != encod_src.src_addr) { ignore_group = TRUE; break; } mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, DONT_CREATE); if (mrtentry_ptr) { for (mrtentry_srcs = mrtentry_ptr->group->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) VIFM_CLR(vifi, mrtentry_srcs->pruned_oifs); } break; } } if (ignore_group == TRUE) { data_ptr += (num_j_srcs_tmp + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } data_ptr = data_ptr_group_p_start; /* Process the Prune part first */ while (num_p_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) { /* (S,G) prune sent toward S */ mrtentry_ptr = find_route(source, group, MRTF_SG, DONT_CREATE); if (!mrtentry_ptr) continue; /* I don't have (S,G) to prune. Ignore. */ /* If the link is point-to-point, timeout the oif * immediately, otherwise decrease the timer to allow * other downstream routers to override the prune. */ /* TODO: XXX: increase the entry timer? */ if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrtentry_ptr->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] > mrtentry_ptr->vif_deletion_delay[vifi]) SET_TIMER(mrtentry_ptr->vif_timers[vifi], mrtentry_ptr->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[vifi]) { VIFM_CLR(vifi, mrtentry_ptr->joined_oifs); VIFM_SET(vifi, mrtentry_ptr->pruned_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } continue; } if ((s_flags & USADDR_RP_BIT) && (!(s_flags & USADDR_WC_BIT))) { /* ~(S,G)RPbit prune sent toward the RP */ mrtentry_ptr = find_route(source, group, MRTF_SG, DONT_CREATE); if (mrtentry_ptr) { SET_TIMER(mrtentry_ptr->timer, holdtime); if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrtentry_ptr->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] > mrtentry_ptr->vif_deletion_delay[vifi]) SET_TIMER(mrtentry_ptr->vif_timers[vifi], mrtentry_ptr->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[vifi]) { VIFM_CLR(vifi, mrtentry_ptr->joined_oifs); VIFM_SET(vifi, mrtentry_ptr->pruned_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } continue; } /* There is no (S,G) entry. Check for (*,G) or (*,*,RP) */ mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrtentry_ptr) { mrtentry_ptr = find_route(source, group, MRTF_SG | MRTF_RP, CREATE); if (!mrtentry_ptr) continue; mrtentry_ptr->flags &= ~MRTF_NEW; RESET_TIMER(mrtentry_ptr->vif_timers[vifi]); /* TODO: XXX: The spec doens't say what value to use for * the entry time. Use the J/P holdtime. */ SET_TIMER(mrtentry_ptr->timer, holdtime); /* TODO: XXX: The spec says to delete the oif. However, * its timer only should be lowered, so the prune can be * overwritten on multiaccess LAN. Spec BUG. */ VIFM_CLR(vifi, mrtentry_ptr->joined_oifs); VIFM_SET(vifi, mrtentry_ptr->pruned_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } continue; } if ((s_flags & USADDR_RP_BIT) && (s_flags & USADDR_WC_BIT)) { /* (*,G) Prune */ mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrtentry_ptr) { if (mrtentry_ptr->flags & MRTF_WC) { /* TODO: XXX: Should check the whole Prune list in * advance for (*,G) prune and if the RP address * does not match the local RP-map, then ignore the * whole group, not only this particular (*,G) prune. */ if (mrtentry_ptr->group->active_rp_grp->rp->rpentry->address != source) continue; /* The RP address doesn't match. */ if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrtentry_ptr->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] > mrtentry_ptr->vif_deletion_delay[vifi]) SET_TIMER(mrtentry_ptr->vif_timers[vifi], mrtentry_ptr->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[vifi]) { VIFM_CLR(vifi, mrtentry_ptr->joined_oifs); VIFM_SET(vifi, mrtentry_ptr->pruned_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } continue; } /* No (*,G) entry, but found (*,*,RP). Create (*,G) */ if (mrtentry_ptr->source->address != source) continue; /* The RP address doesn't match. */ mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); if (!mrtentry_ptr) continue; mrtentry_ptr->flags &= ~MRTF_NEW; RESET_TIMER(mrtentry_ptr->vif_timers[vifi]); /* TODO: XXX: should only lower the oif timer, so it can * be overwritten on multiaccess LAN. Spec bug. */ VIFM_CLR(vifi, mrtentry_ptr->joined_oifs); VIFM_SET(vifi, mrtentry_ptr->pruned_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } /* (*,G) or (*,*,RP) found */ } /* (*,G) prune */ } /* while(num_p_srcs--) */ /* End of (S,G) and (*,G) Prune handling */ /* Jump back to the Join part and process it */ data_ptr = data_ptr_group_j_start; while (num_j_srcs--) { GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); if ((s_flags & USADDR_WC_BIT) && (s_flags & USADDR_RP_BIT)) { /* (*,G) Join toward RP */ /* It has been checked already that this RP address is * the same as the local RP-maping. */ mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); if (!mrtentry_ptr) continue; VIFM_SET(vifi, mrtentry_ptr->joined_oifs); VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs); VIFM_CLR(vifi, mrtentry_ptr->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] < holdtime) { SET_TIMER(mrtentry_ptr->vif_timers[vifi], holdtime); mrtentry_ptr->vif_deletion_delay[vifi] = holdtime/3; } if (mrtentry_ptr->timer < holdtime) SET_TIMER(mrtentry_ptr->timer, holdtime); mrtentry_ptr->flags &= ~MRTF_NEW; change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); /* Need to update the (S,G) entries, because of the previous * cleaning of the pruned_oifs. The reason is that if the * oifs for (*,G) weren't changed, the (S,G) entries won't * be updated by change_interfaces() */ for (mrtentry_srcs = mrtentry_ptr->group->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming, mrtentry_srcs->joined_oifs, mrtentry_srcs->pruned_oifs, mrtentry_srcs->leaves, mrtentry_srcs->asserted_oifs, 0); continue; } if (!(s_flags & (USADDR_WC_BIT | USADDR_RP_BIT))) { /* (S,G) Join toward S */ if (vifi == get_iif(source)) continue; /* Ignore this (S,G) Join */ mrtentry_ptr = find_route(source, group, MRTF_SG, CREATE); if (!mrtentry_ptr) continue; VIFM_SET(vifi, mrtentry_ptr->joined_oifs); VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs); VIFM_CLR(vifi, mrtentry_ptr->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] < holdtime) { SET_TIMER(mrtentry_ptr->vif_timers[vifi], holdtime); mrtentry_ptr->vif_deletion_delay[vifi] = holdtime/3; } if (mrtentry_ptr->timer < holdtime) SET_TIMER(mrtentry_ptr->timer, holdtime); /* TODO: if this is a new entry, send immediately the * Join message toward S. The Join/Prune timer for new * entries is 0, but it does not means the message will * be sent immediately. */ mrtentry_ptr->flags &= ~MRTF_NEW; /* Note that we must create (S,G) without the RPbit set. * If we already had such entry, change_interfaces() will * reset the RPbit propertly. */ change_interfaces(mrtentry_ptr, mrtentry_ptr->source->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); continue; } } /* while(num_j_srcs--) */ data_ptr = data_ptr_group_end; } /* for all groups */ /* Now process the (*,*,RP) Join/Prune */ if (star_star_rp_found != TRUE) return TRUE; data_ptr = data_ptr_start; while (num_groups--) { /* The conservative approach is to scan again the whole message, * just in case if we have more than one (*,*,RP) requests. */ GET_EGADDR(&encod_group, data_ptr); GET_HOSTSHORT(num_j_srcs, data_ptr); GET_HOSTSHORT(num_p_srcs, data_ptr); group = encod_group.mcast_addr; if ((ntohl(group) != CLASSD_PREFIX) || (encod_group.masklen != STAR_STAR_RP_MSKLEN)) { /* This is not (*,*,RP). Jump to the next group. */ data_ptr += (num_j_srcs + num_p_srcs) * sizeof(pim_encod_src_addr_t); continue; } /* (*,*,RP) found */ while (num_j_srcs--) { /* TODO: XXX: check that the iif is different from the Join oifs */ GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); mrtentry_ptr = find_route(source, INADDR_ANY_N, MRTF_PMBR, CREATE); if (mrtentry_ptr == (mrtentry_t *)NULL) continue; VIFM_SET(vifi, mrtentry_ptr->joined_oifs); VIFM_CLR(vifi, mrtentry_ptr->pruned_oifs); VIFM_CLR(vifi, mrtentry_ptr->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] < holdtime) { SET_TIMER(mrtentry_ptr->vif_timers[vifi], holdtime); mrtentry_ptr->vif_deletion_delay[vifi] = holdtime/3; } if (mrtentry_ptr->timer < holdtime) SET_TIMER(mrtentry_ptr->timer, holdtime); mrtentry_ptr->flags &= ~MRTF_NEW; change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); /* Need to update the (S,G) and (*,G) entries, because of * the previous cleaning of the pruned_oifs. The reason is * that if the oifs for (*,*,RP) weren't changed, the * (*,G) and (S,G) entries won't be updated by change_interfaces() */ for (rp_grp_entry_ptr = mrtentry_ptr->source->cand_rp->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) { /* Update the (*,G) entry */ if (grpentry_ptr->grp_route != NULL) { change_interfaces(grpentry_ptr->grp_route, grpentry_ptr->grp_route->incoming, grpentry_ptr->grp_route->joined_oifs, grpentry_ptr->grp_route->pruned_oifs, grpentry_ptr->grp_route->leaves, grpentry_ptr->grp_route->asserted_oifs, 0); } /* Update the (S,G) entries */ for (mrtentry_srcs = grpentry_ptr->mrtlink; mrtentry_srcs != (mrtentry_t *)NULL; mrtentry_srcs = mrtentry_srcs->grpnext) change_interfaces(mrtentry_srcs, mrtentry_srcs->incoming, mrtentry_srcs->joined_oifs, mrtentry_srcs->pruned_oifs, mrtentry_srcs->leaves, mrtentry_srcs->asserted_oifs, 0); } } continue; } while (num_p_srcs--) { /* TODO: XXX: can we have (*,*,RP) Prune? */ GET_ESADDR(&encod_src, data_ptr); source = encod_src.src_addr; if (!inet_valid_host(source)) continue; s_flags = encod_src.flags; MASKLEN_TO_MASK(encod_src.masklen, s_mask); mrtentry_ptr = find_route(source, INADDR_ANY_N, MRTF_PMBR, DONT_CREATE); if (mrtentry_ptr == (mrtentry_t *)NULL) continue; /* If the link is point-to-point, timeout the oif * immediately, otherwise decrease the timer to allow * other downstream routers to override the prune. */ /* TODO: XXX: increase the entry timer? */ if (v->uv_flags & VIFF_POINT_TO_POINT) { FIRE_TIMER(mrtentry_ptr->vif_timers[vifi]); } else { /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->vif_timers[vifi] > mrtentry_ptr->vif_deletion_delay[vifi]) SET_TIMER(mrtentry_ptr->vif_timers[vifi], mrtentry_ptr->vif_deletion_delay[vifi]); } IF_TIMER_NOT_SET(mrtentry_ptr->vif_timers[vifi]) { VIFM_CLR(vifi, mrtentry_ptr->joined_oifs); VIFM_SET(vifi, mrtentry_ptr->pruned_oifs); VIFM_SET(vifi, mrtentry_ptr->asserted_oifs); change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); } } } /* For all groups processing (*,*,R) */ return TRUE; } /* * TODO: NOT USED, probably buggy, but may need it in the future. */ /* * TODO: create two functions: periodic which timeout the timers * and non-periodic which only check but don't timeout the timers. */ /* * Create and send Join/Prune messages per interface. * Only the entries which have the Join/Prune timer expired are included. * In the special case when we have ~(S,G)RPbit Prune entry, we must * include any (*,G) or (*,*,RP) * Currently the whole table is scanned. In the future will have all * routing entries linked in a chain with the corresponding upstream * pim_nbr_entry. * * If pim_nbr is not NULL, then send to only this particular PIM neighbor, */ int send_periodic_pim_join_prune(vifi_t vifi, pim_nbr_entry_t *pim_nbr, u_int16 holdtime) { grpentry_t *grpentry_ptr; mrtentry_t *mrtentry_ptr; rpentry_t *rpentry_ptr; u_int32 src_addr; struct uvif *v; pim_nbr_entry_t *pim_nbr_ptr; cand_rp_t *cand_rp_ptr; /* Walk through all routing entries. The iif must match to include the * entry. Check first the (*,G) entry and then all associated (S,G). * At the end of the message will add any (*,*,RP) entries. * TODO: check other PIM-SM implementations and decide the more * appropriate place to put the (*,*,RP) entries: in the beginning of the * message or at the end. */ v = &uvifs[vifi]; /* Check the (*,G) and (S,G) entries */ for(grpentry_ptr = grplist; grpentry_ptr; grpentry_ptr = grpentry_ptr->next) { mrtentry_ptr = grpentry_ptr->grp_route; /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr && (mrtentry_ptr->incoming == vifi) && (mrtentry_ptr->jp_timer <= TIMER_INTERVAL)) { /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && mrtentry_ptr->upstream != pim_nbr) continue; /* TODO: XXX: The J/P suppression timer is not in the spec! */ if (!VIFM_ISEMPTY(mrtentry_ptr->joined_oifs) || (v->uv_flags & VIFF_DR)) { add_jp_entry(mrtentry_ptr->upstream, holdtime, grpentry_ptr->group, SINGLE_GRP_MSKLEN, grpentry_ptr->rpaddr, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN); } /* TODO: XXX: TIMER implem. dependency! */ if (VIFM_ISEMPTY(mrtentry_ptr->joined_oifs) && (!(v->uv_flags & VIFF_DR)) && (mrtentry_ptr->jp_timer <= TIMER_INTERVAL)) { add_jp_entry(mrtentry_ptr->upstream, holdtime, grpentry_ptr->group, SINGLE_GRP_MSKLEN, grpentry_ptr->rpaddr, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE); } } /* Check the (S,G) entries */ for (mrtentry_ptr = grpentry_ptr->mrtlink; mrtentry_ptr; mrtentry_ptr = mrtentry_ptr->grpnext) { /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && mrtentry_ptr->upstream != pim_nbr) continue; if (mrtentry_ptr->flags & MRTF_RP) { /* RPbit set */ src_addr = mrtentry_ptr->source->address; if (VIFM_ISEMPTY(mrtentry_ptr->joined_oifs) || ((find_vif_direct_local(src_addr) != NO_VIF) && grpentry_ptr->grp_route)) /* TODO: XXX: TIMER implem. dependency! */ if ((grpentry_ptr->grp_route->incoming == vifi) && (grpentry_ptr->grp_route->jp_timer <= TIMER_INTERVAL)) /* S is directly connected. Send toward RP */ add_jp_entry(grpentry_ptr->grp_route->upstream, holdtime, grpentry_ptr->group, SINGLE_GRP_MSKLEN, src_addr, SINGLE_SRC_MSKLEN, MRTF_RP, PIM_ACTION_PRUNE); } else { /* RPbit cleared */ if (VIFM_ISEMPTY(mrtentry_ptr->joined_oifs)) { /* TODO: XXX: TIMER implem. dependency! */ if ((mrtentry_ptr->incoming == vifi) && (mrtentry_ptr->jp_timer <= TIMER_INTERVAL)) add_jp_entry(mrtentry_ptr->upstream, holdtime, grpentry_ptr->group, SINGLE_GRP_MSKLEN, mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_PRUNE); } else { /* TODO: XXX: TIMER implem. dependency! */ if ((mrtentry_ptr->incoming == vifi) && (mrtentry_ptr->jp_timer <= TIMER_INTERVAL)) add_jp_entry(mrtentry_ptr->upstream, holdtime, grpentry_ptr->group, SINGLE_GRP_MSKLEN, mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN, 0, PIM_ACTION_JOIN); } /* TODO: XXX: TIMER implem. dependency! */ if ((mrtentry_ptr->flags & MRTF_SPT) && grpentry_ptr->grp_route && (mrtentry_ptr->incoming != grpentry_ptr->grp_route->incoming) && (grpentry_ptr->grp_route->incoming == vifi) && (grpentry_ptr->grp_route->jp_timer <= TIMER_INTERVAL)) add_jp_entry(grpentry_ptr->grp_route->upstream, holdtime, grpentry_ptr->group, SINGLE_GRP_MSKLEN, mrtentry_ptr->source->address, SINGLE_SRC_MSKLEN, MRTF_RP, PIM_ACTION_PRUNE); } } } /* Check the (*,*,RP) entries */ for (cand_rp_ptr = cand_rp_list; cand_rp_ptr; cand_rp_ptr = cand_rp_ptr->next) { rpentry_ptr = cand_rp_ptr->rpentry; /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && rpentry_ptr->upstream != pim_nbr) continue; /* TODO: XXX: TIMER implem. dependency! */ if (rpentry_ptr->mrtlink && (rpentry_ptr->incoming == vifi) && (rpentry_ptr->mrtlink->jp_timer <= TIMER_INTERVAL)) { add_jp_entry(rpentry_ptr->upstream, holdtime, htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, rpentry_ptr->address, SINGLE_SRC_MSKLEN, MRTF_RP | MRTF_WC, PIM_ACTION_JOIN); } } /* Send all pending Join/Prune messages */ for (pim_nbr_ptr = v->uv_pim_neighbors; pim_nbr_ptr; pim_nbr_ptr = pim_nbr->next) { /* If join/prune to a particular neighbor only was specified */ if (pim_nbr && (pim_nbr_ptr != pim_nbr)) continue; pack_and_send_jp_message(pim_nbr_ptr); } return TRUE; } int add_jp_entry(pim_nbr_entry_t *pim_nbr, u_int16 holdtime, u_int32 group, u_int8 grp_msklen, u_int32 source, u_int8 src_msklen, u_int16 addr_flags, u_int8 join_prune) { build_jp_message_t *bjpm; u_int8 *data_ptr; u_int8 flags = 0; int rp_flag; bjpm = pim_nbr->build_jp_message; if (bjpm) { if ((bjpm->jp_message_size + bjpm->join_list_size + bjpm->prune_list_size + bjpm->rp_list_join_size + bjpm->rp_list_prune_size >= MAX_JP_MESSAGE_SIZE) || (bjpm->join_list_size >= MAX_JOIN_LIST_SIZE) || (bjpm->prune_list_size >= MAX_PRUNE_LIST_SIZE) || (bjpm->rp_list_join_size >= MAX_JOIN_LIST_SIZE) || (bjpm->rp_list_prune_size >= MAX_PRUNE_LIST_SIZE)) { /* TODO: XXX: BUG: If the list is getting too large, must * be careful with the fragmentation. */ pack_and_send_jp_message(pim_nbr); bjpm = pim_nbr->build_jp_message; /* The buffer will be freed */ } } if (bjpm) { if ((bjpm->curr_group != group) || (bjpm->curr_group_msklen != grp_msklen) || (bjpm->holdtime != holdtime)) { pack_jp_message(pim_nbr); } } if (!bjpm) { bjpm = get_jp_working_buff(); if (!bjpm) { logit(LOG_ERR, 0, "Failed allocating working buffer in add_jp_entry()\n"); exit (-1); } pim_nbr->build_jp_message = bjpm; data_ptr = bjpm->jp_message; PUT_EUADDR(pim_nbr->address, data_ptr); PUT_BYTE(0, data_ptr); /* Reserved */ bjpm->num_groups_ptr = data_ptr++; /* The pointer for numgroups */ *(bjpm->num_groups_ptr) = 0; /* Zero groups */ PUT_HOSTSHORT(holdtime, data_ptr); bjpm->holdtime = holdtime; bjpm->jp_message_size = data_ptr - bjpm->jp_message; } /* TODO: move somewhere else, only when it is a new group */ bjpm->curr_group = group; bjpm->curr_group_msklen = grp_msklen; if (group == htonl(CLASSD_PREFIX) && grp_msklen == STAR_STAR_RP_MSKLEN) rp_flag = TRUE; else rp_flag = FALSE; switch (join_prune) { case PIM_ACTION_JOIN: if (rp_flag == TRUE) data_ptr = bjpm->rp_list_join + bjpm->rp_list_join_size; else data_ptr = bjpm->join_list + bjpm->join_list_size; break; case PIM_ACTION_PRUNE: if (rp_flag == TRUE) data_ptr = bjpm->rp_list_prune + bjpm->rp_list_prune_size; else data_ptr = bjpm->prune_list + bjpm->prune_list_size; break; default: return FALSE; } flags |= USADDR_S_BIT; /* Mandatory for PIMv2 */ if (addr_flags & MRTF_RP) flags |= USADDR_RP_BIT; if (addr_flags & MRTF_WC) flags |= USADDR_WC_BIT; PUT_ESADDR(source, src_msklen, flags, data_ptr); switch (join_prune) { case PIM_ACTION_JOIN: if (rp_flag == TRUE) { bjpm->rp_list_join_size = data_ptr - bjpm->rp_list_join; bjpm->rp_list_join_number++; } else { bjpm->join_list_size = data_ptr - bjpm->join_list; bjpm->join_addr_number++; } break; case PIM_ACTION_PRUNE: if (rp_flag == TRUE) { bjpm->rp_list_prune_size = data_ptr - bjpm->rp_list_prune; bjpm->rp_list_prune_number++; } else { bjpm->prune_list_size = data_ptr - bjpm->prune_list; bjpm->prune_addr_number++; } break; default: return FALSE; } return TRUE; } /* TODO: check again the size of the buffers */ static build_jp_message_t *get_jp_working_buff(void) { build_jp_message_t *bjpm_ptr; if (build_jp_message_pool_counter == 0) { bjpm_ptr = (build_jp_message_t *)calloc(1, sizeof(build_jp_message_t)); if (!bjpm_ptr) return NULL; bjpm_ptr->next = (build_jp_message_t *)NULL; bjpm_ptr->jp_message = (u_int8 *)calloc(1, MAX_JP_MESSAGE_SIZE + sizeof(pim_jp_encod_grp_t) + 2 * sizeof(pim_encod_src_addr_t)); if (!bjpm_ptr->jp_message) { free(bjpm_ptr); return NULL; } bjpm_ptr->jp_message_size = 0; bjpm_ptr->join_list_size = 0; bjpm_ptr->join_addr_number = 0; bjpm_ptr->join_list = (u_int8 *)calloc(1, MAX_JOIN_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm_ptr->join_list) { free(bjpm_ptr->jp_message); free(bjpm_ptr); return NULL; } bjpm_ptr->prune_list_size = 0; bjpm_ptr->prune_addr_number = 0; bjpm_ptr->prune_list = (u_int8 *)calloc(1, MAX_PRUNE_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm_ptr->prune_list) { free(bjpm_ptr->join_list); free(bjpm_ptr->jp_message); free(bjpm_ptr); return NULL; } bjpm_ptr->rp_list_join_size = 0; bjpm_ptr->rp_list_join_number = 0; bjpm_ptr->rp_list_join = (u_int8 *)calloc(1, MAX_JOIN_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm_ptr->rp_list_join) { free(bjpm_ptr->prune_list); free(bjpm_ptr->join_list); free(bjpm_ptr->jp_message); free(bjpm_ptr); return NULL; } bjpm_ptr->rp_list_prune_size = 0; bjpm_ptr->rp_list_prune_number = 0; bjpm_ptr->rp_list_prune = (u_int8 *)calloc(1, MAX_PRUNE_LIST_SIZE + sizeof(pim_encod_src_addr_t)); if (!bjpm_ptr->rp_list_prune) { free(bjpm_ptr->rp_list_join); free(bjpm_ptr->prune_list); free(bjpm_ptr->join_list); free(bjpm_ptr->jp_message); free(bjpm_ptr); return NULL; } bjpm_ptr->curr_group = INADDR_ANY_N; bjpm_ptr->curr_group_msklen = 0; bjpm_ptr->holdtime = 0; return bjpm_ptr; } bjpm_ptr = build_jp_message_pool; build_jp_message_pool = build_jp_message_pool->next; build_jp_message_pool_counter--; bjpm_ptr->jp_message_size = 0; bjpm_ptr->join_list_size = 0; bjpm_ptr->join_addr_number = 0; bjpm_ptr->prune_list_size = 0; bjpm_ptr->prune_addr_number = 0; bjpm_ptr->curr_group = INADDR_ANY_N; bjpm_ptr->curr_group_msklen = 0; return bjpm_ptr; } static void return_jp_working_buff(pim_nbr_entry_t *pim_nbr) { build_jp_message_t *bjpm_ptr = pim_nbr->build_jp_message; if (!bjpm_ptr) return; /* Don't waste memory by keeping too many free buffers */ /* TODO: check/modify the definitions for POOL_NUMBER and size */ if (build_jp_message_pool_counter >= MAX_JP_MESSAGE_POOL_NUMBER) { free((void *)bjpm_ptr->jp_message); free((void *)bjpm_ptr->join_list); free((void *)bjpm_ptr->prune_list); free((void *)bjpm_ptr->rp_list_join); free((void *)bjpm_ptr->rp_list_prune); free((void *)bjpm_ptr); } else { bjpm_ptr->next = build_jp_message_pool; build_jp_message_pool = bjpm_ptr; build_jp_message_pool_counter++; } pim_nbr->build_jp_message = (build_jp_message_t *)NULL; } /* TODO: XXX: Currently, the (*,*,RP) stuff goes at the end of the * Join/Prune message. However, this particular implementation of PIM * processes the Join/Prune messages faster if (*,*,RP) is at the beginning. * Modify some of the functions below such that the * outgoing messages place (*,*,RP) at the beginning, not at the end. */ static void pack_jp_message(pim_nbr_entry_t *pim_nbr) { build_jp_message_t *bjpm; u_int8 *data_ptr; bjpm = pim_nbr->build_jp_message; if (!bjpm || (bjpm->curr_group == INADDR_ANY_N)) return; data_ptr = bjpm->jp_message + bjpm->jp_message_size; PUT_EGADDR(bjpm->curr_group, bjpm->curr_group_msklen, 0, data_ptr); PUT_HOSTSHORT(bjpm->join_addr_number, data_ptr); PUT_HOSTSHORT(bjpm->prune_addr_number, data_ptr); memcpy(data_ptr, bjpm->join_list, bjpm->join_list_size); data_ptr += bjpm->join_list_size; memcpy(data_ptr, bjpm->prune_list, bjpm->prune_list_size); data_ptr += bjpm->prune_list_size; bjpm->jp_message_size = (data_ptr - bjpm->jp_message); bjpm->curr_group = INADDR_ANY_N; bjpm->curr_group_msklen = 0; bjpm->join_list_size = 0; bjpm->join_addr_number = 0; bjpm->prune_list_size = 0; bjpm->prune_addr_number = 0; (*bjpm->num_groups_ptr)++; if (*bjpm->num_groups_ptr == ((u_int8)~0 - 1)) { if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number) { /* Add the (*,*,RP) at the end */ data_ptr = bjpm->jp_message + bjpm->jp_message_size; PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data_ptr); PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr); PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr); memcpy(data_ptr, bjpm->rp_list_join, bjpm->rp_list_join_size); data_ptr += bjpm->rp_list_join_size; memcpy(data_ptr, bjpm->rp_list_prune, bjpm->rp_list_prune_size); data_ptr += bjpm->rp_list_prune_size; bjpm->jp_message_size = (data_ptr - bjpm->jp_message); bjpm->rp_list_join_size = 0; bjpm->rp_list_join_number = 0; bjpm->rp_list_prune_size = 0; bjpm->rp_list_prune_number = 0; (*bjpm->num_groups_ptr)++; } send_jp_message(pim_nbr); } } void pack_and_send_jp_message(pim_nbr_entry_t *pim_nbr) { u_int8 *data_ptr; build_jp_message_t *bjpm; if (!pim_nbr || !pim_nbr->build_jp_message) return; pack_jp_message(pim_nbr); bjpm = pim_nbr->build_jp_message; if (bjpm->rp_list_join_number + bjpm->rp_list_prune_number) { /* Add the (*,*,RP) at the end */ data_ptr = bjpm->jp_message + bjpm->jp_message_size; PUT_EGADDR(htonl(CLASSD_PREFIX), STAR_STAR_RP_MSKLEN, 0, data_ptr); PUT_HOSTSHORT(bjpm->rp_list_join_number, data_ptr); PUT_HOSTSHORT(bjpm->rp_list_prune_number, data_ptr); memcpy(data_ptr, bjpm->rp_list_join, bjpm->rp_list_join_size); data_ptr += bjpm->rp_list_join_size; memcpy(data_ptr, bjpm->rp_list_prune, bjpm->rp_list_prune_size); data_ptr += bjpm->rp_list_prune_size; bjpm->jp_message_size = (data_ptr - bjpm->jp_message); bjpm->rp_list_join_size = 0; bjpm->rp_list_join_number = 0; bjpm->rp_list_prune_size = 0; bjpm->rp_list_prune_number = 0; (*bjpm->num_groups_ptr)++; } send_jp_message(pim_nbr); } static void send_jp_message(pim_nbr_entry_t *pim_nbr) { u_int16 datalen; vifi_t vifi; datalen = pim_nbr->build_jp_message->jp_message_size; vifi = pim_nbr->vifi; memcpy(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t), pim_nbr->build_jp_message->jp_message, datalen); send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_JOIN_PRUNE, datalen); return_jp_working_buff(pim_nbr); } /************************************************************************ * PIM_ASSERT ************************************************************************/ int receive_pim_assert(u_int32 src, u_int32 dst __attribute__((unused)), char *pim_message, int datalen) { vifi_t vifi; pim_encod_uni_addr_t eusaddr; pim_encod_grp_addr_t egaddr; u_int32 source, group; mrtentry_t *mrtentry_ptr, *mrtentry_ptr2; u_int8 *data_ptr; struct uvif *v; u_int32 assert_preference; u_int32 assert_metric; u_int32 assert_rptbit; u_int32 local_metric; u_int32 local_preference; u_int8 local_rptbit; u_int8 local_wins; pim_nbr_entry_t *original_upstream_router; vifi = find_vif_direct(src); if (vifi == NO_VIF) { /* Either a local vif or somehow received PIM_ASSERT from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) logit(LOG_INFO, 0, "Ignoring PIM_ASSERT from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); return FALSE; } /* Checksum */ if (inet_cksum((u_int16 *)pim_message, datalen)) return FALSE; v = &uvifs[vifi]; if (uvifs[vifi].uv_flags & (VIFF_DOWN | VIFF_DISABLED | VIFF_NONBRS | VIFF_REGISTER)) return FALSE; /* Shoudn't come on this interface */ data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t)); /* Get the group and source addresses */ GET_EGADDR(&egaddr, data_ptr); GET_EUADDR(&eusaddr, data_ptr); /* Get the metric related info */ GET_HOSTLONG(assert_preference, data_ptr); GET_HOSTLONG(assert_metric, data_ptr); assert_rptbit = assert_preference & PIM_ASSERT_RPT_BIT; source = eusaddr.unicast_addr; group = egaddr.mcast_addr; /* Find the longest "active" entry, i.e. the one with a kernel mirror */ if (assert_rptbit) { mrtentry_ptr = find_route(INADDR_ANY_N, group, MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrtentry_ptr) if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE)) if (mrtentry_ptr->flags & MRTF_WC) { mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink; } } else { mrtentry_ptr = find_route(source, group, MRTF_SG | MRTF_WC | MRTF_PMBR, DONT_CREATE); if (mrtentry_ptr) if (!(mrtentry_ptr->flags & MRTF_KERNEL_CACHE)) { if (mrtentry_ptr->flags & MRTF_SG) { mrtentry_ptr2 = mrtentry_ptr->group->grp_route; if (mrtentry_ptr2 && (mrtentry_ptr2->flags & MRTF_KERNEL_CACHE)) mrtentry_ptr = mrtentry_ptr2; else mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink; } else { if (mrtentry_ptr->flags & MRTF_WC) mrtentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry->mrtlink; } } } if (!mrtentry_ptr || !(mrtentry_ptr->flags & MRTF_KERNEL_CACHE)) { /* No routing entry or not "active" entry. Ignore the assert */ return FALSE; } /* Prepare the local preference and metric */ if ((mrtentry_ptr->flags & MRTF_PMBR) || ((mrtentry_ptr->flags & MRTF_SG) && !(mrtentry_ptr->flags & MRTF_RP))) { /* Either (S,G) (toward S) or (*,*,RP). */ /* TODO: XXX: get the info from mrtentry, or source or from kernel ? */ /* local_metric = mrtentry_ptr->source->metric; local_preference = mrtentry_ptr->source->preference; */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; } else { /* Should be (*,G) or (S,G)RPbit entry. * Get what we need from the RP info. */ /* TODO: get the info from mrtentry, RP-entry or kernel? */ /* local_metric = mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric; local_preference = mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference; */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; } local_rptbit = (mrtentry_ptr->flags & MRTF_RP); if (local_rptbit) { /* Make the RPT bit the most significant one */ local_preference |= PIM_ASSERT_RPT_BIT; } if (VIFM_ISSET(vifi, mrtentry_ptr->oifs)) { /* The ASSERT has arrived on oif */ /* TODO: XXX: here the processing order is different from the spec. * The spec requires first eventually to create a routing entry * (see 3.5.2.1(1) and then compare the metrics. Here we compare * first the metrics with the existing longest match entry and * if we lose then create a new entry and compare again. This saves * us the unnecessary creating of a routing entry if we anyway are * going to lose: for example the local (*,*,RP) vs the remote * (*,*,RP) or (*,G) */ local_wins = compare_metrics(local_preference, local_metric, v->uv_lcl_addr, assert_preference, assert_metric, src); if (local_wins == TRUE) { /* TODO: verify the parameters */ send_pim_assert(source, group, vifi, mrtentry_ptr); return TRUE; } /* Create a "better" routing entry and try again */ if (assert_rptbit && (mrtentry_ptr->flags & MRTF_PMBR)) { /* The matching entry was (*,*,RP). Create (*,G) */ mrtentry_ptr2 = find_route(INADDR_ANY_N, group, MRTF_WC, CREATE); } else if (!assert_rptbit && (mrtentry_ptr->flags & (MRTF_WC | MRTF_PMBR))) { /* create (S,G) */ mrtentry_ptr2 = find_route(source, group, MRTF_SG, CREATE); } else { /* We have no chance to win. Give up and prune the oif */ mrtentry_ptr2 = (mrtentry_t *)NULL; } if (mrtentry_ptr2) { mrtentry_ptr2->flags &= ~MRTF_NEW; /* TODO: XXX: The spec doesn't say what entry timer value * to use when the routing entry is created because of asserts. */ SET_TIMER(mrtentry_ptr2->timer, PIM_DATA_TIMEOUT); if (mrtentry_ptr2->flags & MRTF_RP) { /* Either (*,G) or (S,G)RPbit entry. * Get what we need from the RP info. */ /* TODO: where to get the metric+preference from? */ /* local_metric = mrtentry_ptr->group->active_rp_grp->rp->rpentry->metric; local_preference = mrtentry_ptr->group->active_rp_grp->rp->rpentry->preference; */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; local_preference |= PIM_ASSERT_RPT_BIT; } else { /* (S,G) toward the source */ /* TODO: where to get the metric from ? */ /* local_metric = mrtentry_ptr->source->metric; local_preference = mrtentry_ptr->source->preference; */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; } local_wins = compare_metrics(local_preference, local_metric, v->uv_lcl_addr, assert_preference, assert_metric, src); if (local_wins == TRUE) { /* TODO: verify the parameters */ send_pim_assert(source, group, vifi, mrtentry_ptr); return TRUE; } /* We lost, but have created the entry which has to be pruned */ mrtentry_ptr = mrtentry_ptr2; } /* Have to remove that outgoing vifi from mrtentry_ptr */ VIFM_SET(vifi, mrtentry_ptr->asserted_oifs); /* TODO: XXX: TIMER implem. dependency! */ if (mrtentry_ptr->timer < PIM_ASSERT_TIMEOUT) SET_TIMER(mrtentry_ptr->timer, PIM_ASSERT_TIMEOUT); /* TODO: XXX: check that the timer of all affected routing entries * has been restarted. */ change_interfaces(mrtentry_ptr, mrtentry_ptr->incoming, mrtentry_ptr->joined_oifs, mrtentry_ptr->pruned_oifs, mrtentry_ptr->leaves, mrtentry_ptr->asserted_oifs, 0); return FALSE; /* Doesn't matter the return value */ } /* End of assert received on oif */ if (mrtentry_ptr->incoming == vifi) { /* Assert received on iif */ if (assert_rptbit) { if (!(mrtentry_ptr->flags & MRTF_RP)) return TRUE; /* The locally used upstream router will * win the assert, so don't change it. */ } /* TODO: where to get the local metric and preference from? * system call or mrtentry is fine? */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; if (mrtentry_ptr->flags & MRTF_RP) local_preference |= PIM_ASSERT_RPT_BIT; local_wins = compare_metrics(local_preference, local_metric, mrtentry_ptr->upstream->address, assert_preference, assert_metric, src); if (local_wins == TRUE) return TRUE; /* return whatever */ /* The upstream must be changed to the winner */ mrtentry_ptr->preference = assert_preference; mrtentry_ptr->metric = assert_metric; mrtentry_ptr->upstream = find_pim_nbr(src); /* Check if the upstream router is different from the original one */ if (mrtentry_ptr->flags & MRTF_PMBR) original_upstream_router = mrtentry_ptr->source->upstream; else if (mrtentry_ptr->flags & MRTF_RP) original_upstream_router = mrtentry_ptr->group->active_rp_grp->rp->rpentry->upstream; else original_upstream_router = mrtentry_ptr->source->upstream; if (mrtentry_ptr->upstream != original_upstream_router) { mrtentry_ptr->flags |= MRTF_ASSERTED; SET_TIMER(mrtentry_ptr->assert_timer, PIM_ASSERT_TIMEOUT); } else { mrtentry_ptr->flags &= ~MRTF_ASSERTED; } } return TRUE; } int send_pim_assert(u_int32 source, u_int32 group, vifi_t vifi, mrtentry_t *mrtentry_ptr) { u_int8 *data_ptr; u_int8 *data_start_ptr; u_int32 local_preference; u_int32 local_metric; srcentry_t *srcentry_ptr __attribute__((unused)); /* Don't send assert if the outgoing interface a tunnel or register vif */ /* TODO: XXX: in the code above asserts are accepted over VIFF_TUNNEL. * Check if anything can go wrong if asserts are accepted and/or * sent over VIFF_TUNNEL. */ if (uvifs[vifi].uv_flags & (VIFF_REGISTER | VIFF_TUNNEL)) return FALSE; data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t)); data_start_ptr = data_ptr; PUT_EGADDR(group, SINGLE_GRP_MSKLEN, 0, data_ptr); PUT_EUADDR(source, data_ptr); /* TODO: XXX: where to get the metric from: srcentry_ptr or mrtentry_ptr * or from the kernel? */ if (mrtentry_ptr->flags & MRTF_PMBR) { /* (*,*,RP) */ srcentry_ptr = mrtentry_ptr->source; /* TODO: set_incoming(srcentry_ptr, PIM_IIF_RP); */ } else if (mrtentry_ptr->flags & MRTF_RP) { /* (*,G) or (S,G)RPbit (iif toward RP) */ srcentry_ptr = mrtentry_ptr->group->active_rp_grp->rp->rpentry; /* TODO: set_incoming(srcentry_ptr, PIM_IIF_RP); */ } else { /* (S,G) toward S */ srcentry_ptr = mrtentry_ptr->source; /* TODO: set_incoming(srcentry_ptr, PIM_IIF_SOURCE); */ } /* TODO: check again! local_metric = srcentry_ptr->metric; local_preference = srcentry_ptr->preference; */ local_metric = mrtentry_ptr->metric; local_preference = mrtentry_ptr->preference; if (mrtentry_ptr->flags & MRTF_RP) local_preference |= PIM_ASSERT_RPT_BIT; PUT_HOSTLONG(local_preference, data_ptr); PUT_HOSTLONG(local_metric, data_ptr); send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_ASSERT, data_ptr - data_start_ptr); return TRUE; } /* Return TRUE if the local win, otherwise FALSE */ static int compare_metrics(u_int32 local_preference, u_int32 local_metric, u_int32 local_address, u_int32 remote_preference, u_int32 remote_metric, u_int32 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 * threatment 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 (ntohl(local_address) > ntohl(remote_address)) return TRUE; return FALSE; } /************************************************************************ * PIM_BOOTSTRAP ************************************************************************/ #define PIM_BOOTSTRAP_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN) int receive_pim_bootstrap(u_int32 src, u_int32 dst, char *pim_message, int datalen) { u_int8 *data_ptr; u_int8 *max_data_ptr; u_int16 new_bsr_fragment_tag; u_int8 new_bsr_hash_masklen; u_int8 new_bsr_priority; pim_encod_uni_addr_t new_bsr_uni_addr; u_int32 new_bsr_address; struct rpfctl rpfc; pim_nbr_entry_t *n, *rpf_neighbor __attribute__((unused)); u_int32 neighbor_addr; vifi_t vifi, incoming = NO_VIF; int min_datalen; pim_encod_grp_addr_t curr_group_addr; pim_encod_uni_addr_t curr_rp_addr; u_int8 curr_rp_count; u_int8 curr_frag_rp_count; u_int16 reserved_short __attribute__((unused)); u_int16 curr_rp_holdtime; u_int8 curr_rp_priority; u_int8 reserved_byte __attribute__((unused)); u_int32 curr_group_mask; u_int32 prefix_h; grp_mask_t *grp_mask_ptr; grp_mask_t *grp_mask_next; rp_grp_entry_t *grp_rp_entry_ptr; rp_grp_entry_t *grp_rp_entry_next; /* Checksum */ if (inet_cksum((u_int16 *)pim_message, datalen)) return FALSE; if (find_vif_direct(src) == NO_VIF) { /* Either a local vif or somehow received PIM_BOOTSTRAP from * non-directly connected router. Ignore it. */ if (local_address(src) == NO_VIF) logit(LOG_INFO, 0, "Ignoring PIM_BOOTSTRAP from non-neighbor router %s", inet_fmt(src, s1, sizeof(s1))); return FALSE; } /* sanity check for the minimum length */ if (datalen < PIM_BOOTSTRAP_MINLEN) { logit(LOG_NOTICE, 0, "receive_pim_bootstrap: Bootstrap message size(%u) is too short from %s", datalen, inet_fmt(src, s1, sizeof(s1))); return FALSE; } data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t)); /* Parse the PIM_BOOTSTRAP message */ GET_HOSTSHORT(new_bsr_fragment_tag, data_ptr); GET_BYTE(new_bsr_hash_masklen, data_ptr); GET_BYTE(new_bsr_priority, data_ptr); GET_EUADDR(&new_bsr_uni_addr, data_ptr); new_bsr_address = new_bsr_uni_addr.unicast_addr; if (local_address(new_bsr_address) != NO_VIF) return FALSE; /* The new BSR is one of my local addresses */ /* * Compare the current BSR priority with the priority of the BSR * included in the message. */ /* TODO: if I am just starting and will become the BSR, * I should accept the message coming from the current BSR and get the * current Cand-RP-Set. */ if ((curr_bsr_priority > new_bsr_priority) || ((curr_bsr_priority == new_bsr_priority) && (ntohl(curr_bsr_address) > ntohl(new_bsr_address)))) { /* The message's BSR is less preferred than the current BSR */ return FALSE; /* Ignore the received BSR message */ } /* Check the iif, if this was PIM-ROUTERS multicast */ if (dst == allpimrouters_group) { k_req_incoming(new_bsr_address, &rpfc); if (rpfc.iif == NO_VIF || rpfc.rpfneighbor.s_addr == INADDR_ANY_N) { /* coudn't find a route to the BSR */ return FALSE; } neighbor_addr = rpfc.rpfneighbor.s_addr; incoming = rpfc.iif; if (uvifs[incoming].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) return FALSE; /* Shoudn't arrive on that interface */ /* Find the upstream router */ for (n = uvifs[incoming].uv_pim_neighbors; n != NULL; n = n->next) { if (ntohl(neighbor_addr) < ntohl(n->address)) continue; if (neighbor_addr == n->address) { rpf_neighbor = n; break; } return FALSE; /* No neighbor toward BSR found */ } if (!n || n->address != src) return FALSE; /* Sender of this message is not the RPF neighbor */ } else { if (local_address(dst) == NO_VIF) { /* TODO: XXX: this situation should be handled earlier: * The destination is neither ALL_PIM_ROUTERS neither me */ return FALSE; } /* Probably unicasted from the current DR */ if (cand_rp_list != (cand_rp_t *)NULL) { /* Hmmm, I do have a Cand-RP-list, but some neighbor has a * different opinion and is unicasting it to me. Ignore this guy. */ return FALSE; } for (vifi = 0; vifi < numvifs; vifi++) { if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; if (uvifs[vifi].uv_lcl_addr == dst) { incoming = vifi; break; } } if (incoming == NO_VIF) { /* Cannot find the receiving iif toward that DR */ IF_DEBUG(DEBUG_RPF | DEBUG_PIM_BOOTSTRAP) logit(LOG_DEBUG, 0, "Unicast boostrap message from %s to ignored: cannot find iif", inet_fmt(src, s1, sizeof(s1)), inet_fmt(dst, s2, sizeof(s2))); return FALSE; } /* TODO: check the sender is directly connected and I am really the DR */ } if (cand_rp_flag == TRUE) { /* If change in the BSR address, schedule immediate Cand-RP-Adv */ /* TODO: use some random delay? */ if (new_bsr_address != curr_bsr_address) { SET_TIMER(pim_cand_rp_adv_timer, 0); } } /* Forward the BSR Message first and then update the RP-set list */ /* TODO: if the message was unicasted to me, resend? */ for (vifi = 0; vifi < numvifs; vifi++) { if (vifi == incoming) continue; if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER | VIFF_NONBRS)) continue; memcpy(pim_send_buf + sizeof(struct ip), pim_message, datalen); send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_BOOTSTRAP, datalen - sizeof(pim_header_t)); } max_data_ptr = (u_int8 *)pim_message + datalen; /* TODO: XXX: this 22 is HARDCODING!!! Do a bunch of definitions * and make it stylish! */ min_datalen = 22; if (new_bsr_fragment_tag != curr_bsr_fragment_tag || new_bsr_address != curr_bsr_address) { /* Throw away the old segment */ delete_rp_list(&segmented_cand_rp_list, &segmented_grp_mask_list); } curr_bsr_address = new_bsr_address; curr_bsr_priority = new_bsr_priority; curr_bsr_fragment_tag = new_bsr_fragment_tag; MASKLEN_TO_MASK(new_bsr_hash_masklen, curr_bsr_hash_mask); SET_TIMER(pim_bootstrap_timer, PIM_BOOTSTRAP_TIMEOUT); while (data_ptr + min_datalen <= max_data_ptr) { GET_EGADDR(&curr_group_addr, data_ptr); GET_BYTE(curr_rp_count, data_ptr); GET_BYTE(curr_frag_rp_count, data_ptr); GET_HOSTSHORT(reserved_short, data_ptr); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); if (curr_rp_count == 0) { delete_grp_mask(&cand_rp_list, &grp_mask_list, curr_group_addr.mcast_addr, curr_group_mask); continue; } if (curr_rp_count == curr_frag_rp_count) { /* Add all RPs */ while(curr_frag_rp_count--) { GET_EUADDR(&curr_rp_addr, data_ptr); GET_HOSTSHORT(curr_rp_holdtime, data_ptr); GET_BYTE(curr_rp_priority, data_ptr); GET_BYTE(reserved_byte, data_ptr); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, curr_rp_addr.unicast_addr, curr_rp_priority, curr_rp_holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } continue; } /* * This is a partial list of the RPs for this group prefix. * Save until all segments arrive. */ prefix_h = ntohl(curr_group_addr.mcast_addr & curr_group_mask); for (grp_mask_ptr = segmented_grp_mask_list; grp_mask_ptr != (grp_mask_t *)NULL; grp_mask_ptr = grp_mask_ptr->next) { if (ntohl(grp_mask_ptr->group_addr & grp_mask_ptr->group_mask) > prefix_h) continue; else break; } if ((grp_mask_ptr != (grp_mask_t *)NULL) && (grp_mask_ptr->group_addr == curr_group_addr.mcast_addr) && (grp_mask_ptr->group_mask == curr_group_mask) && (grp_mask_ptr->group_rp_number + curr_frag_rp_count == curr_rp_count)) { /* All missing PRs have arrived. Add all RP entries */ while(curr_frag_rp_count--) { GET_EUADDR(&curr_rp_addr, data_ptr); GET_HOSTSHORT(curr_rp_holdtime, data_ptr); GET_BYTE(curr_rp_priority, data_ptr); GET_BYTE(reserved_byte, data_ptr); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, curr_rp_addr.unicast_addr, curr_rp_priority, curr_rp_holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } /* Add the rest from the previously saved segments */ for(grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next; grp_rp_entry_ptr != (rp_grp_entry_t *)NULL; grp_rp_entry_ptr = grp_rp_entry_ptr->grp_rp_next) { add_rp_grp_entry(&cand_rp_list, &grp_mask_list, grp_rp_entry_ptr->rp->rpentry->address, grp_rp_entry_ptr->priority, grp_rp_entry_ptr->holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } delete_grp_mask(&segmented_cand_rp_list, &segmented_grp_mask_list, curr_group_addr.mcast_addr, curr_group_mask); } else { /*Add the partially received RP-list to the group of pending RPs*/ while(curr_frag_rp_count--) { GET_EUADDR(&curr_rp_addr, data_ptr); GET_HOSTSHORT(curr_rp_holdtime, data_ptr); GET_BYTE(curr_rp_priority, data_ptr); GET_BYTE(reserved_byte, data_ptr); MASKLEN_TO_MASK(curr_group_addr.masklen, curr_group_mask); add_rp_grp_entry(&segmented_cand_rp_list, &segmented_grp_mask_list, curr_rp_addr.unicast_addr, curr_rp_priority, curr_rp_holdtime, curr_group_addr.mcast_addr, curr_group_mask, curr_bsr_hash_mask, curr_bsr_fragment_tag); } } } /* Garbage collection. Check all group prefixes and if the * fragment_tag for a group_prefix is the same as curr_bsr_fragment_tag, * then remove all RPs for this group_prefix which have different * fragment tag. */ for (grp_mask_ptr = grp_mask_list; grp_mask_ptr != (grp_mask_t *)NULL; grp_mask_ptr = grp_mask_next) { grp_mask_next = grp_mask_ptr->next; if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag) { for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next; grp_rp_entry_ptr != (rp_grp_entry_t *)NULL; grp_rp_entry_ptr = grp_rp_entry_next) { grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next; if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag) delete_rp_grp_entry(&cand_rp_list, &grp_mask_list, grp_rp_entry_ptr); } } } /* Cleanup also the list used by incompleted segments */ for (grp_mask_ptr = segmented_grp_mask_list; grp_mask_ptr != (grp_mask_t *)NULL; grp_mask_ptr = grp_mask_next) { grp_mask_next = grp_mask_ptr->next; if (grp_mask_ptr->fragment_tag == curr_bsr_fragment_tag) { for (grp_rp_entry_ptr = grp_mask_ptr->grp_rp_next; grp_rp_entry_ptr != (rp_grp_entry_t *)NULL; grp_rp_entry_ptr = grp_rp_entry_next) { grp_rp_entry_next = grp_rp_entry_ptr->grp_rp_next; if (grp_rp_entry_ptr->fragment_tag != curr_bsr_fragment_tag) delete_rp_grp_entry(&segmented_cand_rp_list, &segmented_grp_mask_list, grp_rp_entry_ptr); } } } return TRUE; } void send_pim_bootstrap(void) { int datalen; vifi_t vifi; if ((datalen = create_pim_bootstrap_message(pim_send_buf))) { for (vifi = 0; vifi < numvifs; vifi++) { if (uvifs[vifi].uv_flags & (VIFF_DISABLED | VIFF_DOWN | VIFF_REGISTER)) continue; send_pim(pim_send_buf, uvifs[vifi].uv_lcl_addr, allpimrouters_group, PIM_BOOTSTRAP, datalen); } } } /************************************************************************ * PIM_CAND_RP_ADV ************************************************************************/ /* * If I am the Bootstrap router, process the advertisement, otherwise * ignore it. */ #define PIM_CAND_RP_ADV_MINLEN (PIM_MINLEN + PIM_ENCODE_UNI_ADDR_LEN) int receive_pim_cand_rp_adv(u_int32 src, u_int32 dst __attribute__((unused)), char *pim_message, int datalen) { u_int8 prefix_cnt; u_int8 priority; u_int16 holdtime; pim_encod_uni_addr_t cand_rp_addr; pim_encod_grp_addr_t encod_grp_addr; u_int8 *data_ptr; u_int32 grp_mask; /* Checksum */ if (inet_cksum((u_int16 *)pim_message, datalen)) return FALSE; /* if I am not the bootstrap RP, then do not accept the message */ if (cand_bsr_flag == FALSE || curr_bsr_address != my_bsr_address) return FALSE; /* sanity check for the minimum length */ if (datalen < PIM_CAND_RP_ADV_MINLEN) { logit(LOG_NOTICE, 0, "receive_pim_cand_rp_adv: cand_RP message size(%u) is too short from %s", datalen, inet_fmt(src, s1, sizeof(s1))); return FALSE; } data_ptr = (u_int8 *)(pim_message + sizeof(pim_header_t)); /* Parse the CAND_RP_ADV message */ /* TODO: XXX: check datalen whether it is at least the minimum */ GET_BYTE(prefix_cnt, data_ptr); GET_BYTE(priority, data_ptr); GET_HOSTSHORT(holdtime, data_ptr); GET_EUADDR(&cand_rp_addr, data_ptr); if (prefix_cnt == 0) { /* The default 224.0.0.0 and masklen of 4 */ MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LENGTH, grp_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, cand_rp_addr.unicast_addr, priority, holdtime, htonl(ALL_MCAST_GROUPS_ADDR), grp_mask, my_bsr_hash_mask, curr_bsr_fragment_tag); return TRUE; } while (prefix_cnt--) { GET_EGADDR(&encod_grp_addr, data_ptr); MASKLEN_TO_MASK(encod_grp_addr.masklen, grp_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, cand_rp_addr.unicast_addr, priority, holdtime, encod_grp_addr.mcast_addr, grp_mask, my_bsr_hash_mask, curr_bsr_fragment_tag); /* TODO: Check for datalen */ } return TRUE; } int send_pim_cand_rp_adv(void) { u_int8 prefix_cnt; u_int32 grp_mask; pim_encod_grp_addr_t encod_grp_addr; u_int8 *data_ptr; if (!inet_valid_host(curr_bsr_address)) return FALSE; /* No BSR yet */ if (curr_bsr_address == my_bsr_address) { /* I am the BSR and have to include my own group_prefix stuff */ prefix_cnt = *cand_rp_adv_message.prefix_cnt_ptr; if (prefix_cnt == 0) { /* The default 224.0.0.0 and masklen of 4 */ MASKLEN_TO_MASK(ALL_MCAST_GROUPS_LENGTH, grp_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, my_cand_rp_address, my_cand_rp_priority, my_cand_rp_holdtime, htonl(ALL_MCAST_GROUPS_ADDR), my_bsr_hash_mask, grp_mask, curr_bsr_fragment_tag); return TRUE; } /* TODO: hardcoding!! */ data_ptr = cand_rp_adv_message.buffer + (4 + 6); while (prefix_cnt--) { GET_EGADDR(&encod_grp_addr, data_ptr); MASKLEN_TO_MASK(encod_grp_addr.masklen, grp_mask); add_rp_grp_entry(&cand_rp_list, &grp_mask_list, my_cand_rp_address, my_cand_rp_priority, my_cand_rp_holdtime, encod_grp_addr.mcast_addr, grp_mask, my_bsr_hash_mask, curr_bsr_fragment_tag); /* TODO: Check for datalen */ } return TRUE; } data_ptr = (u_int8 *)(pim_send_buf + sizeof(struct ip) + sizeof(pim_header_t)); memcpy(data_ptr, cand_rp_adv_message.buffer, cand_rp_adv_message.message_size); send_pim_unicast(pim_send_buf, my_cand_rp_address, curr_bsr_address, PIM_CAND_RP_ADV, cand_rp_adv_message.message_size); return TRUE; } /** * Local Variables: * version-control: t * indent-tabs-mode: t * c-file-style: "ellemtel" * c-basic-offset: 4 * End: */