3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
11 * Reads IP packets from an Windows event trace logfile or an Windows event trace live session
12 * and write out a pcap file with LINKTYPE_ETHERNET, LINKTYPE_RAW or LINKTYPE_IEEE802_11.
13 * The major code of this file is from https://github.com/microsoft/etl2pcapng with some changes by Odysseus Yang.
14 * The changes mainly include but not limited
15 * 1. calling pcapng APIs instead of writing the data in the pcapng binary format by its own implementation in etl2pcapng.
16 * 2. Optimize the process of adding pcapng interfaces so it doesn't need process the same Windows event trace logfile twice,
17 that not only impacts the performance, but also breaks Wireshark live capture function.
20 #define WIN32_LEAN_AND_MEAN 1
32 // inet_ipv6.h and netiodef.h define exactly the same stuff, like _IPV6_ROUTING_HEADER and IP6F_OFF_MASK.
33 // 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
34 #define WTAP_ENCAP_ETHERNET 1
35 #define WTAP_ENCAP_RAW_IP 7
36 #define WTAP_ENCAP_IEEE_802_11 20
38 #define MAX_PACKET_SIZE 0xFFFF
40 // From the ndiscap manifest
41 #define KW_MEDIA_WIRELESS_WAN 0x200
42 #define KW_MEDIA_NATIVE_802_11 0x10000
43 #define KW_PACKET_START 0x40000000
44 #define KW_PACKET_END 0x80000000
45 #define KW_SEND 0x100000000
46 #define KW_RECEIVE 0x200000000
48 #define tidPacketFragment 1001
49 #define tidPacketMetadata 1002
50 #define tidVMSwitchPacketFragment 1003
52 // From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/windot11/ns-windot11-dot11_extsta_recv_context
54 typedef struct _NDIS_OBJECT_HEADER
{
56 unsigned char Revision
;
58 } NDIS_OBJECT_HEADER
, * PNDIS_OBJECT_HEADER
;
60 typedef struct DOT11_EXTSTA_RECV_CONTEXT
{
61 NDIS_OBJECT_HEADER Header
;
62 unsigned long uReceiveFlags
;
64 unsigned long uChCenterFrequency
;
65 unsigned short usNumberOfMPDUsReceived
;
67 unsigned char ucDataRate
;
68 unsigned long uSizeMediaSpecificInfo
;
69 void *pvMediaSpecificInfo
;
70 unsigned long long ullTimestamp
;
71 } DOT11_EXTSTA_RECV_CONTEXT
, * PDOT11_EXTSTA_RECV_CONTEXT
;
74 // From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/windot11/ne-windot11-_dot11_phy_type
75 #define DOT11_PHY_TYPE_NAMES_MAX 10
76 static const char* DOT11_PHY_TYPE_NAMES
[] = {
77 "Unknown", // dot11_phy_type_unknown = 0
78 "Fhss", // dot11_phy_type_fhss = 1
79 "Dsss", // dot11_phy_type_dsss = 2
80 "IrBaseband", // dot11_phy_type_irbaseband = 3
81 "802.11a", // dot11_phy_type_ofdm = 4
82 "802.11b", // dot11_phy_type_hrdsss = 5
83 "802.11g", // dot11_phy_type_erp = 6
84 "802.11n", // dot11_phy_type_ht = 7
85 "802.11ac", // dot11_phy_type_vht = 8
86 "802.11ad", // dot11_phy_type_dmg = 9
87 "802.11ax" // dot11_phy_type_he = 10
90 unsigned long long NumFramesConverted
;
91 char AuxFragBuf
[MAX_PACKET_SIZE
] = {0};
92 unsigned long AuxFragBufOffset
;
94 DOT11_EXTSTA_RECV_CONTEXT PacketMetadata
;
95 BOOLEAN AddWlanMetadata
;
97 typedef struct _NDIS_NET_BUFFER_LIST_8021Q_INFO
{
100 UINT32 UserPriority
: 3; // 802.1p priority
101 UINT32 CanonicalFormatId
: 1; // always 0
102 UINT32 VlanId
: 12; // VLAN Identification
103 UINT32 Reserved
: 16; // set to 0 for ethernet
107 UINT32 UserPriority
: 3; // 802.1p priority
108 UINT32 CanonicalFormatId
: 1; // always 0
109 UINT32 VlanId
: 12; // VLAN Identification
111 UINT32 Reserved
: 12; // set to 0 for Wireless LAN
116 } NDIS_NET_BUFFER_LIST_8021Q_INFO
, *PNDIS_NET_BUFFER_LIST_8021Q_INFO
;
118 // The max OOB data size might increase in the future. If it becomes larger than MaxNetBufferListInfo,
119 // this tool will print a warning and the value of MaxNetBufferListInfo in the code should be increased.
120 // From: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/nblinfo/ne-nblinfo-ndis_net_buffer_list_info
121 #define MaxNetBufferListInfo 200
122 #define Ieee8021QNetBufferListInfo 4
123 PBYTE OobData
[MaxNetBufferListInfo
];
125 typedef struct _VMSWITCH_SOURCE_INFO
{
126 unsigned long SourcePortId
;
127 char* SourcePortName
;
130 } VMSWITCH_SOURCE_INFO
, *PVMSWITCH_SOURCE_INFO
;
132 typedef struct _VMSWITCH_PACKET_FRAGMENT
{
133 unsigned long SourcePortId
;
134 unsigned long DestinationCount
;
136 } VMSWITCH_PACKET_FRAGMENT
, *PVMSWITCH_PACKET_FRAGMENT
;
138 BOOLEAN CurrentPacketIsVMSwitchPacketFragment
;
139 VMSWITCH_PACKET_FRAGMENT VMSwitchPacketFragment
;
142 struct INTERFACE
* Next
;
143 unsigned long LowerIfIndex
;
144 unsigned long MiniportIfIndex
;
145 unsigned long PcapNgIfIndex
;
150 VMSWITCH_SOURCE_INFO VMNic
;
153 #define IFACE_HT_SIZE 100
154 struct INTERFACE
* InterfaceHashTable
[IFACE_HT_SIZE
];
155 unsigned long NumInterfaces
;
157 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
);
158 void wtap_etl_add_interface(int pkt_encap
, char* interface_name
, unsigned short interface_name_length
, char* interface_desc
, unsigned short interface_desc_length
);
160 extern char g_err_info
[FILENAME_MAX
];
163 unsigned long HashInterface(unsigned long LowerIfIndex
)
165 if (CurrentPacketIsVMSwitchPacketFragment
) {
166 return VMSwitchPacketFragment
.SourcePortId
* (VMSwitchPacketFragment
.VlanId
+ 1);
172 struct INTERFACE
* GetInterface(unsigned long LowerIfIndex
)
174 struct INTERFACE
* Iface
= InterfaceHashTable
[HashInterface(LowerIfIndex
) % IFACE_HT_SIZE
];
175 while (Iface
!= NULL
) {
176 if (CurrentPacketIsVMSwitchPacketFragment
) {
177 if (Iface
->IsVMNic
&&
178 Iface
->LowerIfIndex
== LowerIfIndex
&&
179 Iface
->VlanId
== VMSwitchPacketFragment
.VlanId
&&
180 Iface
->VMNic
.SourcePortId
== VMSwitchPacketFragment
.SourcePortId
) {
184 if (!Iface
->IsVMNic
&& Iface
->LowerIfIndex
== LowerIfIndex
&& Iface
->VlanId
== 0) {
193 struct INTERFACE
* AddInterface(PEVENT_RECORD ev
, unsigned long LowerIfIndex
, unsigned long MiniportIfIndex
, int Type
)
195 struct INTERFACE
** Iface
= &InterfaceHashTable
[HashInterface(LowerIfIndex
) % IFACE_HT_SIZE
];
196 struct INTERFACE
* NewIface
= malloc(sizeof(struct INTERFACE
));
198 #define IF_STRING_MAX_SIZE 128
199 char IfName
[IF_STRING_MAX_SIZE
];
200 size_t IfNameLength
= 0;
201 char IfDesc
[IF_STRING_MAX_SIZE
];
202 size_t IfDescLength
= 0;
203 //etw pcagng interface will be 0 always, network pcagng interface will start with 1
204 static PcapNgIfIndex
= 1;
206 if (NewIface
== NULL
) {
207 g_err
= ERROR_OUTOFMEMORY
;
208 sprintf_s(g_err_info
, sizeof(g_err_info
), "malloc failed to allocate memory for NewIface");
212 NewIface
->LowerIfIndex
= LowerIfIndex
;
213 NewIface
->MiniportIfIndex
= MiniportIfIndex
;
214 NewIface
->PktEncapType
= Type
;
215 NewIface
->VlanId
= 0;
216 NewIface
->IsVMNic
= false;
218 if (CurrentPacketIsVMSwitchPacketFragment
) {
220 NewIface
->IsVMNic
= true;
222 wchar_t Buffer
[8192];
223 PROPERTY_DATA_DESCRIPTOR Desc
;
227 Desc
.PropertyName
= (unsigned long long)(L
"SourceNicName");
228 Desc
.ArrayIndex
= ULONG_MAX
;
229 ULONG ParamNameSize
= 0;
230 (void)TdhGetPropertySize(ev
, 0, NULL
, 1, &Desc
, &ParamNameSize
);
231 NewIface
->VMNic
.SourceNicName
= malloc((ParamNameSize
/ sizeof(wchar_t)) + 1);
232 if (NewIface
->VMNic
.SourceNicName
== NULL
) {
233 g_err
= ERROR_OUTOFMEMORY
;
234 sprintf_s(g_err_info
, sizeof(g_err_info
), "malloc failed to allocate memory for NewIface->VMNic.SourceNicName");
237 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(Buffer
), (PBYTE
)Buffer
);
238 if (Err
!= NO_ERROR
) {
241 Buffer
[ParamNameSize
/ sizeof(wchar_t) + 1] = L
'\0';
242 WideCharToMultiByte(CP_ACP
,
246 NewIface
->VMNic
.SourceNicName
,
247 ParamNameSize
/ sizeof(wchar_t) + 1,
250 NewIface
->VMNic
.SourceNicName
[wcslen(Buffer
)] = '\0';
253 Desc
.PropertyName
= (unsigned long long)(L
"SourcePortName");
254 Desc
.ArrayIndex
= ULONG_MAX
;
255 (void)TdhGetPropertySize(ev
, 0, NULL
, 1, &Desc
, &ParamNameSize
);
256 NewIface
->VMNic
.SourcePortName
= malloc((ParamNameSize
/ sizeof(wchar_t)) + 1);
257 if (NewIface
->VMNic
.SourcePortName
== NULL
) {
258 g_err
= ERROR_OUTOFMEMORY
;
259 sprintf_s(g_err_info
, sizeof(g_err_info
), "malloc failed to allocate memory for NewIface->VMNic.SourcePortName");
262 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(Buffer
), (PBYTE
)Buffer
);
263 if (Err
!= NO_ERROR
) {
266 Buffer
[ParamNameSize
/ sizeof(wchar_t) + 1] = L
'\0';
267 WideCharToMultiByte(CP_ACP
,
271 NewIface
->VMNic
.SourcePortName
,
272 ParamNameSize
/ sizeof(wchar_t) + 1,
275 NewIface
->VMNic
.SourcePortName
[wcslen(Buffer
)] = '\0';
278 Desc
.PropertyName
= (unsigned long long)(L
"SourceNicType");
279 Desc
.ArrayIndex
= ULONG_MAX
;
280 (void)TdhGetPropertySize(ev
, 0, NULL
, 1, &Desc
, &ParamNameSize
);
281 NewIface
->VMNic
.SourceNicType
= malloc((ParamNameSize
/ sizeof(wchar_t)) + 1);
282 if (NewIface
->VMNic
.SourceNicType
== NULL
) {
283 g_err
= ERROR_OUTOFMEMORY
;
284 sprintf_s(g_err_info
, sizeof(g_err_info
), "malloc failed to allocate memory for NewIface->VMNic.SourceNicType");
287 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(Buffer
), (PBYTE
)Buffer
);
288 if (Err
!= NO_ERROR
) {
291 Buffer
[ParamNameSize
/ sizeof(wchar_t) + 1] = L
'\0';
292 WideCharToMultiByte(CP_ACP
,
296 NewIface
->VMNic
.SourceNicType
,
297 ParamNameSize
/ sizeof(wchar_t) + 1,
300 NewIface
->VMNic
.SourceNicType
[wcslen(Buffer
)] = '\0';
303 NewIface
->VMNic
.SourcePortId
= VMSwitchPacketFragment
.SourcePortId
;
304 NewIface
->VlanId
= VMSwitchPacketFragment
.VlanId
;
307 NewIface
->Next
= *Iface
;
312 NewIface
->PcapNgIfIndex
= PcapNgIfIndex
;
314 memset(IfName
, 0, sizeof(IfName
));
315 memset(IfDesc
, 0, sizeof(IfDesc
));
316 switch (NewIface
->PktEncapType
) {
317 case WTAP_ENCAP_ETHERNET
:
318 if (NewIface
->IsVMNic
) {
319 printf("IF: medium=%s\tID=%lu\tIfIndex=%lu\tVlanID=%i",
320 NewIface
->VMNic
.SourceNicType
,
321 NewIface
->PcapNgIfIndex
,
322 NewIface
->VMNic
.SourcePortId
,
329 NewIface
->VMNic
.SourcePortName
,
330 NewIface
->VMNic
.SourceNicType
,
331 NewIface
->VMNic
.SourcePortId
,
336 printf("IF: medium=eth\tID=%u\tIfIndex=%u\tVlanID=%i", NewIface
->PcapNgIfIndex
, NewIface
->LowerIfIndex
, NewIface
->VlanId
);
337 StringCchPrintfA(IfName
, IF_STRING_MAX_SIZE
, "eth:%lu:%i", NewIface
->LowerIfIndex
, NewIface
->VlanId
);
340 case WTAP_ENCAP_IEEE_802_11
:
341 printf("IF: medium=wifi ID=%lu\tIfIndex=%lu", NewIface
->PcapNgIfIndex
, NewIface
->LowerIfIndex
);
342 StringCchPrintfA(IfName
, IF_STRING_MAX_SIZE
, "wifi:%lu", NewIface
->LowerIfIndex
);
344 case WTAP_ENCAP_RAW_IP
:
345 printf("IF: medium=mbb ID=%lu\tIfIndex=%lu", NewIface
->PcapNgIfIndex
, NewIface
->LowerIfIndex
);
346 StringCchPrintfA(IfName
, IF_STRING_MAX_SIZE
, "mbb:%lu", NewIface
->LowerIfIndex
);
349 StringCchLengthA(IfName
, IF_STRING_MAX_SIZE
, &IfNameLength
);
351 if (NewIface
->LowerIfIndex
!= NewIface
->MiniportIfIndex
) {
352 printf("\t(LWF over IfIndex %lu)", NewIface
->MiniportIfIndex
);
353 StringCchPrintfA(IfDesc
, IF_STRING_MAX_SIZE
, "LWF over IfIndex %lu", NewIface
->MiniportIfIndex
);
354 StringCchLengthA(IfDesc
, IF_STRING_MAX_SIZE
, &IfDescLength
);
357 if (NewIface
->VlanId
!= 0) {
358 StringCchPrintfA(IfDesc
+ IfDescLength
, IF_STRING_MAX_SIZE
, " VlanID=%i ", NewIface
->VlanId
);
359 StringCchLengthA(IfDesc
, IF_STRING_MAX_SIZE
, &IfDescLength
);
364 wtap_etl_add_interface(NewIface
->PktEncapType
, IfName
, (unsigned short)IfNameLength
, IfDesc
, (unsigned short)IfDescLength
);
368 void ParseVmSwitchPacketFragment(PEVENT_RECORD ev
)
370 // Parse the current VMSwitch packet event for use elsewhere.
371 // NB: Here we only do per-packet parsing. For any event fields that only need to be
372 // parsed once and written into an INTERFACE, we do the parsing in AddInterface.
374 PROPERTY_DATA_DESCRIPTOR Desc
;
376 PNDIS_NET_BUFFER_LIST_8021Q_INFO pNblVlanInfo
;
379 unsigned long OobLength
;
380 Desc
.PropertyName
= (unsigned long long)L
"OOBDataSize";
381 Desc
.ArrayIndex
= ULONG_MAX
;
382 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(OobLength
), (PBYTE
)&OobLength
);
383 if (Err
!= NO_ERROR
) {
385 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty OobLength failed, err is 0x%x", Err
);
389 if (OobLength
> sizeof(OobData
)) {
390 g_err
= ERROR_INVALID_DATA
;
391 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
));
395 Desc
.PropertyName
= (unsigned long long)L
"OOBData";
396 Desc
.ArrayIndex
= ULONG_MAX
;
397 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, OobLength
, (PBYTE
)&OobData
);
398 if (Err
!= NO_ERROR
) {
400 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty OobData failed, err is 0x%x", Err
);
404 pNblVlanInfo
= (PNDIS_NET_BUFFER_LIST_8021Q_INFO
)&OobData
[Ieee8021QNetBufferListInfo
];
405 VMSwitchPacketFragment
.VlanId
= pNblVlanInfo
->TagHeader
.VlanId
;
408 Desc
.PropertyName
= (unsigned long long)L
"SourcePortId";
409 Desc
.ArrayIndex
= ULONG_MAX
;
410 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(VMSwitchPacketFragment
.SourcePortId
), (PBYTE
)&VMSwitchPacketFragment
.SourcePortId
);
411 if (Err
!= NO_ERROR
) {
413 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty SourcePortId failed, err is 0x%x", Err
);
418 Desc
.PropertyName
= (unsigned long long)L
"DestinationCount";
419 Desc
.ArrayIndex
= ULONG_MAX
;
420 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(VMSwitchPacketFragment
.DestinationCount
), (PBYTE
)&VMSwitchPacketFragment
.DestinationCount
);
421 if (Err
!= NO_ERROR
) {
423 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty DestinationCount failed, err is 0x%x", Err
);
428 void etw_dump_write_ndiscap_event(PEVENT_RECORD ev
, ULARGE_INTEGER timestamp
)
431 unsigned long LowerIfIndex
;
433 struct INTERFACE
* Iface
;
434 unsigned long FragLength
;
435 PROPERTY_DATA_DESCRIPTOR Desc
;
437 unsigned long TotalFragmentLength
;
438 unsigned long InferredOriginalFragmentLength
= 0;
439 PETHERNET_HEADER EthHdr
;
440 PIPV4_HEADER Ipv4Hdr
;
441 PIPV6_HEADER Ipv6Hdr
;
443 if ((ev
->EventHeader
.EventDescriptor
.Id
!= tidPacketFragment
&&
444 ev
->EventHeader
.EventDescriptor
.Id
!= tidPacketMetadata
&&
445 ev
->EventHeader
.EventDescriptor
.Id
!= tidVMSwitchPacketFragment
)) {
449 CurrentPacketIsVMSwitchPacketFragment
= (ev
->EventHeader
.EventDescriptor
.Id
== tidVMSwitchPacketFragment
);
450 if (CurrentPacketIsVMSwitchPacketFragment
) {
451 ParseVmSwitchPacketFragment(ev
);
454 Desc
.PropertyName
= (unsigned long long)L
"LowerIfIndex";
455 Desc
.ArrayIndex
= ULONG_MAX
;
456 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(LowerIfIndex
), (PBYTE
)&LowerIfIndex
);
457 if (Err
!= NO_ERROR
) {
459 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty LowerIfIndex failed, err is 0x%x", Err
);
463 Iface
= GetInterface(LowerIfIndex
);
465 if (!!(ev
->EventHeader
.EventDescriptor
.Keyword
& KW_MEDIA_NATIVE_802_11
)) {
466 Type
= WTAP_ENCAP_IEEE_802_11
;
467 } else if (!!(ev
->EventHeader
.EventDescriptor
.Keyword
& KW_MEDIA_WIRELESS_WAN
)) {
468 Type
= WTAP_ENCAP_RAW_IP
;
470 Type
= WTAP_ENCAP_ETHERNET
;
473 // Record the IfIndex if it's a new one.
475 unsigned long MiniportIfIndex
;
476 Desc
.PropertyName
= (unsigned long long)L
"MiniportIfIndex";
477 Desc
.ArrayIndex
= ULONG_MAX
;
478 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(MiniportIfIndex
), (PBYTE
)&MiniportIfIndex
);
479 if (Err
!= NO_ERROR
) {
481 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty MiniportIfIndex failed, err is 0x%x", Err
);
484 Iface
= AddInterface(
490 } else if (Iface
->PktEncapType
!= Type
) {
491 printf("WARNING: inconsistent media type in packet events!\n");
495 // We generated the list of interfaces directly from the
496 // packet traces themselves, so there must be a bug.
497 g_err
= ERROR_INVALID_DATA
;
498 sprintf_s(g_err_info
, sizeof(g_err_info
), "Packet with unrecognized IfIndex");
502 // Save off Ndis/Wlan metadata to be added to the next packet
503 if (ev
->EventHeader
.EventDescriptor
.Id
== tidPacketMetadata
) {
504 unsigned long MetadataLength
= 0;
505 Desc
.PropertyName
= (unsigned long long)L
"MetadataSize";
506 Desc
.ArrayIndex
= ULONG_MAX
;
507 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(MetadataLength
), (PBYTE
)&MetadataLength
);
508 if (Err
!= NO_ERROR
) {
510 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty MetadataSize failed, err is 0x%x", Err
);
514 if (MetadataLength
!= sizeof(PacketMetadata
)) {
515 g_err
= ERROR_INVALID_DATA
;
516 sprintf_s(g_err_info
, sizeof(g_err_info
), "Unknown Metadata length. Expected %lu, got %lu", (unsigned long)sizeof(DOT11_EXTSTA_RECV_CONTEXT
), MetadataLength
);
520 Desc
.PropertyName
= (unsigned long long)L
"Metadata";
521 Desc
.ArrayIndex
= ULONG_MAX
;
522 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, MetadataLength
, (PBYTE
)&PacketMetadata
);
523 if (Err
!= NO_ERROR
) {
525 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty Metadata failed, err is 0x%x", Err
);
529 AddWlanMetadata
= true;
533 // N.B.: Here we are querying the FragmentSize property to get the
534 // total size of the packet, and then reading that many bytes from
535 // the Fragment property. This is unorthodox (normally you are
536 // supposed to use TdhGetPropertySize to get the size of a property)
537 // but required due to the way ndiscap puts packet contents in
538 // multiple adjacent properties (which happen to be contiguous in
541 Desc
.PropertyName
= (unsigned long long)L
"FragmentSize";
542 Desc
.ArrayIndex
= ULONG_MAX
;
543 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, sizeof(FragLength
), (PBYTE
)&FragLength
);
544 if (Err
!= NO_ERROR
) {
546 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty FragmentSize failed, err is 0x%x", Err
);
550 if (FragLength
> RTL_NUMBER_OF(AuxFragBuf
) - AuxFragBufOffset
) {
551 g_err
= ERROR_INVALID_DATA
;
552 sprintf_s(g_err_info
, sizeof(g_err_info
), "Packet too large (size = %u) and skipped", AuxFragBufOffset
+ FragLength
);
556 Desc
.PropertyName
= (unsigned long long)L
"Fragment";
557 Desc
.ArrayIndex
= ULONG_MAX
;
558 Err
= TdhGetProperty(ev
, 0, NULL
, 1, &Desc
, FragLength
, (PBYTE
)(AuxFragBuf
+ AuxFragBufOffset
));
559 if (Err
!= NO_ERROR
) {
561 sprintf_s(g_err_info
, sizeof(g_err_info
), "TdhGetProperty Fragment failed, err is 0x%x", Err
);
565 // The KW_PACKET_START and KW_PACKET_END keywords are used as follows:
566 // -A single-event packet has both KW_PACKET_START and KW_PACKET_END.
567 // -A multi-event packet consists of an event with KW_PACKET_START followed
568 // by an event with KW_PACKET_END, with zero or more events with neither
569 // keyword in between.
571 // So, we accumulate fragments in AuxFragBuf until KW_PACKET_END is
572 // encountered, then call PcapNgWriteEnhancedPacket and start over. There's
573 // no need for us to even look for KW_PACKET_START.
575 // NB: Starting with Windows 8.1, only single-event packets are traced.
576 // This logic is here to support packet captures from older systems.
578 if (!!(ev
->EventHeader
.EventDescriptor
.Keyword
& KW_PACKET_END
)) {
580 if (ev
->EventHeader
.EventDescriptor
.Keyword
& KW_MEDIA_NATIVE_802_11
&&
581 AuxFragBuf
[1] & 0x40) {
582 // Clear Protected bit in the case of 802.11
583 // Ndis captures will be decrypted in the etl file
585 AuxFragBuf
[1] = AuxFragBuf
[1] & 0xBF; // _1011_1111_ - Clear "Protected Flag"
588 // COMMENT_MAX_SIZE must be multiple of 4
589 #define COMMENT_MAX_SIZE 256
590 char Comment
[COMMENT_MAX_SIZE
] = { 0 };
591 size_t CommentLength
= 0;
593 if (AddWlanMetadata
) {
594 if (PacketMetadata
.uPhyId
> DOT11_PHY_TYPE_NAMES_MAX
) {
595 PacketMetadata
.uPhyId
= 0; // Set to unknown if outside known bounds.
598 Err
= StringCchPrintfA(Comment
, COMMENT_MAX_SIZE
, "PID=%d ProcessorNumber=%d Packet Metadata: ReceiveFlags:0x%x, PhyType:%s, CenterCh:%u, NumMPDUsReceived:%u, RSSI:%d, DataRate:%u",
599 ev
->EventHeader
.ProcessId
,
600 ev
->BufferContext
.ProcessorNumber
,
601 PacketMetadata
.uReceiveFlags
,
602 DOT11_PHY_TYPE_NAMES
[PacketMetadata
.uPhyId
],
603 PacketMetadata
.uChCenterFrequency
,
604 PacketMetadata
.usNumberOfMPDUsReceived
,
605 PacketMetadata
.lRSSI
,
606 PacketMetadata
.ucDataRate
);
608 AddWlanMetadata
= false;
609 memset(&PacketMetadata
, 0, sizeof(DOT11_EXTSTA_RECV_CONTEXT
));
610 } else if (CurrentPacketIsVMSwitchPacketFragment
) {
611 if (VMSwitchPacketFragment
.DestinationCount
> 0) {
612 Err
= StringCchPrintfA(Comment
, COMMENT_MAX_SIZE
, "PID=%d ProcessorNumber=%d VlanId=%d SrcPortId=%d SrcNicType=%s SrcNicName=%s SrcPortName=%s DstNicCount=%d",
613 ev
->EventHeader
.ProcessId
,
614 ev
->BufferContext
.ProcessorNumber
,
616 Iface
->VMNic
.SourcePortId
,
617 Iface
->VMNic
.SourceNicType
,
618 Iface
->VMNic
.SourceNicName
,
619 Iface
->VMNic
.SourcePortName
,
620 VMSwitchPacketFragment
.DestinationCount
623 Err
= StringCchPrintfA(Comment
, COMMENT_MAX_SIZE
, "PID=%d ProcessorNumber=%d VlanId=%d SrcPortId=%d SrcNicType=%s SrcNicName=%s SrcPortName=%s",
624 ev
->EventHeader
.ProcessId
,
625 ev
->BufferContext
.ProcessorNumber
,
627 Iface
->VMNic
.SourcePortId
,
628 Iface
->VMNic
.SourceNicType
,
629 Iface
->VMNic
.SourceNicName
,
630 Iface
->VMNic
.SourcePortName
634 Err
= StringCchPrintfA(Comment
, COMMENT_MAX_SIZE
, "PID=%d ProcessorNumber=%d", ev
->EventHeader
.ProcessId
, ev
->BufferContext
.ProcessorNumber
);
637 if (Err
!= NO_ERROR
) {
638 printf("Failed converting comment to string with error: %d\n", Err
);
640 Err
= StringCchLengthA(Comment
, COMMENT_MAX_SIZE
, &CommentLength
);
642 if (Err
!= NO_ERROR
) {
643 printf("Failed getting length of comment string with error: %d\n", Err
);
645 memset(Comment
, 0, COMMENT_MAX_SIZE
);
649 TotalFragmentLength
= AuxFragBufOffset
+ FragLength
;
651 // Parse the packet to see if it's truncated. If so, try to recover the original length.
652 if (Type
== WTAP_ENCAP_ETHERNET
) {
653 if (TotalFragmentLength
>= sizeof(ETHERNET_HEADER
)) {
654 EthHdr
= (PETHERNET_HEADER
)AuxFragBuf
;
655 if (ntohs(EthHdr
->Type
) == ETHERNET_TYPE_IPV4
&&
656 TotalFragmentLength
>= sizeof(IPV4_HEADER
) + sizeof(ETHERNET_HEADER
)) {
657 Ipv4Hdr
= (PIPV4_HEADER
)(EthHdr
+ 1);
658 InferredOriginalFragmentLength
= ntohs(Ipv4Hdr
->TotalLength
) + sizeof(ETHERNET_HEADER
);
659 } else if (ntohs(EthHdr
->Type
) == ETHERNET_TYPE_IPV6
&&
660 TotalFragmentLength
>= sizeof(IPV6_HEADER
) + sizeof(ETHERNET_HEADER
)) {
661 Ipv6Hdr
= (PIPV6_HEADER
)(EthHdr
+ 1);
662 InferredOriginalFragmentLength
= ntohs(Ipv6Hdr
->PayloadLength
) + sizeof(IPV6_HEADER
) + sizeof(ETHERNET_HEADER
);
665 } else if (Type
== WTAP_ENCAP_RAW_IP
) {
666 // Raw frames begins with an IPv4/6 header.
667 if (TotalFragmentLength
>= sizeof(IPV4_HEADER
)) {
668 Ipv4Hdr
= (PIPV4_HEADER
)AuxFragBuf
;
669 if (Ipv4Hdr
->Version
== 4) {
670 InferredOriginalFragmentLength
= ntohs(Ipv4Hdr
->TotalLength
) + sizeof(ETHERNET_HEADER
);
671 } else if (Ipv4Hdr
->Version
== 6) {
672 Ipv6Hdr
= (PIPV6_HEADER
)(AuxFragBuf
);
673 InferredOriginalFragmentLength
= ntohs(Ipv6Hdr
->PayloadLength
) + sizeof(IPV6_HEADER
) + sizeof(ETHERNET_HEADER
);
678 wtap_etl_rec_dump(AuxFragBuf
,
680 // For LSO v2 packets, inferred original fragment length is ignored since length field in IP header is not filled.
681 InferredOriginalFragmentLength
<= TotalFragmentLength
? TotalFragmentLength
: InferredOriginalFragmentLength
,
682 Iface
->PcapNgIfIndex
,
683 !(ev
->EventHeader
.EventDescriptor
.Keyword
& KW_SEND
),
687 (unsigned short)CommentLength
690 AuxFragBufOffset
= 0;
691 NumFramesConverted
++;
693 AuxFragBufOffset
+= FragLength
;
699 * Editor modelines - https://www.wireshark.org/tools/modelines.html
704 * indent-tabs-mode: nil
707 * vi: set shiftwidth=4 tabstop=8 expandtab:
708 * :indentSize=4:tabSize=8:noTabs=true: