Back to Blog

ETW Network Telemetry on Windows: Kernel-Network vs TCPIP and the Correlation Problem

Christopher 11 min read
etwwindowsnetworkingtcpipndis

Windows exposes network activity through several ETW providers that sit at different layers of the stack. Each one emits different fields, and they do not all know which process they are working on behalf of. The provider chosen therefore determines what can be seen and what has to be reconstructed. This post describes the three providers that see network activity, what each one emits, how to attribute an event to a process, and the correlation problem that makes packet-to-process attribution difficult.

GUIDs, keyword masks, and event fields below were read from the provider ETW manifests and the Microsoft.Diagnostics.Tracing.TraceEvent (PerfView) source. Values that are version-specific or commonly cited without a primary source are flagged inline. Confirm any GUID against the manifest on the target build before relying on it.

The three layers of network ETW

There is no single network provider on Windows. There are three layers, and the layer determines what is visible and what must be rebuilt from other events.

LayerProviderWhat it seesProcess context
Socket and connection eventsMicrosoft-Windows-Kernel-Network, or the classic NT Kernel Logger TcpIp eventsconnect, accept, disconnect, send, receive, per operation, with a byte size and a process idYes, except on the failure event
Connection lifecycle and TCP state machineMicrosoft-Windows-TCPIPcontrol-block state transitions, retransmissions, receive-segment coalescing and offload pathsYes, plus Activity IDs for correlation
Raw packets on the wireMicrosoft-Windows-NDIS-PacketCaptureframe bytes, MiniportIfIndex, FragmentSize, the raw Fragment blobNo, captured in DPC or interrupt context

A fourth provider exists only to link the other layers together. Microsoft-Windows-Networking-Correlation emits ActivityTransfer events that map one layer's Activity ID to another's.

The central constraint follows from this split. The layer that has the packet bytes, NDIS, does not know the process. The layers that know the process, Kernel-Network and TCPIP, do not have the packet bytes. Every difficult part of network telemetry on Windows follows from that separation.

Socket and connection events: Kernel-Network

This is the layer for the question of which process talked to which endpoint, when, and roughly how many bytes. There are two ways to get these events and they are closely related.

The classic NT Kernel Logger path is enabled with the NetworkTCPIP kernel keyword (0x00010000) on the Windows Kernel provider ({9E814AAD-3204-11D2-9A82-006008A86939}). This is the strongly typed route in the TraceEvent library. The manifest provider Microsoft-Windows-Kernel-Network ({7DD42A49-5329-4832-8DFD-43D979153A88}) is enabled by name or GUID with the IPv4 (0x10) and IPv6 (0x20) keywords. The two surface broadly equivalent kernel TCP and UDP telemetry.

The manifest provider's send and receive templates carry PID, size, daddr, saddr, dport, sport, seqnum, and connid. The connect template adds handshake detail: mss, rcvwin, window-scale options, and the SACK and timestamp flags. Because these are per-operation events with a byte count, this is the per-operation, byte-level layer. TCPIP, covered below, is not.

Enabling it from C#

With the TraceEvent library, enable the classic network keyword on the kernel session together with the process keyword, then consume the strongly typed events. Run elevated. Note the per-process filter on the first line of each handler.

using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Session;

// Kernel providers live on the kernel logger session.
using var session = new TraceEventSession("ETDuckyNetwork") { BufferSizeMB = 256 };

session.EnableKernelProvider(
    KernelTraceEventParser.Keywords.NetworkTCPIP |   // 0x00010000, TCP and UDP events
    KernelTraceEventParser.Keywords.Process);        // to resolve PID to image

var kernel = session.Source.Kernel;

kernel.TcpIpConnect += d =>
{
    if (!IsTracked(d.ProcessID)) return;             // cheapest check, first
    Console.WriteLine($"{d.ProcessID} connect {d.daddr}:{d.dport}");
};

kernel.TcpIpDisconnect += d =>
{
    if (!IsTracked(d.ProcessID)) return;
    Console.WriteLine($"{d.ProcessID} disconnect {d.daddr}:{d.dport}");
};

session.Source.Process(); // blocks until the session is stopped

IPv6 is not free. It arrives through parallel event classes such as TcpIpConnectIPV6 and TcpIpSendIPV6. Handlers wired only to the IPv4 events silently miss every IPv6 connection on a modern network. Wire both.

The logman equivalent

To capture to an .etl without code, the manifest provider is one command. The provider name is quoted because of the hyphens.

:: IPv4 only (0x10); use 0x30 for IPv4 and IPv6
logman create trace NetCap -p "Microsoft-Windows-Kernel-Network" 0x10 -o C:\traces\net.etl -ets
logman stop  NetCap -ets

The failure event does not carry a process id

The TCP connection-failure event breaks the assumption that every network event has a usable ProcessID. Its manifest template exposes only the protocol and a failure code, so the process id is not reliable on that event. Detection logic that needs to attribute a failed connection to a process, for example a process repeatedly reaching a dead endpoint, cannot get that from Kernel-Network alone. The options are to infer the process id from a preceding connect event that was paired earlier, or to move up to the TCPIP provider. This is a property of the data, not a defect in the consumer.

Connection lifecycle: Microsoft-Windows-TCPIP

The TCPIP provider ({2F07E2EE-15DB-40F1-90EF-9D7BA282188A}) is the provider for the behavior of a connection rather than the fact of it: control-block state transitions, retransmissions, receive-segment coalescing, and offload paths. It carries process context and participates in ETW Activity ID correlation, which is what ties together the sequence of events belonging to one logical connection.

The specifics here should stay conservative. The TCPIP per-event schema is large and version-dependent, and it is not enumerated event by event in this post. Two points are safe and useful. First, TCPIP is state-oriented, not per-datagram-byte-oriented, so it is the right provider for why a connection is slow, retransmitting, or resetting, and the wrong one for summing the bytes a process sent. Second, its events participate in Activity ID correlation, which is what allows a connection timeline to be reconstructed and, as covered next, bridged down to the packet layer. Before building on a specific TCPIP event, read its template from the manifest on the target build.

Raw packets: NDIS-PacketCapture

Microsoft-Windows-NDIS-PacketCapture ({2ED6006E-4729-4609-B423-3EE7BCD678EF}) is the provider that netsh trace and the NetEventPacketCapture cmdlets use. It captures at the NDIS layer, near the bottom of the stack, and emits raw frame fragments: MiniportIfIndex, LowerIfIndex, FragmentSize, and the Fragment binary blob.

It does not emit a trustworthy process id. Packets are captured in DPC or interrupt context, so the thread running when a frame arrives is almost never the socket owner. Any process id read from an NDIS event is noise. As the Digital Operatives PAINT researchers noted in 2012, the Activity ID fields on the packet layer and the connection layer occur in different OS threads, so they cannot be correlated in the ordinary ETW fashion. That sentence states the whole correlation problem.

The correlation problem

Combining the layers makes the difficulty concrete. Given a captured packet from NDIS, the goal is the owning connection and its process id from TCPIP or Kernel-Network. Both sides stamp events with Activity IDs, but two facts prevent a direct join.

First, kernel-mode Activity IDs are not automatic. In user mode, ETW stores the Activity ID per thread, so events logged on one thread share it. In kernel mode, the Activity ID must be passed explicitly at log time. The stack does this, but it means the tidy per-thread grouping available in user mode does not apply.

Second, the two sides run on different threads. The connection-side processing and the NDIS-side packet handling happen on different OS threads, so their Activity IDs differ. A join on Activity ID alone finds nothing.

Windows provides a dedicated provider to bridge the gap. Microsoft-Windows-Networking-Correlation ({83ED54F0-4D48-4E45-B16E-726FFD1FA4AF}) emits ActivityTransfer events, its ActivityTransfer keyword being mask 0x1. The transfer event payload carries a SourceProvider GUID and a context value that map an activity in one provider to an activity in another. Enabling it alongside NDIS and TCPIP allows a packet's activity to be stitched up to the TCPIP connection activity, and from there to the process.

The documented approach for packet-to-process reconstruction on Windows is therefore three providers at once: NDIS-PacketCapture for bytes, TCPIP for the connection and process id, and Networking-Correlation for the transfer events that join them. The PAINT project built packet attribution on this combination and reported near-zero error for UDP and roughly a fifth false negatives for TCP, which is a useful calibration for how reliable this is even when done correctly.

:: Capture all three layers so packets can be tied to connections and PIDs.
logman create trace NetFull -ets -o C:\traces\full.etl -p "Microsoft-Windows-NDIS-PacketCapture" 0xFFFFFFFFFFFFFFFF
logman update NetFull -p "Microsoft-Windows-TCPIP"                  0xFFFFFFFFFFFFFFFF -ets
logman update NetFull -p "Microsoft-Windows-Networking-Correlation" 0x1               -ets
logman stop   NetFull -ets

Attributing an event to a process

Three rules make process attribution reliable.

Filter by process id first. Kernel network volume is large. Decoding fields for events that will be discarded is how a consumer falls behind and starts dropping events. Each handler should open with the tracked-process check before any field decode.

if (!IsTracked(ev.ProcessID)) return;   // before decoding any payload field

Track process start and stop, and follow children. The process id on a network event is only meaningful with a record of what the process is, and process ids are reused. Subscribing to the kernel process events maintains that mapping and allows a spawn chain to be followed end to end, for example a parent application whose embedded browser child makes the actual requests.

kernel.ProcessStart += d => OnProcessStart(d.ProcessID, d.ParentID, d.ImageFileName);
kernel.ProcessStop  += d => OnProcessExit(d.ProcessID);

Treat process-less events as process-less. The TCP failure event and every NDIS packet are cases where the obvious process id is absent or wrong. Backfill from a correlated connection event where possible; otherwise record the event as unknown rather than attributing it to whichever thread held the CPU.

Two sessions, not one

Kernel providers and user-mode manifest providers cannot share one ETW session. Kernel providers must live on the kernel logger, or a private kernel session, while user-mode manifest providers go on a regular session. Enabling a kernel provider on a normal session fails at EnableProvider time.

