forked from osmocom/wireshark
706 lines
28 KiB
C
706 lines
28 KiB
C
/* etw_ndiscap.c
|
|
*
|
|
* Wireshark - Network traffic analyzer
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
* Copyright 1998 Gerald Combs
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
/*
|
|
* Reads IP packets from an Windows event trace logfile or an Windows event trace live session
|
|
* and write out a pcap file with LINKTYPE_ETHERNET, LINKTYPE_RAW or LINKTYPE_IEEE802_11.
|
|
* The major code of this file is from https://github.com/microsoft/etl2pcapng with some changes by Odysseus Yang.
|
|
* The changes mainly include but not limited
|
|
* 1. calling pcapng APIs instead of writing the data in the pcapng binary format by its own implementation in etl2pcapng.
|
|
* 2. Optimize the process of adding pcapng interfaces so it doesn't need process the same Windows event trace logfile twice,
|
|
that not only impacts the performance, but also breaks Wireshark live capture function.
|
|
*/
|
|
|
|
#define WIN32_LEAN_AND_MEAN 1
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <evntrace.h>
|
|
#include <evntcons.h>
|
|
#include <tdh.h>
|
|
#include <strsafe.h>
|
|
#include <winsock2.h>
|
|
#include <netiodef.h>
|
|
|
|
// inet_ipv6.h and netiodef.h define exactly the same stuff, like _IPV6_ROUTING_HEADER and IP6F_OFF_MASK.
|
|
// So wiretap/wtap.h cannot be directly included in this file. Defines below three WTAP_ENCAP types with the value in wtap.h for compile
|
|
#define WTAP_ENCAP_ETHERNET 1
|
|
#define WTAP_ENCAP_RAW_IP 7
|
|
#define WTAP_ENCAP_IEEE_802_11 20
|
|
|
|
#define MAX_PACKET_SIZE 0xFFFF
|
|
|
|
// From the ndiscap manifest
|
|
#define KW_MEDIA_WIRELESS_WAN 0x200
|
|
#define KW_MEDIA_NATIVE_802_11 0x10000
|
|
#define KW_PACKET_START 0x40000000
|
|
#define KW_PACKET_END 0x80000000
|
|
#define KW_SEND 0x100000000
|
|
#define KW_RECEIVE 0x200000000
|
|
|
|
#define tidPacketFragment 1001
|
|
#define tidPacketMetadata 1002
|
|
#define tidVMSwitchPacketFragment 1003
|
|
|
|
// From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/windot11/ns-windot11-dot11_extsta_recv_context
|
|
#pragma pack(push,8)
|
|
typedef struct _NDIS_OBJECT_HEADER {
|
|
unsigned char Type;
|
|
unsigned char Revision;
|
|
unsigned short Size;
|
|
} NDIS_OBJECT_HEADER, * PNDIS_OBJECT_HEADER;
|
|
|
|
typedef struct DOT11_EXTSTA_RECV_CONTEXT {
|
|
NDIS_OBJECT_HEADER Header;
|
|
unsigned long uReceiveFlags;
|
|
unsigned long uPhyId;
|
|
unsigned long uChCenterFrequency;
|
|
unsigned short usNumberOfMPDUsReceived;
|
|
long lRSSI;
|
|
unsigned char ucDataRate;
|
|
unsigned long uSizeMediaSpecificInfo;
|
|
void *pvMediaSpecificInfo;
|
|
unsigned long long ullTimestamp;
|
|
} DOT11_EXTSTA_RECV_CONTEXT, * PDOT11_EXTSTA_RECV_CONTEXT;
|
|
#pragma pack(pop)
|
|
|
|
// From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/windot11/ne-windot11-_dot11_phy_type
|
|
#define DOT11_PHY_TYPE_NAMES_MAX 10
|
|
static const char* DOT11_PHY_TYPE_NAMES[] = {
|
|
"Unknown", // dot11_phy_type_unknown = 0
|
|
"Fhss", // dot11_phy_type_fhss = 1
|
|
"Dsss", // dot11_phy_type_dsss = 2
|
|
"IrBaseband", // dot11_phy_type_irbaseband = 3
|
|
"802.11a", // dot11_phy_type_ofdm = 4
|
|
"802.11b", // dot11_phy_type_hrdsss = 5
|
|
"802.11g", // dot11_phy_type_erp = 6
|
|
"802.11n", // dot11_phy_type_ht = 7
|
|
"802.11ac", // dot11_phy_type_vht = 8
|
|
"802.11ad", // dot11_phy_type_dmg = 9
|
|
"802.11ax" // dot11_phy_type_he = 10
|
|
};
|
|
|
|
unsigned long long NumFramesConverted = 0;
|
|
char AuxFragBuf[MAX_PACKET_SIZE] = {0};
|
|
unsigned long AuxFragBufOffset = 0;
|
|
|
|
DOT11_EXTSTA_RECV_CONTEXT PacketMetadata;
|
|
BOOLEAN AddWlanMetadata = FALSE;
|
|
|
|
typedef struct _NDIS_NET_BUFFER_LIST_8021Q_INFO {
|
|
union {
|
|
struct {
|
|
UINT32 UserPriority : 3; // 802.1p priority
|
|
UINT32 CanonicalFormatId : 1; // always 0
|
|
UINT32 VlanId : 12; // VLAN Identification
|
|
UINT32 Reserved : 16; // set to 0 for ethernet
|
|
} TagHeader;
|
|
|
|
struct {
|
|
UINT32 UserPriority : 3; // 802.1p priority
|
|
UINT32 CanonicalFormatId : 1; // always 0
|
|
UINT32 VlanId : 12; // VLAN Identification
|
|
UINT32 WMMInfo : 4;
|
|
UINT32 Reserved : 12; // set to 0 for Wireless LAN
|
|
} WLanTagHeader;
|
|
|
|
PVOID Value;
|
|
};
|
|
} NDIS_NET_BUFFER_LIST_8021Q_INFO, *PNDIS_NET_BUFFER_LIST_8021Q_INFO;
|
|
|
|
// The max OOB data size might increase in the future. If it becomes larger than MaxNetBufferListInfo,
|
|
// this tool will print a warning and the value of MaxNetBufferListInfo in the code should be increased.
|
|
// From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/nblinfo/ne-nblinfo-ndis_net_buffer_list_info
|
|
#define MaxNetBufferListInfo 200
|
|
#define Ieee8021QNetBufferListInfo 4
|
|
PBYTE OobData[MaxNetBufferListInfo];
|
|
|
|
typedef struct _VMSWITCH_SOURCE_INFO {
|
|
unsigned long SourcePortId;
|
|
char* SourcePortName;
|
|
char* SourceNicName;
|
|
char* SourceNicType;
|
|
} VMSWITCH_SOURCE_INFO, *PVMSWITCH_SOURCE_INFO;
|
|
|
|
typedef struct _VMSWITCH_PACKET_FRAGMENT {
|
|
unsigned long SourcePortId;
|
|
unsigned long DestinationCount;
|
|
short VlanId;
|
|
} VMSWITCH_PACKET_FRAGMENT, *PVMSWITCH_PACKET_FRAGMENT;
|
|
|
|
BOOLEAN CurrentPacketIsVMSwitchPacketFragment = FALSE;
|
|
VMSWITCH_PACKET_FRAGMENT VMSwitchPacketFragment;
|
|
|
|
struct INTERFACE {
|
|
struct INTERFACE* Next;
|
|
unsigned long LowerIfIndex;
|
|
unsigned long MiniportIfIndex;
|
|
unsigned long PcapNgIfIndex;
|
|
int PktEncapType;
|
|
short VlanId;
|
|
|
|
BOOLEAN IsVMNic;
|
|
VMSWITCH_SOURCE_INFO VMNic;
|
|
};
|
|
|
|
#define IFACE_HT_SIZE 100
|
|
struct INTERFACE* InterfaceHashTable[IFACE_HT_SIZE] = {0};
|
|
unsigned long NumInterfaces = 0;
|
|
|
|
void wtap_etl_rec_dump(char* etl_record, ULONG total_packet_length, ULONG original_packet_length, unsigned int interface_id, BOOLEAN is_inbound, ULARGE_INTEGER timestamp, int pkt_encap, char* comment, unsigned short comment_length);
|
|
void wtap_etl_add_interface(int pkt_encap, char* interface_name, unsigned short interface_name_length, char* interface_desc, unsigned short interface_desc_length);
|
|
|
|
extern char g_err_info[FILENAME_MAX];
|
|
extern int g_err;
|
|
|
|
unsigned long HashInterface(unsigned long LowerIfIndex)
|
|
{
|
|
if (CurrentPacketIsVMSwitchPacketFragment) {
|
|
return VMSwitchPacketFragment.SourcePortId * (VMSwitchPacketFragment.VlanId + 1);
|
|
} else {
|
|
return LowerIfIndex;
|
|
}
|
|
}
|
|
|
|
struct INTERFACE* GetInterface(unsigned long LowerIfIndex)
|
|
{
|
|
struct INTERFACE* Iface = InterfaceHashTable[HashInterface(LowerIfIndex) % IFACE_HT_SIZE];
|
|
while (Iface != NULL) {
|
|
if (CurrentPacketIsVMSwitchPacketFragment) {
|
|
if (Iface->IsVMNic &&
|
|
Iface->LowerIfIndex == LowerIfIndex &&
|
|
Iface->VlanId == VMSwitchPacketFragment.VlanId &&
|
|
Iface->VMNic.SourcePortId == VMSwitchPacketFragment.SourcePortId) {
|
|
return Iface;
|
|
}
|
|
} else {
|
|
if (!Iface->IsVMNic && Iface->LowerIfIndex == LowerIfIndex && Iface->VlanId == 0) {
|
|
return Iface;
|
|
}
|
|
}
|
|
Iface = Iface->Next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct INTERFACE* AddInterface(PEVENT_RECORD ev, unsigned long LowerIfIndex, unsigned long MiniportIfIndex, int Type)
|
|
{
|
|
struct INTERFACE** Iface = &InterfaceHashTable[HashInterface(LowerIfIndex) % IFACE_HT_SIZE];
|
|
struct INTERFACE* NewIface = malloc(sizeof(struct INTERFACE));
|
|
|
|
#define IF_STRING_MAX_SIZE 128
|
|
char IfName[IF_STRING_MAX_SIZE];
|
|
size_t IfNameLength = 0;
|
|
char IfDesc[IF_STRING_MAX_SIZE];
|
|
size_t IfDescLength = 0;
|
|
//etw pcagng interface will be 0 always, network pcagng interface will start with 1
|
|
static PcapNgIfIndex = 1;
|
|
|
|
if (NewIface == NULL) {
|
|
g_err = ERROR_OUTOFMEMORY;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface");
|
|
exit(1);
|
|
}
|
|
|
|
NewIface->LowerIfIndex = LowerIfIndex;
|
|
NewIface->MiniportIfIndex = MiniportIfIndex;
|
|
NewIface->PktEncapType = Type;
|
|
NewIface->VlanId = 0;
|
|
NewIface->IsVMNic = FALSE;
|
|
|
|
if (CurrentPacketIsVMSwitchPacketFragment) {
|
|
|
|
NewIface->IsVMNic = TRUE;
|
|
|
|
wchar_t Buffer[8192];
|
|
PROPERTY_DATA_DESCRIPTOR Desc;
|
|
int Err;
|
|
|
|
// SourceNicName
|
|
Desc.PropertyName = (unsigned long long)(L"SourceNicName");
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
ULONG ParamNameSize = 0;
|
|
(void)TdhGetPropertySize(ev, 0, NULL, 1, &Desc, &ParamNameSize);
|
|
NewIface->VMNic.SourceNicName = malloc((ParamNameSize / sizeof(wchar_t)) + 1);
|
|
if (NewIface->VMNic.SourceNicName == NULL) {
|
|
g_err = ERROR_OUTOFMEMORY;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface->VMNic.SourceNicName");
|
|
exit(1);
|
|
}
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
|
|
if (Err != NO_ERROR) {
|
|
Buffer[0] = L'\0';
|
|
}
|
|
Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
Buffer,
|
|
-1,
|
|
NewIface->VMNic.SourceNicName,
|
|
ParamNameSize / sizeof(wchar_t) + 1,
|
|
NULL,
|
|
NULL);
|
|
NewIface->VMNic.SourceNicName[wcslen(Buffer)] = '\0';
|
|
|
|
// SourcePortName
|
|
Desc.PropertyName = (unsigned long long)(L"SourcePortName");
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
(void)TdhGetPropertySize(ev, 0, NULL, 1, &Desc, &ParamNameSize);
|
|
NewIface->VMNic.SourcePortName = malloc((ParamNameSize / sizeof(wchar_t)) + 1);
|
|
if (NewIface->VMNic.SourcePortName == NULL) {
|
|
g_err = ERROR_OUTOFMEMORY;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface->VMNic.SourcePortName");
|
|
exit(1);
|
|
}
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
|
|
if (Err != NO_ERROR) {
|
|
Buffer[0] = L'\0';
|
|
}
|
|
Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
Buffer,
|
|
-1,
|
|
NewIface->VMNic.SourcePortName,
|
|
ParamNameSize / sizeof(wchar_t) + 1,
|
|
NULL,
|
|
NULL);
|
|
NewIface->VMNic.SourcePortName[wcslen(Buffer)] = '\0';
|
|
|
|
// SourceNicType
|
|
Desc.PropertyName = (unsigned long long)(L"SourceNicType");
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
(void)TdhGetPropertySize(ev, 0, NULL, 1, &Desc, &ParamNameSize);
|
|
NewIface->VMNic.SourceNicType = malloc((ParamNameSize / sizeof(wchar_t)) + 1);
|
|
if (NewIface->VMNic.SourceNicType == NULL) {
|
|
g_err = ERROR_OUTOFMEMORY;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "malloc failed to allocate memory for NewIface->VMNic.SourceNicType");
|
|
exit(1);
|
|
}
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
|
|
if (Err != NO_ERROR) {
|
|
Buffer[0] = L'\0';
|
|
}
|
|
Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
Buffer,
|
|
-1,
|
|
NewIface->VMNic.SourceNicType,
|
|
ParamNameSize / sizeof(wchar_t) + 1,
|
|
NULL,
|
|
NULL);
|
|
NewIface->VMNic.SourceNicType[wcslen(Buffer)] = '\0';
|
|
|
|
|
|
NewIface->VMNic.SourcePortId = VMSwitchPacketFragment.SourcePortId;
|
|
NewIface->VlanId = VMSwitchPacketFragment.VlanId;
|
|
}
|
|
|
|
NewIface->Next = *Iface;
|
|
|
|
*Iface = NewIface;
|
|
NumInterfaces++;
|
|
|
|
NewIface->PcapNgIfIndex = PcapNgIfIndex;
|
|
PcapNgIfIndex++;
|
|
memset(IfName, 0, sizeof(IfName));
|
|
memset(IfDesc, 0, sizeof(IfDesc));
|
|
switch (NewIface->PktEncapType) {
|
|
case WTAP_ENCAP_ETHERNET:
|
|
if (NewIface->IsVMNic) {
|
|
printf("IF: medium=%s\tID=%u\tIfIndex=%u\tVlanID=%i",
|
|
NewIface->VMNic.SourceNicType,
|
|
NewIface->PcapNgIfIndex,
|
|
NewIface->VMNic.SourcePortId,
|
|
NewIface->VlanId
|
|
);
|
|
StringCchPrintfA(
|
|
IfName,
|
|
IF_STRING_MAX_SIZE,
|
|
"%s:%s:%lu:%i",
|
|
NewIface->VMNic.SourcePortName,
|
|
NewIface->VMNic.SourceNicType,
|
|
NewIface->VMNic.SourcePortId,
|
|
NewIface->VlanId
|
|
);
|
|
}
|
|
else {
|
|
printf("IF: medium=eth\tID=%u\tIfIndex=%u\tVlanID=%i", NewIface->PcapNgIfIndex, NewIface->LowerIfIndex, NewIface->VlanId);
|
|
StringCchPrintfA(IfName, IF_STRING_MAX_SIZE, "eth:%lu:%i", NewIface->LowerIfIndex, NewIface->VlanId);
|
|
}
|
|
break;
|
|
case WTAP_ENCAP_IEEE_802_11:
|
|
printf("IF: medium=wifi ID=%u\tIfIndex=%u", NewIface->PcapNgIfIndex, NewIface->LowerIfIndex);
|
|
StringCchPrintfA(IfName, IF_STRING_MAX_SIZE, "wifi:%lu", NewIface->LowerIfIndex);
|
|
break;
|
|
case WTAP_ENCAP_RAW_IP:
|
|
printf("IF: medium=mbb ID=%u\tIfIndex=%u", NewIface->PcapNgIfIndex, NewIface->LowerIfIndex);
|
|
StringCchPrintfA(IfName, IF_STRING_MAX_SIZE, "mbb:%lu", NewIface->LowerIfIndex);
|
|
break;
|
|
}
|
|
StringCchLengthA(IfName, IF_STRING_MAX_SIZE, &IfNameLength);
|
|
|
|
if (NewIface->LowerIfIndex != NewIface->MiniportIfIndex) {
|
|
printf("\t(LWF over IfIndex %u)", NewIface->MiniportIfIndex);
|
|
StringCchPrintfA(IfDesc, IF_STRING_MAX_SIZE, "LWF over IfIndex %lu", NewIface->MiniportIfIndex);
|
|
StringCchLengthA(IfDesc, IF_STRING_MAX_SIZE, &IfDescLength);
|
|
}
|
|
|
|
if (NewIface->VlanId != 0) {
|
|
StringCchPrintfA(IfDesc + IfDescLength, IF_STRING_MAX_SIZE, " VlanID=%i ", NewIface->VlanId);
|
|
StringCchLengthA(IfDesc, IF_STRING_MAX_SIZE, &IfDescLength);
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
wtap_etl_add_interface(NewIface->PktEncapType, IfName, (unsigned short)IfNameLength, IfDesc, (unsigned short)IfDescLength);
|
|
return NewIface;
|
|
}
|
|
|
|
void ParseVmSwitchPacketFragment(PEVENT_RECORD ev)
|
|
{
|
|
// Parse the current VMSwitch packet event for use elsewhere.
|
|
// NB: Here we only do per-packet parsing. For any event fields that only need to be
|
|
// parsed once and written into an INTERFACE, we do the parsing in AddInterface.
|
|
|
|
PROPERTY_DATA_DESCRIPTOR Desc;
|
|
int Err;
|
|
PNDIS_NET_BUFFER_LIST_8021Q_INFO pNblVlanInfo;
|
|
|
|
// Get VLAN from OOB
|
|
unsigned long OobLength;
|
|
Desc.PropertyName = (unsigned long long)L"OOBDataSize";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(OobLength), (PBYTE)&OobLength);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty OobLength failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
if (OobLength > sizeof(OobData)) {
|
|
g_err = ERROR_INVALID_DATA;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "OOB data of %lu bytes too large to fit in hardcoded buffer of size %lu", OobLength, (unsigned long)sizeof(OobData));
|
|
return;
|
|
}
|
|
|
|
Desc.PropertyName = (unsigned long long)L"OOBData";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, OobLength, (PBYTE)&OobData);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty OobData failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
pNblVlanInfo = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)&OobData[Ieee8021QNetBufferListInfo];
|
|
VMSwitchPacketFragment.VlanId = pNblVlanInfo->TagHeader.VlanId;
|
|
|
|
// SourcePortId
|
|
Desc.PropertyName = (unsigned long long)L"SourcePortId";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(VMSwitchPacketFragment.SourcePortId), (PBYTE)&VMSwitchPacketFragment.SourcePortId);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty SourcePortId failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
// DestinationCount
|
|
Desc.PropertyName = (unsigned long long)L"DestinationCount";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(VMSwitchPacketFragment.DestinationCount), (PBYTE)&VMSwitchPacketFragment.DestinationCount);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty DestinationCount failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void etw_dump_write_ndiscap_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
|
|
{
|
|
int Err;
|
|
unsigned long LowerIfIndex;
|
|
|
|
struct INTERFACE* Iface;
|
|
unsigned long FragLength;
|
|
PROPERTY_DATA_DESCRIPTOR Desc;
|
|
int Type;
|
|
unsigned long TotalFragmentLength;
|
|
unsigned long InferredOriginalFragmentLength = 0;
|
|
PETHERNET_HEADER EthHdr;
|
|
PIPV4_HEADER Ipv4Hdr;
|
|
PIPV6_HEADER Ipv6Hdr;
|
|
|
|
if ((ev->EventHeader.EventDescriptor.Id != tidPacketFragment &&
|
|
ev->EventHeader.EventDescriptor.Id != tidPacketMetadata &&
|
|
ev->EventHeader.EventDescriptor.Id != tidVMSwitchPacketFragment)) {
|
|
return;
|
|
}
|
|
|
|
CurrentPacketIsVMSwitchPacketFragment = (ev->EventHeader.EventDescriptor.Id == tidVMSwitchPacketFragment);
|
|
if (CurrentPacketIsVMSwitchPacketFragment) {
|
|
ParseVmSwitchPacketFragment(ev);
|
|
}
|
|
|
|
Desc.PropertyName = (unsigned long long)L"LowerIfIndex";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(LowerIfIndex), (PBYTE)&LowerIfIndex);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty LowerIfIndex failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
Iface = GetInterface(LowerIfIndex);
|
|
|
|
if (!!(ev->EventHeader.EventDescriptor.Keyword & KW_MEDIA_NATIVE_802_11)) {
|
|
Type = WTAP_ENCAP_IEEE_802_11;
|
|
} else if (!!(ev->EventHeader.EventDescriptor.Keyword & KW_MEDIA_WIRELESS_WAN)) {
|
|
Type = WTAP_ENCAP_RAW_IP;
|
|
} else {
|
|
Type = WTAP_ENCAP_ETHERNET;
|
|
}
|
|
|
|
// Record the IfIndex if it's a new one.
|
|
if (Iface == NULL) {
|
|
unsigned long MiniportIfIndex;
|
|
Desc.PropertyName = (unsigned long long)L"MiniportIfIndex";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(MiniportIfIndex), (PBYTE)&MiniportIfIndex);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty MiniportIfIndex failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
Iface = AddInterface(
|
|
ev,
|
|
LowerIfIndex,
|
|
MiniportIfIndex,
|
|
Type
|
|
);
|
|
} else if (Iface->PktEncapType != Type) {
|
|
printf("WARNING: inconsistent media type in packet events!\n");
|
|
}
|
|
|
|
if (Iface == NULL) {
|
|
// We generated the list of interfaces directly from the
|
|
// packet traces themselves, so there must be a bug.
|
|
g_err = ERROR_INVALID_DATA;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "Packet with unrecognized IfIndex");
|
|
exit(1);
|
|
}
|
|
|
|
// Save off Ndis/Wlan metadata to be added to the next packet
|
|
if (ev->EventHeader.EventDescriptor.Id == tidPacketMetadata) {
|
|
unsigned long MetadataLength = 0;
|
|
Desc.PropertyName = (unsigned long long)L"MetadataSize";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(MetadataLength), (PBYTE)&MetadataLength);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty MetadataSize failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
if (MetadataLength != sizeof(PacketMetadata)) {
|
|
g_err = ERROR_INVALID_DATA;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "Unknown Metadata length. Expected %lu, got %lu", (unsigned long)sizeof(DOT11_EXTSTA_RECV_CONTEXT), MetadataLength);
|
|
return;
|
|
}
|
|
|
|
Desc.PropertyName = (unsigned long long)L"Metadata";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, MetadataLength, (PBYTE)&PacketMetadata);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty Metadata failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
AddWlanMetadata = TRUE;
|
|
return;
|
|
}
|
|
|
|
// N.B.: Here we are querying the FragmentSize property to get the
|
|
// total size of the packet, and then reading that many bytes from
|
|
// the Fragment property. This is unorthodox (normally you are
|
|
// supposed to use TdhGetPropertySize to get the size of a property)
|
|
// but required due to the way ndiscap puts packet contents in
|
|
// multiple adjacent properties (which happen to be contiguous in
|
|
// memory).
|
|
|
|
Desc.PropertyName = (unsigned long long)L"FragmentSize";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(FragLength), (PBYTE)&FragLength);
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty FragmentSize failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
if (FragLength > RTL_NUMBER_OF(AuxFragBuf) - AuxFragBufOffset) {
|
|
g_err = ERROR_INVALID_DATA;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "Packet too large (size = %u) and skipped", AuxFragBufOffset + FragLength);
|
|
return;
|
|
}
|
|
|
|
Desc.PropertyName = (unsigned long long)L"Fragment";
|
|
Desc.ArrayIndex = ULONG_MAX;
|
|
Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, FragLength, (PBYTE)(AuxFragBuf + AuxFragBufOffset));
|
|
if (Err != NO_ERROR) {
|
|
g_err = Err;
|
|
sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty Fragment failed, err is 0x%x", Err);
|
|
return;
|
|
}
|
|
|
|
// The KW_PACKET_START and KW_PACKET_END keywords are used as follows:
|
|
// -A single-event packet has both KW_PACKET_START and KW_PACKET_END.
|
|
// -A multi-event packet consists of an event with KW_PACKET_START followed
|
|
// by an event with KW_PACKET_END, with zero or more events with neither
|
|
// keyword in between.
|
|
//
|
|
// So, we accumulate fragments in AuxFragBuf until KW_PACKET_END is
|
|
// encountered, then call PcapNgWriteEnhancedPacket and start over. There's
|
|
// no need for us to even look for KW_PACKET_START.
|
|
//
|
|
// NB: Starting with Windows 8.1, only single-event packets are traced.
|
|
// This logic is here to support packet captures from older systems.
|
|
|
|
if (!!(ev->EventHeader.EventDescriptor.Keyword & KW_PACKET_END)) {
|
|
|
|
if (ev->EventHeader.EventDescriptor.Keyword & KW_MEDIA_NATIVE_802_11 &&
|
|
AuxFragBuf[1] & 0x40) {
|
|
// Clear Protected bit in the case of 802.11
|
|
// Ndis captures will be decrypted in the etl file
|
|
|
|
AuxFragBuf[1] = AuxFragBuf[1] & 0xBF; // _1011_1111_ - Clear "Protected Flag"
|
|
}
|
|
|
|
// COMMENT_MAX_SIZE must be multiple of 4
|
|
#define COMMENT_MAX_SIZE 256
|
|
char Comment[COMMENT_MAX_SIZE] = { 0 };
|
|
size_t CommentLength = 0;
|
|
|
|
if (AddWlanMetadata) {
|
|
if (PacketMetadata.uPhyId > DOT11_PHY_TYPE_NAMES_MAX) {
|
|
PacketMetadata.uPhyId = 0; // Set to unknown if outside known bounds.
|
|
}
|
|
|
|
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d Packet Metadata: ReceiveFlags:0x%x, PhyType:%s, CenterCh:%u, NumMPDUsReceived:%u, RSSI:%d, DataRate:%u",
|
|
ev->EventHeader.ProcessId,
|
|
PacketMetadata.uReceiveFlags,
|
|
DOT11_PHY_TYPE_NAMES[PacketMetadata.uPhyId],
|
|
PacketMetadata.uChCenterFrequency,
|
|
PacketMetadata.usNumberOfMPDUsReceived,
|
|
PacketMetadata.lRSSI,
|
|
PacketMetadata.ucDataRate);
|
|
|
|
AddWlanMetadata = FALSE;
|
|
memset(&PacketMetadata, 0, sizeof(DOT11_EXTSTA_RECV_CONTEXT));
|
|
} else if (CurrentPacketIsVMSwitchPacketFragment) {
|
|
if (VMSwitchPacketFragment.DestinationCount > 0) {
|
|
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d VlanId=%d SrcPortId=%d SrcNicType=%s SrcNicName=%s SrcPortName=%s DstNicCount=%d",
|
|
ev->EventHeader.ProcessId,
|
|
Iface->VlanId,
|
|
Iface->VMNic.SourcePortId,
|
|
Iface->VMNic.SourceNicType,
|
|
Iface->VMNic.SourceNicName,
|
|
Iface->VMNic.SourcePortName,
|
|
VMSwitchPacketFragment.DestinationCount
|
|
);
|
|
} else {
|
|
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d VlanId=%d SrcPortId=%d SrcNicType=%s SrcNicName=%s SrcPortName=%s",
|
|
ev->EventHeader.ProcessId,
|
|
Iface->VlanId,
|
|
Iface->VMNic.SourcePortId,
|
|
Iface->VMNic.SourceNicType,
|
|
Iface->VMNic.SourceNicName,
|
|
Iface->VMNic.SourcePortName
|
|
);
|
|
}
|
|
} else {
|
|
Err = StringCchPrintfA(Comment, COMMENT_MAX_SIZE, "PID=%d", ev->EventHeader.ProcessId);
|
|
}
|
|
|
|
if (Err != NO_ERROR) {
|
|
printf("Failed converting comment to string with error: %u\n", Err);
|
|
} else {
|
|
Err = StringCchLengthA(Comment, COMMENT_MAX_SIZE, &CommentLength);
|
|
|
|
if (Err != NO_ERROR) {
|
|
printf("Failed getting length of comment string with error: %u\n", Err);
|
|
CommentLength = 0;
|
|
memset(Comment, 0, COMMENT_MAX_SIZE);
|
|
}
|
|
}
|
|
|
|
TotalFragmentLength = AuxFragBufOffset + FragLength;
|
|
|
|
// Parse the packet to see if it's truncated. If so, try to recover the original length.
|
|
if (Type == WTAP_ENCAP_ETHERNET) {
|
|
if (TotalFragmentLength >= sizeof(ETHERNET_HEADER)) {
|
|
EthHdr = (PETHERNET_HEADER)AuxFragBuf;
|
|
if (ntohs(EthHdr->Type) == ETHERNET_TYPE_IPV4 &&
|
|
TotalFragmentLength >= sizeof(IPV4_HEADER) + sizeof(ETHERNET_HEADER)) {
|
|
Ipv4Hdr = (PIPV4_HEADER)(EthHdr + 1);
|
|
InferredOriginalFragmentLength = ntohs(Ipv4Hdr->TotalLength) + sizeof(ETHERNET_HEADER);
|
|
} else if (ntohs(EthHdr->Type) == ETHERNET_TYPE_IPV6 &&
|
|
TotalFragmentLength >= sizeof(IPV6_HEADER) + sizeof(ETHERNET_HEADER)) {
|
|
Ipv6Hdr = (PIPV6_HEADER)(EthHdr + 1);
|
|
InferredOriginalFragmentLength = ntohs(Ipv6Hdr->PayloadLength) + sizeof(IPV6_HEADER) + sizeof(ETHERNET_HEADER);
|
|
}
|
|
}
|
|
} else if (Type == WTAP_ENCAP_RAW_IP) {
|
|
// Raw frames begins with an IPv4/6 header.
|
|
if (TotalFragmentLength >= sizeof(IPV4_HEADER)) {
|
|
Ipv4Hdr = (PIPV4_HEADER)AuxFragBuf;
|
|
if (Ipv4Hdr->Version == 4) {
|
|
InferredOriginalFragmentLength = ntohs(Ipv4Hdr->TotalLength) + sizeof(ETHERNET_HEADER);
|
|
} else if (Ipv4Hdr->Version == 6) {
|
|
Ipv6Hdr = (PIPV6_HEADER)(AuxFragBuf);
|
|
InferredOriginalFragmentLength = ntohs(Ipv6Hdr->PayloadLength) + sizeof(IPV6_HEADER) + sizeof(ETHERNET_HEADER);
|
|
}
|
|
}
|
|
}
|
|
|
|
wtap_etl_rec_dump(AuxFragBuf,
|
|
TotalFragmentLength,
|
|
// For LSO v2 packets, inferred original fragment length is ignored since length field in IP header is not filled.
|
|
InferredOriginalFragmentLength <= TotalFragmentLength ? TotalFragmentLength : InferredOriginalFragmentLength,
|
|
Iface->PcapNgIfIndex,
|
|
!(ev->EventHeader.EventDescriptor.Keyword & KW_SEND),
|
|
timestamp,
|
|
Type,
|
|
Comment,
|
|
(unsigned short)CommentLength
|
|
);
|
|
|
|
AuxFragBufOffset = 0;
|
|
NumFramesConverted++;
|
|
} else {
|
|
AuxFragBufOffset += FragLength;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
|
*
|
|
* Local variables:
|
|
* c-basic-offset: 4
|
|
* tab-width: 8
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vi: set shiftwidth=4 tabstop=8 expandtab:
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
*/
|