blob: 8b006a0f83f7b35622cfe05332318fb88ce8385c [file] [log] [blame]
/*
*
* 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 a System::Layer API.
*
* 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 <lib/support/CodeUtils.h>
#include <lib/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 {
/**
* 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() {}
/**
* 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.
*
* @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 #CHIP_ERROR_INCORRECT_STATE If the InetLayer is in an
* incorrect state.
* @retval #CHIP_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 #CHIP_NO_ERROR On success.
*
*/
CHIP_ERROR InetLayer::Init(chip::System::Layer & aSystemLayer, void * aContext)
{
Inet::RegisterLayerErrorFormatter();
VerifyOrReturnError(mLayerState.SetInitializing(), CHIP_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;
mSystemLayer = &aSystemLayer;
mContext = aContext;
mLayerState.SetInitialized();
return CHIP_NO_ERROR;
}
/**
* This is the InetLayer explicit deinitializer and should be called
* prior to disposing of an instantiated InetLayer instance.
*
* @return #CHIP_NO_ERROR on success; otherwise, a specific error indicating
* the reason for shutdown failure.
*
*/
CHIP_ERROR InetLayer::Shutdown()
{
VerifyOrReturnError(mLayerState.SetShuttingDown(), CHIP_ERROR_INCORRECT_STATE);
CHIP_ERROR err = CHIP_NO_ERROR;
#if INET_CONFIG_ENABLE_TCP_ENDPOINT
// Abort all TCP endpoints owned by this instance.
TCPEndPointImpl::sPool.ForEachActiveObject([&](TCPEndPoint * lEndPoint) {
if ((lEndPoint != nullptr) && &lEndPoint->Layer() == this)
{
lEndPoint->Abort();
}
return true;
});
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT
#if INET_CONFIG_ENABLE_UDP_ENDPOINT
// Close all UDP endpoints owned by this instance.
UDPEndPointImpl::sPool.ForEachActiveObject([&](UDPEndPoint * lEndPoint) {
if ((lEndPoint != nullptr) && &lEndPoint->Layer() == this)
{
lEndPoint->Close();
}
return true;
});
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
mLayerState.SetShutdown();
mLayerState.Reset(); // Return to uninitialized state to permit re-initialization.
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.
TCPEndPointImpl::sPool.ForEachActiveObject([&](TCPEndPoint * lEndPoint) {
if ((lEndPoint != nullptr) && (lEndPoint->mIdleTimeout != 0))
{
timerRunning = true;
return false;
}
return true;
});
return timerRunning;
}
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0
#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 #CHIP_ERROR_INCORRECT_STATE If the InetLayer object is not initialized.
* @retval #CHIP_ERROR_ENDPOINT_POOL_FULL If the InetLayer TCPEndPoint pool is full and no new
* endpoints can be created.
* @retval #CHIP_NO_ERROR On success.
*
*/
CHIP_ERROR InetLayer::NewTCPEndPoint(TCPEndPoint ** retEndPoint)
{
assertChipStackLockedByCurrentThread();
*retEndPoint = nullptr;
VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
*retEndPoint = TCPEndPointImpl::sPool.CreateObject(*this);
if (*retEndPoint == nullptr)
{
ChipLogError(Inet, "%s endpoint pool FULL", "TCP");
return CHIP_ERROR_ENDPOINT_POOL_FULL;
}
SYSTEM_STATS_INCREMENT(chip::System::Stats::kInetLayer_NumTCPEps);
return CHIP_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 #CHIP_ERROR_INCORRECT_STATE If the InetLayer object is not initialized.
* @retval #CHIP_ERROR_ENDPOINT_POOL_FULL If the InetLayer UDPEndPoint pool is full and no new
* endpoints can be created.
* @retval #CHIP_NO_ERROR On success.
*
*/
CHIP_ERROR InetLayer::NewUDPEndPoint(UDPEndPoint ** retEndPoint)
{
assertChipStackLockedByCurrentThread();
*retEndPoint = nullptr;
VerifyOrReturnError(mLayerState.IsInitialized(), CHIP_ERROR_INCORRECT_STATE);
*retEndPoint = UDPEndPointImpl::sPool.CreateObject(*this);
if (*retEndPoint == nullptr)
{
ChipLogError(Inet, "%s endpoint pool FULL", "UDP");
return CHIP_ERROR_ENDPOINT_POOL_FULL;
}
SYSTEM_STATS_INCREMENT(chip::System::Stats::kInetLayer_NumUDPEps);
return CHIP_NO_ERROR;
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
#if INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0
void InetLayer::HandleTCPInactivityTimer(chip::System::Layer * aSystemLayer, void * aAppState)
{
InetLayer & lInetLayer = *reinterpret_cast<InetLayer *>(aAppState);
bool lTimerRequired = lInetLayer.IsIdleTimerRunning();
TCPEndPointImpl::sPool.ForEachActiveObject([&](TCPEndPoint * lEndPoint) {
if (&lEndPoint->Layer() != &lInetLayer)
return true;
if (!lEndPoint->IsConnected())
return true;
if (lEndPoint->mIdleTimeout == 0)
return true;
if (lEndPoint->mRemainingIdleTime == 0)
{
lEndPoint->DoClose(INET_ERROR_IDLE_TIMEOUT, false);
}
else
{
--lEndPoint->mRemainingIdleTime;
}
return true;
});
if (lTimerRequired)
{
aSystemLayer->StartTimer(System::Clock::Milliseconds32(INET_TCP_IDLE_CHECK_INTERVAL), HandleTCPInactivityTimer,
&lInetLayer);
}
}
#endif // INET_CONFIG_ENABLE_TCP_ENDPOINT && INET_TCP_IDLE_CHECK_INTERVAL > 0
/**
* Reset the members of the IPPacketInfo object.
*
*/
void IPPacketInfo::Clear()
{
SrcAddress = IPAddress::Any;
DestAddress = IPAddress::Any;
Interface = InterfaceId::Null();
SrcPort = 0;
DestPort = 0;
}
} // namespace Inet
} // namespace chip