blob: 381412d0987883491b97f673b6848fa124ab5618 [file] [log] [blame]
/*
* Copyright (c) 2016, 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 "precomp.h"
#include "otNodeApi.tmh"
#define DEBUG_PING 1
#define GUID_FORMAT "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}"
#define GUID_ARG(guid) guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]
typedef DWORD (*fp_otvmpOpenHandle)(_Out_ HANDLE* phandle);
typedef VOID (*fp_otvmpCloseHandle)(_In_ HANDLE handle);
typedef DWORD (*fp_otvmpAddVirtualBus)(_In_ HANDLE handle, _Inout_ ULONG* pBusNumber, _Out_ ULONG* pIfIndex);
typedef DWORD (*fp_otvmpRemoveVirtualBus)(_In_ HANDLE handle, ULONG BusNumber);
typedef DWORD (*fp_otvmpSetAdapterTopologyGuid)(_In_ HANDLE handle, DWORD BusNumber, _In_ const GUID* pTopologyGuid);
typedef void (*fp_otvmpListenerCallback)(_In_opt_ PVOID aContext, _In_ ULONG SourceInterfaceIndex, _In_reads_bytes_(FrameLength) PUCHAR FrameBuffer, _In_ UCHAR FrameLength, _In_ UCHAR Channel);
typedef HANDLE (*fp_otvmpListenerCreate)(_In_ const GUID* pAdapterTopologyGuid);
typedef void (*fp_otvmpListenerDestroy)(_In_opt_ HANDLE pHandle);
typedef void(*fp_otvmpListenerRegister)(_In_ HANDLE pHandle, _In_opt_ fp_otvmpListenerCallback Callback, _In_opt_ PVOID Context);
fp_otvmpOpenHandle otvmpOpenHandle = nullptr;
fp_otvmpCloseHandle otvmpCloseHandle = nullptr;
fp_otvmpAddVirtualBus otvmpAddVirtualBus = nullptr;
fp_otvmpRemoveVirtualBus otvmpRemoveVirtualBus = nullptr;
fp_otvmpSetAdapterTopologyGuid otvmpSetAdapterTopologyGuid = nullptr;
fp_otvmpListenerCreate otvmpListenerCreate = nullptr;
fp_otvmpListenerDestroy otvmpListenerDestroy = nullptr;
fp_otvmpListenerRegister otvmpListenerRegister = nullptr;
HMODULE gVmpModule = nullptr;
HANDLE gVmpHandle = nullptr;
ULONG gNextBusNumber = 1;
GUID gTopologyGuid = {0};
volatile LONG gNumberOfInterfaces = 0;
CRITICAL_SECTION gCS;
vector<otNode*> gNodes;
HANDLE gDeviceArrivalEvent = nullptr;
otApiInstance *gApiInstance = nullptr;
_Success_(return == OT_ERROR_NONE)
otError otNodeParsePrefix(const char *aStrPrefix, _Out_ otIp6Prefix *aPrefix)
{
char *prefixLengthStr;
char *endptr;
if ((prefixLengthStr = (char*)strchr(aStrPrefix, '/')) == NULL)
{
printf("invalid prefix (%s)!\r\n", aStrPrefix);
return OT_ERROR_INVALID_ARGS;
}
*prefixLengthStr++ = '\0';
auto error = otIp6AddressFromString(aStrPrefix, &aPrefix->mPrefix);
if (error != OT_ERROR_NONE)
{
printf("ipaddr (%s) to string failed, 0x%x!\r\n", aStrPrefix, error);
return error;
}
aPrefix->mLength = static_cast<uint8_t>(strtol(prefixLengthStr, &endptr, 0));
if (*endptr != '\0')
{
printf("invalid prefix ending (%s)!\r\n", aStrPrefix);
return OT_ERROR_PARSE;
}
return OT_ERROR_NONE;
}
void OTCALL otNodeDeviceAvailabilityChanged(bool aAdded, const GUID *, void *)
{
if (aAdded) SetEvent(gDeviceArrivalEvent);
}
otApiInstance* GetApiInstance()
{
if (gApiInstance == nullptr)
{
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != 0)
{
printf("WSAStartup failed!\r\n");
return nullptr;
}
gApiInstance = otApiInit();
if (gApiInstance == nullptr)
{
printf("otApiInit failed!\r\n");
Unload();
return nullptr;
}
InitializeCriticalSection(&gCS);
gVmpModule = LoadLibrary(TEXT("otvmpapi.dll"));
if (gVmpModule == nullptr)
{
printf("LoadLibrary(\"otvmpapi\") failed!\r\n");
Unload();
return nullptr;
}
otvmpOpenHandle = (fp_otvmpOpenHandle)GetProcAddress(gVmpModule, "otvmpOpenHandle");
otvmpCloseHandle = (fp_otvmpCloseHandle)GetProcAddress(gVmpModule, "otvmpCloseHandle");
otvmpAddVirtualBus = (fp_otvmpAddVirtualBus)GetProcAddress(gVmpModule, "otvmpAddVirtualBus");
otvmpRemoveVirtualBus = (fp_otvmpRemoveVirtualBus)GetProcAddress(gVmpModule, "otvmpRemoveVirtualBus");
otvmpSetAdapterTopologyGuid = (fp_otvmpSetAdapterTopologyGuid)GetProcAddress(gVmpModule, "otvmpSetAdapterTopologyGuid");
otvmpListenerCreate = (fp_otvmpListenerCreate)GetProcAddress(gVmpModule, "otvmpListenerCreate");
otvmpListenerDestroy = (fp_otvmpListenerDestroy)GetProcAddress(gVmpModule, "otvmpListenerDestroy");
otvmpListenerRegister = (fp_otvmpListenerRegister)GetProcAddress(gVmpModule, "otvmpListenerRegister");
assert(otvmpOpenHandle);
assert(otvmpCloseHandle);
assert(otvmpAddVirtualBus);
assert(otvmpRemoveVirtualBus);
assert(otvmpSetAdapterTopologyGuid);
assert(otvmpListenerCreate);
assert(otvmpListenerDestroy);
assert(otvmpListenerRegister);
if (otvmpOpenHandle == nullptr) printf("otvmpOpenHandle is null!\r\n");
if (otvmpCloseHandle == nullptr) printf("otvmpCloseHandle is null!\r\n");
if (otvmpAddVirtualBus == nullptr) printf("otvmpAddVirtualBus is null!\r\n");
if (otvmpRemoveVirtualBus == nullptr) printf("otvmpRemoveVirtualBus is null!\r\n");
if (otvmpSetAdapterTopologyGuid == nullptr) printf("otvmpSetAdapterTopologyGuid is null!\r\n");
if (otvmpListenerCreate == nullptr) printf("otvmpListenerCreate is null!\r\n");
if (otvmpListenerDestroy == nullptr) printf("otvmpListenerDestroy is null!\r\n");
if (otvmpListenerRegister == nullptr) printf("otvmpListenerRegister is null!\r\n");
(VOID)otvmpOpenHandle(&gVmpHandle);
if (gVmpHandle == nullptr)
{
printf("otvmpOpenHandle failed!\r\n");
Unload();
return nullptr;
}
auto status = UuidCreate(&gTopologyGuid);
if (status != NO_ERROR)
{
printf("UuidCreate failed, 0x%x!\r\n", status);
Unload();
return nullptr;
}
auto offset = getenv("INSTANCE");
if (offset)
{
gNextBusNumber = (atoi(offset) * 32) % 1000 + 1;
}
else
{
srand(gTopologyGuid.Data1);
gNextBusNumber = rand() % 1000 + 1;
}
gDeviceArrivalEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
// Set callback to wait for device arrival notifications
otSetDeviceAvailabilityChangedCallback(gApiInstance, otNodeDeviceAvailabilityChanged, nullptr);
printf("New topology created\r\n" GUID_FORMAT " [%d]\r\n\r\n", GUID_ARG(gTopologyGuid), gNextBusNumber);
}
InterlockedIncrement(&gNumberOfInterfaces);
return gApiInstance;
}
void ReleaseApiInstance()
{
if (0 == InterlockedDecrement(&gNumberOfInterfaces))
{
// Uninitialize everything else if this is the last ref
Unload();
}
}
void Unload()
{
if (gNumberOfInterfaces != 0)
{
printf("Unloaded with %d outstanding nodes!\r\n", gNumberOfInterfaces);
}
if (gApiInstance)
{
otSetDeviceAvailabilityChangedCallback(gApiInstance, nullptr, nullptr);
if (gDeviceArrivalEvent != nullptr)
{
CloseHandle(gDeviceArrivalEvent);
gDeviceArrivalEvent = nullptr;
}
if (gVmpHandle != nullptr)
{
otvmpCloseHandle(gVmpHandle);
gVmpHandle = nullptr;
}
if (gVmpModule != nullptr)
{
FreeLibrary(gVmpModule);
gVmpModule = nullptr;
}
DeleteCriticalSection(&gCS);
otApiFinalize(gApiInstance);
gApiInstance = nullptr;
WSACleanup();
printf("Topology destroyed\r\n");
}
}
int Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength)
{
size_t hexLength = strlen(aHex);
const char *hexEnd = aHex + hexLength;
uint8_t *cur = aBin;
uint8_t numChars = hexLength & 1;
uint8_t byte = 0;
if ((hexLength + 1) / 2 > aBinLength)
{
return -1;
}
while (aHex < hexEnd)
{
if ('A' <= *aHex && *aHex <= 'F')
{
byte |= 10 + (*aHex - 'A');
}
else if ('a' <= *aHex && *aHex <= 'f')
{
byte |= 10 + (*aHex - 'a');
}
else if ('0' <= *aHex && *aHex <= '9')
{
byte |= *aHex - '0';
}
else
{
return -1;
}
aHex++;
numChars++;
if (numChars >= 2)
{
numChars = 0;
*cur++ = byte;
byte = 0;
}
else
{
byte <<= 4;
}
}
return static_cast<int>(cur - aBin);
}
typedef struct otPingHandler
{
otNode* mParentNode;
bool mActive;
otIp6Address mAddress;
SOCKET mSocket;
CHAR mRecvBuffer[1500];
WSAOVERLAPPED mOverlapped;
PTP_WAIT mThreadpoolWait;
WSABUF mWSARecvBuffer;
DWORD mNumBytesReceived;
SOCKADDR_IN6 mSourceAddr6;
int mSourceAddr6Len;
} otPingHandler;
typedef struct otNode
{
uint32_t mId;
DWORD mBusIndex;
DWORD mInterfaceIndex;
otInstance* mInstance;
HANDLE mEnergyScanEvent;
HANDLE mPanIdConflictEvent;
CRITICAL_SECTION mCS;
vector<otPingHandler*> mPingHandlers;
vector<void*> mMemoryToFree;
} otNode;
const char* otDeviceRoleToString(otDeviceRole role)
{
switch (role)
{
case OT_DEVICE_ROLE_DISABLED: return "disabled";
case OT_DEVICE_ROLE_DETACHED: return "detached";
case OT_DEVICE_ROLE_CHILD: return "child";
case OT_DEVICE_ROLE_ROUTER: return "router";
case OT_DEVICE_ROLE_LEADER: return "leader";
default: return "invalid";
}
}
const USHORT CertificationPingPort = htons(12345);
const IN6_ADDR LinkLocalAllNodesAddress = { { 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } };
const IN6_ADDR LinkLocalAllRoutersAddress = { { 0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02 } };
const IN6_ADDR RealmLocalAllNodesAddress = { { 0xFF, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } };
const IN6_ADDR RealmLocalAllRoutersAddress = { { 0xFF, 0x03, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x02 } };
const IN6_ADDR RealmLocalSpecialAddress = { { 0xFF, 0x33, 0, 0x40, 0xfd, 0xde, 0xad, 0, 0xbe, 0xef, 0, 0, 0, 0, 0, 0x01 } };
void
CALLBACK
PingHandlerRecvCallback(
_Inout_ PTP_CALLBACK_INSTANCE /* Instance */,
_Inout_opt_ PVOID Context,
_Inout_ PTP_WAIT /* Wait */,
_In_ TP_WAIT_RESULT /* WaitResult */
)
{
otPingHandler *aPingHandler = (otPingHandler*)Context;
if (aPingHandler == NULL) return;
// Get the result of the IO operation
DWORD cbTransferred = 0;
DWORD dwFlags = 0;
if (!WSAGetOverlappedResult(
aPingHandler->mSocket,
&aPingHandler->mOverlapped,
&cbTransferred,
TRUE,
&dwFlags))
{
int result = WSAGetLastError();
// Only log if we are shutting down
if (result != WSAENOTSOCK && result != ERROR_OPERATION_ABORTED)
printf("WSAGetOverlappedResult failed, 0x%x\r\n", result);
return;
}
int result;
// Make sure it didn't come from our address
if (memcmp(&aPingHandler->mSourceAddr6.sin6_addr, &aPingHandler->mAddress, sizeof(IN6_ADDR)) != 0)
{
bool shouldReply = true;
// TODO - Fix this hack...
auto RecvDest = (const otIp6Address*)aPingHandler->mRecvBuffer;
if (memcmp(RecvDest, &LinkLocalAllRoutersAddress, sizeof(IN6_ADDR)) == 0 ||
memcmp(RecvDest, &RealmLocalAllRoutersAddress, sizeof(IN6_ADDR)) == 0)
{
auto Role = otThreadGetDeviceRole(aPingHandler->mParentNode->mInstance);
if (Role != OT_DEVICE_ROLE_LEADER && Role != OT_DEVICE_ROLE_ROUTER)
shouldReply = false;
}
if (shouldReply)
{
#if DEBUG_PING
CHAR szIpAddress[46] = { 0 };
RtlIpv6AddressToStringA(&aPingHandler->mSourceAddr6.sin6_addr, szIpAddress);
printf("%d: received ping (%d bytes) from %s\r\n", aPingHandler->mParentNode->mId, cbTransferred, szIpAddress);
#endif
// Send the received data back
result =
sendto(
aPingHandler->mSocket,
aPingHandler->mRecvBuffer, cbTransferred, 0,
(SOCKADDR*)&aPingHandler->mSourceAddr6, aPingHandler->mSourceAddr6Len
);
if (result == SOCKET_ERROR)
{
printf("sendto failed, 0x%x\r\n", WSAGetLastError());
}
}
}
// Start the otpool waiting on the overlapped event
SetThreadpoolWait(aPingHandler->mThreadpoolWait, aPingHandler->mOverlapped.hEvent, nullptr);
// Post another recv
dwFlags = MSG_PARTIAL;
aPingHandler->mSourceAddr6Len = sizeof(aPingHandler->mSourceAddr6);
result =
WSARecvFrom(
aPingHandler->mSocket,
&aPingHandler->mWSARecvBuffer, 1, &aPingHandler->mNumBytesReceived, &dwFlags,
(SOCKADDR*)&aPingHandler->mSourceAddr6, &aPingHandler->mSourceAddr6Len,
&aPingHandler->mOverlapped, nullptr
);
if (result != SOCKET_ERROR)
{
// Not pending, so manually trigger the event for the Threadpool to execute
SetEvent(aPingHandler->mOverlapped.hEvent);
}
else
{
result = WSAGetLastError();
if (result != WSA_IO_PENDING)
{
printf("WSARecvFrom failed, 0x%x\r\n", result);
}
}
}
bool IsMeshLocalEID(otNode *aNode, const otIp6Address *aAddress)
{
auto ML_EID = otThreadGetMeshLocalEid(aNode->mInstance);
if (ML_EID == nullptr) return false;
bool result = memcmp(ML_EID->mFields.m8, aAddress->mFields.m8, sizeof(otIp6Address)) == 0;
otFreeMemory(ML_EID);
return result;
}
void AddPingHandler(otNode *aNode, const otIp6Address *aAddress)
{
otPingHandler *aPingHandler = new otPingHandler();
aPingHandler->mParentNode = aNode;
aPingHandler->mAddress = *aAddress;
aPingHandler->mSocket = INVALID_SOCKET;
aPingHandler->mOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
aPingHandler->mWSARecvBuffer = { 1500, aPingHandler->mRecvBuffer };
aPingHandler->mActive = true;
aPingHandler->mThreadpoolWait =
CreateThreadpoolWait(
PingHandlerRecvCallback,
aPingHandler,
nullptr
);
SOCKADDR_IN6 addr6 = { 0 };
addr6.sin6_family = AF_INET6;
addr6.sin6_port = CertificationPingPort;
memcpy(&addr6.sin6_addr, aAddress, sizeof(IN6_ADDR));
#if DEBUG_PING
CHAR szIpAddress[46] = { 0 };
RtlIpv6AddressToStringA(&addr6.sin6_addr, szIpAddress);
printf("%d: starting ping handler for %s\r\n", aNode->mId, szIpAddress);
#endif
// Put the current thead in the correct compartment
bool RevertCompartmentOnExit = false;
ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
if (OriginalCompartmentID != otGetCompartmentId(aNode->mInstance))
{
DWORD dwError = ERROR_SUCCESS;
if ((dwError = SetCurrentThreadCompartmentId(otGetCompartmentId(aNode->mInstance))) != ERROR_SUCCESS)
{
printf("SetCurrentThreadCompartmentId failed, 0x%x\r\n", dwError);
}
RevertCompartmentOnExit = true;
}
int result;
DWORD Flag = FALSE;
IPV6_MREQ MCReg;
MCReg.ipv6mr_interface = otGetDeviceIfIndex(aNode->mInstance);
if (aPingHandler->mOverlapped.hEvent == nullptr ||
aPingHandler->mThreadpoolWait == nullptr)
{
goto exit;
}
// Create the socket
aPingHandler->mSocket = WSASocketW(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (aPingHandler->mSocket == INVALID_SOCKET)
{
printf("WSASocket failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Bind the socket to the address
result = bind(aPingHandler->mSocket, (sockaddr*)&addr6, sizeof(addr6));
if (result == SOCKET_ERROR)
{
printf("bind failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Block our own sends from getting called as receives
result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&Flag, sizeof(Flag));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_MULTICAST_LOOP) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Bind to the multicast addresses
if (IN6_IS_ADDR_LINKLOCAL(&addr6.sin6_addr))
{
// All nodes address
MCReg.ipv6mr_multiaddr = LinkLocalAllNodesAddress;
result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// All routers address
MCReg.ipv6mr_multiaddr = LinkLocalAllRoutersAddress;
result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
}
else if (IsMeshLocalEID(aNode, aAddress))
{
// All nodes address
MCReg.ipv6mr_multiaddr = RealmLocalAllNodesAddress;
result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// All routers address
MCReg.ipv6mr_multiaddr = RealmLocalAllRoutersAddress;
result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Special realm local address
MCReg.ipv6mr_multiaddr = RealmLocalSpecialAddress;
result = setsockopt(aPingHandler->mSocket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&MCReg, sizeof(MCReg));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_ADD_MEMBERSHIP) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
}
// Start the otpool waiting on the overlapped event
SetThreadpoolWait(aPingHandler->mThreadpoolWait, aPingHandler->mOverlapped.hEvent, nullptr);
// Start the receive
Flag = MSG_PARTIAL;
aPingHandler->mSourceAddr6Len = sizeof(aPingHandler->mSourceAddr6);
result =
WSARecvFrom(
aPingHandler->mSocket,
&aPingHandler->mWSARecvBuffer, 1, &aPingHandler->mNumBytesReceived, &Flag,
(SOCKADDR*)&aPingHandler->mSourceAddr6, &aPingHandler->mSourceAddr6Len,
&aPingHandler->mOverlapped, nullptr
);
if (result != SOCKET_ERROR)
{
// Not pending, so manually trigger the event for the Threadpool to execute
SetEvent(aPingHandler->mOverlapped.hEvent);
}
else
{
result = WSAGetLastError();
if (result != WSA_IO_PENDING)
{
printf("WSARecvFrom failed, 0x%x\r\n", result);
goto exit;
}
}
aNode->mPingHandlers.push_back(aPingHandler);
aPingHandler = nullptr;
exit:
// Revert the comparment if necessary
if (RevertCompartmentOnExit)
{
(VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
}
// Clean up ping handler if necessary
if (aPingHandler)
{
if (aPingHandler->mThreadpoolWait != nullptr)
{
if (aPingHandler->mSocket != INVALID_SOCKET)
closesocket(aPingHandler->mSocket);
WaitForThreadpoolWaitCallbacks(aPingHandler->mThreadpoolWait, TRUE);
CloseThreadpoolWait(aPingHandler->mThreadpoolWait);
}
if (aPingHandler->mOverlapped.hEvent)
{
CloseHandle(aPingHandler->mOverlapped.hEvent);
}
delete aPingHandler;
}
}
void HandleAddressChanges(otNode *aNode)
{
otLogFuncEntry();
auto addrs = otIp6GetUnicastAddresses(aNode->mInstance);
EnterCriticalSection(&aNode->mCS);
// Invalidate all handlers
for (ULONG i = 0; i < aNode->mPingHandlers.size(); i++)
aNode->mPingHandlers[i]->mActive = false;
// Search for matches
for (auto addr = addrs; addr; addr = addr->mNext)
{
bool found = false;
for (ULONG i = 0; i < aNode->mPingHandlers.size(); i++)
if (!aNode->mPingHandlers[i]->mActive &&
memcmp(&addr->mAddress, &aNode->mPingHandlers[i]->mAddress, sizeof(otIp6Address)) == 0)
{
found = true;
aNode->mPingHandlers[i]->mActive = true;
break;
}
if (!found) AddPingHandler(aNode, &addr->mAddress);
}
vector<otPingHandler*> pingHandlersToDelete;
// Release all left over handlers
for (int i = aNode->mPingHandlers.size() - 1; i >= 0; i--)
if (aNode->mPingHandlers[i]->mActive == false)
{
auto aPingHandler = aNode->mPingHandlers[i];
#if DEBUG_PING
CHAR szIpAddress[46] = { 0 };
RtlIpv6AddressToStringA((PIN6_ADDR)&aPingHandler->mAddress, szIpAddress);
printf("%d: removing ping handler for %s\r\n", aNode->mId, szIpAddress);
#endif
aNode->mPingHandlers.erase(aNode->mPingHandlers.begin() + i);
shutdown(aPingHandler->mSocket, SD_BOTH);
closesocket(aPingHandler->mSocket);
pingHandlersToDelete.push_back(aPingHandler);
}
LeaveCriticalSection(&aNode->mCS);
for each (auto aPingHandler in pingHandlersToDelete)
{
WaitForThreadpoolWaitCallbacks(aPingHandler->mThreadpoolWait, TRUE);
CloseThreadpoolWait(aPingHandler->mThreadpoolWait);
CloseHandle(aPingHandler->mOverlapped.hEvent);
delete aPingHandler;
}
if (addrs) otFreeMemory(addrs);
otLogFuncExit();
}
void OTCALL otNodeStateChangedCallback(uint32_t aFlags, void *aContext)
{
otLogFuncEntry();
otNode* aNode = (otNode*)aContext;
if ((aFlags & OT_CHANGED_THREAD_ROLE) != 0)
{
auto Role = otThreadGetDeviceRole(aNode->mInstance);
printf("%d: new role: %s\r\n", aNode->mId, otDeviceRoleToString(Role));
}
if ((aFlags & OT_CHANGED_IP6_ADDRESS_ADDED) != 0 || (aFlags & OT_CHANGED_IP6_ADDRESS_REMOVED) != 0 ||
(aFlags & OT_CHANGED_THREAD_RLOC_ADDED) != 0 || (aFlags & OT_CHANGED_THREAD_RLOC_REMOVED) != 0)
{
HandleAddressChanges(aNode);
}
otLogFuncExit();
}
OTNODEAPI int32_t OTCALL otNodeLog(const char *aMessage)
{
LogInfo(OT_API, "%s", aMessage);
return 0;
}
OTNODEAPI otNode* OTCALL otNodeInit(uint32_t id)
{
otLogFuncEntry();
auto ApiInstance = GetApiInstance();
if (ApiInstance == nullptr)
{
printf("GetApiInstance failed!\r\n");
otLogFuncExitMsg("GetApiInstance failed");
return nullptr;
}
bool BusAdded = false;
DWORD newBusIndex;
NET_IFINDEX ifIndex = {};
NET_LUID ifLuid = {};
GUID ifGuid = {};
otNode *node = nullptr;
DWORD dwError;
DWORD tries = 0;
while (tries < 1000)
{
newBusIndex = (gNextBusNumber + tries) % 1000;
if (newBusIndex == 0) newBusIndex++;
dwError = otvmpAddVirtualBus(gVmpHandle, &newBusIndex, &ifIndex);
if (dwError == ERROR_SUCCESS)
{
BusAdded = true;
gNextBusNumber = newBusIndex + 1;
break;
}
else if (dwError == ERROR_INVALID_PARAMETER || dwError == ERROR_FILE_NOT_FOUND)
{
tries++;
}
else
{
printf("otvmpAddVirtualBus failed, 0x%x!\r\n", dwError);
otLogFuncExitMsg("otvmpAddVirtualBus failed");
goto error;
}
}
if (tries == 1000)
{
printf("otvmpAddVirtualBus failed to find an empty bus!\r\n");
otLogFuncExitMsg("otvmpAddVirtualBus failed to find an empty bus");
goto error;
}
if ((dwError = otvmpSetAdapterTopologyGuid(gVmpHandle, newBusIndex, &gTopologyGuid)) != ERROR_SUCCESS)
{
printf("otvmpSetAdapterTopologyGuid failed, 0x%x!\r\n", dwError);
otLogFuncExitMsg("otvmpSetAdapterTopologyGuid failed");
goto error;
}
if (ERROR_SUCCESS != ConvertInterfaceIndexToLuid(ifIndex, &ifLuid))
{
printf("ConvertInterfaceIndexToLuid(%u) failed!\r\n", ifIndex);
otLogFuncExitMsg("ConvertInterfaceIndexToLuid failed");
goto error;
}
if (ERROR_SUCCESS != ConvertInterfaceLuidToGuid(&ifLuid, &ifGuid))
{
printf("ConvertInterfaceLuidToGuid failed!\r\n");
otLogFuncExitMsg("ConvertInterfaceLuidToGuid failed");
goto error;
}
// Keep trying for up to 30 seconds
auto StartTick = GetTickCount64();
otInstance *instance = nullptr;
do
{
instance = otInstanceInit(ApiInstance, &ifGuid);
if (instance != nullptr) break;
auto waitTimeMs = (30 * 1000 - (LONGLONG)(GetTickCount64() - StartTick));
if (waitTimeMs <= 0) break;
auto waitResult = WaitForSingleObject(gDeviceArrivalEvent, (DWORD)waitTimeMs);
if (waitResult != WAIT_OBJECT_0) break;
} while (true);
if (instance == nullptr)
{
printf("otInstanceInit failed!\r\n");
otLogFuncExitMsg("otInstanceInit failed");
goto error;
}
GUID DeviceGuid = otGetDeviceGuid(instance);
uint32_t Compartment = otGetCompartmentId(instance);
node = new otNode();
printf("%d: New Device " GUID_FORMAT " in compartment %d\r\n", id, GUID_ARG(DeviceGuid), Compartment);
node->mId = id;
node->mInterfaceIndex = ifIndex;
node->mBusIndex = newBusIndex;
node->mInstance = instance;
node->mEnergyScanEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
node->mPanIdConflictEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
EnterCriticalSection(&gCS);
gNodes.push_back(node);
LeaveCriticalSection(&gCS);
InitializeCriticalSection(&node->mCS);
// Reset any previously saved settings
otInstanceFactoryReset(instance);
otSetStateChangedCallback(instance, otNodeStateChangedCallback, node);
HandleAddressChanges(node);
otLogFuncExitMsg("success. [%d] = %!GUID!", id, &DeviceGuid);
error:
if (node == nullptr)
{
if (BusAdded)
{
otvmpRemoveVirtualBus(gVmpHandle, newBusIndex);
}
ReleaseApiInstance();
}
return node;
}
OTNODEAPI int32_t OTCALL otNodeFinalize(otNode* aNode)
{
otLogFuncEntry();
if (aNode != nullptr)
{
printf("%d: Removing Device\r\n", aNode->mId);
// Free any memory that we allocated now
for each (auto mem in aNode->mMemoryToFree)
free(mem);
// Clean up callbacks
CloseHandle(aNode->mPanIdConflictEvent);
CloseHandle(aNode->mEnergyScanEvent);
otSetStateChangedCallback(aNode->mInstance, nullptr, nullptr);
EnterCriticalSection(&gCS);
for (uint32_t i = 0; i < gNodes.size(); i++)
{
if (gNodes[i] == aNode)
{
gNodes.erase(gNodes.begin() + i);
break;
}
}
LeaveCriticalSection(&gCS);
// Free the instance
otFreeMemory(aNode->mInstance);
aNode->mInstance = nullptr;
// Free the ping handlers
HandleAddressChanges(aNode);
assert(aNode->mPingHandlers.size() == 0);
if (aNode->mPingHandlers.size() != 0) printf("%d left over ping handlers!!!", (int)aNode->mPingHandlers.size());
DeleteCriticalSection(&aNode->mCS);
// Delete the virtual bus
otvmpRemoveVirtualBus(gVmpHandle, aNode->mBusIndex);
delete aNode;
ReleaseApiInstance();
}
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeSetMode(otNode* aNode, const char *aMode)
{
otLogFuncEntryMsg("[%d] %s", aNode->mId, aMode);
printf("%d: mode %s\r\n", aNode->mId, aMode);
otLinkModeConfig linkMode = {0};
const char *index = aMode;
while (*index)
{
switch (*index)
{
case 'r':
linkMode.mRxOnWhenIdle = true;
break;
case 's':
linkMode.mSecureDataRequests = true;
break;
case 'd':
linkMode.mDeviceType = true;
break;
case 'n':
linkMode.mNetworkData = true;
break;
}
index++;
}
auto result = otThreadSetLinkMode(aNode->mInstance, linkMode);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeInterfaceUp(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: ifconfig up\r\n", aNode->mId);
auto error = otIp6SetEnabled(aNode->mInstance, true);
otLogFuncExit();
return error;
}
OTNODEAPI int32_t OTCALL otNodeInterfaceDown(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: ifconfig down\r\n", aNode->mId);
(void)otIp6SetEnabled(aNode->mInstance, false);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeThreadStart(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: thread start\r\n", aNode->mId);
auto error = otThreadSetEnabled(aNode->mInstance, true);
otLogFuncExit();
return error;
}
OTNODEAPI int32_t OTCALL otNodeThreadStop(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: thread stop\r\n", aNode->mId);
(void)otThreadSetEnabled(aNode->mInstance, false);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeCommissionerStart(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: commissioner start\r\n", aNode->mId);
auto error = otCommissionerStart(aNode->mInstance);
otLogFuncExit();
return error;
}
OTNODEAPI int32_t OTCALL otNodeCommissionerJoinerAdd(otNode* aNode, const char *aExtAddr, const char *aPSKd)
{
otLogFuncEntryMsg("[%d] %s %s", aNode->mId, aExtAddr, aPSKd);
printf("%d: commissioner joiner add %s %s\r\n", aNode->mId, aExtAddr, aPSKd);
const uint32_t kDefaultJoinerTimeout = 120;
otError error;
if (strcmp(aExtAddr, "*") == 0)
{
error = otCommissionerAddJoiner(aNode->mInstance, nullptr, aPSKd, kDefaultJoinerTimeout);
}
else
{
otExtAddress extAddr;
if (Hex2Bin(aExtAddr, extAddr.m8, sizeof(extAddr)) != sizeof(extAddr))
return OT_ERROR_PARSE;
error = otCommissionerAddJoiner(aNode->mInstance, &extAddr, aPSKd, kDefaultJoinerTimeout);
}
otLogFuncExit();
return error;
}
OTNODEAPI int32_t OTCALL otNodeCommissionerStop(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: commissioner stop\r\n", aNode->mId);
(void)otCommissionerStop(aNode->mInstance);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeJoinerStart(otNode* aNode, const char *aPSKd, const char *aProvisioningUrl)
{
otLogFuncEntryMsg("[%d] %s %s", aNode->mId, aPSKd, aProvisioningUrl);
printf("%d: joiner start %s %s\r\n", aNode->mId, aPSKd, aProvisioningUrl);
// TODO: handle the joiner completion callback
auto error = otJoinerStart(aNode->mInstance, aPSKd, aProvisioningUrl, NULL, NULL, NULL, NULL, NULL, NULL);
otLogFuncExit();
return error;
}
OTNODEAPI int32_t OTCALL otNodeJoinerStop(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: joiner stop\r\n", aNode->mId);
(void)otJoinerStop(aNode->mInstance);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeClearWhitelist(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: whitelist clear\r\n", aNode->mId);
otLinkClearWhitelist(aNode->mInstance);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: whitelist enable\r\n", aNode->mId);
otLinkSetWhitelistEnabled(aNode->mInstance, true);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: whitelist disable\r\n", aNode->mId);
otLinkSetWhitelistEnabled(aNode->mInstance, false);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeAddWhitelist(otNode* aNode, const char *aExtAddr, int8_t aRssi)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
if (aRssi == 0)
printf("%d: whitelist add %s\r\n", aNode->mId, aExtAddr);
else printf("%d: whitelist add %s %d\r\n", aNode->mId, aExtAddr, aRssi);
uint8_t extAddr[8];
if (Hex2Bin(aExtAddr, extAddr, sizeof(extAddr)) != sizeof(extAddr))
return OT_ERROR_PARSE;
otError error;
if (aRssi == 0)
{
error = otLinkAddWhitelist(aNode->mInstance, extAddr);
}
else
{
error = otLinkAddWhitelistRssi(aNode->mInstance, extAddr, aRssi);
}
otLogFuncExit();
return error;
}
OTNODEAPI int32_t OTCALL otNodeRemoveWhitelist(otNode* aNode, const char *aExtAddr)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: whitelist remove %s\r\n", aNode->mId, aExtAddr);
uint8_t extAddr[8];
if (Hex2Bin(aExtAddr, extAddr, sizeof(extAddr)) != sizeof(extAddr))
return OT_ERROR_INVALID_ARGS;
otLinkRemoveWhitelist(aNode->mInstance, extAddr);
otLogFuncExit();
return 0;
}
OTNODEAPI uint16_t OTCALL otNodeGetAddr16(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetRloc16(aNode->mInstance);
printf("%d: rloc16\r\n%04x\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI const char* OTCALL otNodeGetHashMacAddress(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
otExtAddress aHashMacAddress = {};
otLinkGetJoinerId(aNode->mInstance, &aHashMacAddress);
char* str = (char*)malloc(18);
if (str != nullptr)
{
aNode->mMemoryToFree.push_back(str);
for (int i = 0; i < 8; i++)
sprintf_s(str + i * 2, 18 - (2 * i), "%02x", aHashMacAddress.m8[i]);
printf("%d: hashmacaddr\r\n%s\r\n", aNode->mId, str);
}
otLogFuncExit();
return str;
}
OTNODEAPI const char* OTCALL otNodeGetAddr64(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto extAddr = otLinkGetExtendedAddress(aNode->mInstance);
char* str = (char*)malloc(18);
if (str != nullptr)
{
aNode->mMemoryToFree.push_back(str);
for (int i = 0; i < 8; i++)
sprintf_s(str + i * 2, 18 - (2 * i), "%02x", extAddr[i]);
printf("%d: extaddr\r\n%s\r\n", aNode->mId, str);
}
otFreeMemory(extAddr);
otLogFuncExit();
return str;
}
OTNODEAPI int32_t OTCALL otNodeSetChannel(otNode* aNode, uint8_t aChannel)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: channel %d\r\n", aNode->mId, aChannel);
auto result = otLinkSetChannel(aNode->mInstance, aChannel);
otLogFuncExit();
return result;
}
OTNODEAPI uint8_t OTCALL otNodeGetChannel(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otLinkGetChannel(aNode->mInstance);
printf("%d: channel\r\n%d\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetMasterkey(otNode* aNode, const char *aMasterkey)
{
otLogFuncEntryMsg("[%d] %s", aNode->mId, aMasterkey);
printf("%d: masterkey %s\r\n", aNode->mId, aMasterkey);
int keyLength;
otMasterKey key;
if ((keyLength = Hex2Bin(aMasterkey, key.m8, sizeof(key.m8))) != OT_MASTER_KEY_SIZE)
{
printf("invalid length key %d\r\n", keyLength);
return OT_ERROR_PARSE;
}
auto error = otThreadSetMasterKey(aNode->mInstance, &key);
otLogFuncExit();
return error;
}
OTNODEAPI const char* OTCALL otNodeGetMasterkey(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto aMasterKey = otThreadGetMasterKey(aNode->mInstance);
uint8_t strLength = 2*sizeof(otMasterKey) + 1;
char* str = (char*)malloc(strLength);
if (str != nullptr)
{
aNode->mMemoryToFree.push_back(str);
for (int i = 0; i < sizeof(otMasterKey); i++)
sprintf_s(str + i * 2, strLength - (2 * i), "%02x", aMasterKey->m8[i]);
printf("%d: masterkey\r\n%s\r\n", aNode->mId, str);
}
otFreeMemory(aMasterKey);
otLogFuncExit();
return str;
}
OTNODEAPI int32_t OTCALL otNodeSetPSKc(otNode* aNode, const char *aPSKc)
{
otLogFuncEntryMsg("[%d] %s", aNode->mId, aPSKc);
printf("%d: pskc %s\r\n", aNode->mId, aPSKc);
uint8_t pskc[OT_PSKC_MAX_SIZE];
if (Hex2Bin(aPSKc, pskc, sizeof(pskc)) != OT_PSKC_MAX_SIZE)
{
printf("invalid pskc %s\r\n", aPSKc);
return OT_ERROR_PARSE;
}
auto error = otThreadSetPSKc(aNode->mInstance, pskc);
otLogFuncExit();
return error;
}
OTNODEAPI const char* OTCALL otNodeGetPSKc(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto aPSKc = otThreadGetPSKc(aNode->mInstance);
uint8_t strLength = 2 * OT_PSKC_MAX_SIZE + 1;
char* str = (char*)malloc(strLength);
if (str != nullptr)
{
aNode->mMemoryToFree.push_back(str);
for (int i = 0; i < OT_PSKC_MAX_SIZE; i++)
sprintf_s(str + i * 2, strLength - (2 * i), "%02x", aPSKc[i]);
printf("%d: pskc\r\n%s\r\n", aNode->mId, str);
}
otFreeMemory(aPSKc);
otLogFuncExit();
return str;
}
OTNODEAPI uint32_t OTCALL otNodeGetKeySequenceCounter(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetKeySequenceCounter(aNode->mInstance);
printf("%d: keysequence\r\n%d\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetKeySequenceCounter(otNode* aNode, uint32_t aSequence)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: keysequence counter %d\r\n", aNode->mId, aSequence);
otThreadSetKeySequenceCounter(aNode->mInstance, aSequence);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeSetKeySwitchGuardTime(otNode* aNode, uint32_t aKeySwitchGuardTime)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: keysequence guardtime %d\r\n", aNode->mId, aKeySwitchGuardTime);
otThreadSetKeySwitchGuardTime(aNode->mInstance, aKeySwitchGuardTime);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeSetNetworkIdTimeout(otNode* aNode, uint8_t aTimeout)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: networkidtimeout %d\r\n", aNode->mId, aTimeout);
otThreadSetNetworkIdTimeout(aNode->mInstance, aTimeout);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeSetNetworkName(otNode* aNode, const char *aName)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: networkname %s\r\n", aNode->mId, aName);
auto result = otThreadSetNetworkName(aNode->mInstance, aName);
otLogFuncExit();
return result;
}
OTNODEAPI const char* OTCALL otNodeGetNetworkName(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetNetworkName(aNode->mInstance);
aNode->mMemoryToFree.push_back((char*)result);
printf("%d: networkname\r\n%s\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI uint16_t OTCALL otNodeGetPanId(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otLinkGetPanId(aNode->mInstance);
printf("%d: panid\r\n0x%04x\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetPanId(otNode* aNode, uint16_t aPanId)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: panid 0x%04x\r\n", aNode->mId, aPanId);
auto result = otLinkSetPanId(aNode->mInstance, aPanId);
otLogFuncExit();
return result;
}
OTNODEAPI uint32_t OTCALL otNodeGetPartitionId(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetLocalLeaderPartitionId(aNode->mInstance);
printf("%d: leaderpartitionid\r\n0x%04x\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetPartitionId(otNode* aNode, uint32_t aPartitionId)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: leaderpartitionid 0x%04x\r\n", aNode->mId, aPartitionId);
otThreadSetLocalLeaderPartitionId(aNode->mInstance, aPartitionId);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeSetRouterUpgradeThreshold(otNode* aNode, uint8_t aThreshold)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: routerupgradethreshold %d\r\n", aNode->mId, aThreshold);
otThreadSetRouterUpgradeThreshold(aNode->mInstance, aThreshold);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeSetRouterDowngradeThreshold(otNode* aNode, uint8_t aThreshold)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: routerdowngradethreshold %d\r\n", aNode->mId, aThreshold);
otThreadSetRouterDowngradeThreshold(aNode->mInstance, aThreshold);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeReleaseRouterId(otNode* aNode, uint8_t aRouterId)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: releaserouterid %d\r\n", aNode->mId, aRouterId);
auto result = otThreadReleaseRouterId(aNode->mInstance, aRouterId);
otLogFuncExit();
return result;
}
OTNODEAPI const char* OTCALL otNodeGetState(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto role = otThreadGetDeviceRole(aNode->mInstance);
auto result = _strdup(otDeviceRoleToString(role));
aNode->mMemoryToFree.push_back(result);
printf("%d: state\r\n%s\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetState(otNode* aNode, const char *aState)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: state %s\r\n", aNode->mId, aState);
otError error;
if (strcmp(aState, "detached") == 0)
{
error = otThreadBecomeDetached(aNode->mInstance);
}
else if (strcmp(aState, "child") == 0)
{
error = otThreadBecomeChild(aNode->mInstance);
}
else if (strcmp(aState, "router") == 0)
{
error = otThreadBecomeRouter(aNode->mInstance);
}
else if (strcmp(aState, "leader") == 0)
{
error = otThreadBecomeLeader(aNode->mInstance);
}
else
{
error = OT_ERROR_INVALID_ARGS;
}
otLogFuncExit();
return error;
}
OTNODEAPI uint32_t OTCALL otNodeGetTimeout(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetChildTimeout(aNode->mInstance);
printf("%d: childtimeout\r\n%d\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetTimeout(otNode* aNode, uint32_t aTimeout)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: childtimeout %d\r\n", aNode->mId, aTimeout);
otThreadSetChildTimeout(aNode->mInstance, aTimeout);
otLogFuncExit();
return 0;
}
OTNODEAPI uint8_t OTCALL otNodeGetWeight(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetLeaderWeight(aNode->mInstance);
printf("%d: leaderweight\r\n%d\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetWeight(otNode* aNode, uint8_t aWeight)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: leaderweight %d\r\n", aNode->mId, aWeight);
otThreadSetLocalLeaderWeight(aNode->mInstance, aWeight);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeAddIpAddr(otNode* aNode, const char *aAddr)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: add ipaddr %s\r\n", aNode->mId, aAddr);
otNetifAddress aAddress;
auto error = otIp6AddressFromString(aAddr, &aAddress.mAddress);
if (error != OT_ERROR_NONE) return error;
aAddress.mPrefixLength = 64;
aAddress.mPreferred = true;
aAddress.mValid = true;
auto result = otIp6AddUnicastAddress(aNode->mInstance, &aAddress);
otLogFuncExit();
return result;
}
inline uint16_t Swap16(uint16_t v)
{
return
(((v & 0x00ffU) << 8) & 0xff00) |
(((v & 0xff00U) >> 8) & 0x00ff);
}
OTNODEAPI const char* OTCALL otNodeGetAddrs(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: ipaddr\r\n", aNode->mId);
auto addrs = otIp6GetUnicastAddresses(aNode->mInstance);
if (addrs == nullptr) return nullptr;
char* str = (char*)malloc(512);
if (str != nullptr)
{
aNode->mMemoryToFree.push_back(str);
RtlZeroMemory(str, 512);
char* cur = str;
for (const otNetifAddress *addr = addrs; addr; addr = addr->mNext)
{
if (cur != str)
{
*cur = '\n';
cur++;
}
auto last = cur;
cur +=
sprintf_s(
cur, 512 - (cur - str),
"%x:%x:%x:%x:%x:%x:%x:%x",
Swap16(addr->mAddress.mFields.m16[0]),
Swap16(addr->mAddress.mFields.m16[1]),
Swap16(addr->mAddress.mFields.m16[2]),
Swap16(addr->mAddress.mFields.m16[3]),
Swap16(addr->mAddress.mFields.m16[4]),
Swap16(addr->mAddress.mFields.m16[5]),
Swap16(addr->mAddress.mFields.m16[6]),
Swap16(addr->mAddress.mFields.m16[7]));
printf("%s\r\n", last);
}
}
otFreeMemory(addrs);
otLogFuncExit();
return str;
}
OTNODEAPI uint32_t OTCALL otNodeGetContextReuseDelay(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
auto result = otThreadGetContextIdReuseDelay(aNode->mInstance);
printf("%d: contextreusedelay\r\n%d\r\n", aNode->mId, result);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetContextReuseDelay(otNode* aNode, uint32_t aDelay)
{
otLogFuncEntryMsg("[%d] %d", aNode->mId, aDelay);
printf("%d: contextreusedelay %d\r\n", aNode->mId, aDelay);
otThreadSetContextIdReuseDelay(aNode->mInstance, aDelay);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeAddPrefix(otNode* aNode, const char *aPrefix, const char *aFlags, const char *aPreference)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: prefix add %s %s %s\r\n", aNode->mId, aPrefix, aFlags, aPreference);
otBorderRouterConfig config = {0};
auto error = otNodeParsePrefix(aPrefix, &config.mPrefix);
if (error != OT_ERROR_NONE) return error;
const char *index = aFlags;
while (*index)
{
switch (*index)
{
case 'p':
config.mPreferred = true;
break;
case 'a':
config.mSlaac = true;
break;
case 'd':
config.mDhcp = true;
break;
case 'c':
config.mConfigure = true;
break;
case 'r':
config.mDefaultRoute = true;
break;
case 'o':
config.mOnMesh = true;
break;
case 's':
config.mStable = true;
break;
default:
return OT_ERROR_INVALID_ARGS;
}
index++;
}
if (strcmp(aPreference, "high") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
}
else if (strcmp(aPreference, "med") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_MED;
}
else if (strcmp(aPreference, "low") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_LOW;
}
else
{
return OT_ERROR_INVALID_ARGS;
}
auto result = otBorderRouterAddOnMeshPrefix(aNode->mInstance, &config);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeRemovePrefix(otNode* aNode, const char *aPrefix)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
otIp6Prefix prefix;
auto error = otNodeParsePrefix(aPrefix, &prefix);
if (error != OT_ERROR_NONE) return error;
auto result = otBorderRouterRemoveOnMeshPrefix(aNode->mInstance, &prefix);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeAddRoute(otNode* aNode, const char *aPrefix, const char *aPreference)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
otExternalRouteConfig config = {0};
auto error = otNodeParsePrefix(aPrefix, &config.mPrefix);
if (error != OT_ERROR_NONE) return error;
if (strcmp(aPreference, "high") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_HIGH;
}
else if (strcmp(aPreference, "med") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_MED;
}
else if (strcmp(aPreference, "low") == 0)
{
config.mPreference = OT_ROUTE_PREFERENCE_LOW;
}
else
{
return OT_ERROR_INVALID_ARGS;
}
auto result = otBorderRouterAddRoute(aNode->mInstance, &config);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeRemoveRoute(otNode* aNode, const char *aPrefix)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
otIp6Prefix prefix;
auto error = otNodeParsePrefix(aPrefix, &prefix);
if (error != OT_ERROR_NONE) return error;
auto result = otBorderRouterRemoveRoute(aNode->mInstance, &prefix);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeRegisterNetdata(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: registernetdata\r\n", aNode->mId);
auto result = otBorderRouterRegister(aNode->mInstance);
otLogFuncExit();
return result;
}
void OTCALL otNodeCommissionerEnergyReportCallback(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, void *aContext)
{
otNode* aNode = (otNode*)aContext;
printf("Energy: 0x%08x\r\n", aChannelMask);
for (uint8_t i = 0; i < aEnergyListLength; i++)
printf("%d ", aEnergyList[i]);
printf("\r\n");
SetEvent(aNode->mEnergyScanEvent);
}
OTNODEAPI int32_t OTCALL otNodeEnergyScan(otNode* aNode, uint32_t aMask, uint8_t aCount, uint16_t aPeriod, uint16_t aDuration, const char *aAddr)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: energy scan 0x%x %d %d %d %s\r\n", aNode->mId, aMask, aCount, aPeriod, aDuration, aAddr);
otIp6Address address = {0};
auto error = otIp6AddressFromString(aAddr, &address);
if (error != OT_ERROR_NONE)
{
printf("otIp6AddressFromString(%s) failed, 0x%x!\r\n", aAddr, error);
return error;
}
ResetEvent(aNode->mEnergyScanEvent);
error = otCommissionerEnergyScan(aNode->mInstance, aMask, aCount, aPeriod, aDuration, &address, otNodeCommissionerEnergyReportCallback, aNode);
if (error != OT_ERROR_NONE)
{
printf("otCommissionerEnergyScan failed, 0x%x!\r\n", error);
return error;
}
auto result = WaitForSingleObject(aNode->mEnergyScanEvent, 8000) == WAIT_OBJECT_0 ? OT_ERROR_NONE : OT_ERROR_NOT_FOUND;
otLogFuncExit();
return result;
}
void OTCALL otNodeCommissionerPanIdConflictCallback(uint16_t aPanId, uint32_t aChannelMask, void *aContext)
{
otNode* aNode = (otNode*)aContext;
printf("Conflict: 0x%04x, 0x%08x\r\n", aPanId, aChannelMask);
SetEvent(aNode->mPanIdConflictEvent);
}
OTNODEAPI int32_t OTCALL otNodePanIdQuery(otNode* aNode, uint16_t aPanId, uint32_t aMask, const char *aAddr)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
printf("%d: panid query 0x%04x 0x%x %s\r\n", aNode->mId, aPanId, aMask, aAddr);
otIp6Address address = {0};
auto error = otIp6AddressFromString(aAddr, &address);
if (error != OT_ERROR_NONE)
{
printf("otIp6AddressFromString(%s) failed, 0x%x!\r\n", aAddr, error);
return error;
}
ResetEvent(aNode->mPanIdConflictEvent);
error = otCommissionerPanIdQuery(aNode->mInstance, aPanId, aMask, &address, otNodeCommissionerPanIdConflictCallback, aNode);
if (error != OT_ERROR_NONE)
{
printf("otCommissionerPanIdQuery failed, 0x%x!\r\n", error);
return error;
}
auto result = WaitForSingleObject(aNode->mPanIdConflictEvent, 8000) == WAIT_OBJECT_0 ? OT_ERROR_NONE : OT_ERROR_NOT_FOUND;
otLogFuncExit();
return result;
}
OTNODEAPI const char* OTCALL otNodeScan(otNode* aNode)
{
otLogFuncEntryMsg("[%d]", aNode->mId);
UNREFERENCED_PARAMETER(aNode);
otLogFuncExit();
return nullptr;
}
OTNODEAPI uint32_t OTCALL otNodePing(otNode* aNode, const char *aAddr, uint16_t aSize, uint32_t aMinReplies, uint16_t aTimeout)
{
otLogFuncEntryMsg("[%d] %s (%d bytes)", aNode->mId, aAddr, aSize);
printf("%d: ping %s (%d bytes)\r\n", aNode->mId, aAddr, aSize);
// Convert string to destination address
otIp6Address otDestinationAddress = {0};
auto error = otIp6AddressFromString(aAddr, &otDestinationAddress);
if (error != OT_ERROR_NONE)
{
printf("otIp6AddressFromString(%s) failed!\r\n", aAddr);
return 0;
}
// Get ML-EID as source address for ping
auto otSourceAddress = otThreadGetMeshLocalEid(aNode->mInstance);
sockaddr_in6 SourceAddress = { AF_INET6, (USHORT)(CertificationPingPort + 1) };
sockaddr_in6 DestinationAddress = { AF_INET6, CertificationPingPort };
memcpy(&SourceAddress.sin6_addr, otSourceAddress, sizeof(IN6_ADDR));
memcpy(&DestinationAddress.sin6_addr, &otDestinationAddress, sizeof(IN6_ADDR));
otFreeMemory(otSourceAddress);
otSourceAddress = nullptr;
// Put the current thead in the correct compartment
bool RevertCompartmentOnExit = false;
ULONG OriginalCompartmentID = GetCurrentThreadCompartmentId();
if (OriginalCompartmentID != otGetCompartmentId(aNode->mInstance))
{
DWORD dwError = ERROR_SUCCESS;
if ((dwError = SetCurrentThreadCompartmentId(otGetCompartmentId(aNode->mInstance))) != ERROR_SUCCESS)
{
printf("SetCurrentThreadCompartmentId failed, 0x%x\r\n", dwError);
}
RevertCompartmentOnExit = true;
}
int result = 0;
auto SendBuffer = (PCHAR)malloc(aSize);
auto RecvBuffer = (PCHAR)malloc(aSize);
WSABUF WSARecvBuffer = { aSize, RecvBuffer };
WSAOVERLAPPED Overlapped = { 0 };
Overlapped.hEvent = WSACreateEvent();
DWORD numberOfReplies = 0;
bool isPending = false;
DWORD Flags;
DWORD cbReceived;
int cbDestinationAddress = sizeof(DestinationAddress);
DWORD hopLimit = 64;
SOCKET Socket = WSASocketW(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, WSA_FLAG_OVERLAPPED);
if (Socket == INVALID_SOCKET)
{
printf("WSASocket failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Bind the socket to the address
result = bind(Socket, (sockaddr*)&SourceAddress, sizeof(SourceAddress));
if (result == SOCKET_ERROR)
{
printf("bind failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Set the multicast hop limit to 64
result = setsockopt(Socket, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hopLimit, sizeof(hopLimit));
if (result == SOCKET_ERROR)
{
printf("setsockopt (IPV6_MULTICAST_HOPS) failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
// Initialize the send buffer pattern.
for (uint32_t i = 0; i < aSize; i++)
SendBuffer[i] = (char)('a' + (i % 23));
// Hack to retrieve destination on other end
memcpy_s(SendBuffer, aSize, &otDestinationAddress, sizeof(IN6_ADDR));
// Send the buffer
result = sendto(Socket, SendBuffer, aSize, 0, (SOCKADDR*)&DestinationAddress, sizeof(DestinationAddress));
if (result == SOCKET_ERROR)
{
printf("sendto failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
auto StartTick = GetTickCount64();
while (numberOfReplies < aMinReplies)
{
Flags = 0; //MSG_PARTIAL;
result = WSARecvFrom(Socket, &WSARecvBuffer, 1, &cbReceived, &Flags, (SOCKADDR*)&DestinationAddress, &cbDestinationAddress, &Overlapped, NULL);
if (result == SOCKET_ERROR)
{
result = WSAGetLastError();
if (result == WSA_IO_PENDING)
{
isPending = true;
}
else
{
printf("WSARecvFrom failed, 0x%x\r\n", result);
goto exit;
}
}
if (isPending)
{
//printf("waiting for completion event...\r\n");
// Wait for the receive to complete
ULONGLONG elapsed = (GetTickCount64() - StartTick);
result = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, (DWORD)(aTimeout - min(aTimeout, elapsed)), TRUE);
if (result == WSA_WAIT_TIMEOUT)
{
//printf("recv timeout\r\n");
goto exit;
}
else if (result == WSA_WAIT_FAILED)
{
printf("recv failed\r\n");
goto exit;
}
}
result = WSAGetOverlappedResult(Socket, &Overlapped, &cbReceived, TRUE, &Flags);
if (result == FALSE)
{
printf("WSAGetOverlappedResult failed, 0x%x\r\n", WSAGetLastError());
goto exit;
}
numberOfReplies++;
}
exit:
// Revert the comparment if necessary
if (RevertCompartmentOnExit)
{
(VOID)SetCurrentThreadCompartmentId(OriginalCompartmentID);
}
free(RecvBuffer);
free(SendBuffer);
WSACloseEvent(Overlapped.hEvent);
if (Socket != INVALID_SOCKET) closesocket(Socket);
otLogFuncExit();
return numberOfReplies;
}
OTNODEAPI int32_t OTCALL otNodeSetRouterSelectionJitter(otNode* aNode, uint8_t aRouterJitter)
{
otLogFuncEntryMsg("[%d] %d", aNode->mId, aRouterJitter);
printf("%d: routerselectionjitter %d\r\n", aNode->mId, aRouterJitter);
otThreadSetRouterSelectionJitter(aNode->mInstance, aRouterJitter);
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otNodeCommissionerAnnounceBegin(otNode* aNode, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, const char *aAddr)
{
otLogFuncEntryMsg("[%d] 0x%08x %d %d %s", aNode->mId, aChannelMask, aCount, aPeriod, aAddr);
printf("%d: commissioner announce 0x%08x %d %d %s\r\n", aNode->mId, aChannelMask, aCount, aPeriod, aAddr);
otIp6Address aAddress;
auto error = otIp6AddressFromString(aAddr, &aAddress);
if (error != OT_ERROR_NONE) return error;
auto result = otCommissionerAnnounceBegin(aNode->mInstance, aChannelMask, aCount, aPeriod, &aAddress);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetActiveDataset(otNode* aNode, uint64_t aTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aMasterKey)
{
otLogFuncEntryMsg("[%d] 0x%llX %d %d", aNode->mId, aTimestamp, aPanId, aChannel);
printf("%d: dataset set active 0x%llX %d %d\r\n", aNode->mId, aTimestamp, aPanId, aChannel);
otOperationalDataset aDataset = {};
aDataset.mActiveTimestamp = aTimestamp;
aDataset.mIsActiveTimestampSet = true;
if (aPanId != 0)
{
aDataset.mPanId = aPanId;
aDataset.mIsPanIdSet = true;
}
if (aChannel != 0)
{
aDataset.mChannel = aChannel;
aDataset.mIsChannelSet = true;
}
if (aChannelMask != 0)
{
aDataset.mChannelMaskPage0 = aChannelMask;
aDataset.mIsChannelMaskPage0Set = true;
}
if (aMasterKey != NULL && strlen(aMasterKey) != 0)
{
int keyLength;
if ((keyLength = Hex2Bin(aMasterKey, aDataset.mMasterKey.m8, sizeof(aDataset.mMasterKey))) != OT_MASTER_KEY_SIZE)
{
printf("invalid length key %d\r\n", keyLength);
return OT_ERROR_PARSE;
}
aDataset.mIsMasterKeySet = true;
}
auto result = otDatasetSetActive(aNode->mInstance, &aDataset);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetPendingDataset(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint16_t aPanId, uint16_t aChannel)
{
otLogFuncEntryMsg("[%d] 0x%llX 0x%llX %d %d", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
printf("%d: dataset set pending 0x%llX 0x%llX %d %d\r\n", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
otOperationalDataset aDataset = {};
if (aActiveTimestamp != 0)
{
aDataset.mActiveTimestamp = aActiveTimestamp;
aDataset.mIsActiveTimestampSet = true;
}
if (aPendingTimestamp != 0)
{
aDataset.mPendingTimestamp = aPendingTimestamp;
aDataset.mIsPendingTimestampSet = true;
}
if (aPanId != 0)
{
aDataset.mPanId = aPanId;
aDataset.mIsPanIdSet = true;
}
if (aChannel != 0)
{
aDataset.mChannel = aChannel;
aDataset.mIsChannelSet = true;
}
auto result = otDatasetSetPending(aNode->mInstance, &aDataset);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSendPendingSet(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint32_t aDelayTimer, uint16_t aPanId, uint16_t aChannel, const char *aMasterKey, const char *aMeshLocal, const char *aNetworkName)
{
otLogFuncEntryMsg("[%d] 0x%llX 0x%llX %d %d", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
printf("%d: dataset send pending 0x%llX 0x%llX %d %d\r\n", aNode->mId, aActiveTimestamp, aPendingTimestamp, aPanId, aChannel);
otOperationalDataset aDataset = {};
if (aActiveTimestamp != 0)
{
aDataset.mActiveTimestamp = aActiveTimestamp;
aDataset.mIsActiveTimestampSet = true;
}
if (aPendingTimestamp != 0)
{
aDataset.mPendingTimestamp = aPendingTimestamp;
aDataset.mIsPendingTimestampSet = true;
}
if (aDelayTimer != 0)
{
aDataset.mDelay = aDelayTimer;
aDataset.mIsDelaySet = true;
}
if (aPanId != 0)
{
aDataset.mPanId = aPanId;
aDataset.mIsPanIdSet = true;
}
if (aChannel != 0)
{
aDataset.mChannel = aChannel;
aDataset.mIsChannelSet = true;
}
if (aMasterKey != NULL && strlen(aMasterKey) != 0)
{
int keyLength;
if ((keyLength = Hex2Bin(aMasterKey, aDataset.mMasterKey.m8, sizeof(aDataset.mMasterKey))) != OT_MASTER_KEY_SIZE)
{
printf("invalid length key %d\r\n", keyLength);
return OT_ERROR_PARSE;
}
aDataset.mIsMasterKeySet = true;
}
if (aMeshLocal != NULL && strlen(aMeshLocal) != 0)
{
otIp6Address prefix;
auto error = otIp6AddressFromString(aMeshLocal, &prefix);
if (error != OT_ERROR_NONE) return error;
memcpy(aDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(aDataset.mMeshLocalPrefix.m8));
aDataset.mIsMeshLocalPrefixSet = true;
}
if (aNetworkName != NULL && strlen(aNetworkName) != 0)
{
strcpy_s(aDataset.mNetworkName.m8, sizeof(aDataset.mNetworkName.m8), aNetworkName);
aDataset.mIsNetworkNameSet = true;
}
auto result = otDatasetSendMgmtPendingSet(aNode->mInstance, &aDataset, nullptr, 0);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSendActiveSet(otNode* aNode, uint64_t aActiveTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aExtPanId, const char *aMasterKey, const char *aMeshLocal, const char *aNetworkName, const char *aBinary)
{
otLogFuncEntryMsg("[%d] 0x%llX %d %d", aNode->mId, aActiveTimestamp, aPanId, aChannel);
printf("%d: dataset send active 0x%llX %d %d\r\n", aNode->mId, aActiveTimestamp, aPanId, aChannel);
otOperationalDataset aDataset = {};
uint8_t tlvs[128];
uint8_t tlvsLength = 0;
if (aActiveTimestamp != 0)
{
aDataset.mActiveTimestamp = aActiveTimestamp;
aDataset.mIsActiveTimestampSet = true;
}
if (aPanId != 0)
{
aDataset.mPanId = aPanId;
aDataset.mIsPanIdSet = true;
}
if (aChannel != 0)
{
aDataset.mChannel = aChannel;
aDataset.mIsChannelSet = true;
}
if (aChannelMask != 0)
{
aDataset.mChannelMaskPage0 = aChannelMask;
aDataset.mIsChannelMaskPage0Set = true;
}
if (aExtPanId != NULL && strlen(aExtPanId) != 0)
{
int keyLength;
if ((keyLength = Hex2Bin(aExtPanId, aDataset.mExtendedPanId.m8, sizeof(aDataset.mExtendedPanId))) != OT_EXT_PAN_ID_SIZE)
{
printf("invalid length ext pan id %d\r\n", keyLength);
return OT_ERROR_PARSE;
}
aDataset.mIsExtendedPanIdSet = true;
}
if (aMasterKey != NULL && strlen(aMasterKey) != 0)
{
int keyLength;
if ((keyLength = Hex2Bin(aMasterKey, aDataset.mMasterKey.m8, sizeof(aDataset.mMasterKey))) != OT_MASTER_KEY_SIZE)
{
printf("invalid length key %d\r\n", keyLength);
return OT_ERROR_PARSE;
}
aDataset.mIsMasterKeySet = true;
}
if (aMeshLocal != NULL && strlen(aMeshLocal) != 0)
{
otIp6Address prefix;
auto error = otIp6AddressFromString(aMeshLocal, &prefix);
if (error != OT_ERROR_NONE) return error;
memcpy(aDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(aDataset.mMeshLocalPrefix.m8));
aDataset.mIsMeshLocalPrefixSet = true;
}
if (aNetworkName != NULL && strlen(aNetworkName) != 0)
{
strcpy_s(aDataset.mNetworkName.m8, sizeof(aDataset.mNetworkName.m8), aNetworkName);
aDataset.mIsNetworkNameSet = true;
}
if (aBinary != NULL && strlen(aBinary) != 0)
{
int length;
if ((length = Hex2Bin(aBinary,tlvs, sizeof(tlvs))) < 0)
{
printf("invalid length tlvs %d\r\n", length);
return OT_ERROR_PARSE;
}
tlvsLength = (uint8_t)length;
}
auto result = otDatasetSendMgmtActiveSet(aNode->mInstance, &aDataset, tlvsLength == 0 ? nullptr : tlvs, tlvsLength);
otLogFuncExit();
return result;
}
OTNODEAPI int32_t OTCALL otNodeSetMaxChildren(otNode* aNode, uint8_t aMaxChildren)
{
otLogFuncEntryMsg("[%d] %d", aNode->mId, aMaxChildren);
printf("%d: childmax %d\r\n", aNode->mId, aMaxChildren);
auto result = otThreadSetMaxAllowedChildren(aNode->mInstance, aMaxChildren);
otLogFuncExit();
return result;
}
typedef struct otMacFrameEntry
{
otMacFrame Frame;
LIST_ENTRY Link;
} otMacFrameEntry;
typedef struct otListener
{
HANDLE mListener;
CRITICAL_SECTION mCS;
HANDLE mStopEvent;
HANDLE mFramesUpdatedEvent;
LIST_ENTRY mFrames; // List of otMacFrameEntry
} otListener;
void
otListenerCallback(
_In_opt_ PVOID aContext,
_In_ ULONG SourceInterfaceIndex,
_In_reads_bytes_(FrameLength) PUCHAR FrameBuffer,
_In_ UCHAR FrameLength,
_In_ UCHAR Channel
)
{
otListener* aListener = (otListener*)aContext;
assert(aListener);
if (FrameLength)
{
otMacFrameEntry* entry = new otMacFrameEntry;
entry->Frame.buffer[0] = Channel;
memcpy_s(entry->Frame.buffer + 1, sizeof(entry->Frame.buffer) - 1, FrameBuffer, FrameLength);
entry->Frame.length = FrameLength + 1;
entry->Frame.nodeid = (uint32_t)-1;
// Look up the Node ID from by interface guid
EnterCriticalSection(&gCS);
for (uint32_t i = 0; i < gNodes.size(); i++)
{
if (gNodes[i]->mInterfaceIndex == SourceInterfaceIndex)
{
entry->Frame.nodeid = gNodes[i]->mId;
break;
}
}
LeaveCriticalSection(&gCS);
// Push the frame on the list to process
EnterCriticalSection(&aListener->mCS);
InsertTailList(&aListener->mFrames, &entry->Link);
LeaveCriticalSection(&aListener->mCS);
// Set event indicating we have a new frame to process
SetEvent(aListener->mFramesUpdatedEvent);
}
}
OTNODEAPI otListener* OTCALL otListenerInit(uint32_t /* nodeid */)
{
otLogFuncEntry();
auto ApiInstance = GetApiInstance();
if (ApiInstance == nullptr)
{
printf("GetApiInstance failed!\r\n");
otLogFuncExitMsg("GetApiInstance failed");
return nullptr;
}
otListener *listener = new otListener();
assert(listener);
InitializeCriticalSection(&listener->mCS);
listener->mStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
listener->mFramesUpdatedEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
InitializeListHead(&listener->mFrames);
// Create the listener
listener->mListener = otvmpListenerCreate(&gTopologyGuid);
if (listener->mListener == nullptr) goto error;
// Register for callbacks
otvmpListenerRegister(listener->mListener, otListenerCallback, listener);
printf("S: Sniffer started\r\n");
error:
// Clean up on failure
if (listener)
{
if (listener->mListener == nullptr)
{
otListenerFinalize(listener);
}
}
otLogFuncExit();
return listener;
}
OTNODEAPI int32_t OTCALL otListenerFinalize(otListener* aListener)
{
otLogFuncEntry();
if (aListener != nullptr)
{
// Set stop event to prevent cancel any pending otListenerRead calls
SetEvent(aListener->mStopEvent);
if (aListener->mListener)
{
// Unregisters (and waits for callbacks to complete) and cleans up the handle
otvmpListenerDestroy(aListener->mListener);
aListener->mListener = nullptr;
// Clean up left over frames
PLIST_ENTRY Link = aListener->mFrames.Flink;
while (Link != &aListener->mFrames)
{
otMacFrameEntry *entry = CONTAINING_RECORD(Link, otMacFrameEntry, Link);
Link = Link->Flink;
delete entry;
}
printf("S: Sniffer stopped\r\n");
}
// Clean up everything else
CloseHandle(aListener->mFramesUpdatedEvent);
aListener->mFramesUpdatedEvent = nullptr;
CloseHandle(aListener->mStopEvent);
aListener->mStopEvent = nullptr;
DeleteCriticalSection(&aListener->mCS);
delete aListener;
ReleaseApiInstance();
}
otLogFuncExit();
return 0;
}
OTNODEAPI int32_t OTCALL otListenerRead(otListener* aListener, otMacFrame *aFrame)
{
do
{
bool exit = false;
EnterCriticalSection(&aListener->mCS);
// If we have a pending frame, return it now
if (!IsListEmpty(&aListener->mFrames))
{
PLIST_ENTRY Link = RemoveHeadList(&aListener->mFrames);
otMacFrameEntry *entry = CONTAINING_RECORD(Link, otMacFrameEntry, Link);
*aFrame = entry->Frame;
delete entry;
exit = true;
}
LeaveCriticalSection(&aListener->mCS);
if (exit) break;
// Wait for the shutdown or frames updated event
auto waitResult = WaitForMultipleObjects(2, &aListener->mStopEvent, FALSE, INFINITE);
if (waitResult == WAIT_OBJECT_0 + 1) // mFramesUpdatedEvent
{
continue;
}
else // mStopEvent
{
return 1;
}
} while (true);
//printf("S: Sniffer read %d bytes from node %d\r\n", aFrame->length, aFrame->nodeid);
return 0;
}