| /* |
| * 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; |
| } |