blob: f6beb3623f81120cfdb3c9cdba32516236e13afa [file] [log] [blame]
/*
*
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file 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, event readiness notification is handled
* via file descriptors and a traditional poll / select
* implementation on the platform adaptation.
*
* For LwIP, event readiness notification is handle 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/InetLayer.h>
#include <InetLayer/InetFaultInjection.h>
#include <SystemLayer/SystemTimer.h>
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <lwip/sys.h>
#include <lwip/netif.h>
#else // !WEAVE_SYSTEM_CONFIG_USE_LWIP
#include <unistd.h>
#include <fcntl.h>
#include <net/if.h>
#ifdef __ANDROID__
#include <ifaddrs-android.h>
#else
#include <ifaddrs.h>
#endif // __ANDROID__
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS
// MARK: InetLayer platform- and system-specific functions for LwIP-native eventing.
struct LwIPInetEvent
{
nl::Inet::InetEventType Type;
nl::Inet::InetLayerBasis* Target;
uintptr_t Arg;
};
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
namespace nl {
namespace Inet {
void InetLayer::UpdateSnapshot(nl::Weave::System::Stats::Snapshot &aSnapshot)
{
#if INET_CONFIG_ENABLE_DNS_RESOLVER
DNSResolver::sPool.GetStatistics(aSnapshot.mResourcesInUse[nl::Weave::System::Stats::kInetLayer_NumDNSResolvers]);
#endif // INET_CONFIG_ENABLE_DNS_RESOLVER
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
TCPEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[nl::Weave::System::Stats::kInetLayer_NumTCPEps]);
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
#if INET_CONFIG_ENABLE_UDP_ENDPOINT
UDPEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[nl::Weave::System::Stats::kInetLayer_NumUDPEps]);
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_RAW_ENDPOINT
RawEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[nl::Weave::System::Stats::kInetLayer_NumRawEps]);
#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT
#if INET_CONFIG_ENABLE_TUN_ENDPOINT
TunEndPoint::sPool.GetStatistics(aSnapshot.mResourcesInUse[nl::Weave::System::Stats::kInetLayer_NumTunEps]);
#endif // INET_CONFIG_ENABLE_TUN_ENDPOINT
}
/**
* This is the InetLayer default constructor.
*
* It performs some basic data member initialization; however, since
* InetLayer follows an explicit initializer design pattern, the ::Init
* method must be called successfully prior to using the object.
*
*/
InetLayer::InetLayer(void)
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
: mImplicitSystemLayer()
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
{
State = kState_NotInitialized;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
if (!sInetEventHandlerDelegate.IsInitialized())
sInetEventHandlerDelegate.Init(HandleInetLayerEvent);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
Weave::System::LwIPEventHandlerDelegate InetLayer::sInetEventHandlerDelegate;
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if INET_CONFIG_MAX_DROPPABLE_EVENTS && WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_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;
mDroppableEvents = xSemaphoreCreateCounting(maximum, initial);
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 // !WEAVE_SYSTEM_CONFIG_FREERTOS_LOCKING
INET_ERROR InetLayer::InitQueueLimiter(void)
{
if (sem_init(&mDroppableEvents, 0, INET_CONFIG_MAX_DROPPABLE_EVENTS) != 0)
{
return INET_MapOSError(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 // !WEAVE_SYSTEM_CONFIG_FREERTOS_LOCKING
#endif // INET_CONFIG_MAX_DROPPABLE_EVENTS && WEAVE_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 Weave 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
* intialization failure.
* @retval #INET_NO_ERROR On success.
*
*/
INET_ERROR InetLayer::Init(Weave::System::Layer& aSystemLayer, void *aContext)
{
INET_ERROR err = INET_NO_ERROR;
if (State != kState_NotInitialized)
return INET_ERROR_INCORRECT_STATE;
// Platform-specific intialization may elect to set this data
// member. Ensure it is set to a sane default value before
// invoking platform-specific intialization.
mPlatformData = NULL;
Platform::InetLayer::WillInit(this, aContext);
SuccessOrExit(err);
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
if (&aSystemLayer == &mImplicitSystemLayer)
{
err = mImplicitSystemLayer.Init(aContext);
SuccessOrExit(err);
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
mSystemLayer = &aSystemLayer;
mContext = aContext;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
err = InitQueueLimiter();
SuccessOrExit(err);
mSystemLayer->AddEventHandlerDelegate(sInetEventHandlerDelegate);
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
State = kState_Initialized;
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
#if INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
err = mAsyncDNSResolver.Init(this);
SuccessOrExit(err);
#endif // INET_CONFIG_ENABLE_DNS_RESOLVER && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
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(void)
{
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 != NULL) && lResolver->IsCreatedByInetLayer(*this))
{
lResolver->Cancel();
}
}
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
err = mAsyncDNSResolver.Shutdown();
#endif // WEAVE_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 != NULL) && 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 != NULL) && 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 != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->Close();
}
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
if (mSystemLayer == &mImplicitSystemLayer)
{
err = mImplicitSystemLayer.Shutdown();
SuccessOrExit(err);
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
}
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(void)
{
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(void)
{
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 != NULL) && (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)
{
INET_ERROR err = INET_NO_ERROR;
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
#if !LWIP_IPV6
err = INET_ERROR_NOT_IMPLEMENTED;
goto out;
#endif //!LWIP_IPV6
#endif //WEAVE_SYSTEM_CONFIG_USE_LWIP
if ( llAddr == NULL )
{
err = INET_ERROR_BAD_ARGS;
goto out;
}
#if WEAVE_SYSTEM_CONFIG_USE_LWIP
for (struct netif *intf = netif_list; intf != NULL; intf = intf->next)
{
if ( (link != NULL) && (link != intf) ) continue;
int j; for (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));
goto out;
}
}
if (link != NULL) {
err = INET_ERROR_ADDRESS_NOT_FOUND;
break;
}
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
struct ifaddrs *ifaddr;
int rv;
rv = getifaddrs(&ifaddr);
if (rv != -1)
{
struct ifaddrs*ifaddr_iter = ifaddr;
while (ifaddr_iter != NULL)
{
if (ifaddr_iter->ifa_addr != NULL)
{
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 = &((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(((struct sockaddr_in6*)ifaddr_iter->ifa_addr)->sin6_addr);
break;
}
}
}
ifaddr_iter = ifaddr_iter->ifa_next;
}
freeifaddrs(ifaddr);
}
else
{
err = INET_ERROR_ADDRESS_NOT_FOUND;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
out:
return err;
}
#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[inout] 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)
{
INET_ERROR err = INET_NO_ERROR;
*retEndPoint = NULL;
VerifyOrExit(State == kState_Initialized, err = INET_ERROR_INCORRECT_STATE);
*retEndPoint = RawEndPoint::sPool.TryCreate(*mSystemLayer);
if (*retEndPoint != NULL)
{
(*retEndPoint)->nl::Inet::RawEndPoint::Init(this, ipVer, ipProto);
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kInetLayer_NumRawEps);
}
else
{
WeaveLogError(Inet, "%s endpoint pool FULL", "Raw");
err = INET_ERROR_NO_ENDPOINTS;
}
exit:
return err;
}
#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[inout] 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)
{
INET_ERROR err = INET_NO_ERROR;
*retEndPoint = NULL;
VerifyOrExit(State == kState_Initialized, err = INET_ERROR_INCORRECT_STATE);
*retEndPoint = TCPEndPoint::sPool.TryCreate(*mSystemLayer);
if (*retEndPoint != NULL)
{
(*retEndPoint)->Init(this);
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kInetLayer_NumTCPEps);
}
else
{
WeaveLogError(Inet, "%s endpoint pool FULL", "TCP");
err = INET_ERROR_NO_ENDPOINTS;
}
exit:
return err;
}
#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[inout] 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)
{
INET_ERROR err = INET_NO_ERROR;
*retEndPoint = NULL;
VerifyOrExit(State == kState_Initialized, err = INET_ERROR_INCORRECT_STATE);
*retEndPoint = UDPEndPoint::sPool.TryCreate(*mSystemLayer);
if (*retEndPoint != NULL)
{
(*retEndPoint)->Init(this);
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kInetLayer_NumUDPEps);
}
else
{
WeaveLogError(Inet, "%s endpoint pool FULL", "UDP");
err = INET_ERROR_NO_ENDPOINTS;
}
exit:
return err;
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_TUN_ENDPOINT
/**
* Creates a new TunEndPoint object.
*
* @note
* This function gets a free TunEndPoint object from a pre-allocated pool
* and also calls the explicit initializer on the new object.
*
* @param[inout] retEndPoint A pointer to a pointer of the TunEndPoint 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 TunEndPoint pool is full and no new
* ones can be created.
* @retval #INET_NO_ERROR On success.
*
*/
INET_ERROR InetLayer::NewTunEndPoint(TunEndPoint **retEndPoint)
{
INET_ERROR err = INET_NO_ERROR;
*retEndPoint = NULL;
VerifyOrExit(State == kState_Initialized, err = INET_ERROR_INCORRECT_STATE);
*retEndPoint = TunEndPoint::sPool.TryCreate(*mSystemLayer);
if (*retEndPoint != NULL)
{
(*retEndPoint)->Init(this);
SYSTEM_STATS_INCREMENT(nl::Weave::System::Stats::kInetLayer_NumTunEps);
}
else
{
WeaveLogError(Inet, "%s endpoint pool FULL", "Tun");
err = INET_ERROR_NO_ENDPOINTS;
}
exit:
return err;
}
#endif // INET_CONFIG_ENABLE_TUN_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,
DNSResolver::OnResolveCompleteFunct onComplete, void *appState)
{
return ResolveHostAddress(hostName, strlen(hostName), 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,
DNSResolver::OnResolveCompleteFunct onComplete, void *appState)
{
INET_ERROR err = INET_NO_ERROR;
DNSResolver *resolver = NULL;
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 != NULL)
{
resolver->InitInetLayerBasis(*this);
}
else
{
WeaveLogError(Inet, "%s resolver pool FULL", "DNS");
ExitNow(err = INET_ERROR_NO_MEMORY);
}
if (IPAddress::FromString(hostName, *addrArray))
{
if (onComplete)
{
onComplete(appState, INET_NO_ERROR, 1, addrArray);
}
resolver->Release();
resolver = NULL;
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 WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
err = mAsyncDNSResolver.PrepareDNSResolver(*resolver, hostName, hostNameLen, maxAddrs,
addrArray, onComplete, appState);
SuccessOrExit(err);
mAsyncDNSResolver.EnqueueRequest(*resolver);
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
#if !INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
err = resolver->Resolve(hostName, hostNameLen, 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(DNSResolver::OnResolveCompleteFunct onComplete, void *appState)
{
if (State != kState_Initialized)
return;
for (size_t i = 0; i < DNSResolver::sPool.Size(); i++)
{
DNSResolver* lResolver = DNSResolver::sPool.Get(*mSystemLayer, i);
if (lResolver == NULL)
{
continue;
}
if (!lResolver->IsCreatedByInetLayer(*this))
{
continue;
}
if (lResolver->OnComplete != onComplete)
{
continue;
}
if (lResolver->AppState != appState)
{
continue;
}
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
if (lResolver->mState == DNSResolver::kState_Canceled)
{
continue;
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS && INET_CONFIG_ENABLE_ASYNC_DNS_SOCKETS
lResolver->Cancel();
break;
}
}
#endif // INET_CONFIG_ENABLE_DNS_RESOLVER
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
static void SystemTimerComplete(Weave::System::Layer* aLayer, void* aAppState, Weave::System::Error aError)
{
Weave::System::Timer& lTimer = *reinterpret_cast<Weave::System::Timer*>(aAppState);
InetLayer& lInetLayer = *lTimer.InetLayer();
InetLayer::TimerCompleteFunct lComplete = reinterpret_cast<InetLayer::TimerCompleteFunct>(lTimer.OnCompleteInetLayer());
void* lAppState = lTimer.AppStateInetLayer();
lComplete(&lInetLayer, lAppState, static_cast<INET_ERROR>(aError));
}
/**
* @brief
* This method registers a one-shot timer(to fire at a specified offset relative to the time
* of registration) with the underlying timer mechanism, through this InetLayer instance.
*
* @note
* Each InetLayer instance could have its own set of timers. This might not be
* significant to applications, as each application usually works with one singleton
* InetLayer instance.
*
* @param[in] aMilliseconds Number of milliseconds before this timer should fire.
*
* @param[in] aComplete A pointer to a callback function to be called when this
* timer fires.
*
* @param[in] aAppState A pointer to an application state object to be passed
* to the callback function as argument.
*
* @retval #INET_ERROR_INCORRECT_STATE If the InetLayer instance is not initialized.
* @retval #INET_ERROR_NO_MEMORY If the InetLayer runs out of resource for this
* request for a new timer.
* @retval #INET_NO_ERROR On success.
*
*/
INET_ERROR InetLayer::StartTimer(uint32_t aMilliseconds, TimerCompleteFunct aComplete, void* aAppState)
{
INET_ERROR lReturn;
if (State != kState_Initialized)
{
return INET_ERROR_INCORRECT_STATE;
}
Weave::System::Timer* lTimer;
lReturn = mSystemLayer->NewTimer(lTimer);
SuccessOrExit(lReturn);
lTimer->AttachInetLayer(*this, reinterpret_cast<void*>(aComplete), aAppState);
lReturn = lTimer->Start(aMilliseconds, SystemTimerComplete, lTimer);
if (lReturn != WEAVE_SYSTEM_NO_ERROR)
{
lTimer->Cancel();
}
exit:
switch (lReturn)
{
case WEAVE_SYSTEM_ERROR_NO_MEMORY: lReturn = INET_ERROR_NO_MEMORY; break;
case WEAVE_SYSTEM_NO_ERROR: lReturn = INET_NO_ERROR; break;
}
return lReturn;
}
/**
* @brief
* This method cancels an one-shot timer, started earlier through @p StartTimer().
*
* @note
* The cancellation could fail silently in two different ways. If the timer
* specified by the combination of the callback function and application state object
* couldn't be found, cancellation could fail. If the timer has fired, but not yet
* removed from memory, cancellation could also fail.
*
* @param[in] aComplete A pointer to the callback function used in calling @p StartTimer().
* @param[in] aAppState A pointer to the application state object used in calling
* @p StartTimer().
*
*/
void InetLayer::CancelTimer(TimerCompleteFunct aComplete, void* aAppState)
{
if (State == kState_Initialized)
{
mSystemLayer->CancelAllMatchingInetTimers(*this, reinterpret_cast<void*>(aComplete), aAppState);
}
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
/**
* 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(Weave::System::Layer* aSystemLayer, void* aAppState, Weave::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 == NULL) 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 WEAVE_SYSTEM_CONFIG_USE_LWIP
Weave::System::Error InetLayer::HandleInetLayerEvent(Weave::System::Object& aTarget, Weave::System::EventType aEventType,
uintptr_t aArgument)
{
Weave::System::Error lReturn = WEAVE_SYSTEM_NO_ERROR;
InetLayerBasis& lBasis = static_cast<InetLayerBasis&>(aTarget);
VerifyOrExit(INET_IsInetEvent(aEventType), lReturn = WEAVE_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(reinterpret_cast<Weave::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(reinterpret_cast<Weave::System::PacketBuffer*>(aArgument));
break;
#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT
#if INET_CONFIG_ENABLE_UDP_ENDPOINT
case kInetEvent_UDPDataReceived:
static_cast<UDPEndPoint &>(aTarget).HandleDataReceived(reinterpret_cast<Weave::System::PacketBuffer*>(aArgument));
break;
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_TUN_ENDPOINT
case kInetEvent_TunDataReceived:
static_cast<TunEndPoint &>(aTarget).HandleDataReceived(reinterpret_cast<Weave::System::PacketBuffer*>(aArgument));
break;
#endif // INET_CONFIG_ENABLE_TUN_ENDPOINT
#if INET_CONFIG_ENABLE_DNS_RESOLVER
case kInetEvent_DNSResolveComplete:
static_cast<DNSResolver &>(aTarget).HandleResolveComplete();
break;
#endif // INET_CONFIG_ENABLE_DNS_RESOLVER
default:
lReturn = WEAVE_SYSTEM_ERROR_UNEXPECTED_EVENT;
ExitNow();
}
// If the event was droppable, record the fact that it has been dequeued.
if (IsDroppableEvent(aEventType))
{
InetLayer& lInetLayer = lBasis.Layer();
lInetLayer.DroppableEventDequeued();
}
exit:
return lReturn;
}
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
/**
* This posts an event / message of the specified type with the
* provided argument to this instance's platform-specific event /
* message queue.
*
* @param[inout] target A pointer to the InetLayer object making the
* post request.
*
* @param[in] type The type of event to post.
*
* @param[inout] arg The argument associated with the event to post.
*
* @retval #INET_ERROR_INCORRECT_STATE If the state of the InetLayer
* object is incorrect.
* @retval #INET_ERROR_BAD_ARGS If #target is NULL.
* @retval #INET_ERROR_NO_MEMORY If the EventQueue is already
* full.
* @retval other platform-specific errors generated indicating the reason
* for failure.
* @retval #INET_NO_ERROR On success.
*
*/
INET_ERROR InetLayer::PostEvent(InetLayerBasis *target, InetEventType type, uintptr_t arg)
{
Weave::System::Layer& lSystemLayer = *mSystemLayer;
INET_ERROR retval = INET_NO_ERROR;
VerifyOrExit(State == kState_Initialized, retval = INET_ERROR_INCORRECT_STATE);
VerifyOrExit(target != NULL, retval = INET_ERROR_BAD_ARGS);
{
Weave::System::Layer& lTargetSystemLayer = target->SystemLayer();
VerifyOrDieWithMsg(target->IsRetained(lSystemLayer), Inet, "wrong system layer! [target %p != instance %p]",
&lTargetSystemLayer, &lSystemLayer);
}
// Sanity check that this instance and the target layer haven't
// been "crossed".
{
InetLayer& lTargetInetLayer = target->Layer();
VerifyOrDieWithMsg(this == &lTargetInetLayer, Inet, "target layer %p != instance layer %p", &lTargetInetLayer, this);
}
if (IsDroppableEvent(type) && !CanEnqueueDroppableEvent())
{
WeaveLogProgress(Inet, "Dropping incoming packet (type %d)", (int)type);
ExitNow(retval = INET_ERROR_NO_MEMORY);
}
retval = Platform::InetLayer::PostEvent(this, mContext, target, type, arg);
if (retval != INET_NO_ERROR)
{
WeaveLogError(Inet, "Failed to queue InetLayer event (type %d): %s", (int)type, ErrorStr(retval));
}
SuccessOrExit(retval);
exit:
return retval;
}
/**
* This is a syntactic wrapper around a platform-specific hook that
* effects an event loop, waiting on a queue that services this
* instance, pulling events off of that queue, and then dispatching
* them for handling.
*
* @return #INET_NO_ERROR on success; otherwise, a specific error
* indicating the reason for intialization failure.
*
*/
INET_ERROR InetLayer::DispatchEvents(void)
{
INET_ERROR retval = INET_NO_ERROR;
VerifyOrExit(State == kState_Initialized, retval = INET_ERROR_INCORRECT_STATE);
retval = Platform::InetLayer::DispatchEvents(this, mContext);
SuccessOrExit(retval);
exit:
return retval;
}
/**
* Start the platform timer with specified msec duration.
*
* @brief
* Calls the Platform specific API to start a platform timer.
* This API is called by the Weave::System::Timer class when one or more
* system timers are active and require deferred execution.
*
* @param[in] inDurMS The timer duration in milliseconds.
*
* @return INET_NO_ERROR on success, error code otherwise.
*
*/
INET_ERROR InetLayer::StartPlatformTimer(uint32_t inDurMS)
{
INET_ERROR retval;
VerifyOrExit(State == kState_Initialized, retval = INET_ERROR_INCORRECT_STATE);
retval = Platform::InetLayer::StartTimer(this, mContext, inDurMS);
exit:
return retval;
}
/**
* Handle the platform timer expiration event.
*
* @brief
* Calls Weave::System::Timer::HandleExpiredTimers to handle any expired
* system timers. It is assumed that this API is called only while
* on the thread which owns the InetLayer object.
*
* @return INET_NO_ERROR on success, error code otherwise.
*
*/
INET_ERROR InetLayer::HandlePlatformTimer(void)
{
INET_ERROR lReturn;
Weave::System::Error lSystemError;
VerifyOrExit(State == kState_Initialized, lReturn = INET_ERROR_INCORRECT_STATE);
lSystemError = mSystemLayer->HandlePlatformTimer();
lReturn = static_cast<INET_ERROR>(lSystemError);
exit:
return lReturn;
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
#if WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/**
* Prepare the sets of file descriptors for @p select() to work with.
*
* @param[out] nfds The range of file descriptors in the file
* descriptor set.
*
* @param[in] readfds A pointer to the set of readable file descriptors.
*
* @param[in] writefds A pointer to the set of writable file descriptors.
*
* @param[in] exceptfds A pointer to the set of file descriptors with errors.
*
*/
void InetLayer::PrepareSelect(int& nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval& sleepTimeTV)
{
if (State != kState_Initialized)
return;
#if INET_CONFIG_ENABLE_RAW_ENDPOINT
for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++)
{
RawEndPoint* lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
lEndPoint->PrepareIO().SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds);
}
#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++)
{
TCPEndPoint* lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
lEndPoint->PrepareIO().SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds);
}
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
#if INET_CONFIG_ENABLE_UDP_ENDPOINT
for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++)
{
UDPEndPoint* lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
lEndPoint->PrepareIO().SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds);
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_TUN_ENDPOINT
for (size_t i = 0; i < TunEndPoint::sPool.Size(); i++)
{
TunEndPoint* lEndPoint = TunEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
lEndPoint->PrepareIO().SetFDs(lEndPoint->mSocket, nfds, readfds, writefds, exceptfds);
}
#endif // INET_CONFIG_ENABLE_TUN_ENDPOINT
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
if (mSystemLayer == &mImplicitSystemLayer)
{
mSystemLayer->PrepareSelect(nfds, readfds, writefds, exceptfds, sleepTimeTV);
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
}
/**
* Handle I/O from a select call. This method registers the pending I/O
* event in each active endpoint and then invokes the respective I/O
* handling functions for those endpoints.
*
* @note
* It is important to set the pending I/O fields for all endpoints
* *before* making any callbacks. This avoids the case where an
* endpoint is closed and then re-opened within the callback for
* another endpoint. When this happens the new endpoint is likely
* to be assigned the same file descriptor as the old endpoint.
* However, any pending I/O for that file descriptor number represents
* I/O related to the old incarnation of the endpoint, not the current
* one. Saving the pending I/O state in each endpoint before acting
* on it allows the endpoint code to clear the I/O flags in the event
* of a close, thus avoiding any confusion.
*
* @param[in] selectRes The return value of the select call.
*
* @param[in] readfds A pointer to the set of read file descriptors.
*
* @param[in] writefds A pointer to the set of write file descriptors.
*
* @param[in] exceptfds A pointer to the set of file descriptors with
* errors.
*
*/
void InetLayer::HandleSelectResult(int selectRes, fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
{
if (State != kState_Initialized)
return;
if (selectRes < 0)
return;
if (selectRes > 0)
{
// Set the pending I/O field for each active endpoint based on the value returned by select.
#if INET_CONFIG_ENABLE_RAW_ENDPOINT
for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++)
{
RawEndPoint* lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds);
}
}
#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++)
{
TCPEndPoint* lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds);
}
}
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
#if INET_CONFIG_ENABLE_UDP_ENDPOINT
for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++)
{
UDPEndPoint* lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds);
}
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_TUN_ENDPOINT
for (size_t i = 0; i < TunEndPoint::sPool.Size(); i++)
{
TunEndPoint* lEndPoint = TunEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->mPendingIO = SocketEvents::FromFDs(lEndPoint->mSocket, readfds, writefds, exceptfds);
}
}
#endif // INET_CONFIG_ENABLE_TUN_ENDPOINT
// Now call each active endpoint to handle its pending I/O.
#if INET_CONFIG_ENABLE_RAW_ENDPOINT
for (size_t i = 0; i < RawEndPoint::sPool.Size(); i++)
{
RawEndPoint* lEndPoint = RawEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->HandlePendingIO();
}
}
#endif // INET_CONFIG_ENABLE_RAW_ENDPOINT
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
for (size_t i = 0; i < TCPEndPoint::sPool.Size(); i++)
{
TCPEndPoint* lEndPoint = TCPEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->HandlePendingIO();
}
}
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
#if INET_CONFIG_ENABLE_UDP_ENDPOINT
for (size_t i = 0; i < UDPEndPoint::sPool.Size(); i++)
{
UDPEndPoint* lEndPoint = UDPEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->HandlePendingIO();
}
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_TUN_ENDPOINT
for (size_t i = 0; i < TunEndPoint::sPool.Size(); i++)
{
TunEndPoint* lEndPoint = TunEndPoint::sPool.Get(*mSystemLayer, i);
if ((lEndPoint != NULL) && lEndPoint->IsCreatedByInetLayer(*this))
{
lEndPoint->HandlePendingIO();
}
}
#endif // INET_CONFIG_ENABLE_TUN_ENDPOINT
}
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
if (mSystemLayer == &mImplicitSystemLayer)
{
mSystemLayer->HandleSelectResult(selectRes, readfds, writefds, exceptfds);
}
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
}
#endif // WEAVE_SYSTEM_CONFIG_USE_SOCKETS
/**
* 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[inout] aLayer A pointer to the InetLayer instance being
* initialized.
*
* @param[inout] 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 intialization failure. Returning non-successful
* status will abort initialization.
*
*/
NL_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[inout] aLayer A pointer to the InetLayer instance being
* initialized.
*
* @param[inout] 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.
*
*/
NL_DLL_EXPORT void DidInit(Inet::InetLayer *aLayer, void *aContext, INET_ERROR anError)
{
(void)aLayer;
(void)aContext;
(void)anError;
return;
}
/**
* 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[inout] aLayer A pointer to the InetLayer instance being
* shutdown.
*
* @param[inout] 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.
*
*/
NL_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[inout] aLayer A pointer to the InetLayer instance being
* shutdown.
*
* @param[inout] 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.
*
*/
NL_DLL_EXPORT void DidShutdown(Inet::InetLayer *aLayer, void *aContext, INET_ERROR anError)
{
(void)aLayer;
(void)aContext;
(void)anError;
return;
}
#if INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
#if WEAVE_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS
/**
* This is a platform-specific event / message post hook. This may be
* overridden by assserting the preprocessor definition,
* #INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS.
*
* This posts an event / message of the specified type with the
* provided argument to this instance's platform-specific event /
* message queue.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aLayer A pointer to the layer instance to which the
* event / message is being posted.
*
* @param[inout] aContext Platform-specific context data passed to
* the layer initialization method, ::Init.
*
* @param[inout] aTarget A pointer to the InetLayer object making the
* post request.
*
* @param[in] aType The type of event to post.
*
* @param[inout] anArg The argument associated with the event to post.
*
* @return #INET_NO_ERROR on success; otherwise, a specific error indicating
* the reason for intialization failure.
*
*/
INET_ERROR PostEvent(Inet::InetLayer* aInetLayer, void* aContext, InetLayerBasis* aTarget, InetEventType aEventType,
uintptr_t aArgument)
{
Weave::System::Layer& lSystemLayer = *aInetLayer->mSystemLayer;
Weave::System::Object& lObject = *aTarget;
Weave::System::Error lReturn = Weave::System::Platform::Layer::PostEvent(lSystemLayer, aContext, lObject, aEventType,
aArgument);
return static_cast<INET_ERROR>(lReturn);
}
/**
* This is a platform-specific event / message dispatch hook. This may
* be overridden by assserting the preprocessor definition,
* INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS.
*
* This effects an event loop, waiting on a queue that services this
* instance, pulling events off of that queue, and then dispatching
* them for handling.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aInetLayer A pointer to the layer instance for which
* events / messages are being dispatched.
*
* @param[inout] aContext Platform-specific context data passed to
* the layer initialization method, ::Init.
*
* @retval #INET_ERROR_BAD_ARGS If #aLayer or #aContext is NULL.
* @retval #INET_ERROR_INCORRECT_STATE If the state of the InetLayer
* object is incorrect.
* @retval #INET_ERROR_UNEXPECTED_EVENT If an event type is unrecognized.
* @retval #INET_NO_ERROR On success.
*
*/
INET_ERROR DispatchEvents(Inet::InetLayer* aInetLayer, void* aContext)
{
Weave::System::Layer& lSystemLayer = *aInetLayer->mSystemLayer;
Weave::System::Error lReturn = Weave::System::Platform::Layer::DispatchEvents(lSystemLayer, aContext);
return static_cast<INET_ERROR>(lReturn);
}
/**
* This is a platform-specific event / message dispatch hook. This may
* be overridden by assserting the preprocessor definition,
* INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS.
*
* This dispatches the specified event for handling, unmarshalling the
* type and arguments from the event for hand off to
* InetLayer::HandleEvent for the actual dispatch.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aInetLayer A pointer to the layer instance for which
* events / messages are being dispatched.
*
* @param[inout] aContext Platform-specific context data passed to
* the layer initialization method, ::Init.
*
* @param[in] aEvent The platform-specific event object to
* dispatch for handling.
*
* @retval #INET_ERROR_BAD_ARGS If #aLayer or the event target is
* NULL.
* @retval #INET_ERROR_UNEXPECTED_EVENT If the event type is unrecognized.
* @retval #INET_ERROR_INCORRECT_STATE If the state of the InetLayer
* object is incorrect.
* @retval #INET_NO_ERROR On success.
*
*/
INET_ERROR DispatchEvent(Inet::InetLayer* aInetLayer, void* aContext, InetEvent aEvent)
{
Weave::System::Layer& lSystemLayer = *aInetLayer->mSystemLayer;
Weave::System::Error lReturn = Weave::System::Platform::Layer::DispatchEvent(lSystemLayer, aContext, aEvent);
return static_cast<INET_ERROR>(lReturn);
}
/**
* This is a platform-specific event / message dispatch hook. This may be overridden by assserting the preprocessor definition,
* #INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS.
*
* @note
* This is an implementation for LwIP.
*
* @param[inout] aInetLayer A reference to the layer instance for which events / messages are being dispatched.
* @param[inout] aContext Platform-specific context data passed to the layer initialization method, ::Init.
* @param[in] aMilliseconds The number of milliseconds to set for the timer.
*
* @retval #INET_NO_ERROR Always succeeds unless overriden.
*/
INET_ERROR StartTimer(Inet::InetLayer* aInetLayer, void* aContext, uint32_t aMilliseconds)
{
Weave::System::Layer& lSystemLayer = *aInetLayer->mSystemLayer;
Weave::System::Error lReturn = Weave::System::Platform::Layer::StartTimer(lSystemLayer, aContext, aMilliseconds);
return static_cast<INET_ERROR>(lReturn);
}
#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP && !INET_CONFIG_WILL_OVERRIDE_PLATFORM_EVENT_FUNCS
#endif // INET_CONFIG_PROVIDE_OBSOLESCENT_INTERFACES
} // namespace InetLayer
} // namespace Platform
#endif // !INET_CONFIG_WILL_OVERRIDE_PLATFORM_XTOR_FUNCS
} // namespace Inet
} // namespace nl