blob: a7468ebb06696d5d57d3202556304cb71fe5deb3 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2019 Google LLC.
* Copyright (c) 2013-2017 Nest Labs, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Implementation of network interface abstraction layer.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include "InetInterface.h"
#include "InetLayer.h"
#include "InetLayerEvents.h"
#include <support/CodeUtils.h>
#include <support/DLLUtil.h>
#if CHIP_SYSTEM_CONFIG_USE_LWIP
#include <lwip/netif.h>
#include <lwip/sys.h>
#include <lwip/tcpip.h>
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
#ifdef HAVE_SYS_SOCKIO_H
#include <sys/sockio.h>
#endif /* HAVE_SYS_SOCKIO_H */
#include <net/if.h>
#include <sys/ioctl.h>
#ifdef __ANDROID__
#include "ifaddrs-android.h"
#else // !defined(__ANDROID__)
#include <ifaddrs.h>
#endif // !defined(__ANDROID__)
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#include <net/net_if.h>
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#include <stdio.h>
#include <string.h>
namespace chip {
namespace Inet {
/**
* @brief Get the name of a network interface
*
* @param[in] intfId a network interface
* @param[in] nameBuf region of memory to write the interface name
* @param[in] nameBufSize size of the region denoted by \c nameBuf
*
* @retval INET_NO_ERROR successful result, interface name written
* @retval INET_ERROR_NO_MEMORY name is too large to be written in buffer
* @retval other another system or platform error
*
* @details
* Writes the name of the network interface as \c NUL terminated text string
* at \c nameBuf. The name of the unspecified network interface is the empty
* string.
*/
DLL_EXPORT INET_ERROR GetInterfaceName(InterfaceId intfId, char * nameBuf, size_t nameBufSize)
{
if (intfId != INET_NULL_INTERFACEID)
{
#if CHIP_SYSTEM_CONFIG_USE_LWIP
int status = snprintf(nameBuf, nameBufSize, "%c%c%d", intfId->name[0], intfId->name[1], intfId->num);
if (status >= static_cast<int>(nameBufSize))
return INET_ERROR_NO_MEMORY;
return INET_NO_ERROR;
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
char intfName[IF_NAMESIZE];
if (if_indextoname(intfId, intfName) == NULL)
return chip::System::MapErrorPOSIX(errno);
if (strlen(intfName) >= nameBufSize)
return INET_ERROR_NO_MEMORY;
strcpy(nameBuf, intfName);
return INET_NO_ERROR;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
net_if * currentInterface = net_if_get_by_index(intfId);
if (!currentInterface)
return INET_ERROR_INCORRECT_STATE;
const char * name = net_if_get_device(currentInterface)->name;
if (strlen(name) >= nameBufSize)
return INET_ERROR_NO_MEMORY;
strcpy(nameBuf, name);
return INET_NO_ERROR;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
}
else
{
if (nameBufSize < 1)
return INET_ERROR_NO_MEMORY;
nameBuf[0] = 0;
return INET_NO_ERROR;
}
}
/**
* @brief Search the list of network interfaces for the indicated name.
*
* @param[in] intfName name of the network interface to find
* @param[out] intfId indicator of the network interface to assign
*
* @retval INET_NO_ERROR success, network interface indicated
* @retval INET_ERROR_UNKNOWN_INTERFACE no network interface found
* @retval other another system or platform error
*
* @details
* On LwIP, this function must be called with the LwIP stack lock acquired.
*
* The \c intfId parameter is not updated unless the value returned is
* \c INET_NO_ERROR. It should be initialized with \c INET_NULL_INTERFACEID
* before calling this function.
*/
DLL_EXPORT INET_ERROR InterfaceNameToId(const char * intfName, InterfaceId & intfId)
{
#if CHIP_SYSTEM_CONFIG_USE_LWIP
if (strlen(intfName) < 3)
return INET_ERROR_UNKNOWN_INTERFACE;
char * parseEnd;
unsigned long intfNum = strtoul(intfName + 2, &parseEnd, 10);
if (*parseEnd != 0 || intfNum > UINT8_MAX)
return INET_ERROR_UNKNOWN_INTERFACE;
struct netif * intf;
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH)
NETIF_FOREACH(intf)
#else
for (intf = netif_list; intf != NULL; intf = intf->next)
#endif
{
if (intf->name[0] == intfName[0] && intf->name[1] == intfName[1] && intf->num == (uint8_t) intfNum)
{
intfId = intf;
return INET_NO_ERROR;
}
}
intfId = INET_NULL_INTERFACEID;
return INET_ERROR_UNKNOWN_INTERFACE;
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
intfId = if_nametoindex(intfName);
if (intfId == 0)
return (errno == ENXIO) ? INET_ERROR_UNKNOWN_INTERFACE : chip::System::MapErrorPOSIX(errno);
return INET_NO_ERROR;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
int currentId = 0;
net_if * currentInterface;
while ((currentInterface = net_if_get_by_index(++currentId)) != nullptr)
{
if (strcmp(net_if_get_device(currentInterface)->name, intfName) == 0)
{
intfId = currentId;
return INET_NO_ERROR;
}
}
return INET_ERROR_UNKNOWN_INTERFACE;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
} // namespace Inet
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
static int sIOCTLSocket = -1;
/**
* @brief Returns a global general purpose socket useful for invoking certain network IOCTLs.
*
* This function is thread-safe on all platforms.
*/
int GetIOCTLSocket(void)
{
if (sIOCTLSocket == -1)
{
int s;
#ifdef SOCK_CLOEXEC
s = socket(AF_INET, SOCK_STREAM, SOCK_CLOEXEC);
if (s < 0)
#endif
{
s = socket(AF_INET, SOCK_STREAM, 0);
fcntl(s, O_CLOEXEC);
}
if (!__sync_bool_compare_and_swap(&sIOCTLSocket, -1, s))
{
close(s);
}
}
return sIOCTLSocket;
}
/**
* @brief Close the global socket created by \c GetIOCTLSocket.
*
* @details
* This function is provided for cases were leaving the global IOCTL socket
* open would register as a leak.
*
* NB: This function is NOT thread-safe with respect to \c GetIOCTLSocket.
*/
void CloseIOCTLSocket(void)
{
if (sIOCTLSocket == -1)
{
close(sIOCTLSocket);
sIOCTLSocket = -1;
}
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
/**
* @fn InterfaceIterator::InterfaceIterator(void)
*
* @brief Constructs an InterfaceIterator object.
*
* @details
* Starts the iterator at the first network interface. On some platforms,
* this constructor may allocate resources recycled by the destructor.
*/
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if __ANDROID__ && __ANDROID_API__ < 24
static struct if_nameindex * backport_if_nameindex(void);
static void backport_if_freenameindex(struct if_nameindex *);
static void backport_if_freenameindex(struct if_nameindex * inArray)
{
if (inArray == NULL)
{
return;
}
for (size_t i = 0; inArray[i].if_index != 0; i++)
{
if (inArray[i].if_name != NULL)
{
free(inArray[i].if_name);
}
}
free(inArray);
}
static struct if_nameindex * backport_if_nameindex(void)
{
int err;
unsigned index;
size_t intfIter = 0;
size_t maxIntfNum = 0;
size_t numIntf = 0;
size_t numAddrs = 0;
struct if_nameindex * retval = NULL;
struct if_nameindex * tmpval = NULL;
struct ifaddrs * addrList = NULL;
struct ifaddrs * addrIter = NULL;
const char * lastIntfName = "";
err = getifaddrs(&addrList);
VerifyOrExit(err >= 0, );
// coalesce on consecutive interface names
for (addrIter = addrList; addrIter != NULL; addrIter = addrIter->ifa_next)
{
numAddrs++;
if (strcmp(addrIter->ifa_name, lastIntfName) == 0)
{
continue;
}
numIntf++;
lastIntfName = addrIter->ifa_name;
}
tmpval = (struct if_nameindex *) malloc((numIntf + 1) * sizeof(struct if_nameindex));
VerifyOrExit(tmpval != NULL, );
memset(tmpval, 0, (numIntf + 1) * sizeof(struct if_nameindex));
lastIntfName = "";
for (addrIter = addrList; addrIter != NULL; addrIter = addrIter->ifa_next)
{
if (strcmp(addrIter->ifa_name, lastIntfName) == 0)
{
continue;
}
index = if_nametoindex(addrIter->ifa_name);
if (index != 0)
{
tmpval[intfIter].if_index = index;
tmpval[intfIter].if_name = strdup(addrIter->ifa_name);
intfIter++;
}
lastIntfName = addrIter->ifa_name;
}
// coalesce on interface index
maxIntfNum = 0;
for (size_t i = 0; tmpval[i].if_index != 0; i++)
{
if (maxIntfNum < tmpval[i].if_index)
{
maxIntfNum = tmpval[i].if_index;
}
}
retval = (struct if_nameindex *) malloc((maxIntfNum + 1) * sizeof(struct if_nameindex));
VerifyOrExit(retval != NULL, );
memset(retval, 0, (maxIntfNum + 1) * sizeof(struct if_nameindex));
for (size_t i = 0; tmpval[i].if_index != 0; i++)
{
struct if_nameindex * intf = &tmpval[i];
if (retval[intf->if_index - 1].if_index == 0)
{
retval[intf->if_index - 1] = *intf;
}
else
{
free(intf->if_name);
intf->if_index = 0;
intf->if_name = 0;
}
}
intfIter = 0;
// coalesce potential gaps between indeces
for (size_t i = 0; i < maxIntfNum; i++)
{
if (retval[i].if_index != 0)
{
retval[intfIter] = retval[i];
intfIter++;
}
}
for (size_t i = intfIter; i < maxIntfNum; i++)
{
retval[i].if_index = 0;
retval[i].if_name = NULL;
}
exit:
if (tmpval != NULL)
{
free(tmpval);
}
if (addrList != NULL)
{
freeifaddrs(addrList);
}
return retval;
}
#endif // __ANDROID__ && __ANDROID_API__ < 24
InterfaceIterator::InterfaceIterator(void)
{
mIntfArray = NULL;
mCurIntf = 0;
mIntfFlags = 0;
mIntfFlagsCached = 0;
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
InterfaceIterator::InterfaceIterator() : mCurrentInterface(net_if_get_by_index(mCurrentId)) {}
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
/**
* @fn InterfaceIterator::~InterfaceIterator(void)
*
* @brief Destroys an InterfaceIterator object.
*
* @details
* Recycles any resources allocated by the constructor.
*/
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
InterfaceIterator::~InterfaceIterator(void)
{
if (mIntfArray != NULL)
{
#if __ANDROID__ && __ANDROID_API__ < 24
backport_if_freenameindex(mIntfArray);
#else
if_freenameindex(mIntfArray);
#endif
mIntfArray = NULL;
}
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
/**
* @fn bool InterfaceIterator::HasCurrent(void)
*
* @brief Test whether the iterator is positioned on an interface
*
* @return \c true if the iterator is positioned on an interface;
* \c false if positioned beyond the end of the interface list.
*/
#if CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
bool InterfaceIterator::HasCurrent(void)
{
return (mIntfArray != NULL) ? mIntfArray[mCurIntf].if_index != 0 : Next();
}
#endif // CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
bool InterfaceIterator::HasCurrent(void)
{
return mCurrentInterface != nullptr;
}
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
/**
* @fn bool InterfaceIterator::Next(void)
*
* @brief Advance the iterator to the next network interface.
*
* @return \c false if advanced beyond the end, else \c true.
*
* @details
* Advances the internal iterator to the next network interface or to a position
* beyond the end of the interface list.
*
* On multi-threaded LwIP systems, this method is thread-safe relative to other
* threads accessing the global LwIP state provided that: 1) the other threads
* hold the LwIP core lock while mutating the list of netifs; and 2) netif objects
* themselves are never destroyed.
*
* Iteration is stable in the face of changes to the underlying system's
* interfaces, *except* in the case of LwIP systems when the currently selected
* interface is removed from the list, which causes iteration to end immediately.
*/
bool InterfaceIterator::Next(void)
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
if (mIntfArray == NULL)
{
#if __ANDROID__ && __ANDROID_API__ < 24
mIntfArray = backport_if_nameindex();
#else
mIntfArray = if_nameindex();
#endif
}
else if (mIntfArray[mCurIntf].if_index != 0)
{
mCurIntf++;
mIntfFlags = 0;
mIntfFlagsCached = false;
}
return (mIntfArray != NULL && mIntfArray[mCurIntf].if_index != 0);
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
mCurrentInterface = net_if_get_by_index(++mCurrentId);
return HasCurrent();
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
// Lock LwIP stack
LOCK_TCPIP_CORE();
// Verify the previous netif is still on the list if netifs. If so,
// advance to the next nextif.
struct netif * prevNetif = mCurNetif;
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH)
NETIF_FOREACH(mCurNetif)
#else
for (mCurNetif = netif_list; mCurNetif != NULL; mCurNetif = mCurNetif->next)
#endif
{
if (mCurNetif == prevNetif)
{
mCurNetif = mCurNetif->next;
break;
}
}
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
return mCurNetif != NULL;
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
/**
* @fn InterfaceId InterfaceIterator::GetInterfaceId(void)
*
* @brief Returns the network interface id at the current iterator position.
*
* @retval INET_NULL_INTERFACEID if advanced beyond the end of the list.
* @retval id the current network interface id.
*/
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
InterfaceId InterfaceIterator::GetInterfaceId(void)
{
return (HasCurrent()) ? mIntfArray[mCurIntf].if_index : INET_NULL_INTERFACEID;
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
InterfaceId InterfaceIterator::GetInterfaceId(void)
{
return HasCurrent() ? mCurrentId : INET_NULL_INTERFACEID;
}
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
/**
* @brief Get the name of the current network interface
*
* @param[in] nameBuf region of memory to write the interface name
* @param[in] nameBufSize size of the region denoted by \c nameBuf
*
* @retval INET_NO_ERROR successful result, interface name written
* @retval INET_ERROR_INCORRECT_STATE
* iterator is positioned beyond the end of
* the list
* @retval INET_ERROR_NO_MEMORY name is too large to be written in buffer
* @retval other another system or platform error
*
* @details
* Writes the name of the network interface as \c NUL terminated text string
* at \c nameBuf.
*/
INET_ERROR InterfaceIterator::GetInterfaceName(char * nameBuf, size_t nameBufSize)
{
INET_ERROR err = INET_ERROR_NOT_IMPLEMENTED;
VerifyOrExit(HasCurrent(), err = INET_ERROR_INCORRECT_STATE);
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
VerifyOrExit(strlen(mIntfArray[mCurIntf].if_name) < nameBufSize, err = INET_ERROR_NO_MEMORY);
strncpy(nameBuf, mIntfArray[mCurIntf].if_name, nameBufSize);
err = INET_NO_ERROR;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
err = ::chip::Inet::GetInterfaceName(mCurrentId, nameBuf, nameBufSize);
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
err = ::chip::Inet::GetInterfaceName(mCurNetif, nameBuf, nameBufSize);
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
exit:
return err;
}
/**
* @brief Returns whether the current network interface is up.
*
* @return \c true if current network interface is up, \c false if not
* or if the iterator is positioned beyond the end of the list.
*/
bool InterfaceIterator::IsUp(void)
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (GetFlags() & IFF_UP) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return HasCurrent() && net_if_is_up(mCurrentInterface);
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
return HasCurrent() && netif_is_up(mCurNetif);
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
/**
* @brief Returns whether the current network interface supports multicast.
*
* @return \c true if current network interface supports multicast, \c false
* if not, or if the iterator is positioned beyond the end of the list.
*/
bool InterfaceIterator::SupportsMulticast(void)
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (GetFlags() & IFF_MULTICAST) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return HasCurrent() && NET_IF_MAX_IPV6_MADDR > 0;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
return HasCurrent() &&
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
(mCurNetif->flags & (NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 | NETIF_FLAG_BROADCAST)) != 0;
#else
(mCurNetif->flags & NETIF_FLAG_POINTTOPOINT) == 0;
#endif // LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
/**
* @brief Returns whether the current network interface has a broadcast address.
*
* @return \c true if current network interface has a broadcast address, \c false
* if not, or if the iterator is positioned beyond the end of the list.
*/
bool InterfaceIterator::HasBroadcastAddress(void)
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (GetFlags() & IFF_BROADCAST) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
// Zephyr seems to handle broadcast address for IPv4 implicitly
return HasCurrent() && INET_CONFIG_ENABLE_IPV4;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
return HasCurrent() && (mCurNetif->flags & NETIF_FLAG_BROADCAST) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
/**
* @fn short InterfaceIterator::GetFlags(void)
*
* @brief Returns the ifr_flags value for the current interface.
*/
short InterfaceIterator::GetFlags(void)
{
struct ifreq intfData;
if (!mIntfFlagsCached && HasCurrent())
{
strncpy(intfData.ifr_name, mIntfArray[mCurIntf].if_name, IFNAMSIZ);
intfData.ifr_name[IFNAMSIZ - 1] = '\0';
int res = ioctl(GetIOCTLSocket(), SIOCGIFFLAGS, &intfData);
if (res == 0)
{
mIntfFlags = intfData.ifr_flags;
mIntfFlagsCached = true;
}
}
return mIntfFlags;
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
/**
* @fn InterfaceAddressIterator::InterfaceAddressIterator(void)
*
* @brief Constructs an InterfaceAddressIterator object.
*
* @details
* Starts the iterator at the first network address. On some platforms,
* this constructor may allocate resources recycled by the destructor.
*/
InterfaceAddressIterator::InterfaceAddressIterator(void)
{
mAddrsList = NULL;
mCurAddr = NULL;
}
#endif // CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
InterfaceAddressIterator::InterfaceAddressIterator() = default;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
/**
* @fn InterfaceAddressIterator::~InterfaceAddressIterator(void)
*
* @brief Destroys an InterfaceAddressIterator object.
*
* @details
* Recycles any resources allocated by the constructor.
*/
#if CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
InterfaceAddressIterator::~InterfaceAddressIterator(void)
{
if (mAddrsList != NULL)
{
freeifaddrs(mAddrsList);
mAddrsList = mCurAddr = NULL;
}
}
#endif // CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
/**
* @fn bool InterfaceIterator::HasCurrent(void)
*
* @brief Test whether the iterator is positioned on an interface address
*
* @return \c true if the iterator is positioned on an interface address;
* \c false if positioned beyond the end of the address list.
*/
bool InterfaceAddressIterator::HasCurrent(void)
{
#if CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (mAddrsList != NULL) ? (mCurAddr != NULL) : Next();
#endif // CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return mIntfIter.HasCurrent() && (mCurAddrIndex >= 0 || Next());
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
return mIntfIter.HasCurrent() && ((mCurAddrIndex != kBeforeStartIndex) || Next());
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
/**
* @fn bool InterfaceAddressIterator::Next(void)
*
* @brief Advance the iterator to the next interface address.
*
* @return \c false if advanced beyond the end, else \c true.
*
* @details
* Advances the iterator to the next interface address or to a position
* beyond the end of the address list.
*
* On LwIP, this method is thread-safe provided that: 1) other threads hold
* the LwIP core lock while mutating the netif list; and 2) netif objects
* themselves are never destroyed. Additionally, iteration on LwIP systems
* will terminate early if the current interface is removed from the list.
*/
bool InterfaceAddressIterator::Next(void)
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
while (true)
{
if (mAddrsList == NULL)
{
int res = getifaddrs(&mAddrsList);
if (res < 0)
{
return false;
}
mCurAddr = mAddrsList;
}
else if (mCurAddr != NULL)
{
mCurAddr = mCurAddr->ifa_next;
}
if (mCurAddr == NULL)
{
return false;
}
if (mCurAddr->ifa_addr != NULL &&
(mCurAddr->ifa_addr->sa_family == AF_INET6
#if INET_CONFIG_ENABLE_IPV4
|| mCurAddr->ifa_addr->sa_family == AF_INET
#endif // INET_CONFIG_ENABLE_IPV4
))
{
return true;
}
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
while (mIntfIter.HasCurrent())
{
if (mCurAddrIndex == -1) // first address for the current interface
{
const net_if_config * config = net_if_get_config(net_if_get_by_index(mIntfIter.GetInterfaceId()));
mIpv6 = config->ip.ipv6;
}
while (++mCurAddrIndex < NET_IF_MAX_IPV6_ADDR)
if (mIpv6->unicast[mCurAddrIndex].is_used)
return true;
mCurAddrIndex = -1;
mIntfIter.Next();
}
return false;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
mCurAddrIndex++;
while (mIntfIter.HasCurrent())
{
struct netif * curIntf = mIntfIter.GetInterfaceId();
while (mCurAddrIndex < LWIP_IPV6_NUM_ADDRESSES)
{
if (ip6_addr_isvalid(netif_ip6_addr_state(curIntf, mCurAddrIndex)))
{
return true;
}
mCurAddrIndex++;
}
#if INET_CONFIG_ENABLE_IPV4 && LWIP_IPV4
if (mCurAddrIndex == LWIP_IPV6_NUM_ADDRESSES)
{
if (!ip4_addr_isany(netif_ip4_addr(curIntf)))
{
return true;
}
}
#endif // INET_CONFIG_ENABLE_IPV4 && LWIP_IPV4
mIntfIter.Next();
mCurAddrIndex = 0;
}
return false;
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
/**
* @fn IPAddress InterfaceAddressIterator::GetAddress(void)
*
* @brief Get the current interface address.
*
* @return the current interface address or \c IPAddress::Any if the iterator
* is positioned beyond the end of the address list.
*/
IPAddress InterfaceAddressIterator::GetAddress(void)
{
if (HasCurrent())
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return IPAddress::FromSockAddr(*mCurAddr->ifa_addr);
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return IPAddress::FromIPv6(mIpv6->unicast[mCurAddrIndex].address.in6_addr);
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
struct netif * curIntf = mIntfIter.GetInterfaceId();
if (mCurAddrIndex < LWIP_IPV6_NUM_ADDRESSES)
{
return IPAddress::FromIPv6(*netif_ip6_addr(curIntf, mCurAddrIndex));
}
#if INET_CONFIG_ENABLE_IPV4 && LWIP_IPV4
else
{
return IPAddress::FromIPv4(*netif_ip4_addr(curIntf));
}
#endif // INET_CONFIG_ENABLE_IPV4 && LWIP_IPV4
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
return IPAddress::Any;
}
/**
* @fn uint8_t InterfaceAddressIterator::GetPrefixLength(void)
*
* @brief Gets the network prefix associated with the current interface address.
*
* @return the network prefix (in bits) or 0 if the iterator is positioned beyond
* the end of the address list.
*
* @details
* On LwIP, this method simply returns the hard-coded constant 64.
*
* Note Well: the standard subnet prefix on all links other than PPP
* links is 64 bits. On PPP links and some non-broadcast multipoint access
* links, the convention is either 127 bits or 128 bits, but it might be
* something else. On most platforms, the system's interface address
* structure can represent arbitrary prefix lengths between 0 and 128.
*/
uint8_t InterfaceAddressIterator::GetPrefixLength(void)
{
if (HasCurrent())
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
if (mCurAddr->ifa_addr->sa_family == AF_INET6)
{
struct sockaddr_in6 & netmask = *(struct sockaddr_in6 *) (mCurAddr->ifa_netmask);
return NetmaskToPrefixLength(netmask.sin6_addr.s6_addr, 16);
}
if (mCurAddr->ifa_addr->sa_family == AF_INET)
{
struct sockaddr_in & netmask = *(struct sockaddr_in *) (mCurAddr->ifa_netmask);
return NetmaskToPrefixLength((const uint8_t *) &netmask.sin_addr.s_addr, 4);
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
net_if * const iface = net_if_get_by_index(mIntfIter.GetInterfaceId());
net_if_ipv6_prefix * const prefix = net_if_ipv6_prefix_get(iface, &mIpv6->unicast[mCurAddrIndex].address.in6_addr);
return prefix ? prefix->len : 128;
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
#if CHIP_SYSTEM_CONFIG_USE_LWIP
if (mCurAddrIndex < LWIP_IPV6_NUM_ADDRESSES)
{
return 64;
}
#if INET_CONFIG_ENABLE_IPV4 && LWIP_IPV4
else
{
struct netif * curIntf = mIntfIter.GetInterfaceId();
return NetmaskToPrefixLength((const uint8_t *) netif_ip4_netmask(curIntf), 4);
}
#endif // INET_CONFIG_ENABLE_IPV4 && LWIP_IPV4
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
}
return 0;
}
/**
* @fn InterfaceId InterfaceAddressIterator::GetInterfaceId(void)
*
* @brief Returns the network interface id associated with the current
* interface address.
*
* @return the interface id or \c INET_NULL_INTERFACEID if the iterator
* is positioned beyond the end of the address list.
*/
InterfaceId InterfaceAddressIterator::GetInterfaceId(void)
{
if (HasCurrent())
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return if_nametoindex(mCurAddr->ifa_name);
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return mIntfIter.GetInterfaceId();
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
}
return INET_NULL_INTERFACEID;
}
/**
* @fn INET_ERROR InterfaceAddressIterator::GetInterfaceName(char * nameBuf, size_t nameBufSize)
*
* @brief Get the name of the network interface associated with the
* current interface address.
*
* @param[in] nameBuf region of memory to write the interface name
* @param[in] nameBufSize size of the region denoted by \c nameBuf
*
* @retval INET_NO_ERROR successful result, interface name written
* @retval INET_ERROR_NO_MEMORY name is too large to be written in buffer
* @retval INET_ERROR_INCORRECT_STATE
* the iterator is not currently positioned on an
* interface address
* @retval other another system or platform error
*
* @details
* Writes the name of the network interface as \c NUL terminated text string
* at \c nameBuf.
*/
INET_ERROR InterfaceAddressIterator::GetInterfaceName(char * nameBuf, size_t nameBufSize)
{
INET_ERROR err = INET_ERROR_NOT_IMPLEMENTED;
VerifyOrExit(HasCurrent(), err = INET_ERROR_INCORRECT_STATE);
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
VerifyOrExit(strlen(mCurAddr->ifa_name) < nameBufSize, err = INET_ERROR_NO_MEMORY);
strncpy(nameBuf, mCurAddr->ifa_name, nameBufSize);
err = INET_NO_ERROR;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
err = mIntfIter.GetInterfaceName(nameBuf, nameBufSize);
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
exit:
return err;
}
/**
* @fn bool InterfaceAddressIterator::IsUp(void)
*
* @brief Returns whether the network interface associated with the current
* interface address is up.
*
* @return \c true if current network interface is up, \c false if not, or
* if the iterator is not positioned on an interface address.
*/
bool InterfaceAddressIterator::IsUp(void)
{
if (HasCurrent())
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (mCurAddr->ifa_flags & IFF_UP) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return mIntfIter.IsUp();
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
}
return false;
}
/**
* @fn bool InterfaceAddressIterator::SupportsMulticast(void)
*
* @brief Returns whether the network interface associated with the current
* interface address supports multicast.
*
* @return \c true if multicast is supported, \c false if not, or
* if the iterator is not positioned on an interface address.
*/
bool InterfaceAddressIterator::SupportsMulticast(void)
{
if (HasCurrent())
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (mCurAddr->ifa_flags & IFF_MULTICAST) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return mIntfIter.SupportsMulticast();
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
}
return false;
}
/**
* @fn bool InterfaceAddressIterator::HasBroadcastAddress(void)
*
* @brief Returns whether the network interface associated with the current
* interface address has an IPv4 broadcast address.
*
* @return \c true if the interface has a broadcast address, \c false if not, or
* if the iterator is not positioned on an interface address.
*/
bool InterfaceAddressIterator::HasBroadcastAddress(void)
{
if (HasCurrent())
{
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
return (mCurAddr->ifa_flags & IFF_BROADCAST) != 0;
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS && CHIP_SYSTEM_CONFIG_USE_BSD_IFADDRS
#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
return mIntfIter.HasBroadcastAddress();
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_ZEPHYR_NET_IF
}
return false;
}
/**
* @fn void InterfaceAddressIterator::GetAddressWithPrefix(IPPrefix & addrWithPrefix)
*
* @brief Returns an IPPrefix containing the address and prefix length
* for the current address.
*/
void InterfaceAddressIterator::GetAddressWithPrefix(IPPrefix & addrWithPrefix)
{
if (HasCurrent())
{
addrWithPrefix.IPAddr = GetAddress();
addrWithPrefix.Length = GetPrefixLength();
}
else
{
addrWithPrefix = IPPrefix::Zero;
}
}
/**
* @fn uint8_t NetmaskToPrefixLength(const uint8_t * netmask, uint16_t netmaskLen)
*
* @brief Compute a prefix length from a variable-length netmask.
*/
uint8_t NetmaskToPrefixLength(const uint8_t * netmask, uint16_t netmaskLen)
{
uint8_t prefixLen = 0;
for (uint16_t i = 0; i < netmaskLen; i++, prefixLen = static_cast<uint8_t>(prefixLen + 8u))
{
uint8_t b = netmask[i];
if (b != 0xFF)
{
if ((b & 0xF0) == 0xF0)
prefixLen = static_cast<uint8_t>(prefixLen + 4u);
else
b = static_cast<uint8_t>(b >> 4);
if ((b & 0x0C) == 0x0C)
prefixLen = static_cast<uint8_t>(prefixLen + 2u);
else
b = static_cast<uint8_t>(b >> 2);
if ((b & 0x02) == 0x02)
prefixLen++;
break;
}
}
return prefixLen;
}
} // namespace Inet
} // namespace chip