/*++ 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 (IPH && ((USHORT)IpLen > IpHdrLen)) { // We have more than the IP Header in this MDL // TCPH = (TCPHeader *) ((PUCHAR)IPH + IpHdrLen); TcpLen = IpLen - IpHdrLen; TcpBuf = IpBuf; } else { // TCP Header is in the next MDL // NdisGetNextBuffer(IpBuf, &TcpBuf); if(!TcpBuf) return FALSE; GeneralVA = NULL; NdisQueryBuffer(TcpBuf, &GeneralVA, &TcpLen ); TCPH = (TCPHeader *) GeneralVA; } /* 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. 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 { // UDP Header is in the next MDL // NdisGetNextBuffer(IpBuf, &UdpBuf); if(!UdpBuf) return FALSE; GeneralVA = NULL; NdisQueryBuffer(UdpBuf, &GeneralVA, &UdpLen ); UDPH = (UDPHeader *) GeneralVA; } /* 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; } // Case 1. DiffServMode // if(WanLink->AdapterMode == AdapterModeDiffservFlow) { // // If we are in Diffserv mode, we classify the packet based on the DSCP in the IP header. // // Is there atleast ONE DiffServ Flow? if(Adapter->IPHeaderOffset && WanLink->pDiffServMapping) { IPHeader *pIpHdr; UCHAR tos; pIpHdr = GetIpHeader(Adapter->IPHeaderOffset, OurPacket); tos = pIpHdr->iph_tos >> 2; if((Vc = WanLink->pDiffServMapping[tos].Vc)) { // // We found a VC for this packet. // PsAssert(Vc->Adapter == Adapter); PsAssert(Vc->WanLink == WanLink); InterlockedIncrement(&Vc->RefCount); SET_TOS_XSUM(OurPacket, pIpHdr, (WanLink->pDiffServMapping[tos].ConformingOutboundDSField)); FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, (WanLink->pDiffServMapping[tos].NonConformingOutboundDSField), WanLink->pDiffServMapping[tos].ConformingUserPriority, WanLink->pDiffServMapping[tos].NonConformingUserPriority, pIpHdr); } // Could not classify packets to ANY of the DiffServ Flows. So, let's just do DRR across the BeVcs. else { // // There are Diffserv flows installed, but not for this DSCP. Lets use the BEVCS. // USHORT SrcPort, DstPort; if( (WanLink->LinkSpeed <= MAX_LINK_SPEED_FOR_DRR) && (GetPortNos( TheirPacket, Adapter->IPHeaderOffset, &SrcPort, &DstPort))) Vc = GetVcForPacket( WanLink, SrcPort, DstPort); else Vc = &WanLink->BestEffortVc; InterlockedIncrement(&Vc->RefCount); FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, Vc->IPPrecedenceNonConforming, Vc->UserPriorityConforming, Vc->UserPriorityNonConforming, pIpHdr); } } // No, there is not One, let's just do DRR across the BeVcs. else { // // We are in Diffserv mode, but no Diffserv flows are created as yet. Let's use the BEVCS. // USHORT SrcPort, DstPort; if( (WanLink->LinkSpeed <= MAX_LINK_SPEED_FOR_DRR) && (GetPortNos( TheirPacket, Adapter->IPHeaderOffset, &SrcPort, &DstPort))) Vc = GetVcForPacket( WanLink, SrcPort, DstPort); else Vc = &WanLink->BestEffortVc; InterlockedIncrement(&Vc->RefCount); FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, Vc->IPPrecedenceNonConforming, Vc->UserPriorityConforming, Vc->UserPriorityNonConforming, NULL); } PS_UNLOCK(&Adapter->Lock); } // Case 2. IntServMode else { 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)) { 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 && TsCount == 0 ) { // 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(Adapter->AdapterMode == AdapterModeDiffservFlow) { // // We are in the diffserv mode. We don't have to use the GPC to classify packets. // For all IP packets, classify on the TOS byte. // PS_LOCK(&Adapter->Lock); if(NDIS_GET_PACKET_PROTOCOL_TYPE(TheirPacket) == NDIS_PROTOCOL_ID_TCP_IP && Adapter->IPHeaderOffset && Adapter->pDiffServMapping) { UCHAR tos; IPHeader *pIpHdr = 0; pIpHdr = GetIpHeader(Adapter->IPHeaderOffset, TheirPacket); tos = pIpHdr->iph_tos >> 2; if((Vc = Adapter->pDiffServMapping[tos].Vc)) { PsAssert(Vc->Adapter == Adapter); if((Status = PsDupPacketContext(Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS) { PS_UNLOCK(&Adapter->Lock); return Status; } InterlockedIncrement(&Vc->RefCount); SET_TOS_XSUM(OurPacket, pIpHdr, (Adapter->pDiffServMapping[tos].ConformingOutboundDSField)); FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, (Adapter->pDiffServMapping[tos].NonConformingOutboundDSField), Adapter->pDiffServMapping[tos].ConformingUserPriority, Adapter->pDiffServMapping[tos].NonConformingUserPriority, pIpHdr); } else { if( ( Adapter->MaxOutstandingSends == 0xffffffff) && ( TsCount == 0)) { Vc = &Adapter->BestEffortVc; PsAssert(Vc->ClVcState == CL_CALL_COMPLETE); // // Bypass scheduling components. // PS_UNLOCK(&Adapter->Lock); SEND_PACKET_OVER_NIC(Adapter, TheirPacket, Vc->UserPriorityConforming, Status); } Vc = &Adapter->BestEffortVc; if((Status = PsDupPacketContext(Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS) { PS_UNLOCK(&Adapter->Lock); return Status; } InterlockedIncrement(&Vc->RefCount); FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, Vc->IPPrecedenceNonConforming, Vc->UserPriorityConforming, Vc->UserPriorityNonConforming, NULL); } } else { // // We are in Diffserv mode, but there are no DiffServ Vcs, or it's not an IP // packet. Send over the Adapter's BE Vc. // Vc = &Adapter->BestEffortVc; if((Status = PsDupPacketContext( Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS) { PS_UNLOCK(&Adapter->Lock); return Status; } InterlockedIncrement(&Vc->RefCount); FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OurPacket, Vc->IPPrecedenceNonConforming, Vc->UserPriorityConforming, Vc->UserPriorityNonConforming, NULL); } PS_UNLOCK(&Adapter->Lock); } else { // We are in RSVP mode. Let's classify with the GPC. Vc = GetVcByClassifyingPacket(Adapter, &Adapter->InterfaceID, TheirPacket); if( !Vc) { if( (Adapter->MaxOutstandingSends == 0xffffffff) && (TsCount == 0)) { 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; #if CBQ PCLASS_MAP_CONTEXT_BLK pClBlk = NULL; #endif NDIS_STATUS Status; ULONG ProtocolType; // // We are in RSVP mode - Use the GPC to classify the packet. If the GPC wants to return a Vc, it will // return with a ref. // ClassificationHandle = (CLASSIFICATION_HANDLE) PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(OurPacket, ClassificationHandlePacketInfo)); if (ClassificationHandle) { PsAssert(GpcEntries.GpcGetCfInfoClientContextHandler); Vc = GpcEntries.GpcGetCfInfoClientContextWithRefHandler(GpcQosClientHandle, ClassificationHandle, FIELD_OFFSET(GPC_CLIENT_VC, RefCount)); #if CBQ pClBlk = NULL; Status = GpcEntries.GpcGetCfInfoClientContextHandler(GpcClassMapClientHandle, ClassificationHandle, &pClBlk); if(pClBlk) { PktContext->Info.ClassMapContext = pClBlk->ComponentContext; } #endif // // 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; } } // // 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 { Status = GpcEntries.GpcClassifyPacketHandler( GpcQosClientHandle, ProtocolType, OurPacket, // This 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. ((NDIS_GET_PACKET_PROTOCOL_TYPE(OurPacket) == NDIS_PROTOCOL_ID_TCP_IP) && (Adapter->IPHeaderOffset)) ? (Adapter->IPHeaderOffset) : Adapter->HeaderSize, pInterfaceID, (PGPC_CLIENT_HANDLE)&Vc, &ClassificationHandle); } 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 CBQ // // Get the CBQ class map context & store it in the // packet. No point doing this if the first classification // failed. // pClBlk = NULL; Status = GpcEntries.GpcGetCfInfoClientContextHandler(GpcClassMapClientHandle, ClassificationHandle, &pClBlk); if(pClBlk) { PktContext->Info.ClassMapContext = pClBlk->ComponentContext; } #endif } 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 */