[ip6] enable multicast loop (#5848)

This commit enables the feature IPV6_MULTICAST_LOOP into OpenThread IPv6
stack. The default-enabled feature allows a node receives a multicast
message sent by its own.
This commit is contained in:
Yakun Xu
2020-11-26 08:29:03 -08:00
committed by GitHub
parent 39e6a881c5
commit 9164355b8c
16 changed files with 302 additions and 50 deletions
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (48)
#define OPENTHREAD_API_VERSION (49)
/**
* @addtogroup api-instance
+1
View File
@@ -223,6 +223,7 @@ typedef struct otMessageInfo
///< Otherwise, specifies the IPv6 Hop Limit.
bool mIsHostInterface : 1; ///< TRUE if packets sent/received via host interface, FALSE otherwise.
bool mAllowZeroHopLimit : 1; ///< TRUE to allow IPv6 Hop Limit 0 in `mHopLimit`, FALSE otherwise.
bool mMulticastLoop : 1; ///< TRUE to allow looping back mutlicast, FALSE otherwise.
} otMessageInfo;
/**
+6 -2
View File
@@ -4450,7 +4450,9 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError,
aError, aMessage, static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}
void Interpreter::HandleDiagnosticGetResponse(otError aError, const otMessage *aMessage, const Ip6::MessageInfo *)
void Interpreter::HandleDiagnosticGetResponse(otError aError,
const otMessage * aMessage,
const Ip6::MessageInfo *aMessageInfo)
{
uint8_t buf[16];
uint16_t bytesToPrint;
@@ -4461,7 +4463,9 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, const otMessage *a
SuccessOrExit(aError);
OutputFormat("DIAG_GET.rsp/ans: ");
OutputFormat("DIAG_GET.rsp/ans from ");
OutputIp6Address(aMessageInfo->mPeerAddr);
OutputFormat(": ");
length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
+2
View File
@@ -217,6 +217,7 @@ otError CoapBase::SendMessage(Message & aMessage,
metadata.mSourceAddress = aMessageInfo.GetSockAddr();
metadata.mDestinationPort = aMessageInfo.GetPeerPort();
metadata.mDestinationAddress = aMessageInfo.GetPeerAddr();
metadata.mMulticastLoop = aMessageInfo.GetMulticastLoop();
metadata.mResponseHandler = aHandler;
metadata.mResponseContext = aContext;
metadata.mRetransmissionsRemaining = aTxParameters.mMaxRetransmit;
@@ -383,6 +384,7 @@ void CoapBase::HandleRetransmissionTimer(void)
messageInfo.SetHopLimit(metadata.mHopLimit);
messageInfo.SetIsHostInterface(metadata.mIsHostInterface);
#endif
messageInfo.SetMulticastLoop(metadata.mMulticastLoop);
SendCopy(*message, messageInfo);
}
+3 -2
View File
@@ -543,8 +543,9 @@ private:
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
uint8_t mHopLimit; // The hop limit.
#endif
bool mAcknowledged : 1; // Information that request was acknowledged.
bool mConfirmable : 1; // Information that message is confirmable.
bool mAcknowledged : 1; // Information that request was acknowledged.
bool mConfirmable : 1; // Information that message is confirmable.
bool mMulticastLoop : 1; // Information that multicast loop is enabled.
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
bool mIsHostInterface : 1; // TRUE if packets sent/received via host interface, FALSE otherwise.
#endif
+26 -8
View File
@@ -165,14 +165,15 @@ struct MessageMetadata
uint8_t mChannel; ///< Used for MLE Announce.
} mPanIdChannel; ///< Used for MLE Discover Request, Response, and Announce messages.
uint8_t mType : 3; ///< Identifies the type of message.
uint8_t mSubType : 4; ///< Identifies the message sub type.
bool mDirectTx : 1; ///< Used to indicate whether a direct transmission is required.
bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
uint8_t mPriority : 2; ///< Identifies the message priority level (higher value is higher priority).
bool mInPriorityQ : 1; ///< Indicates whether the message is queued in normal or priority queue.
bool mTxSuccess : 1; ///< Indicates whether the direct tx of the message was successful.
bool mDoNotEvict : 1; ///< Indicates whether or not this message may be evicted.
uint8_t mType : 3; ///< Identifies the type of message.
uint8_t mSubType : 4; ///< Identifies the message sub type.
bool mDirectTx : 1; ///< Used to indicate whether a direct transmission is required.
bool mLinkSecurity : 1; ///< Indicates whether or not link security is enabled.
uint8_t mPriority : 2; ///< Identifies the message priority level (higher value is higher priority).
bool mInPriorityQ : 1; ///< Indicates whether the message is queued in normal or priority queue.
bool mTxSuccess : 1; ///< Indicates whether the direct tx of the message was successful.
bool mDoNotEvict : 1; ///< Indicates whether or not this message may be evicted.
bool mMulticastLoop : 1; ///< Indicates whether or not this multicast message may be looped back.
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
bool mTimeSync : 1; ///< Indicates whether the message is also used for time sync purpose.
uint8_t mTimeSyncSeq; ///< The time sync sequence.
@@ -529,6 +530,23 @@ public:
*/
bool IsSubTypeMle(void) const;
/**
* This method checks whether this multicast message may be looped back.
*
* @retval TRUE If message may be looped back.
* @retval FALSE If message must not be looped back.
*
*/
bool GetMulticastLoop(void) const { return GetMetadata().mMulticastLoop; }
/**
* This method sets whether multicast may be looped back.
*
* @param[in] aMulticastLoop Whether allow looping back multicast.
*
*/
void SetMulticastLoop(bool aMulticastLoop) { GetMetadata().mMulticastLoop = aMulticastLoop; }
/**
* This method returns the message priority level.
*
+64 -21
View File
@@ -501,6 +501,8 @@ otError Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t
SuccessOrExit(error = AddTunneledMplOption(aMessage, header, aMessageInfo));
}
aMessage.SetMulticastLoop(aMessageInfo.GetMulticastLoop());
if (aMessage.GetLength() > kMaxDatagramLength)
{
error = FragmentDatagram(aMessage, aIpProto);
@@ -531,7 +533,7 @@ void Ip6::HandleSendQueue(void)
}
}
otError Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aForward)
otError Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aForward, bool &aReceive)
{
otError error = OT_ERROR_NONE;
HopByHopHeader hbhHeader;
@@ -561,7 +563,7 @@ otError Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool &aForward)
switch (optionHeader.GetType())
{
case OptionMpl::kType:
SuccessOrExit(error = mMpl.ProcessOption(aMessage, aHeader.GetSource(), aForward));
SuccessOrExit(error = mMpl.ProcessOption(aMessage, aHeader.GetSource(), aForward, aReceive));
break;
default:
@@ -890,7 +892,7 @@ otError Ip6::HandleExtensionHeaders(Message & aMessage,
uint8_t & aNextHeader,
bool aForward,
bool aFromNcpHost,
bool aReceive)
bool & aReceive)
{
otError error = OT_ERROR_NONE;
ExtensionHeader extHeader;
@@ -902,7 +904,7 @@ otError Ip6::HandleExtensionHeaders(Message & aMessage,
switch (aNextHeader)
{
case kProtoHopOpts:
SuccessOrExit(error = HandleOptions(aMessage, aHeader, aForward));
SuccessOrExit(error = HandleOptions(aMessage, aHeader, aForward, aReceive));
break;
case kProtoFragment:
@@ -913,7 +915,7 @@ otError Ip6::HandleExtensionHeaders(Message & aMessage,
break;
case kProtoDstOpts:
SuccessOrExit(error = HandleOptions(aMessage, aHeader, aForward));
SuccessOrExit(error = HandleOptions(aMessage, aHeader, aForward, aReceive));
break;
case kProtoIp6:
@@ -934,14 +936,31 @@ exit:
return error;
}
otError Ip6::HandlePayload(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto)
otError Ip6::HandlePayload(Message & aMessage,
MessageInfo & aMessageInfo,
uint8_t aIpProto,
Message::Ownership aMessageOwnership)
{
otError error = OT_ERROR_NONE;
otError error = OT_ERROR_NONE;
Message *message = nullptr;
VerifyOrExit(aIpProto == kProtoUdp || aIpProto == kProtoIcmp6);
switch (aMessageOwnership)
{
case Message::kTakeCustody:
message = &aMessage;
break;
case Message::kCopyToUse:
VerifyOrExit((message = aMessage.Clone()) != nullptr, error = OT_ERROR_NO_BUFS);
break;
}
switch (aIpProto)
{
case kProtoUdp:
error = mUdp.HandleMessage(aMessage, aMessageInfo);
error = mUdp.HandleMessage(*message, aMessageInfo);
if (error == OT_ERROR_DROP)
{
otLogNoteIp6("Error UDP Checksum");
@@ -949,13 +968,21 @@ otError Ip6::HandlePayload(Message &aMessage, MessageInfo &aMessageInfo, uint8_t
break;
case kProtoIcmp6:
error = mIcmp.HandleMessage(aMessage, aMessageInfo);
error = mIcmp.HandleMessage(*message, aMessageInfo);
break;
default:
break;
}
exit:
if (error != OT_ERROR_NONE)
{
otLogNoteIp6("Failed to handle payload: %s", otThreadErrorToString(error));
}
FreeMessage(message);
return error;
}
@@ -1107,6 +1134,7 @@ otError Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkM
bool forward;
bool multicastPromiscuous;
bool shouldFreeMessage;
bool shouldForwardToHost;
uint8_t nextHeader;
start:
@@ -1126,17 +1154,10 @@ start:
// determine destination of packet
if (header.GetDestination().IsMulticast())
{
Netif *netif;
if (aNetif != nullptr)
{
if (aNetif->IsMulticastSubscribed(header.GetDestination()))
{
receive = true;
}
else if (aNetif->IsMulticastPromiscuousEnabled())
{
multicastPromiscuous = true;
}
#if OPENTHREAD_FTD
if (header.GetDestination().IsMulticastLargerThanRealmLocal() &&
Get<ChildTable>().HasSleepyChildWithAddress(header.GetDestination()))
@@ -1144,10 +1165,23 @@ start:
forward = true;
}
#endif
netif = aNetif;
}
else
{
forward = true;
netif = &Get<ThreadNetif>();
}
if ((aNetif != nullptr || aMessage.GetMulticastLoop()) && netif->IsMulticastSubscribed(header.GetDestination()))
{
receive = true;
}
else if (netif->IsMulticastPromiscuousEnabled())
{
multicastPromiscuous = true;
}
}
else
@@ -1173,6 +1207,8 @@ start:
SuccessOrExit(error = HandleExtensionHeaders(aMessage, aNetif, messageInfo, header, nextHeader, forward,
aFromNcpHost, receive));
shouldForwardToHost = forward && !ShouldForwardToThread(messageInfo, aFromNcpHost);
// process IPv6 Payload
if (receive)
{
@@ -1199,9 +1235,16 @@ start:
}
#endif
IgnoreError(ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, Message::kCopyToUse));
error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, Message::kCopyToUse);
SuccessOrExit(error = HandlePayload(aMessage, messageInfo, nextHeader));
if ((error == OT_ERROR_NONE || error == OT_ERROR_NO_ROUTE) && shouldForwardToHost)
{
forward = false;
}
error =
HandlePayload(aMessage, messageInfo, nextHeader, (forward ? Message::kCopyToUse : Message::kTakeCustody));
shouldFreeMessage = forward;
}
else if (multicastPromiscuous)
{
@@ -1212,7 +1255,7 @@ start:
{
uint8_t hopLimit;
if (!ShouldForwardToThread(messageInfo, aFromNcpHost))
if (shouldForwardToHost)
{
// try passing to host
error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromNcpHost, Message::kTakeCustody);
+6 -3
View File
@@ -343,7 +343,7 @@ private:
uint8_t & aNextHeader,
bool aForward,
bool aFromNcpHost,
bool aReceive);
bool & aReceive);
otError FragmentDatagram(Message &aMessage, uint8_t aIpProto);
otError HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromNcpHost);
#if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE
@@ -356,8 +356,11 @@ private:
otError AddTunneledMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo);
otError InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo);
otError RemoveMplOption(Message &aMessage);
otError HandleOptions(Message &aMessage, Header &aHeader, bool &aForward);
otError HandlePayload(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aIpProto);
otError HandleOptions(Message &aMessage, Header &aHeader, bool &aForward, bool &aReceive);
otError HandlePayload(Message & aMessage,
MessageInfo & aMessageInfo,
uint8_t aIpProto,
Message::Ownership aMessageOwnership);
bool ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromNcpHost) const;
bool IsOnLink(const Address &aAddress) const;
+2 -1
View File
@@ -77,7 +77,7 @@ void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress)
}
}
otError Mpl::ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound)
otError Mpl::ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound, bool &aReceive)
{
otError error;
OptionMpl option;
@@ -104,6 +104,7 @@ otError Mpl::ProcessOption(Message &aMessage, const Address &aAddress, bool aIsO
}
else if (aIsOutbound)
{
aReceive = false;
// In case MPL Data Message is generated locally, ignore potential error of the MPL Seed Set
// to allow subsequent retransmissions with the same sequence number.
ExitNow(error = OT_ERROR_NONE);
+3 -1
View File
@@ -216,12 +216,14 @@ public:
* @param[in] aMessage A reference to the message.
* @param[in] aAddress A reference to the IPv6 Source Address.
* @param[in] aIsOutbound TRUE if this message was locally generated, FALSE otherwise.
* @param[out] aReceive Set to FALSE if the MPL message is a duplicate and must not
* go through the receiving process again, untouched otherwise.
*
* @retval OT_ERROR_NONE Successfully processed the MPL option.
* @retval OT_ERROR_DROP The MPL message is a duplicate and should be dropped.
*
*/
otError ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound);
otError ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound, bool &aReceive);
/**
* This method returns the MPL Seed Id value.
+17
View File
@@ -161,6 +161,23 @@ public:
*/
void SetHopLimit(uint8_t aHopLimit) { mHopLimit = aHopLimit; }
/**
* This method returns whether multicast may be looped back.
*
* @retval TRUE If message may be looped back.
* @retval FALSE If message must not be looped back.
*
*/
bool GetMulticastLoop(void) const { return mMulticastLoop; }
/**
* This method sets whether multicast may be looped back.
*
* @param[in] aMulticastLoop Whether allow looping back multicast.
*
*/
void SetMulticastLoop(bool aMulticastLoop) { mMulticastLoop = aMulticastLoop; }
/**
* This method returns a pointer to the link-specific information object.
*
+1
View File
@@ -90,6 +90,7 @@ otError NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address &aDestination,
if (aDestination.IsMulticast())
{
SuccessOrExit(error = message->InitAsNonConfirmablePost(UriPath::kDiagnosticGetQuery));
messageInfo.SetMulticastLoop(true);
}
else
{
+20 -7
View File
@@ -415,18 +415,31 @@ exit:
otError otPlatUdpSend(otUdpSocket *aUdpSocket, otMessage *aMessage, const otMessageInfo *aMessageInfo)
{
otError error = OT_ERROR_NONE;
int fd;
otError error = OT_ERROR_NONE;
int fd;
uint16_t len;
uint8_t payload[kMaxUdpSize];
VerifyOrExit(aUdpSocket->mHandle != nullptr, error = OT_ERROR_INVALID_ARGS);
fd = FdFromHandle(aUdpSocket->mHandle);
{
uint8_t payload[kMaxUdpSize];
uint16_t len = otMessageGetLength(aMessage);
len = otMessageGetLength(aMessage);
VerifyOrExit(len == otMessageRead(aMessage, 0, payload, len), error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(len == otMessageRead(aMessage, 0, payload, len), error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = transmitPacket(fd, payload, len, *aMessageInfo));
if (aMessageInfo->mMulticastLoop)
{
int value = 1;
VerifyOrDie(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &value, sizeof(value)) == 0, OT_EXIT_ERROR_ERRNO);
}
error = transmitPacket(fd, payload, len, *aMessageInfo);
if (aMessageInfo->mMulticastLoop)
{
int value = 0;
VerifyOrDie(setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &value, sizeof(value)) == 0, OT_EXIT_ERROR_ERRNO);
}
exit:
+13 -3
View File
@@ -125,9 +125,9 @@ proc dispose_all {} {
}
}
proc get_mleid {} {
send "ipaddr mleid\n"
expect "ipaddr mleid"
proc get_ipaddr {type} {
send "ipaddr $type\n"
expect "ipaddr $type"
expect -re {(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4})}
set rval $expect_out(1,string)
expect "Done"
@@ -144,4 +144,14 @@ proc get_extaddr {} {
return $rval
}
proc get_rloc16 {} {
send "rloc16\n"
expect "rloc16"
expect -re {([0-9a-fA-F]{4})}
set rval $expect_out(1,string)
expect "Done"
return $rval
}
set timeout 10
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/expect -f
#
# Copyright (c) 2020, The OpenThread Authors.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# This script verifies multicast loop back.
#
#
# R1 -- R2 -- R3 -- E4
#
source "tests/scripts/expect/_common.exp"
spawn_node 1
set timeout 1
send "panid 0xface\n"
expect "Done"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "leader"
expect "Done"
set extaddr1 [get_extaddr]
get_rloc16
spawn_node 2 cli
send "panid 0xface\n"
expect "Done"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "router"
expect "Done"
sleep 3
get_rloc16
spawn_node 3 cli
send "panid 0xface\n"
expect "Done"
send "macfilter addr add ${extaddr1}\n"
expect "Done"
send "macfilter addr denylist\n"
expect "Done"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "router"
expect "Done"
sleep 4
set extaddr3 [get_extaddr]
get_rloc16
spawn_node 4 cli
send "panid 0xface\n"
expect "Done"
send "mode rn\n"
expect "Done"
send "macfilter addr add ${extaddr3}\n"
expect "Done"
send "macfilter addr allowlist\n"
expect "Done"
send "ifconfig up\n"
expect "Done"
send "thread start\n"
expect "Done"
wait_for "state" "child"
expect "Done"
get_rloc16
switch_node 1
set rloc(1) [get_ipaddr "rloc"]
send "networkdiagnostic get ff03::2 5 16\n"
expect "DIAG_GET.rsp/ans from $rloc(1)"
expect "Done"
sleep 5
dispose_all
@@ -112,7 +112,7 @@ expect "Done"
wait_for "state" "child"
set mleid4 [get_mleid]
set mleid4 [get_ipaddr "mleid"]
switch_node 1