Stream Inspection

Inline Stream Inspection

Inline stream modifiers can edit stream data by permitting or blocking a part of the indicated data by setting the value of the countBytesEnforced member of the FWPS_STREAM_CALLOUT_IO_PACKET0 structure as they return FWP_ACTION_PERMIT or FWP_ACTION_BLOCK from the classifyFn callout function. They can also call the FwpsStreamInjectAsync0 function to add new content to the stream. This content can be new or can replace blocked data.

To replace a pattern found in the middle of an indicated segment (for example, n bytes followed by a pattern of p bytes followed by m bytes), the callout would follow these steps:

  1. The callout's classifyFn function is called by using n + p + m bytes.

  2. The callout returns FWP_ACTION_PERMIT with the countBytesEnforced member set to n.

  3. The callout's classifyFn function is called again with p + m bytes. WFP will call classifyFn again if countBytesEnforced is less than the indicated amount.

  4. From the classifyFn function, the callout calls the FwpsStreamInjectAsync0 function to inject the replacement pattern p'. The callout then returns FWP_ACTION_BLOCK with countBytesEnforced set to p.

  5. The callout's classifyFn function is called again with m bytes.

  6. The callout returns FWP_ACTION_PERMIT with countBytesEnforced set to m.

If the indicated data is insufficient for the callout to make an inspection decision, it can set the streamAction member of the FWPS_STREAM_CALLOUT_IO_PACKET0 structure to FWPS_STREAM_ACTION_NEED_MORE_DATA and set the countBytesRequired member to the minimal amount WFP should accumulate before the data is indicated again. When streamAction is set, the callout should return FWP_ACTION_NONE from the classifyFn function.

WFP can accumulate up to 8 MB of stream data when FWPS_STREAM_ACTION_NEED_MORE_DATA is set. WFP will set the FWPS_CLASSIFY_OUT_FLAG_BUFFER_LIMIT_REACHED flag when it calls the callout's classifyFn function and the buffer space is exhausted. When the latter flag is set, the callout must accept the indicated data in full. A callout must not return FWPS_STREAM_ACTION_NEED_MORE_DATA when the FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA flag is set.

For the convenience of being able to scan a stream pattern from a flat buffer, WFP provides the FwpsCopyStreamDataToBuffer0 utility function, which can copy indicated stream data into a contiguous buffer.

Out-of-Band Stream Inspection

For out-of-band inspection or modification, a stream callout would follow the similar pattern as the packet inspection callout: it would first clone all indicated stream segments for deferred processing, and then it would block those segments. The inspected or modified data is later injected back into the data stream. When injecting data out-of-band, the callout must return FWP_ACTION_BLOCK on all indicated segments to guarantee integrity of the resulting stream. An out-of-band inspection module must not arbitrarily inject a FIN (which indicates no more data from the sender) into an outgoing data stream. If the module must drop the connection, its classifyFn callout function must set the streamAction member of the FWPS_STREAM_CALLOUT_IO_PACKET0 structure to FWPS_STREAM_ACTION_DROP_CONNECTION.

Note It is a violation of contract for callouts to switch from out-of-band to inline, and can cause unexpected behaviors. Ensure out-of-band callouts are meeting each of the specified criteria.

Because stream data can be indicated as a NET_BUFFER_LIST chain, FWP provides the FwpsCloneStreamData0 and FwpsDiscardClonedStreamData0 utility functions that operate on net buffer list chains.

WFP also supports stream data throttling for the incoming direction. If a callout cannot keep pace with the incoming data rate, it can return FWPS_STREAM_ACTION_DEFER to "pause" the stream. The stream can then be "resumed" by calling the FwpsStreamContinue0 function. Deferring a stream with this function causes the TCP/IP stack to stop ACK-processing incoming data. This causes the TCP sliding window to decrease toward 0.

For out-of-band stream inspection callouts, FwpsStreamContinue0 must not be called while the FwpsStreamInjectAsync0 function is called.

Injected stream data will not be re-indicated to the callout, but it will be made available to stream callouts from lower-weight sublayers.

The Windows Filtering Platform Stream Edit Sample in the Windows driver samples repository on GitHub shows how to perform inline and out-of-band editing at the stream layer.

Note  Windows Server 2008 and later do not support removal of a stream filter during the following processes:

  • The callout is performing out-of-band packet injection.

  • The callout is requesting more data by setting the streamAction member of the FWPS_STREAM_CALLOUT_IO_PACKET0 structure to FWPS_STREAM_ACTION_NEED_MORE_DATA.

  • The callout is deferring a stream by setting the streamAction member of the FWPS_STREAM_CALLOUT_IO_PACKET0 structure to FWPS_STREAM_ACTION_DEFER.

Dynamic Stream Inspection

Windows 7 and later support dynamic stream inspections. A dynamic stream inspection operates on an existing stream data flow, rather than creating and tearing down a new one. A callout driver that can perform dynamic stream inspections should set the FWP_CALLOUT_FLAG_ALLOW_MID_STREAM_INSPECTION flag in the Flags member of the FWPS_CALLOUT1 or FWPS_CALLOUT2 structure.

Avoiding Unnecessary Inspections

To perform stream inspections only on connections that the driver is interested in, a callout can set the FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW flag in the Flags member of the FWPS_CALLOUT0 structure. This callout will be ignored on all other connections. Performance will be improved and the driver will not have to maintain unnecessary state data.

Stream Layer Waterfall Model

The stream layer in WFP follows a strict waterfall model; that is, a callout in this layer will be allowed to inspect a stream segment only if the previous callout (if any) explicitly permitted it. If a callout blocks an indicated segment, that segment is permanently taken out of the stream and no callouts will be allowed to inspect it.

Moreover:

  1. Every non-inspect callout at the stream layer must explicitly assign a value to the actionType member of the classifyOut parameter regardless of what value may have been previously set in that parameter.
  2. The FWPS_RIGHT_ACTION_WRITE flag in the rights member of the classifyOut parameter has no significance in the WFP stream layer. Callouts at this layer should not check for the presence of this flag. Callouts may process the indicated layerData parameter regardless of the value of classifyOut->rights.