diff --git a/src/DbInterface.cpp b/src/DbInterface.cpp index a19339e..d7f1d35 100644 --- a/src/DbInterface.cpp +++ b/src/DbInterface.cpp @@ -672,6 +672,8 @@ void DbInterface::processLoopbackInterfacesInfo(std::vector &loopba const std::string loopback3 = "Loopback3|"; bool loopback2IPv4Found = false; bool loopback3IPv4Found = false; + bool loopback2IPv6Found = false; + bool loopback3IPv6Found = false; for (auto &loopbackIntf: loopbackIntfs) { size_t pos2 = loopbackIntf.find(loopback2); @@ -693,7 +695,8 @@ void DbInterface::processLoopbackInterfacesInfo(std::vector &loopba mMuxManagerPtr->setLoopbackIpv4Address(ipAddress); loopback2IPv4Found = true; } else if (ipAddress.is_v6()) { - MUXLOGWARNING(boost::format("Ipv6 for Loopback2: ip: %s is not supported.") % ip); + mMuxManagerPtr->setLoopbackIpv6Address(ipAddress); + loopback2IPv6Found = true; } } else { MUXLOGFATAL(boost::format("Received Loopback2 IP: %s, error code: %d") % ip % errorCode); @@ -714,7 +717,8 @@ void DbInterface::processLoopbackInterfacesInfo(std::vector &loopba mMuxManagerPtr->setLoopback3Ipv4Address(ipAddress); loopback3IPv4Found = true; } else if (ipAddress.is_v6()) { - MUXLOGWARNING(boost::format("Ipv6 for Loopback3: ip: %s is not supported.") % ip); + mMuxManagerPtr->setLoopback3Ipv6Address(ipAddress); + loopback3IPv6Found = true; } } else { MUXLOGFATAL(boost::format("Received Loopback3 IP: %s, error code: %d") % ip % errorCode); @@ -722,12 +726,12 @@ void DbInterface::processLoopbackInterfacesInfo(std::vector &loopba } } - if (!loopback2IPv4Found) { - MUXLOGFATAL(boost::format("Config not found: Loopback2 IPv4 address missing, using default value %s ") % mMuxManagerPtr->getLoopbackIpv4Address().to_string()); + if (!loopback2IPv4Found && !loopback2IPv6Found) { + MUXLOGFATAL(boost::format("Config not found: Loopback2 address missing, using default value %s ") % mMuxManagerPtr->getLoopbackIpv4Address().to_string()); } - if (!loopback3IPv4Found) { - MUXLOGFATAL(boost::format("Config not found: Loopback3 IPv4 address missing, using default value %s ") % mMuxManagerPtr->getLoopback3Ipv4Address().to_string()); + if (!loopback3IPv4Found && !loopback3IPv6Found) { + MUXLOGFATAL(boost::format("Config not found: Loopback3 address missing, using default value %s ") % mMuxManagerPtr->getLoopback3Ipv4Address().to_string()); } } @@ -758,11 +762,17 @@ void DbInterface::processServerIpAddress(std::vector fieldValues = kfvFieldsValues(entry); - std::vector::const_iterator cit = std::find_if( - fieldValues.cbegin(), - fieldValues.cend(), - [] (const swss::FieldValueTuple &fv) {return fvField(fv) == "server_ipv4";} - ); + std::vector::const_iterator cit = fieldValues.cend(); + for (const std::string &field: {"server_ipv4", "server_ipv6"}) { + cit = std::find_if( + fieldValues.cbegin(), + fieldValues.cend(), + [&field] (const swss::FieldValueTuple &fv) {return fvField(fv) == field;} + ); + if (cit != fieldValues.cend()) { + break; + } + } if (cit != fieldValues.cend()) { const std::string f = cit->first; std::string smartNicIpAddress = cit->second; @@ -914,11 +924,17 @@ void DbInterface::processSoCIpAddress(std::vector std::string operation = 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) == "soc_ipv4";} - ); + std::vector::const_iterator cit = fieldValues.cend(); + for (const std::string &field: {"soc_ipv4", "soc_ipv6"}) { + cit = std::find_if( + fieldValues.cbegin(), + fieldValues.cend(), + [&field] (const swss::FieldValueTuple &fv) {return fvField(fv) == field;} + ); + if (cit != fieldValues.cend()) { + break; + } + } if (cit != fieldValues.cend()) { const std::string f = cit->first; std::string SoCIpAddress = cit->second; diff --git a/src/MuxManager.cpp b/src/MuxManager.cpp index a689362..998de50 100644 --- a/src/MuxManager.cpp +++ b/src/MuxManager.cpp @@ -189,14 +189,9 @@ void MuxManager::addOrUpdateMuxPort(const std::string &portName, boost::asio::ip std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); common::MuxPortConfig::PortCableType portCableType = getMuxPortCableType(portName); - if (address.is_v4()) { - if (portCableType == common::MuxPortConfig::PortCableType::ActiveStandby) { - // notify server IP address for ports in active-standby cable type - muxPortPtr->handleBladeIpv4AddressUpdate(address); - } - - } else if (address.is_v6()) { - // handle IPv6 probing + if (portCableType == common::MuxPortConfig::PortCableType::ActiveStandby) { + // notify server IP address for ports in active-standby cable type + muxPortPtr->handleBladeIpv4AddressUpdate(address); } } @@ -212,13 +207,9 @@ void MuxManager::addOrUpdateMuxPortSoCAddress(const std::string &portName, boost std::shared_ptr muxPortPtr = getMuxPortPtrOrThrow(portName); common::MuxPortConfig::PortCableType portCableType = getMuxPortCableType(portName); - if (address.is_v4()) { - if (portCableType == common::MuxPortConfig::PortCableType::ActiveActive) { - // notify NiC IP address for ports in active-active cable type - muxPortPtr->handleSoCIpv4AddressUpdate(address); - } - } else if (address.is_v6()) { - // handle IPv6 probing + if (portCableType == common::MuxPortConfig::PortCableType::ActiveActive) { + // notify NiC IP address for ports in active-active cable type + muxPortPtr->handleSoCIpv4AddressUpdate(address); } } diff --git a/src/MuxManager.h b/src/MuxManager.h index a232da6..e1ec8ab 100644 --- a/src/MuxManager.h +++ b/src/MuxManager.h @@ -259,6 +259,28 @@ class MuxManager */ inline void setLoopback3Ipv4Address(boost::asio::ip::address& address) {mMuxConfig.setLoopback3Ipv4Address(address);}; + /** + *@method setLoopbackIpv6Address + * + *@brief setter for Loopback IPv6 address + * + *@param address (in) IPv6 address + * + *@return none + */ + inline void setLoopbackIpv6Address(boost::asio::ip::address& address) {mMuxConfig.setLoopbackIpv6Address(address);}; + + /** + *@method setLoopback3Ipv6Address + * + *@brief setter for Loopback3 IPv6 address + * + *@param address (in) IPv6 address + * + *@return none + */ + inline void setLoopback3Ipv6Address(boost::asio::ip::address& address) {mMuxConfig.setLoopback3Ipv6Address(address);}; + /** *@method setUseWellKnownMacActiveActive @@ -289,6 +311,24 @@ class MuxManager */ inline boost::asio::ip::address getLoopback3Ipv4Address() {return mMuxConfig.getLoopback3Ipv4Address();}; + /** + * @method getLoopbackIpv6Address + * + * @brief getter for Loopback IPv6 address + * + * @return IPv6 address + */ + inline boost::asio::ip::address getLoopbackIpv6Address() {return mMuxConfig.getLoopbackIpv6Address();}; + + /** + * @method getLoopback3Ipv6Address + * + * @brief getter for Loopback3 IPv6 address + * + * @return IPv6 address + */ + inline boost::asio::ip::address getLoopback3Ipv6Address() {return mMuxConfig.getLoopback3Ipv6Address();}; + /** *@method initialize * diff --git a/src/common/MuxConfig.h b/src/common/MuxConfig.h index dea2370..548297c 100644 --- a/src/common/MuxConfig.h +++ b/src/common/MuxConfig.h @@ -244,6 +244,28 @@ class MuxConfig */ inline void setLoopback3Ipv4Address(boost::asio::ip::address& address) {mLoopback3Ipv4Address = address;}; + /** + *@method setLoopbackIpv6Address + * + *@brief setter for Loopback IPv6 address + * + *@param address (in) IPv6 address + * + *@return none + */ + inline void setLoopbackIpv6Address(boost::asio::ip::address& address) {mLoopbackIpv6Address = address;}; + + /** + *@method setLoopback3Ipv6Address + * + *@brief setter for Loopback3 IPv6 address + * + *@param address (in) IPv6 address + * + *@return none + */ + inline void setLoopback3Ipv6Address(boost::asio::ip::address& address) {mLoopback3Ipv6Address = address;}; + /** *@method getNumberOfThreads * @@ -396,6 +418,24 @@ class MuxConfig *@return IPv4 address */ inline boost::asio::ip::address getLoopback3Ipv4Address() {return mLoopback3Ipv4Address;}; + + /** + *@method getLoopbackIpv6Address + * + *@brief getter for Loopback IPv6 address + * + *@return IPv6 address + */ + inline boost::asio::ip::address getLoopbackIpv6Address() {return mLoopbackIpv6Address;}; + + /** + *@method getLoopback3Ipv6Address + * + *@brief getter for Loopback3 IPv6 address + * + *@return IPv6 address + */ + inline boost::asio::ip::address getLoopback3Ipv6Address() {return mLoopback3Ipv6Address;}; /** *@method getDecreasedTimeoutIpv4_msec @@ -511,6 +551,8 @@ class MuxConfig std::array mVlanMacAddress; boost::asio::ip::address mLoopbackIpv4Address = boost::asio::ip::make_address("10.212.64.0"); boost::asio::ip::address mLoopback3Ipv4Address = boost::asio::ip::make_address("10.212.66.0"); + boost::asio::ip::address mLoopbackIpv6Address = boost::asio::ip::make_address("::"); + boost::asio::ip::address mLoopback3Ipv6Address = boost::asio::ip::make_address("::"); }; } /* namespace common */ diff --git a/src/common/MuxPortConfig.h b/src/common/MuxPortConfig.h index fc2e9bb..0f597ea 100644 --- a/src/common/MuxPortConfig.h +++ b/src/common/MuxPortConfig.h @@ -278,6 +278,24 @@ class MuxPortConfig */ inline boost::asio::ip::address getLoopback3Ipv4Address() const {return mMuxConfig.getLoopback3Ipv4Address();}; + /** + *@method getLoopbackIpv6Address + * + *@brief getter for Loopback IPv6 address + * + *@return IPv6 address + */ + inline boost::asio::ip::address getLoopbackIpv6Address() const {return mMuxConfig.getLoopbackIpv6Address();}; + + /** + *@method getLoopback3Ipv6Address + * + *@brief getter for Loopback3 IPv6 address + * + *@return IPv6 address + */ + inline boost::asio::ip::address getLoopback3Ipv6Address() const {return mMuxConfig.getLoopback3Ipv6Address();}; + /** *@method getPortName * diff --git a/src/link_prober/LinkProberBase.cpp b/src/link_prober/LinkProberBase.cpp index c96c4db..49e55d6 100644 --- a/src/link_prober/LinkProberBase.cpp +++ b/src/link_prober/LinkProberBase.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include "LinkProberBase.h" #include "LinkProberHw.h" #include "LinkProberSw.h" @@ -32,6 +34,25 @@ SockFilter LinkProberBase::mIcmpFilter[] = { [12] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00000000}, }; +SockFilter LinkProberBase::mIcmpV6Filter[] = { + [0] = {.code = 0x28, .jt = 0, .jf = 0, .k = 0x0000000c}, + [1] = {.code = 0x15, .jt = 0, .jf = 13, .k = 0x000086dd}, + [2] = {.code = 0x30, .jt = 0, .jf = 0, .k = 0x00000014}, + [3] = {.code = 0x15, .jt = 0, .jf = 11, .k = 0x0000003a}, + [4] = {.code = 0x20, .jt = 0, .jf = 0, .k = 0x00000016}, + [5] = {.code = 0x15, .jt = 0, .jf = 9, .k = 0x00000000}, + [6] = {.code = 0x20, .jt = 0, .jf = 0, .k = 0x0000001a}, + [7] = {.code = 0x15, .jt = 0, .jf = 7, .k = 0x00000000}, + [8] = {.code = 0x20, .jt = 0, .jf = 0, .k = 0x0000001e}, + [9] = {.code = 0x15, .jt = 0, .jf = 5, .k = 0x00000000}, + [10] = {.code = 0x20, .jt = 0, .jf = 0, .k = 0x00000022}, + [11] = {.code = 0x15, .jt = 0, .jf = 3, .k = 0x00000000}, + [12] = {.code = 0x30, .jt = 0, .jf = 0, .k = 0x00000036}, + [13] = {.code = 0x15, .jt = 0, .jf = 1, .k = 0x00000081}, + [14] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00040000}, + [15] = {.code = 0x6, .jt = 0, .jf = 0, .k = 0x00000000}, +}; + LinkProberBase::LinkProberBase(common::MuxPortConfig &muxPortConfig, boost::asio::io_service &ioService, LinkProberStateMachineBase *linkProberStateMachinePtr) : mMuxPortConfig(muxPortConfig), @@ -42,16 +63,60 @@ LinkProberBase::LinkProberBase(common::MuxPortConfig &muxPortConfig, boost::asio mDeadlineTimer(mIoService), mSuspendTimer(mIoService), mSwitchoverTimer(mIoService) +{ + setSelfGuidData(generateGuid()); +} + +bool LinkProberBase::isIpv6Probing() const +{ + return mMuxPortConfig.getBladeIpv4Address().is_v6(); +} + +size_t LinkProberBase::getIpHeaderSize() const +{ + return isIpv6Probing() ? sizeof(ip6_hdr) : sizeof(iphdr); +} + +void LinkProberBase::updatePacketOffsets() +{ + mPacketHeaderSize = sizeof(ether_header) + getIpHeaderSize() + sizeof(icmphdr); + mTlvStartOffset = mPacketHeaderSize + sizeof(IcmpPayload); +} + +uint32_t LinkProberBase::getIpv6AddressWord(const boost::asio::ip::address_v6::bytes_type &bytes, size_t offset) +{ + return (static_cast(bytes[offset]) << 24) | + (static_cast(bytes[offset + 1]) << 16) | + (static_cast(bytes[offset + 2]) << 8) | + static_cast(bytes[offset + 3]); +} + +void LinkProberBase::initializeSockFilter() { try { + bool ipv6Probing = isIpv6Probing(); + const SockFilter *filterTemplate = ipv6Probing ? mIcmpV6Filter : mIcmpFilter; + size_t filterLength = ipv6Probing ? + sizeof(mIcmpV6Filter) / sizeof(*mIcmpV6Filter) : + sizeof(mIcmpFilter) / sizeof(*mIcmpFilter); + mSockFilterPtr = std::shared_ptr ( - new SockFilter[sizeof(mIcmpFilter) / sizeof(*mIcmpFilter)], + new SockFilter[filterLength], std::default_delete() ); - memcpy(mSockFilterPtr.get(), mIcmpFilter, sizeof(mIcmpFilter)); - - mSockFilterProg.len = sizeof(mIcmpFilter) / sizeof(*mIcmpFilter); + memcpy(mSockFilterPtr.get(), filterTemplate, filterLength * sizeof(SockFilter)); + mSockFilterProg.len = filterLength; mSockFilterProg.filter = mSockFilterPtr.get(); + + if (ipv6Probing) { + auto bytes = mMuxPortConfig.getBladeIpv4Address().to_v6().to_bytes(); + mSockFilterPtr.get()[5].k = getIpv6AddressWord(bytes, 0); + mSockFilterPtr.get()[7].k = getIpv6AddressWord(bytes, 4); + mSockFilterPtr.get()[9].k = getIpv6AddressWord(bytes, 8); + mSockFilterPtr.get()[11].k = getIpv6AddressWord(bytes, 12); + } else { + mSockFilterPtr.get()[3].k = mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint(); + } } catch (const std::bad_alloc& ex) { std::ostringstream errMsg; @@ -59,8 +124,16 @@ LinkProberBase::LinkProberBase(common::MuxPortConfig &muxPortConfig, boost::asio throw MUX_ERROR(BadAlloc, errMsg.str()); } +} - setSelfGuidData(generateGuid()); +boost::asio::ip::address LinkProberBase::getLoopbackAddress() const +{ + return isIpv6Probing() ? mMuxPortConfig.getLoopbackIpv6Address() : mMuxPortConfig.getLoopbackIpv4Address(); +} + +boost::asio::ip::address LinkProberBase::getLoopback3Address() const +{ + return isIpv6Probing() ? mMuxPortConfig.getLoopback3Ipv6Address() : mMuxPortConfig.getLoopback3Ipv4Address(); } // @@ -74,7 +147,7 @@ void LinkProberBase::setupSocket() { addr.sll_family = AF_PACKET; addr.sll_protocol = htons(ETH_P_ALL); - mSocket = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_ICMP); + mSocket = socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL)); if (mSocket < 0) { std::ostringstream errMsg; errMsg << "Failed to open socket with '" << strerror(errno) << "'" @@ -89,7 +162,7 @@ void LinkProberBase::setupSocket() { throw MUX_ERROR(SocketError, errMsg.str()); } - mSockFilterPtr.get()[3].k = mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint(); + initializeSockFilter(); if (setsockopt(mSocket, SOL_SOCKET, SO_ATTACH_FILTER, &mSockFilterProg, sizeof(mSockFilterProg)) != 0) { std::ostringstream errMsg; errMsg << "Failed to attach filter with '" << strerror(errno) << "'" @@ -175,19 +248,72 @@ void LinkProberBase::handleRecv( if (!errorCode) { - iphdr *ipHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header)); - icmphdr *icmpHeader = reinterpret_cast ( - mRxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) - ); + if (bytesTransferred < sizeof(ether_header)) { + startRecv(); + return; + } + + ether_header *ethHeader = reinterpret_cast (mRxBuffer.data()); + icmphdr *icmpHeader = nullptr; + size_t packetHeaderSize = 0; + std::string sourceAddress; + + if (ntohs(ethHeader->ether_type) == ETHERTYPE_IP) { + if (bytesTransferred < sizeof(ether_header) + sizeof(iphdr)) { + startRecv(); + return; + } + + iphdr *ipHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header)); + size_t ipHeaderSize = ipHeader->ihl << 2; + packetHeaderSize = sizeof(ether_header) + ipHeaderSize + sizeof(icmphdr); + if (ipHeader->protocol != IPPROTO_ICMP || bytesTransferred < packetHeaderSize) { + startRecv(); + return; + } + + icmpHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header) + ipHeaderSize); + if (icmpHeader->type != ICMP_ECHOREPLY) { + startRecv(); + return; + } + + sourceAddress = boost::asio::ip::address_v4(ntohl(ipHeader->saddr)).to_string(); + } else if (ntohs(ethHeader->ether_type) == ETHERTYPE_IPV6) { + packetHeaderSize = sizeof(ether_header) + sizeof(ip6_hdr) + sizeof(icmphdr); + if (bytesTransferred < packetHeaderSize) { + startRecv(); + return; + } + + ip6_hdr *ip6Header = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header)); + if (ip6Header->ip6_nxt != IPPROTO_ICMPV6) { + startRecv(); + return; + } + + icmpHeader = reinterpret_cast (mRxBuffer.data() + sizeof(ether_header) + sizeof(ip6_hdr)); + if (icmpHeader->type != ICMP6_ECHO_REPLY) { + startRecv(); + return; + } + + boost::asio::ip::address_v6::bytes_type sourceBytes; + memcpy(sourceBytes.data(), &ip6Header->ip6_src, sourceBytes.size()); + sourceAddress = boost::asio::ip::address_v6(sourceBytes).to_string(); + } else { + startRecv(); + return; + } 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)) + sourceAddress % + (bytesTransferred - packetHeaderSize) ); IcmpPayload *icmpPayload = reinterpret_cast ( - mRxBuffer.data() + mPacketHeaderSize + mRxBuffer.data() + packetHeaderSize ); @@ -329,6 +455,9 @@ void LinkProberBase::startRecv() // void LinkProberBase::initializeSendBuffer() { + mTxBuffer.fill(0); + updatePacketOffsets(); + ether_header *ethHeader = reinterpret_cast (mTxBuffer.data()); memcpy(ethHeader->ether_dhost, mMuxPortConfig.getBladeMacAddress().data(), sizeof(ethHeader->ether_dhost)); if (mMuxPortConfig.ifEnableUseTorMac()) { @@ -336,10 +465,9 @@ void LinkProberBase::initializeSendBuffer() } else { memcpy(ethHeader->ether_shost, mMuxPortConfig.getVlanMacAddress().data(), sizeof(ethHeader->ether_shost)); } - ethHeader->ether_type = htons(ETHERTYPE_IP); + ethHeader->ether_type = htons(isIpv6Probing() ? ETHERTYPE_IPV6 : ETHERTYPE_IP); - iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); - icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + getIpHeaderSize()); IcmpPayload *payloadPtr = new (mTxBuffer.data() + mPacketHeaderSize) IcmpPayload(); memcpy(payloadPtr->uuid, (mSelfUUID.data + 8), sizeof(payloadPtr->uuid)); @@ -347,25 +475,43 @@ void LinkProberBase::initializeSendBuffer() 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); + if (isIpv6Probing()) { + ip6_hdr *ip6Header = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + ip6Header->ip6_flow = htonl((6 << 28) | (0xb8 << 20)); + ip6Header->ip6_plen = htons(sizeof(icmphdr) + totalPayloadSize); + ip6Header->ip6_nxt = IPPROTO_ICMPV6; + ip6Header->ip6_hlim = 64; + + auto sourceBytes = getLoopbackAddress().to_v6().to_bytes(); + auto destinationBytes = mMuxPortConfig.getBladeIpv4Address().to_v6().to_bytes(); + memcpy(&ip6Header->ip6_src, sourceBytes.data(), sourceBytes.size()); + memcpy(&ip6Header->ip6_dst, destinationBytes.data(), destinationBytes.size()); + } else { + iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + 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(getLoopbackAddress().to_v4().to_uint()); + ipHeader->daddr = htonl(mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint()); + computeChecksum(ipHeader, ipHeader->ihl << 2); + } - icmpHeader->type = ICMP_ECHO; + icmpHeader->type = isIpv6Probing() ? ICMP6_ECHO_REQUEST : ICMP_ECHO; icmpHeader->code = 0; icmpHeader->un.echo.id = htons(mMuxPortConfig.getServerId()); icmpHeader->un.echo.sequence = htons(mTxSeqNo); - computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); + if (isIpv6Probing()) { + computeIcmpv6Checksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); + } else { + computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); + } } // @@ -540,6 +686,21 @@ void LinkProberBase::computeChecksum(iphdr *ipHeader, size_t size) addChecksumCarryover(&ipHeader->check, mIpChecksum); } +void LinkProberBase::computeIcmpv6Checksum(icmphdr *icmpHeader, size_t size) +{ + icmpHeader->checksum = 0; + ip6_hdr *ip6Header = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + uint32_t upperLayerPacketLength = htonl(static_cast(size)); + uint32_t nextHeader = htonl(IPPROTO_ICMPV6); + + mIcmpChecksum = calculateChecksum(reinterpret_cast(&ip6Header->ip6_src), sizeof(ip6Header->ip6_src)); + mIcmpChecksum += calculateChecksum(reinterpret_cast(&ip6Header->ip6_dst), sizeof(ip6Header->ip6_dst)); + mIcmpChecksum += calculateChecksum(reinterpret_cast(&upperLayerPacketLength), sizeof(upperLayerPacketLength)); + mIcmpChecksum += calculateChecksum(reinterpret_cast(&nextHeader), sizeof(nextHeader)); + mIcmpChecksum += calculateChecksum(reinterpret_cast (icmpHeader), size); + addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); +} + // @@ -553,7 +714,7 @@ void LinkProberBase::updateIcmpSequenceNo() mRxPeerSeqNo = mTxSeqNo; mRxSelfSeqNo = mTxSeqNo; - icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr)); + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + getIpHeaderSize()); icmpHeader->un.echo.sequence = htons(++mTxSeqNo); mIcmpChecksum += mTxSeqNo ? 1 : 0; addChecksumCarryover(&icmpHeader->checksum, mIcmpChecksum); @@ -625,11 +786,17 @@ void LinkProberBase::initTxBufferTlvSendSwitch() 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); + icmphdr *icmpHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header) + getIpHeaderSize()); + if (isIpv6Probing()) { + ip6_hdr *ip6Header = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + ip6Header->ip6_plen = htons(sizeof(icmphdr) + totalPayloadSize); + computeIcmpv6Checksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); + } else { + iphdr *ipHeader = reinterpret_cast (mTxBuffer.data() + sizeof(ether_header)); + computeChecksum(icmpHeader, sizeof(icmphdr) + totalPayloadSize); + ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + totalPayloadSize); + computeChecksum(ipHeader, ipHeader->ihl << 2); + } } // @@ -704,14 +871,26 @@ void LinkProberBase::getGuidStr(const IcmpPayload *icmpPayload, std::string& gui std::string LinkProberBase::generateGuid() { // Get the last 16 bits from Loopback3 IP address - boost::asio::ip::address loopback3Ip = mMuxPortConfig.getLoopback3Ipv4Address(); - uint32_t loopback3Ipv4 = loopback3Ip.to_v4().to_uint(); - uint16_t loopback3Last16Bits = static_cast(loopback3Ipv4 & 0xFFFF); + boost::asio::ip::address loopback3Ip = getLoopback3Address(); + uint16_t loopback3Last16Bits; + if (loopback3Ip.is_v6()) { + auto bytes = loopback3Ip.to_v6().to_bytes(); + loopback3Last16Bits = (static_cast(bytes[14]) << 8) | bytes[15]; + } else { + uint32_t loopback3Ipv4 = loopback3Ip.to_v4().to_uint(); + loopback3Last16Bits = static_cast(loopback3Ipv4 & 0xFFFF); + } // Get the last 16 bits from SoC/Blade IP address boost::asio::ip::address socIp = mMuxPortConfig.getBladeIpv4Address(); - uint32_t socIpv4 = socIp.to_v4().to_uint(); - uint16_t socLast16Bits = static_cast(socIpv4 & 0xFFFF); + uint16_t socLast16Bits; + if (socIp.is_v6()) { + auto bytes = socIp.to_v6().to_bytes(); + socLast16Bits = (static_cast(bytes[14]) << 8) | bytes[15]; + } else { + uint32_t socIpv4 = socIp.to_v4().to_uint(); + socLast16Bits = static_cast(socIpv4 & 0xFFFF); + } // Combine the two 16-bit values to form a 32-bit GUID uint32_t deterministicGuid = (static_cast(loopback3Last16Bits) << 16) | socLast16Bits; diff --git a/src/link_prober/LinkProberBase.h b/src/link_prober/LinkProberBase.h index 51bfec6..6e660eb 100644 --- a/src/link_prober/LinkProberBase.h +++ b/src/link_prober/LinkProberBase.h @@ -311,7 +311,9 @@ class LinkProberBase */ inline uint32_t getProbingInterval() { MUXLOGDEBUG(mMuxPortConfig.getPortName()); - return mDecreaseProbingInterval? mMuxPortConfig.getDecreasedTimeoutIpv4_msec():mMuxPortConfig.getTimeoutIpv4_msec(); + return mDecreaseProbingInterval ? + mMuxPortConfig.getDecreasedTimeoutIpv4_msec() : + (isIpv6Probing() ? mMuxPortConfig.getTimeoutIpv6_msec() : mMuxPortConfig.getTimeoutIpv4_msec()); } /** @@ -531,6 +533,18 @@ class LinkProberBase */ void computeChecksum(iphdr *ipHeader, size_t size); + /** + *@method computeIcmpv6Checksum + * + *@brief compute ICMPv6 checksum with IPv6 pseudo-header + * + *@param icmpHeader (in, out) pointer ICMP header + *@param size (in) size of ICMPv6 payload + * + *@return none + */ + void computeIcmpv6Checksum(icmphdr *icmpHeader, size_t size); + /** *@method updateIcmpSequenceNo * @@ -603,6 +617,14 @@ class LinkProberBase */ void initializeSendBuffer(); + bool isIpv6Probing() const; + size_t getIpHeaderSize() const; + void updatePacketOffsets(); + void initializeSockFilter(); + boost::asio::ip::address getLoopbackAddress() const; + boost::asio::ip::address getLoopback3Address() const; + static uint32_t getIpv6AddressWord(const boost::asio::ip::address_v6::bytes_type &bytes, size_t offset); + /** *@method appendTlvCommand * @@ -661,6 +683,7 @@ class LinkProberBase static SockFilter mIcmpFilter[]; + static SockFilter mIcmpV6Filter[]; common::MuxPortConfig &mMuxPortConfig; boost::asio::io_service &mIoService; @@ -673,8 +696,8 @@ class LinkProberBase 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); + size_t mPacketHeaderSize = sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr); + size_t mTlvStartOffset = sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload); boost::asio::io_service::strand mStrand; boost::asio::posix::stream_descriptor mStream; diff --git a/src/link_prober/LinkProberHw.cpp b/src/link_prober/LinkProberHw.cpp index 28795b2..f84e93c 100644 --- a/src/link_prober/LinkProberHw.cpp +++ b/src/link_prober/LinkProberHw.cpp @@ -273,9 +273,11 @@ void LinkProberHw::createIcmpEchoSession(std::string hwSessionType, std::string % 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(); + bool ipv6Probing = mMuxPortConfig.getBladeIpv4Address().is_v6(); + uint32_t timeout = ipv6Probing ? mMuxPortConfig.getTimeoutIpv6_msec() : mMuxPortConfig.getTimeoutIpv4_msec(); + std::string tx_interval = std::to_string(timeout); + std::string rx_interval = std::to_string(timeout * mMuxPortConfig.getNegativeStateChangeRetryCount()); + std::string src_ip = ipv6Probing ? mMuxPortConfig.getLoopbackIpv6Address().to_string() : mMuxPortConfig.getLoopbackIpv4Address().to_string(); std::string dst_ip = mMuxPortConfig.getBladeIpv4Address().to_string(); std::string src_mac = ""; std::string dst_mac = etherMacArrayToString(mMuxPortConfig.getBladeMacAddress()); diff --git a/test/FakeDbInterface.cpp b/test/FakeDbInterface.cpp index 79e1100..d7adf43 100644 --- a/test/FakeDbInterface.cpp +++ b/test/FakeDbInterface.cpp @@ -157,6 +157,8 @@ void FakeDbInterface::handleSwssNotification(){ void FakeDbInterface::createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries) { mIcmpSessionsCount++; + mLastIcmpSessionKey = key; + mLastIcmpSessionEntries = *entries; } void FakeDbInterface::deleteIcmpEchoSession(std::string key) { diff --git a/test/FakeDbInterface.h b/test/FakeDbInterface.h index c245569..efae4e5 100644 --- a/test/FakeDbInterface.h +++ b/test/FakeDbInterface.h @@ -50,6 +50,10 @@ class FakeDbInterface: public mux::DbInterface virtual void createIcmpEchoSession(std::string key, IcmpHwOffloadEntriesPtr entries) override; virtual void deleteIcmpEchoSession(std::string key) override; virtual void handleSwssNotification() override; + + std::string mLastIcmpSessionKey; + IcmpHwOffloadEntries mLastIcmpSessionEntries; + virtual void setMuxLinkmgrState( const std::string &portName, link_manager::LinkManagerStateMachineBase::Label label diff --git a/test/LinkProberHardwareTest.cpp b/test/LinkProberHardwareTest.cpp index 2d8fb2f..29399ae 100644 --- a/test/LinkProberHardwareTest.cpp +++ b/test/LinkProberHardwareTest.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "common/MuxException.h" #include "link_prober/IcmpPayload.h" @@ -278,4 +279,29 @@ TEST_F(LinkProberHardwareTest, LinkProberHardwareHandleIcmpPayload) TearDown(); } +TEST_F(LinkProberHardwareTest, CreateIcmpEchoSessionIpv6) +{ + boost::asio::ip::address loopbackIpv6Address = boost::asio::ip::make_address("fc02:1000::1"); + boost::asio::ip::address bladeIpv6Address = boost::asio::ip::make_address("fc02:1001::2"); + mMuxConfig.setLoopbackIpv6Address(loopbackIpv6Address); + mFakeMuxPort.setServerIpv4Address(bladeIpv6Address); + + mLinkProber.startProbing(); + + auto findEntry = [&](const std::string &field) { + return std::find_if( + mDbInterfacePtr->mLastIcmpSessionEntries.begin(), + mDbInterfacePtr->mLastIcmpSessionEntries.end(), + [&](const auto &entry) { return entry.first == field; } + ); + }; + + auto srcIpEntry = findEntry("src_ip"); + auto dstIpEntry = findEntry("dst_ip"); + EXPECT_TRUE(srcIpEntry != mDbInterfacePtr->mLastIcmpSessionEntries.end()); + EXPECT_TRUE(dstIpEntry != mDbInterfacePtr->mLastIcmpSessionEntries.end()); + EXPECT_TRUE(srcIpEntry->second == loopbackIpv6Address.to_string()); + EXPECT_TRUE(dstIpEntry->second == bladeIpv6Address.to_string()); +} + } /* namespace test */ diff --git a/test/LinkProberTest.cpp b/test/LinkProberTest.cpp index 37ed6ef..bbc0a0d 100644 --- a/test/LinkProberTest.cpp +++ b/test/LinkProberTest.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include "common/MuxException.h" #include "link_prober/IcmpPayload.h" @@ -115,6 +117,50 @@ TEST_F(LinkProberTest, InitializeSendBuffer) EXPECT_TRUE(tlvPtr->tlvhead.length == 0); } +TEST_F(LinkProberTest, InitializeSendBufferIpv6) +{ + boost::asio::ip::address loopbackIpv6Address = boost::asio::ip::make_address("fc02:1000::1"); + boost::asio::ip::address loopback3Ipv6Address = boost::asio::ip::make_address("fc02:1002::1"); + boost::asio::ip::address bladeIpv6Address = boost::asio::ip::make_address("fc02:1001::2"); + mMuxConfig.setLoopbackIpv6Address(loopbackIpv6Address); + mMuxConfig.setLoopback3Ipv6Address(loopback3Ipv6Address); + mFakeMuxPort.setServerIpv4Address(bladeIpv6Address); + + initializeSendBuffer(); + std::array txBuffer = getTxBuffer(); + + ether_header *ethHeader = reinterpret_cast (txBuffer.data()); + EXPECT_TRUE(ethHeader->ether_type == htons(ETHERTYPE_IPV6)); + + ip6_hdr *ip6Header = reinterpret_cast (txBuffer.data() + sizeof(ether_header)); + icmphdr *icmpHeader = reinterpret_cast (txBuffer.data() + sizeof(ether_header) + sizeof(ip6_hdr)); + link_prober::IcmpPayload *icmpPayload = reinterpret_cast (txBuffer.data() + sizeof(ether_header) + sizeof(ip6_hdr) + sizeof(icmphdr)); + link_prober::Tlv *tlvPtr = reinterpret_cast (txBuffer.data() + sizeof(ether_header) + sizeof(ip6_hdr) + sizeof(icmphdr) + sizeof(*icmpPayload)); + + EXPECT_TRUE((ntohl(ip6Header->ip6_flow) >> 28) == 6); + EXPECT_TRUE(ip6Header->ip6_plen == htons(sizeof(icmphdr) + sizeof(link_prober::IcmpPayload) + sizeof(link_prober::TlvHead))); + EXPECT_TRUE(ip6Header->ip6_nxt == IPPROTO_ICMPV6); + EXPECT_TRUE(ip6Header->ip6_hlim == 64); + + boost::asio::ip::address_v6::bytes_type sourceBytes; + boost::asio::ip::address_v6::bytes_type destinationBytes; + memcpy(sourceBytes.data(), &ip6Header->ip6_src, sourceBytes.size()); + memcpy(destinationBytes.data(), &ip6Header->ip6_dst, destinationBytes.size()); + EXPECT_TRUE(boost::asio::ip::address_v6(sourceBytes) == loopbackIpv6Address.to_v6()); + EXPECT_TRUE(boost::asio::ip::address_v6(destinationBytes) == bladeIpv6Address.to_v6()); + + EXPECT_TRUE(icmpHeader->type == ICMP6_ECHO_REQUEST); + EXPECT_TRUE(icmpHeader->code == 0); + EXPECT_TRUE(icmpHeader->un.echo.id == htons(mFakeMuxPort.getMuxPortConfig().getServerId())); + EXPECT_TRUE(icmpHeader->un.echo.sequence == htons(0xffff)); + EXPECT_TRUE(icmpHeader->checksum != 0); + + EXPECT_TRUE(icmpPayload->cookie == htonl(link_prober::IcmpPayload::getSoftwareCookie())); + EXPECT_TRUE(icmpPayload->version == htonl(link_prober::IcmpPayload::getVersion())); + EXPECT_TRUE(tlvPtr->tlvhead.type == link_prober::TlvType::TLV_SENTINEL); + EXPECT_TRUE(tlvPtr->tlvhead.length == 0); +} + TEST_F(LinkProberTest, CalculateChecksum) { link_prober::IcmpPayload *icmpPayload = new ( diff --git a/test/MuxManagerTest.cpp b/test/MuxManagerTest.cpp index 532dbe2..a3289db 100644 --- a/test/MuxManagerTest.cpp +++ b/test/MuxManagerTest.cpp @@ -162,6 +162,20 @@ boost::asio::ip::address MuxManagerTest::getLoopbackIpv4Address(std::string port return muxPortPtr->mMuxPortConfig.getLoopbackIpv4Address(); } +boost::asio::ip::address MuxManagerTest::getLoopbackIpv6Address(std::string port) +{ + std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; + + return muxPortPtr->mMuxPortConfig.getLoopbackIpv6Address(); +} + +boost::asio::ip::address MuxManagerTest::getLoopback3Ipv6Address(std::string port) +{ + std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; + + return muxPortPtr->mMuxPortConfig.getLoopback3Ipv6Address(); +} + std::array MuxManagerTest::getTorMacAddress(std::string port) { std::shared_ptr muxPortPtr = mMuxManagerPtr->mPortMap[port]; @@ -600,6 +614,27 @@ TEST_F(MuxManagerTest, Loopback2Address) EXPECT_TRUE(mMuxManagerPtr->getLoopbackIpv4Address() == getLoopbackIpv4Address(port)); } +TEST_F(MuxManagerTest, Loopback2AndLoopback3Ipv6Address) +{ + std::string port = "Ethernet0"; + + createPort(port); + + std::string loopback2Ipv6Address = "2603:10e1:100:d::1"; + std::string loopback3Ipv6Address = "2603:10e1:100:e::1"; + std::vector loopbackIntfs = { + "Loopback2|" + loopback2Ipv6Address + "/128", + "Loopback3|" + loopback3Ipv6Address + "/128", + }; + + processLoopback2InterfaceInfo(loopbackIntfs); + + EXPECT_TRUE(getLoopbackIpv6Address(port).to_string() == loopback2Ipv6Address); + EXPECT_TRUE(getLoopback3Ipv6Address(port).to_string() == loopback3Ipv6Address); + EXPECT_TRUE(mMuxManagerPtr->getLoopbackIpv6Address() == getLoopbackIpv6Address(port)); + EXPECT_TRUE(mMuxManagerPtr->getLoopback3Ipv6Address() == getLoopback3Ipv6Address(port)); +} + TEST_F(MuxManagerTest, Loopback2AddressException) { std::string port = "Ethernet0"; @@ -823,6 +858,62 @@ TEST_F(MuxManagerTest, updateProberType) EXPECT_EQ(getLinkProberType(port), common::MuxPortConfig::LinkProberType::Hardware); } +TEST_F(MuxManagerTest, ServerIpAddressPrefersIpv4) +{ + std::string port = "Ethernet0"; + createPort(port, common::MuxPortConfig::PortCableType::ActiveStandby); + + std::vector servers = { + {port, "SET", {{"server_ipv4", "192.168.0.1/32"}, {"server_ipv6", "2603:10e1:100:f::1/128"}}}, + }; + processServerIpAddress(servers); + runIoService(); + + EXPECT_EQ(getBladeIpv4Address(port).to_string(), "192.168.0.1"); +} + +TEST_F(MuxManagerTest, ServerIpAddressFallsBackToIpv6) +{ + std::string port = "Ethernet0"; + createPort(port, common::MuxPortConfig::PortCableType::ActiveStandby); + + std::vector servers = { + {port, "SET", {{"server_ipv6", "2603:10e1:100:f::1/128"}}}, + }; + processServerIpAddress(servers); + runIoService(); + + EXPECT_EQ(getBladeIpv4Address(port).to_string(), "2603:10e1:100:f::1"); +} + +TEST_F(MuxManagerTest, SoCIpAddressPrefersIpv4) +{ + std::string port = "Ethernet0"; + createPort(port, common::MuxPortConfig::PortCableType::ActiveActive); + + std::vector servers = { + {port, "SET", {{"soc_ipv4", "192.168.0.2/32"}, {"soc_ipv6", "2603:10e1:100:f::2/128"}}}, + }; + processSoCIpAddress(servers); + runIoService(); + + EXPECT_EQ(getBladeIpv4Address(port).to_string(), "192.168.0.2"); +} + +TEST_F(MuxManagerTest, SoCIpAddressFallsBackToIpv6) +{ + std::string port = "Ethernet0"; + createPort(port, common::MuxPortConfig::PortCableType::ActiveActive); + + std::vector servers = { + {port, "SET", {{"soc_ipv6", "2603:10e1:100:f::2/128"}}}, + }; + processSoCIpAddress(servers); + runIoService(); + + EXPECT_EQ(getBladeIpv4Address(port).to_string(), "2603:10e1:100:f::2"); +} + TEST_F(MuxManagerTest, TxIntervalChangeTest) { std::string port = "Ethernet0"; diff --git a/test/MuxManagerTest.h b/test/MuxManagerTest.h index d169344..a2dde6d 100644 --- a/test/MuxManagerTest.h +++ b/test/MuxManagerTest.h @@ -67,6 +67,8 @@ class MuxManagerTest: public testing::Test std::array getLastUpdatedMacAddress(std::string port); std::array getWellKnownMacAddress(std::string port); boost::asio::ip::address getLoopbackIpv4Address(std::string port); + boost::asio::ip::address getLoopbackIpv6Address(std::string port); + boost::asio::ip::address getLoopback3Ipv6Address(std::string port); std::array getTorMacAddress(std::string port); std::array getVlanMacAddress(std::string port); common::MuxPortConfig::PortCableType getPortCableType(const std::string &port);