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:
Nick Banks
2017-01-18 09:10:07 -08:00
committed by Jonathan Hui
parent 419d251e2f
commit 71f47c105e
4 changed files with 359 additions and 2 deletions
+31
View File
@@ -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);
}
+62 -2
View File
@@ -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)