LATER... ei_kerberos_kdc_session_key ...
[wireshark-sm.git] / extcap / etw_ndiscap.c
blob56fa92ec111624f566b1aac541cc022a1cc228fd
1 /* etw_ndiscap.c
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
8 */
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
21 #include <windows.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <stdbool.h>
25 #include <evntrace.h>
26 #include <evntcons.h>
27 #include <tdh.h>
28 #include <strsafe.h>
29 #include <winsock2.h>
30 #include <netiodef.h>
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
53 #pragma pack(push,8)
54 typedef struct _NDIS_OBJECT_HEADER {
55 unsigned char Type;
56 unsigned char Revision;
57 unsigned short Size;
58 } NDIS_OBJECT_HEADER, * PNDIS_OBJECT_HEADER;
60 typedef struct DOT11_EXTSTA_RECV_CONTEXT {
61 NDIS_OBJECT_HEADER Header;
62 unsigned long uReceiveFlags;
63 unsigned long uPhyId;
64 unsigned long uChCenterFrequency;
65 unsigned short usNumberOfMPDUsReceived;
66 long lRSSI;
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;
72 #pragma pack(pop)
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 {
98 union {
99 struct {
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
104 } TagHeader;
106 struct {
107 UINT32 UserPriority : 3; // 802.1p priority
108 UINT32 CanonicalFormatId : 1; // always 0
109 UINT32 VlanId : 12; // VLAN Identification
110 UINT32 WMMInfo : 4;
111 UINT32 Reserved : 12; // set to 0 for Wireless LAN
112 } WLanTagHeader;
114 PVOID Value;
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;
128 char* SourceNicName;
129 char* SourceNicType;
130 } VMSWITCH_SOURCE_INFO, *PVMSWITCH_SOURCE_INFO;
132 typedef struct _VMSWITCH_PACKET_FRAGMENT {
133 unsigned long SourcePortId;
134 unsigned long DestinationCount;
135 short VlanId;
136 } VMSWITCH_PACKET_FRAGMENT, *PVMSWITCH_PACKET_FRAGMENT;
138 BOOLEAN CurrentPacketIsVMSwitchPacketFragment;
139 VMSWITCH_PACKET_FRAGMENT VMSwitchPacketFragment;
141 struct INTERFACE {
142 struct INTERFACE* Next;
143 unsigned long LowerIfIndex;
144 unsigned long MiniportIfIndex;
145 unsigned long PcapNgIfIndex;
146 int PktEncapType;
147 short VlanId;
149 BOOLEAN IsVMNic;
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];
161 extern int g_err;
163 unsigned long HashInterface(unsigned long LowerIfIndex)
165 if (CurrentPacketIsVMSwitchPacketFragment) {
166 return VMSwitchPacketFragment.SourcePortId * (VMSwitchPacketFragment.VlanId + 1);
167 } else {
168 return LowerIfIndex;
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) {
181 return Iface;
183 } else {
184 if (!Iface->IsVMNic && Iface->LowerIfIndex == LowerIfIndex && Iface->VlanId == 0) {
185 return Iface;
188 Iface = Iface->Next;
190 return NULL;
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");
209 exit(1);
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;
224 int Err;
226 // SourceNicName
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");
235 exit(1);
237 Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
238 if (Err != NO_ERROR) {
239 Buffer[0] = L'\0';
241 Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
242 WideCharToMultiByte(CP_ACP,
244 Buffer,
246 NewIface->VMNic.SourceNicName,
247 ParamNameSize / sizeof(wchar_t) + 1,
248 NULL,
249 NULL);
250 NewIface->VMNic.SourceNicName[wcslen(Buffer)] = '\0';
252 // SourcePortName
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");
260 exit(1);
262 Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
263 if (Err != NO_ERROR) {
264 Buffer[0] = L'\0';
266 Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
267 WideCharToMultiByte(CP_ACP,
269 Buffer,
271 NewIface->VMNic.SourcePortName,
272 ParamNameSize / sizeof(wchar_t) + 1,
273 NULL,
274 NULL);
275 NewIface->VMNic.SourcePortName[wcslen(Buffer)] = '\0';
277 // SourceNicType
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");
285 exit(1);
287 Err = TdhGetProperty(ev, 0, NULL, 1, &Desc, sizeof(Buffer), (PBYTE)Buffer);
288 if (Err != NO_ERROR) {
289 Buffer[0] = L'\0';
291 Buffer[ParamNameSize / sizeof(wchar_t) + 1] = L'\0';
292 WideCharToMultiByte(CP_ACP,
294 Buffer,
296 NewIface->VMNic.SourceNicType,
297 ParamNameSize / sizeof(wchar_t) + 1,
298 NULL,
299 NULL);
300 NewIface->VMNic.SourceNicType[wcslen(Buffer)] = '\0';
303 NewIface->VMNic.SourcePortId = VMSwitchPacketFragment.SourcePortId;
304 NewIface->VlanId = VMSwitchPacketFragment.VlanId;
307 NewIface->Next = *Iface;
309 *Iface = NewIface;
310 NumInterfaces++;
312 NewIface->PcapNgIfIndex = PcapNgIfIndex;
313 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,
323 NewIface->VlanId
325 StringCchPrintfA(
326 IfName,
327 IF_STRING_MAX_SIZE,
328 "%s:%s:%lu:%i",
329 NewIface->VMNic.SourcePortName,
330 NewIface->VMNic.SourceNicType,
331 NewIface->VMNic.SourcePortId,
332 NewIface->VlanId
335 else {
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);
339 break;
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);
343 break;
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);
347 break;
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);
362 printf("\n");
364 wtap_etl_add_interface(NewIface->PktEncapType, IfName, (unsigned short)IfNameLength, IfDesc, (unsigned short)IfDescLength);
365 return NewIface;
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;
375 int Err;
376 PNDIS_NET_BUFFER_LIST_8021Q_INFO pNblVlanInfo;
378 // Get VLAN from OOB
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) {
384 g_err = Err;
385 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty OobLength failed, err is 0x%x", Err);
386 return;
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));
392 return;
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) {
399 g_err = Err;
400 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty OobData failed, err is 0x%x", Err);
401 return;
404 pNblVlanInfo = (PNDIS_NET_BUFFER_LIST_8021Q_INFO)&OobData[Ieee8021QNetBufferListInfo];
405 VMSwitchPacketFragment.VlanId = pNblVlanInfo->TagHeader.VlanId;
407 // SourcePortId
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) {
412 g_err = Err;
413 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty SourcePortId failed, err is 0x%x", Err);
414 return;
417 // DestinationCount
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) {
422 g_err = Err;
423 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty DestinationCount failed, err is 0x%x", Err);
424 return;
428 void etw_dump_write_ndiscap_event(PEVENT_RECORD ev, ULARGE_INTEGER timestamp)
430 int Err;
431 unsigned long LowerIfIndex;
433 struct INTERFACE* Iface;
434 unsigned long FragLength;
435 PROPERTY_DATA_DESCRIPTOR Desc;
436 int Type;
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)) {
446 return;
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) {
458 g_err = Err;
459 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty LowerIfIndex failed, err is 0x%x", Err);
460 return;
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;
469 } else {
470 Type = WTAP_ENCAP_ETHERNET;
473 // Record the IfIndex if it's a new one.
474 if (Iface == NULL) {
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) {
480 g_err = Err;
481 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty MiniportIfIndex failed, err is 0x%x", Err);
482 return;
484 Iface = AddInterface(
486 LowerIfIndex,
487 MiniportIfIndex,
488 Type
490 } else if (Iface->PktEncapType != Type) {
491 printf("WARNING: inconsistent media type in packet events!\n");
494 if (Iface == NULL) {
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");
499 exit(1);
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) {
509 g_err = Err;
510 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty MetadataSize failed, err is 0x%x", Err);
511 return;
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);
517 return;
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) {
524 g_err = Err;
525 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty Metadata failed, err is 0x%x", Err);
526 return;
529 AddWlanMetadata = true;
530 return;
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
539 // memory).
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) {
545 g_err = Err;
546 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty FragmentSize failed, err is 0x%x", Err);
547 return;
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);
553 return;
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) {
560 g_err = Err;
561 sprintf_s(g_err_info, sizeof(g_err_info), "TdhGetProperty Fragment failed, err is 0x%x", Err);
562 return;
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,
615 Iface->VlanId,
616 Iface->VMNic.SourcePortId,
617 Iface->VMNic.SourceNicType,
618 Iface->VMNic.SourceNicName,
619 Iface->VMNic.SourcePortName,
620 VMSwitchPacketFragment.DestinationCount
622 } else {
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,
626 Iface->VlanId,
627 Iface->VMNic.SourcePortId,
628 Iface->VMNic.SourceNicType,
629 Iface->VMNic.SourceNicName,
630 Iface->VMNic.SourcePortName
633 } else {
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);
639 } else {
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);
644 CommentLength = 0;
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,
679 TotalFragmentLength,
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),
684 timestamp,
685 Type,
686 Comment,
687 (unsigned short)CommentLength
690 AuxFragBufOffset = 0;
691 NumFramesConverted++;
692 } else {
693 AuxFragBufOffset += FragLength;
699 * Editor modelines - https://www.wireshark.org/tools/modelines.html
701 * Local variables:
702 * c-basic-offset: 4
703 * tab-width: 8
704 * indent-tabs-mode: nil
705 * End:
707 * vi: set shiftwidth=4 tabstop=8 expandtab:
708 * :indentSize=4:tabSize=8:noTabs=true: