diff --git a/include/openthread-types.h b/include/openthread-types.h index 757b2600d..609e42b5d 100644 --- a/include/openthread-types.h +++ b/include/openthread-types.h @@ -841,18 +841,28 @@ typedef struct otMacCounters */ /** - * This structure represents an IPv6 network interface address. + * This structure represents an IPv6 network interface unicast address. * */ typedef struct otNetifAddress { - otIp6Address mAddress; ///< The IPv6 address. + otIp6Address mAddress; ///< The IPv6 unicast address. uint32_t mPreferredLifetime; ///< The Preferred Lifetime. uint32_t mValidLifetime; ///< The Valid lifetime. uint8_t mPrefixLength; ///< The Prefix length. struct otNetifAddress *mNext; ///< A pointer to the next network interface address. } otNetifAddress; +/** + * This structure represents an IPv6 network interface multicast address. + * + */ +typedef struct otNetifMulticastAddress +{ + otIp6Address mAddress; ///< The IPv6 multicast address. + struct otNetifMulticastAddress *mNext; ///< A pointer to the next network interface multicast address. +} otNetifMulticastAddress; + /** * This enumeration represents the list of allowable values for an InterfaceId. */ diff --git a/include/openthread.h b/include/openthread.h index 1f680381f..c5345b402 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -790,6 +790,72 @@ ThreadError otAddUnicastAddress(otInstance *aInstance, const otNetifAddress *aAd */ ThreadError otRemoveUnicastAddress(otInstance *aInstance, const otIp6Address *aAddress); +/** + * Get the list of IPv6 multicast addresses subscribed to the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the first Network Interface Multicast Address. + */ +const otNetifMulticastAddress *otGetMulticastAddresses(otInstance *aInstance); + +/** + * Subscribe the Thread interface to a Network Interface Multicast Address. + * + * The passed in instance @p aAddress will be copied by the Thread interface. The Thread interface only + * supports a fixed number of externally added multicast addresses. See OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aAddress A pointer to an IP Address. + * + * @retval kThreadErrorNone Successfully subscribed to the Network Interface Multicast Address. + * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is invalid address. + * @retval kThreadError_NoBufs The Network Interface is already storing the maximum allowed external multicast addresses. + */ +ThreadError otSubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); + +/** + * Unsubscribe the Thread interface to a Network Interface Multicast Address. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aAddress A pointer to an IP Address. + * + * @retval kThreadErrorNone Successfully unsubscribed to the Network Interface Multicast Address. + * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is an internal address. + * @retval kThreadError_NotFound The IP Address indicated by @p aAddress was not found. + */ +ThreadError otUnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); + +/** + * Check if multicast promiscuous mode is enabled on the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otEnableMulticastPromiscuousMode + * @sa otDisableMulticastPromiscuousMode + */ +bool otIsMulticastPromiscuousModeEnabled(otInstance *aInstance); + +/** + * Enable multicast promiscuous mode on the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otIsMulticastPromiscuousModeEnabled + * @sa otDisableMulticastPromiscuousMode + */ +void otEnableMulticastPromiscuousMode(otInstance *aInstance); + +/** + * Disable multicast promiscuous mode on the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otIsMulticastPromiscuousModeEnabled + * @sa otEnableMulticastPromiscuousMode + */ +void otDisableMulticastPromiscuousMode(otInstance *aInstance); + /** * This function pointer is called to create IPv6 IID during SLAAC procedure. * diff --git a/src/cli/README.md b/src/cli/README.md index e3c56000d..3ea742535 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -25,6 +25,7 @@ OpenThread test scripts use the CLI to execute test cases. * [hashmacaddr](#hashmacaddr) * [ifconfig](#ifconfig) * [ipaddr](#ipaddr) +* [ipmaddr](#ipmaddr) * [joiner](#joiner) * [keysequence](#keysequence) * [leaderpartitionid](#leaderpartitionid) @@ -789,6 +790,64 @@ Delete an IPv6 address from the Thread interface. Done ``` +### ipmaddr + +List all IPv6 multicast addresses subscribed to the Thread interface. + +```bash +> ipmaddr +ff05:0:0:0:0:0:0:1 +ff33:40:fdde:ad00:beef:0:0:1 +ff32:40:fdde:ad00:beef:0:0:1 +Done +``` + +### ipmaddr add \ + +Subscribe the Thread interface to the IPv6 multicast address. + +```bash +> ipmaddr add ff05::1 +Done +``` + +### ipmaddr del \ + +Unsubscribe the Thread interface to the IPv6 multicast address. + +```bash +> ipmaddr del ff05::1 +Done +``` + +### ipmaddr promiscuous + +Get multicast promiscuous mode. + +```bash +> ipmaddr promiscuous +Disabled +Done +``` + +### ipmaddr promiscuous enable + +Enable multicast promiscuous mode. + +```bash +> ipmaddr promiscuous enable +Done +``` + +### ipmaddr promiscuous disable + +Disable multicast promiscuous mode. + +```bash +> ipmaddr promiscuous disable +Done +``` + ### joiner start \ \ Start the Joiner role. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 952fd2e82..f3dce9c1a 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -92,6 +92,7 @@ const struct Command Interpreter::sCommands[] = { "hashmacaddr", &Interpreter::ProcessHashMacAddress }, { "ifconfig", &Interpreter::ProcessIfconfig }, { "ipaddr", &Interpreter::ProcessIpAddr }, + { "ipmaddr", &Interpreter::ProcessIpMulticastAddr }, #if OPENTHREAD_ENABLE_JOINER { "joiner", &Interpreter::ProcessJoiner }, #endif @@ -770,6 +771,108 @@ exit: AppendResult(error); } +ThreadError Interpreter::ProcessIpMulticastAddrAdd(int argc, char *argv[]) +{ + ThreadError error; + struct otIp6Address address; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + + SuccessOrExit(error = otIp6AddressFromString(argv[0], &address)); + error = otSubscribeMulticastAddress(mInstance, &address); + +exit: + return error; +} + +ThreadError Interpreter::ProcessIpMulticastAddrDel(int argc, char *argv[]) +{ + ThreadError error; + struct otIp6Address address; + + VerifyOrExit(argc > 0, error = kThreadError_Parse); + + SuccessOrExit(error = otIp6AddressFromString(argv[0], &address)); + error = otUnsubscribeMulticastAddress(mInstance, &address); + +exit: + return error; +} + +ThreadError Interpreter::ProcessMulticastPromiscuous(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + + if (argc == 0) + { + if (otIsMulticastPromiscuousModeEnabled(mInstance)) + { + sServer->OutputFormat("Enabled\r\n"); + } + else + { + sServer->OutputFormat("Disabled\r\n"); + } + } + else + { + if (strcmp(argv[0], "enable") == 0) + { + otEnableMulticastPromiscuousMode(mInstance); + } + else if (strcmp(argv[0], "disable") == 0) + { + otDisableMulticastPromiscuousMode(mInstance); + } + else + { + ExitNow(error = kThreadError_Parse); + } + } + +exit: + return error; +} + +void Interpreter::ProcessIpMulticastAddr(int argc, char *argv[]) +{ + ThreadError error = kThreadError_None; + + if (argc == 0) + { + for (const otNetifMulticastAddress *addr = otGetMulticastAddresses(mInstance); addr; addr = addr->mNext) + { + sServer->OutputFormat("%x:%x:%x:%x:%x:%x:%x:%x\r\n", + HostSwap16(addr->mAddress.mFields.m16[0]), + HostSwap16(addr->mAddress.mFields.m16[1]), + HostSwap16(addr->mAddress.mFields.m16[2]), + HostSwap16(addr->mAddress.mFields.m16[3]), + HostSwap16(addr->mAddress.mFields.m16[4]), + HostSwap16(addr->mAddress.mFields.m16[5]), + HostSwap16(addr->mAddress.mFields.m16[6]), + HostSwap16(addr->mAddress.mFields.m16[7])); + } + } + else + { + if (strcmp(argv[0], "add") == 0) + { + SuccessOrExit(error = ProcessIpMulticastAddrAdd(argc - 1, argv + 1)); + } + else if (strcmp(argv[0], "del") == 0) + { + SuccessOrExit(error = ProcessIpMulticastAddrDel(argc - 1, argv + 1)); + } + else if (strcmp(argv[0], "promiscuous") == 0) + { + SuccessOrExit(error = ProcessMulticastPromiscuous(argc - 1, argv + 1)); + } + } + +exit: + AppendResult(error); +} + void Interpreter::ProcessKeySequence(int argc, char *argv[]) { ThreadError error = kThreadError_None; diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 810bc97ad..ad4657d06 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -167,6 +167,10 @@ private: void ProcessIpAddr(int argc, char *argv[]); ThreadError ProcessIpAddrAdd(int argc, char *argv[]); ThreadError ProcessIpAddrDel(int argc, char *argv[]); + void ProcessIpMulticastAddr(int argc, char *argv[]); + ThreadError ProcessIpMulticastAddrAdd(int argc, char *argv[]); + ThreadError ProcessIpMulticastAddrDel(int argc, char *argv[]); + ThreadError ProcessMulticastPromiscuous(int argc, char *argv[]); #if OPENTHREAD_ENABLE_JOINER void ProcessJoiner(int argc, char *argv[]); #endif // OPENTHREAD_ENABLE_JOINER diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 97abcb142..2e3c995b6 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -645,6 +645,7 @@ ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, int8_t interface bool receive = false; bool forward = false; bool tunnel = false; + bool multicastPromiscuous = false; uint8_t nextHeader; uint8_t hopLimit; @@ -677,9 +678,16 @@ ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, int8_t interface // determine destination of packet if (header.GetDestination().IsMulticast()) { - if (netif != NULL && netif->IsMulticastSubscribed(header.GetDestination())) + if (netif != NULL) { - receive = true; + if (netif->IsMulticastSubscribed(header.GetDestination())) + { + receive = true; + } + else if (netif->IsMulticastPromiscuousModeEnabled()) + { + multicastPromiscuous = true; + } } if (netif == NULL) @@ -739,6 +747,10 @@ ThreadError Ip6::HandleDatagram(Message &message, Netif *netif, int8_t interface SuccessOrExit(error = HandlePayload(message, messageInfo, nextHeader)); } + else if (multicastPromiscuous) + { + ProcessReceiveCallback(message, messageInfo, nextHeader); + } if (forward) { diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index 5a7f8a933..1d6a3b1cd 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -42,17 +42,18 @@ namespace Ip6 { Netif::Netif(Ip6 &aIp6, int8_t aInterfaceId): mIp6(aIp6), - mStateChangedTask(aIp6.mTaskletScheduler, &Netif::HandleStateChangedTask, this) + mCallbacks(NULL), + mUnicastAddresses(NULL), + mMulticastAddresses(NULL), + mInterfaceId(aInterfaceId), + mAllRoutersSubscribed(false), + mMulticastPromiscuousMode(false), + mStateChangedTask(aIp6.mTaskletScheduler, &Netif::HandleStateChangedTask, this), + mNext(NULL), + mStateChangedFlags(0), + mMaskExtUnicastAddresses(0), + mMaskExtMulticastAddresses(0) { - mCallbacks = NULL; - mUnicastAddresses = NULL; - mMulticastAddresses = NULL; - mInterfaceId = aInterfaceId; - mAllRoutersSubscribed = false; - mNext = NULL; - mMaskExtUnicastAddresses = 0; - - mStateChangedFlags = 0; } ThreadError Netif::RegisterCallback(NetifCallback &aCallback) @@ -132,7 +133,7 @@ bool Netif::IsMulticastSubscribed(const Address &aAddress) const ExitNow(rval = mAllRoutersSubscribed); } - for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->mNext) + for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) { if (memcmp(&cur->mAddress, &aAddress, sizeof(cur->mAddress)) == 0) { @@ -154,11 +155,16 @@ void Netif::UnsubscribeAllRoutersMulticast() mAllRoutersSubscribed = false; } +const NetifMulticastAddress *Netif::GetMulticastAddresses() const +{ + return mMulticastAddresses; +} + ThreadError Netif::SubscribeMulticast(NetifMulticastAddress &aAddress) { ThreadError error = kThreadError_None; - for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->mNext) + for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) { if (cur == &aAddress) { @@ -179,12 +185,12 @@ ThreadError Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress) if (mMulticastAddresses == &aAddress) { - mMulticastAddresses = mMulticastAddresses->mNext; + mMulticastAddresses = mMulticastAddresses->GetNext(); ExitNow(); } else if (mMulticastAddresses != NULL) { - for (NetifMulticastAddress *cur = mMulticastAddresses; cur->mNext; cur = cur->mNext) + for (NetifMulticastAddress *cur = mMulticastAddresses; cur->GetNext(); cur = cur->GetNext()) { if (cur->mNext == &aAddress) { @@ -200,6 +206,102 @@ exit: return error; } +ThreadError Netif::SubscribeExternalMulticast(const Address &aAddress) +{ + ThreadError error = kThreadError_None; + int8_t index = 0; + + for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) + { + if (memcmp(&cur->mAddress, &aAddress, sizeof(otIp6Address)) == 0) + { + VerifyOrExit(GetExtMulticastAddressIndex(cur) != -1, error = kThreadError_InvalidArgs); + ExitNow(error = kThreadError_Already); + } + } + + // Make sure we haven't set all the bits in the mask already + VerifyOrExit(mMaskExtMulticastAddresses != ((1 << OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS) - 1), + error = kThreadError_NoBufs); + + // Get next available entry index + while ((mMaskExtMulticastAddresses & (1 << index)) != 0) + { + index++; + } + + assert(index < OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS); + + // Increase the count and mask the index + mMaskExtMulticastAddresses |= 1 << index; + + // Copy the address to the next available dynamic address + mExtMulticastAddresses[index].mAddress = aAddress; + mExtMulticastAddresses[index].mNext = mMulticastAddresses; + + mMulticastAddresses = &mExtMulticastAddresses[index]; + +exit: + return error; +} + +ThreadError Netif::UnsubscribeExternalMulticast(const Address &aAddress) +{ + ThreadError error = kThreadError_None; + NetifMulticastAddress *last = NULL; + int8_t aAddressIndexToRemove = -1; + + for (NetifMulticastAddress *cur = mMulticastAddresses; cur; cur = cur->GetNext()) + { + if (memcmp(&cur->mAddress, &aAddress, sizeof(otIp6Address)) == 0) + { + aAddressIndexToRemove = GetExtMulticastAddressIndex(cur); + VerifyOrExit(aAddressIndexToRemove != -1, error = kThreadError_InvalidArgs); + + if (last) + { + last->mNext = cur->GetNext(); + } + else + { + mMulticastAddresses = cur->GetNext(); + } + + break; + } + + last = cur; + } + + if (aAddressIndexToRemove != -1) + { + mMaskExtMulticastAddresses &= ~(1 << aAddressIndexToRemove); + } + else + { + error = kThreadError_NotFound; + } + +exit: + + return error; +} + +bool Netif::IsMulticastPromiscuousModeEnabled(void) +{ + return mMulticastPromiscuousMode; +} + +void Netif::EnableMulticastPromiscuousMode(void) +{ + mMulticastPromiscuousMode = true; +} + +void Netif::DisableMulticastPromiscuousMode(void) +{ + mMulticastPromiscuousMode = false; +} + const NetifUnicastAddress *Netif::GetUnicastAddresses() const { return mUnicastAddresses; diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index e42d55a9a..42e0d8a16 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -121,7 +121,7 @@ public: * This class implements an IPv6 network interface multicast address. * */ -class NetifMulticastAddress +class NetifMulticastAddress: public otNetifMulticastAddress { friend class Netif; @@ -143,16 +143,20 @@ public: Address &GetAddress(void) { return *static_cast
(&mAddress); } /** - * This method returns the next multicast address assigned to the interface. + * This method returns the next multicast address subscribed to the interface. * * @returns A pointer to the next multicast address. * */ - const NetifMulticastAddress *GetNext(void) const { return mNext; } + const NetifMulticastAddress *GetNext(void) const { return static_cast(mNext); } -private: - Address mAddress; - NetifMulticastAddress *mNext; + /** + * This method returns the next multicast address subscribed to the interface. + * + * @returns A pointer to the next multicast address. + * + */ + NetifMulticastAddress *GetNext(void) { return static_cast(mNext); } }; /** @@ -349,6 +353,14 @@ public: */ void UnsubscribeAllRoutersMulticast(void); + /** + * This method returns a pointer to the list of multicast addresses. + * + * @returns A pointer to the list of multicast addresses. + * + */ + const NetifMulticastAddress *GetMulticastAddresses(void) const; + /** * This method subscribes the network interface to a multicast address. * @@ -371,6 +383,51 @@ public: */ ThreadError UnsubscribeMulticast(const NetifMulticastAddress &aAddress); + /** + * This method subscribes the network interface to the external (to OpenThread) multicast address. + * + * @param[in] aAddress A reference to the multicast address. + * + * @retval kThreadError_None Successfully subscribed to @p aAddress. + * @retval kThreadError_Already The multicast address is already subscribed. + * @retval kThreadError_InvalidArgs The address indicated by @p aAddress is an internal multicast address. + * @retval kThreadError_NoBufs The maximum number of allowed external multicast addresses are already added. + * + */ + ThreadError SubscribeExternalMulticast(const Address &aAddress); + + /** + * This method ussubscribes the network interface to the external (to OpenThread) multicast address. + * + * @param[in] aAddress A reference to the multicast address. + * + * @retval kThreadError_None Successfully unsubscribed to the unicast address. + * @retval kThreadError_InvalidArgs The address indicated by @p aAddress is an internal address. + * @retval kThreadError_NotFound The multicast address was not found. + * + */ + ThreadError UnsubscribeExternalMulticast(const Address &aAddress); + + /** + * This method checks if multicast promiscuous mode is enabled on the network interface. + * + * @retval TRUE If the multicast promiscuous mode is enabled. + * @retval FALSE If the multicast promiscuous mode is disabled. + */ + bool IsMulticastPromiscuousModeEnabled(void); + + /** + * This method enables multicast promiscuous mode on the network interface. + * + */ + void EnableMulticastPromiscuousMode(void); + + /** + * This method disables multicast promiscuous mode on the network interface. + * + */ + void DisableMulticastPromiscuousMode(void); + /** * This method registers a network interface callback. * @@ -455,6 +512,7 @@ private: NetifMulticastAddress *mMulticastAddresses; int8_t mInterfaceId; bool mAllRoutersSubscribed; + bool mMulticastPromiscuousMode; Tasklet mStateChangedTask; Netif *mNext; @@ -463,6 +521,9 @@ private: NetifUnicastAddress mExtUnicastAddresses[OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS]; uint8_t mMaskExtUnicastAddresses; // Must have enough bits to hold OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS + NetifMulticastAddress mExtMulticastAddresses[OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS]; + uint8_t mMaskExtMulticastAddresses; // Must have enough bits to hold OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS + /** * This method determines if an address is one of the external unicast addresses, and if so returns * the index in the mExtUnicastAddresses array. @@ -480,6 +541,24 @@ private: return static_cast(address - &mExtUnicastAddresses[0]); } + + /** + * This method determines if an address is one of the external multicast addresses, and if so returns + * the index in the mExtMulticastAddresses array. + * + * @param[in] aAddress A pointer to the Network Interface Multicast address. + * + * @returns The index in the mExtMulticastAddresses array or -1 if not part of the array. + * + */ + int8_t GetExtMulticastAddressIndex(const NetifMulticastAddress *address) { + if (address < &mExtMulticastAddresses[0] || + address >= &mExtMulticastAddresses[0] + OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS) { + return -1; + } + + return static_cast(address - &mExtMulticastAddresses[0]); + } }; /** diff --git a/src/core/openthread-core-default-config.h b/src/core/openthread-core-default-config.h index 353676ac1..1b9e5e92f 100644 --- a/src/core/openthread-core-default-config.h +++ b/src/core/openthread-core-default-config.h @@ -125,6 +125,16 @@ #define OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS 4 #endif // OPENTHREAD_CONFIG_MAX_EXT_IP_ADDRS +/** + * @def OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS + * + * The maximum number of supported IPv6 multicast addresses allows to be externally added. + * + */ +#ifndef OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS +#define OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS 2 +#endif // OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS + /** * @def OPENTHREAD_CONFIG_6LOWPAN_REASSEMBLY_TIMEOUT * diff --git a/src/core/openthread.cpp b/src/core/openthread.cpp index 41f3c84d8..855736a86 100644 --- a/src/core/openthread.cpp +++ b/src/core/openthread.cpp @@ -929,6 +929,36 @@ void otDhcp6ClientUpdate(otInstance *aInstance, otNetifAddress *aAddresses, uint } #endif // OPENTHREAD_ENABLE_DHCP6_CLIENT +const otNetifMulticastAddress *otGetMulticastAddresses(otInstance *aInstance) +{ + return aInstance->mThreadNetif.GetMulticastAddresses(); +} + +ThreadError otSubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress) +{ + return aInstance->mThreadNetif.SubscribeExternalMulticast(*static_cast(aAddress)); +} + +ThreadError otUnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress) +{ + return aInstance->mThreadNetif.UnsubscribeExternalMulticast(*static_cast(aAddress)); +} + +bool otIsMulticastPromiscuousModeEnabled(otInstance *aInstance) +{ + return aInstance->mThreadNetif.IsMulticastPromiscuousModeEnabled(); +} + +void otEnableMulticastPromiscuousMode(otInstance *aInstance) +{ + aInstance->mThreadNetif.EnableMulticastPromiscuousMode(); +} + +void otDisableMulticastPromiscuousMode(otInstance *aInstance) +{ + aInstance->mThreadNetif.DisableMulticastPromiscuousMode(); +} + void otSlaacUpdate(otInstance *aInstance, otNetifAddress *aAddresses, uint32_t aNumAddresses, otSlaacIidCreate aIidCreate, void *aContext) {