File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / quagga / pimd / pim_ifchannel.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:09:11 2016 UTC (8 years, 7 months ago) by misho
Branches: quagga, MAIN
CVS tags: v1_0_20160315, HEAD
quagga 1.0.20160315

    1: /*
    2:   PIM for Quagga
    3:   Copyright (C) 2008  Everton da Silva Marques
    4: 
    5:   This program is free software; you can redistribute it and/or modify
    6:   it under the terms of the GNU General Public License as published by
    7:   the Free Software Foundation; either version 2 of the License, or
    8:   (at your option) any later version.
    9: 
   10:   This program is distributed in the hope that it will be useful, but
   11:   WITHOUT ANY WARRANTY; without even the implied warranty of
   12:   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   13:   General Public License for more details.
   14:   
   15:   You should have received a copy of the GNU General Public License
   16:   along with this program; see the file COPYING; if not, write to the
   17:   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
   18:   MA 02110-1301 USA
   19:   
   20:   $QuaggaId: $Format:%an, %ai, %h$ $
   21: */
   22: 
   23: #include <zebra.h>
   24: 
   25: #include "linklist.h"
   26: #include "thread.h"
   27: #include "memory.h"
   28: 
   29: #include "pimd.h"
   30: #include "pim_str.h"
   31: #include "pim_iface.h"
   32: #include "pim_ifchannel.h"
   33: #include "pim_zebra.h"
   34: #include "pim_time.h"
   35: #include "pim_msg.h"
   36: #include "pim_pim.h"
   37: #include "pim_join.h"
   38: #include "pim_rpf.h"
   39: #include "pim_macro.h"
   40: 
   41: void pim_ifchannel_free(struct pim_ifchannel *ch)
   42: {
   43:   zassert(!ch->t_ifjoin_expiry_timer);
   44:   zassert(!ch->t_ifjoin_prune_pending_timer);
   45:   zassert(!ch->t_ifassert_timer);
   46: 
   47:   XFREE(MTYPE_PIM_IFCHANNEL, ch);
   48: }
   49: 
   50: void pim_ifchannel_delete(struct pim_ifchannel *ch)
   51: {
   52:   struct pim_interface *pim_ifp;
   53: 
   54:   pim_ifp = ch->interface->info;
   55:   zassert(pim_ifp);
   56: 
   57:   if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
   58:     pim_upstream_update_join_desired(ch->upstream);
   59:   }
   60: 
   61:   pim_upstream_del(ch->upstream);
   62: 
   63:   THREAD_OFF(ch->t_ifjoin_expiry_timer);
   64:   THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
   65:   THREAD_OFF(ch->t_ifassert_timer);
   66: 
   67:   /*
   68:     notice that listnode_delete() can't be moved
   69:     into pim_ifchannel_free() because the later is
   70:     called by list_delete_all_node()
   71:   */
   72:   listnode_delete(pim_ifp->pim_ifchannel_list, ch);
   73: 
   74:   pim_ifchannel_free(ch);
   75: }
   76: 
   77: #define IFCHANNEL_NOINFO(ch)					\
   78:   (								\
   79:    ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)	\
   80:    &&								\
   81:    ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO)			\
   82:    &&								\
   83:    ((ch)->ifassert_state == PIM_IFASSERT_NOINFO)		\
   84:    )
   85:    
   86: static void delete_on_noinfo(struct pim_ifchannel *ch)
   87: {
   88:   if (IFCHANNEL_NOINFO(ch)) {
   89: 
   90:     /* In NOINFO state, timers should have been cleared */
   91:     zassert(!ch->t_ifjoin_expiry_timer);
   92:     zassert(!ch->t_ifjoin_prune_pending_timer);
   93:     zassert(!ch->t_ifassert_timer);
   94:     
   95:     pim_ifchannel_delete(ch);
   96:   }
   97: }
   98: 
   99: void pim_ifchannel_ifjoin_switch(const char *caller,
  100: 				 struct pim_ifchannel *ch,
  101: 				 enum pim_ifjoin_state new_state)
  102: {
  103:   enum pim_ifjoin_state old_state = ch->ifjoin_state;
  104: 
  105:   if (old_state == new_state) {
  106:     if (PIM_DEBUG_PIM_EVENTS) {
  107:       zlog_debug("%s calledby %s: non-transition on state %d (%s)",
  108: 		 __PRETTY_FUNCTION__, caller, new_state,
  109: 		 pim_ifchannel_ifjoin_name(new_state));
  110:     }
  111:     return;
  112:   }
  113: 
  114:   zassert(old_state != new_state);
  115: 
  116:   ch->ifjoin_state = new_state;
  117: 
  118:   /* Transition to/from NOINFO ? */
  119:   if (
  120:       (old_state == PIM_IFJOIN_NOINFO)
  121:       ||
  122:       (new_state == PIM_IFJOIN_NOINFO)
  123:       ) {
  124: 
  125:     if (PIM_DEBUG_PIM_EVENTS) {
  126:       char src_str[100];
  127:       char grp_str[100];
  128:       pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
  129:       pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
  130:       zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s",
  131: 		 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
  132: 		 src_str, grp_str, ch->interface->name);
  133:     }
  134: 
  135:     /*
  136:       Record uptime of state transition to/from NOINFO
  137:     */
  138:     ch->ifjoin_creation = pim_time_monotonic_sec();
  139: 
  140:     pim_upstream_update_join_desired(ch->upstream);
  141:     pim_ifchannel_update_could_assert(ch);
  142:     pim_ifchannel_update_assert_tracking_desired(ch);
  143:   }
  144: }
  145: 
  146: const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
  147: {
  148:   switch (ifjoin_state) {
  149:   case PIM_IFJOIN_NOINFO:        return "NOINFO";
  150:   case PIM_IFJOIN_JOIN:          return "JOIN";
  151:   case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
  152:   }
  153: 
  154:   return "ifjoin_bad_state";
  155: }
  156: 
  157: const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
  158: {
  159:   switch (ifassert_state) {
  160:   case PIM_IFASSERT_NOINFO:      return "NOINFO";
  161:   case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
  162:   case PIM_IFASSERT_I_AM_LOSER:  return "LOSER";
  163:   }
  164: 
  165:   return "ifassert_bad_state";
  166: }
  167: 
  168: /*
  169:   RFC 4601: 4.6.5.  Assert State Macros
  170: 
  171:   AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
  172:   defaults to Infinity when in the NoInfo state.
  173: */
  174: void reset_ifassert_state(struct pim_ifchannel *ch)
  175: {
  176:   THREAD_OFF(ch->t_ifassert_timer);
  177: 
  178:   pim_ifassert_winner_set(ch,
  179: 			  PIM_IFASSERT_NOINFO,
  180: 			  qpim_inaddr_any,
  181: 			  qpim_infinite_assert_metric);
  182: }
  183: 
  184: static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp,
  185: 					       struct in_addr source_addr,
  186: 					       struct in_addr group_addr)
  187: {
  188:   struct pim_ifchannel *ch;
  189:   struct pim_interface *pim_ifp;
  190:   struct pim_upstream  *up;
  191: 
  192:   pim_ifp = ifp->info;
  193:   zassert(pim_ifp);
  194: 
  195:   up = pim_upstream_add(source_addr, group_addr);
  196:   if (!up) {
  197:     char src_str[100];
  198:     char grp_str[100];
  199:     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  200:     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  201:     zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s",
  202: 	     __PRETTY_FUNCTION__,
  203: 	     src_str, grp_str, ifp->name);
  204:     return 0;
  205:   }
  206: 
  207:   ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
  208:   if (!ch) {
  209:     zlog_err("%s: PIM XMALLOC(%zu) failure",
  210: 	     __PRETTY_FUNCTION__, sizeof(*ch));
  211:     return 0;
  212:   }
  213: 
  214:   ch->flags                        = 0;
  215:   ch->upstream                     = up;
  216:   ch->interface                    = ifp;
  217:   ch->source_addr                  = source_addr;
  218:   ch->group_addr                   = group_addr;
  219:   ch->local_ifmembership           = PIM_IFMEMBERSHIP_NOINFO;
  220: 
  221:   ch->ifjoin_state                 = PIM_IFJOIN_NOINFO;
  222:   ch->t_ifjoin_expiry_timer        = 0;
  223:   ch->t_ifjoin_prune_pending_timer = 0;
  224:   ch->ifjoin_creation              = 0;
  225: 
  226:   /* Assert state */
  227:   ch->t_ifassert_timer   = 0;
  228:   reset_ifassert_state(ch);
  229:   if (pim_macro_ch_could_assert_eval(ch))
  230:     PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
  231:   else
  232:     PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
  233: 
  234:   if (pim_macro_assert_tracking_desired_eval(ch))
  235:     PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
  236:   else
  237:     PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
  238: 
  239:   ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
  240: 
  241:   /* Attach to list */
  242:   listnode_add(pim_ifp->pim_ifchannel_list, ch);
  243: 
  244:   zassert(IFCHANNEL_NOINFO(ch));
  245: 
  246:   return ch;
  247: }
  248: 
  249: struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
  250: 					 struct in_addr source_addr,
  251: 					 struct in_addr group_addr)
  252: {
  253:   struct pim_interface *pim_ifp;
  254:   struct listnode      *ch_node;
  255:   struct pim_ifchannel *ch;
  256: 
  257:   zassert(ifp);
  258: 
  259:   pim_ifp = ifp->info;
  260: 
  261:   if (!pim_ifp) {
  262:     char src_str[100];
  263:     char grp_str[100];
  264:     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  265:     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  266:     zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s",
  267: 	      __PRETTY_FUNCTION__,
  268: 	      src_str, grp_str,
  269: 	      ifp->name);
  270:     return 0;
  271:   }
  272: 
  273:   for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
  274:     if (
  275: 	(source_addr.s_addr == ch->source_addr.s_addr) &&
  276: 	(group_addr.s_addr == ch->group_addr.s_addr)
  277: 	) {
  278:       return ch;
  279:     }
  280:   }
  281: 
  282:   return 0;
  283: }
  284: 
  285: static void ifmembership_set(struct pim_ifchannel *ch,
  286: 			     enum pim_ifmembership membership)
  287: {
  288:   if (ch->local_ifmembership == membership)
  289:     return;
  290: 
  291:   if (PIM_DEBUG_PIM_EVENTS) {
  292:     char src_str[100];
  293:     char grp_str[100];
  294:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
  295:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
  296:     zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s",
  297: 	       __PRETTY_FUNCTION__,
  298: 	       src_str, grp_str,
  299: 	       membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
  300: 	       ch->interface->name);
  301:   }
  302:   
  303:   ch->local_ifmembership = membership;
  304: 
  305:   pim_upstream_update_join_desired(ch->upstream);
  306:   pim_ifchannel_update_could_assert(ch);
  307:   pim_ifchannel_update_assert_tracking_desired(ch);
  308: }
  309: 
  310: 
  311: void pim_ifchannel_membership_clear(struct interface *ifp)
  312: {
  313:   struct pim_interface *pim_ifp;
  314:   struct listnode      *ch_node;
  315:   struct pim_ifchannel *ch;
  316: 
  317:   pim_ifp = ifp->info;
  318:   zassert(pim_ifp);
  319: 
  320:   for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
  321:     ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
  322:   }
  323: }
  324: 
  325: void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
  326: {
  327:   struct pim_interface *pim_ifp;
  328:   struct listnode      *node;
  329:   struct listnode      *next_node;
  330:   struct pim_ifchannel *ch;
  331: 
  332:   pim_ifp = ifp->info;
  333:   zassert(pim_ifp);
  334: 
  335:   for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
  336:     delete_on_noinfo(ch);
  337:   }
  338: }
  339: 
  340: struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
  341: 					struct in_addr source_addr,
  342: 					struct in_addr group_addr)
  343: {
  344:   struct pim_ifchannel *ch;
  345:   char src_str[100];
  346:   char grp_str[100];
  347: 
  348:   ch = pim_ifchannel_find(ifp, source_addr, group_addr);
  349:   if (ch)
  350:     return ch;
  351: 
  352:   ch = pim_ifchannel_new(ifp, source_addr, group_addr);
  353:   if (ch)
  354:     return ch;
  355:     
  356:   pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  357:   pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  358:   zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s",
  359: 	    __PRETTY_FUNCTION__,
  360: 	    src_str, grp_str, ifp->name);
  361: 
  362:   return 0;
  363: }
  364: 
  365: static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
  366: {
  367:   pim_forward_stop(ch);
  368:   pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
  369:   delete_on_noinfo(ch);
  370: }
  371: 
  372: static int on_ifjoin_expiry_timer(struct thread *t)
  373: {
  374:   struct pim_ifchannel *ch;
  375: 
  376:   zassert(t);
  377:   ch = THREAD_ARG(t);
  378:   zassert(ch);
  379: 
  380:   ch->t_ifjoin_expiry_timer = 0;
  381: 
  382:   zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
  383: 
  384:   ifjoin_to_noinfo(ch);
  385:   /* ch may have been deleted */
  386: 
  387:   return 0;
  388: }
  389: 
  390: static void prune_echo(struct interface *ifp,
  391: 		       struct in_addr source_addr,
  392: 		       struct in_addr group_addr)
  393: {
  394:   struct pim_interface *pim_ifp;
  395:   struct in_addr neigh_dst_addr;
  396: 
  397:   pim_ifp = ifp->info;
  398:   zassert(pim_ifp);
  399: 
  400:   neigh_dst_addr = pim_ifp->primary_address;
  401: 
  402:   if (PIM_DEBUG_PIM_EVENTS) {
  403:     char source_str[100];
  404:     char group_str[100];
  405:     char neigh_dst_str[100];
  406:     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
  407:     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
  408:     pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str));
  409:     zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s",
  410: 	       __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name);
  411:   }
  412: 
  413:   pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr,
  414: 		     0 /* boolean: send_join=false (prune) */);
  415: }
  416: 
  417: static int on_ifjoin_prune_pending_timer(struct thread *t)
  418: {
  419:   struct pim_ifchannel *ch;
  420:   int send_prune_echo; /* boolean */
  421:   struct interface *ifp;
  422:   struct pim_interface *pim_ifp;
  423:   struct in_addr ch_source;
  424:   struct in_addr ch_group;
  425: 
  426:   zassert(t);
  427:   ch = THREAD_ARG(t);
  428:   zassert(ch);
  429: 
  430:   ch->t_ifjoin_prune_pending_timer = 0;
  431: 
  432:   zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
  433: 
  434:   /* Send PruneEcho(S,G) ? */
  435:   ifp = ch->interface;
  436:   pim_ifp = ifp->info;
  437:   send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
  438: 
  439:   /* Save (S,G) */
  440:   ch_source = ch->source_addr;
  441:   ch_group = ch->group_addr;
  442: 
  443:   ifjoin_to_noinfo(ch);
  444:   /* from here ch may have been deleted */
  445: 
  446:   if (send_prune_echo)
  447:     prune_echo(ifp, ch_source, ch_group);
  448: 
  449:   return 0;
  450: }
  451: 
  452: static void check_recv_upstream(int is_join,
  453: 				struct interface *recv_ifp,
  454: 				struct in_addr upstream,
  455: 				struct in_addr source_addr,
  456: 				struct in_addr group_addr,
  457: 				uint8_t source_flags,
  458: 				int holdtime)
  459: {
  460:   struct pim_upstream *up;
  461: 
  462:   /* Upstream (S,G) in Joined state ? */
  463:   up = pim_upstream_find(source_addr, group_addr);
  464:   if (!up)
  465:     return;
  466:   if (up->join_state != PIM_UPSTREAM_JOINED)
  467:     return;
  468: 
  469:   /* Upstream (S,G) in Joined state */
  470: 
  471:   if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) {
  472:     /* RPF'(S,G) not found */
  473:     char src_str[100];
  474:     char grp_str[100];
  475:     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  476:     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  477:     zlog_warn("%s %s: RPF'(%s,%s) not found",
  478: 	      __FILE__, __PRETTY_FUNCTION__, 
  479: 	      src_str, grp_str);
  480:     return;
  481:   }
  482: 
  483:   /* upstream directed to RPF'(S,G) ? */
  484:   if (upstream.s_addr != up->rpf.rpf_addr.s_addr) {
  485:     char src_str[100];
  486:     char grp_str[100];
  487:     char up_str[100];
  488:     char rpf_str[100];
  489:     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  490:     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  491:     pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
  492:     pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
  493:     zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s",
  494: 	      __FILE__, __PRETTY_FUNCTION__, 
  495: 	      src_str, grp_str,
  496: 	      up_str, rpf_str, recv_ifp->name);
  497:     return;
  498:   }
  499:   /* upstream directed to RPF'(S,G) */
  500: 
  501:   if (is_join) {
  502:     /* Join(S,G) to RPF'(S,G) */
  503:     pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
  504:     return;
  505:   }
  506: 
  507:   /* Prune to RPF'(S,G) */
  508: 
  509:   if (source_flags & PIM_RPT_BIT_MASK) {
  510:     if (source_flags & PIM_WILDCARD_BIT_MASK) {
  511:       /* Prune(*,G) to RPF'(S,G) */
  512:       pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
  513: 						     up, up->rpf.rpf_addr);
  514:       return;
  515:     }
  516: 
  517:     /* Prune(S,G,rpt) to RPF'(S,G) */
  518:     pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
  519: 						   up, up->rpf.rpf_addr);
  520:     return;
  521:   }
  522: 
  523:   /* Prune(S,G) to RPF'(S,G) */
  524:   pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
  525: 						 up->rpf.rpf_addr);
  526: }
  527: 
  528: static int nonlocal_upstream(int is_join,
  529: 			     struct interface *recv_ifp,
  530: 			     struct in_addr upstream,
  531: 			     struct in_addr source_addr,
  532: 			     struct in_addr group_addr,
  533: 			     uint8_t source_flags,
  534: 			     uint16_t holdtime)
  535: {
  536:   struct pim_interface *recv_pim_ifp;
  537:   int is_local; /* boolean */
  538: 
  539:   recv_pim_ifp = recv_ifp->info;
  540:   zassert(recv_pim_ifp);
  541: 
  542:   is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
  543:   
  544:   if (PIM_DEBUG_PIM_TRACE) {
  545:     char up_str[100];
  546:     char src_str[100];
  547:     char grp_str[100];
  548:     pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
  549:     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  550:     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  551:     zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s",
  552: 	      __PRETTY_FUNCTION__,
  553: 	      is_join ? "join" : "prune",
  554: 	      src_str, grp_str,
  555: 	      is_local ? "local" : "non-local",
  556: 	      up_str, recv_ifp->name);
  557:   }
  558: 
  559:   if (is_local)
  560:     return 0;
  561: 
  562:   /*
  563:     Since recv upstream addr was not directed to our primary
  564:     address, check if we should react to it in any way.
  565:   */
  566:   check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr,
  567: 		      source_flags, holdtime);
  568: 
  569:   return 1; /* non-local */
  570: }
  571: 
  572: void pim_ifchannel_join_add(struct interface *ifp,
  573: 			    struct in_addr neigh_addr,
  574: 			    struct in_addr upstream,
  575: 			    struct in_addr source_addr,
  576: 			    struct in_addr group_addr,
  577: 			    uint8_t source_flags,
  578: 			    uint16_t holdtime)
  579: {
  580:   struct pim_interface *pim_ifp;
  581:   struct pim_ifchannel *ch;
  582: 
  583:   if (nonlocal_upstream(1 /* join */, ifp, upstream,
  584: 			source_addr, group_addr, source_flags, holdtime)) {
  585:     return;
  586:   }
  587: 
  588:   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
  589:   if (!ch)
  590:     return;
  591: 
  592:   /*
  593:     RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
  594: 
  595:     Transitions from "I am Assert Loser" State
  596: 
  597:     Receive Join(S,G) on Interface I
  598: 
  599:     We receive a Join(S,G) that has the Upstream Neighbor Address
  600:     field set to my primary IP address on interface I.  The action is
  601:     to transition to NoInfo state, delete this (S,G) assert state
  602:     (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
  603:     to operate.
  604: 
  605:     Notice: The nonlocal_upstream() test above ensures the upstream
  606:     address of the join message is our primary address.
  607:    */
  608:   if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
  609:     char src_str[100];
  610:     char grp_str[100];
  611:     char neigh_str[100];
  612:     pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str));
  613:     pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str));
  614:     pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
  615:     zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s",
  616: 	      __PRETTY_FUNCTION__,
  617: 	      src_str, grp_str, neigh_str, ifp->name);
  618: 
  619:     assert_action_a5(ch);
  620:   }
  621: 
  622:   pim_ifp = ifp->info;
  623:   zassert(pim_ifp);
  624: 
  625:   switch (ch->ifjoin_state) {
  626:   case PIM_IFJOIN_NOINFO:
  627:     pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
  628:     if (pim_macro_chisin_oiflist(ch)) {
  629:       pim_forward_start(ch);
  630:     }
  631:     break;
  632:   case PIM_IFJOIN_JOIN:
  633:     zassert(!ch->t_ifjoin_prune_pending_timer);
  634: 
  635:     /*
  636:       In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
  637:       previously received join message with holdtime=0xFFFF.
  638:      */
  639:     if (ch->t_ifjoin_expiry_timer) {
  640:       unsigned long remain =
  641: 	thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
  642:       if (remain > holdtime) {
  643: 	/*
  644: 	  RFC 4601: 4.5.3.  Receiving (S,G) Join/Prune Messages
  645: 
  646: 	  Transitions from Join State
  647: 
  648:           The (S,G) downstream state machine on interface I remains in
  649:           Join state, and the Expiry Timer (ET) is restarted, set to
  650:           maximum of its current value and the HoldTime from the
  651:           triggering Join/Prune message.
  652: 
  653: 	  Conclusion: Do not change the ET if the current value is
  654: 	  higher than the received join holdtime.
  655: 	 */
  656: 	return;
  657:       }
  658:     }
  659:     THREAD_OFF(ch->t_ifjoin_expiry_timer);
  660:     break;
  661:   case PIM_IFJOIN_PRUNE_PENDING:
  662:     zassert(!ch->t_ifjoin_expiry_timer);
  663:     zassert(ch->t_ifjoin_prune_pending_timer);
  664:     THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
  665:     pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
  666:     break;
  667:   }
  668: 
  669:   zassert(!IFCHANNEL_NOINFO(ch));
  670: 
  671:   if (holdtime != 0xFFFF) {
  672:     THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
  673: 		    on_ifjoin_expiry_timer,
  674: 		    ch, holdtime);
  675:   }
  676: }
  677: 
  678: void pim_ifchannel_prune(struct interface *ifp,
  679: 			 struct in_addr upstream,
  680: 			 struct in_addr source_addr,
  681: 			 struct in_addr group_addr,
  682: 			 uint8_t source_flags,
  683: 			 uint16_t holdtime)
  684: {
  685:   struct pim_ifchannel *ch;
  686:   int jp_override_interval_msec;
  687: 
  688:   if (nonlocal_upstream(0 /* prune */, ifp, upstream,
  689: 			source_addr, group_addr, source_flags, holdtime)) {
  690:     return;
  691:   }
  692: 
  693:   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
  694:   if (!ch)
  695:     return;
  696: 
  697:   switch (ch->ifjoin_state) {
  698:   case PIM_IFJOIN_NOINFO:
  699:   case PIM_IFJOIN_PRUNE_PENDING:
  700:     /* nothing to do */
  701:     break;
  702:   case PIM_IFJOIN_JOIN:
  703:     {
  704:       struct pim_interface *pim_ifp;
  705: 
  706:       pim_ifp = ifp->info;
  707: 
  708:       zassert(ch->t_ifjoin_expiry_timer);
  709:       zassert(!ch->t_ifjoin_prune_pending_timer);
  710: 
  711:       THREAD_OFF(ch->t_ifjoin_expiry_timer);
  712:       
  713:       pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
  714:       
  715:       if (listcount(pim_ifp->pim_neighbor_list) > 1) {
  716: 	jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
  717:       }
  718:       else {
  719: 	jp_override_interval_msec = 0; /* schedule to expire immediately */
  720: 	/* If we called ifjoin_prune() directly instead, care should
  721: 	   be taken not to use "ch" afterwards since it would be
  722: 	   deleted. */
  723:       }
  724:       
  725:       THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
  726: 			   on_ifjoin_prune_pending_timer,
  727: 			   ch, jp_override_interval_msec);
  728:       
  729:       zassert(!ch->t_ifjoin_expiry_timer);
  730:       zassert(ch->t_ifjoin_prune_pending_timer);
  731:     }
  732:     break;
  733:   }
  734: 
  735: }
  736: 
  737: void pim_ifchannel_local_membership_add(struct interface *ifp,
  738: 					struct in_addr source_addr,
  739: 					struct in_addr group_addr)
  740: {
  741:   struct pim_ifchannel *ch;
  742:   struct pim_interface *pim_ifp;
  743: 
  744:   /* PIM enabled on interface? */
  745:   pim_ifp = ifp->info;
  746:   if (!pim_ifp)
  747:     return;
  748:   if (!PIM_IF_TEST_PIM(pim_ifp->options))
  749:     return;
  750: 
  751:   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
  752:   if (!ch) {
  753:     return;
  754:   }
  755: 
  756:   ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
  757: 
  758:   zassert(!IFCHANNEL_NOINFO(ch));
  759: }
  760: 
  761: void pim_ifchannel_local_membership_del(struct interface *ifp,
  762: 					struct in_addr source_addr,
  763: 					struct in_addr group_addr)
  764: {
  765:   struct pim_ifchannel *ch;
  766:   struct pim_interface *pim_ifp;
  767: 
  768:   /* PIM enabled on interface? */
  769:   pim_ifp = ifp->info;
  770:   if (!pim_ifp)
  771:     return;
  772:   if (!PIM_IF_TEST_PIM(pim_ifp->options))
  773:     return;
  774: 
  775:   ch = pim_ifchannel_find(ifp, source_addr, group_addr);
  776:   if (!ch)
  777:     return;
  778: 
  779:   ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
  780: 
  781:   delete_on_noinfo(ch);
  782: }
  783: 
  784: void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
  785: {
  786:   int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
  787:   int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
  788: 
  789:   if (new_couldassert == old_couldassert)
  790:     return;
  791: 
  792:   if (PIM_DEBUG_PIM_EVENTS) {
  793:     char src_str[100];
  794:     char grp_str[100];
  795:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
  796:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
  797:     zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
  798: 	       __PRETTY_FUNCTION__,
  799: 	       src_str, grp_str, ch->interface->name,
  800: 	       old_couldassert, new_couldassert);
  801:   }
  802: 
  803:   if (new_couldassert) {
  804:     /* CouldAssert(S,G,I) switched from FALSE to TRUE */
  805:     PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
  806:   }
  807:   else {
  808:     /* CouldAssert(S,G,I) switched from TRUE to FALSE */
  809:     PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
  810: 
  811:     if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
  812:       assert_action_a4(ch);
  813:     }
  814:   }
  815: 
  816:   pim_ifchannel_update_my_assert_metric(ch);
  817: }
  818: 
  819: /*
  820:   my_assert_metric may be affected by:
  821: 
  822:   CouldAssert(S,G)
  823:   pim_ifp->primary_address
  824:   rpf->source_nexthop.mrib_metric_preference;
  825:   rpf->source_nexthop.mrib_route_metric;
  826:  */
  827: void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
  828: {
  829:   struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
  830: 
  831:   if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
  832:       return;
  833: 
  834:   if (PIM_DEBUG_PIM_EVENTS) {
  835:     char src_str[100];
  836:     char grp_str[100];
  837:     char old_addr_str[100];
  838:     char new_addr_str[100];
  839:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
  840:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
  841:     pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
  842:     pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
  843:     zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
  844: 	       __PRETTY_FUNCTION__,
  845: 	       src_str, grp_str, ch->interface->name,
  846: 	       ch->ifassert_my_metric.rpt_bit_flag,
  847: 	       ch->ifassert_my_metric.metric_preference,
  848: 	       ch->ifassert_my_metric.route_metric,
  849: 	       old_addr_str,
  850: 	       my_metric_new.rpt_bit_flag,
  851: 	       my_metric_new.metric_preference,
  852: 	       my_metric_new.route_metric,
  853: 	       new_addr_str);
  854:   }
  855: 
  856:   ch->ifassert_my_metric = my_metric_new;
  857: 
  858:   if (pim_assert_metric_better(&ch->ifassert_my_metric,
  859: 			       &ch->ifassert_winner_metric)) {
  860:     assert_action_a5(ch);
  861:   }
  862: }
  863: 
  864: void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
  865: {
  866:   int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
  867:   int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
  868: 
  869:   if (new_atd == old_atd)
  870:     return;
  871: 
  872:   if (PIM_DEBUG_PIM_EVENTS) {
  873:     char src_str[100];
  874:     char grp_str[100];
  875:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
  876:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
  877:     zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
  878: 	       __PRETTY_FUNCTION__,
  879: 	       src_str, grp_str, ch->interface->name,
  880: 	       old_atd, new_atd);
  881:   }
  882: 
  883:   if (new_atd) {
  884:     /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
  885:     PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
  886:   }
  887:   else {
  888:     /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
  889:     PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
  890: 
  891:     if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
  892:       assert_action_a5(ch);
  893:     }
  894:   }
  895: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>