diff options
| author | David Lamparter <equinox@opensourcerouting.org> | 2015-02-04 07:01:14 +0100 | 
|---|---|---|
| committer | Donald Sharp <sharpd@cumulusnetwroks.com> | 2016-05-25 20:38:32 -0400 | 
| commit | 12e41d03bd49a60da9238694b5d8bd0bde3eff97 (patch) | |
| tree | af456c2fa25230e9e08e48bb72308f3ac113a15e /pimd/pim_macro.c | |
| parent | 5b282f59b1b5331d1738414cadc97cc60fbb193e (diff) | |
pimd: merge pimd as of 2015-01-19
Welcome pimd to the Quagga daemon zoo!
This is a merge of commit 77ae369 ("pimd: Log ifindex found for an
interface when zebra lib reports a new connected address."), with
the intermediate "reconnect" changes removed (c9adf00...d274381).
d274381 is replaced with b162ab7, which includes some changes.  In
addition, 4 reconnect-related changes and 1 cosmetic one have been
bumped out.
The rebase command used to produce the branch that is merged here is:
  git rebase --onto b162ab7 c9adf00 77ae369
Note that 3 patches had their author rewritten from
    "Anonymous SR#108542 <>" (which is not a valid git author ID)
to: "Savannah SR#108542 <nbahr@atcorp.com>" (which is the e-mail address
                               listed in the associated Savannah ticket)
Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
Diffstat (limited to 'pimd/pim_macro.c')
| -rw-r--r-- | pimd/pim_macro.c | 437 | 
1 files changed, 437 insertions, 0 deletions
diff --git a/pimd/pim_macro.c b/pimd/pim_macro.c new file mode 100644 index 0000000000..3f56532526 --- /dev/null +++ b/pimd/pim_macro.c @@ -0,0 +1,437 @@ +/* +  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 */ +} +  | 
