|
|
/*++
Copyright (c) 1996-1999 Microsoft Corporation
Module Name:
send.c
Abstract:
routines for sending packets
Author:
Charlie Wickham (charlwi) 07-May-1996 Yoram Bernet (yoramb) Rajesh Sundaram (rajeshsu) 01-Aug-1998.
Environment:
Kernel Mode
Revision History:
--*/
#include "psched.h"
#pragma hdrstop
/* External */
/* Static */
/* Forwad */
#define SEND_PACKET_VIA_SCHEDULER(_pktcontext, _vc, _adapter, _ourpacket) \
{ \ PsAssert((_pktcontext)->Vc != 0); \ (_vc)->Stats.PacketsScheduled++; \ (_vc)->Stats.BytesScheduled.QuadPart += (_pktcontext)->Info.PacketLength; \ if(!(*(_vc)->PsComponent->SubmitPacket)( \ (_vc)->PsPipeContext, \ (_vc)->PsFlowContext, \ (_pktcontext)->Info.ClassMapContext, \ &(_pktcontext)->Info)) { \ \ DropPacket((_adapter), (_vc), (_ourpacket), NDIS_STATUS_FAILURE); \ } \ return NDIS_STATUS_PENDING; \ }
#define FILL_PKT_FOR_NIC(OPacket, UserC) \
{ \ NDIS_PACKET_8021Q_INFO VlanPriInfo; \ \ VlanPriInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET(OPacket, Ieee8021QInfo);\ VlanPriInfo.TagHeader.UserPriority = (UserC); \ NDIS_PER_PACKET_INFO_FROM_PACKET(OPacket, Ieee8021QInfo) = VlanPriInfo.Value;\ }
#define FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OPacket, TOSNC, UserC, UserNC, \
_IPHdr) \ { \ ULONG _PacketLength; \ FILL_PKT_FOR_NIC(OPacket, UserC); \ NdisQueryPacket((OPacket), NULL, NULL, NULL, &(_PacketLength)); \ (PktContext)->Info.PacketLength = (_PacketLength) - (Adapter)->HeaderSize; \ (PktContext)->Info.ConformanceTime.QuadPart = 0; \ (PktContext)->Info.ClassMapContext = 0; \ (PktContext)->Info.UserPriorityNonConforming = (UserNC); \ (PktContext)->Info.TOSNonConforming = (TOSNC); \ (PktContext)->Info.IPHdr = (_IPHdr); \ (PktContext)->Info.IPHeaderOffset = (Adapter)->IPHeaderOffset; \ (PktContext)->Vc = (Vc); \ }
#define SEND_PACKET_OVER_NIC(Adapter, Packet, UserC, Status) \
{ \ PPS_SEND_PACKET_CONTEXT _PktContext; \ PNDIS_PACKET _OurPacket; \ if((Status = PsDupPacketNoContext(Adapter, Packet, &_OurPacket, &_PktContext)) == NDIS_STATUS_SUCCESS) \ { \ FILL_PKT_FOR_NIC(_OurPacket, UserC); \ NdisSend(&Status, Adapter->LowerMpHandle, _OurPacket); \ if(Status != NDIS_STATUS_PENDING) { \ if(_PktContext) { \ PsAssert((_PktContext)->Vc == 0); \ NdisIMCopySendCompletePerPacketInfo(_PktContext->OriginalPacket, _OurPacket); \ NdisFreePacket(_OurPacket); \ } \ } \ } \ return Status; \ }
NDIS_STATUS PsAllocateAndCopyPacket( PADAPTER Adapter, PNDIS_PACKET Packet, PPNDIS_PACKET OurPacket, PPS_SEND_PACKET_CONTEXT *PktContext) { PNDIS_PACKET_OOB_DATA OurOOBData; PNDIS_PACKET_OOB_DATA XportOOBData; PMEDIA_SPECIFIC_INFORMATION OurMediaArea; PVOID MediaSpecificInfo = NULL; UINT MediaSpecificInfoSize = 0; NDIS_STATUS Status;
//
// At this point, we know that there are no packet stacks remaining in the packet.
// we proceed to allocate an NDIS packet using NdisAllocatePacket. Note that here
// we do not have to allocate our per-packet area, since NdisAllocatePacket already
// did this for us.
//
if(!Adapter->SendPacketPool) { PS_LOCK(&Adapter->Lock);
if(!Adapter->SendPacketPool) { NDIS_HANDLE PoolHandle = (void *) NDIS_PACKET_POOL_TAG_FOR_PSCHED;
NdisAllocatePacketPoolEx(&Status, &PoolHandle, MIN_PACKET_POOL_SIZE, MAX_PACKET_POOL_SIZE, Adapter->PacketContextLength);
if(Status != NDIS_STATUS_SUCCESS) { Adapter->Stats.OutOfPackets ++; PS_UNLOCK(&Adapter->Lock);
return Status; }
//
// We successfully allocated a packet pool. We can now free the Fixed Size Block pool for the packet-stack API
//
Adapter->SendPacketPool = PoolHandle; } PS_UNLOCK(&Adapter->Lock);
}
NdisAllocatePacket(&Status, OurPacket, Adapter->SendPacketPool); if(Status != NDIS_STATUS_SUCCESS) { //
// mark as out of resources. Ndis will resubmit.
//
Adapter->Stats.OutOfPackets ++; return(NDIS_STATUS_RESOURCES); } #if DBG
PsAssert((*OurPacket)->Private.Head == NULL);
if(Packet->Private.TotalLength){ PsAssert(Packet->Private.Head); } #endif // DBG
//
// chain the buffers from the upper layer packet to the newly allocated packet.
//
(*OurPacket)->Private.Head = Packet->Private.Head; (*OurPacket)->Private.Tail = Packet->Private.Tail; //
// Copy the Packet Flags from the Packet to OldPacket. Since we handle loopback in the
// QueryInformation handlers, we don't set the NDIS_FLAGS_DONT_LOOPBACK
//
NdisGetPacketFlags(*OurPacket) = NdisGetPacketFlags(Packet); //
// Copy the OOB Offset from the original packet to the new packet.
//
XportOOBData = NDIS_OOB_DATA_FROM_PACKET(Packet); OurOOBData = NDIS_OOB_DATA_FROM_PACKET(*OurPacket); NdisMoveMemory(OurOOBData, XportOOBData, sizeof(NDIS_PACKET_OOB_DATA)); //
// Copy the per packet info into the new packet
//
NdisIMCopySendPerPacketInfo(*OurPacket, Packet); //
// Copy the Media specific information
//
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet, &MediaSpecificInfo, &MediaSpecificInfoSize); if(MediaSpecificInfo || MediaSpecificInfoSize){ NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(*OurPacket, MediaSpecificInfo, MediaSpecificInfoSize); } //
// Remember the original packet so that we can complete it properly.
//
*PktContext = PS_SEND_PACKET_CONTEXT_FROM_PACKET(*OurPacket); (*PktContext)->OriginalPacket = Packet; (*PktContext)->Vc = 0; (*PktContext)->Info.NdisPacket = *OurPacket;
return Status; } NDIS_STATUS PsDupPacketNoContext( PADAPTER Adapter, PNDIS_PACKET Packet, PPNDIS_PACKET OurPacket, PPS_SEND_PACKET_CONTEXT *PktContext) { NDIS_STATUS Status = NDIS_STATUS_SUCCESS; BOOLEAN Remaining; PNDIS_PACKET_STACK PacketStack;
//
// NDIS provides 2 ways for IMs to indicate packets. If the IM can allocate a packet stack, it should use it as
// it is the optimal approach. In this case, we do not have to do any per-packet copying since we don't allocate
// a new packet.
//
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
if(Remaining != 0) { //
// The packet stack has space only for 2 DWORDs. Since we are using more than 2, we need to allocate our own
// memory for the per-packet block. Note that we *DONT* do this when we use the NdisAllocatePacket APIs, because
// we initialized the packet pool to already include the space for the per-packet region.
//
*OurPacket = Packet; *PktContext = 0; PacketStack->IMReserved[0] = 0;
} else { Status = PsAllocateAndCopyPacket(Adapter, Packet, OurPacket, PktContext); }
return Status; }
NDIS_STATUS PsDupPacketContext( PADAPTER Adapter, PNDIS_PACKET Packet, PPNDIS_PACKET OurPacket, PPS_SEND_PACKET_CONTEXT *PktContext) { NDIS_STATUS Status; BOOLEAN Remaining; PNDIS_PACKET_STACK PacketStack;
//
// NDIS provides 2 ways for IMs to indicate packets. If the IM can allocate a packet stack, it should use it as
// it is the optimal approach. In this case, we do not have to do any per-packet copying since we don't allocate
// a new packet.
//
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
if(Remaining != 0) { //
// The packet stack has space only for 2 DWORDs. Since we are using more than 2, we need to allocate our own
// memory for the per-packet block. Note that we *DONT* do this when we use the NdisAllocatePacket APIs, because
// we initialized the packet pool to already include the space for the per-packet region.
//
*OurPacket = Packet;
*PktContext = (PPS_SEND_PACKET_CONTEXT) (ULONG_PTR)NdisAllocateFromBlockPool(Adapter->SendBlockPool); PacketStack->IMReserved[0] = (ULONG_PTR)*PktContext;
if(!*PktContext) { Adapter->Stats.OutOfPackets ++; return NDIS_STATUS_RESOURCES; } else { (*PktContext)->Info.NdisPacket = Packet; (*PktContext)->OriginalPacket = 0; return NDIS_STATUS_SUCCESS; } } else { Status = PsAllocateAndCopyPacket(Adapter, Packet, OurPacket, PktContext); }
return Status; }
//
// Tries to classify this packet based on the port numbers. If not found, will add it to one of the flows (in Round
// Robin fashion) and returns a pointer to that Vc
//
PGPC_CLIENT_VC GetVcForPacket( PPS_WAN_LINK WanLink, USHORT SrcPort, USHORT DstPort) { PGPC_CLIENT_VC pVc, pVc1; int i, j;
for( j = 0; j < BEVC_LIST_LEN; j++) { pVc = &WanLink->BeVcList[j];
// Let's look at the 2 VCs we have now:
for( i = 0; i < PORT_LIST_LEN; i++) { if( (pVc->SrcPort[i] == SrcPort) && (pVc->DstPort[i] == DstPort)) return pVc; } }
// Did not find in any of the VCs. Need to choose the Next VC for insertion and insert these valuse..
pVc = &WanLink->BeVcList[WanLink->NextVc]; WanLink->NextVc = ((WanLink->NextVc + 1) % BEVC_LIST_LEN);
pVc->SrcPort[pVc->NextSlot] = SrcPort; pVc->DstPort[pVc->NextSlot] = DstPort; pVc->NextSlot = ((pVc->NextSlot + 1)% PORT_LIST_LEN ); return pVc; }
//
// This routine returns the Src and Dst Port numbers
BOOLEAN GetPortNos( IN PNDIS_PACKET Packet , IN ULONG TransportHeaderOffset, IN OUT PUSHORT pSrcPort, IN OUT PUSHORT pDstPort ) { PNDIS_BUFFER ArpBuf , IpBuf , TcpBuf, UdpBuf, DataBuf; ULONG ArpLen , IpLen , IpHdrLen , TcpLen , UdpLen, DataLen , TotalLen , TcpHeaderOffset; VOID *ArpH; IPHeader UNALIGNED *IPH; TCPHeader UNALIGNED *TCPH; UDPHeader UNALIGNED *UDPH;
IPAddr Src, Dst; BOOLEAN bFragment; USHORT SrcPort , DstPort , IPID, FragOffset ,Size; PVOID GeneralVA , Data; ULONG i, Ret;
IpBuf = NULL;
// Steps
// Parse the IP Packet.
// Look for the appropriate ports.
// Look for the data portion and put in the Time & length there.
if(1) { PVOID pAddr; PNDIS_BUFFER pNdisBuf1, pNdisBuf2; UINT Len;
NdisGetFirstBufferFromPacket( Packet, &ArpBuf, &ArpH, &ArpLen, &TotalLen );
pNdisBuf1 = Packet->Private.Head; NdisQueryBuffer(pNdisBuf1, &pAddr, &Len);
while(Len <= TransportHeaderOffset) {
TransportHeaderOffset -= Len; NdisGetNextBuffer(pNdisBuf1, &pNdisBuf2); NdisQueryBuffer(pNdisBuf2, &pAddr, &Len); pNdisBuf1 = pNdisBuf2; }
/* Buffer Descriptor corresponding to Ip Packet */ IpBuf = pNdisBuf1;
/* Length of this Buffer (IP buffer) */ IpLen = Len - TransportHeaderOffset;
/* Starting Virtual Address for this buffer */ GeneralVA = pAddr; /* Virtual Address of the IP Header */ IPH = (IPHeader *)(((PUCHAR)pAddr) + TransportHeaderOffset); }
if(!IpBuf) return FALSE;
IpHdrLen = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2); FragOffset = IPH->iph_offset & IP_OFFSET_MASK; FragOffset = net_short(FragOffset) * 8;
bFragment = (IPH->iph_offset & IP_MF_FLAG) || (FragOffset > 0);
// Don't want to deal with Fragmented packets right now..//
if ( bFragment ) return FALSE;
switch (IPH->iph_protocol) { case IPPROTO_TCP :
if ((USHORT)IpLen > IpHdrLen) { // We have more than the IP Header in this MDL //
TCPH = (TCPHeader *) ((PUCHAR)IPH + IpHdrLen); TcpLen = IpLen - IpHdrLen; TcpBuf = IpBuf;
} else { return FALSE; }
/* At this point, TcpBuf, TCPH and TcpLen contain the proper values */
// Get the port numbers out.
SrcPort = net_short(TCPH->tcp_src); DstPort = net_short(TCPH->tcp_dest);
*pSrcPort = SrcPort; *pDstPort = DstPort;
// If the packet is here, it means: The link on which it is being sent is <= MAX_LINK_SPEED_FOR_DRR.
// So, it is OK to adjust the Window size if we are on an ICS box.
// Note that, we only do this on WAN adapters, which do not have the checksum offload
// capability. So, we are fine with changing the checksum.
if(gEnableWindowAdjustment) { USHORT _old, _new; ULONG _sum;
_old = (TCPH)->tcp_window; _new = 1460*6;
if( net_short( _old) < _new) return TRUE;
_new = net_short( _new ); (TCPH)->tcp_window = _new; _sum = ((~(TCPH)->tcp_xsum) & 0xffff) + ((~_old) & 0xffff) + _new; _sum = (_sum & 0xffff) + (_sum >> 16); _sum += (_sum >> 16); (TCPH)->tcp_xsum = (ushort) ((~_sum) & 0xffff); } return TRUE;
case IPPROTO_UDP: if (IpLen > IpHdrLen) { // We have more than the IP Header in this MDL //
UDPH = (UDPHeader *) ((PUCHAR)IPH + IpHdrLen); UdpLen = IpLen - IpHdrLen; UdpBuf = IpBuf; } else { return FALSE; }
/* At this point, UdpBuf, UDPH and UdpLen contain the proper values */
SrcPort = net_short(UDPH->uh_src); DstPort = net_short(UDPH->uh_dest);
*pSrcPort = SrcPort; *pDstPort = DstPort;
return TRUE;
default: ; }
return FALSE; }
//
// This where we get called for each Send
//
NTSTATUS MpSend( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET TheirPacket, IN UINT Flags )
/*++
Routine Description:
Received a xmit request from a legacy transport.
Arguments:
See the DDK...
Return Values:
None
--*/
{ PADAPTER Adapter = (PADAPTER)MiniportAdapterContext; NDIS_STATUS Status; PNDIS_PACKET OurPacket; PPS_SEND_PACKET_CONTEXT PktContext; PGPC_CLIENT_VC BeVc, Vc = NULL; PETH_HEADER pAddr; PNDIS_BUFFER pNdisBuf1; UINT Len; PUSHORT id; PPS_WAN_LINK WanLink;
PsStructAssert(Adapter);
//
// If the device is shutting down, we cannot accept any more sends.
//
if(IsDeviceStateOn(Adapter) == FALSE) { return NDIS_STATUS_FAILURE; }
if(Adapter->MediaType == NdisMediumWan) { if(Adapter->ProtocolType == ARP_ETYPE_IP) { //
// We should not be getting non-ip packets in the NDISWAN-IP binding.
//
PsAssert(NDIS_GET_PACKET_PROTOCOL_TYPE(TheirPacket) == NDIS_PROTOCOL_ID_TCP_IP);
pNdisBuf1 = TheirPacket->Private.Head;
NdisQueryBuffer(pNdisBuf1, &pAddr, &Len);
if(Len < sizeof(ETH_HEADER)) { //
// Packet is too small. we have to fail this bogus packet.
//
return NDIS_STATUS_FAILURE; }
//
// Get to the wanlink using the remote address from the packet.
//
id = (PUSHORT) &pAddr->DestAddr[0];
PS_LOCK(&Adapter->Lock);
WanLink = (PPS_WAN_LINK)(g_WanLinkTable[*id]);
if(WanLink == 0) { //
// We received a packet for a wanlink that has already gone down.
//
PS_UNLOCK(&Adapter->Lock);
return NDIS_STATUS_FAILURE; }
if(WanLink->State != WanStateOpen) { //
// We received a packet for a wanlink that has already gone down.
//
PS_UNLOCK(&Adapter->Lock);
return NDIS_STATUS_FAILURE; }
//
// When we get a StatusIndication for a new WAN link, NDISWAN puts context in the remote address
// When psched intercepts the LineUp, it overwrites NDISWAN's context with its own context. Psched
// uses this context to get to the WanLink from the packet. (see above)
//
// But, when it passes the packet down to NDISWAN, it needs to plumb NDISWAN's context into the packet,
// so that NDISWAN can see the context that it sent to us, as opposed to the context that we sent up to
// wanarp.
//
NdisMoveMemory(pAddr, &WanLink->SendHeader, FIELD_OFFSET(ETH_HEADER, Type));
//
// We optimize psched to bypass the scheduling components when there are no flows. There are a set of
// scheduling components per WanLink, so to be truly optimal, we need to check the FLowCount on a specific
// WanLink.
//
if( (WanLink->LinkSpeed > MAX_LINK_SPEED_FOR_DRR) && (!WanLink->CfInfosInstalled) ) { // Bypass scheduling components, since there are no flows created on this
// wanlink. Note that the UserPriority is never used over wanlinks, so we can set it to 0.
PS_UNLOCK(&Adapter->Lock);
SEND_PACKET_OVER_NIC(Adapter, TheirPacket, 0, Status); } //
// Now, we are going to do either (1) DiffServ Or (2) IntServ. If the packet does not belong to either
// of these categories, we will just hash it into one of the BeVcs we have and do simple DRR.
//
else { //
// There is at least one flow. we need to classify this packet. Since the flow is going
// via the scheduling components, we have to allocate memory for the per-packet info
// (if the packet-stack APIs are used) or a new packet descriptor, which will include the
// per-packet info (if the old NDIS APIs are used) The packet that has been passed to us is
// 'TheirPacket'. If the packet-stack APIs are used, then TheirPacket == OurPacket
// if the non packet-stack APIs are used, then OurPacket == Newly Allocated Packet.
//
// In both cases, the code after this point will just use 'OurPacket' and the right thing will happen.
//
if((Status = PsDupPacketContext(Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS) { PS_UNLOCK(&Adapter->Lock); return Status; }
if (1) { USHORT SrcPort=0, DstPort=0; //
// We are in RSVP mode, and we need to go to the GPC to classify the packet.
// We already have a pointer to our WanLink. But, the wanlink could go away
// when we release the lock and try to classify the packet. So, we take
// a ref on the BestEffortVc for the WanLink.
//
if( (WanLink->LinkSpeed <= MAX_LINK_SPEED_FOR_DRR) && (GetPortNos( TheirPacket, Adapter->IPHeaderOffset, &SrcPort, &DstPort))) BeVc = GetVcForPacket( WanLink, SrcPort, DstPort); else BeVc = &WanLink->BestEffortVc; InterlockedIncrement(&BeVc->RefCount);
PS_UNLOCK(&Adapter->Lock);
if( WanLink->CfInfosInstalled ) Vc = GetVcByClassifyingPacket(Adapter, &WanLink->InterfaceID, OurPacket);
if(!Vc) { Vc = BeVc; } else { DerefClVc(BeVc); }
FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, Vc->IPPrecedenceNonConforming, Vc->UserPriorityConforming, Vc->UserPriorityNonConforming, NULL); }
//
// There is at least one flow - We need to send this packet via the scheduling
// components.
//
if((Vc->ClVcState == CL_CALL_COMPLETE) || (Vc->ClVcState == CL_MODIFY_PENDING) || (Vc->ClVcState == CL_INTERNAL_CALL_COMPLETE)) { SEND_PACKET_VIA_SCHEDULER(PktContext, Vc, Adapter, OurPacket); } else { //
// Deref the ref that was added by the GPC.
//
DerefClVc(Vc); PsDbgSend(DBG_FAILURE, DBG_SEND, MP_SEND, NOT_READY, Adapter, Vc, TheirPacket, OurPacket); if(PktContext->OriginalPacket) { NdisFreePacket(OurPacket); } else { NdisFreeToBlockPool((PUCHAR)PktContext); } return(NDIS_STATUS_FAILURE); } } } //
// Forget about it. It's a Non-IP packet
//
else { //
// For non IP adapters, we just send over the NIC. Note that we don't create a best effort
// Vc for such adapters. The only thing that we lose here is the ability to mark 802.1p on
// such packets (we don't have a Vc, so we cannot supply a UserPriority value to the below
// macro. But that is okay, since 802.1p is meaningful only in non LAN adapters.
//
SEND_PACKET_OVER_NIC(Adapter, TheirPacket, 0, Status); } } else { //
// We have received a send at our non WAN binding.
//
if(!Adapter->CfInfosInstalled && Adapter->BestEffortLimit == UNSPECIFIED_RATE ) { // There is no point in trying to classify if there are no flows installed
Vc = &Adapter->BestEffortVc; PsAssert(Vc->ClVcState == CL_CALL_COMPLETE);
//
// Bypass scheduling components.
//
SEND_PACKET_OVER_NIC(Adapter, TheirPacket, Vc->UserPriorityConforming, Status); } else { //
// There is at least one flow, or we are in LimitedBestEffort mode. Let's try to classify the Vc.
// In this case, the packet will have to go via the scheduling components.
//
//
// Since the flow is going via the scheduling components, we have to allocate the per-packet info.
// (if the new NDIS APIs are used) or a new packet descriptor, which will include the per-packet info
// (if the old NDIS APIs are used)
//
if(1) { // We are in RSVP mode. Let's classify with the GPC.
Vc = GetVcByClassifyingPacket(Adapter, &Adapter->InterfaceID, TheirPacket);
if( !Vc) { if( Adapter->MaxOutstandingSends == 0xffffffff) { Vc = &Adapter->BestEffortVc; PsAssert(Vc->ClVcState == CL_CALL_COMPLETE);
//
// Bypass scheduling components.
//
SEND_PACKET_OVER_NIC(Adapter, TheirPacket, Vc->UserPriorityConforming, Status); }
// We will be doing DRR on this adapter; so send pkt on BeVc
Vc = &Adapter->BestEffortVc;
InterlockedIncrement(&Vc->RefCount); }
if((Status = PsDupPacketContext(Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS) { return Status; }
FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, Vc->IPPrecedenceNonConforming, Vc->UserPriorityConforming, Vc->UserPriorityNonConforming, NULL);
}
if((Vc->ClVcState == CL_CALL_COMPLETE) || (Vc->ClVcState == CL_MODIFY_PENDING) || (Vc->ClVcState == CL_INTERNAL_CALL_COMPLETE)) { SEND_PACKET_VIA_SCHEDULER(PktContext, Vc, Adapter, OurPacket); } else { //
// Deref the ref that was added by the GPC.
//
DerefClVc(Vc); PsDbgSend(DBG_FAILURE, DBG_SEND, MP_SEND, NOT_READY, Adapter, Vc, TheirPacket, OurPacket); if(PktContext->OriginalPacket) { NdisFreePacket(OurPacket); } else { NdisFreeToBlockPool((PUCHAR)PktContext); } return(NDIS_STATUS_FAILURE); } } } }
VOID ClSendComplete( IN NDIS_HANDLE ProtocolBindingContext, IN PNDIS_PACKET Packet, IN NDIS_STATUS Status )
/*++
Routine Description:
Completion routine for NdisSendPackets. Does most of the work for cleaning up after a send.
If necessary, call the PSA's send packet complete function
Arguments:
See the DDK...
Return Values:
None
--*/
{ PGPC_CLIENT_VC Vc; PADAPTER Adapter = (PADAPTER)ProtocolBindingContext; PPS_SEND_PACKET_CONTEXT PktContext; PNDIS_PACKET XportPacket; HANDLE PoolHandle;
//
// Determine if the packet we are completing is the one we allocated. If so, get
// the original packet from the reserved area and free the allocated packet. If this
// is the packet that was sent down to us then just complete the packet.
//
PoolHandle = NdisGetPoolFromPacket(Packet);
if(PoolHandle != Adapter->SendPacketPool) { PNDIS_PACKET_STACK PacketStack; BOOLEAN Remaining;
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
PsAssert(Remaining != 0);
PktContext = (PPS_SEND_PACKET_CONTEXT) PacketStack->IMReserved[0];
if(PktContext != 0) { //
// This packet went via the scheduling components.
//
PsAssert(PktContext->Vc); Vc = PktContext->Vc; PsDbgSend(DBG_INFO, DBG_SEND, CL_SEND_COMPLETE, ENTER, Adapter, Vc, Packet, 0); PsAssert(Vc->Adapter == Adapter); if(Vc->SendComplete) (*Vc->SendComplete)(Vc->SendCompletePipeContext, Packet); DerefClVc(Vc); NdisFreeToBlockPool((PUCHAR)PktContext); }
NdisMSendComplete(Adapter->PsNdisHandle, Packet, Status); } else { //
// get the pointer to the upper layer's packet. Reinit the packet struct and
// push it back on the adapter's packet SList. Remove the reference incurred
// when the packet was handled by MpSend
//
PktContext = PS_SEND_PACKET_CONTEXT_FROM_PACKET(Packet);
//
// Call the scheduler if necessary
//
if(PktContext->Vc) { //
// Some packets never went through the scheduler.
//
Vc = PktContext->Vc; PsDbgSend(DBG_INFO, DBG_SEND, CL_SEND_COMPLETE, ENTER, Adapter, Vc, Packet, 0); PsAssert(Vc->Adapter == Adapter); if(Vc->SendComplete) { (*Vc->SendComplete)(Vc->SendCompletePipeContext, Packet); } //
// We have taken a ref on the VCs when we sent the packets
// through the scheduling components. Now is the time to
// Deref them
//
DerefClVc(Vc); } else { PsDbgSend(DBG_INFO, DBG_SEND, CL_SEND_COMPLETE, ENTER, Adapter, 0, Packet, 0); } XportPacket = PktContext->OriginalPacket; NdisIMCopySendCompletePerPacketInfo(XportPacket, Packet); NdisFreePacket(Packet); NdisMSendComplete(Adapter->PsNdisHandle, XportPacket, Status); } } // ClSendComplete
VOID DropPacket( IN HANDLE PipeContext, IN HANDLE FlowContext, IN PNDIS_PACKET Packet, IN NDIS_STATUS Status )
/*++
Routine Description:
Drop a packet after it was queued by the scheduler.
Arguments:
PipeContext - Pipe context (adapter) FlowContext - Flow context (adapter VC) Packet - Packet to drop Status - Return code to return to NDIS Return Values:
None
--*/
{ PGPC_CLIENT_VC Vc = (PGPC_CLIENT_VC)FlowContext; PADAPTER Adapter = (PADAPTER)PipeContext; PPS_SEND_PACKET_CONTEXT PktContext; PNDIS_PACKET XportPacket; HANDLE PoolHandle;
//
// Determine if the packet we are completing is the one we allocated. If so, get
// the original packet from the reserved area and free the allocated packet. If this
// is the packet that was sent down to us then just complete the packet.
//
PoolHandle = NdisGetPoolFromPacket(Packet);
if(PoolHandle != Adapter->SendPacketPool) { PNDIS_PACKET_STACK PacketStack; BOOLEAN Remaining;
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
PsAssert(Remaining != 0);
PktContext = (PPS_SEND_PACKET_CONTEXT) PacketStack->IMReserved[0];
PsAssert(PktContext != 0); PsAssert(Vc == PktContext->Vc); PsAssert(Adapter == Vc->Adapter); NdisFreeToBlockPool((PUCHAR)PktContext);
NdisMSendComplete(Adapter->PsNdisHandle, Packet, Status);
} else { PktContext = PS_SEND_PACKET_CONTEXT_FROM_PACKET(Packet);
PsAssert(PktContext != 0); PsAssert(Vc == PktContext->Vc); PsAssert(Adapter == Vc->Adapter);
XportPacket = PktContext->OriginalPacket;
NdisFreePacket(Packet);
NdisMSendComplete(Adapter->PsNdisHandle, XportPacket, Status); }
Vc->Stats.DroppedPackets ++;
PsDbgSend(DBG_INFO, DBG_SEND, DROP_PACKET, ENTER, Adapter, Vc, Packet, 0);
DerefClVc(Vc);
} // DropPacket
char* ReturnByteAtOffset( PNDIS_PACKET pNdisPacket, ULONG Offset) { PVOID VA; PNDIS_BUFFER pNdisBuf1, pNdisBuf2; UINT Len;
pNdisBuf1 = pNdisPacket->Private.Head; NdisQueryBuffer(pNdisBuf1, &VA, &Len);
while(Len <= Offset) { Offset -= Len; NdisGetNextBuffer(pNdisBuf1, &pNdisBuf2); NdisQueryBuffer(pNdisBuf2, &VA, &Len); pNdisBuf1 = pNdisBuf2; }
return (char*)(((char*)VA) + Offset); }
PGPC_CLIENT_VC FASTCALL GetVcByClassifyingPacket( PADAPTER Adapter, PTC_INTERFACE_ID pInterfaceID, PNDIS_PACKET OurPacket ) /*+++
---*/ { CLASSIFICATION_HANDLE ClassificationHandle; PGPC_CLIENT_VC Vc = NULL; NDIS_STATUS Status; ULONG ProtocolType;
//
// Let's act based on the ClassificationType we read from the registry key.
// This is specific to debug version only.
// case 0: (default): Use preclassification information, classify otherwise
// case 1: Use prelcassification information ONLY
// case 2: Use classification information ONLY
//
ClassificationHandle = (CLASSIFICATION_HANDLE) PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(OurPacket, ClassificationHandlePacketInfo));
#if DBG
if (ClassificationType == 2) { ClassificationHandle = 0; } #endif
if (ClassificationHandle) {
PsAssert(GpcEntries.GpcGetCfInfoClientContextHandler); Vc = GpcEntries.GpcGetCfInfoClientContextWithRefHandler(GpcQosClientHandle, ClassificationHandle, FIELD_OFFSET(GPC_CLIENT_VC, RefCount));
//
// If we got a Vc that was not destined for this adapter, we have to reject it.
//
if(Vc) { if(Vc->Adapter != Adapter) { DerefClVc(Vc); } else return Vc; } }
#if DBG
if (ClassificationType == 1) { return NULL; } #endif
//
// Let's classify this packet since we did not get a Classification ID or a proper Vc.
//
PsAssert(GpcEntries.GpcClassifyPacketHandler);
switch(NDIS_GET_PACKET_PROTOCOL_TYPE(OurPacket)) { case NDIS_PROTOCOL_ID_TCP_IP: ProtocolType = GPC_PROTOCOL_TEMPLATE_IP; break; case NDIS_PROTOCOL_ID_IPX: ProtocolType = GPC_PROTOCOL_TEMPLATE_IPX; break; default: ProtocolType = GPC_PROTOCOL_TEMPLATE_NOT_SPECIFIED; break; }
//
// If the adapter type is 802.5 (Token Ring), then the MAC header can be of variable size.
// The format of the MAC header is as follows:
// +---------------------+-------------+----------+-----------
// | 2 + 6 (DA) + 6 (SA) | Optional RI | 8 (SNAP) | IP
// +---------------------+-------------+----------+-----------
// Optional RI is present if and only if RI bit as part of SA is set.
// When RI is present, its length is give by the lower 5 bits of the 15th byte.
// 1. Get the VA for the 9th and the 15th bytes.
// 2. If RI if not present, Offset = 14 + 6.
// 3. If present, Offset = 14 + 6 + RI-Size.
if(Adapter->MediaType == NdisMedium802_5) { PNDIS_BUFFER pTempNdisBuffer; PUCHAR pHeaderBuffer; ULONG BufferLength; ULONG TotalLength; ULONG IpOffset;
NdisGetFirstBufferFromPacket( OurPacket, &pTempNdisBuffer, &pHeaderBuffer, &BufferLength, &TotalLength);
ASSERT( BufferLength >= 15);
if( (*(ReturnByteAtOffset(OurPacket, 8)) & 0x80) == 0) IpOffset = 14 + 8; else IpOffset = 14 + 8 + (*(ReturnByteAtOffset(OurPacket, 14)) & 0x1f);
Status = GpcEntries.GpcClassifyPacketHandler( GpcQosClientHandle, ProtocolType, OurPacket, IpOffset, pInterfaceID, (PGPC_CLIENT_HANDLE)&Vc, &ClassificationHandle);
} else { PNDIS_BUFFER pTempNdisBuffer; PUCHAR pHeaderBuffer; ULONG BufferLength; ULONG TotalLength; ULONG IpOffset; PVOID VA; PNDIS_BUFFER pNdisBuf1, pNdisBuf2; UINT Len; ENetHeader UNALIGNED * EHdr;
pNdisBuf1 = OurPacket->Private.Head; NdisQueryBuffer(pNdisBuf1, &VA, &Len);
EHdr = (ENetHeader UNALIGNED *)VA;
if (EHdr == NULL) return NULL;
// We don't want to do any fancy parsing other than this. If the frame
// is not of standard ethernet type, bail out.
if ((Adapter->MediaType == NdisMedium802_3) && (net_short(EHdr->eh_type) >= MIN_ETYPE)) { ULONG FrameOffset;
// The conditional is basically to cover up a bug in wandrv.sys, which gives
// bogus frame header sizes. We look at the IP Header offset supplied
// by the protocol above for IP packets only, as we are saving only
// those for the time being.
if ((NDIS_GET_PACKET_PROTOCOL_TYPE(OurPacket) == NDIS_PROTOCOL_ID_TCP_IP) && (Adapter->IPHeaderOffset)) { FrameOffset = Adapter->IPHeaderOffset; } else { FrameOffset = Adapter->HeaderSize; }
Status = GpcEntries.GpcClassifyPacketHandler( GpcQosClientHandle, ProtocolType, OurPacket, FrameOffset, pInterfaceID, (PGPC_CLIENT_HANDLE)&Vc, &ClassificationHandle); } else { return NULL; }
}
if(Status == GPC_STATUS_SUCCESS) { //
// If we have succeeded, we must get a Classification Handle
//
PsAssert(ClassificationHandle != 0);
//
// The Classification succeeded. If we found a ClassificationHandle
// then we must write it in the packet so that anyone below us can use
// it. The very fact that we are here indicates that we did not start
// with a Classification handle or we got a bad one. So, we need not
// worry about over writing the classification handle in the packet.
//
NDIS_PER_PACKET_INFO_FROM_PACKET(OurPacket, ClassificationHandlePacketInfo) = UlongToPtr(ClassificationHandle); Vc = GpcEntries.GpcGetCfInfoClientContextWithRefHandler( GpcQosClientHandle, ClassificationHandle, FIELD_OFFSET(GPC_CLIENT_VC, RefCount)); }
if(Vc && Vc->Adapter != Adapter) { //
// We have used the GPC APIs that return a Vc with a ref. We have to
// deref here, because we got a wrong Vc for this adapter.
//
DerefClVc(Vc);
return NULL; }
return Vc; }
VOID ClCoSendComplete( IN NDIS_STATUS Status, IN NDIS_HANDLE ProtocolVcContext, IN PNDIS_PACKET Packet ) { PGPC_CLIENT_VC Vc = (PGPC_CLIENT_VC) ProtocolVcContext;
ClSendComplete(Vc->Adapter, Packet, Status); } // ClCoSendComplete
/* end send.c */
|