VOID NTAPI ClassifyBasicPacketModification()

in network/trans/WFPSampler/sys/ClassifyFunctions_BasicPacketModificationCallouts.cpp [5959:6316]


VOID NTAPI ClassifyBasicPacketModification(_In_ const FWPS_INCOMING_VALUES0* pClassifyValues,
                                           _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
                                           _Inout_opt_ VOID* pNetBufferList,
                                           _In_opt_ const VOID* pClassifyContext,
                                           _In_ const FWPS_FILTER* pFilter,
                                           _In_ UINT64 flowContext,
                                           _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut)
{
      NT_ASSERT(pClassifyValues);
      NT_ASSERT(pMetadata);
      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_BASIC_PACKET_MODIFICATION_DATA));
   
#if(NTDDI_VERSION >= NTDDI_WIN8)
   
      NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6 ||
                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);
   
#else
   
      NT_ASSERT(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_TRANSPORT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_DATAGRAM_DATA_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_INBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_ICMP_ERROR_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_RECV_ACCEPT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_AUTH_CONNECT_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6);
      
#endif /// (NTDDI_VERSION >= NTDDI_WIN8)
   
      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_BASIC_PACKET_MODIFICATION_DATA));

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " ---> ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Rights: %#x]",
              KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId),
              pFilter->filterId,
              pClassifyOut->rights);

   if(pClassifyOut->rights & FWPS_RIGHT_ACTION_WRITE)
   {
      /// Packets are not available for TCP @ ALE_AUTH_CONNECT, so skip over as there is nothing to inject
      if(pNetBufferList)
      {
         PC_BASIC_PACKET_MODIFICATION_DATA* pData = (PC_BASIC_PACKET_MODIFICATION_DATA*)pFilter->providerContext->dataBuffer->data;

         if(pData)
         {
            NTSTATUS        status         = STATUS_SUCCESS;
            FWP_VALUE*      pFlags         = 0;
            INJECTION_DATA* pInjectionData = 0;

            pClassifyOut->actionType = FWP_ACTION_CONTINUE;

            pFlags = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                               &FWPM_CONDITION_FLAGS);
            if(pFlags &&
               pFlags->type == FWP_UINT32)
            {
               /// For IPsec interop, if  ALE classification is required, bypass the injection
               if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED &&
                  FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                 FWPS_METADATA_FIELD_ALE_CLASSIFY_REQUIRED))
                  HLPR_BAIL;

               /// Inject the individual fragments, but not the fragment grouping of those fragments
               if(pFlags->uint32 & FWP_CONDITION_FLAG_IS_FRAGMENT_GROUP)
                  HLPR_BAIL;
            }

            if(pData->flags & PCPMDF_MODIFY_TRANSPORT_HEADER &&
               (pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6 ||
               pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
               pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
               pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6))
            {
               UINT32  bytesRetreated = 0;
               UINT8   version        = 0;
               IPPROTO protocol       = IPPROTO_MAX;

               if(pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 ||
                  pClassifyValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6)
               {
                  if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                    FWPS_METADATA_FIELD_IP_HEADER_SIZE))
                     bytesRetreated = pMetadata->ipHeaderSize;
               }

               if(bytesRetreated)
               {
                  /// Initial offset is at the Transport Header for INBOUND_IPPACKET, so retreat the size of the IP Header ...
                  status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                         bytesRetreated,
                                                         0,
                                                         0);
                  if(status != STATUS_SUCCESS)
                  {
                     DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                                DPFLTR_ERROR_LEVEL,
                                " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                                status);
                  
                     HLPR_BAIL;
                  }
               }

               version = KrnlHlprIPHeaderGetVersionField((NET_BUFFER_LIST*)pNetBufferList);

               protocol = KrnlHlprIPHeaderGetProtocolField((NET_BUFFER_LIST*)pNetBufferList,
                                                           version == IPV4 ? AF_INET : AF_INET6);

               if(bytesRetreated)
               {
                  /// ... and advance the offset back to the original position.
                  NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                bytesRetreated,
                                                FALSE,
                                                0);
               }

               /// Exit if this isn't the protocol we are looking for
               if(protocol != pData->originalTransportData.protocol)
                  HLPR_BAIL;
               else
               {
                  UINT16 sourcePort      = 0;
                  UINT16 destinationPort = 0;
                  UINT32 bytesAdvanced   = 0;

                  if(pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 ||
                     pClassifyValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 ||
                     pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V4 ||
                     pClassifyValues->layerId == FWPS_LAYER_IPFORWARD_V6)
                  {
                     if(FWPS_IS_METADATA_FIELD_PRESENT(pMetadata,
                                                       FWPS_METADATA_FIELD_IP_HEADER_SIZE))
                        bytesAdvanced = pMetadata->ipHeaderSize;
                  }

                  if(bytesAdvanced)
                  {
                     /// Initial offset is at the IP Header for OUTBOUND_IPPACKET and IPFORWARD, so 
                     /// advance the size of the IP Header ...
                     NdisAdvanceNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                   bytesAdvanced,
                                                   FALSE,
                                                   0);
                  }

                  sourcePort      = KrnlHlprTransportHeaderGetSourcePortField((NET_BUFFER_LIST*)pNetBufferList,
                                                                              protocol);
                  destinationPort = KrnlHlprTransportHeaderGetDestinationPortField((NET_BUFFER_LIST*)pNetBufferList,
                                                                                   protocol);
                  /// Exit if the ports are not what we are looking for
                  if(pData->originalTransportData.sourcePort &&
                     sourcePort != pData->originalTransportData.sourcePort)
                      HLPR_BAIL;

                   if(pData->originalTransportData.destinationPort &&
                      destinationPort != pData->originalTransportData.destinationPort)
                      HLPR_BAIL;

                  if(bytesAdvanced)
                  {
                     /// ... and retreat the offset back to the original position.
                     status = NdisRetreatNetBufferDataStart(NET_BUFFER_LIST_FIRST_NB((NET_BUFFER_LIST*)pNetBufferList),
                                                            bytesAdvanced,
                                                            0,
                                                            0);
                     if(status != STATUS_SUCCESS)
                     {
                        DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                                   DPFLTR_ERROR_LEVEL,
                                   " !!!! ClassifyBasicPacketModification: NdisRetreatNetBufferDataStart() [status: %#x]\n",
                                   status);
                     
                        HLPR_BAIL;
                     }
                  }
               }
            }

