blob: f5e282d37a62e845715881f2c766bc6917a35e42 [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() {}
#if INET_CONFIG_MAX_DROPPABLE_EVENTS && CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_NO_LOCKING
CHIP_ERROR InetLayer::InitQueueLimiter(void)
{
mDroppableEvents = 0;
return CHIP_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
CHIP_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 CHIP_NO_ERROR;
else
return CHIP_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
CHIP_ERROR InetLayer::InitQueueLimiter(void)
{
if (sem_init(&mDroppableEvents, 0, INET_CONFIG_MAX_DROPPABLE_EVENTS) != 0)
{
return CHIP_ERROR_POSIX(errno);
}
return CHIP_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.
*
* @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;
#if CHIP_SYSTEM_CONFIG_USE_LWIP
ReturnErrorOnFailure(InitQueueLimiter());
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
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.
TCPEndPoint::sPool.ForEachActiveObject([&](TCPEndPoint * lEndPoint) {
if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*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.
UDPEndPoint::sPool.ForEachActiveObject([&](UDPEndPoint * lEndPoint) {
if ((lEndPoint != nullptr) && lEndPoint->IsCreatedByInetLayer(*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.
TCPEndPoint::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
/**
* Get the link local IPv6 address for a specified link or interface.
*
* @param[in] interface The interface for which the link local IPv6
* address is being sought.
*
* @param[out] llAddr The link local IPv6 address for the link.
*
* @retval #CHIP_ERROR_NOT_IMPLEMENTED If IPv6 is not supported.
* @retval #CHIP_ERROR_INVALID_ARGUMENT If the link local address
* is nullptr.
* @retval #INET_ERROR_ADDRESS_NOT_FOUND If the link does not have
* any address configured.
* @retval #CHIP_NO_ERROR On success.
*
*/
CHIP_ERROR InetLayer::GetLinkLocalAddr(InterfaceId interface, IPAddress * llAddr)
{
VerifyOrReturnError(llAddr != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
#if CHIP_SYSTEM_CONFIG_USE_LWIP
#if !LWIP_IPV6
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif //! LWIP_IPV6
struct netif * link = interface.GetPlatformInterface();
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(*netif_ip6_addr(intf, j));
return CHIP_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) &&
(!interface.IsPresent() || (if_nametoindex(ifaddr_iter->ifa_name) == interface.GetPlatformInterface())))
{
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((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 = interface.IsPresent() ? net_if_get_by_index(interface.GetPlatformInterface()) : net_if_get_default();
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(*ip6_addr);
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return CHIP_NO_ERROR;
}
#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 = TCPEndPoint::sPool.CreateObject();
if (*retEndPoint == nullptr)
{
ChipLogError(Inet, "%s endpoint pool FULL", "TCP");
return CHIP_ERROR_ENDPOINT_POOL_FULL;
}
(*retEndPoint)->Init(this);
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 = UDPEndPoint::sPool.CreateObject();
if (*retEndPoint == nullptr)
{
ChipLogError(Inet, "%s endpoint pool FULL", "UDP");
return CHIP_ERROR_ENDPOINT_POOL_FULL;
}
(*retEndPoint)->Init(this);
SYSTEM_STATS_INCREMENT(chip::System::Stats::kInetLayer_NumUDPEps);
return CHIP_NO_ERROR;
}
#endif // INET_CONFIG_ENABLE_UDP_ENDPOINT
/**
* Get the interface identifier for the specified IP address. If the
* interface identifier cannot be derived it is set to the default
* 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 #CHIP_NO_ERROR unconditionally.
*
*/
CHIP_ERROR InetLayer::GetInterfaceFromAddr(const IPAddress & addr, InterfaceId & intfId)
{
InterfaceAddressIterator addrIter;
for (; addrIter.HasCurrent(); addrIter.Next())
{
IPAddress curAddr = addrIter.GetAddress();
if (addr == curAddr)
{
intfId = addrIter.GetInterfaceId();
return CHIP_NO_ERROR;
}
}
intfId = InterfaceId::Null();
return CHIP_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.GetPrefixLength();
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)
{
InetLayer & lInetLayer = *reinterpret_cast<InetLayer *>(aAppState);
bool lTimerRequired = lInetLayer.IsIdleTimerRunning();
TCPEndPoint::sPool.ForEachActiveObject([&](TCPEndPoint * lEndPoint) {
if (!lEndPoint->IsCreatedByInetLayer(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