Skip to content

Commit

Permalink
UDPEndPointImplLwIP: Support LWIP_TCPIP_CORE_LOCKING=0
Browse files Browse the repository at this point in the history
Wrap calls to LwIP APIs in `tcpip_api_call()`, as required.
When `LWIP_TCPIP_CORE_LOCKING` is enabled, this internally becomes `LOCK_TCPIP_CORE/UNLOCK_TCPIP_CORE`
and when it isn't, it posts a message to the TCPIP task to run the function.

Added CHIP stack locking to the UDP receive function.
  • Loading branch information
rojer committed Sep 4, 2023
1 parent 54038c0 commit 05ac99e
Showing 1 changed file with 99 additions and 89 deletions.
188 changes: 99 additions & 89 deletions src/inet/UDPEndPointImplLwIP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#include <inet/UDPEndPointImplLwIP.h>

#include <functional>

#if CHIP_HAVE_CONFIG_H
#include <lwip/lwip_buildconfig.h> // nogncheck
#endif // CHIP_HAVE_CONFIG_H
Expand All @@ -35,16 +37,14 @@
#include <lwip/ip.h>
#include <lwip/mld6.h>
#include <lwip/netif.h>
#include <lwip/priv/tcpip_priv.h>
#include <lwip/raw.h>
#include <lwip/tcpip.h>
#include <lwip/udp.h>

static_assert(LWIP_VERSION_MAJOR > 1, "CHIP requires LwIP 2.0 or later");
#include <platform/PlatformManager.h>

#if !(CHIP_DEVICE_LAYER_TARGET_BL602 || CHIP_DEVICE_LAYER_TARGET_BL702 || CHIP_DEVICE_LAYER_TARGET_BL702L || \
CHIP_DEVICE_LAYER_TARGET_ASR)
static_assert(LWIP_TCPIP_CORE_LOCKING, "CHIP requires config LWIP_TCPIP_CORE_LOCKING enabled");
#endif
static_assert(LWIP_VERSION_MAJOR > 1, "CHIP requires LwIP 2.0 or later");

