diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index c09fcd57f..ae0753f48 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1891,6 +1891,10 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) ret = "MESHCOP_COMMISSIONER_SESSION_ID"; break; + case SPINEL_PROP_MESHCOP_JOINER_DISCERNER: + ret = "MESHCOP_JOINER_DISCERNER"; + break; + case SPINEL_PROP_MESHCOP_COMMISSIONER_ANNOUNCE_BEGIN: ret = "MESHCOP_COMMISSIONER_ANNOUNCE_BEGIN"; break; diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index dcc6e06ac..082279574 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -3112,15 +3112,22 @@ enum SPINEL_PROP_MESHCOP_COMMISSIONER_STATE = SPINEL_PROP_MESHCOP__BEGIN + 2, // Thread Commissioner Joiners - /** Format `A(t(E)UL)` - insert or remove only + /** Format `A(t(t(E|CX)UL))` - get, insert or remove. * * Required capability: SPINEL_CAP_THREAD_COMMISSIONER * - * Data per item is: + * Data per array entry is: * - * `t(E)` | `t()`: Joiner EUI64. Empty struct indicates any Joiner - * `L` : Timeout (in seconds) after which the Joiner is automatically removed - * `U` : PSKd + * `t()` | `t(E)` | `t(CX)` : Joiner info struct (formatting varies). + * + * - `t()` or empty struct indicates any joiner. + * - `t(E)` specifies the Joiner EUI-64. + * - `t(CX) specifies Joiner Discerner, `C` is Discerner length (in bits), and `X` is Discerner value. + * + * The struct is followed by: + * + * `L` : Timeout after which to remove Joiner (when written should be in seconds, when read is in milliseconds) + * `U` : PSKd * * For CMD_PROP_VALUE_REMOVE the timeout and PSKd are optional. * @@ -3143,6 +3150,32 @@ enum */ SPINEL_PROP_MESHCOP_COMMISSIONER_SESSION_ID = SPINEL_PROP_MESHCOP__BEGIN + 5, + /// Thread Joiner Discerner + /** Format `CX` - Read-write + * + * Required capability: SPINEL_CAP_THREAD_JOINER + * + * This property represents a Joiner Discerner. + * + * The Joiner Discerner is used to calculate the Joiner ID used during commissioning/joining process. + * + * By default (when a discerner is not provided or cleared), Joiner ID is derived as first 64 bits of the result + * of computing SHA-256 over factory-assigned IEEE EUI-64. Note that this is the main behavior expected by Thread + * specification. + * + * Format: + * + * 'C' : The Joiner Discerner bit length (number of bits). + * `X` : The Joiner Discerner value (64-bit unsigned) - Only present/applicable when length is non-zero. + * + * When writing to this property, the length can be set to zero to clear any previously set Joiner Discerner value. + * + * When reading this property if there is no currently set Joiner Discerner, zero is returned as the length (with + * no value field). + * + */ + SPINEL_PROP_MESHCOP_JOINER_DISCERNER = SPINEL_PROP_MESHCOP__BEGIN + 6, + SPINEL_PROP_MESHCOP__END = 0x90, SPINEL_PROP_MESHCOP_EXT__BEGIN = 0x1800, diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 74714edf7..7eadb8c45 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -135,10 +135,14 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_FTD #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_STATE), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_JOINERS), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_PROVISIONING_URL), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_SESSION_ID), #endif #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_JOINER_ENABLE + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_JOINER_DISCERNER), +#endif #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_SERVER_SERVICES), @@ -404,6 +408,9 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_COMMISSIONER_PROVISIONING_URL), #endif #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_JOINER_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MESHCOP_JOINER_DISCERNER), +#endif #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SERVER_ALLOW_LOCAL_DATA_CHANGE), #endif diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index 2e6262212..38df0d7ba 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -538,23 +538,72 @@ exit: return error; } +template <> otError NcpBase::HandlePropertyGet(void) +{ + otError error = OT_ERROR_NONE; + uint16_t iter = 0; + otJoinerInfo joinerInfo; + + while (otCommissionerGetNextJoinerInfo(mInstance, &iter, &joinerInfo) == OT_ERROR_NONE) + { + SuccessOrExit(error = mEncoder.OpenStruct()); + + SuccessOrExit(error = mEncoder.OpenStruct()); // Joiner Id (any, EUI64 or a Joiner Discerner) struct + + switch (joinerInfo.mType) + { + case OT_JOINER_INFO_TYPE_ANY: + break; + + case OT_JOINER_INFO_TYPE_EUI64: + SuccessOrExit(error = mEncoder.WriteEui64(joinerInfo.mSharedId.mEui64)); + break; + + case OT_JOINER_INFO_TYPE_DISCERNER: + SuccessOrExit(error = mEncoder.WriteUint8(joinerInfo.mSharedId.mDiscerner.mLength)); + SuccessOrExit(error = mEncoder.WriteUint64(joinerInfo.mSharedId.mDiscerner.mValue)); + break; + } + + SuccessOrExit(error = mEncoder.CloseStruct()); + + SuccessOrExit(error = mEncoder.WriteUint32(joinerInfo.mExpirationTime)); + SuccessOrExit(error = mEncoder.WriteUtf8(joinerInfo.mPskd.m8)); + + SuccessOrExit(error = mEncoder.CloseStruct()); + } + +exit: + return error; +} + template <> otError NcpBase::HandlePropertyInsert(void) { otError error = OT_ERROR_NONE; + otJoinerDiscerner discerner; + bool withDiscerner = false; const otExtAddress *eui64; uint32_t timeout; const char * psk; SuccessOrExit(error = mDecoder.OpenStruct()); - if (!mDecoder.IsAllReadInStruct()) + switch (mDecoder.GetRemainingLengthInStruct()) { - SuccessOrExit(error = mDecoder.ReadEui64(eui64)); - } - else - { - // Empty struct indicates any Joiner (no EUI64 is given so nullptr is used.). + case 0: + // Empty struct indicates any joiner eui64 = nullptr; + break; + + case sizeof(spinel_eui64_t): + SuccessOrExit(error = mDecoder.ReadEui64(eui64)); + break; + + default: + SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength)); + SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue)); + withDiscerner = true; + break; } SuccessOrExit(error = mDecoder.CloseStruct()); @@ -562,7 +611,14 @@ template <> otError NcpBase::HandlePropertyInsert otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; + otJoinerDiscerner discerner; + bool withDiscerner = false; const otExtAddress *eui64; SuccessOrExit(error = mDecoder.OpenStruct()); - if (!mDecoder.IsAllReadInStruct()) + switch (mDecoder.GetRemainingLengthInStruct()) { - SuccessOrExit(error = mDecoder.ReadEui64(eui64)); - } - else - { - // Empty struct indicates any Joiner (no EUI64 is given so nullptr is used.). + case 0: + // Empty struct indicates any joiner eui64 = nullptr; + break; + + case sizeof(spinel_eui64_t): + SuccessOrExit(error = mDecoder.ReadEui64(eui64)); + break; + + default: + SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength)); + SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue)); + withDiscerner = true; + break; } SuccessOrExit(error = mDecoder.CloseStruct()); - error = otCommissionerRemoveJoiner(mInstance, eui64); + if (withDiscerner) + { + error = otCommissionerRemoveJoinerWithDiscerner(mInstance, &discerner); + } + else + { + error = otCommissionerRemoveJoiner(mInstance, eui64); + } exit: return error; diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index 7dda234f2..9d1765bb7 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -1532,7 +1532,47 @@ exit: return error; } -#endif +template <> otError NcpBase::HandlePropertyGet(void) +{ + otError error; + const otJoinerDiscerner *discerner = otJoinerGetDiscerner(mInstance); + + if (discerner == nullptr) + { + SuccessOrExit(error = mEncoder.WriteUint8(0)); + } + else + { + SuccessOrExit(error = mEncoder.WriteUint8(discerner->mLength)); + SuccessOrExit(error = mEncoder.WriteUint64(discerner->mValue)); + } + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error = OT_ERROR_NONE; + otJoinerDiscerner discerner; + + SuccessOrExit(error = mDecoder.ReadUint8(discerner.mLength)); + + if (discerner.mLength == 0) + { + // Clearing any previously set Joiner Discerner + error = otJoinerSetDiscerner(mInstance, nullptr); + ExitNow(); + } + + SuccessOrExit(error = mDecoder.ReadUint64(discerner.mValue)); + error = otJoinerSetDiscerner(mInstance, &discerner); + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_JOINER_ENABLE template <> otError NcpBase::HandlePropertyGet(void) {