3 * Copyright (c) 2020 Project CHIP Authors
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 * This file contains implementation of Device class. The objects of this
22 * class will be used by Controller applications to interact with CHIP
23 * devices. The class provides mechanism to construct, send and receive
24 * messages to and from the corresponding CHIP devices.
27 #include <controller/CHIPDevice.h>
29 #if CONFIG_DEVICE_LAYER
30 #include <platform/CHIPDeviceLayer.h>
33 #if CHIP_SYSTEM_CONFIG_USE_LWIP
35 #include <lwip/tcpip.h>
36 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP
38 #include <app/CommandSender.h>
39 #include <app/server/DataModelHandler.h>
40 #include <core/CHIPCore.h>
41 #include <core/CHIPEncoding.h>
42 #include <core/CHIPSafeCasts.h>
43 #include <support/Base64.h>
44 #include <support/CHIPMem.h>
45 #include <support/CodeUtils.h>
46 #include <support/ErrorStr.h>
47 #include <support/SafeInt.h>
48 #include <support/logging/CHIPLogging.h>
49 #include <system/TLVPacketBufferBackingStore.h>
51 using namespace chip::Inet;
52 using namespace chip::System;
53 using namespace chip::Callback;
56 namespace Controller {
58 CHIP_ERROR Device::SendMessage(System::PacketBufferHandle buffer, PayloadHeader & payloadHeader)
60 System::PacketBufferHandle resend;
61 bool loadedSecureSession = false;
63 VerifyOrReturnError(mSessionManager != nullptr, CHIP_ERROR_INCORRECT_STATE);
64 VerifyOrReturnError(!buffer.IsNull(), CHIP_ERROR_INVALID_ARGUMENT);
66 ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession));
68 if (!loadedSecureSession)
70 // Secure connection already existed
71 // Hold on to the buffer, in case session resumption and resend is needed
72 // Cloning data, instead of increasing the ref count, as the original
73 // buffer might get modified by lower layers before the send fails. So,
74 // that buffer cannot be used for resends.
75 resend = buffer.CloneData();
78 CHIP_ERROR err = mSessionManager->SendMessage(mSecureSession, payloadHeader, std::move(buffer));
81 ChipLogDetail(Controller, "SendMessage returned %d", err);
83 // The send could fail due to network timeouts (e.g. broken pipe)
84 // Try session resumption if needed
85 if (err != CHIP_NO_ERROR && !resend.IsNull() && mState == ConnectionState::SecureConnected)
87 mState = ConnectionState::NotConnected;
89 ReturnErrorOnFailure(LoadSecureSessionParameters(ResetTransport::kYes));
91 err = mSessionManager->SendMessage(mSecureSession, std::move(resend));
92 ChipLogDetail(Controller, "Re-SendMessage returned %d", err);
93 ReturnErrorOnFailure(err);
99 CHIP_ERROR Device::LoadSecureSessionParametersIfNeeded(bool & didLoad)
103 // If there is no secure connection to the device, try establishing it
104 if (mState != ConnectionState::SecureConnected)
106 ReturnErrorOnFailure(LoadSecureSessionParameters(ResetTransport::kNo));
111 Transport::PeerConnectionState * connectionState = nullptr;
112 connectionState = mSessionManager->GetPeerConnectionState(mSecureSession);
114 // Check if the connection state has the correct transport information
115 if (connectionState == nullptr || connectionState->GetPeerAddress().GetTransportType() == Transport::Type::kUndefined ||
116 connectionState->GetTransport() != nullptr)
118 mState = ConnectionState::NotConnected;
119 ReturnErrorOnFailure(LoadSecureSessionParameters(ResetTransport::kNo));
124 return CHIP_NO_ERROR;
127 CHIP_ERROR Device::SendCommands()
129 bool loadedSecureSession = false;
130 ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(loadedSecureSession));
131 VerifyOrReturnError(mCommandSender != nullptr, CHIP_ERROR_INCORRECT_STATE);
132 return mCommandSender->SendCommandRequest(mDeviceId, mAdminId);
135 CHIP_ERROR Device::SendMessage(System::PacketBufferHandle buffer)
137 PayloadHeader unusedHeader;
138 return SendMessage(std::move(buffer), unusedHeader);
141 CHIP_ERROR Device::Serialize(SerializedDevice & output)
143 CHIP_ERROR error = CHIP_NO_ERROR;
144 uint16_t serializedLen = 0;
145 SerializableDevice serializable;
147 static_assert(BASE64_ENCODED_LEN(sizeof(serializable)) <= sizeof(output.inner),
148 "Size of serializable should be <= size of output");
150 CHIP_ZERO_AT(serializable);
152 serializable.mOpsCreds = mPairing;
153 serializable.mDeviceId = Encoding::LittleEndian::HostSwap64(mDeviceId);
154 serializable.mDevicePort = Encoding::LittleEndian::HostSwap16(mDeviceUdpAddress.GetPort());
155 serializable.mAdminId = Encoding::LittleEndian::HostSwap16(mAdminId);
156 SuccessOrExit(error = Inet::GetInterfaceName(mDeviceUdpAddress.GetInterface(), Uint8::to_char(serializable.mInterfaceName),
157 sizeof(serializable.mInterfaceName)));
158 static_assert(sizeof(serializable.mDeviceAddr) <= INET6_ADDRSTRLEN, "Size of device address must fit within INET6_ADDRSTRLEN");
159 mDeviceUdpAddress.GetIPAddress().ToString(Uint8::to_char(serializable.mDeviceAddr), sizeof(serializable.mDeviceAddr));
161 serializedLen = chip::Base64Encode(Uint8::to_const_uchar(reinterpret_cast<uint8_t *>(&serializable)),
162 static_cast<uint16_t>(sizeof(serializable)), Uint8::to_char(output.inner));
163 VerifyOrExit(serializedLen > 0, error = CHIP_ERROR_INVALID_ARGUMENT);
164 VerifyOrExit(serializedLen < sizeof(output.inner), error = CHIP_ERROR_INVALID_ARGUMENT);
165 output.inner[serializedLen] = '\0';
171 CHIP_ERROR Device::Deserialize(const SerializedDevice & input)
173 CHIP_ERROR error = CHIP_NO_ERROR;
174 SerializableDevice serializable;
175 size_t maxlen = BASE64_ENCODED_LEN(sizeof(serializable));
176 size_t len = strnlen(Uint8::to_const_char(&input.inner[0]), maxlen);
177 uint16_t deserializedLen = 0;
179 VerifyOrExit(len < sizeof(SerializedDevice), error = CHIP_ERROR_INVALID_ARGUMENT);
180 VerifyOrExit(CanCastTo<uint16_t>(len), error = CHIP_ERROR_INVALID_ARGUMENT);
182 CHIP_ZERO_AT(serializable);
183 deserializedLen = Base64Decode(Uint8::to_const_char(input.inner), static_cast<uint16_t>(len),
184 Uint8::to_uchar(reinterpret_cast<uint8_t *>(&serializable)));
186 VerifyOrExit(deserializedLen > 0, error = CHIP_ERROR_INVALID_ARGUMENT);
187 VerifyOrExit(deserializedLen <= sizeof(serializable), error = CHIP_ERROR_INVALID_ARGUMENT);
189 Inet::IPAddress ipAddress;
191 Inet::InterfaceId interfaceId;
193 // The second parameter to FromString takes the strlen value. We are subtracting 1
194 // from the sizeof(serializable.mDeviceAddr) to account for null termination, since
195 // strlen doesn't include null character in the size.
197 IPAddress::FromString(Uint8::to_const_char(serializable.mDeviceAddr), sizeof(serializable.mDeviceAddr) - 1, ipAddress),
198 error = CHIP_ERROR_INVALID_ADDRESS);
200 mPairing = serializable.mOpsCreds;
201 mDeviceId = Encoding::LittleEndian::HostSwap64(serializable.mDeviceId);
202 port = Encoding::LittleEndian::HostSwap16(serializable.mDevicePort);
203 mAdminId = Encoding::LittleEndian::HostSwap16(serializable.mAdminId);
205 // The InterfaceNameToId() API requires initialization of mInterface, and lock/unlock of
207 interfaceId = INET_NULL_INTERFACEID;
208 if (serializable.mInterfaceName[0] != '\0')
210 #if CHIP_SYSTEM_CONFIG_USE_LWIP
213 INET_ERROR inetErr = Inet::InterfaceNameToId(Uint8::to_const_char(serializable.mInterfaceName), interfaceId);
214 #if CHIP_SYSTEM_CONFIG_USE_LWIP
217 VerifyOrExit(CHIP_NO_ERROR == inetErr, error = CHIP_ERROR_INTERNAL);
220 mDeviceUdpAddress = Transport::PeerAddress::UDP(ipAddress, port, interfaceId);
226 void Device::OnNewConnection(SecureSessionHandle session, SecureSessionMgr * mgr)
228 mState = ConnectionState::SecureConnected;
229 mSecureSession = session;
232 void Device::OnConnectionExpired(SecureSessionHandle session, SecureSessionMgr * mgr)
234 mState = ConnectionState::NotConnected;
235 mSecureSession = SecureSessionHandle{};
238 void Device::OnMessageReceived(const PacketHeader & header, const PayloadHeader & payloadHeader, SecureSessionHandle session,
239 System::PacketBufferHandle msgBuf, SecureSessionMgr * mgr)
241 if (mState == ConnectionState::SecureConnected)
243 if (mStatusDelegate != nullptr)
245 mStatusDelegate->OnMessage(std::move(msgBuf));
249 HandleDataModelMessage(mDeviceId, std::move(msgBuf));
254 CHIP_ERROR Device::OpenPairingWindow(uint32_t timeout, PairingWindowOption option, SetupPayload & setupPayload)
256 // TODO: This code is temporary, and must be updated to use the Cluster API.
257 // Issue: https://github.com/project-chip/connectedhomeip/issues/4725
259 // Construct and send "open pairing window" message to the device
260 System::PacketBufferHandle buf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize);
261 System::PacketBufferTLVWriter writer;
263 writer.Init(std::move(buf));
264 writer.ImplicitProfileId = chip::Protocols::ServiceProvisioning::Id.ToTLVProfileId();
266 ReturnErrorOnFailure(writer.Put(TLV::ProfileTag(writer.ImplicitProfileId, 1), timeout));
268 if (option != PairingWindowOption::kOriginalSetupCode)
270 ReturnErrorOnFailure(writer.Put(TLV::ProfileTag(writer.ImplicitProfileId, 2), setupPayload.discriminator));
272 PASEVerifier verifier;
273 bool randomSetupPIN = (option == PairingWindowOption::kTokenWithRandomPIN);
274 ReturnErrorOnFailure(PASESession::GeneratePASEVerifier(verifier, randomSetupPIN, setupPayload.setUpPINCode));
275 ReturnErrorOnFailure(writer.PutBytes(TLV::ProfileTag(writer.ImplicitProfileId, 3),
276 reinterpret_cast<const uint8_t *>(verifier), sizeof(verifier)));
279 System::PacketBufferHandle outBuffer;
280 ReturnErrorOnFailure(writer.Finalize(&outBuffer));
282 PayloadHeader header;
284 header.SetMessageType(chip::Protocols::ServiceProvisioning::Id, 0);
286 ReturnErrorOnFailure(SendMessage(std::move(outBuffer), header));
288 setupPayload.version = 0;
289 setupPayload.rendezvousInformation = RendezvousInformationFlags::kBLE;
291 return CHIP_NO_ERROR;
294 CHIP_ERROR Device::UpdateAddress(const Transport::PeerAddress & addr)
298 VerifyOrReturnError(addr.GetTransportType() == Transport::Type::kUdp, CHIP_ERROR_INVALID_ADDRESS);
299 ReturnErrorOnFailure(LoadSecureSessionParametersIfNeeded(didLoad));
301 Transport::PeerConnectionState * connectionState = mSessionManager->GetPeerConnectionState(mSecureSession);
302 VerifyOrReturnError(connectionState != nullptr, CHIP_ERROR_INCORRECT_STATE);
304 mDeviceUdpAddress = addr;
305 connectionState->SetPeerAddress(addr);
307 return CHIP_NO_ERROR;
310 CHIP_ERROR Device::LoadSecureSessionParameters(ResetTransport resetNeeded)
312 CHIP_ERROR err = CHIP_NO_ERROR;
313 PASESession pairingSession;
315 if (mSessionManager == nullptr || mState == ConnectionState::SecureConnected)
317 ExitNow(err = CHIP_ERROR_INCORRECT_STATE);
320 err = pairingSession.FromSerializable(mPairing);
323 if (resetNeeded == ResetTransport::kYes)
325 err = mTransportMgr->ResetTransport(
326 Transport::UdpListenParameters(mInetLayer).SetAddressType(kIPAddressType_IPv6).SetListenPort(mListenPort)
327 #if INET_CONFIG_ENABLE_IPV4
329 Transport::UdpListenParameters(mInetLayer).SetAddressType(kIPAddressType_IPv4).SetListenPort(mListenPort)
335 err = mSessionManager->NewPairing(Optional<Transport::PeerAddress>::Value(mDeviceUdpAddress), mDeviceId, &pairingSession,
336 SecureSessionMgr::PairingDirection::kInitiator, mAdminId);
341 if (err != CHIP_NO_ERROR)
343 ChipLogError(Controller, "LoadSecureSessionParameters returning error %d\n", err);
348 bool Device::GetAddress(Inet::IPAddress & addr, uint16_t & port) const
350 if (mState == ConnectionState::NotConnected)
353 addr = mDeviceUdpAddress.GetIPAddress();
354 port = mDeviceUdpAddress.GetPort();
358 void Device::AddResponseHandler(uint8_t seqNum, Callback::Cancelable * onSuccessCallback, Callback::Cancelable * onFailureCallback)
360 mCallbacksMgr.AddResponseCallback(mDeviceId, seqNum, onSuccessCallback, onFailureCallback);
363 void Device::AddReportHandler(EndpointId endpoint, ClusterId cluster, AttributeId attribute,
364 Callback::Cancelable * onReportCallback)
366 mCallbacksMgr.AddReportCallback(mDeviceId, endpoint, cluster, attribute, onReportCallback);
369 } // namespace Controller