From 4e060e4dd38345942f4888701eedfd457aea1577 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Thu, 16 Nov 2017 05:43:13 -0800 Subject: [PATCH] [ncp] add Thread Dataset related spinel properties and their get/set handlers in NCP (#2327) This commit adds new spinel properties related to Thread Operational Dataset and implements the corresponding get/set handlers for the new properties in `NcpBase`. --- doc/spinel-protocol-src/spinel-tech-thread.md | 161 +++++++++++++ src/ncp/ncp_base.cpp | 6 + src/ncp/ncp_base.hpp | 10 +- src/ncp/ncp_base_ftd.cpp | 226 ++++++++++++++++++ src/ncp/ncp_base_mtd.cpp | 139 ++++++++++- src/ncp/spinel.c | 36 +++ src/ncp/spinel.h | 150 +++++++++++- 7 files changed, 723 insertions(+), 5 deletions(-) diff --git a/doc/spinel-protocol-src/spinel-tech-thread.md b/doc/spinel-protocol-src/spinel-tech-thread.md index 323247172..570c7ae94 100644 --- a/doc/spinel-protocol-src/spinel-tech-thread.md +++ b/doc/spinel-protocol-src/spinel-tech-thread.md @@ -352,3 +352,164 @@ Data per item is: * `C`: Link Quality Out * `C`: Age (seconds since last heard) * `b`: Link established with Router ID or not. + +### PROP 5400: SPINEL_PROP_THREAD_ACTIVE_DATASET (#prop-thread-active-dataset) + + * Type: Read-Write + * Packing-Encoding: `A(t(iD))` + +This property provides access to current Thread Active Operational Dataset. +A Thread device maintains the Operational Dataset that it has stored locally +and the one currently in use by the partition to which it is attached. This +property corresponds to the locally stored Dataset on the device. + +Operational Dataset consists of a set of supported properties (e.g., channel, +master key, network name, PAN id, etc). Note that not all supported properties +may be present (have a value) in a Dataset. + +The Dataset value is encoded as an array of structures containing pairs of +property key (as `i`) followed by the property value (as `D`). The property +value must follow the format associated with the corresponding spinel +property. + +On write, any unknown/unsupported property keys must be ignored. + +The following properties can be included in a Dataset list: + +* SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP +* SPINEL_PROP_PHY_CHAN +* SPINEL_PROP_PHY_CHAN_SUPPORTED (Channel Mask Page 0) +* SPINEL_PROP_NET_MASTER_KEY +* SPINEL_PROP_NET_NETWORK_NAME +* SPINEL_PROP_NET_XPANID +* SPINEL_PROP_MAC_15_4_PANID +* SPINEL_PROP_IPV6_ML_PREFIX +* SPINEL_PROP_NET_PSKC +* SPINEL_PROP_DATASET_SECURITY_POLICY + +### PROP 5401: SPINEL_PROP_THREAD_PENDING_DATASET (#prop-thread-pending-dataset) + + * Type: Read-Write + * Packing-Encoding: `A(t(iD))` + +This property provide access to current Thread Pending Operational Dataset +locally stored on the device. + +The formatting of this property follows the same rules as in +SPINEL_PROP_THREAD_ACTIVE_DATASET. + +In addition supported properties in SPINEL_PROP_THREAD_ACTIVE_DATASET, the +following properties can also be included in the Pending Dataset: + +* SPINEL_PROP_DATASET_PENDING_TIMESTAMP +* SPINEL_PROP_DATASET_DELAY_TIMER + +### PROP 5402: SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET (#prop-thread-mgmt-active-dataset) + + * Type: Write only + * Packing-Encoding: `A(t(iD))` + +The formatting of this property follows the same rules as in +SPINEL_PROP_THREAD_ACTIVE_DATASET. + +This is write-only property. When written, it triggers a MGMT_ACTIVE_SET meshcop +command to be sent to the leader with the given Dataset. The spinel frame response +should be a `LAST_STATUS` with the status of the transmission of MGMT_ACTIVE_SET +command. + +In addition to supported properties in SPINEL_PROP_THREAD_ACTIVE_DATASET, the +following property can be included in the Dataset (to allow for custom raw +TLVs): + +* SPINEL_PROP_DATASET_RAW_TLVS + +### PROP 5403: SPINEL_PROP_THREAD_MGMT_PENDING_DATASET (#prop-thread-mgmt-pending-dataset) + + * Type: Write only + * Packing-Encoding: `A(t(iD))` + +This property is similar to SPINEL_PROP_THREAD_PENDING_DATASET and follows the +same format and rules. + +In addition to supported properties in SPINEL_PROP_THREAD_PENDING_DATASET, the +following property can be included the Dataset (to allow for custom raw TLVs to +be provided): + +* SPINEL_PROP_DATASET_RAW_TLVS + +### PROP 5404: SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP (#prop-dataset-active-timestamps) + + * Type: No direct read or write + * Packing-Encoding: `X` + +This property represents the Active Timestamp field in a Thread Operational +Dataset. + +This can only be included in one of the Dataset related properties below: + +* SPINEL_PROP_THREAD_ACTIVE_DATASET +* SPINEL_PROP_THREAD_PENDING_DATASET +* SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET +* SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + +### PROP 5405: SPINEL_PROP_DATASET_PENDING_TIMESTAMP (#prop-dataset-pending-timestamps) + + * Type: No direct read or write + * Packing-Encoding: `X` + +This property represents the Pending Timestamp field in a Thread Operational +Dataset. + +It can only be included in one of the Pending Dataset properties: + +* SPINEL_PROP_THREAD_PENDING_DATASET +* SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + + +### PROP 5406: SPINEL_PROP_DATASET_DELAY_TIMER (#prop-dataset-delay-timer) + + * Type: No direct read or write + * Packing-Encoding: `L` + +This property represents the Delay Timer field in a Thread Operational Dataset. +Delay timer (in ms) specifies the time renaming until Thread devices overwrite +the value in the Active Operational Dataset with the corresponding values in the +Pending Operational Dataset. + +It can only be included in one of the Pending Dataset properties: + +* SPINEL_PROP_THREAD_PENDING_DATASET +* SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + +### PROP 5407: SPINEL_PROP_DATASET_SECURITY_POLICY (#prop-dataset-security-policy) + + * Type: No direct read or write + * Packing-Encoding: `SC` + +This property represents the Security Policy field in a Thread Operational +Dataset. + +The content is: + +* `S` : Key Rotation Time (in units of hour) +* `C` : Security Policy Flags (as specified in Thread 1.1 Section 8.10.1.15) + +It can only be included in one of the Dataset related properties below: + +* SPINEL_PROP_THREAD_ACTIVE_DATASET +* SPINEL_PROP_THREAD_PENDING_DATASET +* SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET +* SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + +### PROP 5408: SPINEL_PROP_DATASET_RAW_TLVS (#prop-dataset-raw-tlvs) + + * Type: No direct read or write + * Packing-Encoding: `D` + +This property defines extra raw TLVs that can be added to an Operational +DataSet. + +It can only be included in one of the following Dataset properties: + +* SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET +* SPINEL_PROP_THREAD_MGMT_PENDING_DATASET diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 9d6cb58ea..164ecbebf 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -136,6 +136,8 @@ const NcpBase::PropertyHandlerEntry NcpBase::mGetPropertyHandlerTable[] = NCP_GET_PROP_HANDLER_ENTRY(THREAD_NETWORK_DATA), NCP_GET_PROP_HANDLER_ENTRY(THREAD_STABLE_NETWORK_DATA), #endif + NCP_GET_PROP_HANDLER_ENTRY(THREAD_ACTIVE_DATASET), + NCP_GET_PROP_HANDLER_ENTRY(THREAD_PENDING_DATASET), NCP_GET_PROP_HANDLER_ENTRY(IPV6_ADDRESS_TABLE), NCP_GET_PROP_HANDLER_ENTRY(IPV6_ICMP_PING_OFFLOAD), NCP_GET_PROP_HANDLER_ENTRY(IPV6_LL_ADDR), @@ -310,6 +312,10 @@ const NcpBase::PropertyHandlerEntry NcpBase::mSetPropertyHandlerTable[] = NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_ENABLED), NCP_SET_PROP_HANDLER_ENTRY(THREAD_TMF_PROXY_STREAM), #endif + NCP_SET_PROP_HANDLER_ENTRY(THREAD_ACTIVE_DATASET), + NCP_SET_PROP_HANDLER_ENTRY(THREAD_PENDING_DATASET), + NCP_SET_PROP_HANDLER_ENTRY(THREAD_MGMT_ACTIVE_DATASET), + NCP_SET_PROP_HANDLER_ENTRY(THREAD_MGMT_PENDING_DATASET), #endif // #if OPENTHREAD_FTD }; diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index cc5a0ef93..e7e776597 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -243,10 +243,12 @@ private: static void SendDoneTask(void *aContext); void SendDoneTask(void); - otError GetPropertyHandler_ChannelMaskHelper(uint32_t channel_mask); + otError EncodeChannelMask(uint32_t channel_mask); + otError EncodeOperationalDataset(const otOperationalDataset &aDataset); #if OPENTHREAD_FTD otError EncodeChildInfo(const otChildInfo &aChildInfo); + otError DecodeOperationalDataset(otOperationalDataset &aDataset, const uint8_t **aTlvs, uint8_t *aTlvsLength); #endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD @@ -488,6 +490,8 @@ private: NCP_SET_PROP_HANDLER(THREAD_DISCOVERY_SCAN_ENABLE_FILTERING); NCP_GET_PROP_HANDLER(THREAD_DISCOVERY_SCAN_PANID); NCP_SET_PROP_HANDLER(THREAD_DISCOVERY_SCAN_PANID); + NCP_GET_PROP_HANDLER(THREAD_ACTIVE_DATASET); + NCP_GET_PROP_HANDLER(THREAD_PENDING_DATASET); NCP_GET_PROP_HANDLER(CNTR_TX_PKT_TOTAL); NCP_GET_PROP_HANDLER(CNTR_TX_PKT_ACK_REQ); @@ -598,6 +602,10 @@ private: NCP_SET_PROP_HANDLER(THREAD_TMF_PROXY_STREAM); #endif NCP_REMOVE_PROP_HANDLER(THREAD_ACTIVE_ROUTER_IDS); + NCP_SET_PROP_HANDLER(THREAD_ACTIVE_DATASET); + NCP_SET_PROP_HANDLER(THREAD_PENDING_DATASET); + NCP_SET_PROP_HANDLER(THREAD_MGMT_ACTIVE_DATASET); + NCP_SET_PROP_HANDLER(THREAD_MGMT_PENDING_DATASET); #endif // OPENTHREAD_FTD diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index 8f9116469..25da62edb 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -32,6 +32,7 @@ #include "ncp_base.hpp" +#include #include #include #include @@ -39,6 +40,7 @@ #include #include #include + #if OPENTHREAD_ENABLE_TMF_PROXY #include #endif @@ -592,6 +594,230 @@ exit: } #endif // OPENTHREAD_ENABLE_TMF_PROXY +otError NcpBase::DecodeOperationalDataset(otOperationalDataset &aDataset, const uint8_t **aTlvs, uint8_t *aTlvsLength) +{ + otError error = OT_ERROR_NONE; + + memset(&aDataset, 0, sizeof(otOperationalDataset)); + + if (aTlvs != NULL) + { + *aTlvs = NULL; + } + + if (aTlvsLength != NULL) + { + *aTlvsLength = 0; + } + + while (!mDecoder.IsAllReadInStruct()) + { + unsigned int propKey; + + SuccessOrExit(error = mDecoder.OpenStruct()); + SuccessOrExit(error = mDecoder.ReadUintPacked(propKey)); + + switch (static_cast(propKey)) + { + case SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP: + SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mActiveTimestamp)); + aDataset.mIsActiveTimestampSet = true; + break; + + case SPINEL_PROP_DATASET_PENDING_TIMESTAMP: + SuccessOrExit(error = mDecoder.ReadUint64(aDataset.mPendingTimestamp)); + aDataset.mIsPendingTimestampSet = true; + break; + + case SPINEL_PROP_NET_MASTER_KEY: + { + const uint8_t *key; + uint16_t len; + + SuccessOrExit(error = mDecoder.ReadData(key, len)); + VerifyOrExit(len == OT_MASTER_KEY_SIZE, error = OT_ERROR_INVALID_ARGS); + memcpy(aDataset.mMasterKey.m8, key, len); + aDataset.mIsMasterKeySet = true; + break; + } + + case SPINEL_PROP_NET_NETWORK_NAME: + { + const char *name; + size_t len; + + SuccessOrExit(error = mDecoder.ReadUtf8(name)); + len = strlen(name); + VerifyOrExit(len <= OT_NETWORK_NAME_MAX_SIZE, error = OT_ERROR_INVALID_ARGS); + memcpy(aDataset.mNetworkName.m8, name, len + 1); + aDataset.mIsNetworkNameSet = true; + break; + } + + case SPINEL_PROP_NET_XPANID: + { + const uint8_t *xpanid; + uint16_t len; + + SuccessOrExit(error = mDecoder.ReadData(xpanid, len)); + VerifyOrExit(len == OT_EXT_PAN_ID_SIZE, error = OT_ERROR_INVALID_ARGS); + memcpy(aDataset.mExtendedPanId.m8, xpanid, len); + aDataset.mIsExtendedPanIdSet = true; + break; + } + + case SPINEL_PROP_IPV6_ML_PREFIX: + { + const otIp6Address *addr; + uint8_t prefixLen; + + SuccessOrExit(error = mDecoder.ReadIp6Address(addr)); + SuccessOrExit(error = mDecoder.ReadUint8(prefixLen)); + VerifyOrExit(prefixLen == 64, error = OT_ERROR_INVALID_ARGS); + memcpy(aDataset.mMeshLocalPrefix.m8, addr, OT_MESH_LOCAL_PREFIX_SIZE); + aDataset.mIsMeshLocalPrefixSet = true; + break; + } + + case SPINEL_PROP_DATASET_DELAY_TIMER: + SuccessOrExit(error = mDecoder.ReadUint32(aDataset.mDelay)); + aDataset.mIsDelaySet = true; + break; + + case SPINEL_PROP_MAC_15_4_PANID: + SuccessOrExit(error = mDecoder.ReadUint16(aDataset.mPanId)); + aDataset.mIsPanIdSet = true; + break; + + case SPINEL_PROP_PHY_CHAN: + { + uint8_t channel; + + SuccessOrExit(error = mDecoder.ReadUint8(channel)); + aDataset.mChannel = channel; + aDataset.mIsChannelSet = true; + break; + } + + case SPINEL_PROP_NET_PSKC: + { + const uint8_t *psk; + uint16_t len; + + SuccessOrExit(error = mDecoder.ReadData(psk, len)); + VerifyOrExit(len == OT_PSKC_MAX_SIZE, error = OT_ERROR_INVALID_ARGS); + memcpy(aDataset.mPSKc.m8, psk, OT_PSKC_MAX_SIZE); + aDataset.mIsPSKcSet = true; + break; + } + + case SPINEL_PROP_DATASET_SECURITY_POLICY: + SuccessOrExit(error = mDecoder.ReadUint16(aDataset.mSecurityPolicy.mRotationTime)); + SuccessOrExit(error = mDecoder.ReadUint8(aDataset.mSecurityPolicy.mFlags)); + aDataset.mIsSecurityPolicySet = true; + break; + + case SPINEL_PROP_PHY_CHAN_SUPPORTED: + { + uint8_t channel; + + aDataset.mChannelMaskPage0 = 0; + + while (!mDecoder.IsAllReadInStruct()) + { + SuccessOrExit(error = mDecoder.ReadUint8(channel)); + VerifyOrExit(channel <= 31, error = OT_ERROR_INVALID_ARGS); + aDataset.mChannelMaskPage0 |= (1U << channel); + } + + aDataset.mIsChannelMaskPage0Set = true; + } + + case SPINEL_PROP_DATASET_RAW_TLVS: + { + const uint8_t *tlvs; + uint16_t len; + + SuccessOrExit(error = mDecoder.ReadData(tlvs, len)); + VerifyOrExit(len <= 255, error = OT_ERROR_INVALID_ARGS); + + if (aTlvs != NULL) + { + *aTlvs = tlvs; + } + + if (aTlvsLength != NULL) + { + *aTlvsLength = static_cast(len); + } + + break; + } + + default: + break; + } + + SuccessOrExit(error = mDecoder.CloseStruct()); + } + +exit: + return error; +} + +otError NcpBase::SetPropertyHandler_THREAD_ACTIVE_DATASET(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; + + SuccessOrExit(error = DecodeOperationalDataset(dataset, NULL, NULL)); + error = otDatasetSetActive(mInstance, &dataset); + +exit: + return error; +} + +otError NcpBase::SetPropertyHandler_THREAD_PENDING_DATASET(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; + + SuccessOrExit(error = DecodeOperationalDataset(dataset, NULL, NULL)); + error = otDatasetSetPending(mInstance, &dataset); + +exit: + return error; +} + +otError NcpBase::SetPropertyHandler_THREAD_MGMT_ACTIVE_DATASET(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; + const uint8_t *extraTlvs; + uint8_t extraTlvsLength; + + SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength)); + error = otDatasetSendMgmtActiveSet(mInstance, &dataset, extraTlvs, extraTlvsLength); + +exit: + return error; + +} + +otError NcpBase::SetPropertyHandler_THREAD_MGMT_PENDING_DATASET(void) +{ + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; + const uint8_t *extraTlvs; + uint8_t extraTlvsLength; + + SuccessOrExit(error = DecodeOperationalDataset(dataset, &extraTlvs, &extraTlvsLength)); + error = otDatasetSendMgmtPendingSet(mInstance, &dataset, extraTlvs, extraTlvsLength); + +exit: + return error; +} + } // namespace Ncp } // namespace ot diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 5823218da..2740dee5f 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -217,7 +217,7 @@ otError NcpBase::GetPropertyHandler_PHY_FREQ(void) otError NcpBase::GetPropertyHandler_PHY_CHAN_SUPPORTED(void) { - return GetPropertyHandler_ChannelMaskHelper(mSupportedChannelMask); + return EncodeChannelMask(mSupportedChannelMask); } otError NcpBase::GetPropertyHandler_PHY_RSSI(void) @@ -844,6 +844,139 @@ otError NcpBase::SetPropertyHandler_THREAD_DISCOVERY_SCAN_PANID(void) return mDecoder.ReadUint16(mDiscoveryScanPanId); } +otError NcpBase::EncodeOperationalDataset(const otOperationalDataset &aDataset) +{ + otError error = OT_ERROR_NONE; + + if (aDataset.mIsActiveTimestampSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP)); + SuccessOrExit(mEncoder.WriteUint64(aDataset.mActiveTimestamp)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsPendingTimestampSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_PENDING_TIMESTAMP)); + SuccessOrExit(mEncoder.WriteUint64(aDataset.mPendingTimestamp)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsMasterKeySet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_MASTER_KEY)); + SuccessOrExit(mEncoder.WriteData(aDataset.mMasterKey.m8, OT_MASTER_KEY_SIZE)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsNetworkNameSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_NETWORK_NAME)); + SuccessOrExit(mEncoder.WriteUtf8(aDataset.mNetworkName.m8)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsExtendedPanIdSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_XPANID)); + SuccessOrExit(mEncoder.WriteData(aDataset.mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsMeshLocalPrefixSet) + { + otIp6Address addr; + + memcpy(addr.mFields.m8, aDataset.mMeshLocalPrefix.m8, 8); + memset(addr.mFields.m8 + 8, 0, 8); // Zero out the last 8 bytes. + + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_IPV6_ML_PREFIX)); + SuccessOrExit(error = mEncoder.WriteIp6Address(addr)); // Mesh local prefix + SuccessOrExit(error = mEncoder.WriteUint8(64)); // Prefix length (in bits) + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsDelaySet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_DELAY_TIMER)); + SuccessOrExit(mEncoder.WriteUint32(aDataset.mDelay)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsPanIdSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_MAC_15_4_PANID)); + SuccessOrExit(mEncoder.WriteUint16(aDataset.mPanId)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsChannelSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN)); + + // The channel is stored in Dataset as `uint16_t` (to accommodate + // larger number of channels in sub-GHz band), however the current + // definition of `SPINEL_PROP_PHY_CHAN` property limits the channel + // to a `uint8_t`. + + SuccessOrExit(mEncoder.WriteUint8(static_cast(aDataset.mChannel))); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsPSKcSet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_NET_PSKC)); + SuccessOrExit(mEncoder.WriteData(aDataset.mPSKc.m8, sizeof(spinel_net_pskc_t))); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsSecurityPolicySet) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_DATASET_SECURITY_POLICY)); + SuccessOrExit(mEncoder.WriteUint16(aDataset.mSecurityPolicy.mRotationTime)); + SuccessOrExit(mEncoder.WriteUint8(aDataset.mSecurityPolicy.mFlags)); + SuccessOrExit(mEncoder.CloseStruct()); + } + + if (aDataset.mIsChannelMaskPage0Set) + { + SuccessOrExit(mEncoder.OpenStruct()); + SuccessOrExit(mEncoder.WriteUintPacked(SPINEL_PROP_PHY_CHAN_SUPPORTED)); + SuccessOrExit(EncodeChannelMask(aDataset.mChannelMaskPage0)); + SuccessOrExit(mEncoder.CloseStruct()); + } + +exit: + return error; +} + +otError NcpBase::GetPropertyHandler_THREAD_ACTIVE_DATASET(void) +{ + otOperationalDataset dataset; + + IgnoreReturnValue(otDatasetGetActive(mInstance, &dataset)); + return EncodeOperationalDataset(dataset); +} + +otError NcpBase::GetPropertyHandler_THREAD_PENDING_DATASET(void) +{ + otOperationalDataset dataset; + + IgnoreReturnValue(otDatasetGetPending(mInstance, &dataset)); + return EncodeOperationalDataset(dataset); +} + otError NcpBase::GetPropertyHandler_IPV6_ML_PREFIX(void) { otError error = OT_ERROR_NONE; @@ -2254,7 +2387,7 @@ otError NcpBase::GetPropertyHandler_NEST_LEGACY_LAST_NODE_JOINED(void) } #endif // OPENTHREAD_ENABLE_LEGACY -otError NcpBase::GetPropertyHandler_ChannelMaskHelper(uint32_t aChannelMask) +otError NcpBase::EncodeChannelMask(uint32_t aChannelMask) { otError error = OT_ERROR_NONE; @@ -2273,7 +2406,7 @@ exit: otError NcpBase::GetPropertyHandler_MAC_SCAN_MASK(void) { - return GetPropertyHandler_ChannelMaskHelper(mChannelMask); + return EncodeChannelMask(mChannelMask); } otError NcpBase::SetPropertyHandler_MAC_SCAN_MASK(void) diff --git a/src/ncp/spinel.c b/src/ncp/spinel.c index 82fd161e5..4a2b5a6e6 100644 --- a/src/ncp/spinel.c +++ b/src/ncp/spinel.c @@ -1473,6 +1473,42 @@ spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) ret = "PROP_THREAD_ROUTER_TABLE"; break; + case SPINEL_PROP_THREAD_ACTIVE_DATASET: + ret = "PROP_THREAD_ACTIVE_DATASET"; + break; + + case SPINEL_PROP_THREAD_PENDING_DATASET: + ret = "PROP_THREAD_PENDING_DATASET"; + break; + + case SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET: + ret = "PROP_THREAD_MGMT_ACTIVE_DATASET"; + break; + + case SPINEL_PROP_THREAD_MGMT_PENDING_DATASET: + ret = "PROP_THREAD_MGMT_PENDING_DATASET"; + break; + + case SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP: + ret = "PROP_DATASET_ACTIVE_TIMESTAMP"; + break; + + case SPINEL_PROP_DATASET_PENDING_TIMESTAMP: + ret = "PROP_DATASET_PENDING_TIMESTAMP"; + break; + + case SPINEL_PROP_DATASET_DELAY_TIMER: + ret = "PROP_DATASET_DELAY_TIMER"; + break; + + case SPINEL_PROP_DATASET_SECURITY_POLICY: + ret = "PROP_DATASET_SECURITY_POLICY"; + break; + + case SPINEL_PROP_DATASET_RAW_TLVS: + ret = "PROP_DATASET_RAW_TLVS"; + break; + case SPINEL_PROP_IPV6_LL_ADDR: ret = "PROP_IPV6_LL_ADDR"; break; diff --git a/src/ncp/spinel.h b/src/ncp/spinel.h index 789ebce80..2d3bbffad 100644 --- a/src/ncp/spinel.h +++ b/src/ncp/spinel.h @@ -1011,7 +1011,7 @@ typedef enum SPINEL_PROP_THREAD_STEERING_DATA = SPINEL_PROP_THREAD_EXT__BEGIN + 22, /// Thread Router Table. - /** Format: `A(t(ESCCCCCCb)`. + /** Format: `A(t(ESCCCCCCb)` - Read only * * Data per item is: * @@ -1028,6 +1028,154 @@ typedef enum */ SPINEL_PROP_THREAD_ROUTER_TABLE = SPINEL_PROP_THREAD_EXT__BEGIN + 23, + /// Thread Active Operational Dataset + /** Format: `A(t(iD))` - Read-Write + * + * This property provides access to current Thread Active Operational Dataset. A Thread device maintains the + * Operational Dataset that it has stored locally and the one currently in use by the partition to which it is + * attached. This property corresponds to the locally stored Dataset on the device. + * + * Operational Dataset consists of a set of supported properties (e.g., channel, master key, network name, PAN id, + * etc). Note that not all supported properties may be present (have a value) in a Dataset. + * + * The Dataset value is encoded as an array of structs containing pairs of property key (as `i`) followed by the + * property value (as `D`). The property value must follow the format associated with the corresponding property. + * + * On write, any unknown/unsupported property keys must be ignored. + * + * The following properties can be included in a Dataset list: + * + * SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP + * SPINEL_PROP_PHY_CHAN + * SPINEL_PROP_PHY_CHAN_SUPPORTED (Channel Mask Page 0) + * SPINEL_PROP_NET_MASTER_KEY + * SPINEL_PROP_NET_NETWORK_NAME + * SPINEL_PROP_NET_XPANID + * SPINEL_PROP_MAC_15_4_PANID + * SPINEL_PROP_IPV6_ML_PREFIX + * SPINEL_PROP_NET_PSKC + * SPINEL_PROP_DATASET_SECURITY_POLICY + * + */ + SPINEL_PROP_THREAD_ACTIVE_DATASET = SPINEL_PROP_THREAD_EXT__BEGIN + 24, + + /// Thread Pending Operational Dataset + /** Format: `A(t(iD))` - Read-Write + * + * This property provide access to current locally stored Pending Operational Dataset. + * + * The formatting of this property follows the same rules as in SPINEL_PROP_THREAD_ACTIVE_DATASET. + * + * In addition supported properties in SPINEL_PROP_THREAD_ACTIVE_DATASET, the following properties can also + * be included in the Pending Dataset: + * + * SPINEL_PROP_DATASET_PENDING_TIMESTAMP + * SPINEL_PROP_DATASET_DELAY_TIMER + * + */ + SPINEL_PROP_THREAD_PENDING_DATASET = SPINEL_PROP_THREAD_EXT__BEGIN + 25, + + /// Thread Active Operational Dataset (MGMT send) + /** Format: `A(t(iD))` - Write only + * + * The formatting of this property follows the same rules as in SPINEL_PROP_THREAD_ACTIVE_DATASET. + * + * This is write-only property. When written, it triggers a MGMT_ACTIVE_SET meshcop command to be sent to leader + * with the given Dataset. The spinel frame response should be a `LAST_STATUS` with the status of the transmission + * of MGMT_ACTIVE_SET command. + * + * In addition to supported properties in SPINEL_PROP_THREAD_ACTIVE_DATASET, the following property can be + * included in the Dataset (to allow for custom raw TLVs): + * + * SPINEL_PROP_DATASET_RAW_TLVS + * + */ + SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET + = SPINEL_PROP_THREAD_EXT__BEGIN + 26, + + /// Thread Pending Operational Dataset (MGMT send) + /** Format: `A(t(iD))` - Write only + * + * This property is similar to SPINEL_PROP_THREAD_PENDING_DATASET and follows the same format and rules. + * + * In addition to supported properties in SPINEL_PROP_THREAD_PENDING_DATASET, the following property can be + * included the Dataset (to allow for custom raw TLVs to be provided). + * + * SPINEL_PROP_DATASET_RAW_TLVS + * + */ + SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + = SPINEL_PROP_THREAD_EXT__BEGIN + 27, + + /// Operational Dataset Active Timestamp + /** Format: `X` - No direct read or write + * + * It can only be included in one of the Dataset related properties below: + * + * SPINEL_PROP_THREAD_ACTIVE_DATASET + * SPINEL_PROP_THREAD_PENDING_DATASET + * SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET + * SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + * + */ + SPINEL_PROP_DATASET_ACTIVE_TIMESTAMP + = SPINEL_PROP_THREAD_EXT__BEGIN + 28, + + /// Operational Dataset Pending Timestamp + /** Format: `X` - No direct read or write + * + * It can only be included in one of the Pending Dataset properties: + * + * SPINEL_PROP_THREAD_PENDING_DATASET + * SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + * + */ + SPINEL_PROP_DATASET_PENDING_TIMESTAMP + = SPINEL_PROP_THREAD_EXT__BEGIN + 29, + + /// Operational Dataset Delay Timer + /** Format: `L` - No direct read or write + * + * Delay timer (in ms) specifies the time renaming until Thread devices overwrite the value in the Active + * Operational Dataset with the corresponding values in the Pending Operational Dataset. + * + * It can only be included in one of the Pending Dataset properties: + * + * SPINEL_PROP_THREAD_PENDING_DATASET + * SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + * + */ + SPINEL_PROP_DATASET_DELAY_TIMER = SPINEL_PROP_THREAD_EXT__BEGIN + 30, + + /// Operational Dataset Security Policy + /** Format: `SC` - No direct read or write + * + * It can only be included in one of the Dataset related properties below: + * + * SPINEL_PROP_THREAD_ACTIVE_DATASET + * SPINEL_PROP_THREAD_PENDING_DATASET + * SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET + * SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + * + * Content is + * `S` : Key Rotation Time (in units of hour) + * `C` : Security Policy Flags (as specified in Thread 1.1 Section 8.10.1.15) + * + */ + SPINEL_PROP_DATASET_SECURITY_POLICY = SPINEL_PROP_THREAD_EXT__BEGIN + 31, + + /// Operational Dataset Additional Raw TLVs + /** Format: `D` - No direct read or write + * + * This property defines extra raw TLVs that can be added to an Operational DataSet. + * + * It can only be included in one of the following Dataset properties: + * + * SPINEL_PROP_THREAD_MGMT_ACTIVE_DATASET + * SPINEL_PROP_THREAD_MGMT_PENDING_DATASET + * + */ + SPINEL_PROP_DATASET_RAW_TLVS = SPINEL_PROP_THREAD_EXT__BEGIN + 32, SPINEL_PROP_THREAD_EXT__END = 0x1600, SPINEL_PROP_IPV6__BEGIN = 0x60,