NTSTATUS KrnlHlprClassifyDataAcquireLocalCopy(_Inout_ CLASSIFY_DATA* pClassifyData,
                                              _In_ const FWPS_INCOMING_VALUES* pClassifyValues,
                                              _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                              _In_opt_ VOID* pPacket,
                                              _In_opt_ const VOID* pClassifyContext,
                                              _In_ const FWPS_FILTER* pFilter,
                                              _In_ const UINT64 flowContext,
                                              _In_ FWPS_CLASSIFY_OUT* pClassifyOut)
{
#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> KrnlHlprClassifyDataAcquireLocalCopy()\n");

#endif /// DBG
   
   NT_ASSERT(pClassifyData);
   NT_ASSERT(pClassifyValues);
   NT_ASSERT(pMetadata);
   NT_ASSERT(pFilter);
   NT_ASSERT(pClassifyOut);

   NTSTATUS status = STATUS_SUCCESS;

   pClassifyData->pClassifyValues = KrnlHlprFwpsIncomingValuesCreateLocalCopy(pClassifyValues);
   HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pClassifyValues,
                                         status);

   pClassifyData->pMetadataValues = KrnlHlprFwpsIncomingMetadataValuesCreateLocalCopy(pMetadata);
   HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pMetadataValues,
                                         status);

   if(pPacket)
   {
      if(pClassifyValues->layerId == FWPS_LAYER_STREAM_V4 ||
         pClassifyValues->layerId == FWPS_LAYER_STREAM_V4_DISCARD ||
         pClassifyValues->layerId == FWPS_LAYER_STREAM_V6 ||
         pClassifyValues->layerId == FWPS_LAYER_STREAM_V6_DISCARD)
      {
         pClassifyData->pPacket = KrnlHlprFwpsStreamCalloutIOPacketCreateLocalCopy((FWPS_STREAM_CALLOUT_IO_PACKET*)pPacket);
         HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pPacket,
                                               status);
      }

#if(NTDDI_VERSION >= NTDDI_WIN7)

      /// LayerData at the FWPM_LAYER_ALE_{BIND/CONNECT}_REDIRECT_V{4/6} is obtained via KrnlHlprRedirectDataCreate()
      else if(pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V4 ||
              pClassifyValues->layerId == FWPS_LAYER_ALE_BIND_REDIRECT_V6)
      {
         pClassifyData->pPacket = 0;
      }

#endif /// (NTDDI_VERSION >= NTDDI_WIN7)

      else
      {
         if(NET_BUFFER_LIST_NEXT_NBL((NET_BUFFER_LIST*)pPacket))
         {
            pClassifyData->chainedNBL     = TRUE;
            pClassifyData->numChainedNBLs = 1;
         }

         if(pClassifyData->chainedNBL &&
            (
            /// The IPPACKET and IPFORWARD Layers allow for Fragment Grouping if the option is enabled
            pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
            pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
            pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
            pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6

#if(NTDDI_VERSION >= NTDDI_WIN8)

            /// The NDIS layers allow for batched NBLs provided the callout was registered with FWP_CALLOUT_FLAG_ALLOW_L2_BATCH_CLASSIFY set
            ||
            pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_ETHERNET ||
            pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_ETHERNET ||
            pClassifyValues->layerId == FWPS_LAYER_INBOUND_MAC_FRAME_NATIVE ||
            pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_MAC_FRAME_NATIVE ||
            pClassifyValues->layerId == FWPS_LAYER_INGRESS_VSWITCH_ETHERNET ||
            pClassifyValues->layerId == FWPS_LAYER_EGRESS_VSWITCH_ETHERNET

#endif /// (NTDDI_VERSION >= NTDDI_WIN8)

            ))
         {
            for(NET_BUFFER_LIST* pCurrentNBL = (NET_BUFFER_LIST*)pPacket;
                pCurrentNBL;
                pClassifyData->numChainedNBLs++)
            {
               NET_BUFFER_LIST* pNextNBL = NET_BUFFER_LIST_NEXT_NBL(pCurrentNBL);

               FwpsReferenceNetBufferList(pCurrentNBL,
                                          TRUE);

               pCurrentNBL = pNextNBL;

#if DBG

               InterlockedIncrement64((LONG64*)&(g_OutstandingNBLReferences));

#endif /// DBG

            }

            pClassifyData->pPacket = pPacket;
         }
         else
         {
            /// Otherwise we expect to receive a single NBL
            NT_ASSERT(NET_BUFFER_LIST_NEXT_NBL((NET_BUFFER_LIST*)pPacket) == 0);

            FwpsReferenceNetBufferList((NET_BUFFER_LIST*)pPacket,
                                       TRUE);

            pClassifyData->pPacket = pPacket;

#if DBG
         
            InterlockedIncrement64((LONG64*)&(g_OutstandingNBLReferences));
         
#endif /// DBG

         }
      }
   }