A network pipeline that also wants the context around a connection, such as the DNS query that produced an address, the Schannel handshake, or the WinHTTP request, runs two sessions side by side and sizes their buffers to their different volumes. The kernel session carries the high-volume connection events; the user session carries the lower-volume context providers. Correlating the user-mode context back to the kernel connection is a separate task, but the architectural rule is fixed: plan for two sessions.

Firewall drops through the WFP provider

The Windows Filtering Platform exposes an ETW provider that surfaces firewall activity. Enabling it on the kernel session and filtering to drop events surfaces blocked connections; allow events are far too voluminous to retain. WFP telemetry is best-effort. Layer and filter fields vary by event shape, and the provider is not present on every SKU, so enabling it belongs in a try and catch, and field extraction should be defensive. This generalizes: a real ETW pipeline is mostly defensive code, because providers vary by Windows edition and build, event templates shift between versions, and field extraction throws on unexpected shapes. Routing every event through extraction that substitutes empty values rather than failing keeps one unusual event from stopping the capture loop.

Choosing a provider

QuestionProvider
Which process connected to which endpoint, and how much data?Kernel-Network, or classic NT Kernel Logger TcpIp. Has the process id and a per-operation byte size.
Why is this connection slow, retransmitting, or resetting?TCPIP. State machine, retransmits, offload.
What is actually on the wire?NDIS-PacketCapture. Raw frames, no process.
Tie a captured packet back to the owning process.NDIS plus TCPIP plus Networking-Correlation, joined through ActivityTransfer events. Expect imperfect TCP attribution.
Which connections were blocked by the firewall?The WFP ETW provider, drops only, parsed defensively.

How ET Ducky captures network telemetry

ET Ducky's Windows agent, in the component named NetPath, turns these providers into a per-process network picture on an endpoint without installing a packet-capture driver or standing up a SIEM. It runs the two-session model: a kernel session carrying the classic NetworkTCPIP and process keywords, and a user session carrying DNS, Schannel, WinHTTP and WinINet, and the authentication providers. It filters to tracked processes at the point of ingest, follows process spawn chains so a browser child or a script host is attributed to the application that launched it, and enables the WFP provider for drop events. Connection attempts are paired with their later success or disconnect to measure duration, and the process-less failure event is recorded as unknown rather than misattributed. The DNS and TLS context from the user session is folded in so a connection reads as a named endpoint and handshake rather than a bare address and port.

A per-host capture built by hand requires interactive access to each machine, manual trace start and stop, and per-machine analysis, which does not aggregate across a fleet. Running the capture and correlation continuously on the host, and emitting summarized records, is what makes the same telemetry usable across many endpoints at once.

Frequently asked questions

What is the Microsoft-Windows-Kernel-Network provider GUID?

It is {7DD42A49-5329-4832-8DFD-43D979153A88}. It defines two keywords, IPv4 (0x10) and IPv6 (0x20). Confirm the value on a target host with logman query providers | findstr Kernel-Network.

What is the difference between Microsoft-Windows-Kernel-Network and Microsoft-Windows-TCPIP?

Kernel-Network emits per-operation events (connect, accept, disconnect, send, receive) that carry a process id and a byte size, answering which process connected where and how much data moved. TCPIP ({2F07E2EE-15DB-40F1-90EF-9D7BA282188A}) emits connection lifecycle and TCP state-machine detail such as control-block transitions, retransmissions, and offload paths, answering how a connection behaved. Use Kernel-Network for attribution and volume, TCPIP for connection health.

How do you attribute a captured packet to a process on Windows?

NDIS-PacketCapture captures raw frames with no reliable process context, because capture happens in DPC or interrupt context. Correlate NDIS frames to TCPIP connection events using the Networking-Correlation provider ({83ED54F0-4D48-4E45-B16E-726FFD1FA4AF}), which emits ActivityTransfer events that map one layer's Activity ID to another's, then read the process id from the connection layer.

Why do network ETW events not share a single Activity ID?

In user mode the Activity ID is stored per thread, so events on one thread share it automatically. In kernel mode it must be passed explicitly at log time. A single logical connection is processed across different threads, so the packet layer and the connection layer stamp different Activity IDs, which is why a dedicated correlation provider emits transfer events to link them.

References and values to verify on your build

Provider GUIDs and keyword masks above are from the provider ETW manifests and the microsoft/perfview TraceEvent source. Kernel-Network manifest values trace to the Windows 7 7600 manifest and have been stable since, but are version-0 values, so confirm them on the target build. The classic NT Kernel Logger TcpIp task GUID is commonly cited as {9A280AC0-C8E0-11D1-84E2-00C04FB998A2}; that literal is not confirmed here, so use the TraceEvent typed TcpIp events rather than decoding it by GUID. TCPIP per-event field schemas are large and version-dependent; read them from the manifest before coding against a specific event. The Digital Operatives PAINT writeup, "Process Attribution in Network Traffic" (2012), is the reference for the NDIS, TCPIP, and Networking-Correlation approach and its accuracy characteristics.

ET Ducky

Documentation and pricing are available on this site.

View Pricing