#pragma warning(push)
#pragma warning(disable: 6014) /// pInjectionData will be freed in completionFn using BasicPacketModificationCompletionDataDestroy

            status = KrnlHlprInjectionDataCreate(&pInjectionData,
                                                 pClassifyValues,
                                                 pMetadata,
                                                 (NET_BUFFER_LIST*)pNetBufferList,
                                                 pFilter);
            HLPR_BAIL_ON_FAILURE(status);

#pragma warning(pop)

            if(pInjectionData->injectionState != FWPS_PACKET_INJECTED_BY_SELF &&
               pInjectionData->injectionState != FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
            {
               BOOLEAN    performOutOfBand = TRUE;
               FWP_VALUE* pProtocolValue   = 0;

               pClassifyOut->actionType  = FWP_ACTION_BLOCK;
               pClassifyOut->flags      |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
               pClassifyOut->rights     ^= FWPS_RIGHT_ACTION_WRITE;

               if(pFlags &&
                  pFlags->type == FWP_UINT32 &&
                  pFlags->uint32 & FWP_CONDITION_FLAG_IS_IPSEC_SECURED)
                  pInjectionData->isIPsecSecured = TRUE;

               /// Override the default of performing Out of Band with the user's specified setting ...
               if(pData->performInline)
                  performOutOfBand = FALSE;

               /// ... however, due to TCP's locking semantics, TCP can only be injected Out of Band at any transport layer or equivalent, ...
               pProtocolValue = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                          &FWPM_CONDITION_IP_PROTOCOL);
               if((pProtocolValue &&
                  pProtocolValue->uint8 == IPPROTO_TCP &&
                  pClassifyValues->layerId > FWPS_LAYER_IPFORWARD_V6_DISCARD) ||
                  pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V4 ||
                  pClassifyValues->layerId == FWPS_LAYER_STREAM_PACKET_V6)
                  performOutOfBand = TRUE;

               /// ... and inbound injection of loopback traffic requires us to use Out of Band modification as well due to address lookups.
               if(!performOutOfBand &&
                  pInjectionData->direction == FWP_DIRECTION_INBOUND)
               {
                  FWP_VALUE* pRemoteAddress = 0;

                  pRemoteAddress = KrnlHlprFwpValueGetFromFwpsIncomingValues(pClassifyValues,
                                                                             &FWPM_CONDITION_IP_REMOTE_ADDRESS);
                  if(pRemoteAddress &&
                     ((pRemoteAddress->type == FWP_UINT32 &&
                     RtlCompareMemory(&(pRemoteAddress->uint32),
                                      IPV4_LOOPBACK_ADDRESS,
                                      IPV4_ADDRESS_SIZE)) ||
                     (pRemoteAddress->type == FWP_BYTE_ARRAY16_TYPE &&
                      RtlCompareMemory(&(pRemoteAddress->byteArray16->byteArray16),
                                    IPV6_LOOPBACK_ADDRESS,
                                    IPV6_ADDRESS_SIZE))))
                     performOutOfBand = TRUE;
               }

               if(performOutOfBand)
                  status = TriggerBasicPacketModificationOutOfBand(pClassifyValues,
                                                                   pMetadata,
                                                                   pNetBufferList,
                                                                   pClassifyContext,
                                                                   pFilter,
                                                                   flowContext,
                                                                   pClassifyOut,
                                                                   pInjectionData,
                                                                   pData);
               else
                  status = TriggerBasicPacketModificationInline(pClassifyValues,
                                                                pMetadata,
                                                                pNetBufferList,
                                                                pClassifyContext,
                                                                pFilter,
                                                                flowContext,
                                                                pClassifyOut,
                                                                &pInjectionData);
            }
            else
            {
               pClassifyOut->actionType = FWP_ACTION_PERMIT;

               KrnlHlprInjectionDataDestroy(&pInjectionData);

               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_INFO_LEVEL,
                          "   -- Injection previously performed.\n");
            }

            HLPR_BAIL_LABEL:

            NT_ASSERT(status == STATUS_SUCCESS);

            if(status != STATUS_SUCCESS)
            {
               KrnlHlprInjectionDataDestroy(&pInjectionData);

               DbgPrintEx(DPFLTR_IHVNETWORK_ID,
                          DPFLTR_ERROR_LEVEL,
                          " !!!! ClassifyBasicPacketModification() [status: %#x]\n",
                          status);
            }
         }
      }
      else
         pClassifyOut->actionType = FWP_ACTION_PERMIT;
   }

   DbgPrintEx(DPFLTR_IHVNETWORK_ID,
              DPFLTR_INFO_LEVEL,
              " <--- ClassifyBasicPacketModification() [Layer: %s][FilterID: %#I64x][Action: %#x][Rights: %#x][Absorb: %s]\n",
              KrnlHlprFwpsLayerIDToString(pClassifyValues->layerId),
              pFilter->filterId,
              pClassifyOut->actionType,
              pClassifyOut->rights,
              (pClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_ABSORB) ? "TRUE" : "FALSE");

   return;
}