blob: a639fcd31a2cecbde8c47a21ea266b2da16be853 [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/debug.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/message.hpp"
#include "net/ip6.hpp"
namespace ot {
namespace Ip6 {
/*
* Certain fixed multicast addresses are defined as a set of chained (linked-list) constant `otNetifMulticastAddress`
* entries:
*
* LinkLocalAllRouters -> RealmLocalAllRouters -> LinkLocalAll -> RealmLocalAll -> RealmLocalAllMplForwarders -> nullptr
*
* 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(Instance &aInstance)
: InstanceLocator(aInstance)
, mUnicastAddresses()
, mMulticastAddresses()
, mMulticastPromiscuous(false)
, mAddressCallback(nullptr)
, mAddressCallbackContext(nullptr)
, mExtUnicastAddressPool()
, mExtMulticastAddressPool()
{
}
bool Netif::IsMulticastSubscribed(const Address &aAddress) const
{
const NetifMulticastAddress *prev;
return mMulticastAddresses.FindMatching(aAddress, prev) != nullptr;
}
void Netif::SubscribeAllNodesMulticast(void)
{
NetifMulticastAddress *tail;
NetifMulticastAddress &linkLocalAllNodesAddress =
static_cast<NetifMulticastAddress &>(const_cast<otNetifMulticastAddress &>(kLinkLocalAllNodesMulticastAddress));
VerifyOrExit(!mMulticastAddresses.Contains(linkLocalAllNodesAddress), OT_NOOP);
// 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);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
for (const NetifMulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
}
exit:
return;
}
void Netif::UnsubscribeAllNodesMulticast(void)
{
NetifMulticastAddress * prev;
const NetifMulticastAddress &linkLocalAllNodesAddress =
static_cast<NetifMulticastAddress &>(const_cast<otNetifMulticastAddress &>(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 != static_cast<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&kRealmLocalAllRoutersMulticastAddress)));
if (prev == nullptr)
{
mMulticastAddresses.Clear();
}
else
{
prev->SetNext(nullptr);
}
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
for (const NetifMulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
}
exit:
return;
}
void Netif::SubscribeAllRoutersMulticast(void)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *prev = nullptr;
NetifMulticastAddress &linkLocalAllRoutersAddress = static_cast<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(kLinkLocalAllRoutersMulticastAddress));
NetifMulticastAddress &linkLocalAllNodesAddress =
static_cast<NetifMulticastAddress &>(const_cast<otNetifMulticastAddress &>(kLinkLocalAllNodesMulticastAddress));
NetifMulticastAddress &realmLocalAllRoutersAddress = static_cast<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(kRealmLocalAllRoutersMulticastAddress));
error = mMulticastAddresses.Find(linkLocalAllNodesAddress, prev);
// This method MUST be called after `SubscribeAllNodesMulticast()`
// Ensure that the `LinkLocalAll` was found on the list.
OT_ASSERT(error == OT_ERROR_NONE);
OT_UNUSED_VARIABLE(error);
// 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, OT_NOOP);
if (prev == nullptr)
{
mMulticastAddresses.SetHead(&linkLocalAllRoutersAddress);
}
else
{
prev->SetNext(&linkLocalAllRoutersAddress);
}
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
for (const NetifMulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress;
entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
}
exit:
return;
}
void Netif::UnsubscribeAllRoutersMulticast(void)
{
NetifMulticastAddress *prev;
NetifMulticastAddress &linkLocalAllRoutersAddress = static_cast<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(kLinkLocalAllRoutersMulticastAddress));
NetifMulticastAddress &linkLocalAllNodesAddress =
static_cast<NetifMulticastAddress &>(const_cast<otNetifMulticastAddress &>(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);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
for (const NetifMulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress;
entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
}
exit:
return;
}
bool Netif::IsMulticastAddressExternal(const NetifMulticastAddress &aAddress) const
{
return mExtMulticastAddressPool.IsPoolEntry(aAddress);
}
void Netif::SubscribeMulticast(NetifMulticastAddress &aAddress)
{
SuccessOrExit(mMulticastAddresses.Add(aAddress));
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
exit:
return;
}
void Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress)
{
SuccessOrExit(mMulticastAddresses.Remove(aAddress));
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
exit:
return;
}
otError Netif::SubscribeExternalMulticast(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress &linkLocalAllRoutersAddress = static_cast<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(kLinkLocalAllRoutersMulticastAddress));
NetifMulticastAddress *entry;
VerifyOrExit(!IsMulticastSubscribed(aAddress), error = OT_ERROR_ALREADY);
// Check that the address is not one of the fixed addresses:
// LinkLocalAllRouters -> RealmLocalAllRouters -> LinkLocalAllNodes
// -> RealmLocalAllNodes -> RealmLocalAllMpl.
for (const NetifMulticastAddress *cur = &linkLocalAllRoutersAddress; cur; cur = cur->GetNext())
{
VerifyOrExit(cur->GetAddress() != aAddress, error = OT_ERROR_INVALID_ARGS);
}
entry = mExtMulticastAddressPool.Allocate();
VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS);
entry->mAddress = aAddress;
mMulticastAddresses.Push(*entry);
Get<Notifier>().Signal(kEventIp6MulticastSubscribed);
exit:
return error;
}
otError Netif::UnsubscribeExternalMulticast(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *entry;
NetifMulticastAddress *prev;
entry = mMulticastAddresses.FindMatching(aAddress, prev);
VerifyOrExit(entry != nullptr, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(IsMulticastAddressExternal(*entry), error = OT_ERROR_INVALID_ARGS);
mMulticastAddresses.PopAfter(prev);
mExtMulticastAddressPool.Free(*entry);
Get<Notifier>().Signal(kEventIp6MulticastUnsubscribed);
exit:
return error;
}
void Netif::UnsubscribeAllExternalMulticastAddresses(void)
{
NetifMulticastAddress *next;
for (NetifMulticastAddress *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(NetifUnicastAddress &aAddress)
{
SuccessOrExit(mUnicastAddresses.Add(aAddress));
Get<Notifier>().Signal(aAddress.mRloc ? kEventThreadRlocAdded : kEventIp6AddressAdded);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
exit:
return;
}
void Netif::RemoveUnicastAddress(const NetifUnicastAddress &aAddress)
{
SuccessOrExit(mUnicastAddresses.Remove(aAddress));
Get<Notifier>().Signal(aAddress.mRloc ? kEventThreadRlocRemoved : kEventIp6AddressRemoved);
VerifyOrExit(mAddressCallback != nullptr, OT_NOOP);
mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
exit:
return;
}
otError Netif::AddExternalUnicastAddress(const NetifUnicastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
NetifUnicastAddress *entry;
NetifUnicastAddress *prev;
entry = mUnicastAddresses.FindMatching(aAddress.GetAddress(), prev);
if (entry != nullptr)
{
VerifyOrExit(IsUnicastAddressExternal(*entry), error = OT_ERROR_ALREADY);
entry->mPrefixLength = aAddress.mPrefixLength;
entry->mAddressOrigin = aAddress.mAddressOrigin;
entry->mPreferred = aAddress.mPreferred;
entry->mValid = aAddress.mValid;
ExitNow();
}
VerifyOrExit(!aAddress.GetAddress().IsLinkLocal(), error = OT_ERROR_INVALID_ARGS);
entry = mExtUnicastAddressPool.Allocate();
VerifyOrExit(entry != nullptr, error = OT_ERROR_NO_BUFS);
*entry = aAddress;
mUnicastAddresses.Push(*entry);
Get<Notifier>().Signal(kEventIp6AddressAdded);
exit:
return error;
}
otError Netif::RemoveExternalUnicastAddress(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifUnicastAddress *entry;
NetifUnicastAddress *prev;
entry = mUnicastAddresses.FindMatching(aAddress, prev);
VerifyOrExit(entry != nullptr, error = OT_ERROR_NOT_FOUND);
VerifyOrExit(IsUnicastAddressExternal(*entry), error = OT_ERROR_INVALID_ARGS);
mUnicastAddresses.PopAfter(prev);
mExtUnicastAddressPool.Free(*entry);
Get<Notifier>().Signal(kEventIp6AddressRemoved);
exit:
return error;
}
void Netif::RemoveAllExternalUnicastAddresses(void)
{
NetifUnicastAddress *next;
for (NetifUnicastAddress *entry = mUnicastAddresses.GetHead(); entry != nullptr; entry = next)
{
next = entry->GetNext();
if (IsUnicastAddressExternal(*entry))
{
IgnoreError(RemoveExternalUnicastAddress(entry->GetAddress()));
}
}
}
bool Netif::HasUnicastAddress(const Address &aAddress) const
{
const NetifUnicastAddress *prev;
return mUnicastAddresses.FindMatching(aAddress, prev) != nullptr;
}
bool Netif::IsUnicastAddressExternal(const NetifUnicastAddress &aAddress) const
{
return mExtUnicastAddressPool.IsPoolEntry(aAddress);
}
} // namespace Ip6
} // namespace ot