| /* |
| * |
| * Copyright (c) 2020 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @file |
| * This file contains implementation of Device class. The objects of this |
| * class will be used by Controller applications to interact with CHIP |
| * devices. The class provides mechanism to construct, send and receive |
| * messages to and from the corresponding CHIP devices. |
| */ |
| |
| #include <controller/CHIPDevice.h> |
| |
| #include <controller/data_model/gen/CHIPClusters.h> |
| |
| #if CONFIG_DEVICE_LAYER |
| #include <platform/CHIPDeviceLayer.h> |
| #endif |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| #include <lwip/tcp.h> |
| #include <lwip/tcpip.h> |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #include <app/CommandSender.h> |
| #include <app/util/DataModelHandler.h> |
| #include <core/CHIPCore.h> |
| #include <core/CHIPEncoding.h> |
| #include <core/CHIPSafeCasts.h> |
| #include <protocols/Protocols.h> |
| #include <protocols/service_provisioning/ServiceProvisioning.h> |
| #include <support/Base64.h> |
| #include <support/CHIPMem.h> |
| #include <support/CodeUtils.h> |
| #include <support/ErrorStr.h> |
| #include <support/PersistentStorageMacros.h> |
| #include <support/SafeInt.h> |
| #include <support/TypeTraits.h> |
| #include <support/logging/CHIPLogging.h> |
| #include <system/TLVPacketBufferBackingStore.h> |
| #include <transport/MessageCounter.h> |
| #include <transport/PeerMessageCounter.h> |
| |
| using namespace chip::Inet; |
| using namespace chip::System; |
| using namespace chip::Callback; |
| |
| namespace chip { |
| namespace Controller { |
| CHIP_ERROR Device::SendMessage(Protocols::Id protocolId, uint8_t msgType, Messaging::SendFlags sendFlags, |
| System::PacketBufferHandle && buffer) |
| { |
| System::PacketBufferHandle resend; |
| bool loadedSecureSession = false; |
| |
| VerifyOrReturnError(!buffer.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| Messaging::ExchangeContext * exchange = mExchangeMgr->NewContext(mSecureSession, nullptr); |
| VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_NO_MEMORY); |
| |
| if (!loadedSecureSession) |
| { |
| // Secure connection already existed |
| // Hold on to the buffer, in case session resumption and resend is needed |
| // Cloning data, instead of increasing the ref count, as the original |
| // buffer might get modified by lower layers before the send fails. So, |
| // that buffer cannot be used for resends. |
| resend = buffer.CloneData(); |
| } |
| |
| // TODO(#5675): This code is temporary, and must be updated to use the IM API. Currently, we use a temporary Protocol |
| // TempZCL to carry over legacy ZCL messages. We need to set flag kFromInitiator to allow receiver to deliver message to |
| // corresponding unsolicited message handler. |
| sendFlags.Set(Messaging::SendMessageFlags::kFromInitiator); |
| exchange->SetDelegate(this); |
| |
| CHIP_ERROR err = exchange->SendMessage(protocolId, msgType, std::move(buffer), sendFlags); |
| |
| buffer = nullptr; |
| ChipLogDetail(Controller, "SendMessage returned %s", ErrorStr(err)); |
| |
| // The send could fail due to network timeouts (e.g. broken pipe) |
| // Try session resumption if needed |
| if (err != CHIP_NO_ERROR && !resend.IsNull() && mState == ConnectionState::SecureConnected) |
| { |
| mState = ConnectionState::NotConnected; |
| |
| ReturnErrorOnFailure(LoadSecureSessionParameters(ResetTransport::kYes)); |
| |
| err = exchange->SendMessage(protocolId, msgType, std::move(resend), sendFlags); |
| ChipLogDetail(Controller, "Re-SendMessage returned %s", ErrorStr(err)); |
| } |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| exchange->Close(); |
| } |
| |
| return err; |
| } |
| |
| CHIP_ERROR Device::LoadSecureSessionParametersIfNeeded(bool & didLoad) |
| { |
| didLoad = false; |
| |
| // If there is no secure connection to the device, try establishing it |
| if (mState != ConnectionState::SecureConnected) |
| { |
| ReturnErrorOnFailure(LoadSecureSessionParameters(ResetTransport::kNo)); |
| didLoad = true; |
| } |
| else |
| { |
| Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession); |
| |
| // Check if the connection state has the correct transport information |
| if (connectionState == nullptr || connectionState->GetPeerAddress().GetTransportType() == Transport::Type::kUndefined) |
| { |
| mState = ConnectionState::NotConnected; |
| ReturnErrorOnFailure(LoadSecureSessionParameters(ResetTransport::kNo)); |
| didLoad = true; |
| } |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::SendCommands(app::CommandSender * commandObj) |
| { |
| bool loadedSecureSession = false; |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| VerifyOrReturnError(commandObj != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
| return commandObj->SendCommandRequest(mDeviceId, mFabricIndex, &mSecureSession); |
| } |
| |
| CHIP_ERROR Device::Serialize(SerializedDevice & output) |
| { |
| SerializableDevice serializable; |
| |
| static_assert(BASE64_ENCODED_LEN(sizeof(serializable)) <= sizeof(output.inner), |
| "Size of serializable should be <= size of output"); |
| |
| CHIP_ZERO_AT(serializable); |
| CHIP_ZERO_AT(output); |
| |
| serializable.mOpsCreds = mPairing; |
| serializable.mDeviceId = Encoding::LittleEndian::HostSwap64(mDeviceId); |
| serializable.mDevicePort = Encoding::LittleEndian::HostSwap16(mDeviceAddress.GetPort()); |
| serializable.mFabricIndex = Encoding::LittleEndian::HostSwap16(mFabricIndex); |
| |
| Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession); |
| |
| // The connection state could be null if the device is moving from PASE connection to CASE connection. |
| // The device parameters (e.g. mDeviceOperationalCertProvisioned) are updated during this transition. |
| // The state during this transistion is being persisted so that the next access of the device will |
| // trigger the CASE based secure session. |
| if (connectionState != nullptr) |
| { |
| const uint32_t localMessageCounter = connectionState->GetSessionMessageCounter().GetLocalMessageCounter().Value(); |
| const uint32_t peerMessageCounter = connectionState->GetSessionMessageCounter().GetPeerMessageCounter().GetCounter(); |
| |
| serializable.mLocalMessageCounter = Encoding::LittleEndian::HostSwap32(localMessageCounter); |
| serializable.mPeerMessageCounter = Encoding::LittleEndian::HostSwap32(peerMessageCounter); |
| } |
| else |
| { |
| serializable.mLocalMessageCounter = 0; |
| serializable.mPeerMessageCounter = 0; |
| } |
| |
| serializable.mDeviceOperationalCertProvisioned = (mDeviceOperationalCertProvisioned) ? 1 : 0; |
| |
| serializable.mDeviceTransport = to_underlying(mDeviceAddress.GetTransportType()); |
| |
| ReturnErrorOnFailure(Inet::GetInterfaceName(mDeviceAddress.GetInterface(), Uint8::to_char(serializable.mInterfaceName), |
| sizeof(serializable.mInterfaceName))); |
| static_assert(sizeof(serializable.mDeviceAddr) <= INET6_ADDRSTRLEN, "Size of device address must fit within INET6_ADDRSTRLEN"); |
| mDeviceAddress.GetIPAddress().ToString(Uint8::to_char(serializable.mDeviceAddr), sizeof(serializable.mDeviceAddr)); |
| |
| const uint16_t serializedLen = chip::Base64Encode(Uint8::to_const_uchar(reinterpret_cast<uint8_t *>(&serializable)), |
| static_cast<uint16_t>(sizeof(serializable)), Uint8::to_char(output.inner)); |
| VerifyOrReturnError(serializedLen > 0, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(serializedLen < sizeof(output.inner), CHIP_ERROR_INVALID_ARGUMENT); |
| output.inner[serializedLen] = '\0'; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::Deserialize(const SerializedDevice & input) |
| { |
| SerializableDevice serializable; |
| constexpr size_t maxlen = BASE64_ENCODED_LEN(sizeof(serializable)); |
| const size_t len = strnlen(Uint8::to_const_char(&input.inner[0]), maxlen); |
| |
| VerifyOrReturnError(len < sizeof(SerializedDevice), CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(CanCastTo<uint16_t>(len), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| CHIP_ZERO_AT(serializable); |
| const uint16_t deserializedLen = Base64Decode(Uint8::to_const_char(input.inner), static_cast<uint16_t>(len), |
| Uint8::to_uchar(reinterpret_cast<uint8_t *>(&serializable))); |
| |
| VerifyOrReturnError(deserializedLen > 0, CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrReturnError(deserializedLen <= sizeof(serializable), CHIP_ERROR_INVALID_ARGUMENT); |
| |
| // The second parameter to FromString takes the strlen value. We are subtracting 1 |
| // from the sizeof(serializable.mDeviceAddr) to account for null termination, since |
| // strlen doesn't include null character in the size. |
| Inet::IPAddress ipAddress = {}; |
| VerifyOrReturnError( |
| IPAddress::FromString(Uint8::to_const_char(serializable.mDeviceAddr), sizeof(serializable.mDeviceAddr) - 1, ipAddress), |
| CHIP_ERROR_INVALID_ADDRESS); |
| |
| mPairing = serializable.mOpsCreds; |
| mDeviceId = Encoding::LittleEndian::HostSwap64(serializable.mDeviceId); |
| const uint16_t port = Encoding::LittleEndian::HostSwap16(serializable.mDevicePort); |
| const uint16_t index = Encoding::LittleEndian::HostSwap16(serializable.mFabricIndex); |
| mLocalMessageCounter = Encoding::LittleEndian::HostSwap32(serializable.mLocalMessageCounter); |
| mPeerMessageCounter = Encoding::LittleEndian::HostSwap32(serializable.mPeerMessageCounter); |
| |
| VerifyOrReturnError(CanCastTo<FabricIndex>(index), CHIP_ERROR_INVALID_ARGUMENT); |
| mFabricIndex = static_cast<FabricIndex>(index); |
| |
| // TODO - Remove the hack that's incrementing message counter while deserializing device |
| // This hack was added as a quick workaround for TE3 testing. The commissioning code |
| // is closing the exchange after the device has already been serialized and persisted to the storage. |
| // While closing the exchange, the outstanding ack gets sent to the device, thus incrementing |
| // the local message counter. As the device information was stored prior to sending the ack, it now has |
| // the old counter value (which is 1 less than the updated counter). |
| mLocalMessageCounter++; |
| |
| mDeviceOperationalCertProvisioned = (serializable.mDeviceOperationalCertProvisioned != 0); |
| |
| // The InterfaceNameToId() API requires initialization of mInterface, and lock/unlock of |
| // LwIP stack. |
| Inet::InterfaceId interfaceId = INET_NULL_INTERFACEID; |
| if (serializable.mInterfaceName[0] != '\0') |
| { |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| LOCK_TCPIP_CORE(); |
| #endif |
| CHIP_ERROR inetErr = Inet::InterfaceNameToId(Uint8::to_const_char(serializable.mInterfaceName), interfaceId); |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| UNLOCK_TCPIP_CORE(); |
| #endif |
| ReturnErrorOnFailure(inetErr); |
| } |
| |
| static_assert(std::is_same<std::underlying_type<decltype(mDeviceAddress.GetTransportType())>::type, uint8_t>::value, |
| "The underlying type of Transport::Type is not uint8_t."); |
| switch (static_cast<Transport::Type>(serializable.mDeviceTransport)) |
| { |
| case Transport::Type::kUdp: |
| mDeviceAddress = Transport::PeerAddress::UDP(ipAddress, port, interfaceId); |
| break; |
| case Transport::Type::kBle: |
| mDeviceAddress = Transport::PeerAddress::BLE(); |
| break; |
| case Transport::Type::kTcp: |
| case Transport::Type::kUndefined: |
| default: |
| return CHIP_ERROR_INTERNAL; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::Persist() |
| { |
| CHIP_ERROR error = CHIP_NO_ERROR; |
| if (mStorageDelegate != nullptr) |
| { |
| SerializedDevice serialized; |
| ReturnErrorOnFailure(Serialize(serialized)); |
| |
| // TODO: no need to base-64 the serialized values AGAIN |
| PERSISTENT_KEY_OP(GetDeviceId(), kPairedDeviceKeyPrefix, key, |
| error = mStorageDelegate->SyncSetKeyValue(key, serialized.inner, sizeof(serialized.inner))); |
| if (error != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to persist device %" CHIP_ERROR_FORMAT, error.Format()); |
| } |
| } |
| return error; |
| } |
| |
| void Device::OnNewConnection(SecureSessionHandle session) |
| { |
| mState = ConnectionState::SecureConnected; |
| mSecureSession = session; |
| |
| // Reset the message counters here because this is the first time we get a handle to the secure session. |
| // Since CHIPDevices can be serialized/deserialized in the middle of what is conceptually a single PASE session |
| // we need to restore the session counters along with the session information. |
| Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession); |
| VerifyOrReturn(connectionState != nullptr); |
| MessageCounter & localCounter = connectionState->GetSessionMessageCounter().GetLocalMessageCounter(); |
| if (localCounter.SetCounter(mLocalMessageCounter) != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Unable to restore local counter to %" PRIu32, mLocalMessageCounter); |
| } |
| Transport::PeerMessageCounter & peerCounter = connectionState->GetSessionMessageCounter().GetPeerMessageCounter(); |
| peerCounter.SetCounter(mPeerMessageCounter); |
| } |
| |
| void Device::OnConnectionExpired(SecureSessionHandle session) |
| { |
| VerifyOrReturn(session == mSecureSession, |
| ChipLogDetail(Controller, "Connection expired, but it doesn't match the current session")); |
| mState = ConnectionState::NotConnected; |
| mSecureSession = SecureSessionHandle{}; |
| } |
| |
| CHIP_ERROR Device::OnMessageReceived(Messaging::ExchangeContext * exchange, const PacketHeader & header, |
| const PayloadHeader & payloadHeader, System::PacketBufferHandle && msgBuf) |
| { |
| if (mState == ConnectionState::SecureConnected) |
| { |
| if (mStatusDelegate != nullptr) |
| { |
| mStatusDelegate->OnMessage(std::move(msgBuf)); |
| } |
| else |
| { |
| HandleDataModelMessage(exchange, std::move(msgBuf)); |
| } |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::OnResponseTimeout(Messaging::ExchangeContext * ec) {} |
| |
| void Device::OnOpenPairingWindowSuccessResponse(void * context) |
| { |
| ChipLogProgress(Controller, "Successfully opened pairing window on the device"); |
| } |
| |
| void Device::OnOpenPairingWindowFailureResponse(void * context, uint8_t status) |
| { |
| ChipLogError(Controller, "Failed to open pairing window on the device. Status %d", status); |
| } |
| |
| CHIP_ERROR Device::OpenPairingWindow(uint16_t timeout, PairingWindowOption option, SetupPayload & setupPayload) |
| { |
| constexpr EndpointId kAdministratorCommissioningClusterEndpoint = 0; |
| |
| chip::Controller::AdministratorCommissioningCluster cluster; |
| cluster.Associate(this, kAdministratorCommissioningClusterEndpoint); |
| |
| Callback::Cancelable * successCallback = mOpenPairingSuccessCallback.Cancel(); |
| Callback::Cancelable * failureCallback = mOpenPairingFailureCallback.Cancel(); |
| |
| if (option != PairingWindowOption::kOriginalSetupCode) |
| { |
| bool randomSetupPIN = (option == PairingWindowOption::kTokenWithRandomPIN); |
| PASEVerifier verifier; |
| ByteSpan salt(reinterpret_cast<const uint8_t *>(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt)); |
| ReturnErrorOnFailure( |
| PASESession::GeneratePASEVerifier(verifier, kPBKDFMinimumIterations, salt, randomSetupPIN, setupPayload.setUpPINCode)); |
| |
| uint8_t serializedVerifier[2 * kSpake2p_WS_Length]; |
| VerifyOrReturnError(sizeof(serializedVerifier) == sizeof(verifier), CHIP_ERROR_INTERNAL); |
| |
| memcpy(serializedVerifier, verifier.mW0, kSpake2p_WS_Length); |
| memcpy(&serializedVerifier[kSpake2p_WS_Length], verifier.mL, kSpake2p_WS_Length); |
| |
| ReturnErrorOnFailure(cluster.OpenCommissioningWindow( |
| successCallback, failureCallback, timeout, ByteSpan(serializedVerifier, sizeof(serializedVerifier)), |
| setupPayload.discriminator, kPBKDFMinimumIterations, salt, mPAKEVerifierID++)); |
| } |
| else |
| { |
| ReturnErrorOnFailure(cluster.OpenBasicCommissioningWindow(successCallback, failureCallback, timeout)); |
| } |
| |
| setupPayload.version = 0; |
| setupPayload.rendezvousInformation = RendezvousInformationFlags(RendezvousInformationFlag::kBLE); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::CloseSession() |
| { |
| ReturnErrorCodeIf(mState != ConnectionState::SecureConnected, CHIP_ERROR_INCORRECT_STATE); |
| mSessionManager->ExpirePairing(mSecureSession); |
| mState = ConnectionState::NotConnected; |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR Device::UpdateAddress(const Transport::PeerAddress & addr) |
| { |
| bool didLoad; |
| |
| mDeviceAddress = addr; |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(didLoad)); |
| |
| Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession); |
| if (connectionState == nullptr) |
| { |
| // Nothing needs to be done here. It's not an error to not have a |
| // connectionState. For one thing, we could have gotten an different |
| // UpdateAddress already and that caused connections to be torn down and |
| // whatnot. |
| return CHIP_NO_ERROR; |
| } |
| |
| connectionState->SetPeerAddress(addr); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::Reset() |
| { |
| if (IsActive() && mStorageDelegate != nullptr && mSessionManager != nullptr) |
| { |
| // If a session can be found, persist the device so that we track the newest message counter values |
| Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession); |
| if (connectionState != nullptr) |
| { |
| Persist(); |
| } |
| } |
| |
| SetActive(false); |
| mCASESession.Clear(); |
| |
| mState = ConnectionState::NotConnected; |
| mSessionManager = nullptr; |
| mStatusDelegate = nullptr; |
| mInetLayer = nullptr; |
| #if CONFIG_NETWORK_LAYER_BLE |
| mBleLayer = nullptr; |
| #endif |
| if (mExchangeMgr) |
| { |
| // Ensure that any exchange contexts we have open get closed now, |
| // because we don't want them to call back in to us after this |
| // point. |
| mExchangeMgr->CloseAllContextsForDelegate(this); |
| } |
| mExchangeMgr = nullptr; |
| } |
| |
| CHIP_ERROR Device::LoadSecureSessionParameters(ResetTransport resetNeeded) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| PASESession pairingSession; |
| |
| if (mSessionManager == nullptr || mState == ConnectionState::SecureConnected) |
| { |
| ExitNow(err = CHIP_ERROR_INCORRECT_STATE); |
| } |
| |
| if (mState == ConnectionState::Connecting) |
| { |
| ExitNow(err = CHIP_NO_ERROR); |
| } |
| |
| if (resetNeeded == ResetTransport::kYes) |
| { |
| err = mTransportMgr->ResetTransport( |
| Transport::UdpListenParameters(mInetLayer).SetAddressType(kIPAddressType_IPv6).SetListenPort(mListenPort) |
| #if INET_CONFIG_ENABLE_IPV4 |
| , |
| Transport::UdpListenParameters(mInetLayer).SetAddressType(kIPAddressType_IPv4).SetListenPort(mListenPort) |
| #endif |
| #if CONFIG_NETWORK_LAYER_BLE |
| , |
| Transport::BleListenParameters(mBleLayer) |
| #endif |
| ); |
| SuccessOrExit(err); |
| } |
| |
| if (IsOperationalCertProvisioned()) |
| { |
| err = WarmupCASESession(); |
| SuccessOrExit(err); |
| } |
| else |
| { |
| err = pairingSession.FromSerializable(mPairing); |
| SuccessOrExit(err); |
| |
| err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &pairingSession, |
| SecureSession::SessionRole::kInitiator, mFabricIndex); |
| SuccessOrExit(err); |
| } |
| |
| exit: |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "LoadSecureSessionParameters returning error %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| return err; |
| } |
| |
| bool Device::GetAddress(Inet::IPAddress & addr, uint16_t & port) const |
| { |
| if (mState == ConnectionState::NotConnected) |
| return false; |
| |
| addr = mDeviceAddress.GetIPAddress(); |
| port = mDeviceAddress.GetPort(); |
| return true; |
| } |
| |
| void Device::OperationalCertProvisioned() |
| { |
| VerifyOrReturn(!mDeviceOperationalCertProvisioned, |
| ChipLogDetail(Controller, "Operational certificates already provisioned for this device")); |
| |
| ChipLogDetail(Controller, "Enabling CASE session establishment for the device"); |
| mDeviceOperationalCertProvisioned = true; |
| |
| Persist(); |
| CloseSession(); |
| } |
| |
| CHIP_ERROR Device::WarmupCASESession() |
| { |
| VerifyOrReturnError(mDeviceOperationalCertProvisioned, CHIP_ERROR_INCORRECT_STATE); |
| VerifyOrReturnError(mState == ConnectionState::NotConnected, CHIP_NO_ERROR); |
| |
| Messaging::ExchangeContext * exchange = mExchangeMgr->NewContext(SecureSessionHandle(), &mCASESession); |
| VerifyOrReturnError(exchange != nullptr, CHIP_ERROR_INTERNAL); |
| |
| ReturnErrorOnFailure(mCASESession.MessageDispatch().Init(mSessionManager->GetTransportManager())); |
| mCASESession.MessageDispatch().SetPeerAddress(mDeviceAddress); |
| |
| uint16_t keyID = 0; |
| ReturnErrorOnFailure(mIDAllocator->Allocate(keyID)); |
| |
| mLocalMessageCounter = 0; |
| mPeerMessageCounter = 0; |
| |
| ReturnErrorOnFailure( |
| mCASESession.EstablishSession(mDeviceAddress, mCredentials, mCredentialsIndex, mDeviceId, keyID, exchange, this)); |
| |
| mState = ConnectionState::Connecting; |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::OnSessionEstablishmentError(CHIP_ERROR error) |
| { |
| mState = ConnectionState::NotConnected; |
| mIDAllocator->Free(mCASESession.GetLocalKeyId()); |
| |
| Cancelable ready; |
| mConnectionFailure.DequeueAll(ready); |
| while (ready.mNext != &ready) |
| { |
| Callback::Callback<OnDeviceConnectionFailure> * cb = |
| Callback::Callback<OnDeviceConnectionFailure>::FromCancelable(ready.mNext); |
| |
| cb->Cancel(); |
| cb->mCall(cb->mContext, GetDeviceId(), error); |
| } |
| } |
| |
| void Device::OnSessionEstablished() |
| { |
| mCASESession.PeerConnection().SetPeerNodeId(mDeviceId); |
| |
| CHIP_ERROR err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceAddress), mDeviceId, &mCASESession, |
| SecureSession::SessionRole::kInitiator, mFabricIndex); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed in setting up CASE secure channel: err %s", ErrorStr(err)); |
| OnSessionEstablishmentError(err); |
| return; |
| } |
| |
| Cancelable ready; |
| mConnectionSuccess.DequeueAll(ready); |
| while (ready.mNext != &ready) |
| { |
| Callback::Callback<OnDeviceConnected> * cb = Callback::Callback<OnDeviceConnected>::FromCancelable(ready.mNext); |
| |
| cb->Cancel(); |
| cb->mCall(cb->mContext, this); |
| } |
| } |
| |
| CHIP_ERROR Device::EstablishConnectivity(Callback::Callback<OnDeviceConnected> * onConnection, |
| Callback::Callback<OnDeviceConnectionFailure> * onFailure) |
| { |
| bool loadedSecureSession = false; |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| if (loadedSecureSession) |
| { |
| if (IsOperationalCertProvisioned()) |
| { |
| if (onConnection != nullptr) |
| { |
| mConnectionSuccess.Enqueue(onConnection->Cancel()); |
| } |
| |
| if (onFailure != nullptr) |
| { |
| mConnectionFailure.Enqueue(onFailure->Cancel()); |
| } |
| } |
| else |
| { |
| if (onConnection != nullptr) |
| { |
| onConnection->mCall(onConnection->mContext, this); |
| } |
| } |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| void Device::AddResponseHandler(uint8_t seqNum, Callback::Cancelable * onSuccessCallback, Callback::Cancelable * onFailureCallback, |
| app::TLVDataFilter tlvDataFilter) |
| { |
| mCallbacksMgr.AddResponseCallback(mDeviceId, seqNum, onSuccessCallback, onFailureCallback, tlvDataFilter); |
| } |
| |
| void Device::CancelResponseHandler(uint8_t seqNum) |
| { |
| mCallbacksMgr.CancelResponseCallback(mDeviceId, seqNum); |
| } |
| |
| void Device::AddIMResponseHandler(app::CommandSender * commandObj, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback) |
| { |
| // We are using the pointer to command sender object as the identifier of command transactions. This makes sense as long as |
| // there are only one active command transaction on one command sender object. This is a bit tricky, we try to assume that |
| // chip::NodeId is uint64_t so the pointer can be used as a NodeId for CallbackMgr. |
| static_assert(std::is_same<chip::NodeId, uint64_t>::value, "chip::NodeId is not uint64_t"); |
| chip::NodeId transactionId = reinterpret_cast<chip::NodeId>(commandObj); |
| mCallbacksMgr.AddResponseCallback(transactionId, 0 /* seqNum, always 0 for IM before #6559 */, onSuccessCallback, |
| onFailureCallback); |
| } |
| |
| void Device::CancelIMResponseHandler(app::CommandSender * commandObj) |
| { |
| // We are using the pointer to command sender object as the identifier of command transactions. This makes sense as long as |
| // there are only one active command transaction on one command sender object. This is a bit tricky, we try to assume that |
| // chip::NodeId is uint64_t so the pointer can be used as a NodeId for CallbackMgr. |
| static_assert(std::is_same<chip::NodeId, uint64_t>::value, "chip::NodeId is not uint64_t"); |
| chip::NodeId transactionId = reinterpret_cast<chip::NodeId>(commandObj); |
| mCallbacksMgr.CancelResponseCallback(transactionId, 0 /* seqNum, always 0 for IM before #6559 */); |
| } |
| |
| void Device::AddReportHandler(EndpointId endpoint, ClusterId cluster, AttributeId attribute, |
| Callback::Cancelable * onReportCallback) |
| { |
| mCallbacksMgr.AddReportCallback(mDeviceId, endpoint, cluster, attribute, onReportCallback); |
| } |
| |
| CHIP_ERROR Device::SendReadAttributeRequest(app::AttributePathParams aPath, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback, app::TLVDataFilter aTlvDataFilter) |
| { |
| bool loadedSecureSession = false; |
| uint8_t seqNum = GetNextSequenceNumber(); |
| aPath.mNodeId = GetDeviceId(); |
| |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| if (onSuccessCallback != nullptr || onFailureCallback != nullptr) |
| { |
| AddResponseHandler(seqNum, onSuccessCallback, onFailureCallback, aTlvDataFilter); |
| } |
| // The application context is used to identify different requests from client applicaiton the type of it is intptr_t, here we |
| // use the seqNum. |
| CHIP_ERROR err = chip::app::InteractionModelEngine::GetInstance()->SendReadRequest( |
| GetDeviceId(), 0, &mSecureSession, nullptr /*event path params list*/, 0, &aPath, 1, 0 /* event number */, |
| seqNum /* application context */); |
| if (err != CHIP_NO_ERROR) |
| { |
| CancelResponseHandler(seqNum); |
| } |
| return err; |
| } |
| |
| CHIP_ERROR Device::SendWriteAttributeRequest(app::WriteClientHandle aHandle, Callback::Cancelable * onSuccessCallback, |
| Callback::Cancelable * onFailureCallback) |
| { |
| bool loadedSecureSession = false; |
| uint8_t seqNum = GetNextSequenceNumber(); |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| aHandle->SetAppIdentifier(seqNum); |
| ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession)); |
| |
| if (onSuccessCallback != nullptr || onFailureCallback != nullptr) |
| { |
| AddResponseHandler(seqNum, onSuccessCallback, onFailureCallback); |
| } |
| if ((err = aHandle.SendWriteRequest(GetDeviceId(), 0, &mSecureSession)) != CHIP_NO_ERROR) |
| { |
| CancelResponseHandler(seqNum); |
| } |
| return err; |
| } |
| |
| Device::~Device() |
| { |
| if (mExchangeMgr) |
| { |
| // Ensure that any exchange contexts we have open get closed now, |
| // because we don't want them to call back in to us after this |
| // point. |
| mExchangeMgr->CloseAllContextsForDelegate(this); |
| } |
| } |
| |
| CHIP_ERROR Device::ReduceNOCChainBufferSize(size_t new_size) |
| { |
| ReturnErrorCodeIf(new_size > sizeof(mNOCChainBuffer), CHIP_ERROR_INVALID_ARGUMENT); |
| mNOCChainBufferSize = new_size; |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace Controller |
| } // namespace chip |