mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1308 lines
31 KiB
1308 lines
31 KiB
/*++
|
|
|
|
Copyright (c) 1995 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ipinip\send.c
|
|
|
|
Abstract:
|
|
|
|
The file contains the part of interface of the IP in IP tunnel driver
|
|
to the TCP/IP stack that deals with sending data
|
|
|
|
The code is a cleaned up version of wanarp\ipif.c which in turn
|
|
was derived from HenrySa's ip\arp.c
|
|
|
|
Revision History:
|
|
|
|
AmritanR
|
|
|
|
--*/
|
|
|
|
#define __FILE_SIG__ SEND_SIG
|
|
|
|
#include "inc.h"
|
|
|
|
NDIS_STATUS
|
|
WanIpTransmit(
|
|
PVOID pvContext,
|
|
NDIS_PACKET **ppPacketArray,
|
|
UINT uiNumPackets,
|
|
DWORD dwDestAddr,
|
|
RouteCacheEntry *pRce,
|
|
PVOID pvLinkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function called by IP to send an array of packets. We allocate
|
|
one ETH_HEADER for each packet. The adapter (which is the pvContext)
|
|
is locked. If the adapter is not mapped, we fail the send, otherwise
|
|
we lock the interface. If
|
|
|
|
Locks:
|
|
|
|
|
|
Arguments:
|
|
|
|
pvContext Our context to IP for the interface - the PTUNNEL
|
|
ppPacketArray The array of NDIS_PACKETs to send
|
|
uiNumPackets The number of packets in the array
|
|
dwDestAddr The destination (next hop) address
|
|
pRce Pointer to RCE.
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PADAPTER pAdapter;
|
|
PUMODE_INTERFACE pInterface;
|
|
PADDRESS_CONTEXT pAddrContext;
|
|
PCONN_ENTRY pConnEntry;
|
|
KIRQL kiIrql;
|
|
NDIS_STATUS nsResult;
|
|
UINT i;
|
|
LIST_ENTRY leBufferList;
|
|
|
|
TraceEnter(SEND, "IpTransmit");
|
|
|
|
Trace(SEND,TRACE,
|
|
("IpTransmit: %d packet(s) over %p/%p to %d.%d.%d.%d\n",
|
|
uiNumPackets,
|
|
pvContext,
|
|
pvLinkContext,
|
|
PRINT_IPADDR(dwDestAddr)));
|
|
|
|
if(g_nhNdiswanBinding is NULL)
|
|
{
|
|
//
|
|
// In the process of shutting down, return
|
|
//
|
|
|
|
return NDIS_STATUS_ADAPTER_NOT_READY;
|
|
}
|
|
|
|
//
|
|
// Get the ethernet headers for each packet
|
|
//
|
|
|
|
if(!GetBufferListFromPool(&g_bpHeaderBufferPool,
|
|
uiNumPackets,
|
|
&leBufferList))
|
|
{
|
|
//
|
|
// Couldnt get headers for all the buffers
|
|
//
|
|
|
|
Trace(SEND, ERROR,
|
|
("IpTransmit: couldnt allocate %d header buffers\n",
|
|
uiNumPackets));
|
|
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// This function is not guaranteed to be at dispatch
|
|
// The context given to us is a pointer to our adapter
|
|
//
|
|
|
|
pConnEntry = NULL;
|
|
|
|
pAdapter = (PADAPTER)pvContext;
|
|
|
|
RtAcquireSpinLock(&(pAdapter->rlLock),
|
|
&kiIrql);
|
|
|
|
if(pAdapter->byState isnot AS_MAPPED)
|
|
{
|
|
//
|
|
// If the adapter is unmapped, the connection is disconnected
|
|
//
|
|
|
|
RtReleaseSpinLock(&(pAdapter->rlLock),
|
|
kiIrql);
|
|
|
|
FreeBufferListToPool(&g_bpHeaderBufferPool,
|
|
&leBufferList);
|
|
|
|
Trace(SEND, INFO,
|
|
("IpTransmit: Send on %x which is unmapped\n",
|
|
pAdapter));
|
|
|
|
//
|
|
// Cant increment stats because we dont have an interface
|
|
//
|
|
|
|
return NDIS_STATUS_ADAPTER_NOT_READY;
|
|
}
|
|
|
|
//
|
|
// Since the adapter is mapped, it must have an interface
|
|
//
|
|
|
|
pInterface = pAdapter->pInterface;
|
|
|
|
RtAssert(pInterface);
|
|
|
|
RtAcquireSpinLockAtDpcLevel(&(pInterface->rlLock));
|
|
|
|
//
|
|
// If interface is not yet connected (for demand dial case) the copy the
|
|
// packet and succeed the send.
|
|
//
|
|
|
|
if(pInterface->dwOperState isnot IF_OPER_STATUS_CONNECTED)
|
|
{
|
|
if(pInterface->duUsage isnot DU_ROUTER)
|
|
{
|
|
//
|
|
// Just a race condition
|
|
//
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pInterface->rlLock));
|
|
|
|
RtReleaseSpinLock(&(pAdapter->rlLock),
|
|
kiIrql);
|
|
|
|
FreeBufferListToPool(&g_bpHeaderBufferPool,
|
|
&leBufferList);
|
|
|
|
return NDIS_STATUS_ADAPTER_NOT_READY;
|
|
}
|
|
|
|
//
|
|
// If IP is transmitting on us, he must have called out to
|
|
// connect
|
|
//
|
|
|
|
RtAssert(pInterface->dwOperState is IF_OPER_STATUS_CONNECTING);
|
|
|
|
Trace(SEND, INFO,
|
|
("IpTransmit: I/F not connected, queueing packet\n"));
|
|
|
|
//
|
|
// New function which queues the whole packet array
|
|
//
|
|
|
|
nsResult = WanpCopyAndQueuePackets(pAdapter,
|
|
ppPacketArray,
|
|
&leBufferList,
|
|
uiNumPackets);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pInterface->rlLock));
|
|
|
|
RtReleaseSpinLock(&(pAdapter->rlLock),
|
|
kiIrql);
|
|
|
|
if(nsResult isnot STATUS_SUCCESS)
|
|
{
|
|
FreeBufferListToPool(&g_bpHeaderBufferPool,
|
|
&leBufferList);
|
|
}
|
|
|
|
return nsResult;
|
|
}
|
|
|
|
//
|
|
// Find the connection entry for this send
|
|
//
|
|
|
|
if(pAdapter is g_pServerAdapter)
|
|
{
|
|
pConnEntry = (PCONN_ENTRY)pvLinkContext;
|
|
|
|
//RtAssert(pConnEntry);
|
|
|
|
//
|
|
// Hack for multicast
|
|
//
|
|
|
|
if(pConnEntry is NULL)
|
|
{
|
|
pConnEntry = WanpGetConnEntryGivenAddress(dwDestAddr);
|
|
}
|
|
|
|
//
|
|
// We are dont with the adapter lock. All we need is to lock down
|
|
// the connection entry
|
|
// It is important that we release the locks since for dial-in
|
|
// clients the locking hierarchy is CONN_ENTRY->ADAPTER->INTERFACE
|
|
//
|
|
|
|
if(pConnEntry)
|
|
{
|
|
ReferenceConnEntry(pConnEntry);
|
|
}
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pAdapter->rlLock));
|
|
RtReleaseSpinLockFromDpcLevel(&(pInterface->rlLock));
|
|
|
|
//
|
|
// NOTE: The state of the connection can change in this window
|
|
//
|
|
|
|
if(pConnEntry)
|
|
{
|
|
RtAcquireSpinLockAtDpcLevel(&(pConnEntry->rlLock));
|
|
|
|
//
|
|
// Not a useful assert because (i) we add static routes to clients
|
|
// and (ii) we have that hack for netbt broadcasts
|
|
//
|
|
|
|
// RtAssert((pConnEntry->dwRemoteAddr is dwDestAddr) or
|
|
// (dwAddress is 0xFFFFFFFF));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This send is on some adapter other than the server adapter
|
|
// Such an adapter has only one connection. For these sends we
|
|
// lock the adapter instead of the connection
|
|
//
|
|
|
|
|
|
pConnEntry = pAdapter->pConnEntry;
|
|
|
|
if(pConnEntry)
|
|
{
|
|
ReferenceConnEntry(pConnEntry);
|
|
|
|
RtAssert(pConnEntry->pAdapter is pAdapter);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So now we have a locked connection entry (if client)
|
|
// or a locked adapter (for dial out and router)
|
|
//
|
|
|
|
if((pConnEntry is NULL) or
|
|
(pConnEntry->byState isnot CS_CONNECTED))
|
|
{
|
|
if((ULONG)(dwDestAddr & 0x000000FF) < (ULONG) 0x000000E0)
|
|
{
|
|
Trace(SEND, ERROR,
|
|
("IpTransmit: Could not find conn entry for %d.%d.%d.%d\n",
|
|
PRINT_IPADDR(dwDestAddr)));
|
|
}
|
|
|
|
for(i = 0; i < uiNumPackets; i++)
|
|
{
|
|
PLIST_ENTRY pleNode;
|
|
PNDIS_BUFFER pnbBuffer;
|
|
PVOID pvFirstBuffer;
|
|
UINT uiFirstBufLen, uiTotalLen;
|
|
PIP_HEADER pIpHeader;
|
|
|
|
NdisGetFirstBufferFromPacket(ppPacketArray[i],
|
|
&pnbBuffer,
|
|
&pvFirstBuffer,
|
|
&uiFirstBufLen,
|
|
&uiTotalLen);
|
|
|
|
|
|
pIpHeader = (PIP_HEADER)pvFirstBuffer;
|
|
|
|
RtAssert(pIpHeader);
|
|
RtAssert(uiFirstBufLen >= sizeof(IP_HEADER));
|
|
|
|
if(IsUnicastAddr(pIpHeader->dwDest))
|
|
{
|
|
pInterface->ulOutUniPkts++;
|
|
}
|
|
else
|
|
{
|
|
pInterface->ulOutNonUniPkts++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The entry has been disconnected.
|
|
// This is just a window in the timing
|
|
//
|
|
|
|
pInterface->ulOutDiscards += uiNumPackets;
|
|
|
|
if(pAdapter is g_pServerAdapter)
|
|
{
|
|
if(pConnEntry isnot NULL)
|
|
{
|
|
RtReleaseSpinLock(&(pConnEntry->rlLock),
|
|
kiIrql);
|
|
}
|
|
else
|
|
{
|
|
KeLowerIrql(kiIrql);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RtReleaseSpinLockFromDpcLevel(&(pAdapter->rlLock));
|
|
|
|
RtReleaseSpinLock(&(pInterface->rlLock),
|
|
kiIrql);
|
|
}
|
|
|
|
FreeBufferListToPool(&g_bpHeaderBufferPool,
|
|
&leBufferList);
|
|
|
|
if(pConnEntry)
|
|
{
|
|
DereferenceConnEntry(pConnEntry);
|
|
}
|
|
|
|
return NDIS_STATUS_ADAPTER_NOT_READY;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
Trace(SEND, TRACE,
|
|
("IpTransmit: Send on %s\n",
|
|
pAdapter->asDeviceNameA.Buffer));
|
|
|
|
for(i = 0; i < uiNumPackets; i++)
|
|
{
|
|
PacketContext *pPC;
|
|
|
|
pPC = (PacketContext *)((ppPacketArray[i])->ProtocolReserved);
|
|
|
|
RtAssert(pPC->pc_common.pc_owner isnot PACKET_OWNER_LINK);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// This function will free the locks
|
|
//
|
|
|
|
nsResult = WanpSendPackets(pAdapter,
|
|
pInterface,
|
|
pConnEntry,
|
|
ppPacketArray,
|
|
&leBufferList,
|
|
uiNumPackets,
|
|
kiIrql);
|
|
|
|
if(nsResult isnot STATUS_SUCCESS)
|
|
{
|
|
Trace(SEND,TRACE,
|
|
("IpTransmit: SendPackets returned status %x\n",nsResult));
|
|
}
|
|
|
|
DereferenceConnEntry(pConnEntry);
|
|
|
|
return nsResult;
|
|
}
|
|
|
|
NDIS_STATUS
|
|
WanpSendPackets(
|
|
PADAPTER pAdapter,
|
|
PUMODE_INTERFACE pInterface,
|
|
PCONN_ENTRY pConnEntry,
|
|
NDIS_PACKET **ppPacketArray,
|
|
PLIST_ENTRY pleBufferList,
|
|
UINT uiNumPackets,
|
|
KIRQL kiIrql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main routine to send an array of packets
|
|
|
|
|
|
Locks:
|
|
|
|
Called with the connection entry (for dial in) or the adapter+interface
|
|
(all others) locked
|
|
|
|
Arguments:
|
|
|
|
pAdapter The adapter for the connection
|
|
pInterface The interface the adapter is mapped to
|
|
pConnEntry The connection entry for the send
|
|
ppPacketArray The array of packets to send
|
|
pBuffHead A list of buffers for the link layer header
|
|
uiNumPackets Number of packets (and ll header buffers)
|
|
kiIrql Irql at which the adapter or conn entry was locked
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_PENDING
|
|
|
|
--*/
|
|
|
|
{
|
|
NDIS_STATUS nsStatus;
|
|
PBYTE pbyHeader;
|
|
ULONG i;
|
|
|
|
#if DBG
|
|
|
|
Trace(SEND, TRACE,
|
|
("SendPackets: %s\n",
|
|
pAdapter->asDeviceNameA.Buffer));
|
|
|
|
#endif
|
|
|
|
for(i = 0; i < uiNumPackets; i++)
|
|
{
|
|
PNDIS_BUFFER pnbBuffer, pnbTempBuffer;
|
|
PLIST_ENTRY pleNode;
|
|
PVOID pvFirstBuffer;
|
|
UINT uiFirstBufLen, uiTotalBufLen, uiIpHdrLen;
|
|
PIP_HEADER pIpHeader;
|
|
PBUFFER_HEAD pBufferHead;
|
|
|
|
|
|
NdisGetFirstBufferFromPacket(ppPacketArray[i],
|
|
&pnbTempBuffer,
|
|
&pvFirstBuffer,
|
|
&uiFirstBufLen,
|
|
&uiTotalBufLen);
|
|
|
|
|
|
pIpHeader = (PIP_HEADER)pvFirstBuffer;
|
|
|
|
RtAssert(pIpHeader);
|
|
|
|
//
|
|
// ToDo: remove till NK fixes the bug in IP transmit
|
|
// with header inc
|
|
//
|
|
|
|
// RtAssert(uiFirstBufLen >= sizeof(IP_HEADER));
|
|
|
|
#if L2TP_DBG
|
|
|
|
#define L2TP_PORT_NBO 0xA506 // 1701 == 06A5
|
|
|
|
//
|
|
// If this is a l2tp packet, break
|
|
//
|
|
|
|
if(pIpHeader->byProtocol is 17)
|
|
{
|
|
WORD UNALIGNED *pwPort;
|
|
|
|
//
|
|
// See if we have enough data to get to the UDP header in
|
|
// the first buffer
|
|
//
|
|
|
|
uiIpHdrLen = LengthOfIpHeader(pIpHeader);
|
|
|
|
if(uiFirstBufLen >= uiIpHdrLen + sizeof(ULONG))
|
|
{
|
|
pwPort = (WORD UNALIGNED *)((ULONG_PTR)pIpHeader + uiIpHdrLen);
|
|
}
|
|
else
|
|
{
|
|
PNDIS_BUFFER pNextBuf;
|
|
|
|
//
|
|
// Get the next buffer and look into its
|
|
//
|
|
|
|
pNextBuf = pnbTempBuffer->Next;
|
|
|
|
pwPort = (WORD UNALIGNED *)(pnbTempBuffer->MappedSystemVa);
|
|
}
|
|
|
|
if((pwPort[0] is L2TP_PORT_NBO) or
|
|
(pwPort[1] is L2TP_PORT_NBO))
|
|
{
|
|
Trace(SEND, ERROR,
|
|
("SendPackets: %x buffer %x header %x port %d.%d\n",
|
|
pnbTempBuffer, pIpHeader, pwPort,
|
|
pwPort[0], pwPort[1]));
|
|
|
|
RtAssert(FALSE);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// NOTE: If this is a client send, the server interface is not
|
|
// locked. Hence the stats can be inconsistent for the server
|
|
// interface
|
|
//
|
|
|
|
if(IsUnicastAddr(pIpHeader->dwDest))
|
|
{
|
|
pInterface->ulOutUniPkts++;
|
|
}
|
|
else
|
|
{
|
|
pInterface->ulOutNonUniPkts++;
|
|
}
|
|
|
|
pleNode = RemoveHeadList(pleBufferList);
|
|
|
|
#if LIST_DBG
|
|
|
|
pBufferHead = CONTAINING_RECORD(pleNode,
|
|
BUFFER_HEAD,
|
|
leListLink);
|
|
|
|
RtAssert(IsListEmpty(&(pBufferHead->leFreeBufferLink)));
|
|
RtAssert(pBufferHead->bBusy);
|
|
|
|
pBufferHead->leListLink.Flink = NULL;
|
|
pBufferHead->leListLink.Blink = NULL;
|
|
|
|
#else
|
|
|
|
pBufferHead = CONTAINING_RECORD(pleNode,
|
|
BUFFER_HEAD,
|
|
leFreeBufferLink);
|
|
|
|
#endif
|
|
|
|
//
|
|
// Get a pointer to the data and to the buffer
|
|
//
|
|
|
|
|
|
pbyHeader = BUFFER_FROM_HEAD(pBufferHead);
|
|
|
|
pnbBuffer = pBufferHead->pNdisBuffer;
|
|
|
|
//
|
|
// Copy our prebuilt header into each buffer
|
|
//
|
|
|
|
RtlCopyMemory(pbyHeader,
|
|
&(pConnEntry->ehHeader),
|
|
sizeof(ETH_HEADER));
|
|
|
|
//
|
|
// Put the ethernet header in the front of the packet
|
|
//
|
|
|
|
NdisChainBufferAtFront(ppPacketArray[i],
|
|
pnbBuffer);
|
|
|
|
//
|
|
// Reference the entry once for each packet
|
|
//
|
|
|
|
ReferenceConnEntry(pConnEntry);
|
|
|
|
#if PKT_DBG
|
|
|
|
Trace(SEND, ERROR,
|
|
("SendPackets: Pkt %x Eth buff %x (%x) Header %x (%x)\n",
|
|
ppPacketArray[1],
|
|
pnbBuffer,
|
|
pbyHeader,
|
|
pnbTempBuffer,
|
|
pvFirstBuffer));
|
|
|
|
#endif // PKT_DBG
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Increment the output queue length. This will be decremented
|
|
// in the send complete handler
|
|
//
|
|
|
|
pAdapter->ulQueueLen++;
|
|
|
|
//
|
|
// Let go of the locks
|
|
//
|
|
|
|
if(pConnEntry->duUsage isnot DU_CALLIN)
|
|
{
|
|
RtReleaseSpinLockFromDpcLevel(&(pInterface->rlLock));
|
|
}
|
|
|
|
RtReleaseSpinLock(pConnEntry->prlLock,
|
|
kiIrql);
|
|
|
|
NdisSendPackets(g_nhNdiswanBinding,
|
|
ppPacketArray,
|
|
uiNumPackets);
|
|
|
|
//
|
|
// Dont dereference the connection entry. We will deref it in
|
|
// the send complete handler
|
|
//
|
|
|
|
return NDIS_STATUS_PENDING;
|
|
}
|
|
|
|
VOID
|
|
WanNdisSendComplete(
|
|
NDIS_HANDLE nhHandle,
|
|
PNDIS_PACKET pnpPacket,
|
|
NDIS_STATUS nsStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Our send complete handler called by NDIS once for each packet that was
|
|
pending after a send.
|
|
|
|
Locks:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PacketContext *pPC;
|
|
PNDIS_BUFFER pnbBuffer, pnbEthBuffer;
|
|
KIRQL kiIrql;
|
|
PADAPTER pAdapter;
|
|
PUMODE_INTERFACE pInterface;
|
|
PCONN_ENTRY pConnEntry;
|
|
PETH_HEADER pEthHeader;
|
|
ULONG ulIndex;
|
|
PVOID pvFirstBuffer;
|
|
UINT uiFirstBufLen, uiTotalLen;
|
|
|
|
TraceEnter(SEND, "NdisSendComplete");
|
|
|
|
//
|
|
// Get first buffer on packet. This is our ethernet header buffer
|
|
//
|
|
|
|
NdisUnchainBufferAtFront(pnpPacket,
|
|
&pnbEthBuffer);
|
|
|
|
//
|
|
// Get the fake ethernet header
|
|
//
|
|
|
|
pEthHeader = NdisBufferVirtualAddress(pnbEthBuffer);
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// The buffer head should say the same thing
|
|
//
|
|
|
|
RtAssert(pnbEthBuffer is ((HEAD_FROM_BUFFER(pEthHeader))->pNdisBuffer));
|
|
|
|
#endif
|
|
|
|
ulIndex = GetConnIndexFromAddr(pEthHeader->rgbySourceAddr);
|
|
|
|
//
|
|
// Done with our buffer
|
|
//
|
|
|
|
FreeBufferToPool(&g_bpHeaderBufferPool,
|
|
(PBYTE)pEthHeader);
|
|
|
|
//
|
|
// Get the connection entry
|
|
//
|
|
|
|
RtAcquireSpinLock(&g_rlConnTableLock,
|
|
&kiIrql);
|
|
|
|
pConnEntry = GetConnEntryGivenIndex(ulIndex);
|
|
|
|
if(pConnEntry is NULL)
|
|
{
|
|
RtAssert(FALSE);
|
|
|
|
RtReleaseSpinLock(&g_rlConnTableLock,
|
|
kiIrql);
|
|
|
|
Trace(SEND, ERROR,
|
|
("NdisSendComplete: Couldnt find entry for connection %d\n",
|
|
ulIndex));
|
|
|
|
TraceLeave(RCV, "NdisSendComplete");
|
|
|
|
return;
|
|
}
|
|
|
|
RtAcquireSpinLockAtDpcLevel(pConnEntry->prlLock);
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&g_rlConnTableLock);
|
|
|
|
pAdapter = pConnEntry->pAdapter;
|
|
|
|
#if DBG
|
|
|
|
Trace(SEND, INFO,
|
|
("NdisSendComplete: Extracted adapter %x with name %s\n",
|
|
pAdapter,
|
|
pAdapter->asDeviceNameA.Buffer));
|
|
|
|
#endif
|
|
|
|
pAdapter->ulQueueLen--;
|
|
|
|
if(pConnEntry->duUsage is DU_CALLIN)
|
|
{
|
|
pInterface = g_pServerInterface;
|
|
|
|
RtAssert(pAdapter is g_pServerAdapter);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// See if we are still mapped to an interface, if so lock it
|
|
//
|
|
|
|
pInterface = pAdapter->pInterface;
|
|
|
|
if(pInterface isnot NULL)
|
|
{
|
|
RtAcquireSpinLockAtDpcLevel(&(pInterface->rlLock));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Right now we have the adapter + interface or the connection entry
|
|
// locked.
|
|
//
|
|
|
|
if(nsStatus is NDIS_STATUS_SUCCESS)
|
|
{
|
|
NdisGetFirstBufferFromPacket(pnpPacket,
|
|
&pnbBuffer,
|
|
&pvFirstBuffer,
|
|
&uiFirstBufLen,
|
|
&uiTotalLen);
|
|
|
|
|
|
|
|
if(pInterface)
|
|
{
|
|
pInterface->ulOutOctets += uiTotalLen;
|
|
}
|
|
|
|
#if PKT_DBG
|
|
|
|
Trace(SEND, ERROR,
|
|
("NdisSendComplete: Pkt %x Eth buff %x (%x) Header %x (%x)\n",
|
|
pnpPacket,
|
|
pnbEthBuffer,
|
|
pEthHeader,
|
|
pnbBuffer,
|
|
pvFirstBuffer));
|
|
|
|
#endif PKT_DBG
|
|
|
|
}
|
|
else
|
|
{
|
|
Trace(SEND, INFO,
|
|
("NdisSendComplete: Failed %x\n",
|
|
nsStatus));
|
|
|
|
if(pInterface)
|
|
{
|
|
pInterface->ulOutDiscards++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is not our packet return it to the protocol
|
|
//
|
|
|
|
pPC = (PacketContext *)pnpPacket->ProtocolReserved;
|
|
|
|
//
|
|
// Unlock
|
|
//
|
|
|
|
if(pConnEntry->duUsage isnot DU_CALLIN)
|
|
{
|
|
if(pInterface isnot NULL)
|
|
{
|
|
RtReleaseSpinLockFromDpcLevel(&(pInterface->rlLock));
|
|
}
|
|
}
|
|
|
|
RtReleaseSpinLock(pConnEntry->prlLock,
|
|
kiIrql);
|
|
|
|
if(pPC->pc_common.pc_owner isnot PACKET_OWNER_LINK)
|
|
{
|
|
Trace(SEND, TRACE,
|
|
("NdisSendComplete: Calling IPSendComplete for %p over %p(%p)\n",
|
|
pnpPacket,
|
|
pAdapter,
|
|
pAdapter->pvIpContext));
|
|
|
|
g_pfnIpSendComplete(pAdapter->pvIpContext,
|
|
pnpPacket,
|
|
nsStatus);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Free all buffers from our packet and then the packet itself
|
|
//
|
|
|
|
Trace(SEND, TRACE,
|
|
("NdisSendComplete: Not calling IPSendComplete for %p\n",
|
|
pnpPacket));
|
|
|
|
WanpFreePacketAndBuffers(pnpPacket);
|
|
}
|
|
|
|
//
|
|
// Deref the conn entry for the send and for the fact that
|
|
// GetConnEntry.. put a ref on it
|
|
//
|
|
|
|
DereferenceConnEntry(pConnEntry);
|
|
DereferenceConnEntry(pConnEntry);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
WanpTransmitQueuedPackets(
|
|
IN PADAPTER pAdapter,
|
|
IN PUMODE_INTERFACE pInterface,
|
|
IN PCONN_ENTRY pConnEntry,
|
|
IN KIRQL kiIrql
|
|
)
|
|
{
|
|
ULONG i;
|
|
PNDIS_PACKET rgPacketArray[64];
|
|
NDIS_PACKET **ppPacketArray;
|
|
LIST_ENTRY leBufferList, *pleNode;
|
|
|
|
|
|
//
|
|
// This is only called for ROUTER interfaces
|
|
//
|
|
|
|
RtAssert(pConnEntry->duUsage is DU_ROUTER);
|
|
RtAssert(pInterface->duUsage is DU_ROUTER);
|
|
|
|
//
|
|
// If there are no packets to transmit, just release the
|
|
// locks
|
|
//
|
|
|
|
if(pInterface->ulPacketsPending is 0)
|
|
{
|
|
RtAssert(IsListEmpty(&(pAdapter->lePendingPktList)));
|
|
RtAssert(IsListEmpty(&(pAdapter->lePendingHdrList)));
|
|
|
|
|
|
RtReleaseSpinLockFromDpcLevel(&(pInterface->rlLock));
|
|
|
|
RtReleaseSpinLock(&(pAdapter->rlLock),
|
|
kiIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
if(pInterface->ulPacketsPending <= 64)
|
|
{
|
|
//
|
|
// Just use the stack array
|
|
//
|
|
|
|
ppPacketArray = rgPacketArray;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate a packet array
|
|
//
|
|
|
|
ppPacketArray =
|
|
RtAllocate(NonPagedPool,
|
|
sizeof(PNDIS_PACKET) * pInterface->ulPacketsPending,
|
|
WAN_CONN_TAG);
|
|
|
|
if(ppPacketArray is NULL)
|
|
{
|
|
Trace(SEND, ERROR,
|
|
("TransmitQueuedPackets: Unable to allocate %d pointers\n",
|
|
pInterface->ulPacketsPending));
|
|
|
|
while(!IsListEmpty(&(pAdapter->lePendingPktList)))
|
|
{
|
|
PNDIS_PACKET pnpPacket;
|
|
|
|
pleNode = RemoveHeadList(&(pAdapter->lePendingPktList));
|
|
|
|
//
|
|
// get to the packet structure in which LIST_ENTRY is embedded
|
|
//
|
|
|
|
pnpPacket = CONTAINING_RECORD(pleNode,
|
|
NDIS_PACKET,
|
|
MacReserved);
|
|
|
|
WanpFreePacketAndBuffers(pnpPacket);
|
|
}
|
|
|
|
while(!IsListEmpty(&(pAdapter->lePendingHdrList)))
|
|
{
|
|
PBYTE pbyHeader;
|
|
PBUFFER_HEAD pBuffHead;
|
|
|
|
pleNode = RemoveHeadList(&(pAdapter->lePendingHdrList));
|
|
|
|
#if LIST_DBG
|
|
pBuffHead = CONTAINING_RECORD(pleNode,
|
|
BUFFER_HEAD,
|
|
leListLink);
|
|
|
|
RtAssert(IsListEmpty(&(pBuffHead->leFreeBufferLink)));
|
|
RtAssert(pBuffHead->bBusy);
|
|
|
|
pBuffHead->leListLink.Flink = NULL;
|
|
pBuffHead->leListLink.Blink = NULL;
|
|
|
|
#else
|
|
pBuffHead = CONTAINING_RECORD(pleNode,
|
|
BUFFER_HEAD,
|
|
leFreeBufferLink);
|
|
#endif
|
|
|
|
|
|
pbyHeader = BUFFER_FROM_HEAD(pBuffHead);
|
|
|
|
FreeBufferToPool(&g_bpHeaderBufferPool,
|
|
pbyHeader);
|
|
}
|
|
}
|
|
}
|
|
|
|
for(i = 0, pleNode = pAdapter->lePendingPktList.Flink;
|
|
pleNode isnot &(pAdapter->lePendingPktList);
|
|
pleNode = pleNode->Flink, i++)
|
|
{
|
|
PNDIS_PACKET pnpPacket;
|
|
|
|
pnpPacket = CONTAINING_RECORD(pleNode,
|
|
NDIS_PACKET,
|
|
MacReserved);
|
|
|
|
ppPacketArray[i] = pnpPacket;
|
|
}
|
|
|
|
RtAssert(i is pInterface->ulPacketsPending);
|
|
|
|
#if DBG
|
|
|
|
for(i = 0, pleNode = pAdapter->lePendingHdrList.Flink;
|
|
pleNode isnot &(pAdapter->lePendingHdrList);
|
|
pleNode = pleNode->Flink, i++);
|
|
|
|
RtAssert(i is pInterface->ulPacketsPending);
|
|
|
|
#endif
|
|
|
|
//
|
|
// copy out the pending hdr list to leBufferList.
|
|
//
|
|
|
|
leBufferList = pAdapter->lePendingHdrList;
|
|
|
|
pAdapter->lePendingHdrList.Flink->Blink = &leBufferList;
|
|
pAdapter->lePendingHdrList.Blink->Flink = &leBufferList;
|
|
|
|
pInterface->ulPacketsPending = 0;
|
|
|
|
InitializeListHead(&(pAdapter->lePendingPktList));
|
|
InitializeListHead(&(pAdapter->lePendingHdrList));
|
|
|
|
WanpSendPackets(pAdapter,
|
|
pInterface,
|
|
pConnEntry,
|
|
ppPacketArray,
|
|
&leBufferList,
|
|
pInterface->ulPacketsPending,
|
|
kiIrql);
|
|
|
|
if(rgPacketArray isnot ppPacketArray)
|
|
{
|
|
RtFree(ppPacketArray);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NDIS_STATUS
|
|
WanpCopyAndQueuePackets(
|
|
PADAPTER pAdapter,
|
|
NDIS_PACKET **ppPacketArray,
|
|
PLIST_ENTRY pleBufferList,
|
|
UINT uiNumPackets
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine queues the packet to the adapter
|
|
Once this routine is called, the caller can not touch the pleListHead
|
|
|
|
Locks:
|
|
|
|
The ADAPTER must be locked and mapped
|
|
The interface the adapter is mapped to must also be locked
|
|
|
|
Arguments:
|
|
|
|
pAdapter
|
|
ppPacketArray
|
|
pBuffHead
|
|
uiNumPackets
|
|
|
|
Return Value:
|
|
|
|
NDIS_STATUS_SUCCESS
|
|
STATUS_QUOTA_EXCEEDED
|
|
NDIS_STATUS_RESOURCES
|
|
|
|
--*/
|
|
|
|
{
|
|
PacketContext *pPC;
|
|
NDIS_STATUS nsStatus;
|
|
PLIST_ENTRY pleNode;
|
|
ULONG i;
|
|
|
|
#if DBG
|
|
ULONG ulPended = 0, ulHdrs = 0;
|
|
#endif
|
|
|
|
|
|
TraceEnter(SEND, "CopyAndQueuePackets");
|
|
|
|
if(pAdapter->pInterface->ulPacketsPending >= WANARP_MAX_PENDING_PACKETS)
|
|
{
|
|
Trace(SEND, WARN,
|
|
("CopyAndQueuePackets: Dropping packets since cap exceeded\n"));
|
|
|
|
return STATUS_QUOTA_EXCEEDED;
|
|
}
|
|
|
|
for(i = 0; i < uiNumPackets; i++)
|
|
{
|
|
PNDIS_PACKET pnpPacket;
|
|
UINT uiTotalLen, uiBytesCopied;
|
|
|
|
//
|
|
// Get size of buffers required
|
|
//
|
|
|
|
NdisQueryPacket(ppPacketArray[i],
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&uiTotalLen);
|
|
|
|
//
|
|
// Allocate a packet.
|
|
//
|
|
|
|
pnpPacket = NULL;
|
|
|
|
NdisAllocatePacket(&nsStatus,
|
|
&pnpPacket,
|
|
g_nhPacketPool);
|
|
|
|
if(nsStatus isnot NDIS_STATUS_SUCCESS)
|
|
{
|
|
Trace(SEND, ERROR,
|
|
("CopyAndQueuePackets: Cant allocate packet. %x\n",
|
|
nsStatus));
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate buffers for the packet
|
|
//
|
|
|
|
nsStatus = GetBufferChainFromPool(&g_bpDataBufferPool,
|
|
pnpPacket,
|
|
uiTotalLen,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
if(nsStatus is STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// If we got a packet and the buffers, then copy from TCP/IP's
|
|
// packet into ours
|
|
//
|
|
|
|
NdisCopyFromPacketToPacket(pnpPacket,
|
|
0,
|
|
uiTotalLen,
|
|
ppPacketArray[i],
|
|
0,
|
|
&uiBytesCopied);
|
|
|
|
RtAssert(uiBytesCopied is uiTotalLen);
|
|
|
|
//
|
|
// This is now our packet, so set its context
|
|
//
|
|
|
|
pPC = (PacketContext *)pnpPacket->ProtocolReserved;
|
|
|
|
pPC->pc_common.pc_owner = PACKET_OWNER_LINK;
|
|
|
|
//
|
|
// Attach Packet to pending packet list
|
|
// We use the MacReserved portion as the list entry
|
|
//
|
|
|
|
InsertTailList(&pAdapter->lePendingPktList,
|
|
(PLIST_ENTRY)&(pnpPacket->MacReserved));
|
|
|
|
pAdapter->pInterface->ulPacketsPending++;
|
|
|
|
#if DBG
|
|
ulPended++;
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
PBUFFER_HEAD pBufferHead;
|
|
PBYTE pbyHeader;
|
|
|
|
//
|
|
// We either have no packet, or couldnt get a buffer.
|
|
// Nasty Nasty: Side effect of such a failure is that we free
|
|
// one of the header buffers
|
|
//
|
|
|
|
RtAssert(!IsListEmpty(pleBufferList));
|
|
|
|
pleNode = RemoveHeadList(pleBufferList);
|
|
|
|
#if LIST_DBG
|
|
|
|
pBufferHead = CONTAINING_RECORD(pleNode,
|
|
BUFFER_HEAD,
|
|
leListLink);
|
|
|
|
RtAssert(IsListEmpty(&(pBufferHead->leFreeBufferLink)));
|
|
RtAssert(pBufferHead->bBusy);
|
|
|
|
pBufferHead->leListLink.Flink = NULL;
|
|
pBufferHead->leListLink.Blink = NULL;
|
|
|
|
#else
|
|
|
|
pBufferHead = CONTAINING_RECORD(pleNode,
|
|
BUFFER_HEAD,
|
|
leFreeBufferLink);
|
|
|
|
#endif
|
|
|
|
//
|
|
// Get a pointer to the data and to the buffer
|
|
//
|
|
|
|
pbyHeader = BUFFER_FROM_HEAD(pBufferHead);
|
|
|
|
FreeBufferToPool(&g_bpHeaderBufferPool,
|
|
pbyHeader);
|
|
|
|
if(pnpPacket)
|
|
{
|
|
NdisFreePacket(pnpPacket);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// we have queued all the packets we could, and for the ones we
|
|
// failed, we freed the corresponding ethernet header.
|
|
// So the number of headers left on pleBufferList should be the number of
|
|
// packets queued
|
|
//
|
|
|
|
if(!IsListEmpty(pleBufferList))
|
|
{
|
|
|
|
#if DBG
|
|
for(pleNode = pleBufferList->Flink;
|
|
pleNode isnot pleBufferList;
|
|
pleNode = pleNode->Flink)
|
|
{
|
|
ulHdrs++;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Add the headers to the front of the adapter chain
|
|
//
|
|
|
|
pleBufferList->Blink->Flink = pAdapter->lePendingHdrList.Flink;
|
|
pleBufferList->Flink->Blink = &(pAdapter->lePendingHdrList);
|
|
|
|
|
|
pAdapter->lePendingHdrList.Flink->Blink = pleBufferList->Blink;
|
|
pAdapter->lePendingHdrList.Flink = pleBufferList->Flink;
|
|
}
|
|
|
|
#if DBG
|
|
RtAssert(ulPended is ulHdrs);
|
|
#endif
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
WanpFreePacketAndBuffers(
|
|
PNDIS_PACKET pnpPacket
|
|
)
|
|
{
|
|
PNDIS_BUFFER pnbFirstBuffer;
|
|
|
|
FreeBufferChainToPool(&g_bpDataBufferPool,
|
|
pnpPacket);
|
|
|
|
NdisFreePacket(pnpPacket);
|
|
}
|
|
|
|
|
|
VOID
|
|
WanIpInvalidateRce(
|
|
PVOID pvContext,
|
|
RouteCacheEntry *pRce
|
|
)
|
|
{
|
|
return;
|
|
}
|