I have a problem when I receive packets in my intermediate
driver. I have previously posted a related question in the
newsgroup, unfortunately, no solution was found.
Therefore, I decided to post a sample code based on the
passthru example in order to better explain the problem,
and eventually find its cause.
I wrote a NDIS intermediate driver that changes the
contents of the packet. Some of the data it changes is the
IP address in some of the received packets. Since the data
received from the lower layer should be considered as read
only, I must copy it to a new packet. Furthermore, I know
there are some issues that are related to incoming
packets. Therefore, I have decided that my new packet will
consist of a single NDIS_BUFFER. However, I have noticed a
major problem when I receive a packet that its status is
NDIS_STATUS_RESOURCES, this usually occurs when there is
high traffic.
My problem has 2 symptoms, which I believe are related.
1. Whenever QoS packet scheduling is enabled on the same
network card that my intermediate driver is attached,
packets with status NDIS_STATUS_RESOURCES do not reach
user mode (I cannot see it in the socket). If I run
Ethereal on the same computer, it sees the original packet
rather than the altered packet. In addition if I
run "netstat -s" I see under the IPv4 statistics that the
number of received header errors increases according to
the number of received packets with NDIS_STATUS_RESOURCES.
Packets whose status is NDIS_STATUS_SUCCESS pass correctly.
2. If QoS is disabled, the packet reaches user mode and
Ethereal catches the altered packet. However, "netstat -s"
still reports received header errors that increase
according to the number of received packet with
NDIS_STATUS_RESOURCES.
I do not believe that there is a problem with the contents
of the altered packet (for example, the checksum), since
packets with NDIS_STATUS_SUCCESS pass correctly, and
disabling QoS seems to have a positive effect. However, I
still believe there is some kind of problem, maybe with
the way the packet was created, since netstat should not
report such errors.
I would greatly appreciate ideas and comments on this
issue.
In the following code, I tried to simulate packets with
NDIS_STATUS_RESOURCES - I changed the new packet status to
be always NDIS_STATUS_RESOURCES. This change causes the
same effect as in the case that the status is copied from
the original packet (assuming the original packet's status
was NDIS_STATUS_RESOURCES).
The altered code in PtReceive and PtReceivePacket changes
all received ICMP to the address 172.15.10.10. Therefore,
it assumes it is the local address of the computer. A
remote computer can add another address (say 172.15.10.11)
in the ARP table to the MAC address that has the address
172.15.10.10. If it will ping 172.15.10.11 and the packet
passes through, it will get a reply from 172.15.10.10.
Note that the code in PtReceive and PtReceivePacket is
almost a copy paste. As a result, if one changes the code
in one function, usually the other function will have to
be changed as well.
Thanks.
To change the passthru example:
In passthru.h add the following line in the ADAPT
structure:
NDIS_HANDLE BufferPoolHandle;
In protocol.c, add to PtBindAdapter initialization of
BufferPoolHandle after the initialization of
SendPacketPoolHandle, use the follwing code:
// Allocate a buffer pool messages
//
NdisAllocateBufferPool(Status, &pAdapt->BufferPoolHandle,
65535);
if (*Status != NDIS_STATUS_SUCCESS)
{
break;
}
And after the deinitilization of SendPacketPoolHandle (in
case of error), add the following code
if (pAdapt->BufferPoolHandle != NULL)
{
NdisFreeBufferPool(pAdapt-
>BufferPoolHandle);
}
replace PtReceive and PtReceivePacket with the following
functions:
NDIS_STATUS
PtReceive(
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer,
IN UINT LookAheadBufferSize,
IN UINT PacketSize
)
/*++
Routine Description:
Handle receive data indicated up by the miniport below.
We pass
it along to the protocol above us.
If the miniport below indicates packets, NDIS would more
likely call us at our ReceivePacket handler. However we
might be called here in certain situations even though
the miniport below has indicated a receive packet, e.g.
if the miniport had set packet status to
NDIS_STATUS_RESOURCES.
Arguments:
<see DDK ref page for ProtocolReceive>
Return Value:
NDIS_STATUS_SUCCESS if we processed the receive
successfully,
NDIS_STATUS_XXX error code if we discarded it.
--*/
{
PADAPT pAdapt = (PADAPT)
ProtocolBindingContext;
PNDIS_PACKET MyPacket, Packet;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
if ((!pAdapt->MiniportHandle) || (pAdapt->MPDeviceState
> NdisDeviceStateD0))
{
Status = NDIS_STATUS_FAILURE;
}
else do
{
//
// Get at the packet, if any, indicated up by the
miniport below.
//
Packet = NdisGetReceivedPacket(pAdapt->BindingHandle,
MacReceiveContext);
if (Packet != NULL)
{
//
// The miniport below did indicate up a packet. Use
information
// from that packet to construct a new packet to
indicate up.
//
#ifdef NDIS51
//
// NDIS 5.1 NOTE: Do not reuse the original packet
in indicating
// up a receive, even if there is sufficient packet
stack space.
// If we had to do so, we would have had to
overwrite the
// status field in the original packet to
NDIS_STATUS_RESOURCES,
// and it is not allowed for protocols to overwrite
this field
// in received packets.
//
#endif // NDIS51
//
// Get a packet off the pool and indicate that up
//
NdisDprAllocatePacket(&Status,
&MyPacket,
pAdapt->RecvPacketPoolHandle);
if (Status == NDIS_STATUS_SUCCESS)
{
UINT nLength;
PVOID pAddress;
PNDIS_BUFFER MyBuffer;
MyPacket->Private.Head = NULL;
MyPacket->Private.Tail = NULL;
//
// Get the original packet (it could be the same
packet as the
// one received or a different one based on the
number of layered
// miniports below) and set it on the indicated
packet so the OOB
// data is visible correctly at protocols above.
//
NDIS_SET_ORIGINAL_PACKET(MyPacket,
NDIS_GET_ORIGINAL_PACKET(Packet));
NDIS_SET_PACKET_HEADER_SIZE(MyPacket,
HeaderBufferSize);
//
// Copy packet flags.
//
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags
(Packet);
//
// Force protocols above to make a copy if they
want to hang
// on to data in this packet. This is because we
are in our
// Receive handler (not ReceivePacket) and we
can't return a
// ref count from here.
//
NDIS_SET_PACKET_STATUS(MyPacket,
NDIS_STATUS_RESOURCES);
//
// By setting NDIS_STATUS_RESOURCES, we also know
that we can reclaim
// this packet as soon as the call to
NdisMIndicateReceivePacket
// returns.
//
NdisQueryPacketLength(Packet, &nLength);
Status = NdisAllocateMemoryWithTag(&pAddress,
nLength, 'VCER');
if (Status == NDIS_STATUS_SUCCESS)
{
NdisAllocateBuffer(&Status, &MyBuffer, pAdapt-
>BufferPoolHandle, pAddress, nLength);
if (Status == NDIS_STATUS_SUCCESS)
{
unsigned char* pCurrPacket;
UINT nCopied;
const unsigned char DestIP[4] = {172, 15, 10,
10};
NdisChainBufferAtFront(MyPacket, MyBuffer);
NdisCopyFromPacketToPacketSafe(MyPacket, 0,
nLength, Packet, 0, &nCopied, NormalPagePriority);
ASSERT(nCopied == nLength);
pCurrPacket = pAddress;
DBGPRINT(("Current Packet type %X %X - %X",
pCurrPacket[12], pCurrPacket[13], pCurrPacket[23]));
// If current packet is IP
if ((pCurrPacket[12] == 0x08) && (pCurrPacket
[13] == 0x00) &&
(pCurrPacket[23] == 1))
{
DBGPRINT(("Current Dest %X %X %X %X",
pCurrPacket[30], pCurrPacket[31], pCurrPacket[32],
pCurrPacket[33]));
HandleReceivedPacket(&(pCurrPacket[24]), &
(pCurrPacket[30]), DestIP);
//HandleReceivedPacket(&(pCurrPacket[36]), &
(pCurrPacket[30]), DestIP);
NdisMoveMemory(&(pCurrPacket[30]), DestIP,
4);
DBGPRINT(("New Dest %X %X %X %X", pCurrPacket
[30], pCurrPacket[31], pCurrPacket[32], pCurrPacket[33]));
}
NdisMIndicateReceivePacket(pAdapt-
>MiniportHandle, &MyPacket, 1);
NdisFreeBuffer(MyBuffer);
}
NdisFreeMemory(pAddress, 0, 0);
}
//
// Reclaim the indicated packet. Since we had set
its status
// to NDIS_STATUS_RESOURCES, we are guaranteed
that protocols
// above are done with it.
//
NdisDprFreePacket(MyPacket);
break;
}
}
else
{
//
// The miniport below us uses the old-style (not
packet)
// receive indication. Fall through.
//
}
//
// Fall through if the miniport below us has either not
// indicated a packet or we could not allocate one
//
pAdapt->IndicateRcvComplete = TRUE;
switch (pAdapt->Medium)
{
case NdisMedium802_3:
case NdisMediumWan:
NdisMEthIndicateReceive(pAdapt->MiniportHandle,
MacReceiveContext,
HeaderBuffer,
HeaderBufferSize,
LookAheadBuffer,
LookAheadBufferSize,
PacketSize);
break;
case NdisMedium802_5:
NdisMTrIndicateReceive(pAdapt->MiniportHandle,
MacReceiveContext,
HeaderBuffer,
HeaderBufferSize,
LookAheadBuffer,
LookAheadBufferSize,
PacketSize);
break;
case NdisMediumFddi:
NdisMFddiIndicateReceive(pAdapt-
>MiniportHandle,
MacReceiveContext,
HeaderBuffer,
HeaderBufferSize,
LookAheadBuffer,
LookAheadBufferSize,
PacketSize);
break;
default:
ASSERT(FALSE);
break;
}
} while(FALSE);
return Status;
}
INT
PtReceivePacket(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet
)
/*++
Routine Description:
ReceivePacket handler. Called by NDIS if the miniport
below supports
NDIS 4.0 style receives. Re-package the buffer chain in
a new packet
and indicate the new packet to protocols above us. Any
context for
packets indicated up must be kept in the
MiniportReserved field.
NDIS 5.1 - packet stacking - if there is
sufficient "stack space" in
the packet passed to us, we can use the same packet in a
receive
indication.
Arguments:
ProtocolBindingContext - Pointer to our adapter
structure.
Packet - Pointer to the packet
Return Value:
== 0 -> We are done with the packet
!= 0 -> We will keep the packet and call
NdisReturnPackets() this
many times when done.
--*/
{
PADAPT pAdapt =(PADAPT)
ProtocolBindingContext;
NDIS_STATUS Status;
PNDIS_PACKET MyPacket;
BOOLEAN Remaining;
//
// Drop the packet silently if the upper miniport edge
isn't initialized or
// the miniport edge is in low power state
//
if ((!pAdapt->MiniportHandle) || (pAdapt->MPDeviceState
> NdisDeviceStateD0))
{
return 0;
}
/* Cannot use packet stacking since we are setting a new
buffer
#ifdef NDIS51
//
// Check if we can reuse the same packet for indicating
up.
// See also: PtReceive().
//
(VOID)NdisIMGetCurrentPacketStack(Packet, &Remaining);
if (Remaining)
{
//
// We can reuse "Packet". Indicate it up and be done
with it.
//
Status = NDIS_GET_PACKET_STATUS(Packet);
NdisMIndicateReceivePacket(pAdapt->MiniportHandle,
&Packet, 1);
return((Status != NDIS_STATUS_RESOURCES) ? 1 : 0);
}
#endif // NDIS51
*/
//
// Get a packet off the pool and indicate that up
//
NdisDprAllocatePacket(&Status,
&MyPacket,
pAdapt->RecvPacketPoolHandle);
if (Status == NDIS_STATUS_SUCCESS)
{
PRECV_RSVD RecvRsvd;
UINT nLength;
PVOID pAddress;
PNDIS_BUFFER MyBuffer;
NDIS_STATUS OriginalStatus;
RecvRsvd = (PRECV_RSVD)(MyPacket->MiniportReserved);
RecvRsvd->OriginalPkt = Packet;
MyPacket->Private.Head = NULL;
MyPacket->Private.Tail = NULL;
//
// Get the original packet (it could be the same
packet as the one
// received or a different one based on the number of
layered miniports
// below) and set it on the indicated packet so the
OOB data is visible
// correctly to protocols above us.
//
NDIS_SET_ORIGINAL_PACKET(MyPacket,
NDIS_GET_ORIGINAL_PACKET(Packet));
//
// Set Packet Flags
//
NdisGetPacketFlags(MyPacket) = NdisGetPacketFlags
(Packet);
OriginalStatus = NDIS_GET_PACKET_STATUS(Packet);
NDIS_SET_PACKET_STATUS(MyPacket,
NDIS_STATUS_RESOURCES);
NDIS_SET_PACKET_HEADER_SIZE(MyPacket,
NDIS_GET_PACKET_HEADER_SIZE(Packet));
NdisQueryPacketLength(Packet, &nLength);
Status = NdisAllocateMemoryWithTag(&pAddress,
nLength, 'VCER');
if (Status == NDIS_STATUS_SUCCESS)
{
NdisAllocateBuffer(&Status, &MyBuffer, pAdapt-
>BufferPoolHandle, pAddress, nLength);
if (Status == NDIS_STATUS_SUCCESS)
{
unsigned char* pCurrPacket;
const unsigned char DestIP[4] = {172, 15, 10, 10};
UINT nCopied;
NdisChainBufferAtFront(MyPacket, MyBuffer);
NdisCopyFromPacketToPacketSafe(MyPacket, 0,
nLength, Packet, 0, &nCopied, NormalPagePriority);
ASSERT(nCopied == nLength);
pCurrPacket = pAddress;
DBGPRINT(("Current Packet type %X %X - %X",
pCurrPacket[12], pCurrPacket[13], pCurrPacket[23]));
// If current packet is IP
if ((pCurrPacket[12] == 0x08) && (pCurrPacket[13]
== 0x00) &&
(pCurrPacket[23] == 1))
{
DBGPRINT(("Current Dest %X %X %X %X", pCurrPacket
[30], pCurrPacket[31], pCurrPacket[32], pCurrPacket[33]));
HandleReceivedPacket(&(pCurrPacket[24]), &
(pCurrPacket[30]), DestIP);
//HandleReceivedPacket(&(pCurrPacket[36]), &
(pCurrPacket[30]), DestIP);
NdisMoveMemory(&(pCurrPacket[30]), DestIP, 4);
DBGPRINT(("New Dest %X %X %X %X", pCurrPacket
[30], pCurrPacket[31], pCurrPacket[32], pCurrPacket[33]));
}
NdisMIndicateReceivePacket(pAdapt->MiniportHandle,
&MyPacket, 1);
//
// Check if we had indicated up the packet with
NDIS_STATUS_RESOURCES
// NOTE -- do not use NDIS_GET_PACKET_STATUS
(MyPacket) for this since
// it might have changed! Use the value saved in
the local variable.
//
//
// Our ReturnPackets handler will not be called
for this packet.
// We should reclaim it right here.
//
NdisFreeBuffer(MyBuffer);
}
NdisFreeMemory(pAddress, 0, 0);
}
NdisDprFreePacket(MyPacket);
return(0);
}
else
{
//
// We are out of packets. Silently drop it.
//
return(0);
}
}
Also in protocol.c, add the following function before the
implementation of PtReceive
VOID HandleReceivedPacket(unsigned char* pCheckSum,
unsigned char* pIP, const unsigned char nDestIP[4])
{
signed long x, nTemp;
x=pCheckSum[0]*256+pCheckSum[1];
x=~x & 0xFFFF;
nTemp = pIP[0]*256+pIP[1];
x -= nTemp & 0xffff;
if (x<=0) { x--; x&=0xffff; }
nTemp = pIP[2]*256+pIP[3];
x -= nTemp & 0xffff;
if (x<=0) { x--; x&=0xffff; }
nTemp = nDestIP[0]*256+nDestIP[1];
x += nTemp & 0xffff;
if (x & 0x10000) { x++; x&=0xffff; }
nTemp = nDestIP[2]*256+nDestIP[3];
x += nTemp & 0xffff;
if (x & 0x10000) { x++; x&=0xffff; }
x=~x & 0xFFFF;
pCheckSum[0]=(unsigned char)(x/256);
pCheckSum[1]=(unsigned char)(x & 0xff);
}
in miniport.c replace MPFreeAllPacketPools with the
following function:
VOID
MPFreeAllPacketPools(IN PADAPT pAdapt)
/*++
Routine Description:
Free all packet pools on the specified adapter.
Arguments:
pAdapt - pointer to ADAPT structure
Return Value:
None
--*/
{
if (pAdapt->RecvPacketPoolHandle != NULL)
{
//
// Free the packet pool that is used to indicate
receives
//
NdisFreePacketPool(pAdapt->RecvPacketPoolHandle);
pAdapt->RecvPacketPoolHandle = NULL;
}
if (pAdapt->BufferPoolHandle != NULL)
{
NdisFreeBufferPool(pAdapt->BufferPoolHandle);
pAdapt-> BufferPoolHandle = NULL;
}
if (pAdapt->SendPacketPoolHandle != NULL)
{
//
// Free the packet pool that is used to send packets
below
//
NdisFreePacketPool(pAdapt->SendPacketPoolHandle);
pAdapt->SendPacketPoolHandle = NULL;
}
}