Skip to content

Commit

Permalink
Add TLV support to ICMP payload
Browse files Browse the repository at this point in the history
Signed-off-by: Longxiang Lyu <lolv@microsoft.com>
  • Loading branch information
lolyu committed Dec 13, 2021
1 parent 6fec701 commit 5479758
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 43 deletions.
34 changes: 32 additions & 2 deletions src/link_prober/IcmpPayload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@

namespace link_prober
{
//
// ---> TlvPayload();
//
// struct constructor
//
TlvPayload::TlvPayload(uint8_t tlv_type) :
type(tlv_type)
{
if (tlv_type == 0)
{
new (&cmdtlv) TlvCommand();
}
}

//
// static members
//
Expand All @@ -46,9 +60,25 @@ uint32_t IcmpPayload::mVersion = 0;
IcmpPayload::IcmpPayload() :
cookie(htonl(mCookie)),
version(htonl(mVersion)),
command(htonl(static_cast<uint32_t> (Command::COMMAND_NONE)))
seq(0),
tlv(0)
{
memcpy(uuid, mGuid.data, sizeof(uuid));
}

//
// ---> getPayloadSize()
//
// return the actual payload size
//
unsigned int IcmpPayload::getPayloadSize()
{
memcpy(un.uuid.data, mGuid.data, sizeof(un.uuid.data));
unsigned int size = sizeof(IcmpPayload);
if (tlv.type == static_cast<uint8_t> (TlvType::TLV_COMMAND))
{
size += sizeof(TlvCommand) - sizeof(TlvPayload);
}
return size;
}

//
Expand Down
53 changes: 47 additions & 6 deletions src/link_prober/IcmpPayload.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,47 @@ namespace link_prober
*
*@brief Command to be sent to peer device
*/
enum class Command: uint32_t {
enum class Command: uint8_t {
COMMAND_NONE,
COMMAND_SWITCH_ACTIVE,

Count
};

/**
*@enum TlvType
*
*@brief Supported TLV types
*/
enum class TlvType: uint8_t {
TLV_COMMAND
};

/**
*@struct TlvCommand
*
*@brief Build command TLV
*/
struct TlvCommand
{
uint8_t type = 0;
uint16_t length = htons(1);
uint8_t command = static_cast<uint8_t> (Command::COMMAND_NONE);
} __attribute__((packed));

/**
*@union TlvPayload
*
*@brief Union to support multiple TLV structs
*/
union TlvPayload
{
uint8_t type;
TlvCommand cmdtlv;

TlvPayload(uint8_t tlv_type = 0);
} __attribute__((packed));

/**
*@struct IcmpPayload
*
Expand All @@ -63,11 +97,9 @@ enum class Command: uint32_t {
struct IcmpPayload {
uint32_t cookie;
uint32_t version;
union {
uint64_t guid[2];
boost::uuids::uuid uuid;
} un;
uint32_t command;
uint8_t uuid[8];
uint64_t seq;
TlvPayload tlv;

/**
*@method IcmpPayload
Expand All @@ -76,6 +108,15 @@ struct IcmpPayload {
*/
IcmpPayload();

/**
*@method getPayloadSize
*
*@brief get the ICMP payload size based on the tlv contained
*
*@return the ICMP payload actual size
*/
unsigned int getPayloadSize();

/**
*@method generateGuid
*
Expand Down
56 changes: 30 additions & 26 deletions src/link_prober/LinkProber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,14 @@ void LinkProber::handleSendSwitchCommand()
IcmpPayload *icmpPayload = reinterpret_cast<IcmpPayload *> (
mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)
);
icmpPayload->command = htonl(static_cast<uint32_t> (Command::COMMAND_SWITCH_ACTIVE));
icmpPayload->tlv.cmdtlv.command = static_cast<uint8_t> (Command::COMMAND_SWITCH_ACTIVE);

icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr));
computeChecksum(icmpHeader, sizeof(icmphdr) + sizeof(*icmpPayload));

sendHeartbeat();

icmpPayload->command = htonl(static_cast<uint32_t> (Command::COMMAND_NONE));
icmpPayload->tlv.cmdtlv.command = static_cast<uint8_t> (Command::COMMAND_NONE);
computeChecksum(icmpHeader, sizeof(icmphdr) + sizeof(*icmpPayload));

// inform the composite state machine about commend send completion
Expand All @@ -269,9 +269,10 @@ void LinkProber::sendHeartbeat()
if (!mSuspendTx) {
updateIcmpSequenceNo();
boost::system::error_code errorCode;
IcmpPayload *icmpPayload = reinterpret_cast<IcmpPayload *> (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr));
mStream.write_some(
boost::asio::buffer(
mTxBuffer.data(), sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload)
mTxBuffer.data(), sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr) + icmpPayload->getPayloadSize()
),
errorCode
);
Expand Down Expand Up @@ -313,25 +314,27 @@ void LinkProber::handleRecv(
mMuxPortConfig.getPortName() %
mMuxPortConfig.getBladeIpv4Address().to_string()
);
if (memcmp(icmpPayload->un.uuid.data, IcmpPayload::getGuidData(),
IcmpPayload::getGuid().size()) == 0) {
MUXLOGTRACE(boost::format("%s: Matching Guid") % mMuxPortConfig.getPortName());
// echo reply for an echo request generated by this/active ToR
mRxSelfSeqNo = mTxSeqNo;
mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpSelfEvent());
} else {
// echo reply for an echo request generated by peer ToR
mRxPeerSeqNo = mTxSeqNo;
mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpPeerEvent());
if (ntohl(icmpPayload->command) == static_cast<uint32_t> (Command::COMMAND_SWITCH_ACTIVE)) {
boost::asio::io_service::strand &strand = mLinkProberStateMachine.getStrand();
boost::asio::io_service &ioService = strand.context();
ioService.post(strand.wrap(boost::bind(
static_cast<void (LinkProberStateMachine::*) (SwitchActiveRequestEvent&)>
(&LinkProberStateMachine::processEvent),
&mLinkProberStateMachine,
LinkProberStateMachine::getSwitchActiveRequestEvent()
)));
if (icmpPayload->tlv.type == static_cast<uint8_t> (TlvType::TLV_COMMAND))
{
if (memcmp(icmpPayload->uuid, IcmpPayload::getGuidData(), sizeof(icmpPayload->uuid)) == 0) {
MUXLOGTRACE(boost::format("%s: Matching Guid") % mMuxPortConfig.getPortName());
// echo reply for an echo request generated by this/active ToR
mRxSelfSeqNo = mTxSeqNo;
mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpSelfEvent());
} else {
// echo reply for an echo request generated by peer ToR
mRxPeerSeqNo = mTxSeqNo;
mLinkProberStateMachine.postLinkProberStateEvent(LinkProberStateMachine::getIcmpPeerEvent());
if (ntohl(icmpPayload->tlv.cmdtlv.command) == static_cast<uint8_t> (Command::COMMAND_SWITCH_ACTIVE)) {
boost::asio::io_service::strand &strand = mLinkProberStateMachine.getStrand();
boost::asio::io_service &ioService = strand.context();
ioService.post(strand.wrap(boost::bind(
static_cast<void (LinkProberStateMachine::*) (SwitchActiveRequestEvent&)>
(&LinkProberStateMachine::processEvent),
&mLinkProberStateMachine,
LinkProberStateMachine::getSwitchActiveRequestEvent()
)));
}
}
}
} else {
Expand Down Expand Up @@ -551,10 +554,13 @@ void LinkProber::initializeSendBuffer()
ethHeader->ether_type = htons(ETHERTYPE_IP);

iphdr *ipHeader = reinterpret_cast<iphdr *> (mTxBuffer.data() + sizeof(ether_header));
icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr));
IcmpPayload *icmpPayload = new (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)) IcmpPayload();

ipHeader->ihl = sizeof(iphdr) >> 2;
ipHeader->version = IPVERSION;
ipHeader->tos = 0xb8;
ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + sizeof(IcmpPayload));
ipHeader->tot_len = htons(sizeof(iphdr) + sizeof(icmphdr) + icmpPayload->getPayloadSize());
ipHeader->id = static_cast<uint16_t> (rand());
ipHeader->frag_off = 0;
ipHeader->ttl = 64;
Expand All @@ -564,14 +570,12 @@ void LinkProber::initializeSendBuffer()
ipHeader->daddr = htonl(mMuxPortConfig.getBladeIpv4Address().to_v4().to_uint());
computeChecksum(ipHeader, ipHeader->ihl << 2);

icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr));
icmpHeader->type = ICMP_ECHO;
icmpHeader->code = 0;
icmpHeader->un.echo.id = htons(mMuxPortConfig.getServerId());
icmpHeader->un.echo.sequence = htons(mTxSeqNo);

IcmpPayload *icmpPayload = new (mTxBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)) IcmpPayload();
computeChecksum(icmpHeader, sizeof(icmphdr) + sizeof(*icmpPayload));
computeChecksum(icmpHeader, sizeof(icmphdr) + icmpPayload->getPayloadSize());
}

//
Expand Down
22 changes: 13 additions & 9 deletions test/LinkProberTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,22 @@ TEST_F(LinkProberTest, InitializeSendBuffer)
EXPECT_TRUE(ethHeader->ether_type == htons(ETHERTYPE_IP));

iphdr *ipHeader = reinterpret_cast<iphdr *> (txBuffer.data() + sizeof(ether_header));
icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (txBuffer.data() + sizeof(ether_header) + sizeof(iphdr));
link_prober::IcmpPayload *icmpPayload = new (
txBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)
) link_prober::IcmpPayload();

EXPECT_TRUE(ipHeader->ihl == sizeof(iphdr) >> 2);
EXPECT_TRUE(ipHeader->version == IPVERSION);
EXPECT_TRUE(ipHeader->tos == 0xb8);
EXPECT_TRUE(ipHeader->tot_len == htons(sizeof(iphdr) + sizeof(icmphdr) + sizeof(link_prober::IcmpPayload)));
EXPECT_TRUE(ipHeader->tot_len == htons(sizeof(iphdr) + sizeof(icmphdr) + icmpPayload->getPayloadSize()));
EXPECT_TRUE(ipHeader->frag_off == 0);
EXPECT_TRUE(ipHeader->ttl == 64);
EXPECT_TRUE(ipHeader->protocol == IPPROTO_ICMP);
EXPECT_TRUE(ipHeader->check == 62663);
EXPECT_TRUE(ipHeader->saddr == htonl(mFakeMuxPort.getMuxPortConfig().getLoopbackIpv4Address().to_v4().to_uint()));
EXPECT_TRUE(ipHeader->daddr == htonl(mFakeMuxPort.getMuxPortConfig().getBladeIpv4Address().to_v4().to_uint()));

icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (txBuffer.data() + sizeof(ether_header) + sizeof(iphdr));
EXPECT_TRUE(icmpHeader->type == ICMP_ECHO);
EXPECT_TRUE(icmpHeader->code == 0);
EXPECT_TRUE(icmpHeader->un.echo.id == htons(mFakeMuxPort.getMuxPortConfig().getServerId()));
Expand All @@ -87,9 +91,9 @@ TEST_F(LinkProberTest, InitializeSendBuffer)
EXPECT_TRUE(icmpPayload->cookie == htonl(link_prober::IcmpPayload::getCookie()));
EXPECT_TRUE(icmpPayload->version == htonl(link_prober::IcmpPayload::getVersion()));
EXPECT_TRUE(memcmp(
icmpPayload->un.uuid.data,
icmpPayload->uuid,
link_prober::IcmpPayload::getGuidData(),
link_prober::IcmpPayload::getGuid().size()
sizeof(icmpPayload->uuid)
) == 0);
}

Expand All @@ -99,7 +103,7 @@ TEST_F(LinkProberTest, CalculateChecksum)
getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)
) link_prober::IcmpPayload();
boost::uuids::uuid guid = boost::lexical_cast<boost::uuids::uuid> ("44f49d86-c312-414b-b6a1-be82901ac459");
memcpy(icmpPayload->un.uuid.data, guid.data, guid.size());
memcpy(icmpPayload->uuid, guid.data, sizeof(icmpPayload->uuid));
initializeSendBuffer();

icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr));
Expand All @@ -112,7 +116,7 @@ TEST_F(LinkProberTest, UpdateEthernetFrame)
getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)
) link_prober::IcmpPayload();
boost::uuids::uuid guid = boost::lexical_cast<boost::uuids::uuid> ("44f49d86-c312-414b-b6a1-be82901ac459");
memcpy(icmpPayload->un.uuid.data, guid.data, guid.size());
memcpy(icmpPayload->uuid, guid.data, sizeof(icmpPayload->uuid));
handleUpdateEthernetFrame();

icmphdr *icmpHeader = reinterpret_cast<icmphdr *> (getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr));
Expand All @@ -125,7 +129,7 @@ TEST_F(LinkProberTest, UpdateSequenceNo)
getTxBuffer().data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)
) link_prober::IcmpPayload();
boost::uuids::uuid guid = boost::lexical_cast<boost::uuids::uuid> ("44f49d86-c312-414b-b6a1-be82901ac459");
memcpy(icmpPayload->un.uuid.data, guid.data, guid.size());
memcpy(icmpPayload->uuid, guid.data, sizeof(icmpPayload->uuid));

handleUpdateEthernetFrame();

Expand All @@ -150,9 +154,9 @@ TEST_F(LinkProberTest, GenerateGuid)
txBuffer.data() + sizeof(ether_header) + sizeof(iphdr) + sizeof(icmphdr)
) link_prober::IcmpPayload();
EXPECT_TRUE(memcmp(
icmpPayload->un.uuid.data,
icmpPayload->uuid,
link_prober::IcmpPayload::getGuidData(),
link_prober::IcmpPayload::getGuid().size()
sizeof(icmpPayload->uuid)
) == 0);
}

Expand Down

0 comments on commit 5479758

Please sign in to comment.