diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/ndis/ndiswan/send.c | 3080 |
1 files changed, 3080 insertions, 0 deletions
diff --git a/private/ntos/ndis/ndiswan/send.c b/private/ntos/ndis/ndiswan/send.c new file mode 100644 index 000000000..1cf740be1 --- /dev/null +++ b/private/ntos/ndis/ndiswan/send.c @@ -0,0 +1,3080 @@ +/*++ + +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 |