blob: b1d3c8416c5dcf7f785948287d7eb9593f6eb00e [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2018 Google LLC.
* Copyright (c) 2013-2018 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 implements the <tt>Inet::UDPEndPoint</tt>
* class, where the CHIP Inet Layer encapsulates methods for
* interacting with UDP transport endpoints (SOCK_DGRAM sockets
* on Linux and BSD-derived systems) or LwIP UDP protocol
* control blocks, as the system is configured accordingly.
*
*/
#define __APPLE_USE_RFC_3542
#include <inet/UDPEndPoint.h>
#include <inet/InetFaultInjection.h>
#include <inet/InetLayer.h>
#include <inet/arpa-inet-compatibility.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <lib/support/logging/CHIPLogging.h>
#include <system/SystemFaultInjection.h>
#include <cstring>
#include <utility>
#if CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_HAVE_CONFIG_H
#include <lwip/lwip_buildconfig.h> // nogncheck
#endif // CHIP_HAVE_CONFIG_H
#if INET_CONFIG_ENABLE_IPV4
#include <lwip/igmp.h>
#endif // INET_CONFIG_ENABLE_IPV4
#include <lwip/init.h>
#include <lwip/ip.h>
#include <lwip/mld6.h>
#include <lwip/netif.h>
#include <lwip/raw.h>
#include <lwip/tcpip.h>
#include <lwip/udp.h>
#if !defined(RAW_FLAGS_MULTICAST_LOOP) || !defined(UDP_FLAGS_MULTICAST_LOOP) || !defined(raw_clear_flags) || \
!defined(raw_set_flags) || !defined(udp_clear_flags) || !defined(udp_set_flags)
#define HAVE_LWIP_MULTICAST_LOOP 0
#else
#define HAVE_LWIP_MULTICAST_LOOP 1
#endif // !defined(RAW_FLAGS_MULTICAST_LOOP) || !defined(UDP_FLAGS_MULTICAST_LOOP) || !defined(raw_clear_flags) ||
// !defined(raw_set_flags) || !defined(udp_clear_flags) || !defined(udp_set_flags)
// unusual define check for LWIP_IPV6_ND is because espressif fork
// of LWIP does not define the _ND constant.
#if LWIP_IPV6_MLD && (!defined(LWIP_IPV6_ND) || LWIP_IPV6_ND) && LWIP_IPV6
#define HAVE_IPV6_MULTICAST
#else
// Within Project CHIP multicast support is highly desirable: used for mDNS
// as well as group communication.
#undef HAVE_IPV6_MULTICAST
#endif
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif // HAVE_SYS_SOCKET_H
#include <cerrno>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <unistd.h>
// SOCK_CLOEXEC not defined on all platforms, e.g. iOS/macOS:
#ifndef SOCK_CLOEXEC
#define SOCK_CLOEXEC 0
#endif
#if CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS
#include "ZephyrSocket.h"
#endif // CHIP_SYSTEM_CONFIG_USE_ZEPHYR_SOCKET_EXTENSIONS
/*
* Some systems define both IPV6_{ADD,DROP}_MEMBERSHIP and
* IPV6_{JOIN,LEAVE}_GROUP while others only define
* IPV6_{JOIN,LEAVE}_GROUP. Prefer the "_MEMBERSHIP" flavor for
* parallelism with IPv4 and create the alias to the availabile
* definitions.
*/
#if defined(IPV6_ADD_MEMBERSHIP)
#define INET_IPV6_ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP
#elif defined(IPV6_JOIN_GROUP)
#define INET_IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
#elif !__ZEPHYR__
#error \
"Neither IPV6_ADD_MEMBERSHIP nor IPV6_JOIN_GROUP are defined which are required for generalized IPv6 multicast group support."
#endif // IPV6_ADD_MEMBERSHIP
#if defined(IPV6_DROP_MEMBERSHIP)
#define INET_IPV6_DROP_MEMBERSHIP IPV6_DROP_MEMBERSHIP
#elif defined(IPV6_LEAVE_GROUP)
#define INET_IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
#elif !__ZEPHYR__
#error \
"Neither IPV6_DROP_MEMBERSHIP nor IPV6_LEAVE_GROUP are defined which are required for generalized IPv6 multicast group support."
#endif // IPV6_DROP_MEMBERSHIP
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
#define INET_PORTSTRLEN 6
#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
namespace chip {
namespace Inet {
#if CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS
namespace {
CHIP_ERROR CheckMulticastGroupArgs(InterfaceId aInterfaceId, const IPAddress & aAddress)
{
VerifyOrReturnError(aInterfaceId.IsPresent(), INET_ERROR_UNKNOWN_INTERFACE);
VerifyOrReturnError(aAddress.IsMulticast(), INET_ERROR_WRONG_ADDRESS_TYPE);
return CHIP_NO_ERROR;
}
} // anonymous namespace
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP || CHIP_SYSTEM_CONFIG_USE_SOCKETS
#if CHIP_SYSTEM_CONFIG_USE_LWIP
BitMapObjectPool<UDPEndPointImplLwIP, INET_CONFIG_NUM_UDP_ENDPOINTS> UDPEndPointImplLwIP::sPool;
CHIP_ERROR UDPEndPointImplLwIP::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port,
InterfaceId interfaceId)
{
// Lock LwIP stack
LOCK_TCPIP_CORE();
// Make sure we have the appropriate type of PCB.
CHIP_ERROR res = GetPCB(addressType);
// Bind the PCB to the specified address/port.
if (res == CHIP_NO_ERROR)
{
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
ip_addr_t ipAddr = address.ToLwIPAddr();
// TODO: IPAddress ANY has only one constant state, however addressType
// has separate IPV4 and IPV6 'any' settings. This tries to correct
// for this as LWIP default if IPv4 is compiled in is to consider
// 'any == any_v4'
//
// We may want to consider having separate AnyV4 and AnyV6 constants
// inside CHIP to resolve this ambiguity
if ((address.Type() == IPAddressType::kAny) && (addressType == IPAddressType::kIPv6))
{
ipAddr = *IP6_ADDR_ANY;
}
res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipAddr, port));
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
if (addressType == IPAddressType::kIPv6)
{
ip6_addr_t ipv6Addr = address.ToIPv6();
res = chip::System::MapErrorLwIP(udp_bind_ip6(mUDP, &ipv6Addr, port));
}
#if INET_CONFIG_ENABLE_IPV4
else if (addressType == IPAddressType::kIPv4)
{
ip4_addr_t ipv4Addr = address.ToIPv4();
res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipv4Addr, port));
}
#endif // INET_CONFIG_ENABLE_IPV4
else
res = INET_ERROR_WRONG_ADDRESS_TYPE;
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5
}
if (res == CHIP_NO_ERROR)
{
res = LwIPBindInterface(mUDP, interfaceId);
}
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
return res;
}
CHIP_ERROR UDPEndPointImplLwIP::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId)
{
// A lock is required because the LwIP thread may be referring to intf_filter,
// while this code running in the Inet application is potentially modifying it.
// NOTE: this only supports LwIP interfaces whose number is no bigger than 9.
LOCK_TCPIP_CORE();
// Make sure we have the appropriate type of PCB.
CHIP_ERROR err = GetPCB(addrType);
if (err == CHIP_NO_ERROR)
{
err = LwIPBindInterface(mUDP, intfId);
}
UNLOCK_TCPIP_CORE();
return err;
}
CHIP_ERROR UDPEndPointImplLwIP::LwIPBindInterface(struct udp_pcb * aUDP, InterfaceId intfId)
{
struct netif * netifp = nullptr;
if (intfId.IsPresent())
{
netifp = UDPEndPointImplLwIP::FindNetifFromInterfaceId(intfId);
if (netifp == nullptr)
{
return INET_ERROR_UNKNOWN_INTERFACE;
}
}
udp_bind_netif(aUDP, netifp);
return CHIP_NO_ERROR;
}
InterfaceId UDPEndPointImplLwIP::GetBoundInterface() const
{
#if HAVE_LWIP_UDP_BIND_NETIF
return InterfaceId(netif_get_by_index(mUDP->netif_idx));
#else
return InterfaceId(mUDP->intf_filter);
#endif
}
uint16_t UDPEndPointImplLwIP::GetBoundPort() const
{
return mUDP->local_port;
}
CHIP_ERROR UDPEndPointImplLwIP::ListenImpl()
{
// Lock LwIP stack
LOCK_TCPIP_CORE();
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
udp_recv(mUDP, LwIPReceiveUDPMessage, this);
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
if (PCB_ISIPV6(mUDP))
{
udp_recv_ip6(mUDP, LwIPReceiveUDPMessage, this);
}
else
{
udp_recv(mUDP, LwIPReceiveUDPMessage, this);
}
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplLwIP::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg)
{
const IPAddress & destAddr = pktInfo->DestAddress;
if (!msg.HasSoleOwnership())
{
// when retaining a buffer, the caller expects the msg to be unmodified.
// LwIP stack will normally prepend the packet headers as the packet traverses
// the UDP/IP/netif layers, which normally modifies the packet. We need to clone
// msg into a fresh object in this case, and queues that for transmission, leaving
// the original msg available after return.
msg = msg.CloneData();
VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_NO_MEMORY);
}
// Lock LwIP stack
LOCK_TCPIP_CORE();
// Make sure we have the appropriate type of PCB based on the destination address.
CHIP_ERROR res = GetPCB(destAddr.Type());
if (res != CHIP_NO_ERROR)
{
UNLOCK_TCPIP_CORE();
return res;
}
// Send the message to the specified address/port.
// If an outbound interface has been specified, call a specific version of the UDP sendto()
// function that accepts the target interface.
// If a source address has been specified, temporarily override the local_ip of the PCB.
// This results in LwIP using the given address being as the source address for the generated
// packet, as if the PCB had been bound to that address.
err_t lwipErr = ERR_VAL;
const IPAddress & srcAddr = pktInfo->SrcAddress;
const uint16_t & destPort = pktInfo->DestPort;
const InterfaceId & intfId = pktInfo->Interface;
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
ip_addr_t lwipSrcAddr = srcAddr.ToLwIPAddr();
ip_addr_t lwipDestAddr = destAddr.ToLwIPAddr();
ip_addr_t boundAddr;
ip_addr_copy(boundAddr, mUDP->local_ip);
if (!ip_addr_isany(&lwipSrcAddr))
{
ip_addr_copy(mUDP->local_ip, lwipSrcAddr);
}
if (intfId.IsPresent())
{
lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort,
intfId.GetPlatformInterface());
}
else
{
lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort);
}
ip_addr_copy(mUDP->local_ip, boundAddr);
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
ipX_addr_t boundAddr;
ipX_addr_copy(boundAddr, mUDP->local_ip);
if (PCB_ISIPV6(mUDP))
{
ip6_addr_t lwipSrcAddr = srcAddr.ToIPv6();
ip6_addr_t lwipDestAddr = destAddr.ToIPv6();
if (!ip6_addr_isany(&lwipSrcAddr))
{
ipX_addr_copy(mUDP->local_ip, *ip6_2_ipX(&lwipSrcAddr));
}
if (intfId.IsPresent())
{
lwipErr =
udp_sendto_if_ip6(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, intfId);
}
else
{
lwipErr = udp_sendto_ip6(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort);
}
}
#if INET_CONFIG_ENABLE_IPV4
else
{
ip4_addr_t lwipSrcAddr = srcAddr.ToIPv4();
ip4_addr_t lwipDestAddr = destAddr.ToIPv4();
ipX_addr_t boundAddr;
if (!ip_addr_isany(&lwipSrcAddr))
{
ipX_addr_copy(mUDP->local_ip, *ip_2_ipX(&lwipSrcAddr));
}
if (intfId.IsPresent())
{
lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort, intfId);
}
else
{
lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort);
}
}
ipX_addr_copy(mUDP->local_ip, boundAddr);
#endif // INET_CONFIG_ENABLE_IPV4
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
if (lwipErr != ERR_OK)
{
res = chip::System::MapErrorLwIP(lwipErr);
}
return res;
}
void UDPEndPointImplLwIP::CloseImpl()
{
// Lock LwIP stack
LOCK_TCPIP_CORE();
// Since UDP PCB is released synchronously here, but UDP endpoint itself might have to wait
// for destruction asynchronously, there could be more allocated UDP endpoints than UDP PCBs.
if (mUDP != nullptr)
{
udp_remove(mUDP);
mUDP = nullptr;
mLwIPEndPointType = LwIPEndPointType::Unknown;
}
// Unlock LwIP stack
UNLOCK_TCPIP_CORE();
}
void UDPEndPointImplLwIP::Free()
{
Close();
Release();
}
void UDPEndPointImplLwIP::HandleDataReceived(System::PacketBufferHandle && msg)
{
if ((mState == State::kListening) && (OnMessageReceived != nullptr))
{
const IPPacketInfo * pktInfo = GetPacketInfo(msg);
if (pktInfo != nullptr)
{
const IPPacketInfo pktInfoCopy = *pktInfo; // copy the address info so that the app can free the
// PacketBuffer without affecting access to address info.
OnMessageReceived(this, std::move(msg), &pktInfoCopy);
}
else
{
if (OnReceiveError != nullptr)
{
OnReceiveError(this, CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG, nullptr);
}
}
}
}
CHIP_ERROR UDPEndPointImplLwIP::GetPCB(IPAddressType addrType)
{
// IMPORTANT: This method MUST be called with the LwIP stack LOCKED!
// If a PCB hasn't been allocated yet...
if (mUDP == nullptr)
{
// Allocate a PCB of the appropriate type.
if (addrType == IPAddressType::kIPv6)
{
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
mUDP = udp_new_ip_type(IPADDR_TYPE_V6);
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
mUDP = udp_new_ip6();
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5
}
#if INET_CONFIG_ENABLE_IPV4
else if (addrType == IPAddressType::kIPv4)
{
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
mUDP = udp_new_ip_type(IPADDR_TYPE_V4);
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
mUDP = udp_new();
#endif // LWIP_VERSION_MAJOR <= 1 || LWIP_VERSION_MINOR >= 5
}
#endif // INET_CONFIG_ENABLE_IPV4
else
{
return INET_ERROR_WRONG_ADDRESS_TYPE;
}
// Fail if the system has run out of PCBs.
if (mUDP == nullptr)
{
ChipLogError(Inet, "Unable to allocate UDP PCB");
return CHIP_ERROR_NO_MEMORY;
}
// Allow multiple bindings to the same port.
ip_set_option(mUDP, SOF_REUSEADDR);
}
// Otherwise, verify that the existing PCB is the correct type...
else
{
IPAddressType pcbAddrType;
// Get the address type of the existing PCB.
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
switch (static_cast<lwip_ip_addr_type>(IP_GET_TYPE(&mUDP->local_ip)))
{
case IPADDR_TYPE_V6:
pcbAddrType = IPAddressType::kIPv6;
break;
#if INET_CONFIG_ENABLE_IPV4
case IPADDR_TYPE_V4:
pcbAddrType = IPAddressType::kIPv4;
break;
#endif // INET_CONFIG_ENABLE_IPV4
default:
return INET_ERROR_WRONG_ADDRESS_TYPE;
}
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
#if INET_CONFIG_ENABLE_IPV4
pcbAddrType = PCB_ISIPV6(mUDP) ? IPAddressType::kIPv6 : IPAddressType::kIPv4;
#else // !INET_CONFIG_ENABLE_IPV4
pcbAddrType = IPAddressType::kIPv6;
#endif // !INET_CONFIG_ENABLE_IPV4
#endif // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
// Fail if the existing PCB is not the correct type.
VerifyOrReturnError(addrType == pcbAddrType, INET_ERROR_WRONG_ADDRESS_TYPE);
}
return CHIP_NO_ERROR;
}
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, const ip_addr_t * addr,
u16_t port)
#else // LWIP_VERSION_MAJOR <= 1 && LWIP_VERSION_MINOR < 5
void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb, struct pbuf * p, ip_addr_t * addr, u16_t port)
#endif // LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
{
UDPEndPointImplLwIP * ep = static_cast<UDPEndPointImplLwIP *>(arg);
System::Layer * lSystemLayer = ep->Layer().SystemLayer();
IPPacketInfo * pktInfo = nullptr;
System::PacketBufferHandle buf = System::PacketBufferHandle::Adopt(p);
if (buf->HasChainedBuffer())
{
// Try the simple expedient of flattening in-place.
buf->CompactHead();
}
if (buf->HasChainedBuffer())
{
// Have to allocate a new big-enough buffer and copy.
uint16_t messageSize = buf->TotalLength();
System::PacketBufferHandle copy = System::PacketBufferHandle::New(messageSize, 0);
if (copy.IsNull() || buf->Read(copy->Start(), messageSize) != CHIP_NO_ERROR)
{
ChipLogError(Inet, "No memory to flatten incoming packet buffer chain of size %" PRIu16, buf->TotalLength());
return;
}
buf = std::move(copy);
}
pktInfo = GetPacketInfo(buf);
if (pktInfo != nullptr)
{
#if LWIP_VERSION_MAJOR > 1 || LWIP_VERSION_MINOR >= 5
pktInfo->SrcAddress = IPAddress(*addr);
pktInfo->DestAddress = IPAddress(*ip_current_dest_addr());
#else // LWIP_VERSION_MAJOR <= 1
if (PCB_ISIPV6(pcb))
{
pktInfo->SrcAddress = IPAddress(*(ip6_addr_t *) addr);
pktInfo->DestAddress = IPAddress(*ip6_current_dest_addr());
}
#if INET_CONFIG_ENABLE_IPV4
else
{
pktInfo->SrcAddress = IPAddress(*addr);
pktInfo->DestAddress = IPAddress(*ip_current_dest_addr());
}
#endif // INET_CONFIG_ENABLE_IPV4
#endif // LWIP_VERSION_MAJOR <= 1
pktInfo->Interface = InterfaceId(ip_current_netif());
pktInfo->SrcPort = port;
pktInfo->DestPort = pcb->local_port;
}
ep->Retain();
CHIP_ERROR err = lSystemLayer->ScheduleLambda([ep, p = System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(buf)] {
ep->HandleDataReceived(System::PacketBufferHandle::Adopt(p));
ep->Release();
});
if (err == CHIP_NO_ERROR)
{
// If ScheduleLambda() succeeded, it has ownership of the buffer, so we need to release it (without freeing it).
static_cast<void>(std::move(buf).UnsafeRelease());
}
else
{
ep->Release();
}
}
CHIP_ERROR UDPEndPointImplLwIP::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback)
{
#if HAVE_LWIP_MULTICAST_LOOP
if (mLwIPEndPointType == LwIPEndPointType::UDP)
{
if (aLoopback)
{
udp_set_flags(mUDP, UDP_FLAGS_MULTICAST_LOOP);
}
else
{
udp_clear_flags(mUDP, UDP_FLAGS_MULTICAST_LOOP);
}
return CHIP_NO_ERROR;
}
#endif // HAVE_LWIP_MULTICAST_LOOP
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
}
#if INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplLwIP::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join)
{
#if LWIP_IPV4 && LWIP_IGMP
const auto method = join ? igmp_joingroup_netif : igmp_leavegroup_netif;
struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId);
VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
const ip4_addr_t lIPv4Address = aAddress.ToIPv4();
const err_t lStatus = method(lNetif, &lIPv4Address);
if (lStatus == ERR_MEM)
{
return CHIP_ERROR_NO_MEMORY;
}
return chip::System::MapErrorLwIP(lStatus);
#else // LWIP_IPV4 && LWIP_IGMP
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // LWIP_IPV4 && LWIP_IGMP
}
#endif // INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplLwIP::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join)
{
#ifdef HAVE_IPV6_MULTICAST
const auto method = join ? mld6_joingroup_netif : mld6_leavegroup_netif;
struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId);
VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
const ip6_addr_t lIPv6Address = aAddress.ToIPv6();
const err_t lStatus = method(lNetif, &lIPv6Address);
if (lStatus == ERR_MEM)
{
return CHIP_ERROR_NO_MEMORY;
}
return chip::System::MapErrorLwIP(lStatus);
#else // HAVE_IPV6_MULTICAST
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // HAVE_IPV6_MULTICAST
}
struct netif * UDPEndPointImplLwIP::FindNetifFromInterfaceId(InterfaceId aInterfaceId)
{
struct netif * lRetval = nullptr;
#if LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH)
NETIF_FOREACH(lRetval)
{
if (lRetval == aInterfaceId.GetPlatformInterface())
{
break;
}
}
#else // LWIP_VERSION_MAJOR < 2 || !defined(NETIF_FOREACH)
for (lRetval = netif_list; lRetval != nullptr && lRetval != aInterfaceId.GetPlatformInterface(); lRetval = lRetval->next)
;
#endif // LWIP_VERSION_MAJOR >= 2 && LWIP_VERSION_MINOR >= 0 && defined(NETIF_FOREACH)
return (lRetval);
}
IPPacketInfo * UDPEndPointImplLwIP::GetPacketInfo(const System::PacketBufferHandle & aBuffer)
{
if (!aBuffer->EnsureReservedSize(sizeof(IPPacketInfo) + 3))
{
return nullptr;
}
uintptr_t lStart = (uintptr_t) aBuffer->Start();
uintptr_t lPacketInfoStart = lStart - sizeof(IPPacketInfo);
// Align to a 4-byte boundary
return reinterpret_cast<IPPacketInfo *>(lPacketInfoStart & ~(sizeof(uint32_t) - 1));
}
#endif // CHIP_SYSTEM_CONFIG_USE_LWIP
#if CHIP_SYSTEM_CONFIG_USE_SOCKETS
BitMapObjectPool<UDPEndPointImplSockets, INET_CONFIG_NUM_UDP_ENDPOINTS> UDPEndPointImplSockets::sPool;
namespace {
CHIP_ERROR IPv6Bind(int socket, const IPAddress & address, uint16_t port, InterfaceId interface)
{
struct sockaddr_in6 sa;
memset(&sa, 0, sizeof(sa));
sa.sin6_family = AF_INET6;
sa.sin6_port = htons(port);
sa.sin6_addr = address.ToIPv6();
InterfaceId::PlatformType interfaceId = interface.GetPlatformInterface();
if (!CanCastTo<decltype(sa.sin6_scope_id)>(interfaceId))
{
return CHIP_ERROR_INCORRECT_STATE;
}
sa.sin6_scope_id = static_cast<decltype(sa.sin6_scope_id)>(interfaceId);
CHIP_ERROR status = CHIP_NO_ERROR;
if (bind(socket, reinterpret_cast<const sockaddr *>(&sa), static_cast<unsigned>(sizeof(sa))) != 0)
{
status = CHIP_ERROR_POSIX(errno);
}
else
{
#ifdef IPV6_MULTICAST_IF
// Instruct the kernel that any messages to multicast destinations should be
// sent down the interface specified by the caller.
setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_IF, &interfaceId, sizeof(interfaceId));
#endif // defined(IPV6_MULTICAST_IF)
}
#ifdef IPV6_MULTICAST_HOPS
// Instruct the kernel that any messages to multicast destinations should be
// set with the configured hop limit value.
int hops = INET_CONFIG_IP_MULTICAST_HOP_LIMIT;
setsockopt(socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
#endif // defined(IPV6_MULTICAST_HOPS)
return status;
}
#if INET_CONFIG_ENABLE_IPV4
CHIP_ERROR IPv4Bind(int socket, const IPAddress & address, uint16_t port)
{
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr = address.ToIPv4();
CHIP_ERROR status = CHIP_NO_ERROR;
if (bind(socket, reinterpret_cast<const sockaddr *>(&sa), static_cast<unsigned>(sizeof(sa))) != 0)
{
status = CHIP_ERROR_POSIX(errno);
}
else
{
// Allow socket transmitting broadcast packets.
constexpr int enable = 1;
setsockopt(socket, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable));
#ifdef IP_MULTICAST_IF
// Instruct the kernel that any messages to multicast destinations should be
// sent down the interface to which the specified IPv4 address is bound.
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_IF, &sa, sizeof(sa));
#endif // defined(IP_MULTICAST_IF)
}
#ifdef IP_MULTICAST_TTL
// Instruct the kernel that any messages to multicast destinations should be
// set with the configured hop limit value.
constexpr int ttl = INET_CONFIG_IP_MULTICAST_HOP_LIMIT;
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
#endif // defined(IP_MULTICAST_TTL)
return status;
}
#endif // INET_CONFIG_ENABLE_IPV4
} // anonymous namespace
#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sJoinMulticastGroupHandler;
UDPEndPointImplSockets::MulticastGroupHandler UDPEndPointImplSockets::sLeaveMulticastGroupHandler;
#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
CHIP_ERROR UDPEndPointImplSockets::BindImpl(IPAddressType addressType, const IPAddress & addr, uint16_t port, InterfaceId interface)
{
// Make sure we have the appropriate type of socket.
ReturnErrorOnFailure(GetSocket(addressType));
if (addressType == IPAddressType::kIPv6)
{
ReturnErrorOnFailure(IPv6Bind(mSocket, addr, port, interface));
}
#if INET_CONFIG_ENABLE_IPV4
else if (addressType == IPAddressType::kIPv4)
{
ReturnErrorOnFailure(IPv4Bind(mSocket, addr, port));
}
#endif // INET_CONFIG_ENABLE_IPV4
else
{
return INET_ERROR_WRONG_ADDRESS_TYPE;
}
mBoundPort = port;
mBoundIntfId = interface;
// If an ephemeral port was requested, retrieve the actual bound port.
if (port == 0)
{
SockAddr boundAddr;
socklen_t boundAddrLen = sizeof(boundAddr);
if (getsockname(mSocket, &boundAddr.any, &boundAddrLen) == 0)
{
if (boundAddr.any.sa_family == AF_INET)
{
mBoundPort = ntohs(boundAddr.in.sin_port);
}
else if (boundAddr.any.sa_family == AF_INET6)
{
mBoundPort = ntohs(boundAddr.in6.sin6_port);
}
}
}
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
dispatch_queue_t dispatchQueue = static_cast<System::LayerSocketsLoop *>(Layer().SystemLayer())->GetDispatchQueue();
if (dispatchQueue != nullptr)
{
unsigned long fd = static_cast<unsigned long>(mSocket);
mReadableSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, dispatchQueue);
ReturnErrorCodeIf(mReadableSource == nullptr, CHIP_ERROR_NO_MEMORY);
dispatch_source_set_event_handler(mReadableSource, ^{
this->HandlePendingIO(System::SocketEventFlags::kRead);
});
dispatch_resume(mReadableSource);
}
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplSockets::BindInterfaceImpl(IPAddressType addressType, InterfaceId interfaceId)
{
// Make sure we have the appropriate type of socket.
ReturnErrorOnFailure(GetSocket(addressType));
#if HAVE_SO_BINDTODEVICE
CHIP_ERROR status = CHIP_NO_ERROR;
if (interfaceId.IsPresent())
{
// Start filtering on the passed interface.
char interfaceName[IF_NAMESIZE];
if (if_indextoname(interfaceId.GetPlatformInterface(), interfaceName) == nullptr)
{
status = CHIP_ERROR_POSIX(errno);
}
else if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, interfaceName, socklen_t(strlen(interfaceName))) == -1)
{
status = CHIP_ERROR_POSIX(errno);
}
}
else
{
// Stop interface-based filtering.
if (setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, "", 0) == -1)
{
status = CHIP_ERROR_POSIX(errno);
}
}
if (status == CHIP_NO_ERROR)
{
mBoundIntfId = interfaceId;
}
return status;
#else // !HAVE_SO_BINDTODEVICE
return CHIP_ERROR_NOT_IMPLEMENTED;
#endif // HAVE_SO_BINDTODEVICE
}
InterfaceId UDPEndPointImplSockets::GetBoundInterface() const
{
return mBoundIntfId;
}
uint16_t UDPEndPointImplSockets::GetBoundPort() const
{
return mBoundPort;
}
CHIP_ERROR UDPEndPointImplSockets::ListenImpl()
{
// Wait for ability to read on this endpoint.
auto * layer = static_cast<System::LayerSockets *>(Layer().SystemLayer());
ReturnErrorOnFailure(layer->SetCallback(mWatch, HandlePendingIO, reinterpret_cast<intptr_t>(this)));
return layer->RequestCallbackOnPendingRead(mWatch);
}
CHIP_ERROR UDPEndPointImplSockets::SendMsgImpl(const IPPacketInfo * aPktInfo, System::PacketBufferHandle && msg)
{
// Make sure we have the appropriate type of socket based on the
// destination address.
ReturnErrorOnFailure(GetSocket(aPktInfo->DestAddress.Type()));
// Ensure the destination address type is compatible with the endpoint address type.
VerifyOrReturnError(mAddrType == aPktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT);
// For now the entire message must fit within a single buffer.
VerifyOrReturnError(!msg->HasChainedBuffer(), CHIP_ERROR_MESSAGE_TOO_LONG);
struct iovec msgIOV;
msgIOV.iov_base = msg->Start();
msgIOV.iov_len = msg->DataLength();
#if defined(IP_PKTINFO) || defined(IPV6_PKTINFO)
uint8_t controlData[256];
memset(controlData, 0, sizeof(controlData));
#endif // defined(IP_PKTINFO) || defined(IPV6_PKTINFO)
struct msghdr msgHeader;
memset(&msgHeader, 0, sizeof(msgHeader));
msgHeader.msg_iov = &msgIOV;
msgHeader.msg_iovlen = 1;
// Construct a sockaddr_in/sockaddr_in6 structure containing the destination information.
SockAddr peerSockAddr;
memset(&peerSockAddr, 0, sizeof(peerSockAddr));
msgHeader.msg_name = &peerSockAddr;
if (mAddrType == IPAddressType::kIPv6)
{
peerSockAddr.in6.sin6_family = AF_INET6;
peerSockAddr.in6.sin6_port = htons(aPktInfo->DestPort);
peerSockAddr.in6.sin6_addr = aPktInfo->DestAddress.ToIPv6();
InterfaceId::PlatformType intfId = aPktInfo->Interface.GetPlatformInterface();
VerifyOrReturnError(CanCastTo<decltype(peerSockAddr.in6.sin6_scope_id)>(intfId), CHIP_ERROR_INCORRECT_STATE);
peerSockAddr.in6.sin6_scope_id = static_cast<decltype(peerSockAddr.in6.sin6_scope_id)>(intfId);
msgHeader.msg_namelen = sizeof(sockaddr_in6);
}
#if INET_CONFIG_ENABLE_IPV4
else
{
peerSockAddr.in.sin_family = AF_INET;
peerSockAddr.in.sin_port = htons(aPktInfo->DestPort);
peerSockAddr.in.sin_addr = aPktInfo->DestAddress.ToIPv4();
msgHeader.msg_namelen = sizeof(sockaddr_in);
}
#endif // INET_CONFIG_ENABLE_IPV4
// If the endpoint has been bound to a particular interface,
// and the caller didn't supply a specific interface to send
// on, use the bound interface. This appears to be necessary
// for messages to multicast addresses, which under Linux
// don't seem to get sent out the correct interface, despite
// the socket being bound.
InterfaceId intf = aPktInfo->Interface;
if (!intf.IsPresent())
{
intf = mBoundIntfId;
}
// If the packet should be sent over a specific interface, or with a specific source
// address, construct an IP_PKTINFO/IPV6_PKTINFO "control message" to that effect
// add add it to the message header. If the local OS doesn't support IP_PKTINFO/IPV6_PKTINFO
// fail with an error.
if (intf.IsPresent() || aPktInfo->SrcAddress.Type() != IPAddressType::kAny)
{
#if defined(IP_PKTINFO) || defined(IPV6_PKTINFO)
msgHeader.msg_control = controlData;
msgHeader.msg_controllen = sizeof(controlData);
struct cmsghdr * controlHdr = CMSG_FIRSTHDR(&msgHeader);
InterfaceId::PlatformType intfId = intf.GetPlatformInterface();
#if INET_CONFIG_ENABLE_IPV4
if (mAddrType == IPAddressType::kIPv4)
{
#if defined(IP_PKTINFO)
controlHdr->cmsg_level = IPPROTO_IP;
controlHdr->cmsg_type = IP_PKTINFO;
controlHdr->cmsg_len = CMSG_LEN(sizeof(in_pktinfo));
auto * pktInfo = reinterpret_cast<struct in_pktinfo *> CMSG_DATA(controlHdr);
if (!CanCastTo<decltype(pktInfo->ipi_ifindex)>(intfId))
{
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
}
pktInfo->ipi_ifindex = static_cast<decltype(pktInfo->ipi_ifindex)>(intfId);
pktInfo->ipi_spec_dst = aPktInfo->SrcAddress.ToIPv4();
msgHeader.msg_controllen = CMSG_SPACE(sizeof(in_pktinfo));
#else // !defined(IP_PKTINFO)
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // !defined(IP_PKTINFO)
}
#endif // INET_CONFIG_ENABLE_IPV4
if (mAddrType == IPAddressType::kIPv6)
{
#if defined(IPV6_PKTINFO)
controlHdr->cmsg_level = IPPROTO_IPV6;
controlHdr->cmsg_type = IPV6_PKTINFO;
controlHdr->cmsg_len = CMSG_LEN(sizeof(in6_pktinfo));
auto * pktInfo = reinterpret_cast<struct in6_pktinfo *> CMSG_DATA(controlHdr);
if (!CanCastTo<decltype(pktInfo->ipi6_ifindex)>(intfId))
{
return CHIP_ERROR_UNEXPECTED_EVENT;
}
pktInfo->ipi6_ifindex = static_cast<decltype(pktInfo->ipi6_ifindex)>(intfId);
pktInfo->ipi6_addr = aPktInfo->SrcAddress.ToIPv6();
msgHeader.msg_controllen = CMSG_SPACE(sizeof(in6_pktinfo));
#else // !defined(IPV6_PKTINFO)
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // !defined(IPV6_PKTINFO)
}
#else // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO))
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // !(defined(IP_PKTINFO) && defined(IPV6_PKTINFO))
}
// Send IP packet.
const ssize_t lenSent = sendmsg(mSocket, &msgHeader, 0);
if (lenSent == -1)
{
return CHIP_ERROR_POSIX(errno);
}
if (lenSent != msg->DataLength())
{
return CHIP_ERROR_OUTBOUND_MESSAGE_TOO_BIG;
}
return CHIP_NO_ERROR;
}
void UDPEndPointImplSockets::CloseImpl()
{
if (mSocket != kInvalidSocketFd)
{
static_cast<System::LayerSockets *>(Layer().SystemLayer())->StopWatchingSocket(&mWatch);
close(mSocket);
mSocket = kInvalidSocketFd;
}
#if CHIP_SYSTEM_CONFIG_USE_DISPATCH
if (mReadableSource)
{
dispatch_source_cancel(mReadableSource);
dispatch_release(mReadableSource);
}
#endif // CHIP_SYSTEM_CONFIG_USE_DISPATCH
}
void UDPEndPointImplSockets::Free()
{
Close();
Release();
}
CHIP_ERROR UDPEndPointImplSockets::GetSocket(IPAddressType addressType)
{
if (mSocket == kInvalidSocketFd)
{
constexpr int type = (SOCK_DGRAM | SOCK_CLOEXEC);
constexpr int protocol = 0;
int family = PF_UNSPEC;
switch (addressType)
{
case IPAddressType::kIPv6:
family = PF_INET6;
break;
#if INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv4:
family = PF_INET;
break;
#endif // INET_CONFIG_ENABLE_IPV4
default:
return INET_ERROR_WRONG_ADDRESS_TYPE;
}
mSocket = ::socket(family, type, protocol);
if (mSocket == -1)
{
return CHIP_ERROR_POSIX(errno);
}
ReturnErrorOnFailure(static_cast<System::LayerSockets *>(Layer().SystemLayer())->StartWatchingSocket(mSocket, &mWatch));
mAddrType = addressType;
// NOTE WELL: the errors returned by setsockopt() here are not
// returned as Inet layer CHIP_ERROR_POSIX(errno)
// codes because they are normally expected to fail on some
// platforms where the socket option code is defined in the
// header files but not [yet] implemented. Certainly, there is
// room to improve this by connecting the build configuration
// logic up to check for implementations of these options and
// to provide appropriate HAVE_xxxxx definitions accordingly.
constexpr int one = 1;
int res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
static_cast<void>(res);
#ifdef SO_REUSEPORT
res = setsockopt(mSocket, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
if (res != 0)
{
ChipLogError(Inet, "SO_REUSEPORT failed: %d", errno);
}
#endif // defined(SO_REUSEPORT)
// If creating an IPv6 socket, tell the kernel that it will be
// IPv6 only. This makes it posible to bind two sockets to
// the same port, one for IPv4 and one for IPv6.
#ifdef IPV6_V6ONLY
if (addressType == IPAddressType::kIPv6)
{
res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
if (res != 0)
{
ChipLogError(Inet, "IPV6_V6ONLY failed: %d", errno);
}
}
#endif // defined(IPV6_V6ONLY)
#if INET_CONFIG_ENABLE_IPV4
#ifdef IP_PKTINFO
if (addressType == IPAddressType::kIPv4)
{
res = setsockopt(mSocket, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one));
if (res != 0)
{
ChipLogError(Inet, "IP_PKTINFO failed: %d", errno);
}
}
#endif // defined(IP_PKTINFO)
#endif // INET_CONFIG_ENABLE_IPV4
#ifdef IPV6_RECVPKTINFO
if (addressType == IPAddressType::kIPv6)
{
res = setsockopt(mSocket, IPPROTO_IPV6, IPV6_RECVPKTINFO, &one, sizeof(one));
if (res != 0)
{
ChipLogError(Inet, "IPV6_PKTINFO failed: %d", errno);
}
}
#endif // defined(IPV6_RECVPKTINFO)
// On systems that support it, disable the delivery of SIGPIPE
// signals when writing to a closed socket. This is mostly
// needed on iOS which has the peculiar habit of sending
// SIGPIPEs on unconnected UDP sockets.
#ifdef SO_NOSIGPIPE
{
res = setsockopt(mSocket, SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(one));
if (res != 0)
{
ChipLogError(Inet, "SO_NOSIGPIPE failed: %d", errno);
}
}
#endif // defined(SO_NOSIGPIPE)
}
else if (mAddrType != addressType)
{
return CHIP_ERROR_INCORRECT_STATE;
}
return CHIP_NO_ERROR;
}
// static
void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events, intptr_t data)
{
reinterpret_cast<UDPEndPointImplSockets *>(data)->HandlePendingIO(events);
}
void UDPEndPointImplSockets::HandlePendingIO(System::SocketEvents events)
{
if (mState != State::kListening || OnMessageReceived == nullptr || !events.Has(System::SocketEventFlags::kRead))
{
return;
}
CHIP_ERROR lStatus = CHIP_NO_ERROR;
IPPacketInfo lPacketInfo;
System::PacketBufferHandle lBuffer;
lPacketInfo.Clear();
lPacketInfo.DestPort = mBoundPort;
lBuffer = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSizeWithoutReserve, 0);
if (!lBuffer.IsNull())
{
struct iovec msgIOV;
SockAddr lPeerSockAddr;
uint8_t controlData[256];
struct msghdr msgHeader;
msgIOV.iov_base = lBuffer->Start();
msgIOV.iov_len = lBuffer->AvailableDataLength();
memset(&lPeerSockAddr, 0, sizeof(lPeerSockAddr));
memset(&msgHeader, 0, sizeof(msgHeader));
msgHeader.msg_name = &lPeerSockAddr;
msgHeader.msg_namelen = sizeof(lPeerSockAddr);
msgHeader.msg_iov = &msgIOV;
msgHeader.msg_iovlen = 1;
msgHeader.msg_control = controlData;
msgHeader.msg_controllen = sizeof(controlData);
ssize_t rcvLen = recvmsg(mSocket, &msgHeader, MSG_DONTWAIT);
if (rcvLen < 0)
{
lStatus = CHIP_ERROR_POSIX(errno);
}
else if (rcvLen > lBuffer->AvailableDataLength())
{
lStatus = CHIP_ERROR_INBOUND_MESSAGE_TOO_BIG;
}
else
{
lBuffer->SetDataLength(static_cast<uint16_t>(rcvLen));
if (lPeerSockAddr.any.sa_family == AF_INET6)
{
lPacketInfo.SrcAddress = IPAddress(lPeerSockAddr.in6.sin6_addr);
lPacketInfo.SrcPort = ntohs(lPeerSockAddr.in6.sin6_port);
}
#if INET_CONFIG_ENABLE_IPV4
else if (lPeerSockAddr.any.sa_family == AF_INET)
{
lPacketInfo.SrcAddress = IPAddress(lPeerSockAddr.in.sin_addr);
lPacketInfo.SrcPort = ntohs(lPeerSockAddr.in.sin_port);
}
#endif // INET_CONFIG_ENABLE_IPV4
else
{
lStatus = CHIP_ERROR_INCORRECT_STATE;
}
}
if (lStatus == CHIP_NO_ERROR)
{
for (struct cmsghdr * controlHdr = CMSG_FIRSTHDR(&msgHeader); controlHdr != nullptr;
controlHdr = CMSG_NXTHDR(&msgHeader, controlHdr))
{
#if INET_CONFIG_ENABLE_IPV4
#ifdef IP_PKTINFO
if (controlHdr->cmsg_level == IPPROTO_IP && controlHdr->cmsg_type == IP_PKTINFO)
{
auto * inPktInfo = reinterpret_cast<struct in_pktinfo *> CMSG_DATA(controlHdr);
if (!CanCastTo<InterfaceId::PlatformType>(inPktInfo->ipi_ifindex))
{
lStatus = CHIP_ERROR_INCORRECT_STATE;
break;
}
lPacketInfo.Interface = InterfaceId(static_cast<InterfaceId::PlatformType>(inPktInfo->ipi_ifindex));
lPacketInfo.DestAddress = IPAddress(inPktInfo->ipi_addr);
continue;
}
#endif // defined(IP_PKTINFO)
#endif // INET_CONFIG_ENABLE_IPV4
#ifdef IPV6_PKTINFO
if (controlHdr->cmsg_level == IPPROTO_IPV6 && controlHdr->cmsg_type == IPV6_PKTINFO)
{
auto * in6PktInfo = reinterpret_cast<struct in6_pktinfo *> CMSG_DATA(controlHdr);
if (!CanCastTo<InterfaceId::PlatformType>(in6PktInfo->ipi6_ifindex))
{
lStatus = CHIP_ERROR_INCORRECT_STATE;
break;
}
lPacketInfo.Interface = InterfaceId(static_cast<InterfaceId::PlatformType>(in6PktInfo->ipi6_ifindex));
lPacketInfo.DestAddress = IPAddress(in6PktInfo->ipi6_addr);
continue;
}
#endif // defined(IPV6_PKTINFO)
}
}
}
else
{
lStatus = CHIP_ERROR_NO_MEMORY;
}
if (lStatus == CHIP_NO_ERROR)
{
lBuffer.RightSize();
OnMessageReceived(this, std::move(lBuffer), &lPacketInfo);
}
else
{
if (OnReceiveError != nullptr && lStatus != CHIP_ERROR_POSIX(EAGAIN))
{
OnReceiveError(this, lStatus, nullptr);
}
}
}
#if IP_MULTICAST_LOOP || IPV6_MULTICAST_LOOP
static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, bool aLoopback, int aProtocol, int aOption)
{
const unsigned int lValue = static_cast<unsigned int>(aLoopback);
if (setsockopt(aSocket, aProtocol, aOption, &lValue, sizeof(lValue)) != 0)
{
return CHIP_ERROR_POSIX(errno);
}
return CHIP_NO_ERROR;
}
#endif // IP_MULTICAST_LOOP || IPV6_MULTICAST_LOOP
static CHIP_ERROR SocketsSetMulticastLoopback(int aSocket, IPVersion aIPVersion, bool aLoopback)
{
#ifdef IPV6_MULTICAST_LOOP
CHIP_ERROR lRetval;
switch (aIPVersion)
{
case kIPVersion_6:
lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);
break;
#if INET_CONFIG_ENABLE_IPV4
case kIPVersion_4:
lRetval = SocketsSetMulticastLoopback(aSocket, aLoopback, IPPROTO_IP, IP_MULTICAST_LOOP);
break;
#endif // INET_CONFIG_ENABLE_IPV4
default:
lRetval = INET_ERROR_WRONG_ADDRESS_TYPE;
break;
}
return (lRetval);
#else // IPV6_MULTICAST_LOOP
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // IPV6_MULTICAST_LOOP
}
CHIP_ERROR UDPEndPointImplSockets::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback)
{
CHIP_ERROR lRetval = CHIP_ERROR_NOT_IMPLEMENTED;
lRetval = SocketsSetMulticastLoopback(mSocket, aIPVersion, aLoopback);
SuccessOrExit(lRetval);
exit:
return (lRetval);
}
#if INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplSockets::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join)
{
IPAddress lInterfaceAddress;
bool lInterfaceAddressFound = false;
for (InterfaceAddressIterator lAddressIterator; lAddressIterator.HasCurrent(); lAddressIterator.Next())
{
const IPAddress lCurrentAddress = lAddressIterator.GetAddress();
if (lAddressIterator.GetInterfaceId() == aInterfaceId)
{
if (lCurrentAddress.IsIPv4())
{
lInterfaceAddressFound = true;
lInterfaceAddress = lCurrentAddress;
break;
}
}
}
VerifyOrReturnError(lInterfaceAddressFound, INET_ERROR_ADDRESS_NOT_FOUND);
struct ip_mreq lMulticastRequest;
memset(&lMulticastRequest, 0, sizeof(lMulticastRequest));
lMulticastRequest.imr_interface = lInterfaceAddress.ToIPv4();
lMulticastRequest.imr_multiaddr = aAddress.ToIPv4();
const int command = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
if (setsockopt(mSocket, IPPROTO_IP, command, &lMulticastRequest, sizeof(lMulticastRequest)) != 0)
{
return CHIP_ERROR_POSIX(errno);
}
return CHIP_NO_ERROR;
}
#endif // INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplSockets::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress, bool join)
{
#if CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
MulticastGroupHandler handler = join ? sJoinMulticastGroupHandler : sLeaveMulticastGroupHandler;
if (handler != nullptr)
{
return handler(aInterfaceId, aAddress);
}
#endif // CHIP_SYSTEM_CONFIG_USE_PLATFORM_MULTICAST_API
#if defined(INET_IPV6_ADD_MEMBERSHIP) && defined(INET_IPV6_DROP_MEMBERSHIP)
const InterfaceId::PlatformType lIfIndex = aInterfaceId.GetPlatformInterface();
struct ipv6_mreq lMulticastRequest;
memset(&lMulticastRequest, 0, sizeof(lMulticastRequest));
VerifyOrReturnError(CanCastTo<decltype(lMulticastRequest.ipv6mr_interface)>(lIfIndex), CHIP_ERROR_UNEXPECTED_EVENT);
lMulticastRequest.ipv6mr_interface = static_cast<decltype(lMulticastRequest.ipv6mr_interface)>(lIfIndex);
lMulticastRequest.ipv6mr_multiaddr = aAddress.ToIPv6();
const int command = join ? INET_IPV6_ADD_MEMBERSHIP : INET_IPV6_DROP_MEMBERSHIP;
if (setsockopt(mSocket, IPPROTO_IPV6, command, &lMulticastRequest, sizeof(lMulticastRequest)) != 0)
{
return CHIP_ERROR_POSIX(errno);
}
return CHIP_NO_ERROR;
#else // defined(INET_IPV6_ADD_MEMBERSHIP) && defined(INET_IPV6_DROP_MEMBERSHIP)
return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
#endif // defined(INET_IPV6_ADD_MEMBERSHIP) && defined(INET_IPV6_DROP_MEMBERSHIP)
}
#endif // CHIP_SYSTEM_CONFIG_USE_SOCKETS
#if CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
BitMapObjectPool<UDPEndPointImplNetworkFramework, INET_CONFIG_NUM_UDP_ENDPOINTS> UDPEndPointImplNetworkFramework::sPool;
CHIP_ERROR UDPEndPointImplNetworkFramework::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port,
InterfaceId intfId)
{
nw_parameters_configure_protocol_block_t configure_tls;
nw_parameters_t parameters;
if (intfId.IsPresent())
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
configure_tls = NW_PARAMETERS_DISABLE_PROTOCOL;
parameters = nw_parameters_create_secure_udp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION);
VerifyOrReturnError(parameters != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
ReturnErrorOnFailure(ConfigureProtocol(addressType, parameters));
nw_endpoint_t endpoint = nullptr;
CHIP_ERROR res = GetEndPoint(endpoint, addressType, address, port);
nw_parameters_set_local_endpoint(parameters, endpoint);
nw_release(endpoint);
ReturnErrorOnFailure(res);
mDispatchQueue = dispatch_queue_create("inet_dispatch_global", DISPATCH_QUEUE_CONCURRENT);
VerifyOrReturnError(mDispatchQueue != nullptr, CHIP_ERROR_NO_MEMORY);
dispatch_retain(mDispatchQueue);
mConnectionSemaphore = dispatch_semaphore_create(0);
VerifyOrReturnError(mConnectionSemaphore != nullptr, CHIP_ERROR_NO_MEMORY);
dispatch_retain(mConnectionSemaphore);
mSendSemaphore = dispatch_semaphore_create(0);
VerifyOrReturnError(mSendSemaphore != nullptr, CHIP_ERROR_NO_MEMORY);
dispatch_retain(mSendSemaphore);
mAddrType = addressType;
mConnection = nullptr;
mParameters = parameters;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::BindInterfaceImpl(IPAddressType addrType, InterfaceId intfId)
{
return INET_ERROR_UNKNOWN_INTERFACE;
}
InterfaceId UDPEndPointImplNetworkFramework::GetBoundInterface() const
{
return InterfaceId::Null();
}
uint16_t UDPEndPointImplNetworkFramework::GetBoundPort() const
{
nw_endpoint_t endpoint = nw_parameters_copy_local_endpoint(mParameters);
return nw_endpoint_get_port(endpoint);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ListenImpl()
{
return StartListener();
}
CHIP_ERROR UDPEndPointImplNetworkFramework::SendMsgImpl(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg)
{
dispatch_data_t content;
// Ensure the destination address type is compatible with the endpoint address type.
VerifyOrReturnError(mAddrType == pktInfo->DestAddress.Type(), CHIP_ERROR_INVALID_ARGUMENT);
// For now the entire message must fit within a single buffer.
VerifyOrReturnError(msg->Next() == nullptr, CHIP_ERROR_MESSAGE_TOO_LONG);
ReturnErrorOnFailure(GetConnection(pktInfo));
// Send a message, and wait for it to be dispatched.
content = dispatch_data_create(msg->Start(), msg->DataLength(), mDispatchQueue, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
// If there is a current message pending and the state of the network connection change (e.g switch to a
// different network) the connection will enter a nw_connection_state_failed state and the completion handler
// will never be called. In such cases a signal is sent from the connection state change handler to release
// the semaphore. In this case the CHIP_ERROR will not update with the result of the completion handler.
// To make sure caller knows that sending a message has failed the following code consider there is an error
// _unless_ the completion handler says otherwise.
__block CHIP_ERROR res = CHIP_ERROR_UNEXPECTED_EVENT;
nw_connection_send(mConnection, content, NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, ^(nw_error_t error) {
if (error)
{
res = CHIP_ERROR_POSIX(nw_error_get_error_code(error));
}
else
{
res = CHIP_NO_ERROR;
}
dispatch_semaphore_signal(mSendSemaphore);
});
dispatch_release(content);
dispatch_semaphore_wait(mSendSemaphore, DISPATCH_TIME_FOREVER);
return res;
}
void UDPEndPointImplNetworkFramework::CloseImpl()
{
ReleaseAll();
}
void UDPEndPointImplNetworkFramework::ReleaseAll()
{
OnMessageReceived = nullptr;
OnReceiveError = nullptr;
ReleaseConnection();
ReleaseListener();
if (mParameters)
{
nw_release(mParameters);
mParameters = nullptr;
}
if (mDispatchQueue)
{
dispatch_suspend(mDispatchQueue);
dispatch_release(mDispatchQueue);
mDispatchQueue = nullptr;
}
if (mConnectionSemaphore)
{
dispatch_release(mConnectionSemaphore);
mConnectionSemaphore = nullptr;
}
if (mListenerQueue)
{
dispatch_suspend(mListenerQueue);
dispatch_release(mListenerQueue);
mListenerQueue = nullptr;
}
if (mListenerSemaphore)
{
dispatch_release(mListenerSemaphore);
mListenerSemaphore = nullptr;
}
if (mSendSemaphore)
{
dispatch_release(mSendSemaphore);
mSendSemaphore = nullptr;
}
}
void UDPEndPointImplNetworkFramework::Free()
{
Close();
Release();
}
CHIP_ERROR UDPEndPointImplNetworkFramework::SetMulticastLoopback(IPVersion aIPVersion, bool aLoopback)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
#if INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplNetworkFramework::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress,
bool join)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
#endif // INET_CONFIG_ENABLE_IPV4
CHIP_ERROR UDPEndPointImplNetworkFramework::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInterfaceId, const IPAddress & aAddress,
bool join)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ConfigureProtocol(IPAddressType aAddressType, const nw_parameters_t & aParameters)
{
CHIP_ERROR res = CHIP_NO_ERROR;
nw_protocol_stack_t protocolStack = nw_parameters_copy_default_protocol_stack(aParameters);
nw_protocol_options_t ipOptions = nw_protocol_stack_copy_internet_protocol(protocolStack);
switch (aAddressType)
{
case IPAddressType::kIPv6:
nw_ip_options_set_version(ipOptions, nw_ip_version_6);
break;
#if INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv4:
nw_ip_options_set_version(ipOptions, nw_ip_version_4);
break;
#endif // INET_CONFIG_ENABLE_IPV4
default:
res = INET_ERROR_WRONG_ADDRESS_TYPE;
break;
}
nw_release(ipOptions);
nw_release(protocolStack);
return res;
}
void UDPEndPointImplNetworkFramework::GetPacketInfo(const nw_connection_t & aConnection, IPPacketInfo & aPacketInfo)
{
nw_path_t path = nw_connection_copy_current_path(aConnection);
nw_endpoint_t dest_endpoint = nw_path_copy_effective_local_endpoint(path);
nw_endpoint_t src_endpoint = nw_path_copy_effective_remote_endpoint(path);
aPacketInfo.Clear();
aPacketInfo.SrcAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(src_endpoint));
aPacketInfo.DestAddress = IPAddress::FromSockAddr(*nw_endpoint_get_address(dest_endpoint));
aPacketInfo.SrcPort = nw_endpoint_get_port(src_endpoint);
aPacketInfo.DestPort = nw_endpoint_get_port(dest_endpoint);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::GetEndPoint(nw_endpoint_t & aEndPoint, const IPAddressType aAddressType,
const IPAddress & aAddress, uint16_t aPort)
{
char addrStr[INET6_ADDRSTRLEN];
char portStr[INET_PORTSTRLEN];
// Note: aAddress.ToString will return the IPv6 Any address if the address type is Any, but that's not what
// we want if the locale endpoint is IPv4.
if (aAddressType == IPAddressType::kIPv4 && aAddress.Type() == IPAddressType::kAny)
{
const IPAddress anyAddr = IPAddress(aAddress.ToIPv4());
anyAddr.ToString(addrStr, sizeof(addrStr));
}
else
{
aAddress.ToString(addrStr, sizeof(addrStr));
}
snprintf(portStr, sizeof(portStr), "%u", aPort);
aEndPoint = nw_endpoint_create_host(addrStr, portStr);
VerifyOrReturnError(aEndPoint != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::GetConnection(const IPPacketInfo * aPktInfo)
{
VerifyOrReturnError(mParameters != nullptr, CHIP_ERROR_INCORRECT_STATE);
nw_endpoint_t endpoint = nullptr;
nw_connection_t connection = nullptr;
if (mConnection)
{
nw_path_t path = nw_connection_copy_current_path(mConnection);
nw_endpoint_t remote_endpoint = nw_path_copy_effective_remote_endpoint(path);
const IPAddress remote_address = IPAddress::FromSockAddr(*nw_endpoint_get_address(remote_endpoint));
const uint16_t remote_port = nw_endpoint_get_port(remote_endpoint);
const bool isDifferentEndPoint = aPktInfo->DestPort != remote_port || aPktInfo->DestAddress != remote_address;
VerifyOrReturnError(isDifferentEndPoint, CHIP_NO_ERROR);
ReturnErrorOnFailure(ReleaseConnection());
}
ReturnErrorOnFailure(GetEndPoint(endpoint, mAddrType, aPktInfo->DestAddress, aPktInfo->DestPort));
connection = nw_connection_create(endpoint, mParameters);
nw_release(endpoint);
VerifyOrReturnError(connection != nullptr, CHIP_ERROR_INCORRECT_STATE);
return StartConnection(connection);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::StartListener()
{
__block CHIP_ERROR res = CHIP_NO_ERROR;
nw_listener_t listener;
VerifyOrReturnError(mListener == nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mListenerSemaphore == nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mListenerQueue == nullptr, CHIP_ERROR_INCORRECT_STATE);
listener = nw_listener_create(mParameters);
VerifyOrReturnError(listener != nullptr, CHIP_ERROR_INCORRECT_STATE);
mListenerSemaphore = dispatch_semaphore_create(0);
VerifyOrReturnError(mListenerSemaphore != nullptr, CHIP_ERROR_NO_MEMORY);
dispatch_retain(mListenerSemaphore);
mListenerQueue = dispatch_queue_create("inet_dispatch_listener", DISPATCH_QUEUE_CONCURRENT);
VerifyOrReturnError(mListenerQueue != nullptr, CHIP_ERROR_NO_MEMORY);
dispatch_retain(mListenerQueue);
nw_listener_set_queue(listener, mListenerQueue);
nw_listener_set_new_connection_handler(listener, ^(nw_connection_t connection) {
ReleaseConnection();
StartConnection(connection);
});
nw_listener_set_state_changed_handler(listener, ^(nw_listener_state_t state, nw_error_t error) {
switch (state)
{
case nw_listener_state_invalid:
ChipLogDetail(Inet, "Listener: Invalid");
res = CHIP_ERROR_INCORRECT_STATE;
nw_listener_cancel(listener);
break;
case nw_listener_state_waiting:
ChipLogDetail(Inet, "Listener: Waiting");
break;
case nw_listener_state_failed:
ChipLogDetail(Inet, "Listener: Failed");
res = CHIP_ERROR_POSIX(nw_error_get_error_code(error));
break;
case nw_listener_state_ready:
ChipLogDetail(Inet, "Listener: Ready");
res = CHIP_NO_ERROR;
dispatch_semaphore_signal(mListenerSemaphore);
break;
case nw_listener_state_cancelled:
ChipLogDetail(Inet, "Listener: Cancelled");
if (res == CHIP_NO_ERROR)
{
res = CHIP_ERROR_CONNECTION_ABORTED;
}
dispatch_semaphore_signal(mListenerSemaphore);
break;
}
});
nw_listener_start(listener);
dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER);
ReturnErrorOnFailure(res);
mListener = listener;
nw_retain(mListener);
return res;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::StartConnection(nw_connection_t & aConnection)
{
__block CHIP_ERROR res = CHIP_NO_ERROR;
nw_connection_set_queue(aConnection, mDispatchQueue);
nw_connection_set_state_changed_handler(aConnection, ^(nw_connection_state_t state, nw_error_t error) {
switch (state)
{
case nw_connection_state_invalid:
ChipLogDetail(Inet, "Connection: Invalid");
res = CHIP_ERROR_INCORRECT_STATE;
nw_connection_cancel(aConnection);
break;
case nw_connection_state_preparing:
ChipLogDetail(Inet, "Connection: Preparing");
res = CHIP_ERROR_INCORRECT_STATE;
break;
case nw_connection_state_waiting:
ChipLogDetail(Inet, "Connection: Waiting");
nw_connection_cancel(aConnection);
break;
case nw_connection_state_failed:
ChipLogDetail(Inet, "Connection: Failed");
res = CHIP_ERROR_POSIX(nw_error_get_error_code(error));
break;
case nw_connection_state_ready:
ChipLogDetail(Inet, "Connection: Ready");
res = CHIP_NO_ERROR;
dispatch_semaphore_signal(mConnectionSemaphore);
break;
case nw_connection_state_cancelled:
ChipLogDetail(Inet, "Connection: Cancelled");
if (res == CHIP_NO_ERROR)
{
res = CHIP_ERROR_CONNECTION_ABORTED;
}
dispatch_semaphore_signal(mConnectionSemaphore);
break;
}
});
nw_connection_start(aConnection);
dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER);
SuccessOrExit(res);
mConnection = aConnection;
nw_retain(mConnection);
HandleDataReceived(mConnection);
return res;
}
void UDPEndPointImplNetworkFramework::HandleDataReceived(const nw_connection_t & aConnection)
{
nw_connection_receive_completion_t handler =
^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) {
dispatch_block_t schedule_next_receive = ^{
if (receive_error == nullptr)
{
HandleDataReceived(aConnection);
}
else if (OnReceiveError != nullptr)
{
nw_error_domain_t error_domain = nw_error_get_error_domain(receive_error);
errno = nw_error_get_error_code(receive_error);
if (!(error_domain == nw_error_domain_posix && errno == ECANCELED))
{
CHIP_ERROR error = CHIP_ERROR_POSIX(errno);
IPPacketInfo packetInfo;
GetPacketInfo(aConnection, packetInfo);
dispatch_async(mDispatchQueue, ^{
OnReceiveError((UDPEndPoint *) this, error, &packetInfo);
});
}
}
};
if (content != nullptr && OnMessageReceived != nullptr)
{
size_t count = dispatch_data_get_size(content);
System::PacketBufferHandle * packetBuffer = System::PacketBufferHandle::New(count);
dispatch_data_apply(content, ^(dispatch_data_t data, size_t offset, const void * buffer, size_t size) {
memmove(packetBuffer->Start() + offset, buffer, size);
return true;
});
packetBuffer->SetDataLength(count);
IPPacketInfo packetInfo;
GetPacketInfo(aConnection, packetInfo);
dispatch_async(mDispatchQueue, ^{
OnMessageReceived((UDPEndPoint *) this, packetBuffer, &packetInfo);
});
}
schedule_next_receive();
};
nw_connection_receive_message(aConnection, handler);
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseListener()
{
VerifyOrReturnError(mListener, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE);
nw_listener_cancel(mListener);
dispatch_semaphore_wait(mListenerSemaphore, DISPATCH_TIME_FOREVER);
nw_release(mListener);
mListener = nullptr;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPointImplNetworkFramework::ReleaseConnection()
{
VerifyOrReturnError(mConnection, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mDispatchQueue, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(mConnectionSemaphore, CHIP_ERROR_INCORRECT_STATE);
nw_connection_cancel(mConnection);
dispatch_semaphore_wait(mConnectionSemaphore, DISPATCH_TIME_FOREVER);
nw_release(mConnection);
mConnection = nullptr;
return CHIP_NO_ERROR;
}
#endif // CHIP_SYSTEM_CONFIG_USE_NETWORK_FRAMEWORK
CHIP_ERROR UDPEndPoint::Bind(IPAddressType addrType, const IPAddress & addr, uint16_t port, InterfaceId intfId)
{
if (mState != State::kReady && mState != State::kBound)
{
return CHIP_ERROR_INCORRECT_STATE;
}
if ((addr != IPAddress::Any) && (addr.Type() != IPAddressType::kAny) && (addr.Type() != addrType))
{
return INET_ERROR_WRONG_ADDRESS_TYPE;
}
ReturnErrorOnFailure(BindImpl(addrType, addr, port, intfId));
mState = State::kBound;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPoint::BindInterface(IPAddressType addrType, InterfaceId intfId)
{
if (mState != State::kReady && mState != State::kBound)
{
return CHIP_ERROR_INCORRECT_STATE;
}
ReturnErrorOnFailure(BindInterfaceImpl(addrType, intfId));
mState = State::kBound;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPoint::Listen(OnMessageReceivedFunct onMessageReceived, OnReceiveErrorFunct onReceiveError, void * appState)
{
if (mState == State::kListening)
{
return CHIP_NO_ERROR;
}
if (mState != State::kBound)
{
return CHIP_ERROR_INCORRECT_STATE;
}
OnMessageReceived = onMessageReceived;
OnReceiveError = onReceiveError;
mAppState = appState;
ReturnErrorOnFailure(ListenImpl());
mState = State::kListening;
return CHIP_NO_ERROR;
}
CHIP_ERROR UDPEndPoint::SendTo(const IPAddress & addr, uint16_t port, chip::System::PacketBufferHandle && msg, InterfaceId intfId)
{
IPPacketInfo pktInfo;
pktInfo.Clear();
pktInfo.DestAddress = addr;
pktInfo.DestPort = port;
pktInfo.Interface = intfId;
return SendMsg(&pktInfo, std::move(msg));
}
CHIP_ERROR UDPEndPoint::SendMsg(const IPPacketInfo * pktInfo, System::PacketBufferHandle && msg)
{
INET_FAULT_INJECT(FaultInjection::kFault_Send, return INET_ERROR_UNKNOWN_INTERFACE;);
INET_FAULT_INJECT(FaultInjection::kFault_SendNonCritical, return CHIP_ERROR_NO_MEMORY;);
ReturnErrorOnFailure(SendMsgImpl(pktInfo, std::move(msg)));
CHIP_SYSTEM_FAULT_INJECT_ASYNC_EVENT();
return CHIP_NO_ERROR;
}
void UDPEndPoint::Close()
{
if (mState != State::kClosed)
{
CloseImpl();
mState = State::kClosed;
}
}
CHIP_ERROR UDPEndPoint::JoinMulticastGroup(InterfaceId aInterfaceId, const IPAddress & aAddress)
{
CHIP_ERROR lRetval = CHIP_ERROR_NOT_IMPLEMENTED;
const IPAddressType lAddrType = aAddress.Type();
lRetval = CheckMulticastGroupArgs(aInterfaceId, aAddress);
SuccessOrExit(lRetval);
switch (lAddrType)
{
#if INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv4: {
return IPv4JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, true);
}
break;
#endif // INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv6: {
return IPv6JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, true);
}
break;
default:
lRetval = INET_ERROR_WRONG_ADDRESS_TYPE;
break;
}
exit:
return (lRetval);
}
CHIP_ERROR UDPEndPoint::LeaveMulticastGroup(InterfaceId aInterfaceId, const IPAddress & aAddress)
{
CHIP_ERROR lRetval = CHIP_ERROR_NOT_IMPLEMENTED;
const IPAddressType lAddrType = aAddress.Type();
lRetval = CheckMulticastGroupArgs(aInterfaceId, aAddress);
SuccessOrExit(lRetval);
switch (lAddrType)
{
#if INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv4: {
return IPv4JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, false);
}
break;
#endif // INET_CONFIG_ENABLE_IPV4
case IPAddressType::kIPv6: {
return IPv6JoinLeaveMulticastGroupImpl(aInterfaceId, aAddress, false);
}
break;
default:
lRetval = INET_ERROR_WRONG_ADDRESS_TYPE;
break;
}
exit:
return (lRetval);
}
} // namespace Inet
} // namespace chip