#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)
Expand All @@ -67,27 +67,24 @@ static_assert(LWIP_TCPIP_CORE_LOCKING, "CHIP requires config LWIP_TCPIP_CORE_LOC
namespace chip {
namespace Inet {

namespace {
/**
* @brief
* RAII locking for LwIP core to simplify management of
* LOCK_TCPIP_CORE()/UNLOCK_TCPIP_CORE() calls.
*/
class ScopedLwIPLock
EndpointQueueFilter * UDPEndPointImplLwIP::sQueueFilter = nullptr;

struct TCPIPCallContext
{
public:
ScopedLwIPLock() { LOCK_TCPIP_CORE(); }
~ScopedLwIPLock() { UNLOCK_TCPIP_CORE(); }
struct tcpip_api_call_data call;
std::function<err_t()> fn;
};
} // anonymous namespace

EndpointQueueFilter * UDPEndPointImplLwIP::sQueueFilter = nullptr;
err_t RunOnTCPIP(std::function<err_t()> fn)
{
TCPIPCallContext ctx = { .fn = fn };
return tcpip_api_call([](struct tcpip_api_call_data * call) { return reinterpret_cast<TCPIPCallContext *>(call)->fn(); },
&ctx.call);
}

CHIP_ERROR UDPEndPointImplLwIP::BindImpl(IPAddressType addressType, const IPAddress & address, uint16_t port,
InterfaceId interfaceId)
{
ScopedLwIPLock lwipLock;

// Make sure we have the appropriate type of PCB.
CHIP_ERROR res = GetPCB(addressType);

Expand All @@ -100,7 +97,7 @@ CHIP_ERROR UDPEndPointImplLwIP::BindImpl(IPAddressType addressType, const IPAddr

if (res == CHIP_NO_ERROR)
{
res = chip::System::MapErrorLwIP(udp_bind(mUDP, &ipAddr, port));
res = chip::System::MapErrorLwIP(RunOnTCPIP([this, &ipAddr, port]() { return udp_bind(mUDP, &ipAddr, port); }));
}

if (res == CHIP_NO_ERROR)
Expand All @@ -113,10 +110,7 @@ CHIP_ERROR UDPEndPointImplLwIP::BindImpl(IPAddressType addressType, const IPAddr

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.
ScopedLwIPLock lwipLock;

// Make sure we have the appropriate type of PCB.
CHIP_ERROR err = GetPCB(addrType);
Expand All @@ -140,14 +134,15 @@ CHIP_ERROR UDPEndPointImplLwIP::LwIPBindInterface(struct udp_pcb * aUDP, Interfa
}
}

udp_bind_netif(aUDP, netifp);
RunOnTCPIP([aUDP, netifp]() {
udp_bind_netif(aUDP, netifp);
return ERR_OK;
});
return CHIP_NO_ERROR;
}

InterfaceId UDPEndPointImplLwIP::GetBoundInterface() const
{
ScopedLwIPLock lwipLock;

#if HAVE_LWIP_UDP_BIND_NETIF
return InterfaceId(netif_get_by_index(mUDP->netif_idx));
#else
Expand All @@ -162,9 +157,10 @@ uint16_t UDPEndPointImplLwIP::GetBoundPort() const

CHIP_ERROR UDPEndPointImplLwIP::ListenImpl()
{
ScopedLwIPLock lwipLock;

udp_recv(mUDP, LwIPReceiveUDPMessage, this);
RunOnTCPIP([this]() {
udp_recv(mUDP, LwIPReceiveUDPMessage, this);
return ERR_OK;
});
return CHIP_NO_ERROR;
}

Expand All @@ -186,50 +182,47 @@ CHIP_ERROR UDPEndPointImplLwIP::SendMsgImpl(const IPPacketInfo * pktInfo, System
CHIP_ERROR res = CHIP_NO_ERROR;
err_t lwipErr = ERR_VAL;

// Adding a scope here to unlock the LwIP core when the lock is no longer required.
// Make sure we have the appropriate type of PCB based on the destination address.
res = GetPCB(destAddr.Type());
if (res != CHIP_NO_ERROR)
{
ScopedLwIPLock lwipLock;

// Make sure we have the appropriate type of PCB based on the destination address.
res = GetPCB(destAddr.Type());
if (res != CHIP_NO_ERROR)
{
return res;
}
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.
const IPAddress & srcAddr = pktInfo->SrcAddress;
const uint16_t & destPort = pktInfo->DestPort;
const InterfaceId & intfId = pktInfo->Interface;
// 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.
const IPAddress & srcAddr = pktInfo->SrcAddress;
const uint16_t & destPort = pktInfo->DestPort;
const InterfaceId & intfId = pktInfo->Interface;

ip_addr_t lwipSrcAddr = srcAddr.ToLwIPAddr();
ip_addr_t lwipDestAddr = destAddr.ToLwIPAddr();
ip_addr_t lwipSrcAddr = srcAddr.ToLwIPAddr();
ip_addr_t lwipDestAddr = destAddr.ToLwIPAddr();

ip_addr_t boundAddr;
ip_addr_copy(boundAddr, mUDP->local_ip);
ip_addr_t boundAddr;
ip_addr_copy(boundAddr, mUDP->local_ip);

if (!ip_addr_isany(&lwipSrcAddr))
{
ip_addr_copy(mUDP->local_ip, lwipSrcAddr);
}
if (!ip_addr_isany(&lwipSrcAddr))
{
ip_addr_copy(mUDP->local_ip, lwipSrcAddr);
}

lwipErr = RunOnTCPIP([this, &intfId, &msg, &lwipDestAddr, destPort]() {
if (intfId.IsPresent())
{
lwipErr = udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort,
intfId.GetPlatformInterface());
return udp_sendto_if(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort,
intfId.GetPlatformInterface());
}
else
{
lwipErr = udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort);
return udp_sendto(mUDP, System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(msg), &lwipDestAddr, destPort);
}
});

ip_addr_copy(mUDP->local_ip, boundAddr);
}
ip_addr_copy(mUDP->local_ip, boundAddr);

if (lwipErr != ERR_OK)
{
Expand All @@ -241,13 +234,14 @@ CHIP_ERROR UDPEndPointImplLwIP::SendMsgImpl(const IPPacketInfo * pktInfo, System

void UDPEndPointImplLwIP::CloseImpl()
{
ScopedLwIPLock lwipLock;

// 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);
RunOnTCPIP([this]() {
udp_remove(mUDP);
return ERR_OK;
});
mUDP = nullptr;
mLwIPEndPointType = LwIPEndPointType::Unknown;

Expand Down Expand Up @@ -315,12 +309,18 @@ CHIP_ERROR UDPEndPointImplLwIP::GetPCB(IPAddressType addrType)
// Allocate a PCB of the appropriate type.
if (addrType == IPAddressType::kIPv6)
{
mUDP = udp_new_ip_type(IPADDR_TYPE_V6);
RunOnTCPIP([this]() {
mUDP = udp_new_ip_type(IPADDR_TYPE_V6);
return ERR_OK;
});
}
#if INET_CONFIG_ENABLE_IPV4
else if (addrType == IPAddressType::kIPv4)
{
mUDP = udp_new_ip_type(IPADDR_TYPE_V4);
RunOnTCPIP([this]() {
mUDP = udp_new_ip_type(IPADDR_TYPE_V4);
return ERR_OK;
});
}
#endif // INET_CONFIG_ENABLE_IPV4
else
Expand Down Expand Up @@ -370,14 +370,20 @@ void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb
u16_t port)
{
UDPEndPointImplLwIP * ep = static_cast<UDPEndPointImplLwIP *>(arg);

// This
DeviceLayer::PlatformMgr().LockChipStack();

if (ep->mState == State::kClosed)
{
DeviceLayer::PlatformMgr().UnlockChipStack();
return;
}

auto pktInfo = Platform::MakeUnique<IPPacketInfo>();
if (pktInfo.get() == nullptr)
{
DeviceLayer::PlatformMgr().UnlockChipStack();
ChipLogError(Inet, "Cannot allocate packet info");
return;
}
Expand Down Expand Up @@ -415,12 +421,14 @@ void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb
if (filterOutcome != EndpointQueueFilter::FilterOutcome::kAllowPacket)
{
// Logging, if any, should be at the choice of the filter impl at time of filtering.
DeviceLayer::PlatformMgr().UnlockChipStack();
return;
}

// Increase mDelayReleaseCount to delay release of this UDP EndPoint while the HandleDataReceived call is
// pending on it.
ep->mDelayReleaseCount++;
DeviceLayer::PlatformMgr().UnlockChipStack();

CHIP_ERROR err = ep->GetSystemLayer().ScheduleLambda(
[ep, p = System::LwIPPacketBufferView::UnsafeGetLwIPpbuf(buf), pktInfo = pktInfo.get()] {
Expand All @@ -439,6 +447,7 @@ void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb
}
else
{
DeviceLayer::PlatformMgr().LockChipStack();
// On failure to enqueue the processing, we have to tell the filter that
// the packet is basically dequeued, if it tries to keep track of the lifecycle.
if (sQueueFilter != nullptr)
Expand All @@ -448,6 +457,7 @@ void UDPEndPointImplLwIP::LwIPReceiveUDPMessage(void * arg, struct udp_pcb * pcb
}

ep->mDelayReleaseCount--;
DeviceLayer::PlatformMgr().UnlockChipStack();
}
}

Expand Down Expand Up @@ -475,26 +485,25 @@ CHIP_ERROR UDPEndPointImplLwIP::IPv4JoinLeaveMulticastGroupImpl(InterfaceId aInt
{
#if LWIP_IPV4 && LWIP_IGMP
const ip4_addr_t lIPv4Address = aAddress.ToIPv4();
err_t lStatus;

struct netif * lNetif = nullptr;
if (aInterfaceId.IsPresent())
{
ScopedLwIPLock lwipLock;
lNetif = FindNetifFromInterfaceId(aInterfaceId);
VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
}

if (aInterfaceId.IsPresent())
err_t lStatus = RunOnTCPIP([lNetif, &lIPv4Address, join]() {
if (lNetif != nullptr)
{

struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId);
VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE);

lStatus = join ? igmp_joingroup_netif(lNetif, &lIPv4Address) //
: igmp_leavegroup_netif(lNetif, &lIPv4Address);
return join ? igmp_joingroup_netif(lNetif, &lIPv4Address) //
: igmp_leavegroup_netif(lNetif, &lIPv4Address);
}
else
{
lStatus = join ? igmp_joingroup(IP4_ADDR_ANY4, &lIPv4Address) //
: igmp_leavegroup(IP4_ADDR_ANY4, &lIPv4Address);
return join ? igmp_joingroup(IP4_ADDR_ANY4, &lIPv4Address) //
: igmp_leavegroup(IP4_ADDR_ANY4, &lIPv4Address);
}
}
});

if (lStatus == ERR_MEM)
{
Expand All @@ -511,24 +520,25 @@ CHIP_ERROR UDPEndPointImplLwIP::IPv6JoinLeaveMulticastGroupImpl(InterfaceId aInt
{
#ifdef HAVE_IPV6_MULTICAST
const ip6_addr_t lIPv6Address = aAddress.ToIPv6();
err_t lStatus;

struct netif * lNetif = nullptr;
if (aInterfaceId.IsPresent())
{
ScopedLwIPLock lwipLock;
lNetif = FindNetifFromInterfaceId(aInterfaceId);
VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
}

if (aInterfaceId.IsPresent())
err_t lStatus = RunOnTCPIP([lNetif, &lIPv6Address, join]() {
if (lNetif != nullptr)
{
struct netif * const lNetif = FindNetifFromInterfaceId(aInterfaceId);
VerifyOrReturnError(lNetif != nullptr, INET_ERROR_UNKNOWN_INTERFACE);
lStatus = join ? mld6_joingroup_netif(lNetif, &lIPv6Address) //
: mld6_leavegroup_netif(lNetif, &lIPv6Address);
return join ? mld6_joingroup_netif(lNetif, &lIPv6Address) //
: mld6_leavegroup_netif(lNetif, &lIPv6Address);
}
else
{
lStatus = join ? mld6_joingroup(IP6_ADDR_ANY6, &lIPv6Address) //
: mld6_leavegroup(IP6_ADDR_ANY6, &lIPv6Address);
return join ? mld6_joingroup(IP6_ADDR_ANY6, &lIPv6Address) //
: mld6_leavegroup(IP6_ADDR_ANY6, &lIPv6Address);
}
}
});

if (lStatus == ERR_MEM)
{
Expand Down

0 comments on commit 05ac99e

Please sign in to comment.