diff --git a/config/openiotsdk/CMakeLists.txt b/config/openiotsdk/CMakeLists.txt index 040371f32cdc67..cfcca494e9d59d 100644 --- a/config/openiotsdk/CMakeLists.txt +++ b/config/openiotsdk/CMakeLists.txt @@ -66,6 +66,7 @@ matter_add_gn_arg_string("chip_crypto" "${CONFIG_CHIP_C matter_add_gn_arg_string("chip_openiotsdk_software_version" "${CONFIG_CHIP_OPEN_IOT_SDK_SOFTWARE_VERSION}") matter_add_gn_arg_string("chip_openiotsdk_software_version_string" "${CONFIG_CHIP_OPEN_IOT_SDK_SOFTWARE_VERSION_STRING}") matter_add_gn_arg_bool ("chip_enable_ota_requestor" CONFIG_CHIP_OPEN_IOT_SDK_OTA_ENABLE) +matter_add_gn_arg_bool ("chip_system_config_use_iot_socket" CONFIG_CHIP_OPEN_IOT_SDK_USE_IOT_SOCKET) if (TARGET cmsis-rtos-api) matter_add_gn_arg_string("target_os" "cmsis-rtos") endif() diff --git a/config/openiotsdk/cmake/chip.cmake b/config/openiotsdk/cmake/chip.cmake index d05710fa3be717..be79e98bd0351e 100644 --- a/config/openiotsdk/cmake/chip.cmake +++ b/config/openiotsdk/cmake/chip.cmake @@ -36,6 +36,7 @@ set(CONFIG_CHIP_CRYPTO "mbedtls" CACHE STRING "Matter crypto backend. Mbedtls as set(CONFIG_CHIP_OPEN_IOT_SDK_SOFTWARE_VERSION "0" CACHE STRING "Software version number") set(CONFIG_CHIP_OPEN_IOT_SDK_SOFTWARE_VERSION_STRING ${TFM_NS_APP_VERSION} CACHE STRING "Software version in string format x.x.x") set(CONFIG_CHIP_OPEN_IOT_SDK_OTA_ENABLE NO CACHE BOOL "Enable OTA support") +set(CONFIG_CHIP_OPEN_IOT_SDK_USE_IOT_SOCKET NO CACHE BOOL "Enable using IoT Socket API") if(CONFIG_CHIP_OPEN_IOT_SDK_USE_PSA_PS AND NOT TFM_SUPPORT) message( FATAL_ERROR "You can not use PSA Protected Storage without TF-M support" ) diff --git a/config/openiotsdk/cmake/sdk.cmake b/config/openiotsdk/cmake/sdk.cmake index 4071ef27350ad7..7236ad9ee5b2e4 100644 --- a/config/openiotsdk/cmake/sdk.cmake +++ b/config/openiotsdk/cmake/sdk.cmake @@ -64,6 +64,14 @@ FetchContent_Declare( PATCH_COMMAND git reset --hard --quiet && git clean --force -dx --quiet && git apply ${CMAKE_CURRENT_LIST_DIR}/tf-m.patch ) +FetchContent_Declare( + lwip + GIT_REPOSITORY https://github.com/lwip-tcpip/lwip.git + GIT_TAG STABLE-2_1_3_RELEASE + GIT_PROGRESS ON + PATCH_COMMAND git apply --ignore-space-change --ignore-whitespace ${CHIP_ROOT}/config/openiotsdk/lwip/lwip.patch || true +) + # Open IoT SDK configuration set(IOTSDK_FETCH_LIST mcu-driver-reference-platforms-for-arm diff --git a/config/openiotsdk/lwip/lwip.patch b/config/openiotsdk/lwip/lwip.patch new file mode 100644 index 00000000000000..61193f319b98a7 --- /dev/null +++ b/config/openiotsdk/lwip/lwip.patch @@ -0,0 +1,67 @@ +diff --git a/src/api/sockets.c b/src/api/sockets.c +index 78526350..fb6e0796 100644 +--- a/src/api/sockets.c ++++ b/src/api/sockets.c +@@ -1158,23 +1158,34 @@ lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16 + #if LWIP_NETBUF_RECVINFO + /* Check if packet info was recorded */ + if (buf->flags & NETBUF_FLAG_DESTADDR) { +- if (IP_IS_V4(&buf->toaddr)) { ++#if LWIP_IPV4 || LWIP_IPV6 ++ if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) { ++ struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */ ++ chdr->cmsg_level = IPPROTO_IP; ++ chdr->cmsg_type = IP_PKTINFO; + #if LWIP_IPV4 +- if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) { +- struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */ ++ if (IP_IS_V4(&buf->toaddr)) { + struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr); +- chdr->cmsg_level = IPPROTO_IP; +- chdr->cmsg_type = IP_PKTINFO; + chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo)); +- pkti->ipi_ifindex = buf->p->if_idx; +- inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf))); + msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo)); +- wrote_msg = 1; +- } else { +- msg->msg_flags |= MSG_CTRUNC; ++ inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf))); ++ pkti->ipi_ifindex = buf->p->if_idx; + } + #endif /* LWIP_IPV4 */ ++#if LWIP_IPV6 ++ if (IP_IS_V6(&buf->toaddr)) { ++ struct in6_pktinfo *pkti6 = (struct in6_pktinfo *)CMSG_DATA(chdr); ++ chdr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); ++ msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); ++ inet6_addr_from_ip6addr(&pkti6->ipi6_addr, ip_2_ip6(netbuf_destaddr(buf))); ++ pkti6->ipi6_ifindex = buf->p->if_idx; ++ } ++#endif /* LWIP_IPV6 */ ++ wrote_msg = 1; ++ } else { ++ msg->msg_flags |= MSG_CTRUNC; + } ++#endif /* LWIP_IPV4 || LWIP_IPV6 */ + } + #endif /* LWIP_NETBUF_RECVINFO */ + +diff --git a/src/include/lwip/sockets.h b/src/include/lwip/sockets.h +index d70d36c4..1c225041 100644 +--- a/src/include/lwip/sockets.h ++++ b/src/include/lwip/sockets.h +@@ -329,6 +329,13 @@ struct in_pktinfo { + }; + #endif /* LWIP_IPV4 */ + ++#if LWIP_IPV6 ++struct in6_pktinfo { ++ unsigned int ipi6_ifindex; /* Interface index */ ++ struct in6_addr ipi6_addr; /* Destination (from header) address */ ++}; ++#endif /* LWIP_IPV6 */ ++ + #if LWIP_IPV6_MLD + /* + * Options and types related to IPv6 multicast membership diff --git a/config/openiotsdk/lwip/user_lwipopts.h b/config/openiotsdk/lwip/user_lwipopts.h index 48d8d4aee81b21..b6451f7861aa58 100644 --- a/config/openiotsdk/lwip/user_lwipopts.h +++ b/config/openiotsdk/lwip/user_lwipopts.h @@ -49,9 +49,24 @@ */ #define LWIP_RAW (1) -#ifdef LWIP_DEBUG +/** + * LWIP_NETIF_API: Support netif api (in netifapi.c) + */ +#define LWIP_NETIF_API (1) + +/** + * IP_SOF_BROADCAST: Use the SOF_BROADCAST field to enable broadcast + * filter per pcb on udp and raw send operations. + */ +#define IP_SOF_BROADCAST (1) + +/** + * IP_SOF_BROADCAST_RECV: Enable the broadcast filter on recv operations. + */ +#define IP_SOF_BROADCAST_RECV (1) // Debug Options +#ifdef LWIP_DEBUG #define NETIF_DEBUG LWIP_DBG_ON #define IP_DEBUG LWIP_DBG_ON #define TCP_DEBUG LWIP_DBG_ON @@ -60,7 +75,7 @@ #define LWIP_DBG_TYPES_ON LWIP_DBG_ON #define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL -#endif +#endif // LWIP_DEBUG #ifndef LWIP_PLATFORM_DIAG #define LWIP_PLATFORM_DIAG(x) \ diff --git a/src/inet/EndPointStateIoTSocket.h b/src/inet/EndPointStateIoTSocket.h new file mode 100644 index 00000000000000..b7439bbdab4600 --- /dev/null +++ b/src/inet/EndPointStateIoTSocket.h @@ -0,0 +1,51 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2015-2017 Nest Labs, Inc. + * + * 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. + */ + +/** + * Shared state for socket implementations of TCPEndPoint and UDPEndPoint. + */ + +#pragma once + +#include + +namespace chip { +namespace Inet { + +/** + * Definitions shared by all IoT Socket based EndPoint classes. + */ +class EndPointStateIoTSocket +{ +public: + virtual void SelectCallback(void * readMask, void * writeMask, void * exceptionMask){}; + int32_t GetSocketId() { return mSocket; }; + + static constexpr int32_t kInvalidSocketFd = -1; + +protected: + EndPointStateIoTSocket() : mSocket(kInvalidSocketFd){}; + virtual ~EndPointStateIoTSocket(){}; + + int32_t mSocket; /**< Encapsulated socket descriptor. */ + int32_t mSocketFamily; /**< Protocol family, i.e. IPv4 or IPv6. */ + uint16_t mBoundPort; +}; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/IPAddress-StringFuncts.cpp b/src/inet/IPAddress-StringFuncts.cpp index 1f15e05844b0fe..75f66abe053123 100644 --- a/src/inet/IPAddress-StringFuncts.cpp +++ b/src/inet/IPAddress-StringFuncts.cpp @@ -34,6 +34,10 @@ #include #include +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#include +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK #include #endif @@ -43,7 +47,7 @@ namespace Inet { char * IPAddress::ToString(char * buf, uint32_t bufSize) const { -#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET #if INET_CONFIG_ENABLE_IPV4 if (IsIPv4()) { @@ -81,6 +85,21 @@ char * IPAddress::ToString(char * buf, uint32_t bufSize) const #elif CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT otIp6Address addr = ToIPv6(); otIp6AddressToString(&addr, buf, static_cast(bufSize)); +#elif CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#if INET_CONFIG_ENABLE_IPV4 + if (IsIPv4()) + { + ip4_addr_t ip4_addr; + memcpy(&ip4_addr, &Addr[3], sizeof(ip4_addr)); + ip4addr_ntoa_r(&ip4_addr, buf, (int) bufSize); + } + else +#endif // INET_CONFIG_ENABLE_IPV4 + { + ip6_addr_t ip6_addr; + memcpy(&ip6_addr.addr, Addr, sizeof(ip6_addr.addr)); + ip6addr_ntoa_r(&ip6_addr, buf, (int) bufSize); + } #endif // !CHIP_SYSTEM_CONFIG_USE_LWIP return buf; diff --git a/src/inet/IPAddress.cpp b/src/inet/IPAddress.cpp index 19da2acaee94f7..e77df0e1ba225c 100644 --- a/src/inet/IPAddress.cpp +++ b/src/inet/IPAddress.cpp @@ -97,6 +97,7 @@ IPAddress::IPAddress(const ip_addr_t & addr) #endif // INET_CONFIG_ENABLE_IPV4 || LWIP_IPV4 +#if !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET #if INET_CONFIG_ENABLE_IPV4 ip4_addr_t IPAddress::ToIPv4() const @@ -200,12 +201,14 @@ ip6_addr_t IPAddress::ToIPv6() const return ipAddr; } +#endif // !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET #if INET_CONFIG_ENABLE_IPV4 -IPAddress::IPAddress(const struct in_addr & ipv4Addr) +IPAddress::IPAddress(const platform_in_addr & ipv4Addr) { Addr[0] = 0; Addr[1] = 0; @@ -214,29 +217,31 @@ IPAddress::IPAddress(const struct in_addr & ipv4Addr) } #endif // INET_CONFIG_ENABLE_IPV4 -IPAddress::IPAddress(const struct in6_addr & ipv6Addr) +IPAddress::IPAddress(const platform_in6_addr & ipv6Addr) { static_assert(sizeof(*this) == sizeof(ipv6Addr), "in6_addr size mismatch"); memcpy(Addr, &ipv6Addr, sizeof(ipv6Addr)); } #if INET_CONFIG_ENABLE_IPV4 -struct in_addr IPAddress::ToIPv4() const +IPAddress::platform_in_addr IPAddress::ToIPv4() const { - struct in_addr ipv4Addr; + platform_in_addr ipv4Addr; ipv4Addr.s_addr = Addr[3]; return ipv4Addr; } #endif // INET_CONFIG_ENABLE_IPV4 -struct in6_addr IPAddress::ToIPv6() const +IPAddress::platform_in6_addr IPAddress::ToIPv6() const { - in6_addr ipAddr; + platform_in6_addr ipAddr; static_assert(sizeof(ipAddr) == sizeof(Addr), "in6_addr size mismatch"); memcpy(&ipAddr, Addr, sizeof(ipAddr)); return ipAddr; } +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK CHIP_ERROR IPAddress::GetIPAddressFromSockAddr(const SockAddrWithoutStorage & sockaddr, IPAddress & outIPAddress) { #if INET_CONFIG_ENABLE_IPV4 diff --git a/src/inet/IPAddress.h b/src/inet/IPAddress.h index 661d0f84d65a91..a3fe0cee1bc4e7 100644 --- a/src/inet/IPAddress.h +++ b/src/inet/IPAddress.h @@ -42,6 +42,10 @@ #include "inet/IANAConstants.h" +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#include +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + #if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT #include #include @@ -175,12 +179,23 @@ class DLL_EXPORT IPAddress #endif // INET_CONFIG_ENABLE_IPV4 #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - explicit IPAddress(const struct in6_addr & ipv6Addr); +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + using platform_in6_addr = iot_in6_addr; + using platform_in_addr = iot_in_addr; + using platform_sockaddr_in6 = iot_sockaddr_in6; + using platform_sockaddr_in = iot_sockaddr_in; +#else + using platform_in6_addr = struct in6_addr; + using platform_in_addr = struct in_addr; + using platform_sockaddr_in6 = struct sockaddr_in6; + using platform_sockaddr_in = struct sockaddr_in; +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + explicit IPAddress(const platform_in6_addr & ipv6Addr); #if INET_CONFIG_ENABLE_IPV4 - explicit IPAddress(const struct in_addr & ipv4Addr); + explicit IPAddress(const platform_in_addr & ipv4Addr); #endif // INET_CONFIG_ENABLE_IPV4 -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET #if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT explicit IPAddress(const otIp6Address & ipv6Addr); @@ -520,7 +535,7 @@ class DLL_EXPORT IPAddress */ #if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT - +#if !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET /** * @fn ToLwIPAddr() const * @@ -557,17 +572,22 @@ class DLL_EXPORT IPAddress #if INET_CONFIG_ENABLE_IPV4 ip4_addr_t ToIPv4(void) const; #endif // INET_CONFIG_ENABLE_IPV4 +#endif // !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET #endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - - struct in6_addr ToIPv6() const; - +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + platform_in6_addr ToIPv6() const; #if INET_CONFIG_ENABLE_IPV4 - struct in_addr ToIPv4() const; + platform_in_addr ToIPv4() const; #endif // INET_CONFIG_ENABLE_IPV4 + static IPAddress FromSockAddr(const platform_sockaddr_in6 & sockaddr) { return IPAddress(sockaddr.sin6_addr); } +#if INET_CONFIG_ENABLE_IPV4 + static IPAddress FromSockAddr(const platform_sockaddr_in & sockaddr) { return IPAddress(sockaddr.sin_addr); } +#endif // INET_CONFIG_ENABLE_IPV4 +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK /** * Get the IP address from a SockAddr. */ @@ -576,11 +596,6 @@ class DLL_EXPORT IPAddress { return GetIPAddressFromSockAddr(reinterpret_cast(sockaddr), outIPAddress); } - static IPAddress FromSockAddr(const sockaddr_in6 & sockaddr) { return IPAddress(sockaddr.sin6_addr); } -#if INET_CONFIG_ENABLE_IPV4 - static IPAddress FromSockAddr(const sockaddr_in & sockaddr) { return IPAddress(sockaddr.sin_addr); } -#endif // INET_CONFIG_ENABLE_IPV4 - #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_USE_NETWORK_FRAMEWORK #if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT diff --git a/src/inet/InetInterface.cpp b/src/inet/InetInterface.cpp index 7c09c9f12edd61..0ff780ae1d044d 100644 --- a/src/inet/InetInterface.cpp +++ b/src/inet/InetInterface.cpp @@ -42,6 +42,10 @@ #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#include +#endif + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS #include #include diff --git a/src/inet/TCPEndPointImplIoTSocket.cpp b/src/inet/TCPEndPointImplIoTSocket.cpp new file mode 100644 index 00000000000000..415a1f3a0c9051 --- /dev/null +++ b/src/inet/TCPEndPointImplIoTSocket.cpp @@ -0,0 +1,878 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/** + * This file implements Inet::TCPEndPoint using sockets. + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace chip { +namespace System { +CHIP_ERROR MapErrorIoTSocket(int32_t errorCode); +} +namespace Inet { + +using System::MapErrorIoTSocket; + +CHIP_ERROR TCPEndPointImplIoTSocket::BindImpl(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) +{ + CHIP_ERROR res = GetSocket(addrType); + + if (res == CHIP_NO_ERROR && reuseAddr) + { + // Enable SO_REUSEPORT. This permits coexistence between an + // untargetted CHIP client and other services that listen on + // a CHIP port on a specific address (such as a CHIP client + // with TARGETED_LISTEN or TCP proxying services). Note that + // one of the costs of this implementation is the + // non-deterministic connection dispatch when multple clients + // listen on the address with the same degreee of selectivity, + // e.g. two untargetted-listen CHIP clients, or two + // targeted-listen CHIP clients with the same node id. + + int32_t n = 1; + int32_t retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_REUSEADDR, &n, sizeof(n)); + if (retcode) + { + ChipLogError(Inet, "SO_REUSEPORT: %ld", retcode); + } + } + + if (res == CHIP_NO_ERROR) + { + if (addrType == IPAddressType::kIPv6) + { + struct iot_in6_addr iot_address; + iot_address = addr.ToIPv6(); + int32_t retcode = iotSocketBind(mSocket, (uint8_t *) &iot_address, sizeof(iot_address), port); + if (retcode) + { + res = MapErrorIoTSocket(retcode); + } + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + struct iot_in_addr iot_address; + iot_address = addr.ToIPv4(); + int32_t retcode = iotSocketBind(mSocket, (uint8_t *) &iot_address, sizeof(iot_address), port); + if (retcode) + { + res = MapErrorIoTSocket(retcode); + } + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + res = INET_ERROR_WRONG_ADDRESS_TYPE; + } + } + + return res; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::ListenImpl(uint16_t backlog) +{ + int32_t retcode = iotSocketListen(mSocket, backlog); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + // Enable non-blocking mode for the socket. + uint32_t opt = 1; + iotSocketSetOpt(mSocket, IOT_SOCKET_IO_FIONBIO, &opt, sizeof(uint32_t)); + + // Wait for ability to read on this endpoint. + CHIP_ERROR res = static_cast(GetSystemLayer()).EnableSelectCallback(this, true, false); + + return res; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::ConnectImpl(const IPAddress & addr, uint16_t port, InterfaceId intfId) +{ + IPAddressType addrType = addr.Type(); + + ReturnErrorOnFailure(GetSocket(addrType)); + + if (!intfId.IsPresent()) + { + // The behavior when connecting to an IPv6 link-local address without specifying an outbound + // interface is ambiguous. So prevent it in all cases. + if (addr.IsIPv6LinkLocal()) + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + } + else + { + // Try binding to the interface + + // If destination is link-local then there is no need to iotSocketBind to + // interface or address on the interface. + + if (!addr.IsIPv6LinkLocal()) + { + char interfaceName[NETIF_NAMESIZE]; + ReturnErrorOnFailure(intfId.GetInterfaceName(interfaceName, NETIF_NAMESIZE)); + + // Attempt to iotSocketBind to the interface using SO_BINDTODEVICE which requires privileged access. + // If the permission is denied(EACCES) because CHIP is running in a context + // that does not have privileged access, choose a source address on the + // interface to iotSocketBind the connetion to. + int32_t retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_BINDTODEVICE, &interfaceName, strlen(interfaceName)); + + if (retcode == 0) + { + mBoundInterface = intfId; + } + else + { + // Attempting to initiate a connection via a specific interface is not allowed. + // The only way to do this is to iotSocketBind the local to an address on the desired + // interface. + ReturnErrorOnFailure(BindSrcAddrFromIntf(addrType, intfId)); + } + } + } + + // Enable non-blocking mode for the socket. + uint32_t opt = 1; + iotSocketSetOpt(mSocket, IOT_SOCKET_IO_FIONBIO, &opt, sizeof(uint32_t)); + + int conRes; + + if (addrType == IPAddressType::kIPv6) + { + iot_in6_addr iot_address; + iot_address = addr.ToIPv6(); + + conRes = iotSocketConnect(mSocket, (uint8_t *) &iot_address, sizeof(iot_address), port); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + iot_in_addr iot_address; + iot_address = addr.ToIPv4(); + + conRes = iotSocketConnect(mSocket, (uint8_t *) &iot_address, sizeof(iot_address), port); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + if (conRes < 0 && conRes != IOT_SOCKET_EINPROGRESS) + { + CHIP_ERROR res = MapErrorIoTSocket(conRes); + DoClose(res, true); + return res; + } + + // Once Connecting or Connected, bump the reference count. The corresponding Release() will happen in DoClose(). + Retain(); + + if (conRes == 0) + { + mState = State::kConnected; + // Wait for ability to read on this endpoint. + ReturnErrorOnFailure(static_cast(GetSystemLayer()).EnableSelectCallback(this, true, false)); + if (OnConnectComplete != nullptr) + { + OnConnectComplete(this, CHIP_NO_ERROR); + } + } + else + { + mState = State::kConnecting; + // Wait for ability to write on this endpoint. + ReturnErrorOnFailure(static_cast(GetSystemLayer()).EnableSelectCallback(this, false, true)); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::GetInfo(IPAddress * retAddr, uint16_t * retPort, bool local) const +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + union + { + iot_in6_addr addr6; + iot_in_addr addr4; + } addr; + uint32_t iplen = sizeof(addr); + + int32_t ret; + + if (local) + { + ret = iotSocketGetSockName(mSocket, (uint8_t *) &addr, &iplen, retPort); + } + else + { + + ret = iotSocketGetPeerName(mSocket, (uint8_t *) &addr, &iplen, retPort); + } + + if (iplen == sizeof(iot_in6_addr)) + { + *retAddr = IPAddress(addr.addr6); + } + else + { + *retAddr = IPAddress(addr.addr4); + } + + if (ret == 0) + { + return CHIP_NO_ERROR; + } + + return CHIP_ERROR_INCORRECT_STATE; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) const +{ + return GetInfo(retAddr, retPort, false); +} + +CHIP_ERROR TCPEndPointImplIoTSocket::GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) const +{ + return GetInfo(retAddr, retPort, true); +} + +CHIP_ERROR TCPEndPointImplIoTSocket::GetInterfaceId(InterfaceId * retInterface) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + if (retInterface) + { + *retInterface = mBoundInterface; + return CHIP_NO_ERROR; + } + return CHIP_ERROR_INVALID_ARGUMENT; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::SendQueuedImpl(bool queueWasEmpty) +{ + if (queueWasEmpty) + { + // Wait for ability to write on this endpoint. + return static_cast(GetSystemLayer()).EnableSelectCallback(this, false, true); + ; + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::EnableNoDelay() +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true + int32_t val = 1; + int32_t retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_TCP_NODELAY, &val, sizeof(val)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Set the idle interval + int32_t val = interval; + int32_t retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_TCP_KEEPIDLE, &val, sizeof(val)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + // Set the probe retransmission interval. + val = interval; + retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_TCP_KEEPINTVL, &val, sizeof(val)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + // Set the probe timeout count + val = timeoutCount; + retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_TCP_KEEPCNT, &val, sizeof(val)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + // Enable keepalives for the connection. + val = 1; // enable + retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_KEEPALIVE, &val, sizeof(val)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::DisableKeepAlive() +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Disable keepalives on the connection. + int32_t val = 0; // disable + int32_t retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_KEEPALIVE, &val, sizeof(val)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::AckReceive(uint16_t len) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // nothing to do for sockets case + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::SetUserTimeoutImpl(uint32_t userTimeoutMillis) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::DriveSendingImpl() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Pretend send() fails in the while loop below + INET_FAULT_INJECT(FaultInjection::kFault_Send, { + err = MapErrorIoTSocket(EIO); + DoClose(err, false); + return err; + }); + + while (!mSendQueue.IsNull()) + { + uint16_t bufLen = mSendQueue->DataLength(); + + int32_t lenSentRaw = iotSocketSend(mSocket, mSendQueue->Start(), bufLen); + + if (lenSentRaw < 0) + { + if (lenSentRaw != IOT_SOCKET_EAGAIN) + { + err = MapErrorIoTSocket(lenSentRaw); + } + break; + } + + if (lenSentRaw < 0 || lenSentRaw > bufLen) + { + err = CHIP_ERROR_INCORRECT_STATE; + break; + } + + // Cast is safe because bufLen is uint16_t. + uint16_t lenSent = static_cast(lenSentRaw); + + // Mark the connection as being active. + MarkActive(); + + if (lenSent < bufLen) + { + mSendQueue->ConsumeHead(lenSent); + } + else + { + mSendQueue.FreeHead(); + if (mSendQueue.IsNull()) + { + // Do not wait for ability to write on this endpoint. + err = static_cast(GetSystemLayer()).DisableSelectCallback(this, false, true); + if (err != CHIP_NO_ERROR) + { + break; + } + } + } + + if (OnDataSent != nullptr) + { + OnDataSent(this, lenSent); + } + + if (lenSent < bufLen) + { + break; + } + } + + if (err == CHIP_NO_ERROR) + { + // If we're in the SendShutdown state and the send queue is now empty, shutdown writing on the socket. + if (mState == State::kSendShutdown && mSendQueue.IsNull()) + { + int32_t retcode = iotSocketShutdown(mSocket, IOT_SOCKET_SHUTDOWN_WR); + if (retcode) + { + err = MapErrorIoTSocket(retcode); + } + } + } + + return err; +} + +void TCPEndPointImplIoTSocket::HandleConnectCompleteImpl() +{ + // Wait for ability to read or write on this endpoint. + CHIP_ERROR err = static_cast(GetSystemLayer()).EnableSelectCallback(this, true, false); + if (err == CHIP_NO_ERROR) + { + err = static_cast(GetSystemLayer()).EnableSelectCallback(this, false, true); + } + if (err != CHIP_NO_ERROR) + { + DoClose(err, false); + return; + } +} + +void TCPEndPointImplIoTSocket::DoCloseImpl(CHIP_ERROR err, State oldState) +{ + // If the socket hasn't been closed already... + if (mSocket != kInvalidSocketFd) + { + // If entering the Closed state + // OR if entering the Closing state, and there's no unsent data in the send queue + // THEN close the socket. + if (mState == State::kClosed || (mState == State::kClosing && mSendQueue.IsNull())) + { + // If aborting the connection, ensure we send a TCP RST. + if (IsConnected(oldState) && err != CHIP_NO_ERROR) + { + iot_opt_linger lingerStruct; + lingerStruct.l_onoff = 1; + lingerStruct.l_linger = 0; + + int32_t retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_LINGER, &lingerStruct, sizeof(lingerStruct)); + if (retcode) + { + ChipLogError(Inet, "SO_LINGER: %ld", retcode); + } + } + + static_cast(GetSystemLayer()).DisableSelectCallback(this, true, true); + iotSocketClose(mSocket); + mSocket = kInvalidSocketFd; + } + } +} + +CHIP_ERROR TCPEndPointImplIoTSocket::BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId intfId) +{ + // If we are trying to make a TCP connection over a 'specified target interface', + // then we bind the TCPEndPoint to an IP address on that target interface + // and use that address as the source address for that connection. This is + // done in the event that directly binding the connection to the target + // interface is not allowed due to insufficient privileges. + VerifyOrReturnError(mState != State::kBound, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + + bool ipAddrFound = false; + for (InterfaceAddressIterator addrIter; addrIter.HasCurrent(); addrIter.Next()) + { + IPAddress curAddr; + if ((addrIter.GetInterfaceId() == intfId) && (addrIter.GetAddress(curAddr) == CHIP_NO_ERROR)) + { + // Search for an IPv4 address on the TargetInterface + +#if INET_CONFIG_ENABLE_IPV4 + if (addrType == IPAddressType::kIPv4) + { + if (curAddr.IsIPv4()) + { + // Bind to the IPv4 address of the TargetInterface + ipAddrFound = true; + ReturnErrorOnFailure(Bind(IPAddressType::kIPv4, curAddr, 0, true)); + + break; + } + } +#endif // INET_CONFIG_ENABLE_IPV4 + if (addrType == IPAddressType::kIPv6) + { + // Select an IPv6 address on the interface that is not + // a link local or a multicast address. + // TODO: Define a proper IPv6GlobalUnicast address checker. + if (!curAddr.IsIPv4() && !curAddr.IsIPv6LinkLocal() && !curAddr.IsMulticast()) + { + // Bind to the IPv6 address of the TargetInterface + ipAddrFound = true; + ReturnErrorOnFailure(Bind(IPAddressType::kIPv6, curAddr, 0, true)); + + break; + } + } + } + } + + VerifyOrReturnError(ipAddrFound, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE); + mBoundInterface = intfId; + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplIoTSocket::GetSocket(IPAddressType addrType) +{ + if (mSocket == kInvalidSocketFd) + { + if (addrType == IPAddressType::kIPv6) + { + mSocketFamily = IOT_SOCKET_AF_INET6; +#if INET_CONFIG_ENABLE_IPV4 + } + else if (addrType == IPAddressType::kIPv4) + { + mSocketFamily = IOT_SOCKET_AF_INET; +#endif // INET_CONFIG_ENABLE_IPV4 + } + else + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + mSocket = iotSocketCreate(mSocketFamily, IOT_SOCKET_SOCK_STREAM, IOT_SOCKET_IPPROTO_TCP); + if (mSocket < 0) + { + return MapErrorIoTSocket(mSocket); + } + + // If creating an IPv6 socket, tell the kernel that it will be IPv6 only. This makes it + // posible to iotSocketBind two sockets to the same port, one for IPv4 and one for IPv6. + if (mSocketFamily == IOT_SOCKET_AF_INET6) + { + int one = 1; + iotSocketSetOpt(mSocket, IOT_SOCKET_IPV6_V6ONLY, &one, sizeof(one)); + } + } + else + { + if (addrType == IPAddressType::kIPv6 && mSocketFamily != IOT_SOCKET_AF_INET6) + { + return CHIP_ERROR_INCORRECT_STATE; + } + if (addrType == IPAddressType::kIPv4 && mSocketFamily != IOT_SOCKET_AF_INET) + { + return CHIP_ERROR_INCORRECT_STATE; + } + } + + return CHIP_NO_ERROR; +} + +void TCPEndPointImplIoTSocket::SelectCallback(void * readMask, void * writeMask, void * exceptionMask) +{ + bool readEvent = iotSocketMaskIsSet(mSocket, readMask); + bool writeEvent = iotSocketMaskIsSet(mSocket, writeMask); + + if (!readEvent && !writeEvent) + { + return; + } + + // Prevent the end point from being freed while in the middle of a callback. + Retain(); + + // If in the Listening state, and the app is ready to receive a connection, and there is a connection + // ready to be received on the socket, process the incoming connection. + if (mState == State::kListening) + { + if (OnConnectionReceived != nullptr && readEvent) + { + HandleIncomingConnection(); + } + } + + // If in the processes of initiating a connection... + else if (mState == State::kConnecting) + { + // The socket being writable indicates the connection has completed (successfully or otherwise). + if (writeEvent) + { + // connect blocks and never returns EINPROGRESS + // The socket option SO_ERROR is not available. + + // Process the connection result. + HandleConnectComplete(CHIP_NO_ERROR); + } + } + + else + { + // If in a state where sending is allowed, and there is data to be sent, and the socket is ready for + // writing, drive outbound data into the connection. + if (writeEvent && IsConnected() && !mSendQueue.IsNull()) + { + DriveSending(); + } + + // If in a state were receiving is allowed, and the app is ready to receive data, and data is ready + // on the socket, receive inbound data from the connection. + if (readEvent) + { + if (mState == State::kConnected || mState == State::kSendShutdown) + { + if (mReceiveEnabled && OnDataReceived != nullptr) + { + ReceiveData(); + } + } + } + } + + Release(); +} + +void TCPEndPointImplIoTSocket::ReceiveData() +{ + System::PacketBufferHandle rcvBuf; + bool isNewBuf = true; + + if (mRcvQueue.IsNull()) + { + rcvBuf = System::PacketBufferHandle::New(kMaxReceiveMessageSize, 0); + } + else + { + rcvBuf = mRcvQueue->Last(); + if (rcvBuf->AvailableDataLength() == 0) + { + rcvBuf = System::PacketBufferHandle::New(kMaxReceiveMessageSize, 0); + } + else + { + isNewBuf = false; + rcvBuf->CompactHead(); + } + } + + if (rcvBuf.IsNull()) + { + DoClose(CHIP_ERROR_NO_MEMORY, false); + return; + } + + // Attempt to receive data from the socket. + int32_t rcvLen = iotSocketRecv(mSocket, rcvBuf->Start() + rcvBuf->DataLength(), rcvBuf->AvailableDataLength()); + + // If an error occurred, abort the connection. + if (rcvLen < 0) + { + CHIP_ERROR err = MapErrorIoTSocket(rcvLen); + if (err == CHIP_ERROR_BUSY) + { + // Note: in this case, we opt to not retry the recv call, + // and instead we expect that the read flags will get + // reset correctly upon a subsequent return from the + // select call. + ChipLogError(Inet, "recv: EAGAIN, will retry"); + + return; + } + + DoClose(err, false); + } + + else + { + // Mark the connection as being active. + MarkActive(); + + // If the peer closed their end of the connection... + if (rcvLen == 0) + { + // If in the Connected state and the app has provided an OnPeerClose callback, + // enter the ReceiveShutdown state. Providing an OnPeerClose callback allows + // the app to decide whether to keep the send side of the connection open after + // the peer has closed. If no OnPeerClose is provided, we assume that the app + // wants to close both directions and automatically enter the Closing state. + if (mState == State::kConnected && OnPeerClose != nullptr) + { + mState = State::kReceiveShutdown; + } + else + { + mState = State::kClosing; + } + // Do not wait for ability to read on this endpoint. + (void) static_cast(GetSystemLayer()).DisableSelectCallback(this, true, false); + // Call the app's OnPeerClose. + if (OnPeerClose != nullptr) + { + OnPeerClose(this); + } + } + + // Otherwise, add the new data onto the receive queue. + else + { + VerifyOrDie(rcvLen > 0); + size_t newDataLength = rcvBuf->DataLength() + static_cast(rcvLen); + VerifyOrDie(CanCastTo(newDataLength)); + if (isNewBuf) + { + rcvBuf->SetDataLength(static_cast(newDataLength)); + rcvBuf.RightSize(); + if (mRcvQueue.IsNull()) + { + mRcvQueue = std::move(rcvBuf); + } + else + { + mRcvQueue->AddToEnd(std::move(rcvBuf)); + } + } + else + { + rcvBuf->SetDataLength(static_cast(newDataLength), mRcvQueue); + } + } + } + + // Drive any received data into the app. + DriveReceiving(); +} + +void TCPEndPointImplIoTSocket::HandleIncomingConnection() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TCPEndPointImplIoTSocket * conEP = nullptr; + IPAddress peerAddr; + uint16_t peerPort; + int32_t conSocket; + + if (mSocketFamily == IOT_SOCKET_AF_INET6) + { + struct iot_in6_addr iot_address; + memset(&iot_address, 0, sizeof(iot_address)); + uint32_t address_length = sizeof(iot_address); + + conSocket = iotSocketAccept(mSocket, (uint8_t *) &iot_address, &address_length, &peerPort); + peerAddr = IPAddress(iot_address); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (mSocketFamily == IOT_SOCKET_AF_INET) + { + struct iot_in_addr iot_address; + memset(&iot_address, 0, sizeof(iot_address)); + uint32_t address_length = sizeof(iot_address); + + conSocket = iotSocketAccept(mSocket, (uint8_t *) &iot_address, &address_length, &peerPort); + peerAddr = IPAddress(iot_address); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + err = CHIP_ERROR_INCORRECT_STATE; + } + + // Accept the new connection. + + if (conSocket < 0) + { + err = MapErrorIoTSocket(conSocket); + if (err == CHIP_ERROR_BUSY) + { + return; + } + } + + // If there's no callback available, fail with an error. + if (err == CHIP_NO_ERROR && OnConnectionReceived == nullptr) + { + err = CHIP_ERROR_NO_CONNECTION_HANDLER; + } + + // Attempt to allocate an end point object. + if (err == CHIP_NO_ERROR) + { + TCPEndPoint * connectEndPoint = nullptr; + err = GetEndPointManager().NewEndPoint(&connectEndPoint); + conEP = static_cast(connectEndPoint); + } + + // If all went well... + if (err == CHIP_NO_ERROR) + { + // Put the new end point into the Connected state. + conEP->mSocket = conSocket; + + conEP->mState = State::kConnected; + conEP->Retain(); + + // Wait for ability to read on this endpoint. + auto & conEPLayer = static_cast(conEP->GetSystemLayer()); + err = conEPLayer.EnableSelectCallback(conEP, true, false); + + if (err == CHIP_NO_ERROR) + { + // Call the app's callback function. + OnConnectionReceived(this, conEP, peerAddr, peerPort); + return; + } + } + + // Otherwise immediately close the connection, clean up and call the app's error callback. + if (conSocket != -1) + { + iotSocketClose(conSocket); + } + if (conEP != nullptr) + { + if (conEP->mState == State::kConnected) + { + conEP->Release(); + } + conEP->Release(); + } + if (OnAcceptError != nullptr) + { + OnAcceptError(this, err); + } +} // namespace Inet + +} // namespace Inet +} // namespace chip diff --git a/src/inet/TCPEndPointImplIoTSocket.h b/src/inet/TCPEndPointImplIoTSocket.h new file mode 100644 index 00000000000000..49e7964befee73 --- /dev/null +++ b/src/inet/TCPEndPointImplIoTSocket.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/** + * This file declares an implementation of Inet::TCPEndPoint using sockets. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace Inet { + +class TCPEndPointImplIoTSocket : public TCPEndPoint, public EndPointStateIoTSocket +{ +public: + TCPEndPointImplIoTSocket(EndPointManager & endPointManager) : TCPEndPoint(endPointManager) {} + + void SelectCallback(void * readMask, void * writeMask, void * exceptionMask) override; + + // TCPEndPoint overrides. + CHIP_ERROR GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) const override; + CHIP_ERROR GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) const override; + CHIP_ERROR GetInterfaceId(InterfaceId * retInterface) override; + CHIP_ERROR EnableNoDelay() override; + CHIP_ERROR EnableKeepAlive(uint16_t interval, uint16_t timeoutCount) override; + CHIP_ERROR DisableKeepAlive() override; + CHIP_ERROR AckReceive(uint16_t len) override; + +private: + // TCPEndPoint overrides. + CHIP_ERROR BindImpl(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) override; + CHIP_ERROR ListenImpl(uint16_t backlog) override; + CHIP_ERROR ConnectImpl(const IPAddress & addr, uint16_t port, InterfaceId intfId) override; + CHIP_ERROR SendQueuedImpl(bool queueWasEmpty) override; + CHIP_ERROR SetUserTimeoutImpl(uint32_t userTimeoutMillis) override; + CHIP_ERROR DriveSendingImpl() override; + void HandleConnectCompleteImpl() override; + void DoCloseImpl(CHIP_ERROR err, State oldState) override; + + CHIP_ERROR GetSocket(IPAddressType addrType); + + void ReceiveData(); + void HandleIncomingConnection(); + CHIP_ERROR BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId intfId); + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + void TCPUserTimeoutHandler() override{}; +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + +private: + CHIP_ERROR GetInfo(IPAddress * retAddr, uint16_t * retPort, bool local) const; + InterfaceId mBoundInterface; +}; + +using TCPEndPointImpl = TCPEndPointImplIoTSocket; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplIoTSocket.cpp b/src/inet/UDPEndPointImplIoTSocket.cpp new file mode 100644 index 00000000000000..e6ace612b949e1 --- /dev/null +++ b/src/inet/UDPEndPointImplIoTSocket.cpp @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/** + * This file implements Inet::UDPEndPoint using sockets. + */ + +#include +#include +#include +#include +#include + +#if CHIP_SYSTEM_CONFIG_USE_LWIP +#include "lwip/if_api.h" +#include "lwip/ip4.h" +#include "lwip/ip6.h" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + +namespace chip { +namespace System { +CHIP_ERROR MapErrorIoTSocket(int32_t errorCode); +} + +namespace Inet { + +using System::MapErrorIoTSocket; + +namespace { + +CHIP_ERROR IPv6Bind(int socket, const IPAddress & address, uint16_t port, InterfaceId interface) +{ + iot_in6_addr iot_address; + iot_address = address.ToIPv6(); + CHIP_ERROR status = CHIP_NO_ERROR; + int32_t retcode = iotSocketBind(socket, (uint8_t *) &iot_address, sizeof(iot_address), port); + if (retcode) + { + status = MapErrorIoTSocket(retcode); + } + else + { + if (interface.GetPlatformInterface()) + { + // Instruct the kernel that any messages to multicast destinations should be + // sent down the interface specified by the caller. + int32_t index; +#if CHIP_SYSTEM_CONFIG_USE_LWIP + index = interface.GetPlatformInterface()->num + 1; +#else +#error "must provide mapping to interface index" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + retcode = iotSocketSetOpt(socket, IOT_SOCKET_IPV6_MULTICAST_IF, &index, sizeof(int32_t)); + if (retcode) + { + ChipLogError(Inet, "Call to iotSocketSetOpt IOT_SOCKET_IPV6_MULTICAST_IF failed: %ld", retcode); + } + } + } + + // Instruct the kernel that any messages to multicast destinations should be + // set with the configured hop limit value. + int32_t hops = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; + iotSocketSetOpt(socket, IOT_SOCKET_IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); + + return status; +} + +#if INET_CONFIG_ENABLE_IPV4 +CHIP_ERROR IPv4Bind(int socket, const IPAddress & address, uint16_t port) +{ + struct iot_in_addr iot_address; + iot_address = address.ToIPv4(); + CHIP_ERROR status = CHIP_NO_ERROR; + int32_t retcode = iotSocketBind(socket, (uint8_t *) &iot_address, sizeof(iot_address), port); + if (retcode) + { + status = MapErrorIoTSocket(retcode); + } + else + { + // Allow socket transmitting broadcast packets. + constexpr int32_t enable = 1; + iotSocketSetOpt(socket, IOT_SOCKET_SO_BROADCAST, &enable, sizeof(enable)); + + // Instruct the kernel that any messages to multicast destinations should be + // sent down the interface to which the specified IPv4 address is bound. + iotSocketSetOpt(socket, IOT_SOCKET_IP_MULTICAST_IF, &iot_address, sizeof(iot_address)); + } + + // Instruct the kernel that any messages to multicast destinations should be + // set with the configured hop limit value. + constexpr int32_t ttl = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; + iotSocketSetOpt(socket, IOT_SOCKET_IP_MULTICAST_TTL, &ttl, sizeof(ttl)); + + return status; +} +#endif // INET_CONFIG_ENABLE_IPV4 + +} // anonymous namespace + +#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API +UDPEndPointImplIoTSocket::MulticastGroupHandler UDPEndPointImplIoTSocket::sJoinMulticastGroupHandler; +UDPEndPointImplIoTSocket::MulticastGroupHandler UDPEndPointImplIoTSocket::sLeaveMulticastGroupHandler; +#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API + +CHIP_ERROR UDPEndPointImplIoTSocket::BindImpl(IPAddressType addressType, const IPAddress & addr, uint16_t port, + InterfaceId interface) +{ + // Make sure we have the appropriate type of socket. + ReturnErrorOnFailure(GetSocket(addressType)); + + if (addressType == IPAddressType::kIPv6) + { + ReturnErrorOnFailure(IPv6Bind(mSocket, addr, port, interface)); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addressType == IPAddressType::kIPv4) + { + ReturnErrorOnFailure(IPv4Bind(mSocket, addr, port)); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + mBoundPort = port; + mBoundInterface = interface; + + // If an ephemeral port was requested, retrieve the actual bound port. + if (port == 0) + { + if (addressType == IPAddressType::kIPv6) + { + struct iot_in6_addr iot_address; + uint32_t iot_address_len = sizeof(iot_address); + iotSocketGetSockName(mSocket, (uint8_t *) &iot_address, &iot_address_len, &mBoundPort); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addressType == IPAddressType::kIPv4) + { + struct iot_in6_addr iot_address; + uint32_t iot_address_len = sizeof(iot_address); + iotSocketGetSockName(mSocket, (uint8_t *) &iot_address, &iot_address_len, &mBoundPort); + } +#endif // INET_CONFIG_ENABLE_IPV4 + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UDPEndPointImplIoTSocket::BindInterfaceImpl(IPAddressType addressType, InterfaceId interfaceId) +{ + // Make sure we have the appropriate type of socket. + ReturnErrorOnFailure(GetSocket(addressType)); + +#if HAVE_SO_BINDTODEVICE + CHIP_ERROR status = CHIP_NO_ERROR; + int32_t retcode; + + if (interfaceId.IsPresent()) + { + // Start filtering on the passed interface. + char interfaceName[IF_NAMESIZE]; + if (if_indextoname(interfaceId.GetPlatformInterface(), interfaceName) == nullptr) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_BINDTODEVICE, interfaceName, uint32_t(strlen(interfaceName))); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + } + else + { + // Stop interface-based filtering. + if (retcode = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_BINDTODEVICE, "", 0) != 0) + { + return MapErrorIoTSocket(retcode); + } + } + + mBoundInterface = interfaceId; + + return CHIP_NO_ERROR; +#else // !HAVE_SO_BINDTODEVICE + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // HAVE_SO_BINDTODEVICE +} + +InterfaceId UDPEndPointImplIoTSocket::GetBoundInterface() const +{ + return mBoundInterface; +} + +uint16_t UDPEndPointImplIoTSocket::GetBoundPort() const +{ + return mBoundPort; +} + +CHIP_ERROR UDPEndPointImplIoTSocket::ListenImpl() +{ + // Wait for ability to read on this endpoint. + auto * layer = static_cast(&GetSystemLayer()); + return layer->EnableSelectCallback(this, true, false); +} + +CHIP_ERROR UDPEndPointImplIoTSocket::SendMsgImpl(const IPPacketInfo * aPktInfo, System::PacketBufferHandle && msg) +{ + // Make sure we have the appropriate type of socket based on the + // destination address. + ReturnErrorOnFailure(GetSocket(aPktInfo->DestAddress.Type())); + + // Ensure the destination address type is compatible with the endpoint address type. + VerifyOrReturnError(mSocketFamily == + ((aPktInfo->DestAddress.Type() == IPAddressType::kIPv6) ? IOT_SOCKET_AF_INET6 : IOT_SOCKET_AF_INET), + CHIP_ERROR_INVALID_ARGUMENT); + + // For now the entire message must fit within a single buffer. + VerifyOrReturnError(!msg->HasChainedBuffer(), CHIP_ERROR_MESSAGE_TOO_LONG); + + iot_iovec msgIOV; + msgIOV.iov_base = msg->Start(); + msgIOV.iov_len = msg->DataLength(); + + uint8_t controlData[256]; + memset(controlData, 0, sizeof(controlData)); + + iot_msghdr msgHeader; + memset(&msgHeader, 0, sizeof(msgHeader)); + msgHeader.msg_iov = &msgIOV; + msgHeader.msg_iovlen = 1; + + // Construct a sockaddr_in/sockaddr_in6 structure containing the destination information. + iot_sockaddr_in_any peerSockAddr; + memset(&peerSockAddr, 0, sizeof(peerSockAddr)); + msgHeader.msg_name = &peerSockAddr; + if (mSocketFamily == IOT_SOCKET_AF_INET6) + { + peerSockAddr.in6.sin6_len = sizeof(iot_sockaddr_in6); + peerSockAddr.in6.sin6_family = IOT_SOCKET_AF_INET6; + peerSockAddr.in6.sin6_port = htons(aPktInfo->DestPort); + peerSockAddr.in6.sin6_addr = aPktInfo->DestAddress.ToIPv6(); + msgHeader.msg_namelen = sizeof(iot_sockaddr_in6); + } +#if INET_CONFIG_ENABLE_IPV4 + else + { + peerSockAddr.in.sin_len = sizeof(iot_sockaddr_in); + peerSockAddr.in.sin_family = IOT_SOCKET_AF_INET; + peerSockAddr.in.sin_port = htons(aPktInfo->DestPort); + peerSockAddr.in.sin_addr = aPktInfo->DestAddress.ToIPv4(); + msgHeader.msg_namelen = sizeof(iot_sockaddr_in); + } +#endif // INET_CONFIG_ENABLE_IPV4 + + // If the endpoint has been bound to a particular interface, + // and the caller didn't supply a specific interface to send + // on, use the bound interface. This appears to be necessary + // for messages to multicast addresses, which under Linux + // don't seem to get sent out the correct interface, despite + // the socket being bound. + InterfaceId intf = aPktInfo->Interface; + if (!intf.IsPresent()) + { + intf = mBoundInterface; + } + + if (mSocketFamily == IOT_SOCKET_AF_INET6) + { +#if CHIP_SYSTEM_CONFIG_USE_LWIP + // this translates to LWIP interface zone which is equal to the index which maps to netif->num+1 + peerSockAddr.in6.sin6_scope_id = intf.GetPlatformInterface()->num + 1; +#else +#error "must provide translation between implementation interface and interface zone" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + } + + // If the packet should be sent over a specific interface, or with a specific source + // address, construct an IOT_SOCKET_IP_PKTINFO/IOT_SOCKET_IPV6_PKTINFO "control message" to that effect + // add add it to the message header. If the local OS doesn't support IOT_SOCKET_IP_PKTINFO/IOT_SOCKET_IPV6_PKTINFO + // fail with an error. + + // iotSocketSendMsg doesn't support pktinfo, we can't specify the address, fail if we try to + if (aPktInfo->SrcAddress.Type() != IPAddressType::kAny) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + else if (intf.IsPresent()) + { + // if we have a bound interface we'll try to send on that + if (mBoundInterface.IsPresent()) + { + if (mBoundInterface.GetPlatformInterface() != intf.GetPlatformInterface()) + { + // if we're trying to send the packet on an interafce different than bound then fail + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + } + else // we check if we will send on the same interface as requested + { + InterfaceId::PlatformType default_platform_iface; +#if CHIP_SYSTEM_CONFIG_USE_LWIP + // only way to legitimately get the default iface is to use ip route + if (mSocketFamily == IOT_SOCKET_AF_INET6) + { + const iot_in6_addr addr6 = aPktInfo->DestAddress.ToIPv6(); + ip6_addr_t lwip_addr6; + memcpy(&lwip_addr6, &addr6, sizeof(iot_in6_addr)); + lwip_addr6.zone = intf.GetPlatformInterface()->num + 1; + default_platform_iface = ip6_route(nullptr, &lwip_addr6); + } +#if INET_CONFIG_ENABLE_IPV4 + else + { + const iot_in_addr addr = aPktInfo->DestAddress.ToIPv4(); + default_platform_iface = ip4_route((const ip4_addr_t *) &addr); + } +#endif // INET_CONFIG_ENABLE_IPV4 + if (!default_platform_iface) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } +#else +#error "must provide default interface or enable support for pkt info in iotSocketSendMsg" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + if (default_platform_iface != intf.GetPlatformInterface()) + { + // if we're trying to send a packet on a non default interface then fail + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + } + } + + // Send IP packet. + const ssize_t lenSent = iotSocketSendMsg(mSocket, &msgHeader, 0); + if (lenSent < 0) + { + return MapErrorIoTSocket(lenSent); + } + if (lenSent != msg->DataLength()) + { + return CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG; + } + return CHIP_NO_ERROR; +} + +void UDPEndPointImplIoTSocket::CloseImpl() +{ + if (mSocket != kInvalidSocketFd) + { + static_cast(&GetSystemLayer())->DisableSelectCallback(this, true, true); + iotSocketClose(mSocket); + mSocket = kInvalidSocketFd; + } +} + +void UDPEndPointImplIoTSocket::Free() +{ + Close(); + Release(); +} + +CHIP_ERROR UDPEndPointImplIoTSocket::GetSocket(IPAddressType addressType) +{ + if (mSocket == kInvalidSocketFd) + { + int family; + + switch (addressType) + { + case IPAddressType::kIPv6: + family = IOT_SOCKET_AF_INET6; + break; + +#if INET_CONFIG_ENABLE_IPV4 + case IPAddressType::kIPv4: + family = IOT_SOCKET_AF_INET; + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + mSocket = iotSocketCreate(family, IOT_SOCKET_SOCK_DGRAM, IOT_SOCKET_IPPROTO_UDP); + if (mSocket < 0) + { + return MapErrorIoTSocket(mSocket); + } + + mSocketFamily = family; + + // NOTE: some return codes are logged only and not fatal + + constexpr int32_t one = 1; + int32_t res = iotSocketSetOpt(mSocket, IOT_SOCKET_SO_REUSEADDR, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "SO_REUSEADDR failed: %ld", res); + } + + // If creating an IPv6 socket, tell the kernel that it will be + // IPv6 only. This makes it posible to iotSocketBind two sockets to + // the same port, one for IPv4 and one for IPv6. + + if (addressType == IPAddressType::kIPv6) + { + res = iotSocketSetOpt(mSocket, IOT_SOCKET_IPV6_V6ONLY, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "IPV6_V6ONLY failed: %ld", res); + } + } + +#if INET_CONFIG_ENABLE_IPV4 + if (addressType == IPAddressType::kIPv4) + { + res = iotSocketSetOpt(mSocket, IOT_SOCKET_IP_PKTINFO, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "IOT_SOCKET_IP_PKTINFO failed: %ld", res); + } + } +#endif // INET_CONFIG_ENABLE_IPV4 + + if (addressType == IPAddressType::kIPv6) + { + res = iotSocketSetOpt(mSocket, IOT_SOCKET_IPV6_PKTINFO, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "IOT_SOCKET_IPV6_PKTINFO failed: %ld", res); + } + } + } + else + { + if (addressType == IPAddressType::kIPv6 && mSocketFamily != IOT_SOCKET_AF_INET6) + { + return CHIP_ERROR_INCORRECT_STATE; + } + if (addressType == IPAddressType::kIPv4 && mSocketFamily != IOT_SOCKET_AF_INET) + { + return CHIP_ERROR_INCORRECT_STATE; + } + } + + return CHIP_NO_ERROR; +} + +void UDPEndPointImplIoTSocket::SelectCallback(void * readMask, void * writeMask, void * exceptionMask) +{ + bool readEvent = iotSocketMaskIsSet(mSocket, readMask); + + if (mState != State::kListening || OnMessageReceived == nullptr || !readEvent) + { + return; + } + + CHIP_ERROR lStatus = CHIP_NO_ERROR; + IPPacketInfo lPacketInfo; + System::PacketBufferHandle lBuffer; + + lPacketInfo.Clear(); + lPacketInfo.DestPort = mBoundPort; + + lBuffer = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSizeWithoutReserve, 0); + + if (!lBuffer.IsNull()) + { + iot_iovec msgIOV; + iot_sockaddr_in_any lPeerSockAddr; + uint8_t controlData[256]; + struct iot_msghdr msgHeader; + + msgIOV.iov_base = lBuffer->Start(); + msgIOV.iov_len = lBuffer->AvailableDataLength(); + + memset(&lPeerSockAddr, 0, sizeof(lPeerSockAddr)); + + memset(&msgHeader, 0, sizeof(msgHeader)); + + msgHeader.msg_name = &lPeerSockAddr; + msgHeader.msg_namelen = sizeof(lPeerSockAddr); + msgHeader.msg_iov = &msgIOV; + msgHeader.msg_iovlen = 1; + msgHeader.msg_control = controlData; + msgHeader.msg_controllen = sizeof(controlData); + + int32_t rcvLen = iotSocketRecvMsg(mSocket, &msgHeader, IOT_SOCKET_MSG_DONTWAIT); + + if (rcvLen < 0) + { + lStatus = MapErrorIoTSocket(rcvLen); + } + else if (rcvLen > lBuffer->AvailableDataLength()) + { + lStatus = CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG; + } + else + { + lBuffer->SetDataLength(static_cast(rcvLen)); + + if (lPeerSockAddr.in6.sin6_family == IOT_SOCKET_AF_INET6) + { + lPacketInfo.SrcAddress = IPAddress(lPeerSockAddr.in6.sin6_addr); + lPacketInfo.SrcPort = ntohs(lPeerSockAddr.in6.sin6_port); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (lPeerSockAddr.in.sin_family == IOT_SOCKET_AF_INET) + { + lPacketInfo.SrcAddress = IPAddress(lPeerSockAddr.in.sin_addr); + lPacketInfo.SrcPort = ntohs(lPeerSockAddr.in.sin_port); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + lStatus = CHIP_ERROR_INCORRECT_STATE; + } + } + + if (lStatus == CHIP_NO_ERROR) + { + for (iot_cmsghdr * controlHdr = IOT_CMSG_FIRSTHDR(&msgHeader); controlHdr != nullptr; + controlHdr = IOT_CMSG_NXTHDR(&msgHeader, controlHdr)) + { + // the control message contains the pktinfo that has the index of the interface, + // we need to translate that index into a pointer to the interface struct +#if INET_CONFIG_ENABLE_IPV4 + if (controlHdr->cmsg_level == IOT_SOCKET_LEVEL_IPPROTO_IP && controlHdr->cmsg_type == IOT_SOCKET_IP_PKTINFO) + { + iot_in_pktinfo * inPktInfo = (iot_in_pktinfo *) IOT_CMSG_DATA(controlHdr); + InterfaceId iface; +#if CHIP_SYSTEM_CONFIG_USE_LWIP + char name[IF_NAMESIZE]; + if (lwip_if_indextoname(inPktInfo->ipi_ifindex, name)) + { + if (InterfaceId::InterfaceNameToId(name, iface) == CHIP_NO_ERROR) + { + if (!iface.IsPresent()) + { + lStatus = CHIP_ERROR_INCORRECT_STATE; + break; + } + } + } +#else +#error "must provide translation between implementation interface index and chip InterfaceId" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + lPacketInfo.Interface = iface; + lPacketInfo.DestAddress = IPAddress(inPktInfo->ipi_addr); + continue; + } +#endif // INET_CONFIG_ENABLE_IPV4 + if (controlHdr->cmsg_level == IOT_SOCKET_LEVEL_IPPROTO_IPV6 && controlHdr->cmsg_type == IOT_SOCKET_IPV6_PKTINFO) + { + iot_in6_pktinfo * in6PktInfo = (iot_in6_pktinfo *) IOT_CMSG_DATA(controlHdr); + InterfaceId iface; +#if CHIP_SYSTEM_CONFIG_USE_LWIP + char name[IF_NAMESIZE]; + if (lwip_if_indextoname(in6PktInfo->ipi6_ifindex, name)) + { + if (InterfaceId::InterfaceNameToId(name, iface) == CHIP_NO_ERROR) + { + if (!iface.IsPresent()) + { + lStatus = CHIP_ERROR_INCORRECT_STATE; + break; + } + } + } +#else +#error "must provide translation between implementation interface index and chip InterfaceId" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + lPacketInfo.Interface = iface; + lPacketInfo.DestAddress = IPAddress(in6PktInfo->ipi6_addr); + continue; + } + } + } + } + else + { + lStatus = CHIP_ERROR_NO_MEMORY; + } + + if (lStatus == CHIP_NO_ERROR) + { + lBuffer.RightSize(); + OnMessageReceived(this, std::move(lBuffer), &lPacketInfo); + } + else + { + if (OnReceiveError != nullptr && lStatus != CHIP_ERROR_BUSY) + { + OnReceiveError(this, lStatus, nullptr); + } + } +} + +static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, bool aLoopback, int aProtocol, int aOption) +{ + (void) aProtocol; + const int32_t lValue = static_cast(aLoopback); + int32_t retcode = iotSocketSetOpt(aSocket, aOption, &lValue, sizeof(lValue)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + + return CHIP_NO_ERROR; +} + +static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, IPVersion aIPVersion, bool aLoopback) +{ + CHIP_ERROR lRetval; + + switch (aIPVersion) + { + case kIPVersion_6: + lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, 0, IOT_SOCKET_IPV6_MULTICAST_LOOP); + break; + +#if INET_CONFIG_ENABLE_IPV4 + case kIPVersion_4: + lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, 0, IOT_SOCKET_IP_MULTICAST_LOOP); + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + lRetval = INET_ERROR_WRONG_ADDRESS_TYPE; + break; + } + + return (lRetval); +} + +CHIP_ERROR UDPEndPointImplIoTSocket::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback) +{ + CHIP_ERROR lRetval = CHIP_ERROR_NOT_IMPLEMENTED; + + lRetval = SocketsSetMulticastLoopback(mSocket, aIPVersion, aLoopback); + SuccessOrExit(lRetval); + +exit: + return (lRetval); +} + +#if INET_CONFIG_ENABLE_IPV4 + +CHIP_ERROR UDPEndPointImplIoTSocket::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, + bool join) +{ + IPAddress lInterfaceAddress; + bool lInterfaceAddressFound = false; + + for (InterfaceAddressIterator lAddressIterator; lAddressIterator.HasCurrent(); lAddressIterator.Next()) + { + IPAddress lCurrentAddress; + if ((lAddressIterator.GetInterfaceId() == aInterfaceId) && (lAddressIterator.GetAddress(lCurrentAddress) == CHIP_NO_ERROR)) + { + if (lCurrentAddress.IsIPv4()) + { + lInterfaceAddressFound = true; + lInterfaceAddress = lCurrentAddress; + break; + } + } + } + VerifyOrReturnError(lInterfaceAddressFound, INET_ERROR_ADDRESS_NOT_FOUND); + + iot_ip_mreq lMulticastRequest; + lMulticastRequest.imr_interface = lInterfaceAddress.ToIPv4(); + lMulticastRequest.imr_multiaddr = aAddress.ToIPv4(); + + const int command = join ? IOT_SOCKET_IP_ADD_MEMBERSHIP : IOT_SOCKET_IP_DROP_MEMBERSHIP; + int32_t retcode = iotSocketSetOpt(mSocket, command, &lMulticastRequest, sizeof(lMulticastRequest)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + return CHIP_NO_ERROR; +} + +#endif // INET_CONFIG_ENABLE_IPV4 + +CHIP_ERROR UDPEndPointImplIoTSocket::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, + bool join) +{ +#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API + MulticastGroupHandler handler = join ? sJoinMulticastGroupHandler : sLeaveMulticastGroupHandler; + if (handler != nullptr) + { + return handler(aInterfaceId, aAddress); + } +#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API + + const InterfaceId::PlatformType lIfIndex = aInterfaceId.GetPlatformInterface(); + + iot_ipv6_mreq lMulticastRequest; +#if CHIP_SYSTEM_CONFIG_USE_LWIP + // this translates to LWIP interface zone which is equal to the index which maps to netif->num+1 + lMulticastRequest.ipv6mr_interface = (uint32_t) lIfIndex->num + 1; +#else +#error "must provide translation between implementation interface and interface zone" +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + lMulticastRequest.ipv6mr_multiaddr = aAddress.ToIPv6(); + + const int command = join ? IOT_SOCKET_IPV6_ADD_MEMBERSHIP : IOT_SOCKET_IPV6_DROP_MEMBERSHIP; + int32_t retcode = iotSocketSetOpt(mSocket, command, &lMulticastRequest, sizeof(lMulticastRequest)); + if (retcode) + { + return MapErrorIoTSocket(retcode); + } + return CHIP_NO_ERROR; +} + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplIoTSocket.h b/src/inet/UDPEndPointImplIoTSocket.h new file mode 100644 index 00000000000000..58169661caadc0 --- /dev/null +++ b/src/inet/UDPEndPointImplIoTSocket.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2022 Project CHIP Authors + * + * 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. + */ + +/** + * This file declares an implementation of Inet::UDPEndPoint using sockets. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace Inet { + +class UDPEndPointImplIoTSocket : public UDPEndPoint, public EndPointStateIoTSocket +{ +public: + UDPEndPointImplIoTSocket(EndPointManager & endPointManager) : + UDPEndPoint(endPointManager), mBoundInterface(InterfaceId::Null()) + {} + + void SelectCallback(void * readMask, void * writeMask, void * exceptionMask) override; + + // UDPEndPoint overrides. + CHIP_ERROR SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback) override; + InterfaceId GetBoundInterface() const override; + uint16_t GetBoundPort() const override; + void Free() override; + +private: + // UDPEndPoint overrides. +#if INET_CONFIG_ENABLE_IPV4 + CHIP_ERROR IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) override; +#endif // INET_CONFIG_ENABLE_IPV4 + CHIP_ERROR IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) override; + CHIP_ERROR BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port, InterfaceId interfaceId) override; + CHIP_ERROR BindInterfaceImpl(IPAddressType addressType, InterfaceId interfaceId) override; + CHIP_ERROR ListenImpl() override; + CHIP_ERROR SendMsgImpl(const IPPacketInfo * pktInfo, chip::System::PacketBufferHandle && msg) override; + void CloseImpl() override; + + CHIP_ERROR GetSocket(IPAddressType addressType); + +#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API +public: + using MulticastGroupHandler = CHIP_ERROR (*)(InterfaceId, const IPAddress &); + static void SetJoinMulticastGroupHandler(MulticastGroupHandler handler) { sJoinMulticastGroupHandler = handler; } + static void SetLeaveMulticastGroupHandler(MulticastGroupHandler handler) { sLeaveMulticastGroupHandler = handler; } + +private: + static MulticastGroupHandler sJoinMulticastGroupHandler; + static MulticastGroupHandler sLeaveMulticastGroupHandler; +#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API + +private: + InterfaceId mBoundInterface; +}; + +using UDPEndPointImpl = UDPEndPointImplIoTSocket; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/inet.gni b/src/inet/inet.gni index 1c19551da374c6..f3aae9ac6de8af 100644 --- a/src/inet/inet.gni +++ b/src/inet/inet.gni @@ -27,7 +27,9 @@ declare_args() { chip_inet_config_enable_tcp_endpoint = true # Inet implementation type. - if (chip_system_config_use_open_thread_inet_endpoints) { + if (chip_system_config_use_iot_socket) { + chip_system_config_inet = "IoTSocket" + } else if (chip_system_config_use_open_thread_inet_endpoints) { chip_system_config_inet = "OpenThread" } else if (chip_system_config_use_lwip) { chip_system_config_inet = "LwIP" diff --git a/src/inet/tests/TestInetAddress.cpp b/src/inet/tests/TestInetAddress.cpp index a31bbb878efee9..b205489224514a 100644 --- a/src/inet/tests/TestInetAddress.cpp +++ b/src/inet/tests/TestInetAddress.cpp @@ -41,6 +41,7 @@ #include +#include #include #include @@ -1008,15 +1009,20 @@ void CheckToIPv6(nlTestSuite * inSuite, void * inContext) SetupIPAddress(test_addr, lCurrent); -#if CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET ip6_addr_t ip_addr_1 = { 0 }, ip_addr_2 = { 0 }; memcpy(ip_addr_1.addr, addr, sizeof(addr)); #if LWIP_IPV6_SCOPES ip_addr_1.zone = 0; #endif #else - struct in6_addr ip_addr_1, ip_addr_2; +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + IPAddress::platform_in6_addr ip_addr_1, ip_addr_2; + ip_addr_1 = *reinterpret_cast(addr); +#else + in6_addr ip_addr_1, ip_addr_2; ip_addr_1 = *reinterpret_cast(addr); +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET #endif ip_addr_2 = test_addr.ToIPv6(); @@ -1142,12 +1148,16 @@ void CheckToIPv4(nlTestSuite * inSuite, void * inContext) SetupIPAddress(test_addr, lCurrent); -#if CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET ip4_addr_t ip_addr_1, ip_addr_2; ip_addr_1.addr = htonl(lCurrent->mAddr.mAddrQuartets[3]); +#else +#if CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + IPAddress::platform_in_addr ip_addr_1, ip_addr_2; #else struct in_addr ip_addr_1, ip_addr_2; +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK || CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET ip_addr_1.s_addr = htonl(lCurrent->mAddr.mAddrQuartets[3]); #endif @@ -1715,7 +1725,7 @@ void CheckIPPrefix(nlTestSuite * inSuite, void * inContext) } } -#if CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET bool sameLwIPAddress(const ip6_addr_t & a, const ip6_addr_t & b) { @@ -1816,7 +1826,7 @@ void CheckToLwIPAddr(nlTestSuite * inSuite, void * inContext) ++lCurrent; } } -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET /** * Test Suite. It lists all the test functions. @@ -1858,9 +1868,9 @@ const nlTest sTests[] = NL_TEST_DEF("Assemble IPv6 Transient Multicast address", CheckMakeIPv6TransientMulticast), NL_TEST_DEF("Assemble IPv6 Prefix Multicast address", CheckMakeIPv6PrefixMulticast), NL_TEST_DEF("IPPrefix test", CheckIPPrefix), -#if CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET NL_TEST_DEF("Convert IPAddress to LwIP address", CheckToLwIPAddr), -#endif +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET NL_TEST_SENTINEL() }; // clang-format on diff --git a/src/inet/tests/TestInetCommonPosix.cpp b/src/inet/tests/TestInetCommonPosix.cpp index 32554adfe0cbd5..231b748febf424 100644 --- a/src/inet/tests/TestInetCommonPosix.cpp +++ b/src/inet/tests/TestInetCommonPosix.cpp @@ -451,7 +451,7 @@ void ServiceEvents(uint32_t aSleepTimeMilliseconds) gSystemLayer.HandleEvents(); #endif -#if CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_LWIP && !CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET if (gSystemLayer.IsInitialized()) { static uint32_t sRemainingSystemLayerEventDelay = 0; @@ -482,6 +482,15 @@ void ServiceEvents(uint32_t aSleepTimeMilliseconds) TapInterface_Select(&(sTapIFs[0]), &(sNetIFs[0]), aSleepTime, gNetworkOptions.TapDeviceName.size()); #endif // CHIP_TARGET_STYLE_UNIX #endif // CHIP_SYSTEM_CONFIG_USE_LWIP + +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + if (gSystemLayer.IsInitialized()) + { + gSystemLayer.HandlePlatformTimer(); + gSystemLayer.WaitForEvents(); + gSystemLayer.HandleEvents(); + } +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET } #if CHIP_SYSTEM_CONFIG_USE_LWIP && !(CHIP_SYSTEM_CONFIG_LWIP_SKIP_INIT) diff --git a/src/platform/openiotsdk/InetPlatformConfig.h b/src/platform/openiotsdk/InetPlatformConfig.h index d571369d3820f7..e7062f60df7eb1 100644 --- a/src/platform/openiotsdk/InetPlatformConfig.h +++ b/src/platform/openiotsdk/InetPlatformConfig.h @@ -28,3 +28,7 @@ // ==================== Platform Adaptations ==================== // ========== Platform-specific Configuration Overrides ========= + +#ifndef IPV6_MULTICAST_IMPLEMENTED +#define IPV6_MULTICAST_IMPLEMENTED +#endif diff --git a/src/platform/openiotsdk/PlatformManagerImpl.cpp b/src/platform/openiotsdk/PlatformManagerImpl.cpp index 579eb5e7f10429..7601823bef3ed1 100644 --- a/src/platform/openiotsdk/PlatformManagerImpl.cpp +++ b/src/platform/openiotsdk/PlatformManagerImpl.cpp @@ -38,6 +38,13 @@ using namespace ::chip::System; namespace chip { namespace DeviceLayer { +namespace { +LayerImpl & SystemLayerImpl() +{ + return static_cast(DeviceLayer::SystemLayer()); +} +} // anonymous namespace + CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) { if (mInitialized) @@ -46,11 +53,7 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) } // Call up to the base class _InitChipStack() to perform the bulk of the initialization. - CHIP_ERROR err = GenericPlatformManagerImpl::_InitChipStack(); - if (err != CHIP_NO_ERROR) - { - return err; - } + ReturnLogErrorOnFailure(GenericPlatformManagerImpl::_InitChipStack()); // Members are initialized by the stack osMutexAttr_t mut_att = { .attr_bits = osMutexRecursive }; @@ -60,20 +63,20 @@ CHIP_ERROR PlatformManagerImpl::_InitChipStack(void) mEventTaskMutex = osMutexNew(nullptr); mPlatformFlags = osEventFlagsNew(nullptr); mQueue = osMessageQueueNew(CHIP_DEVICE_CONFIG_MAX_EVENT_QUEUE_SIZE, sizeof(ChipDeviceEvent), nullptr); - mTimer = osTimerNew(TimerCallback, osTimerOnce, NULL, NULL); - if (!mChipStackMutex || !mEventTaskMutex || !mPlatformFlags || !mQueue || !mTimer) + if (!mChipStackMutex || !mEventTaskMutex || !mPlatformFlags || !mQueue) { osMutexDelete(mChipStackMutex); osMutexDelete(mEventTaskMutex); osEventFlagsDelete(mPlatformFlags); osMessageQueueDelete(mQueue); - osTimerDelete(mTimer); - mChipStackMutex = mEventTaskMutex = mPlatformFlags = mQueue = mTimer = nullptr; + mChipStackMutex = mEventTaskMutex = mPlatformFlags = mQueue = nullptr; return CHIP_ERROR_INTERNAL; } + ReturnLogErrorOnFailure(PlatformTimerInit()); + mInitialized = true; SetConfigurationMgr(&ConfigurationManagerImpl::GetDefaultInstance()); @@ -110,9 +113,11 @@ CHIP_ERROR PlatformManagerImpl::_PostEvent(const ChipDeviceEvent * eventPtr) ReturnLogErrorOnFailure(_InitChipStack()); osStatus_t status = osMessageQueuePut(mQueue, eventPtr, 0, 0); - CHIP_ERROR ret = (status == osOK) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; osEventFlagsSet(mPlatformFlags, kPostEventFlag); - return ret; +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + SystemLayerImpl().Signal(); +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + return (status == osOK) ? CHIP_NO_ERROR : CHIP_ERROR_INTERNAL; } void PlatformManagerImpl::HandlePostEvent() @@ -130,13 +135,17 @@ void PlatformManagerImpl::HandlePostEvent() LockChipStack(); DispatchEvent(&event); UnlockChipStack(); +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + count = osMessageQueueGetCount(mQueue); +#else count--; +#endif } } void PlatformManagerImpl::HandleTimerEvent(void) { - const CHIP_ERROR err = static_cast(DeviceLayer::SystemLayer()).HandlePlatformTimer(); + CHIP_ERROR err = SystemLayerImpl().HandlePlatformTimer(); if (err != CHIP_NO_ERROR) { ChipLogError(DeviceLayer, "HandlePlatformTimer %ld", err.AsInteger()); @@ -146,16 +155,27 @@ void PlatformManagerImpl::HandleTimerEvent(void) void PlatformManagerImpl::RunEventLoopInternal() { uint32_t flags = 0; + while (true) { +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + if (SystemLayerImpl().WaitForEvents() == CHIP_NO_ERROR) + { + LockChipStack(); + SystemLayerImpl().HandleEvents(); + UnlockChipStack(); + } + + flags = osEventFlagsGet(mPlatformFlags); +#else flags = osEventFlagsWait(mPlatformFlags, kPostEventFlag | kTimerEventFlag | kTaskStopEventFlag, osFlagsWaitAny | osFlagsNoClear, ms2tick(1000)); - // in case of error we still need to know the value of flags we're not waiting for if (flags & osFlagsError) { flags = osEventFlagsGet(mPlatformFlags); } +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET if (flags & kTaskStopEventFlag) { @@ -230,11 +250,12 @@ CHIP_ERROR PlatformManagerImpl::_StartEventLoopTask() osEventFlagsSet(mPlatformFlags, kTaskRunningEventFlag); - // this thread is self terminating - osThreadId_t mEventTask = osThreadNew(EventLoopTask, NULL, &tread_attr); + // these threads are self terminating + osThreadId_t event_task = osThreadNew(EventLoopTask, NULL, &tread_attr); - if (mEventTask == nullptr) + if (event_task == nullptr) { + osEventFlagsSet(mPlatformFlags, kTaskRunningEventFlag); osMutexRelease(mEventTaskMutex); return CHIP_ERROR_INTERNAL; } @@ -272,6 +293,10 @@ CHIP_ERROR PlatformManagerImpl::_StopEventLoopTask() osEventFlagsSet(mPlatformFlags, kTaskStopEventFlag); +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + SystemLayerImpl().Signal(); +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + osMutexRelease(mEventTaskMutex); } @@ -286,6 +311,40 @@ void PlatformManagerImpl::SetEventFlags(uint32_t flags) void PlatformManagerImpl::TimerCallback(void * arg) { PlatformMgrImpl().SetEventFlags(kTimerEventFlag); + +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + SystemLayerImpl().Signal(); +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +} + +CHIP_ERROR PlatformManagerImpl::PlatformTimerInit() +{ + if (mTimer != nullptr) + { + return CHIP_NO_ERROR; + } + + mTimer = osTimerNew(TimerCallback, osTimerOnce, NULL, NULL); + + if (!mTimer) + { + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PlatformManagerImpl::PlatformTimerDeinit() +{ + if (mTimer == nullptr) + { + return CHIP_NO_ERROR; + } + + osTimerDelete(mTimer); + mTimer = nullptr; + + return CHIP_NO_ERROR; } CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) @@ -306,6 +365,7 @@ CHIP_ERROR PlatformManagerImpl::_StartChipTimer(System::Clock::Timeout duration) return CHIP_ERROR_INTERNAL; } } + return CHIP_NO_ERROR; } @@ -339,12 +399,11 @@ void PlatformManagerImpl::_Shutdown() osMutexDelete(mEventTaskMutex); osEventFlagsDelete(mPlatformFlags); osMessageQueueDelete(mQueue); - osTimerDelete(mTimer); + PlatformTimerDeinit(); mChipStackMutex = nullptr; mPlatformFlags = nullptr; mEventTaskMutex = nullptr; mQueue = nullptr; - mTimer = nullptr; mInitialized = false; GenericPlatformManagerImpl::_Shutdown(); diff --git a/src/platform/openiotsdk/PlatformManagerImpl.h b/src/platform/openiotsdk/PlatformManagerImpl.h index 2a5e86e8341e5d..d831b313b3ec61 100644 --- a/src/platform/openiotsdk/PlatformManagerImpl.h +++ b/src/platform/openiotsdk/PlatformManagerImpl.h @@ -71,6 +71,8 @@ class PlatformManagerImpl final : public PlatformManager, public Internal::Gener CHIP_ERROR _StartEventLoopTask(); CHIP_ERROR _StopEventLoopTask(); + CHIP_ERROR PlatformTimerInit(void); + CHIP_ERROR PlatformTimerDeinit(void); CHIP_ERROR _StartChipTimer(System::Clock::Timeout duration); void _Shutdown(); diff --git a/src/system/BUILD.gn b/src/system/BUILD.gn index acc7f790e3471d..a89333b3e46266 100644 --- a/src/system/BUILD.gn +++ b/src/system/BUILD.gn @@ -88,6 +88,7 @@ buildconfig_header("system_buildconfig") { "CHIP_SYSTEM_CONFIG_USE_LWIP=${chip_system_config_use_lwip}", "CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT=${chip_system_config_use_open_thread_inet_endpoints}", "CHIP_SYSTEM_CONFIG_USE_SOCKETS=${chip_system_config_use_sockets}", + "CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET=${chip_system_config_use_iot_socket}", "CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK=false", "CHIP_SYSTEM_CONFIG_POSIX_LOCKING=${chip_system_config_posix_locking}", "CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING=${chip_system_config_freertos_locking}", @@ -238,6 +239,10 @@ static_library("system") { sources += [ "SocketEvents.h" ] } + if (chip_system_config_use_iot_socket) { + sources += [ "${chip_root}/src/inet/EndPointStateIoTSocket.h" ] + } + if (chip_with_nlfaultinjection) { sources += [ "SystemFaultInjection.cpp", diff --git a/src/system/SystemError.cpp b/src/system/SystemError.cpp index 356c697e15c996..8ae77cc8dc1d2a 100644 --- a/src/system/SystemError.cpp +++ b/src/system/SystemError.cpp @@ -37,6 +37,10 @@ #include #endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#include +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + #include #include #include @@ -218,5 +222,42 @@ bool FormatLwIPError(char * buf, uint16_t bufSize, CHIP_ERROR err) #endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + +CHIP_ERROR MapErrorIoTSocket(int32_t errorCode) +{ + switch (errorCode) + { + case 0: + return CHIP_NO_ERROR; + case IOT_SOCKET_EINVAL: + return CHIP_ERROR_INVALID_ARGUMENT; + case IOT_SOCKET_ENOMEM: + return CHIP_ERROR_NO_MEMORY; + case IOT_SOCKET_EAGAIN: + return CHIP_ERROR_BUSY; + case IOT_SOCKET_ETIMEDOUT: + return CHIP_ERROR_TIMEOUT; + case IOT_SOCKET_ENOTCONN: + return CHIP_ERROR_NOT_CONNECTED; + case IOT_SOCKET_ERROR: + case IOT_SOCKET_ESOCK: + case IOT_SOCKET_ENOTSUP: + case IOT_SOCKET_EINPROGRESS: + case IOT_SOCKET_EISCONN: + case IOT_SOCKET_ECONNREFUSED: + case IOT_SOCKET_ECONNRESET: + case IOT_SOCKET_ECONNABORTED: + case IOT_SOCKET_EALREADY: + case IOT_SOCKET_EADDRINUSE: + case IOT_SOCKET_EHOSTNOTFOUND: + return CHIP_ERROR_INTERNAL; + default: + return CHIP_ERROR_INTERNAL; + } +} + +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + } // namespace System } // namespace chip diff --git a/src/system/SystemLayer.h b/src/system/SystemLayer.h index 4321edeb02b883..ff9171b72da39d 100644 --- a/src/system/SystemLayer.h +++ b/src/system/SystemLayer.h @@ -37,6 +37,10 @@ #include #include +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET +#include +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS #include #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS @@ -183,6 +187,20 @@ class LayerFreeRTOS : public Layer #endif // CHIP_SYSTEM_CONFIG_USE_LWIP +#if CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + +class LayerOpenIoTSDK : public Layer +{ +public: + virtual CHIP_ERROR EnableSelectCallback(chip::Inet::EndPointStateIoTSocket * endpoint, bool read, bool write) = 0; + virtual CHIP_ERROR DisableSelectCallback(chip::Inet::EndPointStateIoTSocket * endpoint, bool read, bool write) = 0; + virtual void Signal() = 0; + virtual CHIP_ERROR WaitForEvents() = 0; + virtual void HandleEvents() = 0; +}; + +#endif // CHIP_SYSTEM_CONFIG_USE_IOT_SOCKET + #if CHIP_SYSTEM_CONFIG_USE_SOCKETS class LayerSockets : public Layer diff --git a/src/system/SystemLayerImplIoTSocket.cpp b/src/system/SystemLayerImplIoTSocket.cpp new file mode 100644 index 00000000000000..b638b643806b74 --- /dev/null +++ b/src/system/SystemLayerImplIoTSocket.cpp @@ -0,0 +1,492 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * 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 implements Layer using select(). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if CHIP_SYSTEM_CONFIG_USE_LWIP +#include +#include +#endif // + +using namespace ::chip::Inet; + +namespace chip { +namespace System { + +enum signal_flags_t +{ + SIGNAL_FLAGS_WAITING_FOR_SELECT_RETURN = 0x01, + SIGNAL_FLAGS_SELECT_RETURNED = 0x02, + SIGNAL_FLAGS_SELECT_PENDING = 0x04 +}; + +constexpr uint16_t SIGNAL_SOCKET_PORT = 1; + +std::atomic LayerImplOpenIoTSDK::mSignalSocket{ EndPointStateIoTSocket::kInvalidSocketFd }; +Mutex LayerImplOpenIoTSDK::mSelectMutex{}; +size_t LayerImplOpenIoTSDK::mMaskSize = 0; +LayerImplOpenIoTSDK::SelectMask LayerImplOpenIoTSDK::mMaskMemory = nullptr; + +LayerImplOpenIoTSDK::LayerImplOpenIoTSDK() : mHandlingTimerComplete(false) {} + +CHIP_ERROR LayerImplOpenIoTSDK::Init() +{ + if (mLayerState.IsInitialized()) + { + return CHIP_NO_ERROR; + } + + VerifyOrReturnError(mLayerState.SetInitializing(), CHIP_ERROR_INCORRECT_STATE); + + Mutex::Init(mSelectMutex); + Mutex::Init(mSignalMutex); + mSignalFlags = osEventFlagsNew(nullptr); + +#if CHIP_SYSTEM_CONFIG_USE_LWIP + RegisterLwIPErrorFormatter(); +#endif // CHIP_SYSTEM_CONFIG_USE_LWIP + + VerifyOrReturnError(mLayerState.SetInitialized(), CHIP_ERROR_INCORRECT_STATE); + return CHIP_NO_ERROR; +} + +void LayerImplOpenIoTSDK::Shutdown() +{ + if (mSignalSocket != EndPointStateIoTSocket::kInvalidSocketFd) + { + iotSocketClose(mSignalSocket); + mSignalSocket = EndPointStateIoTSocket::kInvalidSocketFd; + } + if (mSignalFlags) + { + osEventFlagsDelete(mSignalFlags); + mSignalFlags = nullptr; + } + mLayerState.ResetFromInitialized(); +} + +CHIP_ERROR LayerImplOpenIoTSDK::StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) +{ + VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + + CHIP_SYSTEM_FAULT_INJECT(FaultInjection::kFault_TimeoutImmediate, delay = Clock::kZero); + + CancelTimer(onComplete, appState); + + TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp() + delay, onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + + if (mTimerList.Add(timer) == timer) + { + // this is the new earliest timer and so the timer needs (re-)starting provided that + // the system is not currently processing expired timers, in which case it is left to + // HandleExpiredTimers() to re-start the timer. + if (!mHandlingTimerComplete) + { + StartPlatformTimer(delay); + } + } + return CHIP_NO_ERROR; +} + +void LayerImplOpenIoTSDK::CancelTimer(TimerCompleteCallback onComplete, void * appState) +{ + VerifyOrReturn(mLayerState.IsInitialized()); + + TimerList::Node * timer = mTimerList.Remove(onComplete, appState); + if (timer != nullptr) + { + mTimerPool.Release(timer); + } +} + +CHIP_ERROR LayerImplOpenIoTSDK::ScheduleWork(TimerCompleteCallback onComplete, void * appState) +{ + VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + + TimerList::Node * timer = mTimerPool.Create(*this, SystemClock().GetMonotonicTimestamp(), onComplete, appState); + VerifyOrReturnError(timer != nullptr, CHIP_ERROR_NO_MEMORY); + + if (mTimerList.Add(timer) == timer) + { + // The new timer is the earliest, so the time until the next event has probably changed. + Signal(); + } + return CHIP_NO_ERROR; +} + +/** + * Start the platform timer with specified millsecond duration. + */ +CHIP_ERROR LayerImplOpenIoTSDK::StartPlatformTimer(System::Clock::Timeout aDelay) +{ + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + CHIP_ERROR status = PlatformEventing::StartTimer(*this, aDelay); + return status; +} + +/** + * Handle the platform timer expiration event. Completes any timers that have expired. + * + * A static API that gets called when the platform timer expires. Any expired timers are completed and removed from the list + * of active timers in the layer object. If unexpired timers remain on completion, StartPlatformTimer will be called to + * restart the platform timer. + * + * It is assumed that this API is called only while on the thread which owns the CHIP System Layer object. + * + * @note + * It's harmless if this API gets called and there are no expired timers. + * + * @return CHIP_NO_ERROR on success, error code otherwise. + * + */ +CHIP_ERROR LayerImplOpenIoTSDK::HandlePlatformTimer() +{ + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + + // Expire each timer in turn until an unexpired timer is reached or the timerlist is emptied. We set the current expiration + // time outside the loop; that way timers set after the current tick will not be executed within this expiration window + // regardless how long the processing of the currently expired timers took. + // The platform timer API has MSEC resolution so expire any timer with less than 1 msec remaining. + Clock::Timestamp expirationTime = SystemClock().GetMonotonicTimestamp() + Clock::Timeout(1); + + // limit the number of timers handled before the control is returned to the event queue. The bound is similar to + // (though not exactly same) as that on the sockets-based systems. + + size_t timersHandled = 0; + TimerList::Node * timer = nullptr; + while ((timersHandled < CHIP_SYSTEM_CONFIG_NUM_TIMERS) && ((timer = mTimerList.PopIfEarlier(expirationTime)) != nullptr)) + { + mHandlingTimerComplete = true; + mTimerPool.Invoke(timer); + mHandlingTimerComplete = false; + timersHandled++; + } + + if (!mTimerList.Empty()) + { + // timers still exist so restart the platform timer. + Clock::Timeout delay = System::Clock::kZero; + + Clock::Timestamp currentTime = SystemClock().GetMonotonicTimestamp(); + + if (currentTime < mTimerList.Earliest()->AwakenTime()) + { + // the next timer expires in the future, so set the delay to a non-zero value + delay = mTimerList.Earliest()->AwakenTime() - currentTime; + } + + StartPlatformTimer(delay); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR LayerImplOpenIoTSDK::EnableSignalSocket() +{ + // early return to avoid mutex + if (mSignalSocket != EndPointStateIoTSocket::kInvalidSocketFd) + { + return CHIP_NO_ERROR; + } + + mSelectMutex.Lock(); + + // we need to check again in case someone else created it since we got the lock + if (mSignalSocket != EndPointStateIoTSocket::kInvalidSocketFd) + { + mSelectMutex.Unlock(); + return CHIP_NO_ERROR; + } + + CHIP_ERROR err = CHIP_NO_ERROR; + + // create dummy signal socket + int32_t socket_id = iotSocketCreate(IOT_SOCKET_AF_INET, IOT_SOCKET_SOCK_DGRAM, IOT_SOCKET_IPPROTO_UDP); + if (socket_id < 0) + { + // return early, no need to cleanup as we failed to create the socket + mSelectMutex.Unlock(); + return CHIP_ERROR_INTERNAL; + } + + int32_t opt = 1; + if (iotSocketSetOpt(socket_id, IOT_SOCKET_IO_FIONBIO, &opt, sizeof(opt)) != 0) + { + err = CHIP_ERROR_INTERNAL; + } + + if (err == CHIP_NO_ERROR) + { + // build loopback address + iot_in_addr loopback_addr; + loopback_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (iotSocketBind(socket_id, (uint8_t *) &loopback_addr, sizeof(loopback_addr), SIGNAL_SOCKET_PORT) != 0) + { + ChipLogError(NotSpecified, "Cannot bind the signal socket"); + err = CHIP_ERROR_INTERNAL; + } + + // create select masks and add the signal socket to read mask + mMaskSize = iotSocketMaskGetSize(); + + if (mMaskSize && !mMaskMemory) + { + // allocate memory block that can be traversed with uin32_t pointer + const size_t aligned_size = ((mMaskSize * SelectMaskTypeMax) + 3) & ~3; + + mMaskMemory = (SelectMask) malloc(aligned_size); + } + + if (!mMaskMemory) + { + ChipLogError(NotSpecified, "Cannot allocate memory for mask size %lu", (uint32_t)(mMaskSize * SelectMaskTypeMax)); + err = CHIP_ERROR_INTERNAL; + } + else + { + for (size_t i = 0; i < SelectMaskTypeMax; i++) + { + iotSocketMaskZero(GetSelectMask((SelectMaskType) i)); + } + iotSocketMaskSet(socket_id, GetSelectMask(ReadMask)); + } + } + + // if we failed we need to close the socket to be able to retry + if (err != CHIP_NO_ERROR) + { + iotSocketClose(socket_id); + } + else + { + mSignalSocket = socket_id; + } + + mSelectMutex.Unlock(); + + return err; +} + +CHIP_ERROR LayerImplOpenIoTSDK::EnableSelectCallback(chip::Inet::EndPointStateIoTSocket * endpoint, bool read, bool write) +{ + EnableSignalSocket(); + mSelectMutex.Lock(); + + bool add = true; + for (size_t i = 0; i < mSelectEndpointsNumber; ++i) + { + if (mSelectEndpoints[i] == endpoint) + { + add = false; + // already enabled + break; + } + } + + if (add) + { + if (mSelectEndpointsNumber == kSocketMax) + { + // impossible to have more endpoints than allowed + // maybe some endpoints haven't removed their callback + mSelectMutex.Unlock(); + return CHIP_ERROR_INCORRECT_STATE; + } + else + { + mSelectEndpoints[mSelectEndpointsNumber] = endpoint; + mSelectEndpointsNumber++; + } + } + + if (read) + { + iotSocketMaskSet(endpoint->GetSocketId(), GetSelectMask(ReadMask)); + } + if (write) + { + iotSocketMaskSet(endpoint->GetSocketId(), GetSelectMask(WriteMask)); + } + + mSelectMutex.Unlock(); + + Signal(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR LayerImplOpenIoTSDK::DisableSelectCallback(chip::Inet::EndPointStateIoTSocket * endpoint, bool read, bool write) +{ + EnableSignalSocket(); + mSelectMutex.Lock(); + + // if the either mask remains set do not remove + bool keep = false; + if (endpoint->GetSocketId() != EndPointStateIoTSocket::kInvalidSocketFd) + { + if (read) + { + iotSocketMaskUnset(endpoint->GetSocketId(), GetSelectMask(ReadMask)); + } + else + { + keep = iotSocketMaskIsSet(endpoint->GetSocketId(), GetSelectMask(ReadMask)); + } + + if (write) + { + iotSocketMaskUnset(endpoint->GetSocketId(), GetSelectMask(WriteMask)); + } + else + { + keep |= iotSocketMaskIsSet(endpoint->GetSocketId(), GetSelectMask(WriteMask)); + } + } + + if (!keep) + { + // find the one to remove + for (size_t i = 0; i < mSelectEndpointsNumber; ++i) + { + if (mSelectEndpoints[i] == endpoint) + { + mSelectEndpoints[i] = nullptr; + mSelectEndpointsNumber--; + + // if this element wasn't last, grab the last one which is now beyond mSelectEndpointsNumber limit + if (i != mSelectEndpointsNumber) + { + mSelectEndpoints[i] = mSelectEndpoints[mSelectEndpointsNumber]; + } + break; + } + } + } + + bool select_pending = ((osEventFlagsGet(mSignalFlags) & SIGNAL_FLAGS_SELECT_PENDING) != 0); + + mSelectMutex.Unlock(); + + if (select_pending) + { + mSignalMutex.Lock(); + + osEventFlagsSet(mSignalFlags, SIGNAL_FLAGS_WAITING_FOR_SELECT_RETURN); + Signal(); + osEventFlagsWait(mSignalFlags, SIGNAL_FLAGS_SELECT_RETURNED, 0, osWaitForever); + + mSignalMutex.Unlock(); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR LayerImplOpenIoTSDK::WaitForEvents() +{ + if (mSignalSocket != EndPointStateIoTSocket::kInvalidSocketFd) + { + // update the masks + mSelectMutex.Lock(); + osEventFlagsSet(mSignalFlags, SIGNAL_FLAGS_SELECT_PENDING); + memcpy(GetSelectMask(ReadMaskOut), GetSelectMask(ReadMask), mMaskSize); + memcpy(GetSelectMask(WriteMaskOut), GetSelectMask(WriteMask), mMaskSize); + memcpy(GetSelectMask(ExceptionMaskOut), GetSelectMask(ExceptionMask), mMaskSize); + mSelectMutex.Unlock(); + + int32_t ret = iotSocketSelect(GetSelectMask(ReadMaskOut), GetSelectMask(WriteMaskOut), GetSelectMask(ExceptionMaskOut), + osWaitForever); + + osEventFlagsClear(mSignalFlags, SIGNAL_FLAGS_SELECT_PENDING); + if ((osEventFlagsGet(mSignalFlags) & SIGNAL_FLAGS_WAITING_FOR_SELECT_RETURN) != 0) + { + osEventFlagsClear(mSignalFlags, SIGNAL_FLAGS_WAITING_FOR_SELECT_RETURN); + osEventFlagsSet(mSignalFlags, SIGNAL_FLAGS_SELECT_RETURNED); + } + + if (ret < 0) + { + ChipLogError(NotSpecified, "Select failed with error: %ld", ret); + return CHIP_ERROR_INTERNAL; + } + } + + return CHIP_NO_ERROR; +} + +void LayerImplOpenIoTSDK::Signal() +{ + if (EnableSignalSocket() != CHIP_NO_ERROR) + { + ChipLogError(NotSpecified, "Cannot initialise Signal socket"); + } + else + { + iot_in_addr loopback_addr; + loopback_addr.s_addr = htonl(INADDR_LOOPBACK); + const char dummy = 0x01; + int32_t ret = + iotSocketSendTo(mSignalSocket, &dummy, 1, (uint8_t *) &loopback_addr, sizeof(loopback_addr), SIGNAL_SOCKET_PORT); + if (ret < 0) + { + ChipLogError(NotSpecified, "Cannot send on Signal socket"); + } + } +} + +void LayerImplOpenIoTSDK::HandleEvents() +{ + if (mSignalSocket != EndPointStateIoTSocket::kInvalidSocketFd) + { + char dummy; + bool unset = false; + while (iotSocketRecv(mSignalSocket, &dummy, sizeof(dummy)) == sizeof(dummy)) + { + unset = true; + } + + if (unset) + { + iotSocketMaskUnset(mSignalSocket, GetSelectMask(ReadMaskOut)); + } + + if (IsResultSelectMaskSet()) + { + for (size_t i = 0; i < mSelectEndpointsNumber; ++i) + { + mSelectEndpoints[i]->SelectCallback(GetSelectMask(ReadMaskOut), GetSelectMask(WriteMaskOut), + GetSelectMask(ExceptionMaskOut)); + } + } + } +} + +} // namespace System +} // namespace chip diff --git a/src/system/SystemLayerImplIoTSocket.h b/src/system/SystemLayerImplIoTSocket.h new file mode 100644 index 00000000000000..087384dc938d65 --- /dev/null +++ b/src/system/SystemLayerImplIoTSocket.h @@ -0,0 +1,124 @@ +/* + * + * Copyright (c) 2022 Project CHIP Authors + * + * 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 declares an implementation of System::Layer using select(). + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace System { + +class LayerImplOpenIoTSDK : public LayerOpenIoTSDK +{ +public: + LayerImplOpenIoTSDK(); + ~LayerImplOpenIoTSDK() { VerifyOrDie(mLayerState.Destroy()); } + + // Layer overrides. + CHIP_ERROR Init() override; + void Shutdown() override; + bool IsInitialized() const override { return mLayerState.IsInitialized(); } + CHIP_ERROR StartTimer(Clock::Timeout delay, TimerCompleteCallback onComplete, void * appState) override; + void CancelTimer(TimerCompleteCallback onComplete, void * appState) override; + CHIP_ERROR ScheduleWork(TimerCompleteCallback onComplete, void * appState) override; + +public: + // Platform implementation. + CHIP_ERROR HandlePlatformTimer(void); + +private: + friend class PlatformEventing; + + CHIP_ERROR StartPlatformTimer(System::Clock::Timeout aDelay); + + TimerPool mTimerPool; + TimerList mTimerList; + bool mHandlingTimerComplete; // true while handling any timer completion + ObjectLifeCycle mLayerState; + + /* socket */ +public: + CHIP_ERROR EnableSelectCallback(chip::Inet::EndPointStateIoTSocket * endpoint, bool read, bool write) override; + CHIP_ERROR DisableSelectCallback(chip::Inet::EndPointStateIoTSocket * endpoint, bool read, bool write) override; + + void Signal() override; + CHIP_ERROR WaitForEvents() override; + void HandleEvents() override; + + typedef void * SelectMask; + +private: + enum SelectMaskType + { + ReadMask = 0, // used to store the user requested set + WriteMask, // used to store the user requested set + ExceptionMask, // used to store the user requested set + ReadMaskOut, // contains the result of select + WriteMaskOut, // contains the result of select + ExceptionMaskOut, // contains the result of select + SelectMaskTypeMax + }; + + CHIP_ERROR EnableSignalSocket(); + + /* Return the individual mask from the memory block storing all masks */ + SelectMask GetSelectMask(SelectMaskType mask) { return (SelectMask *) ((char *) mMaskMemory + mMaskSize * (size_t) mask); }; + + /* Return true if any of the bits are set in the any of the result masks */ + bool IsResultSelectMaskSet() + { + uint32_t * current = (uint32_t *) GetSelectMask(ReadMaskOut); + const uint32_t * end = (uint32_t *) GetSelectMask(SelectMaskTypeMax); + while (current < end) + { + if (*current) + { + return true; + } + current++; + } + return false; + }; + + static constexpr int kSocketMax = (INET_CONFIG_ENABLE_TCP_ENDPOINT ? INET_CONFIG_NUM_TCP_ENDPOINTS : 0) + + (INET_CONFIG_ENABLE_UDP_ENDPOINT ? INET_CONFIG_NUM_UDP_ENDPOINTS : 0); + + chip::Inet::EndPointStateIoTSocket * mSelectEndpoints[kSocketMax]; + static SelectMask mMaskMemory; + size_t mSelectEndpointsNumber = 0; + + static std::atomic mSignalSocket; + static Mutex mSelectMutex; + static size_t mMaskSize; + Mutex mSignalMutex; + osEventFlagsId_t mSignalFlags = nullptr; +}; + +using LayerImpl = LayerImplOpenIoTSDK; + +} // namespace System +} // namespace chip diff --git a/src/system/SystemMutex.cpp b/src/system/SystemMutex.cpp index ca6283ccd000aa..04451f322abe97 100644 --- a/src/system/SystemMutex.cpp +++ b/src/system/SystemMutex.cpp @@ -113,7 +113,9 @@ DLL_EXPORT void Mutex::Lock(void) #if CHIP_SYSTEM_CONFIG_CMSIS_RTOS_LOCKING DLL_EXPORT CHIP_ERROR Mutex::Init(Mutex & aThis) { - aThis.mCmsisRTOSMutex = osMutexNew(NULL); + osMutexAttr_t mut_att = { .attr_bits = osMutexRecursive }; + + aThis.mCmsisRTOSMutex = osMutexNew(&mut_att); if (aThis.mCmsisRTOSMutex == NULL) { ChipLogError(chipSystemLayer, "osMutexNew failed"); diff --git a/src/system/system.gni b/src/system/system.gni index e91dd0dee460d3..c3f1ce5b923a48 100644 --- a/src/system/system.gni +++ b/src/system/system.gni @@ -34,6 +34,9 @@ declare_args() { # Use OpenThread TCP/UDP stack directly chip_system_config_use_open_thread_inet_endpoints = false + + # Use IoT Socket API. + chip_system_config_use_iot_socket = false } declare_args() { @@ -44,8 +47,10 @@ declare_args() { declare_args() { # Event loop type. - if (chip_system_config_use_lwip || - chip_system_config_use_open_thread_inet_endpoints) { + if (chip_system_config_use_iot_socket) { + chip_system_config_event_loop = "IoTSocket" + } else if (chip_system_config_use_lwip || + chip_system_config_use_open_thread_inet_endpoints) { chip_system_config_event_loop = "FreeRTOS" } else { chip_system_config_event_loop = "Select" diff --git a/src/system/tests/TestSystemTimer.cpp b/src/system/tests/TestSystemTimer.cpp index bc5d3bc6206870..111761c5c21ef9 100644 --- a/src/system/tests/TestSystemTimer.cpp +++ b/src/system/tests/TestSystemTimer.cpp @@ -76,7 +76,7 @@ class LayerEvents class LayerEvents::value>::type> @@ -93,7 +93,26 @@ class LayerEvents +class LayerEvents::value>::type> +{ +public: + static bool HasServiceEvents() { return true; } + static void ServiceEvents(Layer & aLayer) + { + LayerImplOpenIoTSDK & layer = static_cast(aLayer); + if (layer.IsInitialized()) + { + layer.HandlePlatformTimer(); + } + } +}; + +#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS || CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK // Test input vector format. static const uint32_t MAX_NUM_TIMERS = 1000;