pim_msg.c pim_upstream.c pim_rpf.c pim_macro.c \
pim_ssmpingd.c pim_int.c pim_rp.c \
pim_static.c pim_br.c pim_register.c pim_routemap.c \
- pim_msdp.c pim_msdp_socket.c pim_msdp_packet.c
+ pim_msdp.c pim_msdp_socket.c pim_msdp_packet.c \
+ pim_jp_agg.c
noinst_HEADERS = \
pim_memory.h \
pim_msg.h pim_upstream.h pim_rpf.h pim_macro.h \
pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_rp.h \
pim_static.h pim_br.h pim_register.h \
- pim_msdp.h pim_msdp_socket.h pim_msdp_packet.h
+ pim_msdp.h pim_msdp_socket.h pim_msdp_packet.h \
+ pim_jp_agg.h
pimd_SOURCES = \
pim_main.c $(libpim_a_SOURCES)
pim_inet4_dump("<grp?>", up->sg.grp, grp_str, sizeof(grp_str));
pim_time_uptime(uptime, sizeof(uptime), now - up->state_transition);
pim_time_timer_to_hhmmss (join_timer, sizeof(join_timer), up->t_join_timer);
+
+ /*
+ * If we have a J/P timer for the neighbor display that
+ */
+ if (!up->t_join_timer)
+ {
+ struct pim_neighbor *nbr;
+
+ nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr.u.prefix4);
+ if (nbr)
+ pim_time_timer_to_hhmmss (join_timer, sizeof(join_timer), nbr->jp_timer);
+ }
+
pim_time_timer_to_hhmmss (rs_timer, sizeof (rs_timer), up->t_rs_timer);
pim_time_timer_to_hhmmss (ka_timer, sizeof (ka_timer), up->t_ka_timer);
pim_time_timer_to_hhmmss (msdp_reg_timer, sizeof (msdp_reg_timer), up->t_msdp_reg_timer);
list_delete(pim_ifp->pim_neighbor_list);
}
+ if (pim_ifp->upstream_switch_list)
+ list_delete(pim_ifp->upstream_switch_list);
+
if (pim_ifp->pim_ifchannel_list) {
list_delete(pim_ifp->pim_ifchannel_list);
}
if (pim_ifp->pim_ifchannel_hash)
- hash_free (pim_ifp->pim_ifchannel_hash);
+ hash_free(pim_ifp->pim_ifchannel_hash);
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
pim_ifp->igmp_join_list = NULL;
pim_ifp->igmp_socket_list = NULL;
pim_ifp->pim_neighbor_list = NULL;
+ pim_ifp->upstream_switch_list = NULL;
pim_ifp->pim_ifchannel_list = NULL;
pim_ifp->pim_ifchannel_hash = NULL;
pim_ifp->pim_generation_id = 0;
}
pim_ifp->pim_neighbor_list->del = (void (*)(void *)) pim_neighbor_free;
+ pim_ifp->upstream_switch_list = list_new();
+ if (!pim_ifp->upstream_switch_list) {
+ zlog_err("%s %s: failure: upstream_switch_list=list_new()",
+ __FILE__, __PRETTY_FUNCTION__);
+ return if_list_clean(pim_ifp);
+ }
+
/* list of struct pim_ifchannel */
pim_ifp->pim_ifchannel_list = list_new();
if (!pim_ifp->pim_ifchannel_list) {
list_delete(pim_ifp->igmp_socket_list);
list_delete(pim_ifp->pim_neighbor_list);
+ list_delete(pim_ifp->upstream_switch_list);
list_delete(pim_ifp->pim_ifchannel_list);
hash_free (pim_ifp->pim_ifchannel_hash);
#define PIM_I_am_DR(pim_ifp) (pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr
+struct pim_iface_upstream_switch {
+ struct in_addr address;
+ struct list *us;
+};
+
enum pim_interface_type {
PIM_INTERFACE_SSM,
PIM_INTERFACE_SM
uint16_t pim_propagation_delay_msec; /* config */
uint16_t pim_override_interval_msec; /* config */
struct list *pim_neighbor_list; /* list of struct pim_neighbor */
+ struct list *upstream_switch_list;
struct list *pim_ifchannel_list; /* list of struct pim_ifchannel */
struct hash *pim_ifchannel_hash;
rpf.source_nexthop.interface = ifp;
rpf.rpf_addr.u.prefix4 = pim_ifp->primary_address;
- pim_joinprune_send (&rpf, ch->upstream, 0);
+ pim_jp_agg_single_upstream_send(&rpf, ch->upstream, 0);
}
}
else
#include "pim_ifchannel.h"
#include "pim_rpf.h"
#include "pim_rp.h"
+#include "pim_jp_agg.h"
static void
on_trace (const char *label,
return 0;
}
+/*
+ * J/P Message Format
+ *
+ * While the RFC clearly states that this is 32 bits wide, it
+ * is cheating. These fields:
+ * Encoded-Unicast format (6 bytes MIN)
+ * Encoded-Group format (8 bytes MIN)
+ * Encoded-Source format (8 bytes MIN)
+ * are *not* 32 bits wide.
+ *
+ * Nor does the RFC explicitly call out the size for:
+ * Reserved (1 byte)
+ * Num Groups (1 byte)
+ * Holdtime (2 bytes)
+ * Number of Joined Sources (2 bytes)
+ * Number of Pruned Sources (2 bytes)
+ *
+ * This leads to a missleading representation from casual
+ * reading and making assumptions. Be careful!
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |PIM Ver| Type | Reserved | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Upstream Neighbor Address (Encoded-Unicast format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved | Num groups | Holdtime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Multicast Group Address 1 (Encoded-Group format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Number of Joined Sources | Number of Pruned Sources |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Joined Source Address 1 (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Joined Source Address n (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Pruned Source Address 1 (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Pruned Source Address n (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Multicast Group Address m (Encoded-Group format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Number of Joined Sources | Number of Pruned Sources |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Joined Source Address 1 (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Joined Source Address n (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Pruned Source Address 1 (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Pruned Source Address n (Encoded-Source format) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
int pim_joinprune_send(struct pim_rpf *rpf,
- struct pim_upstream *up,
- int send_join)
+ struct list *groups)
{
+ struct pim_jp_agg_group *group;
struct pim_interface *pim_ifp;
- uint8_t pim_msg[9000];
- int pim_msg_size;
+ struct pim_jp_groups *grp = NULL;
+ struct pim_jp *msg;
+ struct listnode *node, *nnode;
+ uint8_t pim_msg[10000];
+ uint8_t *curr_ptr = pim_msg;
+ bool new_packet = true;
+ size_t packet_left = 0;
+ size_t packet_size = 0;
+ size_t group_size = 0;
on_trace (__PRETTY_FUNCTION__, rpf->source_nexthop.interface, rpf->rpf_addr.u.prefix4);
return -1;
}
- if (PIM_INADDR_IS_ANY(rpf->rpf_addr.u.prefix4)) {
- if (PIM_DEBUG_PIM_J_P) {
- char dst_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4, dst_str, sizeof(dst_str));
- zlog_debug("%s: %s(S,G)=%s: upstream=%s is myself on interface %s",
- __PRETTY_FUNCTION__,
- send_join ? "Join" : "Prune",
- up->sg_str, dst_str, rpf->source_nexthop.interface->name);
+ if (PIM_INADDR_IS_ANY(rpf->rpf_addr.u.prefix4))
+ {
+ if (PIM_DEBUG_PIM_J_P) {
+ char dst_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4, dst_str, sizeof(dst_str));
+ zlog_debug("%s: upstream=%s is myself on interface %s",
+ __PRETTY_FUNCTION__,
+ dst_str, rpf->source_nexthop.interface->name);
+ }
+ return 0;
}
- return 0;
- }
/*
RFC 4601: 4.3.1. Sending Hello Messages
*/
pim_hello_require(rpf->source_nexthop.interface);
- /*
- Build PIM message
- */
- pim_msg_size = pim_msg_join_prune_encode (pim_msg, 9000, send_join,
- up, rpf->rpf_addr.u.prefix4, PIM_JP_HOLDTIME);
+ for (ALL_LIST_ELEMENTS(groups, node, nnode, group))
+ {
+ if (new_packet)
+ {
+ msg = (struct pim_jp *)pim_msg;
- if (pim_msg_size < 0)
- return pim_msg_size;
+ memset(msg, 0, sizeof (*msg));
- if (PIM_DEBUG_PIM_J_P) {
- char dst_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4, dst_str, sizeof(dst_str));
- zlog_debug("%s: sending %s(S,G)=%s to upstream=%s on interface %s",
- __PRETTY_FUNCTION__,
- send_join ? "Join" : "Prune",
- up->sg_str, dst_str, rpf->source_nexthop.interface->name);
- }
+ pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg->addr, rpf->rpf_addr.u.prefix4);
+ msg->reserved = 0;
+ msg->holdtime = htons(PIM_JP_HOLDTIME);
- if (pim_msg_send(pim_ifp->pim_sock_fd,
- pim_ifp->primary_address,
- qpim_all_pim_routers_addr,
- pim_msg,
- pim_msg_size,
- rpf->source_nexthop.interface->name)) {
- zlog_warn("%s: could not send PIM message on interface %s",
- __PRETTY_FUNCTION__, rpf->source_nexthop.interface->name);
- return -8;
- }
+ new_packet = false;
+
+ grp = &msg->groups[0];
+ curr_ptr = (uint8_t *)grp;
+ packet_size = sizeof (struct pim_msg_header);
+ packet_size += sizeof (struct pim_encoded_ipv4_unicast);
+ packet_size += 4; // reserved (1) + groups (1) + holdtime (2)
+
+ packet_left = rpf->source_nexthop.interface->mtu - 24;
+ packet_left -= packet_size;
+ }
+ if (PIM_DEBUG_PIM_J_P) {
+ char dst_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<dst?>", rpf->rpf_addr.u.prefix4, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<grp?>", group->group, grp_str, sizeof(grp_str));
+ zlog_debug("%s: sending (G)=%s to upstream=%s on interface %s",
+ __PRETTY_FUNCTION__,
+ grp_str, dst_str, rpf->source_nexthop.interface->name);
+ }
+
+ group_size = pim_msg_get_jp_group_size (group->sources);
+ if (group_size > packet_left)
+ {
+ pim_msg_build_header (pim_msg, packet_size, PIM_MSG_TYPE_JOIN_PRUNE);
+ if (pim_msg_send(pim_ifp->pim_sock_fd,
+ pim_ifp->primary_address,
+ qpim_all_pim_routers_addr,
+ pim_msg,
+ packet_size,
+ rpf->source_nexthop.interface->name)) {
+ zlog_warn("%s: could not send PIM message on interface %s",
+ __PRETTY_FUNCTION__, rpf->source_nexthop.interface->name);
+ }
+
+ msg = (struct pim_jp *)pim_msg;
+ memset(msg, 0, sizeof (*msg));
+
+ pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg->addr, rpf->rpf_addr.u.prefix4);
+ msg->reserved = 0;
+ msg->holdtime = htons(PIM_JP_HOLDTIME);
+
+ new_packet = false;
+
+ grp = &msg->groups[0];
+ curr_ptr = (uint8_t *)grp;
+ packet_size = sizeof (struct pim_msg_header);
+ packet_size += sizeof (struct pim_encoded_ipv4_unicast);
+ packet_size += 4; // reserved (1) + groups (1) + holdtime (2)
+
+ packet_left = rpf->source_nexthop.interface->mtu - 24;
+ packet_left -= packet_size;
+ }
+ msg->num_groups++;
+ /*
+ Build PIM message
+ */
+
+ curr_ptr += group_size;
+ packet_left -= group_size;
+ packet_size += group_size;
+ zlog_debug ("\tpl: %zd ps: %zd", packet_left, packet_size);
+ pim_msg_build_jp_groups (grp, group);
+
+ grp = (struct pim_jp_groups *)curr_ptr;
+ if (packet_left < sizeof (struct pim_jp_groups) || msg->num_groups == 255)
+ {
+ pim_msg_build_header (pim_msg, packet_size, PIM_MSG_TYPE_JOIN_PRUNE);
+ if (pim_msg_send(pim_ifp->pim_sock_fd,
+ pim_ifp->primary_address,
+ qpim_all_pim_routers_addr,
+ pim_msg,
+ packet_size,
+ rpf->source_nexthop.interface->name)) {
+ zlog_warn("%s: could not send PIM message on interface %s",
+ __PRETTY_FUNCTION__, rpf->source_nexthop.interface->name);
+ }
+
+ new_packet = true;
+ }
+ }
+
+
+ if (!new_packet)
+ {
+ //msg->num_groups = htons (msg->num_groups);
+ pim_msg_build_header (pim_msg, packet_size, PIM_MSG_TYPE_JOIN_PRUNE);
+ if (pim_msg_send(pim_ifp->pim_sock_fd,
+ pim_ifp->primary_address,
+ qpim_all_pim_routers_addr,
+ pim_msg,
+ packet_size,
+ rpf->source_nexthop.interface->name)) {
+ zlog_warn("%s: could not send PIM message on interface %s",
+ __PRETTY_FUNCTION__, rpf->source_nexthop.interface->name);
+ }
+ }
return 0;
}
uint8_t *tlv_buf, int tlv_buf_size);
int pim_joinprune_send(struct pim_rpf *nexthop,
- struct pim_upstream *up,
- int send_join);
+ struct list *groups);
#endif /* PIM_JOIN_H */
--- /dev/null
+#include <zebra.h>
+
+#include "linklist.h"
+#include "log.h"
+
+#include "pimd.h"
+#include "pim_msg.h"
+#include "pim_jp_agg.h"
+#include "pim_join.h"
+#include "pim_iface.h"
+
+void
+pim_jp_agg_group_list_free (struct pim_jp_agg_group *jag)
+{
+ list_delete(jag->sources);
+
+ XFREE (MTYPE_PIM_JP_AGG_GROUP, jag);
+}
+
+static void
+pim_jp_agg_src_free (struct pim_jp_sources *js)
+{
+ /*
+ * When we are being called here, we know
+ * that the neighbor is going away start
+ * the normal j/p timer so that it can
+ * pick this shit back up when the
+ * nbr comes back alive
+ */
+ join_timer_start(js->up);
+ XFREE (MTYPE_PIM_JP_AGG_SOURCE, js);
+}
+
+int
+pim_jp_agg_group_list_cmp (void *arg1, void *arg2)
+{
+ const struct pim_jp_agg_group *jag1 = (const struct pim_jp_agg_group *)arg1;
+ const struct pim_jp_agg_group *jag2 = (const struct pim_jp_agg_group *)arg2;
+
+ if (jag1->group.s_addr < jag2->group.s_addr)
+ return -1;
+
+ if (jag1->group.s_addr > jag2->group.s_addr)
+ return 1;
+
+ return 0;
+}
+
+static int
+pim_jp_agg_src_cmp (void *arg1, void *arg2)
+{
+ const struct pim_jp_sources *js1 = (const struct pim_jp_sources *)arg1;
+ const struct pim_jp_sources *js2 = (const struct pim_jp_sources *)arg2;
+
+ if (js1->up->sg.src.s_addr < js2->up->sg.src.s_addr)
+ return -1;
+
+ if (js1->up->sg.src.s_addr > js2->up->sg.src.s_addr)
+ return 1;
+
+ return 0;
+}
+
+void
+pim_jp_agg_clear_group (struct list *group)
+{
+ struct listnode *node, *nnode;
+ struct pim_jp_agg_group *jag;
+
+ for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
+ {
+ list_delete(jag->sources);
+ jag->sources = NULL;
+ listnode_delete(group, jag);
+ XFREE(MTYPE_PIM_JP_AGG_GROUP, jag);
+ }
+}
+
+static struct pim_iface_upstream_switch *
+pim_jp_agg_get_interface_upstream_switch_list (struct pim_rpf *rpf)
+{
+ struct pim_interface *pim_ifp = rpf->source_nexthop.interface->info;
+ struct pim_iface_upstream_switch *pius;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(pim_ifp->upstream_switch_list, node, nnode, pius))
+ {
+ if (pius->address.s_addr == rpf->rpf_addr.u.prefix4.s_addr)
+ break;
+ }
+
+ if (!pius)
+ {
+ pius = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, sizeof (struct pim_iface_upstream_switch));
+ pius->address.s_addr = rpf->rpf_addr.u.prefix4.s_addr;
+ pius->us = list_new();
+ listnode_add (pim_ifp->upstream_switch_list, pius);
+ }
+
+ return pius;
+}
+
+void
+pim_jp_agg_remove_group (struct list *group, struct pim_upstream *up)
+{
+ struct listnode *node, *nnode;
+ struct pim_jp_agg_group *jag = NULL;
+ struct pim_jp_sources *js = NULL;
+
+ for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
+ {
+ if (jag->group.s_addr == up->sg.grp.s_addr)
+ break;
+ }
+
+ if (!jag)
+ return;
+
+ for (ALL_LIST_ELEMENTS(jag->sources, node, nnode, js))
+ {
+ if (js->up == up)
+ break;
+ }
+
+ listnode_delete(jag->sources, js);
+
+ XFREE(MTYPE_PIM_JP_AGG_SOURCE, js);
+
+ if (jag->sources->count == 0)
+ {
+ list_delete(jag->sources);
+ listnode_delete(group, jag);
+ }
+}
+
+void
+pim_jp_agg_add_group (struct list *group, struct pim_upstream *up, bool is_join)
+{
+ struct listnode *node, *nnode;
+ struct pim_jp_agg_group *jag = NULL;
+ struct pim_jp_sources *js;
+
+ for (ALL_LIST_ELEMENTS(group, node, nnode, jag))
+ {
+ if (jag->group.s_addr == up->sg.grp.s_addr)
+ break;
+ }
+
+ if (!jag)
+ {
+ jag = XCALLOC(MTYPE_PIM_JP_AGG_GROUP, sizeof (struct pim_jp_agg_group));
+ jag->group.s_addr = up->sg.grp.s_addr;
+ jag->sources = list_new();
+ jag->sources->cmp = pim_jp_agg_src_cmp;
+ jag->sources->del = (void (*)(void *))pim_jp_agg_src_free;
+ listnode_add (group, jag);
+ }
+
+ js = XCALLOC(MTYPE_PIM_JP_AGG_SOURCE, sizeof (struct pim_jp_sources));
+ js->up = up;
+ js->is_join = is_join;
+
+ listnode_add (jag->sources, js);
+}
+
+void
+pim_jp_agg_switch_interface (struct pim_rpf *orpf,
+ struct pim_rpf *nrpf,
+ struct pim_upstream *up)
+{
+ struct pim_iface_upstream_switch *opius;
+ struct pim_iface_upstream_switch *npius;
+
+ opius = pim_jp_agg_get_interface_upstream_switch_list(orpf);
+ npius = pim_jp_agg_get_interface_upstream_switch_list(nrpf);
+
+ /*
+ * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
+ *
+ * Transitions from Joined State
+ *
+ * RPF'(S,G) changes not due to an Assert
+ *
+ * The upstream (S,G) state machine remains in Joined
+ * state. Send Join(S,G) to the new upstream neighbor, which is
+ * the new value of RPF'(S,G). Send Prune(S,G) to the old
+ * upstream neighbor, which is the old value of RPF'(S,G). Set
+ * the Join Timer (JT) to expire after t_periodic seconds.
+ */
+
+ /* send Prune(S,G) to the old upstream neighbor */
+ pim_jp_agg_add_group (opius->us, up, false);
+
+ /* send Join(S,G) to the current upstream neighbor */
+ pim_jp_agg_add_group (npius->us, up, true);
+
+}
+
+
+void
+pim_jp_agg_single_upstream_send (struct pim_rpf *rpf,
+ struct pim_upstream *up,
+ bool is_join)
+{
+ static struct list *groups = NULL;
+ static struct pim_jp_agg_group jag;
+ static struct pim_jp_sources js;
+
+ static bool first = true;
+
+ if (first)
+ {
+ groups = list_new();
+
+ jag.sources = list_new();
+
+ listnode_add(groups, &jag);
+ listnode_add(jag.sources, &js);
+
+ first = false;
+ }
+
+ jag.group.s_addr = up->sg.grp.s_addr;
+ js.up = up;
+ js.is_join = is_join;
+
+ pim_joinprune_send(rpf, groups);
+}
--- /dev/null
+#ifndef __PIM_JP_AGG_H__
+#define __PIM_JP_AGG_H__
+
+struct pim_jp_sources
+{
+ struct pim_upstream *up;
+ int is_join;
+};
+
+struct pim_jp_agg_group
+{
+ struct in_addr group;
+ //int onetime;
+ struct list *sources;
+};
+
+void pim_jp_agg_group_list_free (struct pim_jp_agg_group *jag);
+int pim_jp_agg_group_list_cmp (void *arg1, void *arg2);
+
+void pim_jp_agg_clear_group (struct list *group);
+void pim_jp_agg_remove_group (struct list *group, struct pim_upstream *up);
+
+void pim_jp_agg_add_group (struct list *group,
+ struct pim_upstream *up, bool is_join);
+
+void pim_jp_agg_switch_interface (struct pim_rpf *orpf,
+ struct pim_rpf *nrpf,
+ struct pim_upstream *up);
+
+void pim_jp_agg_single_upstream_send (struct pim_rpf *rpf,
+ struct pim_upstream *up,
+ bool is_join);
+#endif
DEFINE_MTYPE(PIMD, PIM_MSDP_MG, "PIM MSDP mesh group")
DEFINE_MTYPE(PIMD, PIM_MSDP_MG_MBR, "PIM MSDP mesh group mbr")
DEFINE_MTYPE(PIMD, PIM_SEC_ADDR, "PIM secondary address")
+DEFINE_MTYPE(PIMD, PIM_JP_AGG_GROUP, "PIM JP AGG Group")
+DEFINE_MTYPE(PIMD, PIM_JP_AGG_SOURCE, "PIM JP AGG Source")
DECLARE_MTYPE(PIM_MSDP_MG)
DECLARE_MTYPE(PIM_MSDP_MG_MBR)
DECLARE_MTYPE(PIM_SEC_ADDR)
-
+DECLARE_MTYPE(PIM_JP_AGG_GROUP)
+DECLARE_MTYPE(PIM_JP_AGG_SOURCE)
#endif /* _QUAGGA_PIM_MEMORY_H */
#include "pim_rp.h"
#include "pim_rpf.h"
#include "pim_register.h"
+#include "pim_jp_agg.h"
void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, uint8_t pim_msg_type)
{
return buf + PIM_ENCODED_IPV4_SOURCE_SIZE;
}
-static size_t
-pim_msg_build_jp_groups (struct pim_jp_groups *grp, struct pim_upstream *up, int is_join)
+/*
+ * For the given 'struct pim_jp_sources' list
+ * determine the size_t it would take up.
+ */
+size_t
+pim_msg_get_jp_group_size (struct list *sources)
+{
+ size_t size = 0;
+
+ size += sizeof (struct pim_encoded_group_ipv4);
+ size += 4; // Joined sources (2) + Pruned Sources (2)
+
+ size += sizeof (struct pim_encoded_source_ipv4) * sources->count;
+
+ return size;
+}
+
+size_t
+pim_msg_build_jp_groups (struct pim_jp_groups *grp, struct pim_jp_agg_group *sgs)
{
+ struct listnode *node, *nnode;
+ struct pim_jp_sources *source;
struct in_addr stosend;
uint8_t bits;
+ size_t size = pim_msg_get_jp_group_size (sgs->sources);
+ uint8_t tgroups = 0;
- /* number of joined/pruned sources */
- grp->joins = htons(is_join ? 1 : 0);
- grp->prunes = htons(is_join ? 0 : 1);
+ memset (grp, 0, size);
+ pim_msg_addr_encode_ipv4_group ((uint8_t *)&grp->g, sgs->group);
- if (up->sg.src.s_addr == INADDR_ANY)
- {
- struct pim_rpf *rpf = pim_rp_g (up->sg.grp);
- bits = PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_WC_BIT | PIM_ENCODE_RPT_BIT;
- stosend = rpf->rpf_addr.u.prefix4;
- }
- else
+ for (ALL_LIST_ELEMENTS(sgs->sources, node, nnode, source))
{
- bits = PIM_ENCODE_SPARSE_BIT;
- stosend = up->sg.src;
- }
+ /* number of joined/pruned sources */
+ if (source->is_join)
+ grp->joins++;
+ else
+ grp->prunes++;
- if (!pim_msg_addr_encode_ipv4_source ((uint8_t *)&grp->s[0], stosend, bits)) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", up->sg.src, source_str, sizeof(source_str));
- zlog_warn("%s: failure encoding source address %s",
- __PRETTY_FUNCTION__, source_str);
- return 0;
- }
+ if (source->up->sg.src.s_addr == INADDR_ANY)
+ {
+ struct pim_rpf *rpf = pim_rp_g (source->up->sg.grp);
+ bits = PIM_ENCODE_SPARSE_BIT | PIM_ENCODE_WC_BIT | PIM_ENCODE_RPT_BIT;
+ stosend = rpf->rpf_addr.u.prefix4;
+ }
+ else
+ {
+ bits = PIM_ENCODE_SPARSE_BIT;
+ stosend = source->up->sg.src;
+ }
+
+ pim_msg_addr_encode_ipv4_source ((uint8_t *)&grp->s[tgroups], stosend, bits);
+ tgroups++;
+ }
+ grp->joins = htons(grp->joins);
+ grp->prunes = htons(grp->prunes);
/*
* This is not implemented correctly at this point in time
* Make it stop.
}
#endif
- return sizeof (*grp);
-}
-
-/*
- * J/P Message Format
- *
- * While the RFC clearly states that this is 32 bits wide, it
- * is cheating. These fields:
- * Encoded-Unicast format (6 bytes MIN)
- * Encoded-Group format (8 bytes MIN)
- * Encoded-Source format (8 bytes MIN)
- * are *not* 32 bits wide.
- *
- * Nor does the RFC explicitly call out the size for:
- * Reserved (1 byte)
- * Num Groups (1 byte)
- * Holdtime (2 bytes)
- * Number of Joined Sources (2 bytes)
- * Number of Pruned Sources (2 bytes)
- *
- * This leads to a missleading representation from casual
- * reading and making assumptions. Be careful!
- *
- * 0 1 2 3
- * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |PIM Ver| Type | Reserved | Checksum |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Upstream Neighbor Address (Encoded-Unicast format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Reserved | Num groups | Holdtime |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Multicast Group Address 1 (Encoded-Group format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Number of Joined Sources | Number of Pruned Sources |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Joined Source Address 1 (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | . |
- * | . |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Joined Source Address n (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Pruned Source Address 1 (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | . |
- * | . |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Pruned Source Address n (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Multicast Group Address m (Encoded-Group format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Number of Joined Sources | Number of Pruned Sources |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Joined Source Address 1 (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | . |
- * | . |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Joined Source Address n (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Pruned Source Address 1 (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | . |
- * | . |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * | Pruned Source Address n (Encoded-Source format) |
- * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- */
-int
-pim_msg_join_prune_encode (uint8_t *buf, size_t buf_size, int is_join,
- struct pim_upstream *up,
- struct in_addr upstream, int holdtime)
-{
- struct pim_jp *msg = (struct pim_jp *)buf;
-
- assert(buf_size > sizeof (struct pim_jp));
-
- if (!pim_msg_addr_encode_ipv4_ucast ((uint8_t *)&msg->addr, upstream)) {
- char dst_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<dst?>", upstream, dst_str, sizeof(dst_str));
- zlog_warn("%s: failure encoding destination address %s",
- __PRETTY_FUNCTION__, dst_str);
- return -3;
- }
-
- msg->reserved = 0;
- msg->num_groups = 1;
- msg->holdtime = htons(holdtime);
-
- if (!pim_msg_addr_encode_ipv4_group ((uint8_t *)&msg->groups[0].g, up->sg.grp)) {
- char group_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<grp?>", up->sg.grp, group_str, sizeof(group_str));
- zlog_warn("%s: failure encoding group address %s",
- __PRETTY_FUNCTION__, group_str);
- return -5;
- }
-
- pim_msg_build_jp_groups (&msg->groups[0], up, is_join);
-
- pim_msg_build_header (buf, sizeof (struct pim_jp), PIM_MSG_TYPE_JOIN_PRUNE);
-
- return sizeof (struct pim_jp);
+ return size;
}
#include <netinet/in.h>
+#include "pim_jp_agg.h"
/*
Number Description
---------- ------------------
struct in_addr addr, uint8_t bits);
-int pim_msg_join_prune_encode (uint8_t *buf, size_t buf_size, int is_join,
- struct pim_upstream *up,
- struct in_addr upstream, int holdtime);
+size_t pim_msg_get_jp_group_size (struct list *sources);
+size_t pim_msg_build_jp_groups (struct pim_jp_groups *grp, struct pim_jp_agg_group *sgs);
#endif /* PIM_MSG_H */
#include "pim_ifchannel.h"
#include "pim_rp.h"
#include "pim_zebra.h"
+#include "pim_join.h"
+#include "pim_jp_agg.h"
static void dr_election_by_addr(struct interface *ifp)
{
neigh, neigh->holdtime);
}
+static int
+on_neighbor_jp_timer (struct thread *t)
+{
+ struct pim_neighbor *neigh = THREAD_ARG(t);
+ struct pim_rpf rpf;
+
+ if (PIM_DEBUG_PIM_TRACE)
+ {
+ char src_str[INET_ADDRSTRLEN];
+ pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
+ zlog_debug("%s:Sending JP Agg to %s on %s with %d groups", __PRETTY_FUNCTION__,
+ src_str, neigh->interface->name, neigh->upstream_jp_agg->count);
+ }
+ neigh->jp_timer = NULL;
+
+ rpf.source_nexthop.interface = neigh->interface;
+ rpf.rpf_addr.u.prefix4 = neigh->source_addr;
+ pim_joinprune_send(&rpf, neigh->upstream_jp_agg);
+
+ THREAD_TIMER_ON(master, neigh->jp_timer,
+ on_neighbor_jp_timer,
+ neigh, qpim_t_periodic);
+
+ return 0;
+}
+
+static void
+pim_neighbor_start_jp_timer (struct pim_neighbor *neigh)
+{
+ THREAD_TIMER_OFF(neigh->jp_timer);
+ THREAD_TIMER_ON(master, neigh->jp_timer,
+ on_neighbor_jp_timer,
+ neigh, qpim_t_periodic);
+}
+
static struct pim_neighbor *pim_neighbor_new(struct interface *ifp,
struct in_addr source_addr,
pim_hello_options hello_options,
neigh->t_expire_timer = NULL;
neigh->interface = ifp;
+ neigh->upstream_jp_agg = list_new();
+ neigh->upstream_jp_agg->cmp = pim_jp_agg_group_list_cmp;
+ neigh->upstream_jp_agg->del = (void (*)(void *))pim_jp_agg_group_list_free;
+ pim_neighbor_start_jp_timer(neigh);
+
pim_neighbor_timer_reset(neigh, holdtime);
/*
* The pim_ifstat_hello_sent variable is used to decide if
delete_prefix_list(neigh);
+ list_delete(neigh->upstream_jp_agg);
+ THREAD_OFF(neigh->jp_timer);
+
XFREE(MTYPE_PIM_NEIGHBOR, neigh);
}
struct list *prefix_list; /* list of struct prefix */
struct thread *t_expire_timer;
struct interface *interface;
+
+ struct thread *jp_timer;
+ struct list *upstream_jp_agg;
};
void pim_neighbor_timer_reset(struct pim_neighbor *neigh, uint16_t holdtime);
int pim_nexthop_lookup(struct pim_nexthop *nexthop, struct in_addr addr, int neighbor_needed)
{
struct pim_zlookup_nexthop nexthop_tab[MULTIPATH_NUM];
+ struct pim_neighbor *nbr = NULL;
int num_ifindex;
struct interface *ifp = NULL;
ifindex_t first_ifindex = 0;
}
else if (neighbor_needed && !pim_if_connected_to_source (ifp, addr))
{
- struct pim_neighbor *nbr;
-
nbr = pim_neighbor_find (ifp, nexthop_tab[i].nexthop_addr.u.prefix4);
if (PIM_DEBUG_PIM_TRACE_DETAIL)
zlog_debug ("ifp name: %s, pim nbr: %p", ifp->name, nbr);
nexthop->mrib_route_metric = nexthop_tab[i].route_metric;
nexthop->last_lookup = addr;
nexthop->last_lookup_time = pim_time_monotonic_usec();
+ nexthop->nbr = nbr;
return 0;
}
else
struct prefix mrib_nexthop_addr; /* MRIB.next_hop(S) */
uint32_t mrib_metric_preference; /* MRIB.pref(S) */
uint32_t mrib_route_metric; /* MRIB.metric(S) */
+ struct pim_neighbor *nbr;
};
struct pim_rpf {
#include "pim_br.h"
#include "pim_register.h"
#include "pim_msdp.h"
+#include "pim_jp_agg.h"
struct hash *pim_upstream_hash = NULL;
struct list *pim_upstream_list = NULL;
struct timer_wheel *pim_upstream_sg_wheel = NULL;
-static void join_timer_start(struct pim_upstream *up);
+static void join_timer_stop(struct pim_upstream *up);
static void pim_upstream_update_assert_tracking_desired(struct pim_upstream *up);
/*
if (up->ref_count >= 1)
return;
- THREAD_OFF(up->t_join_timer);
+ join_timer_stop(up);
THREAD_OFF(up->t_ka_timer);
THREAD_OFF(up->t_rs_timer);
THREAD_OFF(up->t_msdp_reg_timer);
if (up->join_state == PIM_UPSTREAM_JOINED) {
- pim_joinprune_send (&up->rpf, up, 0);
+ pim_jp_agg_single_upstream_send (&up->rpf, up, 0);
+
if (up->sg.src.s_addr == INADDR_ANY) {
/* if a (*, G) entry in the joined state is being deleted we
* need to notify MSDP */
}
/* send Join(S,G) to the current upstream neighbor */
- pim_joinprune_send(&up->rpf, up, 1 /* join */);
+ pim_jp_agg_single_upstream_send(&up->rpf, up, 1 /* join */);
}
static int on_join_timer(struct thread *t)
return 0;
}
-static void join_timer_start(struct pim_upstream *up)
+static void join_timer_stop(struct pim_upstream *up)
+{
+ struct pim_neighbor *nbr;
+
+ nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr.u.prefix4);
+
+ if (nbr)
+ pim_jp_agg_remove_group (nbr->upstream_jp_agg, up);
+
+ THREAD_OFF (up->t_join_timer);
+}
+
+void
+join_timer_start(struct pim_upstream *up)
{
+ struct pim_neighbor *nbr;
+
+ nbr = pim_neighbor_find (up->rpf.source_nexthop.interface,
+ up->rpf.rpf_addr.u.prefix4);
+
if (PIM_DEBUG_PIM_EVENTS) {
zlog_debug("%s: starting %d sec timer for upstream (S,G)=%s",
__PRETTY_FUNCTION__,
up->sg_str);
}
- THREAD_OFF (up->t_join_timer);
- THREAD_TIMER_ON(master, up->t_join_timer,
- on_join_timer,
- up, qpim_t_periodic);
+ if (nbr)
+ pim_jp_agg_add_group (nbr->upstream_jp_agg, up, 1);
+ else
+ {
+ THREAD_OFF (up->t_join_timer);
+ THREAD_TIMER_ON(master, up->t_join_timer,
+ on_join_timer,
+ up, qpim_t_periodic);
+ }
}
-void pim_upstream_join_timer_restart(struct pim_upstream *up)
+/*
+ * This is only called when we are switching the upstream
+ * J/P from one neighbor to another
+ *
+ * As such we need to remove from the old list and
+ * add to the new list.
+ */
+void pim_upstream_join_timer_restart(struct pim_upstream *up, struct pim_rpf *old)
{
- THREAD_OFF(up->t_join_timer);
+ struct pim_neighbor *nbr;
+
+ nbr = pim_neighbor_find (old->source_nexthop.interface,
+ old->rpf_addr.u.prefix4);
+ if (nbr)
+ pim_jp_agg_remove_group (nbr->upstream_jp_agg, up);
+
+ //THREAD_OFF(up->t_join_timer);
join_timer_start(up);
}
}
}
else {
+
forward_off(up);
if (old_state == PIM_UPSTREAM_JOINED)
pim_msdp_up_join_state_changed(up);
- pim_joinprune_send(&up->rpf, up, 0 /* prune */);
- if (up->t_join_timer)
- THREAD_OFF(up->t_join_timer);
+
+ pim_jp_agg_single_upstream_send(&up->rpf, up, 0 /* prune */);
+ join_timer_stop(up);
}
}
void pim_upstream_join_timer_decrease_to_t_override(const char *debug_label,
struct pim_upstream *up);
-void pim_upstream_join_timer_restart(struct pim_upstream *up);
+void pim_upstream_join_timer_restart(struct pim_upstream *up, struct pim_rpf *old);
void pim_upstream_rpf_genid_changed(struct in_addr neigh_addr);
void pim_upstream_rpf_interface_changed(struct pim_upstream *up,
struct interface *old_rpf_ifp);
void pim_upstream_init (void);
void pim_upstream_terminate (void);
+
+void join_timer_start (struct pim_upstream *up);
#endif /* PIM_UPSTREAM_H */
#include "pim_ifchannel.h"
#include "pim_rp.h"
#include "pim_igmpv3.h"
+#include "pim_jp_agg.h"
#undef PIM_DEBUG_IFADDR_DUMP
#define PIM_DEBUG_IFADDR_DUMP
static void scan_upstream_rpf_cache()
{
struct listnode *up_node;
+ struct listnode *ifnode;
struct listnode *up_nextnode;
+ struct listnode *node;
struct pim_upstream *up;
+ struct interface *ifp;
for (ALL_LIST_ELEMENTS(pim_upstream_list, up_node, up_nextnode, up)) {
enum pim_rpf_result rpf_result;
struct pim_rpf old;
old.source_nexthop.interface = up->rpf.source_nexthop.interface;
+ old.source_nexthop.nbr = up->rpf.source_nexthop.nbr;
rpf_result = pim_rpf_update(up, &old);
+ zlog_debug ("Looking at upstream: %s %d", up->sg_str, rpf_result);
if (rpf_result == PIM_RPF_FAILURE)
continue;
if (rpf_result == PIM_RPF_CHANGED) {
-
/*
* We have detected a case where we might need to rescan
* the inherited o_list so do it.
if (!up->channel_oil->installed)
pim_mroute_add (up->channel_oil, __PRETTY_FUNCTION__);
- /*
- RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
-
- Transitions from Joined State
-
- RPF'(S,G) changes not due to an Assert
-
- The upstream (S,G) state machine remains in Joined
- state. Send Join(S,G) to the new upstream neighbor, which is
- the new value of RPF'(S,G). Send Prune(S,G) to the old
- upstream neighbor, which is the old value of RPF'(S,G). Set
- the Join Timer (JT) to expire after t_periodic seconds.
- */
-
-
- /* send Prune(S,G) to the old upstream neighbor */
- pim_joinprune_send(&old, up, 0 /* prune */);
-
- /* send Join(S,G) to the current upstream neighbor */
- pim_joinprune_send(&up->rpf, up, 1 /* join */);
-
- pim_upstream_join_timer_restart(up);
+ /*
+ * RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
+ *
+ * Transitions from Joined State
+ *
+ * RPF'(S,G) changes not due to an Assert
+ *
+ * The upstream (S,G) state machine remains in Joined
+ * state. Send Join(S,G) to the new upstream neighbor, which is
+ * the new value of RPF'(S,G). Send Prune(S,G) to the old
+ * upstream neighbor, which is the old value of RPF'(S,G). Set
+ * the Join Timer (JT) to expire after t_periodic seconds.
+ */
+ pim_jp_agg_switch_interface (&old, &up->rpf, up);
+
+ pim_upstream_join_timer_restart(up, &old);
} /* up->join_state == PIM_UPSTREAM_JOINED */
/* FIXME can join_desired actually be changed by pim_rpf_update()
} /* PIM_RPF_CHANGED */
} /* for (qpim_upstream_list) */
-
+
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
+ if (ifp->info)
+ {
+ struct pim_interface *pim_ifp = ifp->info;
+ struct pim_iface_upstream_switch *us;
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->upstream_switch_list, node, us))
+ {
+ struct pim_rpf rpf;
+ rpf.source_nexthop.interface = ifp;
+ rpf.rpf_addr.u.prefix4 = us->address;
+ pim_joinprune_send(&rpf, us->us);
+ pim_jp_agg_clear_group(us->us);
+ }
+ }
}
void