json_object *json_group = NULL;
json_object *json_group_source = NULL;
json_object *json_fhr_sources = NULL;
+ struct pim_secondary_addr *sec_addr;
+ struct listnode *sec_node;
now = pim_time_monotonic_sec();
json_row = json_object_new_object();
json_object_pim_ifp_add(json_row, ifp);
+ if (pim_ifp->sec_addr_list) {
+ json_object *sec_list = NULL;
+
+ sec_list = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) {
+ json_object_array_add(sec_list, json_object_new_string(inet_ntoa(sec_addr->addr)));
+ }
+ json_object_object_add(json_row, "secondaryAddressList", sec_list);
+ }
+
// PIM neighbors
if (pim_ifp->pim_neighbor_list->count) {
json_pim_neighbors = json_object_new_object();
} else {
vty_out(vty, "Interface : %s%s", ifp->name, VTY_NEWLINE);
vty_out(vty, "State : %s%s", if_is_up(ifp) ? "up" : "down", VTY_NEWLINE);
- vty_out(vty, "Address : %s%s", inet_ntoa(ifaddr), VTY_NEWLINE);
+ if (pim_ifp->sec_addr_list) {
+ vty_out(vty, "Address : %s (primary)%s",
+ inet_ntoa(ifaddr), VTY_NEWLINE);
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, sec_node, sec_addr)) {
+ vty_out(vty, " %s%s",
+ inet_ntoa(sec_addr->addr), VTY_NEWLINE);
+ }
+ } else {
+ vty_out(vty, "Address : %s%s", inet_ntoa(ifaddr), VTY_NEWLINE);
+ }
vty_out(vty, "%s", VTY_NEWLINE);
// PIM neighbors
pim_cmd_interface_add (struct interface *ifp, enum pim_interface_type itype)
{
struct pim_interface *pim_ifp = ifp->info;
- struct in_addr null = { .s_addr = 0 };
if (!pim_ifp) {
pim_ifp = pim_if_new(ifp, 0 /* igmp=false */, 1 /* pim=true */);
pim_ifp->itype = itype;
pim_if_addr_add_all(ifp);
pim_if_membership_refresh(ifp);
-
- pim_rp_check_rp (null, pim_ifp->primary_address);
return 1;
}
#include "prefix.h"
#include "vrf.h"
#include "linklist.h"
+#include "plist.h"
#include "pimd.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_time.h"
#include "pim_ssmpingd.h"
+#include "pim_rp.h"
struct interface *pim_regiface = NULL;
struct list *pim_ifchannel_list = NULL;
return changed;
}
+static int pim_sec_addr_comp(const void *p1, const void *p2)
+{
+ const struct pim_secondary_addr *sec1 = p1;
+ const struct pim_secondary_addr *sec2 = p2;
+
+ if (ntohl(sec1->addr.s_addr) < ntohl(sec2->addr.s_addr))
+ return -1;
+
+ if (ntohl(sec1->addr.s_addr) > ntohl(sec2->addr.s_addr))
+ return 1;
+
+ return 0;
+}
+
+static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr)
+{
+ XFREE(MTYPE_PIM_SEC_ADDR, sec_addr);
+}
+
+static struct pim_secondary_addr *
+pim_sec_addr_find(struct pim_interface *pim_ifp, struct in_addr addr)
+{
+ struct pim_secondary_addr *sec_addr;
+ struct listnode *node;
+
+ if (!pim_ifp->sec_addr_list) {
+ return NULL;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+ if (sec_addr->addr.s_addr == addr.s_addr) {
+ return sec_addr;
+ }
+ }
+
+ return NULL;
+}
+
+static void pim_sec_addr_del(struct pim_interface *pim_ifp,
+ struct pim_secondary_addr *sec_addr)
+{
+ listnode_delete(pim_ifp->sec_addr_list, sec_addr);
+ pim_sec_addr_free(sec_addr);
+}
+
+static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct in_addr addr)
+{
+ int changed = 0;
+ struct pim_secondary_addr *sec_addr;
+
+ sec_addr = pim_sec_addr_find(pim_ifp, addr);
+ if (sec_addr) {
+ sec_addr->flags &= ~PIM_SEC_ADDRF_STALE;
+ return changed;
+ }
+
+ if (!pim_ifp->sec_addr_list) {
+ pim_ifp->sec_addr_list = list_new();
+ pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free;
+ pim_ifp->sec_addr_list->cmp = (int (*)(void *, void *))pim_sec_addr_comp;
+ }
+
+ sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr));
+ if (!sec_addr) {
+ if (list_isempty(pim_ifp->sec_addr_list)) {
+ list_free(pim_ifp->sec_addr_list);
+ pim_ifp->sec_addr_list = NULL;
+ }
+ return changed;
+ }
+
+ changed = 1;
+ sec_addr->addr = addr;
+ listnode_add_sort(pim_ifp->sec_addr_list, sec_addr);
+
+ return changed;
+}
+
+static int pim_sec_addr_del_all(struct pim_interface *pim_ifp)
+{
+ int changed = 0;
+
+ if (!pim_ifp->sec_addr_list) {
+ return changed;
+ }
+ if (!list_isempty(pim_ifp->sec_addr_list)) {
+ changed = 1;
+ /* remove all nodes and free up the list itself */
+ list_delete_all_node(pim_ifp->sec_addr_list);
+ list_free(pim_ifp->sec_addr_list);
+ pim_ifp->sec_addr_list = NULL;
+ }
+
+ return changed;
+}
+
+static int pim_sec_addr_update(struct interface *ifp)
+{
+ struct pim_interface *pim_ifp = ifp->info;
+ struct connected *ifc;
+ struct listnode *node;
+ struct listnode *nextnode;
+ struct pim_secondary_addr *sec_addr;
+ int changed = 0;
+
+ if (pim_ifp->sec_addr_list) {
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+ sec_addr->flags |= PIM_SEC_ADDRF_STALE;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ struct prefix *p = ifc->address;
+
+ if (p->family != AF_INET) {
+ continue;
+ }
+
+ if (PIM_INADDR_IS_ANY(p->u.prefix4)) {
+ continue;
+ }
+
+ if (pim_ifp->primary_address.s_addr == p->u.prefix4.s_addr) {
+ /* don't add the primary address into the secondary address list */
+ continue;
+ }
+
+ if (pim_sec_addr_add(pim_ifp, p->u.prefix4)) {
+ changed = 1;
+ }
+ }
+
+ if (pim_ifp->sec_addr_list) {
+ /* Drop stale entries */
+ for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, sec_addr)) {
+ if (sec_addr->flags & PIM_SEC_ADDRF_STALE) {
+ pim_sec_addr_del(pim_ifp, sec_addr);
+ changed = 1;
+ }
+ }
+
+ /* If the list went empty free it up */
+ if (list_isempty(pim_ifp->sec_addr_list)) {
+ list_free(pim_ifp->sec_addr_list);
+ pim_ifp->sec_addr_list = NULL;
+ }
+ }
+
+ return changed;
+}
+
static void detect_secondary_address_change(struct interface *ifp,
+ int force_prim_as_any,
const char *caller)
{
struct pim_interface *pim_ifp;
- int changed;
+ int changed = 0;
pim_ifp = ifp->info;
if (!pim_ifp)
return;
- changed = 1; /* true */
+ if (force_prim_as_any) {
+ /* if primary address is being forced to zero just flush the
+ * secondary address list */
+ pim_sec_addr_del_all(pim_ifp);
+ } else {
+ /* re-evaluate the secondary address list */
+ changed = pim_sec_addr_update(ifp);
+ }
+
if (PIM_DEBUG_ZEBRA)
zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change",
__PRETTY_FUNCTION__, ifp->name);
return;
}
- pim_addr_change(ifp);
+ /* XXX - re-evaluate i_am_rp on addr change */
+ //pim_addr_change(ifp);
}
static void detect_address_change(struct interface *ifp,
return;
}
- detect_secondary_address_change(ifp, caller);
+ detect_secondary_address_change(ifp, force_prim_as_any, caller);
}
void pim_if_addr_add(struct connected *ifc)
struct listnode *nextnode;
int v4_addrs = 0;
int v6_addrs = 0;
+ struct pim_interface *pim_ifp = ifp->info;
+
/* PIM/IGMP enabled ? */
- if (!ifp->info)
+ if (!pim_ifp)
return;
for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) {
if (!v4_addrs && v6_addrs && !if_is_loopback (ifp))
{
- struct pim_interface *pim_ifp = ifp->info;
-
- if (pim_ifp && PIM_IF_TEST_PIM(pim_ifp->options)) {
+ if (PIM_IF_TEST_PIM(pim_ifp->options)) {
/* Interface has a valid primary address ? */
if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) {
}
} /* pim */
}
+
+ pim_rp_check_on_if_add(pim_ifp);
}
void pim_if_addr_del_all(struct interface *ifp)
pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */);
}
+ pim_i_am_rp_re_evaluate();
}
void pim_if_addr_del_all_igmp(struct interface *ifp)
PIM_INTERFACE_SM
};
+enum pim_secondary_addr_flags {
+ PIM_SEC_ADDRF_NONE = 0,
+ PIM_SEC_ADDRF_STALE = (1 << 0)
+};
+
+struct pim_secondary_addr {
+ struct in_addr addr;
+ enum pim_secondary_addr_flags flags;
+};
+
struct pim_interface {
enum pim_interface_type itype;
uint32_t options; /* bit vector */
ifindex_t mroute_vif_index;
struct in_addr primary_address; /* remember addr to detect change */
+ struct list *sec_addr_list; /* list of struct pim_secondary_addr */
int igmp_version; /* IGMP version */
int igmp_default_robustness_variable; /* IGMPv3 QRV */
#include "pim_signals.h"
#include "pim_zebra.h"
#include "pim_msdp.h"
+#include "pim_iface.h"
#include "pim_rp.h"
#ifdef PIM_ZCLIENT_DEBUG
DEFINE_MTYPE(PIMD, PIM_MSDP_SA, "PIM MSDP source-active cache")
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")
DECLARE_MTYPE(PIM_MSDP_SA)
DECLARE_MTYPE(PIM_MSDP_MG)
DECLARE_MTYPE(PIM_MSDP_MG_MBR)
+DECLARE_MTYPE(PIM_SEC_ADDR)
#endif /* _QUAGGA_PIM_MEMORY_H */
#include "pimd.h"
#include "pim_cmd.h"
#include "pim_memory.h"
+#include "pim_iface.h"
#include "pim_rp.h"
#include "pim_str.h"
#include "pim_time.h"
pim_msdp_i_am_rp_changed(void)
{
struct listnode *sanode;
+ struct listnode *nextnode;
struct pim_msdp_sa *sa;
if (!(msdp->flags & PIM_MSDPF_ENABLE)) {
/* re-setup local SA entries */
pim_msdp_sa_local_setup();
- for (ALL_LIST_ELEMENTS_RO(msdp->sa_list, sanode, sa)) {
+ for (ALL_LIST_ELEMENTS(msdp->sa_list, sanode, nextnode, sa)) {
/* purge stale SA entries */
if (sa->flags & PIM_MSDP_SAF_STALE) {
/* clear the stale flag; the entry may be kept even after
* "local-deref" */
sa->flags &= ~PIM_MSDP_SAF_STALE;
+ /* sa_deref can end up freeing the sa; so don't access contents after */
pim_msdp_sa_deref(sa, PIM_MSDP_SAF_LOCAL);
+ } else {
+ /* if the souce is still active check if we can influence SPT */
+ pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change");
}
- /* also check if we can still influence SPT */
- pim_msdp_sa_upstream_update(sa, NULL /* xg_up */, "rp-change");
}
}
XFREE(MTYPE_PIM_MSDP_MG_NAME, mg->mesh_group_name);
XFREE(MTYPE_PIM_MSDP_MG, mg);
+ if (mg->mbr_list)
+ list_free(mg->mbr_list);
msdp->mg = NULL;
}
void
pim_msdp_init(struct thread_master *master)
{
- /* XXX: temporarily enable noisy logs; will be disabled once dev is
- * complete */
- PIM_DO_DEBUG_MSDP_INTERNAL;
-
msdp->master = master;
msdp->peer_hash = hash_create(pim_msdp_peer_hash_key_make,
#include "pim_msg.h"
#include "pim_util.h"
#include "pim_str.h"
+#include "pim_iface.h"
#include "pim_rp.h"
#include "pim_rpf.h"
#include "pimd.h"
#include "pim_vty.h"
#include "pim_str.h"
+#include "pim_iface.h"
#include "pim_rp.h"
#include "pim_str.h"
#include "pim_rpf.h"
pim_rp_refresh_group_to_rp_mapping();
}
+static int
+pim_rp_check_interface_addrs(struct rp_info *rp_info,
+ struct pim_interface *pim_ifp)
+{
+ struct listnode *node;
+ struct pim_secondary_addr *sec_addr;
+
+ if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
+ return 1;
+
+ if (!pim_ifp->sec_addr_list) {
+ return 0;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) {
+ if (sec_addr->addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static void
pim_rp_check_interfaces (struct rp_info *rp_info)
{
struct listnode *node;
struct interface *ifp;
+ rp_info->i_am_rp = 0;
for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp))
{
struct pim_interface *pim_ifp = ifp->info;
if (!pim_ifp)
continue;
- if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
- rp_info->i_am_rp = 1;
+ if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) {
+ rp_info->i_am_rp = 1;
+ }
}
}
}
/*
- * Checks to see if we should elect ourself the actual RP
+ * Checks to see if we should elect ourself the actual RP when new if
+ * addresses are added against an interface.
*/
void
-pim_rp_check_rp (struct in_addr old, struct in_addr new)
+pim_rp_check_on_if_add(struct pim_interface *pim_ifp)
{
struct listnode *node;
struct rp_info *rp_info;
if (qpim_rp_list == NULL)
return;
- for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info))
- {
+ for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) {
+ if (pim_rpf_addr_is_inaddr_none (&rp_info->rp))
+ continue;
+
+ /* if i_am_rp is already set nothing to be done (adding new addresses
+ * is not going to make a difference). */
+ if (rp_info->i_am_rp) {
+ continue;
+ }
+
+ if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) {
+ i_am_rp_changed = true;
+ rp_info->i_am_rp = 1;
if (PIM_DEBUG_ZEBRA) {
- char sold[INET_ADDRSTRLEN];
- char snew[INET_ADDRSTRLEN];
char rp[PREFIX_STRLEN];
pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp));
- pim_inet4_dump("<old?>", old, sold, sizeof(sold));
- pim_inet4_dump("<new?>", new, snew, sizeof(snew));
- zlog_debug("%s: %s for old %s new %s", __func__, rp, sold, snew );
+ zlog_debug("%s: %s: i am rp", __func__, rp);
}
- if (pim_rpf_addr_is_inaddr_none (&rp_info->rp))
- continue;
+ }
+ }
- if (new.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
- {
- if (!rp_info->i_am_rp) {
- i_am_rp_changed = true;
- }
- rp_info->i_am_rp = 1;
- }
+ if (i_am_rp_changed) {
+ pim_msdp_i_am_rp_changed();
+ }
+}
- if (old.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr)
- {
- if (rp_info->i_am_rp) {
- i_am_rp_changed = true;
- }
- rp_info->i_am_rp = 0;
+/* up-optimized re-evaluation of "i_am_rp". this is used when ifaddresses
+ * are removed. Removing numbers is an uncommon event in an active network
+ * so I have made no attempt to optimize it. */
+void
+pim_i_am_rp_re_evaluate(void)
+{
+ struct listnode *node;
+ struct rp_info *rp_info;
+ bool i_am_rp_changed = false;
+ int old_i_am_rp;
+
+ if (qpim_rp_list == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(qpim_rp_list, node, rp_info)) {
+ if (pim_rpf_addr_is_inaddr_none(&rp_info->rp))
+ continue;
+
+ old_i_am_rp = rp_info->i_am_rp;
+ pim_rp_check_interfaces(rp_info);
+
+ if (old_i_am_rp != rp_info->i_am_rp) {
+ i_am_rp_changed = true;
+ if (PIM_DEBUG_ZEBRA) {
+ char rp[PREFIX_STRLEN];
+ pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp));
+ if (rp_info->i_am_rp) {
+ zlog_debug("%s: %s: i am rp", __func__, rp);
+ } else {
+ zlog_debug("%s: %s: i am no longer rp", __func__, rp);
}
+ }
}
+ }
- if (i_am_rp_changed) {
- pim_msdp_i_am_rp_changed();
- }
+ if (i_am_rp_changed) {
+ pim_msdp_i_am_rp_changed();
+ }
}
/*
int pim_rp_setup (void);
-void pim_rp_check_rp (struct in_addr old, struct in_addr new);
int pim_rp_i_am_rp (struct in_addr group);
+void pim_rp_check_on_if_add(struct pim_interface *pim_ifp);
+void pim_i_am_rp_re_evaluate(void);
int pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr);
{
struct connected *c;
struct prefix *p;
- struct in_addr old = { .s_addr = 0 };
+ struct pim_interface *pim_ifp;
/*
zebra api notifies address adds/dels events by using the same call
if (!c)
return 0;
+ pim_ifp = c->ifp->info;
p = c->address;
if (PIM_DEBUG_ZEBRA) {
if (p->family != AF_INET)
{
- struct pim_interface *pim_ifp = c->ifp->info;
struct listnode *cnode;
struct connected *conn;
int v4addrs = 0;
return 0;
}
- pim_rp_check_rp (old, p->u.prefix4);
-
if (!CHECK_FLAG(c->flags, ZEBRA_IFA_SECONDARY)) {
/* trying to add primary address */
/* but we had a primary address already */
char buf[BUFSIZ];
- char old[INET_ADDRSTRLEN];
prefix2str(p, buf, BUFSIZ);
- pim_inet4_dump("<old?>", primary_addr, old, sizeof(old));
- zlog_warn("%s: %s primary addr old=%s: forcing secondary flag on new=%s",
+ zlog_warn("%s: %s : forcing secondary flag on %s",
__PRETTY_FUNCTION__,
- c->ifp->name, old, buf);
+ c->ifp->name, buf);
}
SET_FLAG(c->flags, ZEBRA_IFA_SECONDARY);
}
}
pim_if_addr_add(c);
+ if (pim_ifp)
+ pim_rp_check_on_if_add(pim_ifp);
if (if_is_loopback (c->ifp))
{
{
struct connected *c;
struct prefix *p;
- struct in_addr new = { .s_addr = 0 };
/*
zebra api notifies address adds/dels events by using the same call
#endif
}
- pim_rp_check_rp (p->u.prefix4, new);
pim_if_addr_del(c, 0);
+ pim_i_am_rp_re_evaluate();
return 0;
}