diff --git a/src/DbInterface.cpp b/src/DbInterface.cpp index 2716a3c..4e08ac4 100644 --- a/src/DbInterface.cpp +++ b/src/DbInterface.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include #include @@ -323,6 +325,9 @@ void DbInterface::initialize() mAppDbForwardingCommandTablePtr = std::make_shared ( mAppDbPtr.get(), APP_FORWARDING_STATE_COMMAND_TABLE_NAME ); + mAppDbIcmpEchoSessionTablePtr = std::make_shared ( + mAppDbPtr.get(), APP_ICMP_ECHO_SESSION_TABLE_NAME + ); mStateDbMuxLinkmgrTablePtr = std::make_shared ( mStateDbPtr.get(), STATE_MUX_LINKMGR_TABLE_NAME ); @@ -335,7 +340,11 @@ void DbInterface::initialize() mStateDbSwitchCauseTablePtr = std::make_shared ( mStateDbPtr.get(), STATE_MUX_SWITCH_CAUSE_TABLE_NAME ); + mStateDbIcmpEchoSessionTablePtr = std::make_shared ( + mStateDbPtr.get(), STATE_ICMP_ECHO_SESSION_TABLE_NAME + ); mMuxStateTablePtr = std::make_shared (mStateDbPtr.get(), STATE_MUX_CABLE_TABLE_NAME); + mSwitchCapTablePtr = std::make_shared (mStateDbPtr.get(), STATE_SWITCH_CAPABILITY_TABLE_NAME); mSwssThreadPtr = std::make_shared (&DbInterface::handleSwssNotification, this); } @@ -808,6 +817,61 @@ void DbInterface::getPortCableType(std::shared_ptr configDbCo processPortCableType(entries); } +// +// ---> processProberType(std::vector &entries); +// +// process Mux Cable Table enteries to get proberType by defaut its software +// +void DbInterface::processProberType(std::vector &entries) +{ + // check hardware capability + static bool hw_offload_capable = false; + std::string capable; + if (mSwitchCapTablePtr && mSwitchCapTablePtr->hget("switch", "ICMP_OFFLOAD_CAPABLE", capable)) + { + if (capable == "true") + { + hw_offload_capable = true; + MUXLOGWARNING(boost::format("Hardware Link Prober capability detected")); + } + } + + for (auto &entry: entries) { + std::string portName = kfvKey(entry); + std::string operation = kfvOp(entry); + std::vector fieldValues = kfvFieldsValues(entry); + + std::string field = "prober_type"; + std::vector::const_iterator cit = std::find_if( + fieldValues.cbegin(), + fieldValues.cend(), + [&field] (const swss::FieldValueTuple &fv) {return fvField(fv) == field;} + ); + std::string proberType = ((hw_offload_capable && cit != fieldValues.cend()) ? + cit->second : "software"); + MUXLOGWARNING(boost::format("%s: Link Prober type = {%s}") % portName % proberType); + + MUXLOGDEBUG(boost::format("port: %s, %s = %s") % portName % field % proberType); + + mMuxManagerPtr->updateProberType(portName, proberType); + } +} + +// +// ---> getPortCableType(std::shared_ptr configDbConnector); +// +// retrieve the Link Failure Detection Type (HW/SW) from config +// +void DbInterface::getProberType(std::shared_ptr configDbConnector) +{ + MUXLOGINFO("Reading prober_type"); + swss::Table configDbMuxCableTable(configDbConnector.get(), CFG_MUX_CABLE_TABLE_NAME); + std::vector entries; + + configDbMuxCableTable.getContent(entries); + processProberType(entries); +} + // // ---> processSoCIpAddress(std::vector &entries); // @@ -1524,6 +1588,193 @@ void DbInterface::processTsaEnableNotification(std::deque createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries); +// +// triggers creation of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE +// +void DbInterface::createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries) +{ + MUXLOGDEBUG(boost::format(" %s : ICMP session Being created ") % key); + boost::asio::post(mStrand, boost::bind( + &DbInterface::handleIcmpEchoSession, + this, + key, + entries.release() + )); +} + +// +// ---> handleIcmpEchoSession(std::string key, IcmpHwOffloadEntries *entries); +// +// handles creation of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE +// +void DbInterface::handleIcmpEchoSession(std::string key, IcmpHwOffloadEntries *entries) +{ + std::vector fvs; + for(auto& entry : *entries) { + const std::string field = entry.first; + const std::string value = entry.second; + fvs.emplace_back(field, value); + MUXLOGDEBUG(boost::format("APP_ICMP_ECHO_SESSION_TABLE::key: %s, field: %s, value: %s") % + key % + field % + value + ); + } + mAppDbIcmpEchoSessionTablePtr->set(key, fvs); + delete entries; +} + +// +// ---> updateIntervalv4(uint32_t tx_interval, uint32_t rx_interval); +// +// handles interval_v4 field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE +// +void DbInterface::updateIntervalv4(uint32_t tx_interval, uint32_t rx_interval) +{ + MUXLOGDEBUG(boost::format("Updating Interval v4 tx(%u) rx(%u)") % + tx_interval % rx_interval); + boost::asio::post(mStrand, boost::bind( + &DbInterface::handleUpdateInterval, + this, + tx_interval, + rx_interval + )); +} + +// +// ---> updateIntervalv6(uint32_t tx_interval, uint32_t rx_interval); +// +// handles update of interval_v6 field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE +// +void DbInterface::updateIntervalv6(uint32_t tx_interval, uint32_t rx_interval) +{ + MUXLOGDEBUG(boost::format("Updating Interval v6 tx(%u) rx(%u)") % + tx_interval % rx_interval); + boost::asio::post(mStrand, boost::bind( + &DbInterface::handleUpdateInterval, + this, + tx_interval, + rx_interval + )); +} + +// +// --->handleUpdateInterval(uint32_t tx_interval, uint32_t rx_interval); +// +// handles update of tx/rx interval field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABL +// +void DbInterface::handleUpdateInterval(uint32_t tx_interval, uint32_t rx_interval) +{ + std::shared_ptr appDbPtr = std::make_shared ("APPL_DB", 0); + swss::Table icmpAppDbTbl(appDbPtr.get(), APP_ICMP_ECHO_SESSION_TABLE_NAME); + std::vector keys; + icmpAppDbTbl.getKeys(keys); + std::vector fvs; + fvs.emplace_back("tx_interval", std::to_string(tx_interval)); + fvs.emplace_back("rx_interval", std::to_string(rx_interval)); + for (auto& key : keys) + { + mAppDbIcmpEchoSessionTablePtr->set(key, fvs); + } +} + +// +// ---> deleteIcmpEchoSession(std::string key); +// +// handles deletion of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE +// +void DbInterface::deleteIcmpEchoSession(std::string key) +{ + MUXLOGDEBUG(boost::format("%s : ICMP session Being deleted") % key); + boost::asio::post(mStrand, boost::bind( + &DbInterface::handleDeleteIcmpEchoSession, + this, + key + )); +} + +// +// ---> handleDeleteIcmpEchoSession(std::string key); +// +// handles deletion of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE +// +void DbInterface::handleDeleteIcmpEchoSession(std::string key) { + MUXLOGDEBUG(boost::format("APP_ICMP_ECHO_SESSION_TABLE::key: %s ") % + key + ); + mAppDbIcmpEchoSessionTablePtr->del(key); +} + +// +// ---> extractIfnameAndSessionType(const std::string &key, std::string &ifname, std::string &sessionType); +// +// helper function to extract interface name and session_type from the key +// +void DbInterface::extractIfnameAndSessionType(const std::string &key, std::string &ifname, std::string &sessionType) +{ + size_t firstSep = key.find('|'); + size_t secondSep = key.find('|', firstSep + 1); + size_t thirdSep = key.find('|', secondSep + 1); + + if (firstSep != std::string::npos && + secondSep != std::string::npos && + thirdSep != std::string::npos) { + ifname = key.substr(firstSep + 1, secondSep - firstSep - 1); + sessionType = key.substr(thirdSep + 1); + } else { + std::cerr << "Unexpected format: " << key << std::endl; + } +} + +// +// ---> processIcmpEchoSessionStateNotification(std::deque &entries); +// +// process each entery of ICMP_ECHO_SESSION_TABLE extract Port, Session_Type(Normal/Rx) and state itself +// +void DbInterface::processIcmpEchoSessionStateNotification(std::deque &entries) +{ + for (auto &entry: entries) { + std::string key = kfvKey(entry); + std::string port; + std::string session_type; + extractIfnameAndSessionType(key, port, session_type); + std::string opertation = kfvOp(entry); + std::vector fieldValues = kfvFieldsValues(entry); + + std::vector::const_iterator cit = std::find_if( + fieldValues.cbegin(), + fieldValues.cend(), + [] (const swss::FieldValueTuple &fv) {return fvField(fv) == "state";} + ); + if (cit != fieldValues.cend()) { + const std::string field = cit->first; + const std::string value = cit->second; + + MUXLOGINFO(boost::format("port: %s, f: %s, v: %s") % + port % + field % + value + ); + mMuxManagerPtr->updateLinkFailureDetectionState(port, value, session_type); + } + } +} + +// +// ---> handleIcmpEchoSessionStateNotification(swss::SubscriberStateTable &stateDbIcmpEchoSessionTable); +// +// extract enteries from State_DB ICMP_ECHO_SESSION_TABLE and call processing function +// +void DbInterface::handleIcmpEchoSessionStateNotification(swss::SubscriberStateTable &stateDbIcmpEchoSessionTable) +{ + std::deque entries; + + stateDbIcmpEchoSessionTable.pops(entries); + processIcmpEchoSessionStateNotification(entries); +} + // // ---> handleSwssNotification(); // @@ -1556,11 +1807,14 @@ void DbInterface::handleSwssNotification() swss::SubscriberStateTable stateDbMuxInfoTable(stateDbPtr.get(), MUX_CABLE_INFO_TABLE); // for getting peer's admin forwarding state swss::SubscriberStateTable stateDbPeerMuxTable(stateDbPtr.get(), STATE_PEER_HW_FORWARDING_STATE_TABLE_NAME); + // for getting icmp echo session state + swss::SubscriberStateTable stateDbIcmpEchoSessionTable(stateDbPtr.get(), STATE_ICMP_ECHO_SESSION_TABLE_NAME); getTorMacAddress(configDbPtr); getVlanNames(configDbPtr); getLoopback2InterfaceInfo(configDbPtr); getPortCableType(configDbPtr); + getProberType(configDbPtr); getServerIpAddress(configDbPtr); getSoCIpAddress(configDbPtr); @@ -1584,6 +1838,7 @@ void DbInterface::handleSwssNotification() swssSelect.addSelectable(&stateDbMuxInfoTable); swssSelect.addSelectable(&stateDbPeerMuxTable); swssSelect.addSelectable(&netlinkNeighbor); + swssSelect.addSelectable(&stateDbIcmpEchoSessionTable); while (mPollSwssNotifcation) { swss::Selectable *selectable; @@ -1619,6 +1874,8 @@ void DbInterface::handleSwssNotification() handlePeerLinkStateNotification(stateDbMuxInfoTable); } else if (selectable == static_cast (&stateDbPeerMuxTable)) { handlePeerMuxStateNotification(stateDbPeerMuxTable); + } else if (selectable == static_cast (&stateDbIcmpEchoSessionTable)) { + handleIcmpEchoSessionStateNotification(stateDbIcmpEchoSessionTable); } else if (selectable == static_cast (&netlinkNeighbor)) { continue; } else { diff --git a/src/DbInterface.h b/src/DbInterface.h index 23eba27..d343b18 100644 --- a/src/DbInterface.h +++ b/src/DbInterface.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -35,12 +36,14 @@ #include "swss/producerstatetable.h" #include "swss/subscriberstatetable.h" #include "swss/warm_restart.h" - +#include "link_prober/LinkProberBase.h" #include "link_manager/LinkManagerStateMachineActiveStandby.h" #include "mux_state/MuxState.h" + namespace test { class MuxManagerTest; +class LinkProberHardwareTest; } namespace mux @@ -54,10 +57,15 @@ namespace mux #define APP_PEER_HW_FORWARDING_STATE_TABLE_NAME "HW_FORWARDING_STATE_PEER" #define STATE_PEER_HW_FORWARDING_STATE_TABLE_NAME "HW_MUX_CABLE_TABLE_PEER" +#define STATE_ICMP_ECHO_SESSION_TABLE_NAME "ICMP_ECHO_SESSION_TABLE" +#define APP_ICMP_ECHO_SESSION_TABLE_NAME "ICMP_ECHO_SESSION_TABLE" + #define STATE_MUX_SWITCH_CAUSE_TABLE_NAME "MUX_SWITCH_CAUSE" class MuxManager; using ServerIpPortMap = std::map; +using IcmpHwOffloadEntries = std::vector>; +using IcmpHwOffloadEntriesPtr = std::unique_ptr; /** *@class DbInterface @@ -397,9 +405,45 @@ class DbInterface */ virtual std::map getMuxModeConfig(); + /** + *@method createIcmpEchoSession + * + *@brief retrieve mux mode config + * + *@return triggers creation of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + */ + virtual void createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries); + + /** + *@method deleteIcmpEchoSession + * + *@brief retrieve mux mode config + * + *@return handles deletion of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + */ + virtual void deleteIcmpEchoSession(std::string key); + + /** + *@method updateIntervalv4 + * + *@brief update v4 Interval + * + *@return handles update of interval_v4 field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + */ + virtual void updateIntervalv4(uint32_t tx_interval, uint32_t rx_interval); + + /** + *@method updateIntervalv6 + * + *@brief update v6 Interval + * + *@return handles update of interval_v6 field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + */ + virtual void updateIntervalv6(uint32_t tx_interval, uint32_t rx_interval); + private: friend class test::MuxManagerTest; - + friend class test::LinkProberHardwareTest; /** *@method handleGetMuxState * @@ -823,7 +867,7 @@ class DbInterface * *@return none */ - void handleSwssNotification(); + virtual void handleSwssNotification(); /** * @method processDefaultRouteStateNotification @@ -865,6 +909,87 @@ class DbInterface */ void processTsaEnableNotification(std::deque &entries); + /** + * @method handleIcmpEchoSessionStateNotification + * + * @brief extract enteries from State_DB ICMP_ECHO_SESSION_TABLE and call processing function + * + * @return none + */ + void handleIcmpEchoSessionStateNotification(swss::SubscriberStateTable &stateDbIcmpEchoSessionTable); + + /** + * @method handleIcmpEchoSession + * + * @brief handles creation of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + * + * @return none + */ + void handleIcmpEchoSession(std::string key, IcmpHwOffloadEntries *entries); + + /** + * @method handleDeleteIcmpEchoSession + * + * @brief handles deletion of a ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + * + * @return none + */ + void handleDeleteIcmpEchoSession(std::string key); + + /** + * @method handleUpdateInterval + * + * @brief handles update of interval_v4 field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + * + * @return none + */ + void handleUpdateInterval(uint32_t tx_interval, uint32_t rx_interval); + + /** + * @method handleUpdateTxIntervalv6 + * + * @brief handles update of interval_v6 field for ICMP_ECHO_SESSION in APP_ICMP_ECHO_SESSION_TABLE + * + * @return none + */ + void handleUpdateTxIntervalv6(std::string key, uint32_t tx_interval); + + /** + * @method processTsaEnableNotification + * + * @brief process each entery of ICMP_ECHO_SESSION_TABLE extract Port, Session_Type(Normal/Rx) and state itself + * + * @return none + */ + void processIcmpEchoSessionStateNotification(std::deque &entries); + + /** + * @method processProberType + * + * @brief process Mux Cable Table enteries to get linkFailureDetectionType by defaut its software + * + * @return none + */ + void processProberType(std::vector &entries); + + /** + * @method getProberType + * + * @brief retrieve the Link Failure Detection Type (HW/SW) from config + * + * @return none + */ + void getProberType(std::shared_ptr configDbConnector); + + /** + * @method extractIfnameAndSessionType + * + * @brief helper function to extract interface name and session_type from the key + * + * @return none + */ + void extractIfnameAndSessionType(const std::string &key, std::string &ifname, std::string &sessionType); + private: static std::vector mMuxState; static std::vector mMuxLinkmgrState; @@ -879,9 +1004,12 @@ class DbInterface std::shared_ptr mAppDbPtr; std::shared_ptr mStateDbPtr; std::shared_ptr mMuxStateTablePtr; + std::shared_ptr mSwitchCapTablePtr; - // for communicating with orchagent + // for communicating with orchagent for mux stae std::shared_ptr mAppDbMuxTablePtr; + // for communicating with orchagent for icmp echo session + std::shared_ptr mAppDbIcmpEchoSessionTablePtr; // for communication with driver (setting peer's forwarding state) std::shared_ptr mAppDbPeerMuxTablePtr; // for communicating with the driver (probing the mux) @@ -896,6 +1024,8 @@ class DbInterface std::shared_ptr mStateDbLinkProbeStatsTablePtr; // for writing mux switch reason to state db std::shared_ptr mStateDbSwitchCauseTablePtr; + // for reading icmp echo session state from state db + std::shared_ptr mStateDbIcmpEchoSessionTablePtr; std::shared_ptr mSwssThreadPtr; diff --git a/src/LinkMgrdMain.cpp b/src/LinkMgrdMain.cpp index 2dc9d18..4aeb34c 100644 --- a/src/LinkMgrdMain.cpp +++ b/src/LinkMgrdMain.cpp @@ -35,7 +35,7 @@ #include "link_manager/LinkManagerStateMachineActiveStandby.h" #include "link_prober/LinkProberStateMachineActiveActive.h" #include "link_prober/LinkProberStateMachineActiveStandby.h" -#include "link_prober/LinkProber.h" +#include "link_prober/LinkProberBase.h" #include "link_prober/IcmpPayload.h" // Private namespace for this module @@ -128,9 +128,6 @@ int main(int argc, const char* argv[]) ss << "level: " << level; MUXLOGINFO(ss.str()); - // initialize static data - link_prober::IcmpPayload::generateGuid(); - // warm restart static swss::WarmStart::initialize("linkmgrd", "mux"); swss::WarmStart::checkWarmStart("linkmgrd", "mux"); diff --git a/src/MuxManager.cpp b/src/MuxManager.cpp index 33284c2..48368e1 100644 --- a/src/MuxManager.cpp +++ b/src/MuxManager.cpp @@ -43,9 +43,9 @@ MuxManager::MuxManager() : mMuxConfig(), mWork(mIoService), mSignalSet(boost::asio::signal_set(mIoService, SIGINT, SIGTERM)), - mDbInterfacePtr(std::make_shared (this, &mIoService)), mStrand(mIoService), - mReconciliationTimer(mIoService) + mReconciliationTimer(mIoService), + mDbInterfacePtr(std::make_shared (this, &mIoService)) { mSignalSet.add(SIGUSR1); mSignalSet.add(SIGUSR2); @@ -70,6 +70,31 @@ void MuxManager::setUseWellKnownMacActiveActive(bool useWellKnownMac) } } +// +// ---> setTimeoutIpv4_msec(uint32_t timeout_msec) +// +// update Interval Time +// +void MuxManager::setTimeoutIpv4_msec(uint32_t timeout_msec) +{ + mMuxConfig.setTimeoutIpv4_msec(timeout_msec); + for (const auto & [portName, muxPort] : mPortMap) { + muxPort->setTimeoutIpv4_msec(timeout_msec); + } +} + +// +// ---> setTimeoutIpv6_msec(uint32_t timeout_msec) +// +// update Interval Time +// +void MuxManager::setTimeoutIpv6_msec(uint32_t timeout_msec) +{ + mMuxConfig.setTimeoutIpv6_msec(timeout_msec); + for (const auto & [portName, muxPort] : mPortMap) { + muxPort->setTimeoutIpv6_msec(timeout_msec); + } +} // // ---> initialize(); @@ -237,11 +262,47 @@ void MuxManager::updatePortCableType(const std::string &portName, const std::str mPortCableTypeMap.insert(PortCableTypeMap::value_type(portName, portCableType)); } -// +// +// ---> updateLinkFailureDetectionState(const std::string &portName, const std::string &linkFailureDetectionState, const std::string &session_type); +// +// updates the link state change in state_db for a ICMP_ECHO_SESSION +// +void MuxManager::updateLinkFailureDetectionState(const std::string &portName, const std::string &linkFailureDetectionState, const std::string &session_type) +{ + MUXLOGDEBUG(boost::format("%s: link failure detection state for %s : %s") % portName % session_type %linkFailureDetectionState ); + std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + common::MuxPortConfig::PortCableType portCableType = getMuxPortCableType(portName); + + if (portCableType == common::MuxPortConfig::PortCableType::ActiveActive) { + // notify link failure detection type for ports in active-active cable type + muxPortPtr->updateLinkFailureDetectionState(linkFailureDetectionState, session_type); + } +} + +// +// ---> updateProberType(const std::string &portName, const std::string &proberType); +// +// Updates the link_failure_detection_type for link_prober +// +void MuxManager::updateProberType(const std::string &portName, const std::string &proberType) +{ + MUXLOGDEBUG(boost::format("%s: link failure detection type for : %s") % portName % proberType ); + std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); + common::MuxPortConfig::PortCableType portCableType = getMuxPortCableType(portName); + + if (portCableType == common::MuxPortConfig::PortCableType::ActiveActive) { + // notify prober type for ports in active-active cable type + muxPortPtr->updateProberType(proberType); + } else { + MUXLOGERROR(boost::format("%s: Unsupported link failure detection type for : %s") % portName % proberType ); + } +} + +// // ---> resetPckLossCount(const std::string &portName); // -// reset ICMP packet loss count -// +// reset ICMP packet loss count +// void MuxManager::resetPckLossCount(const std::string &portName) { MUXLOGWARNING(boost::format("%s: reset ICMP packet loss count ") % portName); diff --git a/src/MuxManager.h b/src/MuxManager.h index 3e8724e..8351b05 100644 --- a/src/MuxManager.h +++ b/src/MuxManager.h @@ -110,7 +110,7 @@ class MuxManager * *@return none */ - inline void setTimeoutIpv4_msec(uint32_t timeout_msec) {mMuxConfig.setTimeoutIpv4_msec(timeout_msec);}; + void setTimeoutIpv4_msec(uint32_t timeout_msec); /** *@method setTimeoutIpv6_msec @@ -121,7 +121,7 @@ class MuxManager * *@return none */ - inline void setTimeoutIpv6_msec(uint32_t timeout_msec) {mMuxConfig.setTimeoutIpv6_msec(timeout_msec);}; + void setTimeoutIpv6_msec(uint32_t timeout_msec); /** *@method setOscillationEnabled @@ -506,6 +506,25 @@ class MuxManager */ void processResetSuspendTimer(const std::vector &portNames); + /** + * @method updateLinkFailureDetectionState + * + * @brief update state of ICMP_ECHO_SESSION + * + * @return none + */ + void updateLinkFailureDetectionState(const std::string &portName, const std::string &linkFailureDetectionState, + const std::string &session_type); + + /** + * @method updateProberType + * + * @brief updates link_failure_detection type for link_prober + * + * @return none + */ + void updateProberType(const std::string &portName, const std::string &proberType); + private: /** *@method getMuxPortCableType diff --git a/src/MuxPort.cpp b/src/MuxPort.cpp index 37abb5f..2462e8c 100644 --- a/src/MuxPort.cpp +++ b/src/MuxPort.cpp @@ -39,6 +39,8 @@ #include "MuxPort.h" #include "common/MuxException.h" #include "common/MuxLogger.h" +#include "link_prober/LinkProberStateMachineActiveActive.h" +#include "link_prober/LinkProberStateMachineActiveStandby.h" namespace mux { @@ -119,6 +121,37 @@ void MuxPort::handleSoCIpv4AddressUpdate(boost::asio::ip::address address) )); } +// ---> updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, const std::string &session_type); +// +// handles link failure detection state update for port in active-active cable type +// +void MuxPort::updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, const std::string &session_type) +{ + MUXLOGDEBUG(boost::format("port: %s") % mMuxPortConfig.getPortName()); + + boost::asio::post(mStrand, boost::bind( + &link_manager::LinkManagerStateMachineBase::updateLinkFailureDetectionState, + mLinkManagerStateMachinePtr.get(), + linkFailureDetectionState, + session_type + )); +} + +// ---> updateProberType(const std::string &linkFailureDetectionState); +// +// handles link failure detection type update for port in active-active cable type +// +void MuxPort::updateProberType(const std::string &proberType) +{ + MUXLOGDEBUG(boost::format("port: %s") % mMuxPortConfig.getPortName()); + if(proberType == "hardware") + { + mMuxPortConfig.setLinkProberType(common::MuxPortConfig::LinkProberType::Hardware); + } else { + mMuxPortConfig.setLinkProberType(common::MuxPortConfig::LinkProberType::Software); + } +} + // // ---> handleLinkState(const std::string &linkState); // @@ -177,6 +210,32 @@ void MuxPort::handleGetServerMacAddress(const std::array setTimeoutIpv4_msec (uint32_t timeout_msec); +// +// calls DbInterface API to update Tx v4 Interval for ICMP_ECHO_SESSION +// +void MuxPort::setTimeoutIpv4_msec (uint32_t timeout_msec) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + uint32_t rx_interval = timeout_msec * mMuxPortConfig.getNegativeStateChangeRetryCount(); + + mDbInterfacePtr->updateIntervalv4(timeout_msec, rx_interval); +} + +// +// ---> setTimeoutIpv6_msec (uint32_t timeout_msec) +// +// calls DbInterface API to update Tx v6 Interval for ICMP_ECHO_SESSION +// +void MuxPort::setTimeoutIpv6_msec (uint32_t timeout_msec) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + uint32_t rx_interval = timeout_msec * mMuxPortConfig.getNegativeStateChangeRetryCount(); + + mDbInterfacePtr->updateIntervalv6(timeout_msec, rx_interval); +} + // // ---> handleUseWellKnownMacAddress() // diff --git a/src/MuxPort.h b/src/MuxPort.h index a187f87..11be9ec 100644 --- a/src/MuxPort.h +++ b/src/MuxPort.h @@ -26,8 +26,6 @@ #include #include - -#include "link_prober/LinkProber.h" #include "link_prober/LinkProberStateMachineBase.h" #include "link_manager/LinkManagerStateMachineActiveActive.h" #include "link_manager/LinkManagerStateMachineActiveStandby.h" @@ -42,7 +40,6 @@ class FakeMuxPort; namespace mux { - /** *@class MuxPort * @@ -79,7 +76,7 @@ class MuxPort: public std::enable_shared_from_this *@param ioService (in) reference to Boost IO Service object */ MuxPort( - std::shared_ptr dbInterfacePtr, + std::shared_ptr dbInterfacePtr, common::MuxConfig &muxConfig, const std::string &portName, uint16_t serverId, @@ -382,52 +379,110 @@ class MuxPort: public std::enable_shared_from_this /** - * @method handleDefaultRouteState(const std::string &routeState) - * - * @brief handles default route state notification - * - * @param routeState - * - * @return none + * @method handleDefaultRouteState(const std::string &routeState) + * + * @brief handles default route state notification + * + * @param routeState + * + * @return none */ void handleDefaultRouteState(const std::string &routeState); /** - * @method resetPckLossCount - * - * @brief reset ICMP packet loss count - * - * @return none + * @method resetPckLossCount + * + * @brief reset ICMP packet loss count + * + * @return none */ void resetPckLossCount(); /** - * @method warmRestartReconciliation - * - * @brief port warm restart reconciliation procedure - * - * @return none - */ + * @method warmRestartReconciliation + * + * @brief port warm restart reconciliation procedure + * + * @return none + */ void warmRestartReconciliation(); /** - * @method handleTsaEnable - * - * @brief handle TSA Enable event - * - * @return none - */ + * @method handleTsaEnable + * + * @brief handle TSA Enable event + * + * @return none + */ void handleTsaEnable(bool enable); /** - *@method handleResetSuspendTimer - * - *@brief handle reset suspend timer request - * - * @return none - */ + *@method handleResetSuspendTimer + * + *@brief handle reset suspend timer request + * + * @return none + */ void handleResetSuspendTimer(); + /** + * @method updateLinkFailureDetectionState + * + * @brief handles link failure detection state update for port in active-active cable type + * + * @return none + */ + void updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, const std::string &session_type); + + /** + * @method updateProberType + * + * @brief link failure detection type update for port in active-active cable type + * + * @return none + */ + void updateProberType(const std::string &linkFailureDetectionType); + + /** + * @method createIcmpEchoSession + * + * @brief calls DbInterface API to create ICMO_ECHO_SESSION + * + * @return none + */ + inline void createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries) { + mDbInterfacePtr->createIcmpEchoSession(key, std::move(entries)); + } + + /** + * @method deleteIcmpEchoSession + * + * @brief calls DbInterface API to delete ICMO_ECHO_SESSION + * + * @return none + */ + inline void deleteIcmpEchoSession(std::string key) { + mDbInterfacePtr->deleteIcmpEchoSession(key); + } + + /** + * @method setTimeoutIpv4_msec + * + * @brief calls DbInterface API to update Tx v4 Interval for ICMP_ECHO_SESSION + * + * @return none + */ + void setTimeoutIpv4_msec(uint32_t timeout_msec); + + /** + * @method setTimeoutIpv6_msec + * + * @brief calls DbInterface API to update Tx v6 Interval for ICMP_ECHO_SESSION + * + * @return none + */ + void setTimeoutIpv6_msec(uint32_t timeout_msec); + protected: friend class test::MuxManagerTest; friend class test::FakeMuxPort; @@ -459,7 +514,7 @@ class MuxPort: public std::enable_shared_from_this void resetLinkManagerStateMachinePtr(link_manager::LinkManagerStateMachineBase *stateMachinePtr) { mLinkManagerStateMachinePtr.reset(stateMachinePtr); }; private: - std::shared_ptr mDbInterfacePtr = nullptr; + std::shared_ptr mDbInterfacePtr = nullptr; common::MuxPortConfig mMuxPortConfig; boost::asio::io_service::strand mStrand; diff --git a/src/common/MuxConfig.h b/src/common/MuxConfig.h index 8ad6454..7a93d95 100644 --- a/src/common/MuxConfig.h +++ b/src/common/MuxConfig.h @@ -86,6 +86,17 @@ class MuxConfig */ inline void setTimeoutIpv4_msec(uint32_t timeout_msec) {mTimeoutIpv4_msec = timeout_msec;}; + /** + *@method setRxTimeoutIpv4_msec + * + *@brief setter for IPv4 LinkProber Rx timeout in msec + * + *@param timeout_msec (in) timeout in msec + * + *@return none + */ + inline void setRxTimeoutIpv4_msec(uint32_t timeout_msec) {mRxTimeoutIpv4_msec = timeout_msec;}; + /** *@method setTimeoutIpv6_msec * @@ -455,6 +466,7 @@ class MuxConfig uint8_t mNumberOfThreads = 5; uint32_t mTimeoutIpv4_msec = 100; uint32_t mTimeoutIpv6_msec = 1000; + uint32_t mRxTimeoutIpv4_msec = 300; uint32_t mPositiveStateChangeRetryCount = 1; uint32_t mNegativeStateChangeRetryCount = 3; uint32_t mLinkProberStatUpdateIntervalCount = 300; diff --git a/src/common/MuxPortConfig.h b/src/common/MuxPortConfig.h index fa8a8b6..8f8842a 100644 --- a/src/common/MuxPortConfig.h +++ b/src/common/MuxPortConfig.h @@ -27,6 +27,8 @@ #include #include #include +#include + #include "MuxConfig.h" @@ -51,7 +53,7 @@ class MuxPortConfig Manual, Active, Standby, - Detached // mux mode for active-active cable type only + Detached // mux mode for active-active cable type only }; /** @@ -65,6 +67,18 @@ class MuxPortConfig DefaultType = ActiveStandby }; + + /** + * @enum LinkProberType + * + * @brief Link Prober Type + */ + enum LinkProberType { + Software, + Hardware, + Default=Software + }; + public: /** *@method MuxPortConfig @@ -309,6 +323,26 @@ class MuxPortConfig */ inline PortCableType getPortCableType() const {return mPortCableType;}; + /** + *@method getLinkProberType + * + *@brief getter for LinkProberType + * + *@return LinkProberType + */ + inline LinkProberType getLinkProberType() const {return mLinkProberType;}; + + /** + * @method setLinkProberType + * + * @brief Set the Link Prober Type + * + * @param LinkProberType link prober hardware + * + * @return none + */ + inline void setLinkProberType(LinkProberType linkProberType) { mLinkProberType = linkProberType; }; + /** *@method getDecreasedTimeoutIpv4_msec * @@ -424,6 +458,7 @@ class MuxPortConfig uint16_t mServerId; Mode mMode = Manual; PortCableType mPortCableType; + LinkProberType mLinkProberType; uint32_t mAdminForwardingStateSyncUpInterval_msec = 10000; }; diff --git a/src/link_manager/LinkManagerStateMachineActiveActive.cpp b/src/link_manager/LinkManagerStateMachineActiveActive.cpp index 7511210..2a8fe14 100644 --- a/src/link_manager/LinkManagerStateMachineActiveActive.cpp +++ b/src/link_manager/LinkManagerStateMachineActiveActive.cpp @@ -20,6 +20,10 @@ #include "common/MuxLogger.h" #include "common/MuxException.h" #include "MuxPort.h" +#include + +std::chrono::time_point global_start; +std::chrono::time_point global_end; namespace link_manager { @@ -118,40 +122,61 @@ void ActiveActiveStateMachine::handleSwssSoCIpv4AddressUpdate(boost::asio::ip::a mMuxPortConfig.setBladeIpv4Address(address); try { - mLinkProberPtr = std::make_shared( + bool isHwProber = mMuxPortConfig.getLinkProberType() == common::MuxPortConfig::LinkProberType::Hardware; + MUXLOGINFO( boost::format("%s: detected Prober type HW(%b) ") % mMuxPortConfig.getPortName() % + isHwProber); + + if (isHwProber) + { + mLinkProberPtr = std::make_shared( + mMuxPortConfig, + getStrand().context(), + mLinkProberStateMachinePtr.get(), + mMuxPortPtr + ); + } else { + mLinkProberPtr = std::make_shared( mMuxPortConfig, getStrand().context(), mLinkProberStateMachinePtr.get() - ); + ); + } + mInitializeProberFnPtr = boost::bind( - &link_prober::LinkProber::initialize, mLinkProberPtr.get() + &link_prober::LinkProberBase::initialize, mLinkProberPtr.get() ); mStartProbingFnPtr = boost::bind( - &link_prober::LinkProber::startProbing, mLinkProberPtr.get() + &link_prober::LinkProberBase::startProbing, mLinkProberPtr.get() ); mUpdateEthernetFrameFnPtr = boost::bind( - &link_prober::LinkProber::updateEthernetFrame, mLinkProberPtr.get() + &link_prober::LinkProberBase::updateEthernetFrame, mLinkProberPtr.get() ); mProbePeerTorFnPtr = boost::bind( - &link_prober::LinkProber::probePeerTor, mLinkProberPtr.get() + &link_prober::LinkProberBase::probePeerTor, mLinkProberPtr.get() ); mSuspendTxFnPtr = boost::bind( - &link_prober::LinkProber::suspendTxProbes, mLinkProberPtr.get(), boost::placeholders::_1 + &link_prober::LinkProberBase::suspendTxProbes, mLinkProberPtr.get(), boost::placeholders::_1 ); mResumeTxFnPtr = boost::bind( - &link_prober::LinkProber::resumeTxProbes, mLinkProberPtr.get() + &link_prober::LinkProberBase::resumeTxProbes, mLinkProberPtr.get() ); mShutdownTxFnPtr = boost::bind( - &link_prober::LinkProber::shutdownTxProbes, mLinkProberPtr.get() + &link_prober::LinkProberBase::shutdownTxProbes, mLinkProberPtr.get() ); mRestartTxFnPtr = boost::bind( - &link_prober::LinkProber::restartTxProbes, mLinkProberPtr.get() + &link_prober::LinkProberBase::restartTxProbes, mLinkProberPtr.get() ); mResetIcmpPacketCountsFnPtr = boost::bind( - &link_prober::LinkProber::resetIcmpPacketCounts, mLinkProberPtr.get() + &link_prober::LinkProberBase::resetIcmpPacketCounts, mLinkProberPtr.get() ); mSendPeerProbeCommandFnPtr = boost::bind( - &link_prober::LinkProber::sendPeerProbeCommand, mLinkProberPtr.get() + &link_prober::LinkProberBase::sendPeerProbeCommand, mLinkProberPtr.get() + ); + + mHandleStateDbUpdateFnPtr = boost::bind( + &link_prober::LinkProberBase::handleStateDbStateUpdate, mLinkProberPtr.get(), + boost::placeholders::_1, + boost::placeholders::_2 ); setComponentInitState(LinkProberComponent); @@ -219,7 +244,7 @@ void ActiveActiveStateMachine::handleMuxStateNotification(mux_state::MuxState::L // void ActiveActiveStateMachine::handleSwssLinkStateNotification(const link_state::LinkState::Label label) { - MUXLOGINFO(boost::format("%s: state db link state: %s") % mMuxPortConfig.getPortName() % mLinkStateName[label]); + MUXLOGWARNING(boost::format("%s: state db link state: %s") % mMuxPortConfig.getPortName() % mLinkStateName[label]); if (mComponentInitState.all()) { if (label == link_state::LinkState::Label::Up) { @@ -324,7 +349,7 @@ void ActiveActiveStateMachine::handleProbeMuxFailure() auto expiryTime = mWaitTimer.expires_at(); auto now = boost::posix_time::microsec_clock::universal_time(); - MUXLOGINFO(boost::format("%s: lost gRPC connection, expiry time: %s, now: %s") + MUXLOGWARNING(boost::format("%s: lost gRPC connection, expiry time: %s, now: %s") % mMuxPortConfig.getPortName() % boost::posix_time::to_simple_string(expiryTime) % boost::posix_time::to_simple_string(now) @@ -471,7 +496,6 @@ void ActiveActiveStateMachine::handleSuspendTimerExpiry() void ActiveActiveStateMachine::handleMuxProbeRequestEvent() { MUXLOGDEBUG(mMuxPortConfig.getPortName()); - // if there is no interaction with mux, probe mux if (!mWaitMux) { probeMuxState(); @@ -961,6 +985,12 @@ void ActiveActiveStateMachine::enterLinkProberState( link_prober::LinkProberState::Label label ) { + MUXLOGWARNING( + boost::format("%s: Entering MUX state to '%s'") % + mMuxPortConfig.getPortName() % + label + ); + mLinkProberStateMachinePtr->enterState(label); ps(nextState) = label; } @@ -1056,6 +1086,17 @@ void ActiveActiveStateMachine::switchMuxState( } } +// +// ---> updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, const std::string session_type)); +// +// updates link state to link prober +// +void ActiveActiveStateMachine::updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, + const std::string session_type) +{ + mHandleStateDbUpdateFnPtr(linkFailureDetectionState, session_type); +} + // // ---> switchPeerMuxState(CompositeState &nextState, mux_state::MuxState::Label label, bool forceSwitch); // @@ -1083,6 +1124,7 @@ void ActiveActiveStateMachine::switchPeerMuxState(mux_state::MuxState::Label lab // void ActiveActiveStateMachine::probeMuxState() { + MUXLOGDEBUG(boost::format("%s: probing mux state and start timer ") % mMuxPortConfig.getPortName()); mMuxStateMachine.setWaitStateCause(mux_state::WaitState::WaitStateCause::DriverUpdate); mMuxPortPtr->probeMuxState(); startMuxWaitTimer(); @@ -1103,6 +1145,7 @@ void ActiveActiveStateMachine::updateMuxLinkmgrState() (mLastMuxNotificationType == LastMuxNotificationType::MuxNotificationFromProbe && mLastMuxProbeNotification == mux_state::MuxState::Label::Unknown)) && (mMuxPortConfig.ifEnableDefaultRouteFeature() == false || mDefaultRouteState == DefaultRoute::OK)) { + MUXLOGDEBUG(boost::format("%s: setting Link Manager state Healthy") % mMuxPortConfig.getPortName()); label = Label::Healthy; } setLabel(label); @@ -1171,6 +1214,7 @@ void ActiveActiveStateMachine::initPeerLinkProberState() // void ActiveActiveStateMachine::startMuxProbeTimer() { + MUXLOGDEBUG(boost::format("%s: Start Mux Probe Timer") % mMuxPortConfig.getPortName()); probeMuxState(); mDeadlineTimer.expires_from_now(boost::posix_time::milliseconds( mMuxProbeBackoffFactor * mMuxPortConfig.getNegativeStateChangeRetryCount() * mMuxPortConfig.getTimeoutIpv4_msec() @@ -1190,8 +1234,7 @@ void ActiveActiveStateMachine::startMuxProbeTimer() // void ActiveActiveStateMachine::handleMuxProbeTimeout(boost::system::error_code errorCode) { - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - + MUXLOGDEBUG(boost::format("%s: Mux Probe Timeout") % mMuxPortConfig.getPortName()); stopWaitMux(); if (errorCode == boost::system::errc::success) { if (ms(mCompositeState) == mux_state::MuxState::Label::Unknown || @@ -1213,6 +1256,7 @@ void ActiveActiveStateMachine::handleMuxProbeTimeout(boost::system::error_code e // void ActiveActiveStateMachine::startMuxWaitTimer(uint32_t factor) { + MUXLOGDEBUG(boost::format("%s: Mux Wait Timer Started") % mMuxPortConfig.getPortName()); mWaitTimer.expires_from_now(boost::posix_time::milliseconds( factor * mMuxPortConfig.getNegativeStateChangeRetryCount() * mMuxPortConfig.getTimeoutIpv4_msec() )); @@ -1333,7 +1377,7 @@ void ActiveActiveStateMachine::handleDefaultRouteStateNotification(const Default // void ActiveActiveStateMachine::shutdownOrRestartLinkProberOnDefaultRoute() { - MUXLOGDEBUG(mMuxPortConfig.getPortName()); + MUXLOGINFO(mMuxPortConfig.getPortName()); if (mComponentInitState.all()) { if ((mMuxPortConfig.getMode() == common::MuxPortConfig::Mode::Auto || diff --git a/src/link_manager/LinkManagerStateMachineActiveActive.h b/src/link_manager/LinkManagerStateMachineActiveActive.h index 02e8db1..595716e 100644 --- a/src/link_manager/LinkManagerStateMachineActiveActive.h +++ b/src/link_manager/LinkManagerStateMachineActiveActive.h @@ -26,7 +26,6 @@ #include "common/AsyncEvent.h" #include "link_manager/LinkManagerStateMachineBase.h" -#include "link_prober/LinkProber.h" #include "link_prober/LinkProberState.h" #include "link_state/LinkState.h" #include "link_state/LinkStateMachine.h" @@ -209,6 +208,15 @@ class ActiveActiveStateMachine : public LinkManagerStateMachineBase, * @return none */ void handleResetLinkProberPckLossCount() override; + + /** + * @method updateLinkFailureDetectionState + * + * @brief updates link state to link prober + * + * @return none + */ + void updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, const std::string session_type) override; public: // link prober event handlers /** @@ -691,6 +699,18 @@ class ActiveActiveStateMachine : public LinkManagerStateMachineBase, */ void setSendPeerProbeCommandFnPtr(boost::function sendPeerProbeCommandFnPtr) { mSendPeerProbeCommandFnPtr = sendPeerProbeCommandFnPtr; } + /** + * @method set + * + * @brief set mHandleStateDbUpdateFnPtr. This method is used for testing + * + * @param setIcmpEchoSessionStateUpdate (in) pointer to new sendPeerProbeCommandFnPtr + * + * @return none + */ + void setIcmpEchoSessionStateUpdate(boost::function handleStateDbStateUpdate) { mHandleStateDbUpdateFnPtr = handleStateDbStateUpdate; } + private: enum class LastMuxNotificationType { MuxNotificationNotReceived, @@ -726,6 +746,8 @@ class ActiveActiveStateMachine : public LinkManagerStateMachineBase, boost::function mRestartTxFnPtr; boost::function mResetIcmpPacketCountsFnPtr; boost::function mSendPeerProbeCommandFnPtr; + boost::function mHandleStateDbUpdateFnPtr; bool mContinuousLinkProberUnknownEvent = false; }; diff --git a/src/link_manager/LinkManagerStateMachineActiveStandby.cpp b/src/link_manager/LinkManagerStateMachineActiveStandby.cpp index 4148435..ed89a03 100644 --- a/src/link_manager/LinkManagerStateMachineActiveStandby.cpp +++ b/src/link_manager/LinkManagerStateMachineActiveStandby.cpp @@ -23,6 +23,7 @@ #include +#include "link_prober/LinkProberSw.h" #include "link_manager/LinkManagerStateMachineActiveStandby.h" #include "common/MuxLogger.h" #include "common/MuxException.h" @@ -347,49 +348,49 @@ void ActiveStandbyStateMachine::handleSwssBladeIpv4AddressUpdate(boost::asio::ip mMuxPortConfig.setBladeIpv4Address(address); try { - mLinkProberPtr = std::make_shared ( + mLinkProberPtr = std::make_shared ( mMuxPortConfig, getStrand().context(), mLinkProberStateMachinePtr.get() ); mInitializeProberFnPtr = boost::bind( - &link_prober::LinkProber::initialize, mLinkProberPtr.get() + &link_prober::LinkProberBase::initialize, mLinkProberPtr.get() ); mStartProbingFnPtr = boost::bind( - &link_prober::LinkProber::startProbing, mLinkProberPtr.get() + &link_prober::LinkProberBase::startProbing, mLinkProberPtr.get() ); mUpdateEthernetFrameFnPtr = boost::bind( - &link_prober::LinkProber::updateEthernetFrame, mLinkProberPtr.get() + &link_prober::LinkProberBase::updateEthernetFrame, mLinkProberPtr.get() ); mProbePeerTorFnPtr = boost::bind( - &link_prober::LinkProber::probePeerTor, mLinkProberPtr.get() + &link_prober::LinkProberBase::probePeerTor, mLinkProberPtr.get() ); mDetectLinkFnPtr = boost::bind( - &link_prober::LinkProber::detectLink, mLinkProberPtr.get() + &link_prober::LinkProberBase::detectLink, mLinkProberPtr.get() ); mSuspendTxFnPtr = boost::bind( - &link_prober::LinkProber::suspendTxProbes, mLinkProberPtr.get(), boost::placeholders::_1 + &link_prober::LinkProberBase::suspendTxProbes, mLinkProberPtr.get(), boost::placeholders::_1 ); mResumeTxFnPtr = boost::bind( - &link_prober::LinkProber::resumeTxProbes, mLinkProberPtr.get() + &link_prober::LinkProberBase::resumeTxProbes, mLinkProberPtr.get() ); mSendPeerSwitchCommandFnPtr = boost::bind( - &link_prober::LinkProber::sendPeerSwitchCommand, mLinkProberPtr.get() + &link_prober::LinkProberBase::sendPeerSwitchCommand, mLinkProberPtr.get() ); mResetIcmpPacketCountsFnPtr = boost::bind( - &link_prober::LinkProber::resetIcmpPacketCounts, mLinkProberPtr.get() + &link_prober::LinkProberBase::resetIcmpPacketCounts, mLinkProberPtr.get() ); mShutdownTxFnPtr = boost::bind( - &link_prober::LinkProber::shutdownTxProbes, mLinkProberPtr.get() + &link_prober::LinkProberBase::shutdownTxProbes, mLinkProberPtr.get() ); mRestartTxFnPtr = boost::bind( - &link_prober::LinkProber::restartTxProbes, mLinkProberPtr.get() + &link_prober::LinkProberBase::restartTxProbes, mLinkProberPtr.get() ); mDecreaseIntervalFnPtr = boost::bind( - &link_prober::LinkProber::decreaseProbeIntervalAfterSwitch, mLinkProberPtr.get(), boost::placeholders::_1 + &link_prober::LinkProberBase::decreaseProbeIntervalAfterSwitch, mLinkProberPtr.get(), boost::placeholders::_1 ); mRevertIntervalFnPtr = boost::bind( - &link_prober::LinkProber::revertProbeIntervalAfterSwitchComplete, mLinkProberPtr.get() + &link_prober::LinkProberBase::revertProbeIntervalAfterSwitchComplete, mLinkProberPtr.get() ); mComponentInitState.set(LinkProberComponent); diff --git a/src/link_manager/LinkManagerStateMachineActiveStandby.h b/src/link_manager/LinkManagerStateMachineActiveStandby.h index 29ac334..8a3d244 100644 --- a/src/link_manager/LinkManagerStateMachineActiveStandby.h +++ b/src/link_manager/LinkManagerStateMachineActiveStandby.h @@ -32,7 +32,6 @@ #include #include "link_manager/LinkManagerStateMachineBase.h" -#include "link_prober/LinkProber.h" #include "link_prober/LinkProberState.h" #include "link_state/LinkState.h" #include "link_state/LinkStateMachine.h" diff --git a/src/link_manager/LinkManagerStateMachineBase.cpp b/src/link_manager/LinkManagerStateMachineBase.cpp index b30c2e9..6e037ef 100644 --- a/src/link_manager/LinkManagerStateMachineBase.cpp +++ b/src/link_manager/LinkManagerStateMachineBase.cpp @@ -45,8 +45,8 @@ LinkManagerStateMachineBase::LinkManagerStateMachineBase( common::MuxPortConfig &muxPortConfig, CompositeState initialCompositeState) : StateMachine(strand, muxPortConfig), - mCompositeState(initialCompositeState), mMuxPortPtr(muxPortPtr), + mCompositeState(initialCompositeState), mMuxStateMachine(this, strand, muxPortConfig, ms(mCompositeState)), mLinkStateMachine(this, strand, muxPortConfig, ls(mCompositeState)) { diff --git a/src/link_manager/LinkManagerStateMachineBase.h b/src/link_manager/LinkManagerStateMachineBase.h index 51fdd3c..6fae5ce 100644 --- a/src/link_manager/LinkManagerStateMachineBase.h +++ b/src/link_manager/LinkManagerStateMachineBase.h @@ -25,7 +25,12 @@ #include #include -#include "link_prober/LinkProber.h" +#include "link_prober/LinkProberBase.h" +#include "link_prober/LinkProberSw.h" +#include "link_prober/LinkProberHw.h" +#include "link_prober/LinkProberStateMachineBase.h" +#include "link_prober/LinkProberStateMachineActiveStandby.h" +#include "link_prober/LinkProberStateMachineActiveActive.h" #include "link_prober/LinkProberState.h" #include "link_state/LinkState.h" #include "link_state/LinkStateMachine.h" @@ -514,6 +519,17 @@ class LinkManagerStateMachineBase : public common::StateMachine { */ virtual void handleResetSuspendTimer(); + /** + * @method updateLinkFailureDetectionState + * + * @brief pdates link state to link prober + * + * @return none + */ + virtual void updateLinkFailureDetectionState(const std::string &linkFailureDetectionState, + const std::string session_type) { + } + public: /** *@method getLinkProberStateMachinePtr @@ -551,6 +567,9 @@ class LinkManagerStateMachineBase : public common::StateMachine { */ DefaultRoute getDefaultRouteState() {return mDefaultRouteState;}; +protected: + mux::MuxPort *mMuxPortPtr; + private: /** *@enum anonymous @@ -642,9 +661,8 @@ class LinkManagerStateMachineBase : public common::StateMachine { [link_state::LinkState::Label::Count]; LinkManagerStateMachineBase::CompositeState mCompositeState; - mux::MuxPort *mMuxPortPtr; std::shared_ptr mLinkProberStateMachinePtr; - std::shared_ptr mLinkProberPtr = nullptr; + std::shared_ptr mLinkProberPtr = nullptr; mux_state::MuxStateMachine mMuxStateMachine; link_state::LinkStateMachine mLinkStateMachine; diff --git a/src/link_prober/ActiveState.cpp b/src/link_prober/ActiveState.cpp index 97a265b..e7b48af 100644 --- a/src/link_prober/ActiveState.cpp +++ b/src/link_prober/ActiveState.cpp @@ -97,16 +97,95 @@ LinkProberState* ActiveState::handleEvent(IcmpUnknownEvent &event) LinkProberState *nextState; mPeerEventCount = 0; - if (++mUnknownEventCount >= getMuxPortConfig().getNegativeStateChangeRetryCount()) { + if (++mUnknownEventCount >= getMuxPortConfig().getNegativeStateChangeRetryCount()) + { nextState = dynamic_cast (stateMachine->getUnknownState()); - } - else { + } else { nextState = dynamic_cast (stateMachine->getActiveState()); } return nextState; } +LinkProberState* ActiveState::handleEvent(IcmpWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getWaitState()); + return nextState; + +} + +// +// ---> handleEvent(IcmpHwPeerEvent &event); +// +// handle IcmpHwPeerEvent from LinkProber +// +LinkProberState* ActiveState::handleEvent(IcmpHwPeerEvent &event) +{ + // applicable for active-standby state machine + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mUnknownEventCount = 0; + nextState = dynamic_cast (stateMachine->getStandbyState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwSelfEvent &event); +// +// handle IcmpHwSelfEvent from LinkProber +// +LinkProberState* ActiveState::handleEvent(IcmpHwSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getActiveState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwUnknownEvent &event); +// +// handle IcmpHwUnknownEvent from LinkProber +// +LinkProberState* ActiveState::handleEvent(IcmpHwUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + // detection timer in hardware prober takes into account retry count + // and will move to Unknown state directly + mPeerEventCount = 0; + nextState = dynamic_cast (stateMachine->getUnknownState()); + return nextState; +} + +LinkProberState* ActiveState::handleEvent(IcmpHwWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getWaitState()); + return nextState; + +} + // // ---> resetState(); // diff --git a/src/link_prober/ActiveState.h b/src/link_prober/ActiveState.h index 18fd359..c9672a1 100644 --- a/src/link_prober/ActiveState.h +++ b/src/link_prober/ActiveState.h @@ -107,6 +107,62 @@ class ActiveState: public LinkProberState */ virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpWaitEvent from LinkProber + * + *@param event (in) reference to IcmpWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpWaitEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwSelfEvent from LinkProber + * + *@param event (in) reference to IcmpHwSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwUnknownEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwWaitEvent from LinkProber + * + *@param event (in) reference to IcmpHwWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwWaitEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/IcmpPayload.cpp b/src/link_prober/IcmpPayload.cpp index 2d76acc..7475d0b 100644 --- a/src/link_prober/IcmpPayload.cpp +++ b/src/link_prober/IcmpPayload.cpp @@ -34,8 +34,8 @@ namespace link_prober // // static members // -boost::uuids::uuid IcmpPayload::mGuid; -uint32_t IcmpPayload::mCookie = 0x47656d69; +uint32_t IcmpPayload::mHardwareCookie = 0x58767e7a; +uint32_t IcmpPayload::mSoftwareCookie = 0x47656d69; uint32_t IcmpPayload::mVersion = 0; // @@ -44,24 +44,11 @@ uint32_t IcmpPayload::mVersion = 0; // class constructor // IcmpPayload::IcmpPayload() : - cookie(htonl(mCookie)), + cookie(htonl(mSoftwareCookie)), version(htonl(mVersion)), seq(0) { - memcpy(uuid, mGuid.data, sizeof(uuid)); -} - -// -// ---> generateGuid() -// -// generate GUID for the current instance of linkmgrd -// -void IcmpPayload::generateGuid() -{ - boost::uuids::random_generator gen; - mGuid = gen(); - - MUXLOGWARNING(boost::format("Link Prober generated GUID: {%s}") % boost::uuids::to_string(mGuid)); + } } /* namespace link_prober */ diff --git a/src/link_prober/IcmpPayload.h b/src/link_prober/IcmpPayload.h index 99ab649..5d4f53c 100644 --- a/src/link_prober/IcmpPayload.h +++ b/src/link_prober/IcmpPayload.h @@ -31,7 +31,7 @@ #include #include #include - +#include #include #include @@ -40,6 +40,12 @@ __BEGIN_DECLS +#include +inline uint64_t ntohll(uint64_t x) +{ + return be64toh(x); +} + namespace link_prober { /** @@ -98,40 +104,22 @@ struct IcmpPayload { IcmpPayload(); /** - *@method generateGuid - * - *@brief generate GUID for the current instance of linkmgrd - * - *@return none - */ - static void generateGuid(); - - /** - *@method getGuidData - * - *@brief getter for GUID data - * - *@return pointer to current GUID data - */ - static uint8_t* getGuidData() {return reinterpret_cast (mGuid.data);}; - - /** - *@method getGuid + *@method getCookie * - *@brief getter for GUID object + *@brief getter for ICMP cookie by software probing * - *@return reference to current GUID object + *@return ICMP cookie */ - static boost::uuids::uuid& getGuid() {return mGuid;}; + static uint32_t getSoftwareCookie() {return mSoftwareCookie;}; /** *@method getCookie * - *@brief getter for ICMP cookie + *@brief getter for ICMP cookie by hardware probing * - *@return ICMP coolie + *@return ICMP cookie */ - static uint32_t getCookie() {return mCookie;}; + static uint32_t getHardwareCookie() {return mHardwareCookie;}; /** *@method getVersion @@ -143,9 +131,10 @@ struct IcmpPayload { static uint32_t getVersion() {return mVersion;}; private: - static uint32_t mCookie; + static uint32_t mHardwareCookie; + static uint32_t mSoftwareCookie; static uint32_t mVersion; - static boost::uuids::uuid mGuid; + } __attribute__((packed)); static_assert(sizeof(IcmpPayload) % 2 == 0, diff --git a/src/link_prober/LinkProber.cpp b/src/link_prober/LinkProber.cpp deleted file mode 100644 index 6d210dc..0000000 --- a/src/link_prober/LinkProber.cpp +++ /dev/null @@ -1,1015 +0,0 @@ -/* - * Copyright 2021 (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * LinkProber.cpp - * - * Created on: Oct 4, 2020 - * Author: tamer - */ - -#include -#include -#include -#include - -#include - -#include "common/MuxLogger.h" -#include "LinkProber.h" -#include "common/MuxException.h" - -namespace link_prober -{ - -// -// Berkeley Packet Filter program that captures incoming ICMP traffic -// -SockFilter LinkProber::mIcmpFilter[] = { - [0] = {.code = 0x28, .jt = 0, .jf = 0, .k = 0x0000000c}, - [1] = {.code = 0x15, .jt = 0, .jf = 10, .k = 0x00000800}, - [2] = {.code = 0x20, .jt = 0, .jf = 0, .k = 0x0000001a}, - [3] = {.code = 0x15, .jt = 0, .jf = 8, .k = 0x00000000}, - [4] = {.code = 0x30, .jt = 0, .jf = 0, .k = 0x00000017}, - [5] = {.code = 0x15, .jt = 0, .jf = 6, .k = 0x00000001}, - [6] = {.code = 0x28, .jt = 0, .jf = 0, .k = 0x00000014}, - [7] = {.code = 0x45, .jt = 4, .jf = 0, .k = 0x00001fff}, - [8] = {.code = 0xb1, .jt = 0, .jf = 0, .k = 0x0000000e}, - [9] = {.code = 0x50, .jt = 0, .jf = 0, .k = 0x0000000e}, - [10] = {.code = 0x15, .jt = 0, .jf = 1, .k = 0x00000000}, - [11] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00040000}, - [12] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00000000}, -}; - -// -// ---> LinkProber( -// common::MuxPortConfig &muxPortConfig, -// boost::asio::io_service &ioService, -// LinkProberStateMachineBase &linkProberStateMachine -// ); -// -// class constructor -// -LinkProber::LinkProber( - common::MuxPortConfig &muxPortConfig, - boost::asio::io_service &ioService, - LinkProberStateMachineBase *linkProberStateMachinePtr -) : - mMuxPortConfig(muxPortConfig), - mIoService(ioService), - mLinkProberStateMachinePtr(linkProberStateMachinePtr), - mStrand(mIoService), - mDeadlineTimer(mIoService), - mSuspendTimer(mIoService), - mSwitchoverTimer(mIoService), - mStream(mIoService) -{ - try { - mSockFilterPtr = std::shared_ptr ( - new SockFilter[sizeof(mIcmpFilter) / sizeof(*mIcmpFilter)], - std::default_delete() - ); - memcpy(mSockFilterPtr.get(), mIcmpFilter, sizeof(mIcmpFilter)); - - mSockFilterProg.len = sizeof(mIcmpFilter) / sizeof(*mIcmpFilter); - mSockFilterProg.filter = mSockFilterPtr.get(); - } - catch (const std::bad_alloc& ex) { - std::ostringstream errMsg; - errMsg << "Failed allocate memory. Exception details: " << ex.what(); - - throw MUX_ERROR(BadAlloc, errMsg.str()); - } - - switch (mMuxPortConfig.getPortCableType()) { - case common::MuxPortConfig::PortCableType::ActiveActive: { - mReportHeartbeatReplyReceivedFuncPtr = boost::bind( - &LinkProber::reportHeartbeatReplyReceivedActiveActive, - this, - boost::placeholders::_1 - ); - mReportHeartbeatReplyNotRecivedFuncPtr = boost::bind( - &LinkProber::reportHeartbeatReplyNotReceivedActiveActive, - this - ); - break; - } - case common::MuxPortConfig::PortCableType::ActiveStandby: { - mReportHeartbeatReplyReceivedFuncPtr = boost::bind( - &LinkProber::reportHeartbeatReplyReceivedActiveStandby, - this, - boost::placeholders::_1 - ); - mReportHeartbeatReplyNotRecivedFuncPtr = boost::bind( - &LinkProber::reportHeartbeatReplyNotReceivedActiveStandby, - this - ); - break; - } - default: { - break; - } - } -} - -// -// ---> initialize(); -// -// initialize link prober sockets and builds ICMP packet -// -void LinkProber::initialize() -{ - SockAddrLinkLayer addr = {0}; - addr.sll_ifindex = if_nametoindex(mMuxPortConfig.getPortName().c_str()); - addr.sll_family = AF_PACKET; - addr.sll_protocol = htons(ETH_P_ALL); - - mSocket = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMP); - if (mSocket < 0) { - std::ostringstream errMsg; - errMsg << "Failed to open socket with '" << strerror(errno) << "'" - << std::endl; - throw MUX_ERROR(SocketError, errMsg.str()); - } - - if (bind(mSocket, (struct sockaddr *) &addr, sizeof(addr))) { - std::ostringstream errMsg; - errMsg << "Failed to bind to interface '" << mMuxPortConfig.getPortName() << "' with '" - << strerror(errno) << "'" << std::endl; - throw MUX_ERROR(SocketError, errMsg.str()); - } - - mSockFilterPtr.get()[3].k = mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint(); - if (setsockopt(mSocket, SOL_SOCKET, SO_ATTACH_FILTER, &mSockFilterProg, sizeof(mSockFilterProg)) != 0) { - std::ostringstream errMsg; - errMsg << "Failed to attach filter with '" << strerror(errno) << "'" - << std::endl; - throw MUX_ERROR(SocketError, errMsg.str()); - } - - mStream.assign(mSocket); - initializeSendBuffer(); - - startInitRecv(); -} - -// -// ---> startProbing(); -// -// start sending ICMP ECHOREQUEST packets -// -void LinkProber::startProbing() -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - - mStream.cancel(); - sendHeartbeat(); - startRecv(); - startTimer(); -} - -// -// ---> suspendTxProbes(uint32_t suspendTime_msec); -// -// suspend sending ICMP ECHOREQUEST packets -// -void LinkProber::suspendTxProbes(uint32_t suspendTime_msec) -{ - MUXLOGWARNING(boost::format("%s: suspend ICMP heartbeat probing %dms") % mMuxPortConfig.getPortName() % suspendTime_msec); - - // NOTE: the timer reset also cancels any pending async ops with ec as boost::asio::error::operation_aborted - mSuspendTimer.expires_from_now(boost::posix_time::milliseconds(suspendTime_msec)); - mSuspendTimer.async_wait(mStrand.wrap(boost::bind( - &LinkProber::handleSuspendTimeout, - this, - boost::asio::placeholders::error - ))); - - mSuspendTx = true; - mCancelSuspend = false; -} - -// -// ---> resumeTxProbes(); -// -// resume sending ICMP ECHOREQUEST packets -// -void LinkProber::resumeTxProbes() -{ - MUXLOGWARNING(boost::format("%s: resume ICMP heartbeat probing") % mMuxPortConfig.getPortName()); - - mSuspendTimer.cancel(); - mCancelSuspend = true; -} - -// -// ---> updateEthernetFrame(); -// -// update Ethernet frame of Tx Buffer -// -void LinkProber::updateEthernetFrame() -{ - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind(&LinkProber::handleUpdateEthernetFrame, this))); -} - -// -// ---> probePeerTor(); -// -// send an early HB to peer ToR -// -void LinkProber::probePeerTor() -{ - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind(&LinkProber::sendHeartbeat, this, false))); -} - -// -// ---> detectLink(); -// -// send HBs to detect the link status -// -void LinkProber::detectLink() -{ - boost::asio::io_service &ioService = mStrand.context(); - for (uint32_t i = 0; i < mMuxPortConfig.getPositiveStateChangeRetryCount(); ++i) - { - ioService.post(mStrand.wrap(boost::bind(&LinkProber::sendHeartbeat, this, true))); - } -} - -// -// ---> sendPeerSwitchCommand(); -// -// send send peer switch command -// -void LinkProber::sendPeerSwitchCommand() -{ - boost::asio::io_service &ioService = mStrand.context(); - ioService.post(mStrand.wrap(boost::bind(&LinkProber::handleSendSwitchCommand, this))); -} - -// -// ---> sendPeerProbeCommand(); -// -// send peer probe command -// -void LinkProber::sendPeerProbeCommand() -{ - boost::asio::post(mStrand, boost::bind(&LinkProber::handleSendProbeCommand, this)); -} - -// -// ---> handleUpdateEthernetFrame(); -// -// update Ethernet frame of Tx Buffer -// -void LinkProber::handleUpdateEthernetFrame() -{ - initializeSendBuffer(); -} - -// -// ---> handleSendSwitchCommand(); -// -// send switch command to peer ToR -// -void LinkProber::handleSendSwitchCommand() -{ - initTxBufferTlvSendSwitch(); - - sendHeartbeat(); - - initTxBufferTlvSentinel(); - - // inform the composite state machine about command send completion - boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); - boost::asio::io_service &ioService = strand.context(); - ioService.post(strand.wrap(boost::bind( - static_cast - (&LinkProberStateMachineBase::processEvent), - mLinkProberStateMachinePtr, - LinkProberStateMachineBase::getSwitchActiveCommandCompleteEvent() - ))); -} - -// -// ---> handleSendProbeCommand(); -// -// send probe command to peer ToR -// -void LinkProber::handleSendProbeCommand() -{ - initTxBufferTlvSendProbe(); - - sendHeartbeat(); - - initTxBufferTlvSentinel(); -} - -// -// ---> sendHeartbeat(bool forceSend) -// -// send ICMP ECHOREQUEST packet -// -void LinkProber::sendHeartbeat(bool forceSend) -{ - MUXLOGTRACE(mMuxPortConfig.getPortName()); - - updateIcmpSequenceNo(); - // check if suspend timer is running - if (forceSend || ((!mSuspendTx) && (!mShutdownTx))) { - boost::system::error_code errorCode; - mStream.write_some(boost::asio::buffer(mTxBuffer.data(), mTxPacketSize), errorCode); - - if (errorCode) { - MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Failed to send heartbeat! Error code: " + errorCode.message()); - } else { - MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Done sending data"); - } - } -} - -// -// ---> handleTlvCommandRecv(Tlv *tlvPtr,, bool isPeer); -// -// handle packet reception -// -void LinkProber::handleTlvCommandRecv( - Tlv *tlvPtr, - bool isPeer -) -{ - if (isPeer) { - boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); - - switch (static_cast(tlvPtr->command)) { - case Command::COMMAND_SWITCH_ACTIVE: { - boost::asio::post(mStrand, boost::bind( - static_cast(&LinkProberStateMachineBase::processEvent), - mLinkProberStateMachinePtr, - LinkProberStateMachineBase::getSwitchActiveRequestEvent() - )); - break; - } - case Command::COMMAND_MUX_PROBE: { - boost::asio::post(mStrand, boost::bind( - static_cast(&LinkProberStateMachineBase::processEvent), - mLinkProberStateMachinePtr, - LinkProberStateMachineBase::getMuxProbeRequestEvent() - )); - break; - } - default: { - break; - } - } - } -} - -// -// ---> handleRecv(const boost::system::error_code& errorCode, size_t bytesTransferred); -// -// handle packet reception -// -void LinkProber::handleRecv( - const boost::system::error_code& errorCode, - size_t bytesTransferred -) -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - - if (!errorCode) { - iphdr *ipHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header)); - icmphdr *icmpHeader = reinterpret_cast ( - mRxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) - ); - - MUXLOGTRACE(boost::format("%s: Got data from: %s, size: %d") % - mMuxPortConfig.getPortName() % - boost::asio::ip::address_v4(ntohl(ipHeader->saddr)).to_string() % - (bytesTransferred - sizeof(iphdr) - sizeof(ether_header)) - ); - - IcmpPayload *icmpPayload = reinterpret_cast ( - mRxBuffer.data() + mPacketHeaderSize - ); - - if (ntohl(icmpPayload->cookie) == IcmpPayload::getCookie() && - ntohl(icmpPayload->version) <= IcmpPayload::getVersion() && - ntohs(icmpHeader->un.echo.id) == mMuxPortConfig.getServerId()) { - MUXLOGTRACE(boost::format("%s: Valid ICMP Packet from %s") % - mMuxPortConfig.getPortName() % - mMuxPortConfig.getBladeIpv4Address().to_string() - ); - bool isMatch = (memcmp(icmpPayload->uuid, IcmpPayload::getGuidData(), sizeof(icmpPayload->uuid)) == 0); - HeartbeatType heartbeatType; - if (isMatch) { - MUXLOGTRACE(boost::format("%s: Matching Guid") % mMuxPortConfig.getPortName()); - // echo reply for an echo request generated by this/active ToR - mRxSelfSeqNo = mTxSeqNo; - heartbeatType = HeartbeatType::HEARTBEAT_SELF; - } else { - mRxPeerSeqNo = mTxSeqNo; - heartbeatType = HeartbeatType::HEARTBEAT_PEER; - } - mReportHeartbeatReplyReceivedFuncPtr(heartbeatType); - - size_t nextTlvOffset = mTlvStartOffset; - size_t nextTlvSize = 0; - bool stopProcessTlv = false; - while ((nextTlvSize = findNextTlv(nextTlvOffset, bytesTransferred)) > 0 && !stopProcessTlv) { - Tlv *nextTlvPtr = reinterpret_cast (mRxBuffer.data() + nextTlvOffset); - switch (nextTlvPtr->tlvhead.type) { - case TlvType::TLV_COMMAND: { - handleTlvCommandRecv(nextTlvPtr, !isMatch); - break; - } - case TlvType::TLV_SENTINEL: { - // sentinel TLV, stop processing - stopProcessTlv = true; - break; - } - default: { - // try to skip unknown TLV with valid length(>0) - stopProcessTlv = (nextTlvSize == sizeof(Tlv)); - break; - } - } - nextTlvOffset += nextTlvSize; - } - - if (nextTlvOffset < bytesTransferred) { - size_t BytesNotProcessed = bytesTransferred - nextTlvOffset; - MUXLOGTRACE(boost::format("%s: %d bytes in RxBuffer not processed") % - mMuxPortConfig.getPortName() % - BytesNotProcessed - ); - } - } else { - // Unknown ICMP packet, ignore. - MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Failed to receive heartbeat! Error code: " + errorCode.message()); - } - // start another receive to consume as much as possible of backlog packets if any - startRecv(); - } -} - -// -// ---> handleInitRecv(const boost::system::error_code& errorCode, size_t bytesTransferred); -// -// handle packet reception -// -void LinkProber::handleInitRecv( - const boost::system::error_code& errorCode, - size_t bytesTransferred -) -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - - if (errorCode != boost::asio::error::operation_aborted) { - ether_header *ethHeader = reinterpret_cast (mRxBuffer.data()); - std::array macAddress; - - memcpy(macAddress.data(), ethHeader->ether_shost, macAddress.size()); - - boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); - boost::asio::io_service &ioService = strand.context(); - ioService.post(strand.wrap(boost::bind( - &LinkProberStateMachineBase::handleMackAddressUpdate, - mLinkProberStateMachinePtr, - macAddress - ))); - } -} - -// -// ---> handleTimeout(boost::system::error_code ec); -// -// handle ICMP packet reception timeout -// -void LinkProber::handleTimeout(boost::system::error_code errorCode) -{ - MUXLOGTRACE(boost::format("%s: server: %d, mRxSelfSeqNo: %d, mRxPeerSeqNo: %d, mTxSeqNo: %d") % - mMuxPortConfig.getPortName() % - mMuxPortConfig.getServerId() % - mRxSelfSeqNo % - mRxPeerSeqNo % - mTxSeqNo - ); - - mStream.cancel(); - mReportHeartbeatReplyNotRecivedFuncPtr(); - - mIcmpPacketCount++; - if (mIcmpPacketCount % mMuxPortConfig.getLinkProberStatUpdateIntervalCount() == 0) { - boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); - boost::asio::io_service &ioService = strand.context(); - ioService.post(strand.wrap(boost::bind( - &LinkProberStateMachineBase::handlePckLossRatioUpdate, - mLinkProberStateMachinePtr, - mIcmpUnknownEventCount, - mIcmpPacketCount - ))); - } - - // start another cycle of send/recv - startProbing(); -} - -// -// ---> handleSuspendTimeout(boost::system::error_code errorCode); -// -// handle suspend timer timeout -// -void LinkProber::handleSuspendTimeout(boost::system::error_code errorCode) -{ - MUXLOGWARNING(boost::format("%s: suspend timeout, resume ICMP heartbeat probing") % mMuxPortConfig.getPortName()); - - mSuspendTx = false; - - if (errorCode == boost::system::errc::success || mCancelSuspend) { - // inform the composite state machine about Suspend timer expiry or cancel - boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); - boost::asio::io_service &ioService = strand.context(); - ioService.post(strand.wrap(boost::bind( - static_cast - (&LinkProberStateMachineBase::processEvent), - mLinkProberStateMachinePtr, - LinkProberStateMachineBase::getSuspendTimerExpiredEvent() - ))); - } - - mCancelSuspend = false; -} - -// -// ---> startRecv(); -// -// start ICMP ECHOREPLY reception -// -void LinkProber::startRecv() -{ - MUXLOGTRACE(mMuxPortConfig.getPortName()); - - - mStream.async_read_some( - boost::asio::buffer(mRxBuffer, MUX_MAX_ICMP_BUFFER_SIZE), - mStrand.wrap(boost::bind( - &LinkProber::handleRecv, - this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - )) - ); -} - -// -// ---> startInitRecv(); -// -// start ICMP ECHOREPLY reception -// -void LinkProber::startInitRecv() -{ - MUXLOGTRACE(mMuxPortConfig.getPortName()); - - mStream.async_read_some( - boost::asio::buffer(mRxBuffer, MUX_MAX_ICMP_BUFFER_SIZE), - mStrand.wrap(boost::bind( - &LinkProber::handleInitRecv, - this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred - )) - ); -} - -// -// ---> startTimer(); -// -// start ICMP ECHOREPLY timeout timer -// -void LinkProber::startTimer() -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - // time out these heartbeats - mDeadlineTimer.expires_from_now(boost::posix_time::milliseconds(getProbingInterval())); - mDeadlineTimer.async_wait(mStrand.wrap(boost::bind( - &LinkProber::handleTimeout, - this, - boost::asio::placeholders::error - ))); -} - -// -// ---> calculateChecksum(uint16_t *data, size_t size); -// -// calculate ICMP payload checksum -// -uint32_t LinkProber::calculateChecksum(uint16_t *data, size_t size) -{ - uint32_t sum = 0; - - while (size > 1) { - sum += ntohs(*data++); - size -= sizeof(uint16_t); - } - - if (size) { - sum += ntohs(static_cast ((*reinterpret_cast (data)))); - } - - return sum; -} - -// -// ---> addChecksumCarryover(uint16_t *checksum, uint32_t sum); -// -// add checksum carryover -// -void LinkProber::addChecksumCarryover(uint16_t *checksum, uint32_t sum) -{ - sum = (sum >> 16) + (sum & 0xFFFF); - sum += (sum >> 16); - *checksum = htons(~sum); -} - -// -// ---> computeChecksum(icmphdr *icmpHeader, size_t size); -// -// compute ICMP checksum -// -void LinkProber::computeChecksum(icmphdr *icmpHeader, size_t size) -{ - icmpHeader->checksum = 0; - mIcmpChecksum = calculateChecksum( - reinterpret_cast (icmpHeader), size - ); - addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); -} - -// -// ---> computeChecksum(iphdr *ipHeader, size_t size); -// -// compute IPv4 checksum -// -void LinkProber::computeChecksum(iphdr *ipHeader, size_t size) -{ - ipHeader->check = 0; - mIpChecksum = calculateChecksum( - reinterpret_cast (ipHeader), size - ); - addChecksumCarryover(&ipHeader->check, mIpChecksum); -} - -// -// ---> initializeSendBuffer(); -// -// initialize ICMP packet once -// -void LinkProber::initializeSendBuffer() -{ - ether_header *ethHeader = reinterpret_cast (mTxBuffer.data()); - memcpy(ethHeader->ether_dhost, mMuxPortConfig.getBladeMacAddress().data(), sizeof(ethHeader->ether_dhost)); - if (mMuxPortConfig.ifEnableUseTorMac()) { - memcpy(ethHeader->ether_shost, mMuxPortConfig.getTorMacAddress().data(), sizeof(ethHeader->ether_shost)); - } else { - memcpy(ethHeader->ether_shost, mMuxPortConfig.getVlanMacAddress().data(), sizeof(ethHeader->ether_shost)); - } - ethHeader->ether_type = htons(ETHERTYPE_IP); - - iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); - icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); - - new (mTxBuffer.data() + mPacketHeaderSize) IcmpPayload(); - resetTxBufferTlv(); - appendTlvSentinel(); - size_t totalPayloadSize = mTxPacketSize - mPacketHeaderSize; - - ipHeader->ihl = sizeof(iphdr) >> 2; - ipHeader->version = IPVERSION; - ipHeader->tos = 0xb8; - ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + totalPayloadSize); - ipHeader->id = static_cast (rand()); - ipHeader->frag_off = 0; - ipHeader->ttl = 64; - ipHeader->protocol = IPPROTO_ICMP; - ipHeader->check = 0; - ipHeader->saddr = htonl(mMuxPortConfig.getLoopbackIpv4Address().to_v4().to_uint()); - ipHeader->daddr = htonl(mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint()); - computeChecksum(ipHeader, ipHeader->ihl << 2); - - icmpHeader->type = ICMP_ECHO; - icmpHeader->code = 0; - icmpHeader->un.echo.id = htons(mMuxPortConfig.getServerId()); - icmpHeader->un.echo.sequence = htons(mTxSeqNo); - - computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); -} - -// -// ---> updateIcmpSequenceNo(); -// -// update ICMP packet checksum, used before sending new heartbeat -// -void LinkProber::updateIcmpSequenceNo() -{ - // update received sequence to avoid reporting invalid ICMP event when sequence number rolls over - mRxPeerSeqNo = mTxSeqNo; - mRxSelfSeqNo = mTxSeqNo; - - icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); - icmpHeader->un.echo.sequence = htons(++mTxSeqNo); - mIcmpChecksum += mTxSeqNo ? 1 : 0; - addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); -} - -// -// ---> findNextTlv -// -// Find next TLV to process in rxBuffer -// -size_t LinkProber::findNextTlv(size_t readOffset, size_t bytesTransferred) -{ - size_t tlvSize = 0; - if (readOffset + sizeof(TlvHead) <= bytesTransferred) { - Tlv *tlvPtr = reinterpret_cast (mRxBuffer.data() + readOffset); - tlvSize = (sizeof(TlvHead) + ntohs(tlvPtr->tlvhead.length)); - if (readOffset + tlvSize > bytesTransferred) { - tlvSize = 0; - } - } - return tlvSize; -} - -// -// ---> appendTlvCommand -// -// Append TlvCommand to the end of txBuffer -// -size_t LinkProber::appendTlvCommand(Command commandType) -{ - size_t tlvSize = sizeof(TlvHead) + sizeof(Command); - assert(mTxPacketSize + tlvSize <= MUX_MAX_ICMP_BUFFER_SIZE); - Tlv *tlvPtr = reinterpret_cast (mTxBuffer.data() + mTxPacketSize); - tlvPtr->tlvhead.type = TlvType::TLV_COMMAND; - tlvPtr->tlvhead.length = htons(sizeof(Command)); - tlvPtr->command = static_cast (commandType); - mTxPacketSize += tlvSize; - return tlvSize; -} - -// -// ---> appendTlvSentinel -// -// Append TlvSentinel to the end of txBuffer -// -size_t LinkProber::appendTlvSentinel() -{ - size_t tlvSize = sizeof(TlvHead); - assert(mTxPacketSize + tlvSize <= MUX_MAX_ICMP_BUFFER_SIZE); - Tlv *tlvPtr = reinterpret_cast (mTxBuffer.data() + mTxPacketSize); - tlvPtr->tlvhead.type = TlvType::TLV_SENTINEL; - tlvPtr->tlvhead.length = 0; - mTxPacketSize += tlvSize; - return tlvSize; -} - -// -// ---> appendTlvDummy -// -// Append a dummy TLV, test purpose only -// -size_t LinkProber::appendTlvDummy(size_t paddingSize, int seqNo) -{ - size_t tlvSize = sizeof(TlvHead) + paddingSize + sizeof(uint32_t); - assert(mTxPacketSize + tlvSize <= MUX_MAX_ICMP_BUFFER_SIZE); - Tlv *tlvPtr = reinterpret_cast (mTxBuffer.data() + mTxPacketSize); - tlvPtr->tlvhead.type = TlvType::TLV_DUMMY; - tlvPtr->tlvhead.length = htons(paddingSize + sizeof(uint32_t)); - memset(tlvPtr->data, 0, paddingSize); - *(reinterpret_cast (tlvPtr->data + paddingSize)) = htonl(seqNo); - mTxPacketSize += tlvSize; - return tlvSize; -} - -// -// ---> initTxBufferTlvSendSwitch -// -// Initialize TX buffer TLVs to send switch command to peer -// -void LinkProber::initTxBufferTlvSendSwitch() -{ - resetTxBufferTlv(); - appendTlvCommand(Command::COMMAND_SWITCH_ACTIVE); - appendTlvSentinel(); - - calculateTxPacketChecksum(); -} - -// -// ---> initTxBufferTlvSendProbe -// -// Initialize TX buffer TLVs to send probe command to peer -// -void LinkProber::initTxBufferTlvSendProbe() -{ - resetTxBufferTlv(); - appendTlvCommand(Command::COMMAND_MUX_PROBE); - appendTlvSentinel(); - - calculateTxPacketChecksum(); -} - -// -// ---> initTxBufferTlvSentinel -// -// Initialize TX buffer to have only TLV sentinel -// -void LinkProber::initTxBufferTlvSentinel() -{ - resetTxBufferTlv(); - appendTlvSentinel(); - - calculateTxPacketChecksum(); -} - -// -// ---> calculateChecksum -// -// Calculate TX packet checksums in both IP header and ICMP header -// -void LinkProber::calculateTxPacketChecksum() -{ - size_t totalPayloadSize = mTxPacketSize - mPacketHeaderSize; - iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); - icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); - computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); - ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + totalPayloadSize); - computeChecksum(ipHeader, ipHeader->ihl << 2); -} - -// -// ---> resetIcmpPacketCounts -// -// reset Icmp packet counts, post a pck loss ratio update immediately -// -void LinkProber::resetIcmpPacketCounts() -{ - mIcmpUnknownEventCount = 0; - mIcmpPacketCount = 0; - - boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); - boost::asio::io_service &ioService = strand.context(); - ioService.post(strand.wrap(boost::bind( - &LinkProberStateMachineBase::handlePckLossRatioUpdate, - mLinkProberStateMachinePtr, - mIcmpUnknownEventCount, - mIcmpPacketCount - ))); -} - -void LinkProber::shutdownTxProbes() -{ - MUXLOGWARNING(boost::format("%s: shutdown ICMP heartbeat probing") % mMuxPortConfig.getPortName()); - - mShutdownTx = true; -} - -void LinkProber::restartTxProbes() -{ - MUXLOGWARNING(boost::format("%s: restart ICMP heartbeat probing") % mMuxPortConfig.getPortName()); - - mShutdownTx = false; -} - -// -// ---> decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec); -// -// adjust link prober interval to 10 ms after switchover to better measure the switchover overhead. -// -void LinkProber::decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec) -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - - mSwitchoverTimer.expires_from_now(boost::posix_time::milliseconds(switchTime_msec)); - mSwitchoverTimer.async_wait(mStrand.wrap(boost::bind( - &LinkProber::handleSwitchoverTimeout, - this, - boost::asio::placeholders::error - ))); - - mDecreaseProbingInterval = true; -} - -// ---> revertProbeIntervalAfterSwitchComplete(); -// -// revert probe interval change after switchover is completed -// -void LinkProber::revertProbeIntervalAfterSwitchComplete() -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - - mSwitchoverTimer.cancel(); - mDecreaseProbingInterval = false; -} - -// -// ---> handleSwitchoverTimeout(boost::system::error_code errorCode) -// -// handle switchover time out -// -void LinkProber::handleSwitchoverTimeout(boost::system::error_code errorCode) -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - - mDecreaseProbingInterval = false; - if (errorCode == boost::system::errc::success) { - MUXLOGWARNING(boost::format("%s: link prober timeout on waiting for expected ICMP event after switchover is triggered ") % mMuxPortConfig.getPortName()); - } -} - -// -// ---> getProbingInterval -// -// get link prober interval -// -inline uint32_t LinkProber::getProbingInterval() -{ - MUXLOGDEBUG(mMuxPortConfig.getPortName()); - return mDecreaseProbingInterval? mMuxPortConfig.getDecreasedTimeoutIpv4_msec():mMuxPortConfig.getTimeoutIpv4_msec(); -} - -// -// ---> reportHeartbeatReplyReceivedActiveStandby(HeartbeatType heartbeatType) -// -// report heartbeat reply received to active-standby mode link prober state machine -// -void LinkProber::reportHeartbeatReplyReceivedActiveStandby(HeartbeatType heartbeatType) -{ - if (mTxSeqNo == mRxSelfSeqNo) { - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpSelfEvent()); - } else if (mTxSeqNo == mRxPeerSeqNo) { - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerEvent()); - } -} - -// -// ---> reportHeartbeatReplyNotReceivedActiveStandby -// -// report heartbeat reply received to active-active mode link prober state machine -// -void LinkProber::reportHeartbeatReplyNotReceivedActiveStandby() -{ - if (mTxSeqNo != mRxSelfSeqNo && mTxSeqNo != mRxPeerSeqNo) { - // post unknown event - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpUnknownEvent()); - mIcmpUnknownEventCount++; - } -} - -// -// ---> reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType) -// -// report heartbeat reply not received to active-standby mode link prober state machine -// -void LinkProber::reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType) -{ - if (heartbeatType == HeartbeatType::HEARTBEAT_SELF && mTxSeqNo == mRxSelfSeqNo) { - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpSelfEvent()); - } - if (heartbeatType == HeartbeatType::HEARTBEAT_PEER && mTxSeqNo == mRxPeerSeqNo) { - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerActiveEvent()); - } -} - -// -// ---> reportHeartbeatReplyNotReceivedActiveActive -// -// report heartbeat reply not received to active-active mode link prober state machine -// -void LinkProber::reportHeartbeatReplyNotReceivedActiveActive() -{ - if (mTxSeqNo != mRxSelfSeqNo) { - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpUnknownEvent()); - mIcmpUnknownEventCount++; - } - if (mTxSeqNo != mRxPeerSeqNo) { - mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerUnknownEvent()); - } -} - -} /* namespace link_prober */ diff --git a/src/link_prober/LinkProber.h b/src/link_prober/LinkProber.h deleted file mode 100644 index b65b7f8..0000000 --- a/src/link_prober/LinkProber.h +++ /dev/null @@ -1,643 +0,0 @@ -/* - * Copyright 2021 (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * LinkProber.h - * - * Created on: Oct 4, 2020 - * Author: tamer - */ - -#ifndef LINKPROBER_H_ -#define LINKPROBER_H_ - -#include -#include -#include -#include - -#include -#include -#include - -#include "IcmpPayload.h" -#include "LinkProberStateMachineActiveActive.h" -#include "LinkProberStateMachineActiveStandby.h" -#include "common/MuxPortConfig.h" - -namespace test { -class LinkProberTest; -class LinkProberMockTest; -} - -namespace link_prober -{ -using SockFilter = struct sock_filter; -using SockFilterProg = struct sock_fprog; -using SockAddrLinkLayer = struct sockaddr_ll; - -/** - *@enum HeartbeatType - * - *@brief Received heartbeat type - */ -enum class HeartbeatType: uint8_t { - HEARTBEAT_SELF, - HEARTBEAT_PEER, - - Count -}; - -/** - *@class LinkProber - * - *@brief probes the server sing ICMP ECHPREQUEST packet. The packet payload - * holds GUID that identifies this ToR. Reception of this ToR's GUID - * indicate that the link is in Active state. Reception of unknown - * GUID will indicate standby state. Lack of ICMP packets will signal - * that the link state is unknown. - */ -class LinkProber -{ -public: - /** - *@method LinkProber - * - *@brief class default constructor - */ - LinkProber() = delete; - - /** - *@method LinkProber - * - *@brief class copy constructor - * - *@param LinkProber (in) reference to LinkProber object to be copied - */ - LinkProber(const LinkProber &) = delete; - - /** - *@method LinkProber - * - *@brief class constructor - * - *@param muxPortConfig (in) reference to MuxPortConfig object - *@param ioService (in) reference to boost io_service object - *@param linkProberStateMachinePtr (in) reference to LinkProberStateMachineBase object - */ - LinkProber( - common::MuxPortConfig &muxPortConfig, - boost::asio::io_service &ioService, - LinkProberStateMachineBase *linkProberStateMachinePtr - ); - - /** - *@method ~LinkProber - * - *@brief class destructor - */ - virtual ~LinkProber() = default; - - /** - *@method initialize - * - *@brief initialize link prober sockets and builds ICMP packet - * - *@return none - */ - void initialize(); - - /** - *@method startProbing - * - *@brief start probing server/blade using ICMP ECHOREQUEST - * - *@return none - */ - void startProbing(); - - /** - *@method suspendTxProbes - * - *@brief suspend sending ICMP ECHOREQUEST packets - * - *@param suspendTime_msec suspension time in msec - * - *@return none - */ - void suspendTxProbes(uint32_t suspendTime_msec); - - /** - *@method resumeTxProbes - * - *@brief resume sending ICMP ECHOREQUEST packets - * - *@return none - */ - void resumeTxProbes(); - - /** - *@method updateEthernetFrame - * - *@brief update Ethernet frame of Tx Buffer - * - *@return none - */ - void updateEthernetFrame(); - - /** - *@method probePeerTor - * - *@brief send an early HB to peer ToR - * - *@return none - */ - void probePeerTor(); - - /** - *@method detectLink - * - *@brief detect link status - * - *@return none - */ - void detectLink(); - - /** - *@method sendPeerSwitchCommand - * - *@brief send switch command to peer ToR - * - *@return none - */ - void sendPeerSwitchCommand(); - - /** - *@method sendPeerProbeCommand - * - *@brief send probe command to peer ToR - * - *@return none - */ - void sendPeerProbeCommand(); - - /** - * @method resetIcmpPacketCounts() - * - * @brief reset Icmp packet counts, post a pck loss ratio update immediately - * - * @return none - */ - void resetIcmpPacketCounts(); - - /** - * @method shutdownTxProbes - * - * @brief stop sending ICMP ECHOREQUEST packets indefinitely. - * - * @return none - */ - void shutdownTxProbes(); - - /** - * @method restartTxProbes - * - * @brief restart sending ICMP ECHOREQUEST packets - * - * @return none - */ - void restartTxProbes(); - - /** - * @method decreaseProbeIntervalAfterSwitch - * - * @brief adjust link prober interval to 10 ms after switchover to better measure the switchover overhead. - * - * @param switchTime_msec (in) switchover is expected to complete within this time window - * @param expectingLinkProberEvent (in) depends on which state LinkManager is switching to, link prober expects self or peer events - * - * @return none - */ - void decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec); - - /** - * @method revertProbeIntervalAfterSwitchComplete - * - * @brief revert probe interval change after switchover is completed - * - * @return none - */ - void revertProbeIntervalAfterSwitchComplete(); - -private: - /** - *@method handleUpdateEthernetFrame - * - *@brief update Ethernet frame of Tx Buffer - * - *@return none - */ - void handleUpdateEthernetFrame(); - - /** - *@method handleSendSwitchCommand - * - *@brief send switch command to peer ToR - * - *@return none - */ - void handleSendSwitchCommand(); - - /** - *@method handleSendProbeCommand - * - *@brief send probe command to peer ToR - * - *@return none - */ - void handleSendProbeCommand(); - - /** - *@method sendHeartbeat - * - *@brief send ICMP ECHOREQUEST packet - * - *@param forceSend (in) Force sending heartbeat, used in link detect only - * - *@return none - */ - void sendHeartbeat(bool forceSend = false); - - /** - *@method handleTlvCommandRecv - * - *@brief handle TLV command - * - *@param tlvPtr (in) Tlv ptr points to the start of TlvCommand in mRxBuffer - *@param isPeer (in) True if the reply received is from the peer ToR - * - *@return none - */ - void handleTlvCommandRecv( - Tlv *tlvPtr, - bool isPeer - ); - - /** - *@method handleRecv - * - *@brief handle packet reception - * - *@param errorCode (in) socket error code - *@param bytesTransferred (in) number of bytes received - * - *@return none - */ - void handleRecv( - const boost::system::error_code &errorCode, - size_t bytesTransferred - ); - - /** - *@method handleInitRecv - * - *@brief handle packet reception - * - *@param errorCode (in) socket error code - *@param bytesTransferred (in) number of bytes received - * - *@return none - */ - void handleInitRecv( - const boost::system::error_code &errorCode, - size_t bytesTransferred - ); - - /** - *@method handleTimeout - * - *@brief handle ICMP packet reception timeout - * - *@param errorCode (in) socket error code - * - *@return none - */ - void handleTimeout(boost::system::error_code errorCode); - - /** - *@method handleSuspendTimeout - * - *@brief handle suspend timer timeout - * - *@param errorCode (in) socket error code - * - *@return none - */ - void handleSuspendTimeout(boost::system::error_code errorCode); - - /** - *@method startRecv - * - *@brief start ICMP ECHOREPLY reception - * - *@return none - */ - void startRecv(); - - /** - *@method startInitRecv - * - *@brief start ICMP ECHOREPLY reception - * - *@return none - */ - void startInitRecv(); - - /** - *@method startTimer - * - *@brief start ICMP ECHOREPLY timeout timer - * - *@return none - */ - void startTimer(); - - /** - *@method calculateChecksum - * - *@brief calculate ICMP payload checksum - * - *@param data (in) pointer to data buffer - *@param size (in) size of data buffer - * - *@return CRC checksum - */ - uint32_t calculateChecksum(uint16_t *data, size_t size); - - /** - *@method addChecksumCarryover - * - *@brief add checksum carryover - * - *@param checksum (out) pointer to checksum field - *@param sum (in) current sum of the buffer payload - * - *@return CRC checksum - */ - void addChecksumCarryover(uint16_t *checksum, uint32_t sum); - - /** - *@method computeChecksum - * - *@brief compute ICMP checksum - * - *@param icmpHeader (in, out) pointer ICMP header - *@param size (in) size of ICMP payload - * - *@return CRC checksum - */ - void computeChecksum(icmphdr *icmpHeader, size_t size); - - /** - *@method computeChecksum - * - *@brief compute IPv4 checksum - * - *@param ipHeader (in, out) pointer IPv4 header - *@param size (in) size of IPv4 header - * - *@return CRC checksum - */ - void computeChecksum(iphdr *ipHeader, size_t size); - - /** - *@method initializeSendBuffer - * - *@brief initialize ICMP packet once - * - *@return CRC checksum - */ - void initializeSendBuffer(); - - /** - *@method updateIcmpSequenceNo - * - *@brief update ICMP packet checksum, used before sending new heartbeat - * - *@return CRC checksum - */ - void updateIcmpSequenceNo(); - - /** - *@method getTxBuffer - * - *@brief getter for TxBuffer used for testing - * - *@return tx buffer - */ - std::array getTxBuffer() {return mTxBuffer;}; - - /** - *@method findNextTlv - * - *@brief Find next TLV in rxBuffer starting at readOffset - * - *@param readOffset (in) starting offset to read - *@param bytesTransferred (in) total bytes received in rxBuffer - * - *@return the next TLV size - */ - size_t findNextTlv(size_t readOffset, size_t bytesTransferred); - - void resetTxBufferTlv() {mTxPacketSize = mTlvStartOffset;}; - - /** - *@method appendTlvCommand - * - *@brief append TlvCommand to txBuffer - * - *@param commandType (in) command type - * - *@return the appended TLV size - */ - size_t appendTlvCommand(Command commandType = Command::COMMAND_SWITCH_ACTIVE); - - /** - *@method appendTlvSentinel - * - *@brief append TlvSentinel to txBuffer - * - *@return the appended TLV size - */ - size_t appendTlvSentinel(); - - /** - *@method appendTlvDummy - * - *@brief append dummy TLV, test purpose only - * - *@return the appended TLV size - */ - size_t appendTlvDummy(size_t paddingSize, int seqNo); - - /** - * @method initTxBufferTlvSendSwitch - * - * @brief initialize TX buffer TLVs to send switch command to peer - * - * @return none - */ - void initTxBufferTlvSendSwitch(); - - /** - * @method initTxBufferTlvSendProbe - * - * @brief initialize TX buffer TLVs to send probe command to peer - * - * @return none - */ - void initTxBufferTlvSendProbe(); - - /** - * @method initTxBufferTlvSentinel - * - * @brief initialize TX buffer to have only TLV sentinel - * - * @return none - */ - void initTxBufferTlvSentinel(); - - /** - * @method calculateChecksum - * - * @brief calculate TX packet checksums in both IP header and ICMP header - * - * @return none - */ - inline void calculateTxPacketChecksum(); - - /** - * @method getProbingInterval - * - * @brief get link prober interval - * - * @return link prober interval - */ - inline uint32_t getProbingInterval(); - - /** - * @method handleSwitchoverTimeout - * - * @brief handle switchover time out - * - * @param errorCode (in) socket error code - * - * @return none - */ - void handleSwitchoverTimeout(boost::system::error_code errorCode); - - friend class test::LinkProberTest; - friend class test::LinkProberMockTest; - -private: - static SockFilter mIcmpFilter[]; - -private: - /** - * @method reportHeartbeatReplyReceivedActiveStandby - * - * @brief report heartbeat reply received to active-standby mode link prober state machine - * - * @return none - */ - void reportHeartbeatReplyReceivedActiveStandby(HeartbeatType heartbeatType); - - /** - * @method reportHeartbeatReplyReceivedActiveActive - * - * @brief report heartbeat reply received to active-active mode link prober state machine - * - * @param heartbeatType (in) received heartbeat type - * - * @return none - */ - void reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType); - - /** - * @method reportHeartbeatReplyNotReceivedActiveStandby - * - * @brief report heartbeat reply not received to active-standby mode link prober state machine - * - * @param heartbeatType (in) received heartbeat type - * - * @return none - */ - void reportHeartbeatReplyNotReceivedActiveStandby(); - - /** - * @method reportHeartbeatReplyNotReceivedActiveActive - * - * @brief report heartbeat reply not received to active-active mode link prober state machine - * - * @return none - */ - void reportHeartbeatReplyNotReceivedActiveActive(); - -private: - common::MuxPortConfig &mMuxPortConfig; - boost::asio::io_service &mIoService; - LinkProberStateMachineBase *mLinkProberStateMachinePtr; - - boost::function mReportHeartbeatReplyReceivedFuncPtr; - boost::function mReportHeartbeatReplyNotRecivedFuncPtr; - - uint16_t mTxSeqNo = 0xffff; - uint16_t mRxSelfSeqNo = 0; - uint16_t mRxPeerSeqNo = 0; - - uint32_t mIcmpChecksum = 0; - uint32_t mIpChecksum = 0; - - static const size_t mPacketHeaderSize = sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr); - static const size_t mTlvStartOffset = sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload); - - boost::asio::io_service::strand mStrand; - boost::asio::deadline_timer mDeadlineTimer; - boost::asio::deadline_timer mSuspendTimer; - boost::asio::deadline_timer mSwitchoverTimer; - boost::asio::posix::stream_descriptor mStream; - - std::shared_ptr mSockFilterPtr; - SockFilterProg mSockFilterProg; - - int mSocket = 0; - - std::size_t mTxPacketSize; - std::array mTxBuffer; - std::array mRxBuffer; - - bool mCancelSuspend = false; - bool mSuspendTx = false; - bool mShutdownTx = false; - bool mDecreaseProbingInterval = false; - - uint64_t mIcmpUnknownEventCount = 0; - uint64_t mIcmpPacketCount = 0; -}; - -} /* namespace link_prober */ - -#endif /* LINKPROBER_H_ */ diff --git a/src/link_prober/LinkProberBase.cpp b/src/link_prober/LinkProberBase.cpp new file mode 100644 index 0000000..dfed35d --- /dev/null +++ b/src/link_prober/LinkProberBase.cpp @@ -0,0 +1,728 @@ +#include +#include +#include +#include "LinkProberBase.h" +#include "LinkProberHw.h" +#include "LinkProberSw.h" +#include +#include +#include "common/MuxLogger.h" +#include "common/MuxException.h" +#include "LinkProberStateMachineActiveActive.h" +#include "LinkProberStateMachineActiveStandby.h" + +namespace link_prober +{ +// +// Berkeley Packet Filter program that captures incoming ICMP traffic +// +SockFilter LinkProberBase::mIcmpFilter[] = { + [0] = {.code = 0x28, .jt = 0, .jf = 0, .k = 0x0000000c}, + [1] = {.code = 0x15, .jt = 0, .jf = 10, .k = 0x00000800}, + [2] = {.code = 0x20, .jt = 0, .jf = 0, .k = 0x0000001a}, + [3] = {.code = 0x15, .jt = 0, .jf = 8, .k = 0x00000000}, + [4] = {.code = 0x30, .jt = 0, .jf = 0, .k = 0x00000017}, + [5] = {.code = 0x15, .jt = 0, .jf = 6, .k = 0x00000001}, + [6] = {.code = 0x28, .jt = 0, .jf = 0, .k = 0x00000014}, + [7] = {.code = 0x45, .jt = 4, .jf = 0, .k = 0x00001fff}, + [8] = {.code = 0xb1, .jt = 0, .jf = 0, .k = 0x0000000e}, + [9] = {.code = 0x50, .jt = 0, .jf = 0, .k = 0x0000000e}, + [10] = {.code = 0x15, .jt = 0, .jf = 1, .k = 0x00000000}, + [11] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00040000}, + [12] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00000000}, +}; + +// Set to hold all the session id's accross all ports of system for both Normal/Rx +std::unordered_set LinkProberBase::mGuidSet; + +LinkProberBase::LinkProberBase(common::MuxPortConfig &muxPortConfig, boost::asio::io_service &ioService, + LinkProberStateMachineBase *linkProberStateMachinePtr) : + mMuxPortConfig(muxPortConfig), + mIoService(ioService), + mLinkProberStateMachinePtr(linkProberStateMachinePtr), + mStrand(mIoService), + mStream(mIoService), + mDeadlineTimer(mIoService), + mSuspendTimer(mIoService), + mSwitchoverTimer(mIoService) +{ + try { + mSockFilterPtr = std::shared_ptr ( + new SockFilter[sizeof(mIcmpFilter) / sizeof(*mIcmpFilter)], + std::default_delete() + ); + memcpy(mSockFilterPtr.get(), mIcmpFilter, sizeof(mIcmpFilter)); + + mSockFilterProg.len = sizeof(mIcmpFilter) / sizeof(*mIcmpFilter); + mSockFilterProg.filter = mSockFilterPtr.get(); + } + catch (const std::bad_alloc& ex) { + std::ostringstream errMsg; + errMsg << "Failed allocate memory. Exception details: " << ex.what(); + + throw MUX_ERROR(BadAlloc, errMsg.str()); + } + + setSelfGuidData(generateGuid()); +} + +// +// ---> setupSocket(); +// +// creation of socket to recieve icmp packets only and setup recieve stream +// +void LinkProberBase::setupSocket() { + SockAddrLinkLayer addr = {0}; + addr.sll_ifindex = if_nametoindex(mMuxPortConfig.getPortName().c_str()); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_ALL); + + mSocket = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMP); + if (mSocket < 0) { + std::ostringstream errMsg; + errMsg << "Failed to open socket with '" << strerror(errno) << "'" + << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + if (bind(mSocket, (struct sockaddr *) &addr, sizeof(addr))) { + std::ostringstream errMsg; + errMsg << "Failed to bind to interface '" << mMuxPortConfig.getPortName() << "' with '" + << strerror(errno) << "'" << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + mSockFilterPtr.get()[3].k = mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint(); + if (setsockopt(mSocket, SOL_SOCKET, SO_ATTACH_FILTER, &mSockFilterProg, sizeof(mSockFilterProg)) != 0) { + std::ostringstream errMsg; + errMsg << "Failed to attach filter with '" << strerror(errno) << "'" + << std::endl; + throw MUX_ERROR(SocketError, errMsg.str()); + } + + mStream.assign(mSocket); + + initializeSendBuffer(); + startInitRecv(); +} + +// +// ---> startInitRecv(); +// +// start ICMP ECHOREPLY reception +// +void LinkProberBase::startInitRecv() +{ + MUXLOGTRACE(mMuxPortConfig.getPortName()); + + mStream.async_read_some( + boost::asio::buffer(mRxBuffer, MUX_MAX_ICMP_BUFFER_SIZE), + mStrand.wrap(boost::bind( + &LinkProberBase::handleInitRecv, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred + )) + ); +} + +// +// ---> handleInitRecv(const boost::system::error_code& errorCode, size_t bytesTransferred); +// +// handle packet reception intialization +// +void LinkProberBase::handleInitRecv( + const boost::system::error_code& errorCode, + size_t bytesTransferred +) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + + if (errorCode != boost::asio::error::operation_aborted) { + //ether_header *ethHeader = reinterpret_cast (mRxBuffer.data()); + std::array macAddress; + + if (mMuxPortConfig.ifEnableUseTorMac()) { + memcpy(macAddress.data(), mMuxPortConfig.getTorMacAddress().data(),macAddress.size()); + } else { + memcpy(macAddress.data(), mMuxPortConfig.getVlanMacAddress().data(),macAddress.size()); + } + + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + &LinkProberStateMachineBase::handleMackAddressUpdate, + mLinkProberStateMachinePtr, + macAddress + ))); + } + +} + +// +// ---> handleRecv(const boost::system::error_code& errorCode, size_t bytesTransferred); +// +// handle packet reception +// +void LinkProberBase::handleRecv( + const boost::system::error_code& errorCode, + size_t bytesTransferred +) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + bool isProberHw = mMuxPortConfig.getLinkProberType() == common::MuxPortConfig::LinkProberType::Hardware; + if(isProberHw) + { + MUXLOGWARNING(boost::format("Raw GUID PORT: {%s}") % mMuxPortConfig.getPortName()); + } + + if (!errorCode) + { + iphdr *ipHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header)); + icmphdr *icmpHeader = reinterpret_cast ( + mRxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + ); + + MUXLOGTRACE(boost::format("%s: Got data from: %s, size: %d") % + mMuxPortConfig.getPortName() % + boost::asio::ip::address_v4(ntohl(ipHeader->saddr)).to_string() % + (bytesTransferred - sizeof(iphdr) - sizeof(ether_header)) + ); + + IcmpPayload *icmpPayload = reinterpret_cast ( + mRxBuffer.data() + mPacketHeaderSize + ); + + + // Handling of cookie and guids: + // - reception of peer hw cookie is considered as hw prober in peer + // and will trigger creation of RX session in hw recording the peer guid. + // - reception of new peer guid with hardware cookie will trigger deletion of old + // hardware RX session and creation of new session. + // - TLV probe will always use software. + // - software peer guid may not be unique across mux ports fo backward compatibilty, + // however we will record it in the global set to avoid collisions. + if (isProberHw) + { + (static_cast(this))->handleIcmpPayload(bytesTransferred, icmpHeader, icmpPayload); + } else { + (static_cast(this))->handleIcmpPayload(bytesTransferred, icmpHeader, icmpPayload); + } + } else { + MUXLOGDEBUG(boost::format("Recv System Error {%s} for PORT: {%s}") % errorCode.message() % mMuxPortConfig.getPortName()); + } +} + +// +// ---> handleTlvCommandRecv(Tlv *tlvPtr, bool isPeer); +// +// process icmp Tlv +// +void LinkProberBase::handleTlvCommandRecv( + Tlv *tlvPtr, + bool isPeer +) +{ + if (isPeer) { + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + + switch (static_cast(tlvPtr->command)) { + case Command::COMMAND_SWITCH_ACTIVE: { + MUXLOGWARNING(boost::format("SwitchActiveRequestEvent")); + boost::asio::post(strand, boost::bind( + static_cast(&LinkProberStateMachineBase::processEvent), + mLinkProberStateMachinePtr, + LinkProberStateMachineBase::getSwitchActiveRequestEvent() + )); + break; + } + case Command::COMMAND_MUX_PROBE: { + MUXLOGWARNING(boost::format("MuxProbeRequestEvent")); + boost::asio::post(strand, boost::bind( + static_cast(&LinkProberStateMachineBase::processEvent), + mLinkProberStateMachinePtr, + LinkProberStateMachineBase::getMuxProbeRequestEvent() + )); + break; + } + default: { + break; + } + } + } +} + +// +// ---> findNextTlv +// +// Find next TLV to process in rxBuffer +// +size_t LinkProberBase::findNextTlv(size_t readOffset, size_t bytesTransferred) +{ + size_t tlvSize = 0; + if (readOffset + sizeof(TlvHead) <= bytesTransferred) { + Tlv *tlvPtr = reinterpret_cast (mRxBuffer.data() + readOffset); + tlvSize = (sizeof(TlvHead) + ntohs(tlvPtr->tlvhead.length)); + if (readOffset + tlvSize > bytesTransferred) { + tlvSize = 0; + } + } + return tlvSize; +} + +void LinkProberBase::handleTlvRecv(size_t bytesTransferred, bool isSelfGuid) +{ + size_t nextTlvOffset = mTlvStartOffset; + size_t nextTlvSize = 0; + bool stopProcessTlv = false; + Tlv *nextTlvPtr = nullptr; + while ((nextTlvPtr = getNextTLVPtr(nextTlvOffset, bytesTransferred, nextTlvSize)) && !stopProcessTlv) { + switch (nextTlvPtr->tlvhead.type) { + case TlvType::TLV_COMMAND: { + handleTlvCommandRecv(nextTlvPtr, !isSelfGuid); + break; + } + case TlvType::TLV_SENTINEL: { + // sentinel TLV, stop processing + stopProcessTlv = true; + break; + } + default: { + // try to skip unknown TLV with valid length(>0) + stopProcessTlv = (nextTlvSize == sizeof(Tlv)); + break; + } + } + nextTlvOffset += nextTlvSize; + } + if (nextTlvOffset < bytesTransferred) { + size_t BytesNotProcessed = bytesTransferred - nextTlvOffset; + MUXLOGTRACE(boost::format("%s: %d bytes in RxBuffer not processed") % + mMuxPortConfig.getPortName() % + BytesNotProcessed + ); + } +} + +// +// ---> startRecv(); +// +// start ICMP ECHOREPLY reception +// +void LinkProberBase::startRecv() +{ + MUXLOGTRACE(mMuxPortConfig.getPortName()); + + mStream.async_read_some( + boost::asio::buffer(mRxBuffer, MUX_MAX_ICMP_BUFFER_SIZE), + mStrand.wrap(boost::bind( + &LinkProberBase::handleRecv, + this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred + )) + ); +} + + +// +// ---> initializeSendBuffer(); +// +// initialize ICMP packet once +// +void LinkProberBase::initializeSendBuffer() +{ + ether_header *ethHeader = reinterpret_cast (mTxBuffer.data()); + memcpy(ethHeader->ether_dhost, mMuxPortConfig.getBladeMacAddress().data(), sizeof(ethHeader->ether_dhost)); + if (mMuxPortConfig.ifEnableUseTorMac()) { + memcpy(ethHeader->ether_shost, mMuxPortConfig.getTorMacAddress().data(), sizeof(ethHeader->ether_shost)); + } else { + memcpy(ethHeader->ether_shost, mMuxPortConfig.getVlanMacAddress().data(), sizeof(ethHeader->ether_shost)); + } + ethHeader->ether_type = htons(ETHERTYPE_IP); + + iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + + IcmpPayload *payloadPtr = new (mTxBuffer.data() + mPacketHeaderSize) IcmpPayload(); + memcpy(payloadPtr->uuid, (mSelfUUID.data + 8), sizeof(payloadPtr->uuid)); + resetTxBufferTlv(); + appendTlvSentinel(); + size_t totalPayloadSize = mTxPacketSize - mPacketHeaderSize; + + ipHeader->ihl = sizeof(iphdr) >> 2; + ipHeader->version = IPVERSION; + ipHeader->tos = 0xb8; + ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + totalPayloadSize); + ipHeader->id = static_cast (rand()); + ipHeader->frag_off = 0; + ipHeader->ttl = 64; + ipHeader->protocol = IPPROTO_ICMP; + ipHeader->check = 0; + ipHeader->saddr = htonl(mMuxPortConfig.getLoopbackIpv4Address().to_v4().to_uint()); + ipHeader->daddr = htonl(mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint()); + computeChecksum(ipHeader, ipHeader->ihl << 2); + + icmpHeader->type = ICMP_ECHO; + icmpHeader->code = 0; + icmpHeader->un.echo.id = htons(mMuxPortConfig.getServerId()); + icmpHeader->un.echo.sequence = htons(mTxSeqNo); + + computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); +} + +// +// ---> appendTlvSentinel +// +// Append TlvSentinel to the end of txBuffer +// +size_t LinkProberBase::appendTlvSentinel() +{ + size_t tlvSize = sizeof(TlvHead); + assert(mTxPacketSize + tlvSize <= MUX_MAX_ICMP_BUFFER_SIZE); + Tlv *tlvPtr = reinterpret_cast (mTxBuffer.data() + mTxPacketSize); + tlvPtr->tlvhead.type = TlvType::TLV_SENTINEL; + tlvPtr->tlvhead.length = 0; + mTxPacketSize += tlvSize; + return tlvSize; +} + +// +// ---> sendPeerProbeCommand(); +// +// send peer probe command +// +void LinkProberBase::sendPeerProbeCommand() +{ + boost::asio::post(mStrand, boost::bind(&LinkProberBase::handleSendProbeCommand, this)); +} + +// +// ---> probePeerTor(); +// +// send an early HB to peer ToR +// +void LinkProberBase::probePeerTor() +{ + if(mPeerType == SessionType::SOFTWARE) { + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind(&LinkProberBase::sendHeartbeat, this, false))); + } +} + +// +// ---> handleSendProbeCommand(); +// +// send probe command to peer ToR +// +void LinkProberBase::handleSendProbeCommand() +{ + initTxBufferTlvSendProbe(); + + sendHeartbeat(); + + initTxBufferTlvSentinel(); +} + +// +// ---> handleSendSwitchCommand(); +// +// send switch command to peer ToR +// +void LinkProberBase::handleSendSwitchCommand() +{ + initTxBufferTlvSendSwitch(); + + sendHeartbeat(); + + initTxBufferTlvSentinel(); + + // inform the composite state machine about command send completion + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&LinkProberStateMachineBase::processEvent), + mLinkProberStateMachinePtr, + LinkProberStateMachineBase::getSwitchActiveCommandCompleteEvent() + ))); +} + +// +// ---> sendPeerSwitchCommand(); +// +// send send peer switch command +// +void LinkProberBase::sendPeerSwitchCommand() +{ + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind(&LinkProberBase::handleSendSwitchCommand, this))); +} + +// +// ---> sendHeartbeat(bool forceSend) +// +// send ICMP ECHOREQUEST packet +// +void LinkProberBase::sendHeartbeat(bool forceSend) +{ + MUXLOGTRACE(mMuxPortConfig.getPortName()); + + updateIcmpSequenceNo(); + // check if suspend timer is running + if (forceSend || ((!mSuspendTx) && (!mShutdownTx))) { + boost::system::error_code errorCode; + mStream.write_some(boost::asio::buffer(mTxBuffer.data(), mTxPacketSize), errorCode); + + if (errorCode) { + MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Failed to send heartbeat! Error code: " + errorCode.message()); + } else { + MUXLOGTRACE(mMuxPortConfig.getPortName() + ": Done sending data"); + } + } +} + +// +// ---> calculateChecksum(uint16_t *data, size_t size); +// +// calculate ICMP payload checksum +// +uint32_t LinkProberBase::calculateChecksum(uint16_t *data, size_t size) +{ + uint32_t sum = 0; + + while (size > 1) { + sum += ntohs(*data++); + size -= sizeof(uint16_t); + } + + if (size) { + sum += ntohs(static_cast ((*reinterpret_cast (data)))); + } + + return sum; +} + +// +// ---> addChecksumCarryover(uint16_t *checksum, uint32_t sum); +// +// add checksum carryover +// +void LinkProberBase::addChecksumCarryover(uint16_t *checksum, uint32_t sum) +{ + sum = (sum >> 16) + (sum & 0xFFFF); + sum += (sum >> 16); + *checksum = htons(~sum); +} + +// +// ---> computeChecksum(icmphdr *icmpHeader, size_t size); +// +// compute ICMP checksum +// +void LinkProberBase::computeChecksum(icmphdr *icmpHeader, size_t size) +{ + icmpHeader->checksum = 0; + mIcmpChecksum = calculateChecksum( + reinterpret_cast (icmpHeader), size + ); + addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); +} + +// +// ---> computeChecksum(iphdr *ipHeader, size_t size); +// +// compute IPv4 checksum +// +void LinkProberBase::computeChecksum(iphdr *ipHeader, size_t size) +{ + ipHeader->check = 0; + mIpChecksum = calculateChecksum( + reinterpret_cast (ipHeader), size + ); + addChecksumCarryover(&ipHeader->check, mIpChecksum); +} + + + +// +// ---> updateIcmpSequenceNo(); +// +// update ICMP packet checksum, used before sending new heartbeat +// +void LinkProberBase::updateIcmpSequenceNo() +{ + // update received sequence to avoid reporting invalid ICMP event when sequence number rolls over + mRxPeerSeqNo = mTxSeqNo; + mRxSelfSeqNo = mTxSeqNo; + + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + icmpHeader->un.echo.sequence = htons(++mTxSeqNo); + mIcmpChecksum += mTxSeqNo ? 1 : 0; + addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); +} + +// +// ---> appendTlvCommand +// +// Append TlvCommand to the end of txBuffer +// +size_t LinkProberBase::appendTlvCommand(Command commandType) +{ + size_t tlvSize = sizeof(TlvHead) + sizeof(Command); + assert(mTxPacketSize + tlvSize <= MUX_MAX_ICMP_BUFFER_SIZE); + Tlv *tlvPtr = reinterpret_cast (mTxBuffer.data() + mTxPacketSize); + tlvPtr->tlvhead.type = TlvType::TLV_COMMAND; + tlvPtr->tlvhead.length = htons(sizeof(Command)); + tlvPtr->command = static_cast (commandType); + mTxPacketSize += tlvSize; + return tlvSize; +} + +// +// ---> initTxBufferTlvSendProbe +// +// Initialize TX buffer TLVs to send probe command to peer +// +void LinkProberBase::initTxBufferTlvSendProbe() +{ + resetTxBufferTlv(); + appendTlvCommand(Command::COMMAND_MUX_PROBE); + appendTlvSentinel(); + + calculateTxPacketChecksum(); +} + +// +// ---> initTxBufferTlvSentinel +// +// Initialize TX buffer to have only TLV sentinel +// +void LinkProberBase::initTxBufferTlvSentinel() +{ + resetTxBufferTlv(); + appendTlvSentinel(); + + calculateTxPacketChecksum(); +} + +// +// ---> initTxBufferTlvSendSwitch +// +// Initialize TX buffer TLVs to send switch command to peer +// +void LinkProberBase::initTxBufferTlvSendSwitch() +{ + resetTxBufferTlv(); + appendTlvCommand(Command::COMMAND_SWITCH_ACTIVE); + appendTlvSentinel(); + + calculateTxPacketChecksum(); +} + +// +// ---> calculateTxPacketChecksum +// +// Calculate TX packet checksums in both IP header and ICMP header +// +void LinkProberBase::calculateTxPacketChecksum() +{ + size_t totalPayloadSize = mTxPacketSize - mPacketHeaderSize; + iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); + ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + totalPayloadSize); + computeChecksum(ipHeader, ipHeader->ihl << 2); +} + +// +// ---> appendTlvDummy +// +// Append a dummy TLV, test purpose only +// +size_t LinkProberBase::appendTlvDummy(size_t paddingSize, int seqNo) +{ + size_t tlvSize = sizeof(TlvHead) + paddingSize + sizeof(uint32_t); + assert(mTxPacketSize + tlvSize <= MUX_MAX_ICMP_BUFFER_SIZE); + Tlv *tlvPtr = reinterpret_cast (mTxBuffer.data() + mTxPacketSize); + tlvPtr->tlvhead.type = TlvType::TLV_DUMMY; + tlvPtr->tlvhead.length = htons(paddingSize + sizeof(uint32_t)); + memset(tlvPtr->data, 0, paddingSize); + *(reinterpret_cast (tlvPtr->data + paddingSize)) = htonl(seqNo); + mTxPacketSize += tlvSize; + return tlvSize; +} + +// +// ---> handleUpdateEthernetFrame(); +// +// update Ethernet frame of Tx Buffer +// +void LinkProberBase::handleUpdateEthernetFrame() +{ + initializeSendBuffer(); +} + +// +// ---> uuidToHexString(const boost::uuids::uuid& uuid); +// +// convert uuid to string in hes form +// +std::string LinkProberBase::uuidToHexString(const boost::uuids::uuid& uuid) +{ + std::ostringstream oss; + oss << std::hex << std::setfill('0'); // Hex formatting with leading zeros + + for (const auto& byte : uuid) { + oss << std::setw(2) << static_cast(byte); // Convert each byte to hex + } + + return oss.str(); +} + +void LinkProberBase::getGuidStr(const IcmpPayload *icmpPayload, std::string& guidDataStr) +{ + uint64_t network_guid; + std::memcpy(&network_guid, icmpPayload->uuid, sizeof(network_guid)); + if (network_guid == 0) + { + MUXLOGWARNING(boost::format("Received invalid Raw GUID: {%d}") % network_guid); + guidDataStr = "0"; + } else { + uint64_t host_guid = ntohll(network_guid); + host_guid = static_cast(host_guid); + std::stringstream os; + os << std::hex << std::setw(8) << std::setfill('0') << host_guid; + guidDataStr = os.str(); + MUXLOGWARNING(boost::format("Link Prober recieved GUID: {%s}") % guidDataStr); + } + guidDataStr = "0x" + guidDataStr; +} + +// +// ---> generateGuid(); +// +// generate GUID for link_prober +// +std::string LinkProberBase::generateGuid() +{ + boost::uuids::uuid generatedGuid; + boost::uuids::random_generator gen; + generatedGuid = gen(); + std::fill(generatedGuid.begin(), generatedGuid.end() - 4, 0); + auto generatedGuidStr = uuidToHexString(generatedGuid); + generatedGuidStr = "0x" + generatedGuidStr.substr(generatedGuidStr.length() - 8); + if(mGuidSet.find(generatedGuidStr) == mGuidSet.end()) + { + mGuidSet.insert(generatedGuidStr); + MUXLOGWARNING(boost::format("Link Prober generated GUID: {%s}") % generatedGuidStr); + } + else { + MUXLOGWARNING(boost::format("Guid collision happened for guid : {%s}") % generatedGuidStr); + generatedGuidStr = generateGuid(); + } + mSelfUUID = generatedGuid; + return generatedGuidStr; +} + +} diff --git a/src/link_prober/LinkProberBase.h b/src/link_prober/LinkProberBase.h new file mode 100644 index 0000000..3b0e4d1 --- /dev/null +++ b/src/link_prober/LinkProberBase.h @@ -0,0 +1,711 @@ +#ifndef LINK_PROBER_LINKPROBERBASE_H_ +#define LINK_PROBER_LINKPROBERBASE_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "LinkProberStateMachineBase.h" + +#include "IcmpPayload.h" +#include "common/MuxPortConfig.h" +#include "common/MuxLogger.h" + +namespace std { + template <> + struct hash { + std::size_t operator()(const boost::uuids::uuid& uuid) const { + // Use boost's built-in hash function for UUIDs + return boost::uuids::hash_value(uuid); + } + }; +} + +namespace test { +class LinkProberTest; +class LinkProberMockTest; +class LinkProberHardwareTest; +} + +namespace link_prober +{ + +/** + *@enum HeartbeatType + * + *@brief Received heartbeat type + */ +enum class HeartbeatType: uint8_t { + HEARTBEAT_SELF, + HEARTBEAT_PEER, + + Count +}; + +using SockFilter = struct sock_filter; +using SockFilterProg = struct sock_fprog; +using SockAddrLinkLayer = struct sockaddr_ll; + + +/** + *@class LinkProberBase + * + *@brief probes the server sing ICMP ECHPREQUEST packet. The packet payload + * holds GUID that identifies this ToR. Reception of this ToR's GUID + * indicate that the link is in Active state. Reception of unknown + * GUID will indicate standby state. Lack of ICMP packets will signal + * that the link state is unknown and it might be newly learned peers UID + */ +class LinkProberBase +{ +public: + enum SessionType { + UNKNOWN, + SOFTWARE, + HARDWARE + }; + + /** + *@method LinkProberBase + * + *@brief class default constructor + */ + LinkProberBase() = delete; + + /** + *@method LinkProberBase + * + *@brief class copy constructor + * + *@param LinkProberBase (in) reference to LinkProberBase object to be copied + */ + LinkProberBase(const LinkProberBase &) = delete; + + + /** + *@method ~LinkProberBase + * + *@brief class destructor + */ + ~LinkProberBase() = default; + + /** + *@method LinkProberBase + *@brief class constructor + * + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param ioService (in) reference to boost io_service object + *@param linkProberStateMachinePtr (in) reference to LinkProberStateMachineBase object + */ + LinkProberBase(common::MuxPortConfig &muxPortConfig, boost::asio::io_service &ioService, + LinkProberStateMachineBase *linkProberStateMachinePtr); + + + /** + *@method initialize + * + *@brief initialize link prober sockets and builds ICMP packet + * + *@return none + */ + virtual void initialize() { + MUXLOGWARNING(boost::format("Link Prober Base initialize not implemented")); + }; + + /** + *@method startProbing + * + *@brief start probing server/blade using ICMP ECHOREQUEST + * + *@return none + */ + virtual void startProbing() { + MUXLOGWARNING(boost::format("Link Prober startProbing not implemented")); + } + + /** + *@method suspendTxProbes + * + *@brief suspend sending ICMP ECHOREQUEST packets + * + *@param suspendTime_msec suspension time in msec + * + *@return none + */ + virtual void suspendTxProbes(uint32_t suspendTime_msec) { + MUXLOGWARNING(boost::format("Link Prober suspendTxProbes not implemented")); + } + + /** + *@method resumeTxProbes + * + *@brief resume sending ICMP ECHOREQUEST packets + * + *@return none + */ + virtual void resumeTxProbes() { + MUXLOGWARNING(boost::format("Link Prober resumeTxProbes not implemented")); + } + + /** + *@method detectLink + * + *@brief detect link status + * + *@return none + */ + virtual void detectLink() { + MUXLOGWARNING(boost::format("Link Prober detectLink not implemented")); + } + + /** + * @method resetIcmpPacketCounts() + * + * @brief reset Icmp packet counts, post a pck loss ratio update immediately + * + * @return none + */ + virtual void resetIcmpPacketCounts() { + MUXLOGWARNING(boost::format("Link Prober resetIcmpPacketCounts not implemented")); + } + + /** + * @method handleStateDbStateUpdate + * + * @brief handle state change of ICMP_ECHO_SESSION_TABLE in STATE_DB + * + * @return none + */ + virtual void handleStateDbStateUpdate(const std::string &linkFailureDetectionState, const std::string session_type) { + MUXLOGWARNING(boost::format("Link Prober handleStateDbStateUpdate not implemented")); + } + + /** + *@method shutdownTxProbes + * + *@brief stop sending ICMP ECHOREQUEST packets + * + *@return none + */ + virtual void shutdownTxProbes() { + MUXLOGWARNING(boost::format("Link Prober shutdownTxProbes not implemented")); + } + + /** + * @method restartTxProbes + * + * @brief restart sending ICMP ECHOREQUEST packets + * + * @return none + */ + virtual void restartTxProbes() { + MUXLOGWARNING(boost::format("Link Prober restartTxProbes not implemented")); + } + + /** + * @method decreaseProbeIntervalAfterSwitch + * + * @brief adjust link prober interval to 10 ms after switchover to better measure the switchover overhead. + * + * @param switchTime_msec (in) switchover is expected to complete within this time window + * @param expectingLinkProberEvent (in) depends on which state LinkManager is switching to, link prober expects self or peer events + * + * @return none + */ + virtual void decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec) { + MUXLOGWARNING(boost::format("Link Prober decreaseProbeIntervalAfterSwitch not implemented")); + } + + /** + * @method revertProbeIntervalAfterSwitchComplete + * + * @brief revert probe interval change after switchover is completed + * + * @return none + */ + virtual void revertProbeIntervalAfterSwitchComplete(){ + MUXLOGWARNING(boost::format("Link Prober revertProbeIntervalAfterSwitchComplete not implemented")); + } + + virtual void handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload) { + MUXLOGWARNING(boost::format("Link Prober handleIcmpPayload not implemented")); + } + + /** + *@method getSelfGuidData + * + *@brief getter for self GUID data + * + *@return pointer to current self GUID data + */ + inline std::string getSelfGuidData() { + return mSelfGuid; + } + + /** + *@method getPeerGuidData + * + *@brief getter for PEER GUID data + * + *@return pointer to current PEER GUID data + */ + inline std::string getPeerGuidData() { + return mPeerGuid; + } + + /** + *@method setSelfGuidData + * + *@brief setter for self GUID data + * + *@return none + */ + inline void setSelfGuidData(std::string l_guid) { + mSelfGuid = l_guid; + } + + /** + *@method setPeerGuidData + * + *@brief setter for PEER GUID data + * + *@return none + */ + inline void setPeerGuidData(std::string l_guid) { + mPeerGuid = l_guid; + } + + /** + * @method getPeerSessionType + * + * @brief get peer session type + * + * @return SessionType + */ + inline SessionType getPeerSessionType () { + return mPeerType; + } + + /** + * @method getProbingInterval + * + * @brief get link prober interval + * + * @return link prober interval + */ + inline uint32_t getProbingInterval() { + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + return mDecreaseProbingInterval? mMuxPortConfig.getDecreasedTimeoutIpv4_msec():mMuxPortConfig.getTimeoutIpv4_msec(); + } + + /** + * @method getNextTlvPtr + * + * @brief get the next TLV pointer + * + * @return next Tlv pointer from the offset + */ + inline Tlv* getNextTLVPtr(size_t offset, size_t bytesTransferred, size_t &nextTlvSize) { + nextTlvSize = findNextTlv(offset, bytesTransferred); + if (nextTlvSize == 0) + return nullptr; + + Tlv *nextTlvPtr = reinterpret_cast (mRxBuffer.data() + offset); + return nextTlvPtr; + } + + /** + *@method updateEthernetFrame + * + *@brief update Ethernet frame of Tx Buffer + * + *@return none + */ + virtual void updateEthernetFrame() { + MUXLOGWARNING(boost::format("Link Prober updateEthernetFrame not implemented")); + } + + /** + *@method probePeerTor + * + *@brief send an early HB to peer ToR + * + *@return none + */ + virtual void probePeerTor(); + + /** + *@method sendPeerProbeCommand + * + *@brief send probe command to peer ToR + * + *@return none + */ + virtual void sendPeerProbeCommand(); + + /** + *@method sendPeerSwitchCommand + * + *@brief send switch command to peer ToR + * + *@return none + */ + virtual void sendPeerSwitchCommand(); + + /** + * @method initTxBufferTlvSendProbe + * + * @brief initialize TX buffer TLVs to send probe command to peer + * + * @return none + */ + void initTxBufferTlvSendProbe(); + + /** + * @method initTxBufferTlvSentinel + * + * @brief initialize TX buffer to have only TLV sentinel + * + * @return none + */ + void initTxBufferTlvSentinel(); + + /** + * @method initTxBufferTlvSendSwitch + * + * @brief initialize TX buffer TLVs to send switch command to peer + * + * @return none + */ + void initTxBufferTlvSendSwitch(); + + void resetTxBufferTlv() {mTxPacketSize = mTlvStartOffset;}; + + static std::unordered_set mGuidSet; + boost::uuids::uuid mSelfUUID; + +protected: + + /** + *@method addChecksumCarryover + * + *@brief add checksum carryover + * + *@param checksum (out) pointer to checksum field + *@param sum (in) current sum of the buffer payload + * + *@return CRC checksum + */ + void addChecksumCarryover(uint16_t *checksum, uint32_t sum); + + /** + * @method startInitRecv + * + * @brief start ICMP ECHOREPLY reception + * + * @return none + */ + void startInitRecv(); + + /** + * @method startRecv + * + * @brief start ICMP ECHOREPLY reception + * + * @return none + */ + void startRecv(); + + /** + * @method setupSocket + * + * @brief setup socket filter + * + * @return none + */ + void setupSocket(); + + /** + *@method handleTlvCommandRecv + * + *@brief handle TLV command + * + *@param tlvPtr (in) Tlv ptr points to the start of TlvCommand in mRxBuffer + *@param isPeer (in) True if the reply received is from the peer ToR + * + *@return none + */ + void handleTlvCommandRecv( + Tlv *tlvPtr, + bool isPeer + ); + + void handleTlvRecv( + size_t bytesTransferred, + bool isSelfGuid + ); + /** + *@method handleRecv + * + *@brief handle packet reception + * + *@param errorCode (in) socket error code + *@param bytesTransferred (in) number of bytes received + * + *@return none + */ + void handleRecv( + const boost::system::error_code &errorCode, + size_t bytesTransferred + ); + + /** + *@method handleInitRecv + * + *@brief handle packet reception + * + *@param errorCode (in) socket error code + *@param bytesTransferred (in) number of bytes received + * + *@return none + */ + void handleInitRecv( + const boost::system::error_code &errorCode, + size_t bytesTransferred + ); + + /** + *@method handleSendProbeCommand + * + *@brief send probe command to peer ToR + * + *@return none + */ + void handleSendProbeCommand(); + + /** + *@method handleSendSwitchCommand + * + *@brief send switch command to peer ToR + * + *@return none + */ + void handleSendSwitchCommand(); + + /** + *@method computeChecksum + * + *@brief compute ICMP checksum + * + *@param icmpHeader (in, out) pointer ICMP header + *@param size (in) size of ICMP payload + * + *@return CRC checksum + */ + void computeChecksum(icmphdr *icmpHeader, size_t size); + + /** + *@method computeChecksum + * + *@brief compute IPv4 checksum + * + *@param ipHeader (in, out) pointer IPv4 header + *@param size (in) size of IPv4 header + * + *@return CRC checksum + */ + void computeChecksum(iphdr *ipHeader, size_t size); + + /** + *@method updateIcmpSequenceNo + * + *@brief update ICMP packet checksum, used before sending new heartbeat + * + *@return CRC checksum + */ + void updateIcmpSequenceNo(); + + /** + *@method getTxBuffer + * + *@brief getter for TxBuffer used for testing + * + *@return reference to tx buffer + */ + std::array& getTxBuffer() {return mTxBuffer;}; + + /** + *@method findNextTlv + * + *@brief Find next TLV in rxBuffer starting at readOffset + * + *@param readOffset (in) starting offset to read + *@param bytesTransferred (in) total bytes received in rxBuffer + * + *@return the next TLV size + */ + size_t findNextTlv(size_t readOffset, size_t bytesTransferred); + + /** + *@method handleUpdateEthernetFrame + * + *@brief update Ethernet frame of Tx Buffer + * + *@return none + */ + void handleUpdateEthernetFrame(); + + + /** + *@method sendHeartbeat + * + *@brief send ICMP ECHOREQUEST packet + * + *@param forceSend (in) Force sending heartbeat, used in link detect only + * + *@return none + */ + void sendHeartbeat(bool forceSend = false); + + /** + *@method calculateChecksum + * + *@brief calculate ICMP payload checksum + * + *@param data (in) pointer to data buffer + *@param size (in) size of data buffer + * + *@return CRC checksum + */ + uint32_t calculateChecksum(uint16_t *data, size_t size); + + /** + *@method initializeSendBuffer + * + *@brief initialize ICMP packet once + * + *@return CRC checksum + */ + void initializeSendBuffer(); + + /** + *@method appendTlvCommand + * + *@brief append TlvCommand to txBuffer + * + *@param commandType (in) command type + * + *@return the appended TLV size + */ + size_t appendTlvCommand(Command commandType = Command::COMMAND_SWITCH_ACTIVE); + + /** + *@method uuidToHexString + * + *@brief convert uuid to string in hex form + * + *@return uuid's string repersentation in hex form + */ + std::string uuidToHexString(const boost::uuids::uuid& uuid); + + void getGuidStr(const IcmpPayload *icmpPayload, std::string& guidDataStr); + + /** + *@method generateGuid + * + *@brief generate GUID for each link_prober + * + *@return newly generated guid + */ + std::string generateGuid(); + + /** + *@method appendTlvSentinel + * + *@brief append TlvSentinel to txBuffer + * + *@return the appended TLV size + */ + size_t appendTlvSentinel(); + + /** + *@method appendTlvDummy + * + *@brief append dummy TLV, test purpose only + * + *@return the appended TLV size + */ + size_t appendTlvDummy(size_t paddingSize, int seqNo); + + /** + * @method calculateTxPacketChecksum + * + * @brief calculate TX packet checksums in both IP header and ICMP header + * + * @return none + */ + void calculateTxPacketChecksum(); + + + static SockFilter mIcmpFilter[]; + + common::MuxPortConfig &mMuxPortConfig; + boost::asio::io_service &mIoService; + LinkProberStateMachineBase *mLinkProberStateMachinePtr; + + uint16_t mTxSeqNo = 0xffff; + uint16_t mRxSelfSeqNo = 0; + uint16_t mRxPeerSeqNo = 0; + + uint32_t mIcmpChecksum = 0; + uint32_t mIpChecksum = 0; + + static const size_t mPacketHeaderSize = sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr); + static const size_t mTlvStartOffset = sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload); + + boost::asio::io_service::strand mStrand; + boost::asio::posix::stream_descriptor mStream; + boost::asio::deadline_timer mDeadlineTimer; + boost::asio::deadline_timer mSuspendTimer; + boost::asio::deadline_timer mSwitchoverTimer; + std::shared_ptr mSockFilterPtr; + SockFilterProg mSockFilterProg; + + std::string mSelfGuid; + std::string mPeerGuid; + SessionType mPeerType = SessionType::UNKNOWN; + boost::function mReportHeartbeatReplyReceivedFuncPtr; + boost::function mReportHeartbeatReplyNotReceivedFuncPtr; + + int mSocket = 0; + + std::size_t mTxPacketSize; + std::array mTxBuffer; + std::array mRxBuffer; + + bool mCancelSuspend = false; + bool mSuspendTx = false; + bool mShutdownTx = false; + bool mDecreaseProbingInterval = false; + + uint64_t mIcmpUnknownEventCount = 0; + uint64_t mIcmpPacketCount = 0; +}; + +} /* namespace link_prober */ + +#endif /* LINKPROBER_H_ */ diff --git a/src/link_prober/LinkProberHw.cpp b/src/link_prober/LinkProberHw.cpp new file mode 100644 index 0000000..e0afb1c --- /dev/null +++ b/src/link_prober/LinkProberHw.cpp @@ -0,0 +1,578 @@ +/* + * LinkProberHw.cpp + * + * Created on: Oct 4, 2020 + * Author: Harjot Singh + */ + +#include +#include +#include +#include +#include + +#include + +#include "common/MuxLogger.h" +#include "LinkProberHw.h" +#include "common/MuxException.h" +#include "LinkProberStateMachineActiveActive.h" +#include "LinkProberStateMachineActiveStandby.h" +#include "../MuxPort.h" + +namespace link_prober +{ + +std::string LinkProberHw::mIcmpTableName = "ICMP_ECHO_SESSION"; +std::string LinkProberHw::mSessionCookie = "0x58767e7a"; +std::string LinkProberHw::mDefaultVrfName = "default"; +std::string LinkProberHw::mSessionTypeSelf = "NORMAL"; +std::string LinkProberHw::mSessionTypePeer = "RX"; +std::string LinkProberHw::mKeySeparator = ":"; +std::string LinkProberHw::mUpState = "Up"; +std::string LinkProberHw::mDownState = "Down"; +// +// ---> LinkProberHw::LinkProberHw( +// common::MuxPortConfig &muxPortConfig, +// boost::asio::io_service &ioService, +// LinkProberStateMachineBase *linkProberStateMachinePtr, +// mux::MuxPort* muxPort +// ) : +// +// class constructor +// +LinkProberHw::LinkProberHw( + common::MuxPortConfig &muxPortConfig, + boost::asio::io_service &ioService, + LinkProberStateMachineBase *linkProberStateMachinePtr, + mux::MuxPort* muxPort +) : + LinkProberBase(muxPortConfig, ioService, linkProberStateMachinePtr), + mMuxPortPtr(muxPort), + mSuspendTimer(mIoService), + mPositiveProbingTimer(mIoService), + mPositiveProbingPeerTimer(mIoService) +{ + mReportHeartbeatReplyReceivedFuncPtr = boost::bind( + &LinkProberHw::reportHeartbeatReplyReceivedActiveActive, + this, + boost::placeholders::_1 + ); + mReportHeartbeatReplyNotReceivedFuncPtr = boost::bind( + &LinkProberHw::reportHeartbeatReplyNotReceivedActiveActive, + this, + boost::placeholders::_1 + ); +} + +// +// ---> reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType); +// +// Used only in case of software peer sessions +// +void LinkProberHw::reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType) +{ + // sequence numbers are not used by hardware prober + if (heartbeatType == HeartbeatType::HEARTBEAT_SELF) { + MUXLOGWARNING(boost::format("Invalid NORMAL hardware session packet recieved, check the cookie!")); + } + if (heartbeatType == HeartbeatType::HEARTBEAT_PEER) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerActiveEvent()); + } +} + +// +// ---> reportHeartbeatReplyNotReceivedActiveActive(HeartbeatType heartbeatType); +// +// Used only for software peer +// +void LinkProberHw::reportHeartbeatReplyNotReceivedActiveActive(HeartbeatType heartbeatType) +{ + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerUnknownEvent()); +} + +// +// ---> handleTimeout(boost::system::error_code ec); +// +// handle software peer ICMP timeout +// +void LinkProberHw::handleTimeout(boost::system::error_code errorCode) +{ + MUXLOGTRACE(boost::format("%s: server: %d") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getServerId() + ); + + switch (mPeerType) { + case SessionType::UNKNOWN: + // start another cycle of recv + break; + + case SessionType::SOFTWARE: + mStream.cancel(); + mReportHeartbeatReplyNotReceivedFuncPtr(HeartbeatType::HEARTBEAT_PEER); + + mIcmpPacketCount++; + if (mIcmpPacketCount % mMuxPortConfig.getLinkProberStatUpdateIntervalCount() == 0) { + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + &LinkProberStateMachineBase::handlePckLossRatioUpdate, + mLinkProberStateMachinePtr, + mIcmpUnknownEventCount, + mIcmpPacketCount + ))); + } + break; + + case SessionType::HARDWARE: + break; + } + startRecv(); + startTimer(); +} + +// +// ---> startTimer(); +// +// start ICMP ECHOREPLY timeout timer for software peer +// +void LinkProberHw::startTimer() +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + // time out these heartbeats + mDeadlineTimer.expires_from_now(boost::posix_time::milliseconds(getProbingInterval())); + mDeadlineTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberHw::handleTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> initialize(); +// +// intializing reciving thread and triggers creation of new icmp_echo session +// +void LinkProberHw::initialize() +{ + setupSocket(); + createIcmpEchoSession(mSessionTypeSelf, getSelfGuidData()); +} + +// +// ---> startPositiveProbingTimer(std::string hwSessionType); +// +// triggers handling of expiration of positive probing timer +// +void LinkProberHw::startPositiveProbingTimer(std::string hwSessionType) +{ + MUXLOGDEBUG(boost::format("%s: Postive Probing Timer Started, session type-%s") % + mMuxPortConfig.getPortName() % hwSessionType); + // time out these heartbeats + if(hwSessionType == mSessionTypeSelf){ + mPositiveProbingTimer.expires_from_now(boost::posix_time::milliseconds(getProbingInterval() * mMuxPortConfig.getPositiveStateChangeRetryCount())); + mPositiveProbingTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberHw::handlePositiveProbingTimeout, + this, + hwSessionType + ))); + } else if(hwSessionType == mSessionTypePeer){ + mPositiveProbingPeerTimer.expires_from_now(boost::posix_time::milliseconds(getProbingInterval() * mMuxPortConfig.getPositiveStateChangeRetryCount())); + mPositiveProbingPeerTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberHw::handlePositiveProbingTimeout, + this, + hwSessionType + ))); + } +} + +// +// ---> handlePositiveProbingTimeout(std::string hwSessionType)(); +// +// bring Link prober state machine to active when positive probing timer expires +// +void LinkProberHw::handlePositiveProbingTimeout(std::string hwSessionType) +{ + MUXLOGWARNING(boost::format("%s: Postive Probing Timer Expired, session type-%s") % + mMuxPortConfig.getPortName() % hwSessionType); + if(hwSessionType == mSessionTypeSelf) + { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpHwSelfEvent()); + } else if(hwSessionType == mSessionTypePeer) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpHwPeerActiveEvent()); + } +} + +// +// ---> startProbing(); +// +// triggers creation of ICMP_ECHO_SESSION_TABLE in APP_DB table to start sending/recieving of ICMP session packets +// +void LinkProberHw::startProbing() +{ + if(mStream.is_open()){ + mStream.cancel(); + } + if((!mSuspendTx) && (!mShutdownTx)) + { + createIcmpEchoSession(mSessionTypeSelf, getSelfGuidData()); + } + startRecv(); +} + +// +// ---> handleStateDbStateUpdate(const std::string& session_state, const std::string hwSessionType); +// +// handle state change notification from STATE_DB:ICMP_ECHO_SESSION_TABLE +// +void LinkProberHw::handleStateDbStateUpdate(const std::string& session_state, const std::string hwSessionType) +{ + MUXLOGWARNING(boost::format("%s: Recieved New state %s for icmp_Echo mSuspendTx = %b and mShutdownTx = %b ") % + mMuxPortConfig.getPortName() % session_state % mSuspendTx % mShutdownTx); + + if((!mSuspendTx) && (!mShutdownTx)) + { + if(hwSessionType == mSessionTypeSelf) { + if(session_state == mUpState) { + startPositiveProbingTimer(hwSessionType); + } else if(session_state == mDownState) { + mPositiveProbingTimer.cancel(); + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpHwUnknownEvent()); + } else { + MUXLOGWARNING(boost::format("%s: not a valid state %s for Link Prober") % + mMuxPortConfig.getPortName() % session_state); + } + } else if (hwSessionType == mSessionTypePeer) { + if(session_state == mUpState) { + startPositiveProbingTimer(hwSessionType); + } else if(session_state == mDownState) { + mPositiveProbingPeerTimer.cancel(); + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpHwPeerUnknownEvent()); + } else { + MUXLOGWARNING(boost::format("%s: not a valid peer state %s for Link Prober ") % + mMuxPortConfig.getPortName() % session_state); + } + } + } + else + { + MUXLOGWARNING(boost::format("%s: Recieved State Update %s Even when we are in suspend or shutdown ") % + mMuxPortConfig.getPortName() % session_state); + } +} + +// +// ---> createIcmpEchoSession(std::string hwSessionType, std::string guid); +// +// triggers creation of new icmp_echo_session +// +void LinkProberHw::createIcmpEchoSession(std::string hwSessionType, std::string guid) +{ + MUXLOGDEBUG(boost::format("%s: Creating the Icmp session of type %s with guid {%s}") + % mMuxPortConfig.getPortName() % hwSessionType % guid); + auto entries = std::make_unique(); + std::string portName = mMuxPortConfig.getPortName(); + std::string tx_interval = std::to_string(mMuxPortConfig.getTimeoutIpv4_msec()); + std::string rx_interval = std::to_string(mMuxPortConfig.getTimeoutIpv4_msec() * mMuxPortConfig.getNegativeStateChangeRetryCount()); + std::string src_ip = mMuxPortConfig.getLoopbackIpv4Address().to_string(); + std::string dst_ip = mMuxPortConfig.getBladeIpv4Address().to_string(); + std::string src_mac = ""; + std::string dst_mac = etherMacArrayToString(mMuxPortConfig.getBladeMacAddress()); + + if (mMuxPortConfig.ifEnableUseTorMac()) + { + src_mac = etherMacArrayToString(mMuxPortConfig.getTorMacAddress()); + } else { + src_mac = etherMacArrayToString(mMuxPortConfig.getVlanMacAddress()); + } + + std::string key = mDefaultVrfName + + mKeySeparator + portName + mKeySeparator + guid + mKeySeparator; + + if (hwSessionType == mSessionTypeSelf) { + key += mSessionTypeSelf; + } else { + key += mSessionTypePeer; + } + + entries->emplace_back("tx_interval", tx_interval); + entries->emplace_back("rx_interval", rx_interval); + entries->emplace_back("session_guid", guid); + entries->emplace_back("session_cookie", mSessionCookie); + entries->emplace_back("src_ip", src_ip); + entries->emplace_back("dst_ip", dst_ip); + entries->emplace_back("src_mac", src_mac); + entries->emplace_back("dst_mac", dst_mac); + + mMuxPortPtr->createIcmpEchoSession(key, std::move(entries)); +} + +// +// ---> initialize(); +// +// triggers deletion of icmp_echo_session +// +void LinkProberHw::deleteIcmpEchoSession(std::string hwSessionType, std::string guid) +{ + MUXLOGWARNING(boost::format("%s: Deleting the Icmp session of type %s with guid {%s} ") + % mMuxPortConfig.getPortName() % hwSessionType %guid); + std::string portName = mMuxPortConfig.getPortName(); + std::string key = mDefaultVrfName + + mKeySeparator + portName + mKeySeparator + guid + mKeySeparator; + if (hwSessionType == mSessionTypeSelf) { + key += mSessionTypeSelf; + } else { + key += mSessionTypePeer; + } + mMuxPortPtr->deleteIcmpEchoSession(key); +} + +// +// ---> suspendTxProbes(uint32_t suspendTime_msec); +// +// suspend sending ICMP ECHOREQUEST packets +// +void LinkProberHw::suspendTxProbes(uint32_t suspendTime_msec) +{ + auto guid = getSelfGuidData(); + deleteIcmpEchoSession(mSessionTypeSelf, guid); + mSuspendTx = true; + mCancelSuspend = false; + MUXLOGWARNING(boost::format("%s: suspend ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + mSuspendTimer.expires_from_now(boost::posix_time::milliseconds(suspendTime_msec)); + mSuspendTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberHw::handleSuspendTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> resumeTxProbes(); +// +// resumes sending ICMP ECHOREQUEST packets +// +void LinkProberHw::resumeTxProbes() +{ + mSuspendTimer.cancel(); + MUXLOGWARNING(boost::format("%s: Resume ICMP Probing") % + mMuxPortConfig.getPortName()); + mCancelSuspend = true; + startProbing(); +} + +// +// ---> shutdownTxProbes(); +// +// stop sending ICMP ECHOREQUEST packets +// +void LinkProberHw::shutdownTxProbes() +{ + auto guid = getSelfGuidData(); + MUXLOGWARNING(boost::format("%s: Shutdown ICMP Probing") % + mMuxPortConfig.getPortName()); + deleteIcmpEchoSession(mSessionTypeSelf, guid); + mShutdownTx = true; +} + +// +// ---> restartTxProbes(); +// +// first stop sending and then again resume sending ICMP ECHOREQUEST packets +// +void LinkProberHw::restartTxProbes() +{ + auto guid = getSelfGuidData(); + MUXLOGWARNING(boost::format("Restart ICMP Probing")); + deleteIcmpEchoSession(mSessionTypeSelf, guid); + mShutdownTx = false; + startProbing(); +} + +// +// ---> handleSuspendTimeout(boost::system::error_code errorCode); +// +// when suspendTimer expires start probing again and notify LinkProberStateMachine to get into orignal state +// +void LinkProberHw::handleSuspendTimeout(boost::system::error_code errorCode) +{ + MUXLOGWARNING(boost::format("%s: suspend timeout, resume ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + mSuspendTx = false; + if (errorCode == boost::system::errc::success || mCancelSuspend) { + // inform the composite state machine about Suspend timer expiry + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&LinkProberStateMachineBase::processEvent), + mLinkProberStateMachinePtr, + LinkProberStateMachineBase::getSuspendTimerExpiredEvent() + ))); + } + mCancelSuspend = false; +} + +// +// ---> updateEthernetFrame(); +// +// update Ethernet frame of Tx Buffer +// +void LinkProberHw::updateEthernetFrame() +{ + deleteIcmpEchoSession(mSessionTypeSelf, getSelfGuidData()); + createIcmpEchoSession(mSessionTypeSelf, getSelfGuidData()); + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind(&LinkProberHw::handleUpdateEthernetFrame, this))); +} + +// +// ---> etherMacArrayToString(const std::array& macAddress); +// +// helper to convert ether array structure of mac address to string format +// +std::string LinkProberHw::etherMacArrayToString(const std::array& macAddress) +{ + std::ostringstream oss; + oss << std::hex << std::setfill('0'); // Set hex formatting with leading zeros + + for (size_t i = 0; i < macAddress.size(); ++i) { + oss << std::setw(2) << static_cast(macAddress[i]); // Convert each byte to hex + if (i != macAddress.size() - 1) { + oss << ":"; // Add colon between bytes except after the last byte + } + } + + return oss.str(); +} + +// +// ---> handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload); +// +// handle Icmp packet recieved on socket +// +void LinkProberHw::handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload) +{ + bool isHwCookie = ntohl(icmpPayload->cookie) == IcmpPayload::getHardwareCookie(); + bool isSwCookie = ntohl(icmpPayload->cookie) == IcmpPayload::getSoftwareCookie(); + + if (isHwCookie) + { + if ((ntohl(icmpPayload->version) <= IcmpPayload::getVersion()) && + (ntohs(icmpHeader->un.echo.id) == 0)) + { + // echo.id in hw prober must not be set + MUXLOGTRACE(boost::format("%s: Valid ICMP Packet from %s") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getBladeIpv4Address().to_string() + ); + + std::string guidDataStr; + getGuidStr(icmpPayload, guidDataStr); + + if (guidDataStr == "0x0") { + MUXLOGWARNING(boost::format("%s: Received 0x0 GUID") % + mMuxPortConfig.getPortName()); + //ignore this and get more packets + startRecv(); + return; + } + bool isSelfGuid = getSelfGuidData() == guidDataStr; + // sequence numbers are not used by hw prober + if (!isSelfGuid) + { + // Received a peer guid + MUXLOGWARNING(boost::format("%s: Peer Guid Detected %s") % + mMuxPortConfig.getPortName() % guidDataStr); + // existing peer guid session needs to be deleted, when we learn a new peer session + if ((mPeerType == SessionType::HARDWARE) && (mPeerGuid != "") && + (mPeerGuid != guidDataStr)) + { + deleteIcmpEchoSession(mSessionTypePeer, mPeerGuid); + mPositiveProbingPeerTimer.cancel(); + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerUnknownEvent()); + } + // insert new peer guid + if(mGuidSet.find(guidDataStr) != mGuidSet.end()) + { + mGuidSet.erase(mPeerGuid); + mGuidSet.insert(guidDataStr); + } + mPeerType = LinkProberBase::HARDWARE; + setPeerGuidData(guidDataStr); + createIcmpEchoSession(mSessionTypePeer, getPeerGuidData()); + } + } else { + MUXLOGWARNING(boost::format("%s: Received invalid packet with hwcookie") % + mMuxPortConfig.getPortName()); + } + } else if (isSwCookie) { + if ((ntohl(icmpPayload->version) <= IcmpPayload::getVersion()) && + (ntohs(icmpHeader->un.echo.id) == mMuxPortConfig.getServerId())) + { + MUXLOGTRACE(boost::format("%s: Valid ICMP Packet from %s") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getBladeIpv4Address().to_string() + ); + + // we can get software cookie from only from peer + std::string guidDataStr; + getGuidStr(icmpPayload, guidDataStr); + if (guidDataStr == "0x0") { + MUXLOGWARNING(boost::format("%s: Received 0x0 GUID from") % + mMuxPortConfig.getPortName()); + startRecv(); + return; + } + + if (guidDataStr == mSelfGuid) + { + // ignore this, software generated TLV packets are coming back. + startRecv(); + return; + } + + // check peer TLV packets + bool isTlvPkt = false; + size_t nextTlvSize = 0; + auto *tlvPtr = getNextTLVPtr(mTlvStartOffset, bytesTransferred, nextTlvSize); + if (tlvPtr && tlvPtr->tlvhead.type != TlvType::TLV_SENTINEL) + isTlvPkt = true; + + // peer transitioned to software we need to delete peer HW session + if (!isTlvPkt && (mPeerType == SessionType::HARDWARE)) + { + deleteIcmpEchoSession(mSessionTypePeer, mPeerGuid); + mGuidSet.erase(mPeerGuid); + mPositiveProbingPeerTimer.cancel(); + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpHwPeerUnknownEvent()); + mPeerType = SessionType::SOFTWARE; + } + + // new peer guid + if (mPeerGuid != guidDataStr) + { + if(mGuidSet.find(guidDataStr) != mGuidSet.end()) + { + mGuidSet.insert(guidDataStr); + } + setPeerGuidData(guidDataStr); + mPositiveProbingPeerTimer.cancel(); + mDeadlineTimer.cancel(); + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerUnknownEvent()); + startRecv(); + startTimer(); + return; + } + // seq numbers are not incremented for hw prober + mReportHeartbeatReplyReceivedFuncPtr(HeartbeatType::HEARTBEAT_PEER); + handleTlvRecv(bytesTransferred, false); + } else { + MUXLOGWARNING(boost::format("%s: Received invalid packet with swcookie") % + mMuxPortConfig.getPortName()); + } + } + startRecv(); +} + +} diff --git a/src/link_prober/LinkProberHw.h b/src/link_prober/LinkProberHw.h new file mode 100644 index 0000000..7124fb9 --- /dev/null +++ b/src/link_prober/LinkProberHw.h @@ -0,0 +1,280 @@ +#ifndef LINK_PROBER_LINKPROBERHW_H_ +#define LINK_PROBER_LINKPROBERHW_H_ + +#include "LinkProberBase.h" + +namespace test { +class LinkProberTest; +class LinkProberMockTest; +class LinkProberHardwareTest; +} + +namespace mux { + class MuxPort; +} + +namespace link_prober +{ + + +class LinkProberHw : public LinkProberBase +{ +public: + /** + *@method LinkProberHw + * + *@brief class default constructor + */ + LinkProberHw() = delete; + + /** + *@method LinkProberHw + * + *@brief class copy constructor + * + *@param LinkProberHw (in) reference to LinkProber object to be copied + */ + LinkProberHw(const LinkProberHw &) = delete; + + /** + *@method LinkProberHw + * + *@brief class constructor + * + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param ioService (in) reference to boost io_service object + *@param linkProberStateMachinePtr (in) reference to LinkProberStateMachineBase object + *@param muxPort pointer to MuxPortObject + */ + LinkProberHw( + common::MuxPortConfig &muxPortConfig, + boost::asio::io_service &ioService, + LinkProberStateMachineBase *linkProberStateMachinePtr, + mux::MuxPort *muxPort + ); + + /** + *@method initialize + * + *@brief handle state change notification from STATE_DB:ICMP_ECHO_SESSION_TABLE + * + * + *@return none + */ + virtual void initialize() final override; + + /** + *@method startProbing + * + *@brief triggers creation of ICMP_ECHO_SESSION_TABLE in APP_DB table to start sending/recieving of ICMP session packets + * + *@return none + */ + virtual void startProbing() final override; + + /** + *@method suspendTxProbes + * + *@brief suspend sending ICMP ECHOREQUEST packets + * + *@param suspendTime_msec suspension time in msec + * + *@return none + */ + virtual void suspendTxProbes(uint32_t suspendTime_msec) final override; + + /** + *@method shutdownTxProbes + * + *@brief stop sending ICMP ECHOREQUEST packets + * + *@return none + */ + virtual void shutdownTxProbes() final override; + + /** + * @method restartTxProbes + * + * @brief restart sending ICMP ECHOREQUEST packets + * + * @return none + */ + virtual void restartTxProbes() final override; + + /** + *@method resumeTxProbes + * + *@brief resume sending ICMP ECHOREQUEST packets + * + *@return none + */ + virtual void resumeTxProbes() final override; + + /** + * @method handleStateDbStateUpdate + * + * @brief handle state change of ICMP_ECHO_SESSION_TABLE in STATE_DB + * + * @return none + */ + virtual void handleStateDbStateUpdate(const std::string& state, const std::string torType) final override; + + /** + *@method updateEthernetFrame + * + *@brief update Ethernet frame of Tx Buffer + * + *@return none + */ + virtual void updateEthernetFrame() final override; + + /** + *@method handleIcmpPayload + * + *@brief handle Icmp packet recieved on socket + * + *@return none + */ + virtual void handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload) override; + + /** + *@method startTimer + * + *@brief start ICMP ECHOREPLY timeout timer for software peer + * + *@return none + */ + void startTimer(); + + + /** + *@method ~LinkProber + * + *@brief class destructor + */ + virtual ~LinkProberHw() = default; + +private: + + friend class test::LinkProberTest; + friend class test::LinkProberMockTest; + friend class test::LinkProberHardwareTest; + + /** + *@method handleTimeout + * + *@brief handle ICMP packet reception timeout + * + *@param errorCode (in) socket error code + * + *@return none + */ + void handleTimeout(boost::system::error_code errorCode); + + /** + *@method startPositiveProbingTimer + * + *@brief triggers handling of expiration of positive probing timer + * + *@param torType (in) session tor type + * + *@return none + */ + void startPositiveProbingTimer(std::string torType); + + /** + *@method handlePositiveProbingTimeout + * + *@brief bring Link prober state machine to active when positive probing timer expires + * + *@param torType (in) session tor type + * + *@return none + */ + void handlePositiveProbingTimeout(std::string torType); + + /** + *@method handleSuspendTimeout + * + *@brief handles suspendTimer expires start probing again + * + *@param errorCode (in) boost system error_code during suspend + * + *@return none + */ + void handleSuspendTimeout(boost::system::error_code errorCode); + + /** + *@method createIcmpEchoSession + * + *@brief intializing reciving thread and triggers creation of new icmp_echo session + * + *@param torType (in) session tor type + *@param guid (in) global uuid to be used for session + * + *@return none + */ + void createIcmpEchoSession(std::string torType, std::string guid); + + /** + *@method deleteIcmpEchoSession + * + *@brief intializing reciving thread and triggers creation of new icmp_echo session + * + *@param torType (in) session tor type + *@param guid (in) global uuid to be used for session + * + *@return none + */ + void deleteIcmpEchoSession(std::string torType, std::string guid); + + /** + *@method reportHeartbeatReplyNotReceivedActiveActive + * + *@brief first time recieve a new type guid reply packet + * + *@param heartbeatType (in) session tor type + * + *@return none + */ + void reportHeartbeatReplyNotReceivedActiveActive(HeartbeatType heartbeatType); + + /** + *@method reportHeartbeatReplyReceivedActiveActive + * + *@brief first time recieve a new type guid reply packet + * + *@param heartbeatType (in) session tor type + * + *@return none + */ + void reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType); + + /** + *@method etherMacArrayToString + * + *@brief process LinkProberState suspend timer expiry event + * + *@param macAddress (in) reference to macAdress in ether array format + * + *@return helper to convert ether array structure of mac address to string format + */ + std::string etherMacArrayToString(const std::array& macAddress); + + mux::MuxPort *mMuxPortPtr; + boost::asio::deadline_timer mSuspendTimer; + boost::asio::deadline_timer mPositiveProbingTimer; + boost::asio::deadline_timer mPositiveProbingPeerTimer; + + static std::string mIcmpTableName; + static std::string mSessionCookie; + static std::string mDefaultVrfName; + static std::string mSessionTypeSelf; + static std::string mSessionTypePeer; + static std::string mKeySeparator; + static std::string mUpState; + static std::string mDownState; +}; +} + +#endif diff --git a/src/link_prober/LinkProberState.cpp b/src/link_prober/LinkProberState.cpp index 6c9cd92..94594ac 100644 --- a/src/link_prober/LinkProberState.cpp +++ b/src/link_prober/LinkProberState.cpp @@ -63,6 +63,12 @@ LinkProberState* LinkProberState::handleEvent(IcmpUnknownEvent &event) return nullptr; } +LinkProberState* LinkProberState::handleEvent(IcmpWaitEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + LinkProberState* LinkProberState::handleEvent(IcmpPeerActiveEvent &event) { MUXLOGERROR(getMuxPortConfig().getPortName()); @@ -75,4 +81,52 @@ LinkProberState* LinkProberState::handleEvent(IcmpPeerUnknownEvent &event) return nullptr; } +LinkProberState* LinkProberState::handleEvent(IcmpPeerWaitEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwPeerEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwSelfEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwUnknownEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwWaitEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwPeerActiveEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwPeerUnknownEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + +LinkProberState* LinkProberState::handleEvent(IcmpHwPeerWaitEvent &event) +{ + MUXLOGERROR(getMuxPortConfig().getPortName()); + return nullptr; +} + } /* namespace link_prober */ diff --git a/src/link_prober/LinkProberState.h b/src/link_prober/LinkProberState.h index 8c87d2c..f2ea12d 100644 --- a/src/link_prober/LinkProberState.h +++ b/src/link_prober/LinkProberState.h @@ -31,10 +31,20 @@ namespace link_prober class LinkProberStateMachineBase; class IcmpPeerEvent; class IcmpSelfEvent; +class IcmpWaitEvent; class IcmpUnknownEvent; class SuspendTimerExpiredEvent; class IcmpPeerActiveEvent; class IcmpPeerUnknownEvent; +class IcmpPeerWaitEvent; + +class IcmpHwPeerEvent; +class IcmpHwSelfEvent; +class IcmpHwWaitEvent; +class IcmpHwUnknownEvent; +class IcmpHwPeerActiveEvent; +class IcmpHwPeerUnknownEvent; +class IcmpHwPeerWaitEvent; /** *@class LinkProberState @@ -119,6 +129,17 @@ class LinkProberState: public common::State */ virtual LinkProberState* handleEvent(IcmpSelfEvent &event); + /** + *@method handleEvent + * + *@brief handle IcmpWaitEvent from LinkProber + * + *@param event (in) reference to IcmpWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpWaitEvent &event); + /** *@method handleEvent * @@ -152,6 +173,95 @@ class LinkProberState: public common::State */ virtual LinkProberState* handleEvent(IcmpPeerUnknownEvent &event); + /** + *@method handleEvent + * + *@brief handle IcmpPeerWaitEvent from LinkProber + * + *@param event (in) reference to IcmpPeerWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerWaitEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwSelfEvent from LinkProber + * + *@param event (in) reference to IcmpHwSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwSelfEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwWaitEvent from LinkProber + * + *@param event (in) reference to IcmpHwWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwWaitEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwUnknownEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerActiveEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerActiveEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerActiveEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerUnknownEvent &event); + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerWaitEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerWaitEvent &event); + + /** *@method getStateLabel * diff --git a/src/link_prober/LinkProberStateMachineActiveActive.cpp b/src/link_prober/LinkProberStateMachineActiveActive.cpp index 1958b50..6cf0b65 100644 --- a/src/link_prober/LinkProberStateMachineActiveActive.cpp +++ b/src/link_prober/LinkProberStateMachineActiveActive.cpp @@ -28,6 +28,7 @@ namespace link_prober // link_manager::LinkManagerStateMachineBase &linkManagerStateMachinePtr, // boost::asio::io_service::strand &strand, // common::MuxPortConfig &muxPortConfig, +// bool proberTypeHardware, // LinkProberState::Label label // ); // @@ -193,6 +194,55 @@ void LinkProberStateMachineActiveActive::processEvent(MuxProbeRequestEvent &muxP )); } +// +// ---> processEvent(IcmpHwPeerActiveEvent &icmpHwPeerActiveEvent); +// +// process IcmpHwPeerActiveEvent +// +void LinkProberStateMachineActiveActive::processEvent(IcmpHwPeerActiveEvent &icmpHwPeerActiveEvent) +{ + LinkProberState *currentPeerState = getCurrentPeerState(); + LinkProberState *nextPeerState = currentPeerState->handleEvent(icmpHwPeerActiveEvent); + if (nextPeerState == nullptr) { + MUXLOGERROR( + boost::format( + "%s: link prober state %d could not handle event" + ) % + mMuxPortConfig.getPortName() % + currentPeerState->getStateLabel() + ); + } else { + if (nextPeerState != currentPeerState) { + postLinkManagerPeerEvent(nextPeerState); + } + setCurrentPeerState(nextPeerState); + } +} + +// +// ---> processEvent(IcmpHwPeerUnknownEvent &icmpHwPeerUnknownEvent); +// +// process IcmpHwPeerUnknownEvent +// +void LinkProberStateMachineActiveActive::processEvent(IcmpHwPeerUnknownEvent &IcmpHwPeerUnknownEvent) +{ + LinkProberState *currentPeerState = getCurrentPeerState(); + LinkProberState *nextPeerState = currentPeerState->handleEvent(IcmpHwPeerUnknownEvent); + if (nextPeerState == nullptr) { + MUXLOGERROR( + boost::format( + "%s: link prober state %d could not handle event" + ) % + mMuxPortConfig.getPortName() % + currentPeerState->getStateLabel() + ); + } else { + if (nextPeerState != currentPeerState) { + postLinkManagerPeerEvent(nextPeerState); + } + setCurrentPeerState(nextPeerState); + } +} // // ---> postLinkManagerPeerEvent(LinkProberState* linkProberState); diff --git a/src/link_prober/LinkProberStateMachineActiveActive.h b/src/link_prober/LinkProberStateMachineActiveActive.h index b21ac6d..66620af 100644 --- a/src/link_prober/LinkProberStateMachineActiveActive.h +++ b/src/link_prober/LinkProberStateMachineActiveActive.h @@ -21,7 +21,7 @@ namespace link_manager { -class LinkManagerStateMachineBase; + class LinkManagerStateMachineBase; } namespace link_prober @@ -58,6 +58,7 @@ class LinkProberStateMachineActiveActive : public LinkProberStateMachineBase *@param linkManagerStateMachinePtr (in) pointer to LinkManagerStateMachineBase *@param strand (in) reference to boost serialization object *@param muxPortConfig (in) reference to MuxPortConfig object + *@param proberTypeHardware (in) if Link prober is running in hardware mode *@param label (in) state machine initial state */ LinkProberStateMachineActiveActive( @@ -107,6 +108,29 @@ class LinkProberStateMachineActiveActive : public LinkProberStateMachineBase */ void processEvent(IcmpPeerUnknownEvent &icmpPeerUnknownEvent) override; + /** + *@method processEvent + * + *@brief process IcmpHwPeerActiveEvent + * + *@param icmpPeerHwActiveEvent (in) reference to the IcmpHwPeerActiveEvent event + * + *@return none + */ + void processEvent(IcmpHwPeerActiveEvent &icmpHwPeerActiveEvent) override; + + /** + *@method processEvent + * + *@brief process IcmpHwPeerUnknownEvent + * + *@param icmpHwPeerUnknownEvent (in) reference to the IcmpHwPeerUnknownEvent event + * + *@return none + */ + void processEvent(IcmpHwPeerUnknownEvent &icmpHwPeerUnknownEvent) override; + + /** *@method processEvent * diff --git a/src/link_prober/LinkProberStateMachineActiveStandby.h b/src/link_prober/LinkProberStateMachineActiveStandby.h index 2fa1976..cdbc92e 100644 --- a/src/link_prober/LinkProberStateMachineActiveStandby.h +++ b/src/link_prober/LinkProberStateMachineActiveStandby.h @@ -21,8 +21,8 @@ * Author: tamer */ -#ifndef LINK_PROBER_LINKPROBERSTATEMACHINE_H_ -#define LINK_PROBER_LINKPROBERSTATEMACHINE_H_ +#ifndef LINK_PROBER_LINKPROBERSTATEMACHINEACTIVESTANDBY_H_ +#define LINK_PROBER_LINKPROBERSTATEMACHINEACTIVESTANDBY_H_ #include "link_prober/LinkProberStateMachineBase.h" diff --git a/src/link_prober/LinkProberStateMachineBase.cpp b/src/link_prober/LinkProberStateMachineBase.cpp index ea420aa..ea44cde 100644 --- a/src/link_prober/LinkProberStateMachineBase.cpp +++ b/src/link_prober/LinkProberStateMachineBase.cpp @@ -30,12 +30,22 @@ namespace link_prober IcmpSelfEvent LinkProberStateMachineBase::mIcmpSelfEvent; IcmpPeerEvent LinkProberStateMachineBase::mIcmpPeerEvent; IcmpUnknownEvent LinkProberStateMachineBase::mIcmpUnknownEvent; +IcmpHwSelfEvent LinkProberStateMachineBase::mIcmpHwSelfEvent; +IcmpHwPeerEvent LinkProberStateMachineBase::mIcmpHwPeerEvent; +IcmpHwUnknownEvent LinkProberStateMachineBase::mIcmpHwUnknownEvent; SuspendTimerExpiredEvent LinkProberStateMachineBase::mSuspendTimerExpiredEvent; SwitchActiveCommandCompleteEvent LinkProberStateMachineBase::mSwitchActiveCommandCompleteEvent; SwitchActiveRequestEvent LinkProberStateMachineBase::mSwitchActiveRequestEvent; MuxProbeRequestEvent LinkProberStateMachineBase::mMuxProbeRequestEvent; IcmpPeerActiveEvent LinkProberStateMachineBase::mIcmpPeerActiveEvent; IcmpPeerUnknownEvent LinkProberStateMachineBase::mIcmpPeerUnknownEvent; +IcmpWaitEvent LinkProberStateMachineBase::mIcmpWaitEvent; +IcmpPeerWaitEvent LinkProberStateMachineBase::mIcmpPeerWaitEvent; +IcmpHwPeerActiveEvent LinkProberStateMachineBase::mIcmpHwPeerActiveEvent; +IcmpHwPeerUnknownEvent LinkProberStateMachineBase::mIcmpHwPeerUnknownEvent; +IcmpHwWaitEvent LinkProberStateMachineBase::mIcmpHwWaitEvent; +IcmpHwPeerWaitEvent LinkProberStateMachineBase::mIcmpHwPeerWaitEvent; + LinkProberStateMachineBase::LinkProberStateMachineBase( link_manager::LinkManagerStateMachineBase *linkManagerStateMachinePtr, @@ -122,6 +132,54 @@ void LinkProberStateMachineBase::postLinkProberStateEvent(I template void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpPeerUnknownEvent &event); +// +// ---> LinkProberStateMachineBase::postLinkProberStateEvent(IcmpWaitEvent &e); +// +// post LinkProberState IcmpWaitEvent to the state machine +// +template +void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpWaitEvent &event); + +// +// ---> LinkProberStateMachineBase::postLinkProberStateEvent(IcmpPeerWaitEvent &e); +// +// post LinkProberState IcmpPeerWaitEvent to the state machine +// +template +void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpPeerWaitEvent &event); + +// +// ---> LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwSelfEvent &e); +// +// post LinkProberState IcmpHwSelfEvent to the state machine +// +template +void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwSelfEvent &event); + +// +// ---> LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwPeerActiveEvent &e); +// +// post LinkProberState IcmpHwPeerActiveEvent to the state machine +// +template +void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwPeerActiveEvent &event); + +// +// ---> LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwUnknownEvent &e); +// +// post LinkProberState IcmpHwUnknownEvent to the state machine +// +template +void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwUnknownEvent &event); + +// +// ---> LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwPeerUnknownEvent &e); +// +// post LinkProberState IcmpHwPeerUnknownEvent to the state machine +// +template +void LinkProberStateMachineBase::postLinkProberStateEvent(IcmpHwPeerUnknownEvent &event); + // // ---> LinkProberStateMachineBase::processEvent(T &t); // @@ -172,6 +230,43 @@ void LinkProberStateMachineBase::processEvent(IcmpPeerEvent &eve template void LinkProberStateMachineBase::processEvent(IcmpUnknownEvent &event); +template +void LinkProberStateMachineBase::processEvent(IcmpWaitEvent &event); + +template +void LinkProberStateMachineBase::processEvent(IcmpPeerWaitEvent &event); + +// +// ---> LinkProberStateMachineBase::processEvent(IcmpHwSelfEvent &t); +// +// process LinkProberState IcmpHwSelfEvent +// +template +void LinkProberStateMachineBase::processEvent(IcmpHwSelfEvent &event); + +// +// ---> LinkProberStateMachineBase::processEvent(IcmpPeerEvent &t); +// +// process LinkProberState IcmpPeerEvent +// +template +void LinkProberStateMachineBase::processEvent(IcmpHwPeerEvent &event); + +// +// ---> LinkProberStateMachineBase::processEvent(IcmpHwUnknownEvent &t); +// +// process LinkProberState IcmpHwUnknownEvent +// +template +void LinkProberStateMachineBase::processEvent(IcmpHwUnknownEvent &event); + +template +void LinkProberStateMachineBase::processEvent(IcmpHwWaitEvent &event); + +template +void LinkProberStateMachineBase::processEvent(IcmpHwPeerWaitEvent &event); + + // // ---> processEvent(SuspendTimerExpiredEvent &suspendTimerExpiredEvent); // @@ -183,9 +278,9 @@ void LinkProberStateMachineBase::processEvent(SuspendTimerExpiredEvent &suspendT } // -// ---> processEvent(IcmpPeerActiveEvent &suspendTimerExpiredEvent); +// ---> processEvent(IcmpPeerActiveEvent &icmpPeerActiveEvent); // -// process LinkProberState suspend timer expiry event +// process LinkProberState IcmpPeerActiveEvent // void LinkProberStateMachineBase::processEvent(IcmpPeerActiveEvent &icmpPeerActiveEvent) { @@ -193,15 +288,35 @@ void LinkProberStateMachineBase::processEvent(IcmpPeerActiveEvent &icmpPeerActiv } // -// ---> processEvent(IcmpPeerUnknownEvent &suspendTimerExpiredEvent); +// ---> processEvent(IcmpPeerUnknownEvent &icmpPeerUnknownEvent); // -// process LinkProberState suspend timer expiry event +// process LinkProberState IcmpPeerUnknownEvent // void LinkProberStateMachineBase::processEvent(IcmpPeerUnknownEvent &icmpPeerUnknownEvent) { MUXLOGDEBUG(mMuxPortConfig.getPortName()); } +// +// ---> processEvent(IcmpHwPeerActiveEvent &icmpHwPeerActiveEvent); +// +// process LinkProberState IcmpHwPeerActiveEvent +// +void LinkProberStateMachineBase::processEvent(IcmpHwPeerActiveEvent &icmpHwPeerActiveEvent) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); +} + +// +// ---> processEvent(IcmpHwPeerUnknownEvent &icmpHwPeerUnknownEvent); +// +// process LinkProberState IcmpPeerUnknownEvent +// +void LinkProberStateMachineBase::processEvent(IcmpHwPeerUnknownEvent &icmpHwPeerUnknownEvent) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); +} + // // ---> processEvent(SwitchActiveCommandCompleteEvent &switchActiveCommandCompleteEvent); // diff --git a/src/link_prober/LinkProberStateMachineBase.h b/src/link_prober/LinkProberStateMachineBase.h index bfe97e2..15f52aa 100644 --- a/src/link_prober/LinkProberStateMachineBase.h +++ b/src/link_prober/LinkProberStateMachineBase.h @@ -68,6 +68,30 @@ class IcmpUnknownEvent ~IcmpUnknownEvent() = default; }; +/** + *@class IcmpWaitEvent + * + *@brief signals a IcmpWaitEvent event to LinkProber state machine + */ +class IcmpWaitEvent +{ +public: + IcmpWaitEvent() = default; + ~IcmpWaitEvent() = default; +}; + +/** + *@class IcmpPeerWaitEvent + * + *@brief signals a IcmpPeerWaitEvent event to LinkProber state machine + */ +class IcmpPeerWaitEvent +{ +public: + IcmpPeerWaitEvent() = default; + ~IcmpPeerWaitEvent() = default; +}; + /** *@class IcmpPeerActiveEvent * @@ -92,6 +116,90 @@ class IcmpPeerUnknownEvent ~IcmpPeerUnknownEvent() = default; }; +/** + *@class IcmpHwSelfEvent + * + *@brief signals a IcmpHwSelfEvent event to LinkProber state machine + */ +class IcmpHwSelfEvent +{ +public: + IcmpHwSelfEvent() = default; + ~IcmpHwSelfEvent() = default; +}; + +/** + *@class IcmpHwPeerEvent + * + *@brief signals a IcmpHwPeerEvent event to LinkProber state machine + */ +class IcmpHwPeerEvent +{ +public: + IcmpHwPeerEvent() = default; + ~IcmpHwPeerEvent() = default; +}; + +/** + *@class IcmpHwUnknownEvent + * + *@brief signals a IcmpHwUnknownEvent event to LinkProber state machine + */ +class IcmpHwUnknownEvent +{ +public: + IcmpHwUnknownEvent() = default; + ~IcmpHwUnknownEvent() = default; +}; + +/** + *@class IcmpHwWaitEvent + * + *@brief signals a IcmpHwWaitEvent event to LinkProber state machine + */ +class IcmpHwWaitEvent +{ +public: + IcmpHwWaitEvent() = default; + ~IcmpHwWaitEvent() = default; +}; + +/** + *@class IcmpHwPeerWaitEvent + * + *@brief signals a IcmpHwPeerWaitEvent event to LinkProber state machine + */ +class IcmpHwPeerWaitEvent +{ +public: + IcmpHwPeerWaitEvent() = default; + ~IcmpHwPeerWaitEvent() = default; +}; + +/** + *@class IcmpHwPeerActiveEvent + * + *@brief signals a IcmpHwPeerActiveEvent event to LinkProber state machine + */ +class IcmpHwPeerActiveEvent +{ +public: + IcmpHwPeerActiveEvent() = default; + ~IcmpHwPeerActiveEvent() = default; +}; + +/** + *@class IcmpHwPeerUnknownEvent + * + *@brief signals a IcmpHwPeerUnknownEvent event to LinkProber state machine + */ +class IcmpHwPeerUnknownEvent +{ +public: + IcmpHwPeerUnknownEvent() = default; + ~IcmpHwPeerUnknownEvent() = default; +}; + /** *@class SuspendTimerExpiredEvent * @@ -247,6 +355,29 @@ class LinkProberStateMachineBase : public common::StateMachine */ virtual void processEvent(IcmpPeerUnknownEvent &icmpPeerUnknownEvent); + /** + *@method processEvent + * + *@brief process IcmpPeerActiveEvent + * + *@param icmpHwPeerActiveEvent (in) reference to the IcmpHwPeerActiveEvent event + * + *@return none + */ + virtual void processEvent(IcmpHwPeerActiveEvent &icmpHwPeerActiveEvent); + + /** + *@method processEvent + * + *@brief process IcmpHwPeerUnknownEvent + * + *@param icmpHwPeerUnknownEvent (in) reference to the IcmpHwPeerUnknownEvent event + * + *@return none + */ + virtual void processEvent(IcmpHwPeerUnknownEvent &icmpHwPeerUnknownEvent); + + /** *@method processEvent * @@ -435,6 +566,33 @@ class LinkProberStateMachineBase : public common::StateMachine */ static IcmpUnknownEvent &getIcmpUnknownEvent() { return mIcmpUnknownEvent; }; + /** + *@method getIcmpHwSelfEvent + * + *@brief getter for IcmpHwSelfEvent object + * + *@return pointer to IcmpHwSelfEvent object + */ + static IcmpHwSelfEvent &getIcmpHwSelfEvent() { return mIcmpHwSelfEvent; }; + + /** + *@method getIcmpHwPeerEvent + * + *@brief getter for IcmpHwPeerEvent object + * + *@return pointer to IcmpHwPeerEvent object + */ + static IcmpHwPeerEvent &getIcmpHwPeerEvent() { return mIcmpHwPeerEvent; }; + + /** + *@method getIcmpHwUnknownEvent + * + *@brief getter for IcmpHwUnknownEvent object + * + *@return pointer to IcmpHwUnknownEvent object + */ + static IcmpHwUnknownEvent &getIcmpHwUnknownEvent() { return mIcmpHwUnknownEvent; }; + /** *@method getSuspendTimerExpiredEvent * @@ -489,6 +647,60 @@ class LinkProberStateMachineBase : public common::StateMachine */ static IcmpPeerUnknownEvent &getIcmpPeerUnknownEvent() { return mIcmpPeerUnknownEvent; } + /** + *@method getIcmpWaitEvent + * + *@brief getter for IcmpPeerWaitEvent object + * + *@return pointer to IcmpPeerWaitEvent object + */ + static IcmpPeerWaitEvent &getIcmpPeerWaitEvent() { return mIcmpPeerWaitEvent; } + + /** + *@method getIcmpWaitEvent + * + *@brief getter for IcmpWaitEvent object + * + *@return pointer to IcmpWaitEvent object + */ + static IcmpWaitEvent &getIcmpWaitEvent() { return mIcmpWaitEvent; } + + /** + *@method getIcmpHwPeerActiveEvent + * + *@brief getter for IcmpHwPeerActiveEvent object + * + *@return pointer to IcmpHwPeerActiveEvent object + */ + static IcmpHwPeerActiveEvent &getIcmpHwPeerActiveEvent() { return mIcmpHwPeerActiveEvent; } + + /** + *@method getIcmpHwPeerUnknownEvent + * + *@brief getter for IcmpHwPeerUnknownEvent object + * + *@return pointer to IcmpHwPeerUnknownEvent object + */ + static IcmpHwPeerUnknownEvent &getIcmpHwPeerUnknownEvent() { return mIcmpHwPeerUnknownEvent; } + + /** + *@method getIcmpHwWaitEvent + * + *@brief getter for IcmpHwPeerWaitEvent object + * + *@return pointer to IcmpHwPeerWaitEvent object + */ + static IcmpHwPeerWaitEvent &getIcmpHwPeerWaitEvent() { return mIcmpHwPeerWaitEvent; } + + /** + *@method getIcmpHwWaitEvent + * + *@brief getter for IcmpHwWaitEvent object + * + *@return pointer to IcmpHwWaitEvent object + */ + static IcmpHwWaitEvent &getIcmpHwWaitEvent() { return mIcmpHwWaitEvent; } + private: /** *@method postLinkManagerEvent @@ -505,12 +717,21 @@ class LinkProberStateMachineBase : public common::StateMachine static IcmpSelfEvent mIcmpSelfEvent; static IcmpPeerEvent mIcmpPeerEvent; static IcmpUnknownEvent mIcmpUnknownEvent; + static IcmpHwSelfEvent mIcmpHwSelfEvent; + static IcmpHwPeerEvent mIcmpHwPeerEvent; + static IcmpHwUnknownEvent mIcmpHwUnknownEvent; static SuspendTimerExpiredEvent mSuspendTimerExpiredEvent; static SwitchActiveCommandCompleteEvent mSwitchActiveCommandCompleteEvent; static SwitchActiveRequestEvent mSwitchActiveRequestEvent; static MuxProbeRequestEvent mMuxProbeRequestEvent; static IcmpPeerActiveEvent mIcmpPeerActiveEvent; static IcmpPeerUnknownEvent mIcmpPeerUnknownEvent; + static IcmpWaitEvent mIcmpWaitEvent; + static IcmpPeerWaitEvent mIcmpPeerWaitEvent; + static IcmpHwPeerActiveEvent mIcmpHwPeerActiveEvent; + static IcmpHwPeerUnknownEvent mIcmpHwPeerUnknownEvent; + static IcmpHwWaitEvent mIcmpHwWaitEvent; + static IcmpHwPeerWaitEvent mIcmpHwPeerWaitEvent; private: friend class LinkProberStateMachineActiveStandby; diff --git a/src/link_prober/LinkProberSw.cpp b/src/link_prober/LinkProberSw.cpp new file mode 100644 index 0000000..1759bb9 --- /dev/null +++ b/src/link_prober/LinkProberSw.cpp @@ -0,0 +1,443 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * LinkProberSw.cpp + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#include + +#include "common/MuxLogger.h" +#include "LinkProberSw.h" +#include "common/MuxException.h" +#include "LinkProberStateMachineActiveActive.h" +#include "LinkProberStateMachineActiveStandby.h" + +namespace link_prober +{ + + +// +// ---> LinkProberSw( +// common::MuxPortConfig &muxPortConfig, +// boost::asio::io_service &ioService, +// LinkProberStateMachineBase &linkProberStateMachine +// ); +// +// class constructor +// +LinkProberSw::LinkProberSw( + common::MuxPortConfig &muxPortConfig, + boost::asio::io_service &ioService, + LinkProberStateMachineBase *linkProberStateMachinePtr +) : + LinkProberBase(muxPortConfig, ioService, linkProberStateMachinePtr) +{ + switch (mMuxPortConfig.getPortCableType()) { + case common::MuxPortConfig::PortCableType::ActiveActive: { + mReportHeartbeatReplyReceivedFuncPtr = boost::bind( + &LinkProberSw::reportHeartbeatReplyReceivedActiveActive, + this, + boost::placeholders::_1 + ); + mReportHeartbeatReplyNotReceivedFuncPtr = boost::bind( + &LinkProberSw::reportHeartbeatReplyNotReceivedActiveActive, + this, + boost::placeholders::_1 + ); + break; + } + case common::MuxPortConfig::PortCableType::ActiveStandby: { + mReportHeartbeatReplyReceivedFuncPtr = boost::bind( + &LinkProberSw::reportHeartbeatReplyReceivedActiveStandby, + this, + boost::placeholders::_1 + ); + mReportHeartbeatReplyNotReceivedFuncPtr = boost::bind( + &LinkProberSw::reportHeartbeatReplyNotReceivedActiveStandby, + this, + boost::placeholders::_1 + ); + break; + } + default: { + break; + } + } +} + +// +// ---> initialize(); +// +// initialize link prober sockets and builds ICMP packet +// +void LinkProberSw::initialize() +{ + setupSocket(); +} + +// +// ---> startProbing(); +// +// start sending ICMP ECHOREQUEST packets +// +void LinkProberSw::startProbing() +{ + mStream.cancel(); + sendHeartbeat(); + startRecv(); + startTimer(); +} + +// +// ---> suspendTxProbes(uint32_t suspendTime_msec); +// +// suspend sending ICMP ECHOREQUEST packets +// +void LinkProberSw::suspendTxProbes(uint32_t suspendTime_msec) +{ + MUXLOGWARNING(boost::format("%s: suspend ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + + mSuspendTimer.expires_from_now(boost::posix_time::milliseconds(suspendTime_msec)); + mSuspendTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberSw::handleSuspendTimeout, + this, + boost::asio::placeholders::error + ))); + + mSuspendTx = true; +} + +// +// ---> resumeTxProbes(); +// +// resume sending ICMP ECHOREQUEST packets +// +void LinkProberSw::resumeTxProbes() +{ + MUXLOGWARNING(boost::format("%s: resume ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + + mSuspendTimer.cancel(); + +} + +// +// ---> updateEthernetFrame(); +// +// update Ethernet frame of Tx Buffer +// +void LinkProberSw::updateEthernetFrame() +{ + boost::asio::io_service &ioService = mStrand.context(); + ioService.post(mStrand.wrap(boost::bind(&LinkProberSw::handleUpdateEthernetFrame, this))); +} + +// +// ---> detectLink(); +// +// send HBs to detect the link status +// +void LinkProberSw::detectLink() +{ + boost::asio::io_service &ioService = mStrand.context(); + for (uint32_t i = 0; i < mMuxPortConfig.getPositiveStateChangeRetryCount(); ++i) + { + ioService.post(mStrand.wrap(boost::bind(&LinkProberSw::sendHeartbeat, this, true))); + } +} + +// +// ---> handleTimeout(boost::system::error_code ec); +// +// handle ICMP packet reception timeout +// +void LinkProberSw::handleTimeout(boost::system::error_code errorCode) +{ + MUXLOGTRACE(boost::format("%s: server: %d, mRxSelfSeqNo: %d, mRxPeerSeqNo: %d, mTxSeqNo: %d") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getServerId() % + mRxSelfSeqNo % + mRxPeerSeqNo % + mTxSeqNo + ); + + mStream.cancel(); + mReportHeartbeatReplyNotReceivedFuncPtr(HeartbeatType::HEARTBEAT_SELF); + + mIcmpPacketCount++; + if (mIcmpPacketCount % mMuxPortConfig.getLinkProberStatUpdateIntervalCount() == 0) { + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + &LinkProberStateMachineBase::handlePckLossRatioUpdate, + mLinkProberStateMachinePtr, + mIcmpUnknownEventCount, + mIcmpPacketCount + ))); + } + // start another cycle of send/recv + startProbing(); +} + +// +// ---> handleSuspendTimeout(boost::system::error_code errorCode); +// +// handle suspend timer timeout +// +void LinkProberSw::handleSuspendTimeout(boost::system::error_code errorCode) +{ + MUXLOGWARNING(boost::format("%s: suspend timeout, resume ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + + mSuspendTx = false; + + if (errorCode == boost::system::errc::success) { + // inform the composite state machine about Suspend timer expiry + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + static_cast + (&LinkProberStateMachineBase::processEvent), + mLinkProberStateMachinePtr, + LinkProberStateMachineBase::getSuspendTimerExpiredEvent() + ))); + } +} + +// +// ---> resetIcmpPacketCounts +// +// reset Icmp packet counts, post a pck loss ratio update immediately +// +void LinkProberSw::resetIcmpPacketCounts() +{ + mIcmpUnknownEventCount = 0; + mIcmpPacketCount = 0; + + boost::asio::io_service::strand &strand = mLinkProberStateMachinePtr->getStrand(); + boost::asio::io_service &ioService = strand.context(); + ioService.post(strand.wrap(boost::bind( + &LinkProberStateMachineBase::handlePckLossRatioUpdate, + mLinkProberStateMachinePtr, + mIcmpUnknownEventCount, + mIcmpPacketCount + ))); +} + +// +// ---> shutdownTxProbes(); +// +// stop sending ICMP ECHOREQUEST packets +// +void LinkProberSw::shutdownTxProbes() +{ + MUXLOGWARNING(boost::format("%s: shutdown ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + + mShutdownTx = true; +} + +// +// ---> restartTxProbes(); +// +// first stop sending and then again resume sending ICMP ECHOREQUEST packets +// +void LinkProberSw::restartTxProbes() +{ + MUXLOGWARNING(boost::format("%s: restart ICMP heartbeat probing") % mMuxPortConfig.getPortName()); + + mShutdownTx = false; +} + +// +// ---> decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec); +// +// adjust link prober interval to 10 ms after switchover to better measure the switchover overhead. +// +void LinkProberSw::decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + + mSwitchoverTimer.expires_from_now(boost::posix_time::milliseconds(switchTime_msec)); + mSwitchoverTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberSw::handleSwitchoverTimeout, + this, + boost::asio::placeholders::error + ))); + + mDecreaseProbingInterval = true; +} + +// ---> revertProbeIntervalAfterSwitchComplete(); +// +// revert probe interval change after switchover is completed +// +void LinkProberSw::revertProbeIntervalAfterSwitchComplete() +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + + mSwitchoverTimer.cancel(); + mDecreaseProbingInterval = false; +} + +// +// ---> handleSwitchoverTimeout(boost::system::error_code errorCode) +// +// handle switchover time out +// +void LinkProberSw::handleSwitchoverTimeout(boost::system::error_code errorCode) +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + + mDecreaseProbingInterval = false; + if (errorCode == boost::system::errc::success) { + MUXLOGWARNING(boost::format("%s: link prober timeout on waiting for expected ICMP event after switchover is triggered ") % mMuxPortConfig.getPortName()); + } +} + +// +// ---> reportHeartbeatReplyReceivedActiveStandby(HeartbeatType heartbeatType) +// +// report heartbeat reply received to active-standby mode link prober state machine +// +void LinkProberSw::reportHeartbeatReplyReceivedActiveStandby(HeartbeatType heartbeatType) +{ + if (mTxSeqNo == mRxSelfSeqNo) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpSelfEvent()); + } else if (mTxSeqNo == mRxPeerSeqNo) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerEvent()); + } +} + +// +// ---> reportHeartbeatReplyNotReceivedActiveStandby +// +// report heartbeat reply received to active-active mode link prober state machine +// +void LinkProberSw::reportHeartbeatReplyNotReceivedActiveStandby(HeartbeatType heartbeatType) +{ + if (mTxSeqNo != mRxSelfSeqNo && mTxSeqNo != mRxPeerSeqNo) { + // post unknown event + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpUnknownEvent()); + mIcmpUnknownEventCount++; + } +} + +// +// ---> reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType) +// +// report heartbeat reply not received to active-standby mode link prober state machine +// +void LinkProberSw::reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType) +{ + if (heartbeatType == HeartbeatType::HEARTBEAT_SELF && mTxSeqNo == mRxSelfSeqNo) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpSelfEvent()); + } + if (heartbeatType == HeartbeatType::HEARTBEAT_PEER && mTxSeqNo == mRxPeerSeqNo) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerActiveEvent()); + } +} + +// +// ---> reportHeartbeatReplyNotReceivedActiveActive +// +// report heartbeat reply not received to active-active mode link prober state machine +// +void LinkProberSw::reportHeartbeatReplyNotReceivedActiveActive(HeartbeatType heartbeatType) +{ + if (mTxSeqNo != mRxSelfSeqNo) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpUnknownEvent()); + mIcmpUnknownEventCount++; + } + if (mTxSeqNo != mRxPeerSeqNo) { + mLinkProberStateMachinePtr->postLinkProberStateEvent(LinkProberStateMachineBase::getIcmpPeerUnknownEvent()); + } +} + +// +// ---> startTimer(); +// +// start ICMP ECHOREPLY timeout timer +// +void LinkProberSw::startTimer() +{ + MUXLOGDEBUG(mMuxPortConfig.getPortName()); + // time out these heartbeats + mDeadlineTimer.expires_from_now(boost::posix_time::milliseconds(getProbingInterval())); + mDeadlineTimer.async_wait(mStrand.wrap(boost::bind( + &LinkProberSw::handleTimeout, + this, + boost::asio::placeholders::error + ))); +} + +// +// ---> handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload); +// +// handle Icmp packet recieved on socket +// +void LinkProberSw::handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload) +{ + bool isHwCookie = ntohl(icmpPayload->cookie) == IcmpPayload::getHardwareCookie(); + + if (ntohl(icmpPayload->version) <= IcmpPayload::getVersion() && + ((ntohs(icmpHeader->un.echo.id) == mMuxPortConfig.getServerId()) || + isHwCookie)) { + // echo.id in hw prober will not be set + MUXLOGTRACE(boost::format("%s: Valid ICMP Packet from %s") % + mMuxPortConfig.getPortName() % + mMuxPortConfig.getBladeIpv4Address().to_string() + ); + std::string guidDataStr; + getGuidStr(icmpPayload, guidDataStr); + + if (guidDataStr == "0x0") { + MUXLOGWARNING(boost::format("%s: Received 0x0 GUID") % + mMuxPortConfig.getPortName()); + startRecv(); + return; + } + + bool isSelfGuid = getSelfGuidData() == guidDataStr; + HeartbeatType heartbeatType; + if (isSelfGuid) { + // echo reply for an echo request generated by this/active ToR + mRxSelfSeqNo = mTxSeqNo; + heartbeatType = HeartbeatType::HEARTBEAT_SELF; + } else { + mRxPeerSeqNo = mTxSeqNo; + heartbeatType = HeartbeatType::HEARTBEAT_PEER; + MUXLOGDEBUG(boost::format("Peer Guid Detected %s") % guidDataStr); + // add the peer guid to the set if not present + setPeerGuidData(guidDataStr); + if(isHwCookie){ + mPeerType = SessionType::HARDWARE; + } + if (mGuidSet.find(mPeerGuid) != mGuidSet.end()) + { + mGuidSet.insert(guidDataStr); + } + } + mReportHeartbeatReplyReceivedFuncPtr(heartbeatType); + handleTlvRecv(bytesTransferred, isSelfGuid); + } else { + MUXLOGWARNING(boost::format("Received invalid packet in software prober")); + } + startRecv(); +} + +} /* namespace link_prober */ diff --git a/src/link_prober/LinkProberSw.h b/src/link_prober/LinkProberSw.h new file mode 100644 index 0000000..9e31ca9 --- /dev/null +++ b/src/link_prober/LinkProberSw.h @@ -0,0 +1,294 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * LinkProberSw.h + * + * Created on: Oct 4, 2020 + * Author: tamer + */ + +#ifndef LINK_PROBER_LINKPROBERSW_H_ +#define LINK_PROBER_LINKPROBERSW_H_ + +#include "LinkProberBase.h" + +namespace test { +class LinkProberTest; +class LinkProberMockTest; +} + +namespace link_prober +{ +/** + *@class LinkProberSw + * + *@brief probes the server sing ICMP ECHPREQUEST packet. The packet payload + * holds GUID that identifies this ToR. Reception of this ToR's GUID + * indicate that the link is in Active state. Reception of unknown + * GUID will indicate standby state. Lack of ICMP packets will signal + * that the link state is unknown. + */ +class LinkProberSw : public LinkProberBase +{ +public: + /** + *@method LinkProberSw + * + *@brief class default constructor + */ + LinkProberSw() = delete; + + /** + *@method LinkProberSw + * + *@brief class copy constructor + * + *@param LinkProberSw (in) reference to LinkProberSw object to be copied + */ + LinkProberSw(const LinkProberSw &) = delete; + + /** + *@method LinkProberSw + * + *@brief class constructor + * + *@param muxPortConfig (in) reference to MuxPortConfig object + *@param ioService (in) reference to boost io_service object + *@param linkProberStateMachinePtr (in) reference to LinkProberStateMachineBase object + */ + LinkProberSw( + common::MuxPortConfig &muxPortConfig, + boost::asio::io_service &ioService, + LinkProberStateMachineBase *linkProberStateMachinePtr + ); + + /** + *@method ~LinkProberSw + * + *@brief class destructor + */ + virtual ~LinkProberSw() = default; + + +private: + + /** + *@method handleTimeout + * + *@brief handle ICMP packet reception timeout + * + *@param errorCode (in) socket error code + * + *@return none + */ + void handleTimeout(boost::system::error_code errorCode); + + /** + *@method handleSuspendTimeout + * + *@brief handle suspend timer timeout + * + *@param errorCode (in) socket error code + * + *@return none + */ + void handleSuspendTimeout(boost::system::error_code errorCode); + + /** + *@method startTimer + * + *@brief start ICMP ECHOREPLY timeout timer + * + *@return none + */ + void startTimer(); + + /** + * @method handleSwitchoverTimeout + * + * @brief handle switchover time out + * + * @param errorCode (in) socket error code + * + * @return none + */ + void handleSwitchoverTimeout(boost::system::error_code errorCode); + + friend class test::LinkProberTest; + friend class test::LinkProberMockTest; + +public: + + /** + *@method startProbing + * + *@brief start probing server/blade using ICMP ECHOREQUEST + * + *@return none + */ + virtual void startProbing() override; + + /** + *@method initialize + * + *@brief initialize link prober sockets and builds ICMP packet + * + *@return none + */ + virtual void initialize() override; + + /** + *@method suspendTxProbes + * + *@brief suspend sending ICMP ECHOREQUEST packets + * + *@param suspendTime_msec suspension time in msec + * + *@return none + */ + virtual void suspendTxProbes(uint32_t suspendTime_msec) override; + + /** + *@method resumeTxProbes + * + *@brief resume sending ICMP ECHOREQUEST packets + * + *@return none + */ + virtual void resumeTxProbes() override; + + /** + *@method updateEthernetFrame + * + *@brief update Ethernet frame of Tx Buffer + * + *@return none + */ + virtual void updateEthernetFrame() override; + + /** + *@method detectLink + * + *@brief detect link status + * + *@return none + */ + virtual void detectLink() override; + + /** + * @method resetIcmpPacketCounts() + * + * @brief reset Icmp packet counts, post a pck loss ratio update immediately + * + * @return none + */ + virtual void resetIcmpPacketCounts() override; + + /** + *@method shutdownTxProbes + * + *@brief stop sending ICMP ECHOREQUEST packets + * + *@return none + */ + virtual void shutdownTxProbes() override; + + /** + * @method restartTxProbes + * + * @brief restart sending ICMP ECHOREQUEST packets + * + * @return none + */ + virtual void restartTxProbes() override; + + /** + * @method decreaseProbeIntervalAfterSwitch + * + * @brief adjust link prober interval to 10 ms after switchover to better measure the switchover overhead. + * + * @param switchTime_msec (in) switchover is expected to complete within this time window + * @param expectingLinkProberEvent (in) depends on which state LinkManager is switching to, link prober expects self or peer events + * + * @return none + */ + virtual void decreaseProbeIntervalAfterSwitch(uint32_t switchTime_msec) override; + + /** + * @method revertProbeIntervalAfterSwitchComplete + * + * @brief revert probe interval change after switchover is completed + * + * @return none + */ + virtual void revertProbeIntervalAfterSwitchComplete() override; + + /** + * @method reportHeartbeatReplyReceivedActiveStandby + * + * @brief report heartbeat reply received to active-standby mode link prober state machine + * + * @return none + */ + void reportHeartbeatReplyReceivedActiveStandby(HeartbeatType heartbeatType); + + /** + * @method reportHeartbeatReplyNotReceivedActiveStandby + * + * @brief report heartbeat reply not received to active-standby mode link prober state machine + * + * @param heartbeatType (in) received heartbeat type + * + * @return none + */ + void reportHeartbeatReplyNotReceivedActiveStandby(HeartbeatType heartbeatType); + + /** + * @method reportHeartbeatReplyReceivedActiveActive + * + * @brief report heartbeat reply received to active-active mode link prober state machine + * + * @param heartbeatType (in) received heartbeat type + * + * @return none + */ + void reportHeartbeatReplyReceivedActiveActive(HeartbeatType heartbeatType); + + /** + * @method reportHeartbeatReplyNotReceivedActiveActive + * + * @brief report heartbeat reply not received to active-active mode link prober state machine + * + * @return none + */ + void reportHeartbeatReplyNotReceivedActiveActive(HeartbeatType heartbeatType); + + /** + *@method handleIcmpPayload + * + *@brief handle Icmp packet recieved on socket + * + *@return none + */ + virtual void handleIcmpPayload(size_t bytesTransferred, icmphdr *icmpHeader, IcmpPayload *icmpPayload) override; + +private: +}; + +} /* namespace link_prober */ + +#endif /* LINKPROBER_H_ */ diff --git a/src/link_prober/PeerActiveState.cpp b/src/link_prober/PeerActiveState.cpp index 0f38ff0..0a62ec5 100644 --- a/src/link_prober/PeerActiveState.cpp +++ b/src/link_prober/PeerActiveState.cpp @@ -73,6 +73,73 @@ LinkProberState* PeerActiveState::handleEvent(IcmpPeerUnknownEvent &event) return nextState; } +// +// ---> handleEvent(IcmpPeerWaitEvent &event); +// +// handle IcmpPeerWaitEvent from LinkProber +// +LinkProberState* PeerActiveState::handleEvent(IcmpPeerWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getPeerWaitState()); + return nextState; + +} + +// +// ---> handleEvent(IcmpHwPeerActiveEvent &event); +// +// handle IcmpHwPeerActiveEvent from LinkProber +// +LinkProberState* PeerActiveState::handleEvent(IcmpHwPeerActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getPeerActiveState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwPeerUnknownEvent &event); +// +// handle IcmpHwPeerUnknownEvent from LinkProber +// +LinkProberState* PeerActiveState::handleEvent(IcmpHwPeerUnknownEvent &event) +{ + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + // detection timer in hardware prober takes into account retry count + // and will move to Unknown state directly + nextState = dynamic_cast (stateMachine->getPeerUnknownState()); + return nextState; +} + +// +// ---> handleEvent(IcmpHwPeerWaitEvent &event); +// +// handle IcmpHwPeerWaitEvent from LinkProber +// +LinkProberState* PeerActiveState::handleEvent(IcmpHwPeerWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getPeerWaitState()); + return nextState; + +} + // // ---> resetState(); // diff --git a/src/link_prober/PeerActiveState.h b/src/link_prober/PeerActiveState.h index 46ebed7..2edbfa1 100644 --- a/src/link_prober/PeerActiveState.h +++ b/src/link_prober/PeerActiveState.h @@ -88,6 +88,51 @@ class PeerActiveState : public LinkProberState { */ virtual LinkProberState *handleEvent(IcmpPeerUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpPeerWaitEvent from LinkProber + * + *@param event (in) reference to IcmpPeerWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerWaitEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerActiveEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerActiveEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpHwPeerActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpHwPeerUnknownEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerWaitEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerWaitEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/PeerUnknownState.cpp b/src/link_prober/PeerUnknownState.cpp index 9b18d11..5028bb6 100644 --- a/src/link_prober/PeerUnknownState.cpp +++ b/src/link_prober/PeerUnknownState.cpp @@ -76,6 +76,76 @@ LinkProberState* PeerUnknownState::handleEvent(IcmpPeerUnknownEvent &event) return nextState; } +// +// ---> handleEvent(IcmpPeerWaitEvent &event); +// +// handle IcmpPeerWaitEvent from LinkProber +// +LinkProberState* PeerUnknownState::handleEvent(IcmpPeerWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getPeerWaitState()); + return nextState; + +} + +// +// ---> handleEvent(IcmpHwPeerActiveEvent &event); +// +// handle IcmpHwPeerActiveEvent from LinkProber +// +LinkProberState* PeerUnknownState::handleEvent(IcmpHwPeerActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = + dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getPeerActiveState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwPeerUnknownEvent &event); +// +// handle IcmpHwPeerUnknownEvent from LinkProber +// +LinkProberState* PeerUnknownState::handleEvent(IcmpHwPeerUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getPeerUnknownState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwPeerWaitEvent &event); +// +// handle IcmpHwPeerWaitEvent from LinkProber +// +LinkProberState* PeerUnknownState::handleEvent(IcmpHwPeerWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getPeerWaitState()); + return nextState; + +} + + // // ---> resetState(); // diff --git a/src/link_prober/PeerUnknownState.h b/src/link_prober/PeerUnknownState.h index 01d26fb..919ebc6 100644 --- a/src/link_prober/PeerUnknownState.h +++ b/src/link_prober/PeerUnknownState.h @@ -90,6 +90,51 @@ class PeerUnknownState : public LinkProberState { */ virtual LinkProberState *handleEvent(IcmpPeerUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpPeerWaitEvent from LinkProber + * + *@param event (in) reference to IcmpPeerWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpPeerWaitEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerActiveEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerActiveEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpHwPeerActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpHwPeerUnknownEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerWaitEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerWaitEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/PeerWaitState.cpp b/src/link_prober/PeerWaitState.cpp index 8a07621..bc505d3 100644 --- a/src/link_prober/PeerWaitState.cpp +++ b/src/link_prober/PeerWaitState.cpp @@ -81,6 +81,44 @@ LinkProberState *PeerWaitState::handleEvent(IcmpPeerUnknownEvent &event) return nextState; } +// +// ---> handleEvent(IcmpHwPeerActiveEvent &event); +// +// handle IcmpPeerActiveEvent from LinkProber +// +LinkProberState *PeerWaitState::handleEvent(IcmpHwPeerActiveEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast(getStateMachine()); + LinkProberState *nextState; + + // moving to Active state directly here, + // only after positive probing timer expiry + mPeerUnknownEvent = 0; + nextState = dynamic_cast(stateMachine->getPeerActiveState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwPeerUnknownEvent &event); +// +// handle IcmpHwPeerUnknownEvent from LinkProber +// +LinkProberState *PeerWaitState::handleEvent(IcmpHwPeerUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast(getStateMachine()); + LinkProberState *nextState; + + mPeerActiveEvent = 0; + nextState = dynamic_cast(stateMachine->getPeerUnknownState()); + + return nextState; +} + // // ---> resetState(); // diff --git a/src/link_prober/PeerWaitState.h b/src/link_prober/PeerWaitState.h index 0ac38f6..64dfe3b 100644 --- a/src/link_prober/PeerWaitState.h +++ b/src/link_prober/PeerWaitState.h @@ -91,6 +91,29 @@ class PeerWaitState : public LinkProberState */ virtual LinkProberState *handleEvent(IcmpPeerUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerActiveEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerActiveEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpHwPeerActiveEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState *handleEvent(IcmpHwPeerUnknownEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/StandbyState.cpp b/src/link_prober/StandbyState.cpp index 7561bda..a1a1295 100644 --- a/src/link_prober/StandbyState.cpp +++ b/src/link_prober/StandbyState.cpp @@ -108,6 +108,60 @@ LinkProberState* StandbyState::handleEvent(IcmpUnknownEvent &event) return nextState; } +// +// ---> handleEvent(IcmpHwPeerEvent &event); +// +// handle IcmpHwPeerEvent from LinkProber +// +LinkProberState* StandbyState::handleEvent(IcmpHwPeerEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = + dynamic_cast (stateMachine->getStandbyState()); + + resetState(); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwSelfEvent &event); +// +// handle IcmpHwSelfEvent from LinkProber +// +LinkProberState* StandbyState::handleEvent(IcmpHwSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mUnknownEventCount = 0; + nextState = dynamic_cast (stateMachine->getActiveState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwUnknownEvent &event); +// +// handle IcmpHwUnknownEvent from LinkProber +// +LinkProberState* StandbyState::handleEvent(IcmpHwUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mSelfEventCount = 0; + nextState = dynamic_cast (stateMachine->getUnknownState()); + + return nextState; +} + // // ---> resetState(); // diff --git a/src/link_prober/StandbyState.h b/src/link_prober/StandbyState.h index 8e536d6..ec1ab2f 100644 --- a/src/link_prober/StandbyState.h +++ b/src/link_prober/StandbyState.h @@ -107,6 +107,40 @@ class StandbyState : public LinkProberState */ virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwSelfEvent from LinkProber + * + *@param event (in) reference to IcmpHwSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwUnknownEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/UnknownState.cpp b/src/link_prober/UnknownState.cpp index 536ab21..c79ba75 100644 --- a/src/link_prober/UnknownState.cpp +++ b/src/link_prober/UnknownState.cpp @@ -109,6 +109,86 @@ LinkProberState* UnknownState::handleEvent(IcmpUnknownEvent &event) return nextState; } +LinkProberState* UnknownState::handleEvent(IcmpWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getWaitState()); + return nextState; + +} + +// +// ---> handleEvent(IcmpHwPeerEvent &event); +// +// handle IcmpHwPeerEvent from LinkProber +// +LinkProberState* UnknownState::handleEvent(IcmpHwPeerEvent &event) +{ + // used for active-standby state machine + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = + dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mSelfEventCount = 0; + nextState = dynamic_cast (stateMachine->getStandbyState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwSelfEvent &event); +// +// handle IcmpSelfEvent from LinkProber +// +LinkProberState* UnknownState::handleEvent(IcmpHwSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = + dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mPeerEventCount = 0; + nextState = dynamic_cast (stateMachine->getActiveState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwUnknownEvent &event); +// +// handle IcmpHwUnknownEvent from LinkProber +// +LinkProberState* UnknownState::handleEvent(IcmpHwUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getUnknownState()); + + resetState(); + + return nextState; +} + +LinkProberState* UnknownState::handleEvent(IcmpHwWaitEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + nextState = dynamic_cast (stateMachine->getWaitState()); + return nextState; + +} + // // ---> resetState(); // diff --git a/src/link_prober/UnknownState.h b/src/link_prober/UnknownState.h index 189227f..e87db7b 100644 --- a/src/link_prober/UnknownState.h +++ b/src/link_prober/UnknownState.h @@ -109,6 +109,62 @@ class UnknownState : public LinkProberState */ virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpWaitEvent from LinkProber + * + *@param event (in) reference to IcmpWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpWaitEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwSelfEvent from LinkProber + * + *@param event (in) reference to IcmpHwSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwUnknownEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwWaitEvent from LinkProber + * + *@param event (in) reference to IcmpHwWaitEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwWaitEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/WaitState.cpp b/src/link_prober/WaitState.cpp index 3e66339..da744a9 100644 --- a/src/link_prober/WaitState.cpp +++ b/src/link_prober/WaitState.cpp @@ -107,6 +107,10 @@ LinkProberState* WaitState::handleEvent(IcmpUnknownEvent &event) switch (portCableType) { case common::MuxPortConfig::PortCableType::ActiveActive: + if(getMuxPortConfig().getLinkProberType()){ + nextState = dynamic_cast (stateMachine->getUnknownState()); + return nextState; + } if (++mUnknownEventCount >= getMuxPortConfig().getNegativeStateChangeRetryCount()) { nextState = dynamic_cast (stateMachine->getUnknownState()); } @@ -121,6 +125,71 @@ LinkProberState* WaitState::handleEvent(IcmpUnknownEvent &event) return nextState; } +// +// ---> handleEvent(IcmpHwPeerEvent &event); +// +// handle IcmpHwPeerEvent from LinkProber +// +LinkProberState* WaitState::handleEvent(IcmpHwPeerEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mSelfEventCount = 0; + mUnknownEventCount = 0; + nextState = dynamic_cast (stateMachine->getStandbyState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwSelfEvent &event); +// +// handle IcmpHwSelfEvent from LinkProber +// +LinkProberState* WaitState::handleEvent(IcmpHwSelfEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState; + + mPeerEventCount = 0; + mUnknownEventCount = 0; + nextState = dynamic_cast (stateMachine->getActiveState()); + + return nextState; +} + +// +// ---> handleEvent(IcmpHwUnknownEvent &event); +// +// handle IcmpHwUnknownEvent from LinkProber +// +LinkProberState* WaitState::handleEvent(IcmpHwUnknownEvent &event) +{ + MUXLOGDEBUG(getMuxPortConfig().getPortName()); + + LinkProberStateMachineBase *stateMachine = dynamic_cast (getStateMachine()); + LinkProberState *nextState = dynamic_cast (stateMachine->getWaitState()); + common::MuxPortConfig::PortCableType portCableType = stateMachine->getMuxPortConfig().getPortCableType(); + + switch (portCableType) { + case common::MuxPortConfig::PortCableType::ActiveActive: + nextState = dynamic_cast (stateMachine->getUnknownState()); + break; + case common::MuxPortConfig::PortCableType::ActiveStandby: + resetState(); + break; + default: + break; + } + + return nextState; +} + // // ---> resetState(); // diff --git a/src/link_prober/WaitState.h b/src/link_prober/WaitState.h index 8cce5d9..6b2ff43 100644 --- a/src/link_prober/WaitState.h +++ b/src/link_prober/WaitState.h @@ -109,6 +109,40 @@ class WaitState : public LinkProberState */ virtual LinkProberState* handleEvent(IcmpUnknownEvent &event) override; + /** + *@method handleEvent + * + *@brief handle IcmpHwPeerEvent from LinkProber + * + *@param event (in) reference to IcmpHwPeerEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwPeerEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwSelfEvent from LinkProber + * + *@param event (in) reference to IcmpHwSelfEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwSelfEvent &event) override; + + /** + *@method handleEvent + * + *@brief handle IcmpHwUnknownEvent from LinkProber + * + *@param event (in) reference to IcmpHwUnknownEvent + * + *@return pointer to next LinkProberState + */ + virtual LinkProberState* handleEvent(IcmpHwUnknownEvent &event) override; + + /** *@method resetState * diff --git a/src/link_prober/subdir.mk b/src/link_prober/subdir.mk index 2c65a91..7a93932 100644 --- a/src/link_prober/subdir.mk +++ b/src/link_prober/subdir.mk @@ -5,7 +5,9 @@ CPP_SRCS += \ ./src/link_prober/PeerUnknownState.cpp \ ./src/link_prober/PeerWaitState.cpp \ ./src/link_prober/IcmpPayload.cpp \ - ./src/link_prober/LinkProber.cpp \ + ./src/link_prober/LinkProberBase.cpp \ + ./src/link_prober/LinkProberHw.cpp \ + ./src/link_prober/LinkProberSw.cpp \ ./src/link_prober/LinkProberState.cpp \ ./src/link_prober/LinkProberStateMachineBase.cpp \ ./src/link_prober/LinkProberStateMachineActiveStandby.cpp \ @@ -20,7 +22,9 @@ OBJS += \ ./src/link_prober/PeerUnknownState.o \ ./src/link_prober/PeerWaitState.o \ ./src/link_prober/IcmpPayload.o \ - ./src/link_prober/LinkProber.o \ + ./src/link_prober/LinkProberBase.o \ + ./src/link_prober/LinkProberHw.o \ + ./src/link_prober/LinkProberSw.o \ ./src/link_prober/LinkProberState.o \ ./src/link_prober/LinkProberStateMachineBase.o \ ./src/link_prober/LinkProberStateMachineActiveStandby.o \ diff --git a/test/FakeDbInterface.cpp b/test/FakeDbInterface.cpp index 311b98e..79e1100 100644 --- a/test/FakeDbInterface.cpp +++ b/test/FakeDbInterface.cpp @@ -67,6 +67,16 @@ void FakeDbInterface::handleProbeForwardingState(const std::string portName) mProbeForwardingStateInvokeCount++; } +void FakeDbInterface::updateIntervalv4(uint32_t tx_interval, uint32_t rx_interval) +{ + mUpdateIntervalV4Count++; +} + +void FakeDbInterface::updateIntervalv6(uint32_t tx_interval, uint32_t rx_interval) +{ + mUpdateIntervalV6Count++; +} + void FakeDbInterface::setMuxLinkmgrState(const std::string &portName, link_manager::LinkManagerStateMachineBase::Label label) { mLastSetMuxLinkmgrState = label; @@ -141,4 +151,16 @@ std::map FakeDbInterface::getMuxModeConfig() return muxModeConfig; } +void FakeDbInterface::handleSwssNotification(){ + return; +} + +void FakeDbInterface::createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries) { + mIcmpSessionsCount++; +} + +void FakeDbInterface::deleteIcmpEchoSession(std::string key) { + mIcmpSessionsCount--; +} + } /* namespace test */ diff --git a/test/FakeDbInterface.h b/test/FakeDbInterface.h index 99bec86..c245569 100644 --- a/test/FakeDbInterface.h +++ b/test/FakeDbInterface.h @@ -26,6 +26,9 @@ #include "DbInterface.h" +using IcmpHwOffloadEntries = std::vector>; +using IcmpHwOffloadEntriesPtr = std::unique_ptr; + namespace test { @@ -42,6 +45,11 @@ class FakeDbInterface: public mux::DbInterface virtual std::map getMuxModeConfig() override; virtual void probeMuxState(const std::string &portName) override; virtual void handleProbeForwardingState(const std::string portName) override; + virtual void updateIntervalv4(uint32_t tx_interval, uint32_t rx_interval) override; + virtual void updateIntervalv6(uint32_t tx_interval, uint32_t rx_interval) override; + virtual void createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries) override; + virtual void deleteIcmpEchoSession(std::string key) override; + virtual void handleSwssNotification() override; virtual void setMuxLinkmgrState( const std::string &portName, link_manager::LinkManagerStateMachineBase::Label label @@ -88,6 +96,8 @@ class FakeDbInterface: public mux::DbInterface uint32_t mGetMuxStateInvokeCount = 0; uint32_t mProbeMuxStateInvokeCount = 0; uint32_t mProbeForwardingStateInvokeCount = 0; + uint32_t mUpdateIntervalV4Count = 0; + uint32_t mUpdateIntervalV6Count = 0; uint32_t mSetMuxLinkmgrStateInvokeCount = 0; uint32_t mPostMetricsInvokeCount = 0; uint32_t mPostLinkProberMetricsInvokeCount = 0; @@ -97,6 +107,7 @@ class FakeDbInterface: public mux::DbInterface uint32_t mSetWarmStartStateReconciledInvokeCount = 0; uint32_t mPostSwitchCauseInvokeCount = 0; uint32_t mGetMuxModeConfigInvokeCount = 0; + uint32_t mIcmpSessionsCount = 0; link_manager::ActiveStandbyStateMachine::SwitchCause mLastPostedSwitchCause; diff --git a/test/FakeLinkProber.cpp b/test/FakeLinkProber.cpp index 0d05fc8..8763e00 100644 --- a/test/FakeLinkProber.cpp +++ b/test/FakeLinkProber.cpp @@ -141,6 +141,13 @@ void FakeLinkProber::sendPeerProbeCommand() mSendPeerProbeCommand++; } +void FakeLinkProber::handleStateDbStateUpdate() +{ + MUXLOGINFO(""); + + mIcmpEchoSessionStateUpdateCallCount++; +} + void FakeLinkProber::handleSendSwitchCommand() { // inform the composite state machine about command send completion diff --git a/test/FakeLinkProber.h b/test/FakeLinkProber.h index fd18142..c6dc6f4 100644 --- a/test/FakeLinkProber.h +++ b/test/FakeLinkProber.h @@ -56,6 +56,7 @@ class FakeLinkProber void handleSendSwitchCommand(); void handleSwitchCommandRecv(); void handleMuxProbeCommandRecv(); + void handleStateDbStateUpdate(); public: uint32_t mInitializeCallCount = 0; @@ -75,6 +76,7 @@ class FakeLinkProber uint32_t mDecreaseIntervalCallCount = 0; uint32_t mRevertIntervalCallCount = 0; + uint32_t mIcmpEchoSessionStateUpdateCallCount = 0; private: link_prober::LinkProberStateMachineBase *mLinkProberStateMachine; diff --git a/test/LinkProberHardwareTest.cpp b/test/LinkProberHardwareTest.cpp new file mode 100644 index 0000000..6fa62c1 --- /dev/null +++ b/test/LinkProberHardwareTest.cpp @@ -0,0 +1,280 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * LinkProberTest.cpp + * + * Created on: May 12, 2025 + * Author: harjosin + */ + +#include +#include + +#include "common/MuxException.h" +#include "link_prober/IcmpPayload.h" +#include "LinkProberHardwareTest.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include +#include +#include // For close() + +#define VALIDATE_STATE(p, m, l) \ + do { \ + mTestCompositeState = mFakeMuxPort.getCompositeState(); \ + EXPECT_EQ(ps(mTestCompositeState), link_prober::LinkProberState::Label::p); \ + EXPECT_EQ(ms(mTestCompositeState), mux_state::MuxState::Label::m); \ + EXPECT_EQ(ls(mTestCompositeState), link_state::LinkState::Label::l); \ + } while (0) + +#define VALIDATE_PEER_STATE(p, m) \ + do { \ + EXPECT_EQ(mFakeMuxPort.getPeerLinkProberState(), link_prober::LinkProberState::Label::p); \ + EXPECT_EQ(mFakeMuxPort.getPeerMuxState(), mux_state::MuxState::Label::m); \ + } while (0) + +using ::testing::InSequence; +using ::testing::Mock; + +namespace test +{ + +LinkProberHardwareTest::LinkProberHardwareTest() : + mDbInterfacePtr(std::make_shared (&mIoService)), + mFakeMuxPort( + mDbInterfacePtr, + mMuxConfig, + mPortName, + mServerId, + mIoService, + common::MuxPortConfig::PortCableType::ActiveActive + ), + mLinkProber(const_cast ( + mFakeMuxPort.getMuxPortConfig()), + mIoService, + mFakeMuxPort.getLinkProberStateMachinePtr(), + &mFakeMuxPort + ) +{ + mMuxConfig.setTimeoutIpv4_msec(1); + boost::uuids::random_generator gen; + mPeerGuid = gen(); + mLinkProber.initializeSendBuffer(); +} + +void LinkProberHardwareTest::changePeerGuid() +{ + boost::uuids::random_generator gen; + mPeerGuid = gen(); + mLinkProber.initializeSendBuffer(); +} + +void LinkProberHardwareTest::buildIcmpReply() +{ + memcpy(mBuffer.data(), getTxBuffer().data(), mBuffer.size()); + ether_header *txEtherHeader = reinterpret_cast(getTxBuffer().data()); + ether_header *rxEtherHeader = reinterpret_cast(mBuffer.data()); + memcpy(rxEtherHeader->ether_shost, txEtherHeader->ether_dhost, sizeof(rxEtherHeader->ether_shost)); + memcpy(rxEtherHeader->ether_dhost, txEtherHeader->ether_shost, sizeof(rxEtherHeader->ether_dhost)); + + iphdr *txIpHeader = reinterpret_cast(getTxBuffer().data() + sizeof(ether_header)); + iphdr *rxIpHeader = reinterpret_cast(mBuffer.data() + sizeof(ether_header)); + rxIpHeader->saddr = txIpHeader->daddr; + rxIpHeader->daddr = txIpHeader->saddr; + + icmphdr *rxIcmpHeader = reinterpret_cast(mBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + rxIcmpHeader->type = 0; + computeChecksum(rxIcmpHeader, sizeof(icmphdr) + getTxPacketSize() - getPacketHeaderSize()); +} + +void LinkProberHardwareTest::receivePeerSoftwareIcmpReply() +{ + buildIcmpReply(); + memcpy(getRxBuffer().data(), mBuffer.data(), getRxBuffer().size()); + link_prober::IcmpPayload *icmpPayload = reinterpret_cast( + getRxBuffer().data() + getPacketHeaderSize() + ); + memcpy(icmpPayload->uuid, mPeerGuid.data, sizeof(icmpPayload->uuid)); +} + +void LinkProberHardwareTest::receivePeerHardwareIcmpReply() +{ + buildIcmpReply(); + memcpy(getRxBuffer().data(), mBuffer.data(), getRxBuffer().size()); + link_prober::IcmpPayload *icmpPayload = reinterpret_cast( + getRxBuffer().data() + getPacketHeaderSize() + ); + icmphdr *icmpHeader = reinterpret_cast ( + getRxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + ); + uint32_t HwCookie = htonl(icmpPayload->getHardwareCookie()); + memcpy(icmpPayload->uuid, mPeerGuid.data, sizeof(icmpPayload->uuid)); + icmpHeader->un.echo.id = 0; + icmpPayload->cookie = HwCookie; + +} + +void LinkProberHardwareTest::runIoService(uint32_t count) +{ + for (uint32_t i = 0; i < count; i++) { + mIoService.run_one(); + mIoService.reset(); + } +} + +void LinkProberHardwareTest::TearDown() +{ + Mock::VerifyAndClearExpectations(mFakeMuxPort.getActiveActiveStateMachinePtr().get()); + mIoService.stop(); +} + +TEST_F(LinkProberHardwareTest, createIcmpSessionProbingTest) +{ + mLinkProber.startProbing(); + EXPECT_EQ(1, mDbInterfacePtr->mIcmpSessionsCount); +} + +TEST_F(LinkProberHardwareTest, deleteIcmpSessionShutdownTest) +{ + mLinkProber.startProbing(); + mLinkProber.shutdownTxProbes(); + EXPECT_EQ(0, mDbInterfacePtr->mIcmpSessionsCount); +} + +TEST_F(LinkProberHardwareTest, icmpSessionRestartTest) +{ + mLinkProber.startProbing(); + mLinkProber.restartTxProbes(); + EXPECT_EQ(1, mDbInterfacePtr->mIcmpSessionsCount); +} + +TEST_F(LinkProberHardwareTest, icmpSessionSuspendTest) +{ + mLinkProber.startProbing(); + mLinkProber.suspendTxProbes(2); + EXPECT_EQ(0, mDbInterfacePtr->mIcmpSessionsCount); + EXPECT_TRUE(getSuspendTx()); + sleep(3); + runIoService(2); + EXPECT_FALSE(getSuspendTx()); +} + +TEST_F(LinkProberHardwareTest, icmpSessionSuspendResumeTest) +{ + mLinkProber.startProbing(); + mLinkProber.suspendTxProbes(10000); + EXPECT_EQ(0, mDbInterfacePtr->mIcmpSessionsCount); + EXPECT_TRUE(getSuspendTx()); + mLinkProber.resumeTxProbes(); + runIoService(2); + EXPECT_FALSE(getSuspendTx()); +} + +TEST_F(LinkProberHardwareTest, updateEthernetFrameTest) +{ + mLinkProber.startProbing(); + EXPECT_EQ(1, mDbInterfacePtr->mIcmpSessionsCount); + mLinkProber.updateEthernetFrame(); + EXPECT_EQ(1, mDbInterfacePtr->mIcmpSessionsCount); +} + +TEST_F(LinkProberHardwareTest, TestHandleSelfStateDbUpdate) +{ + std::string state = "Up"; + std::string sessionType = "NORMAL"; + mMuxConfig.enableDefaultRouteFeature(false); + mFakeMuxPort.activateStateMachine(); + VALIDATE_STATE(Wait, Wait, Down); + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_STATE(Active, Wait, Down); + state = "Down"; + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_STATE(Unknown, Standby, Down); + state = "Up"; + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_STATE(Active, Standby, Down); +} + +TEST_F(LinkProberHardwareTest, TestHandlePeerStateDbUpdate) +{ + std::string state = "Up"; + std::string sessionType = "RX"; + mMuxConfig.enableDefaultRouteFeature(false); + mFakeMuxPort.activateStateMachine(); + VALIDATE_PEER_STATE(PeerWait, Wait); + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_PEER_STATE(PeerActive, Active); + state = "Down"; + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_PEER_STATE(PeerUnknown, Active); + state = "Up"; + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_PEER_STATE(PeerActive, Active); +} + +TEST_F(LinkProberHardwareTest, TestHandleStateDbWaitToUnknownUpdate) +{ + std::string state = "Down"; + std::string sessionType = "RX"; + mMuxConfig.enableDefaultRouteFeature(false); + mFakeMuxPort.activateStateMachine(); + VALIDATE_PEER_STATE(PeerWait, Wait); + VALIDATE_STATE(Wait, Wait, Down); + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_PEER_STATE(PeerUnknown, Wait); + sessionType = "NORMAL"; + mLinkProber.handleStateDbStateUpdate(state, sessionType); + runIoService(4); + VALIDATE_STATE(Unknown, Standby, Down); +} + +TEST_F(LinkProberHardwareTest, LinkProberHardwareHandleIcmpPayload) +{ + InSequence seq; + receivePeerHardwareIcmpReply(); + handleRecv(); + runIoService(1); + receivePeerHardwareIcmpReply(); + handleRecv(); + runIoService(1); + changePeerGuid(); + receivePeerHardwareIcmpReply(); + handleRecv(); + runIoService(1); + receivePeerSoftwareIcmpReply(); + handleRecv(); + runIoService(1); + receivePeerSoftwareIcmpReply(); + handleRecv(); + runIoService(1); + changePeerGuid(); + receivePeerSoftwareIcmpReply(); + handleRecv(); + runIoService(1); + TearDown(); +} + +} /* namespace test */ diff --git a/test/LinkProberHardwareTest.h b/test/LinkProberHardwareTest.h new file mode 100644 index 0000000..70c984f --- /dev/null +++ b/test/LinkProberHardwareTest.h @@ -0,0 +1,74 @@ +/* + * Copyright 2021 (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * LinkProberHardwareTest.h + * + * Created on: May 12, 2025 + * Author: Harjot SIngh + */ + +#ifndef LINKPROBERHARDWARETEST_H_ +#define LINKPROBERHARDWARETEST_H_ + +#include "gtest/gtest.h" + +#include "FakeMuxPort.h" +#include "link_prober/LinkProberHw.h" +#include "common/MuxLogger.h" +namespace test +{ + +class LinkProberHardwareTest: public ::testing::Test +{ +public: + LinkProberHardwareTest(); + virtual ~LinkProberHardwareTest() = default; + + boost::asio::io_service mIoService; + common::MuxConfig mMuxConfig; + std::shared_ptr mDbInterfacePtr; + std::string mPortName = "EtherTest01"; + std::string mSmartNicIpAddress = "192.168.1.20"; + uint16_t mServerId = 01; + boost::uuids::uuid mPeerGuid; + + FakeMuxPort mFakeMuxPort; + //MuxPort mMuxPort; + //link_prober::LinkProberStateMachineActiveActive mLinkProberStateMachine; + link_prober::LinkProberHw mLinkProber; + std::array mBuffer; + + void runIoService(uint32_t count); + bool getSuspendTx() {return mLinkProber.mSuspendTx;}; + std::size_t getTxPacketSize() { return mLinkProber.mTxPacketSize; } + const std::size_t getPacketHeaderSize() { return mLinkProber.mPacketHeaderSize; } + void handleRecv() { mLinkProber.handleRecv(boost::system::error_code(), getTxPacketSize()); } + void computeChecksum(icmphdr* icmpHeader, size_t size) { mLinkProber.computeChecksum(icmpHeader, size); } + link_manager::ActiveActiveStateMachine::CompositeState mTestCompositeState; + + std::array& getTxBuffer() { return mLinkProber.mTxBuffer; } + std::array& getRxBuffer() { return mLinkProber.mRxBuffer; } + void receivePeerSoftwareIcmpReply(); + void receivePeerHardwareIcmpReply(); + void changePeerGuid(); + void buildIcmpReply(); + void TearDown(); +}; + +} /* namespace test */ + +#endif /* LINKPROBERHARDWARETEST_H_ */ diff --git a/test/LinkProberTest.cpp b/test/LinkProberTest.cpp index 6b2e924..37ed6ef 100644 --- a/test/LinkProberTest.cpp +++ b/test/LinkProberTest.cpp @@ -103,11 +103,11 @@ TEST_F(LinkProberTest, InitializeSendBuffer) EXPECT_TRUE(icmpHeader->un.echo.id == htons(mFakeMuxPort.getMuxPortConfig().getServerId())); EXPECT_TRUE(icmpHeader->un.echo.sequence == htons(0xffff)); - EXPECT_TRUE(icmpPayload->cookie == htonl(link_prober::IcmpPayload::getCookie())); + EXPECT_TRUE(icmpPayload->cookie == htonl(link_prober::IcmpPayload::getSoftwareCookie())); EXPECT_TRUE(icmpPayload->version == htonl(link_prober::IcmpPayload::getVersion())); EXPECT_TRUE(memcmp( icmpPayload->uuid, - link_prober::IcmpPayload::getGuidData(), + (mLinkProber.mSelfUUID.data+8), sizeof(icmpPayload->uuid) ) == 0); @@ -121,15 +121,16 @@ TEST_F(LinkProberTest, CalculateChecksum) getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) ) link_prober::IcmpPayload(); boost::uuids::uuid guid = boost::lexical_cast ("44f49d86-c312-414b-b6a1-be82901ac459"); - memcpy(icmpPayload->uuid, guid.data, sizeof(icmpPayload->uuid)); + memcpy((mLinkProber.mSelfUUID.data + 8) , guid.data, (sizeof(mLinkProber.mSelfUUID.data)-8)); initializeSendBuffer(); - icmphdr *icmpHeader = reinterpret_cast (getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr)); - EXPECT_TRUE(icmpHeader->checksum == 12100); + EXPECT_EQ(icmpHeader->checksum, 22109); } TEST_F(LinkProberTest, handleSendSwitchCommand) { + boost::uuids::uuid guid = boost::lexical_cast ("44f49d86-c312-414b-b6a1-be82901ac459"); + memcpy((mLinkProber.mSelfUUID.data + 8) , guid.data, (sizeof(mLinkProber.mSelfUUID.data)-8)); initializeSendBuffer(); iphdr *ipHeader = reinterpret_cast(getTxBufferData() + sizeof(ether_header)); @@ -137,19 +138,21 @@ TEST_F(LinkProberTest, handleSendSwitchCommand) ipHeader->id = static_cast (17767); initTxBufferSentinel(); EXPECT_TRUE(ipHeader->check == 62919); - EXPECT_TRUE(icmpHeader->checksum == 12100); + EXPECT_EQ(icmpHeader->checksum, 22109); initTxBufferTlvSendSwitch(); EXPECT_TRUE(ipHeader->check == 61895); - EXPECT_TRUE(icmpHeader->checksum == 11838); + EXPECT_EQ(icmpHeader->checksum, 21847); initTxBufferSentinel(); EXPECT_TRUE(ipHeader->check == 62919); - EXPECT_TRUE(icmpHeader->checksum == 12100); + EXPECT_EQ(icmpHeader->checksum, 22109); } TEST_F(LinkProberTest, handleSendProbeCommand) { + boost::uuids::uuid guid = boost::lexical_cast ("44f49d86-c312-414b-b6a1-be82901ac459"); + memcpy((mLinkProber.mSelfUUID.data + 8) , guid.data, (sizeof(mLinkProber.mSelfUUID.data)-8)); initializeSendBuffer(); iphdr *ipHeader = reinterpret_cast(getTxBufferData() + sizeof(ether_header)); @@ -157,15 +160,15 @@ TEST_F(LinkProberTest, handleSendProbeCommand) ipHeader->id = static_cast (17767); initTxBufferSentinel(); EXPECT_TRUE(ipHeader->check == 62919); - EXPECT_TRUE(icmpHeader->checksum == 12100); + EXPECT_EQ(icmpHeader->checksum, 22109); initTxBufferTlvSendProbe(); EXPECT_TRUE(ipHeader->check == 61895); - EXPECT_TRUE(icmpHeader->checksum == 11582); + EXPECT_EQ(icmpHeader->checksum, 21591); initTxBufferSentinel(); EXPECT_TRUE(ipHeader->check == 62919); - EXPECT_TRUE(icmpHeader->checksum == 12100); + EXPECT_EQ(icmpHeader->checksum, 22109); } TEST_F(LinkProberTest, UpdateEthernetFrame) @@ -174,11 +177,11 @@ TEST_F(LinkProberTest, UpdateEthernetFrame) getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) ) link_prober::IcmpPayload(); boost::uuids::uuid guid = boost::lexical_cast ("44f49d86-c312-414b-b6a1-be82901ac459"); - memcpy(icmpPayload->uuid, guid.data, sizeof(icmpPayload->uuid)); + memcpy((mLinkProber.mSelfUUID.data + 8) , guid.data, (sizeof(mLinkProber.mSelfUUID.data)-8)); handleUpdateEthernetFrame(); icmphdr *icmpHeader = reinterpret_cast (getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr)); - EXPECT_TRUE(icmpHeader->checksum == 12100); + EXPECT_EQ(icmpHeader->checksum, 22109); } TEST_F(LinkProberTest, UpdateSequenceNo) @@ -187,7 +190,7 @@ TEST_F(LinkProberTest, UpdateSequenceNo) getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) ) link_prober::IcmpPayload(); boost::uuids::uuid guid = boost::lexical_cast ("44f49d86-c312-414b-b6a1-be82901ac459"); - memcpy(icmpPayload->uuid, guid.data, sizeof(icmpPayload->uuid)); + memcpy((mLinkProber.mSelfUUID.data + 8) , guid.data, (sizeof(mLinkProber.mSelfUUID.data)-8)); handleUpdateEthernetFrame(); @@ -196,7 +199,7 @@ TEST_F(LinkProberTest, UpdateSequenceNo) handleUpdateSequenceNumber(); icmphdr *icmpHeader = reinterpret_cast (getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr)); - EXPECT_TRUE(icmpHeader->checksum == 11844); + EXPECT_EQ(icmpHeader->checksum, 21853); EXPECT_TRUE(getRxSelfSeqNo() + 1 == ntohs(icmpHeader->un.echo.sequence)); EXPECT_TRUE(getRxPeerSeqNo() + 1 == ntohs(icmpHeader->un.echo.sequence)); @@ -215,7 +218,6 @@ TEST_F(LinkProberTest, UpdateSequenceNo) TEST_F(LinkProberTest, GenerateGuid) { - link_prober::IcmpPayload::generateGuid(); initializeSendBuffer(); std::array txBuffer = getTxBuffer(); @@ -224,15 +226,13 @@ TEST_F(LinkProberTest, GenerateGuid) ) link_prober::IcmpPayload(); EXPECT_TRUE(memcmp( icmpPayload->uuid, - link_prober::IcmpPayload::getGuidData(), + (mLinkProber.mSelfUUID.data+8), sizeof(icmpPayload->uuid) ) == 0); } TEST_F(LinkProberTest, UpdateToRMac) { - link_prober::IcmpPayload::generateGuid(); - std::array torMac = {0, 'b', 2, 'd', 4, 'f'}; mMuxConfig.setTorMacAddress(torMac); diff --git a/test/LinkProberTest.h b/test/LinkProberTest.h index e61fbad..f4edd04 100644 --- a/test/LinkProberTest.h +++ b/test/LinkProberTest.h @@ -27,7 +27,7 @@ #include "gtest/gtest.h" #include "FakeMuxPort.h" -#include "link_prober/LinkProber.h" +#include "link_prober/LinkProberSw.h" namespace test { @@ -50,7 +50,7 @@ class LinkProberTest: public ::testing::Test size_t appendTlvSentinel(); size_t appendTlvDummy(size_t paddingSize, int seqNo); size_t findNextTlv(size_t readOffset, size_t bytesTransferred); - std::array getTxBuffer() {return mLinkProber.getTxBuffer();}; + std::array& getTxBuffer() {return mLinkProber.getTxBuffer();}; uint8_t *getTxBufferData() {return mLinkProber.mTxBuffer.data();}; uint8_t *getRxBufferData() {return mLinkProber.mRxBuffer.data();}; @@ -73,7 +73,7 @@ class LinkProberTest: public ::testing::Test uint16_t mServerId = 01; FakeMuxPort mFakeMuxPort; - link_prober::LinkProber mLinkProber; + link_prober::LinkProberSw mLinkProber; }; } /* namespace test */ diff --git a/test/MockLinkProberTest.cpp b/test/MockLinkProberTest.cpp index 859752f..d084a3f 100644 --- a/test/MockLinkProberTest.cpp +++ b/test/MockLinkProberTest.cpp @@ -69,12 +69,12 @@ void LinkProberMockTest::SetUp(common::MuxPortConfig::PortCableType portCableTyp break; } } - mLinkProberPtr = std::make_shared( + mLinkProberPtr = std::make_shared( mMuxPortConfig, mIoService, mLinkProberStateMachinePtr.get() ); - link_prober::IcmpPayload::generateGuid(); + //link_prober::IcmpPayload::generateGuid(); initializeSendBuffer(); } diff --git a/test/MockLinkProberTest.h b/test/MockLinkProberTest.h index 56b56a9..327e282 100644 --- a/test/MockLinkProberTest.h +++ b/test/MockLinkProberTest.h @@ -20,7 +20,7 @@ #include #include "common/MuxPortConfig.h" -#include "link_prober/LinkProber.h" +#include "link_prober/LinkProberSw.h" #include "link_prober/LinkProberStateMachineActiveStandby.h" #include "link_prober/LinkProberStateMachineActiveActive.h" @@ -46,10 +46,11 @@ class LinkProberMockTest : public ::testing::Test const std::size_t getPacketHeaderSize() { return mLinkProberPtr->mPacketHeaderSize; } std::shared_ptr getLinkManagerStateMachinePtr() { return mLinkManagerStateMachinePtr; } std::shared_ptr getLinkProberStateMachinePtr() { return mLinkProberStateMachinePtr; } - std::shared_ptr getLinkProberPtr() { return mLinkProberPtr; } + std::shared_ptr getLinkProberPtr() { return mLinkProberPtr; } void computeChecksum(icmphdr* icmpHeader, size_t size) { mLinkProberPtr->computeChecksum(icmpHeader, size); } - void handleRecv() { mLinkProberPtr->handleRecv(boost::system::error_code(), getTxPacketSize()); } - void handleTimeout() { mLinkProberPtr->mReportHeartbeatReplyNotRecivedFuncPtr(); } + void handleRecv() { mLinkProberPtr->handleRecv(boost::system::error_code(), getTxPacketSize()); + } + void handleTimeout() { mLinkProberPtr->mReportHeartbeatReplyNotReceivedFuncPtr(link_prober::HeartbeatType::HEARTBEAT_SELF); } void receiveSelfIcmpReply(); void receivePeerIcmpReply(); @@ -65,7 +66,7 @@ class LinkProberMockTest : public ::testing::Test boost::asio::io_service::strand mStrand; std::shared_ptr mLinkManagerStateMachinePtr; std::shared_ptr mLinkProberStateMachinePtr; - std::shared_ptr mLinkProberPtr; + std::shared_ptr mLinkProberPtr; std::array mBuffer; boost::uuids::uuid mPeerGuid; }; diff --git a/test/MuxManagerTest.cpp b/test/MuxManagerTest.cpp index d77349f..9a227cf 100644 --- a/test/MuxManagerTest.cpp +++ b/test/MuxManagerTest.cpp @@ -41,7 +41,7 @@ MuxManagerTest::MuxManagerTest() : { mMuxManagerPtr->setDbInterfacePtr(mDbInterfacePtr); - link_prober::IcmpPayload::generateGuid(); + //link_prober::IcmpPayload::generateGuid(); } void MuxManagerTest::runIoService(uint32_t count) @@ -186,6 +186,13 @@ void MuxManagerTest::processTsaEnableNotification(std::dequeprocessTsaEnableNotification(entries); } +common::MuxPortConfig::LinkProberType MuxManagerTest::getLinkProberType(const std::string &port) +{ + std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; + + return muxPortPtr->mMuxPortConfig.getLinkProberType(); +} + link_manager::LinkManagerStateMachineBase::CompositeState MuxManagerTest::getCompositeStateMachineState(std::string port) { std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; @@ -262,6 +269,27 @@ void MuxManagerTest::updatePortCableType(const std::string &port, const std::str mMuxManagerPtr->updatePortCableType(port, cableType); } +void MuxManagerTest::updateLinkFailureDetectionState(const std::string &portName, const std::string + &linkFailureDetectionState, const std::string &session_type) +{ + mMuxManagerPtr->updateLinkFailureDetectionState(portName, linkFailureDetectionState, session_type); +} + +void MuxManagerTest::updateProberType(const std::string &portName, const std::string &proberType) +{ + mMuxManagerPtr->updateProberType(portName, proberType); +} + +void MuxManagerTest::setTimeoutIpv4_msec(uint32_t timeout_msec) +{ + mMuxManagerPtr->setTimeoutIpv4_msec(timeout_msec); +} + +void MuxManagerTest::setTimeoutIpv6_msec(uint32_t timeout_msec) +{ + mMuxManagerPtr->setTimeoutIpv6_msec(timeout_msec); +} + void MuxManagerTest::warmRestartReconciliation(const std::string &portName) { std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[portName]; @@ -340,6 +368,9 @@ void MuxManagerTest::initLinkProberActiveActive(std::shared_ptrsetRestartTxFnPtr( boost::bind(&FakeLinkProber::restartTxProbes, mFakeLinkProber.get()) ); + linkManagerStateMachineActiveActive->setIcmpEchoSessionStateUpdate( + boost::bind(&FakeLinkProber::handleStateDbStateUpdate, mFakeLinkProber.get()) + ); linkManagerStateMachineActiveActive->mComponentInitState.set(0); } @@ -761,6 +792,53 @@ TEST_F(MuxManagerTest, ServerMacAddressException) EXPECT_TRUE(serverMacBefore == serverMacAfter); } +TEST_F(MuxManagerTest, updateLinkFailureDetectionState) +{ + std::string port = "Ethernet0"; + std::string linkFailureDetectionState = "Up"; + std::string session_type = "NORMAL"; + + createPort(port, common::MuxPortConfig::ActiveActive); + + uint32_t handleStateDbStateUpdateCallCountBefore = mFakeLinkProber->mIcmpEchoSessionStateUpdateCallCount; + + updateLinkFailureDetectionState(port, linkFailureDetectionState, session_type); + + runIoService(1); + + EXPECT_EQ(mFakeLinkProber->mIcmpEchoSessionStateUpdateCallCount, handleStateDbStateUpdateCallCountBefore + 1); +} + +TEST_F(MuxManagerTest, updateProberType) +{ + std::string port = "Ethernet0"; + std::string proberType = "hardware"; + + createPort(port, common::MuxPortConfig::ActiveActive); + + updateProberType(port, proberType); + + runIoService(1); + + EXPECT_EQ(getLinkProberType(port), common::MuxPortConfig::LinkProberType::Hardware); +} + +TEST_F(MuxManagerTest, TxIntervalChangeTest) +{ + std::string port = "Ethernet0"; + + createPort(port); + + setTimeoutIpv4_msec(3000); + + EXPECT_EQ(3000,getTimeoutIpv4_msec(port)); + + setTimeoutIpv6_msec(1000); + + EXPECT_EQ(1000,getTimeoutIpv6_msec(port)); + +} + TEST_F(MuxManagerTest, ServerMacBeforeLinkProberInit) { std::string port = "Ethernet0"; @@ -906,9 +984,13 @@ TEST_F(MuxManagerTest, LinkmgrdConfig) {"LINK_PROBER", "SET", {{"reset_suspend_timer", "Ethernet0"}}}, {"MUXLOGGER", "SET", {{"log_verbosity", "warning"}}}, }; + EXPECT_EQ(mDbInterfacePtr->mUpdateIntervalV4Count, 0); + EXPECT_EQ(mDbInterfacePtr->mUpdateIntervalV6Count, 0); processMuxLinkmgrConfigNotifiction(entries); runIoService(2); + EXPECT_EQ(mDbInterfacePtr->mUpdateIntervalV4Count, 1); + EXPECT_EQ(mDbInterfacePtr->mUpdateIntervalV6Count, 1); EXPECT_TRUE(getTimeoutIpv4_msec(port) == v4PorbeInterval); EXPECT_TRUE(getTimeoutIpv6_msec(port) == v6ProveInterval); EXPECT_TRUE(getPositiveStateChangeRetryCount(port) == positiveSignalCount); diff --git a/test/MuxManagerTest.h b/test/MuxManagerTest.h index 8d3fda9..2aaa592 100644 --- a/test/MuxManagerTest.h +++ b/test/MuxManagerTest.h @@ -61,6 +61,7 @@ class MuxManagerTest: public testing::Test bool setUseWellKnownMacActiveActive(bool use); bool getIfUseToRMac(std::string port); bool getOscillationEnabled(std::string port); + common::MuxPortConfig::LinkProberType getLinkProberType(const std::string &port); boost::asio::ip::address getBladeIpv4Address(std::string port); std::array getBladeMacAddress(std::string port); std::array getLastUpdatedMacAddress(std::string port); @@ -96,6 +97,11 @@ class MuxManagerTest: public testing::Test void setMuxState(const std::string &portName, mux_state::MuxState::Label label); void initializeThread(); void terminate(); + void updateLinkFailureDetectionState(const std::string &portName, const std::string + &linkFailureDetectionState, const std::string &session_type); + void updateProberType(const std::string &portName, const std::string &proberType); + void setTimeoutIpv4_msec(uint32_t timeout_msec); + void setTimeoutIpv6_msec(uint32_t timeout_msec); public: static const std::string PortName; diff --git a/test/subdir.mk b/test/subdir.mk index 325e4e1..73bf9bb 100644 --- a/test/subdir.mk +++ b/test/subdir.mk @@ -6,6 +6,7 @@ CPP_SRCS += \ ./test/LinkManagerStateMachineTest.cpp \ ./test/LinkManagerStateMachineActiveActiveTest.cpp \ ./test/LinkProberTest.cpp \ + ./test/LinkProberHardwareTest.cpp \ ./test/MuxManagerTest.cpp \ ./test/MockLinkManagerStateMachine.cpp \ ./test/MockLinkProberTest.cpp \ @@ -21,6 +22,7 @@ OBJS_LINKMGRD_TEST += \ ./test/LinkManagerStateMachineTest.o \ ./test/LinkManagerStateMachineActiveActiveTest.o \ ./test/LinkProberTest.o \ + ./test/LinkProberHardwareTest.o \ ./test/MuxManagerTest.o \ ./test/MockLinkManagerStateMachine.o \ ./test/MockLinkProberTest.o \ @@ -36,6 +38,7 @@ CPP_DEPS += \ ./test/LinkManagerStateMachineTest.d \ ./test/LinkManagerStateMachineActiveActiveTest.d \ ./test/LinkProberTest.d \ + ./test/LinkProberHardwareTest.d \ ./test/MuxManagerTest.d \ ./test/MockLinkManagerStateMachine.d \ ./test/MockLinkProberTest.d \