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

Generated by: LCOV version 1.14