Annotation of embedaddon/quagga/pimd/pim_ifchannel.c, revision 1.1

1.1     ! misho       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>