blob: 3f56532526ad7736b79e68c6c74a9dc5b5d4bc7f [file] [log] [blame]
/*
PIM for Quagga
Copyright (C) 2008 Everton da Silva Marques
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
MA 02110-1301 USA
$QuaggaId: $Format:%an, %ai, %h$ $
*/
#include <zebra.h>
#include "log.h"
#include "pim_macro.h"
#include "pimd.h"
#include "pim_str.h"
#include "pim_iface.h"
#include "pim_ifchannel.h"
#define PIM_IFP_I_am_DR(pim_ifp) ((pim_ifp)->pim_dr_addr.s_addr == (pim_ifp)->primary_address.s_addr)
/*
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 ?
*/
static int downstream_jpstate_isjoined(const struct pim_ifchannel *ch)
{
return ch->ifjoin_state != PIM_IFJOIN_NOINFO;
}
/*
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) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): null interface",
__PRETTY_FUNCTION__,
src_str, grp_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) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_str, ifp->name);
return 0; /* false */
}
if (PIM_INADDR_IS_ANY(ch->ifassert_winner))
return 0; /* false */
/* AssertWinner(S,G,I) == me ? */
if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
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;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_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 (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
return 1; /* true */
return (
/* I_am_DR( I ) ? */
PIM_IFP_I_am_DR(pim_ifp)
&&
/* 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;
/* SPTbit(S,G) is always true for PIM-SSM-Only Routers */
ifp = ch->interface;
if (!ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): null interface",
__PRETTY_FUNCTION__,
src_str, grp_str);
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,
struct in_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 qpim_infinite_assert_metric;
}
/*
RFC 4601 4.2. Data Packet Forwarding Rules
RFC 4601 4.8.2. PIM-SSM-Only Routers
Macro:
inherited_olist(S,G) =
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_JOINED) {
/* 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) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): null interface",
__PRETTY_FUNCTION__,
src_str, grp_str);
return 0; /* false */
}
pim_ifp = ifp->info;
if (!pim_ifp) {
char src_str[100];
char grp_str[100];
pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
__PRETTY_FUNCTION__,
src_str, grp_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_IFP_I_am_DR(pim_ifp))
return 1; /* true */
/* AssertWinner(S,G,I) == me ? */
if (ch->ifassert_winner.s_addr == pim_ifp->primary_address.s_addr)
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 */
}