/* * 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 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(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(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 mPingHandlers; vector 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 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); otLinkFilterClearAddresses(aNode->mInstance); otLogFuncExit(); return 0; } OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode) { otLogFuncEntryMsg("[%d]", aNode->mId); printf("%d: whitelist enable\r\n", aNode->mId); otError error = otLinkFilterSetAddressMode(aNode->mInstance, OT_MAC_FILTER_ADDRESS_MODE_WHITELIST); otLogFuncExit(); return error; } OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode) { otLogFuncEntryMsg("[%d]", aNode->mId); printf("%d: whitelist disable\r\n", aNode->mId); otError error = otLinkFilterSetAddressMode(aNode->mInstance, OT_MAC_FILTER_ADDRESS_MODE_DISABLED); otLogFuncExit(); return error; } OTNODEAPI int32_t OTCALL otNodeAddWhitelist(otNode* aNode, const char *aExtAddr, int8_t aRssi = OT_MAC_FILTER_FIXED_RSS_DISABLED) { otLogFuncEntryMsg("[%d]", aNode->mId); otError error = OT_ERROR_NONE; otExtAddress extAddress; if (Hex2Bin(aExtAddr, extAddress.m8, OT_EXT_ADDRESS_SIZE) != OT_EXT_ADDRESS_SIZE) return OT_ERROR_PARSE; printf("%d: whitelist add %s", aNode->mId, aExtAddr); error = otLinkFilterAddAddress(aNode->mInstance, &extAddress); if (error == OT_ERROR_NONE || error == OT_ERROR_ALREADY) { if (aRssi != OT_MAC_FILTER_FIXED_RSS_DISABLED) { error = otLinkFilterAddRssIn(aNode->mInstance, &extAddress, aRssi); printf(" %d", aRssi); } } printf("\r\n"); 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); otExtAddress extAddress; if (Hex2Bin(aExtAddr, extAddress.m8, OT_EXT_ADDRESS_SIZE) != OT_EXT_ADDRESS_SIZE) return OT_ERROR_PARSE; otError error = otLinkFilterRemoveAddress(aNode->mInstance, &extAddress); otLogFuncExit(); return error; } 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 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->m8[i]); printf("%d: extaddr\r\n%s\r\n", aNode->mId, str); } otFreeMemory(extAddr); otLogFuncExit(); return str; } OTNODEAPI const char* OTCALL otNodeGetEui64(otNode* aNode) { otLogFuncEntryMsg("[%d]", aNode->mId); otExtAddress aEui64 = {}; otLinkGetFactoryAssignedIeeeEui64(aNode->mInstance, &aEui64); 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", aEui64.m8[i]); printf("%d: eui64\r\n%s\r\n", aNode->mId, str); } otLogFuncExit(); return str; } OTNODEAPI const char* OTCALL otNodeGetJoinerId(otNode* aNode) { otLogFuncEntryMsg("[%d]", aNode->mId); otExtAddress aJoinerId = {}; otJoinerGetId(aNode->mInstance, &aJoinerId); 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", aJoinerId.m8[i]); printf("%d: joinerid\r\n%s\r\n", aNode->mId, str); } 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); int pskcLength; otPSKc pskc; if ((pskcLength = Hex2Bin(aPSKc, pskc.m8, sizeof(aPSKc))) != OT_PSKC_MAX_SIZE) { printf("invalid length pskd %d\r\n", pskcLength); 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->m8[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.mComponents.mIsActiveTimestampPresent = true; if (aPanId != 0) { aDataset.mPanId = aPanId; aDataset.mComponents.mIsPanIdPresent = true; } if (aChannel != 0) { aDataset.mChannel = aChannel; aDataset.mComponents.mIsChannelPresent = true; } if (aChannelMask != 0) { aDataset.mChannelMask = aChannelMask; aDataset.mComponents.mIsChannelMaskPresent = 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.mComponents.mIsMasterKeyPresent = 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.mComponents.mIsActiveTimestampPresent = true; } if (aPendingTimestamp != 0) { aDataset.mPendingTimestamp = aPendingTimestamp; aDataset.mComponents.mIsPendingTimestampPresent = true; } if (aPanId != 0) { aDataset.mPanId = aPanId; aDataset.mComponents.mIsPanIdPresent = true; } if (aChannel != 0) { aDataset.mChannel = aChannel; aDataset.mComponents.mIsChannelPresent = 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.mComponents.mIsActiveTimestampPresent = true; } if (aPendingTimestamp != 0) { aDataset.mPendingTimestamp = aPendingTimestamp; aDataset.mComponents.mIsPendingTimestampPresent = true; } if (aDelayTimer != 0) { aDataset.mDelay = aDelayTimer; aDataset.mComponents.mIsDelayPresent = true; } if (aPanId != 0) { aDataset.mPanId = aPanId; aDataset.mComponents.mIsPanIdPresent = true; } if (aChannel != 0) { aDataset.mChannel = aChannel; aDataset.mComponents.mIsChannelPresent = 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.mComponents.mIsMasterKeyPresent = 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.mComponents.mIsMeshLocalPrefixPresent = true; } if (aNetworkName != NULL && strlen(aNetworkName) != 0) { strcpy_s(aDataset.mNetworkName.m8, sizeof(aDataset.mNetworkName.m8), aNetworkName); aDataset.mComponents.mIsNetworkNamePresent = 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.mComponents.mIsActiveTimestampPresent = true; } if (aPanId != 0) { aDataset.mPanId = aPanId; aDataset.mComponents.mIsPanIdPresent = true; } if (aChannel != 0) { aDataset.mChannel = aChannel; aDataset.mComponents.mIsChannelPresent = true; } if (aChannelMask != 0) { aDataset.mChannelMask = aChannelMask; aDataset.mComponents.mIsChannelMaskPresent = 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.mComponents.mIsExtendedPanIdPresent = 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.mComponents.mIsMasterKeyPresent = 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.mComponents.mIsMeshLocalPrefixPresent = true; } if (aNetworkName != NULL && strlen(aNetworkName) != 0) { strcpy_s(aDataset.mNetworkName.m8, sizeof(aDataset.mNetworkName.m8), aNetworkName); aDataset.mComponents.mIsNetworkNamePresent = 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; }