blob: a55914f83dbd2f68381376d7773e63b82e3b2eb5 [file] [log] [blame]
/*
* Copyright (c) 2016, 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
* This file implements IPv6 network interfaces.
*/
#include "netif.hpp"
#include "common/as_core_type.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/locator_getters.hpp"
#include "common/message.hpp"
#include "net/ip6.hpp"
namespace ot {
namespace Ip6 {
class AddressInfo : public otIp6AddressInfo
{
public:
explicit AddressInfo(const Netif::UnicastAddress &aAddress)
{
mAddress = &aAddress.mAddress;
mPrefixLength = aAddress.mPrefixLength;
mScope = aAddress.GetScope();
mPreferred = aAddress.mPreferred;
}
explicit AddressInfo(const Netif::MulticastAddress &aAddress)
{
mAddress = &aAddress.GetAddress();
mPrefixLength = kMulticastPrefixLength;
mScope = aAddress.GetAddress().GetScope();
mPreferred = false;
}
private:
static constexpr uint8_t kMulticastPrefixLength =
128; ///< Multicast prefix length used to notify internal address changes.
};
/*
* Certain fixed multicast addresses are defined as a set of chained (linked-list) constant `otNetifMulticastAddress`
* entries:
*
* LinkLocalAllRouters -> RealmLocalAllRouters -> LinkLocalAll -> RealmLocalAll -> RealmLocalAllMplForwarders.
*
* All or a portion of the chain is appended to the end of `mMulticastAddresses` linked-list. If the interface is
* subscribed to all-routers multicast addresses (using `SubscribeAllRoutersMulticast()`) then all the five entries
* are appended. Otherwise only the last three are appended.
*
*/
// "ff03::fc"
const otNetifMulticastAddress Netif::kRealmLocalAllMplForwardersMulticastAddress = {
{{{0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc}}},
nullptr};
// "ff03::01"
const otNetifMulticastAddress Netif::kRealmLocalAllNodesMulticastAddress = {
{{{0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}},
&Netif::kRealmLocalAllMplForwardersMulticastAddress};
// "ff02::01"
const otNetifMulticastAddress Netif::kLinkLocalAllNodesMulticastAddress = {
{{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}},
&Netif::kRealmLocalAllNodesMulticastAddress};
// "ff03::02"
const otNetifMulticastAddress Netif::kRealmLocalAllRoutersMulticastAddress = {
{{{0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}},
&Netif::kLinkLocalAllNodesMulticastAddress};
// "ff02::02"
const otNetifMulticastAddress Netif::kLinkLocalAllRoutersMulticastAddress = {
{{{0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}},
&Netif::kRealmLocalAllRoutersMulticastAddress};
//---------------------------------------------------------------------------------------------------------------------
// Netif
Netif::Netif(Instance &aInstance)
: InstanceLocator(aInstance)
, mMulticastPromiscuous(false)
, mAddressCallback(nullptr)
, mAddressCallbackContext(nullptr)
{
}
bool Netif::IsMulticastSubscribed(const Address &aAddress) const
{
return mMulticastAddresses.ContainsMatching(aAddress);
}
void Netif::SubscribeAllNodesMulticast(void)
{
MulticastAddress *tail;
MulticastAddress &linkLocalAllNodesAddress = AsCoreType(&AsNonConst(kLinkLocalAllNodesMulticastAddress));
VerifyOrExit(!mMulticastAddresses.Contains(linkLocalAllNodesAddress));
// Append the fixed chain of three multicast addresses to the
// tail of the list:
//
// LinkLocalAll -> RealmLocalAll -> RealmLocalAllMpl.
tail = mMulticastAddresses.GetTail();
if (tail == nullptr)
{
mMulticastAddresses.SetHead(&linkLocalAllNodesAddress);
}
else
{
tail->SetNext(&linkLocalAllNodesAddress);
}
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
VerifyOrExit(mAddressCallback != nullptr);
#endif
for (const MulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext())
{
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressAdded, *entry, kOriginThread);
if (mAddressCallback != nullptr)
#endif
{
AddressInfo addressInfo(*entry);
mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext);
}
}
exit:
return;
}
void Netif::UnsubscribeAllNodesMulticast(void)
{
MulticastAddress * prev;
const MulticastAddress &linkLocalAllNodesAddress = AsCoreType(&AsNonConst(kLinkLocalAllNodesMulticastAddress));
// The tail of multicast address linked list contains the
// fixed addresses. Search if LinkLocalAll is present
// in the list and find entry before it.
//
// LinkLocalAll -> RealmLocalAll -> RealmLocalAllMpl.
SuccessOrExit(mMulticastAddresses.Find(linkLocalAllNodesAddress, prev));
// This method MUST be called after `UnsubscribeAllRoutersMulticast().
// Verify this by checking the chain at the end of the list only
// contains three entries and not the five fixed addresses (check that
// `prev` entry before `LinkLocalAll` is not `RealmLocalRouters`):
//
// LinkLocalAllRouters -> RealmLocalAllRouters -> LinkLocalAll
// -> RealmLocalAll -> RealmLocalAllMpl.
OT_ASSERT(prev != AsCoreTypePtr(AsNonConst(&kRealmLocalAllRoutersMulticastAddress)));
if (prev == nullptr)
{
mMulticastAddresses.Clear();
}
else
{
prev->SetNext(nullptr);
}
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
VerifyOrExit(mAddressCallback != nullptr);
#endif
for (const MulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext())
{
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressRemoved, *entry, kOriginThread);
if (mAddressCallback != nullptr)
#endif
{
AddressInfo addressInfo(*entry);
mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext);
}
}
exit:
return;
}
void Netif::SubscribeAllRoutersMulticast(void)
{
MulticastAddress *prev = nullptr;
MulticastAddress &linkLocalAllRoutersAddress = AsCoreType(&AsNonConst(kLinkLocalAllRoutersMulticastAddress));
MulticastAddress &linkLocalAllNodesAddress = AsCoreType(&AsNonConst(kLinkLocalAllNodesMulticastAddress));
MulticastAddress &realmLocalAllRoutersAddress = AsCoreType(&AsNonConst(kRealmLocalAllRoutersMulticastAddress));
// This method MUST be called after `SubscribeAllNodesMulticast()`
// Ensure that the `LinkLocalAll` was found on the list.
SuccessOrAssert(mMulticastAddresses.Find(linkLocalAllNodesAddress, prev));
// The tail of multicast address linked list contains the
// fixed addresses. We either have a chain of five addresses
//
// LinkLocalAllRouters -> RealmLocalAllRouters ->
// LinkLocalAll -> RealmLocalAll -> RealmLocalAllMpl.
//
// or just the last three addresses
//
// LinkLocalAll -> RealmLocalAll -> RealmLocalAllMpl.
//
// If the previous entry behind `LinkLocalAll` is
// `RealmLocalAllRouters` then all five addresses are on
// the list already.
VerifyOrExit(prev != &realmLocalAllRoutersAddress);
if (prev == nullptr)
{
mMulticastAddresses.SetHead(&linkLocalAllRoutersAddress);
}
else
{
prev->SetNext(&linkLocalAllRoutersAddress);
}
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
VerifyOrExit(mAddressCallback != nullptr);
#endif
for (const MulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress;
entry = entry->GetNext())
{
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressAdded, *entry, kOriginThread);
if (mAddressCallback != nullptr)
#endif
{
AddressInfo addressInfo(*entry);
mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext);
}
}
exit:
return;
}
void Netif::UnsubscribeAllRoutersMulticast(void)
{
MulticastAddress *prev;
MulticastAddress &linkLocalAllRoutersAddress = AsCoreType(&AsNonConst(kLinkLocalAllRoutersMulticastAddress));
MulticastAddress &linkLocalAllNodesAddress = AsCoreType(&AsNonConst(kLinkLocalAllNodesMulticastAddress));
// The tail of multicast address linked list contains the
// fixed addresses. We check for the chain of five addresses:
//
// LinkLocalAllRouters -> RealmLocalAllRouters ->
// LinkLocalAll -> RealmLocalAll -> RealmLocalAllMpl.
//
// If found, we then replace the entry behind `LinkLocalAllRouters`
// to point to `LinkLocalAll` instead (so that tail contains the
// three fixed addresses at end of the chain).
SuccessOrExit(mMulticastAddresses.Find(linkLocalAllRoutersAddress, prev));
if (prev == nullptr)
{
mMulticastAddresses.SetHead(&linkLocalAllNodesAddress);
}
else
{
prev->SetNext(&linkLocalAllNodesAddress);
}
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
VerifyOrExit(mAddressCallback != nullptr);
#endif
for (const MulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress;
entry = entry->GetNext())
{
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressRemoved, *entry, kOriginThread);
if (mAddressCallback != nullptr)
#endif
{
AddressInfo addressInfo(*entry);
mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext);
}
}
exit:
return;
}
bool Netif::IsMulticastAddressExternal(const MulticastAddress &aAddress) const
{
return mExtMulticastAddressPool.IsPoolEntry(static_cast<const ExternalMulticastAddress &>(aAddress));
}
void Netif::SubscribeMulticast(MulticastAddress &aAddress)
{
SuccessOrExit(mMulticastAddresses.Add(aAddress));
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressAdded, aAddress, kOriginThread);
#endif
if (mAddressCallback != nullptr)
{
AddressInfo addressInfo(aAddress);
mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext);
}
exit:
return;
}
void Netif::UnsubscribeMulticast(const MulticastAddress &aAddress)
{
SuccessOrExit(mMulticastAddresses.Remove(aAddress));
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressRemoved, aAddress, kOriginThread);
#endif
if (mAddressCallback != nullptr)
{
AddressInfo addressInfo(aAddress);
mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext);
}
exit:
return;
}
Error Netif::SubscribeExternalMulticast(const Address &aAddress)
{
Error error = kErrorNone;
MulticastAddress &linkLocalAllRoutersAddress = AsCoreType(&AsNonConst(kLinkLocalAllRoutersMulticastAddress));
ExternalMulticastAddress *entry;
VerifyOrExit(aAddress.IsMulticast(), error = kErrorInvalidArgs);
VerifyOrExit(!IsMulticastSubscribed(aAddress), error = kErrorAlready);
// Check that the address is not one of the fixed addresses:
// LinkLocalAllRouters -> RealmLocalAllRouters -> LinkLocalAllNodes
// -> RealmLocalAllNodes -> RealmLocalAllMpl.
for (const MulticastAddress *cur = &linkLocalAllRoutersAddress; cur; cur = cur->GetNext())
{
VerifyOrExit(cur->GetAddress() != aAddress, error = kErrorRejected);
}
entry = mExtMulticastAddressPool.Allocate();
VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
entry->mAddress = aAddress;
#if OPENTHREAD_CONFIG_MLR_ENABLE
entry->mMlrState = kMlrStateToRegister;
#endif
mMulticastAddresses.Push(*entry);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressAdded, *entry, kOriginManual);
#endif
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
exit:
return error;
}
Error Netif::UnsubscribeExternalMulticast(const Address &aAddress)
{
Error error = kErrorNone;
MulticastAddress *entry;
MulticastAddress *prev;
entry = mMulticastAddresses.FindMatching(aAddress, prev);
VerifyOrExit(entry != nullptr, error = kErrorNotFound);
VerifyOrExit(IsMulticastAddressExternal(*entry), error = kErrorRejected);
mMulticastAddresses.PopAfter(prev);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressRemoved, *entry, kOriginManual);
#endif
mExtMulticastAddressPool.Free(static_cast<ExternalMulticastAddress &>(*entry));
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
exit:
return error;
}
void Netif::UnsubscribeAllExternalMulticastAddresses(void)
{
MulticastAddress *next;
for (MulticastAddress *entry = mMulticastAddresses.GetHead(); entry != nullptr; entry = next)
{
next = entry->GetNext();
if (IsMulticastAddressExternal(*entry))
{
IgnoreError(UnsubscribeExternalMulticast(entry->GetAddress()));
}
}
}
void Netif::SetAddressCallback(otIp6AddressCallback aCallback, void *aCallbackContext)
{
mAddressCallback = aCallback;
mAddressCallbackContext = aCallbackContext;
}
void Netif::AddUnicastAddress(UnicastAddress &aAddress)
{
SuccessOrExit(mUnicastAddresses.Add(aAddress));
Get<Notifier>().Signal(aAddress.mRloc ? kEventThreadRlocAdded : kEventIp6AddressAdded);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressAdded, aAddress);
#endif
if (mAddressCallback != nullptr)
{
AddressInfo addressInfo(aAddress);
mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext);
}
exit:
return;
}
void Netif::RemoveUnicastAddress(const UnicastAddress &aAddress)
{
SuccessOrExit(mUnicastAddresses.Remove(aAddress));
Get<Notifier>().Signal(aAddress.mRloc ? kEventThreadRlocRemoved : kEventIp6AddressRemoved);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressRemoved, aAddress);
#endif
if (mAddressCallback != nullptr)
{
AddressInfo addressInfo(aAddress);
mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext);
}
exit:
return;
}
Error Netif::AddExternalUnicastAddress(const UnicastAddress &aAddress)
{
Error error = kErrorNone;
UnicastAddress *entry;
VerifyOrExit(!aAddress.GetAddress().IsMulticast(), error = kErrorInvalidArgs);
entry = mUnicastAddresses.FindMatching(aAddress.GetAddress());
if (entry != nullptr)
{
VerifyOrExit(IsUnicastAddressExternal(*entry), error = kErrorAlready);
entry->mPrefixLength = aAddress.mPrefixLength;
entry->mAddressOrigin = aAddress.mAddressOrigin;
entry->mPreferred = aAddress.mPreferred;
entry->mValid = aAddress.mValid;
ExitNow();
}
VerifyOrExit(!aAddress.GetAddress().IsLinkLocal(), error = kErrorInvalidArgs);
entry = mExtUnicastAddressPool.Allocate();
VerifyOrExit(entry != nullptr, error = kErrorNoBufs);
*entry = aAddress;
mUnicastAddresses.Push(*entry);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressAdded, *entry);
#endif
Get<Notifier>().Signal(kEventIp6AddressAdded);
exit:
return error;
}
Error Netif::RemoveExternalUnicastAddress(const Address &aAddress)
{
Error error = kErrorNone;
UnicastAddress *entry;
UnicastAddress *prev;
entry = mUnicastAddresses.FindMatching(aAddress, prev);
VerifyOrExit(entry != nullptr, error = kErrorNotFound);
VerifyOrExit(IsUnicastAddressExternal(*entry), error = kErrorRejected);
mUnicastAddresses.PopAfter(prev);
#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE
Get<Utils::HistoryTracker>().RecordAddressEvent(kAddressRemoved, *entry);
#endif
mExtUnicastAddressPool.Free(*entry);
Get<Notifier>().Signal(kEventIp6AddressRemoved);
exit:
return error;
}
void Netif::RemoveAllExternalUnicastAddresses(void)
{
UnicastAddress *next;
for (UnicastAddress *entry = mUnicastAddresses.GetHead(); entry != nullptr; entry = next)
{
next = entry->GetNext();
if (IsUnicastAddressExternal(*entry))
{
IgnoreError(RemoveExternalUnicastAddress(entry->GetAddress()));
}
}
}
bool Netif::HasUnicastAddress(const Address &aAddress) const
{
return mUnicastAddresses.ContainsMatching(aAddress);
}
bool Netif::IsUnicastAddressExternal(const UnicastAddress &aAddress) const
{
return mExtUnicastAddressPool.IsPoolEntry(aAddress);
}
//---------------------------------------------------------------------------------------------------------------------
// Netif::UnicastAddress
void Netif::UnicastAddress::InitAsThreadOrigin(bool aPreferred)
{
Clear();
mPrefixLength = NetworkPrefix::kLength;
mAddressOrigin = kOriginThread;
mPreferred = aPreferred;
mValid = true;
}
void Netif::UnicastAddress::InitAsThreadOriginRealmLocalScope(void)
{
InitAsThreadOrigin();
SetScopeOverride(Address::kRealmLocalScope);
}
void Netif::UnicastAddress::InitAsThreadOriginGlobalScope(void)
{
Clear();
mAddressOrigin = kOriginThread;
mValid = true;
SetScopeOverride(Address::kGlobalScope);
}
void Netif::UnicastAddress::InitAsSlaacOrigin(uint8_t aPrefixLength, bool aPreferred)
{
Clear();
mPrefixLength = aPrefixLength;
mAddressOrigin = kOriginSlaac;
mPreferred = aPreferred;
mValid = true;
}
//---------------------------------------------------------------------------------------------------------------------
// Netif::ExternalMulticastAddress::Iterator
Netif::ExternalMulticastAddress::Iterator::Iterator(const Netif &aNetif, Address::TypeFilter aFilter)
: ItemPtrIterator(nullptr)
, mNetif(aNetif)
, mFilter(aFilter)
{
AdvanceFrom(mNetif.GetMulticastAddresses().GetHead());
}
void Netif::ExternalMulticastAddress::Iterator::AdvanceFrom(const MulticastAddress *aAddr)
{
while (aAddr != nullptr &&
!(mNetif.IsMulticastAddressExternal(*aAddr) && aAddr->GetAddress().MatchesFilter(mFilter)))
{
aAddr = aAddr->GetNext();
}
mItem = AsNonConst(static_cast<const ExternalMulticastAddress *>(aAddr));
}
} // namespace Ip6
} // namespace ot