blob: 372b9d0dcca2a0d52c9e0a3a5fc360e73cc57565 [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/code_utils.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 -> NULL
*
* 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}}},
NULL};
// "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(NULL)
, mAddressCallbackContext(NULL)
{
for (NetifUnicastAddress *entry = &mExtUnicastAddresses[0]; entry < OT_ARRAY_END(mExtUnicastAddresses); entry++)
{
entry->MarkAsNotInUse();
}
for (NetifMulticastAddress *entry = &mExtMulticastAddresses[0]; entry < OT_ARRAY_END(mExtMulticastAddresses);
entry++)
{
entry->MarkAsNotInUse();
}
}
bool Netif::IsMulticastSubscribed(const Address &aAddress) const
{
bool rval = false;
for (const NetifMulticastAddress *cur = mMulticastAddresses.GetHead(); cur; cur = cur->GetNext())
{
if (cur->GetAddress() == aAddress)
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
otError Netif::SubscribeAllNodesMulticast(void)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *tail;
NetifMulticastAddress &linkLocalAllNodesAddress =
static_cast<NetifMulticastAddress &>(const_cast<otNetifMulticastAddress &>(kLinkLocalAllNodesMulticastAddress));
VerifyOrExit(!mMulticastAddresses.Contains(linkLocalAllNodesAddress), error = OT_ERROR_ALREADY);
// Append the fixed chain of three multicast addresses to the
// tail of the list:
//
// LinkLocalAll -> RealmLocalAll -> RealmLocalAllMpl.
tail = mMulticastAddresses.GetTail();
if (tail == NULL)
{
mMulticastAddresses.SetHead(&linkLocalAllNodesAddress);
}
else
{
tail->SetNext(&linkLocalAllNodesAddress);
}
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_SUBSCRIBED);
VerifyOrExit(mAddressCallback != NULL);
for (const NetifMulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
}
exit:
return error;
}
otError Netif::UnsubscribeAllNodesMulticast(void)
{
otError error = OT_ERROR_NONE;
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(error = 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.
assert(prev != static_cast<NetifMulticastAddress *>(
const_cast<otNetifMulticastAddress *>(&kRealmLocalAllRoutersMulticastAddress)));
if (prev == NULL)
{
mMulticastAddresses.Clear();
}
else
{
prev->SetNext(NULL);
}
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED);
VerifyOrExit(mAddressCallback != NULL);
for (const NetifMulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
}
exit:
return error;
}
otError Netif::SubscribeAllRoutersMulticast(void)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *prev;
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.
assert(error == OT_ERROR_NONE);
// 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, error = OT_ERROR_ALREADY);
if (prev == NULL)
{
mMulticastAddresses.SetHead(&linkLocalAllRoutersAddress);
}
else
{
prev->SetNext(&linkLocalAllRoutersAddress);
}
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_SUBSCRIBED);
VerifyOrExit(mAddressCallback != NULL);
for (const NetifMulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress;
entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
}
exit:
return error;
}
otError Netif::UnsubscribeAllRoutersMulticast(void)
{
otError error;
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(error = mMulticastAddresses.Find(linkLocalAllRoutersAddress, prev));
if (prev == NULL)
{
mMulticastAddresses.SetHead(&linkLocalAllNodesAddress);
}
else
{
prev->SetNext(&linkLocalAllNodesAddress);
}
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED);
VerifyOrExit(mAddressCallback != NULL);
for (const NetifMulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress;
entry = entry->GetNext())
{
mAddressCallback(&entry->GetAddress(), kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
}
exit:
return error;
}
otError Netif::SubscribeMulticast(NetifMulticastAddress &aAddress)
{
otError error;
SuccessOrExit(error = mMulticastAddresses.Add(aAddress));
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_SUBSCRIBED);
VerifyOrExit(mAddressCallback != NULL);
mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
exit:
return error;
}
otError Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress)
{
otError error;
SuccessOrExit(error = mMulticastAddresses.Remove(aAddress));
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED);
VerifyOrExit(mAddressCallback != NULL);
mAddressCallback(&aAddress.mAddress, kMulticastPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
exit:
return error;
}
otError Netif::GetNextExternalMulticast(uint8_t &aIterator, Address &aAddress) const
{
otError error = OT_ERROR_NOT_FOUND;
size_t num = OT_ARRAY_LENGTH(mExtMulticastAddresses);
VerifyOrExit(aIterator < num);
for (uint8_t i = aIterator; i < num; i++)
{
const NetifMulticastAddress &entry = mExtMulticastAddresses[i];
if (entry.IsInUse())
{
aAddress = entry.GetAddress();
aIterator = i + 1;
ExitNow(error = OT_ERROR_NONE);
}
}
exit:
return error;
}
otError Netif::SubscribeExternalMulticast(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *entry;
NetifMulticastAddress &linkLocalAllRoutersAddress = static_cast<NetifMulticastAddress &>(
const_cast<otNetifMulticastAddress &>(kLinkLocalAllRoutersMulticastAddress));
// 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);
}
VerifyOrExit(!IsMulticastSubscribed(aAddress), error = OT_ERROR_ALREADY);
for (entry = &mExtMulticastAddresses[0]; entry < OT_ARRAY_END(mExtMulticastAddresses); entry++)
{
if (!entry->IsInUse())
{
entry->mAddress = aAddress;
mMulticastAddresses.Push(*entry);
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_SUBSCRIBED);
ExitNow();
}
}
error = OT_ERROR_NO_BUFS;
exit:
return error;
}
otError Netif::UnsubscribeExternalMulticast(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *entry;
NetifMulticastAddress *last = NULL;
for (entry = mMulticastAddresses.GetHead(); entry; entry = entry->GetNext())
{
if (entry->GetAddress() == aAddress)
{
VerifyOrExit((entry >= &mExtMulticastAddresses[0]) && (entry < OT_ARRAY_END(mExtMulticastAddresses)),
error = OT_ERROR_INVALID_ARGS);
if (last)
{
mMulticastAddresses.PopAfter(*last);
}
else
{
mMulticastAddresses.Pop();
}
break;
}
last = entry;
}
VerifyOrExit(entry != NULL, error = OT_ERROR_NOT_FOUND);
entry->MarkAsNotInUse();
Get<Notifier>().Signal(OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED);
exit:
return error;
}
void Netif::UnsubscribeAllExternalMulticastAddresses(void)
{
for (NetifMulticastAddress *entry = &mExtMulticastAddresses[0]; entry < OT_ARRAY_END(mExtMulticastAddresses);
entry++)
{
if (entry->IsInUse())
{
UnsubscribeExternalMulticast(entry->GetAddress());
}
}
}
void Netif::SetAddressCallback(otIp6AddressCallback aCallback, void *aCallbackContext)
{
mAddressCallback = aCallback;
mAddressCallbackContext = aCallbackContext;
}
otError Netif::AddUnicastAddress(NetifUnicastAddress &aAddress)
{
otError error;
SuccessOrExit(error = mUnicastAddresses.Add(aAddress));
Get<Notifier>().Signal(aAddress.mRloc ? OT_CHANGED_THREAD_RLOC_ADDED : OT_CHANGED_IP6_ADDRESS_ADDED);
VerifyOrExit(mAddressCallback != NULL);
mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, /* IsAdded */ true, mAddressCallbackContext);
exit:
return error;
}
otError Netif::RemoveUnicastAddress(const NetifUnicastAddress &aAddress)
{
otError error;
SuccessOrExit(error = mUnicastAddresses.Remove(aAddress));
Get<Notifier>().Signal(aAddress.mRloc ? OT_CHANGED_THREAD_RLOC_REMOVED : OT_CHANGED_IP6_ADDRESS_REMOVED);
VerifyOrExit(mAddressCallback != NULL);
mAddressCallback(&aAddress.mAddress, aAddress.mPrefixLength, /* IsAdded */ false, mAddressCallbackContext);
exit:
return error;
}
otError Netif::AddExternalUnicastAddress(const NetifUnicastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
NetifUnicastAddress *entry;
VerifyOrExit(!aAddress.GetAddress().IsLinkLocal(), error = OT_ERROR_INVALID_ARGS);
for (entry = mUnicastAddresses.GetHead(); entry; entry = entry->GetNext())
{
if (entry->GetAddress() == aAddress.GetAddress())
{
VerifyOrExit((entry >= &mExtUnicastAddresses[0]) && (entry < OT_ARRAY_END(mExtUnicastAddresses)),
error = OT_ERROR_INVALID_ARGS);
entry->mPrefixLength = aAddress.mPrefixLength;
entry->mPreferred = aAddress.mPreferred;
entry->mValid = aAddress.mValid;
ExitNow();
}
}
for (entry = &mExtUnicastAddresses[0]; entry < OT_ARRAY_END(mExtUnicastAddresses); entry++)
{
if (!entry->IsInUse())
{
*entry = aAddress;
mUnicastAddresses.Push(*entry);
Get<Notifier>().Signal(OT_CHANGED_IP6_ADDRESS_ADDED);
ExitNow();
}
}
error = OT_ERROR_NO_BUFS;
exit:
return error;
}
otError Netif::RemoveExternalUnicastAddress(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifUnicastAddress *entry;
NetifUnicastAddress *last = NULL;
for (entry = mUnicastAddresses.GetHead(); entry; entry = entry->GetNext())
{
if (entry->GetAddress() == aAddress)
{
VerifyOrExit((entry >= &mExtUnicastAddresses[0]) && (entry < OT_ARRAY_END(mExtUnicastAddresses)),
error = OT_ERROR_INVALID_ARGS);
if (last)
{
mUnicastAddresses.PopAfter(*last);
}
else
{
mUnicastAddresses.Pop();
}
break;
}
last = entry;
}
VerifyOrExit(entry != NULL, error = OT_ERROR_NOT_FOUND);
entry->MarkAsNotInUse();
Get<Notifier>().Signal(OT_CHANGED_IP6_ADDRESS_REMOVED);
exit:
return error;
}
void Netif::RemoveAllExternalUnicastAddresses(void)
{
for (NetifUnicastAddress *entry = &mExtUnicastAddresses[0]; entry < OT_ARRAY_END(mExtUnicastAddresses); entry++)
{
if (entry->IsInUse())
{
RemoveExternalUnicastAddress(entry->GetAddress());
}
}
}
bool Netif::IsUnicastAddress(const Address &aAddress) const
{
bool rval = false;
for (const NetifUnicastAddress *cur = mUnicastAddresses.GetHead(); cur; cur = cur->GetNext())
{
if (cur->GetAddress() == aAddress)
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
} // namespace Ip6
} // namespace ot