#if(NTDDI_VERSION >= NTDDI_WIN7)
   
      if(pClassifyContext)
      {
         /// ClassifyHandle for these layers is obtained in REDIRECT_DATA
         if(pClassifyValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V4 &&
            pClassifyValues->layerId != FWPS_LAYER_ALE_CONNECT_REDIRECT_V6 &&
            pClassifyValues->layerId != FWPS_LAYER_ALE_BIND_REDIRECT_V4 &&
            pClassifyValues->layerId != FWPS_LAYER_ALE_BIND_REDIRECT_V6)
         {
            status = FwpsAcquireClassifyHandle((VOID*)pClassifyContext,
                                               0,
                                               &(pClassifyData->classifyContextHandle));
            HLPR_BAIL_ON_FAILURE(status);
         }
      }
#else
   
      UNREFERENCED_PARAMETER(pClassifyContext);
   
#endif /// (NTDDI_VERSION >= NTDDI_WIN7)

   if(pFilter)
   {
      pClassifyData->pFilter = KrnlHlprFwpsFilterCreateLocalCopy(pFilter);
      HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pFilter,
                                            status);
   }

   pClassifyData->flowContext = flowContext;

   if(pClassifyOut)
   {
      pClassifyData->pClassifyOut = KrnlHlprFwpsClassifyOutCreateLocalCopy(pClassifyOut);
      HLPR_BAIL_ON_NULL_POINTER_WITH_STATUS(pClassifyData->pClassifyOut,
                                            status);
   }

   HLPR_BAIL_LABEL:

   if(status != STATUS_SUCCESS)
      KrnlHlprClassifyDataReleaseLocalCopy(pClassifyData);

#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- KrnlHlprClassifyDataAcquireLocalCopy() [status: %#x]\n",
              status);

#endif /// DBG
   
   return status;
}
NTSTATUS KrnlHlprRedirectDataPopulate(_Inout_ REDIRECT_DATA* pRedirectData,
                                      _In_ const VOID* pClassifyContext,
                                      _In_ const FWPS_FILTER* pFilter,
                                      _In_ FWPS_CLASSIFY_OUT* pClassifyOut)
{
#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> KrnlHlprRedirectDataPopulate()\n");

#endif /// DBG
   
   NT_ASSERT(pRedirectData);
   NT_ASSERT(pClassifyContext);
   NT_ASSERT(pFilter);
   NT_ASSERT(pClassifyOut);
   NT_ASSERT(pFilter->providerContext);
   NT_ASSERT(pFilter->providerContext->type == FWPM_GENERAL_CONTEXT);
   NT_ASSERT(pFilter->providerContext->dataBuffer);
   NT_ASSERT(pFilter->providerContext->dataBuffer->size == sizeof(PC_PROXY_DATA));
   NT_ASSERT(pFilter->providerContext->dataBuffer->data);

   NTSTATUS status = STATUS_SUCCESS;

   status = FwpsAcquireClassifyHandle((void*)pClassifyContext,
                                      0,
                                      &(pRedirectData->classifyHandle));
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! KrnlHlprRedirectDataPopulate : FwpsAcquireClassifyHandle() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

#if(NTDDI_VERSION >= NTDDI_WIN8)

   status = FwpsRedirectHandleCreate(&WFPSAMPLER_PROVIDER,
                                     0,
                                     &(pRedirectData->redirectHandle));
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! KrnlHlprRedirectDataPopulate : FwpsRedirectHandleCreate() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

#endif

   status = FwpsAcquireWritableLayerDataPointer(pRedirectData->classifyHandle,
                                                pFilter->filterId,
                                                0,
                                                &(pRedirectData->pWritableLayerData),
                                                pClassifyOut);
   if(status != STATUS_SUCCESS)
   {
      DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                 DPFLTR_ERROR_LEVEL,
                 " !!!! KrnlHlprRedirectDataPopulate : FwpsAcquireWritableLayerDataPointer() [status: %#x]\n",
                 status);

      HLPR_BAIL;
   }

   pRedirectData->pProxyData = (PC_PROXY_DATA*)pFilter->providerContext->dataBuffer->data;

   HLPR_BAIL_LABEL:

   if(status != STATUS_SUCCESS)
      KrnlHlprRedirectDataPurge(pRedirectData);

