mirror of
https://github.com/espressif/openthread.git
synced 2026-08-16 15:47:46 +00:00
Python Sniffer Implementation for Windows (#1147)
* Initial function loading of new listener interface. * Additional changes for Node sniffer interface * Use Interface Index instead of GUID * Initialize Handle in sniffer * Put Channel in the sniffer message
This commit is contained in:
@@ -365,6 +365,37 @@ OTNODEAPI int32_t OTCALL otNodeSendActiveSet(otNode* aNode, uint64_t aActiveTime
|
||||
*/
|
||||
OTNODEAPI int32_t OTCALL otNodeSetMaxChildren(otNode* aNode, uint8_t aMaxChildren);
|
||||
|
||||
/**
|
||||
* The interface used to listen in on virtual nodes' MAC frames
|
||||
*/
|
||||
|
||||
typedef struct otListener otListener;
|
||||
|
||||
/**
|
||||
* Creates and starts a new listener
|
||||
*/
|
||||
OTNODEAPI otListener* OTCALL otListenerInit(uint32_t nodeid);
|
||||
|
||||
/**
|
||||
* Frees a listener
|
||||
*/
|
||||
OTNODEAPI int32_t OTCALL otListenerFinalize(otListener* aListener);
|
||||
|
||||
/**
|
||||
* Structure that represents a received MAC frame from the listener
|
||||
*/
|
||||
typedef struct otMacFrame
|
||||
{
|
||||
uint8_t buffer[128];
|
||||
uint8_t length;
|
||||
uint32_t nodeid;
|
||||
} otMacFrame;
|
||||
|
||||
/**
|
||||
* Reads the next MAC frame from the listener
|
||||
*/
|
||||
OTNODEAPI int32_t OTCALL otListenerRead(otListener* aListener, otMacFrame *aFrame);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@@ -39,12 +39,19 @@ typedef VOID (*fp_otvmpCloseHandle)(_In_ HANDLE handle);
|
||||
typedef DWORD (*fp_otvmpAddVirtualBus)(_In_ HANDLE handle, _Inout_ ULONG* pBusNumber, _Out_ ULONG* pIfIndex);
|
||||
typedef DWORD (*fp_otvmpRemoveVirtualBus)(_In_ HANDLE handle, ULONG BusNumber);
|
||||
typedef DWORD (*fp_otvmpSetAdapterTopologyGuid)(_In_ HANDLE handle, DWORD BusNumber, _In_ const GUID* pTopologyGuid);
|
||||
typedef void (*fp_otvmpListenerCallback)(_In_opt_ PVOID aContext, _In_ ULONG SourceInterfaceIndex, _In_reads_bytes_(FrameLength) PUCHAR FrameBuffer, _In_ UCHAR FrameLength, _In_ UCHAR Channel);
|
||||
typedef HANDLE (*fp_otvmpListenerCreate)(_In_ const GUID* pAdapterTopologyGuid);
|
||||
typedef void (*fp_otvmpListenerDestroy)(_In_opt_ HANDLE pHandle);
|
||||
typedef void(*fp_otvmpListenerRegister)(_In_ HANDLE pHandle, _In_opt_ fp_otvmpListenerCallback Callback, _In_opt_ PVOID Context);
|
||||
|
||||
fp_otvmpOpenHandle otvmpOpenHandle = nullptr;
|
||||
fp_otvmpCloseHandle otvmpCloseHandle = nullptr;
|
||||
fp_otvmpAddVirtualBus otvmpAddVirtualBus = nullptr;
|
||||
fp_otvmpRemoveVirtualBus otvmpRemoveVirtualBus = nullptr;
|
||||
fp_otvmpSetAdapterTopologyGuid otvmpSetAdapterTopologyGuid = nullptr;
|
||||
fp_otvmpListenerCreate otvmpListenerCreate = nullptr;
|
||||
fp_otvmpListenerDestroy otvmpListenerDestroy = nullptr;
|
||||
fp_otvmpListenerRegister otvmpListenerRegister = nullptr;
|
||||
|
||||
HMODULE gVmpModule = nullptr;
|
||||
HANDLE gVmpHandle = nullptr;
|
||||
@@ -53,6 +60,8 @@ ULONG gNextBusNumber = 1;
|
||||
GUID gTopologyGuid = {0};
|
||||
|
||||
volatile LONG gNumberOfInterfaces = 0;
|
||||
CRITICAL_SECTION gCS;
|
||||
vector<otNode*> gNodes;
|
||||
|
||||
otApiInstance *gApiInstance = nullptr;
|
||||
|
||||
@@ -119,18 +128,27 @@ otApiInstance* GetApiInstance()
|
||||
otvmpAddVirtualBus = (fp_otvmpAddVirtualBus)GetProcAddress(gVmpModule, "otvmpAddVirtualBus");
|
||||
otvmpRemoveVirtualBus = (fp_otvmpRemoveVirtualBus)GetProcAddress(gVmpModule, "otvmpRemoveVirtualBus");
|
||||
otvmpSetAdapterTopologyGuid = (fp_otvmpSetAdapterTopologyGuid)GetProcAddress(gVmpModule, "otvmpSetAdapterTopologyGuid");
|
||||
otvmpListenerCreate = (fp_otvmpListenerCreate)GetProcAddress(gVmpModule, "otvmpListenerCreate");
|
||||
otvmpListenerDestroy = (fp_otvmpListenerDestroy)GetProcAddress(gVmpModule, "otvmpListenerDestroy");
|
||||
otvmpListenerRegister = (fp_otvmpListenerRegister)GetProcAddress(gVmpModule, "otvmpListenerRegister");
|
||||
|
||||
assert(otvmpOpenHandle);
|
||||
assert(otvmpCloseHandle);
|
||||
assert(otvmpAddVirtualBus);
|
||||
assert(otvmpRemoveVirtualBus);
|
||||
assert(otvmpSetAdapterTopologyGuid);
|
||||
assert(otvmpListenerCreate);
|
||||
assert(otvmpListenerDestroy);
|
||||
assert(otvmpListenerRegister);
|
||||
|
||||
if (otvmpOpenHandle == nullptr) printf("otvmpOpenHandle is null!\r\n");
|
||||
if (otvmpCloseHandle == nullptr) printf("otvmpCloseHandle is null!\r\n");
|
||||
if (otvmpAddVirtualBus == nullptr) printf("otvmpAddVirtualBus is null!\r\n");
|
||||
if (otvmpRemoveVirtualBus == nullptr) printf("otvmpRemoveVirtualBus is null!\r\n");
|
||||
if (otvmpSetAdapterTopologyGuid == nullptr) printf("otvmpSetAdapterTopologyGuid is null!\r\n");
|
||||
if (otvmpListenerCreate == nullptr) printf("otvmpListenerCreate is null!\r\n");
|
||||
if (otvmpListenerDestroy == nullptr) printf("otvmpListenerDestroy is null!\r\n");
|
||||
if (otvmpListenerRegister == nullptr) printf("otvmpListenerRegister is null!\r\n");
|
||||
|
||||
(VOID)otvmpOpenHandle(&gVmpHandle);
|
||||
if (gVmpHandle == nullptr)
|
||||
@@ -146,6 +164,8 @@ otApiInstance* GetApiInstance()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InitializeCriticalSection(&gCS);
|
||||
|
||||
auto offset = getenv("INSTANCE");
|
||||
if (offset)
|
||||
{
|
||||
@@ -187,6 +207,8 @@ void Unload()
|
||||
otApiFinalize(gApiInstance);
|
||||
gApiInstance = nullptr;
|
||||
|
||||
DeleteCriticalSection(&gCS);
|
||||
|
||||
WSACleanup();
|
||||
|
||||
printf("Topology destroyed\r\n");
|
||||
@@ -263,6 +285,7 @@ typedef struct otNode
|
||||
{
|
||||
uint32_t mId;
|
||||
DWORD mBusIndex;
|
||||
DWORD mInterfaceIndex;
|
||||
otInstance* mInstance;
|
||||
HANDLE mEnergyScanEvent;
|
||||
HANDLE mPanIdConflictEvent;
|
||||
@@ -762,12 +785,17 @@ OTNODEAPI otNode* OTCALL otNodeInit(uint32_t id)
|
||||
printf("%d: New Device " GUID_FORMAT " in compartment %d\r\n", id, GUID_ARG(DeviceGuid), Compartment);
|
||||
|
||||
node->mId = id;
|
||||
node->mInterfaceIndex = ifIndex;
|
||||
node->mBusIndex = newBusIndex;
|
||||
node->mInstance = instance;
|
||||
|
||||
node->mEnergyScanEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
node->mPanIdConflictEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
|
||||
EnterCriticalSection(&gCS);
|
||||
gNodes.push_back(node);
|
||||
LeaveCriticalSection(&gCS);
|
||||
|
||||
InitializeCriticalSection(&node->mCS);
|
||||
|
||||
otSetStateChangedCallback(instance, otNodeStateChangedCallback, node);
|
||||
@@ -795,6 +823,17 @@ OTNODEAPI int32_t OTCALL otNodeFinalize(otNode* aNode)
|
||||
CloseHandle(aNode->mEnergyScanEvent);
|
||||
otSetStateChangedCallback(aNode->mInstance, nullptr, nullptr);
|
||||
|
||||
EnterCriticalSection(&gCS);
|
||||
for (uint32_t i = 0; i < gNodes.size(); i++)
|
||||
{
|
||||
if (gNodes[i] == aNode)
|
||||
{
|
||||
gNodes.erase(gNodes.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&gCS);
|
||||
|
||||
// Free the instance
|
||||
otFreeMemory(aNode->mInstance);
|
||||
aNode->mInstance = nullptr;
|
||||
@@ -2056,3 +2095,178 @@ OTNODEAPI int32_t OTCALL otNodeSetMaxChildren(otNode* aNode, uint8_t aMaxChildre
|
||||
otLogFuncExit();
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct otMacFrameEntry
|
||||
{
|
||||
otMacFrame Frame;
|
||||
LIST_ENTRY Link;
|
||||
} otMacFrameEntry;
|
||||
|
||||
typedef struct otListener
|
||||
{
|
||||
HANDLE mListener;
|
||||
CRITICAL_SECTION mCS;
|
||||
HANDLE mStopEvent;
|
||||
HANDLE mFramesUpdatedEvent;
|
||||
LIST_ENTRY mFrames; // List of otMacFrameEntry
|
||||
} otListener;
|
||||
|
||||
void
|
||||
otListenerCallback(
|
||||
_In_opt_ PVOID aContext,
|
||||
_In_ ULONG SourceInterfaceIndex,
|
||||
_In_reads_bytes_(FrameLength) PUCHAR FrameBuffer,
|
||||
_In_ UCHAR FrameLength,
|
||||
_In_ UCHAR Channel
|
||||
)
|
||||
{
|
||||
otListener* aListener = (otListener*)aContext;
|
||||
assert(aListener);
|
||||
|
||||
if (FrameLength)
|
||||
{
|
||||
otMacFrameEntry* entry = new otMacFrameEntry;
|
||||
entry->Frame.buffer[0] = Channel;
|
||||
memcpy_s(entry->Frame.buffer + 1, sizeof(entry->Frame.buffer) - 1, FrameBuffer, FrameLength);
|
||||
entry->Frame.length = FrameLength + 1;
|
||||
entry->Frame.nodeid = (uint32_t)-1;
|
||||
|
||||
// Look up the Node ID from by interface guid
|
||||
EnterCriticalSection(&gCS);
|
||||
for (uint32_t i = 0; i < gNodes.size(); i++)
|
||||
{
|
||||
if (gNodes[i]->mInterfaceIndex == SourceInterfaceIndex)
|
||||
{
|
||||
entry->Frame.nodeid = gNodes[i]->mId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&gCS);
|
||||
|
||||
// Push the frame on the list to process
|
||||
EnterCriticalSection(&aListener->mCS);
|
||||
InsertTailList(&aListener->mFrames, &entry->Link);
|
||||
LeaveCriticalSection(&aListener->mCS);
|
||||
|
||||
// Set event indicating we have a new frame to process
|
||||
SetEvent(aListener->mFramesUpdatedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
OTNODEAPI otListener* OTCALL otListenerInit(uint32_t /* nodeid */)
|
||||
{
|
||||
otLogFuncEntry();
|
||||
|
||||
auto ApiInstance = GetApiInstance();
|
||||
if (ApiInstance == nullptr)
|
||||
{
|
||||
printf("GetApiInstance failed!\r\n");
|
||||
otLogFuncExitMsg("GetApiInstance failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InterlockedIncrement(&gNumberOfInterfaces);
|
||||
|
||||
otListener *listener = new otListener();
|
||||
assert(listener);
|
||||
|
||||
InitializeCriticalSection(&listener->mCS);
|
||||
listener->mStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
listener->mFramesUpdatedEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
|
||||
InitializeListHead(&listener->mFrames);
|
||||
|
||||
// Create the listener
|
||||
listener->mListener = otvmpListenerCreate(&gTopologyGuid);
|
||||
assert(listener->mListener);
|
||||
|
||||
// Register for callbacks
|
||||
otvmpListenerRegister(listener->mListener, otListenerCallback, listener);
|
||||
|
||||
printf("S: Sniffer started\r\n");
|
||||
|
||||
otLogFuncExit();
|
||||
|
||||
return listener;
|
||||
}
|
||||
|
||||
OTNODEAPI int32_t OTCALL otListenerFinalize(otListener* aListener)
|
||||
{
|
||||
otLogFuncEntry();
|
||||
if (aListener != nullptr)
|
||||
{
|
||||
// Set stop event to prevent cancel any pending otListenerRead calls
|
||||
SetEvent(aListener->mStopEvent);
|
||||
|
||||
// Unregisters (and waits for callbacks to complete) and cleans up the handle
|
||||
otvmpListenerDestroy(aListener->mListener);
|
||||
aListener->mListener = nullptr;
|
||||
|
||||
// Clean up left over frames
|
||||
PLIST_ENTRY Link = aListener->mFrames.Flink;
|
||||
while (Link != &aListener->mFrames)
|
||||
{
|
||||
otMacFrameEntry *entry = CONTAINING_RECORD(Link, otMacFrameEntry, Link);
|
||||
Link = Link->Flink;
|
||||
delete entry;
|
||||
}
|
||||
|
||||
// Clean up everything else
|
||||
CloseHandle(aListener->mFramesUpdatedEvent);
|
||||
aListener->mFramesUpdatedEvent = nullptr;
|
||||
CloseHandle(aListener->mStopEvent);
|
||||
aListener->mStopEvent = nullptr;
|
||||
DeleteCriticalSection(&aListener->mCS);
|
||||
delete aListener;
|
||||
|
||||
printf("S: Sniffer stopped\r\n");
|
||||
|
||||
if (0 == InterlockedDecrement(&gNumberOfInterfaces))
|
||||
{
|
||||
// Uninitialize everything else if this is the last ref
|
||||
Unload();
|
||||
}
|
||||
}
|
||||
otLogFuncExit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
OTNODEAPI int32_t OTCALL otListenerRead(otListener* aListener, otMacFrame *aFrame)
|
||||
{
|
||||
do
|
||||
{
|
||||
bool exit = false;
|
||||
|
||||
EnterCriticalSection(&aListener->mCS);
|
||||
|
||||
// If we have a pending frame, return it now
|
||||
if (!IsListEmpty(&aListener->mFrames))
|
||||
{
|
||||
PLIST_ENTRY Link = RemoveHeadList(&aListener->mFrames);
|
||||
otMacFrameEntry *entry = CONTAINING_RECORD(Link, otMacFrameEntry, Link);
|
||||
*aFrame = entry->Frame;
|
||||
delete entry;
|
||||
exit = true;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&aListener->mCS);
|
||||
|
||||
if (exit) break;
|
||||
|
||||
// Wait for the shutdown or frames updated event
|
||||
auto waitResult = WaitForMultipleObjects(2, &aListener->mStopEvent, FALSE, INFINITE);
|
||||
|
||||
if (waitResult == WAIT_OBJECT_0 + 1) // mFramesUpdatedEvent
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else // mStopEvent
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
//printf("S: Sniffer read %d bytes from node %d\r\n", aFrame->length, aFrame->nodeid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -56,3 +56,55 @@ using namespace std;
|
||||
#include <otNode.h>
|
||||
|
||||
void Unload();
|
||||
|
||||
FORCEINLINE
|
||||
VOID
|
||||
InitializeListHead(
|
||||
_Out_ PLIST_ENTRY ListHead
|
||||
)
|
||||
{
|
||||
ListHead->Flink = ListHead->Blink = ListHead;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
PLIST_ENTRY
|
||||
RemoveHeadList(
|
||||
_Inout_ PLIST_ENTRY ListHead
|
||||
)
|
||||
|
||||
{
|
||||
|
||||
PLIST_ENTRY Entry;
|
||||
PLIST_ENTRY NextEntry;
|
||||
Entry = ListHead->Flink;
|
||||
NextEntry = Entry->Flink;
|
||||
ListHead->Flink = NextEntry;
|
||||
NextEntry->Blink = ListHead;
|
||||
return Entry;
|
||||
}
|
||||
|
||||
FORCEINLINE
|
||||
VOID
|
||||
InsertTailList(
|
||||
_Inout_ PLIST_ENTRY ListHead,
|
||||
_Out_ __drv_aliasesMem PLIST_ENTRY Entry
|
||||
)
|
||||
{
|
||||
|
||||
PLIST_ENTRY PrevEntry;
|
||||
PrevEntry = ListHead->Blink;
|
||||
Entry->Flink = ListHead;
|
||||
Entry->Blink = PrevEntry;
|
||||
PrevEntry->Flink = Entry;
|
||||
ListHead->Blink = Entry;
|
||||
}
|
||||
|
||||
_Must_inspect_result_
|
||||
BOOLEAN
|
||||
CFORCEINLINE
|
||||
IsListEmpty(
|
||||
_In_ const LIST_ENTRY * ListHead
|
||||
)
|
||||
{
|
||||
return (BOOLEAN)(ListHead->Flink == ListHead);
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
|
||||
|
||||
class SnifferTransport(object):
|
||||
""" Interface for transport that allows eavesdrop other nodes. """
|
||||
|
||||
@@ -148,6 +148,66 @@ class SnifferSocketTransport(SnifferTransport):
|
||||
return bytearray(data), nodeid
|
||||
|
||||
|
||||
class MacFrame(ctypes.Structure):
|
||||
_fields_ = [("buffer", ctypes.c_ubyte * 128),
|
||||
("length", ctypes.c_ubyte),
|
||||
("nodeid", ctypes.c_uint)]
|
||||
|
||||
class SnifferVirtualTransport(SnifferTransport):
|
||||
""" Virtual interface based implementation of sniffer transport. """
|
||||
|
||||
def __init__(self, nodeid):
|
||||
self.Handle = None
|
||||
|
||||
# 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.otListenerInit.argtypes = [ctypes.c_uint]
|
||||
self.Api.otListenerInit.restype = ctypes.c_void_p
|
||||
|
||||
self.Api.otListenerFinalize.argtypes = [ctypes.c_void_p]
|
||||
|
||||
self.Api.otListenerRead.argtypes = [ctypes.c_void_p, ctypes.POINTER(MacFrame)]
|
||||
|
||||
def __del__(self):
|
||||
if not self.is_opened:
|
||||
return
|
||||
|
||||
self.close()
|
||||
|
||||
def open(self):
|
||||
if self.is_opened:
|
||||
raise RuntimeError("Transport is already opened.")
|
||||
|
||||
# Initialize a listener
|
||||
self.Handle = self.Api.otListenerInit(0)
|
||||
|
||||
if not self.is_opened:
|
||||
raise RuntimeError("Transport opening failed.")
|
||||
|
||||
def close(self):
|
||||
if not self.is_opened:
|
||||
raise RuntimeError("Transport is closed.")
|
||||
|
||||
self.Api.otListenerFinalize(self.Handle);
|
||||
self.Handle = None
|
||||
|
||||
@property
|
||||
def is_opened(self):
|
||||
return bool(self.Handle is not None)
|
||||
|
||||
def recv(self, bufsize):
|
||||
frame = MacFrame()
|
||||
pFrame = ctypes.pointer(frame);
|
||||
|
||||
self.Api.otListenerRead(self.Handle, pFrame)
|
||||
|
||||
return bytearray(frame.buffer)[:frame.length], frame.nodeid
|
||||
|
||||
|
||||
class SnifferTransportFactory(object):
|
||||
|
||||
def create_transport(self, nodeid):
|
||||
@@ -155,4 +215,4 @@ class SnifferTransportFactory(object):
|
||||
return SnifferSocketTransport(nodeid)
|
||||
|
||||
else:
|
||||
raise NotImplementedError
|
||||
return SnifferVirtualTransport(nodeid)
|
||||
|
||||
Reference in New Issue
Block a user