blob: 00dae386173c37b1d58e02fd5aa4767a6063edb5 [file] [log] [blame]
/*
* Copyright (c) 2017, 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.
*/
/**
* @file
* The file implements the Thread border agent.
*/
#define OTBR_LOG_TAG "AGENT"
#include "agent/border_agent.hpp"
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openthread/border_agent.h>
#include <openthread/thread_ftd.h>
#include <openthread/platform/toolchain.h>
#include "agent/ncp_openthread.hpp"
#include "agent/uris.hpp"
#if OTBR_ENABLE_BACKBONE_ROUTER
#include "backbone_router/backbone_agent.hpp"
#endif
#include "common/byteswap.hpp"
#include "common/code_utils.hpp"
#include "common/logging.hpp"
#include "common/tlv.hpp"
#include "common/types.hpp"
#include "utils/hex.hpp"
#include "utils/strcpy_utils.hpp"
namespace otbr {
static const char kBorderAgentServiceType[] = "_meshcop._udp"; ///< Border agent service type of mDNS
static const char kBorderAgentServiceInstanceName[] =
OTBR_MESHCOP_SERVICE_INSTANCE_NAME; ///< Border agent service name of mDNS
/**
* Locators
*
*/
enum
{
kAloc16Leader = 0xfc00, ///< leader anycast locator.
kInvalidLocator = 0xffff, ///< invalid locator.
};
uint32_t BorderAgent::StateBitmap::ToUint32(void) const
{
uint32_t bitmap = 0;
bitmap |= mConnectionMode << 0;
bitmap |= mThreadIfStatus << 3;
bitmap |= mAvailability << 5;
bitmap |= mBbrIsActive << 7;
bitmap |= mBbrIsPrimary << 8;
return bitmap;
}
BorderAgent::BorderAgent(otbr::Ncp::ControllerOpenThread &aNcp)
: mNcp(aNcp)
#if OTBR_ENABLE_MDNS_AVAHI || OTBR_ENABLE_MDNS_MDNSSD || OTBR_ENABLE_MDNS_MOJO
, mPublisher(Mdns::Publisher::Create(AF_UNSPEC, /* aDomain */ nullptr, HandleMdnsState, this))
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
, mAdvertisingProxy(aNcp, *mPublisher)
#endif
#else
, mPublisher(nullptr)
#endif
#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
, mDiscoveryProxy(aNcp, *mPublisher)
#endif
#if OTBR_ENABLE_BACKBONE_ROUTER
, mBackboneAgent(aNcp)
#endif
{
}
void BorderAgent::Init(void)
{
mNcp.AddThreadStateChangedCallback([this](otChangedFlags aFlags) { HandleThreadStateChanged(aFlags); });
#if OTBR_ENABLE_BACKBONE_ROUTER
mBackboneAgent.Init();
#endif
if (IsThreadStarted())
{
Start();
}
else
{
Stop();
}
}
otbrError BorderAgent::Start(void)
{
otbrError error = OTBR_ERROR_NONE;
VerifyOrExit(IsThreadStarted(), errno = EAGAIN, error = OTBR_ERROR_ERRNO);
// In case we didn't receive Thread down event.
Stop();
#if OTBR_ENABLE_MDNS_AVAHI || OTBR_ENABLE_MDNS_MDNSSD || OTBR_ENABLE_MDNS_MOJO
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
mAdvertisingProxy.Start();
#endif
UpdateMeshCopService();
#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
mDiscoveryProxy.Start();
#endif
#endif // OTBR_ENABLE_MDNS_AVAHI || OTBR_ENABLE_MDNS_MDNSSD || OTBR_ENABLE_MDNS_MOJO
exit:
otbrLogResult(error, "Start Thread Border Agent");
return error;
}
void BorderAgent::Stop(void)
{
otbrLogInfo("Stop Thread Border Agent");
#if OTBR_ENABLE_MDNS_AVAHI || OTBR_ENABLE_MDNS_MDNSSD || OTBR_ENABLE_MDNS_MOJO
mPublisher->Stop();
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
mAdvertisingProxy.Stop();
#endif
#if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
mDiscoveryProxy.Stop();
#endif
#endif
}
void BorderAgent::HandleMdnsState(void *aContext, Mdns::Publisher::State aState)
{
static_cast<BorderAgent *>(aContext)->HandleMdnsState(aState);
}
BorderAgent::~BorderAgent(void)
{
Stop();
if (mPublisher != nullptr)
{
delete mPublisher;
mPublisher = nullptr;
}
}
void BorderAgent::HandleMdnsState(Mdns::Publisher::State aState)
{
switch (aState)
{
case Mdns::Publisher::State::kReady:
UpdateMeshCopService();
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
mAdvertisingProxy.PublishAllHostsAndServices();
#endif
break;
default:
otbrLogWarning("mDNS service not available!");
break;
}
}
void BorderAgent::Update(MainloopContext &aMainloop)
{
#if OTBR_ENABLE_BACKBONE_ROUTER
mBackboneAgent.Update(aMainloop);
#endif
if (mPublisher != nullptr)
{
mPublisher->Update(aMainloop);
}
}
void BorderAgent::Process(const MainloopContext &aMainloop)
{
#if OTBR_ENABLE_BACKBONE_ROUTER
mBackboneAgent.Process(aMainloop);
#endif
if (mPublisher != nullptr)
{
mPublisher->Process(aMainloop);
}
}
void BorderAgent::PublishMeshCopService(void)
{
StateBitmap state;
uint32_t stateUint32;
otInstance * instance = mNcp.GetInstance();
const otExtendedPanId * extPanId = otThreadGetExtendedPanId(instance);
const otExtAddress * extAddr = otLinkGetExtendedAddress(instance);
const char * networkName = otThreadGetNetworkName(instance);
Mdns::Publisher::TxtList txtList{{"rv", "1"}};
otbrLogInfo("Publish meshcop service %s.%s.local.", kBorderAgentServiceInstanceName, kBorderAgentServiceType);
txtList.emplace_back("nn", networkName);
txtList.emplace_back("xp", extPanId->m8, sizeof(extPanId->m8));
txtList.emplace_back("tv", mNcp.GetThreadVersion());
// "dd" represents for device discriminator which
// should always be the IEEE 802.15.4 extended address.
txtList.emplace_back("dd", extAddr->m8, sizeof(extAddr->m8));
state.mConnectionMode = kConnectionModePskc;
state.mAvailability = kAvailabilityHigh;
#if OTBR_ENABLE_BACKBONE_ROUTER
state.mBbrIsActive = otBackboneRouterGetState(instance) != OT_BACKBONE_ROUTER_STATE_DISABLED;
state.mBbrIsPrimary = otBackboneRouterGetState(instance) == OT_BACKBONE_ROUTER_STATE_PRIMARY;
#endif
switch (otThreadGetDeviceRole(instance))
{
case OT_DEVICE_ROLE_DISABLED:
state.mThreadIfStatus = kThreadIfStatusNotInitialized;
break;
case OT_DEVICE_ROLE_DETACHED:
state.mThreadIfStatus = kThreadIfStatusInitialized;
break;
default:
state.mThreadIfStatus = kThreadIfStatusActive;
}
stateUint32 = htobe32(state.ToUint32());
txtList.emplace_back("sb", reinterpret_cast<uint8_t *>(&stateUint32), sizeof(stateUint32));
if (state.mThreadIfStatus == kThreadIfStatusActive)
{
otError error;
otOperationalDataset activeDataset;
uint64_t activeTimestamp;
uint32_t partitionId;
if ((error = otDatasetGetActive(instance, &activeDataset)) != OT_ERROR_NONE)
{
otbrLogWarning("Failed to get active dataset: %s", otThreadErrorToString(error));
}
else
{
activeTimestamp = htobe64(activeDataset.mActiveTimestamp);
txtList.emplace_back("at", reinterpret_cast<uint8_t *>(&activeTimestamp), sizeof(activeTimestamp));
}
partitionId = otThreadGetPartitionId(instance);
txtList.emplace_back("pt", reinterpret_cast<uint8_t *>(&partitionId), sizeof(partitionId));
}
#if OTBR_ENABLE_BACKBONE_ROUTER
if (state.mBbrIsActive)
{
otBackboneRouterConfig bbrConfig;
uint16_t bbrPort = htobe16(BackboneRouter::BackboneAgent::kBackboneUdpPort);
otBackboneRouterGetConfig(instance, &bbrConfig);
txtList.emplace_back("sq", &bbrConfig.mSequenceNumber, sizeof(bbrConfig.mSequenceNumber));
txtList.emplace_back("bb", reinterpret_cast<const uint8_t *>(&bbrPort), sizeof(bbrPort));
}
txtList.emplace_back("dn", otThreadGetDomainName(instance));
#endif
mPublisher->PublishService(/* aHostName */ nullptr, otBorderAgentGetUdpPort(instance),
kBorderAgentServiceInstanceName, kBorderAgentServiceType, Mdns::Publisher::SubTypeList{},
txtList);
}
void BorderAgent::UnpublishMeshCopService(void)
{
assert(IsThreadStarted());
VerifyOrExit(!mNetworkName.empty());
otbrLogInfo("Unpublish meshcop service %s.%s.local.", kBorderAgentServiceInstanceName, kBorderAgentServiceType);
mPublisher->UnpublishService(kBorderAgentServiceInstanceName, kBorderAgentServiceType);
exit:
return;
}
void BorderAgent::UpdateMeshCopService(void)
{
assert(IsThreadStarted());
const char *networkName = otThreadGetNetworkName(mNcp.GetInstance());
VerifyOrExit(mPublisher->IsStarted(), mPublisher->Start());
VerifyOrExit(IsPskcInitialized(), UnpublishMeshCopService());
PublishMeshCopService();
mNetworkName = networkName;
exit:
return;
}
void BorderAgent::HandleThreadStateChanged(otChangedFlags aFlags)
{
VerifyOrExit(mPublisher != nullptr);
VerifyOrExit(aFlags & (OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_EXT_PANID | OT_CHANGED_THREAD_NETWORK_NAME |
OT_CHANGED_ACTIVE_DATASET | OT_CHANGED_THREAD_PARTITION_ID |
OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE | OT_CHANGED_PSKC));
if (aFlags & OT_CHANGED_THREAD_ROLE)
{
otbrLogInfo("Thread is %s", (IsThreadStarted() ? "up" : "down"));
if (IsThreadStarted())
{
Start();
}
else
{
Stop();
}
}
else if (IsThreadStarted())
{
UpdateMeshCopService();
}
exit:
return;
}
bool BorderAgent::IsThreadStarted(void) const
{
otDeviceRole role = otThreadGetDeviceRole(mNcp.GetInstance());
return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
}
bool BorderAgent::IsPskcInitialized(void) const
{
bool initialized = false;
const otPskc *pskc = otThreadGetPskc(mNcp.GetInstance());
for (uint8_t byte : pskc->m8)
{
if (byte != 0x00)
{
initialized = true;
break;
}
}
return initialized;
}
} // namespace otbr