up = pim_upstream_find_or_add(
&sg, ifp, PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
__PRETTY_FUNCTION__);
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
return 0;
}
pim_upstream_keep_alive_timer_start(up, pim_ifp->pim->keep_alive_time);
up->channel_oil->cc.pktcnt++;
- PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
// resolve mfcc_parent prior to mroute_add in channel_add_oif
if (up->rpf.source_nexthop.interface &&
up->channel_oil->oil.mfcc_parent >= MAXVIFS) {
pim_upstream_inherited_olist(pim_ifp->pim, up);
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil,
+ pim_upstream_mroute_add(up->channel_oil,
__PRETTY_FUNCTION__);
} else {
if (I_am_RP(pim_ifp->pim, up->sg.grp)) {
up->channel_oil->cc.pktcnt++;
pim_register_join(up);
pim_upstream_inherited_olist(pim_ifp->pim, up);
+ if (!up->channel_oil->installed)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
// Send the packet to the RP
pim_mroute_msg_wholepkt(fd, ifp, buf);
PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE,
__PRETTY_FUNCTION__, NULL);
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil,
+ __PRETTY_FUNCTION__);
}
return 0;
}
}
-int pim_mroute_add(struct channel_oil *c_oil, const char *name)
+/* This function must not be called directly 0
+ * use pim_upstream_mroute_add or pim_static_mroute_add instead
+ */
+static int pim_mroute_add(struct channel_oil *c_oil, const char *name)
{
struct pim_instance *pim = c_oil->pim;
struct mfcctl tmp_oil;
pim->mroute_add_last = pim_time_monotonic_sec();
++pim->mroute_add_events;
- /* Do not install route if incoming interface is undefined. */
- if (c_oil->oil.mfcc_parent >= MAXVIFS) {
- if (PIM_DEBUG_MROUTE) {
- char buf[1000];
- zlog_debug(
- "%s(%s) %s Attempting to add vifi that is invalid to mroute table",
- __PRETTY_FUNCTION__, name,
- pim_channel_oil_dump(c_oil, buf, sizeof(buf)));
- }
- return -2;
- }
-
/* Copy the oil to a temporary structure to fixup (without need to
* later restore) before sending the mroute add to the dataplane
*/
return 0;
}
+/* In the case of "PIM state machine" added mroutes an upstream entry
+ * must be present to decide on the SPT-forwarding vs. RPT-forwarding.
+ */
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+ vifi_t iif = MAXVIFS;
+ char buf[1000];
+ struct interface *ifp = NULL;
+ struct pim_interface *pim_ifp;
+ struct pim_upstream *up = c_oil->up;
+
+ if (up) {
+ if (PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags)) {
+ if (up->parent)
+ ifp = up->parent->rpf.source_nexthop.interface;
+ } else {
+ ifp = up->rpf.source_nexthop.interface;
+ }
+ if (ifp) {
+ pim_ifp = (struct pim_interface *)ifp->info;
+ if (pim_ifp)
+ iif = pim_ifp->mroute_vif_index;
+ }
+ }
+
+ c_oil->oil.mfcc_parent = iif;
+
+ if (c_oil->oil.mfcc_parent >= MAXVIFS) {
+ /* the c_oil cannot be installed as a mroute yet */
+ if (PIM_DEBUG_MROUTE)
+ zlog_debug(
+ "%s(%s) %s mroute not ready to be installed; %s",
+ __PRETTY_FUNCTION__, name,
+ pim_channel_oil_dump(c_oil, buf,
+ sizeof(buf)),
+ c_oil->installed ?
+ "uninstall" : "skip");
+ /* if already installed flush it out as we are going to stop
+ * updates to it leaving it in a stale state
+ */
+ if (c_oil->installed)
+ pim_mroute_del(c_oil, name);
+ /* return success (skipped) */
+ return 0;
+ }
+
+ return pim_mroute_add(c_oil, name);
+}
+
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name)
+{
+ return pim_mroute_add(c_oil, name);
+}
+
int pim_mroute_del(struct channel_oil *c_oil, const char *name)
{
struct pim_instance *pim = c_oil->pim;
unsigned char flags);
int pim_mroute_del_vif(struct interface *ifp);
-int pim_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_upstream_mroute_add(struct channel_oil *c_oil, const char *name);
+int pim_static_mroute_add(struct channel_oil *c_oil, const char *name);
int pim_mroute_del(struct channel_oil *c_oil, const char *name);
void pim_mroute_update_counters(struct channel_oil *c_oil);
if (input_vif_index == MAXVIFS)
pim_mroute_del(c_oil, name);
else
- pim_mroute_add(c_oil, name);
+ pim_upstream_mroute_add(c_oil, name);
} else
if (old_vif_index == MAXVIFS)
- pim_mroute_add(c_oil, name);
+ pim_upstream_mroute_add(c_oil, name);
return;
}
/* clear mute; will be re-evaluated when the OIF becomes valid again */
channel_oil->oif_flags[pim_ifp->mroute_vif_index] &= ~PIM_OIF_FLAG_MUTE;
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
c_oil->oif_flags[pim_ifp->mroute_vif_index] &=
~PIM_OIF_FLAG_MUTE;
- pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
}
/* pim_upstream has been set or cleared on the c_oil. re-eval mute state
* valid to get installed in kernel.
*/
if (channel_oil->oil.mfcc_parent != MAXVIFS) {
- if (pim_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
+ if (pim_upstream_mroute_add(channel_oil, __PRETTY_FUNCTION__)) {
if (PIM_DEBUG_MROUTE) {
char group_str[INET_ADDRSTRLEN];
char source_str[INET_ADDRSTRLEN];
s_route->c_oil.pim = pim;
- if (pim_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
+ if (pim_static_mroute_add(&s_route->c_oil, __PRETTY_FUNCTION__)) {
char gifaddr_str[INET_ADDRSTRLEN];
char sifaddr_str[INET_ADDRSTRLEN];
pim_inet4_dump("<ifaddr?>", group, gifaddr_str,
if (s_route->c_oil.oil_ref_count <= 0
? pim_mroute_del(&s_route->c_oil,
__PRETTY_FUNCTION__)
- : pim_mroute_add(&s_route->c_oil,
+ : pim_static_mroute_add(&s_route->c_oil,
__PRETTY_FUNCTION__)) {
char gifaddr_str[INET_ADDRSTRLEN];
char sifaddr_str[INET_ADDRSTRLEN];
}
}
+/* RFC7761, Section 4.2 “Data Packet Forwarding Rules” says we should
+ * forward a S -
+ * 1. along the SPT if SPTbit is set
+ * 2. and along the RPT if SPTbit is not set
+ * If forwarding is hw accelerated i.e. control and dataplane components
+ * are separate you may not be able to reliably set SPT bit on intermediate
+ * routers while still fowarding on the (S,G,rpt).
+ *
+ * This macro is a slight deviation on the RFC and uses "traffic-agnostic"
+ * criteria to decide between using the RPT vs. SPT for forwarding.
+ */
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+ bool update_mroute)
+{
+ bool old_use_rpt;
+ bool new_use_rpt;
+
+ if (up->sg.src.s_addr == INADDR_ANY)
+ return;
+
+ old_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+
+ /* We will use the SPT (IIF=RPF_interface(S) if -
+ * 1. We have decided to join the SPT
+ * 2. We are FHR
+ * 3. Source is directly connected
+ * 4. We are RP (parent's IIF is lo or vrf-device)
+ * In all other cases the source will stay along the RPT and
+ * IIF=RPF_interface(RP).
+ */
+ if (up->join_state == PIM_UPSTREAM_JOINED ||
+ PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) ||
+ pim_if_connected_to_source(
+ up->rpf.source_nexthop.interface,
+ up->sg.src) ||
+ /* XXX - need to switch this to a more efficient
+ * lookup API
+ */
+ I_am_RP(up->pim, up->sg.grp))
+ /* use SPT */
+ PIM_UPSTREAM_FLAG_UNSET_USE_RPT(up->flags);
+ else
+ /* use RPT */
+ PIM_UPSTREAM_FLAG_SET_USE_RPT(up->flags);
+
+ new_use_rpt = !!PIM_UPSTREAM_FLAG_TEST_USE_RPT(up->flags);
+ if (old_use_rpt != new_use_rpt) {
+ if (PIM_DEBUG_PIM_EVENTS)
+ zlog_debug("%s switched from %s to %s",
+ up->sg_str,
+ old_use_rpt?"RPT":"SPT",
+ new_use_rpt?"RPT":"SPT");
+ if (update_mroute)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
+ }
+}
+
void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
enum pim_upstream_state new_state)
{
0 /* prune */);
join_timer_stop(up);
}
+
+ if (old_state != new_state)
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
}
int pim_upstream_compare(void *arg1, void *arg2)
up = XCALLOC(MTYPE_PIM_UPSTREAM, sizeof(*up));
+ up->pim = pim;
up->sg = *sg;
pim_str_sg_set(sg, up->sg_str);
if (ch)
pim_ifp->mroute_vif_index,
__PRETTY_FUNCTION__);
}
+ pim_upstream_update_use_rpt(up,
+ false /*update_mroute*/);
}
listnode_add_sort(pim->upstream_list, up);
}
struct pim_upstream *pim_upstream_find_or_add(struct prefix_sg *sg,
- struct interface *incoming,
- int flags, const char *name)
+ struct interface *incoming,
+ int flags, const char *name)
{
- struct pim_upstream *up;
- struct pim_interface *pim_ifp;
-
- pim_ifp = incoming->info;
-
- up = pim_upstream_find(pim_ifp->pim, sg);
+ struct pim_interface *pim_ifp = incoming->info;
- if (up) {
- if (!(up->flags & flags)) {
- up->flags |= flags;
- up->ref_count++;
- if (PIM_DEBUG_PIM_TRACE)
- zlog_debug(
- "%s(%s): upstream %s ref count %d increment",
- __PRETTY_FUNCTION__, name, up->sg_str,
- up->ref_count);
- }
- } else
- up = pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
- NULL);
-
- return up;
+ return (pim_upstream_add(pim_ifp->pim, sg, incoming, flags, name,
+ NULL));
}
void pim_upstream_ref(struct pim_upstream *up, int flags, const char *name)
{
+ /* when we go from non-FHR to FHR we need to re-eval traffic
+ * forwarding path
+ */
+ if (!PIM_UPSTREAM_FLAG_TEST_FHR(up->flags) &&
+ PIM_UPSTREAM_FLAG_TEST_FHR(flags)) {
+ PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
+ }
+
up->flags |= flags;
++up->ref_count;
if (PIM_DEBUG_PIM_TRACE)
PIM_UPSTREAM_FLAG_SET_FHR(up->flags);
if (up->reg_state == PIM_REG_NOINFO)
pim_register_join(up);
+ pim_upstream_update_use_rpt(up, true /*update_mroute*/);
}
}
* associated with an upstream
*/
#define PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE (1 << 19)
+/* By default as SG entry will use the SPT for forwarding traffic
+ * unless it was setup as a result of a Prune(S,G,rpt) from a
+ * downstream router and has JoinDesired(S,G) as False.
+ * This flag is only relevant for (S,G) entries.
+ */
+#define PIM_UPSTREAM_FLAG_MASK_USE_RPT (1 << 20)
#define PIM_UPSTREAM_FLAG_ALL 0xFFFFFFFF
#define PIM_UPSTREAM_FLAG_TEST_MLAG_VXLAN(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
#define PIM_UPSTREAM_FLAG_TEST_MLAG_NON_DF(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
#define PIM_UPSTREAM_FLAG_TEST_SRC_NOCACHE(flags) ((flags) &PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE)
+#define PIM_UPSTREAM_FLAG_TEST_USE_RPT(flags) ((flags) & PIM_UPSTREAM_FLAG_MASK_USE_RPT)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_SET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
#define PIM_UPSTREAM_FLAG_SET_SRC_VXLAN_TERM(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM)
#define PIM_UPSTREAM_FLAG_SET_MLAG_VXLAN(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
#define PIM_UPSTREAM_FLAG_SET_MLAG_NON_DF(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
+#define PIM_UPSTREAM_FLAG_SET_USE_RPT(flags) ((flags) |= PIM_UPSTREAM_FLAG_MASK_USE_RPT)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED)
#define PIM_UPSTREAM_FLAG_UNSET_DR_JOIN_DESIRED_UPDATED(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_DR_JOIN_DESIRED_UPDATED)
#define PIM_UPSTREAM_FLAG_UNSET_MLAG_VXLAN(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_VXLAN)
#define PIM_UPSTREAM_FLAG_UNSET_MLAG_NON_DF(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_MLAG_NON_DF)
#define PIM_UPSTREAM_FLAG_UNSET_SRC_NOCACHE(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_SRC_NOCACHE)
+#define PIM_UPSTREAM_FLAG_UNSET_USE_RPT(flags) ((flags) &= ~PIM_UPSTREAM_FLAG_MASK_USE_RPT)
enum pim_upstream_state {
PIM_UPSTREAM_NOTJOINED,
*/
struct pim_upstream {
+ struct pim_instance *pim;
struct pim_upstream *parent;
struct in_addr upstream_addr; /* Who we are talking to */
struct in_addr upstream_register; /*Who we received a register from*/
struct pim_upstream *up);
void pim_upstream_fill_static_iif(struct pim_upstream *up,
struct interface *incoming);
+void pim_upstream_update_use_rpt(struct pim_upstream *up,
+ bool update_mroute);
#endif /* PIM_UPSTREAM_H */
pim_delete_tracked_nexthop(vxlan_sg->pim,
&nht_p, up, NULL, false);
}
+ /* We are acting FHR; clear out use_rpt setting if any */
+ pim_upstream_update_use_rpt(up, false /*update_mroute*/);
pim_upstream_ref(up, flags, __PRETTY_FUNCTION__);
vxlan_sg->up = up;
pim_vxlan_orig_mr_up_iif_update(vxlan_sg);
/* update the inherited OIL */
pim_upstream_inherited_olist(vxlan_sg->pim, up);
+ if (!up->channel_oil->installed)
+ pim_upstream_mroute_add(up->channel_oil, __func__);
}
static void pim_vxlan_orig_mr_oif_add(struct pim_vxlan_sg *vxlan_sg)
* so install it.
*/
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil,
+ pim_upstream_mroute_add(up->channel_oil,
__PRETTY_FUNCTION__);
/*
}
if (!up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil,
+ __PRETTY_FUNCTION__);
}
/* FIXME can join_desired actually be changed by pim_rpf_update()
if (input_iface_vif_index == c_oil->oil.mfcc_parent) {
if (!c_oil->installed)
- pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
/* RPF unchanged */
return;
/* update iif vif_index */
pim_channel_oil_change_iif(c_oil->pim, c_oil, input_iface_vif_index,
__PRETTY_FUNCTION__);
- pim_mroute_add(c_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(c_oil, __PRETTY_FUNCTION__);
}
void pim_scan_oil(struct pim_instance *pim)
PIM_OIF_FLAG_PROTO_PIM, __func__);
if (install_it && !up->channel_oil->installed)
- pim_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
+ pim_upstream_mroute_add(up->channel_oil, __PRETTY_FUNCTION__);
}
void pim_zebra_zclient_update(struct vty *vty)