2020-09-30 17:12:29 +02:00

3081 lines
68 KiB
C

/*++
Copyright (c) 1990-1995 Microsoft Corporation
Module Name:
Send.c
Abstract:
This file contains the procedures for doing a send from a protocol, bound
to the upper interface of NdisWan, to a Wan Miniport link, bound to the
lower interfaceof NdisWan. The upper interface of NdisWan conforms to the
NDIS 3.1 Miniport specification. The lower interface of NdisWan conforms
to the NDIS 3.1 Extentions for Wan Miniport drivers.
Author:
Tony Bell (TonyBe) June 06, 1995
Environment:
Kernel Mode
Revision History:
TonyBe 06/06/95 Created
--*/
#include "wan.h"
#include "tcpip.h"
#include "vjslip.h"
#include "compress.h"
#include <rc4.h>
#define EXTRA_COPY 1
//
// Local function prototypes
//
NDIS_STATUS
FrameAndSend(
PBUNDLECB BundleCB,
PPROTOCOLCB ProtocolCB,
PNDIS_PACKET NdisPacket,
BOOLEAN DoMultilink,
PULONG BytesSent
);
NDIS_STATUS
SendPacketOnBundle(
PBUNDLECB BundleCB
);
#ifdef BANDWIDTH_ON_DEMAND
BOOLEAN
IsProtocolQuotaFilled(
PPROTOCOLCB ProtocolCB
);
VOID
AgeSampleTable(
PSAMPLE_TABLE SampleTable
);
VOID
UpdateSampleTable(
PSAMPLE_TABLE SampleTable,
ULONG BytesSent
);
BOOLEAN
IsSampleTableFull(
PSAMPLE_TABLE SampleTable
);
VOID
UpdateBandwidthOnDemand(
PBUNDLECB BundleCB,
ULONG BytesSent
);
VOID
CheckUpperThreshold(
PBUNDLECB BundleCB
);
VOID
CheckLowerThreshold(
PBUNDLECB BundleCB
);
#endif // end of BANDWIDTH_ON_DEMAND
ULONG
GetNumSendingLinks(
PBUNDLECB BundleCB
);
PLINKCB
GetNextLinkToXmitOn(
PBUNDLECB BundleCB
);
VOID
BuildLinkHeader(
PHEADER_FRAMING_INFO FramingInfo,
PUCHAR StartBuffer
);
//VOID
//AddPPPProtocolID(
// PHEADER_FRAMING_INFO FramingInfo,
// USHORT ProtocolID
// );
//VOID
//AddMultilinkInfo(
// PHEADER_FRAMING_INFO FramingInfo,
// UCHAR Flags,
// ULONG SequenceNumber,
// ULONG SequenceMask
// );
//VOID
//AddCompressionInfo(
// PHEADER_FRAMING_INFO FramingInfo,
// USHORT CoherencyCounter
// );
PNDIS_WAN_PACKET
GetWanPacketFromLink(
PLINKCB LinkCB
);
VOID
ReturnWanPacketToLink(
PLINKCB LinkCB,
PNDIS_WAN_PACKET WanPacket
);
VOID
DestroyIoPacket(
PNDIS_PACKET NdisPacket
);
#if DBG
VOID
InsertDbgPacket(
PDBG_SEND_CONTEXT DbgContext
);
BOOLEAN
RemoveDbgPacket(
PDBG_SEND_CONTEXT DbgContext
);
#endif
//
// end of local function prototypes
//
NDIS_STATUS
NdisWanSend(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET NdisPacket,
IN UINT Flags
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PADAPTERCB AdapterCB = (PADAPTERCB)MiniportAdapterContext;
ULONG BundleIndex = 0, ProtocolIndex = 0, BytesCopied = 0;
BOOLEAN SendOnWire;
PBUNDLECB BundleCB;
PPROTOCOLCB ProtocolCB;
PETH_HEADER EthernetHeader;
PUCHAR DestAddr, SrcAddr;
USHORT ProtocolType = AdapterCB->ProtocolType;
PNDIS_BUFFER FirstBuffer;
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("NdisWanSend: Enter"));
NdisWanDbgOut(DBG_INFO, DBG_SEND, ("s-0x%8.8x, 0x%8.8x, 0x%8.8x", NdisPacket,
*((PULONG)&NdisPacket->WrapperReserved[0]), *((PULONG)&NdisPacket->WrapperReserved[4])));
NdisWanInterlockedInc(&AdapterCB->ulReferenceCount);
//
// Get the ethernet address. This is stolen from the
// NDIS wrapper code. This may be a Win95 portability issue.
//
FirstBuffer = NdisPacket->Private.Head;
EthernetHeader = (PETH_HEADER)MDL_ADDRESS(FirstBuffer);
// NdisWanCopyFromPacketToBuffer(NdisPacket,
// 0,
// sizeof(ETH_HEADER),
// (PUCHAR)&EthernetHeader,
// &BytesCopied);
// if (BytesCopied < ETH_LENGTH_OF_ADDRESS) {
//
// goto NdisWanSendExit;
// }
DestAddr = EthernetHeader->DestAddr;
SrcAddr = EthernetHeader->SrcAddr;
//
// Is this destined for the wire or is it self directed?
// If SendOnWire is FALSE this is a self directed packet.
//
ETH_COMPARE_NETWORK_ADDRESSES_EQ(DestAddr, SrcAddr, &SendOnWire);
//
// Do we need to do loopback? We can check for both multicast
// and broadcast with one check because we don't differentiate
// between the two.
//
if (!SendOnWire || (DestAddr[0] & 1)) {
//
// Put on loopback queue
//
NdisWanQueueLoopbackPacket(AdapterCB, NdisPacket);
}
if (!SendOnWire || AdapterCB == NdisWanCB.PromiscuousAdapter) {
goto NdisWanSendExit;
}
//
// We play special tricks with NBF because NBF is
// guaranteed to have a one-to-one mapping between
// an adapter and a bundle.
//
if (AdapterCB->ProtocolType == PROTOCOL_NBF) {
BundleCB = AdapterCB->NbfBundleCB;
if (BundleCB == NULL) {
//
// This should just fall through and complete successfully.
//
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: BundleCB is NULL!, BundleHandle: 0x%8.8x", BundleIndex));
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: AdapterCB: 0x%8.8x, ProtocolType: 0x%4.4x!", AdapterCB, ProtocolType));
goto NdisWanSendExit;
}
ProtocolIndex = (ULONG)AdapterCB->NbfProtocolHandle;
} else {
//
// If this a multicast or broadcast our destination
// address context has been compromised. We have to
// lift the bundle information out of the SRC address.
//
//
if (DestAddr[0] & 1) {
//
// Get the stashed BundleIndex
//
GetTransportBundleIndex(SrcAddr, BundleIndex);
BUNDLECB_FROM_BUNDLEH(BundleCB, BundleIndex);
if (BundleCB == NULL) {
//
// This should just fall through and complete successfully.
//
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: BundleCB is NULL!, BundleHandle: 0x%8.8x", BundleIndex));
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: AdapterCB: 0x%8.8x, ProtocolType: 0x%4.4x!", AdapterCB, ProtocolType));
goto NdisWanSendExit;
}
//
// Get the ProtocolIndex from the BundleCB's
// list of protocols.
//
GetProtocolIndexFromProtocolList(&BundleCB->ProtocolCBList,
ProtocolType,
ProtocolIndex);
} else {
//
// Get the Bundle Index and BundleCB
//
GetNdisWanBundleIndex(DestAddr, BundleIndex);
BUNDLECB_FROM_BUNDLEH(BundleCB, BundleIndex);
if (BundleCB == NULL) {
//
// This should just fall through and complete successfully.
//
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: BundleCB is NULL!, BundleHandle: 0x%8.8x", BundleIndex));
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: AdapterCB: 0x%8.8x, ProtocolType: 0x%4.4x!", AdapterCB, ProtocolType));
goto NdisWanSendExit;
}
//
// Get the Protocol Index
//
GetNdisWanProtocolIndex(DestAddr, ProtocolIndex);
}
}
NdisAcquireSpinLock(&BundleCB->Lock);
//
// Get the ProtocolCB from the BundleCB->ProtocolCBTable
//
ProtocolCBFromProtocolH(BundleCB, ProtocolIndex, ProtocolCB);
if ((BundleCB->State != BUNDLE_UP) || !(BundleCB->Flags & BUNDLE_ROUTED) ||
!IsValidProtocolCB(ProtocolCB) || !(ProtocolCB->Flags & PROTOCOL_ROUTED) ||
ProtocolCB->hProtocolHandle == 0) {
NdisReleaseSpinLock(&BundleCB->Lock);
NdisWanDbgOut(DBG_INFO, DBG_SEND,("NdisWanSend: Problem with route!"));
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: BundleCB: 0x%8.8x State: 0x%8.8x, Flags: 0x%8.8x",
BundleCB, BundleCB->State, BundleCB->Flags));
NdisWanDbgOut(DBG_INFO, DBG_SEND,
("NdisWanSend: ProtocolCB: 0x%8.8x, ProtocolHandle: 0x%8.8x, Flags: 0x%8.8x",
ProtocolCB, ProtocolIndex, ProtocolCB->Flags));
goto NdisWanSendExit;
}
NdisWanInterlockedInc(&NdisWanCB.SendCount);
#if DBG
{
DBG_SEND_CONTEXT DbgContext;
DbgContext.Packet = NdisPacket;
DbgContext.PacketType = PACKET_TYPE_NDIS;
DbgContext.BundleCB = BundleCB;
DbgContext.ProtocolCB = ProtocolCB;
DbgContext.LinkCB = NULL;
DbgContext.ListHead = &ProtocolCB->AdapterCB->DbgNdisPacketList;
DbgContext.ListLock = &ProtocolCB->AdapterCB->Lock;
InsertDbgPacket(&DbgContext);
}
#endif
//
// Queue the packet on the ProtocolCB NdisPacketQueue
//
InsertTailNdisPacketQueue(ProtocolCB, NdisPacket);
//
// Try to send a packet on the BundleCB. Called
// with bundle lock held but returns with lock
// free.
//
Status = SendPacketOnBundle(BundleCB);
ASSERT (Status == NDIS_STATUS_PENDING);
NdisWanSendExit:
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("NdisWanSend: Exit, Status: 0x%8.8x", Status));
NdisWanInterlockedDec(&AdapterCB->ulReferenceCount);
return (Status);
}
VOID
NdisWanSendCompleteHandler(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_WAN_PACKET WanPacket,
IN NDIS_STATUS Status
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
)
{
PLINKCB LinkCB;
PBUNDLECB BundleCB;
PPROTOCOLCB ProtocolCB;
PWAN_IO_PROTOCOL_RESERVED ProtocolReserved;
PNDIS_PACKET NdisPacket;
BOOLEAN FreeLink = FALSE;
BOOLEAN FreeBundle = FALSE;
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("NdisWanSendComplete: Enter"));
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("WanPacket: 0x%8.8x", WanPacket));
//
// Get info from the WanPacket
//
LinkCB = (PLINKCB)WanPacket->ProtocolReserved1;
NdisPacket = (PNDIS_PACKET)WanPacket->ProtocolReserved2;
ProtocolCB = (PPROTOCOLCB)WanPacket->ProtocolReserved3;
NdisWanDbgOut(DBG_INFO, DBG_SEND, ("sc-0x%8.8x, 0x%8.8x, 0x%8.8x", NdisPacket,
*((PULONG)&NdisPacket->WrapperReserved[0]), *((PULONG)&NdisPacket->WrapperReserved[4])));
//
// Bundle that this link is on
//
BundleCB = LinkCB->BundleCB;
#if DBG
{
DBG_SEND_CONTEXT DbgContext;
DbgContext.Packet = WanPacket;
DbgContext.PacketType = PACKET_TYPE_WAN;
DbgContext.BundleCB = BundleCB;
DbgContext.ProtocolCB = ProtocolCB;
DbgContext.LinkCB = LinkCB;
DbgContext.ListHead = &LinkCB->WanAdapterCB->DbgWanPacketList;
DbgContext.ListLock = &LinkCB->WanAdapterCB->Lock;
RemoveDbgPacket(&DbgContext);
}
#endif
NdisAcquireSpinLock(&BundleCB->Lock);
//
// Return the WanPacket to the link
//
ReturnWanPacketToLink(LinkCB, WanPacket);
//
// Update link stats
//
if ((--LinkCB->OutstandingFrames == 0) &&
(LinkCB->State == LINK_GOING_DOWN)) {
LinkCB->State = LINK_DOWN;
FreeLink = TRUE;
RemoveLinkFromBundle(BundleCB, LinkCB);
if (BundleCB->ulLinkCBCount == 0) {
BundleCB->State = BUNDLE_GOING_DOWN;
}
}
NdisReleaseSpinLock(&BundleCB->Lock);
ASSERT((SHORT)PMINIPORT_RESERVED_FROM_NDIS(NdisPacket)->ReferenceCount > 0);
//
// See if the reference count is zero
//
if (--(PMINIPORT_RESERVED_FROM_NDIS(NdisPacket)->ReferenceCount)) {
//
// The reference count is not yet zero
//
return;
}
TryToCompleteNdisPacket(ProtocolCB->AdapterCB, NdisPacket);
NdisAcquireSpinLock(&BundleCB->Lock);
//
// If this bundle is going away but is waiting on all outstanding frames
// we need to do cleanup.
//
if (--BundleCB->OutstandingFrames == 0) {
//
// If this bundle is going away but unroute is waiting on
// all outstanding frames we need to signal the waiting thread.
//
if (BundleCB->Flags & FRAMES_PENDING) {
NdisWanSetSyncEvent(&BundleCB->OutstandingFramesEvent);
} else if ((BundleCB->State == BUNDLE_GOING_DOWN) &&
!(BundleCB->Flags & BUNDLE_ROUTED)){
BundleCB->State = BUNDLE_DOWN;
FreeBundle = TRUE;
}
}
//
// Called with bundle lock help but returns with lock released
//
SendPacketOnBundle(BundleCB);
if (FreeLink) {
//
// Remove this link from the connection table
//
RemoveLinkFromConnectionTable(LinkCB);
NdisWanReturnLinkCB(LinkCB);
}
if (FreeBundle) {
//
// Remove this bundle from the connection table
//
RemoveBundleFromConnectionTable(BundleCB);
NdisWanReturnBundleCB(BundleCB);
}
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("NdisWanSendComplete: Exit"));
}
VOID
TryToCompleteNdisPacket(
PADAPTERCB AdapterCB,
PNDIS_PACKET NdisPacket
)
{
//
// If this is a packet that we created we need to free the resources
//
if (PMINIPORT_RESERVED_FROM_NDIS(NdisPacket)->MagicNumber == NDISWAN_MAGIC_NUMBER) {
DestroyIoPacket(NdisPacket);
} else {
PDEFERRED_DESC DeferredDesc;
if ((AdapterCB->ulReferenceCount == 0) &&
NdisWanAcquireMiniportLock(AdapterCB)) {
NdisAcquireSpinLock(&AdapterCB->Lock);
if (IsDeferredQueueEmpty(&AdapterCB->DeferredQueue[SendComplete])) {
NdisReleaseSpinLock(&AdapterCB->Lock);
#if DBG
{
DBG_SEND_CONTEXT DbgContext;
DbgContext.Packet = NdisPacket;
DbgContext.PacketType = PACKET_TYPE_NDIS;
DbgContext.BundleCB = NULL;
DbgContext.ProtocolCB = NULL;
DbgContext.LinkCB = NULL;
DbgContext.ListHead = &AdapterCB->DbgNdisPacketList;
DbgContext.ListLock = &AdapterCB->Lock;
RemoveDbgPacket(&DbgContext);
}
#endif
//
// We got the lock and there are no pending send completes
// so go ahead and complete this one!
//
NdisMSendComplete(AdapterCB->hMiniportHandle,
NdisPacket,
NDIS_STATUS_SUCCESS);
//
// Increment global count
//
NdisWanInterlockedInc(&NdisWanCB.SendCompleteCount);
NdisWanReleaseMiniportLock(AdapterCB);
return;
}
NdisReleaseSpinLock(&AdapterCB->Lock);
NdisWanReleaseMiniportLock(AdapterCB);
}
NdisAcquireSpinLock(&AdapterCB->Lock);
NdisWanGetDeferredDesc(AdapterCB, &DeferredDesc);
DeferredDesc->Context = NdisPacket;
InsertTailDeferredQueue(&AdapterCB->DeferredQueue[SendComplete], DeferredDesc);
NdisWanSetDeferred(AdapterCB);
NdisReleaseSpinLock(&AdapterCB->Lock);
}
}
VOID
NdisWanProcessSendCompletes(
PADAPTERCB AdapterCB
)
{
while (!IsDeferredQueueEmpty(&AdapterCB->DeferredQueue[SendComplete])) {
PNDIS_PACKET NdisPacket;
PDEFERRED_DESC ReturnDesc;
ReturnDesc = RemoveHeadDeferredQueue(&AdapterCB->DeferredQueue[SendComplete]);
NdisReleaseSpinLock(&AdapterCB->Lock);
NdisPacket = ReturnDesc->Context;
#if DBG
{
DBG_SEND_CONTEXT DbgContext;
DbgContext.Packet = NdisPacket;
DbgContext.PacketType = PACKET_TYPE_NDIS;
DbgContext.BundleCB = NULL;
DbgContext.ProtocolCB = NULL;
DbgContext.LinkCB = NULL;
DbgContext.ListHead = &AdapterCB->DbgNdisPacketList;
DbgContext.ListLock = &AdapterCB->Lock;
RemoveDbgPacket(&DbgContext);
}
#endif
NdisMSendComplete(AdapterCB->hMiniportHandle,
NdisPacket,
NDIS_STATUS_SUCCESS);
//
// Increment global count
//
NdisWanInterlockedInc(&NdisWanCB.SendCompleteCount);
NdisAcquireSpinLock(&AdapterCB->Lock);
InsertHeadDeferredQueue(&AdapterCB->FreeDeferredQueue, ReturnDesc);
}
}
NDIS_STATUS
SendPacketOnBundle(
PBUNDLECB BundleCB
)
/*++
Routine Name:
Routine Description:
Called with bundle lock held but returns with lock released!!!
Arguments:
Return Values:
--*/
{
NDIS_STATUS Status = NDIS_STATUS_PENDING;
ULONG ulProtocolSending, BytesSent;
PLIST_ENTRY ProtocolCBList;
PPROTOCOLCB IOProtocolCB;
PPROTOCOLCB ProtocolCB;
BOOLEAN DoMultilink = TRUE;
PLINKCB LinkCB;
#ifdef BANDWIDTH_ON_DEMAND
ULONG ulFirstTime;
#endif
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("SendPacketOnBundle: Enter"));
//
// Are we already involved in a send on this bundlecb?
//
if (BundleCB->Flags & IN_SEND) {
//
// If so flag that we should try back later
// and get the hell out.
//
BundleCB->Flags |= TRY_SEND_AGAIN;
NdisReleaseSpinLock(&BundleCB->Lock);
return (NDIS_STATUS_PENDING);
}
BundleCB->Flags |= IN_SEND;
ProtocolCBList = &BundleCB->ProtocolCBList;
IOProtocolCB = (PPROTOCOLCB)ProtocolCBList->Flink;
SendPacketOnBundleTryAgain:
//
// This contains a bit mask with a bit set for each possible send queue.
// To start off we will set all of the bits so each send queue will have
// a chance to send. If a send queue can send it just sets the bit in
// the mask so sends will continue to happen. If the send queue does not
// have anything to send the bit for that send queue is turned off. When
// all bits are turned off we will fall through the send loop.
//
ulProtocolSending = BundleCB->SendMask;
#ifdef BANDWIDTH_ON_DEMAND
ulFirstTime = (ulProtocolSending & ~IOProtocolCB->SendMaskBit);
#endif
if ((PVOID)(ProtocolCB = (PPROTOCOLCB)IOProtocolCB->Linkage.Flink) ==
(PVOID)ProtocolCBList) {
ProtocolCB = NULL;
}
//
// Stay in loop as long as we have protocols sending and endpoints
// accepting sends.
//
while (ulProtocolSending && (BundleCB->SendingLinks != 0)) {
PNDIS_PACKET NdisPacket;
PPROTOCOLCB SendingProtocolCB = NULL;
ULONG MagicNumber = 0;
//
// Always check to see if there is an I/O (PPP) packet to
// be sent!
//
if (!IsNdisPacketQueueEmpty(IOProtocolCB)) {
PWAN_IO_PROTOCOL_RESERVED pProtocolReserved;
PLINKCB LinkCB;
MagicNumber = NDISWAN_MAGIC_NUMBER;
NdisPacket = IOProtocolCB->HeadNdisPacketQueue;
pProtocolReserved = (PWAN_IO_PROTOCOL_RESERVED)NdisPacket->ProtocolReserved;
//
// Is this a directed PPP packet
//
if (((LinkCB = pProtocolReserved->LinkCB) == NULL) ||
(LinkCB->State != LINK_UP)) {
//
// The link has gone down since this send was
// queued so destroy the packet
//
RemoveHeadNdisPacketQueue(IOProtocolCB);
DestroyIoPacket(NdisPacket);
BundleCB->Flags |= TRY_SEND_AGAIN;
break;
}
if (!IsLinkSendWindowOpen(LinkCB)) {
//
// We can not send from the I/O queue because the send
// window for this link is closed. We will not send
// any data until the link has resources!
//
break;
}
BundleCB->NextLinkToXmit = LinkCB;
DoMultilink = FALSE;
//
// We are sending this packet so take it off of the list
//
RemoveHeadNdisPacketQueue(IOProtocolCB);
ulProtocolSending |= IOProtocolCB->SendMaskBit;
SendingProtocolCB = IOProtocolCB;
//
// End of I/O send check
//
} else {
ulProtocolSending &= ~IOProtocolCB->SendMaskBit;
//
// If there is not another protocol to check get out
//
if (ProtocolCB == NULL)
break;
if (!IsNdisPacketQueueEmpty(ProtocolCB)) {
#ifdef BANDWIDTH_ON_DEMAND
BOOLEAN FirstPass;
FirstPass = (ulFirstTime != 0);
//
// Clear the first time bit. The entire mask will only be
// cleared when all of the protocols have had a chance to
// send at least once.
//
ulFirstTime &= ~ProtocolCB->SendMaskBit;
if (IsSampleTableFull(&ProtocolCB->SampleTable)) {
//
// We don't want this protocol to send again
// until its sampletable has an open entry so clear
// the protocols send bit.
//
ulProtocolSending &= ~ProtocolCB->SendMaskBit;
goto GetNextProtocolCB;
}
//
// We will send a packet from this protocol if it's bandwidth
// quota has not been met or if it's quota has been met we
// can still send if this is not the first time through the
// send loop (all other protocols have had a change to send).
//
if (IsProtocolQuotaFilled(ProtocolCB) && FirstPass) {
goto GetNextProtocolCB;
}
#endif // end of BANDWIDTH_ON_DEMAND
ulProtocolSending |= ProtocolCB->SendMaskBit;
NdisPacket = RemoveHeadNdisPacketQueue(ProtocolCB);
SendingProtocolCB = ProtocolCB;
} else {
//
// Protocol does not have anything to send so mark it
// and get the next protocol.
//
ulProtocolSending &= ~ProtocolCB->SendMaskBit;
#ifdef BANDWIDTH_ON_DEMAND
ulFirstTime &= ~ProtocolCB->SendMaskBit;
#endif
goto GetNextProtocolCB;
}
}
ASSERT(NdisPacket != NULL);
ASSERT(SendingProtocolCB != NULL);
//
// We we get here we should have a valid NdisPacket with at least one link
// that is accepting sends
//
//
// The magic number is only set to a non-zero value if this is a send
// through our I/O interface.
//
PMINIPORT_RESERVED_FROM_NDIS(NdisPacket)->MagicNumber = MagicNumber;
//
// We will get the packet into a contiguous buffer, and do framing,
// compression and encryption. This is called with the bundle lock
// held and returns with it released.
//
Status = FrameAndSend(BundleCB,
SendingProtocolCB,
NdisPacket,
DoMultilink,
&BytesSent);
#ifdef BANDWIDTH_ON_DEMAND
//
// Update this protocols sample array with the latest send.
//
UpdateProtocolQuota(SendingProtocolCB, BytesSent);
//
// Update the bandwidth on demand sample array with the latest send.
// If we need to notify someone of a bandwidth event do it.
//
UpdateBandwidthOnDemand(BundleCB, BytesSent);
#endif
NdisAcquireSpinLock(&BundleCB->Lock);
//
// This will force round-robin sends if no protocol
// prioritization has been set.
//
#ifdef BANDWIDTH_ON_DEMAND
if (!(BundleCB->Flags & PROTOCOL_PRIORITY) &&
(ProtocolCB != NULL)) {
#else // end of BANDWIDTH_ON_DEMAND
if (ProtocolCB != NULL) {
#endif // end of !BANDWIDTH_ON_DEMAND
GetNextProtocolCB:
if ((PVOID)(ProtocolCB = (PPROTOCOLCB)ProtocolCB->Linkage.Flink) ==
(PVOID)ProtocolCBList) {
ProtocolCB = (PPROTOCOLCB)IOProtocolCB->Linkage.Flink;
}
}
} // end of the send while loop
//
// Did someone try to do a send while we were already
// sending on this bundle?
//
if (BundleCB->Flags & TRY_SEND_AGAIN) {
//
// If so clear the flag and try another send.
//
BundleCB->Flags &= ~TRY_SEND_AGAIN;
goto SendPacketOnBundleTryAgain;
}
//
// Clear the in send flag.
//
BundleCB->Flags &= ~IN_SEND;
NdisReleaseSpinLock(&BundleCB->Lock);
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("SendPacketOnBundle: Exit"));
return (Status);
}
NDIS_STATUS
FrameAndSend(
PBUNDLECB BundleCB,
PPROTOCOLCB ProtocolCB,
PNDIS_PACKET NdisPacket,
BOOLEAN DoMultilink,
PULONG BytesSent
)
/*++
Routine Name:
FrameAndSend
Routine Description:
This routine does all of the data manipulation required to take the
NdisPacket and make it into the appropriate number of WanPackets. These
WanPackets will be queued on a list on their linkcbs. The manipulation
that occurs includes getting the data from the ndispacket into a contiguous
buffer, protocol header compression, data compression, data encryption,
PPP framing, multilink fragmentation and framing.
Called with bundle lock held but returns with lock released!!!
A finished frame will look like
PPPHeader
ProtocolHeader
Data
Arguments:
BundleCB - Pointer to the BundleCB that we are sending over
ProtocolCB - Pointer to the ProtocolCB that this send is for
NdisPacket - Pointer to the NdisPacket that is being sent
Return Values:
NDIS_STATUS_SUCCESS
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
ULONG BytesCopied, PacketDataOffset;
ULONG FragmentsLeft, FragmentsSent, DataLeft;
PWAN_STATS BundleStats = &BundleCB->BundleStats;
USHORT PPPProtocolID = ProtocolCB->usPPPProtocolID;
//
// Framing information flags
//
ULONG BundleFraming = BundleCB->FramingInfo.SendFramingBits;
ULONG LinkFraming;
//
// This is the next link to be transmitted over
//
PLINKCB LinkCB;
//
// These are pointers to the active WanPacket and
// data buffer.
//
// WanPacket points to the active WanPacket
// StartBuffer points to the begining of the frame
// DataBuffer points to where the data begins in the frame
// FrameLength is the length of the frame
// DataLength is the length of the data
//
PNDIS_WAN_PACKET WanPacket;
PUCHAR StartBuffer, CurrentBuffer, DataBuffer;
ULONG DataLength = 0;
//
// These are points to the second WanPacket and
// data buffer. These are used to compress into
// if compression is on.
//
// WanPacket2 points to the WanPacket used if compression occurs
// PacketNotUsed points to the WanPacket that is to be returned
// StartBuffer2 points to the begining of the data buffer in WanPacket2
//
PNDIS_WAN_PACKET WanPacket2, PacketNotUsed;
PUCHAR StartBuffer2, CurrentBuffer2, DataBuffer2;
//
// Flags set to make decisions on whether to compress and/or encrypt the data
//
ULONG Flags;
BOOLEAN FirstFragment = TRUE;
//
// Used to gather information about the link header
//
HEADER_FRAMING_INFO FramingInfo1, FramingInfo2;
PHEADER_FRAMING_INFO FramingInfo = &FramingInfo1;
ULONG ProtocolHeaderLength;
UCHAR ProtocolBuffer[40];
PUCHAR ProtocolHeader = ProtocolBuffer;
ULONG EthernetHeaderLength;
#ifdef EXTRA_COPY
PUCHAR EthernetHeader;
#else
UCHAR EthernetHeader[12];
#endif
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("FrameAndSend: Enter"));
//
// Clear out the bytes sent count
//
*BytesSent = 0;
//
// Get the next link to xmit on.
//
LinkCB = GetNextLinkToXmitOn(BundleCB);
ASSERT(LinkCB != NULL);
ASSERT(IsLinkSendWindowOpen(LinkCB));
//
// Set flags for compression, encryption and multilink
//
Flags = ((BundleCB->SendCompInfo.MSCompType & NDISWAN_COMPRESSION) &&
(BundleCB->SendCompressContext != NULL)) ? DO_COMPRESSION : 0;
if (BundleCB->SendRC4Key != NULL) {
if (BundleCB->SendCompInfo.MSCompType & NDISWAN_ENCRYPTION) {
Flags |= (DO_ENCRYPTION | DO_LEGACY_ENCRYPTION);
} else if (BundleCB->SendCompInfo.MSCompType & NDISWAN_40_ENCRYPTION) {
Flags |= (DO_ENCRYPTION | DO_40_ENCRYPTION);
}
#ifdef ENCRYPT_128BIT
else if (BundleCB->SendCompInfo.MSCompType & NDISWAN_128_ENCRYPTION) {
Flags |= (DO_ENCRYPTION | DO_128_ENCRYPTION);
}
#endif
}
Flags |= (DoMultilink && (BundleFraming & PPP_FRAMING) &&
(BundleFraming & PPP_MULTILINK_FRAMING)) ? DO_MULTILINK : 0;
if (PPPProtocolID == PPP_PROTOCOL_PRIVATE_IO) {
Flags |= IO_PROTOCOLID;
Flags &= ~(DO_COMPRESSION | DO_ENCRYPTION);
}
Flags |= FIRST_FRAGMENT;
//
// Did the last receive cause us to flush?
//
if (BundleCB->Flags & RECV_PACKET_FLUSH) {
BundleCB->Flags &= ~RECV_PACKET_FLUSH;
Flags |= DO_FLUSH;
}
FramingInfo->FramingBits =
LinkFraming = LinkCB->LinkInfo.SendFramingBits;
FramingInfo->Flags = Flags;
//
// Bump the outstanding frames on the bundle
//
BundleCB->OutstandingFrames++;
//
// If we are in promiscuous mode we should indicate this
// baby back up.
//
if (NdisWanCB.PromiscuousAdapter != NULL) {
NdisWanQueueLoopbackPacket(NdisWanCB.PromiscuousAdapter, NdisPacket);
}
//
// See if we are in pass through mode
//
if (!(BundleFraming & PASS_THROUGH_MODE) &&
!(BundleFraming & RAW_PASS_THROUGH_MODE)) {
//
// Get two wanpackets from the next to send link.
//
WanPacket = GetWanPacketFromLink(LinkCB);
PacketNotUsed =
WanPacket2 = GetWanPacketFromLink(LinkCB);
NdisReleaseSpinLock(&BundleCB->Lock);
//
// This is where we will build the frame. This needs to be
// on a 8 byte boundary.
//
StartBuffer = WanPacket->StartBuffer +
LinkCB->LinkInfo.HeaderPadding +
sizeof(PVOID);
(ULONG)StartBuffer &= (ULONG)~(sizeof(PVOID) - 1);
//
// This is where we will build the frame. This needs to be
// on a 8 byte boundary.
//
StartBuffer2 = WanPacket2->StartBuffer +
LinkCB->LinkInfo.HeaderPadding +
sizeof(PVOID);
(ULONG)StartBuffer2 &= (ULONG)~(sizeof(PVOID) - 1);
BuildLinkHeader(FramingInfo, StartBuffer);
FramingInfo2.FramingBits = FramingInfo->FramingBits;
FramingInfo2.Flags = FramingInfo->Flags;
BuildLinkHeader(&FramingInfo2, StartBuffer2);
DataBuffer =
CurrentBuffer = StartBuffer + FramingInfo->HeaderLength;
DataBuffer2 =
CurrentBuffer2 = StartBuffer2 + FramingInfo->HeaderLength;
//
// If this is a netbios frame and we have to ship the mac header
//
if ((BundleFraming & NBF_PRESERVE_MAC_ADDRESS) &&
(PPPProtocolID == PPP_PROTOCOL_NBF)) {
#ifdef EXTRA_COPY
EthernetHeader = CurrentBuffer;
#endif
//
// Copy Ethernet header to temp buffer
//
NdisWanCopyFromPacketToBuffer(NdisPacket,
0,
12,
EthernetHeader,
&BytesCopied);
ASSERT(BytesCopied == 12);
CurrentBuffer += BytesCopied;
DataLength += BytesCopied;
EthernetHeaderLength = BytesCopied;
}
//
// We are beyond the mac header (also skip the length/protocoltype field)
//
if (PPPProtocolID == PPP_PROTOCOL_PRIVATE_IO) {
PacketDataOffset = 12;
} else {
PacketDataOffset = 14;
}
//
// Do protocol header compression - IP only!
//
if ((PPPProtocolID == PPP_PROTOCOL_IP) &&
(BundleCB->VJCompress != NULL) &&
((BundleFraming & SLIP_VJ_COMPRESSION) || (BundleFraming & PPP_FRAMING))) {
UCHAR CompType = TYPE_IP;
BundleStats->BytesTransmittedUncompressed += 40;
//
// Get the protocol header
//
NdisWanCopyFromPacketToBuffer(NdisPacket,
PacketDataOffset,
40,
ProtocolHeader,
&ProtocolHeaderLength);
PacketDataOffset += ProtocolHeaderLength;
NdisWanDbgOut(DBG_INFO, DBG_SEND_VJ,
("svj %d", ProtocolHeaderLength));
//
// Are we compressing TCP/IP headers? There is a nasty
// hack in VJs implementation for attempting to detect
// interactive TCP/IP sessions. That is, telnet, login,
// klogin, eklogin, and ftp sessions. If detected,
// the traffic gets put on a higher TypeOfService (TOS). We do
// no such hack for RAS. Also, connection ID compression
// is negotiated, but we always don't compress it.
//
CompType = sl_compress_tcp(&ProtocolHeader,
&ProtocolHeaderLength,
BundleCB->VJCompress,
0);
if (BundleFraming & SLIP_VJ_COMPRESSION) {
//
// For SLIP, the upper bits of the first byte
// are for VJ header compression control bits
//
ProtocolHeader[0] |= CompType;
}
#ifdef EXTRA_COPY
NdisMoveMemory(CurrentBuffer, ProtocolHeader, ProtocolHeaderLength);
CurrentBuffer += ProtocolHeaderLength;
DataLength += ProtocolHeaderLength;
#else
#endif
NdisWanDbgOut(DBG_INFO, DBG_SEND_VJ,
("svj %2.2x %d",CompType, ProtocolHeaderLength));
BundleStats->BytesTransmittedCompressed += ProtocolHeaderLength;
switch (CompType) {
case TYPE_IP:
PPPProtocolID = PPP_PROTOCOL_IP;
break;
case TYPE_UNCOMPRESSED_TCP:
PPPProtocolID = PPP_PROTOCOL_UNCOMPRESSED_TCP;
break;
case TYPE_COMPRESSED_TCP:
PPPProtocolID = PPP_PROTOCOL_COMPRESSED_TCP;
break;
default:
DbgBreakPoint();
break;
}
}
#ifdef EXTRA_COPY
//
// Copy the rest of the data from the ndis packet to
// a contiguous buffer
//
NdisWanCopyFromPacketToBuffer(NdisPacket,
PacketDataOffset,
0xFFFFFFFF,
CurrentBuffer,
&BytesCopied);
DataLength += BytesCopied;
#endif
//
// Add the PPP Protocol ID to the PPP header
//
AddPPPProtocolID(FramingInfo, PPPProtocolID);
//
// At this point we have our framinginfo structure created
// StartBuffer points to the begining of the frame, DataBuffer
// points to the place where the data starts in the frame,
// DataLength is the length of the data in the frame.
//
//
// If compression and/or encryption is on and this is not a PPP CP frame do
// data compression.
//
if (Flags & (DO_COMPRESSION | DO_ENCRYPTION)) {
union {
USHORT uShort;
UCHAR uChar[2];
}CoherencyCounter;
//
// If we are compressing/encrypting, the ProtocolID
// is part of the compressed data so fix the pointer
// and the length;
//
DataBuffer -= FramingInfo->ProtocolID.Length;
DataBuffer2 -= FramingInfo->ProtocolID.Length;
DataLength += FramingInfo->ProtocolID.Length;
FramingInfo->HeaderLength -= FramingInfo->ProtocolID.Length;
//
// Get the coherency counter
//
CoherencyCounter.uShort = BundleCB->SCoherencyCounter;
CoherencyCounter.uChar[1] &= 0x0F;
//
// Bump the coherency count
//
BundleCB->SCoherencyCounter++;
if (Flags & DO_COMPRESSION) {
BundleStats->BytesTransmittedUncompressed += DataLength;
if (Flags & DO_FLUSH) {
//
// Init the compression history table and tree
//
initsendcontext(BundleCB->SendCompressContext);
}
#ifdef EXTRA_COPY
//
// We are doing the copy to get things into a contiguous buffer before
// compression occurs
//
CoherencyCounter.uChar[1] |= compress(DataBuffer,
DataBuffer2,
&DataLength,
BundleCB->SendCompressContext);
#else
//
// Compression will occur on fragments. We are not doing a copy
// to get things into a contiguous buffer before compressing
//
//
// If we need to include the ethernet header, compress it
//
//
// If we have a compressed protocol header, compress it again
//
//
// Now we need to walk the NdisBuffer chain compressing each
// buffer as we go. We need to get to the buffer where our
// current DataOffset is. Once we get to this buffer we will
// compress what is left of the buffer. We then go into a loop
// that walks the rest of the buffers in the buffer chain.
//
#endif
if (CoherencyCounter.uChar[1] & PACKET_FLUSHED) {
//
// If encryption is enabled this will force a
// reinit of the table
//
Flags |= DO_FLUSH;
} else {
//
// We compressed the packet so now the active WanPacket will be
// WanPacket2. We need to copy the PPP header from WanPacket to
// WanPacket2. The header includes everything except for the
// protocolid field.
//
NdisMoveMemory(StartBuffer2,
StartBuffer,
FramingInfo->HeaderLength - FramingInfo->ProtocolID.Length);
//
// Now WanPacket2 and all of it's relevant pointers
// and structures are active.
//
PacketNotUsed = WanPacket;
WanPacket = WanPacket2;
DataBuffer = DataBuffer2;
StartBuffer = StartBuffer2;
FramingInfo = &FramingInfo2;
FramingInfo->HeaderLength -= FramingInfo->ProtocolID.Length;
}
BundleStats->BytesTransmittedCompressed += DataLength;
}
//
// Do data encryption
//
if (Flags & DO_ENCRYPTION) {
PUCHAR SessionKey = BundleCB->SendEncryptInfo.SessionKey;
ULONG SessionKeyLength = BundleCB->SendEncryptInfo.SessionKeyLength;
PVOID SendRC4Key = BundleCB->SendRC4Key;
//
// We may need to reinit the rc4 table
//
if (Flags & DO_FLUSH) {
rc4_key(SendRC4Key, SessionKeyLength, SessionKey);
}
//
// Mark this as being encrypted
//
CoherencyCounter.uChar[1] |= PACKET_ENCRYPTED;
//
// Every 256 frames change the RC4 session key
//
if ((BundleCB->SCoherencyCounter & 0xFF) == 0) {
if (Flags & DO_LEGACY_ENCRYPTION) {
//
// Simple munge for legacy encryption
//
SessionKey[3] += 1;
SessionKey[4] += 3;
SessionKey[5] += 13;
SessionKey[6] += 57;
SessionKey[7] += 19;
} else {
//
// Use SHA to get new sessionkey
//
GetNewKeyFromSHA(&BundleCB->SendEncryptInfo);
}
//
// We use rc4 to scramble and recover a new key
//
//
// Re-initialize the rc4 receive table to the
// intermediate value
//
rc4_key(SendRC4Key, SessionKeyLength, SessionKey);
//
// Scramble the existing session key
//
rc4(SendRC4Key, SessionKeyLength, SessionKey);
//
// If this is 40 bit encryption we need to fix
// the first 3 bytes of the key.
//
#ifdef ENCRYPT_128BIT
if (!(Flags & DO_128_ENCRYPTION)) {
#endif
//
// Re-Salt the first 3 bytes
//
SessionKey[0] = 0xD1;
SessionKey[1] = 0x26;
SessionKey[2] = 0x9E;
#ifdef ENCRYPT_128BIT
}
#endif
NdisWanDbgOut(DBG_TRACE, DBG_CCP,
("RC4 Send encryption KeyLength %d", BundleCB->SendEncryptInfo.SessionKeyLength));
NdisWanDbgOut(DBG_TRACE, DBG_CCP,
("RC4 Send encryption Key %.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x",
BundleCB->SendEncryptInfo.SessionKey[0],
BundleCB->SendEncryptInfo.SessionKey[1],
BundleCB->SendEncryptInfo.SessionKey[2],
BundleCB->SendEncryptInfo.SessionKey[3],
BundleCB->SendEncryptInfo.SessionKey[4],
BundleCB->SendEncryptInfo.SessionKey[5],
BundleCB->SendEncryptInfo.SessionKey[6],
BundleCB->SendEncryptInfo.SessionKey[7],
BundleCB->SendEncryptInfo.SessionKey[8],
BundleCB->SendEncryptInfo.SessionKey[9],
BundleCB->SendEncryptInfo.SessionKey[10],
BundleCB->SendEncryptInfo.SessionKey[11],
BundleCB->SendEncryptInfo.SessionKey[12],
BundleCB->SendEncryptInfo.SessionKey[13],
BundleCB->SendEncryptInfo.SessionKey[14],
BundleCB->SendEncryptInfo.SessionKey[15]));
//
// Re-initialize the rc4 receive table to the
// scrambled session key
//
rc4_key(SendRC4Key, SessionKeyLength, SessionKey);
}
//
// Encrypt the data
//
rc4(SendRC4Key, DataLength, DataBuffer);
}
//
// Did the last receive cause us to flush?
//
if (Flags & DO_FLUSH) {
CoherencyCounter.uChar[1] |= PACKET_FLUSHED;
}
//
// Add the coherency bytes to the frame
//
AddCompressionInfo(FramingInfo, CoherencyCounter.uShort);
ASSERT(((CoherencyCounter.uShort + 1) & 0x0FFF) ==
(BundleCB->SCoherencyCounter & 0x0FFF));
}
NdisAcquireSpinLock(&BundleCB->Lock);
//
// Return the unused wanpacket to the pool
//
ReturnWanPacketToLink(LinkCB, PacketNotUsed);
//
// At this point we have our framinginfo structure initialized,
// StartBuffer pointing to the begining of the frame, DataBuffer
// pointing to the begining of the data, DataLength is the
// length of the data.
//
FragmentsLeft = BundleCB->SendingLinks;
DataLeft = DataLength;
FragmentsSent = 0;
FirstFragment = TRUE;
//
// For all fragments we loop fixing up the multilink header
// if multilink is on, fixing up pointers in the wanpacket,
// and queuing the wanpackets for further processing.
//
while (DataLeft) {
ULONG FragDataLength;
ULONG LinkBandwidth;
if (!FirstFragment) {
//
// We had more than one fragment, get the next
// link to send over and a wanpacket from the
// link.
//
LinkCB = GetNextLinkToXmitOn(BundleCB);
ASSERT(IsLinkSendWindowOpen(LinkCB));
WanPacket = GetWanPacketFromLink(LinkCB);
//
// This is where we will build the frame. This needs to be
// on a 8 byte boundary.
//
StartBuffer = WanPacket->StartBuffer +
LinkCB->LinkInfo.HeaderPadding +
sizeof(PVOID);
(ULONG)StartBuffer &= (ULONG)~(sizeof(PVOID) - 1);
//
// Get new framing information and build a new
// header for the new link.
//
FramingInfo->FramingBits =
LinkFraming = LinkCB->LinkInfo.SendFramingBits;
FramingInfo->Flags = (DoMultilink) ? DO_MULTILINK : 0;
BuildLinkHeader(FramingInfo, StartBuffer);
}
LinkBandwidth = LinkCB->ulBandwidth;
if ((Flags & DO_MULTILINK) && (FragmentsLeft > 1) &&
(LinkBandwidth < 85)) {
//
// Calculate the length of this fragment
//
FragDataLength = (DataLength * LinkBandwidth / 100);
FragDataLength = (FragDataLength < NdisWanCB.ulMinFragmentSize) ?
NdisWanCB.ulMinFragmentSize : FragDataLength;
if ((FragDataLength > DataLeft) ||
((LONG)DataLeft - FragDataLength < NdisWanCB.ulMinFragmentSize)) {
//
// This will leave a fragment of less than min frag size
// so send all of the data
//
FragDataLength = DataLeft;
FragmentsLeft = 1;
}
} else {
//
// We either have one fragment left or this link has
// more than 85 percent of the bundle so send what
// data is left
//
FragDataLength = DataLeft;
FragmentsLeft = 1;
}
if (!FirstFragment) {
//
// Copy the data to the new buffer from the old buffer.
//
NdisMoveMemory(StartBuffer + FramingInfo->HeaderLength,
DataBuffer,
FragDataLength);
}
//
// Update the data pointer and the length left to send
//
DataBuffer += FragDataLength;
DataLeft -= FragDataLength;
if (Flags & DO_MULTILINK) {
UCHAR MultilinkFlags = 0;
//
// Multlink is on so create flags for this
// fragment.
//
if (FirstFragment) {
MultilinkFlags = MULTILINK_BEGIN_FRAME;
FirstFragment = FALSE;
}
if (FragmentsLeft == 1) {
MultilinkFlags |= MULTILINK_END_FRAME;
}
//
// Add the multilink header information and
// take care of the sequence number.
//
AddMultilinkInfo(FramingInfo,
MultilinkFlags,
BundleCB->SendSeqNumber,
BundleCB->SendSeqMask);
NdisWanDbgOut(DBG_INFO, DBG_MULTILINK_SEND, ("sf %8.8x %8.8x %d",
BundleCB->SendSeqNumber, MultilinkFlags, FragDataLength));
BundleCB->SendSeqNumber++;
}
//
// Initialize the WanPacket
//
WanPacket->CurrentBuffer = StartBuffer;
WanPacket->CurrentLength = FragDataLength + FramingInfo->HeaderLength;
WanPacket->ProtocolReserved1 = (PVOID)LinkCB;
WanPacket->ProtocolReserved2 = (PVOID)NdisPacket;
WanPacket->ProtocolReserved3 = (PVOID)ProtocolCB;
NdisWanDbgOut(DBG_INFO, DBG_MULTILINK_SEND,
("l %8.8x %8.8x", LinkCB->hLinkHandle));
//
// Add up the bytes that we are sending over all
// links in this bundle.
//
*BytesSent += WanPacket->CurrentLength;
//
// Queue for further processing.
//
InsertTailList(&BundleCB->SendPacketQueue, &WanPacket->WanPacketQueue);
FragmentsSent++;
FragmentsLeft--;
} // end of the fragment loop
ASSERT(FragmentsLeft == 0);
//
// Get the mac reserved structure from the ndispacket. This
// is where we will keep the reference count on the packet.
//
ASSERT((LONG)FragmentsSent > 0 && FragmentsSent <= BundleCB->ulLinkCBCount);
PMINIPORT_RESERVED_FROM_NDIS(NdisPacket)->ReferenceCount = (USHORT)FragmentsSent;
BundleCB->BundleStats.FramesTransmitted++;
//
// At this point we have a list of wanpackets that need to be sent,
// update the total bytes associated with this send, and send
// the packets over their links.
//
while (!IsListEmpty(&BundleCB->SendPacketQueue)) {
Status = NDIS_STATUS_SUCCESS;
//
// Get the wanpacket off of the list
//
WanPacket = (PNDIS_WAN_PACKET)RemoveHeadList(&BundleCB->SendPacketQueue);
//
// Get the link to send over
//
LinkCB = WanPacket->ProtocolReserved1;
//
// Update the outstanding frames on the link
//
LinkCB->LinkStats.FramesTransmitted++;
LinkCB->LinkStats.BytesTransmitted += WanPacket->CurrentLength;
BundleCB->BundleStats.BytesTransmitted += WanPacket->CurrentLength;
#if DBG
{
DBG_SEND_CONTEXT DbgContext;
DbgContext.Packet = WanPacket;
DbgContext.PacketType = PACKET_TYPE_WAN;
DbgContext.BundleCB = BundleCB;
DbgContext.ProtocolCB = ProtocolCB;
DbgContext.LinkCB = LinkCB;
DbgContext.ListHead = &LinkCB->WanAdapterCB->DbgWanPacketList;
DbgContext.ListLock = &LinkCB->WanAdapterCB->Lock;
InsertDbgPacket(&DbgContext);
}
#endif
NdisReleaseSpinLock(&BundleCB->Lock);
//
// If the link is up send the packet
//
if (LinkCB->State == LINK_UP) {
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("FrameAndSend: LinkCB: 0x%8.8x, WanPacket: 0x%8.8x", LinkCB, WanPacket));
WanMiniportSend(&Status,
LinkCB->WanAdapterCB->hNdisBindingHandle,
LinkCB->LineUpInfo.NdisLinkHandle,
WanPacket);
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("FrameAndSend: Status: 0x%8.8x", Status));
}
//
// If we get something other than pending back we need to
// do the send complete.
//
if (Status != NDIS_STATUS_PENDING) {
NdisWanSendCompleteHandler(NULL,
WanPacket,
NDIS_STATUS_SUCCESS);
Status = NDIS_STATUS_PENDING;
}
NdisAcquireSpinLock(&BundleCB->Lock);
}
NdisReleaseSpinLock(&BundleCB->Lock);
} else {
//
// We need to get a WanPacket
//
WanPacket = GetWanPacketFromLink(LinkCB);
NdisReleaseSpinLock(&BundleCB->Lock);
//
// Copy the data into the WanPacket
//
//
// This is where we will build the frame. This needs to be
// on a 8 byte boundary.
//
StartBuffer = WanPacket->StartBuffer +
LinkCB->LinkInfo.HeaderPadding +
sizeof(PVOID);
(ULONG)StartBuffer &= (ULONG)~(sizeof(PVOID) - 1);
NdisWanCopyFromPacketToBuffer(NdisPacket,
0,
0xFFFFFFFF,
StartBuffer,
&BytesCopied);
//
// If we are in pass through mode set the protocol type
//
if (BundleFraming & PASS_THROUGH_MODE) {
StartBuffer[12] = (UCHAR)(ProtocolCB->usProtocolType << 8);
StartBuffer[13] = (UCHAR)ProtocolCB->usProtocolType;
}
WanPacket->CurrentBuffer = StartBuffer;
WanPacket->CurrentLength = BytesCopied;
WanPacket->ProtocolReserved1 = (PVOID)LinkCB;
WanPacket->ProtocolReserved2 = (PVOID)NdisPacket;
WanPacket->ProtocolReserved3 = (PVOID)ProtocolCB;
if (LinkCB->State == LINK_UP) {
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("FrameAndSend: LinkCB: 0x%8.8x, WanPacket: 0x%8.8x", LinkCB, WanPacket));
WanMiniportSend(&Status,
LinkCB->WanAdapterCB->hNdisBindingHandle,
LinkCB->LineUpInfo.NdisLinkHandle,
WanPacket);
}
//
// If we get something other than pending back we need to
// do the send complete.
//
if (Status != NDIS_STATUS_PENDING) {
NdisWanSendCompleteHandler(NULL,
WanPacket,
NDIS_STATUS_SUCCESS);
Status = NDIS_STATUS_PENDING;
}
}
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("FrameAndSend: Exit"));
return (Status);
}
#ifdef BANDWIDTH_ON_DEMAND
BOOLEAN
IsProtocolQuotaFilled(
PPROTOCOLCB ProtocolCB
)
/*++
Routine Name:
IsProtocolQuotaFilled
Routine Description:
This routine checks to see if the protocol has filled it's
bandwidth quota.
Arguments:
ProtocolCB - Pointer to the protocolcb that is sending.
Return Values:
TRUE Quota filled
FALSE Quota not filled
--*/
{
BOOLEAN QuotaMet = FALSE;
PSAMPLE_TABLE SampleTable = &ProtocolCB->SampleTable;
PSEND_SAMPLE FirstSample, CurrentSample;
AgeSampleTable(SampleTable);
if (ProtocolCB->ulByteQuota < SampleTable->ulCurrentSampleByteCount) {
QuotaMet = TRUE;
}
return (QuotaMet);
}
VOID
AgeSampleTable(
PSAMPLE_TABLE SampleTable
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
WAN_TIME CurrentTime, TimeDiff;
ULONG FirstIndex = SampleTable->ulFirstIndex;
ULONG CurrentIndex = SampleTable->ulCurrentIndex;
PSEND_SAMPLE FirstSample = &SampleTable->SampleArray[FirstIndex];
//
// Should return CurrentTime in 100ns units
//
NdisWanGetSystemTime(&CurrentTime);
//
// We will search through the sample indexing over samples that are more than
// one second older than the current time.
//
NdisWanCalcTimeDiff(&TimeDiff, &CurrentTime, &FirstSample->TimeStamp);
while ( !NdisWanIsTimeDiffLess(&TimeDiff, &SampleTable->SamplePeriod) &&
(FirstIndex != CurrentIndex) ) {
SampleTable->ulCurrentSampleByteCount -= FirstSample->ulBytesThisSend;
ASSERT((LONG)SampleTable->ulCurrentSampleByteCount >= 0);
FirstSample->ulReferenceCount = 0;
if (++FirstIndex == SampleTable->ulSampleArraySize) {
FirstIndex = 0;
}
SampleTable->ulFirstIndex = FirstIndex ;
FirstSample = &SampleTable->SampleArray[FirstIndex];
NdisWanCalcTimeDiff(&TimeDiff, &CurrentTime, &FirstSample->TimeStamp);
}
}
BOOLEAN
IsSampleTableFull(
PSAMPLE_TABLE SampleTable
)
{
LONG Diff;
// AgeSampleTable(SampleTable);
Diff = (LONG)(SampleTable->ulCurrentIndex - SampleTable->ulFirstIndex);
return((Diff == (LONG)(SampleTable->ulSampleArraySize - 1)) || (Diff == -1));
}
VOID
UpdateSampleTable(
PSAMPLE_TABLE SampleTable,
ULONG BytesSent
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
WAN_TIME CurrentTime, TimeDiff;
ULONG CurrentIndex = SampleTable->ulCurrentIndex;
PSEND_SAMPLE CurrentSample = &SampleTable->SampleArray[CurrentIndex];
NdisWanGetSystemTime(&CurrentTime);
NdisWanCalcTimeDiff(&TimeDiff, &CurrentTime, &CurrentSample->TimeStamp);
if ( NdisWanIsTimeDiffLess(&TimeDiff, &SampleTable->SampleRate) ||
IsSampleTableFull(SampleTable)) {
//
// Add this send on the previous sample
//
CurrentSample->ulBytesThisSend += BytesSent;
CurrentSample->ulReferenceCount++;
} else {
//
// We need a new sample
//
if (++CurrentIndex == SampleTable->ulSampleArraySize) {
CurrentIndex = 0;
}
SampleTable->ulCurrentIndex = CurrentIndex;
CurrentSample = &SampleTable->SampleArray[CurrentIndex];
CurrentSample->TimeStamp = CurrentTime;
CurrentSample->ulBytesThisSend = BytesSent;
CurrentSample->ulReferenceCount = 1;
}
SampleTable->ulCurrentSampleByteCount += BytesSent;
}
VOID
UpdateBandwidthOnDemand(
PBUNDLECB BundleCB,
ULONG BytesSent
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
ULONG EventCount;
PSAMPLE_TABLE UpperSampleTable = &BundleCB->UpperBonDInfo.SampleTable;
PSAMPLE_TABLE LowerSampleTable = &BundleCB->LowerBonDInfo.SampleTable;
//
// Age and update the sample table
//
AgeSampleTable(UpperSampleTable);
UpdateSampleTable(UpperSampleTable, BytesSent);
AgeSampleTable(LowerSampleTable);
UpdateSampleTable(LowerSampleTable, BytesSent);
GetGlobalListCount(ThresholdEventQueue, EventCount);
if (EventCount != 0) {
CheckUpperThreshold(BundleCB);
CheckLowerThreshold(BundleCB);
}
}
VOID
CheckUpperThreshold(
PBUNDLECB BundleCB
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
WAN_TIME CurrentTime, TimeDiff;
PBOND_INFO BonDInfo = &BundleCB->UpperBonDInfo;
PSAMPLE_TABLE SampleTable = &BonDInfo->SampleTable;
ULONG Bps = SampleTable->ulCurrentSampleByteCount;
//
// Switch on the current state
//
switch (BonDInfo->State) {
case BonDIdle:
//
// We are currently below the upper threshold. If we
// go over the upperthreshold we will set the time and
// transition to the monitor state.
//
if (Bps >= BonDInfo->ulBytesThreshold) {
NdisWanGetSystemTime(&BonDInfo->StartTime);
BonDInfo->State = BonDMonitor;
}
break;
case BonDMonitor:
//
// We are currently in the monitor state which means that
// we have gone above the upper threshold. If we fall below
// the upper threshold we will go back to the idle state.
//
if (Bps < BonDInfo->ulBytesThreshold) {
BonDInfo->State = BonDIdle;
} else {
NdisWanGetSystemTime(&CurrentTime);
NdisWanCalcTimeDiff(&TimeDiff, &CurrentTime, &BonDInfo->StartTime);
if (!NdisWanIsTimeDiffLess(&TimeDiff, &SampleTable->SamplePeriod)) {
//
// We have been above the threshold for time greater than the
// threshold sample period so we need to notify someone of this
// historic event!
//
CompleteThresholdEvent(BundleCB, UPPER_THRESHOLD);
//
// I'm not sure what state we should be in now!
//
BonDInfo->State = BonDSignaled;
}
}
break;
case BonDSignaled:
break;
}
}
VOID
CheckLowerThreshold(
PBUNDLECB BundleCB
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
WAN_TIME CurrentTime, TimeDiff;
PBOND_INFO BonDInfo = &BundleCB->LowerBonDInfo;
PSAMPLE_TABLE SampleTable = &BonDInfo->SampleTable;
ULONG Bps = SampleTable->ulCurrentSampleByteCount;
//
// Switch on the current state
//
switch (BonDInfo->State) {
case BonDIdle:
//
// We are currently above the lower threshold. If we
// go below the lowerthreshold we will set the time and
// transition to the monitor state.
//
if (Bps <= BonDInfo->ulBytesThreshold) {
NdisWanGetSystemTime(&BonDInfo->StartTime);
BonDInfo->State = BonDMonitor;
}
break;
case BonDMonitor:
//
// We are currently in the monitor state which means that
// we have gone below the lower threshold. If we rise above
// the lower threshold we will go back to the idle state.
//
if (Bps > BonDInfo->ulBytesThreshold) {
BonDInfo->State = BonDIdle;
} else {
NdisWanGetSystemTime(&CurrentTime);
NdisWanCalcTimeDiff(&TimeDiff, &CurrentTime, &BonDInfo->StartTime);
if (!NdisWanIsTimeDiffLess(&TimeDiff, &SampleTable->SamplePeriod)) {
//
// We have been below the lower threshold for time greater than the
// threshold sample period so we need to notify someone of this
// historic event!
//
CompleteThresholdEvent(BundleCB, LOWER_THRESHOLD);
//
// I'm not sure what state we should be in now!
//
BonDInfo->State = BonDSignaled;
}
}
break;
case BonDSignaled:
break;
}
}
#endif // end of BANDWIDTH_ON_DEMAND
PLINKCB
GetNextLinkToXmitOn(
PBUNDLECB BundleCB
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
PLINKCB LinkCB = BundleCB->NextLinkToXmit;
PLIST_ENTRY LinkCBList = &BundleCB->LinkCBList;
//
// We need to find the first link that has an open send window
//
while (LinkCB->ulWanPacketCount < 2) {
LinkCB = (PLINKCB)LinkCB->Linkage.Flink;
if ((PVOID)LinkCB == (PVOID)LinkCBList) {
LinkCB = (PLINKCB)LinkCBList->Flink;
}
}
BundleCB->NextLinkToXmit =
((PVOID)LinkCB->Linkage.Flink == (PVOID)LinkCBList) ?
(PLINKCB)LinkCBList->Flink : (PLINKCB)LinkCB->Linkage.Flink;
LinkCB->OutstandingFrames++;
return(LinkCB);
}
NDIS_STATUS
BuildIoPacket(
IN PNDISWAN_IO_PACKET pWanIoPacket,
IN BOOLEAN SendImmediate
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
NDIS_STATUS Status = NDIS_STATUS_RESOURCES;
PWAN_IO_PROTOCOL_RESERVED pProtocolReserved;
PPROTOCOLCB ProtocolCB;
ULONG Stage = 0, ulAllocationSize = 0;
PUCHAR pAllocatedMemory = NULL, pSrcAddr, pDestAddr;
NDIS_HANDLE hPacketPool, hBufferPool, hBundle, hLink;
PNDIS_PACKET pNdisPacket;
PNDIS_BUFFER pNdisBuffer;
PBUNDLECB BundleCB;
PLINKCB LinkCB = NULL;
UCHAR SendHeader[] = {' ', 'S', 'E', 'N', 'D', 0xFF};
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("BuildIoPacket: Enter!"));
//
// Some time in the future this should be redone so that
// there is a pool of packets and buffers attached to a
// BundleCB. This pool could be grown and shrunk as needed
// but some minimum number would live for the lifetime of
// the BundleCB.
if (pWanIoPacket->usHandleType == LINKHANDLE) {
hLink = pWanIoPacket->hHandle;
LINKCB_FROM_LINKH(LinkCB, hLink);
if (LinkCB == NULL) {
return (NDIS_STATUS_SUCCESS);
}
BundleCB = LinkCB->BundleCB;
if (BundleCB == NULL) {
return (NDIS_STATUS_SUCCESS);
}
} else {
hBundle = pWanIoPacket->hHandle;
BUNDLECB_FROM_BUNDLEH(BundleCB, hBundle);
if (BundleCB == NULL) {
return (NDIS_STATUS_SUCCESS);
}
LinkCB = (PLINKCB)BundleCB->LinkCBList.Flink;
if (LinkCB == NULL) {
return (NDIS_STATUS_SUCCESS);
}
}
NdisAcquireSpinLock(&BundleCB->Lock);
if ((LinkCB->State != LINK_UP) ||
(BundleCB->State != BUNDLE_UP)) {
NdisReleaseSpinLock(&BundleCB->Lock);
return (NDIS_STATUS_SUCCESS);
}
//
// We only support ethernet headers right now so the supplied header
// either has to be ethernet or none at all!
//
//
//
// Get an NdisPacket for this send
//
NdisAllocatePacketPool(&Status,
&hPacketPool,
1,
sizeof(WAN_IO_PROTOCOL_RESERVED));
if (Status != NDIS_STATUS_SUCCESS) {
NdisWanDbgOut(DBG_FAILURE, DBG_SEND, ("BuildIoPacket: Error Allocating PacketPool!"));
NdisReleaseSpinLock(&BundleCB->Lock);
goto RESOURCE_ERROR;
}
Stage++;
NdisAllocatePacket(&Status,
&pNdisPacket,
hPacketPool);
if (Status != NDIS_STATUS_SUCCESS) {
NdisWanDbgOut(DBG_FAILURE, DBG_SEND, ("BuildIoPacket: Error Allocating Packet!"));
NdisReleaseSpinLock(&BundleCB->Lock);
goto RESOURCE_ERROR;
}
Stage++;
//
// Get an NdisBuffer for this send
//
NdisAllocateBufferPool(&Status,
&hBufferPool,
2);
if (Status != NDIS_STATUS_SUCCESS) {
NdisWanDbgOut(DBG_FAILURE, DBG_SEND, ("BuildIoPacket: Error Allocating BufferPool!"));
NdisReleaseSpinLock(&BundleCB->Lock);
goto RESOURCE_ERROR;
}
Stage++;
if (pWanIoPacket->usHeaderSize == 0) {
ulAllocationSize = 12;
}
ulAllocationSize += pWanIoPacket->usPacketSize;
NdisWanAllocateMemory(&pAllocatedMemory, ulAllocationSize);
if (pAllocatedMemory == NULL) {
NdisWanDbgOut(DBG_FAILURE, DBG_SEND, ("BuildIoPacket: Error Allocating Memory! Size: %d", ulAllocationSize));
NdisReleaseSpinLock(&BundleCB->Lock);
goto RESOURCE_ERROR;
}
Stage++;
NdisAllocateBuffer(&Status,
&pNdisBuffer,
hBufferPool,
pAllocatedMemory,
ulAllocationSize);
if (Status != NDIS_STATUS_SUCCESS) {
NdisWanDbgOut(DBG_FAILURE, DBG_SEND, ("BuildIoPacket: Error Allocating Buffer!"));
NdisReleaseSpinLock(&BundleCB->Lock);
goto RESOURCE_ERROR;
}
Stage++;
pProtocolReserved = (PWAN_IO_PROTOCOL_RESERVED)pNdisPacket->ProtocolReserved;
pProtocolReserved->LinkCB = LinkCB;
pProtocolReserved->hPacketPool = hPacketPool;
pProtocolReserved->pNdisPacket = pNdisPacket;
pProtocolReserved->hBufferPool = hBufferPool;
pProtocolReserved->pNdisBuffer = pNdisBuffer;
pProtocolReserved->pAllocatedMemory = pAllocatedMemory;
pProtocolReserved->ulAllocationSize = ulAllocationSize;
pDestAddr = &pAllocatedMemory[0];
pSrcAddr = &pAllocatedMemory[6];
//
// If no header build a header
//
if (pWanIoPacket->usHeaderSize == 0) {
//
// Header will look like " S XXYYYY" where
// XX is the ProtocolCB index and YYYY is the
// BundleCB index. Both the Src and Dst addresses
// look the same.
//
NdisMoveMemory(pDestAddr,
SendHeader,
sizeof(SendHeader));
NdisMoveMemory(pSrcAddr,
SendHeader,
sizeof(SendHeader));
} else {
//
// Header supplied so go ahead and move it.
//
NdisMoveMemory(pDestAddr,
pWanIoPacket->PacketData,
pWanIoPacket->usHeaderSize);
}
//
// Fill the BundleCB Index for the Src and Dest Address
//
FillNdisWanProtocolIndex(pDestAddr, hLink);
FillNdisWanProtocolIndex(pSrcAddr, hLink);
//
// Copy the data to the buffer
//
NdisMoveMemory(&pAllocatedMemory[12],
&pWanIoPacket->PacketData[pWanIoPacket->usHeaderSize],
pWanIoPacket->usPacketSize);
//
// Chain buffer to ndis packet
//
NdisChainBufferAtFront(pNdisPacket, pNdisBuffer);
//
// Queue the packet on the bundlecb
//
ProtocolCB = BundleCB->ProtocolCBTable[0];
ASSERT(ProtocolCB != NULL);
if (SendImmediate) {
InsertHeadNdisPacketQueue(ProtocolCB, pNdisPacket);
} else {
InsertTailNdisPacketQueue(ProtocolCB, pNdisPacket);
}
//
// Try to send
//
// Called with lock held and returns with
// lock released
//
Status = SendPacketOnBundle(BundleCB);
//
// We don't pend I/O packets so complete
// as if it succeeded
//
if (Status == NDIS_STATUS_PENDING) {
Status = NDIS_STATUS_SUCCESS;
}
if (Status != NDIS_STATUS_SUCCESS) {
RESOURCE_ERROR:
//
// Free all of the allocated resources
//
switch (Stage) {
case 5:
NdisFreeBuffer(pNdisBuffer);
case 4:
NdisWanFreeMemory(pAllocatedMemory);
case 3:
NdisFreeBufferPool(hBufferPool);
case 2:
NdisFreePacket(pNdisPacket);
case 1:
NdisFreePacketPool(hPacketPool);
}
}
NdisWanDbgOut(DBG_TRACE, DBG_SEND, ("BuildIoPacket: Exit-Status: 0x%8.8x\n", Status));
return (Status);
}
//ULONG
//GetNumSendingLinks(
// PBUNDLECB BundleCB
// )
///*++
//
//Routine Name:
//
//Routine Description:
//
//Arguments:
//
//Return Values:
//
//--*/
//{
// ULONG LinkCount = 0;
// PLINKCB LinkCB;
//
// //
// // We need to walk through the list of links on this bundle and
// // count how many have an open send window.
// //
// for (LinkCB = (PLINKCB)BundleCB->LinkCBList.Flink;
// (PVOID)LinkCB != (PVOID)&BundleCB->LinkCBList;
// LinkCB = (PLINKCB)LinkCB->Linkage.Flink) {
//
// //
// // Since we create enough sendwindow + 1 wanpackets
// // for each link, if the send window is open we will
// // have atleast 2 wanpackets available.
// //
// if (LinkCB->ulWanPacketCount > 1) {
// LinkCount++;
// }
// }
//
// return (LinkCount);
//}
VOID
BuildLinkHeader(
PHEADER_FRAMING_INFO FramingInfo,
PUCHAR StartBuffer
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
ULONG LinkFraming = FramingInfo->FramingBits;
ULONG Flags = FramingInfo->Flags;
PUCHAR CurrentPointer = StartBuffer;
FramingInfo->HeaderLength =
FramingInfo->AddressControl.Length =
FramingInfo->Multilink.Length =
FramingInfo->Compression.Length =
FramingInfo->ProtocolID.Length = 0;
if (LinkFraming & PPP_FRAMING) {
if (!(LinkFraming & PPP_COMPRESS_ADDRESS_CONTROL)) {
//
// If there is no address/control compression
// we need a pointer and a length
//
FramingInfo->AddressControl.Pointer = CurrentPointer;
*CurrentPointer++ = 0xFF;
*CurrentPointer++ = 0x03;
FramingInfo->AddressControl.Length = 2;
FramingInfo->HeaderLength += FramingInfo->AddressControl.Length;
}
if (!(Flags & IO_PROTOCOLID)) {
//
// If this is not from our private I/O interface we will
// build the rest of the header.
//
if ((Flags & DO_MULTILINK) && (LinkFraming & PPP_MULTILINK_FRAMING)) {
//
// We are doing multilink so we need a pointer
// and a length
//
FramingInfo->Multilink.Pointer = CurrentPointer;
if (!(LinkFraming & PPP_COMPRESS_PROTOCOL_FIELD)) {
//
// No protocol compression
//
*CurrentPointer++ = 0x00;
FramingInfo->Multilink.Length++;
}
*CurrentPointer++ = 0x3D;
FramingInfo->Multilink.Length++;
if (!(LinkFraming & PPP_SHORT_SEQUENCE_HDR_FORMAT)) {
//
// We are using long sequence number
//
FramingInfo->Multilink.Length += 2;
CurrentPointer += 2;
}
FramingInfo->Multilink.Length += 2;
CurrentPointer += 2;
FramingInfo->HeaderLength += FramingInfo->Multilink.Length;
}
if (Flags & (DO_COMPRESSION | DO_ENCRYPTION)) {
//
// We are doing compression/encryption so we need
// a pointer and a length
//
FramingInfo->Compression.Pointer = CurrentPointer;
//
// It appears that legacy ras (< NT 4.0) requires that
// the PPP protocol field in a compressed packet not
// be compressed, ie has to have the leading 0x00
//
if (!(LinkFraming & PPP_COMPRESS_PROTOCOL_FIELD)) {
//
// No protocol compression
//
*CurrentPointer++ = 0x00;
FramingInfo->Compression.Length++;
}
*CurrentPointer++ = 0xFD;
FramingInfo->Compression.Length++;
//
// Add coherency bytes
//
FramingInfo->Compression.Length += 2;
CurrentPointer += 2;
FramingInfo->HeaderLength += FramingInfo->Compression.Length;
}
if (Flags & FIRST_FRAGMENT) {
FramingInfo->ProtocolID.Pointer = CurrentPointer;
if (!(LinkFraming & PPP_COMPRESS_PROTOCOL_FIELD) ||
(Flags & (DO_COMPRESSION | DO_ENCRYPTION))) {
FramingInfo->ProtocolID.Length++;
CurrentPointer++;
}
FramingInfo->ProtocolID.Length++;
FramingInfo->HeaderLength += FramingInfo->ProtocolID.Length;
}
}
} else if ((LinkFraming & RAS_FRAMING)) {
//
// If this is old ras framing:
//
// Alter the framing so that 0xFF 0x03 is not added
// and that the first byte is 0xFD not 0x00 0xFD
//
// So basically, a RAS compression looks like
// <0xFD> <2 BYTE COHERENCY> <NBF DATA FIELD>
//
// Whereas uncompressed looks like
// <NBF DATA FIELD> which always starts with 0xF0
//
// If this is ppp framing:
//
// A compressed frame will look like (before address/control
// - multilink is added)
// <0x00> <0xFD> <2 Byte Coherency> <Compressed Data>
//
if (Flags & (DO_COMPRESSION | DO_ENCRYPTION)) {
FramingInfo->Compression.Pointer = CurrentPointer;
*CurrentPointer++ = 0xFD;
FramingInfo->Compression.Length++;
//
// Coherency bytes
//
FramingInfo->Compression.Length += 2;
CurrentPointer += 2;
FramingInfo->HeaderLength += FramingInfo->Compression.Length;
}
}
}
VOID
NdisWanCopyFromPacketToBuffer(
IN PNDIS_PACKET pNdisPacket,
IN ULONG Offset,
IN ULONG BytesToCopy,
OUT PUCHAR Buffer,
OUT PULONG BytesCopied
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
ULONG NdisBufferCount;
PNDIS_BUFFER CurrentBuffer;
PVOID VirtualAddress;
ULONG CurrentLength, AmountToMove;
ULONG LocalBytesCopied = 0;
*BytesCopied = 0;
//
// Take care of zero byte copy
//
if (!BytesToCopy) {
return;
}
//
// Get the buffer count
//
NdisQueryPacket(pNdisPacket,
NULL,
&NdisBufferCount,
&CurrentBuffer,
NULL);
//
// Could be a null packet
//
if (!NdisBufferCount) {
return;
}
NdisQueryBuffer(CurrentBuffer,
&VirtualAddress,
&CurrentLength);
while (LocalBytesCopied < BytesToCopy) {
//
// No more bytes left in this buffer
//
if (!CurrentLength) {
//
// Get the next buffer
//
NdisGetNextBuffer(CurrentBuffer,
&CurrentBuffer);
//
// End of the packet, copy what we can
//
if (CurrentBuffer == NULL) {
break;
}
//
//
//
NdisQueryBuffer(CurrentBuffer,
&VirtualAddress,
&CurrentLength);
continue;
}
//
// Get to the point where we can start copying
//
if (Offset) {
if (Offset > CurrentLength) {
//
// Not in this buffer, go to the next one
//
Offset -= CurrentLength;
CurrentLength = 0;
continue;
} else {
//
// At least some in this buffer
//
VirtualAddress = (PUCHAR)VirtualAddress + Offset;
CurrentLength -= Offset;
Offset = 0;
}
}
//
// We can copy some data. If we need more data than is available
// in this buffer we can copy what we need and go back for more.
//
AmountToMove = (CurrentLength > (BytesToCopy - LocalBytesCopied)) ?
(BytesToCopy - LocalBytesCopied) : CurrentLength;
NdisMoveMemory(Buffer, VirtualAddress, AmountToMove);
Buffer = (PUCHAR)Buffer + AmountToMove;
VirtualAddress = (PUCHAR)VirtualAddress + AmountToMove;
LocalBytesCopied += AmountToMove;
CurrentLength -= AmountToMove;
}
*BytesCopied = LocalBytesCopied;
}
PNDIS_WAN_PACKET
GetWanPacketFromLink(
PLINKCB LinkCB
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
PNDIS_WAN_PACKET WanPacket;
ULONG PrevCount = LinkCB->ulWanPacketCount;
ASSERT(LinkCB->ulWanPacketCount);
//
// If the current count is greater than threshold and the
// new count falls below we need to decrement the sending
// link count.
//
if ((--LinkCB->ulWanPacketCount < 2) && (PrevCount > 1)) {
((PBUNDLECB)LinkCB->BundleCB)->SendingLinks--;
}
WanPacket = (PNDIS_WAN_PACKET)RemoveHeadList(&LinkCB->WanPacketPool);
return (WanPacket);
}
VOID
ReturnWanPacketToLink(
PLINKCB LinkCB,
PNDIS_WAN_PACKET WanPacket
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
ULONG PrevCount = LinkCB->ulWanPacketCount;
//
// If the current count is below the threshold and the
// new count puts us over we need to increment the sending
// link count.
//
if ((++LinkCB->ulWanPacketCount > 1) && (PrevCount < 2)) {
((PBUNDLECB)LinkCB->BundleCB)->SendingLinks++;
}
InsertTailList(&LinkCB->WanPacketPool, &WanPacket->WanPacketQueue);
}
VOID
DestroyIoPacket(
PNDIS_PACKET NdisPacket
)
/*++
Routine Name:
Routine Description:
Arguments:
Return Values:
--*/
{
PWAN_IO_PROTOCOL_RESERVED ProtocolReserved =
(PWAN_IO_PROTOCOL_RESERVED)NdisPacket->ProtocolReserved;
NDIS_HANDLE PacketPool = ProtocolReserved->hPacketPool;
NdisWanFreeMemory(ProtocolReserved->pAllocatedMemory);
NdisFreeBuffer(ProtocolReserved->pNdisBuffer);
NdisFreeBufferPool(ProtocolReserved->hBufferPool);
NdisFreePacket(NdisPacket);
NdisFreePacketPool(PacketPool);
}
#if DBG
VOID
InsertDbgPacket(
PDBG_SEND_CONTEXT DbgContext
)
{
PDBG_SEND_PACKET DbgPacket;
PBUNDLECB BundleCB = DbgContext->BundleCB;
PPROTOCOLCB ProtocolCB = DbgContext->ProtocolCB;
PLINKCB LinkCB = DbgContext->LinkCB;
NdisWanAllocateMemory(&DbgPacket, sizeof(DBG_SEND_PACKET));
if (DbgPacket == NULL) {
return;
}
DbgPacket->Packet = DbgContext->Packet;
DbgPacket->PacketType = DbgContext->PacketType;
DbgPacket->BundleCB = BundleCB;
if (BundleCB) {
DbgPacket->BundleState = BundleCB->State;
DbgPacket->BundleFlags = BundleCB->Flags;
}
DbgPacket->ProtocolCB = ProtocolCB;
if (ProtocolCB) {
DbgPacket->ProtocolFlags = ProtocolCB->Flags;
}
DbgPacket->LinkCB = LinkCB;
if (LinkCB) {
DbgPacket->LinkState = LinkCB->State;
}
NdisAcquireSpinLock(DbgContext->ListLock);
InsertTailList(DbgContext->ListHead, &DbgPacket->Linkage);
NdisReleaseSpinLock(DbgContext->ListLock);
}
BOOLEAN
RemoveDbgPacket(
PDBG_SEND_CONTEXT DbgContext
)
{
PDBG_SEND_PACKET DbgPacket = NULL;
BOOLEAN Found = FALSE;
NdisAcquireSpinLock(DbgContext->ListLock);
if (!IsListEmpty(DbgContext->ListHead)) {
for (DbgPacket = (PDBG_SEND_PACKET)DbgContext->ListHead->Flink;
(PVOID)DbgPacket != (PVOID)DbgContext->ListHead;
DbgPacket = (PDBG_SEND_PACKET)DbgPacket->Linkage.Flink) {
if (DbgPacket->Packet == DbgContext->Packet) {
RemoveEntryList(&DbgPacket->Linkage);
NdisWanFreeMemory(DbgPacket);
Found = TRUE;
break;
}
}
}
ASSERT(Found == TRUE);
NdisReleaseSpinLock(DbgContext->ListLock);
return (Found);
}
#endif