blob: ad9dee1e7a9e3b8ed4991c1647549d86e369aee3 [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 <openthread/config.h>
#include "netif.hpp"
#include "openthread-instance.h"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/message.hpp"
#include "net/ip6.hpp"
namespace ot {
namespace Ip6 {
Netif::Netif(Ip6 &aIp6, int8_t aInterfaceId):
Ip6Locator(aIp6),
mCallbacks(NULL),
mUnicastAddresses(NULL),
mMulticastAddresses(NULL),
mInterfaceId(aInterfaceId),
mAllRoutersSubscribed(false),
mMulticastPromiscuous(false),
mStateChangedTask(aIp6.mTaskletScheduler, &Netif::HandleStateChangedTask, this),
mNext(NULL),
mStateChangedFlags(0)
{
#if OPENTHREAD_CONFIG_NEST_STAY_SUBSCRIBED_TO_ALL_ROUTERS_MULTICAST_ADDR
mAllRoutersSubscribed = true;
#endif
for (size_t i = 0; i < sizeof(mExtUnicastAddresses) / sizeof(mExtUnicastAddresses[0]); i++)
{
// To mark the address as unused/available, set the `mNext` to point back to itself.
mExtUnicastAddresses[i].mNext = &mExtUnicastAddresses[i];
}
for (size_t i = 0; i < sizeof(mExtMulticastAddresses) / sizeof(mExtMulticastAddresses[0]); i++)
{
// To mark the address as unused/available, set the `mNext` to point back to itself.
mExtMulticastAddresses[i].mNext = &mExtMulticastAddresses[i];
}
}
otError Netif::RegisterCallback(NetifCallback &aCallback)
{
otError error = OT_ERROR_NONE;
for (NetifCallback *cur = mCallbacks; cur; cur = cur->mNext)
{
if (cur == &aCallback)
{
ExitNow(error = OT_ERROR_ALREADY);
}
}
aCallback.mNext = mCallbacks;
mCallbacks = &aCallback;
exit:
return error;
}
otError Netif::RemoveCallback(NetifCallback &aCallback)
{
otError error = OT_ERROR_ALREADY;
NetifCallback *prev = NULL;
for (NetifCallback *cur = mCallbacks; cur; cur = cur->mNext)
{
if (cur == &aCallback)
{
if (prev)
{
prev->mNext = cur->mNext;
}
else
{
mCallbacks = mCallbacks->mNext;
}
cur->mNext = NULL;
error = OT_ERROR_NONE;
break;
}
prev = cur;
}
return error;
}
bool Netif::IsMulticastSubscribed(const Address &aAddress) const
{
bool rval = false;
if (aAddress.IsLinkLocalAllNodesMulticast() || aAddress.IsRealmLocalAllNodesMulticast() ||
aAddress.IsRealmLocalAllMplForwarders())
{
ExitNow(rval = true);
}
else if (aAddress.IsLinkLocalAllRoutersMulticast() || aAddress.IsRealmLocalAllRoutersMulticast())
{
ExitNow(rval = mAllRoutersSubscribed);
}
for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext())
{
if (memcmp(&cur->mAddress, &aAddress, sizeof(cur->mAddress)) == 0)
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
otError Netif::SubscribeMulticast(NetifMulticastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext())
{
if (cur == &aAddress)
{
ExitNow(error = OT_ERROR_ALREADY);
}
}
aAddress.mNext = mMulticastAddresses;
mMulticastAddresses = &aAddress;
exit:
return error;
}
otError Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
if (mMulticastAddresses == &aAddress)
{
mMulticastAddresses = mMulticastAddresses->GetNext();
ExitNow();
}
else if (mMulticastAddresses != NULL)
{
for (NetifMulticastAddress *cur = mMulticastAddresses; cur->GetNext(); cur = cur->GetNext())
{
if (cur->mNext == &aAddress)
{
cur->mNext = aAddress.mNext;
ExitNow();
}
}
}
ExitNow(error = OT_ERROR_NOT_FOUND);
exit:
return error;
}
otError Netif::SubscribeExternalMulticast(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *entry;
size_t num = sizeof(mExtMulticastAddresses) / sizeof(mExtMulticastAddresses[0]);
if (IsMulticastSubscribed(aAddress))
{
ExitNow(error = OT_ERROR_ALREADY);
}
// Find an available entry in the `mExtMulticastAddresses` array.
for (entry = &mExtMulticastAddresses[0]; num > 0; num--, entry++)
{
// In an unused/available entry, `mNext` points back to the entry itself.
if (entry->mNext == entry)
{
break;
}
}
VerifyOrExit(num > 0, error = OT_ERROR_NO_BUFS);
// Copy the address into the available entry and add it to linked-list.
entry->mAddress = aAddress;
entry->mNext = mMulticastAddresses;
mMulticastAddresses = entry;
exit:
return error;
}
otError Netif::UnsubscribeExternalMulticast(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifMulticastAddress *entry;
NetifMulticastAddress *last = NULL;
size_t num = sizeof(mExtMulticastAddresses) / sizeof(mExtMulticastAddresses[0]);
for (entry = mMulticastAddresses; entry; entry = entry->GetNext())
{
if (memcmp(&entry->mAddress, &aAddress, sizeof(otIp6Address)) == 0)
{
VerifyOrExit((entry >= &mExtMulticastAddresses[0]) && (entry < &mExtMulticastAddresses[num]),
error = OT_ERROR_INVALID_ARGS);
if (last)
{
last->mNext = entry->GetNext();
}
else
{
mMulticastAddresses = entry->GetNext();
}
break;
}
last = entry;
}
VerifyOrExit(entry != NULL, error = OT_ERROR_NOT_FOUND);
// To mark the address entry as unused/available, set the `mNext` pointer back to the entry itself.
entry->mNext = entry;
exit:
return error;
}
void Netif::UnsubscribeAllExternalMulticastAddresses(void)
{
size_t num = sizeof(mExtMulticastAddresses) / sizeof(mExtMulticastAddresses[0]);
for (NetifMulticastAddress *entry = &mExtMulticastAddresses[0]; num > 0; num--, entry++)
{
// In unused entries, the `mNext` points back to the entry itself.
if (entry->mNext != entry)
{
UnsubscribeExternalMulticast(*static_cast<Address *>(&entry->mAddress));
}
}
}
otError Netif::AddUnicastAddress(NetifUnicastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
for (NetifUnicastAddress *cur = mUnicastAddresses; cur; cur = cur->GetNext())
{
if (cur == &aAddress)
{
ExitNow(error = OT_ERROR_ALREADY);
}
}
aAddress.mNext = mUnicastAddresses;
mUnicastAddresses = &aAddress;
SetStateChangedFlags(aAddress.mRloc ? OT_CHANGED_THREAD_RLOC_ADDED : OT_CHANGED_IP6_ADDRESS_ADDED);
exit:
return error;
}
otError Netif::RemoveUnicastAddress(const NetifUnicastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
if (mUnicastAddresses == &aAddress)
{
mUnicastAddresses = mUnicastAddresses->GetNext();
ExitNow();
}
else if (mUnicastAddresses != NULL)
{
for (NetifUnicastAddress *cur = mUnicastAddresses; cur->GetNext(); cur = cur->GetNext())
{
if (cur->mNext == &aAddress)
{
cur->mNext = aAddress.mNext;
ExitNow();
}
}
}
ExitNow(error = OT_ERROR_NOT_FOUND);
exit:
if (error != OT_ERROR_NOT_FOUND)
{
SetStateChangedFlags(aAddress.mRloc ? OT_CHANGED_THREAD_RLOC_REMOVED : OT_CHANGED_IP6_ADDRESS_REMOVED);
}
return error;
}
otError Netif::AddExternalUnicastAddress(const NetifUnicastAddress &aAddress)
{
otError error = OT_ERROR_NONE;
NetifUnicastAddress *entry;
size_t num = sizeof(mExtUnicastAddresses) / sizeof(mExtUnicastAddresses[0]);
for (entry = mUnicastAddresses; entry; entry = entry->GetNext())
{
if (memcmp(&entry->mAddress, &aAddress.mAddress, sizeof(otIp6Address)) == 0)
{
VerifyOrExit((entry >= &mExtUnicastAddresses[0]) && (entry < &mExtUnicastAddresses[num]),
error = OT_ERROR_INVALID_ARGS);
entry->mPrefixLength = aAddress.mPrefixLength;
entry->mPreferred = aAddress.mPreferred;
entry->mValid = aAddress.mValid;
ExitNow();
}
}
// Find an available entry in the `mExtUnicastAddresses` array.
for (entry = &mExtUnicastAddresses[0]; num > 0; num--, entry++)
{
// In an unused/available entry, `mNext` points back to the entry itself.
if (entry->mNext == entry)
{
break;
}
}
VerifyOrExit(num > 0, error = OT_ERROR_NO_BUFS);
// Copy the new address into the available entry and insert it in linked-list.
*entry = aAddress;
entry->mNext = mUnicastAddresses;
mUnicastAddresses = entry;
SetStateChangedFlags(OT_CHANGED_IP6_ADDRESS_ADDED);
exit:
return error;
}
otError Netif::RemoveExternalUnicastAddress(const Address &aAddress)
{
otError error = OT_ERROR_NONE;
NetifUnicastAddress *entry;
NetifUnicastAddress *last = NULL;
size_t num = sizeof(mExtUnicastAddresses) / sizeof(mExtUnicastAddresses[0]);
for (entry = mUnicastAddresses; entry; entry = entry->GetNext())
{
if (memcmp(&entry->mAddress, &aAddress, sizeof(otIp6Address)) == 0)
{
VerifyOrExit((entry >= &mExtUnicastAddresses[0]) && (entry < &mExtUnicastAddresses[num]),
error = OT_ERROR_INVALID_ARGS);
if (last)
{
last->mNext = entry->mNext;
}
else
{
mUnicastAddresses = entry->GetNext();
}
break;
}
last = entry;
}
VerifyOrExit(entry != NULL, error = OT_ERROR_NOT_FOUND);
// To mark the address entry as unused/available, set the `mNext` pointer back to the entry itself.
entry->mNext = entry;
SetStateChangedFlags(OT_CHANGED_IP6_ADDRESS_REMOVED);
exit:
return error;
}
void Netif::RemoveAllExternalUnicastAddresses(void)
{
size_t num = sizeof(mExtUnicastAddresses) / sizeof(mExtUnicastAddresses[0]);
for (NetifUnicastAddress *entry = &mExtUnicastAddresses[0]; num > 0; num--, entry++)
{
// In unused entries, the `mNext` points back to the entry itself.
if (entry->mNext != entry)
{
RemoveExternalUnicastAddress(*static_cast<Address *>(&entry->mAddress));
}
}
}
bool Netif::IsUnicastAddress(const Address &aAddress) const
{
bool rval = false;
for (const NetifUnicastAddress *cur = mUnicastAddresses; cur; cur = cur->GetNext())
{
if (cur->GetAddress() == aAddress)
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
void Netif::SetStateChangedFlags(uint32_t aFlags)
{
mStateChangedFlags |= aFlags;
mStateChangedTask.Post();
}
void Netif::HandleStateChangedTask(Tasklet &aTasklet)
{
GetOwner(aTasklet).HandleStateChangedTask();
}
void Netif::HandleStateChangedTask(void)
{
uint32_t flags = mStateChangedFlags;
mStateChangedFlags = 0;
for (NetifCallback *callback = mCallbacks; callback; callback = callback->mNext)
{
callback->Callback(flags);
}
}
Netif &Netif::GetOwner(const Context &aContext)
{
#if OPENTHREAD_ENABLE_MULTIPLE_INSTANCES
Netif &netif = *static_cast<Netif *>(aContext.GetContext());
#else
Netif &netif = otGetThreadNetif();
OT_UNUSED_VARIABLE(aContext);
#endif
return netif;
}
} // namespace Ip6
} // namespace ot