Annotation of embedaddon/quagga/pimd/pim_assert.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 "log.h"
        !            26: #include "prefix.h"
        !            27: 
        !            28: #include "pimd.h"
        !            29: #include "pim_str.h"
        !            30: #include "pim_tlv.h"
        !            31: #include "pim_msg.h"
        !            32: #include "pim_pim.h"
        !            33: #include "pim_int.h"
        !            34: #include "pim_time.h"
        !            35: #include "pim_iface.h"
        !            36: #include "pim_hello.h"
        !            37: #include "pim_macro.h"
        !            38: #include "pim_assert.h"
        !            39: #include "pim_ifchannel.h"
        !            40: 
        !            41: static int assert_action_a3(struct pim_ifchannel *ch);
        !            42: static void assert_action_a2(struct pim_ifchannel *ch,
        !            43:                             struct pim_assert_metric winner_metric);
        !            44: static void assert_action_a6(struct pim_ifchannel *ch,
        !            45:                             struct pim_assert_metric winner_metric);
        !            46: 
        !            47: void pim_ifassert_winner_set(struct pim_ifchannel     *ch,
        !            48:                             enum pim_ifassert_state   new_state,
        !            49:                             struct in_addr            winner,
        !            50:                             struct pim_assert_metric  winner_metric)
        !            51: {
        !            52:   int winner_changed = (ch->ifassert_winner.s_addr != winner.s_addr);
        !            53:   int metric_changed = !pim_assert_metric_match(&ch->ifassert_winner_metric,
        !            54:                                                &winner_metric);
        !            55: 
        !            56:   if (PIM_DEBUG_PIM_EVENTS) {
        !            57:     if (ch->ifassert_state != new_state) {
        !            58:       char src_str[100];
        !            59:       char grp_str[100];
        !            60:       pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !            61:       pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !            62:       zlog_debug("%s: (S,G)=(%s,%s) assert state changed from %s to %s on interface %s",
        !            63:                __PRETTY_FUNCTION__,
        !            64:                src_str, grp_str,
        !            65:                pim_ifchannel_ifassert_name(ch->ifassert_state),
        !            66:                pim_ifchannel_ifassert_name(new_state),
        !            67:                ch->interface->name);
        !            68:     }
        !            69: 
        !            70:     if (winner_changed) {
        !            71:       char src_str[100];
        !            72:       char grp_str[100];
        !            73:       char was_str[100];
        !            74:       char winner_str[100];
        !            75:       pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !            76:       pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !            77:       pim_inet4_dump("<was?>", ch->ifassert_winner, was_str, sizeof(was_str));
        !            78:       pim_inet4_dump("<winner?>", winner, winner_str, sizeof(winner_str));
        !            79:       zlog_debug("%s: (S,G)=(%s,%s) assert winner changed from %s to %s on interface %s",
        !            80:                __PRETTY_FUNCTION__,
        !            81:                src_str, grp_str,
        !            82:                was_str, winner_str, ch->interface->name);
        !            83:     }
        !            84:   } /* PIM_DEBUG_PIM_EVENTS */
        !            85: 
        !            86:   ch->ifassert_state         = new_state;
        !            87:   ch->ifassert_winner        = winner;
        !            88:   ch->ifassert_winner_metric = winner_metric;
        !            89:   ch->ifassert_creation      = pim_time_monotonic_sec();
        !            90: 
        !            91:   if (winner_changed || metric_changed) {
        !            92:     pim_upstream_update_join_desired(ch->upstream);
        !            93:     pim_ifchannel_update_could_assert(ch);
        !            94:     pim_ifchannel_update_assert_tracking_desired(ch);
        !            95:   }
        !            96: }
        !            97: 
        !            98: static void on_trace(const char *label,
        !            99:                     struct interface *ifp, struct in_addr src)
        !           100: {
        !           101:   if (PIM_DEBUG_PIM_TRACE) {
        !           102:     char src_str[100];
        !           103:     pim_inet4_dump("<src?>", src, src_str, sizeof(src_str));
        !           104:     zlog_debug("%s: from %s on %s",
        !           105:               label, src_str, ifp->name);
        !           106:   }
        !           107: }
        !           108: 
        !           109: static int preferred_assert(const struct pim_ifchannel *ch,
        !           110:                            const struct pim_assert_metric *recv_metric)
        !           111: {
        !           112:   return pim_assert_metric_better(recv_metric,
        !           113:                                  &ch->ifassert_winner_metric);
        !           114: }
        !           115: 
        !           116: static int acceptable_assert(const struct pim_assert_metric *my_metric,
        !           117:                             const struct pim_assert_metric *recv_metric)
        !           118: {
        !           119:   return pim_assert_metric_better(recv_metric,
        !           120:                                  my_metric);
        !           121: }
        !           122: 
        !           123: static int inferior_assert(const struct pim_assert_metric *my_metric,
        !           124:                           const struct pim_assert_metric *recv_metric)
        !           125: {
        !           126:   return pim_assert_metric_better(my_metric,
        !           127:                                  recv_metric);
        !           128: }
        !           129: 
        !           130: static int cancel_assert(const struct pim_assert_metric *recv_metric)
        !           131: {
        !           132:   return (recv_metric->metric_preference == PIM_ASSERT_METRIC_PREFERENCE_MAX)
        !           133:     &&
        !           134:     (recv_metric->route_metric == PIM_ASSERT_ROUTE_METRIC_MAX);
        !           135: }
        !           136: 
        !           137: static void if_could_assert_do_a1(const char *caller,
        !           138:                                  struct pim_ifchannel *ch)
        !           139: {
        !           140:   if (PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags)) {
        !           141:     if (assert_action_a1(ch)) {
        !           142:       char src_str[100];
        !           143:       char grp_str[100];
        !           144:       pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           145:       pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           146:       zlog_warn("%s: %s: (S,G)=(%s,%s) assert_action_a1 failure on interface %s",
        !           147:                __PRETTY_FUNCTION__, caller,
        !           148:                src_str, grp_str, ch->interface->name);
        !           149:       /* log warning only */
        !           150:     }
        !           151:   }
        !           152: }
        !           153: 
        !           154: static int dispatch_assert(struct interface *ifp,
        !           155:                           struct in_addr source_addr,
        !           156:                           struct in_addr group_addr,
        !           157:                           struct pim_assert_metric recv_metric)
        !           158: {
        !           159:   struct pim_ifchannel *ch;
        !           160: 
        !           161:   ch = pim_ifchannel_add(ifp, source_addr, group_addr);
        !           162:   if (!ch) {
        !           163:     char source_str[100];
        !           164:     char group_str[100];
        !           165:     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
        !           166:     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
        !           167:     zlog_warn("%s: (S,G)=(%s,%s) failure creating channel on interface %s",
        !           168:              __PRETTY_FUNCTION__,
        !           169:              source_str, group_str, ifp->name);
        !           170:     return -1;
        !           171:   }
        !           172: 
        !           173:   switch (ch->ifassert_state) {
        !           174:   case PIM_IFASSERT_NOINFO:
        !           175:     if (recv_metric.rpt_bit_flag) {
        !           176:       /* RPT bit set */
        !           177:       if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
        !           178:     }
        !           179:     else {
        !           180:       /* RPT bit clear */
        !           181:       if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
        !           182:        if_could_assert_do_a1(__PRETTY_FUNCTION__, ch);
        !           183:       }
        !           184:       else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
        !           185:        if (PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags)) {
        !           186:          assert_action_a6(ch, recv_metric);
        !           187:        }
        !           188:       }
        !           189:     }
        !           190:     break;
        !           191:   case PIM_IFASSERT_I_AM_WINNER:
        !           192:     if (preferred_assert(ch, &recv_metric)) {
        !           193:       assert_action_a2(ch, recv_metric);
        !           194:     }
        !           195:     else {
        !           196:       if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
        !           197:        zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
        !           198:        assert_action_a3(ch);
        !           199:       }
        !           200:     }
        !           201:     break;
        !           202:   case PIM_IFASSERT_I_AM_LOSER:
        !           203:     if (recv_metric.ip_address.s_addr == ch->ifassert_winner.s_addr) {
        !           204:       /* Assert from current winner */
        !           205: 
        !           206:       if (cancel_assert(&recv_metric)) {
        !           207:        assert_action_a5(ch);
        !           208:       }
        !           209:       else {
        !           210:        if (inferior_assert(&ch->ifassert_my_metric, &recv_metric)) {
        !           211:          assert_action_a5(ch);
        !           212:        }
        !           213:        else if (acceptable_assert(&ch->ifassert_my_metric, &recv_metric)) {
        !           214:          if (!recv_metric.rpt_bit_flag) {
        !           215:            assert_action_a2(ch, recv_metric);
        !           216:          }
        !           217:        }
        !           218:       }
        !           219:     }
        !           220:     else if (preferred_assert(ch, &recv_metric)) {
        !           221:       assert_action_a2(ch, recv_metric);
        !           222:     }
        !           223:     break;
        !           224:   default:
        !           225:     {
        !           226:       char source_str[100];
        !           227:       char group_str[100];
        !           228:       pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
        !           229:       pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
        !           230:       zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
        !           231:                __PRETTY_FUNCTION__,
        !           232:                source_str, group_str, ch->ifassert_state, ifp->name);
        !           233:     }
        !           234:     return -2;
        !           235:   }
        !           236: 
        !           237:   return 0;
        !           238: }
        !           239: 
        !           240: int pim_assert_recv(struct interface *ifp,
        !           241:                    struct pim_neighbor *neigh,
        !           242:                    struct in_addr src_addr,
        !           243:                    uint8_t *buf, int buf_size)
        !           244: {
        !           245:   struct prefix            msg_group_addr;
        !           246:   struct prefix            msg_source_addr;
        !           247:   struct pim_assert_metric msg_metric;
        !           248:   int offset;
        !           249:   uint8_t *curr;
        !           250:   int curr_size;
        !           251: 
        !           252:   on_trace(__PRETTY_FUNCTION__, ifp, src_addr);
        !           253: 
        !           254:   curr      = buf;
        !           255:   curr_size = buf_size;
        !           256: 
        !           257:   /*
        !           258:     Parse assert group addr
        !           259:    */
        !           260:   offset = pim_parse_addr_group(ifp->name, src_addr,
        !           261:                                &msg_group_addr,
        !           262:                                curr, curr_size);
        !           263:   if (offset < 1) {
        !           264:     char src_str[100];
        !           265:     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        !           266:     zlog_warn("%s: pim_parse_addr_group() failure: from %s on %s",
        !           267:              __PRETTY_FUNCTION__,
        !           268:              src_str, ifp->name);
        !           269:     return -1;
        !           270:   }
        !           271:   curr      += offset;
        !           272:   curr_size -= offset;
        !           273: 
        !           274:   /*
        !           275:     Parse assert source addr
        !           276:   */
        !           277:   offset = pim_parse_addr_ucast(ifp->name, src_addr,
        !           278:                                &msg_source_addr,
        !           279:                                curr, curr_size);
        !           280:   if (offset < 1) {
        !           281:     char src_str[100];
        !           282:     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        !           283:     zlog_warn("%s: pim_parse_addr_ucast() failure: from %s on %s",
        !           284:              __PRETTY_FUNCTION__,
        !           285:              src_str, ifp->name);
        !           286:     return -2;
        !           287:   }
        !           288:   curr      += offset;
        !           289:   curr_size -= offset;
        !           290: 
        !           291:   if (curr_size != 8) {
        !           292:     char src_str[100];
        !           293:     pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str));
        !           294:     zlog_warn("%s: preference/metric size is not 8: size=%d from %s on interface %s",
        !           295:              __PRETTY_FUNCTION__,
        !           296:              curr_size,
        !           297:              src_str, ifp->name);
        !           298:     return -3;
        !           299:   }
        !           300: 
        !           301:   /*
        !           302:     Parse assert metric preference
        !           303:   */
        !           304: 
        !           305:   msg_metric.metric_preference = pim_read_uint32_host(curr);
        !           306: 
        !           307:   msg_metric.rpt_bit_flag = msg_metric.metric_preference & 0x80000000; /* save highest bit */
        !           308:   msg_metric.metric_preference &= ~0x80000000; /* clear highest bit */
        !           309: 
        !           310:   curr += 4;
        !           311: 
        !           312:   /*
        !           313:     Parse assert route metric
        !           314:   */
        !           315: 
        !           316:   msg_metric.route_metric = pim_read_uint32_host(curr);
        !           317: 
        !           318:   if (PIM_DEBUG_PIM_TRACE) {
        !           319:     char neigh_str[100];
        !           320:     char source_str[100];
        !           321:     char group_str[100];
        !           322:     pim_inet4_dump("<neigh?>", src_addr, neigh_str, sizeof(neigh_str));
        !           323:     pim_inet4_dump("<src?>", msg_source_addr.u.prefix4, source_str, sizeof(source_str));
        !           324:     pim_inet4_dump("<grp?>", msg_group_addr.u.prefix4, group_str, sizeof(group_str));
        !           325:     zlog_debug("%s: from %s on %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
        !           326:               __PRETTY_FUNCTION__, neigh_str, ifp->name,
        !           327:               source_str, group_str,
        !           328:               msg_metric.metric_preference,
        !           329:               msg_metric.route_metric,
        !           330:               PIM_FORCE_BOOLEAN(msg_metric.rpt_bit_flag));
        !           331:   }
        !           332: 
        !           333:   msg_metric.ip_address = src_addr;
        !           334: 
        !           335:   return dispatch_assert(ifp,
        !           336:                         msg_source_addr.u.prefix4,
        !           337:                         msg_group_addr.u.prefix4,
        !           338:                         msg_metric);
        !           339: }
        !           340: 
        !           341: /*
        !           342:   RFC 4601: 4.6.3.  Assert Metrics
        !           343: 
        !           344:    Assert metrics are defined as:
        !           345: 
        !           346:    When comparing assert_metrics, the rpt_bit_flag, metric_preference,
        !           347:    and route_metric field are compared in order, where the first lower
        !           348:    value wins.  If all fields are equal, the primary IP address of the
        !           349:    router that sourced the Assert message is used as a tie-breaker,
        !           350:    with the highest IP address winning.
        !           351: */
        !           352: int pim_assert_metric_better(const struct pim_assert_metric *m1,
        !           353:                             const struct pim_assert_metric *m2)
        !           354: {
        !           355:   if (m1->rpt_bit_flag < m2->rpt_bit_flag)
        !           356:     return 1;
        !           357:   if (m1->rpt_bit_flag > m2->rpt_bit_flag)
        !           358:     return 0;
        !           359: 
        !           360:   if (m1->metric_preference < m2->metric_preference)
        !           361:     return 1;
        !           362:   if (m1->metric_preference > m2->metric_preference)
        !           363:     return 0;
        !           364: 
        !           365:   if (m1->route_metric < m2->route_metric)
        !           366:     return 1;
        !           367:   if (m1->route_metric > m2->route_metric)
        !           368:     return 0;
        !           369: 
        !           370:   return ntohl(m1->ip_address.s_addr) > ntohl(m2->ip_address.s_addr);
        !           371: }
        !           372: 
        !           373: int pim_assert_metric_match(const struct pim_assert_metric *m1,
        !           374:                            const struct pim_assert_metric *m2)
        !           375: {
        !           376:   if (m1->rpt_bit_flag != m2->rpt_bit_flag)
        !           377:     return 0;
        !           378:   if (m1->metric_preference != m2->metric_preference)
        !           379:     return 0;
        !           380:   if (m1->route_metric != m2->route_metric)
        !           381:     return 0;
        !           382:   
        !           383:   return m1->ip_address.s_addr == m2->ip_address.s_addr;
        !           384: }
        !           385: 
        !           386: int pim_assert_build_msg(uint8_t *pim_msg, int buf_size,
        !           387:                         struct interface *ifp,
        !           388:                         struct in_addr group_addr,
        !           389:                         struct in_addr source_addr,
        !           390:                         uint32_t metric_preference,
        !           391:                         uint32_t route_metric,
        !           392:                         uint32_t rpt_bit_flag)
        !           393: {
        !           394:   uint8_t *buf_pastend = pim_msg + buf_size;
        !           395:   uint8_t *pim_msg_curr;
        !           396:   int pim_msg_size;
        !           397:   int remain;
        !           398: 
        !           399:   pim_msg_curr = pim_msg + PIM_MSG_HEADER_LEN; /* skip room for pim header */
        !           400: 
        !           401:   /* Encode group */
        !           402:   remain = buf_pastend - pim_msg_curr;
        !           403:   pim_msg_curr = pim_msg_addr_encode_ipv4_group(pim_msg_curr,
        !           404:                                                remain,
        !           405:                                                group_addr);
        !           406:   if (!pim_msg_curr) {
        !           407:     char group_str[100];
        !           408:     pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
        !           409:     zlog_warn("%s: failure encoding group address %s: space left=%d",
        !           410:              __PRETTY_FUNCTION__, group_str, remain);
        !           411:     return -1;
        !           412:   }
        !           413: 
        !           414:   /* Encode source */
        !           415:   remain = buf_pastend - pim_msg_curr;
        !           416:   pim_msg_curr = pim_msg_addr_encode_ipv4_ucast(pim_msg_curr,
        !           417:                                                remain,
        !           418:                                                source_addr);
        !           419:   if (!pim_msg_curr) {
        !           420:     char source_str[100];
        !           421:     pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
        !           422:     zlog_warn("%s: failure encoding source address %s: space left=%d",
        !           423:              __PRETTY_FUNCTION__, source_str, remain);
        !           424:     return -2;
        !           425:   }
        !           426: 
        !           427:   /* Metric preference */
        !           428:   pim_write_uint32(pim_msg_curr, rpt_bit_flag ?
        !           429:                   metric_preference | 0x80000000 :
        !           430:                   metric_preference);
        !           431:   pim_msg_curr += 4;
        !           432: 
        !           433:   /* Route metric */
        !           434:   pim_write_uint32(pim_msg_curr, route_metric);
        !           435:   pim_msg_curr += 4;
        !           436: 
        !           437:   /*
        !           438:     Add PIM header
        !           439:   */
        !           440:   pim_msg_size = pim_msg_curr - pim_msg;
        !           441:   pim_msg_build_header(pim_msg, pim_msg_size,
        !           442:                       PIM_MSG_TYPE_ASSERT);
        !           443: 
        !           444:   return pim_msg_size;
        !           445: }
        !           446: 
        !           447: static int pim_assert_do(struct pim_ifchannel *ch,
        !           448:                         struct pim_assert_metric metric)
        !           449: {
        !           450:   struct interface *ifp;
        !           451:   struct pim_interface *pim_ifp;
        !           452:   uint8_t pim_msg[1000];
        !           453:   int pim_msg_size;
        !           454: 
        !           455:   ifp = ch->interface;
        !           456:   zassert(ifp);
        !           457: 
        !           458:   pim_ifp = ifp->info;
        !           459:   if (!pim_ifp) {
        !           460:     zlog_warn("%s: pim not enabled on interface: %s",
        !           461:              __PRETTY_FUNCTION__, ifp->name);
        !           462:     return -1;
        !           463:   }
        !           464: 
        !           465:   pim_msg_size = pim_assert_build_msg(pim_msg, sizeof(pim_msg), ifp,
        !           466:                                      ch->group_addr, ch->source_addr,
        !           467:                                      metric.metric_preference,
        !           468:                                      metric.route_metric,
        !           469:                                      metric.rpt_bit_flag);
        !           470:   if (pim_msg_size < 1) {
        !           471:     zlog_warn("%s: failure building PIM assert message: msg_size=%d",
        !           472:              __PRETTY_FUNCTION__, pim_msg_size);
        !           473:     return -2;
        !           474:   }
        !           475: 
        !           476:   /*
        !           477:     RFC 4601: 4.3.1.  Sending Hello Messages
        !           478:     
        !           479:     Thus, if a router needs to send a Join/Prune or Assert message on
        !           480:     an interface on which it has not yet sent a Hello message with the
        !           481:     currently configured IP address, then it MUST immediately send the
        !           482:     relevant Hello message without waiting for the Hello Timer to
        !           483:     expire, followed by the Join/Prune or Assert message.
        !           484:   */
        !           485:   pim_hello_require(ifp);
        !           486: 
        !           487:   if (PIM_DEBUG_PIM_TRACE) {
        !           488:     char source_str[100];
        !           489:     char group_str[100];
        !           490:     pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
        !           491:     pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
        !           492:     zlog_debug("%s: to %s: (S,G)=(%s,%s) pref=%u metric=%u rpt_bit=%u",
        !           493:               __PRETTY_FUNCTION__, 
        !           494:               ifp->name, source_str, group_str,
        !           495:               metric.metric_preference,
        !           496:               metric.route_metric,
        !           497:               PIM_FORCE_BOOLEAN(metric.rpt_bit_flag));
        !           498:   }
        !           499: 
        !           500:   if (pim_msg_send(pim_ifp->pim_sock_fd,
        !           501:                   qpim_all_pim_routers_addr,
        !           502:                   pim_msg,
        !           503:                   pim_msg_size,
        !           504:                   ifp->name)) {
        !           505:     zlog_warn("%s: could not send PIM message on interface %s",
        !           506:              __PRETTY_FUNCTION__, ifp->name);
        !           507:     return -3;
        !           508:   }
        !           509: 
        !           510:   return 0;
        !           511: }
        !           512: 
        !           513: int pim_assert_send(struct pim_ifchannel *ch)
        !           514: {
        !           515:   return pim_assert_do(ch, ch->ifassert_my_metric);
        !           516: }
        !           517: 
        !           518: /*
        !           519:   RFC 4601: 4.6.4.  AssertCancel Messages
        !           520: 
        !           521:   An AssertCancel(S,G) is an infinite metric assert with the RPT bit
        !           522:   set that names S as the source.
        !           523:  */
        !           524: static int pim_assert_cancel(struct pim_ifchannel *ch)
        !           525: {
        !           526:   struct pim_assert_metric metric;
        !           527: 
        !           528:   metric.rpt_bit_flag      = 0;
        !           529:   metric.metric_preference = PIM_ASSERT_METRIC_PREFERENCE_MAX;
        !           530:   metric.route_metric      = PIM_ASSERT_ROUTE_METRIC_MAX;
        !           531:   metric.ip_address        = ch->source_addr;
        !           532: 
        !           533:   return pim_assert_do(ch, metric);
        !           534: }
        !           535: 
        !           536: static int on_assert_timer(struct thread *t)
        !           537: {
        !           538:   struct pim_ifchannel *ch;
        !           539:   struct interface *ifp;
        !           540: 
        !           541:   zassert(t);
        !           542:   ch = THREAD_ARG(t);
        !           543:   zassert(ch);
        !           544: 
        !           545:   ifp = ch->interface;
        !           546:   zassert(ifp);
        !           547: 
        !           548:   if (PIM_DEBUG_PIM_TRACE) {
        !           549:     char src_str[100];
        !           550:     char grp_str[100];
        !           551:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           552:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           553:     zlog_debug("%s: (S,G)=(%s,%s) timer expired on interface %s",
        !           554:               __PRETTY_FUNCTION__,
        !           555:               src_str, grp_str, ifp->name);
        !           556:   }
        !           557: 
        !           558:   ch->t_ifassert_timer = 0;
        !           559: 
        !           560:   switch (ch->ifassert_state) {
        !           561:   case PIM_IFASSERT_I_AM_WINNER:
        !           562:     zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
        !           563:     assert_action_a3(ch);
        !           564:     break;
        !           565:   case PIM_IFASSERT_I_AM_LOSER:
        !           566:     assert_action_a5(ch);
        !           567:     break;
        !           568:   default:
        !           569:     {
        !           570:       char source_str[100];
        !           571:       char group_str[100];
        !           572:       pim_inet4_dump("<src?>", ch->source_addr, source_str, sizeof(source_str));
        !           573:       pim_inet4_dump("<grp?>", ch->group_addr, group_str, sizeof(group_str));
        !           574:       zlog_warn("%s: (S,G)=(%s,%s) invalid assert state %d on interface %s",
        !           575:                __PRETTY_FUNCTION__,
        !           576:                source_str, group_str, ch->ifassert_state, ifp->name);
        !           577:     }
        !           578:   }
        !           579: 
        !           580:   return 0;
        !           581: }
        !           582: 
        !           583: static void assert_timer_off(struct pim_ifchannel *ch)
        !           584: {
        !           585:   struct interface *ifp;
        !           586: 
        !           587:   zassert(ch);
        !           588:   ifp = ch->interface;
        !           589:   zassert(ifp);
        !           590: 
        !           591:   if (PIM_DEBUG_PIM_TRACE) {
        !           592:     if (ch->t_ifassert_timer) {
        !           593:       char src_str[100];
        !           594:       char grp_str[100];
        !           595:       pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           596:       pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           597:       zlog_debug("%s: (S,G)=(%s,%s) cancelling timer on interface %s",
        !           598:                 __PRETTY_FUNCTION__,
        !           599:                 src_str, grp_str, ifp->name);
        !           600:     }
        !           601:   }
        !           602:   THREAD_OFF(ch->t_ifassert_timer);
        !           603:   zassert(!ch->t_ifassert_timer);
        !           604: }
        !           605: 
        !           606: static void pim_assert_timer_set(struct pim_ifchannel *ch,
        !           607:                                 int interval)
        !           608: {
        !           609:   struct interface *ifp;
        !           610: 
        !           611:   zassert(ch);
        !           612:   ifp = ch->interface;
        !           613:   zassert(ifp);
        !           614: 
        !           615:   assert_timer_off(ch);
        !           616: 
        !           617:   if (PIM_DEBUG_PIM_TRACE) {
        !           618:     char src_str[100];
        !           619:     char grp_str[100];
        !           620:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           621:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           622:     zlog_debug("%s: (S,G)=(%s,%s) starting %u sec timer on interface %s",
        !           623:               __PRETTY_FUNCTION__,
        !           624:               src_str, grp_str, interval, ifp->name);
        !           625:   }
        !           626: 
        !           627:   THREAD_TIMER_ON(master, ch->t_ifassert_timer,
        !           628:                  on_assert_timer,
        !           629:                  ch, interval);
        !           630: }
        !           631: 
        !           632: static void pim_assert_timer_reset(struct pim_ifchannel *ch)
        !           633: {
        !           634:   pim_assert_timer_set(ch, PIM_ASSERT_TIME - PIM_ASSERT_OVERRIDE_INTERVAL);
        !           635: }
        !           636: 
        !           637: /*
        !           638:   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
        !           639: 
        !           640:   (S,G) Assert State machine Actions
        !           641: 
        !           642:   A1:  Send Assert(S,G).
        !           643:   Set Assert Timer to (Assert_Time - Assert_Override_Interval).
        !           644:   Store self as AssertWinner(S,G,I).
        !           645:   Store spt_assert_metric(S,I) as AssertWinnerMetric(S,G,I).
        !           646: */
        !           647: int assert_action_a1(struct pim_ifchannel *ch)
        !           648: {
        !           649:   struct interface *ifp = ch->interface;
        !           650:   struct pim_interface *pim_ifp;
        !           651: 
        !           652:   zassert(ifp);
        !           653: 
        !           654:   pim_ifp = ifp->info;
        !           655:   if (!pim_ifp) {
        !           656:     char src_str[100];
        !           657:     char grp_str[100];
        !           658:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           659:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           660:     zlog_warn("%s: (S,G)=(%s,%s) multicast not enabled on interface %s",
        !           661:              __PRETTY_FUNCTION__,
        !           662:              src_str, grp_str, ifp->name);
        !           663:     return -1; /* must return since pim_ifp is used below */
        !           664:   }
        !           665: 
        !           666:   /* Switch to I_AM_WINNER before performing action_a3 below */
        !           667:   pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_WINNER,
        !           668:                          pim_ifp->primary_address,
        !           669:                          pim_macro_spt_assert_metric(&ch->upstream->rpf,
        !           670:                                                      pim_ifp->primary_address));
        !           671: 
        !           672:   zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER); /* a3 requirement */
        !           673:   if (assert_action_a3(ch)) {
        !           674:     char src_str[100];
        !           675:     char grp_str[100];
        !           676:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           677:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           678:     zlog_warn("%s: (S,G)=(%s,%s) assert_action_a3 failure on interface %s",
        !           679:              __PRETTY_FUNCTION__,
        !           680:              src_str, grp_str, ifp->name);
        !           681:     /* warning only */
        !           682:   }
        !           683: 
        !           684:   zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
        !           685: 
        !           686:   return 0;
        !           687: }
        !           688: 
        !           689: /*
        !           690:   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
        !           691: 
        !           692:   (S,G) Assert State machine Actions
        !           693: 
        !           694:      A2:  Store new assert winner as AssertWinner(S,G,I) and assert
        !           695:           winner metric as AssertWinnerMetric(S,G,I).
        !           696:           Set Assert Timer to Assert_Time.
        !           697: */
        !           698: static void assert_action_a2(struct pim_ifchannel *ch,
        !           699:                             struct pim_assert_metric winner_metric)
        !           700: {
        !           701:   pim_ifassert_winner_set(ch, PIM_IFASSERT_I_AM_LOSER,
        !           702:                          winner_metric.ip_address,
        !           703:                          winner_metric);
        !           704:   
        !           705:   pim_assert_timer_set(ch, PIM_ASSERT_TIME);
        !           706: 
        !           707:   zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
        !           708: }
        !           709: 
        !           710: /*
        !           711:   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
        !           712: 
        !           713:   (S,G) Assert State machine Actions
        !           714: 
        !           715:   A3:  Send Assert(S,G).
        !           716:   Set Assert Timer to (Assert_Time - Assert_Override_Interval).
        !           717: */
        !           718: static int assert_action_a3(struct pim_ifchannel *ch)
        !           719: {
        !           720:   zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
        !           721: 
        !           722:   pim_assert_timer_reset(ch);
        !           723: 
        !           724:   if (pim_assert_send(ch)) {
        !           725:     char src_str[100];
        !           726:     char grp_str[100];
        !           727:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           728:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           729: 
        !           730:     zlog_warn("%s: (S,G)=(%s,%s) failure sending assert on interface %s",
        !           731:              __PRETTY_FUNCTION__,
        !           732:              src_str, grp_str, ch->interface->name);
        !           733:     return -1;
        !           734:   }
        !           735: 
        !           736:   zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER);
        !           737: 
        !           738:   return 0;
        !           739: }
        !           740: 
        !           741: /*
        !           742:   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
        !           743: 
        !           744:   (S,G) Assert State machine Actions
        !           745: 
        !           746:      A4:  Send AssertCancel(S,G).
        !           747:           Delete assert info (AssertWinner(S,G,I) and
        !           748:           AssertWinnerMetric(S,G,I) will then return their default
        !           749:           values).
        !           750: */
        !           751: void assert_action_a4(struct pim_ifchannel *ch)
        !           752: {
        !           753:   if (pim_assert_cancel(ch)) {
        !           754:     char src_str[100];
        !           755:     char grp_str[100];
        !           756:     pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str));
        !           757:     pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str));
        !           758:     zlog_warn("%s: failure sending AssertCancel(%s,%s) on interface %s",
        !           759:              __PRETTY_FUNCTION__,
        !           760:              src_str, grp_str, ch->interface->name);
        !           761:     /* log warning only */
        !           762:   }
        !           763: 
        !           764:   assert_action_a5(ch);
        !           765: 
        !           766:   zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
        !           767: }
        !           768: 
        !           769: /*
        !           770:   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
        !           771: 
        !           772:   (S,G) Assert State machine Actions
        !           773: 
        !           774:   A5: Delete assert info (AssertWinner(S,G,I) and
        !           775:   AssertWinnerMetric(S,G,I) will then return their default values).
        !           776: */
        !           777: void assert_action_a5(struct pim_ifchannel *ch)
        !           778: {
        !           779:   reset_ifassert_state(ch);
        !           780:   zassert(ch->ifassert_state == PIM_IFASSERT_NOINFO);
        !           781: }
        !           782: 
        !           783: /*
        !           784:   RFC 4601: 4.6.1.  (S,G) Assert Message State Machine
        !           785: 
        !           786:   (S,G) Assert State machine Actions
        !           787: 
        !           788:      A6:  Store new assert winner as AssertWinner(S,G,I) and assert
        !           789:           winner metric as AssertWinnerMetric(S,G,I).
        !           790:           Set Assert Timer to Assert_Time.
        !           791:           If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true)
        !           792:           set SPTbit(S,G) to TRUE.
        !           793: */
        !           794: static void assert_action_a6(struct pim_ifchannel *ch,
        !           795:                             struct pim_assert_metric winner_metric)
        !           796: {
        !           797:   assert_action_a2(ch, winner_metric);
        !           798: 
        !           799:   /*
        !           800:     If (I is RPF_interface(S)) AND (UpstreamJPState(S,G) == true) set
        !           801:     SPTbit(S,G) to TRUE.
        !           802:     
        !           803:     Notice: For PIM SSM, SPTbit(S,G) is already always true.
        !           804:   */
        !           805: 
        !           806:   zassert(ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER);
        !           807: }
        !           808: 

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