From 24b98dbb857e03795e603fa07ce729a4b2f001a7 Mon Sep 17 00:00:00 2001 From: Nick Banks Date: Tue, 29 Nov 2016 12:10:34 -0800 Subject: [PATCH] Windows Cert Testing (#943) * PR for cert test interface for Windows driver model. --- .appveyor/install_driver.cmd | 2 +- .appveyor/test_driver.cmd | 4 + etc/visual-studio/UnitTests.vcxproj | 6 - etc/visual-studio/openthread.sln | 30 + etc/visual-studio/ot-cli.vcxproj | 7 +- etc/visual-studio/ot-ncp-spi.vcxproj | 7 +- etc/visual-studio/ot-ncp-uart.vcxproj | 7 +- etc/visual-studio/otApi.vcxproj | 12 +- etc/visual-studio/otLwf.vcxproj | 10 +- etc/visual-studio/otNodeApi.vcxproj | 77 + etc/visual-studio/otNodeApi.vcxproj.filters | 30 + etc/visual-studio/otTestRunner.csproj | 112 + examples/drivers/windows/include/otNode.h | 372 +++ examples/drivers/windows/otApi/otApi.cpp | 11 + .../drivers/windows/otNodeApi/dllmain.cpp | 59 + .../drivers/windows/otNodeApi/otNodeApi.cpp | 2056 +++++++++++++++++ examples/drivers/windows/otNodeApi/precomp.h | 58 + include/openthread-ip6.h | 129 ++ include/openthread.h | 131 +- src/core/net/ip6_mpl.cpp | 4 +- tests/otTestRunner/App.config | 6 + tests/otTestRunner/AssemblyInfo.cs | 64 + tests/otTestRunner/Program.cs | 376 +++ tests/scripts/Makefile.am | 1 + tests/scripts/thread-cert/Test_otLwf.py | 77 + tests/scripts/thread-cert/node.py | 509 +--- tests/scripts/thread-cert/node_api.py | 588 +++++ tests/scripts/thread-cert/node_cli.py | 581 +++++ 28 files changed, 4722 insertions(+), 604 deletions(-) create mode 100644 etc/visual-studio/otNodeApi.vcxproj create mode 100644 etc/visual-studio/otNodeApi.vcxproj.filters create mode 100644 etc/visual-studio/otTestRunner.csproj create mode 100644 examples/drivers/windows/include/otNode.h create mode 100644 examples/drivers/windows/otNodeApi/dllmain.cpp create mode 100644 examples/drivers/windows/otNodeApi/otNodeApi.cpp create mode 100644 examples/drivers/windows/otNodeApi/precomp.h create mode 100644 tests/otTestRunner/App.config create mode 100644 tests/otTestRunner/AssemblyInfo.cs create mode 100644 tests/otTestRunner/Program.cs create mode 100644 tests/scripts/thread-cert/Test_otLwf.py create mode 100644 tests/scripts/thread-cert/node_api.py create mode 100644 tests/scripts/thread-cert/node_cli.py diff --git a/.appveyor/install_driver.cmd b/.appveyor/install_driver.cmd index 36ecb6b1a..73e0742a8 100644 --- a/.appveyor/install_driver.cmd +++ b/.appveyor/install_driver.cmd @@ -36,7 +36,7 @@ certutil -addstore TrustedPublisher otLwf.cer cd otLwf -REM Install the NDSI LWF driver, otLwf.sys +REM Install the NDIS LWF driver, otLwf.sys netcfg.exe -v -l otlwf.inf -c s -i otLwf diff --git a/.appveyor/test_driver.cmd b/.appveyor/test_driver.cmd index 0455577d3..74254195a 100644 --- a/.appveyor/test_driver.cmd +++ b/.appveyor/test_driver.cmd @@ -34,6 +34,10 @@ REM Query the driver state sc query otlwf +REM Run the basic driver test + +otTestRunner.exe ..\tests\scripts\thread-cert Test_otLwf* appveyor + REM Grab the logs mkdir logs diff --git a/etc/visual-studio/UnitTests.vcxproj b/etc/visual-studio/UnitTests.vcxproj index d41ec3bf1..614990728 100644 --- a/etc/visual-studio/UnitTests.vcxproj +++ b/etc/visual-studio/UnitTests.vcxproj @@ -59,12 +59,6 @@ %(AdditionalLibraryDirectories); $(VCInstallDir)UnitTest\lib; - - %(AdditionalDependencies); - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-ncp-uart.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib; - diff --git a/etc/visual-studio/openthread.sln b/etc/visual-studio/openthread.sln index d76e83ddc..8313dcd5d 100644 --- a/etc/visual-studio/openthread.sln +++ b/etc/visual-studio/openthread.sln @@ -48,6 +48,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otLwf", "otLwf.vcxproj", "{ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otApi", "otApi.vcxproj", "{ED0EA262-C222-42C7-98D3-E70C72978ED2}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "otNodeApi", "otNodeApi.vcxproj", "{B7C6F344-7287-4930-AF38-223622BD8CBB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "otTestRunner", "otTestRunner.csproj", "{D5577E51-FA31-4802-8669-1DB32805935E}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenThread", "OpenThread.vcxproj", "{F8C22844-9B93-4978-80DF-8AF2B37A7ABB}" ProjectSection(ProjectDependencies) = postProject {ED0EA262-C222-42C7-98D3-E70C72978ED2} = {ED0EA262-C222-42C7-98D3-E70C72978ED2} @@ -245,6 +249,30 @@ Global {ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x64.Build.0 = Release|x64 {ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x86.ActiveCfg = Release|Win32 {ED0EA262-C222-42C7-98D3-E70C72978ED2}.Release|x86.Build.0 = Release|Win32 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|ARM.ActiveCfg = Debug|ARM + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|ARM.Build.0 = Debug|ARM + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x64.ActiveCfg = Debug|x64 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x64.Build.0 = Debug|x64 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x86.ActiveCfg = Debug|Win32 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Debug|x86.Build.0 = Debug|Win32 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|ARM.ActiveCfg = Release|ARM + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|ARM.Build.0 = Release|ARM + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x64.ActiveCfg = Release|x64 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x64.Build.0 = Release|x64 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x86.ActiveCfg = Release|Win32 + {B7C6F344-7287-4930-AF38-223622BD8CBB}.Release|x86.Build.0 = Release|Win32 + {D5577E51-FA31-4802-8669-1DB32805935E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D5577E51-FA31-4802-8669-1DB32805935E}.Debug|ARM.Build.0 = Debug|Any CPU + {D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x64.ActiveCfg = Debug|x64 + {D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x64.Build.0 = Debug|x64 + {D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x86.ActiveCfg = Debug|Win32 + {D5577E51-FA31-4802-8669-1DB32805935E}.Debug|x86.Build.0 = Debug|Win32 + {D5577E51-FA31-4802-8669-1DB32805935E}.Release|ARM.ActiveCfg = Release|Any CPU + {D5577E51-FA31-4802-8669-1DB32805935E}.Release|ARM.Build.0 = Release|Any CPU + {D5577E51-FA31-4802-8669-1DB32805935E}.Release|x64.ActiveCfg = Release|x64 + {D5577E51-FA31-4802-8669-1DB32805935E}.Release|x64.Build.0 = Release|x64 + {D5577E51-FA31-4802-8669-1DB32805935E}.Release|x86.ActiveCfg = Release|Win32 + {D5577E51-FA31-4802-8669-1DB32805935E}.Release|x86.Build.0 = Release|Win32 {F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|ARM.ActiveCfg = Debug|ARM {F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|ARM.Build.0 = Debug|ARM {F8C22844-9B93-4978-80DF-8AF2B37A7ABB}.Debug|ARM.Deploy.0 = Debug|ARM @@ -282,6 +310,8 @@ Global {9EEF9DCD-EA8F-4154-BD02-AB2B31CEC324} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD} {3F1F7F6C-2A33-4635-9880-08FC5BC4E435} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133} {ED0EA262-C222-42C7-98D3-E70C72978ED2} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133} + {B7C6F344-7287-4930-AF38-223622BD8CBB} = {61E8A4A0-8138-49DB-97B4-3BEC87C8E133} + {D5577E51-FA31-4802-8669-1DB32805935E} = {35F6AB71-5BD2-4D53-8A51-D75CCD4CCC3D} {F8C22844-9B93-4978-80DF-8AF2B37A7ABB} = {76EA541E-CAB1-4DB5-A39F-E3DB2A78CDDD} EndGlobalSection EndGlobal diff --git a/etc/visual-studio/ot-cli.vcxproj b/etc/visual-studio/ot-cli.vcxproj index 10c7073e2..121890ab2 100644 --- a/etc/visual-studio/ot-cli.vcxproj +++ b/etc/visual-studio/ot-cli.vcxproj @@ -52,12 +52,7 @@ Console - %(AdditionalDependencies); - ws2_32.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-cli.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-windows.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib; + mincore.lib; diff --git a/etc/visual-studio/ot-ncp-spi.vcxproj b/etc/visual-studio/ot-ncp-spi.vcxproj index 09ef434fa..cc56e3d19 100644 --- a/etc/visual-studio/ot-ncp-spi.vcxproj +++ b/etc/visual-studio/ot-ncp-spi.vcxproj @@ -52,12 +52,7 @@ Console - %(AdditionalDependencies); - ws2_32.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-ncp-spi.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-windows.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib; + mincore.lib; diff --git a/etc/visual-studio/ot-ncp-uart.vcxproj b/etc/visual-studio/ot-ncp-uart.vcxproj index 9f0cbc99d..c1c8767ab 100644 --- a/etc/visual-studio/ot-ncp-uart.vcxproj +++ b/etc/visual-studio/ot-ncp-uart.vcxproj @@ -52,12 +52,7 @@ Console - %(AdditionalDependencies); - ws2_32.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-ncp-uart.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread-windows.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls.lib; + mincore.lib; diff --git a/etc/visual-studio/otApi.vcxproj b/etc/visual-studio/otApi.vcxproj index a65173ca1..fb7f10050 100644 --- a/etc/visual-studio/otApi.vcxproj +++ b/etc/visual-studio/otApi.vcxproj @@ -39,7 +39,6 @@ Level3 %(PreprocessorDefinitions); - WIN32;_WINDOWS;_USRDLL; OTAPI_EXPORTS; true @@ -53,17 +52,8 @@ ..\..\include\platform\logging-windows.h otApi true - Sync - Sync + Sync - - - onecore.lib; - onecoreuap.lib; - mincore.lib; - ntdll.lib; - - diff --git a/etc/visual-studio/otLwf.vcxproj b/etc/visual-studio/otLwf.vcxproj index 315961910..116e7ef95 100644 --- a/etc/visual-studio/otLwf.vcxproj +++ b/etc/visual-studio/otLwf.vcxproj @@ -1,6 +1,6 @@  - + {3F1F7F6C-2A33-4635-9880-08FC5BC4E435} {8b1800b9-d017-4029-9785-13ef5e5b328e} @@ -76,8 +76,6 @@ netio.lib; uuid.lib; cng.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\libopenthread_k.lib; - ..\..\build\bin\$(Platform)\$(Configuration)\lib\mbedtls_k.lib @@ -88,6 +86,7 @@ + ..;.;%(AdditionalIncludeDirectories) @@ -98,9 +97,6 @@ - - - @@ -109,6 +105,8 @@ + + diff --git a/etc/visual-studio/otNodeApi.vcxproj b/etc/visual-studio/otNodeApi.vcxproj new file mode 100644 index 000000000..ec3f11940 --- /dev/null +++ b/etc/visual-studio/otNodeApi.vcxproj @@ -0,0 +1,77 @@ + + + + + {B7C6F344-7287-4930-AF38-223622BD8CBB} + Win32Proj + Thread + 12.0 + Universal + + + + DynamicLibrary + true + WindowsApplicationForDrivers10.0 + + + DynamicLibrary + false + WindowsApplicationForDrivers10.0 + true + + + + + + + + + + ..\..\build\obj\$(Platform)\$(Configuration)\$(ProjectName)\ + ..\..\build\bin\$(Platform)\$(Configuration)\dll\ + true + false + + + + + %(PreprocessorDefinitions); + OTAPI_EXPORTS; + + + %(AdditionalIncludeDirectories); + ..\..\examples\drivers\windows\include; + ..\..\examples\drivers\windows\otApi; + ..\..\include; + + Level3 + true + true + ..\..\include\platform\logging-windows.h + otNodeApi + true + Sync + + + + ntdll.lib; + + + + + + + + + + + + + {ed0ea262-c222-42c7-98d3-e70c72978ed2} + + + + + + \ No newline at end of file diff --git a/etc/visual-studio/otNodeApi.vcxproj.filters b/etc/visual-studio/otNodeApi.vcxproj.filters new file mode 100644 index 000000000..8e527aeea --- /dev/null +++ b/etc/visual-studio/otNodeApi.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/etc/visual-studio/otTestRunner.csproj b/etc/visual-studio/otTestRunner.csproj new file mode 100644 index 000000000..958403786 --- /dev/null +++ b/etc/visual-studio/otTestRunner.csproj @@ -0,0 +1,112 @@ + + + + + Debug + AnyCPU + {D5577E51-FA31-4802-8669-1DB32805935E} + Exe + Properties + otTestRunner + otTestRunner + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\bin\$(Platform)\$(Configuration)\exe\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\bin\$(Platform)\$(Configuration)\exe\ + TRACE + prompt + 4 + + + true + DEBUG;TRACE + full + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\bin\$(Platform)\$(Configuration)\exe\ + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + TRACE + true + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\bin\$(Platform)\$(Configuration)\exe\ + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + true + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\bin\$(Platform)\$(Configuration)\exe\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\obj\$(Platform)\$(Configuration)\otTestRunner\ + ..\..\build\bin\$(Platform)\$(Configuration)\exe\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/drivers/windows/include/otNode.h b/examples/drivers/windows/include/otNode.h new file mode 100644 index 000000000..175c2d30e --- /dev/null +++ b/examples/drivers/windows/include/otNode.h @@ -0,0 +1,372 @@ +/* + * 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. + */ + +/** + * @file + * @brief + * This file defines a node interface for openthread.h to be used for certification tests + */ + +#ifndef OTNODE_H_ +#define OTNODE_H_ + +#include + +#ifndef OTNODEAPI +#define OTNODEAPI __declspec(dllimport) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Represents a virtual node for an openthread interface + */ +typedef struct otNode otNode; + +/** + * Logs a WPP message + */ +OTNODEAPI int32_t OTCALL otNodeLog(const char *aMessage); + +/** + * Allocates a new virtual node + */ +OTNODEAPI otNode* OTCALL otNodeInit(uint32_t id); + +/** + * Frees a node + */ +OTNODEAPI int32_t OTCALL otNodeFinalize(otNode* aNode); + +/** + * Sets the link mode + */ +OTNODEAPI int32_t OTCALL otNodeSetMode(otNode* aNode, const char *aMode); + +/** + * Starts the thread interface + */ +OTNODEAPI int32_t OTCALL otNodeInterfaceUp(otNode* aNode); + +/** + * Stops the thread interface + */ +OTNODEAPI int32_t OTCALL otNodeInterfaceDown(otNode* aNode); + +/** + * Starts the thread logic + */ +OTNODEAPI int32_t OTCALL otNodeThreadStart(otNode* aNode); + +/** + * Stops the thread logic + */ +OTNODEAPI int32_t OTCALL otNodeThreadStop(otNode* aNode); + +/** + * Starts the commissioner logic + */ +OTNODEAPI int32_t OTCALL otNodeCommissionerStart(otNode* aNode); + +/** + * Adds a new joiner to the list for commissioning + */ +OTNODEAPI int32_t OTCALL otNodeCommissionerJoinerAdd(otNode* aNode, const char *aExtAddr, const char *aPSKd); + +/** + * Stops the commissioner logic + */ +OTNODEAPI int32_t OTCALL otNodeCommissionerStop(otNode* aNode); + +/** + * Starts the joiner logic + */ +OTNODEAPI int32_t OTCALL otNodeJoinerStart(otNode* aNode, const char *aPSKd, const char *aProvisioningUrl); + +/** + * Clears the node's whitelist + */ +OTNODEAPI int32_t OTCALL otNodeClearWhitelist(otNode* aNode); + +/** + * Enables the node's whitelist + */ +OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode); + +/** + * Disables the node's whitelist + */ +OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode); + +/** + * Adds an entry to the node's whitelist + */ +OTNODEAPI int32_t OTCALL otNodeAddWhitelist(otNode* aNode, const char *aExtAddr, int8_t aRssi); + +/** + * Removes an entry to the node's whitelist + */ +OTNODEAPI int32_t OTCALL otNodeRemoveWhitelist(otNode* aNode, const char *aExtAddr); + +/** + * Gets the node's short mac address (Rloc16) + */ +OTNODEAPI uint16_t OTCALL otNodeGetAddr16(otNode* aNode); + +/** + * Gets the node's extended mac address + */ +OTNODEAPI const char* OTCALL otNodeGetAddr64(otNode* aNode); + +/** + * Gets the node's hash mac address + */ +OTNODEAPI const char* OTCALL otNodeGetHashMacAddress(otNode* aNode); + +/** + * Sets the channel for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetChannel(otNode* aNode, uint8_t aChannel); + +/** + * Gets the channel for the node + */ +OTNODEAPI uint8_t OTCALL otNodeGetChannel(otNode* aNode); + +/** + * sets the node's master key + */ +OTNODEAPI int32_t OTCALL otNodeSetMasterkey(otNode* aNode, const char *aMasterkey); + +/** + * Gets the node's master key + */ +OTNODEAPI const char* OTCALL otNodeGetMasterkey(otNode* aNode); + +/** + * Gets the key sequence counter for the node + */ +OTNODEAPI uint32_t OTCALL otNodeGetKeySequenceCounter(otNode* aNode); + +/** + * Sets the key sequence counter for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetKeySequenceCounter(otNode* aNode, uint32_t aSequence); + +/** + * Sets the key switch guard time for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetKeySwitchGuardTime(otNode* aNode, uint32_t aSequence); + +/** + * Sets the network id timeout for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetNetworkIdTimeout(otNode* aNode, uint8_t aTimeout); + +/** + * Gets the network name for the node + */ +OTNODEAPI const char* OTCALL otNodeGetNetworkName(otNode* aNode); + +/** + * Sets the network name for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetNetworkName(otNode* aNode, const char *aName); + +/** + * Gets the pan id for the node + */ +OTNODEAPI uint16_t OTCALL otNodeGetPanId(otNode* aNode); + +/** + * Sets the pan id for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetPanId(otNode* aNode, uint16_t aPanId); + +/** + * Gets the partition id for the node + */ +OTNODEAPI uint32_t OTCALL otNodeGetPartitionId(otNode* aNode); + +/** + * Sets the partition id for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetPartitionId(otNode* aNode, uint32_t aPartitionId); + +/** + * Sets the router upgrade threshold for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetRouterUpgradeThreshold(otNode* aNode, uint8_t aThreshold); + +/** + * Sets the router downgrade threshold for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetRouterDowngradeThreshold(otNode* aNode, uint8_t aThreshold); + +/** + * Releases a router id for the node + */ +OTNODEAPI int32_t OTCALL otNodeReleaseRouterId(otNode* aNode, uint8_t aRouterId); + +/** + * Gets the node's state + */ +OTNODEAPI const char* OTCALL otNodeGetState(otNode* aNode); + +/** + * Sets the node's state + */ +OTNODEAPI int32_t OTCALL otNodeSetState(otNode* aNode, const char *aState); + +/** + * Gets the child timeout for the node + */ +OTNODEAPI uint32_t OTCALL otNodeGetTimeout(otNode* aNode); + +/** + * Sets the child timeout for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetTimeout(otNode* aNode, uint32_t aTimeout); + +/** + * Gets the leader weight for the node + */ +OTNODEAPI uint8_t OTCALL otNodeGetWeight(otNode* aNode); + +/** + * Sets the leader weight for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetWeight(otNode* aNode, uint8_t aWeight); + +/** + * Adds an IP address for the node + */ +OTNODEAPI int32_t OTCALL otNodeAddIpAddr(otNode* aNode, const char *aAddr); + +/** + * Gets the IP address for the node + */ +OTNODEAPI const char* OTCALL otNodeGetAddrs(otNode* aNode); + +/** + * Gets the context reuse delay for the node + */ +OTNODEAPI uint32_t OTCALL otNodeGetContextReuseDelay(otNode* aNode); + +/** + * Sets the context reuse delay for the node + */ +OTNODEAPI int32_t OTCALL otNodeSetContextReuseDelay(otNode* aNode, uint32_t aDelay); + +/** + * Adds an IP prefix for the node + */ +OTNODEAPI int32_t OTCALL otNodeAddPrefix(otNode* aNode, const char *aPrefix, const char *aFlags, const char *aPreference); + +/** + * Removes an IP prefix from the node + */ +OTNODEAPI int32_t OTCALL otNodeRemovePrefix(otNode* aNode, const char *aPrefix); + +/** + * Adds an IP route for the node + */ +OTNODEAPI int32_t OTCALL otNodeAddRoute(otNode* aNode, const char *aPrefix, const char *aPreference); + +/** + * Removes an IP route from the node + */ +OTNODEAPI int32_t OTCALL otNodeRemoveRoute(otNode* aNode, const char *aPrefix); + +/** + * Registers the net data for the node + */ +OTNODEAPI int32_t OTCALL otNodeRegisterNetdata(otNode* aNode); + +/** + * Performs an energy scan for the node + */ +OTNODEAPI int32_t OTCALL otNodeEnergyScan(otNode* aNode, uint32_t aMask, uint8_t aCount, uint16_t aPeriod, uint16_t aDuration, const char *aAddr); + +/** + * Performs a panid query for the node + */ +OTNODEAPI int32_t OTCALL otNodePanIdQuery(otNode* aNode, uint16_t aPanId, uint32_t aMask, const char *aAddr); + +/** + * Performs an scan for the node + */ +OTNODEAPI const char* OTCALL otNodeScan(otNode* aNode); + +/** + * Performs an scan for the node + */ +OTNODEAPI uint32_t OTCALL otNodePing(otNode* aNode, const char *aAddr, uint16_t aSize, uint32_t aMinReplies); + +/** + * Sets the router selection jitter value for a node + */ +OTNODEAPI int32_t OTCALL otNodeSetRouterSelectionJitter(otNode* aNode, uint8_t aRouterJitter); + +/** + * Sends the announce message for a node + */ +OTNODEAPI int32_t OTCALL otNodeCommissionerAnnounceBegin(otNode* aNode, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, const char *aAddr); + +/** + * Sets the active dataset for a node + */ +OTNODEAPI int32_t OTCALL otNodeSetActiveDataset(otNode* aNode, uint64_t aTimestamp, uint16_t aPanId, uint16_t aChannel, uint32_t aChannelMask, const char *aMasterKey); + +/** + * Sets the pending dataset for a node + */ +OTNODEAPI int32_t OTCALL otNodeSetPendingDataset(otNode* aNode, uint64_t aActiveTimestamp, uint64_t aPendingTimestamp, uint16_t aPanId, uint16_t aChannel); + +/** + * Sends a pending set for a node + */ +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); + +/** + * Sends a active set for a node + */ +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); + +/** + * Sets the maximum number of children for a node + */ +OTNODEAPI int32_t OTCALL otNodeSetMaxChildren(otNode* aNode, uint8_t aMaxChildren); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OTNODE_H_ diff --git a/examples/drivers/windows/otApi/otApi.cpp b/examples/drivers/windows/otApi/otApi.cpp index be7595169..015b905c3 100644 --- a/examples/drivers/windows/otApi/otApi.cpp +++ b/examples/drivers/windows/otApi/otApi.cpp @@ -2837,6 +2837,17 @@ otPlatformReset( if (aInstance) (void)SetIOCTL(aInstance, IOCTL_OTLWF_OT_PLATFORM_RESET); } +OTAPI +void +OTCALL +otFactoryReset( + _In_ otInstance *aInstance + ) +{ + UNREFERENCED_PARAMETER(aInstance); + // TODO ... +} + OTAPI ThreadError OTCALL diff --git a/examples/drivers/windows/otNodeApi/dllmain.cpp b/examples/drivers/windows/otNodeApi/dllmain.cpp new file mode 100644 index 000000000..beae2f684 --- /dev/null +++ b/examples/drivers/windows/otNodeApi/dllmain.cpp @@ -0,0 +1,59 @@ +/* + * 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 "dllmain.tmh" + +BOOL +__stdcall +DllMain( + HINSTANCE hinstDll, + DWORD dwReason, + LPVOID /* lpvReserved */ + ) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDll); + WPP_INIT_TRACING(L"otNodeApi"); + break; + + case DLL_PROCESS_DETACH: + Unload(); + WPP_CLEANUP(); + break; + + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + + return TRUE; +} + diff --git a/examples/drivers/windows/otNodeApi/otNodeApi.cpp b/examples/drivers/windows/otNodeApi/otNodeApi.cpp new file mode 100644 index 000000000..74a29b4de --- /dev/null +++ b/examples/drivers/windows/otNodeApi/otNodeApi.cpp @@ -0,0 +1,2056 @@ +/* + * 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); + +fp_otvmpOpenHandle otvmpOpenHandle = nullptr; +fp_otvmpCloseHandle otvmpCloseHandle = nullptr; +fp_otvmpAddVirtualBus otvmpAddVirtualBus = nullptr; +fp_otvmpRemoveVirtualBus otvmpRemoveVirtualBus = nullptr; +fp_otvmpSetAdapterTopologyGuid otvmpSetAdapterTopologyGuid = nullptr; + +HMODULE gVmpModule = nullptr; +HANDLE gVmpHandle = nullptr; + +ULONG gNextBusNumber = 1; +GUID gTopologyGuid = {0}; + +volatile LONG gNumberOfInterfaces = 0; + +otApiInstance *gApiInstance = nullptr; + +_Success_(return == kThreadError_None) +ThreadError 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 kThreadError_InvalidArgs; + } + + *prefixLengthStr++ = '\0'; + + auto error = otIp6AddressFromString(aStrPrefix, &aPrefix->mPrefix); + if (error != kThreadError_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 kThreadError_Parse; + } + + return kThreadError_None; +} + +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"); + return nullptr; + } + + gVmpModule = LoadLibrary(TEXT("otvmpapi.dll")); + if (gVmpModule == nullptr) + { + printf("LoadLibrary(\"otvmpapi\") failed!\r\n"); + 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"); + + assert(otvmpOpenHandle); + assert(otvmpCloseHandle); + assert(otvmpAddVirtualBus); + assert(otvmpRemoveVirtualBus); + assert(otvmpSetAdapterTopologyGuid); + + 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"); + + (VOID)otvmpOpenHandle(&gVmpHandle); + if (gVmpHandle == nullptr) + { + printf("otvmpOpenHandle failed!\r\n"); + return nullptr; + } + + auto status = UuidCreate(&gTopologyGuid); + if (status != NO_ERROR) + { + printf("UuidCreate failed, 0x%x!\r\n", status); + return nullptr; + } + + auto offset = getenv("INSTANCE"); + if (offset) + { + gNextBusNumber = (atoi(offset) * 32) % 1000 + 1; + } + else + { + srand(gTopologyGuid.Data1); + gNextBusNumber = rand() % 1000 + 1; + } + + printf("New topology created\r\n" GUID_FORMAT " [%d]\r\n\r\n", GUID_ARG(gTopologyGuid), gNextBusNumber); + } + + return gApiInstance; +} + +void Unload() +{ + if (gNumberOfInterfaces != 0) + { + printf("Unloaded with %d outstanding nodes!\r\n", gNumberOfInterfaces); + } + + if (gApiInstance) + { + if (gVmpHandle != nullptr) + { + otvmpCloseHandle(gVmpHandle); + gVmpHandle = nullptr; + } + + if (gVmpModule != nullptr) + { + CloseHandle(gVmpModule); + gVmpModule = nullptr; + } + + 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; + otInstance* mInstance; + HANDLE mEnergyScanEvent; + HANDLE mPanIdConflictEvent; + CRITICAL_SECTION mCS; + vector mPingHandlers; + vector mMemoryToFree; +} otNode; + +const char* otDeviceRoleToString(otDeviceRole role) +{ + switch (role) + { + case kDeviceRoleOffline: return "offline"; + case kDeviceRoleDisabled: return "disabled"; + case kDeviceRoleDetached: return "detached"; + case kDeviceRoleChild: return "child"; + case kDeviceRoleRouter: return "router"; + case kDeviceRoleLeader: 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 = otGetDeviceRole(aPingHandler->mParentNode->mInstance); + if (Role != kDeviceRoleLeader && Role != kDeviceRoleRouter) + 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 = otGetMeshLocalEid(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 = otGetUnicastAddresses(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_NET_ROLE) != 0) + { + auto Role = otGetDeviceRole(aNode->mInstance); + printf("%d: new role: %s\r\n", aNode->mId, otDeviceRoleToString(Role)); + } + + if ((aFlags & OT_IP6_ADDRESS_ADDED) != 0 || (aFlags & OT_IP6_ADDRESS_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; + } + + DWORD newBusIndex; + NET_IFINDEX ifIndex = {}; + + 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) + { + 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"); + return nullptr; + } + } + + if (tries == 1000) + { + printf("otvmpAddVirtualBus failed to find an empty bus!\r\n"); + otLogFuncExitMsg("otvmpAddVirtualBus failed to find an empty bus"); + return nullptr; + } + + if ((dwError = otvmpSetAdapterTopologyGuid(gVmpHandle, newBusIndex, &gTopologyGuid)) != ERROR_SUCCESS) + { + printf("otvmpSetAdapterTopologyGuid failed, 0x%x!\r\n", dwError); + otLogFuncExitMsg("otvmpSetAdapterTopologyGuid failed"); + otvmpRemoveVirtualBus(gVmpHandle, newBusIndex); + return nullptr; + } + + NET_LUID ifLuid = {}; + if (ERROR_SUCCESS != ConvertInterfaceIndexToLuid(ifIndex, &ifLuid)) + { + printf("ConvertInterfaceIndexToLuid(%u) failed!\r\n", ifIndex); + otLogFuncExitMsg("ConvertInterfaceIndexToLuid failed"); + otvmpRemoveVirtualBus(gVmpHandle, newBusIndex); + return nullptr; + } + + GUID ifGuid = {}; + if (ERROR_SUCCESS != ConvertInterfaceLuidToGuid(&ifLuid, &ifGuid)) + { + printf("ConvertInterfaceLuidToGuid failed!\r\n"); + otLogFuncExitMsg("ConvertInterfaceLuidToGuid failed"); + otvmpRemoveVirtualBus(gVmpHandle, newBusIndex); + return nullptr; + } + + auto instance = otInstanceInit(ApiInstance, &ifGuid); + if (instance == nullptr) + { + printf("otInstanceInit failed!\r\n"); + otLogFuncExitMsg("otInstanceInit failed"); + otvmpRemoveVirtualBus(gVmpHandle, newBusIndex); + return nullptr; + } + + InterlockedIncrement(&gNumberOfInterfaces); + + GUID DeviceGuid = otGetDeviceGuid(instance); + uint32_t Compartment = otGetCompartmentId(instance); + + otNode *node = new otNode(); + printf("%d: New Device " GUID_FORMAT " in compartment %d\r\n", id, GUID_ARG(DeviceGuid), Compartment); + + node->mId = id; + node->mBusIndex = newBusIndex; + node->mInstance = instance; + + node->mEnergyScanEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + node->mPanIdConflictEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + + InitializeCriticalSection(&node->mCS); + + otSetStateChangedCallback(instance, otNodeStateChangedCallback, node); + + HandleAddressChanges(node); + + otLogFuncExitMsg("success. [%d] = %!GUID!", id, &DeviceGuid); + + 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); + + // 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; + + if (0 == InterlockedDecrement(&gNumberOfInterfaces)) + { + // Uninitialize everything else if this is the last ref + Unload(); + } + } + 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 = otSetLinkMode(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 = otInterfaceUp(aNode->mInstance); + + otLogFuncExit(); + return error; +} + +OTNODEAPI int32_t OTCALL otNodeInterfaceDown(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + printf("%d: ifconfig down\r\n", aNode->mId); + + (void)otInterfaceDown(aNode->mInstance); + + otLogFuncExit(); + return 0; +} + +OTNODEAPI int32_t OTCALL otNodeThreadStart(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + printf("%d: thread start\r\n", aNode->mId); + + auto error = otThreadStart(aNode->mInstance); + + otLogFuncExit(); + return error; +} + +OTNODEAPI int32_t OTCALL otNodeThreadStop(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + printf("%d: thread stop\r\n", aNode->mId); + + (void)otThreadStop(aNode->mInstance); + + 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); + + ThreadError error; + + if (strcmp(aExtAddr, "*") == 0) + { + error = otCommissionerAddJoiner(aNode->mInstance, nullptr, aPSKd); + } + else + { + otExtAddress extAddr; + if (Hex2Bin(aExtAddr, extAddr.m8, sizeof(extAddr)) != sizeof(extAddr)) + return kThreadError_Parse; + + error = otCommissionerAddJoiner(aNode->mInstance, &extAddr, aPSKd); + } + + 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); + + auto error = otJoinerStart(aNode->mInstance, aPSKd, aProvisioningUrl); + + 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); + + otClearMacWhitelist(aNode->mInstance); + otLogFuncExit(); + return 0; +} + +OTNODEAPI int32_t OTCALL otNodeEnableWhitelist(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + printf("%d: whitelist enable\r\n", aNode->mId); + + otEnableMacWhitelist(aNode->mInstance); + otLogFuncExit(); + return 0; +} + +OTNODEAPI int32_t OTCALL otNodeDisableWhitelist(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + printf("%d: whitelist disable\r\n", aNode->mId); + + otDisableMacWhitelist(aNode->mInstance); + 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 kThreadError_Parse; + + ThreadError error; + if (aRssi == 0) + { + error = otAddMacWhitelist(aNode->mInstance, extAddr); + } + else + { + error = otAddMacWhitelistRssi(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 kThreadError_InvalidArgs; + + otRemoveMacWhitelist(aNode->mInstance, extAddr); + otLogFuncExit(); + return 0; +} + +OTNODEAPI uint16_t OTCALL otNodeGetAddr16(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetRloc16(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 = {}; + otGetHashMacAddress(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 = otGetExtendedAddress(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 = otSetChannel(aNode->mInstance, aChannel); + otLogFuncExit(); + return result; +} + +OTNODEAPI uint8_t OTCALL otNodeGetChannel(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetChannel(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; + uint8_t key[OT_MASTER_KEY_SIZE]; + if ((keyLength = Hex2Bin(aMasterkey, key, sizeof(key))) != OT_MASTER_KEY_SIZE) + { + printf("invalid length key %d\r\n", keyLength); + return kThreadError_Parse; + } + + auto error = otSetMasterKey(aNode->mInstance, key, (uint8_t)keyLength); + otLogFuncExit(); + return error; +} + +OTNODEAPI const char* OTCALL otNodeGetMasterkey(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + uint8_t aKeyLength = 0; + auto aMasterKey = otGetMasterKey(aNode->mInstance, &aKeyLength); + uint8_t strLength = 2*aKeyLength + 1; + char* str = (char*)malloc(strLength); + if (str != nullptr) + { + aNode->mMemoryToFree.push_back(str); + for (int i = 0; i < aKeyLength; i++) + sprintf_s(str + i * 2, strLength - (2 * i), "%02x", aMasterKey[i]); + printf("%d: masterkey\r\n%s\r\n", aNode->mId, str); + } + otFreeMemory(aMasterKey); + otLogFuncExit(); + return str; +} + +OTNODEAPI uint32_t OTCALL otNodeGetKeySequenceCounter(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetKeySequenceCounter(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); + otSetKeySequenceCounter(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); + otSetKeySwitchGuardTime(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); + otSetNetworkIdTimeout(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 = otSetNetworkName(aNode->mInstance, aName); + otLogFuncExit(); + return result; +} + +OTNODEAPI const char* OTCALL otNodeGetNetworkName(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetNetworkName(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 = otGetPanId(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 = otSetPanId(aNode->mInstance, aPanId); + otLogFuncExit(); + return result; +} + +OTNODEAPI uint32_t OTCALL otNodeGetPartitionId(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetLocalLeaderPartitionId(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); + otSetLocalLeaderPartitionId(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); + otSetRouterUpgradeThreshold(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); + otSetRouterDowngradeThreshold(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 = otReleaseRouterId(aNode->mInstance, aRouterId); + otLogFuncExit(); + return result; +} + +OTNODEAPI const char* OTCALL otNodeGetState(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto role = otGetDeviceRole(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); + + ThreadError error; + if (strcmp(aState, "detached") == 0) + { + error = otBecomeDetached(aNode->mInstance); + } + else if (strcmp(aState, "child") == 0) + { + error = otBecomeChild(aNode->mInstance, kMleAttachAnyPartition); + } + else if (strcmp(aState, "router") == 0) + { + error = otBecomeRouter(aNode->mInstance); + } + else if (strcmp(aState, "leader") == 0) + { + error = otBecomeLeader(aNode->mInstance); + } + else + { + error = kThreadError_InvalidArgs; + } + otLogFuncExit(); + return error; +} + +OTNODEAPI uint32_t OTCALL otNodeGetTimeout(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetChildTimeout(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); + otSetChildTimeout(aNode->mInstance, aTimeout); + otLogFuncExit(); + return 0; +} + +OTNODEAPI uint8_t OTCALL otNodeGetWeight(otNode* aNode) +{ + otLogFuncEntryMsg("[%d]", aNode->mId); + auto result = otGetLeaderWeight(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); + otSetLocalLeaderWeight(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 != kThreadError_None) return error; + + aAddress.mPrefixLength = 64; + aAddress.mPreferredLifetime = 0xffffffff; + aAddress.mValidLifetime = 0xffffffff; + auto result = otAddUnicastAddress(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 = otGetUnicastAddresses(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 = otGetContextIdReuseDelay(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); + otSetContextIdReuseDelay(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 != kThreadError_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 kThreadError_InvalidArgs; + } + + index++; + } + + if (strcmp(aPreference, "high") == 0) + { + config.mPreference = 1; + } + else if (strcmp(aPreference, "med") == 0) + { + config.mPreference = 1; + } + else if (strcmp(aPreference, "low") == 0) + { + config.mPreference = -1; + } + else + { + return kThreadError_InvalidArgs; + } + + auto result = otAddBorderRouter(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 != kThreadError_None) return error; + + auto result = otRemoveBorderRouter(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 != kThreadError_None) return error; + + if (strcmp(aPreference, "high") == 0) + { + config.mPreference = 1; + } + else if (strcmp(aPreference, "med") == 0) + { + config.mPreference = 1; + } + else if (strcmp(aPreference, "low") == 0) + { + config.mPreference = -1; + } + else + { + return kThreadError_InvalidArgs; + } + + auto result = otAddExternalRoute(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 != kThreadError_None) return error; + + auto result = otRemoveExternalRoute(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 = otSendServerData(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 != kThreadError_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 != kThreadError_None) + { + printf("otCommissionerEnergyScan failed, 0x%x!\r\n", error); + return error; + } + + auto result = WaitForSingleObject(aNode->mEnergyScanEvent, 8000) == WAIT_OBJECT_0 ? kThreadError_None : kThreadError_NotFound; + 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 != kThreadError_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 != kThreadError_None) + { + printf("otCommissionerPanIdQuery failed, 0x%x!\r\n", error); + return error; + } + + auto result = WaitForSingleObject(aNode->mPanIdConflictEvent, 8000) == WAIT_OBJECT_0 ? kThreadError_None : kThreadError_NotFound; + 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) +{ + 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 != kThreadError_None) + { + printf("otIp6AddressFromString(%s) failed!\r\n", aAddr); + return 0; + } + + // Get ML-EID as source address for ping + auto otSourceAddress = otGetMeshLocalEid(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 + result = WSAWaitForMultipleEvents(1, &Overlapped.hEvent, TRUE, (DWORD)(2000 - (GetTickCount64() - StartTick)), 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); + otSetRouterSelectionJitter(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 != kThreadError_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 kThreadError_Parse; + } + aDataset.mIsMasterKeySet = true; + } + + auto result = otSetActiveDataset(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 = otSetPendingDataset(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 kThreadError_Parse; + } + aDataset.mIsMasterKeySet = true; + } + + if (aMeshLocal != NULL && strlen(aMeshLocal) != 0) + { + otIp6Address prefix; + auto error = otIp6AddressFromString(aMeshLocal, &prefix); + if (error != kThreadError_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 = otSendPendingSet(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 kThreadError_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 kThreadError_Parse; + } + aDataset.mIsMasterKeySet = true; + } + + if (aMeshLocal != NULL && strlen(aMeshLocal) != 0) + { + otIp6Address prefix; + auto error = otIp6AddressFromString(aMeshLocal, &prefix); + if (error != kThreadError_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 kThreadError_Parse; + } + tlvsLength = (uint8_t)length; + } + + auto result = otSendActiveSet(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 = otSetMaxAllowedChildren(aNode->mInstance, aMaxChildren); + otLogFuncExit(); + return result; +} diff --git a/examples/drivers/windows/otNodeApi/precomp.h b/examples/drivers/windows/otNodeApi/precomp.h new file mode 100644 index 000000000..d234355ca --- /dev/null +++ b/examples/drivers/windows/otNodeApi/precomp.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#pragma once + +#define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Define to export necessary functions +#define OTDLL +#define OTNODEAPI EXTERN_C __declspec(dllexport) + +#include +#include +#include +#include +#include + +void Unload(); diff --git a/include/openthread-ip6.h b/include/openthread-ip6.h index 63904d77a..c7fba834e 100644 --- a/include/openthread-ip6.h +++ b/include/openthread-ip6.h @@ -138,6 +138,135 @@ ThreadError otSetLinkPromiscuous(otInstance *aInstance, bool aPromiscuous); * */ +/** + * Get the list of IPv6 multicast addresses subscribed to the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the first Network Interface Multicast Address. + */ +const otNetifMulticastAddress *otGetMulticastAddresses(otInstance *aInstance); + +/** + * Subscribe the Thread interface to a Network Interface Multicast Address. + * + * The passed in instance @p aAddress will be copied by the Thread interface. The Thread interface only + * supports a fixed number of externally added multicast addresses. See OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aAddress A pointer to an IP Address. + * + * @retval kThreadErrorNone Successfully subscribed to the Network Interface Multicast Address. + * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is invalid address. + * @retval kThreadError_NoBufs The Network Interface is already storing the maximum allowed external multicast addresses. + */ +ThreadError otSubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); + +/** + * Unsubscribe the Thread interface to a Network Interface Multicast Address. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aAddress A pointer to an IP Address. + * + * @retval kThreadErrorNone Successfully unsubscribed to the Network Interface Multicast Address. + * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is an internal address. + * @retval kThreadError_NotFound The IP Address indicated by @p aAddress was not found. + */ +ThreadError otUnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); + +/** + * Check if multicast promiscuous mode is enabled on the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otEnableMulticastPromiscuousMode + * @sa otDisableMulticastPromiscuousMode + */ +bool otIsMulticastPromiscuousModeEnabled(otInstance *aInstance); + +/** + * Enable multicast promiscuous mode on the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otIsMulticastPromiscuousModeEnabled + * @sa otDisableMulticastPromiscuousMode + */ +void otEnableMulticastPromiscuousMode(otInstance *aInstance); + +/** + * Disable multicast promiscuous mode on the Thread interface. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otIsMulticastPromiscuousModeEnabled + * @sa otEnableMulticastPromiscuousMode + */ +void otDisableMulticastPromiscuousMode(otInstance *aInstance); + +/** + * This function pointer is called to create IPv6 IID during SLAAC procedure. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. + * @param[inout] aContext A pointer to creator-specific context. + * + * @retval kThreadError_None Created valid IID for given IPv6 address. + * @retval kThreadError_Ipv6AddressCreationFailure Creation of valid IID for given IPv6 address failed. + * + */ +typedef ThreadError(*otSlaacIidCreate)(otInstance *aInstance, otNetifAddress *aAddress, void *aContext); + +/** + * Update all automatically created IPv6 addresses for prefixes from current Network Data with SLAAC procedure. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[inout] aAddresses A pointer to an array of automatically created IPv6 addresses. + * @param[in] aNumAddresses The number of slots in aAddresses array. + * @param[in] aIidCreate A pointer to a function that is called to create IPv6 IIDs. + * @param[in] aContext A pointer to data passed to aIidCreate function. + * + */ +void otSlaacUpdate(otInstance *aInstance, otNetifAddress *aAddresses, uint32_t aNumAddresses, + otSlaacIidCreate aIidCreate, void *aContext); + +/** + * Create random IID for given IPv6 address. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. + * @param[in] aContext A pointer to unused data. + * + * @retval kThreadError_None Created valid IID for given IPv6 address. + * + */ +ThreadError otCreateRandomIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); + +/** + * Create IID for given IPv6 address using extended MAC address. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. + * @param[in] aContext A pointer to unused data. + * + * @retval kThreadError_None Created valid IID for given IPv6 address. + * + */ +ThreadError otCreateMacIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); + +/** + * Create semantically opaque IID for given IPv6 address. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. + * @param[inout] aContext A pointer to a otSemanticallyOpaqueIidGeneratorData structure. + * + * @retval kThreadError_None Created valid IID for given IPv6 address. + * @retval kThreadError_Ipv6AddressCreationFailure Could not create valid IID for given IPv6 address. + * + */ +ThreadError otCreateSemanticallyOpaqueIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); + /** * Allocate a new message buffer for sending an IPv6 message. * diff --git a/include/openthread.h b/include/openthread.h index 60b1c3702..ed0ed46cc 100644 --- a/include/openthread.h +++ b/include/openthread.h @@ -858,135 +858,6 @@ OTAPI ThreadError OTCALL otAddUnicastAddress(otInstance *aInstance, const otNeti */ OTAPI ThreadError OTCALL otRemoveUnicastAddress(otInstance *aInstance, const otIp6Address *aAddress); -/** - * Get the list of IPv6 multicast addresses subscribed to the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * - * @returns A pointer to the first Network Interface Multicast Address. - */ -const otNetifMulticastAddress *otGetMulticastAddresses(otInstance *aInstance); - -/** - * Subscribe the Thread interface to a Network Interface Multicast Address. - * - * The passed in instance @p aAddress will be copied by the Thread interface. The Thread interface only - * supports a fixed number of externally added multicast addresses. See OPENTHREAD_CONFIG_MAX_EXT_MULTICAST_IP_ADDRS. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aAddress A pointer to an IP Address. - * - * @retval kThreadErrorNone Successfully subscribed to the Network Interface Multicast Address. - * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is invalid address. - * @retval kThreadError_NoBufs The Network Interface is already storing the maximum allowed external multicast addresses. - */ -ThreadError otSubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); - -/** - * Unsubscribe the Thread interface to a Network Interface Multicast Address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aAddress A pointer to an IP Address. - * - * @retval kThreadErrorNone Successfully unsubscribed to the Network Interface Multicast Address. - * @retval kThreadError_InvalidArgs The IP Address indicated by @p aAddress is an internal address. - * @retval kThreadError_NotFound The IP Address indicated by @p aAddress was not found. - */ -ThreadError otUnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); - -/** - * Check if multicast promiscuous mode is enabled on the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * - * @sa otEnableMulticastPromiscuousMode - * @sa otDisableMulticastPromiscuousMode - */ -bool otIsMulticastPromiscuousModeEnabled(otInstance *aInstance); - -/** - * Enable multicast promiscuous mode on the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * - * @sa otIsMulticastPromiscuousModeEnabled - * @sa otDisableMulticastPromiscuousMode - */ -void otEnableMulticastPromiscuousMode(otInstance *aInstance); - -/** - * Disable multicast promiscuous mode on the Thread interface. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * - * @sa otIsMulticastPromiscuousModeEnabled - * @sa otEnableMulticastPromiscuousMode - */ -void otDisableMulticastPromiscuousMode(otInstance *aInstance); - -/** - * This function pointer is called to create IPv6 IID during SLAAC procedure. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * @param[inout] aContext A pointer to creator-specific context. - * - * @retval kThreadError_None Created valid IID for given IPv6 address. - * @retval kThreadError_Ipv6AddressCreationFailure Creation of valid IID for given IPv6 address failed. - * - */ -typedef ThreadError(*otSlaacIidCreate)(otInstance *aInstance, otNetifAddress *aAddress, void *aContext); - -/** - * Update all automatically created IPv6 addresses for prefixes from current Network Data with SLAAC procedure. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddresses A pointer to an array of automatically created IPv6 addresses. - * @param[in] aNumAddresses The number of slots in aAddresses array. - * @param[in] aIidCreate A pointer to a function that is called to create IPv6 IIDs. - * @param[in] aContext A pointer to data passed to aIidCreate function. - * - */ -void otSlaacUpdate(otInstance *aInstance, otNetifAddress *aAddresses, uint32_t aNumAddresses, - otSlaacIidCreate aIidCreate, void *aContext); - -/** - * Create random IID for given IPv6 address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * @param[in] aContext A pointer to unused data. - * - * @retval kThreadError_None Created valid IID for given IPv6 address. - * - */ -ThreadError otCreateRandomIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); - -/** - * Create IID for given IPv6 address using extended MAC address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * @param[in] aContext A pointer to unused data. - * - * @retval kThreadError_None Created valid IID for given IPv6 address. - * - */ -ThreadError otCreateMacIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); - -/** - * Create semantically opaque IID for given IPv6 address. - * - * @param[in] aInstance A pointer to an OpenThread instance. - * @param[inout] aAddress A pointer to structure containing IPv6 address for which IID is being created. - * @param[inout] aContext A pointer to a otSemanticallyOpaqueIidGeneratorData structure. - * - * @retval kThreadError_None Created valid IID for given IPv6 address. - * @retval kThreadError_Ipv6AddressCreationFailure Could not create valid IID for given IPv6 address. - * - */ -ThreadError otCreateSemanticallyOpaqueIid(otInstance *aInstance, otNetifAddress *aAddresses, void *aContext); - /** * This function pointer is called to notify certain configuration or state changes within OpenThread. * @@ -1802,7 +1673,7 @@ OTAPI void OTCALL otPlatformReset(otInstance *aInstance); * * @param[in] aInstance A pointer to an OpenThread instance. */ -void otFactoryReset(otInstance *aInstance); +OTAPI void OTCALL otFactoryReset(otInstance *aInstance); /** * Get the ROUTER_DOWNGRADE_THRESHOLD parameter used in the Router role. diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index b9ea61193..2eda0fdd8 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -164,7 +164,7 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque Message *messageCopy = NULL; MplBufferedMessageMetadata messageMetadata; uint32_t nextTransmissionTime; - uint8_t hopLimit; + uint8_t hopLimit = 0; VerifyOrExit(GetTimerExpirations() > 0,); VerifyOrExit((messageCopy = aMessage.Clone()) != NULL, error = kThreadError_NoBufs); @@ -172,7 +172,7 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque if (!aIsOutbound) { aMessage.Read(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit); - VerifyOrExit(--hopLimit > 0, error = kThreadError_Drop); + VerifyOrExit(hopLimit-- > 1, error = kThreadError_Drop); messageCopy->Write(Header::GetHopLimitOffset(), Header::GetHopLimitSize(), &hopLimit); } diff --git a/tests/otTestRunner/App.config b/tests/otTestRunner/App.config new file mode 100644 index 000000000..88fa4027b --- /dev/null +++ b/tests/otTestRunner/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/otTestRunner/AssemblyInfo.cs b/tests/otTestRunner/AssemblyInfo.cs new file mode 100644 index 000000000..c3f2b7cd3 --- /dev/null +++ b/tests/otTestRunner/AssemblyInfo.cs @@ -0,0 +1,64 @@ +/* + * 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. + */ + + using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("otTestRunner")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("otTestRunner")] +[assembly: AssemblyCopyright("Copyright © The OpenThread Authors 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d5577e51-fa31-4802-8669-1db32805935e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tests/otTestRunner/Program.cs b/tests/otTestRunner/Program.cs new file mode 100644 index 000000000..f1dddce80 --- /dev/null +++ b/tests/otTestRunner/Program.cs @@ -0,0 +1,376 @@ +/* + * 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. + */ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace otTestRunner +{ + class Program + { + struct TestResults + { + public bool Pass; + public List Output; + public string Error; + } + + static string EscapeJson(string data) + { + return data.Replace("\\", "\\\\").Replace("\"", "\\\""); + } + + /// + /// Executes an exe with the given args and captures the output + /// + static async Task ExecuteAsync(string name, string args = null, int timeoutMilliseconds = -1, int instanceIndex = -1) + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.CreateNoWindow = true; + startInfo.UseShellExecute = false; + startInfo.RedirectStandardError = true; + startInfo.RedirectStandardOutput = true; + startInfo.FileName = name; + startInfo.Arguments = args; + + startInfo.EnvironmentVariables["NODE_TYPE"] = "win-sim"; + + if (instanceIndex != -1) + { + startInfo.EnvironmentVariables["INSTANCE"] = instanceIndex.ToString(); + } + + TestResults Results = new TestResults(); + Results.Pass = false; + Results.Output = new List(); + Results.Error = null; + var Errors = new List(); + + Results.Output.Add(string.Format("> set NODE_TYPE=win-sim")); + Results.Output.Add(string.Format("> {0} {1}", name, args)); + Results.Output.Add("----------------------------------------------------------------------"); + + try + { + // Execute process + using (Process process = Process.Start(startInfo)) + { + process.OutputDataReceived += + (object sender, DataReceivedEventArgs e) => { + if (e.Data != null && e.Data.Length > 0) + lock (Results.Output) { Results.Output.Add(e.Data); } + }; + process.ErrorDataReceived += + (object sender, DataReceivedEventArgs e) => { + if (e.Data != null && e.Data.Length > 0) + lock (Results.Output) { Errors.Add(e.Data); } + }; + + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + +#if DEBUG + Console.WriteLine("Starting {0} {1}", name, args); +#endif + + // Wait for process to complete + await Task.Run( + () => { + if (timeoutMilliseconds == -1) + process.WaitForExit(); + else if (!process.WaitForExit(timeoutMilliseconds)) + { + process.Kill(); + Results.Output.Add(string.Format("Killed {0} on execution timeout!", name)); + } + }); + + // Wait a bit for any output to collect + await Task.Delay(1000); + + process.CancelOutputRead(); + process.CancelErrorRead(); + + Results.Output.AddRange(Errors); + Results.Pass = Errors.Count > 0 && Errors[Errors.Count - 1] == "OK"; + + if (!Results.Pass) Results.Error = EscapeJson(string.Join("\\r\\n", Errors)); + + // Make sure the process is killed + try { process.Kill(); } catch (Exception) { } + +#if DEBUG + Console.WriteLine("Completed {0} {1}", name, args); +#endif + } + } + catch (Exception e) + { + Results.Output.Add("Encountered exception: " + e.Message); + Results.Output.Add(e.StackTrace); + } + + return Results; + } + + static bool VerboseOutput = false; + static int RetiresOnFailure = 0; + static bool AppVeyorMode = false; + static string AppVeyorApiUrl = null; + static string ResultsFolder = "Results_" + DateTime.Now.ToString("yyyyMMdd_HH.mm.ss"); + + static void UploadAppVeyorTestResult(string name, bool passed, long durationMS, string error = null) + { + if (AppVeyorApiUrl == null) return; + + string jsonData = null; + + try + { + var request = (HttpWebRequest)WebRequest.Create(Path.Combine(AppVeyorApiUrl, "api/tests")); + + jsonData = + string.Format( + "{{" + + "\"testName\": \"{0}\", " + + "\"testFramework\": \"MSTest\", " + + "\"fileName\": \"{0}.py\", " + + "\"outcome\": \"{1}\", " + + "\"durationMilliseconds\": \"{2}\", " + + "\"ErrorMessage\": \"{3}\"" + + "}}", + name, + passed ? "Passed" : "Failed", + durationMS, + error == null ? "" : error + ); + + var data = Encoding.UTF8.GetBytes(jsonData); + + request.Method = "POST"; + request.ContentType = "application/json"; + request.ContentLength = data.Length; + + using (var stream = request.GetRequestStream()) + { + stream.Write(data, 0, data.Length); + } + + var response = (HttpWebResponse)request.GetResponse(); + var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd(); + } + catch (Exception e) + { + Console.WriteLine("Encountered exception for http post:"); + Console.WriteLine(e.Message); + Console.WriteLine(e.StackTrace); + + Console.WriteLine("Json content:"); + Console.WriteLine(jsonData); + } + } + + /// + /// Runs a python test file and returns success/failure + /// + static async Task RunTest(string file, int index) + { + string pythonPath = "python.exe"; + if (AppVeyorMode) + { + if (Environment.GetEnvironmentVariable("Platform").ToLower() == "x64") + { + pythonPath = @"c:\python35-x64\python.exe"; + } + else + { + pythonPath = @"c:\python35\python.exe"; + } + } + + int tries = 0; + Stopwatch Timer; + TestResults Results; + + do + { + Timer = new Stopwatch(); + Timer.Start(); + + Results = await ExecuteAsync(pythonPath, file, 30 * 60 * 1000, index); + + Timer.Stop(); + + } while (++tries < RetiresOnFailure + 1 && Results.Pass == false); + + if (VerboseOutput) + { + lock (ResultsFolder) + { + foreach (var line in Results.Output) + Console.WriteLine(line); + } + } + + UploadAppVeyorTestResult(Path.GetFileNameWithoutExtension(file), Results.Pass, Timer.ElapsedMilliseconds, Results.Error); + + // Write the output to a file + var filePrefix = Results.Pass ? "P_" : "F_"; + var outputFilePath = Path.Combine(ResultsFolder, filePrefix + Path.GetFileNameWithoutExtension(file) + ".txt"); + try { + File.WriteAllLines(outputFilePath, Results.Output); + } catch (Exception e) { + Console.WriteLine("Exception while trying to write {0}:\n{1}!", outputFilePath, e.Message); + } + + return Results.Pass; + } + + /// + /// Runs all the tests as indicated by input arguments + /// + static void Main(string[] args) + { + if (args.Length < 2) + { + Console.WriteLine("Usage: otTestRunner.exe [path] [search pattern] (parallel:n) (verbose)"); + return; + } + + var files = Directory.GetFiles(args[0], args[1]); + if (files.Length == 0) + { + Console.WriteLine("No tests found with that path & pattern!"); + return; + } + + var NumberOfTestsToRunInParallel = 1; + for (var i = 2; i < args.Length; i++) + { + if (args[i].StartsWith("parallel:")) + NumberOfTestsToRunInParallel = int.Parse(args[i].Substring(9)); + else if (args[i].StartsWith("retry:")) + RetiresOnFailure = int.Parse(args[i].Substring(6)); + else if (args[i].StartsWith("verbose")) + VerboseOutput = true; + else if (args[i].StartsWith("appveyor")) + { + AppVeyorMode = true; + AppVeyorApiUrl = Environment.GetEnvironmentVariable("APPVEYOR_API_URL"); + //Console.WriteLine("AppVeyorApiUrl = {0}", AppVeyorApiUrl); + } + } + + var CurNumTestsRunning = 0; + var ReadyToRunEvent = new ManualResetEvent(true); + + var TestPassCount = 0; + Stopwatch Timer = new Stopwatch(); + + Directory.CreateDirectory(ResultsFolder); + Console.WriteLine("Test results saved: .\\{0}", ResultsFolder); + + Console.WriteLine("Running {0} tests, {1} at a time:", files.Length, NumberOfTestsToRunInParallel); + /*for (var i = 0; i < files.Length; i++) + Console.WriteLine(Path.GetFileName(files[i]));*/ + Console.WriteLine(""); + + Timer.Start(); + for (var i = 0; i < files.Length; i++) + { + // Wait for the event to be set, if not already + ReadyToRunEvent.WaitOne(); + + if (i != 0) + { + // Wait a bit to stagger the starts + Task.Delay(1000).Wait(); + } + + lock (ResultsFolder) + { + if (++CurNumTestsRunning == NumberOfTestsToRunInParallel) + ReadyToRunEvent.Reset(); + } + + var index = i; + var fileName = files[i]; + + // Start the test, but don't wait for it + Task.Run( + async () => + { + var result = await RunTest(fileName, index); + lock (ResultsFolder) + { + var PrevColor = Console.ForegroundColor; + if (result) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.Write("PASS"); + TestPassCount++; + } + else + { + Console.ForegroundColor = ConsoleColor.Red; + Console.Write("FAIL"); + } + Console.ForegroundColor = PrevColor; + Console.WriteLine(": {0}", Path.GetFileNameWithoutExtension(fileName)); + + CurNumTestsRunning--; + ReadyToRunEvent.Set(); + } + }); + } + + // Wait for all the tests to complete + while (CurNumTestsRunning != 0) + ReadyToRunEvent.WaitOne(); + + Timer.Stop(); + TimeSpan ts = Timer.Elapsed; + string elapsedTime = + String.Format("{0:00}:{1:00}:{2:00}.{3:00}", + ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); + + Console.WriteLine("{0} tests run in {1}", files.Length, elapsedTime); + Console.WriteLine("{0} passed and {1} failed", TestPassCount, files.Length - TestPassCount); + + if (!AppVeyorMode) + Environment.ExitCode = files.Length == TestPassCount ? 0 : 1; + } + } +} diff --git a/tests/scripts/Makefile.am b/tests/scripts/Makefile.am index 4c6e78ee7..984656b2e 100644 --- a/tests/scripts/Makefile.am +++ b/tests/scripts/Makefile.am @@ -131,6 +131,7 @@ EXTRA_DIST = \ thread-cert/net_crypto.py \ thread-cert/network_data.py \ thread-cert/node.py \ + thread-cert/node_cli.py \ thread-cert/sniffer.py \ $(NULL) diff --git a/tests/scripts/thread-cert/Test_otLwf.py b/tests/scripts/thread-cert/Test_otLwf.py new file mode 100644 index 000000000..b97ad39a2 --- /dev/null +++ b/tests/scripts/thread-cert/Test_otLwf.py @@ -0,0 +1,77 @@ +#!/usr/bin/python +# +# 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. +# + +import unittest +import ctypes + +class GUID(ctypes.Structure): + _fields_ = [("Data1", ctypes.c_uint), + ("Data2", ctypes.c_ushort), + ("Data3", ctypes.c_ushort), + ("Data4", ctypes.c_ubyte * 8)] + +class otDeviceList(ctypes.Structure): + _fields_ = [("aDevicesLength", ctypes.c_ushort), + ("aDevices", GUID * 64)] + +class Cert_otLwf(unittest.TestCase): + def setUp(self): + + # Load the DLL + self.Api = ctypes.WinDLL("otApi.dll") + if self.Api == None: + raise OSError("Failed to load otApi.dll!") + + # Define the functions + self.Api.otApiInit.restype = ctypes.c_void_p + self.Api.otApiFinalize.argtypes = [ctypes.c_void_p] + self.Api.otApiFinalize.restype = None + self.Api.otFreeMemory.argtypes = [ctypes.c_void_p] + self.Api.otFreeMemory.restype = None + self.Api.otEnumerateDevices.argtypes = [ctypes.c_void_p] + self.Api.otEnumerateDevices.restype = ctypes.POINTER(otDeviceList) + + def tearDown(self): + if self.ApiInstance: + self.Api.otApiFinalize(self.ApiInstance) + + def test(self): + # Instantiate the API + self.ApiInstance = self.Api.otApiInit() + # Assert that it didn't return NULL + self.assertNotEqual(self.ApiInstance, None) + # Query the device list + devices = self.Api.otEnumerateDevices(self.ApiInstance) + # Assert that it didn't return NULL + self.assertNotEqual(devices, None) + # Print the number of devices + print("devices found: %d" % devices.contents.aDevicesLength) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index 4e35aefb6..d5954890f 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -30,568 +30,217 @@ import os import sys import time -import pexpect +if sys.platform != 'win32': + import node_cli +else: + import node_api import unittest class Node: def __init__(self, nodeid): - self.nodeid = nodeid - self.verbose = int(float(os.getenv('VERBOSE', 0))) - self.node_type = os.getenv('NODE_TYPE', 'sim') - - if self.node_type == 'soc': - self.__init_soc(nodeid) - elif self.node_type == 'ncp-sim': - self.__init_ncp_sim(nodeid) + if sys.platform != 'win32': + self.interface = node_cli.otCli(nodeid) else: - self.__init_sim(nodeid) + self.interface = node_api.otApi(nodeid) - if self.verbose: - self.pexpect.logfile_read = sys.stdout - - self.clear_whitelist() - self.disable_whitelist() - self.set_timeout(100) - - def __init_sim(self, nodeid): - """ Initialize a simulation node. """ - if "OT_CLI_PATH" in os.environ.keys(): - cmd = os.environ['OT_CLI_PATH'] - elif "top_builddir" in os.environ.keys(): - srcdir = os.environ['top_builddir'] - cmd = '%s/examples/apps/cli/ot-cli-ftd' % srcdir - else: - cmd = './ot-cli-ftd' - cmd += ' %d' % nodeid - print ("%s" % cmd) - - self.pexpect = pexpect.spawn(cmd, timeout=4) - - # Add delay to ensure that the process is ready to receive commands. - time.sleep(0.2) - - - def __init_ncp_sim(self, nodeid): - """ Initialize an NCP simulation node. """ - if "top_builddir" in os.environ.keys(): - builddir = os.environ['top_builddir'] - if "top_srcdir" in os.environ.keys(): - srcdir = os.environ['top_srcdir'] - else: - srcdir = os.path.dirname(os.path.realpath(__file__)) - srcdir += "/../../.." - cmd = 'python %s/tools/spinel-cli/spinel-cli.py -p %s/examples/apps/ncp/ot-ncp -n' % (srcdir, builddir) - else: - cmd = './ot-ncp' - cmd += ' %d' % nodeid - print ("%s" % cmd) - - self.pexpect = pexpect.spawn(cmd, timeout=4) - time.sleep(0.2) - self.pexpect.expect('spinel-cli >') - self.debug(int(os.getenv('DEBUG', '0'))) - - def __init_soc(self, nodeid): - """ Initialize a System-on-a-chip node connected via UART. """ - import fdpexpect - serialPort = '/dev/ttyUSB%d' % ((nodeid-1)*2) - self.pexpect = fdpexpect.fdspawn(os.open(serialPort, os.O_RDWR|os.O_NONBLOCK|os.O_NOCTTY)) + self.interface.clear_whitelist() + self.interface.disable_whitelist() + self.interface.set_timeout(100) def __del__(self): - if self.pexpect.isalive(): - self.send_command('exit') - self.pexpect.expect(pexpect.EOF) - self.pexpect.terminate() - self.pexpect.close(force=True) + del self.interface - def send_command(self, cmd): - print ("%d: %s" % (self.nodeid, cmd)) - self.pexpect.sendline(cmd) - - def get_commands(self): - self.send_command('?') - self.pexpect.expect('Commands:') - commands = [] - while True: - i = self.pexpect.expect(['Done', '(\S+)']) - if i != 0: - commands.append(self.pexpect.match.groups()[0]) - else: - break - return commands - - def set_mode(self, mode): - cmd = 'mode ' + mode - self.send_command(cmd) - self.pexpect.expect('Done') + def set_mode(self, mode): + self.interface.set_mode(mode) def debug(self, level): - self.send_command('debug '+str(level)) + self.interface.debug(level) def interface_up(self): - self.send_command('ifconfig up') - self.pexpect.expect('Done') + self.interface.interface_up() def interface_down(self): - self.send_command('ifconfig down') - self.pexpect.expect('Done') + self.interface.interface_down() def thread_start(self): - self.send_command('thread start') - self.pexpect.expect('Done') + self.interface.thread_start() def thread_stop(self): - self.send_command('thread stop') - self.pexpect.expect('Done') - + self.interface.thread_stop() + def commissioner_start(self): - cmd = 'commissioner start' - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.commissioner_start() def commissioner_add_joiner(self, addr, psk): - cmd = 'commissioner joiner add ' + addr + ' ' + psk - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.commissioner_add_joiner(addr, psk) def joiner_start(self, pskd='', provisioning_url=''): - cmd = 'joiner start ' + pskd + ' ' + provisioning_url - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.joiner_start(pskd, provisioning_url) def start(self): - self.interface_up() - self.thread_start() + self.interface.interface_up() + self.interface.thread_start() def stop(self): - self.thread_stop() - self.interface_down() + self.interface.thread_stop() + self.interface.interface_down() def clear_whitelist(self): - self.send_command('whitelist clear') - self.pexpect.expect('Done') + self.interface.clear_whitelist() def enable_whitelist(self): - self.send_command('whitelist enable') - self.pexpect.expect('Done') + self.interface.enable_whitelist() def disable_whitelist(self): - self.send_command('whitelist disable') - self.pexpect.expect('Done') + self.interface.disable_whitelist() def add_whitelist(self, addr, rssi=None): - cmd = 'whitelist add ' + addr - if rssi != None: - cmd += ' ' + str(rssi) - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.add_whitelist(addr, rssi) def remove_whitelist(self, addr): - cmd = 'whitelist remove ' + addr - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.remove_whitelist(addr) def get_addr16(self): - self.send_command('rloc16') - i = self.pexpect.expect('([0-9a-fA-F]{4})') - if i == 0: - addr16 = int(self.pexpect.match.groups()[0], 16) - self.pexpect.expect('Done') - return addr16 + return self.interface.get_addr16() def get_addr64(self): - self.send_command('extaddr') - i = self.pexpect.expect('([0-9a-fA-F]{16})') - if i == 0: - addr64 = self.pexpect.match.groups()[0].decode("utf-8") - self.pexpect.expect('Done') - return addr64 + return self.interface.get_addr64() def get_hashmacaddr(self): - self.send_command('hashmacaddr') - i = self.pexpect.expect('([0-9a-fA-F]{16})') - if i == 0: - addr = self.pexpect.match.groups()[0].decode("utf-8") - self.pexpect.expect('Done') - return addr + return self.interface.get_hashmacaddr() def get_channel(self): - self.send_command('channel') - i = self.pexpect.expect('(\d+)\r\n') - if i == 0: - channel = int(self.pexpect.match.groups()[0]) - self.pexpect.expect('Done') - return channel + return self.interface.get_channel() def set_channel(self, channel): - cmd = 'channel %d' % channel - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_channel(channel) def get_masterkey(self): - self.send_command('masterkey') - i = self.pexpect.expect('([0-9a-fA-F]{32})') - if i == 0: - masterkey = self.pexpect.match.groups()[0].decode("utf-8") - self.pexpect.expect('Done') - return masterkey + return self.interface.get_masterkey() def set_masterkey(self, masterkey): - cmd = 'masterkey ' + masterkey - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_masterkey(masterkey) def get_key_sequence_counter(self): - self.send_command('keysequence counter') - i = self.pexpect.expect('(\d+)\r\n') - if i == 0: - key_sequence_counter = int(self.pexpect.match.groups()[0]) - self.pexpect.expect('Done') - return key_sequence_counter + return self.interface.get_key_sequence_counter() def set_key_sequence_counter(self, key_sequence_counter): - cmd = 'keysequence counter %d' % key_sequence_counter - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_key_sequence_counter(key_sequence_counter) def set_key_switch_guardtime(self, key_switch_guardtime): - cmd = 'keysequence guardtime %d' % key_switch_guardtime - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_key_switch_guardtime(key_switch_guardtime) def set_network_id_timeout(self, network_id_timeout): - cmd = 'networkidtimeout %d' % network_id_timeout - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_network_id_timeout(network_id_timeout) def get_network_name(self): - self.send_command('networkname') - while True: - i = self.pexpect.expect(['Done', '(\S+)']) - if i != 0: - network_name = self.pexpect.match.groups()[0].decode('utf-8') - else: - break - return network_name + return self.interface.get_network_name() def set_network_name(self, network_name): - cmd = 'networkname ' + network_name - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_network_name(network_name) def get_panid(self): - self.send_command('panid') - i = self.pexpect.expect('([0-9a-fA-F]{4})') - if i == 0: - panid = int(self.pexpect.match.groups()[0], 16) - self.pexpect.expect('Done') - return panid + return self.interface.get_panid() def set_panid(self, panid): - cmd = 'panid %d' % panid - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_panid(panid) def get_partition_id(self): - self.send_command('leaderpartitionid') - i = self.pexpect.expect('(\d+)\r\n') - if i == 0: - weight = self.pexpect.match.groups()[0] - self.pexpect.expect('Done') - return weight + return self.interface.get_partition_id() def set_partition_id(self, partition_id): - cmd = 'leaderpartitionid %d' % partition_id - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_partition_id(partition_id) def set_router_upgrade_threshold(self, threshold): - cmd = 'routerupgradethreshold %d' % threshold - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_router_upgrade_threshold(threshold) def set_router_downgrade_threshold(self, threshold): - cmd = 'routerdowngradethreshold %d' % threshold - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_router_downgrade_threshold(threshold) def release_router_id(self, router_id): - cmd = 'releaserouterid %d' % router_id - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.release_router_id(router_id) def get_state(self): - states = ['detached', 'child', 'router', 'leader'] - self.send_command('state') - match = self.pexpect.expect(states) - self.pexpect.expect('Done') - return states[match] + return self.interface.get_state() def set_state(self, state): - cmd = 'state ' + state - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_state(state) def get_timeout(self): - self.send_command('childtimeout') - i = self.pexpect.expect('(\d+)\r\n') - if i == 0: - timeout = self.pexpect.match.groups()[0] - self.pexpect.expect('Done') - return timeout + return self.interface.get_timeout() def set_timeout(self, timeout): - cmd = 'childtimeout %d' % timeout - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_timeout(timeout) def set_max_children(self, number): - cmd = 'childmax %d' % number - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_max_children(number) def get_weight(self): - self.send_command('leaderweight') - i = self.pexpect.expect('(\d+)\r\n') - if i == 0: - weight = self.pexpect.match.groups()[0] - self.pexpect.expect('Done') - return weight + return self.interface.get_weight() def set_weight(self, weight): - cmd = 'leaderweight %d' % weight - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_weight(weight) def add_ipaddr(self, ipaddr): - cmd = 'ipaddr add ' + ipaddr - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.add_ipaddr(ipaddr) def get_addrs(self): - addrs = [] - self.send_command('ipaddr') - - while True: - i = self.pexpect.expect(['(\S+:\S+)\r\n', 'Done']) - if i == 0: - addrs.append(self.pexpect.match.groups()[0].decode("utf-8")) - elif i == 1: - break - - return addrs + return self.interface.get_addrs() def get_context_reuse_delay(self): - self.send_command('contextreusedelay') - i = self.pexpect.expect('(\d+)\r\n') - if i == 0: - timeout = self.pexpect.match.groups()[0] - self.pexpect.expect('Done') - return timeout + return self.interface.get_context_reuse_delay() def set_context_reuse_delay(self, delay): - cmd = 'contextreusedelay %d' % delay - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_context_reuse_delay(delay) def add_prefix(self, prefix, flags, prf = 'med'): - cmd = 'prefix add ' + prefix + ' ' + flags + ' ' + prf - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.add_prefix(prefix, flags, prf) def remove_prefix(self, prefix): - cmd = ' prefix remove ' + prefix - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.remove_prefix(prefix) def add_route(self, prefix, prf = 'med'): - cmd = 'route add ' + prefix + ' ' + prf - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.add_route(prefix, prf) def remove_route(self, prefix): - cmd = 'route remove ' + prefix - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.remove_route(prefix) def register_netdata(self): - self.send_command('netdataregister') - self.pexpect.expect('Done') + self.interface.register_netdata() def energy_scan(self, mask, count, period, scan_duration, ipaddr): - cmd = 'commissioner energy ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + str(scan_duration) + ' ' + ipaddr - self.send_command(cmd) - self.pexpect.expect('Energy:', timeout=8) + self.interface.energy_scan(mask, count, period, scan_duration, ipaddr) def panid_query(self, panid, mask, ipaddr): - cmd = 'commissioner panid ' + str(panid) + ' ' + str(mask) + ' ' + ipaddr - self.send_command(cmd) - self.pexpect.expect('Conflict:', timeout=8) + self.interface.panid_query(panid, mask, ipaddr) def scan(self): - self.send_command('scan') - - results = [] - while True: - i = self.pexpect.expect(['\|\s(\S+)\s+\|\s(\S+)\s+\|\s([0-9a-fA-F]{4})\s\|\s([0-9a-fA-F]{16})\s\|\s(\d+)\r\n', - 'Done']) - if i == 0: - results.append(self.pexpect.match.groups()) - else: - break - - return results + return self.interface.scan() def ping(self, ipaddr, num_responses=1, size=None): - cmd = 'ping ' + ipaddr - if size != None: - cmd += ' ' + str(size) - - self.send_command(cmd) - - result = True - try: - responders = {} - while len(responders) < num_responses: - i = self.pexpect.expect(['from (\S+):']) - if i == 0: - responders[self.pexpect.match.groups()[0]] = 1 - self.pexpect.expect('\n') - except pexpect.TIMEOUT: - result = False - - return result + return self.interface.ping(ipaddr, num_responses, size) def set_router_selection_jitter(self, jitter): - cmd = 'routerselectionjitter %d' % jitter - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.set_router_selection_jitter(jitter) def set_active_dataset(self, timestamp, panid=None, channel=None, channel_mask=None, master_key=None): - self.send_command('dataset clear') - self.pexpect.expect('Done') - - cmd = 'dataset activetimestamp %d' % timestamp - self.send_command(cmd) - self.pexpect.expect('Done') - - if panid != None: - cmd = 'dataset panid %d' % panid - self.send_command(cmd) - self.pexpect.expect('Done') - - if channel != None: - cmd = 'dataset channel %d' % channel - self.send_command(cmd) - self.pexpect.expect('Done') - - if channel_mask != None: - cmd = 'dataset channelmask %d' % channel_mask - self.send_command(cmd) - self.pexpect.expect('Done') - - if master_key != None: - cmd = 'dataset masterkey ' + master_key - self.send_command(cmd) - self.pexpect.expect('Done') - - self.send_command('dataset commit active') - self.pexpect.expect('Done') + self.interface.set_active_dataset(timestamp, panid, channel, channel_mask, master_key) def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None): - self.send_command('dataset clear') - self.pexpect.expect('Done') - - cmd = 'dataset pendingtimestamp %d' % pendingtimestamp - self.send_command(cmd) - self.pexpect.expect('Done') - - cmd = 'dataset activetimestamp %d' % activetimestamp - self.send_command(cmd) - self.pexpect.expect('Done') - - if panid != None: - cmd = 'dataset panid %d' % panid - self.send_command(cmd) - self.pexpect.expect('Done') - - if channel != None: - cmd = 'dataset channel %d' % channel - self.send_command(cmd) - self.pexpect.expect('Done') - - self.send_command('dataset commit pending') - self.pexpect.expect('Done') + self.interface.set_pending_dataset(pendingtimestamp, activetimestamp, panid, channel) def announce_begin(self, mask, count, period, ipaddr): - cmd = 'commissioner announce ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + ipaddr - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.announce_begin(mask, count, period, ipaddr) def send_mgmt_active_set(self, active_timestamp=None, channel=None, channel_mask=None, extended_panid=None, panid=None, master_key=None, mesh_local=None, network_name=None, binary=None): - cmd = 'dataset mgmtsetcommand active ' - - if active_timestamp != None: - cmd += 'activetimestamp %d ' % active_timestamp - - if channel != None: - cmd += 'channel %d ' % channel - - if channel_mask != None: - cmd += 'channelmask %d ' % channel_mask - - if extended_panid != None: - cmd += 'extpanid ' + extended_panid + ' ' - - if panid != None: - cmd += 'panid %d ' % panid - - if master_key != None: - cmd += 'masterkey ' + master_key + ' ' - - if mesh_local != None: - cmd += 'localprefix ' + mesh_local + ' ' - - if network_name != None: - cmd += 'networkname ' + network_name + ' ' - - if binary != None: - cmd += 'binary ' + binary + ' ' - - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.send_mgmt_active_set(active_timestamp, channel, channel_mask, extended_panid, panid, + master_key, mesh_local, network_name, binary) def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None, panid=None, master_key=None, mesh_local=None, network_name=None): - cmd = 'dataset mgmtsetcommand pending ' - - if pending_timestamp != None: - cmd += 'pendingtimestamp %d ' % pending_timestamp - - if active_timestamp != None: - cmd += 'activetimestamp %d ' % active_timestamp - - if delay_timer != None: - cmd += 'delaytimer %d ' % delay_timer - - if channel != None: - cmd += 'channel %d ' % channel - - if panid != None: - cmd += 'panid %d ' % panid - - if master_key != None: - cmd += 'masterkey ' + master_key + ' ' - - if mesh_local != None: - cmd += 'localprefix ' + mesh_local + ' ' - - if network_name != None: - cmd += 'networkname ' + network_name + ' ' - - self.send_command(cmd) - self.pexpect.expect('Done') + self.interface.send_mgmt_pending_set(pending_timestamp, active_timestamp, delay_timer, channel, panid, + master_key, mesh_local, network_name) if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/node_api.py b/tests/scripts/thread-cert/node_api.py new file mode 100644 index 000000000..298403725 --- /dev/null +++ b/tests/scripts/thread-cert/node_api.py @@ -0,0 +1,588 @@ +#!/usr/bin/python +# +# 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. +# + +import os +import sys +import time +import ctypes + +class otApi: + def __init__(self, nodeid): + self.verbose = int(float(os.getenv('VERBOSE', 0))) + self.__init_dll(nodeid) + + def __del__(self): + self.Api.otNodeFinalize(self.otNode) + + def set_mode(self, mode): + if self.Api.otNodeSetMode(self.otNode, mode.encode('utf-8')) != 0: + raise OSError("otNodeSetMode failed!") + + def interface_up(self): + if self.Api.otNodeInterfaceUp(self.otNode) != 0: + raise OSError("otNodeInterfaceUp failed!") + + def interface_down(self): + if self.Api.otNodeInterfaceDown(self.otNode) != 0: + raise OSError("otNodeInterfaceDown failed!") + + def thread_start(self): + if self.Api.otNodeThreadStart(self.otNode) != 0: + raise OSError("otNodeThreadStart failed!") + + def thread_stop(self): + if self.Api.otNodeThreadStop(self.otNode) != 0: + raise OSError("otNodeThreadStop failed!") + + def commissioner_start(self): + if self.Api.otNodeCommissionerStart(self.otNode) != 0: + raise OSError("otNodeCommissionerStart failed!") + + def commissioner_add_joiner(self, addr, psk): + if self.Api.otNodeCommissionerJoinerAdd(self.otNode, addr.encode('utf-8'), psk.encode('utf-8')) != 0: + raise OSError("otNodeCommissionerJoinerAdd failed!") + + def joiner_start(self, pskd='', provisioning_url=''): + if self.Api.otNodeJoinerStart(self.otNode, pskd.encode('utf-8'), provisioning_url.encode('utf-8')) != 0: + raise OSError("otNodeJoinerStart failed!") + + def clear_whitelist(self): + if self.Api.otNodeClearWhitelist(self.otNode) != 0: + raise OSError("otNodeClearWhitelist failed!") + + def enable_whitelist(self): + if self.Api.otNodeEnableWhitelist(self.otNode) != 0: + raise OSError("otNodeEnableWhitelist failed!") + + def disable_whitelist(self): + if self.Api.otNodeDisableWhitelist(self.otNode) != 0: + raise OSError("otNodeDisableWhitelist failed!") + + def add_whitelist(self, addr, rssi=None): + if rssi == None: + rssi = 0 + if self.Api.otNodeAddWhitelist(self.otNode, addr.encode('utf-8'), ctypes.c_byte(rssi)) != 0: + raise OSError("otNodeAddWhitelist failed!") + + def remove_whitelist(self, addr): + if self.Api.otNodeRemoveWhitelist(self.otNode, addr.encode('utf-8')) != 0: + raise OSError("otNodeRemoveWhitelist failed!") + + def get_addr16(self): + return self.Api.otNodeGetAddr16(self.otNode) + + def get_addr64(self): + return self.Api.otNodeGetAddr64(self.otNode).decode('utf-8') + + def get_hashmacaddr(self): + return self.Api.otNodeGetHashMacAddress(self.otNode).decode('utf-8') + + def get_channel(self): + return self.Api.otNodeGetChannel(self.otNode) + + def set_channel(self, channel): + if self.Api.otNodeSetChannel(self.otNode, ctypes.c_ubyte(channel)) != 0: + raise OSError("otNodeSetChannel failed!") + + def get_masterkey(self): + return self.Api.otNodeGetMasterkey(self.otNode).decode("utf-8") + + def set_masterkey(self, masterkey): + if self.Api.otNodeSetMasterkey(self.otNode, masterkey.encode('utf-8')) != 0: + raise OSError("otNodeSetMasterkey failed!") + + def get_key_sequence_counter(self): + return self.Api.otNodeGetKeySequenceCounter(self.otNode) + + def set_key_sequence_counter(self, key_sequence_counter): + if self.Api.otNodeSetKeySequenceCounter(self.otNode, ctypes.c_uint(key_sequence_counter)) != 0: + raise OSError("otNodeSetKeySequenceCounter failed!") + + def set_key_switch_guardtime(self, key_switch_guardtime): + if self.Api.otNodeSetKeySwitchGuardTime(self.otNode, ctypes.c_uint(key_switch_guardtime)) != 0: + raise OSError("otNodeSetKeySwitchGuardTime failed!") + + def set_network_id_timeout(self, network_id_timeout): + if self.Api.otNodeSetNetworkIdTimeout(self.otNode, ctypes.c_ubyte(network_id_timeout)) != 0: + raise OSError("otNodeSetNetworkIdTimeout failed!") + + def get_network_name(self): + return self.Api.otNodeGetNetworkName(self.otNode).decode("utf-8") + + def set_network_name(self, network_name): + if self.Api.otNodeSetNetworkName(self.otNode, network_name.encode('utf-8')) != 0: + raise OSError("otNodeSetNetworkName failed!") + + def get_panid(self): + return int(self.Api.otNodeGetPanId(self.otNode)) + + def set_panid(self, panid): + if self.Api.otNodeSetPanId(self.otNode, ctypes.c_ushort(panid)) != 0: + raise OSError("otNodeSetPanId failed!") + + def get_partition_id(self): + return int(self.Api.otNodeGetPartitionId(self.otNode)) + + def set_partition_id(self, partition_id): + if self.Api.otNodeSetPartitionId(self.otNode, ctypes.c_uint(partition_id)) != 0: + raise OSError("otNodeSetPartitionId failed!") + + def set_router_upgrade_threshold(self, threshold): + if self.Api.otNodeSetRouterUpgradeThreshold(self.otNode, ctypes.c_ubyte(threshold)) != 0: + raise OSError("otNodeSetRouterUpgradeThreshold failed!") + + def set_router_downgrade_threshold(self, threshold): + if self.Api.otNodeSetRouterDowngradeThreshold(self.otNode, ctypes.c_ubyte(threshold)) != 0: + raise OSError("otNodeSetRouterDowngradeThreshold failed!") + + def release_router_id(self, router_id): + if self.Api.otNodeReleaseRouterId(self.otNode, ctypes.c_ubyte(router_id)) != 0: + raise OSError("otNodeReleaseRouterId failed!") + + def get_state(self): + return self.Api.otNodeGetState(self.otNode).decode('utf-8') + + def set_state(self, state): + if self.Api.otNodeSetState(self.otNode, state.encode('utf-8')) != 0: + raise OSError("otNodeSetState failed!") + + def get_timeout(self): + return int(self.Api.otNodeGetTimeout(self.otNode)) + + def set_timeout(self, timeout): + if self.Api.otNodeSetTimeout(self.otNode, ctypes.c_uint(timeout)) != 0: + raise OSError("otNodeSetTimeout failed!") + + def set_max_children(self, number): + if self.Api.otNodeSetMaxChildren(self.otNode, ctypes.c_ubyte(number)) != 0: + raise OSError("otNodeSetMaxChildren failed!") + + def get_weight(self): + return int(self.Api.otNodeGetWeight(self.otNode)) + + def set_weight(self, weight): + if self.Api.otNodeSetWeight(self.otNode, ctypes.c_ubyte(weight)) != 0: + raise OSError("otNodeSetWeight failed!") + + def add_ipaddr(self, ipaddr): + if self.Api.otNodeAddIpAddr(self.otNode, ipaddr.encode('utf-8')) != 0: + raise OSError("otNodeAddIpAddr failed!") + + def get_addrs(self): + return self.Api.otNodeGetAddrs(self.otNode).decode("utf-8").split("\n") + + def get_context_reuse_delay(self): + return int(self.Api.otNodeGetContextReuseDelay(self.otNode)) + + def set_context_reuse_delay(self, delay): + if self.Api.otNodeSetContextReuseDelay(self.otNode, ctypes.c_uint(delay)) != 0: + raise OSError("otNodeSetContextReuseDelay failed!") + + def add_prefix(self, prefix, flags, prf = 'med'): + if self.Api.otNodeAddPrefix(self.otNode, prefix.encode('utf-8'), flags.encode('utf-8'), prf.encode('utf-8')) != 0: + raise OSError("otNodeAddPrefix failed!") + + def remove_prefix(self, prefix): + if self.Api.otNodeRemovePrefix(self.otNode, prefix.encode('utf-8')) != 0: + raise OSError("otNodeRemovePrefix failed!") + + def add_route(self, prefix, prf = 'med'): + if self.Api.otNodeAddRoute(self.otNode, prefix.encode('utf-8'), prf.encode('utf-8')) != 0: + raise OSError("otNodeAddRoute failed!") + + def remove_route(self, prefix): + if self.Api.otNodeRemoveRoute(self.otNode, prefix.encode('utf-8')) != 0: + raise OSError("otNodeRemovePrefix failed!") + + def register_netdata(self): + if self.Api.otNodeRegisterNetdata(self.otNode) != 0: + raise OSError("otNodeRegisterNetdata failed!") + + def energy_scan(self, mask, count, period, scan_duration, ipaddr): + if self.Api.otNodeEnergyScan(self.otNode, ctypes.c_uint(mask), ctypes.c_ubyte(count), ctypes.c_ushort(period), ctypes.c_ushort(scan_duration), ipaddr.encode('utf-8')) != 0: + raise OSError("otNodeEnergyScan failed!") + + def panid_query(self, panid, mask, ipaddr): + if self.Api.otNodePanIdQuery(self.otNode, ctypes.c_ushort(panid), ctypes.c_uint(mask), ipaddr.encode('utf-8')) != 0: + raise OSError("otNodePanIdQuery failed!") + + def scan(self): + return self.Api.otNodeScan(self.otNode).decode("utf-8").split("\n") + + def ping(self, ipaddr, num_responses=1, size=None): + if size == None: + size = 100 + numberOfResponders = self.Api.otNodePing(self.otNode, ipaddr.encode('utf-8'), ctypes.c_ushort(size), ctypes.c_uint(num_responses)) + return numberOfResponders >= num_responses + + def set_router_selection_jitter(self, jitter): + if self.Api.otNodeSetRouterSelectionJitter(self.otNode, ctypes.c_ubyte(jitter)) != 0: + raise OSError("otNodeSetRouterSelectionJitter failed!") + + def set_active_dataset(self, timestamp, panid=None, channel=None, channel_mask=None, master_key=None): + if panid == None: + panid = 0 + if channel == None: + channel = 0 + if channel_mask == None: + channel_mask = 0 + if master_key == None: + master_key = "" + if self.Api.otNodeSetActiveDataset( + self.otNode, + ctypes.c_ulonglong(timestamp), + ctypes.c_ushort(panid), + ctypes.c_ushort(channel), + ctypes.c_uint(channel_mask), + master_key.encode('utf-8') + ) != 0: + raise OSError("otNodeSetActiveDataset failed!") + + def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None): + if pendingtimestamp == None: + pendingtimestamp = 0 + if activetimestamp == None: + activetimestamp = 0 + if panid == None: + panid = 0 + if channel == None: + channel = 0 + if self.Api.otNodeSetPendingDataset( + self.otNode, + ctypes.c_ulonglong(activetimestamp), + ctypes.c_ulonglong(pendingtimestamp), + ctypes.c_ushort(panid), + ctypes.c_ushort(channel) + ) != 0: + raise OSError("otNodeSetPendingDataset failed!") + + def announce_begin(self, mask, count, period, ipaddr): + if self.Api.otNodeCommissionerAnnounceBegin(self.otNode, ctypes.c_uint(mask), ctypes.c_ubyte(count), ctypes.c_ushort(period), ipaddr.encode('utf-8')) != 0: + raise OSError("otNodeCommissionerAnnounceBegin failed!") + + def send_mgmt_active_set(self, active_timestamp=None, channel=None, channel_mask=None, extended_panid=None, + panid=None, master_key=None, mesh_local=None, network_name=None, binary=None): + if active_timestamp == None: + active_timestamp = 0 + if panid == None: + panid = 0 + if channel == None: + channel = 0 + if channel_mask == None: + channel_mask = 0 + if extended_panid == None: + extended_panid = "" + if master_key == None: + master_key = "" + if mesh_local == None: + mesh_local = "" + if network_name == None: + network_name = "" + if binary == None: + binary = "" + if self.Api.otNodeSendActiveSet( + self.otNode, + ctypes.c_ulonglong(active_timestamp), + ctypes.c_ushort(panid), + ctypes.c_ushort(channel), + ctypes.c_uint(channel_mask), + extended_panid.encode('utf-8'), + master_key.encode('utf-8'), + mesh_local.encode('utf-8'), + network_name.encode('utf-8'), + binary.encode('utf-8') + ) != 0: + raise OSError("otNodeSendActiveSet failed!") + + def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None, + panid=None, master_key=None, mesh_local=None, network_name=None): + if pending_timestamp == None: + pending_timestamp = 0 + if active_timestamp == None: + active_timestamp = 0 + if delay_timer == None: + delay_timer = 0 + if panid == None: + panid = 0 + if channel == None: + channel = 0 + if master_key == None: + master_key = "" + if mesh_local == None: + mesh_local = "" + if network_name == None: + network_name = "" + if self.Api.otNodeSendPendingSet( + self.otNode, + ctypes.c_ulonglong(active_timestamp), + ctypes.c_ulonglong(pending_timestamp), + ctypes.c_uint(delay_timer), + ctypes.c_ushort(panid), + ctypes.c_ushort(channel), + master_key.encode('utf-8'), + mesh_local.encode('utf-8'), + network_name.encode('utf-8') + ) != 0: + raise OSError("otNodeSendPendingSet failed!") + + def log(self, message): + self.Api.otNodeLog(message) + + def __init_dll(self, nodeid): + """ Initialize the API from a Windows DLL. """ + + # Load the DLL + self.Api = ctypes.WinDLL("otnodeapi.dll") + if self.Api == None: + raise OSError("Failed to load otnodeapi.dll!") + + # Define the functions + self.Api.otNodeLog.argtypes = [ctypes.c_char_p] + + self.Api.otNodeInit.argtypes = [ctypes.c_uint] + self.Api.otNodeInit.restype = ctypes.c_void_p + + self.Api.otNodeFinalize.argtypes = [ctypes.c_void_p] + + self.Api.otNodeSetMode.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeInterfaceUp.argtypes = [ctypes.c_void_p] + + self.Api.otNodeInterfaceDown.argtypes = [ctypes.c_void_p] + + self.Api.otNodeThreadStart.argtypes = [ctypes.c_void_p] + + self.Api.otNodeThreadStop.argtypes = [ctypes.c_void_p] + + self.Api.otNodeCommissionerStart.argtypes = [ctypes.c_void_p] + + self.Api.otNodeCommissionerJoinerAdd.argtypes = [ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p] + + self.Api.otNodeCommissionerStop.argtypes = [ctypes.c_void_p] + + self.Api.otNodeJoinerStart.argtypes = [ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p] + + self.Api.otNodeJoinerStop.argtypes = [ctypes.c_void_p] + + self.Api.otNodeClearWhitelist.argtypes = [ctypes.c_void_p] + + self.Api.otNodeEnableWhitelist.argtypes = [ctypes.c_void_p] + + self.Api.otNodeDisableWhitelist.argtypes = [ctypes.c_void_p] + + self.Api.otNodeAddWhitelist.argtypes = [ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_byte] + + self.Api.otNodeRemoveWhitelist.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeGetAddr16.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetAddr16.restype = ctypes.c_ushort + + self.Api.otNodeGetAddr64.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetAddr64.restype = ctypes.c_char_p + + self.Api.otNodeGetHashMacAddress.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetHashMacAddress.restype = ctypes.c_char_p + + self.Api.otNodeSetChannel.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeGetChannel.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetChannel.restype = ctypes.c_ubyte + + self.Api.otNodeSetMasterkey.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeGetMasterkey.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetMasterkey.restype = ctypes.c_char_p + + self.Api.otNodeGetKeySequenceCounter.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetKeySequenceCounter.restype = ctypes.c_uint + + self.Api.otNodeSetKeySequenceCounter.argtypes = [ctypes.c_void_p, + ctypes.c_uint] + + self.Api.otNodeSetKeySwitchGuardTime.argtypes = [ctypes.c_void_p, + ctypes.c_uint] + + self.Api.otNodeSetNetworkIdTimeout.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeGetNetworkName.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetNetworkName.restype = ctypes.c_char_p + + self.Api.otNodeSetNetworkName.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeGetPanId.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetPanId.restype = ctypes.c_ushort + + self.Api.otNodeSetPanId.argtypes = [ctypes.c_void_p, + ctypes.c_ushort] + + self.Api.otNodeGetPartitionId.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetPartitionId.restype = ctypes.c_uint + + self.Api.otNodeSetPartitionId.argtypes = [ctypes.c_void_p, + ctypes.c_uint] + + self.Api.otNodeSetRouterUpgradeThreshold.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeSetRouterDowngradeThreshold.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeReleaseRouterId.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeGetState.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetState.restype = ctypes.c_char_p + + self.Api.otNodeSetState.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeGetTimeout.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetTimeout.restype = ctypes.c_uint + + self.Api.otNodeSetTimeout.argtypes = [ctypes.c_void_p, + ctypes.c_uint] + + self.Api.otNodeGetWeight.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetWeight.restype = ctypes.c_ubyte + + self.Api.otNodeSetWeight.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeAddIpAddr.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeGetAddrs.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetAddrs.restype = ctypes.c_char_p + + self.Api.otNodeGetContextReuseDelay.argtypes = [ctypes.c_void_p] + self.Api.otNodeGetContextReuseDelay.restype = ctypes.c_uint + + self.Api.otNodeSetContextReuseDelay.argtypes = [ctypes.c_void_p, + ctypes.c_uint] + + self.Api.otNodeAddPrefix.argtypes = [ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p] + + self.Api.otNodeRemovePrefix.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeAddRoute.argtypes = [ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_char_p] + + self.Api.otNodeRemoveRoute.argtypes = [ctypes.c_void_p, + ctypes.c_char_p] + + self.Api.otNodeRegisterNetdata.argtypes = [ctypes.c_void_p] + + self.Api.otNodeEnergyScan.argtypes = [ctypes.c_void_p, + ctypes.c_uint, + ctypes.c_ubyte, + ctypes.c_ushort, + ctypes.c_ushort, + ctypes.c_char_p] + + self.Api.otNodePanIdQuery.argtypes = [ctypes.c_void_p, + ctypes.c_ushort, + ctypes.c_uint, + ctypes.c_char_p] + + self.Api.otNodeScan.argtypes = [ctypes.c_void_p] + self.Api.otNodeScan.restype = ctypes.c_char_p + + self.Api.otNodePing.argtypes = [ctypes.c_void_p, + ctypes.c_char_p, + ctypes.c_ushort, + ctypes.c_uint] + self.Api.otNodePing.restype = ctypes.c_uint + + self.Api.otNodeSetRouterSelectionJitter.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + self.Api.otNodeCommissionerAnnounceBegin.argtypes = [ctypes.c_void_p, + ctypes.c_uint, + ctypes.c_ubyte, + ctypes.c_ushort, + ctypes.c_char_p] + + self.Api.otNodeSetActiveDataset.argtypes = [ctypes.c_void_p, + ctypes.c_ulonglong, + ctypes.c_ushort, + ctypes.c_ushort, + ctypes.c_uint, + ctypes.c_char_p] + + self.Api.otNodeSetPendingDataset.argtypes = [ctypes.c_void_p, + ctypes.c_ulonglong, + ctypes.c_ulonglong, + ctypes.c_ushort, + ctypes.c_ushort] + + self.Api.otNodeSendPendingSet.argtypes = [ctypes.c_void_p, + ctypes.c_ulonglong, + ctypes.c_ulonglong, + ctypes.c_uint, + ctypes.c_ushort, + ctypes.c_ushort, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p] + + self.Api.otNodeSendActiveSet.argtypes = [ctypes.c_void_p, + ctypes.c_ulonglong, + ctypes.c_ushort, + ctypes.c_ushort, + ctypes.c_uint, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p] + + self.Api.otNodeSetMaxChildren.argtypes = [ctypes.c_void_p, + ctypes.c_ubyte] + + # Initialize a new node + self.otNode = self.Api.otNodeInit(ctypes.c_uint(nodeid)) + if self.otNode == None: + raise OSError("otNodeInit failed!") diff --git a/tests/scripts/thread-cert/node_cli.py b/tests/scripts/thread-cert/node_cli.py new file mode 100644 index 000000000..126c9f59f --- /dev/null +++ b/tests/scripts/thread-cert/node_cli.py @@ -0,0 +1,581 @@ +#!/usr/bin/python +# +# 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. +# + +import os +import sys +import time +import pexpect + +class otCli: + def __init__(self, nodeid): + self.nodeid = nodeid + self.verbose = int(float(os.getenv('VERBOSE', 0))) + self.node_type = os.getenv('NODE_TYPE', 'sim') + + if self.node_type == 'soc': + self.__init_soc(nodeid) + elif self.node_type == 'ncp-sim': + self.__init_ncp_sim(nodeid) + else: + self.__init_sim(nodeid) + + if self.verbose: + self.pexpect.logfile_read = sys.stdout + + def __init_sim(self, nodeid): + """ Initialize a simulation node. """ + if "OT_CLI_PATH" in os.environ.keys(): + cmd = os.environ['OT_CLI_PATH'] + elif "top_builddir" in os.environ.keys(): + srcdir = os.environ['top_builddir'] + cmd = '%s/examples/apps/cli/ot-cli-ftd' % srcdir + else: + cmd = './ot-cli-ftd' + cmd += ' %d' % nodeid + print ("%s" % cmd) + + self.pexpect = pexpect.spawn(cmd, timeout=4) + + # Add delay to ensure that the process is ready to receive commands. + time.sleep(0.2) + + + def __init_ncp_sim(self, nodeid): + """ Initialize an NCP simulation node. """ + if "top_builddir" in os.environ.keys(): + builddir = os.environ['top_builddir'] + if "top_srcdir" in os.environ.keys(): + srcdir = os.environ['top_srcdir'] + else: + srcdir = os.path.dirname(os.path.realpath(__file__)) + srcdir += "/../../.." + cmd = 'python %s/tools/spinel-cli/spinel-cli.py -p %s/examples/apps/ncp/ot-ncp -n' % (srcdir, builddir) + else: + cmd = './ot-ncp' + cmd += ' %d' % nodeid + print ("%s" % cmd) + + self.pexpect = pexpect.spawn(cmd, timeout=4) + time.sleep(0.2) + self.pexpect.expect('spinel-cli >') + self.debug(int(os.getenv('DEBUG', '0'))) + + def __init_soc(self, nodeid): + """ Initialize a System-on-a-chip node connected via UART. """ + import fdpexpect + serialPort = '/dev/ttyUSB%d' % ((nodeid-1)*2) + self.pexpect = fdpexpect.fdspawn(os.open(serialPort, os.O_RDWR|os.O_NONBLOCK|os.O_NOCTTY)) + + def __del__(self): + if self.pexpect.isalive(): + self.send_command('exit') + self.pexpect.expect(pexpect.EOF) + self.pexpect.terminate() + self.pexpect.close(force=True) + + def send_command(self, cmd): + print ("%d: %s" % (self.nodeid, cmd)) + self.pexpect.sendline(cmd) + + def get_commands(self): + self.send_command('?') + self.pexpect.expect('Commands:') + commands = [] + while True: + i = self.pexpect.expect(['Done', '(\S+)']) + if i != 0: + commands.append(self.pexpect.match.groups()[0]) + else: + break + return commands + + def set_mode(self, mode): + cmd = 'mode ' + mode + self.send_command(cmd) + self.pexpect.expect('Done') + + def debug(self, level): + self.send_command('debug '+str(level)) + + def interface_up(self): + self.send_command('ifconfig up') + self.pexpect.expect('Done') + + def interface_down(self): + self.send_command('ifconfig down') + self.pexpect.expect('Done') + + def thread_start(self): + self.send_command('thread start') + self.pexpect.expect('Done') + + def thread_stop(self): + self.send_command('thread stop') + self.pexpect.expect('Done') + + def commissioner_start(self): + cmd = 'commissioner start' + self.send_command(cmd) + self.pexpect.expect('Done') + + def commissioner_add_joiner(self, addr, psk): + cmd = 'commissioner joiner add ' + addr + ' ' + psk + self.send_command(cmd) + self.pexpect.expect('Done') + + def joiner_start(self, pskd='', provisioning_url=''): + cmd = 'joiner start ' + pskd + ' ' + provisioning_url + self.send_command(cmd) + self.pexpect.expect('Done') + + def clear_whitelist(self): + self.send_command('whitelist clear') + self.pexpect.expect('Done') + + def enable_whitelist(self): + self.send_command('whitelist enable') + self.pexpect.expect('Done') + + def disable_whitelist(self): + self.send_command('whitelist disable') + self.pexpect.expect('Done') + + def add_whitelist(self, addr, rssi=None): + cmd = 'whitelist add ' + addr + if rssi != None: + cmd += ' ' + str(rssi) + self.send_command(cmd) + self.pexpect.expect('Done') + + def remove_whitelist(self, addr): + cmd = 'whitelist remove ' + addr + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_addr16(self): + self.send_command('rloc16') + i = self.pexpect.expect('([0-9a-fA-F]{4})') + if i == 0: + addr16 = int(self.pexpect.match.groups()[0], 16) + self.pexpect.expect('Done') + return addr16 + + def get_addr64(self): + self.send_command('extaddr') + i = self.pexpect.expect('([0-9a-fA-F]{16})') + if i == 0: + addr64 = self.pexpect.match.groups()[0].decode("utf-8") + self.pexpect.expect('Done') + return addr64 + + def get_hashmacaddr(self): + self.send_command('hashmacaddr') + i = self.pexpect.expect('([0-9a-fA-F]{16})') + if i == 0: + addr = self.pexpect.match.groups()[0].decode("utf-8") + self.pexpect.expect('Done') + return addr + + def get_channel(self): + self.send_command('channel') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + channel = int(self.pexpect.match.groups()[0]) + self.pexpect.expect('Done') + return channel + + def set_channel(self, channel): + cmd = 'channel %d' % channel + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_masterkey(self): + self.send_command('masterkey') + i = self.pexpect.expect('([0-9a-fA-F]{32})') + if i == 0: + masterkey = self.pexpect.match.groups()[0].decode("utf-8") + self.pexpect.expect('Done') + return masterkey + + def set_masterkey(self, masterkey): + cmd = 'masterkey ' + masterkey + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_key_sequence_counter(self): + self.send_command('keysequence counter') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + key_sequence_counter = int(self.pexpect.match.groups()[0]) + self.pexpect.expect('Done') + return key_sequence_counter + + def set_key_sequence_counter(self, key_sequence_counter): + cmd = 'keysequence counter %d' % key_sequence_counter + self.send_command(cmd) + self.pexpect.expect('Done') + + def set_key_switch_guardtime(self, key_switch_guardtime): + cmd = 'keysequence guardtime %d' % key_switch_guardtime + self.send_command(cmd) + self.pexpect.expect('Done') + + def set_network_id_timeout(self, network_id_timeout): + cmd = 'networkidtimeout %d' % network_id_timeout + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_network_name(self): + self.send_command('networkname') + while True: + i = self.pexpect.expect(['Done', '(\S+)']) + if i != 0: + network_name = self.pexpect.match.groups()[0].decode('utf-8') + else: + break + return network_name + + def set_network_name(self, network_name): + cmd = 'networkname ' + network_name + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_panid(self): + self.send_command('panid') + i = self.pexpect.expect('([0-9a-fA-F]{4})') + if i == 0: + panid = int(self.pexpect.match.groups()[0], 16) + self.pexpect.expect('Done') + return panid + + def set_panid(self, panid): + cmd = 'panid %d' % panid + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_partition_id(self): + self.send_command('leaderpartitionid') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + weight = self.pexpect.match.groups()[0] + self.pexpect.expect('Done') + return weight + + def set_partition_id(self, partition_id): + cmd = 'leaderpartitionid %d' % partition_id + self.send_command(cmd) + self.pexpect.expect('Done') + + def set_router_upgrade_threshold(self, threshold): + cmd = 'routerupgradethreshold %d' % threshold + self.send_command(cmd) + self.pexpect.expect('Done') + + def set_router_downgrade_threshold(self, threshold): + cmd = 'routerdowngradethreshold %d' % threshold + self.send_command(cmd) + self.pexpect.expect('Done') + + def release_router_id(self, router_id): + cmd = 'releaserouterid %d' % router_id + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_state(self): + states = ['detached', 'child', 'router', 'leader'] + self.send_command('state') + match = self.pexpect.expect(states) + self.pexpect.expect('Done') + return states[match] + + def set_state(self, state): + cmd = 'state ' + state + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_timeout(self): + self.send_command('childtimeout') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + timeout = self.pexpect.match.groups()[0] + self.pexpect.expect('Done') + return timeout + + def set_timeout(self, timeout): + cmd = 'childtimeout %d' % timeout + self.send_command(cmd) + self.pexpect.expect('Done') + + def set_max_children(self, number): + cmd = 'childmax %d' % number + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_weight(self): + self.send_command('leaderweight') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + weight = self.pexpect.match.groups()[0] + self.pexpect.expect('Done') + return weight + + def set_weight(self, weight): + cmd = 'leaderweight %d' % weight + self.send_command(cmd) + self.pexpect.expect('Done') + + def add_ipaddr(self, ipaddr): + cmd = 'ipaddr add ' + ipaddr + self.send_command(cmd) + self.pexpect.expect('Done') + + def get_addrs(self): + addrs = [] + self.send_command('ipaddr') + + while True: + i = self.pexpect.expect(['(\S+:\S+)\r\n', 'Done']) + if i == 0: + addrs.append(self.pexpect.match.groups()[0].decode("utf-8")) + elif i == 1: + break + + return addrs + + def get_context_reuse_delay(self): + self.send_command('contextreusedelay') + i = self.pexpect.expect('(\d+)\r\n') + if i == 0: + timeout = self.pexpect.match.groups()[0] + self.pexpect.expect('Done') + return timeout + + def set_context_reuse_delay(self, delay): + cmd = 'contextreusedelay %d' % delay + self.send_command(cmd) + self.pexpect.expect('Done') + + def add_prefix(self, prefix, flags, prf = 'med'): + cmd = 'prefix add ' + prefix + ' ' + flags + ' ' + prf + self.send_command(cmd) + self.pexpect.expect('Done') + + def remove_prefix(self, prefix): + cmd = ' prefix remove ' + prefix + self.send_command(cmd) + self.pexpect.expect('Done') + + def add_route(self, prefix, prf = 'med'): + cmd = 'route add ' + prefix + ' ' + prf + self.send_command(cmd) + self.pexpect.expect('Done') + + def remove_route(self, prefix): + cmd = 'route remove ' + prefix + self.send_command(cmd) + self.pexpect.expect('Done') + + def register_netdata(self): + self.send_command('netdataregister') + self.pexpect.expect('Done') + + def energy_scan(self, mask, count, period, scan_duration, ipaddr): + cmd = 'commissioner energy ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + str(scan_duration) + ' ' + ipaddr + self.send_command(cmd) + self.pexpect.expect('Energy:', timeout=8) + + def panid_query(self, panid, mask, ipaddr): + cmd = 'commissioner panid ' + str(panid) + ' ' + str(mask) + ' ' + ipaddr + self.send_command(cmd) + self.pexpect.expect('Conflict:', timeout=8) + + def scan(self): + self.send_command('scan') + + results = [] + while True: + i = self.pexpect.expect(['\|\s(\S+)\s+\|\s(\S+)\s+\|\s([0-9a-fA-F]{4})\s\|\s([0-9a-fA-F]{16})\s\|\s(\d+)\r\n', + 'Done']) + if i == 0: + results.append(self.pexpect.match.groups()) + else: + break + + return results + + def ping(self, ipaddr, num_responses=1, size=None): + cmd = 'ping ' + ipaddr + if size != None: + cmd += ' ' + str(size) + + self.send_command(cmd) + + result = True + try: + responders = {} + while len(responders) < num_responses: + i = self.pexpect.expect(['from (\S+):']) + if i == 0: + responders[self.pexpect.match.groups()[0]] = 1 + self.pexpect.expect('\n') + except pexpect.TIMEOUT: + result = False + + return result + + def set_router_selection_jitter(self, jitter): + cmd = 'routerselectionjitter %d' % jitter + self.send_command(cmd) + self.pexpect.expect('Done') + + def set_active_dataset(self, timestamp, panid=None, channel=None, channel_mask=None, master_key=None): + self.send_command('dataset clear') + self.pexpect.expect('Done') + + cmd = 'dataset activetimestamp %d' % timestamp + self.send_command(cmd) + self.pexpect.expect('Done') + + if panid != None: + cmd = 'dataset panid %d' % panid + self.send_command(cmd) + self.pexpect.expect('Done') + + if channel != None: + cmd = 'dataset channel %d' % channel + self.send_command(cmd) + self.pexpect.expect('Done') + + if channel_mask != None: + cmd = 'dataset channelmask %d' % channel_mask + self.send_command(cmd) + self.pexpect.expect('Done') + + if master_key != None: + cmd = 'dataset masterkey ' + master_key + self.send_command(cmd) + self.pexpect.expect('Done') + + self.send_command('dataset commit active') + self.pexpect.expect('Done') + + def set_pending_dataset(self, pendingtimestamp, activetimestamp, panid=None, channel=None): + self.send_command('dataset clear') + self.pexpect.expect('Done') + + cmd = 'dataset pendingtimestamp %d' % pendingtimestamp + self.send_command(cmd) + self.pexpect.expect('Done') + + cmd = 'dataset activetimestamp %d' % activetimestamp + self.send_command(cmd) + self.pexpect.expect('Done') + + if panid != None: + cmd = 'dataset panid %d' % panid + self.send_command(cmd) + self.pexpect.expect('Done') + + if channel != None: + cmd = 'dataset channel %d' % channel + self.send_command(cmd) + self.pexpect.expect('Done') + + self.send_command('dataset commit pending') + self.pexpect.expect('Done') + + def announce_begin(self, mask, count, period, ipaddr): + cmd = 'commissioner announce ' + str(mask) + ' ' + str(count) + ' ' + str(period) + ' ' + ipaddr + self.send_command(cmd) + self.pexpect.expect('Done') + + def send_mgmt_active_set(self, active_timestamp=None, channel=None, channel_mask=None, extended_panid=None, + panid=None, master_key=None, mesh_local=None, network_name=None, binary=None): + cmd = 'dataset mgmtsetcommand active ' + + if active_timestamp != None: + cmd += 'activetimestamp %d ' % active_timestamp + + if channel != None: + cmd += 'channel %d ' % channel + + if channel_mask != None: + cmd += 'channelmask %d ' % channel_mask + + if extended_panid != None: + cmd += 'extpanid ' + extended_panid + ' ' + + if panid != None: + cmd += 'panid %d ' % panid + + if master_key != None: + cmd += 'masterkey ' + master_key + ' ' + + if mesh_local != None: + cmd += 'localprefix ' + mesh_local + ' ' + + if network_name != None: + cmd += 'networkname ' + network_name + ' ' + + if binary != None: + cmd += 'binary ' + binary + ' ' + + self.send_command(cmd) + self.pexpect.expect('Done') + + def send_mgmt_pending_set(self, pending_timestamp=None, active_timestamp=None, delay_timer=None, channel=None, + panid=None, master_key=None, mesh_local=None, network_name=None): + cmd = 'dataset mgmtsetcommand pending ' + + if pending_timestamp != None: + cmd += 'pendingtimestamp %d ' % pending_timestamp + + if active_timestamp != None: + cmd += 'activetimestamp %d ' % active_timestamp + + if delay_timer != None: + cmd += 'delaytimer %d ' % delay_timer + + if channel != None: + cmd += 'channel %d ' % channel + + if panid != None: + cmd += 'panid %d ' % panid + + if master_key != None: + cmd += 'masterkey ' + master_key + ' ' + + if mesh_local != None: + cmd += 'localprefix ' + mesh_local + ' ' + + if network_name != None: + cmd += 'networkname ' + network_name + ' ' + + self.send_command(cmd) + self.pexpect.expect('Done')