From 5ba896266c9cbeb059931cc8044cbaa8e0964b6a Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 17 Jul 2017 20:58:56 -0700 Subject: [PATCH] [ncp] Support for multicast address table (#2001) This commit adds a new spinel property (along with its prop handlers) `SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE` to provide the list of multicast IPv6 addresses. It also adds `OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED` and `OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED` to flags used in `NetifCallback` to indicate changes in multicast address list. --- doc/spinel-protocol-src/spinel-prop-ipv6.md | 8 ++ include/openthread/types.h | 2 + src/core/net/netif.cpp | 10 ++ src/ncp/ncp_base.cpp | 126 ++++++++++++++++++++ src/ncp/ncp_base.hpp | 3 + src/ncp/spinel.c | 4 + src/ncp/spinel.h | 3 + 7 files changed, 156 insertions(+) diff --git a/doc/spinel-protocol-src/spinel-prop-ipv6.md b/doc/spinel-protocol-src/spinel-prop-ipv6.md index 0a874daac..88438761b 100644 --- a/doc/spinel-protocol-src/spinel-prop-ipv6.md +++ b/doc/spinel-protocol-src/spinel-prop-ipv6.md @@ -22,6 +22,7 @@ IPv6 Prefix + Prefix Length * Type: Read-Write * Packed-Encoding: `A(t(6CLLC))` +This property provides all unicast addresses. Array of structures containing: * `6`: IPv6 Address @@ -39,3 +40,10 @@ turned on, ping request ICMP packets will not be passed to the host. Default value is `false`. +### PROP 102: SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE {#prop-ipv6-multicast-address-table} +* Type: Read-Write +* Packed-Encoding: `A(t(6))` + +Array of structures containing: + +* `6`: Multicast IPv6 Address diff --git a/include/openthread/types.h b/include/openthread/types.h index f84a7d5e2..c5a48a057 100644 --- a/include/openthread/types.h +++ b/include/openthread/types.h @@ -653,6 +653,8 @@ enum OT_CHANGED_THREAD_NETDATA = 1 << 9, ///< Thread Network Data changed OT_CHANGED_THREAD_CHILD_ADDED = 1 << 10, ///< Child was added OT_CHANGED_THREAD_CHILD_REMOVED = 1 << 11, ///< Child was removed + OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED = 1 << 12, ///< Subscribed to a IPv6 multicast address + OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED = 1 << 13, ///< Unsubscribed from a IPv6 multicast address }; /** diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index 31a04ba5f..b8a9fa641 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -156,6 +156,7 @@ otError Netif::SubscribeMulticast(NetifMulticastAddress &aAddress) aAddress.mNext = mMulticastAddresses; mMulticastAddresses = &aAddress; + SetStateChangedFlags(OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED); exit: return error; @@ -185,6 +186,12 @@ otError Netif::UnsubscribeMulticast(const NetifMulticastAddress &aAddress) ExitNow(error = OT_ERROR_NOT_FOUND); exit: + + if (error != OT_ERROR_NOT_FOUND) + { + SetStateChangedFlags(OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED); + } + return error; } @@ -215,6 +222,7 @@ otError Netif::SubscribeExternalMulticast(const Address &aAddress) entry->mAddress = aAddress; entry->mNext = mMulticastAddresses; mMulticastAddresses = entry; + SetStateChangedFlags(OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED); exit: return error; @@ -254,6 +262,8 @@ otError Netif::UnsubscribeExternalMulticast(const Address &aAddress) // To mark the address entry as unused/available, set the `mNext` pointer back to the entry itself. entry->mNext = entry; + SetStateChangedFlags(OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED); + exit: return error; } diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index a8739b298..3e8231849 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -184,6 +184,7 @@ const NcpBase::GetPropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = NCP_GET_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE), NCP_GET_PROP_HANDLER_ENTRY(IPV6_ROUTE_TABLE), NCP_GET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD), + NCP_GET_PROP_HANDLER_ENTRY(IPV6_MULTICAST_ADDRESS_TABLE), NCP_GET_PROP_HANDLER_ENTRY(THREAD_RLOC16_DEBUG_PASSTHRU), NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_JOINER_FLAG), NCP_GET_PROP_HANDLER_ENTRY(THREAD_DISCOVERY_SCAN_ENABLE_FILTERING), @@ -372,6 +373,7 @@ const NcpBase::InsertPropertyHandlerEntry NcpBase::mInsertPropertyHandlerTable[] NCP_INSERT_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES), #endif NCP_INSERT_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE), + NCP_INSERT_PROP_HANDLER_ENTRY(IPV6_MULTICAST_ADDRESS_TABLE), NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_ASSISTING_PORTS), #if OPENTHREAD_ENABLE_BORDER_ROUTER NCP_INSERT_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES), @@ -396,6 +398,7 @@ const NcpBase::RemovePropertyHandlerEntry NcpBase::mRemovePropertyHandlerTable[] NCP_REMOVE_PROP_HANDLER_ENTRY(MAC_SRC_MATCH_EXTENDED_ADDRESSES), #endif NCP_REMOVE_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE), + NCP_REMOVE_PROP_HANDLER_ENTRY(IPV6_MULTICAST_ADDRESS_TABLE), #if OPENTHREAD_ENABLE_BORDER_ROUTER NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_OFF_MESH_ROUTES), NCP_REMOVE_PROP_HANDLER_ENTRY(THREAD_ON_MESH_NETS), @@ -1340,6 +1343,17 @@ void NcpBase::UpdateChangedProps(void) mChangedFlags &= ~static_cast(OT_CHANGED_IP6_ADDRESS_ADDED | OT_CHANGED_IP6_ADDRESS_REMOVED); } + else if ((mChangedFlags & (OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED | OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED)) != 0) + { + SuccessOrExit( + HandleCommandPropertyGet( + SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, + SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE + )); + + mChangedFlags &= ~static_cast(OT_CHANGED_IP6_MULTICAST_SUBSRCRIBED | + OT_CHANGED_IP6_MULTICAST_UNSUBSRCRIBED); + } else if ((mChangedFlags & (OT_CHANGED_THREAD_CHILD_ADDED | OT_CHANGED_THREAD_CHILD_REMOVED)) != 0) { SuccessOrExit( @@ -3430,6 +3444,40 @@ otError NcpBase::GetPropertyHandler_IPV6_ICMP_PING_OFFLOAD(uint8_t aHeader, spin ); } +otError NcpBase::GetPropertyHandler_IPV6_MULTICAST_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey) +{ + otError error = OT_ERROR_NONE; + const otNetifMulticastAddress *address; + + mDisableStreamWrite = true; + + SuccessOrExit(error = OutboundFrameBegin(aHeader)); + SuccessOrExit( + error = OutboundFrameFeedPacked( + SPINEL_DATATYPE_COMMAND_PROP_S, + aHeader, + SPINEL_CMD_PROP_VALUE_IS, + aKey + )); + + for (address = otIp6GetMulticastAddresses(mInstance); address; address = address->mNext) + { + SuccessOrExit( + error = OutboundFrameFeedPacked( + SPINEL_DATATYPE_STRUCT_S( + SPINEL_DATATYPE_IPv6ADDR_S // IPv6 address + ), + &address->mAddress + )); + } + + SuccessOrExit(error = OutboundFrameSend()); + +exit: + mDisableStreamWrite = false; + return error; +} + otError NcpBase::GetPropertyHandler_THREAD_RLOC16_DEBUG_PASSTHRU(uint8_t aHeader, spinel_prop_key_t aKey) { // Note reverse logic: passthru enabled = filter disabled @@ -6804,6 +6852,46 @@ exit: return error; } +otError NcpBase::InsertPropertyHandler_IPV6_MULTICAST_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey, + const uint8_t *aValuePtr, uint16_t aValueLen) +{ + spinel_ssize_t parsedLength; + otError error = OT_ERROR_NONE; + spinel_status_t spinelError = SPINEL_STATUS_OK; + otIp6Address *addrPtr; + + parsedLength = spinel_datatype_unpack( + aValuePtr, + aValueLen, + SPINEL_DATATYPE_IPv6ADDR_S, // IPv6 address + &addrPtr + ); + + VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); + + error = otIp6SubscribeMulticastAddress(mInstance, addrPtr); + VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); + + error = SendPropertyUpdate( + aHeader, + SPINEL_CMD_PROP_VALUE_INSERTED, + aKey, + aValuePtr, + aValueLen + ); + + spinelError = SPINEL_STATUS_OK; + +exit: + + if (spinelError != SPINEL_STATUS_OK) + { + error = SendLastStatus(aHeader, spinelError); + } + + return error; +} + #if OPENTHREAD_ENABLE_BORDER_ROUTER otError NcpBase::InsertPropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, uint16_t aValueLen) @@ -7305,6 +7393,44 @@ exit: return error; } +otError NcpBase::RemovePropertyHandler_IPV6_MULTICAST_ADDRESS_TABLE(uint8_t aHeader, spinel_prop_key_t aKey, + const uint8_t *aValuePtr, uint16_t aValueLen) +{ + spinel_ssize_t parsedLength; + otError error = OT_ERROR_NONE; + spinel_status_t spinelError = SPINEL_STATUS_OK; + otIp6Address *addrPtr; + + parsedLength = spinel_datatype_unpack( + aValuePtr, + aValueLen, + SPINEL_DATATYPE_IPv6ADDR_S, + &addrPtr + ); + + VerifyOrExit(parsedLength > 0, spinelError = SPINEL_STATUS_PARSE_ERROR); + + error = otIp6UnsubscribeMulticastAddress(mInstance, addrPtr); + VerifyOrExit(error == OT_ERROR_NONE, spinelError = ThreadErrorToSpinelStatus(error)); + + error = SendPropertyUpdate( + aHeader, + SPINEL_CMD_PROP_VALUE_REMOVED, + aKey, + aValuePtr, + aValueLen + ); + +exit: + + if (spinelError != SPINEL_STATUS_OK) + { + error = SendLastStatus(aHeader, spinelError); + } + + return error; +} + #if OPENTHREAD_ENABLE_BORDER_ROUTER otError NcpBase::RemovePropertyHandler_THREAD_OFF_MESH_ROUTES(uint8_t aHeader, spinel_prop_key_t aKey, const uint8_t *aValuePtr, uint16_t aValueLen) diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index 33e56a099..fd1971fa4 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -375,6 +375,7 @@ private: NCP_GET_PROP_HANDLER(IPV6_ADDRESS_TABLE); NCP_GET_PROP_HANDLER(IPV6_ROUTE_TABLE); NCP_GET_PROP_HANDLER(IPV6_ICMP_PING_OFFLOAD); + NCP_GET_PROP_HANDLER(IPV6_MULTICAST_ADDRESS_TABLE); NCP_GET_PROP_HANDLER(THREAD_RLOC16_DEBUG_PASSTHRU); NCP_GET_PROP_HANDLER(THREAD_OFF_MESH_ROUTES); NCP_GET_PROP_HANDLER(STREAM_NET); @@ -543,6 +544,7 @@ private: NCP_INSERT_PROP_HANDLER(MAC_SRC_MATCH_EXTENDED_ADDRESSES); #endif NCP_INSERT_PROP_HANDLER(IPV6_ADDRESS_TABLE); + NCP_INSERT_PROP_HANDLER(IPV6_MULTICAST_ADDRESS_TABLE); #if OPENTHREAD_ENABLE_BORDER_ROUTER NCP_INSERT_PROP_HANDLER(THREAD_OFF_MESH_ROUTES); NCP_INSERT_PROP_HANDLER(THREAD_ON_MESH_NETS); @@ -564,6 +566,7 @@ private: NCP_REMOVE_PROP_HANDLER(MAC_SRC_MATCH_EXTENDED_ADDRESSES); #endif NCP_REMOVE_PROP_HANDLER(IPV6_ADDRESS_TABLE); + NCP_REMOVE_PROP_HANDLER(IPV6_MULTICAST_ADDRESS_TABLE); #if OPENTHREAD_ENABLE_BORDER_ROUTER NCP_REMOVE_PROP_HANDLER(THREAD_OFF_MESH_ROUTES); NCP_REMOVE_PROP_HANDLER(THREAD_ON_MESH_NETS); diff --git a/src/ncp/spinel.c b/src/ncp/spinel.c index 2ed66ba43..da688e5eb 100644 --- a/src/ncp/spinel.c +++ b/src/ncp/spinel.c @@ -1332,6 +1332,10 @@ spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) ret = "PROP_IPV6_ICMP_PING_OFFLOAD"; break; + case SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE: + ret = "PROP_IPV6_MULTICAST_ADDRESS_TABLE"; + break; + case SPINEL_PROP_STREAM_DEBUG: ret = "PROP_STREAM_DEBUG"; break; diff --git a/src/ncp/spinel.h b/src/ncp/spinel.h index fae5b0ff7..ec3e09764 100644 --- a/src/ncp/spinel.h +++ b/src/ncp/spinel.h @@ -990,6 +990,9 @@ typedef enum */ SPINEL_PROP_IPV6_ICMP_PING_OFFLOAD = SPINEL_PROP_IPV6__BEGIN + 5, ///< [b] + SPINEL_PROP_IPV6_MULTICAST_ADDRESS_TABLE + = SPINEL_PROP_IPV6__BEGIN + 6, ///< [A(t(6))] + SPINEL_PROP_IPV6__END = 0x70, SPINEL_PROP_STREAM__BEGIN = 0x70,