Callout Driver Programming Considerations
Consider the following topics when you program a Windows Filtering Platform callout driver.
If the desired filtering can be done by using the standard filtering functionality that is built in to the Windows Filtering Platform, independent software vendors (ISVs) should write user-mode management applications to configure the filter engine instead of writing kernel-mode callout drivers. A kernel-mode callout driver should only be written when you must process the network data in ways that cannot be handled by the standard, built-in filtering functionality. For information about how to write a user-mode Windows Filtering Platform management application, see the Windows Filtering Platform documentation in the Microsoft Windows SDK.
A callout driver should filter the network data at the highest possible filtering layer in the network stack. For example, if the desired filtering task can be handled at the stream layer, it should not be implemented at the network layer. For more information about recommendations of the filtering layers your driver should use to guarantee compatibility with IPsec in Windows, see Developing IPsec-Compatible Callout Drivers.
Usually, if a callout has been added to the filter engine at one of the ALE flow established filtering layers (FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 or FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6), its classifyFn callout function should never return FWP_ACTION_BLOCK for the action. A decision to authorize or reject a connection should not be made at one of the ALE flow established filtering layers. Such a decision should always be made at one of the other ALE filtering layers.
The only valid reason for such a classifyFn callout function to return FWP_ACTION_BLOCK for the action is if an error occurs that could pose a potential security risk if the established connection is not ended. In this case, returning FWP_ACTION_BLOCK for the action closes the connection to prevent the potential security risk from being exploited.
Because the filter engine typically calls a callout's callout functions at IRQL = DISPATCH_LEVEL, make sure that these functions complete their execution as quickly as possible to keep the system running efficiently. Extended execution at IRQL = DISPATCH_LEVEL can adversely affect the overall performance of the system.
Callouts should recalculate IP checksums before they call packet injection functions that inject into the receive data path because the checksum in the original packet might not be correct when the packet is reassembled from IP packet fragments. There is no reliable mechanism that indicates whether a net buffer list is reassembled from fragments.
Because of the TCP stack's locking behavior, a callout at the transport layer cannot inject a new or cloned TCP packet from the classifyFn callout function. If inline injection is desired, the callout must queue a DPC to perform the injection.
The MDL that describes the IP header in a net buffer list (NET_BUFFER_CURRENT_MDL(NET_BUFFER_LIST_FIRST_NB(netBufferList))) must be pointer-aligned when one of the packet injection functions is used to inject packet data into an outgoing path. Because an incoming packet's IP header MDL may be pointer-aligned, a callout must rebuild the IP header (if not already aligned) when injecting an incoming packet into an outgoing path.