summaryrefslogtreecommitdiffstats
path: root/pimd/pim_macro.c
diff options
context:
space:
mode:
Diffstat (limited to 'pimd/pim_macro.c')
-rw-r--r--pimd/pim_macro.c416
1 files changed, 416 insertions, 0 deletions
diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c
new file mode 100644
index 0000000..2690fca
--- /dev/null
+++ b/pimd/pim_macro.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PIM for Quagga
+ * Copyright (C) 2008 Everton da Silva Marques
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "prefix.h"
+#include "vty.h"
+#include "plist.h"
+
+#include "pimd.h"
+#include "pim_instance.h"
+#include "pim_macro.h"
+#include "pim_iface.h"
+#include "pim_ifchannel.h"
+#include "pim_rp.h"
+
+/*
+ DownstreamJPState(S,G,I) is the per-interface state machine for
+ receiving (S,G) Join/Prune messages.
+
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending
+ DownstreamJPState(*,G,I) is either Join or Prune-Pending
+*/
+static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
+{
+ switch (ch->ifjoin_state) {
+ case PIM_IFJOIN_NOINFO:
+ case PIM_IFJOIN_PRUNE:
+ case PIM_IFJOIN_PRUNE_TMP:
+ case PIM_IFJOIN_PRUNE_PENDING_TMP:
+ return 0;
+ case PIM_IFJOIN_JOIN:
+ case PIM_IFJOIN_PRUNE_PENDING:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ The clause "local_receiver_include(S,G,I)" is true if the IGMP/MLD
+ module or other local membership mechanism has determined that local
+ members on interface I desire to receive traffic sent specifically
+ by S to G.
+*/
+static int local_receiver_include(const struct pim_ifchannel *ch)
+{
+ /* local_receiver_include(S,G,I) ? */
+ return ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE;
+}
+
+/*
+ RFC 4601: 4.1.6. State Summarization Macros
+
+ The set "joins(S,G)" is the set of all interfaces on which the
+ router has received (S,G) Joins:
+
+ joins(S,G) =
+ { all interfaces I such that
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending }
+
+ DownstreamJPState(S,G,I) is either Join or Prune-Pending ?
+*/
+int pim_macro_chisin_joins(const struct pim_ifchannel *ch)
+{
+ return downstream_jpstate_isjoined(ch);
+}
+
+/*
+ RFC 4601: 4.6.5. Assert State Macros
+
+ The set "lost_assert(S,G)" is the set of all interfaces on which the
+ router has received (S,G) joins but has lost an (S,G) assert.
+
+ lost_assert(S,G) =
+ { all interfaces I such that
+ lost_assert(S,G,I) == true }
+
+ bool lost_assert(S,G,I) {
+ if ( RPF_interface(S) == I ) {
+ return false
+ } else {
+ return ( AssertWinner(S,G,I) != NULL AND
+ AssertWinner(S,G,I) != me AND
+ (AssertWinnerMetric(S,G,I) is better
+ than spt_assert_metric(S,I) )
+ }
+ }
+
+ AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
+ packet that won an Assert.
+*/
+int pim_macro_ch_lost_assert(const struct pim_ifchannel *ch)
+{
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_assert_metric spt_assert_metric;
+
+ ifp = ch->interface;
+ if (!ifp) {
+ zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str);
+ return 0; /* false */
+ }
+
+ /* RPF_interface(S) == I ? */
+ if (ch->upstream->rpf.source_nexthop.interface == ifp)
+ return 0; /* false */
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
+ __func__, ch->sg_str, ifp->name);
+ return 0; /* false */
+ }
+
+ if (pim_addr_is_any(ch->ifassert_winner))
+ return 0; /* false */
+
+ /* AssertWinner(S,G,I) == me ? */
+ if (!pim_addr_cmp(ch->ifassert_winner, pim_ifp->primary_address))
+ return 0; /* false */
+
+ spt_assert_metric = pim_macro_spt_assert_metric(
+ &ch->upstream->rpf, pim_ifp->primary_address);
+
+ return pim_assert_metric_better(&ch->ifassert_winner_metric,
+ &spt_assert_metric);
+}
+
+/*
+ RFC 4601: 4.1.6. State Summarization Macros
+
+ pim_include(S,G) =
+ { all interfaces I such that:
+ ( (I_am_DR( I ) AND lost_assert(S,G,I) == false )
+ OR AssertWinner(S,G,I) == me )
+ AND local_receiver_include(S,G,I) }
+
+ AssertWinner(S,G,I) is the IP source address of the Assert(S,G)
+ packet that won an Assert.
+*/
+int pim_macro_chisin_pim_include(const struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp = ch->interface->info;
+ bool mlag_active = false;
+
+ if (!pim_ifp) {
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
+ __func__, ch->sg_str, ch->interface->name);
+ return 0; /* false */
+ }
+
+ /* local_receiver_include(S,G,I) ? */
+ if (!local_receiver_include(ch))
+ return 0; /* false */
+
+ /* OR AssertWinner(S,G,I) == me ? */
+ if (!pim_addr_cmp(ch->ifassert_winner, pim_ifp->primary_address))
+ return 1; /* true */
+
+ /*
+ * When we have a activeactive interface we need to signal
+ * that this interface is interesting to the upstream
+ * decision to JOIN *if* we are syncing over the interface
+ */
+ if (pim_ifp->activeactive) {
+ struct pim_upstream *up = ch->upstream;
+
+ if (PIM_UPSTREAM_FLAG_TEST_MLAG_INTERFACE(up->flags))
+ mlag_active = true;
+ }
+
+ return (
+ /* I_am_DR( I ) ? */
+ (PIM_I_am_DR(pim_ifp) || mlag_active) &&
+ /* lost_assert(S,G,I) == false ? */
+ (!pim_macro_ch_lost_assert(ch)));
+}
+
+int pim_macro_chisin_joins_or_include(const struct pim_ifchannel *ch)
+{
+ if (pim_macro_chisin_joins(ch))
+ return 1; /* true */
+
+ return pim_macro_chisin_pim_include(ch);
+}
+
+/*
+ RFC 4601: 4.6.1. (S,G) Assert Message State Machine
+
+ CouldAssert(S,G,I) =
+ SPTbit(S,G)==TRUE
+ AND (RPF_interface(S) != I)
+ AND (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+ (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
+ (-) lost_assert(*,G)
+ (+) joins(S,G) (+) pim_include(S,G) ) )
+
+ CouldAssert(S,G,I) is true for downstream interfaces that would be in
+ the inherited_olist(S,G) if (S,G) assert information was not taken
+ into account.
+
+ CouldAssert(S,G,I) may be affected by changes in the following:
+
+ pim_ifp->primary_address
+ pim_ifp->pim_dr_addr
+ ch->ifassert_winner_metric
+ ch->ifassert_winner
+ ch->local_ifmembership
+ ch->ifjoin_state
+ ch->upstream->rpf.source_nexthop.mrib_metric_preference
+ ch->upstream->rpf.source_nexthop.mrib_route_metric
+ ch->upstream->rpf.source_nexthop.interface
+*/
+int pim_macro_ch_could_assert_eval(const struct pim_ifchannel *ch)
+{
+ struct interface *ifp;
+
+ ifp = ch->interface;
+ if (!ifp) {
+ zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str);
+ return 0; /* false */
+ }
+
+ /* SPTbit(S,G) == true */
+ if (ch->upstream->sptbit == PIM_UPSTREAM_SPTBIT_FALSE)
+ return 0; /* false */
+
+ /* RPF_interface(S) != I ? */
+ if (ch->upstream->rpf.source_nexthop.interface == ifp)
+ return 0; /* false */
+
+ /* I in joins(S,G) (+) pim_include(S,G) ? */
+ return pim_macro_chisin_joins_or_include(ch);
+}
+
+/*
+ RFC 4601: 4.6.3. Assert Metrics
+
+ spt_assert_metric(S,I) gives the assert metric we use if we're
+ sending an assert based on active (S,G) forwarding state:
+
+ assert_metric
+ spt_assert_metric(S,I) {
+ return {0,MRIB.pref(S),MRIB.metric(S),my_ip_address(I)}
+ }
+*/
+struct pim_assert_metric pim_macro_spt_assert_metric(const struct pim_rpf *rpf,
+ pim_addr ifaddr)
+{
+ struct pim_assert_metric metric;
+
+ metric.rpt_bit_flag = 0;
+ metric.metric_preference = rpf->source_nexthop.mrib_metric_preference;
+ metric.route_metric = rpf->source_nexthop.mrib_route_metric;
+ metric.ip_address = ifaddr;
+
+ return metric;
+}
+
+/*
+ RFC 4601: 4.6.3. Assert Metrics
+
+ An assert metric for (S,G) to include in (or compare against) an
+ Assert message sent on interface I should be computed using the
+ following pseudocode:
+
+ assert_metric my_assert_metric(S,G,I) {
+ if( CouldAssert(S,G,I) == true ) {
+ return spt_assert_metric(S,I)
+ } else if( CouldAssert(*,G,I) == true ) {
+ return rpt_assert_metric(G,I)
+ } else {
+ return infinite_assert_metric()
+ }
+ }
+*/
+struct pim_assert_metric
+pim_macro_ch_my_assert_metric_eval(const struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp;
+
+ pim_ifp = ch->interface->info;
+
+ if (pim_ifp) {
+ if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
+ return pim_macro_spt_assert_metric(
+ &ch->upstream->rpf, pim_ifp->primary_address);
+ }
+ }
+
+ return router->infinite_assert_metric;
+}
+
+/*
+ RFC 4601 4.2. Data Packet Forwarding Rules
+
+ Macro:
+ inherited_olist(S,G) =
+ inherited_olist(S,G,rpt) (+)
+ joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+*/
+static int pim_macro_chisin_inherited_olist(const struct pim_ifchannel *ch)
+{
+ if (pim_macro_ch_lost_assert(ch))
+ return 0; /* false */
+
+ return pim_macro_chisin_joins_or_include(ch);
+}
+
+/*
+ RFC 4601 4.2. Data Packet Forwarding Rules
+ RFC 4601 4.8.2. PIM-SSM-Only Routers
+
+ Additionally, the Packet forwarding rules of Section 4.2 can be
+ simplified in a PIM-SSM-only router:
+
+ iif is the incoming interface of the packet.
+ oiflist = NULL
+ if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
+ oiflist = inherited_olist(S,G)
+ } else if (iif is in inherited_olist(S,G)) {
+ send Assert(S,G) on iif
+ }
+ oiflist = oiflist (-) iif
+ forward packet on all interfaces in oiflist
+
+ Macro:
+ inherited_olist(S,G) =
+ joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
+
+ Note:
+ - The following test is performed as response to WRONGVIF kernel
+ upcall:
+ if (iif is in inherited_olist(S,G)) {
+ send Assert(S,G) on iif
+ }
+ See pim_mroute.c mroute_msg().
+*/
+int pim_macro_chisin_oiflist(const struct pim_ifchannel *ch)
+{
+ if (ch->upstream->join_state == PIM_UPSTREAM_NOTJOINED) {
+ /* oiflist is NULL */
+ return 0; /* false */
+ }
+
+ /* oiflist = oiflist (-) iif */
+ if (ch->interface == ch->upstream->rpf.source_nexthop.interface)
+ return 0; /* false */
+
+ return pim_macro_chisin_inherited_olist(ch);
+}
+
+/*
+ RFC 4601: 4.6.1. (S,G) Assert Message State Machine
+
+ AssertTrackingDesired(S,G,I) =
+ (I in ( ( joins(*,*,RP(G)) (+) joins(*,G) (-) prunes(S,G,rpt) )
+ (+) ( pim_include(*,G) (-) pim_exclude(S,G) )
+ (-) lost_assert(*,G)
+ (+) joins(S,G) ) )
+ OR (local_receiver_include(S,G,I) == true
+ AND (I_am_DR(I) OR (AssertWinner(S,G,I) == me)))
+ OR ((RPF_interface(S) == I) AND (JoinDesired(S,G) == true))
+ OR ((RPF_interface(RP(G)) == I) AND (JoinDesired(*,G) == true)
+ AND (SPTbit(S,G) == false))
+
+ AssertTrackingDesired(S,G,I) is true on any interface in which an
+ (S,G) assert might affect our behavior.
+*/
+int pim_macro_assert_tracking_desired_eval(const struct pim_ifchannel *ch)
+{
+ struct pim_interface *pim_ifp;
+ struct interface *ifp;
+
+ ifp = ch->interface;
+ if (!ifp) {
+ zlog_warn("%s: (S,G)=%s: null interface", __func__, ch->sg_str);
+ return 0; /* false */
+ }
+
+ pim_ifp = ifp->info;
+ if (!pim_ifp) {
+ zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
+ __func__, ch->sg_str, ch->interface->name);
+ return 0; /* false */
+ }
+
+ /* I in joins(S,G) ? */
+ if (pim_macro_chisin_joins(ch))
+ return 1; /* true */
+
+ /* local_receiver_include(S,G,I) ? */
+ if (local_receiver_include(ch)) {
+ /* I_am_DR(I) ? */
+ if (PIM_I_am_DR(pim_ifp))
+ return 1; /* true */
+
+ /* AssertWinner(S,G,I) == me ? */
+ if (!pim_addr_cmp(ch->ifassert_winner,
+ pim_ifp->primary_address))
+ return 1; /* true */
+ }
+
+ /* RPF_interface(S) == I ? */
+ if (ch->upstream->rpf.source_nexthop.interface == ifp) {
+ /* JoinDesired(S,G) ? */
+ if (PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags))
+ return 1; /* true */
+ }
+
+ return 0; /* false */
+}