| /* |
| * |
| * 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 file defines classes for abstracting access to and |
| * interactions with a platform- and system-specific Internet |
| * Protocol stack which, as of this implementation, may be either |
| * BSD/POSIX Sockets or LwIP. |
| * |
| * Major abstractions provided are: |
| * |
| * * Timers |
| * * Domain Name System (DNS) resolution |
| * * TCP network transport |
| * * UDP network transport |
| * * Raw network transport |
| * |
| * For BSD/POSIX Sockets (CHIP_SYSTEM_CONFIG_USE_SOCKETS), event readiness |
| * notification is handled via file descriptors, using System::WatchableSocket. |
| * |
| * For LwIP (CHIP_SYSTEM_CONFIG_USE_LWIP), event readiness notification is handled |
| * via events / messages and platform- and system-specific hooks for the event |
| * / message system. |
| * |
| */ |
| |
| #ifndef __STDC_LIMIT_MACROS |
| #define __STDC_LIMIT_MACROS |
| #endif |
| #include "InetLayer.h" |
| |
| #include "InetFaultInjection.h" |
| |
| #include <platform/LockTracker.h> |
| |
| #include <system/SystemTimer.h> |
| |
| #include <support/CodeUtils.h> |
| #include <support/logging/CHIPLogging.h> |
| |
| #include <errno.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <utility> |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| #include <lwip/netif.h> |
| #include <lwip/sys.h> |
| #else // !CHIP_SYSTEM_CONFIG_USE_LWIP |
| #include <fcntl.h> |
| #include <net/if.h> |
| #include <unistd.h> |
| #ifdef __ANDROID__ |
| #include <ifaddrs-android.h> |
| #elif CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS |
| #include <ifaddrs.h> |
| #endif |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF |
| #include <net/net_if.h> |
| #endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF |
| |
| namespace chip { |
| namespace Inet { |
| |
| void InetLayer::UpdateSnapshot(chip::System::Stats::Snapshot & aSnapshot) |
| { |
| #if INET_CONFIG_ENABLE_DNS_RESOLVER |
| DNSResolver::sPool.GetStatistics(aSnapshot.mResourcesInUse[chip::System::Stats::kInetLayer_NumDNSResolvers], |
| aSnapshot.mHighWatermarks[chip::System::Stats::kInetLayer_NumDNSResolvers]); |
| #endif // INET_CONFIG_ENABLE_DNS_RESOLVER |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
| TCPEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[chip::System::Stats::kInetLayer_NumTCPEps], |
| aSnapshot.mHighWatermarks[chip::System::Stats::kInetLayer_NumTCPEps]); |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
| #if INET_CONFIG_ENABLE_UDP_ENDPOINT |
| UDPEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[chip::System::Stats::kInetLayer_NumUDPEps], |
| aSnapshot.mHighWatermarks[chip::System::Stats::kInetLayer_NumUDPEps]); |
| #endif // INET_CONFIG_ENABLE_UDP_ENDPOINT |
| #if INET_CONFIG_ENABLE_RAW_ENDPOINT |
| RawEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[chip::System::Stats::kInetLayer_NumRawEps], |
| aSnapshot.mHighWatermarks[chip::System::Stats::kInetLayer_NumRawEps]); |
| #endif // INET_CONFIG_ENABLE_RAW_ENDPOINT |
| } |
| |
| /** |
| * This is the InetLayer default constructor. |
| * |
| * It performs some basic data member initialization; however, since |
| * InetLayer follows an explicit initializer design pattern, the InetLayer::Init |
| * method must be called successfully prior to using the object. |
| * |
| */ |
| InetLayer::InetLayer() |
| { |
| State = kState_NotInitialized; |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| if (!sInetEventHandlerDelegate.IsInitialized()) |
| sInetEventHandlerDelegate.Init(HandleInetLayerEvent); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| chip::System::LwIPEventHandlerDelegate InetLayer::sInetEventHandlerDelegate; |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if INET_CONFIG_MAX_DROPPABLE_EVENTS && CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if CHIP_SYSTEM_CONFIG_NO_LOCKING |
| |
| INET_ERROR InetLayer::InitQueueLimiter(void) |
| { |
| mDroppableEvents = 0; |
| return INET_NO_ERROR; |
| } |
| |
| bool InetLayer::CanEnqueueDroppableEvent(void) |
| { |
| if (__sync_add_and_fetch(&mDroppableEvents, 1) <= INET_CONFIG_MAX_DROPPABLE_EVENTS) |
| { |
| return true; |
| } |
| else |
| { |
| __sync_add_and_fetch(&mDroppableEvents, -1); |
| return false; |
| } |
| } |
| |
| void InetLayer::DroppableEventDequeued(void) |
| { |
| __sync_add_and_fetch(&mDroppableEvents, -1); |
| } |
| |
| #elif CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING |
| |
| INET_ERROR InetLayer::InitQueueLimiter(void) |
| { |
| const unsigned portBASE_TYPE maximum = INET_CONFIG_MAX_DROPPABLE_EVENTS; |
| const unsigned portBASE_TYPE initial = INET_CONFIG_MAX_DROPPABLE_EVENTS; |
| |
| #if (configSUPPORT_STATIC_ALLOCATION == 1) |
| mDroppableEvents = xSemaphoreCreateCountingStatic(maximum, initial, &mDroppableEventsObj); |
| #else |
| mDroppableEvents = xSemaphoreCreateCounting(maximum, initial); |
| #endif |
| |
| if (mDroppableEvents != NULL) |
| return INET_NO_ERROR; |
| else |
| return INET_ERROR_NO_MEMORY; |
| } |
| |
| bool InetLayer::CanEnqueueDroppableEvent(void) |
| { |
| if (xSemaphoreTake(mDroppableEvents, 0) != pdTRUE) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| void InetLayer::DroppableEventDequeued(void) |
| { |
| xSemaphoreGive(mDroppableEvents); |
| } |
| |
| #else // !CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING |
| |
| INET_ERROR InetLayer::InitQueueLimiter(void) |
| { |
| if (sem_init(&mDroppableEvents, 0, INET_CONFIG_MAX_DROPPABLE_EVENTS) != 0) |
| { |
| return chip::System::MapErrorPOSIX(errno); |
| } |
| return INET_NO_ERROR; |
| } |
| |
| bool InetLayer::CanEnqueueDroppableEvent(void) |
| { |
| // Striclty speaking, we should check for EAGAIN. But, other |
| // errno values probably should signal that that we should drop |
| // the packet: EINVAL means that the semaphore is not valid (we |
| // failed initialization), and EINTR should probably also lead to |
| // dropping a packet. |
| |
| if (sem_trywait(&mDroppableEvents) != 0) |
| { |
| return false; |
| } |
| return true; |
| } |
| |
| void InetLayer::DroppableEventDequeued(void) |
| { |
| sem_post(&mDroppableEvents); |
| } |
| |
| #endif // !CHIP_SYSTEM_CONFIG_FREERTOS_LOCKING |
| #endif // INET_CONFIG_MAX_DROPPABLE_EVENTS && CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| /** |
| * This is the InetLayer explicit initializer. This must be called |
| * and complete successfully before the InetLayer may be used. |
| * |
| * The caller may provide an optional context argument which will be |
| * passed back via any platform-specific hook functions. For |
| * LwIP-based adaptations, this will typically be a pointer to the |
| * event queue associated with the InetLayer instance. |
| * |
| * Platforms may choose to assert |
| * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS in their |
| * platform-specific configuration header and enable the |
| * Platform::InetLayer::WillInit and Platform::InetLayer::DidInit |
| * hooks to effect platform-specific customizations or data extensions |
| * to InetLayer. |
| * |
| * @param[in] aSystemLayer A required instance of the chip System Layer |
| * already successfully initialized. |
| * |
| * @param[in] aContext An optional context argument which will be passed |
| * back to the caller via any platform-specific hook |
| * functions. |
| * |
| * @retval #INET_ERROR_INCORRECT_STATE If the InetLayer is in an |
| * incorrect state. |
| * @retval #INET_ERROR_NO_MEMORY If the InetLayer runs out |
| * of resource for this |
| * request for a new timer. |
| * @retval other Platform-specific errors indicating the reason for |
| * initialization failure. |
| * @retval #INET_NO_ERROR On success. |
| * |
| */ |
| INET_ERROR InetLayer::Init(chip::System::Layer & aSystemLayer, void * aContext) |
| { |
| INET_ERROR err = INET_NO_ERROR; |
| |
| Inet::RegisterLayerErrorFormatter(); |
| |
| if (State != kState_NotInitialized) |
| return INET_ERROR_INCORRECT_STATE; |
| |
| // Platform-specific initialization may elect to set this data |
| // member. Ensure it is set to a sane default value before |
| // invoking platform-specific initialization. |
| |
| mPlatformData = nullptr; |
| |
| err = Platform::InetLayer::WillInit(this, aContext); |
| SuccessOrExit(err); |
| |
| mSystemLayer = &aSystemLayer; |
| mContext = aContext; |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| err = InitQueueLimiter(); |
| SuccessOrExit(err); |
| |
| mSystemLayer->AddEventHandlerDelegate(sInetEventHandlerDelegate); |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| State = kState_Initialized; |
| |
| #if INET_CONFIG_ENABLE_DNS_RESOLVER |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| err = mAsyncDNSResolver.Init(this); |
| SuccessOrExit(err); |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| #endif // INET_CONFIG_ENABLE_DNS_RESOLVER |
| |
| exit: |
| Platform::InetLayer::DidInit(this, mContext, err); |
| return err; |
| } |
| |
| /** |
| * This is the InetLayer explicit deinitializer and should be called |
| * prior to disposing of an instantiated InetLayer instance. |
| * |
| * Platforms may choose to assert |
| * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS in their |
| * platform-specific configuration header and enable the |
| * Platform::InetLayer::WillShutdown and |
| * Platform::InetLayer::DidShutdown hooks to effect clean-up of |
| * platform-specific customizations or data extensions to InetLayer. |
| * |
| * @return #INET_NO_ERROR on success; otherwise, a specific error indicating |
| * the reason for shutdown failure. |
| * |
| */ |
| INET_ERROR InetLayer::Shutdown() |
| { |
| INET_ERROR err; |
| |
| err = Platform::InetLayer::WillShutdown(this, mContext); |
| SuccessOrExit(err); |
| |
| if (State == kState_Initialized) |
| { |
| #if INET_CONFIG_ENABLE_DNS_RESOLVER |
| // Cancel all DNS resolution requests owned by this instance. |
| for (size_t i = 0; i < DNSResolver::sPool.Size(); i++) |
| { |
| DNSResolver * lResolver = DNSResolver::sPool.Get(*mSystemLayer, i); |
| if ((lResolver != nullptr) && lResolver->IsCreatedByInetLayer(*this)) |
| { |
| lResolver->Cancel(); |
| } |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| |
| err = mAsyncDNSResolver.Shutdown(); |
| |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| #endif // INET_CONFIG_ENABLE_DNS_RESOLVER |
| |
| #if INET_CONFIG_ENABLE_RAW_ENDPOINT |
| // Close all raw endpoints owned by this Inet layer instance. |
| for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++) |
| { |
| RawEndPoint * lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i); |
| if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) |
| { |
| lEndPoint->Close(); |
| } |
| } |
| #endif // INET_CONFIG_ENABLE_RAW_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
| // Abort all TCP endpoints owned by this instance. |
| for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++) |
| { |
| TCPEndPoint * lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i); |
| if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) |
| { |
| lEndPoint->Abort(); |
| } |
| } |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_UDP_ENDPOINT |
| // Close all UDP endpoints owned by this instance. |
| for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++) |
| { |
| UDPEndPoint * lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i); |
| if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*this)) |
| { |
| lEndPoint->Close(); |
| } |
| } |
| #endif // INET_CONFIG_ENABLE_UDP_ENDPOINT |
| } |
| |
| State = kState_NotInitialized; |
| |
| exit: |
| Platform::InetLayer::DidShutdown(this, mContext, err); |
| |
| return err; |
| } |
| |
| /** |
| * This returns any client-specific platform data assigned to the |
| * instance, if it has been previously set. |
| * |
| * @return Client-specific platform data, if is has been previously set; |
| * otherwise, NULL. |
| * |
| */ |
| void * InetLayer::GetPlatformData() |
| { |
| return mPlatformData; |
| } |
| |
| /** |
| * This sets the specified client-specific platform data to the |
| * instance for later retrieval by the client platform. |
| * |
| * @param[in] aPlatformData The client-specific platform data to set. |
| * |
| */ |
| void InetLayer::SetPlatformData(void * aPlatformData) |
| { |
| mPlatformData = aPlatformData; |
| } |
| |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0 |
| bool InetLayer::IsIdleTimerRunning() |
| { |
| bool timerRunning = false; |
| |
| // see if there are any TCP connections with the idle timer check in use. |
| for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++) |
| { |
| TCPEndPoint * lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i); |
| |
| if ((lEndPoint != nullptr) && (lEndPoint->mIdleTimeout != 0)) |
| { |
| timerRunning = true; |
| break; |
| } |
| } |
| |
| return timerRunning; |
| } |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0 |
| |
| /** |
| * Get the link local IPv6 address for a specified link or interface. |
| * |
| * @param[in] link The interface for which the link local IPv6 |
| * address is being sought. |
| * |
| * @param[out] llAddr The link local IPv6 address for the link. |
| * |
| * @retval #INET_ERROR_NOT_IMPLEMENTED If IPv6 is not supported. |
| * @retval #INET_ERROR_BAD_ARGS If the link local address |
| * is NULL. |
| * @retval #INET_ERROR_ADDRESS_NOT_FOUND If the link does not have |
| * any address configured. |
| * @retval #INET_NO_ERROR On success. |
| * |
| */ |
| INET_ERROR InetLayer::GetLinkLocalAddr(InterfaceId link, IPAddress * llAddr) |
| { |
| VerifyOrReturnError(llAddr != nullptr, INET_ERROR_BAD_ARGS); |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| #if !LWIP_IPV6 |
| return INET_ERROR_NOT_IMPLEMENTED; |
| #endif //! LWIP_IPV6 |
| |
| for (struct netif * intf = netif_list; intf != NULL; intf = intf->next) |
| { |
| if ((link != NULL) && (link != intf)) |
| continue; |
| for (int j = 0; j < LWIP_IPV6_NUM_ADDRESSES; ++j) |
| { |
| if (ip6_addr_isvalid(netif_ip6_addr_state(intf, j)) && ip6_addr_islinklocal(netif_ip6_addr(intf, j))) |
| { |
| (*llAddr) = IPAddress::FromIPv6(*netif_ip6_addr(intf, j)); |
| return INET_NO_ERROR; |
| } |
| } |
| if (link != NULL) |
| { |
| return INET_ERROR_ADDRESS_NOT_FOUND; |
| } |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS |
| struct ifaddrs * ifaddr; |
| const int rv = getifaddrs(&ifaddr); |
| if (rv == -1) |
| { |
| return INET_ERROR_ADDRESS_NOT_FOUND; |
| } |
| |
| for (struct ifaddrs * ifaddr_iter = ifaddr; ifaddr_iter != nullptr; ifaddr_iter = ifaddr_iter->ifa_next) |
| { |
| if (ifaddr_iter->ifa_addr != nullptr) |
| { |
| if ((ifaddr_iter->ifa_addr->sa_family == AF_INET6) && |
| ((link == INET_NULL_INTERFACEID) || (if_nametoindex(ifaddr_iter->ifa_name) == link))) |
| { |
| struct in6_addr * sin6_addr = &(reinterpret_cast<struct sockaddr_in6 *>(ifaddr_iter->ifa_addr))->sin6_addr; |
| if (sin6_addr->s6_addr[0] == 0xfe && (sin6_addr->s6_addr[1] & 0xc0) == 0x80) // Link Local Address |
| { |
| (*llAddr) = IPAddress::FromIPv6((reinterpret_cast<struct sockaddr_in6 *>(ifaddr_iter->ifa_addr))->sin6_addr); |
| break; |
| } |
| } |
| } |
| } |
| freeifaddrs(ifaddr); |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS |
| |
| #if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF |
| net_if * const iface = (link == INET_NULL_INTERFACEID) ? net_if_get_default() : net_if_get_by_index(link); |
| VerifyOrReturnError(iface != nullptr, INET_ERROR_ADDRESS_NOT_FOUND); |
| |
| in6_addr * const ip6_addr = net_if_ipv6_get_ll(iface, NET_ADDR_PREFERRED); |
| VerifyOrReturnError(ip6_addr != nullptr, INET_ERROR_ADDRESS_NOT_FOUND); |
| |
| *llAddr = IPAddress::FromIPv6(*ip6_addr); |
| #endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF |
| |
| return INET_NO_ERROR; |
| } |
| |
| #if INET_CONFIG_ENABLE_RAW_ENDPOINT |
| /** |
| * Creates a new RawEndPoint object for a specific IP version and protocol. |
| * |
| * @note |
| * This function gets a free RawEndPoint object from a pre-allocated pool |
| * and also calls the explicit initializer on the new object. |
| * |
| * @param[in] ipVer IPv4 or IPv6. |
| * |
| * @param[in] ipProto A protocol within the IP family (e.g., ICMPv4 or ICMPv6). |
| * |
| * @param[in,out] retEndPoint A pointer to a pointer of the RawEndPoint object that is |
| * a return parameter upon completion of the object creation. |
| * *retEndPoint is NULL if creation fails. |
| * |
| * @retval #INET_ERROR_INCORRECT_STATE If the InetLayer object is not initialized. |
| * @retval #INET_ERROR_NO_ENDPOINTS If the InetLayer RawEndPoint pool is full and no new |
| * endpoints can be created. |
| * @retval #INET_NO_ERROR On success. |
| * |
| */ |
| INET_ERROR InetLayer::NewRawEndPoint(IPVersion ipVer, IPProtocol ipProto, RawEndPoint ** retEndPoint) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| *retEndPoint = nullptr; |
| |
| VerifyOrReturnError(State == kState_Initialized, INET_ERROR_INCORRECT_STATE); |
| |
| *retEndPoint = RawEndPoint::sPool.TryCreate(*mSystemLayer); |
| if (*retEndPoint == nullptr) |
| { |
| ChipLogError(Inet, "%s endpoint pool FULL", "Raw"); |
| return INET_ERROR_NO_ENDPOINTS; |
| } |
| |
| (*retEndPoint)->Inet::RawEndPoint::Init(this, ipVer, ipProto); |
| SYSTEM_STATS_INCREMENT(chip::System::Stats::kInetLayer_NumRawEps); |
| |
| return INET_NO_ERROR; |
| } |
| #endif // INET_CONFIG_ENABLE_RAW_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
| /** |
| * Creates a new TCPEndPoint object. |
| * |
| * @note |
| * This function gets a free TCPEndPoint object from a pre-allocated pool |
| * and also calls the explicit initializer on the new object. |
| * |
| * @param[in,out] retEndPoint A pointer to a pointer of the TCPEndPoint object that is |
| * a return parameter upon completion of the object creation. |
| * *retEndPoint is NULL if creation fails. |
| * |
| * @retval #INET_ERROR_INCORRECT_STATE If the InetLayer object is not initialized. |
| * @retval #INET_ERROR_NO_ENDPOINTS If the InetLayer TCPEndPoint pool is full and no new |
| * endpoints can be created. |
| * @retval #INET_NO_ERROR On success. |
| * |
| */ |
| INET_ERROR InetLayer::NewTCPEndPoint(TCPEndPoint ** retEndPoint) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| *retEndPoint = nullptr; |
| |
| VerifyOrReturnError(State == kState_Initialized, INET_ERROR_INCORRECT_STATE); |
| |
| *retEndPoint = TCPEndPoint::sPool.TryCreate(*mSystemLayer); |
| if (*retEndPoint == nullptr) |
| { |
| ChipLogError(Inet, "%s endpoint pool FULL", "TCP"); |
| return INET_ERROR_NO_ENDPOINTS; |
| } |
| |
| (*retEndPoint)->Init(this); |
| SYSTEM_STATS_INCREMENT(chip::System::Stats::kInetLayer_NumTCPEps); |
| |
| return INET_NO_ERROR; |
| } |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_UDP_ENDPOINT |
| /** |
| * Creates a new UDPEndPoint object. |
| * |
| * @note |
| * This function gets a free UDPEndPoint object from a pre-allocated pool |
| * and also calls the explicit initializer on the new object. |
| * |
| * @param[in,out] retEndPoint A pointer to a pointer of the UDPEndPoint object that is |
| * a return parameter upon completion of the object creation. |
| * *retEndPoint is NULL if creation fails. |
| * |
| * @retval #INET_ERROR_INCORRECT_STATE If the InetLayer object is not initialized. |
| * @retval #INET_ERROR_NO_ENDPOINTS If the InetLayer UDPEndPoint pool is full and no new |
| * endpoints can be created. |
| * @retval #INET_NO_ERROR On success. |
| * |
| */ |
| INET_ERROR InetLayer::NewUDPEndPoint(UDPEndPoint ** retEndPoint) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| *retEndPoint = nullptr; |
| |
| VerifyOrReturnError(State == kState_Initialized, INET_ERROR_INCORRECT_STATE); |
| |
| *retEndPoint = UDPEndPoint::sPool.TryCreate(*mSystemLayer); |
| if (*retEndPoint == nullptr) |
| { |
| ChipLogError(Inet, "%s endpoint pool FULL", "UDP"); |
| return INET_ERROR_NO_ENDPOINTS; |
| } |
| |
| (*retEndPoint)->Init(this); |
| SYSTEM_STATS_INCREMENT(chip::System::Stats::kInetLayer_NumUDPEps); |
| |
| return INET_NO_ERROR; |
| } |
| #endif // INET_CONFIG_ENABLE_UDP_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_DNS_RESOLVER |
| /** |
| * Perform an IP address resolution of a specified hostname. |
| * |
| * @note |
| * This is an asynchronous operation and the result will be communicated back |
| * via the OnComplete() callback. |
| * |
| * @param[in] hostName A pointer to a NULL-terminated C string representing |
| * the host name to be queried. |
| * |
| * @param[in] maxAddrs The maximum number of addresses to store in the DNS |
| * table. |
| * |
| * @param[in] addrArray A pointer to the DNS table. |
| * |
| * @param[in] onComplete A pointer to the callback function when a DNS |
| * request is complete. |
| * |
| * @param[in] appState A pointer to the application state to be passed to |
| * onComplete when a DNS request is complete. |
| * |
| * @retval #INET_NO_ERROR if a DNS request is handled |
| * successfully. |
| * @retval #INET_ERROR_NO_MEMORY if the Inet layer resolver pool |
| * is full. |
| * @retval #INET_ERROR_HOST_NAME_TOO_LONG if a requested host name is too |
| * long. |
| * @retval #INET_ERROR_HOST_NOT_FOUND if a request host name could not be |
| * resolved to an address. |
| * @retval #INET_ERROR_DNS_TRY_AGAIN if a name server returned a |
| * temporary failure indication; |
| * try again later. |
| * @retval #INET_ERROR_DNS_NO_RECOVERY if a name server returned an |
| * unrecoverable error. |
| * @retval #INET_ERROR_NOT_IMPLEMENTED if DNS resolution is not enabled on |
| * the underlying platform. |
| * @retval other POSIX network or OS error returned by the underlying DNS |
| * resolver implementation. |
| * |
| */ |
| INET_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint8_t maxAddrs, IPAddress * addrArray, |
| DNSResolveCompleteFunct onComplete, void * appState) |
| { |
| size_t hostNameLength = strlen(hostName); |
| if (hostNameLength > UINT16_MAX) |
| { |
| return INET_ERROR_HOST_NAME_TOO_LONG; |
| } |
| return ResolveHostAddress(hostName, static_cast<uint16_t>(hostNameLength), maxAddrs, addrArray, onComplete, appState); |
| } |
| |
| /** |
| * Perform an IP address resolution of a specified hostname. |
| * |
| * @param[in] hostName A pointer to a non NULL-terminated C string representing the host name |
| * to be queried. |
| * |
| * @param[in] hostNameLen The string length of host name. |
| * |
| * @param[in] maxAddrs The maximum number of addresses to store in the DNS |
| * table. |
| * |
| * @param[in] addrArray A pointer to the DNS table. |
| * |
| * @param[in] onComplete A pointer to the callback function when a DNS |
| * request is complete. |
| * |
| * @param[in] appState A pointer to the application state to be passed to |
| * onComplete when a DNS request is complete. |
| * |
| * @retval #INET_NO_ERROR if a DNS request is handled |
| * successfully. |
| * @retval #INET_ERROR_NO_MEMORY if the Inet layer resolver pool |
| * is full. |
| * @retval #INET_ERROR_HOST_NAME_TOO_LONG if a requested host name is too |
| * long. |
| * @retval #INET_ERROR_HOST_NOT_FOUND if a request host name could not be |
| * resolved to an address. |
| * @retval #INET_ERROR_DNS_TRY_AGAIN if a name server returned a |
| * temporary failure indication; |
| * try again later. |
| * @retval #INET_ERROR_DNS_NO_RECOVERY if a name server returned an |
| * unrecoverable error. |
| * @retval #INET_ERROR_NOT_IMPLEMENTED if DNS resolution is not enabled on |
| * the underlying platform. |
| * @retval other POSIX network or OS error returned by the underlying DNS |
| * resolver implementation. |
| * |
| */ |
| INET_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNameLen, uint8_t maxAddrs, IPAddress * addrArray, |
| DNSResolveCompleteFunct onComplete, void * appState) |
| { |
| return ResolveHostAddress(hostName, hostNameLen, kDNSOption_Default, maxAddrs, addrArray, onComplete, appState); |
| } |
| |
| /** |
| * Perform an IP address resolution of a specified hostname. |
| * |
| * @param[in] hostName A pointer to a non NULL-terminated C string representing the host name |
| * to be queried. |
| * |
| * @param[in] hostNameLen The string length of host name. |
| * |
| * @param[in] options An integer value controlling how host name resolution is performed. |
| * |
| * Value should be one of the address family values from the |
| * #DNSOptions enumeration: |
| * |
| * #kDNSOption_AddrFamily_Any |
| * #kDNSOption_AddrFamily_IPv4Only |
| * #kDNSOption_AddrFamily_IPv6Only |
| * #kDNSOption_AddrFamily_IPv4Preferred |
| * #kDNSOption_AddrFamily_IPv6Preferred |
| * |
| * @param[in] maxAddrs The maximum number of addresses to store in the DNS |
| * table. |
| * |
| * @param[in] addrArray A pointer to the DNS table. |
| * |
| * @param[in] onComplete A pointer to the callback function when a DNS |
| * request is complete. |
| * |
| * @param[in] appState A pointer to the application state to be passed to |
| * onComplete when a DNS request is complete. |
| * |
| * @retval #INET_NO_ERROR if a DNS request is handled |
| * successfully. |
| * @retval #INET_ERROR_NO_MEMORY if the Inet layer resolver pool |
| * is full. |
| * @retval #INET_ERROR_HOST_NAME_TOO_LONG if a requested host name is too |
| * long. |
| * @retval #INET_ERROR_HOST_NOT_FOUND if a request host name could not be |
| * resolved to an address. |
| * @retval #INET_ERROR_DNS_TRY_AGAIN if a name server returned a |
| * temporary failure indication; |
| * try again later. |
| * @retval #INET_ERROR_DNS_NO_RECOVERY if a name server returned an |
| * unrecoverable error. |
| * @retval #INET_ERROR_NOT_IMPLEMENTED if DNS resolution is not enabled on |
| * the underlying platform. |
| * @retval other POSIX network or OS error returned by the underlying DNS |
| * resolver implementation. |
| * |
| */ |
| INET_ERROR InetLayer::ResolveHostAddress(const char * hostName, uint16_t hostNameLen, uint8_t options, uint8_t maxAddrs, |
| IPAddress * addrArray, DNSResolveCompleteFunct onComplete, void * appState) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| INET_ERROR err = INET_NO_ERROR; |
| DNSResolver * resolver = nullptr; |
| |
| VerifyOrExit(State == kState_Initialized, err = INET_ERROR_INCORRECT_STATE); |
| |
| INET_FAULT_INJECT(FaultInjection::kFault_DNSResolverNew, return INET_ERROR_NO_MEMORY); |
| |
| // Store context information and set the resolver state. |
| VerifyOrExit(hostNameLen <= NL_DNS_HOSTNAME_MAX_LEN, err = INET_ERROR_HOST_NAME_TOO_LONG); |
| VerifyOrExit(maxAddrs > 0, err = INET_ERROR_NO_MEMORY); |
| |
| resolver = DNSResolver::sPool.TryCreate(*mSystemLayer); |
| if (resolver != nullptr) |
| { |
| resolver->InitInetLayerBasis(*this); |
| } |
| else |
| { |
| ChipLogError(Inet, "%s resolver pool FULL", "DNS"); |
| ExitNow(err = INET_ERROR_NO_MEMORY); |
| } |
| |
| // Short-circuit full address resolution if the supplied host name is a text-form |
| // IP address... |
| if (IPAddress::FromString(hostName, hostNameLen, *addrArray)) |
| { |
| uint8_t addrTypeOption = (options & kDNSOption_AddrFamily_Mask); |
| IPAddressType addrType = addrArray->Type(); |
| |
| if ((addrTypeOption == kDNSOption_AddrFamily_IPv6Only && addrType != kIPAddressType_IPv6) |
| #if INET_CONFIG_ENABLE_IPV4 |
| || (addrTypeOption == kDNSOption_AddrFamily_IPv4Only && addrType != kIPAddressType_IPv4) |
| #endif |
| ) |
| { |
| err = INET_ERROR_INCOMPATIBLE_IP_ADDRESS_TYPE; |
| } |
| |
| if (onComplete) |
| { |
| onComplete(appState, err, (err == INET_NO_ERROR) ? 1 : 0, addrArray); |
| } |
| |
| resolver->Release(); |
| resolver = nullptr; |
| |
| ExitNow(err = INET_NO_ERROR); |
| } |
| |
| // After this point, the resolver will be released by: |
| // - mAsyncDNSResolver (in case of ASYNC_DNS_SOCKETS) |
| // - resolver->Resolve() (in case of synchronous resolving) |
| // - the event handlers (in case of LwIP) |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| |
| err = |
| mAsyncDNSResolver.PrepareDNSResolver(*resolver, hostName, hostNameLen, options, maxAddrs, addrArray, onComplete, appState); |
| SuccessOrExit(err); |
| |
| mAsyncDNSResolver.EnqueueRequest(*resolver); |
| |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| |
| #if !INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| err = resolver->Resolve(hostName, hostNameLen, options, maxAddrs, addrArray, onComplete, appState); |
| #endif // !INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| exit: |
| |
| return err; |
| } |
| |
| /** |
| * Cancel any outstanding DNS query (for a matching completion callback and |
| * application state) that may still be active. |
| * |
| * @note |
| * This situation can arise if the application initiates a connection |
| * to a peer using a hostname and then aborts/closes the connection |
| * before the hostname resolution completes. |
| * |
| * @param[in] onComplete A pointer to the callback function when a DNS |
| * request is complete. |
| * |
| * @param[in] appState A pointer to an application state object to be passed |
| * to the callback function as argument. |
| * |
| */ |
| void InetLayer::CancelResolveHostAddress(DNSResolveCompleteFunct onComplete, void * appState) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| if (State != kState_Initialized) |
| return; |
| |
| for (size_t i = 0; i < DNSResolver::sPool.Size(); i++) |
| { |
| DNSResolver * lResolver = DNSResolver::sPool.Get(*mSystemLayer, i); |
| |
| if (lResolver == nullptr) |
| { |
| continue; |
| } |
| |
| if (!lResolver->IsCreatedByInetLayer(*this)) |
| { |
| continue; |
| } |
| |
| if (lResolver->OnComplete != onComplete) |
| { |
| continue; |
| } |
| |
| if (lResolver->AppState != appState) |
| { |
| continue; |
| } |
| |
| #if CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| if (lResolver->mState == DNSResolver::kState_Canceled) |
| { |
| continue; |
| } |
| #endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS |
| |
| lResolver->Cancel(); |
| break; |
| } |
| } |
| |
| #endif // INET_CONFIG_ENABLE_DNS_RESOLVER |
| |
| /** |
| * Get the interface identifier for the specified IP address. If the |
| * interface identifier cannot be derived it is set to the |
| * #INET_NULL_INTERFACEID. |
| * |
| * @note |
| * This function fetches the first interface (from the configured list |
| * of interfaces) that matches the specified IP address. |
| * |
| * @param[in] addr A reference to the IPAddress object. |
| * |
| * @param[out] intfId A reference to the InterfaceId object. |
| * |
| * @return #INET_NO_ERROR unconditionally. |
| * |
| */ |
| INET_ERROR InetLayer::GetInterfaceFromAddr(const IPAddress & addr, InterfaceId & intfId) |
| { |
| InterfaceAddressIterator addrIter; |
| |
| for (; addrIter.HasCurrent(); addrIter.Next()) |
| { |
| IPAddress curAddr = addrIter.GetAddress(); |
| if (addr == curAddr) |
| { |
| intfId = addrIter.GetInterface(); |
| return INET_NO_ERROR; |
| } |
| } |
| |
| intfId = INET_NULL_INTERFACEID; |
| |
| return INET_NO_ERROR; |
| } |
| |
| /** |
| * Check if there is a prefix match between the specified IPv6 address and any of |
| * the locally configured IPv6 addresses. |
| * |
| * @param[in] addr The IPv6 address to check for the prefix-match. |
| * |
| * @return true if a successful match is found, otherwise false. |
| * |
| */ |
| bool InetLayer::MatchLocalIPv6Subnet(const IPAddress & addr) |
| { |
| if (addr.IsIPv6LinkLocal()) |
| return true; |
| |
| InterfaceAddressIterator ifAddrIter; |
| for (; ifAddrIter.HasCurrent(); ifAddrIter.Next()) |
| { |
| IPPrefix addrPrefix; |
| addrPrefix.IPAddr = ifAddrIter.GetAddress(); |
| #if INET_CONFIG_ENABLE_IPV4 |
| if (addrPrefix.IPAddr.IsIPv4()) |
| continue; |
| #endif // INET_CONFIG_ENABLE_IPV4 |
| if (addrPrefix.IPAddr.IsIPv6LinkLocal()) |
| continue; |
| addrPrefix.Length = ifAddrIter.GetIPv6PrefixLength(); |
| if (addrPrefix.MatchAddress(addr)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0 |
| void InetLayer::HandleTCPInactivityTimer(chip::System::Layer * aSystemLayer, void * aAppState, chip::System::Error aError) |
| { |
| InetLayer & lInetLayer = *reinterpret_cast<InetLayer *>(aAppState); |
| bool lTimerRequired = lInetLayer.IsIdleTimerRunning(); |
| |
| for (size_t i = 0; i < INET_CONFIG_NUM_TCP_ENDPOINTS; i++) |
| { |
| TCPEndPoint * lEndPoint = TCPEndPoint::sPool.Get(*aSystemLayer, i); |
| |
| if (lEndPoint == nullptr) |
| continue; |
| if (!lEndPoint->IsCreatedByInetLayer(lInetLayer)) |
| continue; |
| if (!lEndPoint->IsConnected()) |
| continue; |
| if (lEndPoint->mIdleTimeout == 0) |
| continue; |
| |
| if (lEndPoint->mRemainingIdleTime == 0) |
| { |
| lEndPoint->DoClose(INET_ERROR_IDLE_TIMEOUT, false); |
| } |
| else |
| { |
| --lEndPoint->mRemainingIdleTime; |
| } |
| } |
| |
| if (lTimerRequired) |
| { |
| aSystemLayer->StartTimer(INET_TCP_IDLE_CHECK_INTERVAL, HandleTCPInactivityTimer, &lInetLayer); |
| } |
| } |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0 |
| |
| #if CHIP_SYSTEM_CONFIG_USE_LWIP |
| chip::System::Error InetLayer::HandleInetLayerEvent(chip::System::Object & aTarget, chip::System::EventType aEventType, |
| uintptr_t aArgument) |
| { |
| assertChipStackLockedByCurrentThread(); |
| |
| VerifyOrReturnError(INET_IsInetEvent(aEventType), CHIP_SYSTEM_ERROR_UNEXPECTED_EVENT); |
| |
| // Dispatch the event according to its type. |
| switch (aEventType) |
| { |
| #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
| case kInetEvent_TCPConnectComplete: |
| static_cast<TCPEndPoint &>(aTarget).HandleConnectComplete(static_cast<INET_ERROR>(aArgument)); |
| break; |
| |
| case kInetEvent_TCPConnectionReceived: |
| static_cast<TCPEndPoint &>(aTarget).HandleIncomingConnection(reinterpret_cast<TCPEndPoint *>(aArgument)); |
| break; |
| |
| case kInetEvent_TCPDataReceived: |
| static_cast<TCPEndPoint &>(aTarget).HandleDataReceived( |
| System::PacketBufferHandle::Adopt(reinterpret_cast<chip::System::PacketBuffer *>(aArgument))); |
| break; |
| |
| case kInetEvent_TCPDataSent: |
| static_cast<TCPEndPoint &>(aTarget).HandleDataSent(static_cast<uint16_t>(aArgument)); |
| break; |
| |
| case kInetEvent_TCPError: |
| static_cast<TCPEndPoint &>(aTarget).HandleError(static_cast<INET_ERROR>(aArgument)); |
| break; |
| #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_RAW_ENDPOINT |
| case kInetEvent_RawDataReceived: |
| static_cast<RawEndPoint &>(aTarget).HandleDataReceived( |
| System::PacketBufferHandle::Adopt(reinterpret_cast<chip::System::PacketBuffer *>(aArgument))); |
| break; |
| #endif // INET_CONFIG_ENABLE_RAW_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_UDP_ENDPOINT |
| case kInetEvent_UDPDataReceived: |
| static_cast<UDPEndPoint &>(aTarget).HandleDataReceived( |
| System::PacketBufferHandle::Adopt(reinterpret_cast<chip::System::PacketBuffer *>(aArgument))); |
| break; |
| #endif // INET_CONFIG_ENABLE_UDP_ENDPOINT |
| |
| #if INET_CONFIG_ENABLE_DNS_RESOLVER |
| case kInetEvent_DNSResolveComplete: |
| static_cast<DNSResolver &>(aTarget).HandleResolveComplete(); |
| break; |
| #endif // INET_CONFIG_ENABLE_DNS_RESOLVER |
| |
| default: |
| return CHIP_SYSTEM_ERROR_UNEXPECTED_EVENT; |
| } |
| |
| // If the event was droppable, record the fact that it has been dequeued. |
| if (IsDroppableEvent(aEventType)) |
| { |
| InetLayerBasis & lBasis = static_cast<InetLayerBasis &>(aTarget); |
| InetLayer & lInetLayer = lBasis.Layer(); |
| |
| lInetLayer.DroppableEventDequeued(); |
| } |
| |
| return CHIP_SYSTEM_NO_ERROR; |
| } |
| |
| #endif // CHIP_SYSTEM_CONFIG_USE_LWIP |
| |
| /** |
| * Reset the members of the IPPacketInfo object. |
| * |
| */ |
| void IPPacketInfo::Clear() |
| { |
| SrcAddress = IPAddress::Any; |
| DestAddress = IPAddress::Any; |
| Interface = INET_NULL_INTERFACEID; |
| SrcPort = 0; |
| DestPort = 0; |
| } |
| |
| #if !INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS |
| |
| // MARK: InetLayer platform- and system-specific functions for InetLayer |
| // construction and destruction. |
| |
| namespace Platform { |
| namespace InetLayer { |
| |
| /** |
| * This is a platform-specific InetLayer pre-initialization hook. This |
| * may be overridden by assserting the preprocessor definition, |
| * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS. |
| * |
| * @param[in,out] aLayer A pointer to the InetLayer instance being |
| * initialized. |
| * |
| * @param[in,out] aContext Platform-specific context data passed to |
| * the layer initialization method, \::Init. |
| * |
| * @return #INET_NO_ERROR on success; otherwise, a specific error indicating |
| * the reason for initialization failure. Returning non-successful |
| * status will abort initialization. |
| * |
| */ |
| DLL_EXPORT INET_ERROR WillInit(Inet::InetLayer * aLayer, void * aContext) |
| { |
| (void) aLayer; |
| (void) aContext; |
| |
| return INET_NO_ERROR; |
| } |
| |
| /** |
| * This is a platform-specific InetLayer post-initialization hook. This |
| * may be overridden by assserting the preprocessor definition, |
| * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS. |
| * |
| * @param[in,out] aLayer A pointer to the InetLayer instance being |
| * initialized. |
| * |
| * @param[in,out] aContext Platform-specific context data passed to |
| * the layer initialization method, \::Init. |
| * |
| * @param[in] anError The overall status being returned via the |
| * InetLayer \::Init method. |
| * |
| */ |
| DLL_EXPORT void DidInit(Inet::InetLayer * aLayer, void * aContext, INET_ERROR anError) |
| { |
| (void) aLayer; |
| (void) aContext; |
| (void) anError; |
| } |
| |
| /** |
| * This is a platform-specific InetLayer pre-shutdown hook. This |
| * may be overridden by assserting the preprocessor definition, |
| * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS. |
| * |
| * @param[in,out] aLayer A pointer to the InetLayer instance being |
| * shutdown. |
| * |
| * @param[in,out] aContext Platform-specific context data passed to |
| * the layer initialization method, \::Init. |
| * |
| * @return #INET_NO_ERROR on success; otherwise, a specific error indicating |
| * the reason for shutdown failure. Returning non-successful |
| * status will abort shutdown. |
| * |
| */ |
| DLL_EXPORT INET_ERROR WillShutdown(Inet::InetLayer * aLayer, void * aContext) |
| { |
| (void) aLayer; |
| (void) aContext; |
| |
| return INET_NO_ERROR; |
| } |
| |
| /** |
| * This is a platform-specific InetLayer post-shutdown hook. This |
| * may be overridden by assserting the preprocessor definition, |
| * #INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS. |
| * |
| * @param[in,out] aLayer A pointer to the InetLayer instance being |
| * shutdown. |
| * |
| * @param[in,out] aContext Platform-specific context data passed to |
| * the layer initialization method, \::Init. |
| * |
| * @param[in] anError The overall status being returned via the |
| * InetLayer \::Shutdown method. |
| * |
| */ |
| DLL_EXPORT void DidShutdown(Inet::InetLayer * aLayer, void * aContext, INET_ERROR anError) |
| { |
| (void) aLayer; |
| (void) aContext; |
| (void) anError; |
| } |
| |
| } // namespace InetLayer |
| } // namespace Platform |
| |
| #endif // !INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS |
| |
| } // namespace Inet |
| } // namespace chip |