[mlr] implement native multicast routing for backbone router (#5793)

This commit implements native multicast routing using kernel Ip6
multicast forwarding.
This commit is contained in:
Simon Lin
2020-11-22 23:31:05 -08:00
committed by GitHub
parent a8c869a849
commit 6af97d61b5
14 changed files with 909 additions and 31 deletions
+1
View File
@@ -300,6 +300,7 @@ LOCAL_SRC_FILES := \
src/posix/platform/hdlc_interface.cpp \
src/posix/platform/logging.cpp \
src/posix/platform/misc.cpp \
src/posix/platform/multicast_routing.cpp \
src/posix/platform/netif.cpp \
src/posix/platform/radio.cpp \
src/posix/platform/radio_url.cpp \
+1 -1
View File
@@ -230,7 +230,7 @@ do_build_otbr_docker()
local otdir
local otbrdir
local otbr_options="-DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON -DOTBR_REST=OFF -DOTBR_WEB=OFF"
local otbr_commit=${OTBR_COMMIT:-8d0b6ccd857441208f56ed29050a9367d8d74a62} # [backbone-router] implement Backbone ND proxy #581
local otbr_commit=${OTBR_COMMIT:-5092e71b06b23f78f2288fcc5386bce3a8986bb1} # [backbone-router] remove MLR multicast routing using smcroute #604
local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci}
otbrdir=$(mktemp -d -t otbr_XXXXXX)
+29
View File
@@ -2218,6 +2218,35 @@ const char *otLogLevelToPrefixString(otLogLevel aLogLevel);
#endif // OPENTHREAD_CONFIG_LOG_DEFINE_AS_MACRO_ONLY
#define _otLogResult(aRegion, aError, ...) \
do \
{ \
otError _err = (aError); \
\
if (_err == OT_ERROR_NONE) \
{ \
otLogInfo##aRegion(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), \
otThreadErrorToString(_err)); \
} \
else \
{ \
otLogWarn##aRegion(OT_FIRST_ARG(__VA_ARGS__) ": %s" OT_REST_ARGS(__VA_ARGS__), \
otThreadErrorToString(_err)); \
} \
} while (false)
/**
* @def otLogResultPlat
*
* This function generates a log for the Plat region according to the error result. If @p aError is `OT_ERROR_NONE`, the
* log level is info. Otherwise the log level is warn.
*
* @param[in] aError The error result.
* @param[in] ... Arguments for the format specification.
*
*/
#define otLogResultPlat(aError, ...) _otLogResult(Plat, aError, OT_FIRST_ARG(__VA_ARGS__) OT_REST_ARGS(__VA_ARGS__))
#ifdef __cplusplus
}
#endif
+23
View File
@@ -533,6 +533,15 @@ public:
*/
const uint8_t *GetBytes(void) const { return mFields.m8; }
/**
* This method sets the IPv6 address from a given byte array.
*
* @param[in] aBuffer Pointer to an array containing the IPv6 address. `kSize` bytes from the buffer
* are copied to form the IPv6 address.
*
*/
void SetBytes(const uint8_t *aBuffer) { memcpy(mFields.m8, aBuffer, kSize); }
/**
* This method indicates whether or not the IPv6 address is the Unspecified Address.
*
@@ -885,6 +894,20 @@ public:
*/
InfoString ToString(void) const;
/**
* This method overloads operator `<` to compare two IPv6 addresses.
*
* @param[in] aOther The other IPv6 address to compare with.
*
* @retval true The IPv6 address is smaller than @p aOther.
* @retval false The IPv6 address is larger than or equal to @p aOther.
*
*/
bool operator<(const Ip6::Address &aOther) const
{
return memcmp(mFields.m8, aOther.mFields.m8, sizeof(Ip6::Address)) < 0;
}
private:
void SetPrefix(uint8_t aOffset, const uint8_t *aPrefix, uint8_t aPrefixLength);
void SetToLocator(const NetworkPrefix &aNetworkPrefix, uint16_t aLocator);
+7 -20
View File
@@ -101,15 +101,13 @@ void __gcov_flush();
*/
enum
{
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
OT_POSIX_OPT_BACKBONE_INTERFACE_NAME = 'B',
#endif
OT_POSIX_OPT_DEBUG_LEVEL = 'd',
OT_POSIX_OPT_DRY_RUN = 'n',
OT_POSIX_OPT_HELP = 'h',
OT_POSIX_OPT_INTERFACE_NAME = 'I',
OT_POSIX_OPT_TIME_SPEED = 's',
OT_POSIX_OPT_VERBOSE = 'v',
OT_POSIX_OPT_DEBUG_LEVEL = 'd',
OT_POSIX_OPT_DRY_RUN = 'n',
OT_POSIX_OPT_HELP = 'h',
OT_POSIX_OPT_INTERFACE_NAME = 'I',
OT_POSIX_OPT_TIME_SPEED = 's',
OT_POSIX_OPT_VERBOSE = 'v',
OT_POSIX_OPT_SHORT_MAX = 128,
@@ -118,9 +116,7 @@ enum
};
static const struct option kOptions[] = {
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
{"backbone-interface-name", required_argument, NULL, OT_POSIX_OPT_BACKBONE_INTERFACE_NAME},
#endif
{"debug-level", required_argument, NULL, OT_POSIX_OPT_DEBUG_LEVEL},
{"dry-run", no_argument, NULL, OT_POSIX_OPT_DRY_RUN},
{"help", no_argument, NULL, OT_POSIX_OPT_HELP},
@@ -137,9 +133,7 @@ static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode)
"Syntax:\n"
" %s [Options] RadioURL\n"
"Options:\n"
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
" -B --backbone-interface-name Backbone network interface name.\n"
#endif
" -d --debug-level Debug level of logging.\n"
" -h --help Display this usage information.\n"
" -I --interface-name name Thread network interface name.\n"
@@ -173,12 +167,7 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
while (true)
{
int index = 0;
int option = getopt_long(aArgCount, aArgVector,
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
"B:"
#endif
"d:hI:ns:v",
kOptions, &index);
int option = getopt_long(aArgCount, aArgVector, "B:d:hI:ns:v", kOptions, &index);
if (option == -1)
{
@@ -196,11 +185,9 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
case OT_POSIX_OPT_INTERFACE_NAME:
aConfig->mPlatformConfig.mInterfaceName = optarg;
break;
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
case OT_POSIX_OPT_BACKBONE_INTERFACE_NAME:
aConfig->mPlatformConfig.mBackboneInterfaceName = optarg;
break;
#endif
case OT_POSIX_OPT_DRY_RUN:
aConfig->mIsDryRun = true;
break;
+1
View File
@@ -66,6 +66,7 @@ add_library(openthread-posix
hdlc_interface.cpp
logging.cpp
misc.cpp
multicast_routing.cpp
netif.cpp
radio.cpp
radio_url.cpp
+2
View File
@@ -50,6 +50,7 @@ libopenthread_posix_a_SOURCES = \
hdlc_interface.cpp \
logging.cpp \
misc.cpp \
multicast_routing.cpp \
netif.cpp \
radio.cpp \
radio_url.cpp \
@@ -68,6 +69,7 @@ libopenthread_posix_a_LIBADD
noinst_HEADERS = \
hdlc_interface.hpp \
multicast_routing.hpp \
openthread-posix-config.h \
platform-posix.h \
radio_url.hpp \
+21 -5
View File
@@ -35,17 +35,19 @@
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include "multicast_routing.hpp"
#include "platform-posix.h"
#include "common/code_utils.hpp"
char gBackboneNetifName[IFNAMSIZ] = "";
unsigned int gBackboneNetifIndex = 0;
char gBackboneNetifName[IFNAMSIZ] = "";
unsigned int gBackboneNetifIndex = 0;
static ot::Posix::MulticastRoutingManager sMulticastRoutingManager;
void platformBackboneInit(otInstance *aInstance, const char *aInterfaceName)
{
OT_UNUSED_VARIABLE(aInstance);
VerifyOrExit(aInterfaceName != nullptr, otLogInfoPlat("Backbone interface is not configured"));
VerifyOrDie(aInterfaceName != nullptr, OT_EXIT_INVALID_ARGUMENTS);
VerifyOrDie(strnlen(aInterfaceName, IFNAMSIZ) <= IFNAMSIZ - 1, OT_EXIT_INVALID_ARGUMENTS);
strcpy(gBackboneNetifName, aInterfaceName);
@@ -55,8 +57,22 @@ void platformBackboneInit(otInstance *aInstance, const char *aInterfaceName)
otLogInfoPlat("Backbone interface is configured to %s (%d)", gBackboneNetifName, gBackboneNetifIndex);
exit:
return;
sMulticastRoutingManager.Init(aInstance);
}
void platformBackboneUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd)
{
sMulticastRoutingManager.UpdateFdSet(aReadFdSet, aMaxFd);
}
void platformBackboneProcess(const fd_set &aReadSet)
{
sMulticastRoutingManager.Process(aReadSet);
}
void platformBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags)
{
sMulticastRoutingManager.HandleStateChange(aInstance, aFlags);
}
#endif
+569
View File
@@ -0,0 +1,569 @@
/*
* 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.
*/
#include "posix/platform/multicast_routing.hpp"
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <assert.h>
#include <net/if.h>
#include <netinet/icmp6.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#if __linux__
#include <linux/mroute6.h>
#else
#error "Multicast Routing feature is not ported to non-Linux platforms yet."
#endif
#include <openthread/backbone_router_ftd.h>
#include "core/common/logging.hpp"
namespace ot {
namespace Posix {
void MulticastRoutingManager::Init(otInstance *aInstance)
{
mInstance = aInstance;
otBackboneRouterSetMulticastListenerCallback(aInstance,
&MulticastRoutingManager::HandleBackboneMulticastListenerEvent, this);
}
void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(void * aContext,
otBackboneRouterMulticastListenerEvent aEvent,
const otIp6Address * aAddress)
{
static_cast<MulticastRoutingManager *>(aContext)->HandleBackboneMulticastListenerEvent(
aEvent, static_cast<const Ip6::Address &>(*aAddress));
}
void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(otBackboneRouterMulticastListenerEvent aEvent,
const Ip6::Address & aAddress)
{
switch (aEvent)
{
case OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED:
Add(aAddress);
break;
case OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED:
Remove(aAddress);
break;
}
}
void MulticastRoutingManager::Enable(void)
{
VerifyOrExit(!IsEnabled());
InitMulticastRouterSock();
otLogResultPlat(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
exit:
return;
}
void MulticastRoutingManager::Disable(void)
{
FinalizeMulticastRouterSock();
otLogResultPlat(OT_ERROR_NONE, "MulticastRoutingManager: %s", __FUNCTION__);
}
void MulticastRoutingManager::Add(const Ip6::Address &aAddress)
{
VerifyOrExit(IsEnabled());
UnblockInboundMulticastForwardingCache(aAddress);
otLogResultPlat(OT_ERROR_NONE, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
exit:
return;
}
void MulticastRoutingManager::Remove(const Ip6::Address &aAddress)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(IsEnabled());
RemoveInboundMulticastForwardingCache(aAddress);
otLogResultPlat(error, "MulticastRoutingManager: %s: %s", __FUNCTION__, aAddress.ToString().AsCString());
exit:
return;
}
bool MulticastRoutingManager::HasMulticastListener(const Ip6::Address &aAddress) const
{
bool found = false;
otBackboneRouterMulticastListenerIterator iter = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ITERATOR_INIT;
otBackboneRouterMulticastListenerInfo listenerInfo;
while (otBackboneRouterMulticastListenerGetNext(mInstance, &iter, &listenerInfo) == OT_ERROR_NONE)
{
VerifyOrExit(static_cast<const Ip6::Address &>(listenerInfo.mAddress) != aAddress, found = true);
}
exit:
return found;
}
void MulticastRoutingManager::UpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) const
{
VerifyOrExit(IsEnabled());
FD_SET(mMulticastRouterSock, &aReadFdSet);
aMaxFd = OT_MAX(aMaxFd, mMulticastRouterSock);
exit:
return;
}
void MulticastRoutingManager::Process(const fd_set &aReadFdSet)
{
VerifyOrExit(IsEnabled());
ExpireMulticastForwardingCache();
if (FD_ISSET(mMulticastRouterSock, &aReadFdSet))
{
ProcessMulticastRouterMessages();
}
exit:
return;
}
void MulticastRoutingManager::InitMulticastRouterSock(void)
{
int one = 1;
struct icmp6_filter filter;
struct mif6ctl mif6ctl;
// Create a Multicast Routing socket
mMulticastRouterSock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
VerifyOrDie(mMulticastRouterSock != -1, OT_EXIT_ERROR_ERRNO);
// Enable Multicast Forwarding in Kernel
VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_INIT, &one, sizeof(one)), OT_EXIT_ERROR_ERRNO);
// Filter all ICMPv6 messages
ICMP6_FILTER_SETBLOCKALL(&filter);
VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_ICMPV6, ICMP6_FILTER, (void *)&filter, sizeof(filter)),
OT_EXIT_ERROR_ERRNO);
memset(&mif6ctl, 0, sizeof(mif6ctl));
mif6ctl.mif6c_flags = 0;
mif6ctl.vifc_threshold = 1;
mif6ctl.vifc_rate_limit = 0;
// Add Thread network interface to MIF
mif6ctl.mif6c_mifi = kMifIndexThread;
mif6ctl.mif6c_pifi = if_nametoindex(gNetifName);
VerifyOrDie(mif6ctl.mif6c_pifi > 0, OT_EXIT_ERROR_ERRNO);
VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MIF, &mif6ctl, sizeof(mif6ctl)),
OT_EXIT_ERROR_ERRNO);
// Add Backbone network interface to MIF
mif6ctl.mif6c_mifi = kMifIndexBackbone;
mif6ctl.mif6c_pifi = if_nametoindex(gBackboneNetifName);
VerifyOrDie(mif6ctl.mif6c_pifi > 0, OT_EXIT_ERROR_ERRNO);
VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MIF, &mif6ctl, sizeof(mif6ctl)),
OT_EXIT_ERROR_ERRNO);
}
void MulticastRoutingManager::FinalizeMulticastRouterSock(void)
{
VerifyOrExit(IsEnabled());
close(mMulticastRouterSock);
mMulticastRouterSock = -1;
exit:
return;
}
void MulticastRoutingManager::ProcessMulticastRouterMessages(void)
{
otError error = OT_ERROR_NONE;
char buf[sizeof(struct mrt6msg)];
int nr;
struct mrt6msg *mrt6msg;
Ip6::Address src, dst;
nr = read(mMulticastRouterSock, buf, sizeof(buf));
VerifyOrExit(nr >= static_cast<int>(sizeof(struct mrt6msg)), error = OT_ERROR_FAILED);
mrt6msg = reinterpret_cast<struct mrt6msg *>(buf);
VerifyOrExit(mrt6msg->im6_mbz == 0);
VerifyOrExit(mrt6msg->im6_msgtype == MRT6MSG_NOCACHE);
src.SetBytes(mrt6msg->im6_src.s6_addr);
dst.SetBytes(mrt6msg->im6_dst.s6_addr);
error = AddMulticastForwardingCache(src, dst, static_cast<MifIndex>(mrt6msg->im6_mif));
exit:
otLogResultPlat(error, "MulticastRoutingManager: %s", __FUNCTION__);
}
otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address &aSrcAddr,
const Ip6::Address &aGroupAddr,
MifIndex aIif)
{
otError error = OT_ERROR_NONE;
struct mf6cctl mf6cctl;
MifIndex forwardMif = kMifIndexNone;
VerifyOrExit(aIif == kMifIndexThread || aIif == kMifIndexBackbone, error = OT_ERROR_INVALID_ARGS);
ExpireMulticastForwardingCache();
if (aIif == kMifIndexBackbone)
{
// Forward multicast traffic from Backbone to Thread if the group address is subscribed by any Thread device via
// MLR.
if (HasMulticastListener(aGroupAddr))
{
forwardMif = kMifIndexThread;
}
}
else
{
// Forward multicast traffic from Thread to Backbone if multicast scope > kRealmLocalScope
// TODO: (MLR) allow scope configuration of outbound multicast routing
if (aGroupAddr.GetScope() > Ip6::Address::kRealmLocalScope)
{
forwardMif = kMifIndexBackbone;
}
}
memset(&mf6cctl, 0, sizeof(mf6cctl));
memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, aSrcAddr.GetBytes(), sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aGroupAddr.GetBytes(),
sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
mf6cctl.mf6cc_parent = aIif;
if (forwardMif != kMifIndexNone)
{
IF_SET(forwardMif, &mf6cctl.mf6cc_ifset);
}
// Note that kernel reports repetitive `MRT6MSG_NOCACHE` upcalls with a rate limit (e.g. once per 10s for Linux).
// Because of it, we need to add a "blocking" MFC even if there is no forwarding for this group address.
// When a Multicast Listener is later added, the "blocking" MFC will be altered to be a "forwarding" MFC so that
// corresponding multicast traffic can be forwarded instantly.
VerifyOrExit(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MFC, &mf6cctl, sizeof(mf6cctl)),
error = OT_ERROR_FAILED);
SaveMulticastForwardingCache(aSrcAddr, aGroupAddr, aIif, forwardMif);
exit:
otLogResultPlat(error, "MulticastRoutingManager: %s: add dynamic route: %s %s => %s %s", __FUNCTION__,
MifIndexToString(aIif), aSrcAddr.ToString().AsCString(), aGroupAddr.ToString().AsCString(),
MifIndexToString(forwardMif));
return error;
}
void MulticastRoutingManager::UnblockInboundMulticastForwardingCache(const Ip6::Address &aGroupAddr)
{
struct mf6cctl mf6cctl;
memset(&mf6cctl, 0, sizeof(mf6cctl));
memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aGroupAddr.GetBytes(),
sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
mf6cctl.mf6cc_parent = kMifIndexBackbone;
IF_SET(kMifIndexThread, &mf6cctl.mf6cc_ifset);
for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
{
otError error;
if (!mfc.IsValid() || mfc.mIif != kMifIndexBackbone || mfc.mOif == kMifIndexThread ||
mfc.mGroupAddr != aGroupAddr)
{
continue;
}
// Unblock this inbound route
memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, mfc.mSrcAddr.GetBytes(),
sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
error = (0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MFC, &mf6cctl, sizeof(mf6cctl)))
? OT_ERROR_NONE
: OT_ERROR_FAILED;
mfc.Set(kMifIndexBackbone, kMifIndexThread);
otLogResultPlat(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(mfc.mIif),
mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
MifIndexToString(kMifIndexThread));
}
}
void MulticastRoutingManager::RemoveInboundMulticastForwardingCache(const Ip6::Address &aGroupAddr)
{
for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
{
if (mfc.IsValid() && mfc.mIif == kMifIndexBackbone && mfc.mGroupAddr == aGroupAddr)
{
RemoveMulticastForwardingCache(mfc);
}
}
}
void MulticastRoutingManager::ExpireMulticastForwardingCache(void)
{
struct sioc_sg_req6 sioc_sg_req6;
uint64_t now = otPlatTimeGet();
struct mf6cctl mf6cctl;
VerifyOrExit(now >= mLastExpireTime + kMulticastForwardingCacheExpiringInterval * US_PER_S);
mLastExpireTime = now;
memset(&mf6cctl, 0, sizeof(mf6cctl));
memset(&sioc_sg_req6, 0, sizeof(sioc_sg_req6));
for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
{
if (mfc.IsValid() && mfc.mLastUseTime + kMulticastForwardingCacheExpireTimeout * US_PER_S < now)
{
if (!UpdateMulticastRouteInfo(mfc))
{
// The multicast route is expired
RemoveMulticastForwardingCache(mfc);
}
}
}
DumpMulticastForwardingCache();
exit:
return;
}
bool MulticastRoutingManager::UpdateMulticastRouteInfo(MulticastForwardingCache &aMfc) const
{
bool updated = false;
struct sioc_sg_req6 sioc_sg_req6;
memset(&sioc_sg_req6, 0, sizeof(sioc_sg_req6));
memcpy(sioc_sg_req6.src.sin6_addr.s6_addr, aMfc.mSrcAddr.GetBytes(), sizeof(sioc_sg_req6.src.sin6_addr.s6_addr));
memcpy(sioc_sg_req6.grp.sin6_addr.s6_addr, aMfc.mGroupAddr.GetBytes(), sizeof(sioc_sg_req6.grp.sin6_addr.s6_addr));
if (ioctl(mMulticastRouterSock, SIOCGETSGCNT_IN6, &sioc_sg_req6) != -1)
{
unsigned long validPktCnt;
otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s: bytecnt=%lu, pktcnt=%lu, wrong_if=%lu",
__FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
sioc_sg_req6.bytecnt, sioc_sg_req6.pktcnt, sioc_sg_req6.wrong_if);
validPktCnt = sioc_sg_req6.pktcnt - sioc_sg_req6.wrong_if;
if (validPktCnt != aMfc.mValidPktCnt)
{
aMfc.SetValidPktCnt(validPktCnt);
updated = true;
}
}
else
{
otLogWarnPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__,
aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), strerror(errno));
}
return updated;
}
const char *MulticastRoutingManager::MifIndexToString(MifIndex aMif)
{
const char *string = "Unknown";
switch (aMif)
{
case kMifIndexNone:
string = "None";
break;
case kMifIndexThread:
string = "Thread";
break;
case kMifIndexBackbone:
string = "Backbone";
break;
}
return string;
}
void MulticastRoutingManager::DumpMulticastForwardingCache(void) const
{
#if OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_DEBG
otLogDebgPlat("MulticastRoutingManager: ==================== MFC ENTRIES ====================");
for (const MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
{
if (mfc.IsValid())
{
otLogDebgPlat("MulticastRoutingManager: %s %s => %s %s", MifIndexToString(mfc.mIif),
mfc.mSrcAddr.ToString().AsCString(), mfc.mGroupAddr.ToString().AsCString(),
MifIndexToString(mfc.mOif));
}
}
otLogDebgPlat("MulticastRoutingManager: =====================================================");
#endif
}
void MulticastRoutingManager::HandleStateChange(otInstance *aInstance, otChangedFlags aFlags)
{
if (aFlags & OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE)
{
otBackboneRouterState state = otBackboneRouterGetState(aInstance);
switch (state)
{
case OT_BACKBONE_ROUTER_STATE_DISABLED:
case OT_BACKBONE_ROUTER_STATE_SECONDARY:
Disable();
break;
case OT_BACKBONE_ROUTER_STATE_PRIMARY:
Enable();
break;
}
}
}
void MulticastRoutingManager::MulticastForwardingCache::Set(MulticastRoutingManager::MifIndex aIif,
MulticastRoutingManager::MifIndex aOif)
{
mIif = aIif;
mOif = aOif;
mValidPktCnt = 0;
mLastUseTime = otPlatTimeGet();
}
void MulticastRoutingManager::MulticastForwardingCache::Set(const Ip6::Address &aSrcAddr,
const Ip6::Address &aGroupAddr,
MifIndex aIif,
MifIndex aOif)
{
mSrcAddr = aSrcAddr;
mGroupAddr = aGroupAddr;
Set(aIif, aOif);
}
void MulticastRoutingManager::MulticastForwardingCache::SetValidPktCnt(unsigned long aValidPktCnt)
{
mValidPktCnt = aValidPktCnt;
mLastUseTime = otPlatTimeGet();
}
void MulticastRoutingManager::SaveMulticastForwardingCache(const Ip6::Address & aSrcAddr,
const Ip6::Address & aGroupAddr,
MulticastRoutingManager::MifIndex aIif,
MulticastRoutingManager::MifIndex aOif)
{
MulticastForwardingCache *invalid = nullptr;
MulticastForwardingCache *oldest = nullptr;
for (MulticastForwardingCache &mfc : mMulticastForwardingCacheTable)
{
if (mfc.IsValid())
{
if (mfc.mSrcAddr == aSrcAddr && mfc.mGroupAddr == aGroupAddr)
{
mfc.Set(aIif, aOif);
ExitNow();
}
if (oldest == nullptr || mfc.mLastUseTime < oldest->mLastUseTime)
{
oldest = &mfc;
}
}
else if (invalid == nullptr)
{
invalid = &mfc;
}
}
if (invalid != nullptr)
{
invalid->Set(aSrcAddr, aGroupAddr, aIif, aOif);
}
else
{
RemoveMulticastForwardingCache(*oldest);
oldest->Set(aSrcAddr, aGroupAddr, aIif, aOif);
}
exit:
return;
}
void MulticastRoutingManager::RemoveMulticastForwardingCache(
MulticastRoutingManager::MulticastForwardingCache &aMfc) const
{
otError error;
struct mf6cctl mf6cctl;
memset(&mf6cctl, 0, sizeof(mf6cctl));
memcpy(mf6cctl.mf6cc_origin.sin6_addr.s6_addr, aMfc.mSrcAddr.GetBytes(),
sizeof(mf6cctl.mf6cc_origin.sin6_addr.s6_addr));
memcpy(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr, aMfc.mGroupAddr.GetBytes(),
sizeof(mf6cctl.mf6cc_mcastgrp.sin6_addr.s6_addr));
mf6cctl.mf6cc_parent = aMfc.mIif;
error = (0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_DEL_MFC, &mf6cctl, sizeof(mf6cctl)))
? OT_ERROR_NONE
: OT_ERROR_FAILED;
otLogResultPlat(error, "MulticastRoutingManager: %s: %s %s => %s %s", __FUNCTION__, MifIndexToString(aMfc.mIif),
aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(),
MifIndexToString(aMfc.mOif));
aMfc.Erase();
}
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
+180
View File
@@ -0,0 +1,180 @@
/*
* 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.
*/
#ifndef OT_POSIX_PLATFORM_MULTICAST_ROUTING_HPP_
#define OT_POSIX_PLATFORM_MULTICAST_ROUTING_HPP_
#include "openthread-posix-config.h"
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#include <stdio.h>
#include <stdlib.h>
#include <openthread/backbone_router_ftd.h>
#include <openthread/openthread-system.h>
#include "platform-posix.h"
#include "core/common/non_copyable.hpp"
#include "core/net/ip6_address.hpp"
#include "lib/url/url.hpp"
namespace ot {
namespace Posix {
/**
* This class implements Multicast Routing management.
*
*/
class MulticastRoutingManager : private NonCopyable
{
public:
/**
* This constructor initializes a Multicast Routing manager instance.
*
*/
explicit MulticastRoutingManager()
: mLastExpireTime(0)
, mMulticastRouterSock(-1)
, mInstance(nullptr)
{
}
/**
* This method initializes the Multicast Routing manager.
*
* @param[in] aInstance A pointer to an OpenThread instance.
*
*/
void Init(otInstance *aInstance);
/**
* This method updates the fd_set and timeout for mainloop.
*
* @param[inout] aReadFdSet A reference to fd_set for polling read.
* @param[inout] aMaxFd A reference to the current max fd in fd_sets.
*
*/
void UpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) const;
/**
* This method performs Multicast Routing processing.
*
* @param[in] aReadFdSet A reference to read file descriptors.
*
*/
void Process(const fd_set &aReadFdSet);
/**
* This method handles Thread state changes.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aFlags Flags that denote the state change events.
*
*/
void HandleStateChange(otInstance *aInstance, otChangedFlags aFlags);
private:
enum
{
kMulticastForwardingCacheExpireTimeout = 300, //< Expire timeout of Multicast Forwarding Cache (in seconds)
kMulticastForwardingCacheExpiringInterval = 60, //< Expire interval of Multicast Forwarding Cache (in seconds)
kMulitcastForwardingCacheTableSize =
OPENTHREAD_POSIX_CONFIG_MAX_MULTICAST_FORWARDING_CACHE_TABLE, //< The max size of MFC table.
};
enum MifIndex : uint8_t
{
kMifIndexNone = 0xff,
kMifIndexThread = 0,
kMifIndexBackbone = 1,
};
class MulticastForwardingCache
{
friend class MulticastRoutingManager;
private:
MulticastForwardingCache()
: mIif(kMifIndexNone)
{
}
bool IsValid() const { return mIif != kMifIndexNone; }
void Set(MifIndex aIif, MifIndex aOif);
void Set(const Ip6::Address &aSrcAddr, const Ip6::Address &aGroupAddr, MifIndex aIif, MifIndex aOif);
void Erase(void) { mIif = kMifIndexNone; }
void SetValidPktCnt(unsigned long aValidPktCnt);
Ip6::Address mSrcAddr;
Ip6::Address mGroupAddr;
uint64_t mLastUseTime;
unsigned long mValidPktCnt;
MifIndex mIif;
MifIndex mOif;
};
void Enable(void);
void Disable(void);
void Add(const Ip6::Address &aAddress);
void Remove(const Ip6::Address &aAddress);
bool HasMulticastListener(const Ip6::Address &aAddress) const;
bool IsEnabled(void) const { return mMulticastRouterSock >= 0; }
void InitMulticastRouterSock(void);
void FinalizeMulticastRouterSock(void);
void ProcessMulticastRouterMessages(void);
otError AddMulticastForwardingCache(const Ip6::Address &aSrcAddr, const Ip6::Address &aGroupAddr, MifIndex aIif);
void SaveMulticastForwardingCache(const Ip6::Address &aSrcAddr,
const Ip6::Address &aGroupAddr,
MifIndex aIif,
MifIndex aOif);
void UnblockInboundMulticastForwardingCache(const Ip6::Address &aGroupAddr);
void RemoveInboundMulticastForwardingCache(const Ip6::Address &aGroupAddr);
void ExpireMulticastForwardingCache(void);
bool UpdateMulticastRouteInfo(MulticastForwardingCache &aMfc) const;
void RemoveMulticastForwardingCache(MulticastForwardingCache &aMfc) const;
static const char *MifIndexToString(MifIndex aMif);
void DumpMulticastForwardingCache(void) const;
static void HandleBackboneMulticastListenerEvent(void * aContext,
otBackboneRouterMulticastListenerEvent aEvent,
const otIp6Address * aAddress);
void HandleBackboneMulticastListenerEvent(otBackboneRouterMulticastListenerEvent aEvent,
const Ip6::Address & aAddress);
MulticastForwardingCache mMulticastForwardingCacheTable[kMulitcastForwardingCacheTableSize];
uint64_t mLastExpireTime;
int mMulticastRouterSock;
otInstance * mInstance;
};
} // namespace Posix
} // namespace ot
#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#endif // OT_POSIX_PLATFORM_MULTICAST_ROUTING_HPP_
+2 -3
View File
@@ -537,11 +537,11 @@ static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsA
}
}
static void processStateChange(otChangedFlags aFlags, void *aContext)
void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags)
{
if (OT_CHANGED_THREAD_NETIF_STATE & aFlags)
{
UpdateLink(static_cast<otInstance *>(aContext));
UpdateLink(aInstance);
}
}
@@ -1500,7 +1500,6 @@ void platformNetifInit(otInstance *aInstance, const char *aInterfaceName)
otIcmp6SetEchoMode(aInstance, OT_ICMP6_ECHO_HANDLER_DISABLED);
otIp6SetReceiveCallback(aInstance, processReceive, aInstance);
otIp6SetAddressCallback(aInstance, processAddressChange, aInstance);
SuccessOrDie(otSetStateChangedCallback(aInstance, processStateChange, aInstance));
#if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED
otIp6SetMulticastPromiscuousEnabled(aInstance, true);
#endif
@@ -101,6 +101,16 @@
#define OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE 0
#endif
/**
* @def OPENTHREAD_POSIX_CONFIG_MAX_MULTICAST_FORWARDING_CACHE_TABLE
*
* This setting configures the maximum number of Multicast Forwarding Cache table for POSIX native multicast routing.
*
*/
#ifndef OPENTHREAD_POSIX_CONFIG_MAX_MULTICAST_FORWARDING_CACHE_TABLE
#define OPENTHREAD_POSIX_CONFIG_MAX_MULTICAST_FORWARDING_CACHE_TABLE (OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS * 10)
#endif
#ifdef __APPLE__
/**
+35 -2
View File
@@ -270,6 +270,15 @@ void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *a
*/
void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet);
/**
* This function performs notifies state changes to platform netif.
*
* @param[in] aInstance A pointer to the OpenThread instance.
* @param[in] aFlags Flags that denote the state change events.
*
*/
void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags);
/**
* This function initialize virtual time simulation.
*
@@ -407,7 +416,6 @@ extern char gNetifName[IFNAMSIZ];
*/
extern unsigned int gNetifIndex;
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
/**
* This function initializes platform Backbone network.
*
@@ -417,6 +425,32 @@ extern unsigned int gNetifIndex;
*/
void platformBackboneInit(otInstance *aInstance, const char *aInterfaceName);
/**
* This function updates the file descriptor sets with file descriptors used by the platform Backbone network.
*
* @param[inout] aReadFdSet A reference to the read file descriptors.
* @param[inout] aMaxFd A reference to the max file descriptor.
*
*/
void platformBackboneUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd);
/**
* This function performs platform Backbone network processing.
*
* @param[in] aReadFdSet A reference to the read file descriptors.
*
*/
void platformBackboneProcess(const fd_set &aReadSet);
/**
* This function performs notifies state changes to platform Backbone network.
*
* @param[in] aInstance A pointer to the OpenThread instance.
* @param[in] aFlags Flags that denote the state change events.
*
*/
void platformBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags);
/**
* The name of Backbone network interface.
*
@@ -428,7 +462,6 @@ extern char gBackboneNetifName[IFNAMSIZ];
*
*/
extern unsigned int gBackboneNetifIndex;
#endif
#ifdef __cplusplus
}
+28
View File
@@ -46,6 +46,24 @@
#include "common/code_utils.hpp"
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
static void processStateChange(otChangedFlags aFlags, void *aContext)
{
otInstance *instance = static_cast<otInstance *>(aContext);
OT_UNUSED_VARIABLE(instance);
OT_UNUSED_VARIABLE(aFlags);
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
platformNetifStateChange(instance, aFlags);
#endif
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
platformBackboneStateChange(instance, aFlags);
#endif
}
#endif
otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
{
otInstance * instance = nullptr;
@@ -82,6 +100,10 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
platformUdpInit(aPlatformConfig->mInterfaceName);
#endif
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE || OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
SuccessOrDie(otSetStateChangedCallback(instance, processStateChange, instance));
#endif
return instance;
}
@@ -142,6 +164,9 @@ void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop)
platformNetifUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet,
&aMainloop->mMaxFd);
#endif
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
platformBackboneUpdateFdSet(aMainloop->mReadFdSet, aMainloop->mMaxFd);
#endif
#if OPENTHREAD_POSIX_VIRTUAL_TIME
virtualTimeUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mMaxFd,
&aMainloop->mTimeout);
@@ -214,6 +239,9 @@ void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMa
#if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
platformUdpProcess(aInstance, &aMainloop->mReadFdSet);
#endif
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
platformBackboneProcess(aMainloop->mReadFdSet);
#endif
}
#if OPENTHREAD_CONFIG_OTNS_ENABLE