Hi!
I am new to WFP and driver development in general and OSR have been so helpful through this learning journey. Right now I am facing a similar problem described on this post: Cisco AnyConnect VPN compatibility woes. I have tried to apply the solution posted there, but I can’t get it to work, so I was hoping to the get some pointer of what could be happening and how could I get this to work. Let me described what I am trying to do:
I am trying to implement the same scenario that Jason_Stephenson described here , a local DNS proxy and a callout driver that redirects the DNS queries to this proxy. I managed to get the redirection to the DNS proxy working by registering a callout filter at the FWPM_LAYER_OUTBOUND_TRANSPORT_V4 layer
, and then re-inject the packet using FwpsInjectTransportSendAsync
. My problem now is that I can’t get the “de-proxy” of the response from the DNS proxy to work, as I understand for the Windows DNS client to accept the response the packet must come from the original IP and port, so the DNS proxy response’s source address and port should be restored to the original DNS query destination address and port.
Supposedly the “de-proxy” should be done on the FWPM_LAYER_INBOUND_TRANSPORT_V4
, I have register a filter without conditions and the response packet is not hitting it (I can identify the packet by the remote ip address and port). Neither the packet is present on the FWPM_LAYER_INBOUND_TRANSPORT_V4_DISCARD
nor on the FWPM_LAYER_DATAGRAM_DATA_V4/FWPM_LAYER_DATAGRAM_V4_DISCARD
or FWPM_LAYER_INBOUND_IPPACKET_V4_DISCARD
layers. Can another callout be absorbing the packet? or Is the TCP/IP stack dropping the packet?, but if it is dropping the packet, how come I can’t get the packet on any of the discard layers?
The only layer where i can get the packet is FWPM_LAYER_INBOUND_IPPACKET_V4
layer, so I am modifying the packet on this layer. My code looks like this:
void classifyFunc(...)
{
NET_BUFFER_LIST * netBufferList = (NET_BUFFER_LIST *)layerData;
// Verify that the packet wasn't injected before
FWPS_PACKET_INJECTION_STATE packetState = FwpsQueryPacketInjectionState(injectionNetworkHandle, netBufferList, NULL);
if (packetState == FWPS_PACKET_INJECTED_BY_SELF || packetState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF)
{
classifyOut->actionType = FWP_ACTION_PERMIT;
if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT)
{
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
}
return;
}
MetadataValues* metadataValues = ExAllocatePoolWithTag(NonPagedPool, sizeof(MetadataValues), SOME_TAG);
// Get all information needed for redirection (compartmentId, interfaceIndex, subInterfaceIndex, IpHeaderSize, etc) and stored it on metadataValues
// Block-adsorb the original NET_BUFFER_LIST and queued it to be processed by a worker thread
FwpsReferenceNetBufferList(netBufferList, TRUE);
classifyOut->actionType = FWP_ACTION_BLOCK;
classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
// Queue the packet to be modified out of bounds by a worker thread.
addPacketToWorkerQueue(netBufferList, metadataValues);
}
void workerThreadFunc(NET_BUFFER_LIST * netBufferList, MetadataValues * metadataValues) //this Is the function that the worker thread executes in order to modify and re-inject the packet
{
//Retread the NET_BUFFER_LIST by the amount specified in inMetadataValues->ipHeaderSize
// Cloned it and advance the original NET_BUFFER_LIST to its original position.
NET_BUFFER_LIST * clonedBufferList;
NDIS_STATUS ndis_status = NdisRetreatNetBufferListDataStart(netBufferList, metadataValues->ipHeaderSize, 0, NULL, NULL);
status = FwpsAllocateCloneNetBufferList(netBufferList, NULL, NULL, 0, &clonedBufferList);
NdisAdvanceNetBufferListDataStart(netBufferList, metadataValues->ipHeaderSize, FALSE, NULL);
// Advance the cloned NET_BUFFER_LIST to the transport header
NET_BUFFER * netBuffer = NET_BUFFER_LIST_FIRST_NB(clonedBufferList);
NdisAdvanceNetBufferDataStart(netBuffer, metadataValues->ipHeaderSize, FALSE, NULL);
// Get the UDP header and modify the source port
UDP_HEADER * udpHeader = (UDP_HEADER *)NdisGetDataBuffer(netBuffer, sizeof(UDP_HEADER), NULL, sizeof(UINT16), 0);
udpHeader->srcPort = RtlUshortByteSwap(53);
// Go back to the beginning of the IP Header
ndis_status = NdisRetreatNetBufferDataStart(netBuffer, metadataValues->ipHeaderSize, 0, NULL);
// Get the IP header and modify the source and destination ip addresses
UINT32 source = RtlUlongByteSwap(originalDNS);
UINT32 destiny = RtlUlongByteSwap(nicIpAddress);
IP_HEADER_V4 * ipHeader = (IP_HEADER_V4 *)NdisGetDataBuffer(netBuffer, metadataValues->ipHeaderSize, NULL, sizeof(UINT16), 0);
RtlCopyMemory(ipHeader->pSourceAddress, &source, sizeof(UINT32));
RtlCopyMemory(ipHeader->pDestinationAddress, &destiny, sizeof(UINT32));
// Finally fix the IpHeader Checksum
fixIpChecksum(ipHeader, metadataValues->ipHeaderSize);
//Inject the modified NET_BUFFER_LIST
FwpsInjectNetworkReceiveAsync(
injectionNetworkHandle, //This injection handle was created using the FWPS_INJECTION_TYPE_NETWORK flag
NULL,
0,
metadataValues->compartmentId,
metadataValues->interfaceIndex,
metadataValues->subInterfaceIndex,
clonedBuffer,
InjectToDNSClientCompleteFunc,
NULL);
}
To recalculate the Ip Header checksum I am using this function:
void fixIpChecksum(IP_HEADER_V4* ipHeader, UINT32 size)
{
UINT32 sum = 0;
UINT32 words = size / 2;
UINT16 UNALIGNED* pStart = (UINT16*)ipHeader;
ipHeader->checksum = 0;
for (UINT8 i = 0; i < words; i++)
{
sum += pStart[i];
}
sum = (sum & 0x0000ffff) + (sum >> 16);
sum += (sum >> 16);
ipHeader->checksum = (UINT16)~sum;
}
The injection function returns STATUS_SUCCESS
and on the completion function the status of the injected NET_BUFFER_LIST is STATUS_SUCCESS
. But the DNS client is timing out and the packet is not hitting any of the filters that I previously placed on the TRANSPORT_V4/TRANSPORT_V4_DISCARD
, IPPACKET_V4/IPPACKET_V4_DISCARD
nor DATAGRAMA_DATA_V4/DATAGRAM_DATA_V4_DISCARD
layers.
I also tried to modify and re inject the DNS proxy response packet on the FWPM_LAYER_OUTBOUND_TRANSPORT_V4
layer using the FwpsInjectTransportSendAsync
using the following steps:
- Verify that the packet hasn’t being re injected before:
- Retread the NET_BUFFER_LIST by the amount specified on inMetadataValues->ipHeaderSize + inMetadataValues->ipHeaderSize
- Clone the NET_BUFFER_LIST
- Restore the original NET_BUFFER_LIST (advance the packet by the amount specified on inMetadataValues->ipHeaderSize + inMetadataValues->ipHeaderSize)
- Block-adsorb the original NET_BUFFER_LIST
- Advance the cloned NET_BUFFER_LIST to the transport
- Get UDP header and modify the source port
- Retread the cloned NET_BUFFER_LIST to the ip header
- Use
FwpsConstructIpHeaderForTransportPacket
to create the new Ip Header, using the original DNS ip as source and the machines NIC’s ip as the destination ip - Re-inject the cloned NET_BUFFER_LIST using
FwpsInjectTransportSendAsyc
function
As before the injection function returnsSTATUS_SUCCESS
, but the completion function returnsSTATUS_DATA_NOT_ACCEPTED
.
Also, I have seen some posts on the msdn forums recommending to use the ALE_CONNECT_REDIRECT_V4 layer but as far as I have tested this layer doesn’t seem work with DNS Queries, my test filter did hit the packet and performed the redirection successfully, but the packet didn’t made it to the DNS proxy, It was again swallowed by the TCP/IP stack.
Do you have any idea of what could I be doing wrong? or any pointers on what should I do to get this to work? or Should I use another type of driver to do this?
Carlos