Developing IPsec-Compatible Callout Drivers

Layers That Are Compatible With IPsec

To be fully compatible with the Windows implementation of IPsec that begins with Windows Vista and Windows Server 2008, a callout driver should be registered at one of the following run-time filtering layers:

TCP Packet Filtering
Stream Layers:



Non-TCP and Non-Error ICMP Packet Filtering
Datagram-Data Layers:





Except for the case when incoming packets must be rebuilt before they are receive-injected from a datagram-data layer, callout drivers that are registered at these data layers are compatible with IPsec.

Layers That Are Incompatible With IPsec

Network and forwarding layers are incompatible with IPsec because at these layers IPsec traffic has not yet been decrypted or verified. IPsec policies are enforced at the transport layer, which occurs after a network layer classify operation.

The following run-time filtering layers are incompatible with IPsec because IPsec processing in Windows occurs below the following layers:









Special Considerations for Transport Layers

To make a callout driver that is registered with a transport layer (FWPS_LAYER_XXX_TRANSPORT_V4 or _V6) compatible with IPsec, follow these guidelines:

  1. Register the callout at ALE authorize receive/accept layers (FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 or FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6) in addition to transport layers (FWPS_LAYER_XXX_TRANSPORT_V4 or _V6).

  2. To prevent interference with internal Windows IPsec processing, register the callout at a sublayer that has a lower weight than FWPM_SUBLAYER_UNIVERSAL. Use the FwpmSubLayerEnum0 function to find the sublayer's weight. For information about this function, see the Windows Filtering Platform documentation in the Microsoft Windows SDK.

  3. An incoming transport packet that requires ALE classification must be inspected at the ALE authorize receive/accept layers (FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 or FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6). Such a packet must be permitted from incoming transport layers. Beginning with Windows Vista with Service Pack 1 (SP1) and Windows Server 2008, use the FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED metadata flag to determine whether the incoming packet will be indicated to the FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4 and FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6 filtering layers. This metadata flag replaces the FWP_CONDITION_FLAG_REQUIRES_ALE_CLASSIFY condition flag that was used in Windows Vista.

  4. To prevent interference with internal Windows IPsec processing, do not intercept IPsec tunnel-mode traffic at transport layers if the IPsec traffic is not yet detunneled. The following code example shows how to bypass such packets.

    if (packetInfo.ipsecInformation.inbound.isTunnelMode &&
     classifyOut->actionType = FWP_ACTION_PERMIT;
     goto Exit;
  5. After an IPsec-protected packet is decrypted and verified at the transport layer, the AH/ESP header remains in the IP header. If such a packet has to be reinjected back into the TCP/IP stack, the IP header must be rebuilt to remove the AH/ESP header. Beginning with Windows Vista with SP1 and Windows Server 2008, you can do this by cloning the packet and calling the FwpsConstructIpHeaderForTransportPacket0 function that has the headerIncludeHeaderSize parameter set to the IP header size of the cloned packet.

  6. At the ALE receive/accept layer, a callout can detect IPsec-protected traffic by checking whether the FWP_CONDITION_FLAG_IS_IPSEC_SECURED flag is set. At transport layers, a callout can detect IPsec-protected traffic by calling the FwpsGetPacketListSecurityInformation0 function and checking whether the FWPS_PACKET_LIST_INFORMATION0 flag is set in the queryFlags parameter.

Working With IPsec ESP Packets

When the engine indicates decrypted encapsulating security payload (ESP) packets, it truncates them to exclude trailing ESP data. Because of the way the engine handles such packets, the MDL data in the NET_BUFFER structure does not reflect the correct packet length. The correct length can be obtained by using the NET_BUFFER_DATA_LENGTH macro to retrieve the data length of the NET_BUFFER structure.