[posix] handle dynamic infra if status (#6186)

This commit includes below changes:

- handles dynamic infrastructure interface status changes. The Routing
  Manager will be started if the infra interface status turns to be
  RUNNING and a valid link-local address is present. Otherwise, the
  Routing Manager will be stopped.

- changes the default status of the Routing Manager from enabled to
  disabled and it is initially enabled for the posix platform.

- the posix implementation now doesn't require the presence of the
  link-local address at initialization stage. In this case, the
  Routing Manager will not be started before a link-local address is
  added to the infra interface.

- add CI tests for the new behavior.
This commit is contained in:
kangping
2021-02-25 13:19:31 -08:00
committed by GitHub
parent b45f287629
commit da0d9f66b6
12 changed files with 432 additions and 106 deletions
+17 -7
View File
@@ -57,20 +57,30 @@ extern "C" {
* *
* @note This method MUST be called before any other otBorderRouting* APIs. * @note This method MUST be called before any other otBorderRouting* APIs.
* *
* @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aInfraIfIndex The infrastructure interface index. * @param[in] aInfraIfIndex The infrastructure interface index.
* @param[in] aInfraIfIsRunning A boolean that indicates whether the infrastructure
* interface is running.
* @param[in] aInfraIfLinkLocalAddress A pointer to the IPv6 link-local address of the infrastructure
* interface. NULL if the IPv6 link-local address is missing.
* *
* @retval OT_ERROR_NONE Successfully started the Border Routing manager on given infrastructure. * @retval OT_ERROR_NONE Successfully started the Border Routing Manager on given infrastructure.
* @retval OT_ERROR_INVALID_ARGS The index of the infra interface is not valid. * @retval OT_ERROR_INVALID_STATE The Border Routing Manager has already been initialized.
* @retval OT_ERROR_FAILED Internal failure. This is usually failed to generate random prefixes. * @retval OT_ERROR_INVALID_ARGS The index or the IPv6 link-local address of the infra interface is not valid.
* @retval OT_ERROR_FAILED Internal failure. Usually due to failure in generating random prefixes.
*
* @sa otPlatInfraIfStateChanged.
* *
*/ */
otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex); otError otBorderRoutingInit(otInstance * aInstance,
uint32_t aInfraIfIndex,
bool aInfraIfIsRunning,
const otIp6Address *aInfraIfLinkLocalAddress);
/** /**
* This method enables/disables the Border Routing Manager. * This method enables/disables the Border Routing Manager.
* *
* @note The Border Routing Manager is enabled by default. * @note The Border Routing Manager is disabled by default.
* *
* @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aEnabled A boolean to enable/disable the routing manager. * @param[in] aEnabled A boolean to enable/disable the routing manager.
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs. * @note This number versions both OpenThread platform and user APIs.
* *
*/ */
#define OPENTHREAD_API_VERSION (79) #define OPENTHREAD_API_VERSION (80)
/** /**
* @addtogroup api-instance * @addtogroup api-instance
+23 -10
View File
@@ -46,16 +46,6 @@
extern "C" { extern "C" {
#endif #endif
/**
* This method returns the IPv6 link-local address of given infrastructure interface.
*
* @param[in] aInfraIfIndex The index of the infrastructure interface.
*
* @returns A pointer to the IPv6 link-local address. NULL if no valid IPv6 link-local address found.
*
*/
const otIp6Address *otPlatInfraIfGetLinkLocalAddress(uint32_t aInfraIfIndex);
/** /**
* This method sends an ICMPv6 Neighbor Discovery message on given infrastructure interface. * This method sends an ICMPv6 Neighbor Discovery message on given infrastructure interface.
* *
@@ -100,6 +90,29 @@ extern void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance,
const uint8_t * aBuffer, const uint8_t * aBuffer,
uint16_t aBufferLength); uint16_t aBufferLength);
/**
* The infra interface driver calls this method to notify OpenThread
* of the interface state changes.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aInfraIfIndex The index of the infrastructure interface.
* @param[in] aIsRunning A boolean that indicates whether the infrastructure
* interface is running.
* @param[in] aLinkLocalAddress A pointer to the IPv6 link-local address of the infrastructure
* interface. NULL if the IPv6 link-local address is lost.
*
* @retval OT_ERROR_NONE Successfully updated the infra interface status.
* @retval OT_ERROR_INVALID_STATE The Routing Manager is not initialized.
* @retval OT_ERROR_INVALID_ARGS The @p aInfraIfIndex doesn't match the infra interface the
* Routing Manager are initialized with, or the @p aLinkLocalAddress
* is not a valid IPv6 link-local address.
*
*/
extern otError otPlatInfraIfStateChanged(otInstance * aInstance,
uint32_t aInfraIfIndex,
bool aIsRunning,
const otIp6Address *aLinkLocalAddress);
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif
+6 -2
View File
@@ -44,11 +44,15 @@
using namespace ot; using namespace ot;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex) otError otBorderRoutingInit(otInstance * aInstance,
uint32_t aInfraIfIndex,
bool aInfraIfIsRunning,
const otIp6Address *aInfraIfLinkLocalAddress)
{ {
Instance &instance = *static_cast<Instance *>(aInstance); Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<BorderRouter::RoutingManager>().Init(aInfraIfIndex); return instance.Get<BorderRouter::RoutingManager>().Init(
aInfraIfIndex, aInfraIfIsRunning, static_cast<const Ip6::Address *>(aInfraIfLinkLocalAddress));
} }
otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled) otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled)
@@ -52,4 +52,16 @@ extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance,
instance.Get<BorderRouter::RoutingManager>().RecvIcmp6Message( instance.Get<BorderRouter::RoutingManager>().RecvIcmp6Message(
aInfraIfIndex, static_cast<const Ip6::Address &>(*aSrcAddress), aBuffer, aBufferLength); aInfraIfIndex, static_cast<const Ip6::Address &>(*aSrcAddress), aBuffer, aBufferLength);
} }
extern "C" otError otPlatInfraIfStateChanged(otInstance * aInstance,
uint32_t aInfraIfIndex,
bool aIsRunning,
const otIp6Address *aLinkLocalAddress)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<BorderRouter::RoutingManager>().HandleInfraIfStateChanged(
aInfraIfIndex, aIsRunning, static_cast<const Ip6::Address *>(aLinkLocalAddress));
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+61 -23
View File
@@ -60,8 +60,9 @@ namespace BorderRouter {
RoutingManager::RoutingManager(Instance &aInstance) RoutingManager::RoutingManager(Instance &aInstance)
: InstanceLocator(aInstance) : InstanceLocator(aInstance)
, mIsRunning(false) , mIsRunning(false)
, mIsEnabled(false)
, mInfraIfIsRunning(false)
, mInfraIfIndex(0) , mInfraIfIndex(0)
, mEnabled(true) // The routing manager is by default enabled.
, mAdvertisedOmrPrefixNum(0) , mAdvertisedOmrPrefixNum(0)
, mAdvertisedOnLinkPrefix(nullptr) , mAdvertisedOnLinkPrefix(nullptr)
, mDiscoveredPrefixNum(0) , mDiscoveredPrefixNum(0)
@@ -71,6 +72,8 @@ RoutingManager::RoutingManager(Instance &aInstance)
, mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer) , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer)
, mRouterSolicitCount(0) , mRouterSolicitCount(0)
{ {
mInfraIfLinkLocalAddress.Clear();
mLocalOmrPrefix.Clear(); mLocalOmrPrefix.Clear();
memset(mAdvertisedOmrPrefixes, 0, sizeof(mAdvertisedOmrPrefixes)); memset(mAdvertisedOmrPrefixes, 0, sizeof(mAdvertisedOmrPrefixes));
@@ -79,11 +82,13 @@ RoutingManager::RoutingManager(Instance &aInstance)
memset(mDiscoveredPrefixes, 0, sizeof(mDiscoveredPrefixes)); memset(mDiscoveredPrefixes, 0, sizeof(mDiscoveredPrefixes));
} }
otError RoutingManager::Init(uint32_t aInfraIfIndex) otError RoutingManager::Init(uint32_t aInfraIfIndex,
bool aInfraIfIsRunning,
const Ip6::Address *aInfraIfLinkLocalAddress)
{ {
otError error; otError error;
OT_ASSERT(!IsInitialized()); VerifyOrExit(!IsInitialized(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(aInfraIfIndex > 0, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(aInfraIfIndex > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix()); SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix());
@@ -91,7 +96,14 @@ otError RoutingManager::Init(uint32_t aInfraIfIndex)
mInfraIfIndex = aInfraIfIndex; mInfraIfIndex = aInfraIfIndex;
// Initialize the infra interface status.
SuccessOrExit(error = HandleInfraIfStateChanged(mInfraIfIndex, aInfraIfIsRunning, aInfraIfLinkLocalAddress));
exit: exit:
if (error != OT_ERROR_NONE)
{
mInfraIfIndex = 0;
}
return error; return error;
} }
@@ -101,18 +113,10 @@ otError RoutingManager::SetEnabled(bool aEnabled)
VerifyOrExit(IsInitialized(), error = OT_ERROR_INVALID_STATE); VerifyOrExit(IsInitialized(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(aEnabled != mEnabled); VerifyOrExit(aEnabled != mIsEnabled);
mEnabled = aEnabled; mIsEnabled = aEnabled;
EvaluateState();
if (!mEnabled)
{
Stop();
}
else if (Get<Mle::MleRouter>().IsAttached())
{
Start();
}
exit: exit:
return error; return error;
@@ -172,6 +176,18 @@ exit:
return error; return error;
} }
void RoutingManager::EvaluateState(void)
{
if (mIsEnabled && Get<Mle::MleRouter>().IsAttached() && mInfraIfIsRunning && mInfraIfLinkLocalAddress.IsLinkLocal())
{
Start();
}
else
{
Stop();
}
}
void RoutingManager::Start(void) void RoutingManager::Start(void)
{ {
if (!mIsRunning) if (!mIsRunning)
@@ -232,7 +248,7 @@ void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex,
VerifyOrExit(IsInitialized() && mIsRunning, error = OT_ERROR_DROP); VerifyOrExit(IsInitialized() && mIsRunning, error = OT_ERROR_DROP);
VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP); VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP);
infraLinkLocalAddr = static_cast<const Ip6::Address *>(otPlatInfraIfGetLinkLocalAddress(mInfraIfIndex)); infraLinkLocalAddr = static_cast<const Ip6::Address *>(&mInfraIfLinkLocalAddress);
// Drop any ICMPv6 messages sent from myself. // Drop any ICMPv6 messages sent from myself.
VerifyOrExit(infraLinkLocalAddr != nullptr && aSrcAddress != *infraLinkLocalAddr, error = OT_ERROR_DROP); VerifyOrExit(infraLinkLocalAddr != nullptr && aSrcAddress != *infraLinkLocalAddr, error = OT_ERROR_DROP);
@@ -260,20 +276,42 @@ exit:
} }
} }
otError RoutingManager::HandleInfraIfStateChanged(uint32_t aInfraIfIndex,
bool aIsRunning,
const Ip6::Address *aLinkLocalAddress)
{
otError error = OT_ERROR_NONE;
VerifyOrExit(IsInitialized(), error = OT_ERROR_INVALID_STATE);
VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_INVALID_ARGS);
VerifyOrExit(aLinkLocalAddress == nullptr || aLinkLocalAddress->IsLinkLocal(), error = OT_ERROR_INVALID_ARGS);
otLogInfoBr("infra interface state changed: %s, link-local-addr=%s", aIsRunning ? "RUNNING" : "NOT RUNNING",
(aLinkLocalAddress != nullptr) ? aLinkLocalAddress->ToString().AsCString() : "(null)");
mInfraIfIsRunning = aIsRunning;
if (aLinkLocalAddress == nullptr)
{
mInfraIfLinkLocalAddress.Clear();
}
else
{
mInfraIfLinkLocalAddress = *aLinkLocalAddress;
}
EvaluateState();
exit:
return error;
}
void RoutingManager::HandleNotifierEvents(Events aEvents) void RoutingManager::HandleNotifierEvents(Events aEvents)
{ {
VerifyOrExit(IsInitialized() && IsEnabled()); VerifyOrExit(IsInitialized() && IsEnabled());
if (aEvents.Contains(kEventThreadRoleChanged)) if (aEvents.Contains(kEventThreadRoleChanged))
{ {
if (Get<Mle::MleRouter>().IsAttached()) EvaluateState();
{
Start();
}
else
{
Stop();
}
} }
if (aEvents.Contains(kEventThreadNetdataChanged)) if (aEvents.Contains(kEventThreadNetdataChanged))
+46 -4
View File
@@ -79,12 +79,16 @@ public:
* This method initializes the routing manager on given infrastructure interface. * This method initializes the routing manager on given infrastructure interface.
* *
* @param[in] aInfraIfIndex An infrastructure network interface index. * @param[in] aInfraIfIndex An infrastructure network interface index.
* @param[in] aInfraIfIsRunning A boolean that indicates whether the infrastructure
* interface is running.
* @param[in] aInfraIfLinkLocalAddress A pointer to the IPv6 link-local address of the infrastructure
* interface. NULL if the IPv6 link-local address is missing.
* *
* @retval OT_ERROR_NONE Successfully started the routing manager. * @retval OT_ERROR_NONE Successfully started the routing manager.
* @retval OT_ERROR_INVALID_ARGS The index of the infra interface is not valid. * @retval OT_ERROR_INVALID_ARGS The index of the infra interface is not valid.
* *
*/ */
otError Init(uint32_t aInfraIfIndex); otError Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning, const Ip6::Address *aInfraIfLinkLocalAddress);
/** /**
* This method enables/disables the Border Routing Manager. * This method enables/disables the Border Routing Manager.
@@ -115,6 +119,24 @@ public:
const uint8_t * aBuffer, const uint8_t * aBuffer,
uint16_t aBufferLength); uint16_t aBufferLength);
/**
* This method handles infrastructure interface state changes.
*
* @param[in] aInfraIfIndex The index of the infrastructure interface.
* @param[in] aIsRunning A boolean that indicates whether the infrastructure
* interface is running.
* @param[in] aLinkLocalAddress A pointer to the IPv6 link local address of the infrastructure
* interface. NULL if the IPv6 link local address is lost.
*
* @retval OT_ERROR_NONE Successfully updated the infra interface status.
* @retval OT_ERROR_INVALID_STATE The Routing Manager is not initialized.
* @retval OT_ERROR_INVALID_ARGS The @p aInfraIfIndex doesn't match the infra interface the
* Routing Manager are initialized with, or the @p aLinkLocalAddress
* is not a valid IPv6 link-local address.
*
*/
otError HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning, const Ip6::Address *aLinkLocalAddress);
private: private:
enum : uint16_t enum : uint16_t
{ {
@@ -161,11 +183,12 @@ private:
bool mIsOnLinkPrefix; bool mIsOnLinkPrefix;
}; };
void EvaluateState(void);
void Start(void); void Start(void);
void Stop(void); void Stop(void);
void HandleNotifierEvents(Events aEvents); void HandleNotifierEvents(Events aEvents);
bool IsInitialized(void) const { return mInfraIfIndex != 0; } bool IsInitialized(void) const { return mInfraIfIndex != 0; }
bool IsEnabled(void) const { return mEnabled; } bool IsEnabled(void) const { return mIsEnabled; }
otError LoadOrGenerateRandomOmrPrefix(void); otError LoadOrGenerateRandomOmrPrefix(void);
otError LoadOrGenerateRandomOnLinkPrefix(void); otError LoadOrGenerateRandomOnLinkPrefix(void);
@@ -210,9 +233,28 @@ private:
static bool ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum); static bool ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum);
static uint32_t GetPrefixExpireDelay(uint32_t aValidLifetime); static uint32_t GetPrefixExpireDelay(uint32_t aValidLifetime);
bool mIsRunning; // Indicates whether the Routing Manager is running (started).
bool mIsRunning;
// Indicates whether the Routing manager is enabled.
// The Routing Manager will be stopped if we are
// disabled.
bool mIsEnabled;
// Indicates whether the infra interface is running.
// The Routing Manager will be stopped when the
// Infra interface is not running.
bool mInfraIfIsRunning;
// The index of the infra interface on which Router
// Advertisement messages will be sent.
uint32_t mInfraIfIndex; uint32_t mInfraIfIndex;
bool mEnabled;
// The IPv6 link-local address of the infra interface.
// It's UNSPECIFIED if no valid IPv6 link-local address
// is associated with the infra interface and the Routing
// Manager will be stopped.
Ip6::Address mInfraIfLinkLocalAddress;
// The OMR prefix loaded from local persistent storage or randomly generated // The OMR prefix loaded from local persistent storage or randomly generated
// if non is found in persistent storage. // if non is found in persistent storage.
+164 -44
View File
@@ -45,24 +45,30 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/icmp6.h> #include <netinet/icmp6.h>
// clang-format on // clang-format on
#include <sys/ioctl.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#ifdef __linux__
#include <linux/rtnetlink.h>
#endif
#include <openthread/border_router.h>
#include <openthread/platform/infra_if.h> #include <openthread/platform/infra_if.h>
#include "common/code_utils.hpp" #include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "lib/platform/exit_code.h" #include "lib/platform/exit_code.h"
static char sInfraIfName[IFNAMSIZ]; static char sInfraIfName[IFNAMSIZ];
static uint32_t sInfraIfIndex = 0; static uint32_t sInfraIfIndex = 0;
static int sInfraIfIcmp6Socket = -1; static int sInfraIfIcmp6Socket = -1;
static int sNetLinkSocket = -1;
static otIp6Address sInfraIfLinkLocalAddr; static otIp6Address sInfraIfLinkLocalAddr;
const otIp6Address *otPlatInfraIfGetLinkLocalAddress(uint32_t aInfraIfIndex) static int CreateIcmp6Socket(void);
{ static int CreateNetLinkSocket(void);
VerifyOrDie(aInfraIfIndex == sInfraIfIndex, OT_EXIT_FAILURE); static void ReceiveNetLinkMessage(otInstance *aInstance);
return &sInfraIfLinkLocalAddr; static void ReceiveIcmp6Message(otInstance *aInstance);
}
otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
const otIp6Address *aDestAddress, const otIp6Address *aDestAddress,
@@ -138,10 +144,10 @@ exit:
return error; return error;
} }
static otError InitLinkLocalAddress(void) const otIp6Address *platformInfraIfGetLinkLocalAddress(void)
{ {
otError error; const otIp6Address *ret = nullptr;
struct ifaddrs *ifAddrs = nullptr; struct ifaddrs * ifAddrs = nullptr;
VerifyOrDie(getifaddrs(&ifAddrs) != -1, OT_EXIT_ERROR_ERRNO); VerifyOrDie(getifaddrs(&ifAddrs) != -1, OT_EXIT_ERROR_ERRNO);
@@ -158,51 +164,50 @@ static otError InitLinkLocalAddress(void)
if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr)) if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr))
{ {
memcpy(&sInfraIfLinkLocalAddr, &ip6Addr->sin6_addr, sizeof(sInfraIfLinkLocalAddr)); memcpy(&sInfraIfLinkLocalAddr, &ip6Addr->sin6_addr, sizeof(sInfraIfLinkLocalAddr));
ExitNow(error = OT_ERROR_NONE); ExitNow(ret = &sInfraIfLinkLocalAddr);
} }
} }
otLogCritPlat("cannot find IPv6 link-local address for interface %s", sInfraIfName); otLogWarnPlat("cannot find IPv6 link-local address for interface %s", sInfraIfName);
error = OT_ERROR_NOT_FOUND;
exit: exit:
freeifaddrs(ifAddrs); freeifaddrs(ifAddrs);
return error; return ret;
} }
void platformInfraIfInit(otInstance *aInstance, const char *aIfName) bool platformInfraIfIsRunning(void)
{ {
OT_UNUSED_VARIABLE(aInstance); int sock;
struct ifreq ifReq;
OT_ASSERT(sInfraIfIndex != 0);
sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP);
VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
memset(&ifReq, 0, sizeof(ifReq));
strncpy(ifReq.ifr_name, sInfraIfName, sizeof(ifReq.ifr_name));
VerifyOrDie(ioctl(sock, SIOCGIFFLAGS, &ifReq) != -1, OT_EXIT_ERROR_ERRNO);
close(sock);
return (ifReq.ifr_flags & IFF_RUNNING);
}
static int CreateIcmp6Socket(void)
{
int sock; int sock;
int rval;
struct icmp6_filter filter; struct icmp6_filter filter;
ssize_t rval;
const int kEnable = 1; const int kEnable = 1;
const int kIpv6ChecksumOffset = 2; const int kIpv6ChecksumOffset = 2;
const int kHopLimit = 255; const int kHopLimit = 255;
uint32_t ifIndex = 0;
if (strlen(aIfName) >= sizeof(sInfraIfName))
{
otLogCritPlat("infra interface name '%s' is too long", aIfName);
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
strcpy(sInfraIfName, aIfName);
// Initializes the infra interface.
ifIndex = if_nametoindex(aIfName);
if (ifIndex == 0)
{
otLogCritPlat("failed to get the index for infra interface %s: %s", aIfName, strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
sInfraIfIndex = ifIndex;
// Initializes the ICMPv6 socket. // Initializes the ICMPv6 socket.
sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO); VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
// Only accept router advertisements and router solicits. // Only accept router advertisements and solicitations.
ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
@@ -231,15 +236,43 @@ void platformInfraIfInit(otInstance *aInstance, const char *aIfName)
rval = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kHopLimit, sizeof(kHopLimit)); rval = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO); VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
return sock;
}
uint32_t platformInfraIfInit(otInstance *aInstance, const char *aIfName)
{
OT_UNUSED_VARIABLE(aInstance);
ssize_t rval;
uint32_t ifIndex = 0;
if (strlen(aIfName) >= sizeof(sInfraIfName))
{
otLogCritPlat("infra interface name '%s' is too long", aIfName);
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
strcpy(sInfraIfName, aIfName);
// Initializes the infra interface.
ifIndex = if_nametoindex(aIfName);
if (ifIndex == 0)
{
otLogCritPlat("failed to get the index for infra interface %s", aIfName);
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
sInfraIfIndex = ifIndex;
sInfraIfIcmp6Socket = CreateIcmp6Socket();
#ifdef __linux__ #ifdef __linux__
rval = setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, sInfraIfName, strlen(sInfraIfName)); rval = setsockopt(sInfraIfIcmp6Socket, SOL_SOCKET, SO_BINDTODEVICE, sInfraIfName, strlen(sInfraIfName));
#else // __NetBSD__ || __FreeBSD__ || __APPLE__ #else // __NetBSD__ || __FreeBSD__ || __APPLE__
rval = setsockopt(sock, IPPROTO_IP, IP_BOUND_IF, &sInfraIfIndex, sizeof(sInfraIfIndex)); rval = setsockopt(sInfraIfIcmp6Socket, IPPROTO_IP, IP_BOUND_IF, &sInfraIfIndex, sizeof(sInfraIfIndex));
#endif // __linux__ #endif // __linux__
VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO); VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
sInfraIfIcmp6Socket = sock; sNetLinkSocket = CreateNetLinkSocket();
SuccessOrDie(InitLinkLocalAddress());
return sInfraIfIndex;
} }
void platformInfraIfDeinit(void) void platformInfraIfDeinit(void)
@@ -250,21 +283,97 @@ void platformInfraIfDeinit(void)
sInfraIfIcmp6Socket = -1; sInfraIfIcmp6Socket = -1;
} }
if (sNetLinkSocket != -1)
{
close(sNetLinkSocket);
sNetLinkSocket = -1;
}
sInfraIfIndex = 0; sInfraIfIndex = 0;
} }
void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd) void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd)
{ {
VerifyOrExit(sInfraIfIcmp6Socket != -1); VerifyOrExit(sInfraIfIcmp6Socket != -1);
VerifyOrExit(sNetLinkSocket != -1);
FD_SET(sInfraIfIcmp6Socket, &aReadFdSet); FD_SET(sInfraIfIcmp6Socket, &aReadFdSet);
aMaxFd = OT_MAX(aMaxFd, sInfraIfIcmp6Socket); aMaxFd = OT_MAX(aMaxFd, sInfraIfIcmp6Socket);
FD_SET(sNetLinkSocket, &aReadFdSet);
aMaxFd = OT_MAX(aMaxFd, sNetLinkSocket);
exit: exit:
return; return;
} }
void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet) // Create a net-link socket that subscribes to link & addresses events.
static int CreateNetLinkSocket(void)
{
int sock;
int rval;
struct sockaddr_nl addr;
sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO);
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR;
addr.nl_pid = getpid();
rval = bind(sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr));
VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO);
return sock;
}
static void ReceiveNetLinkMessage(otInstance *aInstance)
{
const size_t kMaxNetLinkBufSize = 8192;
ssize_t len;
union
{
nlmsghdr mHeader;
uint8_t mBuffer[kMaxNetLinkBufSize];
} msgBuffer;
len = recv(sNetLinkSocket, msgBuffer.mBuffer, sizeof(msgBuffer.mBuffer), 0);
if (len < 0)
{
otLogCritPlat("failed to receive netlink message: %s", strerror(errno));
ExitNow();
}
for (struct nlmsghdr *header = &msgBuffer.mHeader; NLMSG_OK(header, static_cast<size_t>(len));
header = NLMSG_NEXT(header, len))
{
switch (header->nlmsg_type)
{
case RTM_NEWLINK:
case RTM_DELLINK:
case RTM_NEWADDR:
case RTM_DELADDR:
SuccessOrDie(otPlatInfraIfStateChanged(aInstance, sInfraIfIndex, platformInfraIfIsRunning(),
platformInfraIfGetLinkLocalAddress()));
break;
case NLMSG_ERROR:
{
struct nlmsgerr *errMsg = reinterpret_cast<struct nlmsgerr *>(NLMSG_DATA(header));
OT_UNUSED_VARIABLE(errMsg);
otLogWarnPlat("netlink NLMSG_ERROR response: seq=%u, error=%d", header->nlmsg_seq, errMsg->error);
}
default:
break;
}
}
exit:
return;
}
static void ReceiveIcmp6Message(otInstance *aInstance)
{ {
otError error = OT_ERROR_NONE; otError error = OT_ERROR_NONE;
uint8_t buffer[1500]; uint8_t buffer[1500];
@@ -281,10 +390,6 @@ void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet)
struct sockaddr_in6 srcAddr; struct sockaddr_in6 srcAddr;
struct in6_addr dstAddr; struct in6_addr dstAddr;
// It is not an error when there is no input data on the socket.
VerifyOrExit(sInfraIfIcmp6Socket != -1);
VerifyOrExit(FD_ISSET(sInfraIfIcmp6Socket, &aReadFdSet));
memset(&srcAddr, 0, sizeof(srcAddr)); memset(&srcAddr, 0, sizeof(srcAddr));
memset(&dstAddr, 0, sizeof(dstAddr)); memset(&dstAddr, 0, sizeof(dstAddr));
@@ -340,8 +445,23 @@ exit:
} }
} }
uint32_t platformInfraIfGetIndex(void) void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet)
{ {
return sInfraIfIndex; VerifyOrExit(sInfraIfIcmp6Socket != -1);
VerifyOrExit(sNetLinkSocket != -1);
if (FD_ISSET(sInfraIfIcmp6Socket, &aReadFdSet))
{
ReceiveIcmp6Message(aInstance);
}
if (FD_ISSET(sNetLinkSocket, &aReadFdSet))
{
ReceiveNetLinkMessage(aInstance);
}
exit:
return;
} }
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+20 -9
View File
@@ -47,6 +47,7 @@
#include <openthread/error.h> #include <openthread/error.h>
#include <openthread/instance.h> #include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/openthread-system.h> #include <openthread/openthread-system.h>
#include <openthread/platform/time.h> #include <openthread/platform/time.h>
@@ -505,8 +506,10 @@ extern unsigned int gBackboneNetifIndex;
* @param[in] aInstance The OpenThread instance. * @param[in] aInstance The OpenThread instance.
* @param[in] aIfName The name of the infrastructure interface. * @param[in] aIfName The name of the infrastructure interface.
* *
* @returns The index of the infrastructure interface.
*
*/ */
void platformInfraIfInit(otInstance *aInstance, const char *aIfName); uint32_t platformInfraIfInit(otInstance *aInstance, const char *aIfName);
/** /**
* This function deinitializes the infrastructure interface. * This function deinitializes the infrastructure interface.
@@ -514,6 +517,22 @@ void platformInfraIfInit(otInstance *aInstance, const char *aIfName);
*/ */
void platformInfraIfDeinit(void); void platformInfraIfDeinit(void);
/**
* This function tells if the infrastructure interface is running.
*
* @returns TRUE if the infrastructure interface is running, FALSE if not.
*
*/
bool platformInfraIfIsRunning(void);
/**
* this function returns the IPv6 link-local address of the infrastructure interface.
*
* @returns A pointer to the link-local address; NULL if no link-local address is present.
*
*/
const otIp6Address *platformInfraIfGetLinkLocalAddress(void);
/** /**
* This function updates the read fd set. * This function updates the read fd set.
* *
@@ -532,14 +551,6 @@ void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd);
*/ */
void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet); void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet);
/**
* This function returns the index of the infrastructure interface.
*
* @returns The index of the infrastructure interface. 0 indicates invalid index.
*
*/
uint32_t platformInfraIfGetIndex(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+16 -6
View File
@@ -42,6 +42,7 @@
#include <openthread/heap.h> #include <openthread/heap.h>
#include <openthread/tasklet.h> #include <openthread/tasklet.h>
#include <openthread/platform/alarm-milli.h> #include <openthread/platform/alarm-milli.h>
#include <openthread/platform/infra_if.h>
#include <openthread/platform/otns.h> #include <openthread/platform/otns.h>
#include <openthread/platform/radio.h> #include <openthread/platform/radio.h>
#include <openthread/platform/uart.h> #include <openthread/platform/uart.h>
@@ -100,8 +101,21 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
#endif #endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
// Reuse the backbone interface name. {
platformInfraIfInit(instance, aPlatformConfig->mBackboneInterfaceName); uint32_t infraIfIndex;
// Reuse the backbone interface name.
if (aPlatformConfig->mBackboneInterfaceName == nullptr || strlen(aPlatformConfig->mBackboneInterfaceName) == 0)
{
DieNowWithMessage("no infra interface is specified", OT_EXIT_INVALID_ARGUMENTS);
}
infraIfIndex = platformInfraIfInit(instance, aPlatformConfig->mBackboneInterfaceName);
SuccessOrDie(otBorderRoutingInit(instance, infraIfIndex, platformInfraIfIsRunning(),
platformInfraIfGetLinkLocalAddress()));
SuccessOrDie(otBorderRoutingSetEnabled(instance, /* aEnabled */ true));
}
#endif #endif
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
@@ -114,10 +128,6 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
SuccessOrDie(otSetStateChangedCallback(instance, processStateChange, instance)); SuccessOrDie(otSetStateChangedCallback(instance, processStateChange, instance));
#endif #endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
SuccessOrDie(otBorderRoutingInit(instance, platformInfraIfGetIndex()));
#endif
return instance; return instance;
} }
@@ -255,6 +255,60 @@ class SingleBorderRouter(thread_cert.TestCase):
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0], self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
backbone=True)) backbone=True))
#
# Case 4. The Routing Manager should be stopped if the infra interface went down.
#
self.nodes[BR1].disable_ether()
self.simulator.go(10)
self.collect_ipaddrs()
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 0)
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 0)
self.assertTrue(len(self.nodes[BR1].get_routes()) == 0)
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 0)
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0)
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 0)
self.nodes[BR1].enable_ether()
# It takes around 10 seconds to start sending RA messages.
self.simulator.go(15)
self.collect_ipaddrs()
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[BR1].get_routes()) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_routes()) == 1)
# The same local OMR and on-link prefix should be re-registered.
self.assertEqual(omr_prefix, self.nodes[BR1].get_prefixes()[0])
self.assertEqual(omr_prefix, self.nodes[ROUTER1].get_prefixes()[0])
self.assertEqual(external_route, self.nodes[BR1].get_routes()[0])
self.assertEqual(external_route, self.nodes[ROUTER1].get_routes()[0])
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
self.assertEqual(br1_omr_address, self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
self.assertEqual(router1_omr_address, self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0])
self.assertEqual(host_ula_address, self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])
# Router1 can ping to/from the Host on infra link.
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
backbone=True))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
+12
View File
@@ -2592,6 +2592,18 @@ class LinuxHost():
PING_RESPONSE_PATTERN = re.compile(r'\d+ bytes from .*:.*') PING_RESPONSE_PATTERN = re.compile(r'\d+ bytes from .*:.*')
ETH_DEV = config.BACKBONE_IFNAME ETH_DEV = config.BACKBONE_IFNAME
def enable_ether(self):
"""Enable the ethernet interface.
"""
self.bash(f'ifconfig {self.ETH_DEV} up')
def disable_ether(self):
"""Disable the ethernet interface.
"""
self.bash(f'ifconfig {self.ETH_DEV} down')
def get_ether_addrs(self): def get_ether_addrs(self):
output = self.bash(f'ip -6 addr list dev {self.ETH_DEV}') output = self.bash(f'ip -6 addr list dev {self.ETH_DEV}')