#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- KrnlHlprRedirectDataPopulate() [status: %#x]\n",
              status);

#endif /// DBG
   
   return status;
}
NTSTATUS KrnlHlprPendDataPopulate(_Inout_ PEND_DATA* pPendData,
                                  _In_ const FWPS_INCOMING_VALUES* pClassifyValues,
                                  _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                  _In_opt_ NET_BUFFER_LIST* pNBL,
                                  _In_ const FWPS_FILTER* pFilter,
                                  _In_opt_ VOID* pClassifyContext,                         /* 0 */
                                  _In_opt_ FWPS_CLASSIFY_OUT* pClassifyOut)                /* 0 */
{
#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> KrnlHlprPendDataPopulate()\n");

#endif /// DBG
   
   NT_ASSERT(pPendData);
   NT_ASSERT(pClassifyValues);
   NT_ASSERT(pMetadata);
   NT_ASSERT(pFilter);

   NTSTATUS status = STATUS_SUCCESS;

   pPendData->layerID = pClassifyValues->layerId;

#if(NTDDI_VERSION >= NTDDI_WIN7)

   if(pPendData->layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V4 ||
      pPendData->layerID == FWPS_LAYER_ALE_ENDPOINT_CLOSURE_V6)
   {
      NT_ASSERT(pClassifyContext);
      NT_ASSERT(pClassifyOut);

      if(pClassifyContext &&
         pClassifyOut)
      {
         status = FwpsAcquireClassifyHandle(pClassifyContext,
                                            0,
                                            &(pPendData->classifyHandle));
         if(status != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! KrnlHlprPendDataPopulate : FwpsAcquireClassifyHandle() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }

         status = FwpsPendClassify(pPendData->classifyHandle,
                                   pFilter->filterId,
                                   0,
                                   pClassifyOut);
         if(status != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! KrnlHlprPendDataPopulate : FwpsPendClassify() [status: %#x]\n",
                       status);
         
            HLPR_BAIL;
         }

         RtlCopyMemory(&(pPendData->classifyOut),
                       pClassifyOut,
                       sizeof(FWPS_CLASSIFY_OUT));

         pPendData->pPCPendData = pFilter->providerContext->dataBuffer->data;
         pPendData->isPended    = TRUE;
      }
      else
      {
         status = STATUS_INVALID_PARAMETER;

         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! KrnlHlprPendDataPopulate : [status: %#x][pClassifyContext: %#p][pClassifyOut: %#p]\n",
                    status,
                    pClassifyContext,
                    pClassifyOut);

         HLPR_BAIL;
      }
   }
   else

#else

   UNREFERENCED_PARAMETER(pClassifyContext);
   UNREFERENCED_PARAMETER(pClassifyOut);

#endif /// (NTDDI_VERSION >= NTDDI_WIN7)

   {


      if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                        FWPS_METADATA_FIELD_COMPLETION_HANDLE))
      {
         status = FwpsPendOperation(pMetadata->completionHandle,
                                    &(pPendData->completionContext));
         if(status != STATUS_SUCCESS)
         {
            DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                       DPFLTR_ERROR_LEVEL,
                       " !!!! KrnlHlprPendDataPopulate : FwpsPendOperation() [status: %#x]\n",
                       status);

            HLPR_BAIL;
         }

         pPendData->pNBL        = pNBL;
         pPendData->pPCPendData = pFilter->providerContext->dataBuffer->data;
         pPendData->isPended    = TRUE;
      }
      else
      {
         status = STATUS_INVALID_HANDLE;

         DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                    DPFLTR_ERROR_LEVEL,
                    " !!!! KrnlHlprPendDataPopulate() [status: %#x]\n",
                    status);
      }
   }

   HLPR_BAIL_LABEL:

   if(status != STATUS_SUCCESS)
      KrnlHlprPendDataPurge(pPendData);


#if DBG
   
   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- KrnlHlprPendDataPopulate() [status: %#x]\n",
              status);

#endif /// DBG
   
   return status;
}