From 163521713d0c18baf70a79c6bbcb2785d921b569 Mon Sep 17 00:00:00 2001 From: Kevin Schoedel <67607049+kpschoedel@users.noreply.github.com> Date: Wed, 8 Dec 2021 03:24:49 -0500 Subject: [PATCH] Inet: Split EndPoint into per-implementation files (#12688) #### Problem Part of #7715 _Virtualize System and Inet interfaces_ #### Change overview Move each `UDPEndPoint` and `TCPEndPoint` implementation into a separate file. (This follows the pattern used by `System::Layer`.) #### Testing CI; no change to functionality. --- src/inet/BUILD.gn | 11 + src/inet/TCPEndPoint.cpp | 2284 ----------------- src/inet/TCPEndPoint.h | 141 - src/inet/TCPEndPointImpl.h | 40 + src/inet/TCPEndPointImplLwIP.cpp | 1125 ++++++++ src/inet/TCPEndPointImplLwIP.h | 101 + src/inet/TCPEndPointImplSockets.cpp | 1223 +++++++++ src/inet/TCPEndPointImplSockets.h | 96 + src/inet/UDPEndPoint.cpp | 1938 +------------- src/inet/UDPEndPoint.h | 185 +- src/inet/UDPEndPointImpl.h | 40 + src/inet/UDPEndPointImplLwIP.cpp | 613 +++++ src/inet/UDPEndPointImplLwIP.h | 92 + src/inet/UDPEndPointImplNetworkFramework.cpp | 533 ++++ src/inet/UDPEndPointImplNetworkFramework.h | 77 + src/inet/UDPEndPointImplSockets.cpp | 819 ++++++ src/inet/UDPEndPointImplSockets.h | 87 + src/inet/tests/TestInetCommon.h | 4 +- src/platform/Globals.cpp | 2 + .../Zephyr/ThreadStackManagerImpl.cpp | 2 +- 20 files changed, 4876 insertions(+), 4537 deletions(-) create mode 100644 src/inet/TCPEndPointImpl.h create mode 100644 src/inet/TCPEndPointImplLwIP.cpp create mode 100644 src/inet/TCPEndPointImplLwIP.h create mode 100644 src/inet/TCPEndPointImplSockets.cpp create mode 100644 src/inet/TCPEndPointImplSockets.h create mode 100644 src/inet/UDPEndPointImpl.h create mode 100644 src/inet/UDPEndPointImplLwIP.cpp create mode 100644 src/inet/UDPEndPointImplLwIP.h create mode 100644 src/inet/UDPEndPointImplNetworkFramework.cpp create mode 100644 src/inet/UDPEndPointImplNetworkFramework.h create mode 100644 src/inet/UDPEndPointImplSockets.cpp create mode 100644 src/inet/UDPEndPointImplSockets.h diff --git a/src/inet/BUILD.gn b/src/inet/BUILD.gn index 44f33bbe834643..7dfe6ac5441ac6 100644 --- a/src/inet/BUILD.gn +++ b/src/inet/BUILD.gn @@ -49,6 +49,11 @@ buildconfig_header("inet_buildconfig") { defines += [ "INET_PLATFORM_CONFIG_INCLUDE=${chip_inet_platform_config_include}" ] } + + defines += [ + "INET_TCP_END_POINT_IMPL_CONFIG_FILE=", + "INET_UDP_END_POINT_IMPL_CONFIG_FILE=", + ] } source_set("inet_config_header") { @@ -105,6 +110,9 @@ static_library("inet") { sources += [ "TCPEndPoint.cpp", "TCPEndPoint.h", + "TCPEndPointImpl${chip_system_config_inet}.cpp", + "TCPEndPointImpl${chip_system_config_inet}.h", + "TCPEndPointImpl.h", ] } @@ -112,6 +120,9 @@ static_library("inet") { sources += [ "UDPEndPoint.cpp", "UDPEndPoint.h", + "UDPEndPointImpl${chip_system_config_inet}.cpp", + "UDPEndPointImpl${chip_system_config_inet}.h", + "UDPEndPointImpl.h", ] } diff --git a/src/inet/TCPEndPoint.cpp b/src/inet/TCPEndPoint.cpp index d5f2428a66c33b..b60a735769b546 100644 --- a/src/inet/TCPEndPoint.cpp +++ b/src/inet/TCPEndPoint.cpp @@ -26,12 +26,6 @@ * */ -#define __APPLE_USE_RFC_3542 - -#ifndef __STDC_LIMIT_MACROS -#define __STDC_LIMIT_MACROS -#endif - #include #include @@ -46,2287 +40,9 @@ #include #include -#if CHIP_SYSTEM_CONFIG_USE_LWIP -#include -#include -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include -#include -#include -#include -#include -#include -#include -#include - -// SOCK_CLOEXEC not defined on all platforms, e.g. iOS/macOS: -#ifndef SOCK_CLOEXEC -#define SOCK_CLOEXEC 0 -#endif - -#if defined(SOL_TCP) -// socket option level for Linux and BSD systems. -#define TCP_SOCKOPT_LEVEL SOL_TCP -#else -// socket option level for macOS & iOS systems. -#define TCP_SOCKOPT_LEVEL IPPROTO_TCP -#endif - -#if defined(TCP_KEEPIDLE) -// socket option for Linux and BSD systems. -#define TCP_IDLE_INTERVAL_OPT_NAME TCP_KEEPIDLE -#else -// socket option for macOS & iOS systems. -#define TCP_IDLE_INTERVAL_OPT_NAME TCP_KEEPALIVE -#endif -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - namespace chip { namespace Inet { -#if CHIP_SYSTEM_CONFIG_USE_LWIP - -namespace { - -/* - * This logic to register a null operation callback with the LwIP TCP/IP task - * ensures that the TCP timer loop is started when a connection is established, - * which is necessary to ensure that initial SYN and SYN-ACK packets are - * retransmitted during the 3-way handshake. - */ - -void nil_tcpip_callback(void * _aContext) {} - -err_t start_tcp_timers(void) -{ - return tcpip_callback(nil_tcpip_callback, NULL); -} - -} // anonymous namespace - -CHIP_ERROR TCPEndPointImplLwIP::BindImpl(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) -{ - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - // Get the appropriate type of PCB. - CHIP_ERROR res = GetPCB(addrType); - - // Bind the PCB to the specified address/port. - if (res == CHIP_NO_ERROR) - { - if (reuseAddr) - { - ip_set_option(mTCP, SOF_REUSEADDR); - } - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - - ip_addr_t ipAddr; - if (addr != IPAddress::Any) - { - ipAddr = addr.ToLwIPAddr(); - } - else if (addrType == IPAddressType::kIPv6) - { - ipAddr = ip6_addr_any; - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - { - ipAddr = ip_addr_any; - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - res = INET_ERROR_WRONG_ADDRESS_TYPE; - res = chip::System::MapErrorLwIP(tcp_bind(mTCP, &ipAddr, port)); - -#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - - if (addrType == IPAddressType::kIPv6) - { - ip6_addr_t ipv6Addr = addr.ToIPv6(); - res = chip::System::MapErrorLwIP(tcp_bind_ip6(mTCP, &ipv6Addr, port)); - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - { - ip_addr_t ipv4Addr = addr.ToIPv4(); - res = chip::System::MapErrorLwIP(tcp_bind(mTCP, &ipv4Addr, port)); - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - res = INET_ERROR_WRONG_ADDRESS_TYPE; - -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::ListenImpl(uint16_t backlog) -{ - // Start listening for incoming connections. - mTCP = tcp_listen(mTCP); - mLwIPEndPointType = LwIPEndPointType::TCP; - - tcp_arg(mTCP, this); - - tcp_accept(mTCP, LwIPHandleIncomingConnection); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplLwIP::ConnectImpl(const IPAddress & addr, uint16_t port, InterfaceId intfId) -{ - CHIP_ERROR res = CHIP_NO_ERROR; - IPAddressType addrType = addr.Type(); - - // LwIP does not provides an API for initiating a TCP connection via a specific interface. - // As a work-around, if the destination is an IPv6 link-local address, we bind the PCB - // to the link local address associated with the source interface; however this is only - // viable if the endpoint hasn't already been bound. - if (intfId.IsPresent()) - { - IPAddress intfLLAddr; - - if (!addr.IsIPv6LinkLocal() || mState == State::kBound) - return CHIP_ERROR_NOT_IMPLEMENTED; - - res = intfId.GetLinkLocalAddr(&intfLLAddr); - if (res != CHIP_NO_ERROR) - return res; - - res = Bind(IPAddressType::kIPv6, intfLLAddr, 0, true); - if (res != CHIP_NO_ERROR) - return res; - } - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - res = GetPCB(addrType); - - if (res == CHIP_NO_ERROR) - { - tcp_arg(mTCP, this); - tcp_err(mTCP, LwIPHandleError); - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - ip_addr_t lwipAddr = addr.ToLwIPAddr(); - res = chip::System::MapErrorLwIP(tcp_connect(mTCP, &lwipAddr, port, LwIPHandleConnectComplete)); -#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - if (addrType == IPAddressType::kIPv6) - { - ip6_addr_t lwipAddr = addr.ToIPv6(); - res = chip::System::MapErrorLwIP(tcp_connect_ip6(mTCP, &lwipAddr, port, LwIPHandleConnectComplete)); - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - { - ip_addr_t lwipAddr = addr.ToIPv4(); - res = chip::System::MapErrorLwIP(tcp_connect(mTCP, &lwipAddr, port, LwIPHandleConnectComplete)); - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - res = INET_ERROR_WRONG_ADDRESS_TYPE; -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - - // Ensure that TCP timers are started - if (res == CHIP_NO_ERROR) - { - err_t error = start_tcp_timers(); - if (error != ERR_OK) - { - res = chip::System::MapErrorLwIP(error); - } - } - - if (res == CHIP_NO_ERROR) - { - mState = State::kConnecting; - Retain(); - } - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) const -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - CHIP_ERROR res = CHIP_ERROR_CONNECTION_ABORTED; - if (mTCP != nullptr) - { - *retPort = mTCP->remote_port; - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - *retAddr = IPAddress(mTCP->remote_ip); -#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 -#if INET_CONFIG_ENABLE_IPV4 - *retAddr = PCB_ISIPV6(mTCP) ? IPAddress(mTCP->remote_ip.ip6) : IPAddress(mTCP->remote_ip.ip4); -#else // !INET_CONFIG_ENABLE_IPV4 - *retAddr = IPAddress(mTCP->remote_ip.ip6); -#endif // !INET_CONFIG_ENABLE_IPV4 -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - res = CHIP_NO_ERROR; - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) const -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - CHIP_ERROR res = CHIP_ERROR_CONNECTION_ABORTED; - if (mTCP != nullptr) - { - *retPort = mTCP->local_port; - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - *retAddr = IPAddress(mTCP->local_ip); -#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 -#if INET_CONFIG_ENABLE_IPV4 - *retAddr = PCB_ISIPV6(mTCP) ? IPAddress(mTCP->local_ip.ip6) : IPAddress(mTCP->local_ip.ip4); -#else // !INET_CONFIG_ENABLE_IPV4 - *retAddr = IPAddress(mTCP->local_ip.ip6); -#endif // !INET_CONFIG_ENABLE_IPV4 -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - res = CHIP_NO_ERROR; - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::GetInterfaceId(InterfaceId * retInterface) -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // TODO: Does netif_get_by_index(mTCP->netif_idx) do the right thing? I - // can't quite tell whether LwIP supports a specific interface id for TCP at - // all. For now just claim no particular interface id. - *retInterface = InterfaceId::Null(); - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplLwIP::SendQueuedImpl(bool queueWasEmpty) -{ -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - if (!mUserTimeoutTimerRunning) - { - // Timer was not running before this send. So, start - // the timer. - StartTCPUserTimeoutTimer(); - } -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplLwIP::EnableNoDelay() -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - CHIP_ERROR res = CHIP_ERROR_CONNECTION_ABORTED; - if (mTCP != nullptr) - { - tcp_nagle_disable(mTCP); - res = CHIP_NO_ERROR; - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount) -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - CHIP_ERROR res = CHIP_ERROR_NOT_IMPLEMENTED; - -#if LWIP_TCP_KEEPALIVE - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - if (mTCP != NULL) - { - // Set the idle interval - mTCP->keep_idle = (uint32_t) interval * 1000; - - // Set the probe retransmission interval. - mTCP->keep_intvl = (uint32_t) interval * 1000; - - // Set the probe timeout count - mTCP->keep_cnt = timeoutCount; - - // Enable keepalives for the connection. - ip_set_option(mTCP, SOF_KEEPALIVE); - res = CHIP_NO_ERROR; - } - else - { - res = CHIP_ERROR_CONNECTION_ABORTED; - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - -#endif // LWIP_TCP_KEEPALIVE - - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::DisableKeepAlive() -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - CHIP_ERROR res = CHIP_ERROR_NOT_IMPLEMENTED; - -#if LWIP_TCP_KEEPALIVE - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - if (mTCP != NULL) - { - // Disable keepalives on the connection. - ip_reset_option(mTCP, SOF_KEEPALIVE); - res = CHIP_NO_ERROR; - } - else - { - res = CHIP_ERROR_CONNECTION_ABORTED; - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - -#endif // LWIP_TCP_KEEPALIVE - - return res; -} - -CHIP_ERROR TCPEndPointImplLwIP::SetUserTimeoutImpl(uint32_t userTimeoutMillis) -{ - return CHIP_ERROR_NOT_IMPLEMENTED; -} - -CHIP_ERROR TCPEndPointImplLwIP::DriveSendingImpl() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - // If the connection hasn't been aborted ... - if (mTCP != NULL) - { - err_t lwipErr; - - // Determine the current send window size. This is the maximum amount we can write to the connection. - uint16_t sendWindowSize = tcp_sndbuf(mTCP); - - // If there's data to be sent and the send window is open... - bool canSend = (RemainingToSend() > 0 && sendWindowSize > 0); - if (canSend) - { - // Find first packet buffer with remaining data to send by skipping - // all sent but un-acked data. - TCPEndPointImplLwIP::BufferOffset startOfUnsent = FindStartOfUnsent(); - - // While there's data to be sent and a window to send it in... - do - { - VerifyOrDie(!startOfUnsent.buffer.IsNull()); - - uint16_t bufDataLen = startOfUnsent.buffer->DataLength(); - - // Get a pointer to the start of unsent data within the first buffer on the unsent queue. - const uint8_t * sendData = startOfUnsent.buffer->Start() + startOfUnsent.offset; - - // Determine the amount of data to send from the current buffer. - uint16_t sendLen = static_cast(bufDataLen - startOfUnsent.offset); - if (sendLen > sendWindowSize) - sendLen = sendWindowSize; - - // Call LwIP to queue the data to be sent, telling it if there's more data to come. - // Data is queued in-place as a reference within the source packet buffer. It is - // critical that the underlying packet buffer not be freed until the data - // is acknowledged, otherwise retransmissions could use an invalid - // backing. Using TCP_WRITE_FLAG_COPY would eliminate this requirement, but overall - // requires many more memory allocations which may be problematic when very - // memory-constrained or when using pool-based allocations. - lwipErr = tcp_write(mTCP, sendData, sendLen, (canSend) ? TCP_WRITE_FLAG_MORE : 0); - if (lwipErr != ERR_OK) - { - err = chip::System::MapErrorLwIP(lwipErr); - break; - } - // Start accounting for the data sent as yet-to-be-acked. - // This cast is safe, because mUnackedLength + sendLen <= bufDataLen, which fits in uint16_t. - mUnackedLength = static_cast(mUnackedLength + sendLen); - - // Adjust the unsent data offset by the length of data that was written. - // If the entire buffer has been sent advance to the next one. - // This cast is safe, because startOfUnsent.offset + sendLen <= bufDataLen, which fits in uint16_t. - startOfUnsent.offset = static_cast(startOfUnsent.offset + sendLen); - if (startOfUnsent.offset == bufDataLen) - { - startOfUnsent.buffer.Advance(); - startOfUnsent.offset = 0; - } - - // Adjust the remaining window size. - sendWindowSize = static_cast(sendWindowSize - sendLen); - - // Determine if there's more data to be sent after this buffer. - canSend = (RemainingToSend() > 0 && sendWindowSize > 0); - } while (canSend); - - // Call LwIP to send the queued data. - INET_FAULT_INJECT(FaultInjection::kFault_Send, err = chip::System::MapErrorLwIP(ERR_RTE)); - - if (err == CHIP_NO_ERROR) - { - lwipErr = tcp_output(mTCP); - - if (lwipErr != ERR_OK) - err = chip::System::MapErrorLwIP(lwipErr); - } - } - - if (err == CHIP_NO_ERROR) - { - // If in the SendShutdown state and the unsent queue is now empty, shutdown the PCB for sending. - if (mState == State::kSendShutdown && (RemainingToSend() == 0)) - { - lwipErr = tcp_shutdown(mTCP, 0, 1); - if (lwipErr != ERR_OK) - err = chip::System::MapErrorLwIP(lwipErr); - } - } - } - - else - err = CHIP_ERROR_CONNECTION_ABORTED; - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return err; -} - -void TCPEndPointImplLwIP::HandleConnectCompleteImpl() {} - -void TCPEndPointImplLwIP::DoCloseImpl(CHIP_ERROR err, State oldState) -{ - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - // If the LwIP PCB hasn't been closed yet... - if (mTCP != NULL) - { - // If the endpoint was a connection endpoint (vs. a listening endpoint)... - if (oldState != State::kListening) - { - // Prevent further callbacks for incoming data. This has the effect of instructing - // LwIP to discard any further data received from the peer. - tcp_recv(mTCP, NULL); - - // If entering the Closed state... - if (mState == State::kClosed) - { - // Prevent further callbacks to the error handler. - // - // Note: It is important to understand that LwIP can continue to make callbacks after - // a PCB has been closed via the tcp_close() API. In particular, LwIP will continue - // to call the 'data sent' callback to signal the acknowledgment of data that was - // sent, but not acknowledged, prior to the close call. Additionally, LwIP will call - // the error callback if the peer fails to respond in a timely manner to the either - // sent data or the FIN. Unfortunately, there is no callback in the case where the - // connection closes successfully. Because of this, it is impossible know definitively - // when LwIP will no longer make callbacks to its user. Thus we must block further - // callbacks to prevent them from happening after the endpoint has been freed. - // - tcp_err(mTCP, NULL); - - // If the endpoint is being closed without error, THEN call tcp_close() to close the underlying - // TCP connection gracefully, preserving any in-transit send data. - if (err == CHIP_NO_ERROR) - { - tcp_close(mTCP); - } - - // OTHERWISE, call tcp_abort() to abort the TCP connection, discarding any in-transit data. - else - { - tcp_abort(mTCP); - } - - // Discard the reference to the PCB to ensure there is no further interaction with it - // after this point. - mTCP = NULL; - mLwIPEndPointType = LwIPEndPointType::Unknown; - } - } - - // OTHERWISE the endpoint was being used for listening, so simply close it. - else - { - tcp_close(mTCP); - - // Discard the reference to the PCB to ensure there is no further interaction with it - // after this point. - mTCP = NULL; - mLwIPEndPointType = LwIPEndPointType::Unknown; - } - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - if (mState == State::kClosed) - { - mUnackedLength = 0; - } -} - -CHIP_ERROR TCPEndPointImplLwIP::AckReceive(uint16_t len) -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - CHIP_ERROR res = CHIP_NO_ERROR; - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - if (mTCP != nullptr) - tcp_recved(mTCP, len); - else - res = CHIP_ERROR_CONNECTION_ABORTED; - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return res; -} - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT -void TCPEndPointImplLwIP::TCPUserTimeoutHandler() -{ - // Set the timer running flag to false - mUserTimeoutTimerRunning = false; - - // Close Connection as we have timed out and there is still - // data not sent out successfully. - DoClose(INET_ERROR_TCP_USER_TIMEOUT, false); -} -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - -uint16_t TCPEndPointImplLwIP::RemainingToSend() -{ - if (mSendQueue.IsNull()) - { - return 0; - } - else - { - // We can never have reported more unacked data than there is pending - // in the send queue! This would indicate a critical accounting bug. - VerifyOrDie(mUnackedLength <= mSendQueue->TotalLength()); - - return static_cast(mSendQueue->TotalLength() - mUnackedLength); - } -} - -TCPEndPointImplLwIP::BufferOffset TCPEndPointImplLwIP::FindStartOfUnsent() -{ - // Find first packet buffer with remaining data to send by skipping - // all sent but un-acked data. This is necessary because of the Consume() - // call in HandleDataSent(), which potentially releases backing memory for - // fully-sent packet buffers, causing an invalidation of all possible - // offsets one might have cached. The TCP acnowledgements may come back - // with a variety of sizes depending on prior activity, and size of the - // send window. The only way to ensure we get the correct offsets into - // unsent data while retaining the buffers that have un-acked data is to - // traverse all sent-but-unacked data in the chain to reach the beginning - // of ready-to-send data. - TCPEndPointImplLwIP::BufferOffset startOfUnsent(mSendQueue.Retain()); - uint16_t leftToSkip = mUnackedLength; - - VerifyOrDie(leftToSkip < mSendQueue->TotalLength()); - - while (leftToSkip > 0) - { - VerifyOrDie(!startOfUnsent.buffer.IsNull()); - uint16_t bufDataLen = startOfUnsent.buffer->DataLength(); - if (leftToSkip >= bufDataLen) - { - // We have more to skip than current packet buffer size. - // Follow the chain to continue. - startOfUnsent.buffer.Advance(); - leftToSkip = static_cast(leftToSkip - bufDataLen); - } - else - { - // Done skipping all data, currentUnsentBuf is first packet buffer - // containing unsent data. - startOfUnsent.offset = leftToSkip; - leftToSkip = 0; - } - } - - return startOfUnsent; -} - -CHIP_ERROR TCPEndPointImplLwIP::GetPCB(IPAddressType addrType) -{ - // IMMPORTANT: This method MUST be called with the LwIP stack LOCKED! - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - if (mTCP == NULL) - { - switch (addrType) - { - case IPAddressType::kIPv6: - mTCP = tcp_new_ip_type(IPADDR_TYPE_V6); - break; - -#if INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv4: - mTCP = tcp_new_ip_type(IPADDR_TYPE_V4); - break; -#endif // INET_CONFIG_ENABLE_IPV4 - - default: - return INET_ERROR_WRONG_ADDRESS_TYPE; - } - - if (mTCP == NULL) - { - return CHIP_ERROR_NO_MEMORY; - } - else - { - mLwIPEndPointType = LwIPEndPointType::TCP; - } - } - else - { - switch (IP_GET_TYPE(&mTCP->local_ip)) - { - case IPADDR_TYPE_V6: - if (addrType != IPAddressType::kIPv6) - return INET_ERROR_WRONG_ADDRESS_TYPE; - break; - -#if INET_CONFIG_ENABLE_IPV4 - case IPADDR_TYPE_V4: - if (addrType != IPAddressType::kIPv4) - return INET_ERROR_WRONG_ADDRESS_TYPE; - break; -#endif // INET_CONFIG_ENABLE_IPV4 - - default: - break; - } - } -#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - if (mTCP == NULL) - { - if (addrType == IPAddressType::kIPv6) - mTCP = tcp_new_ip6(); -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - mTCP = tcp_new(); -#endif // INET_CONFIG_ENABLE_IPV4 - else - return INET_ERROR_WRONG_ADDRESS_TYPE; - if (mTCP == NULL) - { - return CHIP_ERROR_NO_MEMORY; - } - else - { - mLwIPEndPointType = LwIPEndPointType::TCP; - } - } - else - { -#if INET_CONFIG_ENABLE_IPV4 - const IPAddressType pcbType = PCB_ISIPV6(mTCP) ? IPAddressType::kIPv6 : IPAddressType::kIPv4; -#else // !INET_CONFIG_ENABLE_IPV4 - const IPAddressType pcbType = IPAddressType::kIPv6; -#endif // !INET_CONFIG_ENABLE_IPV4 - if (addrType != pcbType) - return INET_ERROR_WRONG_ADDRESS_TYPE; - } -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - - return CHIP_NO_ERROR; -} - -void TCPEndPointImplLwIP::HandleDataSent(uint16_t lenSent) -{ - if (IsConnected()) - { - // Ensure we do not have internal inconsistency in the lwIP, which - // could cause invalid pointer accesses. - if (lenSent > mUnackedLength) - { - ChipLogError(Inet, "Got more ACKed bytes (%d) than were pending (%d)", (int) lenSent, (int) mUnackedLength); - DoClose(CHIP_ERROR_UNEXPECTED_EVENT, false); - return; - } - else if (mSendQueue.IsNull()) - { - ChipLogError(Inet, "Got ACK for %d bytes but data backing gone", (int) lenSent); - DoClose(CHIP_ERROR_UNEXPECTED_EVENT, false); - return; - } - - // Consume data off the head of the send queue equal to the amount of data being acknowledged. - mSendQueue.Consume(lenSent); - mUnackedLength = static_cast(mUnackedLength - lenSent); - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - // Only change the UserTimeout timer if lenSent > 0, - // indicating progress being made in sending data - // across. - if (lenSent > 0) - { - if (RemainingToSend() == 0) - { - // If the output queue has been flushed then stop the timer. - - StopTCPUserTimeoutTimer(); - -#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - // Notify up if all outstanding data has been acknowledged - - SetTCPSendIdleAndNotifyChange(true); -#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - } - else - { - // Progress is being made. So, shift the timer - // forward if it was started. - RestartTCPUserTimeoutTimer(); - } - } -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - - // Mark the connection as being active. - MarkActive(); - - // If requested, call the app's OnDataSent callback. - if (OnDataSent != NULL) - OnDataSent(this, lenSent); - - // If unsent data exists, attempt to send it now... - if (RemainingToSend() > 0) - DriveSending(); - - // If in the closing state and the send queue is now empty, attempt to transition to closed. - if ((mState == State::kClosing) && (RemainingToSend() == 0)) - DoClose(CHIP_NO_ERROR, false); - } -} - -void TCPEndPointImplLwIP::HandleDataReceived(System::PacketBufferHandle && buf) -{ - // Only receive new data while in the Connected or SendShutdown states. - if (mState == State::kConnected || mState == State::kSendShutdown) - { - // Mark the connection as being active. - MarkActive(); - - // If we received a data buffer, queue it on the receive queue. If there's already data in - // the queue, compact the data into the head buffer. - if (!buf.IsNull()) - { - if (mRcvQueue.IsNull()) - { - mRcvQueue = std::move(buf); - } - else - { - mRcvQueue->AddToEnd(std::move(buf)); - mRcvQueue->CompactHead(); - } - } - - // Otherwise buf == NULL means the other side closed the connection, so ... - else - { - - // 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 != NULL) - mState = State::kReceiveShutdown; - else - mState = State::kClosing; - - // Call the app's OnPeerClose. - if (OnPeerClose != NULL) - OnPeerClose(this); - } - - // Drive the received data into the app. - DriveReceiving(); - } -} - -void TCPEndPointImplLwIP::HandleIncomingConnection(TCPEndPoint * conEP) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - IPAddress peerAddr; - uint16_t peerPort; - - if (mState == State::kListening) - { - // If there's no callback available, fail with an error. - if (OnConnectionReceived == NULL) - err = CHIP_ERROR_NO_CONNECTION_HANDLER; - - // Extract the peer's address information. - if (err == CHIP_NO_ERROR) - err = conEP->GetPeerInfo(&peerAddr, &peerPort); - - // If successful, call the app's callback function. - if (err == CHIP_NO_ERROR) - OnConnectionReceived(this, conEP, peerAddr, peerPort); - - // Otherwise clean up and call the app's error callback. - else if (OnAcceptError != NULL) - OnAcceptError(this, err); - } - else - err = CHIP_ERROR_INCORRECT_STATE; - - // If something failed above, abort and free the connection end point. - if (err != CHIP_NO_ERROR) - conEP->Free(); -} - -void TCPEndPointImplLwIP::HandleError(CHIP_ERROR err) -{ - if (mState == State::kListening) - { - if (OnAcceptError != NULL) - OnAcceptError(this, err); - } - else - DoClose(err, false); -} - -err_t TCPEndPointImplLwIP::LwIPHandleConnectComplete(void * arg, struct tcp_pcb * tpcb, err_t lwipErr) -{ - err_t res = ERR_OK; - - if (arg != NULL) - { - TCPEndPointImplLwIP * ep = static_cast(arg); - - if (lwipErr == ERR_OK) - { - // Setup LwIP callback functions for data transmission. - tcp_recv(ep->mTCP, LwIPHandleDataReceived); - tcp_sent(ep->mTCP, LwIPHandleDataSent); - } - - // Post callback to HandleConnectComplete. - ep->Retain(); - CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, conErr = System::MapErrorLwIP(lwipErr)] { - ep->HandleConnectComplete(conErr); - ep->Release(); - }); - if (err != CHIP_NO_ERROR) - { - ep->Release(); - res = ERR_ABRT; - } - } - else - res = ERR_ABRT; - - if (res != ERR_OK) - tcp_abort(tpcb); - - return res; -} - -err_t TCPEndPointImplLwIP::LwIPHandleIncomingConnection(void * arg, struct tcp_pcb * tpcb, err_t lwipErr) -{ - CHIP_ERROR err = chip::System::MapErrorLwIP(lwipErr); - - if (arg != NULL) - { - TCPEndPointImplLwIP * listenEP = static_cast(arg); - TCPEndPointImplLwIP * conEP = NULL; - System::Layer & lSystemLayer = listenEP->GetSystemLayer(); - - // Tell LwIP we've accepted the connection so it can decrement the listen PCB's pending_accepts counter. - tcp_accepted(listenEP->mTCP); - - // If we did in fact receive a connection, rather than an error, attempt to allocate an end point object. - // - // NOTE: Although most of the LwIP callbacks defer the real work to happen on the endpoint's thread - // (by posting events to the thread's event queue) we can't do that here because as soon as this - // function returns, LwIP is free to begin calling callbacks on the new PCB. For that to work we need - // to have an end point associated with the PCB. - // - if (err == CHIP_NO_ERROR) - { - TCPEndPoint * connectEndPoint = nullptr; - err = listenEP->GetEndPointManager().NewEndPoint(&connectEndPoint); - conEP = static_cast(connectEndPoint); - } - - // Ensure that TCP timers have been started - if (err == CHIP_NO_ERROR) - { - err_t error = start_tcp_timers(); - if (error != ERR_OK) - { - err = chip::System::MapErrorLwIP(error); - } - } - - // If successful in allocating an end point... - if (err == CHIP_NO_ERROR) - { - // Put the new end point into the Connected state. - conEP->mState = State::kConnected; - conEP->mTCP = tpcb; - conEP->mLwIPEndPointType = LwIPEndPointType::TCP; - conEP->Retain(); - - // Setup LwIP callback functions for the new PCB. - tcp_arg(tpcb, conEP); - tcp_recv(tpcb, LwIPHandleDataReceived); - tcp_sent(tpcb, LwIPHandleDataSent); - tcp_err(tpcb, LwIPHandleError); - - // Post a callback to the HandleConnectionReceived() function, passing it the new end point. - listenEP->Retain(); - conEP->Retain(); - err = lSystemLayer.ScheduleLambda([listenEP, conEP] { - listenEP->HandleIncomingConnection(conEP); - conEP->Release(); - listenEP->Release(); - }); - if (err != CHIP_NO_ERROR) - { - conEP->Release(); // for the Ref in ScheduleLambda - listenEP->Release(); - err = CHIP_ERROR_CONNECTION_ABORTED; - conEP->Release(); // for the Retain() above - conEP->Release(); // for the implied Retain() on construction - } - } - - // Otherwise, there was an error accepting the connection, so post a callback to the HandleError function. - else - { - listenEP->Retain(); - err = lSystemLayer.ScheduleLambda([listenEP, err] { - listenEP->HandleError(err); - listenEP->Release(); - }); - if (err != CHIP_NO_ERROR) - { - listenEP->Release(); - } - } - } - else - err = CHIP_ERROR_CONNECTION_ABORTED; - - if (err != CHIP_NO_ERROR && tpcb != NULL) - { - tcp_abort(tpcb); - return ERR_ABRT; - } - else - { - return ERR_OK; - } -} - -err_t TCPEndPointImplLwIP::LwIPHandleDataReceived(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t _err) -{ - err_t res = ERR_OK; - - if (arg != NULL) - { - TCPEndPointImplLwIP * ep = static_cast(arg); - - // Post callback to HandleDataReceived. - ep->Retain(); - CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, p] { - ep->HandleDataReceived(System::PacketBufferHandle::Adopt(p)); - ep->Release(); - }); - if (err != CHIP_NO_ERROR) - { - ep->Release(); - res = ERR_ABRT; - } - } - else - res = ERR_ABRT; - - if (res != ERR_OK) - { - if (p != nullptr) - { - pbuf_free(p); - } - tcp_abort(tpcb); - } - - return res; -} - -err_t TCPEndPointImplLwIP::LwIPHandleDataSent(void * arg, struct tcp_pcb * tpcb, u16_t len) -{ - err_t res = ERR_OK; - - if (arg != NULL) - { - TCPEndPointImplLwIP * ep = static_cast(arg); - - // Post callback to HandleDataReceived. - ep->Retain(); - CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, len] { - ep->HandleDataSent(len); - ep->Release(); - }); - if (err != CHIP_NO_ERROR) - { - ep->Release(); - res = ERR_ABRT; - } - } - else - res = ERR_ABRT; - - if (res != ERR_OK) - tcp_abort(tpcb); - - return res; -} - -void TCPEndPointImplLwIP::LwIPHandleError(void * arg, err_t lwipErr) -{ - if (arg != NULL) - { - TCPEndPointImplLwIP * ep = static_cast(arg); - System::LayerLwIP & lSystemLayer = static_cast(ep->GetSystemLayer()); - - // At this point LwIP has already freed the PCB. Since the thread that owns the TCPEndPoint may - // try to use the PCB before it receives the TCPError event posted below, we set the PCB to NULL - // as a means to signal the other thread that the connection has been aborted. The implication - // of this is that the mTCP field is shared state between the two threads and thus must only be - // accessed with the LwIP lock held. - ep->mTCP = NULL; - ep->mLwIPEndPointType = LwIPEndPointType::Unknown; - - // Post callback to HandleError. - ep->Retain(); - CHIP_ERROR err = lSystemLayer.ScheduleLambda([ep, conErr = System::MapErrorLwIP(lwipErr)] { - ep->HandleError(conErr); - ep->Release(); - }); - if (err != CHIP_NO_ERROR) - ep->Release(); - } -} - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -CHIP_ERROR TCPEndPointImplSockets::BindImpl(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) -{ - CHIP_ERROR res = GetSocket(addrType); - - if (res == CHIP_NO_ERROR && reuseAddr) - { - int n = 1; - setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); - -#ifdef SO_REUSEPORT - // 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 TARGETTED_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 wih the same degreee of selectivity, - // e.g. two untargetted-listen CHIP clients, or two - // targetted-listen CHIP clients with the same node id. - - if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n)) != 0) - { - ChipLogError(Inet, "SO_REUSEPORT: %d", errno); - } -#endif // defined(SO_REUSEPORT) - } - - if (res == CHIP_NO_ERROR) - { - if (addrType == IPAddressType::kIPv6) - { - struct sockaddr_in6 sa; - memset(&sa, 0, sizeof(sa)); - sa.sin6_family = AF_INET6; - sa.sin6_port = htons(port); - sa.sin6_flowinfo = 0; - sa.sin6_addr = addr.ToIPv6(); - sa.sin6_scope_id = 0; - - if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) - { - res = CHIP_ERROR_POSIX(errno); - } - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - { - struct sockaddr_in sa; - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr = addr.ToIPv4(); - - if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) - { - res = CHIP_ERROR_POSIX(errno); - } - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - { - res = INET_ERROR_WRONG_ADDRESS_TYPE; - } - } - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t dispatchQueue = static_cast(GetSystemLayer()).GetDispatchQueue(); - if (dispatchQueue != nullptr) - { - unsigned long fd = static_cast(mSocket); - - mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); - ReturnErrorCodeIf(mReadableSource == nullptr, CHIP_ERROR_NO_MEMORY); - - mWriteableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, dispatchQueue); - ReturnErrorCodeIf(mWriteableSource == nullptr, CHIP_ERROR_NO_MEMORY); - - dispatch_source_set_event_handler(mReadableSource, ^{ - this->HandlePendingIO(System::SocketEventFlags::kRead); - }); - - dispatch_source_set_event_handler(mWriteableSource, ^{ - this->HandlePendingIO(System::SocketEventFlags::kWrite); - }); - - dispatch_resume(mReadableSource); - dispatch_resume(mWriteableSource); - } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - return res; -} - -CHIP_ERROR TCPEndPointImplSockets::ListenImpl(uint16_t backlog) -{ - if (listen(mSocket, backlog) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - // Enable non-blocking mode for the socket. - int flags = fcntl(mSocket, F_GETFL, 0); - fcntl(mSocket, F_SETFL, flags | O_NONBLOCK); - - // Wait for ability to read on this endpoint. - CHIP_ERROR res = static_cast(GetSystemLayer()) - .SetCallback(mWatch, HandlePendingIO, reinterpret_cast(this)); - if (res == CHIP_NO_ERROR) - { - res = static_cast(GetSystemLayer()).RequestCallbackOnPendingRead(mWatch); - } - - return res; -} - -CHIP_ERROR TCPEndPointImplSockets::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 bind to - // interface or address on the interface. - - if (!addr.IsIPv6LinkLocal()) - { -#ifdef SO_BINDTODEVICE - struct ::ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - - ReturnErrorOnFailure(intfId.GetInterfaceName(ifr.ifr_name, sizeof(ifr.ifr_name))); - - // Attempt to bind 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 bind the connetion to. - int r = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); - if (r < 0 && errno != EACCES) - { - return CHIP_ERROR_POSIX(errno); - } - - if (r < 0) -#endif // SO_BINDTODEVICE - { - // Attempting to initiate a connection via a specific interface is not allowed. - // The only way to do this is to bind the local to an address on the desired - // interface. - ReturnErrorOnFailure(BindSrcAddrFromIntf(addrType, intfId)); - } - } - } - - // Disable generation of SIGPIPE. -#ifdef SO_NOSIGPIPE - int n = 1; - setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &n, sizeof(n)); -#endif // defined(SO_NOSIGPIPE) - - // Enable non-blocking mode for the socket. - int flags = fcntl(mSocket, F_GETFL, 0); - fcntl(mSocket, F_SETFL, flags | O_NONBLOCK); - - socklen_t sockaddrsize = 0; - const sockaddr * sockaddrptr = nullptr; - - SockAddr sa; - memset(&sa, 0, sizeof(sa)); - - if (addrType == IPAddressType::kIPv6) - { - sa.in6.sin6_family = AF_INET6; - sa.in6.sin6_port = htons(port); - sa.in6.sin6_flowinfo = 0; - sa.in6.sin6_addr = addr.ToIPv6(); - sa.in6.sin6_scope_id = intfId.GetPlatformInterface(); - sockaddrsize = sizeof(sockaddr_in6); - sockaddrptr = reinterpret_cast(&sa.in6); - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - { - sa.in.sin_family = AF_INET; - sa.in.sin_port = htons(port); - sa.in.sin_addr = addr.ToIPv4(); - sockaddrsize = sizeof(sockaddr_in); - sockaddrptr = reinterpret_cast(&sa.in); - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - { - return INET_ERROR_WRONG_ADDRESS_TYPE; - } - - int conRes = connect(mSocket, sockaddrptr, sockaddrsize); - - if (conRes == -1 && errno != EINPROGRESS) - { - CHIP_ERROR res = CHIP_ERROR_POSIX(errno); - DoClose(res, true); - return res; - } - - ReturnErrorOnFailure(static_cast(GetSystemLayer()) - .SetCallback(mWatch, HandlePendingIO, reinterpret_cast(this))); - - // 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()).RequestCallbackOnPendingRead(mWatch)); - if (OnConnectComplete != nullptr) - { - OnConnectComplete(this, CHIP_NO_ERROR); - } - } - else - { - mState = State::kConnecting; - // Wait for ability to write on this endpoint. - ReturnErrorOnFailure(static_cast(GetSystemLayer()).RequestCallbackOnPendingWrite(mWatch)); - } - - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) const -{ - return GetSocketInfo(getpeername, retAddr, retPort); -} - -CHIP_ERROR TCPEndPointImplSockets::GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) const -{ - return GetSocketInfo(getsockname, retAddr, retPort); -} - -CHIP_ERROR TCPEndPointImplSockets::GetSocketInfo(int getname(int, sockaddr *, socklen_t *), IPAddress * retAddr, - uint16_t * retPort) const -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - SockAddr sa; - memset(&sa, 0, sizeof(sa)); - socklen_t saLen = sizeof(sa); - - if (getname(mSocket, &sa.any, &saLen) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - if (sa.any.sa_family == AF_INET6) - { - *retAddr = IPAddress(sa.in6.sin6_addr); - *retPort = ntohs(sa.in6.sin6_port); - return CHIP_NO_ERROR; - } - -#if INET_CONFIG_ENABLE_IPV4 - if (sa.any.sa_family == AF_INET) - { - *retAddr = IPAddress(sa.in.sin_addr); - *retPort = ntohs(sa.in.sin_port); - return CHIP_NO_ERROR; - } -#endif // INET_CONFIG_ENABLE_IPV4 - - return CHIP_ERROR_INCORRECT_STATE; -} - -CHIP_ERROR TCPEndPointImplSockets::GetInterfaceId(InterfaceId * retInterface) -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - SockAddr sa; - memset(&sa, 0, sizeof(sa)); - socklen_t saLen = sizeof(sa); - - if (getpeername(mSocket, &sa.any, &saLen) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - if (sa.any.sa_family == AF_INET6) - { - if (IPAddress(sa.in6.sin6_addr).IsIPv6LinkLocal()) - { - *retInterface = InterfaceId(sa.in6.sin6_scope_id); - } - else - { - // TODO: Is there still a meaningful interface id in this case? - *retInterface = InterfaceId::Null(); - } - return CHIP_NO_ERROR; - } - -#if INET_CONFIG_ENABLE_IPV4 - if (sa.any.sa_family == AF_INET) - { - // No interface id available for IPv4 sockets. - *retInterface = InterfaceId::Null(); - return CHIP_NO_ERROR; - } -#endif // INET_CONFIG_ENABLE_IPV4 - - *retInterface = InterfaceId::Null(); - return INET_ERROR_WRONG_ADDRESS_TYPE; -} - -CHIP_ERROR TCPEndPointImplSockets::SendQueuedImpl(bool queueWasEmpty) -{ - if (queueWasEmpty) - { - // Wait for ability to write on this endpoint. - return static_cast(GetSystemLayer()).RequestCallbackOnPendingWrite(mWatch); - } - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::EnableNoDelay() -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - -#ifdef TCP_NODELAY - // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true - int val = 1; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_NODELAY, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } -#endif // defined(TCP_NODELAY) - - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount) -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // Set the idle interval - int val = interval; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_IDLE_INTERVAL_OPT_NAME, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - // Set the probe retransmission interval. - val = interval; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_KEEPINTVL, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - // Set the probe timeout count - val = timeoutCount; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_KEEPCNT, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - // Enable keepalives for the connection. - val = 1; // enable - if (setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::DisableKeepAlive() -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // Disable keepalives on the connection. - int val = 0; // disable - if (setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::AckReceive(uint16_t len) -{ - VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); - - // nothing to do for sockets case - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::SetUserTimeoutImpl(uint32_t userTimeoutMillis) -{ -#if defined(TCP_USER_TIMEOUT) - // Set the user timeout - uint32_t val = userTimeoutMillis; - if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_USER_TIMEOUT, &val, sizeof(val)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - return CHIP_NO_ERROR; -#else // TCP_USER_TIMEOUT - return CHIP_ERROR_NOT_IMPLEMENTED; -#endif // defined(TCP_USER_TIMEOUT) -} - -CHIP_ERROR TCPEndPointImplSockets::DriveSendingImpl() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - -#ifdef MSG_NOSIGNAL - const int sendFlags = MSG_NOSIGNAL; -#else - const int sendFlags = 0; -#endif - - // Pretend send() fails in the while loop below - INET_FAULT_INJECT(FaultInjection::kFault_Send, { - err = CHIP_ERROR_POSIX(EIO); - DoClose(err, false); - return err; - }); - - while (!mSendQueue.IsNull()) - { - uint16_t bufLen = mSendQueue->DataLength(); - - ssize_t lenSentRaw = send(mSocket, mSendQueue->Start(), bufLen, sendFlags); - - if (lenSentRaw == -1) - { - if (errno != EAGAIN && errno != EWOULDBLOCK) - { - err = (errno == EPIPE) ? INET_ERROR_PEER_DISCONNECTED : CHIP_ERROR_POSIX(errno); - } - 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()).ClearCallbackOnPendingWrite(mWatch); - if (err != CHIP_NO_ERROR) - { - break; - } - } - } - - if (OnDataSent != nullptr) - { - OnDataSent(this, lenSent); - } - -#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - // TCP Send is not Idle; Set state and notify if needed - - SetTCPSendIdleAndNotifyChange(false); -#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - mBytesWrittenSinceLastProbe += lenSent; - - bool isProgressing = false; - - err = CheckConnectionProgress(isProgressing); - if (err != CHIP_NO_ERROR) - { - break; - } - - if (!mUserTimeoutTimerRunning) - { - // Timer was not running before this write. So, start - // the timer. - - StartTCPUserTimeoutTimer(); - } - else if (isProgressing) - { - // Progress is being made. So, shift the timer - // forward if it was started. - - RestartTCPUserTimeoutTimer(); - } -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - - 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()) - { - if (shutdown(mSocket, SHUT_WR) != 0) - { - err = CHIP_ERROR_POSIX(errno); - } - } - } - - return err; -} - -void TCPEndPointImplSockets::HandleConnectCompleteImpl() -{ - // Wait for ability to read or write on this endpoint. - CHIP_ERROR err = static_cast(GetSystemLayer()).RequestCallbackOnPendingRead(mWatch); - if (err == CHIP_NO_ERROR) - { - err = static_cast(GetSystemLayer()).RequestCallbackOnPendingWrite(mWatch); - } - if (err != CHIP_NO_ERROR) - { - DoClose(err, false); - return; - } -} - -void TCPEndPointImplSockets::DoCloseImpl(CHIP_ERROR err, State oldState) -{ - struct linger lingerStruct; - - // 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) - { - lingerStruct.l_onoff = 1; - lingerStruct.l_linger = 0; - - if (setsockopt(mSocket, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct)) != 0) - { - ChipLogError(Inet, "SO_LINGER: %d", errno); - } - } - - static_cast(GetSystemLayer()).StopWatchingSocket(&mWatch); - close(mSocket); - mSocket = kInvalidSocketFd; - } - } - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - if (mReadableSource) - { - dispatch_source_cancel(mReadableSource); - dispatch_release(mReadableSource); - } - if (mWriteableSource) - { - dispatch_source_cancel(mWriteableSource); - dispatch_release(mWriteableSource); - } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH -} - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT -void TCPEndPointImplSockets::TCPUserTimeoutHandler() -{ - // Set the timer running flag to false - mUserTimeoutTimerRunning = false; - - CHIP_ERROR err = CHIP_NO_ERROR; - bool isProgressing = false; - err = CheckConnectionProgress(isProgressing); - SuccessOrExit(err); - - if (mLastTCPKernelSendQueueLen == 0) - { -#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - // If the kernel TCP send queue as well as the TCPEndPoint - // send queue have been flushed then notify application - // that all data has been acknowledged. - - if (mSendQueue.IsNull()) - { - SetTCPSendIdleAndNotifyChange(true); - } -#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - } - else - // There is data in the TCP Send Queue - { - if (isProgressing) - { - // Data is flowing, so restart the UserTimeout timer - // to shift it forward while also resetting the max - // poll count. - - StartTCPUserTimeoutTimer(); - } - else - { -#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - // Data flow is not progressing. - // Decrement the remaining max TCP send queue polls. - - mTCPSendQueueRemainingPollCount--; - - VerifyOrExit(mTCPSendQueueRemainingPollCount != 0, err = INET_ERROR_TCP_USER_TIMEOUT); - - // Restart timer to poll again - - ScheduleNextTCPUserTimeoutPoll(mTCPSendQueuePollPeriodMillis); -#else - // Close the connection as the TCP UserTimeout has expired - - ExitNow(err = INET_ERROR_TCP_USER_TIMEOUT); -#endif // !INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - } - } - -exit: - - if (err != CHIP_NO_ERROR) - { - // Close the connection as the TCP UserTimeout has expired - - DoClose(err, false); - } -} -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - -CHIP_ERROR TCPEndPointImplSockets::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()) - { - const IPAddress curAddr = addrIter.GetAddress(); - const InterfaceId curIntfId = addrIter.GetInterfaceId(); - - if (curIntfId == intfId) - { - // 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); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR TCPEndPointImplSockets::GetSocket(IPAddressType addrType) -{ - if (mSocket == kInvalidSocketFd) - { - int family; - if (addrType == IPAddressType::kIPv6) - { - family = PF_INET6; -#if INET_CONFIG_ENABLE_IPV4 - } - else if (addrType == IPAddressType::kIPv4) - { - family = PF_INET; -#endif // INET_CONFIG_ENABLE_IPV4 - } - else - { - return INET_ERROR_WRONG_ADDRESS_TYPE; - } - mSocket = ::socket(family, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (mSocket == -1) - { - return CHIP_ERROR_POSIX(errno); - } - ReturnErrorOnFailure(static_cast(GetSystemLayer()).StartWatchingSocket(mSocket, &mWatch)); - mAddrType = addrType; - - // If creating an IPv6 socket, tell the kernel that it will be IPv6 only. This makes it - // posible to bind two sockets to the same port, one for IPv4 and one for IPv6. -#ifdef IPV6_V6ONLY - if (family == PF_INET6) - { - int one = 1; - setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - } -#endif // defined(IPV6_V6ONLY) - - // On systems that support it, disable the delivery of SIGPIPE signals when writing to a closed - // socket. -#ifdef SO_NOSIGPIPE - { - int one = 1; - int res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - if (res != 0) - { - ChipLogError(Inet, "SO_NOSIGPIPE: %d", errno); - } - } -#endif // defined(SO_NOSIGPIPE) - } - else if (mAddrType != addrType) - { - return CHIP_ERROR_INCORRECT_STATE; - } - - return CHIP_NO_ERROR; -} - -// static -void TCPEndPointImplSockets::HandlePendingIO(System::SocketEvents events, intptr_t data) -{ - reinterpret_cast(data)->HandlePendingIO(events); -} - -void TCPEndPointImplSockets::HandlePendingIO(System::SocketEvents events) -{ - // 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 && events.Has(System::SocketEventFlags::kRead)) - { - 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 (events.Has(System::SocketEventFlags::kWrite)) - { -#if !__MBED__ - // Get the connection result from the socket. - int osConRes; - socklen_t optLen = sizeof(osConRes); - if (getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &osConRes, &optLen) != 0) - { - osConRes = errno; - } -#else - // On Mbed OS, connect blocks and never returns EINPROGRESS - // The socket option SO_ERROR is not available. - int osConRes = 0; -#endif - CHIP_ERROR conRes = CHIP_ERROR_POSIX(osConRes); - - // Process the connection result. - HandleConnectComplete(conRes); - } - } - - 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 (IsConnected() && !mSendQueue.IsNull() && events.Has(System::SocketEventFlags::kWrite)) - { - 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 ((mState == State::kConnected || mState == State::kSendShutdown) && mReceiveEnabled && OnDataReceived != nullptr && - events.Has(System::SocketEventFlags::kRead)) - { - ReceiveData(); - } - } - - Release(); -} - -void TCPEndPointImplSockets::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. - ssize_t rcvLen = recv(mSocket, rcvBuf->Start() + rcvBuf->DataLength(), rcvBuf->AvailableDataLength(), 0); - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - CHIP_ERROR err; - bool isProgressing = false; - - err = CheckConnectionProgress(isProgressing); - if (err != CHIP_NO_ERROR) - { - DoClose(err, false); - - return; - } - - if (mLastTCPKernelSendQueueLen == 0) - { - // If the output queue has been flushed then stop the timer. - - StopTCPUserTimeoutTimer(); - -#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - // Notify up if all outstanding data has been acknowledged - - if (mSendQueue.IsNull()) - { - SetTCPSendIdleAndNotifyChange(true); - } -#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS - } - else if (isProgressing && mUserTimeoutTimerRunning) - { - // Progress is being made. So, shift the timer - // forward if it was started. - RestartTCPUserTimeoutTimer(); - } -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - // If an error occurred, abort the connection. - if (rcvLen < 0) - { - int systemErrno = errno; - if (systemErrno == EAGAIN) - { - // 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(CHIP_ERROR_POSIX(systemErrno), 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()).ClearCallbackOnPendingRead(mWatch); - // 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 TCPEndPointImplSockets::HandleIncomingConnection() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - TCPEndPointImplSockets * conEP = nullptr; - IPAddress peerAddr; - uint16_t peerPort; - - SockAddr sa; - memset(&sa, 0, sizeof(sa)); - socklen_t saLen = sizeof(sa); - - // Accept the new connection. - int conSocket = accept(mSocket, &sa.any, &saLen); - if (conSocket == -1) - { - if (errno == EAGAIN || errno == EWOULDBLOCK) - { - return; - } - else - { - err = CHIP_ERROR_POSIX(errno); - } - } - - // If there's no callback available, fail with an error. - if (err == CHIP_NO_ERROR && OnConnectionReceived == nullptr) - { - err = CHIP_ERROR_NO_CONNECTION_HANDLER; - } - - // Extract the peer's address information. - if (err == CHIP_NO_ERROR) - { - if (sa.any.sa_family == AF_INET6) - { - peerAddr = IPAddress(sa.in6.sin6_addr); - peerPort = ntohs(sa.in6.sin6_port); - } -#if INET_CONFIG_ENABLE_IPV4 - else if (sa.any.sa_family == AF_INET) - { - peerAddr = IPAddress(sa.in.sin_addr); - peerPort = ntohs(sa.in.sin_port); - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - { - err = CHIP_ERROR_INCORRECT_STATE; - } - } - - // 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; - err = static_cast(GetSystemLayer()).StartWatchingSocket(conSocket, &conEP->mWatch); - if (err == CHIP_NO_ERROR) - { - conEP->mState = State::kConnected; -#if INET_CONFIG_ENABLE_IPV4 - conEP->mAddrType = (sa.any.sa_family == AF_INET6) ? IPAddressType::kIPv6 : IPAddressType::kIPv4; -#else // !INET_CONFIG_ENABLE_IPV4 - conEP->mAddrType = IPAddressType::kIPv6; -#endif // !INET_CONFIG_ENABLE_IPV4 - conEP->Retain(); - - // Wait for ability to read on this endpoint. - auto & conEPLayer = static_cast(conEP->GetSystemLayer()); - err = conEPLayer.SetCallback(conEP->mWatch, HandlePendingIO, reinterpret_cast(conEP)); - if (err == CHIP_NO_ERROR) - { - err = conEPLayer.RequestCallbackOnPendingRead(conEP->mWatch); - } - 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) - { - close(conSocket); - } - if (conEP != nullptr) - { - if (conEP->mState == State::kConnected) - { - conEP->Release(); - } - conEP->Release(); - } - if (OnAcceptError != nullptr) - { - OnAcceptError(this, err); - } -} - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT -/** - * This function probes the TCP output queue and checks if data is successfully - * being transferred to the other end. - */ -CHIP_ERROR TCPEndPointImplSockets::CheckConnectionProgress(bool & isProgressing) -{ - int currPendingBytesRaw = 0; - uint32_t currPendingBytes; // Will be initialized once we know it's safe. - - // Fetch the bytes pending successful transmission in the TCP out queue. - - if (ioctl(mSocket, TIOCOUTQ, &currPendingBytesRaw) < 0) - { - return CHIP_ERROR_POSIX(errno); - } - - if (!CanCastTo(currPendingBytesRaw)) - { - return CHIP_ERROR_INCORRECT_STATE; - } - - currPendingBytes = static_cast(currPendingBytesRaw); - - if ((currPendingBytes != 0) && (mBytesWrittenSinceLastProbe + mLastTCPKernelSendQueueLen == currPendingBytes)) - { - // No progress has been made - - isProgressing = false; - } - else - { - // Data is flowing successfully - - isProgressing = true; - } - - // Reset the value of the bytes written since the last probe into the tcp - // outqueue was made and update the last tcp outqueue sample. - - mBytesWrittenSinceLastProbe = 0; - - mLastTCPKernelSendQueueLen = currPendingBytes; - - return CHIP_NO_ERROR; -} -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - CHIP_ERROR TCPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) { VerifyOrReturnError(mState == State::kReady, CHIP_ERROR_INCORRECT_STATE); diff --git a/src/inet/TCPEndPoint.h b/src/inet/TCPEndPoint.h index 31ee56f1521009..503a2bba9a08dc 100644 --- a/src/inet/TCPEndPoint.h +++ b/src/inet/TCPEndPoint.h @@ -36,17 +36,6 @@ #include -#if CHIP_SYSTEM_CONFIG_USE_LWIP -#include -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH -#include -#endif - namespace chip { namespace Transport { @@ -699,136 +688,6 @@ class DLL_EXPORT TCPEndPoint : public EndPointBasis virtual void DoCloseImpl(CHIP_ERROR err, State oldState) = 0; }; -#if CHIP_SYSTEM_CONFIG_USE_LWIP - -class TCPEndPointImplLwIP : public TCPEndPoint, public EndPointStateLwIP -{ -public: - TCPEndPointImplLwIP(EndPointManager & endPointManager) : TCPEndPoint(endPointManager), mUnackedLength(0) {} - - // 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; -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - void TCPUserTimeoutHandler() override; -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - -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; - - struct BufferOffset - { - BufferOffset(System::PacketBufferHandle && aBuffer) : buffer(std::move(aBuffer)), offset(0) {} - BufferOffset(BufferOffset && aOther) - { - buffer = std::move(aOther.buffer); - offset = aOther.offset; - } - chip::System::PacketBufferHandle buffer; - uint16_t offset; - }; - - uint16_t mUnackedLength; // Amount sent but awaiting ACK. Used as a form of reference count - // to hang-on to backing packet buffers until they are no longer needed. - - uint16_t RemainingToSend(); - BufferOffset FindStartOfUnsent(); - CHIP_ERROR GetPCB(IPAddressType addrType); - void HandleDataSent(uint16_t len); - void HandleDataReceived(chip::System::PacketBufferHandle && buf); - void HandleIncomingConnection(TCPEndPoint * pcb); - void HandleError(CHIP_ERROR err); - - static err_t LwIPHandleConnectComplete(void * arg, struct tcp_pcb * tpcb, err_t lwipErr); - static err_t LwIPHandleIncomingConnection(void * arg, struct tcp_pcb * tcpConPCB, err_t lwipErr); - static err_t LwIPHandleDataReceived(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err); - static err_t LwIPHandleDataSent(void * arg, struct tcp_pcb * tpcb, u16_t len); - static void LwIPHandleError(void * arg, err_t err); -}; - -using TCPEndPointImpl = TCPEndPointImplLwIP; - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -class TCPEndPointImplSockets : public TCPEndPoint, public EndPointStateSockets -{ -public: - TCPEndPointImplSockets(EndPointManager & endPointManager) : - TCPEndPoint(endPointManager) -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - , - mBytesWrittenSinceLastProbe(0), mLastTCPKernelSendQueueLen(0) -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - {} - - // 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; -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - void TCPUserTimeoutHandler() override; -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - -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 GetSocketInfo(int getname(int, sockaddr *, socklen_t *), IPAddress * retAddr, uint16_t * retPort) const; - CHIP_ERROR GetSocket(IPAddressType addrType); - void HandlePendingIO(System::SocketEvents events); - void ReceiveData(); - void HandleIncomingConnection(); - CHIP_ERROR BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId intfId); - static void HandlePendingIO(System::SocketEvents events, intptr_t data); - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_source_t mReadableSource = nullptr; - dispatch_source_t mWriteableSource = nullptr; -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - -#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT - /// This counts the number of bytes written on the TCP socket since thelast probe into the TCP outqueue was made. - uint32_t mBytesWrittenSinceLastProbe; - - /// This is the measured size(in bytes) of the kernel TCP send queue at the end of the last user timeout window. - uint32_t mLastTCPKernelSendQueueLen; - - CHIP_ERROR CheckConnectionProgress(bool & IsProgressing); -#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT -}; - -using TCPEndPointImpl = TCPEndPointImplSockets; - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -using TCPEndPointManagerImpl = EndPointManagerImplPool; - template <> struct EndPointProperties { diff --git a/src/inet/TCPEndPointImpl.h b/src/inet/TCPEndPointImpl.h new file mode 100644 index 00000000000000..43c69bd48bca38 --- /dev/null +++ b/src/inet/TCPEndPointImpl.h @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC + * Copyright (c) 2013-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. + */ + +/** + * This file includes a specific configured concrete implementation of Inet::TCPEndPoint. + */ + +#pragma once + +#include + +#ifdef INET_TCP_END_POINT_IMPL_CONFIG_FILE +#include INET_TCP_END_POINT_IMPL_CONFIG_FILE +#else // INET_TCP_END_POINT_IMPL_CONFIG_FILE +#error "INET_TCP_END_POINT_IMPL_CONFIG_FILE not defined" +#endif // INET_TCP_END_POINT_IMPL_CONFIG_FILE + +namespace chip { +namespace Inet { + +using TCPEndPointManagerImpl = EndPointManagerImplPool; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/TCPEndPointImplLwIP.cpp b/src/inet/TCPEndPointImplLwIP.cpp new file mode 100644 index 00000000000000..4be562848e17fb --- /dev/null +++ b/src/inet/TCPEndPointImplLwIP.cpp @@ -0,0 +1,1125 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2013-2018 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. + */ + +/** + * This file implements Inet::TCPEndPoint using LwIP. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace chip { +namespace Inet { + +namespace { + +/* + * This logic to register a null operation callback with the LwIP TCP/IP task + * ensures that the TCP timer loop is started when a connection is established, + * which is necessary to ensure that initial SYN and SYN-ACK packets are + * retransmitted during the 3-way handshake. + */ + +void nil_tcpip_callback(void * _aContext) {} + +err_t start_tcp_timers(void) +{ + return tcpip_callback(nil_tcpip_callback, NULL); +} + +} // anonymous namespace + +CHIP_ERROR TCPEndPointImplLwIP::BindImpl(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) +{ + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // Get the appropriate type of PCB. + CHIP_ERROR res = GetPCB(addrType); + + // Bind the PCB to the specified address/port. + if (res == CHIP_NO_ERROR) + { + if (reuseAddr) + { + ip_set_option(mTCP, SOF_REUSEADDR); + } + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + + ip_addr_t ipAddr; + if (addr != IPAddress::Any) + { + ipAddr = addr.ToLwIPAddr(); + } + else if (addrType == IPAddressType::kIPv6) + { + ipAddr = ip6_addr_any; + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + ipAddr = ip_addr_any; + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + res = INET_ERROR_WRONG_ADDRESS_TYPE; + res = chip::System::MapErrorLwIP(tcp_bind(mTCP, &ipAddr, port)); + +#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + + if (addrType == IPAddressType::kIPv6) + { + ip6_addr_t ipv6Addr = addr.ToIPv6(); + res = chip::System::MapErrorLwIP(tcp_bind_ip6(mTCP, &ipv6Addr, port)); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + ip_addr_t ipv4Addr = addr.ToIPv4(); + res = chip::System::MapErrorLwIP(tcp_bind(mTCP, &ipv4Addr, port)); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + res = INET_ERROR_WRONG_ADDRESS_TYPE; + +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::ListenImpl(uint16_t backlog) +{ + // Start listening for incoming connections. + mTCP = tcp_listen(mTCP); + mLwIPEndPointType = LwIPEndPointType::TCP; + + tcp_arg(mTCP, this); + + tcp_accept(mTCP, LwIPHandleIncomingConnection); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplLwIP::ConnectImpl(const IPAddress & addr, uint16_t port, InterfaceId intfId) +{ + CHIP_ERROR res = CHIP_NO_ERROR; + IPAddressType addrType = addr.Type(); + + // LwIP does not provides an API for initiating a TCP connection via a specific interface. + // As a work-around, if the destination is an IPv6 link-local address, we bind the PCB + // to the link local address associated with the source interface; however this is only + // viable if the endpoint hasn't already been bound. + if (intfId.IsPresent()) + { + IPAddress intfLLAddr; + + if (!addr.IsIPv6LinkLocal() || mState == State::kBound) + return CHIP_ERROR_NOT_IMPLEMENTED; + + res = intfId.GetLinkLocalAddr(&intfLLAddr); + if (res != CHIP_NO_ERROR) + return res; + + res = Bind(IPAddressType::kIPv6, intfLLAddr, 0, true); + if (res != CHIP_NO_ERROR) + return res; + } + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + res = GetPCB(addrType); + + if (res == CHIP_NO_ERROR) + { + tcp_arg(mTCP, this); + tcp_err(mTCP, LwIPHandleError); + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + ip_addr_t lwipAddr = addr.ToLwIPAddr(); + res = chip::System::MapErrorLwIP(tcp_connect(mTCP, &lwipAddr, port, LwIPHandleConnectComplete)); +#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + if (addrType == IPAddressType::kIPv6) + { + ip6_addr_t lwipAddr = addr.ToIPv6(); + res = chip::System::MapErrorLwIP(tcp_connect_ip6(mTCP, &lwipAddr, port, LwIPHandleConnectComplete)); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + ip_addr_t lwipAddr = addr.ToIPv4(); + res = chip::System::MapErrorLwIP(tcp_connect(mTCP, &lwipAddr, port, LwIPHandleConnectComplete)); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + res = INET_ERROR_WRONG_ADDRESS_TYPE; +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + + // Ensure that TCP timers are started + if (res == CHIP_NO_ERROR) + { + err_t error = start_tcp_timers(); + if (error != ERR_OK) + { + res = chip::System::MapErrorLwIP(error); + } + } + + if (res == CHIP_NO_ERROR) + { + mState = State::kConnecting; + Retain(); + } + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) const +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + CHIP_ERROR res = CHIP_ERROR_CONNECTION_ABORTED; + if (mTCP != nullptr) + { + *retPort = mTCP->remote_port; + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + *retAddr = IPAddress(mTCP->remote_ip); +#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 +#if INET_CONFIG_ENABLE_IPV4 + *retAddr = PCB_ISIPV6(mTCP) ? IPAddress(mTCP->remote_ip.ip6) : IPAddress(mTCP->remote_ip.ip4); +#else // !INET_CONFIG_ENABLE_IPV4 + *retAddr = IPAddress(mTCP->remote_ip.ip6); +#endif // !INET_CONFIG_ENABLE_IPV4 +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + res = CHIP_NO_ERROR; + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) const +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + CHIP_ERROR res = CHIP_ERROR_CONNECTION_ABORTED; + if (mTCP != nullptr) + { + *retPort = mTCP->local_port; + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + *retAddr = IPAddress(mTCP->local_ip); +#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 +#if INET_CONFIG_ENABLE_IPV4 + *retAddr = PCB_ISIPV6(mTCP) ? IPAddress(mTCP->local_ip.ip6) : IPAddress(mTCP->local_ip.ip4); +#else // !INET_CONFIG_ENABLE_IPV4 + *retAddr = IPAddress(mTCP->local_ip.ip6); +#endif // !INET_CONFIG_ENABLE_IPV4 +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + res = CHIP_NO_ERROR; + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::GetInterfaceId(InterfaceId * retInterface) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // TODO: Does netif_get_by_index(mTCP->netif_idx) do the right thing? I + // can't quite tell whether LwIP supports a specific interface id for TCP at + // all. For now just claim no particular interface id. + *retInterface = InterfaceId::Null(); + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplLwIP::SendQueuedImpl(bool queueWasEmpty) +{ +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + if (!mUserTimeoutTimerRunning) + { + // Timer was not running before this send. So, start + // the timer. + StartTCPUserTimeoutTimer(); + } +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplLwIP::EnableNoDelay() +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + CHIP_ERROR res = CHIP_ERROR_CONNECTION_ABORTED; + if (mTCP != nullptr) + { + tcp_nagle_disable(mTCP); + res = CHIP_NO_ERROR; + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + CHIP_ERROR res = CHIP_ERROR_NOT_IMPLEMENTED; + +#if LWIP_TCP_KEEPALIVE + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + if (mTCP != NULL) + { + // Set the idle interval + mTCP->keep_idle = (uint32_t) interval * 1000; + + // Set the probe retransmission interval. + mTCP->keep_intvl = (uint32_t) interval * 1000; + + // Set the probe timeout count + mTCP->keep_cnt = timeoutCount; + + // Enable keepalives for the connection. + ip_set_option(mTCP, SOF_KEEPALIVE); + res = CHIP_NO_ERROR; + } + else + { + res = CHIP_ERROR_CONNECTION_ABORTED; + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + +#endif // LWIP_TCP_KEEPALIVE + + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::DisableKeepAlive() +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + CHIP_ERROR res = CHIP_ERROR_NOT_IMPLEMENTED; + +#if LWIP_TCP_KEEPALIVE + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + if (mTCP != NULL) + { + // Disable keepalives on the connection. + ip_reset_option(mTCP, SOF_KEEPALIVE); + res = CHIP_NO_ERROR; + } + else + { + res = CHIP_ERROR_CONNECTION_ABORTED; + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + +#endif // LWIP_TCP_KEEPALIVE + + return res; +} + +CHIP_ERROR TCPEndPointImplLwIP::SetUserTimeoutImpl(uint32_t userTimeoutMillis) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR TCPEndPointImplLwIP::DriveSendingImpl() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // If the connection hasn't been aborted ... + if (mTCP != NULL) + { + err_t lwipErr; + + // Determine the current send window size. This is the maximum amount we can write to the connection. + uint16_t sendWindowSize = tcp_sndbuf(mTCP); + + // If there's data to be sent and the send window is open... + bool canSend = (RemainingToSend() > 0 && sendWindowSize > 0); + if (canSend) + { + // Find first packet buffer with remaining data to send by skipping + // all sent but un-acked data. + TCPEndPointImplLwIP::BufferOffset startOfUnsent = FindStartOfUnsent(); + + // While there's data to be sent and a window to send it in... + do + { + VerifyOrDie(!startOfUnsent.buffer.IsNull()); + + uint16_t bufDataLen = startOfUnsent.buffer->DataLength(); + + // Get a pointer to the start of unsent data within the first buffer on the unsent queue. + const uint8_t * sendData = startOfUnsent.buffer->Start() + startOfUnsent.offset; + + // Determine the amount of data to send from the current buffer. + uint16_t sendLen = static_cast(bufDataLen - startOfUnsent.offset); + if (sendLen > sendWindowSize) + sendLen = sendWindowSize; + + // Call LwIP to queue the data to be sent, telling it if there's more data to come. + // Data is queued in-place as a reference within the source packet buffer. It is + // critical that the underlying packet buffer not be freed until the data + // is acknowledged, otherwise retransmissions could use an invalid + // backing. Using TCP_WRITE_FLAG_COPY would eliminate this requirement, but overall + // requires many more memory allocations which may be problematic when very + // memory-constrained or when using pool-based allocations. + lwipErr = tcp_write(mTCP, sendData, sendLen, (canSend) ? TCP_WRITE_FLAG_MORE : 0); + if (lwipErr != ERR_OK) + { + err = chip::System::MapErrorLwIP(lwipErr); + break; + } + // Start accounting for the data sent as yet-to-be-acked. + // This cast is safe, because mUnackedLength + sendLen <= bufDataLen, which fits in uint16_t. + mUnackedLength = static_cast(mUnackedLength + sendLen); + + // Adjust the unsent data offset by the length of data that was written. + // If the entire buffer has been sent advance to the next one. + // This cast is safe, because startOfUnsent.offset + sendLen <= bufDataLen, which fits in uint16_t. + startOfUnsent.offset = static_cast(startOfUnsent.offset + sendLen); + if (startOfUnsent.offset == bufDataLen) + { + startOfUnsent.buffer.Advance(); + startOfUnsent.offset = 0; + } + + // Adjust the remaining window size. + sendWindowSize = static_cast(sendWindowSize - sendLen); + + // Determine if there's more data to be sent after this buffer. + canSend = (RemainingToSend() > 0 && sendWindowSize > 0); + } while (canSend); + + // Call LwIP to send the queued data. + INET_FAULT_INJECT(FaultInjection::kFault_Send, err = chip::System::MapErrorLwIP(ERR_RTE)); + + if (err == CHIP_NO_ERROR) + { + lwipErr = tcp_output(mTCP); + + if (lwipErr != ERR_OK) + err = chip::System::MapErrorLwIP(lwipErr); + } + } + + if (err == CHIP_NO_ERROR) + { + // If in the SendShutdown state and the unsent queue is now empty, shutdown the PCB for sending. + if (mState == State::kSendShutdown && (RemainingToSend() == 0)) + { + lwipErr = tcp_shutdown(mTCP, 0, 1); + if (lwipErr != ERR_OK) + err = chip::System::MapErrorLwIP(lwipErr); + } + } + } + + else + err = CHIP_ERROR_CONNECTION_ABORTED; + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return err; +} + +void TCPEndPointImplLwIP::HandleConnectCompleteImpl() {} + +void TCPEndPointImplLwIP::DoCloseImpl(CHIP_ERROR err, State oldState) +{ + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // If the LwIP PCB hasn't been closed yet... + if (mTCP != NULL) + { + // If the endpoint was a connection endpoint (vs. a listening endpoint)... + if (oldState != State::kListening) + { + // Prevent further callbacks for incoming data. This has the effect of instructing + // LwIP to discard any further data received from the peer. + tcp_recv(mTCP, NULL); + + // If entering the Closed state... + if (mState == State::kClosed) + { + // Prevent further callbacks to the error handler. + // + // Note: It is important to understand that LwIP can continue to make callbacks after + // a PCB has been closed via the tcp_close() API. In particular, LwIP will continue + // to call the 'data sent' callback to signal the acknowledgment of data that was + // sent, but not acknowledged, prior to the close call. Additionally, LwIP will call + // the error callback if the peer fails to respond in a timely manner to the either + // sent data or the FIN. Unfortunately, there is no callback in the case where the + // connection closes successfully. Because of this, it is impossible know definitively + // when LwIP will no longer make callbacks to its user. Thus we must block further + // callbacks to prevent them from happening after the endpoint has been freed. + // + tcp_err(mTCP, NULL); + + // If the endpoint is being closed without error, THEN call tcp_close() to close the underlying + // TCP connection gracefully, preserving any in-transit send data. + if (err == CHIP_NO_ERROR) + { + tcp_close(mTCP); + } + + // OTHERWISE, call tcp_abort() to abort the TCP connection, discarding any in-transit data. + else + { + tcp_abort(mTCP); + } + + // Discard the reference to the PCB to ensure there is no further interaction with it + // after this point. + mTCP = NULL; + mLwIPEndPointType = LwIPEndPointType::Unknown; + } + } + + // OTHERWISE the endpoint was being used for listening, so simply close it. + else + { + tcp_close(mTCP); + + // Discard the reference to the PCB to ensure there is no further interaction with it + // after this point. + mTCP = NULL; + mLwIPEndPointType = LwIPEndPointType::Unknown; + } + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + if (mState == State::kClosed) + { + mUnackedLength = 0; + } +} + +CHIP_ERROR TCPEndPointImplLwIP::AckReceive(uint16_t len) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + CHIP_ERROR res = CHIP_NO_ERROR; + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + if (mTCP != nullptr) + tcp_recved(mTCP, len); + else + res = CHIP_ERROR_CONNECTION_ABORTED; + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return res; +} + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT +void TCPEndPointImplLwIP::TCPUserTimeoutHandler() +{ + // Set the timer running flag to false + mUserTimeoutTimerRunning = false; + + // Close Connection as we have timed out and there is still + // data not sent out successfully. + DoClose(INET_ERROR_TCP_USER_TIMEOUT, false); +} +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + +uint16_t TCPEndPointImplLwIP::RemainingToSend() +{ + if (mSendQueue.IsNull()) + { + return 0; + } + else + { + // We can never have reported more unacked data than there is pending + // in the send queue! This would indicate a critical accounting bug. + VerifyOrDie(mUnackedLength <= mSendQueue->TotalLength()); + + return static_cast(mSendQueue->TotalLength() - mUnackedLength); + } +} + +TCPEndPointImplLwIP::BufferOffset TCPEndPointImplLwIP::FindStartOfUnsent() +{ + // Find first packet buffer with remaining data to send by skipping + // all sent but un-acked data. This is necessary because of the Consume() + // call in HandleDataSent(), which potentially releases backing memory for + // fully-sent packet buffers, causing an invalidation of all possible + // offsets one might have cached. The TCP acnowledgements may come back + // with a variety of sizes depending on prior activity, and size of the + // send window. The only way to ensure we get the correct offsets into + // unsent data while retaining the buffers that have un-acked data is to + // traverse all sent-but-unacked data in the chain to reach the beginning + // of ready-to-send data. + TCPEndPointImplLwIP::BufferOffset startOfUnsent(mSendQueue.Retain()); + uint16_t leftToSkip = mUnackedLength; + + VerifyOrDie(leftToSkip < mSendQueue->TotalLength()); + + while (leftToSkip > 0) + { + VerifyOrDie(!startOfUnsent.buffer.IsNull()); + uint16_t bufDataLen = startOfUnsent.buffer->DataLength(); + if (leftToSkip >= bufDataLen) + { + // We have more to skip than current packet buffer size. + // Follow the chain to continue. + startOfUnsent.buffer.Advance(); + leftToSkip = static_cast(leftToSkip - bufDataLen); + } + else + { + // Done skipping all data, currentUnsentBuf is first packet buffer + // containing unsent data. + startOfUnsent.offset = leftToSkip; + leftToSkip = 0; + } + } + + return startOfUnsent; +} + +CHIP_ERROR TCPEndPointImplLwIP::GetPCB(IPAddressType addrType) +{ + // IMMPORTANT: This method MUST be called with the LwIP stack LOCKED! + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + if (mTCP == NULL) + { + switch (addrType) + { + case IPAddressType::kIPv6: + mTCP = tcp_new_ip_type(IPADDR_TYPE_V6); + break; + +#if INET_CONFIG_ENABLE_IPV4 + case IPAddressType::kIPv4: + mTCP = tcp_new_ip_type(IPADDR_TYPE_V4); + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + if (mTCP == NULL) + { + return CHIP_ERROR_NO_MEMORY; + } + else + { + mLwIPEndPointType = LwIPEndPointType::TCP; + } + } + else + { + switch (IP_GET_TYPE(&mTCP->local_ip)) + { + case IPADDR_TYPE_V6: + if (addrType != IPAddressType::kIPv6) + return INET_ERROR_WRONG_ADDRESS_TYPE; + break; + +#if INET_CONFIG_ENABLE_IPV4 + case IPADDR_TYPE_V4: + if (addrType != IPAddressType::kIPv4) + return INET_ERROR_WRONG_ADDRESS_TYPE; + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + break; + } + } +#else // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + if (mTCP == NULL) + { + if (addrType == IPAddressType::kIPv6) + mTCP = tcp_new_ip6(); +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + mTCP = tcp_new(); +#endif // INET_CONFIG_ENABLE_IPV4 + else + return INET_ERROR_WRONG_ADDRESS_TYPE; + if (mTCP == NULL) + { + return CHIP_ERROR_NO_MEMORY; + } + else + { + mLwIPEndPointType = LwIPEndPointType::TCP; + } + } + else + { +#if INET_CONFIG_ENABLE_IPV4 + const IPAddressType pcbType = PCB_ISIPV6(mTCP) ? IPAddressType::kIPv6 : IPAddressType::kIPv4; +#else // !INET_CONFIG_ENABLE_IPV4 + const IPAddressType pcbType = IPAddressType::kIPv6; +#endif // !INET_CONFIG_ENABLE_IPV4 + if (addrType != pcbType) + return INET_ERROR_WRONG_ADDRESS_TYPE; + } +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + + return CHIP_NO_ERROR; +} + +void TCPEndPointImplLwIP::HandleDataSent(uint16_t lenSent) +{ + if (IsConnected()) + { + // Ensure we do not have internal inconsistency in the lwIP, which + // could cause invalid pointer accesses. + if (lenSent > mUnackedLength) + { + ChipLogError(Inet, "Got more ACKed bytes (%d) than were pending (%d)", (int) lenSent, (int) mUnackedLength); + DoClose(CHIP_ERROR_UNEXPECTED_EVENT, false); + return; + } + else if (mSendQueue.IsNull()) + { + ChipLogError(Inet, "Got ACK for %d bytes but data backing gone", (int) lenSent); + DoClose(CHIP_ERROR_UNEXPECTED_EVENT, false); + return; + } + + // Consume data off the head of the send queue equal to the amount of data being acknowledged. + mSendQueue.Consume(lenSent); + mUnackedLength = static_cast(mUnackedLength - lenSent); + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + // Only change the UserTimeout timer if lenSent > 0, + // indicating progress being made in sending data + // across. + if (lenSent > 0) + { + if (RemainingToSend() == 0) + { + // If the output queue has been flushed then stop the timer. + + StopTCPUserTimeoutTimer(); + +#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + // Notify up if all outstanding data has been acknowledged + + SetTCPSendIdleAndNotifyChange(true); +#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + } + else + { + // Progress is being made. So, shift the timer + // forward if it was started. + RestartTCPUserTimeoutTimer(); + } + } +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + + // Mark the connection as being active. + MarkActive(); + + // If requested, call the app's OnDataSent callback. + if (OnDataSent != NULL) + OnDataSent(this, lenSent); + + // If unsent data exists, attempt to send it now... + if (RemainingToSend() > 0) + DriveSending(); + + // If in the closing state and the send queue is now empty, attempt to transition to closed. + if ((mState == State::kClosing) && (RemainingToSend() == 0)) + DoClose(CHIP_NO_ERROR, false); + } +} + +void TCPEndPointImplLwIP::HandleDataReceived(System::PacketBufferHandle && buf) +{ + // Only receive new data while in the Connected or SendShutdown states. + if (mState == State::kConnected || mState == State::kSendShutdown) + { + // Mark the connection as being active. + MarkActive(); + + // If we received a data buffer, queue it on the receive queue. If there's already data in + // the queue, compact the data into the head buffer. + if (!buf.IsNull()) + { + if (mRcvQueue.IsNull()) + { + mRcvQueue = std::move(buf); + } + else + { + mRcvQueue->AddToEnd(std::move(buf)); + mRcvQueue->CompactHead(); + } + } + + // Otherwise buf == NULL means the other side closed the connection, so ... + else + { + + // 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 != NULL) + mState = State::kReceiveShutdown; + else + mState = State::kClosing; + + // Call the app's OnPeerClose. + if (OnPeerClose != NULL) + OnPeerClose(this); + } + + // Drive the received data into the app. + DriveReceiving(); + } +} + +void TCPEndPointImplLwIP::HandleIncomingConnection(TCPEndPoint * conEP) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + IPAddress peerAddr; + uint16_t peerPort; + + if (mState == State::kListening) + { + // If there's no callback available, fail with an error. + if (OnConnectionReceived == NULL) + err = CHIP_ERROR_NO_CONNECTION_HANDLER; + + // Extract the peer's address information. + if (err == CHIP_NO_ERROR) + err = conEP->GetPeerInfo(&peerAddr, &peerPort); + + // If successful, call the app's callback function. + if (err == CHIP_NO_ERROR) + OnConnectionReceived(this, conEP, peerAddr, peerPort); + + // Otherwise clean up and call the app's error callback. + else if (OnAcceptError != NULL) + OnAcceptError(this, err); + } + else + err = CHIP_ERROR_INCORRECT_STATE; + + // If something failed above, abort and free the connection end point. + if (err != CHIP_NO_ERROR) + conEP->Free(); +} + +void TCPEndPointImplLwIP::HandleError(CHIP_ERROR err) +{ + if (mState == State::kListening) + { + if (OnAcceptError != NULL) + OnAcceptError(this, err); + } + else + DoClose(err, false); +} + +err_t TCPEndPointImplLwIP::LwIPHandleConnectComplete(void * arg, struct tcp_pcb * tpcb, err_t lwipErr) +{ + err_t res = ERR_OK; + + if (arg != NULL) + { + TCPEndPointImplLwIP * ep = static_cast(arg); + + if (lwipErr == ERR_OK) + { + // Setup LwIP callback functions for data transmission. + tcp_recv(ep->mTCP, LwIPHandleDataReceived); + tcp_sent(ep->mTCP, LwIPHandleDataSent); + } + + // Post callback to HandleConnectComplete. + ep->Retain(); + CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, conErr = System::MapErrorLwIP(lwipErr)] { + ep->HandleConnectComplete(conErr); + ep->Release(); + }); + if (err != CHIP_NO_ERROR) + { + ep->Release(); + res = ERR_ABRT; + } + } + else + res = ERR_ABRT; + + if (res != ERR_OK) + tcp_abort(tpcb); + + return res; +} + +err_t TCPEndPointImplLwIP::LwIPHandleIncomingConnection(void * arg, struct tcp_pcb * tpcb, err_t lwipErr) +{ + CHIP_ERROR err = chip::System::MapErrorLwIP(lwipErr); + + if (arg != NULL) + { + TCPEndPointImplLwIP * listenEP = static_cast(arg); + TCPEndPointImplLwIP * conEP = NULL; + System::Layer & lSystemLayer = listenEP->GetSystemLayer(); + + // Tell LwIP we've accepted the connection so it can decrement the listen PCB's pending_accepts counter. + tcp_accepted(listenEP->mTCP); + + // If we did in fact receive a connection, rather than an error, attempt to allocate an end point object. + // + // NOTE: Although most of the LwIP callbacks defer the real work to happen on the endpoint's thread + // (by posting events to the thread's event queue) we can't do that here because as soon as this + // function returns, LwIP is free to begin calling callbacks on the new PCB. For that to work we need + // to have an end point associated with the PCB. + // + if (err == CHIP_NO_ERROR) + { + TCPEndPoint * connectEndPoint = nullptr; + err = listenEP->GetEndPointManager().NewEndPoint(&connectEndPoint); + conEP = static_cast(connectEndPoint); + } + + // Ensure that TCP timers have been started + if (err == CHIP_NO_ERROR) + { + err_t error = start_tcp_timers(); + if (error != ERR_OK) + { + err = chip::System::MapErrorLwIP(error); + } + } + + // If successful in allocating an end point... + if (err == CHIP_NO_ERROR) + { + // Put the new end point into the Connected state. + conEP->mState = State::kConnected; + conEP->mTCP = tpcb; + conEP->mLwIPEndPointType = LwIPEndPointType::TCP; + conEP->Retain(); + + // Setup LwIP callback functions for the new PCB. + tcp_arg(tpcb, conEP); + tcp_recv(tpcb, LwIPHandleDataReceived); + tcp_sent(tpcb, LwIPHandleDataSent); + tcp_err(tpcb, LwIPHandleError); + + // Post a callback to the HandleConnectionReceived() function, passing it the new end point. + listenEP->Retain(); + conEP->Retain(); + err = lSystemLayer.ScheduleLambda([listenEP, conEP] { + listenEP->HandleIncomingConnection(conEP); + conEP->Release(); + listenEP->Release(); + }); + if (err != CHIP_NO_ERROR) + { + conEP->Release(); // for the Ref in ScheduleLambda + listenEP->Release(); + err = CHIP_ERROR_CONNECTION_ABORTED; + conEP->Release(); // for the Retain() above + conEP->Release(); // for the implied Retain() on construction + } + } + + // Otherwise, there was an error accepting the connection, so post a callback to the HandleError function. + else + { + listenEP->Retain(); + err = lSystemLayer.ScheduleLambda([listenEP, err] { + listenEP->HandleError(err); + listenEP->Release(); + }); + if (err != CHIP_NO_ERROR) + { + listenEP->Release(); + } + } + } + else + err = CHIP_ERROR_CONNECTION_ABORTED; + + if (err != CHIP_NO_ERROR && tpcb != NULL) + { + tcp_abort(tpcb); + return ERR_ABRT; + } + else + { + return ERR_OK; + } +} + +err_t TCPEndPointImplLwIP::LwIPHandleDataReceived(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t _err) +{ + err_t res = ERR_OK; + + if (arg != NULL) + { + TCPEndPointImplLwIP * ep = static_cast(arg); + + // Post callback to HandleDataReceived. + ep->Retain(); + CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, p] { + ep->HandleDataReceived(System::PacketBufferHandle::Adopt(p)); + ep->Release(); + }); + if (err != CHIP_NO_ERROR) + { + ep->Release(); + res = ERR_ABRT; + } + } + else + res = ERR_ABRT; + + if (res != ERR_OK) + { + if (p != nullptr) + { + pbuf_free(p); + } + tcp_abort(tpcb); + } + + return res; +} + +err_t TCPEndPointImplLwIP::LwIPHandleDataSent(void * arg, struct tcp_pcb * tpcb, u16_t len) +{ + err_t res = ERR_OK; + + if (arg != NULL) + { + TCPEndPointImplLwIP * ep = static_cast(arg); + + // Post callback to HandleDataReceived. + ep->Retain(); + CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, len] { + ep->HandleDataSent(len); + ep->Release(); + }); + if (err != CHIP_NO_ERROR) + { + ep->Release(); + res = ERR_ABRT; + } + } + else + res = ERR_ABRT; + + if (res != ERR_OK) + tcp_abort(tpcb); + + return res; +} + +void TCPEndPointImplLwIP::LwIPHandleError(void * arg, err_t lwipErr) +{ + if (arg != NULL) + { + TCPEndPointImplLwIP * ep = static_cast(arg); + System::LayerLwIP & lSystemLayer = static_cast(ep->GetSystemLayer()); + + // At this point LwIP has already freed the PCB. Since the thread that owns the TCPEndPoint may + // try to use the PCB before it receives the TCPError event posted below, we set the PCB to NULL + // as a means to signal the other thread that the connection has been aborted. The implication + // of this is that the mTCP field is shared state between the two threads and thus must only be + // accessed with the LwIP lock held. + ep->mTCP = NULL; + ep->mLwIPEndPointType = LwIPEndPointType::Unknown; + + // Post callback to HandleError. + ep->Retain(); + CHIP_ERROR err = lSystemLayer.ScheduleLambda([ep, conErr = System::MapErrorLwIP(lwipErr)] { + ep->HandleError(conErr); + ep->Release(); + }); + if (err != CHIP_NO_ERROR) + ep->Release(); + } +} + +} // namespace Inet +} // namespace chip diff --git a/src/inet/TCPEndPointImplLwIP.h b/src/inet/TCPEndPointImplLwIP.h new file mode 100644 index 00000000000000..36a995b0cfd699 --- /dev/null +++ b/src/inet/TCPEndPointImplLwIP.h @@ -0,0 +1,101 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2013-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. + */ + +/** + * @file + * This header file defines the Inet::TCPEndPoint + * class, where the CHIP Inet Layer encapsulates methods for + * interacting with TCP transport endpoints (SOCK_DGRAM sockets + * on Linux and BSD-derived systems) or LwIP TCP protocol + * control blocks, as the system is configured accordingly. + */ + +/** + * This file declares an implementation of Inet::TCPEndPoint using LwIP. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace Inet { + +class TCPEndPointImplLwIP : public TCPEndPoint, public EndPointStateLwIP +{ +public: + TCPEndPointImplLwIP(EndPointManager & endPointManager) : TCPEndPoint(endPointManager), mUnackedLength(0) {} + + // 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; +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + void TCPUserTimeoutHandler() override; +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + +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; + + struct BufferOffset + { + BufferOffset(System::PacketBufferHandle && aBuffer) : buffer(std::move(aBuffer)), offset(0) {} + BufferOffset(BufferOffset && aOther) + { + buffer = std::move(aOther.buffer); + offset = aOther.offset; + } + chip::System::PacketBufferHandle buffer; + uint16_t offset; + }; + + uint16_t mUnackedLength; // Amount sent but awaiting ACK. Used as a form of reference count + // to hang-on to backing packet buffers until they are no longer needed. + + uint16_t RemainingToSend(); + BufferOffset FindStartOfUnsent(); + CHIP_ERROR GetPCB(IPAddressType addrType); + void HandleDataSent(uint16_t len); + void HandleDataReceived(chip::System::PacketBufferHandle && buf); + void HandleIncomingConnection(TCPEndPoint * pcb); + void HandleError(CHIP_ERROR err); + + static err_t LwIPHandleConnectComplete(void * arg, struct tcp_pcb * tpcb, err_t lwipErr); + static err_t LwIPHandleIncomingConnection(void * arg, struct tcp_pcb * tcpConPCB, err_t lwipErr); + static err_t LwIPHandleDataReceived(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err); + static err_t LwIPHandleDataSent(void * arg, struct tcp_pcb * tpcb, u16_t len); + static void LwIPHandleError(void * arg, err_t err); +}; + +using TCPEndPointImpl = TCPEndPointImplLwIP; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/TCPEndPointImplSockets.cpp b/src/inet/TCPEndPointImplSockets.cpp new file mode 100644 index 00000000000000..1f6bd368b1e948 --- /dev/null +++ b/src/inet/TCPEndPointImplSockets.cpp @@ -0,0 +1,1223 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2013-2018 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. + */ + +/** + * This file implements Inet::TCPEndPoint using sockets. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// SOCK_CLOEXEC not defined on all platforms, e.g. iOS/macOS: +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + +#if defined(SOL_TCP) +// socket option level for Linux and BSD systems. +#define TCP_SOCKOPT_LEVEL SOL_TCP +#else +// socket option level for macOS & iOS systems. +#define TCP_SOCKOPT_LEVEL IPPROTO_TCP +#endif + +#if defined(TCP_KEEPIDLE) +// socket option for Linux and BSD systems. +#define TCP_IDLE_INTERVAL_OPT_NAME TCP_KEEPIDLE +#else +// socket option for macOS & iOS systems. +#define TCP_IDLE_INTERVAL_OPT_NAME TCP_KEEPALIVE +#endif + +namespace chip { +namespace Inet { + +CHIP_ERROR TCPEndPointImplSockets::BindImpl(IPAddressType addrType, const IPAddress & addr, uint16_t port, bool reuseAddr) +{ + CHIP_ERROR res = GetSocket(addrType); + + if (res == CHIP_NO_ERROR && reuseAddr) + { + int n = 1; + setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); + +#ifdef SO_REUSEPORT + // 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 TARGETTED_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 wih the same degreee of selectivity, + // e.g. two untargetted-listen CHIP clients, or two + // targetted-listen CHIP clients with the same node id. + + if (setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &n, sizeof(n)) != 0) + { + ChipLogError(Inet, "SO_REUSEPORT: %d", errno); + } +#endif // defined(SO_REUSEPORT) + } + + if (res == CHIP_NO_ERROR) + { + if (addrType == IPAddressType::kIPv6) + { + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_port = htons(port); + sa.sin6_flowinfo = 0; + sa.sin6_addr = addr.ToIPv6(); + sa.sin6_scope_id = 0; + + if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + { + res = CHIP_ERROR_POSIX(errno); + } + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr = addr.ToIPv4(); + + if (bind(mSocket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + { + res = CHIP_ERROR_POSIX(errno); + } + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + res = INET_ERROR_WRONG_ADDRESS_TYPE; + } + } + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_queue_t dispatchQueue = static_cast(GetSystemLayer()).GetDispatchQueue(); + if (dispatchQueue != nullptr) + { + unsigned long fd = static_cast(mSocket); + + mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); + ReturnErrorCodeIf(mReadableSource == nullptr, CHIP_ERROR_NO_MEMORY); + + mWriteableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, dispatchQueue); + ReturnErrorCodeIf(mWriteableSource == nullptr, CHIP_ERROR_NO_MEMORY); + + dispatch_source_set_event_handler(mReadableSource, ^{ + this->HandlePendingIO(System::SocketEventFlags::kRead); + }); + + dispatch_source_set_event_handler(mWriteableSource, ^{ + this->HandlePendingIO(System::SocketEventFlags::kWrite); + }); + + dispatch_resume(mReadableSource); + dispatch_resume(mWriteableSource); + } +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + return res; +} + +CHIP_ERROR TCPEndPointImplSockets::ListenImpl(uint16_t backlog) +{ + if (listen(mSocket, backlog) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + // Enable non-blocking mode for the socket. + int flags = fcntl(mSocket, F_GETFL, 0); + fcntl(mSocket, F_SETFL, flags | O_NONBLOCK); + + // Wait for ability to read on this endpoint. + CHIP_ERROR res = static_cast(GetSystemLayer()) + .SetCallback(mWatch, HandlePendingIO, reinterpret_cast(this)); + if (res == CHIP_NO_ERROR) + { + res = static_cast(GetSystemLayer()).RequestCallbackOnPendingRead(mWatch); + } + + return res; +} + +CHIP_ERROR TCPEndPointImplSockets::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 bind to + // interface or address on the interface. + + if (!addr.IsIPv6LinkLocal()) + { +#ifdef SO_BINDTODEVICE + struct ::ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + + ReturnErrorOnFailure(intfId.GetInterfaceName(ifr.ifr_name, sizeof(ifr.ifr_name))); + + // Attempt to bind 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 bind the connetion to. + int r = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)); + if (r < 0 && errno != EACCES) + { + return CHIP_ERROR_POSIX(errno); + } + + if (r < 0) +#endif // SO_BINDTODEVICE + { + // Attempting to initiate a connection via a specific interface is not allowed. + // The only way to do this is to bind the local to an address on the desired + // interface. + ReturnErrorOnFailure(BindSrcAddrFromIntf(addrType, intfId)); + } + } + } + + // Disable generation of SIGPIPE. +#ifdef SO_NOSIGPIPE + int n = 1; + setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &n, sizeof(n)); +#endif // defined(SO_NOSIGPIPE) + + // Enable non-blocking mode for the socket. + int flags = fcntl(mSocket, F_GETFL, 0); + fcntl(mSocket, F_SETFL, flags | O_NONBLOCK); + + socklen_t sockaddrsize = 0; + const sockaddr * sockaddrptr = nullptr; + + SockAddr sa; + memset(&sa, 0, sizeof(sa)); + + if (addrType == IPAddressType::kIPv6) + { + sa.in6.sin6_family = AF_INET6; + sa.in6.sin6_port = htons(port); + sa.in6.sin6_flowinfo = 0; + sa.in6.sin6_addr = addr.ToIPv6(); + sa.in6.sin6_scope_id = intfId.GetPlatformInterface(); + sockaddrsize = sizeof(sockaddr_in6); + sockaddrptr = reinterpret_cast(&sa.in6); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { + sa.in.sin_family = AF_INET; + sa.in.sin_port = htons(port); + sa.in.sin_addr = addr.ToIPv4(); + sockaddrsize = sizeof(sockaddr_in); + sockaddrptr = reinterpret_cast(&sa.in); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + int conRes = connect(mSocket, sockaddrptr, sockaddrsize); + + if (conRes == -1 && errno != EINPROGRESS) + { + CHIP_ERROR res = CHIP_ERROR_POSIX(errno); + DoClose(res, true); + return res; + } + + ReturnErrorOnFailure(static_cast(GetSystemLayer()) + .SetCallback(mWatch, HandlePendingIO, reinterpret_cast(this))); + + // 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()).RequestCallbackOnPendingRead(mWatch)); + if (OnConnectComplete != nullptr) + { + OnConnectComplete(this, CHIP_NO_ERROR); + } + } + else + { + mState = State::kConnecting; + // Wait for ability to write on this endpoint. + ReturnErrorOnFailure(static_cast(GetSystemLayer()).RequestCallbackOnPendingWrite(mWatch)); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::GetPeerInfo(IPAddress * retAddr, uint16_t * retPort) const +{ + return GetSocketInfo(getpeername, retAddr, retPort); +} + +CHIP_ERROR TCPEndPointImplSockets::GetLocalInfo(IPAddress * retAddr, uint16_t * retPort) const +{ + return GetSocketInfo(getsockname, retAddr, retPort); +} + +CHIP_ERROR TCPEndPointImplSockets::GetSocketInfo(int getname(int, sockaddr *, socklen_t *), IPAddress * retAddr, + uint16_t * retPort) const +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + SockAddr sa; + memset(&sa, 0, sizeof(sa)); + socklen_t saLen = sizeof(sa); + + if (getname(mSocket, &sa.any, &saLen) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + if (sa.any.sa_family == AF_INET6) + { + *retAddr = IPAddress(sa.in6.sin6_addr); + *retPort = ntohs(sa.in6.sin6_port); + return CHIP_NO_ERROR; + } + +#if INET_CONFIG_ENABLE_IPV4 + if (sa.any.sa_family == AF_INET) + { + *retAddr = IPAddress(sa.in.sin_addr); + *retPort = ntohs(sa.in.sin_port); + return CHIP_NO_ERROR; + } +#endif // INET_CONFIG_ENABLE_IPV4 + + return CHIP_ERROR_INCORRECT_STATE; +} + +CHIP_ERROR TCPEndPointImplSockets::GetInterfaceId(InterfaceId * retInterface) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + SockAddr sa; + memset(&sa, 0, sizeof(sa)); + socklen_t saLen = sizeof(sa); + + if (getpeername(mSocket, &sa.any, &saLen) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + if (sa.any.sa_family == AF_INET6) + { + if (IPAddress(sa.in6.sin6_addr).IsIPv6LinkLocal()) + { + *retInterface = InterfaceId(sa.in6.sin6_scope_id); + } + else + { + // TODO: Is there still a meaningful interface id in this case? + *retInterface = InterfaceId::Null(); + } + return CHIP_NO_ERROR; + } + +#if INET_CONFIG_ENABLE_IPV4 + if (sa.any.sa_family == AF_INET) + { + // No interface id available for IPv4 sockets. + *retInterface = InterfaceId::Null(); + return CHIP_NO_ERROR; + } +#endif // INET_CONFIG_ENABLE_IPV4 + + *retInterface = InterfaceId::Null(); + return INET_ERROR_WRONG_ADDRESS_TYPE; +} + +CHIP_ERROR TCPEndPointImplSockets::SendQueuedImpl(bool queueWasEmpty) +{ + if (queueWasEmpty) + { + // Wait for ability to write on this endpoint. + return static_cast(GetSystemLayer()).RequestCallbackOnPendingWrite(mWatch); + } + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::EnableNoDelay() +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + +#ifdef TCP_NODELAY + // Disable TCP Nagle buffering by setting TCP_NODELAY socket option to true + int val = 1; + if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_NODELAY, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } +#endif // defined(TCP_NODELAY) + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::EnableKeepAlive(uint16_t interval, uint16_t timeoutCount) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Set the idle interval + int val = interval; + if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_IDLE_INTERVAL_OPT_NAME, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + // Set the probe retransmission interval. + val = interval; + if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_KEEPINTVL, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + // Set the probe timeout count + val = timeoutCount; + if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_KEEPCNT, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + // Enable keepalives for the connection. + val = 1; // enable + if (setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::DisableKeepAlive() +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // Disable keepalives on the connection. + int val = 0; // disable + if (setsockopt(mSocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::AckReceive(uint16_t len) +{ + VerifyOrReturnError(IsConnected(), CHIP_ERROR_INCORRECT_STATE); + + // nothing to do for sockets case + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::SetUserTimeoutImpl(uint32_t userTimeoutMillis) +{ +#if defined(TCP_USER_TIMEOUT) + // Set the user timeout + uint32_t val = userTimeoutMillis; + if (setsockopt(mSocket, TCP_SOCKOPT_LEVEL, TCP_USER_TIMEOUT, &val, sizeof(val)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + return CHIP_NO_ERROR; +#else // TCP_USER_TIMEOUT + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(TCP_USER_TIMEOUT) +} + +CHIP_ERROR TCPEndPointImplSockets::DriveSendingImpl() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + +#ifdef MSG_NOSIGNAL + const int sendFlags = MSG_NOSIGNAL; +#else + const int sendFlags = 0; +#endif + + // Pretend send() fails in the while loop below + INET_FAULT_INJECT(FaultInjection::kFault_Send, { + err = CHIP_ERROR_POSIX(EIO); + DoClose(err, false); + return err; + }); + + while (!mSendQueue.IsNull()) + { + uint16_t bufLen = mSendQueue->DataLength(); + + ssize_t lenSentRaw = send(mSocket, mSendQueue->Start(), bufLen, sendFlags); + + if (lenSentRaw == -1) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + { + err = (errno == EPIPE) ? INET_ERROR_PEER_DISCONNECTED : CHIP_ERROR_POSIX(errno); + } + 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()).ClearCallbackOnPendingWrite(mWatch); + if (err != CHIP_NO_ERROR) + { + break; + } + } + } + + if (OnDataSent != nullptr) + { + OnDataSent(this, lenSent); + } + +#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + // TCP Send is not Idle; Set state and notify if needed + + SetTCPSendIdleAndNotifyChange(false); +#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + mBytesWrittenSinceLastProbe += lenSent; + + bool isProgressing = false; + + err = CheckConnectionProgress(isProgressing); + if (err != CHIP_NO_ERROR) + { + break; + } + + if (!mUserTimeoutTimerRunning) + { + // Timer was not running before this write. So, start + // the timer. + + StartTCPUserTimeoutTimer(); + } + else if (isProgressing) + { + // Progress is being made. So, shift the timer + // forward if it was started. + + RestartTCPUserTimeoutTimer(); + } +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + + 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()) + { + if (shutdown(mSocket, SHUT_WR) != 0) + { + err = CHIP_ERROR_POSIX(errno); + } + } + } + + return err; +} + +void TCPEndPointImplSockets::HandleConnectCompleteImpl() +{ + // Wait for ability to read or write on this endpoint. + CHIP_ERROR err = static_cast(GetSystemLayer()).RequestCallbackOnPendingRead(mWatch); + if (err == CHIP_NO_ERROR) + { + err = static_cast(GetSystemLayer()).RequestCallbackOnPendingWrite(mWatch); + } + if (err != CHIP_NO_ERROR) + { + DoClose(err, false); + return; + } +} + +void TCPEndPointImplSockets::DoCloseImpl(CHIP_ERROR err, State oldState) +{ + struct linger lingerStruct; + + // 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) + { + lingerStruct.l_onoff = 1; + lingerStruct.l_linger = 0; + + if (setsockopt(mSocket, SOL_SOCKET, SO_LINGER, &lingerStruct, sizeof(lingerStruct)) != 0) + { + ChipLogError(Inet, "SO_LINGER: %d", errno); + } + } + + static_cast(GetSystemLayer()).StopWatchingSocket(&mWatch); + close(mSocket); + mSocket = kInvalidSocketFd; + } + } + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + if (mReadableSource) + { + dispatch_source_cancel(mReadableSource); + dispatch_release(mReadableSource); + } + if (mWriteableSource) + { + dispatch_source_cancel(mWriteableSource); + dispatch_release(mWriteableSource); + } +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +} + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT +void TCPEndPointImplSockets::TCPUserTimeoutHandler() +{ + // Set the timer running flag to false + mUserTimeoutTimerRunning = false; + + CHIP_ERROR err = CHIP_NO_ERROR; + bool isProgressing = false; + err = CheckConnectionProgress(isProgressing); + SuccessOrExit(err); + + if (mLastTCPKernelSendQueueLen == 0) + { +#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + // If the kernel TCP send queue as well as the TCPEndPoint + // send queue have been flushed then notify application + // that all data has been acknowledged. + + if (mSendQueue.IsNull()) + { + SetTCPSendIdleAndNotifyChange(true); + } +#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + } + else + // There is data in the TCP Send Queue + { + if (isProgressing) + { + // Data is flowing, so restart the UserTimeout timer + // to shift it forward while also resetting the max + // poll count. + + StartTCPUserTimeoutTimer(); + } + else + { +#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + // Data flow is not progressing. + // Decrement the remaining max TCP send queue polls. + + mTCPSendQueueRemainingPollCount--; + + VerifyOrExit(mTCPSendQueueRemainingPollCount != 0, err = INET_ERROR_TCP_USER_TIMEOUT); + + // Restart timer to poll again + + ScheduleNextTCPUserTimeoutPoll(mTCPSendQueuePollPeriodMillis); +#else + // Close the connection as the TCP UserTimeout has expired + + ExitNow(err = INET_ERROR_TCP_USER_TIMEOUT); +#endif // !INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + } + } + +exit: + + if (err != CHIP_NO_ERROR) + { + // Close the connection as the TCP UserTimeout has expired + + DoClose(err, false); + } +} +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + +CHIP_ERROR TCPEndPointImplSockets::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()) + { + const IPAddress curAddr = addrIter.GetAddress(); + const InterfaceId curIntfId = addrIter.GetInterfaceId(); + + if (curIntfId == intfId) + { + // 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); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR TCPEndPointImplSockets::GetSocket(IPAddressType addrType) +{ + if (mSocket == kInvalidSocketFd) + { + int family; + if (addrType == IPAddressType::kIPv6) + { + family = PF_INET6; +#if INET_CONFIG_ENABLE_IPV4 + } + else if (addrType == IPAddressType::kIPv4) + { + family = PF_INET; +#endif // INET_CONFIG_ENABLE_IPV4 + } + else + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + mSocket = ::socket(family, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (mSocket == -1) + { + return CHIP_ERROR_POSIX(errno); + } + ReturnErrorOnFailure(static_cast(GetSystemLayer()).StartWatchingSocket(mSocket, &mWatch)); + mAddrType = addrType; + + // If creating an IPv6 socket, tell the kernel that it will be IPv6 only. This makes it + // posible to bind two sockets to the same port, one for IPv4 and one for IPv6. +#ifdef IPV6_V6ONLY + if (family == PF_INET6) + { + int one = 1; + setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + } +#endif // defined(IPV6_V6ONLY) + + // On systems that support it, disable the delivery of SIGPIPE signals when writing to a closed + // socket. +#ifdef SO_NOSIGPIPE + { + int one = 1; + int res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "SO_NOSIGPIPE: %d", errno); + } + } +#endif // defined(SO_NOSIGPIPE) + } + else if (mAddrType != addrType) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +// static +void TCPEndPointImplSockets::HandlePendingIO(System::SocketEvents events, intptr_t data) +{ + reinterpret_cast(data)->HandlePendingIO(events); +} + +void TCPEndPointImplSockets::HandlePendingIO(System::SocketEvents events) +{ + // 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 && events.Has(System::SocketEventFlags::kRead)) + { + 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 (events.Has(System::SocketEventFlags::kWrite)) + { +#if !__MBED__ + // Get the connection result from the socket. + int osConRes; + socklen_t optLen = sizeof(osConRes); + if (getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &osConRes, &optLen) != 0) + { + osConRes = errno; + } +#else + // On Mbed OS, connect blocks and never returns EINPROGRESS + // The socket option SO_ERROR is not available. + int osConRes = 0; +#endif + CHIP_ERROR conRes = CHIP_ERROR_POSIX(osConRes); + + // Process the connection result. + HandleConnectComplete(conRes); + } + } + + 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 (IsConnected() && !mSendQueue.IsNull() && events.Has(System::SocketEventFlags::kWrite)) + { + 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 ((mState == State::kConnected || mState == State::kSendShutdown) && mReceiveEnabled && OnDataReceived != nullptr && + events.Has(System::SocketEventFlags::kRead)) + { + ReceiveData(); + } + } + + Release(); +} + +void TCPEndPointImplSockets::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. + ssize_t rcvLen = recv(mSocket, rcvBuf->Start() + rcvBuf->DataLength(), rcvBuf->AvailableDataLength(), 0); + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + CHIP_ERROR err; + bool isProgressing = false; + + err = CheckConnectionProgress(isProgressing); + if (err != CHIP_NO_ERROR) + { + DoClose(err, false); + + return; + } + + if (mLastTCPKernelSendQueueLen == 0) + { + // If the output queue has been flushed then stop the timer. + + StopTCPUserTimeoutTimer(); + +#if INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + // Notify up if all outstanding data has been acknowledged + + if (mSendQueue.IsNull()) + { + SetTCPSendIdleAndNotifyChange(true); + } +#endif // INET_CONFIG_ENABLE_TCP_SEND_IDLE_CALLBACKS + } + else if (isProgressing && mUserTimeoutTimerRunning) + { + // Progress is being made. So, shift the timer + // forward if it was started. + RestartTCPUserTimeoutTimer(); + } +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + // If an error occurred, abort the connection. + if (rcvLen < 0) + { + int systemErrno = errno; + if (systemErrno == EAGAIN) + { + // 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(CHIP_ERROR_POSIX(systemErrno), 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()).ClearCallbackOnPendingRead(mWatch); + // 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 TCPEndPointImplSockets::HandleIncomingConnection() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TCPEndPointImplSockets * conEP = nullptr; + IPAddress peerAddr; + uint16_t peerPort; + + SockAddr sa; + memset(&sa, 0, sizeof(sa)); + socklen_t saLen = sizeof(sa); + + // Accept the new connection. + int conSocket = accept(mSocket, &sa.any, &saLen); + if (conSocket == -1) + { + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + return; + } + else + { + err = CHIP_ERROR_POSIX(errno); + } + } + + // If there's no callback available, fail with an error. + if (err == CHIP_NO_ERROR && OnConnectionReceived == nullptr) + { + err = CHIP_ERROR_NO_CONNECTION_HANDLER; + } + + // Extract the peer's address information. + if (err == CHIP_NO_ERROR) + { + if (sa.any.sa_family == AF_INET6) + { + peerAddr = IPAddress(sa.in6.sin6_addr); + peerPort = ntohs(sa.in6.sin6_port); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (sa.any.sa_family == AF_INET) + { + peerAddr = IPAddress(sa.in.sin_addr); + peerPort = ntohs(sa.in.sin_port); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + err = CHIP_ERROR_INCORRECT_STATE; + } + } + + // 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; + err = static_cast(GetSystemLayer()).StartWatchingSocket(conSocket, &conEP->mWatch); + if (err == CHIP_NO_ERROR) + { + conEP->mState = State::kConnected; +#if INET_CONFIG_ENABLE_IPV4 + conEP->mAddrType = (sa.any.sa_family == AF_INET6) ? IPAddressType::kIPv6 : IPAddressType::kIPv4; +#else // !INET_CONFIG_ENABLE_IPV4 + conEP->mAddrType = IPAddressType::kIPv6; +#endif // !INET_CONFIG_ENABLE_IPV4 + conEP->Retain(); + + // Wait for ability to read on this endpoint. + auto & conEPLayer = static_cast(conEP->GetSystemLayer()); + err = conEPLayer.SetCallback(conEP->mWatch, HandlePendingIO, reinterpret_cast(conEP)); + if (err == CHIP_NO_ERROR) + { + err = conEPLayer.RequestCallbackOnPendingRead(conEP->mWatch); + } + 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) + { + close(conSocket); + } + if (conEP != nullptr) + { + if (conEP->mState == State::kConnected) + { + conEP->Release(); + } + conEP->Release(); + } + if (OnAcceptError != nullptr) + { + OnAcceptError(this, err); + } +} + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT +/** + * This function probes the TCP output queue and checks if data is successfully + * being transferred to the other end. + */ +CHIP_ERROR TCPEndPointImplSockets::CheckConnectionProgress(bool & isProgressing) +{ + int currPendingBytesRaw = 0; + uint32_t currPendingBytes; // Will be initialized once we know it's safe. + + // Fetch the bytes pending successful transmission in the TCP out queue. + + if (ioctl(mSocket, TIOCOUTQ, &currPendingBytesRaw) < 0) + { + return CHIP_ERROR_POSIX(errno); + } + + if (!CanCastTo(currPendingBytesRaw)) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + currPendingBytes = static_cast(currPendingBytesRaw); + + if ((currPendingBytes != 0) && (mBytesWrittenSinceLastProbe + mLastTCPKernelSendQueueLen == currPendingBytes)) + { + // No progress has been made + + isProgressing = false; + } + else + { + // Data is flowing successfully + + isProgressing = true; + } + + // Reset the value of the bytes written since the last probe into the tcp + // outqueue was made and update the last tcp outqueue sample. + + mBytesWrittenSinceLastProbe = 0; + + mLastTCPKernelSendQueueLen = currPendingBytes; + + return CHIP_NO_ERROR; +} +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + +} // namespace Inet +} // namespace chip diff --git a/src/inet/TCPEndPointImplSockets.h b/src/inet/TCPEndPointImplSockets.h new file mode 100644 index 00000000000000..036939e826323a --- /dev/null +++ b/src/inet/TCPEndPointImplSockets.h @@ -0,0 +1,96 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2013-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. + */ + +/** + * This file declares an implementation of Inet::TCPEndPoint using sockets. + */ + +#pragma once + +#include +#include + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH +#include +#endif + +namespace chip { +namespace Inet { + +class TCPEndPointImplSockets : public TCPEndPoint, public EndPointStateSockets +{ +public: + TCPEndPointImplSockets(EndPointManager & endPointManager) : + TCPEndPoint(endPointManager) +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + , + mBytesWrittenSinceLastProbe(0), mLastTCPKernelSendQueueLen(0) +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + {} + + // 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; +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + void TCPUserTimeoutHandler() override; +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + +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 GetSocketInfo(int getname(int, sockaddr *, socklen_t *), IPAddress * retAddr, uint16_t * retPort) const; + CHIP_ERROR GetSocket(IPAddressType addrType); + void HandlePendingIO(System::SocketEvents events); + void ReceiveData(); + void HandleIncomingConnection(); + CHIP_ERROR BindSrcAddrFromIntf(IPAddressType addrType, InterfaceId intfId); + static void HandlePendingIO(System::SocketEvents events, intptr_t data); + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_source_t mReadableSource = nullptr; + dispatch_source_t mWriteableSource = nullptr; +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + +#if INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT + /// This counts the number of bytes written on the TCP socket since thelast probe into the TCP outqueue was made. + uint32_t mBytesWrittenSinceLastProbe; + + /// This is the measured size(in bytes) of the kernel TCP send queue at the end of the last user timeout window. + uint32_t mLastTCPKernelSendQueueLen; + + CHIP_ERROR CheckConnectionProgress(bool & IsProgressing); +#endif // INET_CONFIG_OVERRIDE_SYSTEM_TCP_USER_TIMEOUT +}; + +using TCPEndPointImpl = TCPEndPointImplSockets; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPoint.cpp b/src/inet/UDPEndPoint.cpp index 578e7fc4f77d4d..e0907d951a9e65 100644 --- a/src/inet/UDPEndPoint.cpp +++ b/src/inet/UDPEndPoint.cpp @@ -27,7 +27,6 @@ * */ -#define __APPLE_USE_RFC_3542 #include #include @@ -41,1897 +40,8 @@ #include #include -#if CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_HAVE_CONFIG_H -#include // nogncheck -#endif // CHIP_HAVE_CONFIG_H - -#if INET_CONFIG_ENABLE_IPV4 -#include -#endif // INET_CONFIG_ENABLE_IPV4 - -#include -#include -#include -#include -#include -#include -#include - -#if !defined(RAW_FLAGS_MULTICAST_LOOP) || !defined(UDP_FLAGS_MULTICAST_LOOP) || !defined(raw_clear_flags) || \ - !defined(raw_set_flags) || !defined(udp_clear_flags) || !defined(udp_set_flags) -#define HAVE_LWIP_MULTICAST_LOOP 0 -#else -#define HAVE_LWIP_MULTICAST_LOOP 1 -#endif // !defined(RAW_FLAGS_MULTICAST_LOOP) || !defined(UDP_FLAGS_MULTICAST_LOOP) || !defined(raw_clear_flags) || - // !defined(raw_set_flags) || !defined(udp_clear_flags) || !defined(udp_set_flags) - -// unusual define check for LWIP_IPV6_ND is because espressif fork -// of LWIP does not define the _ND constant. -#if LWIP_IPV6_MLD && (!defined(LWIP_IPV6_ND) || LWIP_IPV6_ND) && LWIP_IPV6 -#define HAVE_IPV6_MULTICAST -#else -// Within Project CHIP multicast support is highly desirable: used for mDNS -// as well as group communication. -#undef HAVE_IPV6_MULTICAST -#endif -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#if HAVE_SYS_SOCKET_H -#include -#endif // HAVE_SYS_SOCKET_H - -#include -#include -#include -#include -#include - -// SOCK_CLOEXEC not defined on all platforms, e.g. iOS/macOS: -#ifndef SOCK_CLOEXEC -#define SOCK_CLOEXEC 0 -#endif - -#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS -#include "ZephyrSocket.h" -#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS - -/* - * Some systems define both IPV6_{ADD,DROP}_MEMBERSHIP and - * IPV6_{JOIN,LEAVE}_GROUP while others only define - * IPV6_{JOIN,LEAVE}_GROUP. Prefer the "_MEMBERSHIP" flavor for - * parallelism with IPv4 and create the alias to the availabile - * definitions. - */ -#if defined(IPV6_ADD_MEMBERSHIP) -#define INET_IPV6_ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP -#elif defined(IPV6_JOIN_GROUP) -#define INET_IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP -#elif !__ZEPHYR__ -#error \ - "Neither IPV6_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP are defined which are required for generalized IPv6 multicast group support." -#endif // IPV6_ADD_MEMBERSHIP - -#if defined(IPV6_DROP_MEMBERSHIP) -#define INET_IPV6_DROP_MEMBERSHIP IPV6_DROP_MEMBERSHIP -#elif defined(IPV6_LEAVE_GROUP) -#define INET_IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP -#elif !__ZEPHYR__ -#error \ - "Neither IPV6_DROP_MEMBERSHIP nor IPV6_LEAVE_GROUP are defined which are required for generalized IPv6 multicast group support." -#endif // IPV6_DROP_MEMBERSHIP -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#define INET_PORTSTRLEN 6 -#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -namespace chip { -namespace Inet { - -#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS - -namespace { - -CHIP_ERROR CheckMulticastGroupArgs(InterfaceId aInterfaceId, const IPAddress & aAddress) -{ - VerifyOrReturnError(aAddress.IsMulticast(), INET_ERROR_WRONG_ADDRESS_TYPE); - return CHIP_NO_ERROR; -} - -} // anonymous namespace - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_USE_LWIP - -CHIP_ERROR UDPEndPointImplLwIP::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port, - InterfaceId interfaceId) -{ - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - // Make sure we have the appropriate type of PCB. - CHIP_ERROR res = GetPCB(addressType); - - // Bind the PCB to the specified address/port. - if (res == CHIP_NO_ERROR) - { -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - ip_addr_t ipAddr = address.ToLwIPAddr(); - - // TODO: IPAddress ANY has only one constant state, however addressType - // has separate IPV4 and IPV6 'any' settings. This tries to correct - // for this as LWIP default if IPv4 is compiled in is to consider - // 'any == any_v4' - // - // We may want to consider having separate AnyV4 and AnyV6 constants - // inside CHIP to resolve this ambiguity - if ((address.Type() == IPAddressType::kAny) && (addressType == IPAddressType::kIPv6)) - { - ipAddr = *IP6_ADDR_ANY; - } - - res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipAddr, port)); -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - if (addressType == IPAddressType::kIPv6) - { - ip6_addr_t ipv6Addr = address.ToIPv6(); - res = chip::System::MapErrorLwIP(udp_bind_ip6(mUDP, &ipv6Addr, port)); - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addressType == IPAddressType::kIPv4) - { - ip4_addr_t ipv4Addr = address.ToIPv4(); - res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipv4Addr, port)); - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - res = INET_ERROR_WRONG_ADDRESS_TYPE; -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - } - - if (res == CHIP_NO_ERROR) - { - res = LwIPBindInterface(mUDP, interfaceId); - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return res; -} - -CHIP_ERROR UDPEndPointImplLwIP::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId) -{ - // A lock is required because the LwIP thread may be referring to intf_filter, - // while this code running in the Inet application is potentially modifying it. - // NOTE: this only supports LwIP interfaces whose number is no bigger than 9. - LOCK_TCPIP_CORE(); - - // Make sure we have the appropriate type of PCB. - CHIP_ERROR err = GetPCB(addrType); - - if (err == CHIP_NO_ERROR) - { - err = LwIPBindInterface(mUDP, intfId); - } - - UNLOCK_TCPIP_CORE(); - - return err; -} - -CHIP_ERROR UDPEndPointImplLwIP::LwIPBindInterface(struct udp_pcb * aUDP, InterfaceId intfId) -{ - struct netif * netifp = nullptr; - if (intfId.IsPresent()) - { - netifp = UDPEndPointImplLwIP::FindNetifFromInterfaceId(intfId); - if (netifp == nullptr) - { - return INET_ERROR_UNKNOWN_INTERFACE; - } - } - - udp_bind_netif(aUDP, netifp); - return CHIP_NO_ERROR; -} - -InterfaceId UDPEndPointImplLwIP::GetBoundInterface() const -{ -#if HAVE_LWIP_UDP_BIND_NETIF - return InterfaceId(netif_get_by_index(mUDP->netif_idx)); -#else - return InterfaceId(mUDP->intf_filter); -#endif -} - -uint16_t UDPEndPointImplLwIP::GetBoundPort() const -{ - return mUDP->local_port; -} - -CHIP_ERROR UDPEndPointImplLwIP::ListenImpl() -{ - // Lock LwIP stack - LOCK_TCPIP_CORE(); - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - udp_recv(mUDP, LwIPReceiveUDPMessage, this); -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - if (PCB_ISIPV6(mUDP)) - { - udp_recv_ip6(mUDP, LwIPReceiveUDPMessage, this); - } - else - { - udp_recv(mUDP, LwIPReceiveUDPMessage, this); - } -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR UDPEndPointImplLwIP::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg) -{ - const IPAddress & destAddr = pktInfo->DestAddress; - - if (!msg.HasSoleOwnership()) - { - // when retaining a buffer, the caller expects the msg to be unmodified. - // LwIP stack will normally prepend the packet headers as the packet traverses - // the UDP/IP/netif layers, which normally modifies the packet. We need to clone - // msg into a fresh object in this case, and queues that for transmission, leaving - // the original msg available after return. - msg = msg.CloneData(); - VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_NO_MEMORY); - } - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - // Make sure we have the appropriate type of PCB based on the destination address. - CHIP_ERROR res = GetPCB(destAddr.Type()); - if (res != CHIP_NO_ERROR) - { - UNLOCK_TCPIP_CORE(); - return res; - } - - // Send the message to the specified address/port. - // If an outbound interface has been specified, call a specific version of the UDP sendto() - // function that accepts the target interface. - // If a source address has been specified, temporarily override the local_ip of the PCB. - // This results in LwIP using the given address being as the source address for the generated - // packet, as if the PCB had been bound to that address. - err_t lwipErr = ERR_VAL; - const IPAddress & srcAddr = pktInfo->SrcAddress; - const uint16_t & destPort = pktInfo->DestPort; - const InterfaceId & intfId = pktInfo->Interface; - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - - ip_addr_t lwipSrcAddr = srcAddr.ToLwIPAddr(); - ip_addr_t lwipDestAddr = destAddr.ToLwIPAddr(); - - ip_addr_t boundAddr; - ip_addr_copy(boundAddr, mUDP->local_ip); - - if (!ip_addr_isany(&lwipSrcAddr)) - { - ip_addr_copy(mUDP->local_ip, lwipSrcAddr); - } - - if (intfId.IsPresent()) - { - lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, - intfId.GetPlatformInterface()); - } - else - { - lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort); - } - - ip_addr_copy(mUDP->local_ip, boundAddr); - -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - - ipX_addr_t boundAddr; - ipX_addr_copy(boundAddr, mUDP->local_ip); - - if (PCB_ISIPV6(mUDP)) - { - ip6_addr_t lwipSrcAddr = srcAddr.ToIPv6(); - ip6_addr_t lwipDestAddr = destAddr.ToIPv6(); - - if (!ip6_addr_isany(&lwipSrcAddr)) - { - ipX_addr_copy(mUDP->local_ip, *ip6_2_ipX(&lwipSrcAddr)); - } - - if (intfId.IsPresent()) - { - lwipErr = - udp_sendto_if_ip6(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, intfId); - } - else - { - lwipErr = udp_sendto_ip6(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort); - } - } - -#if INET_CONFIG_ENABLE_IPV4 - - else - { - ip4_addr_t lwipSrcAddr = srcAddr.ToIPv4(); - ip4_addr_t lwipDestAddr = destAddr.ToIPv4(); - ipX_addr_t boundAddr; - - if (!ip_addr_isany(&lwipSrcAddr)) - { - ipX_addr_copy(mUDP->local_ip, *ip_2_ipX(&lwipSrcAddr)); - } - - if (intfId.IsPresent()) - { - lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, intfId); - } - else - { - lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort); - } - } - - ipX_addr_copy(mUDP->local_ip, boundAddr); - -#endif // INET_CONFIG_ENABLE_IPV4 -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); - - if (lwipErr != ERR_OK) - { - res = chip::System::MapErrorLwIP(lwipErr); - } - - return res; -} - -void UDPEndPointImplLwIP::CloseImpl() -{ - - // Lock LwIP stack - LOCK_TCPIP_CORE(); - - // Since UDP PCB is released synchronously here, but UDP endpoint itself might have to wait - // for destruction asynchronously, there could be more allocated UDP endpoints than UDP PCBs. - if (mUDP != nullptr) - { - udp_remove(mUDP); - mUDP = nullptr; - mLwIPEndPointType = LwIPEndPointType::Unknown; - } - - // Unlock LwIP stack - UNLOCK_TCPIP_CORE(); -} - -void UDPEndPointImplLwIP::Free() -{ - Close(); - Release(); -} - -void UDPEndPointImplLwIP::HandleDataReceived(System::PacketBufferHandle && msg) -{ - if ((mState == State::kListening) && (OnMessageReceived != nullptr)) - { - const IPPacketInfo * pktInfo = GetPacketInfo(msg); - - if (pktInfo != nullptr) - { - const IPPacketInfo pktInfoCopy = *pktInfo; // copy the address info so that the app can free the - // PacketBuffer without affecting access to address info. - OnMessageReceived(this, std::move(msg), &pktInfoCopy); - } - else - { - if (OnReceiveError != nullptr) - { - OnReceiveError(this, CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG, nullptr); - } - } - } -} - -CHIP_ERROR UDPEndPointImplLwIP::GetPCB(IPAddressType addrType) -{ - // IMPORTANT: This method MUST be called with the LwIP stack LOCKED! - - // If a PCB hasn't been allocated yet... - if (mUDP == nullptr) - { - // Allocate a PCB of the appropriate type. - if (addrType == IPAddressType::kIPv6) - { -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - mUDP = udp_new_ip_type(IPADDR_TYPE_V6); -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - mUDP = udp_new_ip6(); -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - } -#if INET_CONFIG_ENABLE_IPV4 - else if (addrType == IPAddressType::kIPv4) - { -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - mUDP = udp_new_ip_type(IPADDR_TYPE_V4); -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - mUDP = udp_new(); -#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 - } -#endif // INET_CONFIG_ENABLE_IPV4 - else - { - return INET_ERROR_WRONG_ADDRESS_TYPE; - } - - // Fail if the system has run out of PCBs. - if (mUDP == nullptr) - { - ChipLogError(Inet, "Unable to allocate UDP PCB"); - return CHIP_ERROR_NO_MEMORY; - } - - // Allow multiple bindings to the same port. - ip_set_option(mUDP, SOF_REUSEADDR); - } - - // Otherwise, verify that the existing PCB is the correct type... - else - { - IPAddressType pcbAddrType; - - // Get the address type of the existing PCB. -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - switch (static_cast(IP_GET_TYPE(&mUDP->local_ip))) - { - case IPADDR_TYPE_V6: - pcbAddrType = IPAddressType::kIPv6; - break; -#if INET_CONFIG_ENABLE_IPV4 - case IPADDR_TYPE_V4: - pcbAddrType = IPAddressType::kIPv4; - break; -#endif // INET_CONFIG_ENABLE_IPV4 - default: - return INET_ERROR_WRONG_ADDRESS_TYPE; - } -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 -#if INET_CONFIG_ENABLE_IPV4 - pcbAddrType = PCB_ISIPV6(mUDP) ? IPAddressType::kIPv6 : IPAddressType::kIPv4; -#else // !INET_CONFIG_ENABLE_IPV4 - pcbAddrType = IPAddressType::kIPv6; -#endif // !INET_CONFIG_ENABLE_IPV4 -#endif // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - - // Fail if the existing PCB is not the correct type. - VerifyOrReturnError(addrType == pcbAddrType, INET_ERROR_WRONG_ADDRESS_TYPE); - } - - return CHIP_NO_ERROR; -} - -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 -void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, const ip_addr_t * addr, - u16_t port) -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 -void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, ip_addr_t * addr, u16_t port) -#endif // LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 -{ - UDPEndPointImplLwIP * ep = static_cast(arg); - IPPacketInfo * pktInfo = nullptr; - System::PacketBufferHandle buf = System::PacketBufferHandle::Adopt(p); - if (buf->HasChainedBuffer()) - { - // Try the simple expedient of flattening in-place. - buf->CompactHead(); - } - - if (buf->HasChainedBuffer()) - { - // Have to allocate a new big-enough buffer and copy. - uint16_t messageSize = buf->TotalLength(); - System::PacketBufferHandle copy = System::PacketBufferHandle::New(messageSize, 0); - if (copy.IsNull() || buf->Read(copy->Start(), messageSize) != CHIP_NO_ERROR) - { - ChipLogError(Inet, "No memory to flatten incoming packet buffer chain of size %" PRIu16, buf->TotalLength()); - return; - } - buf = std::move(copy); - } - - pktInfo = GetPacketInfo(buf); - if (pktInfo != nullptr) - { -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - pktInfo->SrcAddress = IPAddress(*addr); - pktInfo->DestAddress = IPAddress(*ip_current_dest_addr()); -#else // LWIP_VERSION_MAJOR <= 1 - if (PCB_ISIPV6(pcb)) - { - pktInfo->SrcAddress = IPAddress(*(ip6_addr_t *) addr); - pktInfo->DestAddress = IPAddress(*ip6_current_dest_addr()); - } -#if INET_CONFIG_ENABLE_IPV4 - else - { - pktInfo->SrcAddress = IPAddress(*addr); - pktInfo->DestAddress = IPAddress(*ip_current_dest_addr()); - } -#endif // INET_CONFIG_ENABLE_IPV4 -#endif // LWIP_VERSION_MAJOR <= 1 - - pktInfo->Interface = InterfaceId(ip_current_netif()); - pktInfo->SrcPort = port; - pktInfo->DestPort = pcb->local_port; - } - - ep->Retain(); - CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, p = System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(buf)] { - ep->HandleDataReceived(System::PacketBufferHandle::Adopt(p)); - ep->Release(); - }); - if (err == CHIP_NO_ERROR) - { - // If ScheduleLambda() succeeded, it has ownership of the buffer, so we need to release it (without freeing it). - static_cast(std::move(buf).UnsafeRelease()); - } - else - { - ep->Release(); - } -} - -CHIP_ERROR UDPEndPointImplLwIP::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback) -{ -#if HAVE_LWIP_MULTICAST_LOOP - if (mLwIPEndPointType == LwIPEndPointType::UDP) - { - if (aLoopback) - { - udp_set_flags(mUDP, UDP_FLAGS_MULTICAST_LOOP); - } - else - { - udp_clear_flags(mUDP, UDP_FLAGS_MULTICAST_LOOP); - } - return CHIP_NO_ERROR; - } -#endif // HAVE_LWIP_MULTICAST_LOOP - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -} - -#if INET_CONFIG_ENABLE_IPV4 -CHIP_ERROR UDPEndPointImplLwIP::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) -{ -#if LWIP_IPV4 && LWIP_IGMP - const auto method = join ? igmp_joingroup_netif : igmp_leavegroup_netif; - - struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId); - VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE); - - const ip4_addr_t lIPv4Address = aAddress.ToIPv4(); - const err_t lStatus = method(lNetif, &lIPv4Address); - if (lStatus == ERR_MEM) - { - return CHIP_ERROR_NO_MEMORY; - } - return chip::System::MapErrorLwIP(lStatus); -#else // LWIP_IPV4 && LWIP_IGMP - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif // LWIP_IPV4 && LWIP_IGMP -} -#endif // INET_CONFIG_ENABLE_IPV4 - -CHIP_ERROR UDPEndPointImplLwIP::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) -{ -#ifdef HAVE_IPV6_MULTICAST - const auto method = join ? mld6_joingroup_netif : mld6_leavegroup_netif; - - struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId); - VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE); - - const ip6_addr_t lIPv6Address = aAddress.ToIPv6(); - const err_t lStatus = method(lNetif, &lIPv6Address); - if (lStatus == ERR_MEM) - { - return CHIP_ERROR_NO_MEMORY; - } - - return chip::System::MapErrorLwIP(lStatus); -#else // HAVE_IPV6_MULTICAST - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif // HAVE_IPV6_MULTICAST -} - -struct netif * UDPEndPointImplLwIP::FindNetifFromInterfaceId(InterfaceId aInterfaceId) -{ - struct netif * lRetval = nullptr; - -#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH) - NETIF_FOREACH(lRetval) - { - if (lRetval == aInterfaceId.GetPlatformInterface()) - { - break; - } - } -#else // LWIP_VERSION_MAJOR < 2 || !defined(NETIF_FOREACH) - for (lRetval = netif_list; lRetval != nullptr && lRetval != aInterfaceId.GetPlatformInterface(); lRetval = lRetval->next) - ; -#endif // LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH) - - return (lRetval); -} - -IPPacketInfo * UDPEndPointImplLwIP::GetPacketInfo(const System::PacketBufferHandle & aBuffer) -{ - if (!aBuffer->EnsureReservedSize(sizeof(IPPacketInfo) + 3)) - { - return nullptr; - } - - uintptr_t lStart = (uintptr_t) aBuffer->Start(); - uintptr_t lPacketInfoStart = lStart - sizeof(IPPacketInfo); - - // Align to a 4-byte boundary - return reinterpret_cast(lPacketInfoStart & ~(sizeof(uint32_t) - 1)); -} - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -namespace { - -CHIP_ERROR IPv6Bind(int socket, const IPAddress & address, uint16_t port, InterfaceId interface) -{ - struct sockaddr_in6 sa; - memset(&sa, 0, sizeof(sa)); - sa.sin6_family = AF_INET6; - sa.sin6_port = htons(port); - sa.sin6_addr = address.ToIPv6(); - InterfaceId::PlatformType interfaceId = interface.GetPlatformInterface(); - if (!CanCastTo(interfaceId)) - { - return CHIP_ERROR_INCORRECT_STATE; - } - sa.sin6_scope_id = static_cast(interfaceId); - - CHIP_ERROR status = CHIP_NO_ERROR; - if (bind(socket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) - { - status = CHIP_ERROR_POSIX(errno); - } - else - { -#ifdef IPV6_MULTICAST_IF - // Instruct the kernel that any messages to multicast destinations should be - // sent down the interface specified by the caller. - setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceId, sizeof(interfaceId)); -#endif // defined(IPV6_MULTICAST_IF) - } - -#ifdef IPV6_MULTICAST_HOPS - // Instruct the kernel that any messages to multicast destinations should be - // set with the configured hop limit value. - int hops = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; - setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); -#endif // defined(IPV6_MULTICAST_HOPS) - - return status; -} - -#if INET_CONFIG_ENABLE_IPV4 -CHIP_ERROR IPv4Bind(int socket, const IPAddress & address, uint16_t port) -{ - struct sockaddr_in sa; - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr = address.ToIPv4(); - - CHIP_ERROR status = CHIP_NO_ERROR; - if (bind(socket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) - { - status = CHIP_ERROR_POSIX(errno); - } - else - { - // Allow socket transmitting broadcast packets. - constexpr int enable = 1; - setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)); - -#ifdef IP_MULTICAST_IF - // Instruct the kernel that any messages to multicast destinations should be - // sent down the interface to which the specified IPv4 address is bound. - setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &sa, sizeof(sa)); -#endif // defined(IP_MULTICAST_IF) - } - -#ifdef IP_MULTICAST_TTL - // Instruct the kernel that any messages to multicast destinations should be - // set with the configured hop limit value. - constexpr int ttl = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; - setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); -#endif // defined(IP_MULTICAST_TTL) - - return status; -} -#endif // INET_CONFIG_ENABLE_IPV4 - -} // anonymous namespace - -#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API -UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sJoinMulticastGroupHandler; -UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sLeaveMulticastGroupHandler; -#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API - -CHIP_ERROR UDPEndPointImplSockets::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; - mBoundIntfId = interface; - - // If an ephemeral port was requested, retrieve the actual bound port. - if (port == 0) - { - SockAddr boundAddr; - socklen_t boundAddrLen = sizeof(boundAddr); - - if (getsockname(mSocket, &boundAddr.any, &boundAddrLen) == 0) - { - if (boundAddr.any.sa_family == AF_INET) - { - mBoundPort = ntohs(boundAddr.in.sin_port); - } - else if (boundAddr.any.sa_family == AF_INET6) - { - mBoundPort = ntohs(boundAddr.in6.sin6_port); - } - } - } - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_queue_t dispatchQueue = static_cast(&GetSystemLayer())->GetDispatchQueue(); - if (dispatchQueue != nullptr) - { - unsigned long fd = static_cast(mSocket); - - mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); - ReturnErrorCodeIf(mReadableSource == nullptr, CHIP_ERROR_NO_MEMORY); - - dispatch_source_set_event_handler(mReadableSource, ^{ - this->HandlePendingIO(System::SocketEventFlags::kRead); - }); - dispatch_resume(mReadableSource); - } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - - return CHIP_NO_ERROR; -} - -CHIP_ERROR UDPEndPointImplSockets::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; - - if (interfaceId.IsPresent()) - { - // Start filtering on the passed interface. - char interfaceName[IF_NAMESIZE]; - if (if_indextoname(interfaceId.GetPlatformInterface(), interfaceName) == nullptr) - { - status = CHIP_ERROR_POSIX(errno); - } - else if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, interfaceName, socklen_t(strlen(interfaceName))) == -1) - { - status = CHIP_ERROR_POSIX(errno); - } - } - else - { - // Stop interface-based filtering. - if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, "", 0) == -1) - { - status = CHIP_ERROR_POSIX(errno); - } - } - - if (status == CHIP_NO_ERROR) - { - mBoundIntfId = interfaceId; - } - - return status; -#else // !HAVE_SO_BINDTODEVICE - return CHIP_ERROR_NOT_IMPLEMENTED; -#endif // HAVE_SO_BINDTODEVICE -} - -InterfaceId UDPEndPointImplSockets::GetBoundInterface() const -{ - return mBoundIntfId; -} - -uint16_t UDPEndPointImplSockets::GetBoundPort() const -{ - return mBoundPort; -} - -CHIP_ERROR UDPEndPointImplSockets::ListenImpl() -{ - // Wait for ability to read on this endpoint. - auto * layer = static_cast(&GetSystemLayer()); - ReturnErrorOnFailure(layer->SetCallback(mWatch, HandlePendingIO, reinterpret_cast(this))); - return layer->RequestCallbackOnPendingRead(mWatch); -} - -CHIP_ERROR UDPEndPointImplSockets::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(mAddrType == aPktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT); - - // For now the entire message must fit within a single buffer. - VerifyOrReturnError(!msg->HasChainedBuffer(), CHIP_ERROR_MESSAGE_TOO_LONG); - - struct iovec msgIOV; - msgIOV.iov_base = msg->Start(); - msgIOV.iov_len = msg->DataLength(); - -#if defined(IP_PKTINFO) || defined(IPV6_PKTINFO) - uint8_t controlData[256]; - memset(controlData, 0, sizeof(controlData)); -#endif // defined(IP_PKTINFO) || defined(IPV6_PKTINFO) - - struct 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. - SockAddr peerSockAddr; - memset(&peerSockAddr, 0, sizeof(peerSockAddr)); - msgHeader.msg_name = &peerSockAddr; - if (mAddrType == IPAddressType::kIPv6) - { - peerSockAddr.in6.sin6_family = AF_INET6; - peerSockAddr.in6.sin6_port = htons(aPktInfo->DestPort); - peerSockAddr.in6.sin6_addr = aPktInfo->DestAddress.ToIPv6(); - InterfaceId::PlatformType intfId = aPktInfo->Interface.GetPlatformInterface(); - VerifyOrReturnError(CanCastTo(intfId), CHIP_ERROR_INCORRECT_STATE); - peerSockAddr.in6.sin6_scope_id = static_cast(intfId); - msgHeader.msg_namelen = sizeof(sockaddr_in6); - } -#if INET_CONFIG_ENABLE_IPV4 - else - { - peerSockAddr.in.sin_family = AF_INET; - peerSockAddr.in.sin_port = htons(aPktInfo->DestPort); - peerSockAddr.in.sin_addr = aPktInfo->DestAddress.ToIPv4(); - msgHeader.msg_namelen = sizeof(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 = mBoundIntfId; - } - - // If the packet should be sent over a specific interface, or with a specific source - // address, construct an IP_PKTINFO/IPV6_PKTINFO "control message" to that effect - // add add it to the message header. If the local OS doesn't support IP_PKTINFO/IPV6_PKTINFO - // fail with an error. - if (intf.IsPresent() || aPktInfo->SrcAddress.Type() != IPAddressType::kAny) - { -#if defined(IP_PKTINFO) || defined(IPV6_PKTINFO) - msgHeader.msg_control = controlData; - msgHeader.msg_controllen = sizeof(controlData); - - struct cmsghdr * controlHdr = CMSG_FIRSTHDR(&msgHeader); - InterfaceId::PlatformType intfId = intf.GetPlatformInterface(); - -#if INET_CONFIG_ENABLE_IPV4 - - if (mAddrType == IPAddressType::kIPv4) - { -#if defined(IP_PKTINFO) - controlHdr->cmsg_level = IPPROTO_IP; - controlHdr->cmsg_type = IP_PKTINFO; - controlHdr->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); - - auto * pktInfo = reinterpret_cast CMSG_DATA(controlHdr); - if (!CanCastToipi_ifindex)>(intfId)) - { - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; - } - - pktInfo->ipi_ifindex = static_castipi_ifindex)>(intfId); - pktInfo->ipi_spec_dst = aPktInfo->SrcAddress.ToIPv4(); - - msgHeader.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo)); -#else // !defined(IP_PKTINFO) - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif // !defined(IP_PKTINFO) - } - -#endif // INET_CONFIG_ENABLE_IPV4 - - if (mAddrType == IPAddressType::kIPv6) - { -#if defined(IPV6_PKTINFO) - controlHdr->cmsg_level = IPPROTO_IPV6; - controlHdr->cmsg_type = IPV6_PKTINFO; - controlHdr->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); - - auto * pktInfo = reinterpret_cast CMSG_DATA(controlHdr); - if (!CanCastToipi6_ifindex)>(intfId)) - { - return CHIP_ERROR_UNEXPECTED_EVENT; - } - pktInfo->ipi6_ifindex = static_castipi6_ifindex)>(intfId); - pktInfo->ipi6_addr = aPktInfo->SrcAddress.ToIPv6(); - - msgHeader.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo)); -#else // !defined(IPV6_PKTINFO) - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif // !defined(IPV6_PKTINFO) - } - -#else // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO)) - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO)) - } - - // Send IP packet. - const ssize_t lenSent = sendmsg(mSocket, &msgHeader, 0); - if (lenSent == -1) - { - return CHIP_ERROR_POSIX(errno); - } - if (lenSent != msg->DataLength()) - { - return CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG; - } - return CHIP_NO_ERROR; -} - -void UDPEndPointImplSockets::CloseImpl() -{ - if (mSocket != kInvalidSocketFd) - { - static_cast(&GetSystemLayer())->StopWatchingSocket(&mWatch); - close(mSocket); - mSocket = kInvalidSocketFd; - } - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - if (mReadableSource) - { - dispatch_source_cancel(mReadableSource); - dispatch_release(mReadableSource); - } -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH -} - -void UDPEndPointImplSockets::Free() -{ - Close(); - Release(); -} - -CHIP_ERROR UDPEndPointImplSockets::GetSocket(IPAddressType addressType) -{ - if (mSocket == kInvalidSocketFd) - { - constexpr int type = (SOCK_DGRAM | SOCK_CLOEXEC); - constexpr int protocol = 0; - - int family = PF_UNSPEC; - - switch (addressType) - { - case IPAddressType::kIPv6: - family = PF_INET6; - break; - -#if INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv4: - family = PF_INET; - break; -#endif // INET_CONFIG_ENABLE_IPV4 - - default: - return INET_ERROR_WRONG_ADDRESS_TYPE; - } - - mSocket = ::socket(family, type, protocol); - if (mSocket == -1) - { - return CHIP_ERROR_POSIX(errno); - } - ReturnErrorOnFailure(static_cast(&GetSystemLayer())->StartWatchingSocket(mSocket, &mWatch)); - - mAddrType = addressType; - - // NOTE WELL: the errors returned by setsockopt() here are not - // returned as Inet layer CHIP_ERROR_POSIX(errno) - // codes because they are normally expected to fail on some - // platforms where the socket option code is defined in the - // header files but not [yet] implemented. Certainly, there is - // room to improve this by connecting the build configuration - // logic up to check for implementations of these options and - // to provide appropriate HAVE_xxxxx definitions accordingly. - - constexpr int one = 1; - int res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - static_cast(res); - -#ifdef SO_REUSEPORT - res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); - if (res != 0) - { - ChipLogError(Inet, "SO_REUSEPORT failed: %d", errno); - } -#endif // defined(SO_REUSEPORT) - - // If creating an IPv6 socket, tell the kernel that it will be - // IPv6 only. This makes it posible to bind two sockets to - // the same port, one for IPv4 and one for IPv6. - -#ifdef IPV6_V6ONLY - if (addressType == IPAddressType::kIPv6) - { - res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if (res != 0) - { - ChipLogError(Inet, "IPV6_V6ONLY failed: %d", errno); - } - } -#endif // defined(IPV6_V6ONLY) - -#if INET_CONFIG_ENABLE_IPV4 -#ifdef IP_PKTINFO - if (addressType == IPAddressType::kIPv4) - { - res = setsockopt(mSocket, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); - if (res != 0) - { - ChipLogError(Inet, "IP_PKTINFO failed: %d", errno); - } - } -#endif // defined(IP_PKTINFO) -#endif // INET_CONFIG_ENABLE_IPV4 - -#ifdef IPV6_RECVPKTINFO - if (addressType == IPAddressType::kIPv6) - { - res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); - if (res != 0) - { - ChipLogError(Inet, "IPV6_PKTINFO failed: %d", errno); - } - } -#endif // defined(IPV6_RECVPKTINFO) - - // On systems that support it, disable the delivery of SIGPIPE - // signals when writing to a closed socket. This is mostly - // needed on iOS which has the peculiar habit of sending - // SIGPIPEs on unconnected UDP sockets. -#ifdef SO_NOSIGPIPE - { - res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); - if (res != 0) - { - ChipLogError(Inet, "SO_NOSIGPIPE failed: %d", errno); - } - } -#endif // defined(SO_NOSIGPIPE) - } - else if (mAddrType != addressType) - { - return CHIP_ERROR_INCORRECT_STATE; - } - - return CHIP_NO_ERROR; -} - -// static -void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events, intptr_t data) -{ - reinterpret_cast(data)->HandlePendingIO(events); -} - -void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events) -{ - if (mState != State::kListening || OnMessageReceived == nullptr || !events.Has(System::SocketEventFlags::kRead)) - { - 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()) - { - struct iovec msgIOV; - SockAddr lPeerSockAddr; - uint8_t controlData[256]; - struct 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); - - ssize_t rcvLen = recvmsg(mSocket, &msgHeader, MSG_DONTWAIT); - - if (rcvLen < 0) - { - lStatus = CHIP_ERROR_POSIX(errno); - } - else if (rcvLen > lBuffer->AvailableDataLength()) - { - lStatus = CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG; - } - else - { - lBuffer->SetDataLength(static_cast(rcvLen)); - - if (lPeerSockAddr.any.sa_family == AF_INET6) - { - lPacketInfo.SrcAddress = IPAddress(lPeerSockAddr.in6.sin6_addr); - lPacketInfo.SrcPort = ntohs(lPeerSockAddr.in6.sin6_port); - } -#if INET_CONFIG_ENABLE_IPV4 - else if (lPeerSockAddr.any.sa_family == 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 (struct cmsghdr * controlHdr = CMSG_FIRSTHDR(&msgHeader); controlHdr != nullptr; - controlHdr = CMSG_NXTHDR(&msgHeader, controlHdr)) - { -#if INET_CONFIG_ENABLE_IPV4 -#ifdef IP_PKTINFO - if (controlHdr->cmsg_level == IPPROTO_IP && controlHdr->cmsg_type == IP_PKTINFO) - { - auto * inPktInfo = reinterpret_cast CMSG_DATA(controlHdr); - if (!CanCastTo(inPktInfo->ipi_ifindex)) - { - lStatus = CHIP_ERROR_INCORRECT_STATE; - break; - } - lPacketInfo.Interface = InterfaceId(static_cast(inPktInfo->ipi_ifindex)); - lPacketInfo.DestAddress = IPAddress(inPktInfo->ipi_addr); - continue; - } -#endif // defined(IP_PKTINFO) -#endif // INET_CONFIG_ENABLE_IPV4 - -#ifdef IPV6_PKTINFO - if (controlHdr->cmsg_level == IPPROTO_IPV6 && controlHdr->cmsg_type == IPV6_PKTINFO) - { - auto * in6PktInfo = reinterpret_cast CMSG_DATA(controlHdr); - if (!CanCastTo(in6PktInfo->ipi6_ifindex)) - { - lStatus = CHIP_ERROR_INCORRECT_STATE; - break; - } - lPacketInfo.Interface = InterfaceId(static_cast(in6PktInfo->ipi6_ifindex)); - lPacketInfo.DestAddress = IPAddress(in6PktInfo->ipi6_addr); - continue; - } -#endif // defined(IPV6_PKTINFO) - } - } - } - 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_POSIX(EAGAIN)) - { - OnReceiveError(this, lStatus, nullptr); - } - } -} - -#if IP_MULTICAST_LOOP || IPV6_MULTICAST_LOOP -static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, bool aLoopback, int aProtocol, int aOption) -{ - const unsigned int lValue = static_cast(aLoopback); - if (setsockopt(aSocket, aProtocol, aOption, &lValue, sizeof(lValue)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - - return CHIP_NO_ERROR; -} -#endif // IP_MULTICAST_LOOP || IPV6_MULTICAST_LOOP - -static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, IPVersion aIPVersion, bool aLoopback) -{ -#ifdef IPV6_MULTICAST_LOOP - CHIP_ERROR lRetval; - - switch (aIPVersion) - { - - case kIPVersion_6: - lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); - break; - -#if INET_CONFIG_ENABLE_IPV4 - case kIPVersion_4: - lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, IPPROTO_IP, IP_MULTICAST_LOOP); - break; -#endif // INET_CONFIG_ENABLE_IPV4 - - default: - lRetval = INET_ERROR_WRONG_ADDRESS_TYPE; - break; - } - - return (lRetval); -#else // IPV6_MULTICAST_LOOP - return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; -#endif // IPV6_MULTICAST_LOOP -} - -CHIP_ERROR UDPEndPointImplSockets::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 UDPEndPointImplSockets::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) -{ - IPAddress lInterfaceAddress; - bool lInterfaceAddressFound = false; - - for (InterfaceAddressIterator lAddressIterator; lAddressIterator.HasCurrent(); lAddressIterator.Next()) - { - const IPAddress lCurrentAddress = lAddressIterator.GetAddress(); - - if (lAddressIterator.GetInterfaceId() == aInterfaceId) - { - if (lCurrentAddress.IsIPv4()) - { - lInterfaceAddressFound = true; - lInterfaceAddress = lCurrentAddress; - break; - } - } - } - VerifyOrReturnError(lInterfaceAddressFound, INET_ERROR_ADDRESS_NOT_FOUND); - - struct ip_mreq lMulticastRequest; - memset(&lMulticastRequest, 0, sizeof(lMulticastRequest)); - lMulticastRequest.imr_interface = lInterfaceAddress.ToIPv4(); - lMulticastRequest.imr_multiaddr = aAddress.ToIPv4(); - - const int command = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; - if (setsockopt(mSocket, IPPROTO_IP, command, &lMulticastRequest, sizeof(lMulticastRequest)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - return CHIP_NO_ERROR; -} - -#endif // INET_CONFIG_ENABLE_IPV4 - -CHIP_ERROR UDPEndPointImplSockets::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 - -#ifdef IPV6_MULTICAST_IMPLEMENTED - const InterfaceId::PlatformType lIfIndex = aInterfaceId.GetPlatformInterface(); - - struct ipv6_mreq lMulticastRequest; - memset(&lMulticastRequest, 0, sizeof(lMulticastRequest)); - VerifyOrReturnError(CanCastTo(lIfIndex), CHIP_ERROR_UNEXPECTED_EVENT); - - lMulticastRequest.ipv6mr_interface = static_cast(lIfIndex); - lMulticastRequest.ipv6mr_multiaddr = aAddress.ToIPv6(); - - const int command = join ? INET_IPV6_ADD_MEMBERSHIP : INET_IPV6_DROP_MEMBERSHIP; - if (setsockopt(mSocket, IPPROTO_IPV6, command, &lMulticastRequest, sizeof(lMulticastRequest)) != 0) - { - return CHIP_ERROR_POSIX(errno); - } - return CHIP_NO_ERROR; -#else - return CHIP_ERROR_NOT_IMPLEMENTED; -#endif -} - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -CHIP_ERROR UDPEndPointImplNetworkFramework::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port, - InterfaceId intfId) -{ - nw_parameters_configure_protocol_block_t configure_tls; - nw_parameters_t parameters; - - if (intfId.IsPresent()) - { - return CHIP_ERROR_NOT_IMPLEMENTED; - } - - configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL; - parameters = nw_parameters_create_secure_udp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION); - VerifyOrReturnError(parameters != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - - ReturnErrorOnFailure(ConfigureProtocol(addressType, parameters)); - - nw_endpoint_t endpoint = nullptr; - CHIP_ERROR res = GetEndPoint(endpoint, addressType, address, port); - nw_parameters_set_local_endpoint(parameters, endpoint); - nw_release(endpoint); - ReturnErrorOnFailure(res); - - mDispatchQueue = dispatch_queue_create("inet_dispatch_global", DISPATCH_QUEUE_CONCURRENT); - VerifyOrReturnError(mDispatchQueue != nullptr, CHIP_ERROR_NO_MEMORY); - dispatch_retain(mDispatchQueue); - - mConnectionSemaphore = dispatch_semaphore_create(0); - VerifyOrReturnError(mConnectionSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); - dispatch_retain(mConnectionSemaphore); - - mSendSemaphore = dispatch_semaphore_create(0); - VerifyOrReturnError(mSendSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); - dispatch_retain(mSendSemaphore); - - mAddrType = addressType; - mConnection = nullptr; - mParameters = parameters; - - return CHIP_NO_ERROR; -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId) -{ - return INET_ERROR_UNKNOWN_INTERFACE; -} - -InterfaceId UDPEndPointImplNetworkFramework::GetBoundInterface() const -{ - return InterfaceId::Null(); -} - -uint16_t UDPEndPointImplNetworkFramework::GetBoundPort() const -{ - nw_endpoint_t endpoint = nw_parameters_copy_local_endpoint(mParameters); - return nw_endpoint_get_port(endpoint); -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::ListenImpl() -{ - return StartListener(); -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg) -{ - dispatch_data_t content; - - // Ensure the destination address type is compatible with the endpoint address type. - VerifyOrReturnError(mAddrType == pktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT); - - // For now the entire message must fit within a single buffer. - VerifyOrReturnError(msg->Next() == nullptr, CHIP_ERROR_MESSAGE_TOO_LONG); - - ReturnErrorOnFailure(GetConnection(pktInfo)); - - // Send a message, and wait for it to be dispatched. - content = dispatch_data_create(msg->Start(), msg->DataLength(), mDispatchQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); - - // If there is a current message pending and the state of the network connection change (e.g switch to a - // different network) the connection will enter a nw_connection_state_failed state and the completion handler - // will never be called. In such cases a signal is sent from the connection state change handler to release - // the semaphore. In this case the CHIP_ERROR will not update with the result of the completion handler. - // To make sure caller knows that sending a message has failed the following code consider there is an error - // _unless_ the completion handler says otherwise. - __block CHIP_ERROR res = CHIP_ERROR_UNEXPECTED_EVENT; - nw_connection_send(mConnection, content, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t error) { - if (error) - { - res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); - } - else - { - res = CHIP_NO_ERROR; - } - dispatch_semaphore_signal(mSendSemaphore); - }); - dispatch_release(content); - - dispatch_semaphore_wait(mSendSemaphore, DISPATCH_TIME_FOREVER); - - return res; -} - -void UDPEndPointImplNetworkFramework::CloseImpl() -{ - ReleaseAll(); -} - -void UDPEndPointImplNetworkFramework::ReleaseAll() -{ - - OnMessageReceived = nullptr; - OnReceiveError = nullptr; - - ReleaseConnection(); - ReleaseListener(); - - if (mParameters) - { - nw_release(mParameters); - mParameters = nullptr; - } - - if (mDispatchQueue) - { - dispatch_suspend(mDispatchQueue); - dispatch_release(mDispatchQueue); - mDispatchQueue = nullptr; - } - - if (mConnectionSemaphore) - { - dispatch_release(mConnectionSemaphore); - mConnectionSemaphore = nullptr; - } - - if (mListenerQueue) - { - dispatch_suspend(mListenerQueue); - dispatch_release(mListenerQueue); - mListenerQueue = nullptr; - } - - if (mListenerSemaphore) - { - dispatch_release(mListenerSemaphore); - mListenerSemaphore = nullptr; - } - - if (mSendSemaphore) - { - dispatch_release(mSendSemaphore); - mSendSemaphore = nullptr; - } -} - -void UDPEndPointImplNetworkFramework::Free() -{ - Close(); - Release(); -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback) -{ - return CHIP_ERROR_NOT_IMPLEMENTED; -} - -#if INET_CONFIG_ENABLE_IPV4 -CHIP_ERROR UDPEndPointImplNetworkFramework::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, - bool join) -{ - return CHIP_ERROR_NOT_IMPLEMENTED; -} -#endif // INET_CONFIG_ENABLE_IPV4 - -CHIP_ERROR UDPEndPointImplNetworkFramework::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, - bool join) -{ - return CHIP_ERROR_NOT_IMPLEMENTED; -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::ConfigureProtocol(IPAddressType aAddressType, const nw_parameters_t & aParameters) -{ - CHIP_ERROR res = CHIP_NO_ERROR; - - nw_protocol_stack_t protocolStack = nw_parameters_copy_default_protocol_stack(aParameters); - nw_protocol_options_t ipOptions = nw_protocol_stack_copy_internet_protocol(protocolStack); - - switch (aAddressType) - { - - case IPAddressType::kIPv6: - nw_ip_options_set_version(ipOptions, nw_ip_version_6); - break; - -#if INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv4: - nw_ip_options_set_version(ipOptions, nw_ip_version_4); - break; -#endif // INET_CONFIG_ENABLE_IPV4 - - default: - res = INET_ERROR_WRONG_ADDRESS_TYPE; - break; - } - nw_release(ipOptions); - nw_release(protocolStack); - - return res; -} - -void UDPEndPointImplNetworkFramework::GetPacketInfo(const nw_connection_t & aConnection, IPPacketInfo & aPacketInfo) -{ - nw_path_t path = nw_connection_copy_current_path(aConnection); - nw_endpoint_t dest_endpoint = nw_path_copy_effective_local_endpoint(path); - nw_endpoint_t src_endpoint = nw_path_copy_effective_remote_endpoint(path); - - aPacketInfo.Clear(); - aPacketInfo.SrcAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(src_endpoint)); - aPacketInfo.DestAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(dest_endpoint)); - aPacketInfo.SrcPort = nw_endpoint_get_port(src_endpoint); - aPacketInfo.DestPort = nw_endpoint_get_port(dest_endpoint); -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::GetEndPoint(nw_endpoint_t & aEndPoint, const IPAddressType aAddressType, - const IPAddress & aAddress, uint16_t aPort) -{ - char addrStr[INET6_ADDRSTRLEN]; - char portStr[INET_PORTSTRLEN]; - - // Note: aAddress.ToString will return the IPv6 Any address if the address type is Any, but that's not what - // we want if the locale endpoint is IPv4. - if (aAddressType == IPAddressType::kIPv4 && aAddress.Type() == IPAddressType::kAny) - { - const IPAddress anyAddr = IPAddress(aAddress.ToIPv4()); - anyAddr.ToString(addrStr, sizeof(addrStr)); - } - else - { - aAddress.ToString(addrStr, sizeof(addrStr)); - } - - snprintf(portStr, sizeof(portStr), "%u", aPort); - - aEndPoint = nw_endpoint_create_host(addrStr, portStr); - VerifyOrReturnError(aEndPoint != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - - return CHIP_NO_ERROR; -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::GetConnection(const IPPacketInfo * aPktInfo) -{ - VerifyOrReturnError(mParameters != nullptr, CHIP_ERROR_INCORRECT_STATE); - - nw_endpoint_t endpoint = nullptr; - nw_connection_t connection = nullptr; - - if (mConnection) - { - nw_path_t path = nw_connection_copy_current_path(mConnection); - nw_endpoint_t remote_endpoint = nw_path_copy_effective_remote_endpoint(path); - const IPAddress remote_address = IPAddress::FromSockAddr(*nw_endpoint_get_address(remote_endpoint)); - const uint16_t remote_port = nw_endpoint_get_port(remote_endpoint); - const bool isDifferentEndPoint = aPktInfo->DestPort != remote_port || aPktInfo->DestAddress != remote_address; - VerifyOrReturnError(isDifferentEndPoint, CHIP_NO_ERROR); - - ReturnErrorOnFailure(ReleaseConnection()); - } - - ReturnErrorOnFailure(GetEndPoint(endpoint, mAddrType, aPktInfo->DestAddress, aPktInfo->DestPort)); - - connection = nw_connection_create(endpoint, mParameters); - nw_release(endpoint); - - VerifyOrReturnError(connection != nullptr, CHIP_ERROR_INCORRECT_STATE); - - return StartConnection(connection); -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::StartListener() -{ - __block CHIP_ERROR res = CHIP_NO_ERROR; - nw_listener_t listener; - - VerifyOrReturnError(mListener == nullptr, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mListenerSemaphore == nullptr, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mListenerQueue == nullptr, CHIP_ERROR_INCORRECT_STATE); - - listener = nw_listener_create(mParameters); - VerifyOrReturnError(listener != nullptr, CHIP_ERROR_INCORRECT_STATE); - - mListenerSemaphore = dispatch_semaphore_create(0); - VerifyOrReturnError(mListenerSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); - dispatch_retain(mListenerSemaphore); - - mListenerQueue = dispatch_queue_create("inet_dispatch_listener", DISPATCH_QUEUE_CONCURRENT); - VerifyOrReturnError(mListenerQueue != nullptr, CHIP_ERROR_NO_MEMORY); - dispatch_retain(mListenerQueue); - - nw_listener_set_queue(listener, mListenerQueue); - - nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) { - ReleaseConnection(); - StartConnection(connection); - }); - - nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) { - switch (state) - { - - case nw_listener_state_invalid: - ChipLogDetail(Inet, "Listener: Invalid"); - res = CHIP_ERROR_INCORRECT_STATE; - nw_listener_cancel(listener); - break; - - case nw_listener_state_waiting: - ChipLogDetail(Inet, "Listener: Waiting"); - break; - - case nw_listener_state_failed: - ChipLogDetail(Inet, "Listener: Failed"); - res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); - break; - - case nw_listener_state_ready: - ChipLogDetail(Inet, "Listener: Ready"); - res = CHIP_NO_ERROR; - dispatch_semaphore_signal(mListenerSemaphore); - break; - - case nw_listener_state_cancelled: - ChipLogDetail(Inet, "Listener: Cancelled"); - if (res == CHIP_NO_ERROR) - { - res = CHIP_ERROR_CONNECTION_ABORTED; - } - - dispatch_semaphore_signal(mListenerSemaphore); - break; - } - }); - - nw_listener_start(listener); - dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); - ReturnErrorOnFailure(res); - - mListener = listener; - nw_retain(mListener); - return res; -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::StartConnection(nw_connection_t & aConnection) -{ - __block CHIP_ERROR res = CHIP_NO_ERROR; - - nw_connection_set_queue(aConnection, mDispatchQueue); - - nw_connection_set_state_changed_handler(aConnection, ^(nw_connection_state_t state, nw_error_t error) { - switch (state) - { - - case nw_connection_state_invalid: - ChipLogDetail(Inet, "Connection: Invalid"); - res = CHIP_ERROR_INCORRECT_STATE; - nw_connection_cancel(aConnection); - break; - - case nw_connection_state_preparing: - ChipLogDetail(Inet, "Connection: Preparing"); - res = CHIP_ERROR_INCORRECT_STATE; - break; - - case nw_connection_state_waiting: - ChipLogDetail(Inet, "Connection: Waiting"); - nw_connection_cancel(aConnection); - break; - - case nw_connection_state_failed: - ChipLogDetail(Inet, "Connection: Failed"); - res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); - break; - - case nw_connection_state_ready: - ChipLogDetail(Inet, "Connection: Ready"); - res = CHIP_NO_ERROR; - dispatch_semaphore_signal(mConnectionSemaphore); - break; - - case nw_connection_state_cancelled: - ChipLogDetail(Inet, "Connection: Cancelled"); - if (res == CHIP_NO_ERROR) - { - res = CHIP_ERROR_CONNECTION_ABORTED; - } - - dispatch_semaphore_signal(mConnectionSemaphore); - break; - } - }); - - nw_connection_start(aConnection); - dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); - SuccessOrExit(res); - - mConnection = aConnection; - nw_retain(mConnection); - HandleDataReceived(mConnection); - - return res; -} - -void UDPEndPointImplNetworkFramework::HandleDataReceived(const nw_connection_t & aConnection) -{ - nw_connection_receive_completion_t handler = - ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) { - dispatch_block_t schedule_next_receive = ^{ - if (receive_error == nullptr) - { - HandleDataReceived(aConnection); - } - else if (OnReceiveError != nullptr) - { - nw_error_domain_t error_domain = nw_error_get_error_domain(receive_error); - errno = nw_error_get_error_code(receive_error); - if (!(error_domain == nw_error_domain_posix && errno == ECANCELED)) - { - CHIP_ERROR error = CHIP_ERROR_POSIX(errno); - IPPacketInfo packetInfo; - GetPacketInfo(aConnection, packetInfo); - dispatch_async(mDispatchQueue, ^{ - OnReceiveError((UDPEndPoint *) this, error, &packetInfo); - }); - } - } - }; - - if (content != nullptr && OnMessageReceived != nullptr) - { - size_t count = dispatch_data_get_size(content); - System::PacketBufferHandle * packetBuffer = System::PacketBufferHandle::New(count); - dispatch_data_apply(content, ^(dispatch_data_t data, size_t offset, const void * buffer, size_t size) { - memmove(packetBuffer->Start() + offset, buffer, size); - return true; - }); - packetBuffer->SetDataLength(count); - - IPPacketInfo packetInfo; - GetPacketInfo(aConnection, packetInfo); - dispatch_async(mDispatchQueue, ^{ - OnMessageReceived((UDPEndPoint *) this, packetBuffer, &packetInfo); - }); - } - - schedule_next_receive(); - }; - - nw_connection_receive_message(aConnection, handler); -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseListener() -{ - VerifyOrReturnError(mListener, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE); - - nw_listener_cancel(mListener); - dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); - nw_release(mListener); - mListener = nullptr; - - return CHIP_NO_ERROR; -} - -CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseConnection() -{ - VerifyOrReturnError(mConnection, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE); - VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE); - - nw_connection_cancel(mConnection); - dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); - nw_release(mConnection); - mConnection = nullptr; - - return CHIP_NO_ERROR; -} - -#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK +namespace chip { +namespace Inet { CHIP_ERROR UDPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, InterfaceId intfId) { @@ -2022,66 +132,42 @@ void UDPEndPoint::Close() CHIP_ERROR UDPEndPoint::JoinMulticastGroup(InterfaceId aInterfaceId, const IPAddress & aAddress) { - CHIP_ERROR lRetval = CHIP_NO_ERROR; - - const IPAddressType lAddrType = aAddress.Type(); - lRetval = CheckMulticastGroupArgs(aInterfaceId, aAddress); - SuccessOrExit(lRetval); + VerifyOrReturnError(aAddress.IsMulticast(), INET_ERROR_WRONG_ADDRESS_TYPE); - switch (lAddrType) + switch (aAddress.Type()) { #if INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv4: { + case IPAddressType::kIPv4: return IPv4JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, true); - } - break; #endif // INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv6: { + case IPAddressType::kIPv6: return IPv6JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, true); - } - break; default: - lRetval = INET_ERROR_WRONG_ADDRESS_TYPE; - break; + return INET_ERROR_WRONG_ADDRESS_TYPE; } - -exit: - return (lRetval); } CHIP_ERROR UDPEndPoint::LeaveMulticastGroup(InterfaceId aInterfaceId, const IPAddress & aAddress) { - CHIP_ERROR lRetval = CHIP_ERROR_NOT_IMPLEMENTED; - - const IPAddressType lAddrType = aAddress.Type(); - lRetval = CheckMulticastGroupArgs(aInterfaceId, aAddress); - SuccessOrExit(lRetval); + VerifyOrReturnError(aAddress.IsMulticast(), INET_ERROR_WRONG_ADDRESS_TYPE); - switch (lAddrType) + switch (aAddress.Type()) { #if INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv4: { + case IPAddressType::kIPv4: return IPv4JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, false); - } - break; #endif // INET_CONFIG_ENABLE_IPV4 - case IPAddressType::kIPv6: { + case IPAddressType::kIPv6: return IPv6JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, false); - } - break; default: - lRetval = INET_ERROR_WRONG_ADDRESS_TYPE; - break; + return INET_ERROR_WRONG_ADDRESS_TYPE; } - -exit: - return (lRetval); } } // namespace Inet diff --git a/src/inet/UDPEndPoint.h b/src/inet/UDPEndPoint.h index 71ef054da488c2..f18720a63288ee 100644 --- a/src/inet/UDPEndPoint.h +++ b/src/inet/UDPEndPoint.h @@ -28,28 +28,15 @@ #pragma once +#include + #include #include #include #include #include -#include #include -#if CHIP_SYSTEM_CONFIG_USE_LWIP -#include -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS -#include -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS -#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK -#include -#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH -#include -#endif - namespace chip { namespace Inet { @@ -302,174 +289,6 @@ class DLL_EXPORT UDPEndPoint : public EndPointBasis virtual void CloseImpl() = 0; }; -#if CHIP_SYSTEM_CONFIG_USE_LWIP - -class UDPEndPointImplLwIP : public UDPEndPoint, public EndPointStateLwIP -{ -public: - UDPEndPointImplLwIP(EndPointManager & endPointManager) : UDPEndPoint(endPointManager) {} - - // 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; - - static struct netif * FindNetifFromInterfaceId(InterfaceId aInterfaceId); - static CHIP_ERROR LwIPBindInterface(struct udp_pcb * aUDP, InterfaceId intfId); - - void HandleDataReceived(chip::System::PacketBufferHandle && aBuffer); - - /** - * Get LwIP IP layer source and destination addressing information. - * - * @param[in] aBuffer The packet buffer containing the IP message. - * - * @returns a pointer to the address information on success; otherwise, - * nullptr if there is insufficient space in the packet for - * the address information. - * - * When using LwIP information about the packet is 'hidden' in the reserved space before the start of the - * data in the packet buffer. This is necessary because the system layer events only have two arguments, - * which in this case are used to convey the pointer to the end point and the pointer to the buffer. - * - * In most cases this trick of storing information before the data works because the first buffer in an - * LwIP IP message contains the space that was used for the Ethernet/IP/UDP headers. However, given the - * current size of the IPPacketInfo structure (40 bytes), it is possible for there to not be enough room - * to store the structure along with the payload in a single packet buffer. In practice, this should only - * happen for extremely large IPv4 packets that arrive without an Ethernet header. - */ - static IPPacketInfo * GetPacketInfo(const chip::System::PacketBufferHandle & aBuffer); - - CHIP_ERROR GetPCB(IPAddressType addrType4); -#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 - static void LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, const ip_addr_t * addr, u16_t port); -#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 - static void LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, ip_addr_t * addr, u16_t port); -#endif // LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 -}; - -using UDPEndPointImpl = UDPEndPointImplLwIP; - -#endif // CHIP_SYSTEM_CONFIG_USE_LWIP - -#if CHIP_SYSTEM_CONFIG_USE_SOCKETS - -class UDPEndPointImplSockets : public UDPEndPoint, public EndPointStateSockets -{ -public: - UDPEndPointImplSockets(EndPointManager & endPointManager) : - UDPEndPoint(endPointManager), mBoundIntfId(InterfaceId::Null()) - {} - - // 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); - void HandlePendingIO(System::SocketEvents events); - static void HandlePendingIO(System::SocketEvents events, intptr_t data); - - InterfaceId mBoundIntfId; - uint16_t mBoundPort; - -#if CHIP_SYSTEM_CONFIG_USE_DISPATCH - dispatch_source_t mReadableSource = nullptr; -#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH - -#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 -}; - -using UDPEndPointImpl = UDPEndPointImplSockets; - -#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS - -#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -class UDPEndPointImplNetworkFramework : public UDPEndPoint, public EndPointStateNetworkFramework -{ -public: - UDPEndPointImplNetworkFramework(EndPointManager & endPointManager) : UDPEndPoint(endPointManager) {} - - // 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; - - nw_listener_t mListener; - dispatch_semaphore_t mListenerSemaphore; - dispatch_queue_t mListenerQueue; - nw_connection_t mConnection; - dispatch_semaphore_t mConnectionSemaphore; - dispatch_queue_t mDispatchQueue; - dispatch_semaphore_t mSendSemaphore; - - CHIP_ERROR ConfigureProtocol(IPAddressType aAddressType, const nw_parameters_t & aParameters); - CHIP_ERROR StartListener(); - CHIP_ERROR GetConnection(const IPPacketInfo * aPktInfo); - CHIP_ERROR GetEndPoint(nw_endpoint_t & aEndpoint, const IPAddressType aAddressType, const IPAddress & aAddress, uint16_t aPort); - CHIP_ERROR StartConnection(nw_connection_t & aConnection); - void GetPacketInfo(const nw_connection_t & aConnection, IPPacketInfo & aPacketInfo); - void HandleDataReceived(const nw_connection_t & aConnection); - CHIP_ERROR ReleaseListener(); - CHIP_ERROR ReleaseConnection(); - void ReleaseAll(); -}; - -using UDPEndPointImpl = UDPEndPointImplNetworkFramework; - -#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK - -using UDPEndPointManagerImpl = EndPointManagerImplPool; - template <> struct EndPointProperties { diff --git a/src/inet/UDPEndPointImpl.h b/src/inet/UDPEndPointImpl.h new file mode 100644 index 00000000000000..6abf72c5d73257 --- /dev/null +++ b/src/inet/UDPEndPointImpl.h @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC + * Copyright (c) 2013-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. + */ + +/** + * This file includes a specific configured concrete implementation of Inet::UDPEndPoint. + */ + +#pragma once + +#include + +#ifdef INET_UDP_END_POINT_IMPL_CONFIG_FILE +#include INET_UDP_END_POINT_IMPL_CONFIG_FILE +#else // INET_UDP_END_POINT_IMPL_CONFIG_FILE +#error "INET_UDP_END_POINT_IMPL_CONFIG_FILE not defined" +#endif // INET_UDP_END_POINT_IMPL_CONFIG_FILE + +namespace chip { +namespace Inet { + +using UDPEndPointManagerImpl = EndPointManagerImplPool; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplLwIP.cpp b/src/inet/UDPEndPointImplLwIP.cpp new file mode 100644 index 00000000000000..6231f9248c6c34 --- /dev/null +++ b/src/inet/UDPEndPointImplLwIP.cpp @@ -0,0 +1,613 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC. + * Copyright (c) 2013-2018 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. + */ + +/** + * This file implements Inet::UDPEndPoint using LwIP. + */ + +#include + +#if CHIP_HAVE_CONFIG_H +#include // nogncheck +#endif // CHIP_HAVE_CONFIG_H + +#if INET_CONFIG_ENABLE_IPV4 +#include +#endif // INET_CONFIG_ENABLE_IPV4 + +#include +#include +#include +#include +#include +#include +#include + +#if !defined(RAW_FLAGS_MULTICAST_LOOP) || !defined(UDP_FLAGS_MULTICAST_LOOP) || !defined(raw_clear_flags) || \ + !defined(raw_set_flags) || !defined(udp_clear_flags) || !defined(udp_set_flags) +#define HAVE_LWIP_MULTICAST_LOOP 0 +#else +#define HAVE_LWIP_MULTICAST_LOOP 1 +#endif // !defined(RAW_FLAGS_MULTICAST_LOOP) || !defined(UDP_FLAGS_MULTICAST_LOOP) || !defined(raw_clear_flags) || + // !defined(raw_set_flags) || !defined(udp_clear_flags) || !defined(udp_set_flags) + +// unusual define check for LWIP_IPV6_ND is because espressif fork +// of LWIP does not define the _ND constant. +#if LWIP_IPV6_MLD && (!defined(LWIP_IPV6_ND) || LWIP_IPV6_ND) && LWIP_IPV6 +#define HAVE_IPV6_MULTICAST +#else +// Within Project CHIP multicast support is highly desirable: used for mDNS +// as well as group communication. +#undef HAVE_IPV6_MULTICAST +#endif + +namespace chip { +namespace Inet { + +CHIP_ERROR UDPEndPointImplLwIP::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port, + InterfaceId interfaceId) +{ + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // Make sure we have the appropriate type of PCB. + CHIP_ERROR res = GetPCB(addressType); + + // Bind the PCB to the specified address/port. + if (res == CHIP_NO_ERROR) + { +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + ip_addr_t ipAddr = address.ToLwIPAddr(); + + // TODO: IPAddress ANY has only one constant state, however addressType + // has separate IPV4 and IPV6 'any' settings. This tries to correct + // for this as LWIP default if IPv4 is compiled in is to consider + // 'any == any_v4' + // + // We may want to consider having separate AnyV4 and AnyV6 constants + // inside CHIP to resolve this ambiguity + if ((address.Type() == IPAddressType::kAny) && (addressType == IPAddressType::kIPv6)) + { + ipAddr = *IP6_ADDR_ANY; + } + + res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipAddr, port)); +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + if (addressType == IPAddressType::kIPv6) + { + ip6_addr_t ipv6Addr = address.ToIPv6(); + res = chip::System::MapErrorLwIP(udp_bind_ip6(mUDP, &ipv6Addr, port)); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addressType == IPAddressType::kIPv4) + { + ip4_addr_t ipv4Addr = address.ToIPv4(); + res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipv4Addr, port)); + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + res = INET_ERROR_WRONG_ADDRESS_TYPE; +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + } + + if (res == CHIP_NO_ERROR) + { + res = LwIPBindInterface(mUDP, interfaceId); + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return res; +} + +CHIP_ERROR UDPEndPointImplLwIP::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId) +{ + // A lock is required because the LwIP thread may be referring to intf_filter, + // while this code running in the Inet application is potentially modifying it. + // NOTE: this only supports LwIP interfaces whose number is no bigger than 9. + LOCK_TCPIP_CORE(); + + // Make sure we have the appropriate type of PCB. + CHIP_ERROR err = GetPCB(addrType); + + if (err == CHIP_NO_ERROR) + { + err = LwIPBindInterface(mUDP, intfId); + } + + UNLOCK_TCPIP_CORE(); + + return err; +} + +CHIP_ERROR UDPEndPointImplLwIP::LwIPBindInterface(struct udp_pcb * aUDP, InterfaceId intfId) +{ + struct netif * netifp = nullptr; + if (intfId.IsPresent()) + { + netifp = UDPEndPointImplLwIP::FindNetifFromInterfaceId(intfId); + if (netifp == nullptr) + { + return INET_ERROR_UNKNOWN_INTERFACE; + } + } + + udp_bind_netif(aUDP, netifp); + return CHIP_NO_ERROR; +} + +InterfaceId UDPEndPointImplLwIP::GetBoundInterface() const +{ +#if HAVE_LWIP_UDP_BIND_NETIF + return InterfaceId(netif_get_by_index(mUDP->netif_idx)); +#else + return InterfaceId(mUDP->intf_filter); +#endif +} + +uint16_t UDPEndPointImplLwIP::GetBoundPort() const +{ + return mUDP->local_port; +} + +CHIP_ERROR UDPEndPointImplLwIP::ListenImpl() +{ + // Lock LwIP stack + LOCK_TCPIP_CORE(); + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + udp_recv(mUDP, LwIPReceiveUDPMessage, this); +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + if (PCB_ISIPV6(mUDP)) + { + udp_recv_ip6(mUDP, LwIPReceiveUDPMessage, this); + } + else + { + udp_recv(mUDP, LwIPReceiveUDPMessage, this); + } +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UDPEndPointImplLwIP::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg) +{ + const IPAddress & destAddr = pktInfo->DestAddress; + + if (!msg.HasSoleOwnership()) + { + // when retaining a buffer, the caller expects the msg to be unmodified. + // LwIP stack will normally prepend the packet headers as the packet traverses + // the UDP/IP/netif layers, which normally modifies the packet. We need to clone + // msg into a fresh object in this case, and queues that for transmission, leaving + // the original msg available after return. + msg = msg.CloneData(); + VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_NO_MEMORY); + } + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // Make sure we have the appropriate type of PCB based on the destination address. + CHIP_ERROR res = GetPCB(destAddr.Type()); + if (res != CHIP_NO_ERROR) + { + UNLOCK_TCPIP_CORE(); + return res; + } + + // Send the message to the specified address/port. + // If an outbound interface has been specified, call a specific version of the UDP sendto() + // function that accepts the target interface. + // If a source address has been specified, temporarily override the local_ip of the PCB. + // This results in LwIP using the given address being as the source address for the generated + // packet, as if the PCB had been bound to that address. + err_t lwipErr = ERR_VAL; + const IPAddress & srcAddr = pktInfo->SrcAddress; + const uint16_t & destPort = pktInfo->DestPort; + const InterfaceId & intfId = pktInfo->Interface; + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + + ip_addr_t lwipSrcAddr = srcAddr.ToLwIPAddr(); + ip_addr_t lwipDestAddr = destAddr.ToLwIPAddr(); + + ip_addr_t boundAddr; + ip_addr_copy(boundAddr, mUDP->local_ip); + + if (!ip_addr_isany(&lwipSrcAddr)) + { + ip_addr_copy(mUDP->local_ip, lwipSrcAddr); + } + + if (intfId.IsPresent()) + { + lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, + intfId.GetPlatformInterface()); + } + else + { + lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort); + } + + ip_addr_copy(mUDP->local_ip, boundAddr); + +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + + ipX_addr_t boundAddr; + ipX_addr_copy(boundAddr, mUDP->local_ip); + + if (PCB_ISIPV6(mUDP)) + { + ip6_addr_t lwipSrcAddr = srcAddr.ToIPv6(); + ip6_addr_t lwipDestAddr = destAddr.ToIPv6(); + + if (!ip6_addr_isany(&lwipSrcAddr)) + { + ipX_addr_copy(mUDP->local_ip, *ip6_2_ipX(&lwipSrcAddr)); + } + + if (intfId.IsPresent()) + { + lwipErr = + udp_sendto_if_ip6(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, intfId); + } + else + { + lwipErr = udp_sendto_ip6(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort); + } + } + +#if INET_CONFIG_ENABLE_IPV4 + + else + { + ip4_addr_t lwipSrcAddr = srcAddr.ToIPv4(); + ip4_addr_t lwipDestAddr = destAddr.ToIPv4(); + ipX_addr_t boundAddr; + + if (!ip_addr_isany(&lwipSrcAddr)) + { + ipX_addr_copy(mUDP->local_ip, *ip_2_ipX(&lwipSrcAddr)); + } + + if (intfId.IsPresent()) + { + lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, intfId); + } + else + { + lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort); + } + } + + ipX_addr_copy(mUDP->local_ip, boundAddr); + +#endif // INET_CONFIG_ENABLE_IPV4 +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); + + if (lwipErr != ERR_OK) + { + res = chip::System::MapErrorLwIP(lwipErr); + } + + return res; +} + +void UDPEndPointImplLwIP::CloseImpl() +{ + + // Lock LwIP stack + LOCK_TCPIP_CORE(); + + // Since UDP PCB is released synchronously here, but UDP endpoint itself might have to wait + // for destruction asynchronously, there could be more allocated UDP endpoints than UDP PCBs. + if (mUDP != nullptr) + { + udp_remove(mUDP); + mUDP = nullptr; + mLwIPEndPointType = LwIPEndPointType::Unknown; + } + + // Unlock LwIP stack + UNLOCK_TCPIP_CORE(); +} + +void UDPEndPointImplLwIP::Free() +{ + Close(); + Release(); +} + +void UDPEndPointImplLwIP::HandleDataReceived(System::PacketBufferHandle && msg) +{ + if ((mState == State::kListening) && (OnMessageReceived != nullptr)) + { + const IPPacketInfo * pktInfo = GetPacketInfo(msg); + + if (pktInfo != nullptr) + { + const IPPacketInfo pktInfoCopy = *pktInfo; // copy the address info so that the app can free the + // PacketBuffer without affecting access to address info. + OnMessageReceived(this, std::move(msg), &pktInfoCopy); + } + else + { + if (OnReceiveError != nullptr) + { + OnReceiveError(this, CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG, nullptr); + } + } + } +} + +CHIP_ERROR UDPEndPointImplLwIP::GetPCB(IPAddressType addrType) +{ + // IMPORTANT: This method MUST be called with the LwIP stack LOCKED! + + // If a PCB hasn't been allocated yet... + if (mUDP == nullptr) + { + // Allocate a PCB of the appropriate type. + if (addrType == IPAddressType::kIPv6) + { +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + mUDP = udp_new_ip_type(IPADDR_TYPE_V6); +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + mUDP = udp_new_ip6(); +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + } +#if INET_CONFIG_ENABLE_IPV4 + else if (addrType == IPAddressType::kIPv4) + { +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + mUDP = udp_new_ip_type(IPADDR_TYPE_V4); +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + mUDP = udp_new(); +#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5 + } +#endif // INET_CONFIG_ENABLE_IPV4 + else + { + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + // Fail if the system has run out of PCBs. + if (mUDP == nullptr) + { + ChipLogError(Inet, "Unable to allocate UDP PCB"); + return CHIP_ERROR_NO_MEMORY; + } + + // Allow multiple bindings to the same port. + ip_set_option(mUDP, SOF_REUSEADDR); + } + + // Otherwise, verify that the existing PCB is the correct type... + else + { + IPAddressType pcbAddrType; + + // Get the address type of the existing PCB. +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + switch (static_cast(IP_GET_TYPE(&mUDP->local_ip))) + { + case IPADDR_TYPE_V6: + pcbAddrType = IPAddressType::kIPv6; + break; +#if INET_CONFIG_ENABLE_IPV4 + case IPADDR_TYPE_V4: + pcbAddrType = IPAddressType::kIPv4; + break; +#endif // INET_CONFIG_ENABLE_IPV4 + default: + return INET_ERROR_WRONG_ADDRESS_TYPE; + } +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 +#if INET_CONFIG_ENABLE_IPV4 + pcbAddrType = PCB_ISIPV6(mUDP) ? IPAddressType::kIPv6 : IPAddressType::kIPv4; +#else // !INET_CONFIG_ENABLE_IPV4 + pcbAddrType = IPAddressType::kIPv6; +#endif // !INET_CONFIG_ENABLE_IPV4 +#endif // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + + // Fail if the existing PCB is not the correct type. + VerifyOrReturnError(addrType == pcbAddrType, INET_ERROR_WRONG_ADDRESS_TYPE); + } + + return CHIP_NO_ERROR; +} + +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 +void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, const ip_addr_t * addr, + u16_t port) +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 +void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, ip_addr_t * addr, u16_t port) +#endif // LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 +{ + UDPEndPointImplLwIP * ep = static_cast(arg); + IPPacketInfo * pktInfo = nullptr; + System::PacketBufferHandle buf = System::PacketBufferHandle::Adopt(p); + if (buf->HasChainedBuffer()) + { + // Try the simple expedient of flattening in-place. + buf->CompactHead(); + } + + if (buf->HasChainedBuffer()) + { + // Have to allocate a new big-enough buffer and copy. + uint16_t messageSize = buf->TotalLength(); + System::PacketBufferHandle copy = System::PacketBufferHandle::New(messageSize, 0); + if (copy.IsNull() || buf->Read(copy->Start(), messageSize) != CHIP_NO_ERROR) + { + ChipLogError(Inet, "No memory to flatten incoming packet buffer chain of size %" PRIu16, buf->TotalLength()); + return; + } + buf = std::move(copy); + } + + pktInfo = GetPacketInfo(buf); + if (pktInfo != nullptr) + { +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + pktInfo->SrcAddress = IPAddress(*addr); + pktInfo->DestAddress = IPAddress(*ip_current_dest_addr()); +#else // LWIP_VERSION_MAJOR <= 1 + if (PCB_ISIPV6(pcb)) + { + pktInfo->SrcAddress = IPAddress(*(ip6_addr_t *) addr); + pktInfo->DestAddress = IPAddress(*ip6_current_dest_addr()); + } +#if INET_CONFIG_ENABLE_IPV4 + else + { + pktInfo->SrcAddress = IPAddress(*addr); + pktInfo->DestAddress = IPAddress(*ip_current_dest_addr()); + } +#endif // INET_CONFIG_ENABLE_IPV4 +#endif // LWIP_VERSION_MAJOR <= 1 + + pktInfo->Interface = InterfaceId(ip_current_netif()); + pktInfo->SrcPort = port; + pktInfo->DestPort = pcb->local_port; + } + + ep->Retain(); + CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda([ep, p = System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(buf)] { + ep->HandleDataReceived(System::PacketBufferHandle::Adopt(p)); + ep->Release(); + }); + if (err == CHIP_NO_ERROR) + { + // If ScheduleLambda() succeeded, it has ownership of the buffer, so we need to release it (without freeing it). + static_cast(std::move(buf).UnsafeRelease()); + } + else + { + ep->Release(); + } +} + +CHIP_ERROR UDPEndPointImplLwIP::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback) +{ +#if HAVE_LWIP_MULTICAST_LOOP + if (mLwIPEndPointType == LwIPEndPointType::UDP) + { + if (aLoopback) + { + udp_set_flags(mUDP, UDP_FLAGS_MULTICAST_LOOP); + } + else + { + udp_clear_flags(mUDP, UDP_FLAGS_MULTICAST_LOOP); + } + return CHIP_NO_ERROR; + } +#endif // HAVE_LWIP_MULTICAST_LOOP + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +} + +#if INET_CONFIG_ENABLE_IPV4 +CHIP_ERROR UDPEndPointImplLwIP::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) +{ +#if LWIP_IPV4 && LWIP_IGMP + const auto method = join ? igmp_joingroup_netif : igmp_leavegroup_netif; + + struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId); + VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE); + + const ip4_addr_t lIPv4Address = aAddress.ToIPv4(); + const err_t lStatus = method(lNetif, &lIPv4Address); + if (lStatus == ERR_MEM) + { + return CHIP_ERROR_NO_MEMORY; + } + return chip::System::MapErrorLwIP(lStatus); +#else // LWIP_IPV4 && LWIP_IGMP + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif // LWIP_IPV4 && LWIP_IGMP +} +#endif // INET_CONFIG_ENABLE_IPV4 + +CHIP_ERROR UDPEndPointImplLwIP::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) +{ +#ifdef HAVE_IPV6_MULTICAST + const auto method = join ? mld6_joingroup_netif : mld6_leavegroup_netif; + + struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId); + VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE); + + const ip6_addr_t lIPv6Address = aAddress.ToIPv6(); + const err_t lStatus = method(lNetif, &lIPv6Address); + if (lStatus == ERR_MEM) + { + return CHIP_ERROR_NO_MEMORY; + } + + return chip::System::MapErrorLwIP(lStatus); +#else // HAVE_IPV6_MULTICAST + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif // HAVE_IPV6_MULTICAST +} + +struct netif * UDPEndPointImplLwIP::FindNetifFromInterfaceId(InterfaceId aInterfaceId) +{ + struct netif * lRetval = nullptr; + +#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH) + NETIF_FOREACH(lRetval) + { + if (lRetval == aInterfaceId.GetPlatformInterface()) + { + break; + } + } +#else // LWIP_VERSION_MAJOR < 2 || !defined(NETIF_FOREACH) + for (lRetval = netif_list; lRetval != nullptr && lRetval != aInterfaceId.GetPlatformInterface(); lRetval = lRetval->next) + ; +#endif // LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH) + + return (lRetval); +} + +IPPacketInfo * UDPEndPointImplLwIP::GetPacketInfo(const System::PacketBufferHandle & aBuffer) +{ + if (!aBuffer->EnsureReservedSize(sizeof(IPPacketInfo) + 3)) + { + return nullptr; + } + + uintptr_t lStart = (uintptr_t) aBuffer->Start(); + uintptr_t lPacketInfoStart = lStart - sizeof(IPPacketInfo); + + // Align to a 4-byte boundary + return reinterpret_cast(lPacketInfoStart & ~(sizeof(uint32_t) - 1)); +} + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplLwIP.h b/src/inet/UDPEndPointImplLwIP.h new file mode 100644 index 00000000000000..7319526c8affdd --- /dev/null +++ b/src/inet/UDPEndPointImplLwIP.h @@ -0,0 +1,92 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC + * Copyright (c) 2013-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. + */ + +/** + * This file declares an implementation of Inet::UDPEndPoint using LwIP. + */ + +#pragma once + +#include +#include + +namespace chip { +namespace Inet { + +class UDPEndPointImplLwIP : public UDPEndPoint, public EndPointStateLwIP +{ +public: + UDPEndPointImplLwIP(EndPointManager & endPointManager) : UDPEndPoint(endPointManager) {} + + // 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; + + static struct netif * FindNetifFromInterfaceId(InterfaceId aInterfaceId); + static CHIP_ERROR LwIPBindInterface(struct udp_pcb * aUDP, InterfaceId intfId); + + void HandleDataReceived(chip::System::PacketBufferHandle && aBuffer); + + /** + * Get LwIP IP layer source and destination addressing information. + * + * @param[in] aBuffer The packet buffer containing the IP message. + * + * @returns a pointer to the address information on success; otherwise, + * nullptr if there is insufficient space in the packet for + * the address information. + * + * When using LwIP information about the packet is 'hidden' in the reserved space before the start of the + * data in the packet buffer. This is necessary because the system layer events only have two arguments, + * which in this case are used to convey the pointer to the end point and the pointer to the buffer. + * + * In most cases this trick of storing information before the data works because the first buffer in an + * LwIP IP message contains the space that was used for the Ethernet/IP/UDP headers. However, given the + * current size of the IPPacketInfo structure (40 bytes), it is possible for there to not be enough room + * to store the structure along with the payload in a single packet buffer. In practice, this should only + * happen for extremely large IPv4 packets that arrive without an Ethernet header. + */ + static IPPacketInfo * GetPacketInfo(const chip::System::PacketBufferHandle & aBuffer); + + CHIP_ERROR GetPCB(IPAddressType addrType4); +#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 + static void LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, const ip_addr_t * addr, u16_t port); +#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5 + static void LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, ip_addr_t * addr, u16_t port); +#endif // LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5 +}; + +using UDPEndPointImpl = UDPEndPointImplLwIP; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplNetworkFramework.cpp b/src/inet/UDPEndPointImplNetworkFramework.cpp new file mode 100644 index 00000000000000..35f6d135a0fc08 --- /dev/null +++ b/src/inet/UDPEndPointImplNetworkFramework.cpp @@ -0,0 +1,533 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC. + * Copyright (c) 2013-2018 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. + */ + +/** + * This file implements Inet::UDPEndPoint using Network Framework. + */ + +#define __APPLE_USE_RFC_3542 +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define INET_PORTSTRLEN 6 + +namespace chip { +namespace Inet { + +CHIP_ERROR UDPEndPointImplNetworkFramework::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port, + InterfaceId intfId) +{ + nw_parameters_configure_protocol_block_t configure_tls; + nw_parameters_t parameters; + + if (intfId.IsPresent()) + { + return CHIP_ERROR_NOT_IMPLEMENTED; + } + + configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL; + parameters = nw_parameters_create_secure_udp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION); + VerifyOrReturnError(parameters != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + ReturnErrorOnFailure(ConfigureProtocol(addressType, parameters)); + + nw_endpoint_t endpoint = nullptr; + CHIP_ERROR res = GetEndPoint(endpoint, addressType, address, port); + nw_parameters_set_local_endpoint(parameters, endpoint); + nw_release(endpoint); + ReturnErrorOnFailure(res); + + mDispatchQueue = dispatch_queue_create("inet_dispatch_global", DISPATCH_QUEUE_CONCURRENT); + VerifyOrReturnError(mDispatchQueue != nullptr, CHIP_ERROR_NO_MEMORY); + dispatch_retain(mDispatchQueue); + + mConnectionSemaphore = dispatch_semaphore_create(0); + VerifyOrReturnError(mConnectionSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); + dispatch_retain(mConnectionSemaphore); + + mSendSemaphore = dispatch_semaphore_create(0); + VerifyOrReturnError(mSendSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); + dispatch_retain(mSendSemaphore); + + mAddrType = addressType; + mConnection = nullptr; + mParameters = parameters; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId) +{ + return INET_ERROR_UNKNOWN_INTERFACE; +} + +InterfaceId UDPEndPointImplNetworkFramework::GetBoundInterface() const +{ + return InterfaceId::Null(); +} + +uint16_t UDPEndPointImplNetworkFramework::GetBoundPort() const +{ + nw_endpoint_t endpoint = nw_parameters_copy_local_endpoint(mParameters); + return nw_endpoint_get_port(endpoint); +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::ListenImpl() +{ + return StartListener(); +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg) +{ + dispatch_data_t content; + + // Ensure the destination address type is compatible with the endpoint address type. + VerifyOrReturnError(mAddrType == pktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT); + + // For now the entire message must fit within a single buffer. + VerifyOrReturnError(msg->Next() == nullptr, CHIP_ERROR_MESSAGE_TOO_LONG); + + ReturnErrorOnFailure(GetConnection(pktInfo)); + + // Send a message, and wait for it to be dispatched. + content = dispatch_data_create(msg->Start(), msg->DataLength(), mDispatchQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + + // If there is a current message pending and the state of the network connection change (e.g switch to a + // different network) the connection will enter a nw_connection_state_failed state and the completion handler + // will never be called. In such cases a signal is sent from the connection state change handler to release + // the semaphore. In this case the CHIP_ERROR will not update with the result of the completion handler. + // To make sure caller knows that sending a message has failed the following code consider there is an error + // _unless_ the completion handler says otherwise. + __block CHIP_ERROR res = CHIP_ERROR_UNEXPECTED_EVENT; + nw_connection_send(mConnection, content, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t error) { + if (error) + { + res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); + } + else + { + res = CHIP_NO_ERROR; + } + dispatch_semaphore_signal(mSendSemaphore); + }); + dispatch_release(content); + + dispatch_semaphore_wait(mSendSemaphore, DISPATCH_TIME_FOREVER); + + return res; +} + +void UDPEndPointImplNetworkFramework::CloseImpl() +{ + ReleaseAll(); +} + +void UDPEndPointImplNetworkFramework::ReleaseAll() +{ + + OnMessageReceived = nullptr; + OnReceiveError = nullptr; + + ReleaseConnection(); + ReleaseListener(); + + if (mParameters) + { + nw_release(mParameters); + mParameters = nullptr; + } + + if (mDispatchQueue) + { + dispatch_suspend(mDispatchQueue); + dispatch_release(mDispatchQueue); + mDispatchQueue = nullptr; + } + + if (mConnectionSemaphore) + { + dispatch_release(mConnectionSemaphore); + mConnectionSemaphore = nullptr; + } + + if (mListenerQueue) + { + dispatch_suspend(mListenerQueue); + dispatch_release(mListenerQueue); + mListenerQueue = nullptr; + } + + if (mListenerSemaphore) + { + dispatch_release(mListenerSemaphore); + mListenerSemaphore = nullptr; + } + + if (mSendSemaphore) + { + dispatch_release(mSendSemaphore); + mSendSemaphore = nullptr; + } +} + +void UDPEndPointImplNetworkFramework::Free() +{ + Close(); + Release(); +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +#if INET_CONFIG_ENABLE_IPV4 +CHIP_ERROR UDPEndPointImplNetworkFramework::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, + bool join) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} +#endif // INET_CONFIG_ENABLE_IPV4 + +CHIP_ERROR UDPEndPointImplNetworkFramework::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, + bool join) +{ + return CHIP_ERROR_NOT_IMPLEMENTED; +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::ConfigureProtocol(IPAddressType aAddressType, const nw_parameters_t & aParameters) +{ + CHIP_ERROR res = CHIP_NO_ERROR; + + nw_protocol_stack_t protocolStack = nw_parameters_copy_default_protocol_stack(aParameters); + nw_protocol_options_t ipOptions = nw_protocol_stack_copy_internet_protocol(protocolStack); + + switch (aAddressType) + { + + case IPAddressType::kIPv6: + nw_ip_options_set_version(ipOptions, nw_ip_version_6); + break; + +#if INET_CONFIG_ENABLE_IPV4 + case IPAddressType::kIPv4: + nw_ip_options_set_version(ipOptions, nw_ip_version_4); + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + res = INET_ERROR_WRONG_ADDRESS_TYPE; + break; + } + nw_release(ipOptions); + nw_release(protocolStack); + + return res; +} + +void UDPEndPointImplNetworkFramework::GetPacketInfo(const nw_connection_t & aConnection, IPPacketInfo & aPacketInfo) +{ + nw_path_t path = nw_connection_copy_current_path(aConnection); + nw_endpoint_t dest_endpoint = nw_path_copy_effective_local_endpoint(path); + nw_endpoint_t src_endpoint = nw_path_copy_effective_remote_endpoint(path); + + aPacketInfo.Clear(); + aPacketInfo.SrcAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(src_endpoint)); + aPacketInfo.DestAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(dest_endpoint)); + aPacketInfo.SrcPort = nw_endpoint_get_port(src_endpoint); + aPacketInfo.DestPort = nw_endpoint_get_port(dest_endpoint); +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::GetEndPoint(nw_endpoint_t & aEndPoint, const IPAddressType aAddressType, + const IPAddress & aAddress, uint16_t aPort) +{ + char addrStr[INET6_ADDRSTRLEN]; + char portStr[INET_PORTSTRLEN]; + + // Note: aAddress.ToString will return the IPv6 Any address if the address type is Any, but that's not what + // we want if the locale endpoint is IPv4. + if (aAddressType == IPAddressType::kIPv4 && aAddress.Type() == IPAddressType::kAny) + { + const IPAddress anyAddr = IPAddress(aAddress.ToIPv4()); + anyAddr.ToString(addrStr, sizeof(addrStr)); + } + else + { + aAddress.ToString(addrStr, sizeof(addrStr)); + } + + snprintf(portStr, sizeof(portStr), "%u", aPort); + + aEndPoint = nw_endpoint_create_host(addrStr, portStr); + VerifyOrReturnError(aEndPoint != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::GetConnection(const IPPacketInfo * aPktInfo) +{ + VerifyOrReturnError(mParameters != nullptr, CHIP_ERROR_INCORRECT_STATE); + + nw_endpoint_t endpoint = nullptr; + nw_connection_t connection = nullptr; + + if (mConnection) + { + nw_path_t path = nw_connection_copy_current_path(mConnection); + nw_endpoint_t remote_endpoint = nw_path_copy_effective_remote_endpoint(path); + const IPAddress remote_address = IPAddress::FromSockAddr(*nw_endpoint_get_address(remote_endpoint)); + const uint16_t remote_port = nw_endpoint_get_port(remote_endpoint); + const bool isDifferentEndPoint = aPktInfo->DestPort != remote_port || aPktInfo->DestAddress != remote_address; + VerifyOrReturnError(isDifferentEndPoint, CHIP_NO_ERROR); + + ReturnErrorOnFailure(ReleaseConnection()); + } + + ReturnErrorOnFailure(GetEndPoint(endpoint, mAddrType, aPktInfo->DestAddress, aPktInfo->DestPort)); + + connection = nw_connection_create(endpoint, mParameters); + nw_release(endpoint); + + VerifyOrReturnError(connection != nullptr, CHIP_ERROR_INCORRECT_STATE); + + return StartConnection(connection); +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::StartListener() +{ + __block CHIP_ERROR res = CHIP_NO_ERROR; + nw_listener_t listener; + + VerifyOrReturnError(mListener == nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mListenerSemaphore == nullptr, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mListenerQueue == nullptr, CHIP_ERROR_INCORRECT_STATE); + + listener = nw_listener_create(mParameters); + VerifyOrReturnError(listener != nullptr, CHIP_ERROR_INCORRECT_STATE); + + mListenerSemaphore = dispatch_semaphore_create(0); + VerifyOrReturnError(mListenerSemaphore != nullptr, CHIP_ERROR_NO_MEMORY); + dispatch_retain(mListenerSemaphore); + + mListenerQueue = dispatch_queue_create("inet_dispatch_listener", DISPATCH_QUEUE_CONCURRENT); + VerifyOrReturnError(mListenerQueue != nullptr, CHIP_ERROR_NO_MEMORY); + dispatch_retain(mListenerQueue); + + nw_listener_set_queue(listener, mListenerQueue); + + nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) { + ReleaseConnection(); + StartConnection(connection); + }); + + nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) { + switch (state) + { + + case nw_listener_state_invalid: + ChipLogDetail(Inet, "Listener: Invalid"); + res = CHIP_ERROR_INCORRECT_STATE; + nw_listener_cancel(listener); + break; + + case nw_listener_state_waiting: + ChipLogDetail(Inet, "Listener: Waiting"); + break; + + case nw_listener_state_failed: + ChipLogDetail(Inet, "Listener: Failed"); + res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); + break; + + case nw_listener_state_ready: + ChipLogDetail(Inet, "Listener: Ready"); + res = CHIP_NO_ERROR; + dispatch_semaphore_signal(mListenerSemaphore); + break; + + case nw_listener_state_cancelled: + ChipLogDetail(Inet, "Listener: Cancelled"); + if (res == CHIP_NO_ERROR) + { + res = CHIP_ERROR_CONNECTION_ABORTED; + } + + dispatch_semaphore_signal(mListenerSemaphore); + break; + } + }); + + nw_listener_start(listener); + dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); + ReturnErrorOnFailure(res); + + mListener = listener; + nw_retain(mListener); + return res; +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::StartConnection(nw_connection_t & aConnection) +{ + __block CHIP_ERROR res = CHIP_NO_ERROR; + + nw_connection_set_queue(aConnection, mDispatchQueue); + + nw_connection_set_state_changed_handler(aConnection, ^(nw_connection_state_t state, nw_error_t error) { + switch (state) + { + + case nw_connection_state_invalid: + ChipLogDetail(Inet, "Connection: Invalid"); + res = CHIP_ERROR_INCORRECT_STATE; + nw_connection_cancel(aConnection); + break; + + case nw_connection_state_preparing: + ChipLogDetail(Inet, "Connection: Preparing"); + res = CHIP_ERROR_INCORRECT_STATE; + break; + + case nw_connection_state_waiting: + ChipLogDetail(Inet, "Connection: Waiting"); + nw_connection_cancel(aConnection); + break; + + case nw_connection_state_failed: + ChipLogDetail(Inet, "Connection: Failed"); + res = CHIP_ERROR_POSIX(nw_error_get_error_code(error)); + break; + + case nw_connection_state_ready: + ChipLogDetail(Inet, "Connection: Ready"); + res = CHIP_NO_ERROR; + dispatch_semaphore_signal(mConnectionSemaphore); + break; + + case nw_connection_state_cancelled: + ChipLogDetail(Inet, "Connection: Cancelled"); + if (res == CHIP_NO_ERROR) + { + res = CHIP_ERROR_CONNECTION_ABORTED; + } + + dispatch_semaphore_signal(mConnectionSemaphore); + break; + } + }); + + nw_connection_start(aConnection); + dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); + SuccessOrExit(res); + + mConnection = aConnection; + nw_retain(mConnection); + HandleDataReceived(mConnection); + + return res; +} + +void UDPEndPointImplNetworkFramework::HandleDataReceived(const nw_connection_t & aConnection) +{ + nw_connection_receive_completion_t handler = + ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) { + dispatch_block_t schedule_next_receive = ^{ + if (receive_error == nullptr) + { + HandleDataReceived(aConnection); + } + else if (OnReceiveError != nullptr) + { + nw_error_domain_t error_domain = nw_error_get_error_domain(receive_error); + errno = nw_error_get_error_code(receive_error); + if (!(error_domain == nw_error_domain_posix && errno == ECANCELED)) + { + CHIP_ERROR error = CHIP_ERROR_POSIX(errno); + IPPacketInfo packetInfo; + GetPacketInfo(aConnection, packetInfo); + dispatch_async(mDispatchQueue, ^{ + OnReceiveError((UDPEndPoint *) this, error, &packetInfo); + }); + } + } + }; + + if (content != nullptr && OnMessageReceived != nullptr) + { + size_t count = dispatch_data_get_size(content); + System::PacketBufferHandle * packetBuffer = System::PacketBufferHandle::New(count); + dispatch_data_apply(content, ^(dispatch_data_t data, size_t offset, const void * buffer, size_t size) { + memmove(packetBuffer->Start() + offset, buffer, size); + return true; + }); + packetBuffer->SetDataLength(count); + + IPPacketInfo packetInfo; + GetPacketInfo(aConnection, packetInfo); + dispatch_async(mDispatchQueue, ^{ + OnMessageReceived((UDPEndPoint *) this, packetBuffer, &packetInfo); + }); + } + + schedule_next_receive(); + }; + + nw_connection_receive_message(aConnection, handler); +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseListener() +{ + VerifyOrReturnError(mListener, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE); + + nw_listener_cancel(mListener); + dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER); + nw_release(mListener); + mListener = nullptr; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseConnection() +{ + VerifyOrReturnError(mConnection, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE); + VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE); + + nw_connection_cancel(mConnection); + dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER); + nw_release(mConnection); + mConnection = nullptr; + + return CHIP_NO_ERROR; +} + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplNetworkFramework.h b/src/inet/UDPEndPointImplNetworkFramework.h new file mode 100644 index 00000000000000..55c5ef181212e4 --- /dev/null +++ b/src/inet/UDPEndPointImplNetworkFramework.h @@ -0,0 +1,77 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC + * Copyright (c) 2013-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. + */ + +/** + * This file declares an implementation of Inet::UDPEndPoint using Network Framework. + */ + +#pragma once + +#include + +namespace chip { +namespace Inet { + +class UDPEndPointImplNetworkFramework : public UDPEndPoint, public EndPointStateNetworkFramework +{ +public: + UDPEndPointImplNetworkFramework(EndPointManager & endPointManager) : UDPEndPoint(endPointManager) {} + + // 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; + + nw_listener_t mListener; + dispatch_semaphore_t mListenerSemaphore; + dispatch_queue_t mListenerQueue; + nw_connection_t mConnection; + dispatch_semaphore_t mConnectionSemaphore; + dispatch_queue_t mDispatchQueue; + dispatch_semaphore_t mSendSemaphore; + + CHIP_ERROR ConfigureProtocol(IPAddressType aAddressType, const nw_parameters_t & aParameters); + CHIP_ERROR StartListener(); + CHIP_ERROR GetConnection(const IPPacketInfo * aPktInfo); + CHIP_ERROR GetEndPoint(nw_endpoint_t & aEndpoint, const IPAddressType aAddressType, const IPAddress & aAddress, uint16_t aPort); + CHIP_ERROR StartConnection(nw_connection_t & aConnection); + void GetPacketInfo(const nw_connection_t & aConnection, IPPacketInfo & aPacketInfo); + void HandleDataReceived(const nw_connection_t & aConnection); + CHIP_ERROR ReleaseListener(); + CHIP_ERROR ReleaseConnection(); + void ReleaseAll(); +}; + +using UDPEndPointImpl = UDPEndPointImplNetworkFramework; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplSockets.cpp b/src/inet/UDPEndPointImplSockets.cpp new file mode 100644 index 00000000000000..ffae9f1f533d34 --- /dev/null +++ b/src/inet/UDPEndPointImplSockets.cpp @@ -0,0 +1,819 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC. + * Copyright (c) 2013-2018 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. + */ + +/** + * This file implements Inet::UDPEndPoint using sockets. + */ + +#include + +#include +#include +#include + +#if HAVE_SYS_SOCKET_H +#include +#endif // HAVE_SYS_SOCKET_H + +#include +#include +#include +#include +#include +#include + +// SOCK_CLOEXEC not defined on all platforms, e.g. iOS/macOS: +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 0 +#endif + +#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS +#include "ZephyrSocket.h" +#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS + +/* + * Some systems define both IPV6_{ADD,DROP}_MEMBERSHIP and + * IPV6_{JOIN,LEAVE}_GROUP while others only define + * IPV6_{JOIN,LEAVE}_GROUP. Prefer the "_MEMBERSHIP" flavor for + * parallelism with IPv4 and create the alias to the availabile + * definitions. + */ +#if defined(IPV6_ADD_MEMBERSHIP) +#define INET_IPV6_ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP +#elif defined(IPV6_JOIN_GROUP) +#define INET_IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#elif !__ZEPHYR__ +#error \ + "Neither IPV6_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP are defined which are required for generalized IPv6 multicast group support." +#endif // IPV6_ADD_MEMBERSHIP + +#if defined(IPV6_DROP_MEMBERSHIP) +#define INET_IPV6_DROP_MEMBERSHIP IPV6_DROP_MEMBERSHIP +#elif defined(IPV6_LEAVE_GROUP) +#define INET_IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#elif !__ZEPHYR__ +#error \ + "Neither IPV6_DROP_MEMBERSHIP nor IPV6_LEAVE_GROUP are defined which are required for generalized IPv6 multicast group support." +#endif // IPV6_DROP_MEMBERSHIP + +namespace chip { +namespace Inet { + +namespace { + +CHIP_ERROR IPv6Bind(int socket, const IPAddress & address, uint16_t port, InterfaceId interface) +{ + struct sockaddr_in6 sa; + memset(&sa, 0, sizeof(sa)); + sa.sin6_family = AF_INET6; + sa.sin6_port = htons(port); + sa.sin6_addr = address.ToIPv6(); + InterfaceId::PlatformType interfaceId = interface.GetPlatformInterface(); + if (!CanCastTo(interfaceId)) + { + return CHIP_ERROR_INCORRECT_STATE; + } + sa.sin6_scope_id = static_cast(interfaceId); + + CHIP_ERROR status = CHIP_NO_ERROR; + if (bind(socket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + { + status = CHIP_ERROR_POSIX(errno); + } + else + { +#ifdef IPV6_MULTICAST_IF + // Instruct the kernel that any messages to multicast destinations should be + // sent down the interface specified by the caller. + setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceId, sizeof(interfaceId)); +#endif // defined(IPV6_MULTICAST_IF) + } + +#ifdef IPV6_MULTICAST_HOPS + // Instruct the kernel that any messages to multicast destinations should be + // set with the configured hop limit value. + int hops = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; + setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); +#endif // defined(IPV6_MULTICAST_HOPS) + + return status; +} + +#if INET_CONFIG_ENABLE_IPV4 +CHIP_ERROR IPv4Bind(int socket, const IPAddress & address, uint16_t port) +{ + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons(port); + sa.sin_addr = address.ToIPv4(); + + CHIP_ERROR status = CHIP_NO_ERROR; + if (bind(socket, reinterpret_cast(&sa), static_cast(sizeof(sa))) != 0) + { + status = CHIP_ERROR_POSIX(errno); + } + else + { + // Allow socket transmitting broadcast packets. + constexpr int enable = 1; + setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable)); + +#ifdef IP_MULTICAST_IF + // Instruct the kernel that any messages to multicast destinations should be + // sent down the interface to which the specified IPv4 address is bound. + setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &sa, sizeof(sa)); +#endif // defined(IP_MULTICAST_IF) + } + +#ifdef IP_MULTICAST_TTL + // Instruct the kernel that any messages to multicast destinations should be + // set with the configured hop limit value. + constexpr int ttl = INET_CONFIG_IP_MULTICAST_HOP_LIMIT; + setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); +#endif // defined(IP_MULTICAST_TTL) + + return status; +} +#endif // INET_CONFIG_ENABLE_IPV4 + +} // anonymous namespace + +#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API +UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sJoinMulticastGroupHandler; +UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sLeaveMulticastGroupHandler; +#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API + +CHIP_ERROR UDPEndPointImplSockets::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; + mBoundIntfId = interface; + + // If an ephemeral port was requested, retrieve the actual bound port. + if (port == 0) + { + SockAddr boundAddr; + socklen_t boundAddrLen = sizeof(boundAddr); + + if (getsockname(mSocket, &boundAddr.any, &boundAddrLen) == 0) + { + if (boundAddr.any.sa_family == AF_INET) + { + mBoundPort = ntohs(boundAddr.in.sin_port); + } + else if (boundAddr.any.sa_family == AF_INET6) + { + mBoundPort = ntohs(boundAddr.in6.sin6_port); + } + } + } + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_queue_t dispatchQueue = static_cast(&GetSystemLayer())->GetDispatchQueue(); + if (dispatchQueue != nullptr) + { + unsigned long fd = static_cast(mSocket); + + mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue); + ReturnErrorCodeIf(mReadableSource == nullptr, CHIP_ERROR_NO_MEMORY); + + dispatch_source_set_event_handler(mReadableSource, ^{ + this->HandlePendingIO(System::SocketEventFlags::kRead); + }); + dispatch_resume(mReadableSource); + } +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + + return CHIP_NO_ERROR; +} + +CHIP_ERROR UDPEndPointImplSockets::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; + + if (interfaceId.IsPresent()) + { + // Start filtering on the passed interface. + char interfaceName[IF_NAMESIZE]; + if (if_indextoname(interfaceId.GetPlatformInterface(), interfaceName) == nullptr) + { + status = CHIP_ERROR_POSIX(errno); + } + else if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, interfaceName, socklen_t(strlen(interfaceName))) == -1) + { + status = CHIP_ERROR_POSIX(errno); + } + } + else + { + // Stop interface-based filtering. + if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, "", 0) == -1) + { + status = CHIP_ERROR_POSIX(errno); + } + } + + if (status == CHIP_NO_ERROR) + { + mBoundIntfId = interfaceId; + } + + return status; +#else // !HAVE_SO_BINDTODEVICE + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // HAVE_SO_BINDTODEVICE +} + +InterfaceId UDPEndPointImplSockets::GetBoundInterface() const +{ + return mBoundIntfId; +} + +uint16_t UDPEndPointImplSockets::GetBoundPort() const +{ + return mBoundPort; +} + +CHIP_ERROR UDPEndPointImplSockets::ListenImpl() +{ + // Wait for ability to read on this endpoint. + auto * layer = static_cast(&GetSystemLayer()); + ReturnErrorOnFailure(layer->SetCallback(mWatch, HandlePendingIO, reinterpret_cast(this))); + return layer->RequestCallbackOnPendingRead(mWatch); +} + +CHIP_ERROR UDPEndPointImplSockets::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(mAddrType == aPktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT); + + // For now the entire message must fit within a single buffer. + VerifyOrReturnError(!msg->HasChainedBuffer(), CHIP_ERROR_MESSAGE_TOO_LONG); + + struct iovec msgIOV; + msgIOV.iov_base = msg->Start(); + msgIOV.iov_len = msg->DataLength(); + +#if defined(IP_PKTINFO) || defined(IPV6_PKTINFO) + uint8_t controlData[256]; + memset(controlData, 0, sizeof(controlData)); +#endif // defined(IP_PKTINFO) || defined(IPV6_PKTINFO) + + struct 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. + SockAddr peerSockAddr; + memset(&peerSockAddr, 0, sizeof(peerSockAddr)); + msgHeader.msg_name = &peerSockAddr; + if (mAddrType == IPAddressType::kIPv6) + { + peerSockAddr.in6.sin6_family = AF_INET6; + peerSockAddr.in6.sin6_port = htons(aPktInfo->DestPort); + peerSockAddr.in6.sin6_addr = aPktInfo->DestAddress.ToIPv6(); + InterfaceId::PlatformType intfId = aPktInfo->Interface.GetPlatformInterface(); + VerifyOrReturnError(CanCastTo(intfId), CHIP_ERROR_INCORRECT_STATE); + peerSockAddr.in6.sin6_scope_id = static_cast(intfId); + msgHeader.msg_namelen = sizeof(sockaddr_in6); + } +#if INET_CONFIG_ENABLE_IPV4 + else + { + peerSockAddr.in.sin_family = AF_INET; + peerSockAddr.in.sin_port = htons(aPktInfo->DestPort); + peerSockAddr.in.sin_addr = aPktInfo->DestAddress.ToIPv4(); + msgHeader.msg_namelen = sizeof(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 = mBoundIntfId; + } + + // If the packet should be sent over a specific interface, or with a specific source + // address, construct an IP_PKTINFO/IPV6_PKTINFO "control message" to that effect + // add add it to the message header. If the local OS doesn't support IP_PKTINFO/IPV6_PKTINFO + // fail with an error. + if (intf.IsPresent() || aPktInfo->SrcAddress.Type() != IPAddressType::kAny) + { +#if defined(IP_PKTINFO) || defined(IPV6_PKTINFO) + msgHeader.msg_control = controlData; + msgHeader.msg_controllen = sizeof(controlData); + + struct cmsghdr * controlHdr = CMSG_FIRSTHDR(&msgHeader); + InterfaceId::PlatformType intfId = intf.GetPlatformInterface(); + +#if INET_CONFIG_ENABLE_IPV4 + + if (mAddrType == IPAddressType::kIPv4) + { +#if defined(IP_PKTINFO) + controlHdr->cmsg_level = IPPROTO_IP; + controlHdr->cmsg_type = IP_PKTINFO; + controlHdr->cmsg_len = CMSG_LEN(sizeof(in_pktinfo)); + + auto * pktInfo = reinterpret_cast CMSG_DATA(controlHdr); + if (!CanCastToipi_ifindex)>(intfId)) + { + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; + } + + pktInfo->ipi_ifindex = static_castipi_ifindex)>(intfId); + pktInfo->ipi_spec_dst = aPktInfo->SrcAddress.ToIPv4(); + + msgHeader.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo)); +#else // !defined(IP_PKTINFO) + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif // !defined(IP_PKTINFO) + } + +#endif // INET_CONFIG_ENABLE_IPV4 + + if (mAddrType == IPAddressType::kIPv6) + { +#if defined(IPV6_PKTINFO) + controlHdr->cmsg_level = IPPROTO_IPV6; + controlHdr->cmsg_type = IPV6_PKTINFO; + controlHdr->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo)); + + auto * pktInfo = reinterpret_cast CMSG_DATA(controlHdr); + if (!CanCastToipi6_ifindex)>(intfId)) + { + return CHIP_ERROR_UNEXPECTED_EVENT; + } + pktInfo->ipi6_ifindex = static_castipi6_ifindex)>(intfId); + pktInfo->ipi6_addr = aPktInfo->SrcAddress.ToIPv6(); + + msgHeader.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo)); +#else // !defined(IPV6_PKTINFO) + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif // !defined(IPV6_PKTINFO) + } + +#else // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO)) + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO)) + } + + // Send IP packet. + const ssize_t lenSent = sendmsg(mSocket, &msgHeader, 0); + if (lenSent == -1) + { + return CHIP_ERROR_POSIX(errno); + } + if (lenSent != msg->DataLength()) + { + return CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG; + } + return CHIP_NO_ERROR; +} + +void UDPEndPointImplSockets::CloseImpl() +{ + if (mSocket != kInvalidSocketFd) + { + static_cast(&GetSystemLayer())->StopWatchingSocket(&mWatch); + close(mSocket); + mSocket = kInvalidSocketFd; + } + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + if (mReadableSource) + { + dispatch_source_cancel(mReadableSource); + dispatch_release(mReadableSource); + } +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH +} + +void UDPEndPointImplSockets::Free() +{ + Close(); + Release(); +} + +CHIP_ERROR UDPEndPointImplSockets::GetSocket(IPAddressType addressType) +{ + if (mSocket == kInvalidSocketFd) + { + constexpr int type = (SOCK_DGRAM | SOCK_CLOEXEC); + constexpr int protocol = 0; + + int family = PF_UNSPEC; + + switch (addressType) + { + case IPAddressType::kIPv6: + family = PF_INET6; + break; + +#if INET_CONFIG_ENABLE_IPV4 + case IPAddressType::kIPv4: + family = PF_INET; + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + return INET_ERROR_WRONG_ADDRESS_TYPE; + } + + mSocket = ::socket(family, type, protocol); + if (mSocket == -1) + { + return CHIP_ERROR_POSIX(errno); + } + ReturnErrorOnFailure(static_cast(&GetSystemLayer())->StartWatchingSocket(mSocket, &mWatch)); + + mAddrType = addressType; + + // NOTE WELL: the errors returned by setsockopt() here are not + // returned as Inet layer CHIP_ERROR_POSIX(errno) + // codes because they are normally expected to fail on some + // platforms where the socket option code is defined in the + // header files but not [yet] implemented. Certainly, there is + // room to improve this by connecting the build configuration + // logic up to check for implementations of these options and + // to provide appropriate HAVE_xxxxx definitions accordingly. + + constexpr int one = 1; + int res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + static_cast(res); + +#ifdef SO_REUSEPORT + res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "SO_REUSEPORT failed: %d", errno); + } +#endif // defined(SO_REUSEPORT) + + // If creating an IPv6 socket, tell the kernel that it will be + // IPv6 only. This makes it posible to bind two sockets to + // the same port, one for IPv4 and one for IPv6. + +#ifdef IPV6_V6ONLY + if (addressType == IPAddressType::kIPv6) + { + res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "IPV6_V6ONLY failed: %d", errno); + } + } +#endif // defined(IPV6_V6ONLY) + +#if INET_CONFIG_ENABLE_IPV4 +#ifdef IP_PKTINFO + if (addressType == IPAddressType::kIPv4) + { + res = setsockopt(mSocket, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "IP_PKTINFO failed: %d", errno); + } + } +#endif // defined(IP_PKTINFO) +#endif // INET_CONFIG_ENABLE_IPV4 + +#ifdef IPV6_RECVPKTINFO + if (addressType == IPAddressType::kIPv6) + { + res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "IPV6_PKTINFO failed: %d", errno); + } + } +#endif // defined(IPV6_RECVPKTINFO) + + // On systems that support it, disable the delivery of SIGPIPE + // signals when writing to a closed socket. This is mostly + // needed on iOS which has the peculiar habit of sending + // SIGPIPEs on unconnected UDP sockets. +#ifdef SO_NOSIGPIPE + { + res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one)); + if (res != 0) + { + ChipLogError(Inet, "SO_NOSIGPIPE failed: %d", errno); + } + } +#endif // defined(SO_NOSIGPIPE) + } + else if (mAddrType != addressType) + { + return CHIP_ERROR_INCORRECT_STATE; + } + + return CHIP_NO_ERROR; +} + +// static +void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events, intptr_t data) +{ + reinterpret_cast(data)->HandlePendingIO(events); +} + +void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events) +{ + if (mState != State::kListening || OnMessageReceived == nullptr || !events.Has(System::SocketEventFlags::kRead)) + { + 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()) + { + struct iovec msgIOV; + SockAddr lPeerSockAddr; + uint8_t controlData[256]; + struct 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); + + ssize_t rcvLen = recvmsg(mSocket, &msgHeader, MSG_DONTWAIT); + + if (rcvLen < 0) + { + lStatus = CHIP_ERROR_POSIX(errno); + } + else if (rcvLen > lBuffer->AvailableDataLength()) + { + lStatus = CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG; + } + else + { + lBuffer->SetDataLength(static_cast(rcvLen)); + + if (lPeerSockAddr.any.sa_family == AF_INET6) + { + lPacketInfo.SrcAddress = IPAddress(lPeerSockAddr.in6.sin6_addr); + lPacketInfo.SrcPort = ntohs(lPeerSockAddr.in6.sin6_port); + } +#if INET_CONFIG_ENABLE_IPV4 + else if (lPeerSockAddr.any.sa_family == 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 (struct cmsghdr * controlHdr = CMSG_FIRSTHDR(&msgHeader); controlHdr != nullptr; + controlHdr = CMSG_NXTHDR(&msgHeader, controlHdr)) + { +#if INET_CONFIG_ENABLE_IPV4 +#ifdef IP_PKTINFO + if (controlHdr->cmsg_level == IPPROTO_IP && controlHdr->cmsg_type == IP_PKTINFO) + { + auto * inPktInfo = reinterpret_cast CMSG_DATA(controlHdr); + if (!CanCastTo(inPktInfo->ipi_ifindex)) + { + lStatus = CHIP_ERROR_INCORRECT_STATE; + break; + } + lPacketInfo.Interface = InterfaceId(static_cast(inPktInfo->ipi_ifindex)); + lPacketInfo.DestAddress = IPAddress(inPktInfo->ipi_addr); + continue; + } +#endif // defined(IP_PKTINFO) +#endif // INET_CONFIG_ENABLE_IPV4 + +#ifdef IPV6_PKTINFO + if (controlHdr->cmsg_level == IPPROTO_IPV6 && controlHdr->cmsg_type == IPV6_PKTINFO) + { + auto * in6PktInfo = reinterpret_cast CMSG_DATA(controlHdr); + if (!CanCastTo(in6PktInfo->ipi6_ifindex)) + { + lStatus = CHIP_ERROR_INCORRECT_STATE; + break; + } + lPacketInfo.Interface = InterfaceId(static_cast(in6PktInfo->ipi6_ifindex)); + lPacketInfo.DestAddress = IPAddress(in6PktInfo->ipi6_addr); + continue; + } +#endif // defined(IPV6_PKTINFO) + } + } + } + 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_POSIX(EAGAIN)) + { + OnReceiveError(this, lStatus, nullptr); + } + } +} + +#if IP_MULTICAST_LOOP || IPV6_MULTICAST_LOOP +static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, bool aLoopback, int aProtocol, int aOption) +{ + const unsigned int lValue = static_cast(aLoopback); + if (setsockopt(aSocket, aProtocol, aOption, &lValue, sizeof(lValue)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + + return CHIP_NO_ERROR; +} +#endif // IP_MULTICAST_LOOP || IPV6_MULTICAST_LOOP + +static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, IPVersion aIPVersion, bool aLoopback) +{ +#ifdef IPV6_MULTICAST_LOOP + CHIP_ERROR lRetval; + + switch (aIPVersion) + { + + case kIPVersion_6: + lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, IPPROTO_IPV6, IPV6_MULTICAST_LOOP); + break; + +#if INET_CONFIG_ENABLE_IPV4 + case kIPVersion_4: + lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, IPPROTO_IP, IP_MULTICAST_LOOP); + break; +#endif // INET_CONFIG_ENABLE_IPV4 + + default: + lRetval = INET_ERROR_WRONG_ADDRESS_TYPE; + break; + } + + return (lRetval); +#else // IPV6_MULTICAST_LOOP + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif // IPV6_MULTICAST_LOOP +} + +CHIP_ERROR UDPEndPointImplSockets::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 UDPEndPointImplSockets::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join) +{ + IPAddress lInterfaceAddress; + bool lInterfaceAddressFound = false; + + for (InterfaceAddressIterator lAddressIterator; lAddressIterator.HasCurrent(); lAddressIterator.Next()) + { + const IPAddress lCurrentAddress = lAddressIterator.GetAddress(); + + if (lAddressIterator.GetInterfaceId() == aInterfaceId) + { + if (lCurrentAddress.IsIPv4()) + { + lInterfaceAddressFound = true; + lInterfaceAddress = lCurrentAddress; + break; + } + } + } + VerifyOrReturnError(lInterfaceAddressFound, INET_ERROR_ADDRESS_NOT_FOUND); + + struct ip_mreq lMulticastRequest; + memset(&lMulticastRequest, 0, sizeof(lMulticastRequest)); + lMulticastRequest.imr_interface = lInterfaceAddress.ToIPv4(); + lMulticastRequest.imr_multiaddr = aAddress.ToIPv4(); + + const int command = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + if (setsockopt(mSocket, IPPROTO_IP, command, &lMulticastRequest, sizeof(lMulticastRequest)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + return CHIP_NO_ERROR; +} + +#endif // INET_CONFIG_ENABLE_IPV4 + +CHIP_ERROR UDPEndPointImplSockets::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 + +#ifdef IPV6_MULTICAST_IMPLEMENTED + const InterfaceId::PlatformType lIfIndex = aInterfaceId.GetPlatformInterface(); + + struct ipv6_mreq lMulticastRequest; + memset(&lMulticastRequest, 0, sizeof(lMulticastRequest)); + VerifyOrReturnError(CanCastTo(lIfIndex), CHIP_ERROR_UNEXPECTED_EVENT); + + lMulticastRequest.ipv6mr_interface = static_cast(lIfIndex); + lMulticastRequest.ipv6mr_multiaddr = aAddress.ToIPv6(); + + const int command = join ? INET_IPV6_ADD_MEMBERSHIP : INET_IPV6_DROP_MEMBERSHIP; + if (setsockopt(mSocket, IPPROTO_IPV6, command, &lMulticastRequest, sizeof(lMulticastRequest)) != 0) + { + return CHIP_ERROR_POSIX(errno); + } + return CHIP_NO_ERROR; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif +} + +} // namespace Inet +} // namespace chip diff --git a/src/inet/UDPEndPointImplSockets.h b/src/inet/UDPEndPointImplSockets.h new file mode 100644 index 00000000000000..490360da5e9ec1 --- /dev/null +++ b/src/inet/UDPEndPointImplSockets.h @@ -0,0 +1,87 @@ +/* + * + * Copyright (c) 2020-2021 Project CHIP Authors + * Copyright (c) 2018 Google LLC + * Copyright (c) 2013-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. + */ + +/** + * This file declares an implementation of Inet::UDPEndPoint using sockets. + */ + +#pragma once + +#include +#include + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH +#include +#endif + +namespace chip { +namespace Inet { + +class UDPEndPointImplSockets : public UDPEndPoint, public EndPointStateSockets +{ +public: + UDPEndPointImplSockets(EndPointManager & endPointManager) : + UDPEndPoint(endPointManager), mBoundIntfId(InterfaceId::Null()) + {} + + // 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); + void HandlePendingIO(System::SocketEvents events); + static void HandlePendingIO(System::SocketEvents events, intptr_t data); + + InterfaceId mBoundIntfId; + uint16_t mBoundPort; + +#if CHIP_SYSTEM_CONFIG_USE_DISPATCH + dispatch_source_t mReadableSource = nullptr; +#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH + +#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 +}; + +using UDPEndPointImpl = UDPEndPointImplSockets; + +} // namespace Inet +} // namespace chip diff --git a/src/inet/tests/TestInetCommon.h b/src/inet/tests/TestInetCommon.h index e920fdb73de5e0..0e1d7ab474c1e0 100644 --- a/src/inet/tests/TestInetCommon.h +++ b/src/inet/tests/TestInetCommon.h @@ -40,8 +40,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/src/platform/Globals.cpp b/src/platform/Globals.cpp index eaed4f1fbaa20c..23bfe7ca5c8f94 100644 --- a/src/platform/Globals.cpp +++ b/src/platform/Globals.cpp @@ -19,6 +19,8 @@ /* this file behaves like a config.h, comes first */ #include +#include +#include #include #include diff --git a/src/platform/Zephyr/ThreadStackManagerImpl.cpp b/src/platform/Zephyr/ThreadStackManagerImpl.cpp index 3b0616e6a3ffa1..f0e02f073638e9 100644 --- a/src/platform/Zephyr/ThreadStackManagerImpl.cpp +++ b/src/platform/Zephyr/ThreadStackManagerImpl.cpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include