LCOV - code coverage report
Current view: top level - vnsw/agent/services - ndp_entry.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 0 548 0.0 %
Date: 2026-11-09 02:10:49 Functions: 0 117 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "base/os.h"
       7             : #include <boost/statechart/custom_reaction.hpp>
       8             : #include <boost/statechart/state.hpp>
       9             : #include <boost/statechart/state_machine.hpp>
      10             : #include <boost/statechart/transition.hpp>
      11             : 
      12             : #include "services/ndp_entry.h"
      13             : #include "services/services_types.h"
      14             : #include "services/icmpv6_proto.h"
      15             : //#include "services/services_sandesh.h"
      16             : #include "services/services_init.h"
      17             : #include "oper/route_common.h"
      18             : 
      19             : using std::ostream;
      20             : using std::ostringstream;
      21             : using std::string;
      22             : 
      23             : namespace mpl = boost::mpl;
      24             : namespace sc = boost::statechart;
      25             : 
      26             : const int NdpEntry::kMaxRetries = 3;
      27             : const int NdpEntry::kMaxUnicastRetries = 3;
      28             : 
      29             : #define SM_LOG(level, _Msg)                                    \
      30             :     do {                                                       \
      31             :         ostringstream out;                                     \
      32             :         out << _Msg;                                           \
      33             :         if (LoggingDisabled()) break;                          \
      34             :         ICMPV6_TRACE(Trace, _Msg); \
      35             :     } while (false)
      36             : 
      37             : namespace fsm {
      38             : 
      39             : struct EvTestStateChange : sc::event<EvTestStateChange> {
      40           0 :     EvTestStateChange(NdpEntry::State state, int retry) :state_(state),
      41           0 :                         retry_(retry) {}
      42             :     static const char *Name() {
      43             :         return "EvTestStateChange";
      44             :     }
      45           0 :     bool validate(NdpEntry *state_machine) const {
      46           0 :         return true;
      47             :     }
      48             : 
      49             :     NdpEntry::State state_;
      50             :     int retry_;
      51             : };
      52             : 
      53             : struct EvPktOut : sc::event<EvPktOut> {
      54           0 :     EvPktOut() {
      55           0 :     }
      56             :     static const char *Name() {
      57             :         return "EvPktOut";
      58             :     }
      59           0 :     bool validate(NdpEntry *state_machine) const {
      60           0 :         return true;
      61             :     }
      62             : };
      63             : 
      64             : struct EvDelayTimerExpired : sc::event<EvDelayTimerExpired> {
      65           0 :     explicit EvDelayTimerExpired()  {
      66           0 :     }
      67             :     static const char *Name() {
      68             :         return "EvDelayTimerExpired";
      69             :     }
      70           0 :     bool validate(NdpEntry *state_machine) const {
      71           0 :         return true;
      72             :     }
      73             : };
      74             : 
      75             : struct EvRetransmitTimerExpired : sc::event<EvRetransmitTimerExpired> {
      76           0 :     explicit EvRetransmitTimerExpired()  {
      77           0 :     }
      78             :     static const char *Name() {
      79             :         return "EvRetransmitTimerExpired";
      80             :     }
      81           0 :     bool validate(NdpEntry *state_machine) const {
      82           0 :         return true;
      83             :     }
      84             : };
      85             : 
      86             : struct EvReachableTimerExpired : sc::event<EvReachableTimerExpired> {
      87           0 :     explicit EvReachableTimerExpired()  {
      88           0 :     }
      89             :     static const char *Name() {
      90             :         return "EvReachableTimerExpired";
      91             :     }
      92           0 :     bool validate(NdpEntry *state_machine) const {
      93           0 :         return true;
      94             :     }
      95             : };
      96             : 
      97             : struct EvNsIn : sc::event<EvNsIn> {
      98           0 :     explicit EvNsIn(nd_neighbor_solicit ns, MacAddress mac) :
      99           0 :              mac_(mac), ns_(ns) {
     100           0 :     }
     101             :     static const char *Name() {
     102             :         return "EvNsIn";
     103             :     }
     104           0 :     bool validate(NdpEntry *state_machine) const {
     105           0 :         return true;
     106             :     }
     107             : 
     108             :     MacAddress mac_;
     109             :     nd_neighbor_solicit ns_;
     110             : };
     111             : 
     112             : struct EvSolNaIn : sc::event<EvSolNaIn> {
     113           0 :     explicit EvSolNaIn(nd_neighbor_advert na, MacAddress mac) :
     114           0 :         mac_(mac), na_(na) {
     115           0 :     }
     116             :     static const char *Name() {
     117             :         return "EvSolNaIn";
     118             :     }
     119           0 :     bool validate(NdpEntry *state_machine) const {
     120           0 :         return true;
     121             :     }
     122             : 
     123             :     MacAddress mac_;
     124             :     nd_neighbor_advert na_;
     125             : };
     126             : 
     127             : struct EvUnsolNaIn : sc::event<EvUnsolNaIn> {
     128           0 :     explicit EvUnsolNaIn(nd_neighbor_advert na, MacAddress mac) :
     129           0 :         mac_(mac), na_(na) {
     130           0 :     }
     131             :     static const char *Name() {
     132             :         return "EvUnsolNaIn";
     133             :     }
     134           0 :     bool validate(NdpEntry *state_machine) const {
     135           0 :         return true;
     136             :     }
     137             : 
     138             :     MacAddress mac_;
     139             :     nd_neighbor_advert na_;
     140             : };
     141             : 
     142             : // States for the NDP state machine.
     143             : struct NoState;
     144             : struct Incomplete;
     145             : struct Reachable;
     146             : struct Stale;
     147             : struct Delay;
     148             : struct Probe;
     149             : 
     150             : //
     151             : //
     152             : struct NoState : sc::state<NoState, NdpEntry> {
     153             :     typedef mpl::list<
     154             :         sc::custom_reaction<EvNsIn>,
     155             :         sc::custom_reaction<EvPktOut>,
     156             :         sc::custom_reaction<EvTestStateChange>
     157             :     > reactions;
     158             : 
     159           0 :     explicit NoState(my_context ctx) : my_base(ctx) {
     160           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     161           0 :         state_machine->set_state(NdpEntry::NOSTATE);
     162           0 :     }
     163             : 
     164           0 :     ~NoState() {
     165           0 :     }
     166             : 
     167             :     // copy the mac and move to stale
     168           0 :     sc::result react(const EvNsIn &event) {
     169           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     170           0 :         state_machine->set_mac(event.mac_);
     171           0 :         return transit<Stale>();
     172             :     }
     173             : 
     174             :     // Send multicast NS probe and start retransmit timer
     175           0 :     sc::result react(const EvPktOut &event) {
     176           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     177             :         // In UTs, ndp_entry may not be there
     178           0 :         if (state_machine->get_interface())
     179           0 :             state_machine->SendNeighborSolicit();
     180           0 :         state_machine->StartRetransmitTimer();
     181           0 :         return transit<Incomplete>();
     182             :     }
     183             : 
     184           0 :     sc::result react(const EvTestStateChange &event) {
     185           0 :         NdpEntry::State state = event.state_;
     186           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     187           0 :         state_machine->set_state(state);
     188           0 :         state_machine->set_mac(MacAddress());
     189           0 :         switch (state) {
     190           0 :             case NdpEntry::NOSTATE: return transit<NoState>();
     191           0 :             case NdpEntry::INCOMPLETE: return transit<Incomplete>();
     192           0 :             case NdpEntry::REACHABLE: return transit<Reachable>();
     193           0 :             case NdpEntry::STALE: return transit<Stale>();
     194           0 :             case NdpEntry::DELAY: return transit<Delay>();
     195           0 :             case NdpEntry::PROBE: return transit<Probe>();
     196           0 :             default: return discard_event();
     197             :         }
     198             :     }
     199             : };
     200             : 
     201             : //
     202             : //
     203             : struct Incomplete : sc::state<Incomplete, NdpEntry> {
     204             :     typedef mpl::list<
     205             :         sc::custom_reaction<EvNsIn>,
     206             :         sc::custom_reaction<EvUnsolNaIn>,
     207             :         sc::custom_reaction<EvSolNaIn>,
     208             :         sc::custom_reaction<EvRetransmitTimerExpired>,
     209             :         sc::custom_reaction<EvTestStateChange>
     210             :     > reactions;
     211             : 
     212           0 :     explicit Incomplete(my_context ctx) : my_base(ctx) {
     213           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     214           0 :         state_machine->retry_count_clear();
     215           0 :         state_machine->set_state(NdpEntry::INCOMPLETE);
     216           0 :     }
     217             : 
     218           0 :     ~Incomplete() {
     219           0 :     }
     220             : 
     221             :     // If different mac then move to stale
     222           0 :     sc::result react(const EvNsIn &event) {
     223           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     224           0 :         if (state_machine->mac() != event.mac_) {
     225           0 :             state_machine->mac() = event.mac_;
     226           0 :             return transit<Stale>();
     227             :         }
     228           0 :         return discard_event();
     229             :     }
     230             : 
     231             :     // move to reachable
     232           0 :     sc::result react(const EvSolNaIn &event) {
     233           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     234           0 :         if (state_machine->mac() != event.mac_) {
     235           0 :             state_machine->mac() = event.mac_;
     236             :         }
     237           0 :         return transit<Reachable>();
     238             :     }
     239             : 
     240             :     // move to stale
     241           0 :     sc::result react(const EvUnsolNaIn &event) {
     242           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     243           0 :         state_machine->mac() = event.mac_;
     244           0 :         return transit<Stale>();
     245             :     }
     246             : 
     247             : 
     248             :     // If less than N retransmissions then retransmit and restart timer
     249             :     // Else discard entry and send icmp error
     250           0 :     sc::result react(const EvRetransmitTimerExpired &event) {
     251           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     252           0 :         if (state_machine->retry_count() < NdpEntry::kMaxRetries) {
     253             :             // In UTs, ndp_entry may not be there
     254           0 :             if (state_machine->get_interface())
     255           0 :                 state_machine->SendNeighborSolicit();
     256           0 :             state_machine->StartRetransmitTimer();
     257             :         } else {
     258           0 :             state_machine->retry_count_clear();
     259           0 :             if (state_machine->DeleteNdpRoute())
     260           0 :                 return discard_event();
     261             :         }
     262           0 :         return discard_event();
     263             :     }
     264             : 
     265           0 :     sc::result react(const EvTestStateChange &event) {
     266           0 :         NdpEntry::State state = event.state_;
     267           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     268           0 :         state_machine->set_state(state);
     269           0 :         state_machine->set_mac(MacAddress());
     270           0 :         switch (state) {
     271           0 :             case NdpEntry::NOSTATE: return transit<NoState>();
     272           0 :             case NdpEntry::INCOMPLETE: return transit<Incomplete>();
     273           0 :             case NdpEntry::REACHABLE: return transit<Reachable>();
     274           0 :             case NdpEntry::STALE: return transit<Stale>();
     275           0 :             case NdpEntry::DELAY: return transit<Delay>();
     276           0 :             case NdpEntry::PROBE: return transit<Probe>();
     277           0 :             default: return discard_event();
     278             :         }
     279             :     }
     280             : };
     281             : 
     282             : struct Reachable : sc::state<Reachable, NdpEntry> {
     283             :     typedef mpl::list<
     284             :         sc::custom_reaction<EvNsIn>,
     285             :         sc::custom_reaction<EvUnsolNaIn>,
     286             :         sc::custom_reaction<EvSolNaIn>,
     287             :         sc::custom_reaction<EvReachableTimerExpired>,
     288             :         sc::custom_reaction<EvTestStateChange>
     289             :     > reactions;
     290             : 
     291           0 :     explicit Reachable(my_context ctx) : my_base(ctx) {
     292           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     293           0 :         state_machine->set_state(NdpEntry::REACHABLE);
     294           0 :         state_machine->StartReachableTimer();
     295           0 :     }
     296             : 
     297           0 :     ~Reachable() {
     298           0 :     }
     299             : 
     300             :     // If different mac then move to stale
     301           0 :     sc::result react(const EvNsIn &event) {
     302           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     303           0 :         if (state_machine->mac() != event.mac_) {
     304           0 :             state_machine->mac() = event.mac_;
     305           0 :             return transit<Stale>();
     306             :         }
     307           0 :         return discard_event();
     308             :     }
     309             : 
     310             : 
     311             :     // If different mac then move to stale else unchanged
     312           0 :     sc::result react(const EvUnsolNaIn &event) {
     313           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     314           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     315           0 :             if (state_machine->mac() != event.mac_) {
     316           0 :                 state_machine->set_mac(event.mac_);
     317           0 :                 return transit<Stale>();
     318             :             }
     319             :         }
     320           0 :         return discard_event();
     321             :     }
     322             : 
     323             : 
     324             :     // If non override and diff mac then move to stale
     325             :     // if override then update mac
     326           0 :     sc::result react(const EvSolNaIn &event) {
     327           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     328           0 :         if (!(event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE)) {
     329           0 :             if (state_machine->mac() != event.mac_) {
     330           0 :                 return transit<Stale>();
     331             :             }
     332             :         } else {
     333           0 :             if (state_machine->mac() != event.mac_) {
     334           0 :                 state_machine->set_mac(event.mac_);
     335             :             }
     336             :         }
     337           0 :         return discard_event();
     338             :     }
     339             : 
     340             :     // move to stale
     341           0 :     sc::result react(const EvReachableTimerExpired &event) {
     342           0 :         return transit<Stale>();
     343             :     }
     344             : 
     345           0 :     sc::result react(const EvTestStateChange &event) {
     346           0 :         NdpEntry::State state = event.state_;
     347           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     348           0 :         state_machine->set_state(state);
     349           0 :         state_machine->set_mac(MacAddress());
     350           0 :         switch (state) {
     351           0 :             case NdpEntry::NOSTATE: return transit<NoState>();
     352           0 :             case NdpEntry::INCOMPLETE: return transit<Incomplete>();
     353           0 :             case NdpEntry::REACHABLE: return transit<Reachable>();
     354           0 :             case NdpEntry::STALE: return transit<Stale>();
     355           0 :             case NdpEntry::DELAY: return transit<Delay>();
     356           0 :             case NdpEntry::PROBE: return transit<Probe>();
     357           0 :             default: return discard_event();
     358             :         }
     359             :     }
     360             : };
     361             : 
     362             : //
     363             : //
     364             : struct Stale : sc::state<Stale, NdpEntry> {
     365             :     typedef mpl::list<
     366             :         sc::custom_reaction<EvNsIn>,
     367             :         sc::custom_reaction<EvUnsolNaIn>,
     368             :         sc::custom_reaction<EvSolNaIn>,
     369             :         sc::custom_reaction<EvPktOut>,
     370             :         sc::custom_reaction<EvTestStateChange>
     371             :     > reactions;
     372             : 
     373             :     // Send a KEEPALIVE and start the keepalive timer on the peer. Also start
     374             :     // the hold timer based on the negotiated hold time value.
     375           0 :     explicit Stale(my_context ctx) : my_base(ctx) {
     376           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     377           0 :         state_machine->set_state(NdpEntry::STALE);
     378           0 :     }
     379             : 
     380             :     // Cancel the hold timer.  If we go to Established, the timer will get
     381             :     // started again from the constructor for that state.
     382           0 :     ~Stale() {
     383           0 :     }
     384             : 
     385             :     // If different mac then move to stale
     386           0 :     sc::result react(const EvNsIn &event) {
     387           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     388           0 :         if (state_machine->mac() != event.mac_) {
     389           0 :             state_machine->set_mac(event.mac_);
     390             :         }
     391           0 :         return discard_event();
     392             :     }
     393             : 
     394             :     // If different mac then move to stale else unchanged
     395           0 :     sc::result react(const EvUnsolNaIn &event) {
     396           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     397           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     398           0 :             if (state_machine->mac() != event.mac_) {
     399           0 :                 state_machine->set_mac(event.mac_);
     400           0 :                 return transit<Stale>();
     401             :             }
     402             :         }
     403           0 :         return discard_event();
     404             :     }
     405             : 
     406             : 
     407             :     // If same mac then move to reachable else unchanged
     408           0 :     sc::result react(const EvSolNaIn &event) {
     409           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     410           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     411           0 :             if (state_machine->mac() != event.mac_) {
     412           0 :                 state_machine->set_mac(event.mac_);
     413             :             }
     414           0 :             return transit<Reachable>();
     415             :         }
     416           0 :         return discard_event();
     417             :     }
     418             : 
     419             :     // start delay timer and move to delay
     420           0 :     sc::result react(const EvPktOut &event) {
     421           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     422           0 :         state_machine->StartDelayTimer();
     423           0 :         return transit<Delay>();
     424             :     }
     425             : 
     426           0 :     sc::result react(const EvTestStateChange &event) {
     427           0 :         NdpEntry::State state = event.state_;
     428           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     429           0 :         state_machine->set_state(state);
     430           0 :         state_machine->set_mac(MacAddress());
     431           0 :         state_machine->retry_count_set(event.retry_);
     432           0 :         switch (state) {
     433           0 :             case NdpEntry::NOSTATE: return transit<NoState>();
     434           0 :             case NdpEntry::INCOMPLETE: return transit<Incomplete>();
     435           0 :             case NdpEntry::REACHABLE: return transit<Reachable>();
     436           0 :             case NdpEntry::STALE: return transit<Stale>();
     437           0 :             case NdpEntry::DELAY: return transit<Delay>();
     438           0 :             case NdpEntry::PROBE: return transit<Probe>();
     439           0 :             default: return discard_event();
     440             :         }
     441             :     }
     442             : };
     443             : 
     444             : //
     445             : // Established is the final state for an operation peer.
     446             : //
     447             : struct Delay : sc::state<Delay, NdpEntry> {
     448             :     typedef mpl::list<
     449             :         sc::custom_reaction<EvNsIn>,
     450             :         sc::custom_reaction<EvUnsolNaIn>,
     451             :         sc::custom_reaction<EvSolNaIn>,
     452             :         sc::custom_reaction<EvDelayTimerExpired>,
     453             :         sc::custom_reaction<EvTestStateChange>
     454             :     > reactions;
     455             : 
     456           0 :     explicit Delay(my_context ctx) : my_base(ctx) {
     457           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     458           0 :         state_machine->retry_count_clear();
     459           0 :         state_machine->set_state(NdpEntry::DELAY);
     460           0 :     }
     461             : 
     462           0 :     ~Delay() {
     463           0 :     }
     464             : 
     465             :     // If different mac then move to stale
     466           0 :     sc::result react(const EvNsIn &event) {
     467           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     468           0 :         if (state_machine->mac() != event.mac_) {
     469           0 :             state_machine->set_mac(event.mac_);
     470           0 :             return transit<Stale>();
     471             :         }
     472           0 :         return discard_event();
     473             :     }
     474             : 
     475             : 
     476             :     // If different mac then move to stale else unchanged
     477           0 :     sc::result react(const EvUnsolNaIn &event) {
     478           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     479           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     480           0 :             if (state_machine->mac() != event.mac_) {
     481           0 :                 state_machine->set_mac(event.mac_);
     482           0 :                 return transit<Stale>();
     483             :             }
     484             :         }
     485           0 :         return discard_event();
     486             :     }
     487             : 
     488             : 
     489             :     // If same mac then move to reachable else unchanged
     490           0 :     sc::result react(const EvSolNaIn &event) {
     491           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     492           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     493           0 :             if (state_machine->mac() != event.mac_) {
     494           0 :                 state_machine->set_mac(event.mac_);
     495             :             }
     496           0 :             return transit<Reachable>();
     497             :         }
     498           0 :         return discard_event();
     499             :     }
     500             : 
     501             :     // Send unicast NS probe and start retransmit timer
     502           0 :     sc::result react(const EvDelayTimerExpired &event) {
     503           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     504             :         // In UTs, ndp_entry may not be there
     505           0 :         if (state_machine->get_interface())
     506           0 :             state_machine->SendNeighborSolicit();
     507           0 :         state_machine->StartRetransmitTimer();
     508           0 :         return transit<Probe>();
     509             :     }
     510             : 
     511           0 :     sc::result react(const EvTestStateChange &event) {
     512           0 :         NdpEntry::State state = event.state_;
     513           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     514           0 :         state_machine->set_state(state);
     515           0 :         state_machine->set_mac(MacAddress());
     516           0 :         switch (state) {
     517           0 :             case NdpEntry::NOSTATE: return transit<NoState>();
     518           0 :             case NdpEntry::INCOMPLETE: return transit<Incomplete>();
     519           0 :             case NdpEntry::REACHABLE: return transit<Reachable>();
     520           0 :             case NdpEntry::STALE: return transit<Stale>();
     521           0 :             case NdpEntry::DELAY: return transit<Delay>();
     522           0 :             case NdpEntry::PROBE: return transit<Probe>();
     523           0 :             default: return discard_event();
     524             :         }
     525             :     }
     526             : };
     527             : 
     528             : //
     529             : // Established is the final state for an operation peer.
     530             : //
     531             : struct Probe : sc::state<Probe, NdpEntry> {
     532             :     typedef mpl::list<
     533             :         sc::custom_reaction<EvNsIn>,
     534             :         sc::custom_reaction<EvUnsolNaIn>,
     535             :         sc::custom_reaction<EvSolNaIn>,
     536             :         sc::custom_reaction<EvRetransmitTimerExpired>,
     537             :         sc::custom_reaction<EvTestStateChange>
     538             :     > reactions;
     539             : 
     540           0 :     explicit Probe(my_context ctx) : my_base(ctx) {
     541           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     542           0 :         state_machine->retry_count_clear();
     543           0 :         state_machine->set_state(NdpEntry::PROBE);
     544           0 :     }
     545             : 
     546           0 :     ~Probe() {
     547           0 :     }
     548             : 
     549             :     // If different mac then move to stale
     550           0 :     sc::result react(const EvNsIn &event) {
     551           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     552           0 :         if (state_machine->mac() != event.mac_) {
     553           0 :             state_machine->set_mac(event.mac_);
     554           0 :             return transit<Stale>();
     555             :         }
     556           0 :         return discard_event();
     557             :     }
     558             : 
     559             : 
     560             :     // If different mac then move to stale else unchanged
     561           0 :     sc::result react(const EvUnsolNaIn &event) {
     562           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     563           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     564           0 :             if (state_machine->mac() != event.mac_) {
     565           0 :                 state_machine->set_mac(event.mac_);
     566           0 :                 return transit<Stale>();
     567             :             }
     568             :         }
     569           0 :         return discard_event();
     570             :     }
     571             : 
     572             : 
     573             :     // If same mac then move to reachable else unchanged
     574           0 :     sc::result react(const EvSolNaIn &event) {
     575           0 :         if (event.na_.nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE) {
     576           0 :             NdpEntry *state_machine = &context<NdpEntry>();
     577           0 :             if (state_machine->mac() != event.mac_) {
     578           0 :                 state_machine->set_mac(event.mac_);
     579             :             }
     580           0 :             return transit<Reachable>();
     581             :         }
     582           0 :         return discard_event();
     583             :     }
     584             : 
     585             :     // If more than N then stale
     586           0 :     sc::result react(const EvRetransmitTimerExpired &event) {
     587           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     588           0 :         if (state_machine->retry_count() < NdpEntry::kMaxUnicastRetries) {
     589           0 :             if (state_machine->get_interface())
     590           0 :                 state_machine->SendNeighborSolicit();
     591           0 :             state_machine->StartRetransmitTimer();
     592           0 :             return discard_event();
     593             :         } else {
     594           0 :             state_machine->retry_count_clear();
     595           0 :             if (state_machine->DeleteNdpRoute())
     596           0 :                 return discard_event();
     597             :         }
     598           0 :         return discard_event();
     599             :     }
     600             : 
     601           0 :     sc::result react(const EvTestStateChange &event) {
     602           0 :         NdpEntry::State state = event.state_;
     603           0 :         NdpEntry *state_machine = &context<NdpEntry>();
     604           0 :         state_machine->set_state(state);
     605           0 :         state_machine->set_mac(MacAddress());
     606           0 :         switch (state) {
     607           0 :             case NdpEntry::NOSTATE: return transit<NoState>();
     608           0 :             case NdpEntry::INCOMPLETE: return transit<Incomplete>();
     609           0 :             case NdpEntry::REACHABLE: return transit<Reachable>();
     610           0 :             case NdpEntry::STALE: return transit<Stale>();
     611           0 :             case NdpEntry::DELAY: return transit<Delay>();
     612           0 :             case NdpEntry::PROBE: return transit<Probe>();
     613           0 :             default: return discard_event();
     614             :         }
     615             :     }
     616             : };
     617             : 
     618             : }  // namespace fsm
     619             : 
     620           0 : NdpEntry::NdpEntry(boost::asio::io_context &io, Icmpv6Handler *handler,
     621           0 :                    NdpKey &key, const VrfEntry *vrf, const Interface *itf)
     622           0 :     : work_queue_(TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
     623             :       NULL,
     624             :       boost::bind(&NdpEntry::DequeueEvent, this, _1)),
     625           0 :       delay_timer_(TimerManager::CreateTimer(
     626             :                   io, "Delay timer",
     627             :                   TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
     628             :                   PktHandler::ICMPV6)),
     629           0 :       retransmit_timer_(TimerManager::CreateTimer(
     630             :                   io, "Retransmit timer",
     631             :                   TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
     632             :                   PktHandler::ICMPV6)),
     633           0 :       reachable_timer_(TimerManager::CreateTimer(
     634             :                   io, "Reachable timer",
     635             :                   TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
     636             :                   PktHandler::ICMPV6)),
     637           0 :       retransmit_time_(1000),
     638           0 :       delay_time_(5000),
     639           0 :       reachable_time_(30000),
     640           0 :       retry_count_(0),
     641           0 :       deleted_(false),
     642           0 :       state_(NOSTATE),
     643           0 :       last_state_(NOSTATE),
     644           0 :       io_(io), key_(key), nh_vrf_(vrf), handler_(handler), interface_(itf) {
     645           0 :     initiate();
     646           0 : }
     647             : 
     648           0 : NdpEntry::~NdpEntry() {
     649           0 :     work_queue_.Shutdown();
     650           0 :     terminate();
     651           0 :     DeleteAllTimers();
     652           0 :     if (handler_.get())
     653           0 :         handler_.reset(NULL);
     654           0 : }
     655             : 
     656           0 : void NdpEntry::DeleteAllTimers() {
     657           0 :     TimerManager::DeleteTimer(delay_timer_);
     658           0 :     TimerManager::DeleteTimer(retransmit_timer_);
     659           0 :     TimerManager::DeleteTimer(reachable_timer_);
     660           0 : }
     661             : 
     662           0 : void NdpEntry::StartDelayTimer() {
     663           0 :     if (delay_time_ <= 0)
     664           0 :         return;
     665             : 
     666           0 :     delay_timer_->Cancel();
     667           0 :     delay_timer_->Start(delay_time_,
     668             :         boost::bind(&NdpEntry::DelayTimerExpired, this), NULL);
     669             : }
     670             : 
     671           0 : void NdpEntry::StartReachableTimer() {
     672           0 :     if (reachable_time_ <= 0)
     673           0 :         return;
     674             : 
     675           0 :     reachable_timer_->Cancel();
     676           0 :     reachable_timer_->Start(reachable_time_,
     677             :         boost::bind(&NdpEntry::ReachableTimerExpired, this), NULL);
     678             : }
     679             : 
     680           0 : bool NdpEntry::ReachableTimerExpired() {
     681           0 :     Enqueue(fsm::EvReachableTimerExpired());
     682           0 :     return false;
     683             : }
     684             : 
     685           0 : void NdpEntry::StartRetransmitTimer() {
     686           0 :     if (retransmit_time_ <= 0)
     687           0 :         return;
     688             : 
     689           0 :     retry_count_inc();
     690           0 :     retransmit_timer_->Cancel();
     691           0 :     retransmit_timer_->Start(retransmit_time_,
     692             :         boost::bind(&NdpEntry::RetransmitTimerExpired, this), NULL);
     693             : }
     694             : 
     695           0 : bool NdpEntry::RetransmitTimerExpired() {
     696           0 :     Enqueue(fsm::EvRetransmitTimerExpired());
     697           0 :     return false;
     698             : }
     699             : 
     700           0 : bool NdpEntry::DelayTimerExpired() {
     701           0 :     Enqueue(fsm::EvDelayTimerExpired());
     702           0 :     return false;
     703             : }
     704             : 
     705           0 : bool NdpEntry::EnqueuePktOut() {
     706           0 :     Enqueue(fsm::EvPktOut());
     707           0 :     return false;
     708             : }
     709           0 : bool NdpEntry::EnqueueRetransmitTimerExpired() {
     710           0 :     Enqueue(fsm::EvRetransmitTimerExpired());
     711           0 :     return false;
     712             : }
     713           0 : bool NdpEntry::EnqueueDelayTimerExpired() {
     714           0 :     Enqueue(fsm::EvDelayTimerExpired());
     715           0 :     return false;
     716             : }
     717           0 : bool NdpEntry::EnqueueNsIn(nd_neighbor_solicit ns, MacAddress mac) {
     718           0 :     Enqueue(fsm::EvNsIn(ns, mac));
     719           0 :     return false;
     720             : }
     721           0 : bool NdpEntry::EnqueueNaIn(nd_neighbor_advert na, MacAddress mac) {
     722           0 :     if (na.nd_na_flags_reserved & ND_NA_FLAG_SOLICITED) {
     723           0 :         Enqueue(fsm::EvSolNaIn(na, mac));
     724             :     } else {
     725           0 :         Enqueue(fsm::EvUnsolNaIn(na, mac));
     726             :     }
     727           0 :     return false;
     728             : }
     729             : 
     730           0 : bool NdpEntry::EnqueueUnsolNaIn(nd_neighbor_advert na, MacAddress mac) {
     731           0 :     Enqueue(fsm::EvUnsolNaIn(na, mac));
     732           0 :     return false;
     733             : }
     734             : 
     735           0 : bool NdpEntry::EnqueueSolNaIn(nd_neighbor_advert na, MacAddress mac) {
     736           0 :     Enqueue(fsm::EvSolNaIn(na, mac));
     737           0 :     return false;
     738             : }
     739             : 
     740           0 : bool NdpEntry::EnqueueTestStateChange(State state, int retry_count) {
     741           0 :     Enqueue(fsm::EvTestStateChange(state, retry_count));
     742           0 :     return false;
     743             : }
     744             : 
     745             : static const string state_names[] = {
     746             :     "NoState",
     747             :     "Incomplete",
     748             :     "Reachable",
     749             :     "Stale",
     750             :     "Delay",
     751             :     "Probe"
     752             : };
     753             : 
     754           0 : const string &NdpEntry::StateName() const {
     755           0 :     return state_names[state_];
     756             : }
     757             : 
     758           0 : const string &NdpEntry::LastStateName() const {
     759           0 :     return state_names[last_state_];
     760             : }
     761             : 
     762           0 : const string NdpEntry::last_state_change_at() const {
     763           0 :     return integerToString(UTCUsecToPTime(last_state_change_at_));
     764             : }
     765             : 
     766           0 : const uint64_t NdpEntry::last_state_change_usecs_at() const {
     767           0 :     return last_state_change_at_;
     768             : }
     769             : 
     770           0 : ostream &operator<<(ostream &out, const NdpEntry::State &state) {
     771           0 :     out << state_names[state];
     772           0 :     return out;
     773             : }
     774             : 
     775             : // This class determines whether a given class has a method called 'validate'.
     776             : template <typename Ev>
     777             : struct HasValidate {
     778             :     template <typename T, bool (T::*)(NdpEntry *) const> struct SFINAE {};
     779             :     template <typename T> static char Test(SFINAE<T, &T::validate>*);
     780             :     template <typename T> static int Test(...);
     781             :     static const bool Has = sizeof(Test<Ev>(0)) == sizeof(char);
     782             : };
     783             : 
     784             : template <typename Ev, bool has_validate>
     785             : struct ValidateFn {
     786             :     EvValidate operator()(const Ev *event) {
     787             :         return NULL;
     788             :     }
     789             : };
     790             : 
     791             : template <typename Ev>
     792             : struct ValidateFn<Ev, true> {
     793           0 :     EvValidate operator()(const Ev *event) {
     794           0 :         return boost::bind(&Ev::validate, event, _1);
     795             :     }
     796             : };
     797             : 
     798             : template <typename Ev>
     799           0 : bool NdpEntry::Enqueue(const Ev &event) {
     800           0 :     LogEvent(TYPE_NAME(event), "Enqueue");
     801           0 :     EventContainer ec;
     802           0 :     ec.event = event.intrusive_from_this();
     803           0 :     ec.validate = ValidateFn<Ev, HasValidate<Ev>::Has>()(
     804           0 :         static_cast<const Ev *>(ec.event.get()));
     805           0 :     work_queue_.Enqueue(ec);
     806             : 
     807           0 :     return true;
     808           0 : }
     809             : 
     810           0 : void NdpEntry::LogEvent(string event_name, string msg,
     811             :                         SandeshLevel::type log_level) {
     812           0 :     SM_LOG(log_level, msg << " " << event_name << " in state " << StateName());
     813           0 : }
     814             : 
     815           0 : bool NdpEntry::DequeueEvent(NdpEntry::EventContainer ec) {
     816           0 :     set_last_event(TYPE_NAME(*ec.event));
     817           0 :     if (ec.validate.empty() || ec.validate(this)) {
     818           0 :         LogEvent(TYPE_NAME(*ec.event), "Dequeue");
     819           0 :         process_event(*ec.event);
     820             :     } else {
     821           0 :         LogEvent(TYPE_NAME(*ec.event), "Discard", SandeshLevel::SYS_INFO);
     822             :     }
     823           0 :     ec.event.reset();
     824             : 
     825           0 :     return true;
     826             : }
     827             : 
     828           0 : void NdpEntry::DequeueEventDone(bool done) {
     829           0 : }
     830             : 
     831           0 : void NdpEntry::set_last_event(const std::string &event) {
     832           0 :     last_event_ = event;
     833           0 :     last_event_at_ = UTCTimestampUsec();
     834           0 : }
     835             : 
     836           0 : void NdpEntry::set_last_notification_out(int code, int subcode,
     837             :     const string &reason) {
     838           0 :     last_notification_out_ = std::make_pair(code, subcode);
     839           0 :     last_notification_out_at_ = UTCTimestampUsec();
     840           0 :     last_notification_out_error_ = reason;
     841             : 
     842           0 : }
     843             : 
     844           0 : void NdpEntry::set_last_notification_in(int code, int subcode,
     845             :     const string &reason) {
     846           0 :     last_notification_in_ = std::make_pair(code, subcode);
     847           0 :     last_notification_in_at_ = UTCTimestampUsec();
     848           0 :     last_notification_in_error_ = reason;
     849             : 
     850           0 : }
     851             : 
     852           0 : void NdpEntry::set_state(State state) {
     853           0 :     if (state == state_)
     854           0 :         return;
     855           0 :     last_state_ = state_; state_ = state;
     856           0 :     last_state_change_at_ = UTCTimestampUsec();
     857             : 
     858             : }
     859             : 
     860           0 : void NdpEntry::reset_last_info() {
     861           0 :     last_notification_in_ = std::make_pair(0, 0);
     862           0 :     last_notification_in_at_ = 0;
     863           0 :     last_notification_in_error_ = std::string();
     864           0 :     last_notification_out_ = std::make_pair(0, 0);
     865           0 :     last_notification_out_at_ = 0;
     866           0 :     last_notification_out_error_ = std::string();
     867           0 :     last_state_ = NOSTATE;
     868           0 :     last_event_ = "";
     869           0 :     last_state_change_at_ = 0;
     870           0 :     last_event_at_ = 0;
     871             : 
     872           0 : }
     873           0 : bool NdpEntry::IsResolved() {
     874           0 :     return (get_state() != (NdpEntry::NOSTATE) &&
     875           0 :             get_state() != (NdpEntry::INCOMPLETE));
     876             : }
     877             : 
     878           0 : bool NdpEntry::IsDerived() {
     879           0 :     return false;
     880             :     if (key_.vrf != nh_vrf_) {
     881             :         return true;
     882             :     }
     883             :     return false;
     884             : }
     885             : 
     886           0 : void NdpEntry::SendNeighborSolicit(bool send_unicast) {
     887           0 :     assert(!IsDerived());
     888           0 :     IpAddress ip = handler_->agent()->router_id6();
     889           0 :     uint32_t vrf_id = get_interface()->vrf_id();
     890             : 
     891           0 :     const VmInterface *vmi = dynamic_cast<const VmInterface *>(get_interface());
     892           0 :     if (vrf_id != VrfEntry::kInvalidIndex) {
     893           0 :         if (ip.is_v6()) {
     894           0 :             handler_->SendNeighborSolicit(ip.to_v6(), key_.ip,
     895             :                                           vmi, vrf_id, send_unicast);
     896             :         }
     897             :     }
     898           0 : }
     899             : 
     900           0 : void NdpEntry::SendNeighborAdvert(bool solicited) {
     901           0 :     assert(!IsDerived());
     902           0 :     Agent *agent = handler_->agent();
     903           0 :     IpAddress ip;
     904           0 :     const VmInterface *vmi = NULL;
     905           0 :     if (interface_->type() == Interface::VM_INTERFACE) {
     906           0 :         vmi = static_cast<const VmInterface *>(interface_.get());
     907           0 :         MacAddress smac = vmi->GetVifMac(agent);
     908           0 :         if (key_.vrf && key_.vrf->vn()) {
     909           0 :             IpAddress gw_ip = key_.vrf->vn()->GetGatewayFromIpam
     910           0 :                     (Ip6Address(key_.ip));
     911           0 :             IpAddress dns_ip = key_.vrf->vn()->GetDnsFromIpam
     912           0 :                     (Ip6Address(key_.ip));
     913           0 :             if (!gw_ip.is_unspecified() && gw_ip.is_v6())  {
     914           0 :                 handler_->SendNeighborAdvert(gw_ip.to_v6(), key_.ip,
     915             :                                              smac, vmi->vm_mac(), vmi->id(),
     916           0 :                                              key_.vrf->vrf_id(), solicited);
     917             :             }
     918           0 :             if (!dns_ip.is_unspecified() && dns_ip.is_v6() && dns_ip != gw_ip) {
     919           0 :                 handler_->SendNeighborAdvert(dns_ip.to_v6(), key_.ip,
     920             :                                              smac, vmi->vm_mac(), vmi->id(),
     921           0 :                                              key_.vrf->vrf_id(), solicited);
     922             :             }
     923             :         }
     924             :     } else {
     925           0 :         if (agent->router_id6().is_v6()) {
     926           0 :             handler_->SendNeighborAdvert(agent->router_id6().to_v6(), key_.ip,
     927             :                  agent->icmpv6_proto()->ip_fabric_interface_mac(),
     928           0 :                  MacAddress(),
     929             :                  agent->icmpv6_proto()->ip_fabric_interface_index(),
     930           0 :                  key_.vrf->vrf_id(), solicited);
     931             :         }
     932             :     }
     933           0 : }
     934             : 
     935           0 : void NdpEntry::HandleNsRequest(nd_neighbor_solicit ns, MacAddress mac) {
     936           0 :     if (IsResolved())
     937           0 :         AddNdpRoute(true);
     938             :     else {
     939           0 :         AddNdpRoute(false);
     940             :     }
     941           0 :     EnqueueNsIn(ns, mac);
     942           0 : }
     943             : 
     944           0 : void NdpEntry::AddNdpRoute(bool resolved) {
     945           0 :     if (key_.vrf->GetName() == handler_->agent()->linklocal_vrf_name()) {
     946             :         // Do not squash existing route entry.
     947             :         // should be smarter and not replace an existing route.
     948           0 :         return;
     949             :     }
     950             : 
     951           0 :     Ip6Address ip(key_.ip);
     952           0 :     const string& vrf_name = key_.vrf->GetName();
     953           0 :     NdpNHKey nh_key(nh_vrf_->GetName(), ip, false);
     954           0 :     NdpNH *ndp_nh = static_cast<NdpNH *>(handler_->agent()->nexthop_table()->
     955           0 :                                          FindActiveEntry(&nh_key));
     956             : 
     957           0 :     if (ndp_nh && ndp_nh->GetResolveState() &&
     958           0 :         mac().CompareTo(ndp_nh->GetMac()) == 0) {
     959             :         // MAC address unchanged, ignore
     960           0 :         if (!IsDerived()) {
     961           0 :             return;
     962             :         } else {
     963             :             /* Return if the route is already existing */
     964             :             InetUnicastRouteKey *rt_key = new InetUnicastRouteKey(
     965           0 :                     handler_->agent()->local_peer(), vrf_name, ip, 32);
     966           0 :             AgentRoute *entry = key_.vrf->GetInet4UnicastRouteTable()->
     967           0 :                 FindActiveEntry(rt_key);
     968           0 :             delete rt_key;
     969           0 :             if (entry) {
     970           0 :                 return;
     971             :             }
     972           0 :             resolved = true;
     973             :         }
     974             :     }
     975             : 
     976           0 :     NDP_TRACE(Trace, "Add", ip.to_string(), vrf_name, mac().ToString());
     977           0 :     AgentRoute *entry = key_.vrf->GetInet6UnicastRouteTable()->FindLPM(ip);
     978             : 
     979           0 :     bool policy = false;
     980           0 :     SecurityGroupList sg;
     981           0 :     TagList tag;
     982           0 :     VnListType vn_list;
     983             :     const NextHop *nh;
     984           0 :     if (entry && (nh = entry->GetActiveNextHop()) != NULL) {
     985           0 :         policy = nh->PolicyEnabled();
     986           0 :         sg = entry->GetActivePath()->sg_list();
     987           0 :         tag = entry->GetActivePath()->tag_list();
     988           0 :         vn_list = entry->GetActivePath()->dest_vn_list();
     989             :     }
     990             : 
     991           0 :     const Interface *itf = handler_->agent()->icmpv6_proto()->ip_fabric_interface();
     992           0 :     if (interface_->type() == Interface::VM_INTERFACE) {
     993             :         const VmInterface *vintf =
     994           0 :             static_cast<const VmInterface *>(interface_.get());
     995           0 :         if (vintf->vmi_type() == VmInterface::VHOST) {
     996           0 :             itf = vintf->parent_list()[0];
     997             :         }
     998             :     }
     999             : 
    1000           0 :     handler_->agent()->fabric_inet4_unicast_table()->NdpRoute(
    1001           0 :                        DBRequest::DB_ENTRY_ADD_CHANGE, vrf_name, ip, mac(),
    1002           0 :                        nh_vrf_->GetName(), *itf, resolved, 128, policy,
    1003             :                        vn_list, sg, tag);
    1004           0 : }
    1005             : 
    1006           0 : bool NdpEntry::DeleteNdpRoute() {
    1007           0 :     if (key_.vrf->GetName() == handler_->agent()->linklocal_vrf_name()) {
    1008           0 :         return true;
    1009             :     }
    1010             : 
    1011           0 :     Ip6Address ip(key_.ip);
    1012           0 :     const string& vrf_name = key_.vrf->GetName();
    1013           0 :     NdpNHKey nh_key(nh_vrf_->GetName(), ip, false);
    1014           0 :     NdpNH *ndp_nh = static_cast<NdpNH *>(handler_->agent()->nexthop_table()->
    1015           0 :                                          FindActiveEntry(&nh_key));
    1016           0 :     if (!ndp_nh)
    1017           0 :         return true;
    1018             : 
    1019           0 :     NDP_TRACE(Trace, "Delete", ip.to_string(), vrf_name, mac().ToString());
    1020           0 :     if (IsDerived()) {
    1021             :         //Just enqueue a delete, no need to mark nexthop invalid
    1022           0 :         InetUnicastAgentRouteTable::Delete(handler_->agent()->local_peer(),
    1023             :                                            vrf_name, ip, 32);
    1024           0 :         return true;
    1025             :     }
    1026             : 
    1027           0 :     handler_->agent()->fabric_inet4_unicast_table()->NdpRoute(
    1028           0 :                        DBRequest::DB_ENTRY_DELETE, vrf_name, ip, mac(),
    1029           0 :                        nh_vrf_->GetName(), *interface_, false, 128, false,
    1030           0 :                        Agent::NullStringList(), SecurityGroupList(), TagList());
    1031           0 :     return false;
    1032           0 : }
    1033             : 
    1034           0 : void NdpEntry::Resync(bool policy, const VnListType &vnlist,
    1035             :                       const SecurityGroupList &sg,
    1036             :                       const TagList &tag) {
    1037           0 :     Ip6Address ip(key_.ip);
    1038           0 :     const string& vrf_name = key_.vrf->GetName();
    1039           0 :     NDP_TRACE(Trace, "Resync", ip.to_string(), vrf_name,
    1040             :               mac().ToString());
    1041           0 :     handler_->agent()->fabric_inet4_unicast_table()->NdpRoute(
    1042           0 :                        DBRequest::DB_ENTRY_ADD_CHANGE, key_.vrf->GetName(), ip,
    1043           0 :                        mac(), nh_vrf_->GetName(), *interface_, IsResolved(),
    1044             :                        32, policy, vnlist, sg, tag);
    1045           0 : }

Generated by: LCOV version 1.14