limit network bandwith(TDI) problem

Hi guys, I’m writing a driver to limit the global network bandwith usage, after reading related DDK documents, I decide to use TDI to implement it. But I meet some strange phenomenon that I cannot explain, could someone here give me any clue to solve the problem? *Thanks in advanced*.

In my implementation, I only handle ‘ClientEventReceive / ClientEventReceiveExpedited’ call and TDI_RECEIVE event, discard ‘ClientEventChainedReceive’ and ‘ClientEventChainedReceiveExpedited’ IRP (return STATUS_ACCESS_DENIED).

The procedure of my TDI_RECEIVE handle routine is as follows:
1.insert this IRP into a private list, and fork a thread for scheduling the IRP list.
2.The scheduling thread first read MDL in IRP package(MmGetSystemAddressForMdlSafe). And I struct another IRP to read the data of the TCP device.
3.After reading the TCP device’s data, copy the data back to the mapping address(applied by MmGetSystemAddressForMdlSafe() function)

If everything is OK, the data SHOULD be the same as the TCP device, but my data is partial wrong. For example the data I received is ‘A->B->C’ though the correct sequence is ‘A->C->B’. But if I try redirecting the IRP while processing TDP_RECEIVE event, everything is perfect, the data is correct.

I’ve no idea about how does it happen…Could someone explain why my first method produces the wrong data ?

The key functions:

void InsertIrpList(PDEVICE_OBJECT pdev,PIRP pIrp,ULONG InFlags,ULONG byte)
{
KeWaitForMutexObject(&irpListMutex,Executive,KernelMode,FALSE,0);
PIO_STACK_LOCATION irpsl;
PIRPLISTENTRY p=(PIRPLISTENTRY)ExAllocatePool(NonPagedPool,sizeof(IRPLISTENTRY));
if (NULL==p)
{
return;
}
irpsl=IoGetCurrentIrpStackLocation(pIrp);
p->offset=0;
p->realbyte=0;//real read bytes
p->Buffer=MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
p->pFileObject=irpsl->FileObject;
p->pdev=pdev;//tcp device
p->byte=byte; // total to read bytes
p->pIrp=pIrp; //old irp
p->pNext=NULL;
p->InFlags=InFlags;// tdi receive flag
if (last!=0)
{
last->pNext=p;
p->pPrevious=last;
last=p;
}
else
{
last=p;
g_IrpListHead.pNext=p;
p->pPrevious=&g_IrpListHead;
}
KeReleaseMutex(&irpListMutex,FALSE);
}


ULONG RecevieTcpData(PIRPLISTENTRY pEntry,ULONG recvbyte)
{
PMDL mdl;
PIO_STACK_LOCATION nextStack;
IO_STATUS_BLOCK iostatus;
PIRP p2=IoAllocateIrp(pEntry->pdev->StackSize,FALSE);
p2->Tail.Overlay.Thread=PsGetCurrentThread();
p2->UserIosb=&iostatus;
p2->RequestorMode = KernelMode;
PVOID buffer=ExAllocatePoolWithTag(NonPagedPool,recvbyte,0x101);
RtlZeroMemory(buffer,recvbyte);
mdl=IoAllocateMdl(buffer,recvbyte,FALSE,FALSE,NULL);
KeInitializeEvent(&pEntry->uevent,NotificationEvent,FALSE);
if (mdl==NULL)
{
DbgPrint(“Error Mdl is Null\n”);
return 0;
}
__try
{
MmProbeAndLockPages(mdl, KernelMode, IoWriteAccess);
}
__except(1)
{
IoFreeMdl(mdl);
DbgPrint(“Error Lock\n”);
return 0;
}

ULONG len;
//build tdi_recive irp
TdiBuildReceive(p2,pEntry->pdev,pEntry->pFileObject,MyIrpReceiveComplete,pEntry,mdl,pEntry->InFlags,recvbyte);
NTSTATUS st=IoCallDriver(pEntry->pdev,p2);
if(STATUS_PENDING==st)
{
st=KeWaitForSingleObject(&pEntry->uevent,Executive,KernelMode,FALSE,NULL);
}
//copy data
memcpy((PVOID)((ULONG)pEntry->Buffer+pEntry->offset),buffer,pEntry->ulen);
pEntry->offset+=pEntry->ulen;
ExFreePool(buffer);
return pEntry->ulen;
}


Receive thread

p1=GetIrpEntryList();
ULONG len=RecevieTcpData(p1,p1->byte);
if( buffer full ) //complete old irp
{
p1->pIrp->IoStatus.Information=p1->realbyte;
p1->pIrp->IoStatus.Status=p1->status;
IoCompleteRequest(p1->pIrp,IO_NO_INCREMENT);
}


I’m writing a driver to limit the global network bandwith usage, after
reading related DDK documents, I decide to use TDI to implement it.


I strongly suggest you go back and re-think that decision.

Here are some things to think about:

1. Limiting ‘bandwidth’ at TDI is limiting bandwidth ‘across’ an API
(facility) not bandwidth (or aggregated bandwidth) of the network
attachments. Think about loopback…

2. TDI is only one observation point for a certain class of network traffic.
What about traffic that that is not generated or received and delivered
through a TDI endpoint?

3. As you have discovered, TDI is a challenging place to try and get
something like this done. TDI ‘filtering’ is hard.

If by ‘global’ bandwidth you mean an aggregate of all links attached to the
station, you can probably (and more easily) manage that sort of effect in an
NDIS IM driver or Light Weight Filter driver where you can observe and work
with the actually network frames being sent & received.

Now to try and help with your actual question:

I suggest you add some debug output which clearly show the sequence of
actions as they occur (not as you think they occur). Logging the IRP
addresses, MDL addresses, etc.

I suspect that your assumptions about the order of operations is incorrect.
In particular, the Receive event offers the client opportunity to accept
data immediately from the TSDU. If you are queuing TDI_RECEIVE Irps but
allowing the ‘lookahead’ to be read, data ordering failures are not hard to
imagine.

Good Luck,
Dave Cattley

tanks
but ClientEventReceive will call until set TDI_RECEIVE irp complete . In those time no irp into my device.