mirror of
https://github.com/espressif/openthread.git
synced 2026-07-22 20:14:07 +00:00
6677bdd762
This fixes issues where the closing of the TLS session or sudden closing of the BLE link did not always lead to the required behavior. Changes: - detail the API for the callback otHandleBleSecureConnect() including valid state combinations and expected moments of calling. - introduce a new internal state kClosing for the BLE link. Tracking the "is closing" state avoids making duplicate platform function calls or duplicate callbacks. - if a TLS handshake is ongoing and the BLE link goes down, mTls state is now also properly cleaned up. - if the TLS session closes (for whatever reason), the BLE link is now also torn down. The 'Disconnect()' call is used to achieve this. When needed, guard time is used to properly close TLS - e.g. sending the Alert and let the BLE link transmit that beforing closing the link. - the simulation BLE platform had an issue that the closing handshake caused the simulated BLE link to be immediately 'connected' again. This is fixed by introducing a state var sIsAdvertising, draining any pending UDP data of the simulated BLE link, and checking sIsAdvertising before accepting a new BLE connection. - avoid calling platform BLE advertisement-enable function while the BleSecure link is still active/closing up. Request BLE advertising again after final disconnect. - code readability improvements. Unit tests are expanded to test more details of the flow.
357 lines
11 KiB
C
357 lines
11 KiB
C
/*
|
|
* Copyright (c) 2023, The OpenThread Authors.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "platform-simulation.h"
|
|
|
|
#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <openthread/error.h>
|
|
#include <openthread/logging.h>
|
|
#include <openthread/tcat.h>
|
|
#include <openthread/platform/ble.h>
|
|
|
|
#include "lib/platform/exit_code.h"
|
|
#include "utils/code_utils.h"
|
|
|
|
#define PLAT_BLE_MSG_DATA_MAX 2048
|
|
static uint8_t sBleBuffer[PLAT_BLE_MSG_DATA_MAX];
|
|
|
|
static int sFd = -1;
|
|
static bool sIsConnected = false;
|
|
static bool sIsDisconnecting = false;
|
|
static bool sIsAdvertising = false;
|
|
static bool sIsEnabled = false;
|
|
|
|
static const uint16_t kPortBase = 10000;
|
|
static uint16_t sPort = 0;
|
|
struct sockaddr_in sSockaddr;
|
|
|
|
static void initFds(void)
|
|
{
|
|
int fd;
|
|
int one = 1;
|
|
struct sockaddr_in sockaddr;
|
|
|
|
memset(&sockaddr, 0, sizeof(sockaddr));
|
|
|
|
sPort = (uint16_t)(kPortBase + gNodeId);
|
|
sockaddr.sin_family = AF_INET;
|
|
sockaddr.sin_port = htons(sPort);
|
|
sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
|
|
otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sFd)"));
|
|
|
|
otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) != -1,
|
|
perror("setsockopt(sFd, SO_REUSEADDR)"));
|
|
otEXPECT_ACTION(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)) != -1,
|
|
perror("setsockopt(sFd, SO_REUSEPORT)"));
|
|
|
|
otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sFd)"));
|
|
|
|
// Fd is successfully initialized.
|
|
sFd = fd;
|
|
|
|
exit:
|
|
if (sFd == -1)
|
|
{
|
|
DieNow(OT_EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void deinitFds(void)
|
|
{
|
|
if (sFd != -1)
|
|
{
|
|
close(sFd);
|
|
sFd = -1;
|
|
}
|
|
}
|
|
|
|
otError otPlatBleGetAdvertisementBuffer(otInstance *aInstance, uint8_t **aAdvertisementBuffer)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
static uint8_t sAdvertisementBuffer[OT_TCAT_ADVERTISEMENT_MAX_LEN];
|
|
|
|
*aAdvertisementBuffer = sAdvertisementBuffer;
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleEnable(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
if (!sIsEnabled)
|
|
{
|
|
initFds();
|
|
sIsEnabled = true;
|
|
sIsConnected = false;
|
|
sIsDisconnecting = false;
|
|
sIsAdvertising = false;
|
|
}
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleDisable(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
if (sIsEnabled)
|
|
{
|
|
deinitFds();
|
|
sIsEnabled = false;
|
|
}
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
if (sIsConnected || !sIsEnabled)
|
|
{
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
otLogDebgPlat("BLE adv start (interval %u)", aInterval);
|
|
sIsAdvertising = true;
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapAdvStop(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
if (!sIsEnabled)
|
|
{
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
otLogDebgPlat("BLE adv stop");
|
|
sIsAdvertising = false;
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapDisconnect(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
if (!sIsConnected)
|
|
{
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
otLogDebgPlat("BLE GAP disconnect");
|
|
// Only flag the disconnection here. The 'disconnected' event is delivered asynchronously
|
|
// by platformBleProcess() (via otPlatBleGapOnDisconnected), per API contract.
|
|
sIsDisconnecting = true;
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
*aMtu = PLAT_BLE_MSG_DATA_MAX - 1;
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aHandle);
|
|
|
|
ssize_t rval;
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
otEXPECT_ACTION(sFd != -1, error = OT_ERROR_INVALID_STATE);
|
|
rval = sendto(sFd, (const char *)aPacket->mValue, aPacket->mLength, 0, (struct sockaddr *)&sSockaddr,
|
|
sizeof(sSockaddr));
|
|
if (rval == -1)
|
|
{
|
|
perror("BLE simulation sendto failed.");
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void platformBleDeinit(void) { deinitFds(); }
|
|
|
|
void platformBleUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd)
|
|
{
|
|
OT_UNUSED_VARIABLE(aWriteFdSet);
|
|
|
|
if (aReadFdSet != NULL && sFd != -1)
|
|
{
|
|
FD_SET(sFd, aReadFdSet);
|
|
|
|
if (aMaxFd != NULL && *aMaxFd < sFd)
|
|
{
|
|
*aMaxFd = sFd;
|
|
}
|
|
}
|
|
|
|
// A pending disconnection must be delivered promptly; ensure the main loop does not block.
|
|
if (sIsDisconnecting && aTimeout != NULL)
|
|
{
|
|
aTimeout->tv_sec = 0;
|
|
aTimeout->tv_usec = 0;
|
|
}
|
|
}
|
|
|
|
void platformBleProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet)
|
|
{
|
|
OT_UNUSED_VARIABLE(aWriteFdSet);
|
|
|
|
otEXPECT(sFd != -1);
|
|
|
|
// Deliver a pending disconnection (requested earlier via otPlatBleGapDisconnect).
|
|
if (sIsDisconnecting)
|
|
{
|
|
// Drain any remaining data and drop it: prevent stale data from triggering a new connection later on.
|
|
while (recvfrom(sFd, sBleBuffer, sizeof(sBleBuffer), MSG_DONTWAIT, NULL, NULL) >= 0)
|
|
{
|
|
}
|
|
|
|
sIsConnected = false;
|
|
sIsDisconnecting = false;
|
|
otPlatBleGapOnDisconnected(aInstance, 0);
|
|
}
|
|
else if (FD_ISSET(sFd, aReadFdSet))
|
|
{
|
|
socklen_t len = sizeof(sSockaddr);
|
|
ssize_t rval;
|
|
memset(&sSockaddr, 0, sizeof(sSockaddr));
|
|
rval = recvfrom(sFd, sBleBuffer, sizeof(sBleBuffer), 0, (struct sockaddr *)&sSockaddr, &len);
|
|
if (rval > 0)
|
|
{
|
|
otBleRadioPacket myPacket;
|
|
|
|
if (!sIsConnected)
|
|
{
|
|
// Only accept a new connection while advertising. This helps to ignore trailing data from a
|
|
// closing TCAT session until the device is ready again for a new session.
|
|
otEXPECT(sIsAdvertising);
|
|
|
|
sIsConnected = true;
|
|
sIsAdvertising = false; // per API contract, advertising stops once a client connects
|
|
otLogDebgPlat("BLE client connected");
|
|
otPlatBleGapOnConnected(aInstance, 0);
|
|
}
|
|
|
|
myPacket.mValue = sBleBuffer;
|
|
myPacket.mLength = (uint16_t)rval;
|
|
myPacket.mPower = 0;
|
|
otPlatBleGattServerOnWriteRequest(
|
|
aInstance, 0,
|
|
&myPacket); // TODO consider passing otPlatBleGattServerOnWriteRequest as a callback function
|
|
}
|
|
else if (rval == 0)
|
|
{
|
|
// A zero-length datagram: simulates Commissioner suddenly disconnected the BLE link.
|
|
if (sIsConnected)
|
|
{
|
|
sIsConnected = false;
|
|
otLogDebgPlat("BLE client link disconnected");
|
|
otPlatBleGapOnDisconnected(aInstance, 0);
|
|
}
|
|
}
|
|
else if (errno != EINTR && errno != EAGAIN)
|
|
{
|
|
perror("recvfrom BLE simulation failed");
|
|
DieNow(OT_EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return;
|
|
}
|
|
|
|
/* Weak stubs for callbacks defined in the FTD/MTD core library, not available for RCP targets. */
|
|
|
|
OT_TOOL_WEAK void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aConnectionId);
|
|
assert(false);
|
|
}
|
|
|
|
OT_TOOL_WEAK void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aConnectionId);
|
|
assert(false);
|
|
}
|
|
|
|
OT_TOOL_WEAK void otPlatBleGattServerOnWriteRequest(otInstance *aInstance,
|
|
uint16_t aHandle,
|
|
const otBleRadioPacket *aPacket)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aHandle);
|
|
OT_UNUSED_VARIABLE(aPacket);
|
|
assert(false);
|
|
}
|
|
|
|
void otPlatBleGetLinkCapabilities(otInstance *aInstance, otBleLinkCapabilities *aBleLinkCapabilities)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
aBleLinkCapabilities->mGattNotifications = 1;
|
|
aBleLinkCapabilities->mL2CapDirect = 0;
|
|
aBleLinkCapabilities->mRsv = 0;
|
|
}
|
|
|
|
otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aAdvertisementData);
|
|
OT_UNUSED_VARIABLE(aAdvertisementLen);
|
|
if (!sIsEnabled || sIsAdvertising)
|
|
{
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatBleGapAdvUpdateData(otInstance *aInstance, uint8_t *aAdvertisementData, uint16_t aAdvertisementLen)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aAdvertisementData);
|
|
OT_UNUSED_VARIABLE(aAdvertisementLen);
|
|
if (!sIsEnabled || !sIsAdvertising)
|
|
{
|
|
return OT_ERROR_INVALID_STATE;
|
|
}
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
bool otPlatBleSupportsMultiRadio(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
return true;
|
|
}
|
|
|
|
#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
|