windows-nt/Source/XPSP1/NT/net/rras/ndis/rasl2tp/send.c
2020-09-26 16:20:57 +08:00

3102 lines
81 KiB
C

// Copyright (c) 1997, Microsoft Corporation, all rights reserved
//
// send.c
// RAS L2TP WAN mini-port/call-manager driver
// Send routines
//
// 01/07/97 Steve Cobb
#include "l2tpp.h"
#ifdef PSDEBUG
// List of all allocated PAYLOADSENT contexts and the lock that protects the
// list. (for debug purposes only)
//
NDIS_SPIN_LOCK g_lockDebugPs;
LIST_ENTRY g_listDebugPs;
#endif
// Debug counts of client oddities that should not be happening.
//
ULONG g_ulSendZlbWithoutHostRoute = 0;
// Callback to add AVPs to an outgoing control message. 'PTunnel' is the
// tunnel control block. 'PVc' is the VC control block for call control
// messages or NULL for tunnel control messages. 'ulArg1', 'ulArg2', and
// 'pvArg3' are caller's arguments as passed for SendControl. 'PAvpBuffer' is
// the address of the buffer to receive the built AVPs. '*PulAvpLength' is
// set to the length of the built AVPs.
//
typedef
VOID
(*PBUILDAVPS)(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
//-----------------------------------------------------------------------------
// Local prototypes (alphabetically)
//-----------------------------------------------------------------------------
USHORT
BuildAvpAch(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN CHAR* pszValue,
IN USHORT usValueLength,
OUT CHAR* pAvp );
USHORT
BuildAvpAul(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN UNALIGNED ULONG* pulValue,
IN USHORT usValues,
OUT CHAR* pAvp );
USHORT
BuildAvpFlag(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
OUT CHAR* pAvp );
USHORT
BuildAvpUl(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN ULONG ulValue,
OUT CHAR* pAvp );
USHORT
BuildAvpUs(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN USHORT usValue,
OUT CHAR* pAvp );
USHORT
BuildAvp2UsAndAch(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN USHORT usValue1,
IN USHORT usValue2,
IN CHAR* pszValue,
IN USHORT usValueLength,
OUT CHAR* pAvp );
VOID
BuildCdnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildHelloAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildIccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildIcrpAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildIcrqAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
ULONG
BuildL2tpHeader(
IN OUT CHAR* pBuffer,
IN BOOLEAN fControl,
IN BOOLEAN fReset,
IN USHORT* pusTunnelId,
IN USHORT* pusCallId,
IN USHORT* pusNs,
IN USHORT usNr );
VOID
BuildOccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildOcrpAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildOcrqAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildScccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildSccrpAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildSccrqAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildStopccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
BuildWenAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength );
VOID
CompletePayloadSent(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs );
VOID
SendControlComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer );
VOID
SendHeaderComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer );
VOID
SendPayloadReset(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs );
VOID
SendPayloadSeq(
TUNNELWORK* pWork,
TUNNELCB* pTunnel,
VCCB* pVc,
ULONG_PTR* punpArgs );
VOID
SendPayloadSeqComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer );
VOID
SendPayloadUnseq(
TUNNELWORK* pWork,
TUNNELCB* pTunnel,
VCCB* pVc,
ULONG_PTR* punpArgs );
VOID
SendPayloadUnseqComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer );
VOID
SendPayloadTimerEvent(
IN TIMERQITEM* pItem,
IN VOID* pContext,
IN TIMERQEVENT event );
VOID
SendZlb(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN USHORT usNs,
IN USHORT usNr,
IN BOOLEAN fReset );
VOID
UpdateControlHeaderNr(
IN CHAR* pBuffer,
IN USHORT usNr );
VOID
UpdateHeaderLength(
IN CHAR* pBuffer,
IN USHORT usLength );
//-----------------------------------------------------------------------------
// Send routines
//-----------------------------------------------------------------------------
VOID
SendControl(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN USHORT usMsgType,
IN ULONG ulBuildAvpsArg1,
IN ULONG ulBuildAvpsArg2,
IN PVOID pvBuildAvpsArg3,
IN ULONG ulFlags )
// Build and send a control message. 'PTunnel' is the tunnel control
// block, always non-NULL. 'PVc' is the VC control block, non-NULL for
// call connection (as opposed to tunnel connection) messages.
// 'UsMsgType' is the message type AVP value of the message to build.
// 'UlBuildAvpsArgX' are the arguments passed to the PBUILDAVP handler
// associated with 'usMsgType', where the meaning depends on the specific
// handler. 'UlFlags' is the CSF_* flag options associated with the sent
// message context, or 0 if none.
//
// IMPORTANT: Caller must hold 'pTunnel->lockT'. If 'pVc' is non-NULL
// caller must also hold 'pVc->lockV'.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
NDIS_BUFFER* pNdisBuffer;
PBUILDAVPS pBuildAvpsHandler;
TIMERQITEM* pTqiSendTimeout;
CONTROLSENT* pCs;
USHORT usAssignedCallId;
ULONG ulLength;
ULONG ulAvpLength;
CHAR* pBuffer;
static PBUILDAVPS apBuildAvpHandlers[ 16 ] =
{
BuildSccrqAvps, // CMT_SCCRQ
BuildSccrpAvps, // CMT_SCCRP
BuildScccnAvps, // CMT_SCCCN
BuildStopccnAvps, // CMT_StopCCN
NULL, // CMT_StopCCRP (obsolete)
BuildHelloAvps, // CMT_Hello
BuildOcrqAvps, // CMT_OCRQ
BuildOcrpAvps, // CMT_OCRP
BuildOccnAvps, // CMT_OCCN
BuildIcrqAvps, // CMT_ICRQ
BuildIcrpAvps, // CMT_ICRP
BuildIccnAvps, // CMT_ICCN
NULL, // CMT_CCRQ (obsolete)
BuildCdnAvps, // CMT_CDN
BuildWenAvps, // CMT_WEN
NULL // CMT_SLI
};
TRACE( TL_V, TM_Send, ( "SendControl" ) );
pAdapter = pTunnel->pAdapter;
pBuffer = NULL;
pTqiSendTimeout = NULL;
pCs = NULL;
do
{
// Get an NDIS_BUFFER to hold the control message.
//
pBuffer = GetBufferFromPool( &pAdapter->poolFrameBuffers );
if (!pBuffer)
{
ASSERT( !"GetBfP?" );
status = NDIS_STATUS_RESOURCES;
break;
}
// Get an "unacknowledged send timeout" timer event descriptor.
//
pTqiSendTimeout = ALLOC_TIMERQITEM( pAdapter );
if (!pTqiSendTimeout)
{
ASSERT( !"Alloc TQI?" );
status = NDIS_STATUS_RESOURCES;
break;
}
// Get a "control message sent" context.
//
pCs = ALLOC_CONTROLSENT( pAdapter );
if (!pCs)
{
ASSERT( !"Alloc PS?" );
status = NDIS_STATUS_RESOURCES;
break;
}
status = NDIS_STATUS_SUCCESS;
}
while (FALSE);
if (status != NDIS_STATUS_SUCCESS)
{
if (pBuffer)
{
FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE );
}
if (pTqiSendTimeout)
{
FREE_TIMERQITEM( pAdapter, pTqiSendTimeout );
}
// System is probably toast but try to be orderly.
//
ScheduleTunnelWork(
pTunnel, NULL, FsmCloseTunnel,
(ULONG_PTR )TRESULT_GeneralWithError,
(ULONG_PTR )GERR_NoResources,
0, 0, FALSE, FALSE );
return;
}
// Build an L2TP control header in 'pBuffer'. The Call-ID is 0 for tunnel
// control messages, or peer's assigned call ID for call control messages.
//
usAssignedCallId = (pVc) ? pVc->usAssignedCallId : 0;
ulLength =
BuildL2tpHeader(
pBuffer,
TRUE,
FALSE,
&pTunnel->usAssignedTunnelId,
&usAssignedCallId,
&pTunnel->usNs,
pTunnel->usNr );
// Call the message type's "build AVPs" handler to add AVPs to the buffer
// following the header.
//
ASSERT( usMsgType > 0 && usMsgType <= 16 );
pBuildAvpsHandler = apBuildAvpHandlers[ usMsgType - 1 ];
pBuildAvpsHandler(
pTunnel, pVc,
ulBuildAvpsArg1, ulBuildAvpsArg2, pvBuildAvpsArg3,
pBuffer + ulLength, &ulAvpLength );
ulLength += ulAvpLength;
UpdateHeaderLength( pBuffer, (USHORT )ulLength );
// Pare down the frame buffer to the actual length used.
//
pNdisBuffer = NdisBufferFromBuffer( pBuffer );
NdisAdjustBufferLength( pNdisBuffer, (UINT )ulLength );
// Set up the "control message sent" context with the information needed
// to send the message and track it's progress through retransmissions.
//
pCs->lRef = 0;
pCs->usNs = pTunnel->usNs;
pCs->usMsgType = usMsgType;
TimerQInitializeItem( pTqiSendTimeout );
pCs->pTqiSendTimeout = pTqiSendTimeout;
pCs->ulRetransmits = 0;
pCs->pBuffer = pBuffer;
pCs->ulBufferLength = ulLength;
pCs->pTunnel = pTunnel;
pCs->pVc = pVc;
pCs->ulFlags = ulFlags | CSF_Pending;
pCs->pIrp = NULL;
// Bump the 'Next Send' counter since this message has been assigned the
// current value.
//
++pTunnel->usNs;
// Take a reference that is removed when the context is removed from the
// "outstanding send" list. Take a VC and tunnel reference that is
// removed when the context is freed.
//
ReferenceControlSent( pCs );
ReferenceTunnel( pTunnel, FALSE );
if (pCs->pVc)
{
ReferenceVc( pCs->pVc );
}
// Queue the context as "active" with transmission pending in 'Next Sent'
// sort order, i.e. at the tail.
//
InsertTailList( &pTunnel->listSendsOut, &pCs->linkSendsOut );
// See if the send window allows it to go now.
//
ScheduleTunnelWork(
pTunnel, NULL, SendPending,
0, 0, 0, 0, FALSE, FALSE );
}
VOID
SendPending(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to try to send pending messages from the
// "outstanding send" list until the send window is full.
//
// This routine is called only at PASSIVE IRQL.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
LIST_ENTRY* pLink;
CONTROLSENT* pCs;
ULONG ulFlags;
TRACE( TL_N, TM_Send, ( "SendPending(sout=%d,sw=%d)",
pTunnel->ulSendsOut, pTunnel->ulSendWindow ) );
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
FREE_TUNNELWORK( pAdapter, pWork );
NdisAcquireSpinLock( &pTunnel->lockT );
{
for (;;)
{
if (pTunnel->ulSendsOut >= pTunnel->ulSendWindow)
{
// The send window is closed.
//
break;
}
// Scan the "outstanding send" queue for the next send context
// pending transmission. Can't save our place for the next
// iteration because the lock must be released and re-acquired
// below to send the packet.
//
for (pLink = pTunnel->listSendsOut.Flink;
pLink != &pTunnel->listSendsOut;
pLink = pLink->Flink)
{
pCs = CONTAINING_RECORD( pLink, CONTROLSENT, linkSendsOut );
if (pCs->ulFlags & CSF_Pending)
{
break;
}
}
if (pLink == &pTunnel->listSendsOut)
{
// There is nothing pending.
//
break;
}
// The send window is open and a pending send has been found.
// Mark the context "not pending" and close the window by one to
// account for the coming send.
//
ulFlags = pCs->ulFlags;
pCs->ulFlags &= ~(CSF_Pending | CSF_QueryMediaSpeed);
++pTunnel->ulSendsOut;
// Cancel any pending delayed acknowledge timeout, because the
// acknowledge will piggyback on this packet.
//
if (pTunnel->pTqiDelayedAck)
{
TimerQCancelItem( pTunnel->pTimerQ, pTunnel->pTqiDelayedAck );
pTunnel->pTqiDelayedAck = NULL;
}
if (pCs->ulRetransmits == 0)
{
LARGE_INTEGER lrgTime;
// This is the original send so note the time sent.
//
NdisGetCurrentSystemTime( &lrgTime );
pCs->llTimeSent = lrgTime.QuadPart;
}
else
{
// In the retransmission, the 'Next Send' is the same as the
// original, but the 'Next Receive' field is updated.
//
UpdateControlHeaderNr( pCs->pBuffer, pTunnel->usNr );
}
// Take a reference that will be removed in the send completion
// routine.
//
ReferenceControlSent( pCs );
TRACE( TL_A, TM_CMsg, ( "%sSEND(%d) %s, +sout=%d, to=%d",
((g_ulTraceLevel <= TL_I) ? "" : "\nL2TP: "),
pCs->ulRetransmits,
MsgTypePszFromUs( pCs->usMsgType ),
pTunnel->ulSendsOut,
pTunnel->ulSendTimeoutMs ) );
DUMPW( TL_A, TM_MDmp, pCs->pBuffer, pCs->ulBufferLength );
NdisReleaseSpinLock( &pTunnel->lockT );
// query media speed if necessary
if(ulFlags & CSF_QueryMediaSpeed)
{
TdixGetInterfaceInfo(&pAdapter->tdix,
pTunnel->myaddress.ulIpAddress,
&pTunnel->ulMediaSpeed);
}
{
FILE_OBJECT* FileObj;
PTDIX_SEND_HANDLER SendFunc;
// Call TDI to send the control message.
//
if (ReadFlags(&pTunnel->ulFlags) & TCBF_SendConnected) {
ASSERT(pTunnel->pRoute != NULL);
FileObj =
CtrlObjFromUdpContext(&pTunnel->udpContext);
SendFunc = TdixSend;
} else {
FileObj = NULL;
SendFunc = TdixSendDatagram;
}
status = SendFunc(&pAdapter->tdix,
FileObj,
SendControlComplete,
pCs,
NULL,
&pTunnel->address,
pCs->pBuffer,
pCs->ulBufferLength,
&pCs->pIrp );
ASSERT( status == NDIS_STATUS_PENDING );
}
NdisAcquireSpinLock( &pTunnel->lockT );
}
}
NdisReleaseSpinLock( &pTunnel->lockT );
}
VOID
SendPayload(
IN VCCB* pVc,
IN NDIS_PACKET* pPacket )
// Sends payload packet 'pPacket' on VC 'pVc' eventually calling
// NdisMCoSendComplete with the result.
//
// IMPORTANT: Caller must not hold any locks.
//
{
NDIS_STATUS status;
TUNNELCB* pTunnel;
ADAPTERCB* pAdapter;
CHAR* pBuffer;
TRACE( TL_V, TM_Send, ( "SendPayload" ) );
pAdapter = pVc->pAdapter;
pTunnel = pVc->pTunnel;
status = NDIS_STATUS_SUCCESS;
if (pTunnel)
{
if (ReadFlags( &pTunnel->ulFlags ) & TCBF_HostRouteAdded)
{
// Take a reference on the call. For unsequenced sends, this is
// released when the TdixSendDatagram completes. For sequenced
// sends, it is released when the PAYLOADSENT context is freed.
//
if (ReferenceCall( pVc ))
{
// Get an NDIS_BUFFER to hold the L2TP header that will be
// tacked onto the front of NDISWAN's PPP-framed data packet.
//
pBuffer = GetBufferFromPool( &pAdapter->poolHeaderBuffers );
if (!pBuffer)
{
ASSERT( !"GetBfP?" );
DereferenceCall( pVc );
status = NDIS_STATUS_RESOURCES;
}
}
else
{
TRACE( TL_A, TM_Send, ( "Send on inactive $%p", pVc ) );
status = NDIS_STATUS_FAILURE;
}
}
else
{
TRACE( TL_A, TM_Send, ( "SendPayload w/o host route?" ) );
status = NDIS_STATUS_FAILURE;
}
}
else
{
TRACE( TL_A, TM_Send, ( "Send $%p w/o pT?", pVc ) );
status = NDIS_STATUS_FAILURE;
}
if (status != NDIS_STATUS_SUCCESS)
{
NDIS_SET_PACKET_STATUS( pPacket, status );
TRACE( TL_A, TM_Send, ( "NdisMCoSendComp($%x)", status ) );
NdisMCoSendComplete( status, pVc->NdisVcHandle, pPacket );
TRACE( TL_N, TM_Send, ( "NdisMCoSendComp done" ) );
return;
}
if (ReadFlags( &pVc->ulFlags ) & VCBF_Sequencing)
{
ScheduleTunnelWork(
pTunnel, pVc, SendPayloadSeq,
(ULONG_PTR )pPacket, (ULONG_PTR )pBuffer, 0, 0, FALSE, FALSE );
}
else
{
ScheduleTunnelWork(
pTunnel, pVc, SendPayloadUnseq,
(ULONG_PTR )pPacket, (ULONG_PTR )pBuffer, 0, 0, FALSE, FALSE );
}
}
VOID
SendPayloadSeq(
TUNNELWORK* pWork,
TUNNELCB* pTunnel,
VCCB* pVc,
ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to handle sending a sequenced payload packet on a
// VC. Arg0 is the packet to send. Arg1 is the header buffer to fill in.
//
// This routine is called only at PASSIVE IRQL.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
PAYLOADSENT* pPs;
TIMERQITEM* pTqiSendTimeout;
LARGE_INTEGER lrgTime;
ULONG ulLength;
ULONG ulFullLength;
NDIS_PACKET* pPacket;
CHAR* pBuffer;
NDIS_BUFFER* pNdisBuffer;
USHORT usNs;
TRACE( TL_V, TM_Send, ( "SendPayloadSeq" ) );
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
pPacket = (NDIS_PACKET* )(punpArgs[ 0 ]);
pBuffer = (CHAR* )(punpArgs[ 1 ]);
FREE_TUNNELWORK( pAdapter, pWork );
pTqiSendTimeout = NULL;
pPs = NULL;
do
{
// Get an "unacknowledged send timeout" timer event descriptor.
//
pTqiSendTimeout = ALLOC_TIMERQITEM( pAdapter );
if (!pTqiSendTimeout)
{
ASSERT( !"Alloc TQI?" );
status = NDIS_STATUS_RESOURCES;
break;
}
// Get a "payload message sent" context.
//
pPs = ALLOC_PAYLOADSENT( pAdapter );
if (!pPs)
{
ASSERT( !"Alloc PS?" );
status = NDIS_STATUS_RESOURCES;
break;
}
NdisAcquireSpinLock( &pVc->lockV );
{
// Retrieve the 'Next Send' value to assign this packet, then
// bump the counter for the next guy.
//
usNs = pVc->usNs;
++pVc->usNs;
// Build an L2TP payload header with Ns/Nr fields in
// 'pBuffer'.
//
ulLength =
BuildL2tpHeader(
pBuffer,
FALSE,
FALSE,
&pTunnel->usAssignedTunnelId,
&pVc->usAssignedCallId,
&usNs,
pVc->usNr );
// Pare down the header buffer to the actual length used then
// chain it onto the PPP-framed data we got from NDISWAN.
//
pNdisBuffer = NdisBufferFromBuffer( pBuffer );
NdisAdjustBufferLength( pNdisBuffer, (UINT )ulLength );
NdisChainBufferAtFront( pPacket, pNdisBuffer );
NdisQueryPacket( pPacket, NULL, NULL, NULL, &ulFullLength );
UpdateHeaderLength( pBuffer, (USHORT )ulFullLength );
// Cancel any pending delayed acknowledge timeout, because the
// acknowledge will piggyback on this packet.
//
if (pVc->pTqiDelayedAck)
{
TimerQCancelItem( pTunnel->pTimerQ, pVc->pTqiDelayedAck );
pVc->pTqiDelayedAck = NULL;
}
// Fill the "payload message sent" context with the information
// needed to track the progress of the payload's acknowledgement.
//
pPs->usNs = usNs;
pPs->lRef = 0;
TimerQInitializeItem( pTqiSendTimeout );
pPs->pTqiSendTimeout = pTqiSendTimeout;
pPs->pPacket = pPacket;
pPs->pBuffer = pBuffer;
ReferenceTunnel( pTunnel, FALSE );
pPs->pTunnel = pTunnel;
ReferenceVc( pVc );
pPs->pVc = pVc;
pPs->status = NDIS_STATUS_FAILURE;
NdisGetCurrentSystemTime( &lrgTime );
pPs->llTimeSent = lrgTime.QuadPart;
pPs->pIrp = NULL;
// Link the payload in the "outstanding" list and take a reference
// on the context corresponding to this linkage. Take a second
// reference that will be removed by the send completion handler.
// Take a third that will be removed by the timer event handler.
//
ReferencePayloadSent( pPs );
InsertTailList( &pVc->listSendsOut, &pPs->linkSendsOut );
ReferencePayloadSent( pPs );
ReferencePayloadSent( pPs );
#ifdef PSDEBUG
{
extern LIST_ENTRY g_listDebugPs;
extern NDIS_SPIN_LOCK g_lockDebugPs;
NdisAcquireSpinLock( &g_lockDebugPs );
{
InsertTailList( &g_listDebugPs, &pPs->linkDebugPs );
}
NdisReleaseSpinLock( &g_lockDebugPs );
}
#endif
TimerQScheduleItem(
pTunnel->pTimerQ,
pPs->pTqiSendTimeout,
pVc->ulSendTimeoutMs,
SendPayloadTimerEvent,
pPs );
TRACE( TL_A, TM_Msg,
( "%sSEND payload, len=%d Ns=%d Nr=%d to=%d",
((g_ulTraceLevel <= TL_I) ? "" : "\nL2TP: "),
ulFullLength, pPs->usNs, pVc->usNr, pVc->ulSendTimeoutMs ) );
DUMPW( TL_A, TM_MDmp, pPs->pBuffer, ulLength );
++pVc->stats.ulSentDataPacketsSeq;
pVc->stats.ulDataBytesSent += (ulFullLength - ulLength);
pVc->stats.ulSendWindowTotal += pVc->ulSendWindow;
}
NdisReleaseSpinLock( &pVc->lockV );
status = NDIS_STATUS_SUCCESS;
}
while (FALSE);
if (status != NDIS_STATUS_SUCCESS)
{
FreeBufferToPool( &pAdapter->poolHeaderBuffers, pBuffer, TRUE );
if (pTqiSendTimeout)
{
FREE_TIMERQITEM( pAdapter, pTqiSendTimeout );
}
ASSERT( !pPs );
// Complete the send, indicating the failure.
//
NDIS_SET_PACKET_STATUS( pPacket, status );
TRACE( TL_A, TM_Send, ( "NdisMCoSendComp($%x)", status ) );
NdisMCoSendComplete( status, pVc->NdisVcHandle, pPacket );
TRACE( TL_N, TM_Send, ( "NdisMCoSendComp done" ) );
return;
}
// Call TDI to send the payload message.
//
{
FILE_OBJECT* FileObj;
PTDIX_SEND_HANDLER SendFunc;
if (ReadFlags(&pTunnel->ulFlags) & TCBF_SendConnected) {
ASSERT(pTunnel->pRoute != NULL);
FileObj = PayloadObjFromUdpContext(&pTunnel->udpContext);
SendFunc = TdixSend;
} else {
FileObj = NULL;
SendFunc = TdixSendDatagram;
}
status = SendFunc(&pAdapter->tdix,
FileObj,
SendPayloadSeqComplete,
pPs,
NULL,
&pTunnel->address,
pBuffer,
ulFullLength,
&pPs->pIrp );
}
ASSERT( status == NDIS_STATUS_PENDING );
}
VOID
SendPayloadUnseq(
TUNNELWORK* pWork,
TUNNELCB* pTunnel,
VCCB* pVc,
ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to handle sending an unsequenced payload packet
// on a VC. Arg0 is the NDIS_PACKET. Arg1 is the header buffer to fill
// in.
//
// This routine is called only at PASSIVE IRQL.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
ULONG ulLength;
UINT unFullLength;
NDIS_PACKET* pPacket;
CHAR* pBuffer;
NDIS_BUFFER* pNdisBuffer;
TRACE( TL_V, TM_Send, ( "SendPayloadUnseq" ) );
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
pPacket = (NDIS_PACKET* )(punpArgs[ 0 ]);
pBuffer = (CHAR* )(punpArgs[ 1 ]);
FREE_TUNNELWORK( pAdapter, pWork );
NdisAcquireSpinLock( &pVc->lockV );
{
// Build an L2TP payload header without Ns/Nr fields in 'pBuffer'.
//
ulLength =
BuildL2tpHeader(
pBuffer,
FALSE,
FALSE,
&pTunnel->usAssignedTunnelId,
&pVc->usAssignedCallId,
NULL,
0 );
// Pare down the header buffer to the actual length used then
// chain it onto the PPP-framed data we got from NDISWAN. Poke
// the L2TP header to update the length field accounting for the
// data.
//
pNdisBuffer = NdisBufferFromBuffer( pBuffer );
NdisAdjustBufferLength( pNdisBuffer, (UINT )ulLength );
NdisChainBufferAtFront( pPacket, pNdisBuffer );
NdisQueryPacket( pPacket, NULL, NULL, NULL, &unFullLength );
UpdateHeaderLength( pBuffer, (USHORT )unFullLength );
TRACE( TL_A, TM_Msg,
( "%sSEND payload(%d), len=%d",
((g_ulTraceLevel <= TL_I) ? "" : "\nL2TP: "),
++pVc->usNs,
unFullLength ) );
DUMPW( TL_A, TM_MDmp, pBuffer, ulLength );
++pVc->stats.ulSentDataPacketsUnSeq;
pVc->stats.ulDataBytesSent += ((ULONG )unFullLength - ulLength);
}
NdisReleaseSpinLock( &pVc->lockV );
// Call TDI to send the payload message.
//
{
FILE_OBJECT* FileObj;
PTDIX_SEND_HANDLER SendFunc;
NdisAcquireSpinLock(&pTunnel->lockT);
if (pTunnel->pRoute != NULL) {
FileObj = PayloadObjFromUdpContext(&pTunnel->udpContext);
SendFunc = TdixSend;
} else {
FileObj = NULL;
SendFunc = TdixSendDatagram;
}
NdisReleaseSpinLock(&pTunnel->lockT);
status = SendFunc(&pAdapter->tdix,
FileObj,
SendPayloadUnseqComplete,
pVc,
pPacket,
&pTunnel->address,
pBuffer,
(ULONG )unFullLength,
NULL );
}
ASSERT( status == NDIS_STATUS_PENDING );
}
VOID
SendControlAck(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to send a control acknowledge.
//
// This routine is called only at PASSIVE IRQL.
//
{
ADAPTERCB* pAdapter;
TRACE( TL_N, TM_Send, ( "SendControlAck" ) );
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
FREE_TUNNELWORK( pAdapter, pWork );
SendZlb( pTunnel, NULL, pTunnel->usNs, pTunnel->usNr, FALSE );
}
VOID
SendPayloadAck(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to send a payload acknowledge.
//
// This routine is called only at PASSIVE IRQL.
//
// IMPORTANT: Caller must take a call reference before calling that is
// removed by the send completion handler.
//
{
ADAPTERCB* pAdapter;
TRACE( TL_N, TM_Send, ( "SendPayloadAck" ) );
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
FREE_TUNNELWORK( pAdapter, pWork );
ASSERT( pVc );
ASSERT( pVc->usAssignedCallId > 0 );
SendZlb( pTunnel, pVc, pVc->usNs, pVc->usNr, FALSE );
}
VOID
SendPayloadReset(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to send a payload reset. Arg0 is the "Next Sent"
// value to send in the reset message.
//
// This routine is called only at PASSIVE IRQL.
//
// IMPORTANT: Caller must take a call reference before calling that is
// removed by the send completion handler.
//
{
ADAPTERCB* pAdapter;
USHORT usNs;
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
usNs = (USHORT )(punpArgs[ 0 ]);
FREE_TUNNELWORK( pAdapter, pWork );
TRACE( TL_A, TM_Send, ( "Send Reset=%d", (LONG )usNs ) );
ASSERT( pVc );
ASSERT( pVc->usAssignedCallId > 0 );
SendZlb( pTunnel, pVc, usNs, pVc->usNr, TRUE );
}
VOID
ReferenceControlSent(
IN CONTROLSENT* pCs )
// Reference the control-sent context 'pCs'.
//
{
LONG lRef;
lRef = NdisInterlockedIncrement( &pCs->lRef );
TRACE( TL_N, TM_Ref, ( "RefCs to %d", lRef ) );
}
LONG
DereferenceControlSent(
IN CONTROLSENT* pCs )
// Reference the control-sent context 'pCs'.
//
// Returns the reference count of the dereferenced context.
//
{
LONG lRef;
ADAPTERCB* pAdapter;
NDIS_BUFFER* pNdisBuffer;
lRef = NdisInterlockedDecrement( &pCs->lRef );
TRACE( TL_N, TM_Ref, ( "DerefCs to %d", lRef ) );
ASSERT( lRef >= 0 );
if (lRef == 0)
{
pAdapter = pCs->pTunnel->pAdapter;
ASSERT( pCs->linkSendsOut.Flink == &pCs->linkSendsOut );
pNdisBuffer = NdisBufferFromBuffer( pCs->pBuffer );
NdisAdjustBufferLength(
pNdisBuffer, BufferSizeFromBuffer( pCs->pBuffer ) );
FreeBufferToPool(
&pAdapter->poolFrameBuffers, pCs->pBuffer, TRUE );
if (pCs->pVc)
{
DereferenceVc( pCs->pVc );
}
ASSERT( pCs->pTunnel )
DereferenceTunnel( pCs->pTunnel );
FREE_TIMERQITEM( pAdapter, pCs->pTqiSendTimeout );
FREE_CONTROLSENT( pAdapter, pCs );
}
return lRef;
}
VOID
ReferencePayloadSent(
IN PAYLOADSENT* pPs )
// Reference the payload-sent context 'pPs'.
//
{
LONG lRef;
lRef = NdisInterlockedIncrement( &pPs->lRef );
TRACE( TL_N, TM_Ref, ( "RefPs to %d", lRef ) );
}
LONG
DereferencePayloadSent(
IN PAYLOADSENT* pPs )
// Reference the payload-sent context 'pPs'.
//
// Returns the reference count of the dereferenced context.
//
{
LONG lRef;
ADAPTERCB* pAdapter;
lRef = NdisInterlockedDecrement( &pPs->lRef );
TRACE( TL_N, TM_Ref, ( "DerefPs to %d", lRef ) );
ASSERT( lRef >= 0 );
if (lRef == 0)
{
ASSERT( pPs->linkSendsOut.Flink == &pPs->linkSendsOut );
// The actual work is scheduled because it calls outside the driver
// and we don't want any lock restrictions on this routine.
//
ScheduleTunnelWork(
pPs->pTunnel, pPs->pVc, CompletePayloadSent,
(ULONG_PTR )pPs, 0, 0, 0, FALSE, FALSE );
}
return lRef;
}
//-----------------------------------------------------------------------------
// Send utility routines (alphabetically)
//-----------------------------------------------------------------------------
USHORT
BuildAvpAch(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN CHAR* pszValue,
IN USHORT usValueLength,
OUT CHAR* pAvp )
// Builds a byte-array-valued AVP in caller's buffer 'pAvp' with attribute
// field value 'usAttribute' and value the first 'usValueLength' bytes of
// array 'pszlValue'. 'FMandatory' indicates the M-bit should be set in
// the AVP.
//
// Returns the length of the built AVP.
//
{
UNALIGNED USHORT* pusCur;
UNALIGNED USHORT* pusBits;
USHORT usLength;
pusCur = (UNALIGNED USHORT* )pAvp;
pusBits = pusCur;
++pusCur;
// Set Vendor ID to "IETF-defined".
//
*pusCur = 0;
++pusCur;
// Set Attribute field.
//
*pusCur = htons( usAttribute );
++pusCur;
// Set Value field.
//
if (usValueLength)
{
NdisMoveMemory( (CHAR* )pusCur, pszValue, (ULONG )usValueLength );
((CHAR* )pusCur) += usValueLength;
}
// Now, go back and set bits/length field.
//
usLength = (USHORT )(((CHAR* )pusCur) - pAvp);
*pusBits = usLength;
if (fMandatory)
{
*pusBits |= ABM_M;
}
*pusBits = htons( *pusBits );
return usLength;
}
USHORT
BuildAvpAul(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN UNALIGNED ULONG* pulValue,
IN USHORT usValues,
OUT CHAR* pAvp )
// Builds a ULONG-array-valued AVP in caller's buffer 'pAvp' with
// attribute field value 'usAttribute' and value the first 'usValues'
// ULONGS of array 'pszlValue'. 'FMandatory' indicates the M-bit should
// be set in the AVP.
//
// Returns the length of the built AVP.
//
{
UNALIGNED USHORT* pusCur;
UNALIGNED USHORT* pusBits;
USHORT usLength;
USHORT i;
pusCur = (UNALIGNED USHORT* )pAvp;
pusBits = pusCur;
++pusCur;
// Set Vendor ID to "IETF-defined".
//
*pusCur = 0;
++pusCur;
// Set Attribute field.
//
*pusCur = htons( usAttribute );
++pusCur;
// Set Value field.
//
for (i = 0; i < usValues; ++i)
{
*((UNALIGNED ULONG* )pusCur) = pulValue[ i ];
*((UNALIGNED ULONG* )pusCur) = htonl( *((UNALIGNED ULONG* )pusCur) );
pusCur += 2;
}
// Now, go back and set bits/length field.
//
usLength = (USHORT )(((CHAR* )pusCur) - pAvp);
*pusBits = usLength;
if (fMandatory)
{
*pusBits |= ABM_M;
}
*pusBits = htons( *pusBits );
return usLength;
}
USHORT
BuildAvpFlag(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
OUT CHAR* pAvp )
// Builds an empty (no data) flag AVP in caller's buffer 'pAvp' with
// attribute field value 'usAttribute'. 'FMandatory' indicates the M-bit
// should be set in the AVP.
//
// Returns the length of the built AVP.
//
{
UNALIGNED USHORT* pusCur;
UNALIGNED USHORT* pusBits;
USHORT usLength;
pusCur = (UNALIGNED USHORT* )pAvp;
pusBits = pusCur;
++pusCur;
// Set Vendor ID to "IETF-defined".
//
*pusCur = 0;
++pusCur;
// Set Attribute field.
//
*pusCur = htons( usAttribute );
++pusCur;
// Now, go back and set bits/length field.
//
usLength = (USHORT )(((CHAR* )pusCur) - pAvp);
*pusBits = usLength;
if (fMandatory)
{
*pusBits |= ABM_M;
}
*pusBits = htons( *pusBits );
return usLength;
}
USHORT
BuildAvpUl(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN ULONG ulValue,
OUT CHAR* pAvp )
// Builds a ULONG-valued AVP in caller's buffer 'pAvp' with attribute
// field value 'usAttribute' and value 'ulValue'. 'FMandatory' indicates
// the M-bit should be set in the AVP.
//
// Returns the length of the built AVP.
//
{
UNALIGNED USHORT* pusCur;
UNALIGNED USHORT* pusBits;
USHORT usLength;
pusCur = (UNALIGNED USHORT* )pAvp;
pusBits = pusCur;
++pusCur;
// Set Vendor ID to "IETF-defined".
//
*pusCur = 0;
++pusCur;
// Set Attribute field.
//
*pusCur = htons( usAttribute );
++pusCur;
// Set Value field.
//
*((UNALIGNED ULONG* )pusCur) = htonl( ulValue );
pusCur += 2;
// Now, go back and set bits/length field.
//
usLength = (USHORT )(((CHAR* )pusCur) - pAvp);
*pusBits = usLength;
if (fMandatory)
{
*pusBits |= ABM_M;
}
*pusBits = htons( *pusBits );
return usLength;
}
USHORT
BuildAvpUs(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN USHORT usValue,
OUT CHAR* pAvp )
// Builds a USHORT-valued AVP in caller's buffer 'pAvp' with attribute
// field value 'usAttribute' and value 'usValue'. 'FMandatory' indicates
// the M-bit should be set in the AVP.
//
// Returns the length of the built AVP.
//
{
UNALIGNED USHORT* pusCur;
UNALIGNED USHORT* pusBits;
USHORT usLength;
pusCur = (UNALIGNED USHORT* )pAvp;
pusBits = pusCur;
++pusCur;
// Set Vendor ID to "IETF-defined".
//
*pusCur = 0;
++pusCur;
// Set Attribute field.
//
*pusCur = htons( usAttribute );
++pusCur;
// Set Value field.
//
*pusCur = htons( usValue );
++pusCur;
// Now, go back and set bits/length field.
//
usLength = (USHORT )(((CHAR* )pusCur) - pAvp);
*pusBits = usLength;
if (fMandatory)
{
*pusBits |= ABM_M;
}
*pusBits = htons( *pusBits );
return usLength;
}
USHORT
BuildAvp2UsAndAch(
IN USHORT usAttribute,
IN BOOLEAN fMandatory,
IN USHORT usValue1,
IN USHORT usValue2,
IN CHAR* pszValue,
IN USHORT usValueLength,
OUT CHAR* pAvp )
// Builds an AVP consisting of 'usValue1' and 'usValue2' followed by
// message 'pszValue' of length 'usValueLength' bytes in caller's buffer
// 'pAvp' with attribute field value 'usAttribute'. 'FMandatory'
// indicates the M-bit should be set in the AVP.
//
// Returns the length of the built AVP.
//
{
UNALIGNED USHORT* pusCur;
UNALIGNED USHORT* pusBits;
USHORT usLength;
pusCur = (UNALIGNED USHORT* )pAvp;
pusBits = pusCur;
++pusCur;
// Set Vendor ID to "IETF-defined".
//
*pusCur = 0;
++pusCur;
// Set Attribute field.
//
*pusCur = htons( usAttribute );
++pusCur;
// Set first USHORT value field.
//
*pusCur = htons( usValue1 );
++pusCur;
// Set second USHORT value field.
//
*pusCur = htons( usValue2 );
++pusCur;
// Set message value field.
//
if (usValueLength)
{
NdisMoveMemory( (CHAR* )pusCur, pszValue, (ULONG )usValueLength );
((CHAR*)pusCur) += usValueLength;
}
// Now, go back and set bits/length field.
//
usLength = (USHORT )(((CHAR* )pusCur) - pAvp);
*pusBits = usLength;
if (fMandatory)
{
*pusBits |= ABM_M;
}
*pusBits = htons( *pusBits );
return usLength;
}
VOID
BuildCdnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing CallDisconnNotify control
// message. 'PTunnel' and 'pVc' are the tunnel/VC control blocks.
// 'ulArg1' and 'ulArg2' are the result and error codes to be returned.
// 'pvArg3' is ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
USHORT usResult;
USHORT usError;
TRACE( TL_V, TM_Send, ( "BuildCdnAvps" ) );
usResult = (USHORT )ulArg1;
usError = (USHORT )ulArg2;
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_CDN, pCurAvp );
pCurAvp += BuildAvp2UsAndAch(
ATTR_Result, TRUE, usResult, usError, NULL, 0, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedCallId, TRUE, pVc->usCallId, pCurAvp );
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildHelloAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Hello control message.
// 'PTunnel' is the tunnel control block. 'PVc', 'ulArgX' and 'pvArg3' are ignored.
// 'PAvpBuffer' is the address of the buffer to receive the built AVPs.
// '*PulAvpLength' is set to the length of the built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
TRACE( TL_V, TM_Send, ( "BuildHelloAvps" ) );
pAdapter = pTunnel->pAdapter;
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_Hello, pCurAvp );
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildIccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Incoming-Call-Connected
// control message. 'PTunnel' and 'pVc' are the tunnel/VC control blocks.
// 'UlArgX' and 'pvArg3' are ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
BOOLEAN fSequencing;
pAdapter = pTunnel->pAdapter;
TRACE( TL_V, TM_Send, ( "BuildIccnAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_ICCN, pCurAvp );
// For now, we don't support WAN link relays, so this is the estimated
// speed of the LAN relay. This could be totally wrong if, for instance,
// the tunnel is itself tunneled over a PPP link.
//
pCurAvp += BuildAvpUl(
ATTR_TxConnectSpeed, TRUE, pVc->ulConnectBps, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_FramingType, TRUE, FBM_Sync, pCurAvp );
fSequencing = !!(ReadFlags( &pVc->ulFlags ) & VCBF_Sequencing);
if (fSequencing)
{
USHORT usRWindow;
usRWindow = pAdapter->usPayloadReceiveWindow;
if (!usRWindow)
{
usRWindow = L2TP_DefaultReceiveWindow;
}
pCurAvp += BuildAvpUs(
ATTR_RWindowSize, TRUE, usRWindow, pCurAvp );
}
#if 0
// Use the LNS default PPD even when we're LAC, for now.
//
pCurAvp += BuildAvpUs(
ATTR_PacketProcDelay, TRUE, L2TP_LnsDefaultPpd, pCurAvp );
#endif
pCurAvp += BuildAvpUs(
ATTR_ProxyAuthType, FALSE, PAT_None, pCurAvp );
if (fSequencing)
{
pCurAvp += BuildAvpFlag(
ATTR_SequencingRequired, TRUE, pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildIcrpAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Incoming-Call-Reply
// control message. 'PTunnel' and 'pVc' are the tunnel/VC control blocks.
// 'UlArgX' and 'pvArg3' are ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
pAdapter = pTunnel->pAdapter;
TRACE( TL_V, TM_Send, ( "BuildIcrpAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_ICRP, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedCallId, TRUE, pVc->usCallId, pCurAvp );
if (ReadFlags( &pVc->ulFlags ) & VCBF_Sequencing)
{
USHORT usRWindow;
usRWindow = pAdapter->usPayloadReceiveWindow;
if (!usRWindow)
usRWindow = L2TP_DefaultReceiveWindow;
pCurAvp += BuildAvpUs(
ATTR_RWindowSize, TRUE, usRWindow, pCurAvp );
}
#if 0
pCurAvp += BuildAvpUs(
ATTR_PacketProcDelay, TRUE, L2TP_LnsDefaultPpd, pCurAvp );
#endif
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildIcrqAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Incoming-Call-Request
// control message. 'PTunnel' and 'pVc' are the tunnel/VC control block.
// 'UlArgX' and 'pvArg3' are ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
pAdapter = pTunnel->pAdapter;
TRACE( TL_V, TM_Send, ( "BuildIcrqAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_ICRQ, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedCallId, TRUE, pVc->usCallId, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_CallSerialNumber, TRUE,
pVc->pLcParams->ulCallSerialNumber, pCurAvp );
{
ULONG ulBearerType;
ulBearerType = 0;
if (pVc->pTcParams->ulMediaMode & LINEMEDIAMODE_DATAMODEM)
{
ulBearerType |= BBM_Analog;
}
if (pVc->pTcParams->ulMediaMode & LINEMEDIAMODE_DIGITALDATA)
{
ulBearerType |= BBM_Digital;
}
pCurAvp += BuildAvpUl(
ATTR_BearerType, TRUE, ulBearerType, pCurAvp );
}
if (pVc->pLcParams->ulPhysicalChannelId != 0xFFFFFFFF)
{
pCurAvp += BuildAvpUl(
ATTR_PhysicalChannelId, FALSE,
pVc->pLcParams->ulPhysicalChannelId, pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
ULONG
BuildL2tpHeader(
IN OUT CHAR* pBuffer,
IN BOOLEAN fControl,
IN BOOLEAN fReset,
IN USHORT* pusTunnelId,
IN USHORT* pusCallId,
IN USHORT* pusNs,
IN USHORT usNr )
// Fill in caller's 'pBuffer' with an L2TP header matching caller's
// arguments. 'FControl' indicates to build a control header, otherwise a
// payload header is built. 'fReset' indicates to build a reset rather
// than a simple acknowledge. Arguments that are not to appear in the
// header are NULL. Note that 'usNr' is not a pointer because it's
// appearance in the header is tied to the appearance of 'pusNs'.
//
// Returns the total length of the header.
//
{
UNALIGNED USHORT* pusBits;
UNALIGNED USHORT* pusLength;
UNALIGNED USHORT* pusCur;
ULONG ulLength;
pusCur = (UNALIGNED USHORT* )pBuffer;
pusBits = pusCur;
++pusCur;
pusLength = pusCur;
++pusCur;
// Initialize header bit mask with the version, and set the length bit
// since the Length field is always sent.
//
*pusBits = HBM_L | VER_L2tp;
if (fControl)
{
ASSERT( pusTunnelId && pusCallId && pusNs && !fReset );
*pusBits |= HBM_T;
}
else if (fReset)
{
ASSERT( pusTunnelId && pusCallId && pusNs );
*pusBits |= HBM_R;
}
if (pusTunnelId)
{
// Tunnel-ID field present. Draft-05 removes the 'I' bit that used to
// indicate the Tunnel-ID is present. It is now assumed to be always
// present.
//
*pusCur = htons( *pusTunnelId );
++pusCur;
}
if (pusCallId)
{
// Call-ID field present. Draft-05 removes the 'C' bit that used to
// indicate the Tunnel-ID is present. It is now assumed to be always
// present.
//
*pusCur = htons( *pusCallId );
++pusCur;
}
if (pusNs)
{
// Ns and Nr fields are present.
//
*pusBits |= HBM_F;
*pusCur = htons( *pusNs );
++pusCur;
*pusCur = htons( usNr );
++pusCur;
}
// Fill in the header and length fields with the accumulated
// values.
//
*pusBits = htons( *pusBits );
*pusLength = (USHORT )(((CHAR* )pusCur) - pBuffer);
ulLength = (ULONG )*pusLength;
*pusLength = htons( *pusLength );
return ulLength;
}
VOID
BuildOccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Outgoing-Call-Connected
// control message. 'PTunnel' and 'pVc' are the tunnel/VC control blocks.
// 'UlArgX' and 'pvArg3' are ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
BOOLEAN fSequencing;
pAdapter = pTunnel->pAdapter;
TRACE( TL_V, TM_Send, ( "BuildOccnAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_OCCN, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_TxConnectSpeed, TRUE, pVc->ulConnectBps, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_FramingType, TRUE, FBM_Sync, pCurAvp );
fSequencing = !!(ReadFlags( &pVc->ulFlags ) & VCBF_Sequencing);
if (fSequencing)
{
USHORT usRWindow;
usRWindow = pAdapter->usPayloadReceiveWindow;
if (!usRWindow)
{
usRWindow = L2TP_DefaultReceiveWindow;
}
pCurAvp += BuildAvpUs(
ATTR_RWindowSize, TRUE, usRWindow, pCurAvp );
}
#if 0
// Use the LNS default PPD even when we're LAC, for now.
//
pCurAvp += BuildAvpUs(
ATTR_PacketProcDelay, TRUE, L2TP_LnsDefaultPpd, pCurAvp );
#endif
if (fSequencing)
{
pCurAvp += BuildAvpFlag(
ATTR_SequencingRequired, TRUE, pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildOcrpAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Outgoing-Call-Reply
// control message. 'PTunnel' and 'pVc' are the tunnel/VC control blocks.
// 'UlArgX' and 'pvArg3' are ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
TRACE( TL_V, TM_Send, ( "BuildOcrpAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_OCRP, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedCallId, TRUE, pVc->usCallId, pCurAvp );
ASSERT( pVc->pLcParams );
if (pVc->pLcParams->ulPhysicalChannelId != 0xFFFFFFFF)
{
pCurAvp += BuildAvpUl(
ATTR_PhysicalChannelId, FALSE,
pVc->pLcParams->ulPhysicalChannelId, pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildOcrqAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Outgoing-Call-Request
// control message. 'PTunnel' and 'pVc' are the tunnel/VC control block.
// 'UlArgX' are ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
pAdapter = pTunnel->pAdapter;
TRACE( TL_V, TM_Send, ( "BuildOcrqAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_OCRQ, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedCallId, TRUE, pVc->usCallId, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_CallSerialNumber, TRUE,
pVc->pLcParams->ulCallSerialNumber, pCurAvp );
{
ULONG ulBps;
ulBps = pVc->pTcParams->ulMinRate;
if (ulBps == 0)
{
ulBps = 1;
}
else if (ulBps > 0x7FFFFFFF)
{
ulBps = 0x7FFFFFFF;
}
pCurAvp += BuildAvpUl(
ATTR_MinimumBps, TRUE, ulBps, pCurAvp );
ulBps = pVc->pTcParams->ulMaxRate;
if (ulBps == 0)
{
ulBps = 1;
}
else if (ulBps > 0x7FFFFFFF)
{
ulBps = 0x7FFFFFFF;
}
pCurAvp += BuildAvpUl(
ATTR_MaximumBps, TRUE, ulBps, pCurAvp );
}
{
ULONG ulBearerType;
ulBearerType = 0;
if (pVc->pTcParams->ulMediaMode & LINEMEDIAMODE_DATAMODEM)
{
ulBearerType |= BBM_Analog;
}
if (pVc->pTcParams->ulMediaMode & LINEMEDIAMODE_DIGITALDATA)
{
ulBearerType |= BBM_Digital;
}
pCurAvp += BuildAvpUl(
ATTR_BearerType, TRUE, ulBearerType, pCurAvp );
}
pCurAvp += BuildAvpUl(
ATTR_FramingType, TRUE, FBM_Sync, pCurAvp );
if (ReadFlags( &pVc->ulFlags ) & VCBF_Sequencing)
{
ASSERT( pAdapter->usPayloadReceiveWindow );
pCurAvp += BuildAvpUs(
ATTR_RWindowSize, TRUE,
pAdapter->usPayloadReceiveWindow, pCurAvp );
}
#if 0
pCurAvp += BuildAvpUs(
ATTR_PacketProcDelay, TRUE, L2TP_LnsDefaultPpd, pCurAvp );
#endif
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildScccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Start-Cc-Connected
// control message. 'PTunnel' is the tunnel control block. 'PVc' is
// ignored. 'UlArg1' is the true if a challenge response is to be sent,
// false otherwise. 'UlArg2' and 'pvArg3' are ignored. 'PAvpBuffer' is
// the address of the buffer to receive the built AVPs. '*PulAvpLength'
// is set to the length of the built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
TRACE( TL_V, TM_Send, ( "BuildScccnAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_SCCCN, pCurAvp );
if (ulArg1)
{
pCurAvp += BuildAvpAch(
ATTR_ChallengeResponse, TRUE,
pTunnel->achResponseToSend, sizeof(pTunnel->achResponseToSend),
pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildSccrpAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Start-Cc-Reply control
// message. 'PTunnel' is the tunnel control block. 'PVc' is ignored.
// 'UlArg1' is true if a challenge response is to be sent, false
// otherwise. 'UlArg2' and 'pvArg3' are ignored. 'PAvpBuffer' is the
// address of the buffer to receive the built AVPs. '*PulAvpLength' is
// set to the length of the built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
TRACE( TL_N, TM_Send, ( "BuildSccrpAvps" ) );
pAdapter = pTunnel->pAdapter;
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_SCCRP, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_ProtocolVersion, TRUE, L2TP_ProtocolVersion, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_FramingCaps, TRUE, pAdapter->ulFramingCaps, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_BearerCaps, TRUE, pAdapter->ulBearerCaps, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_FirmwareRevision, FALSE, L2TP_FirmwareRevision, pCurAvp );
ASSERT( pAdapter->pszHostName );
pCurAvp += BuildAvpAch(
ATTR_HostName, TRUE,
pAdapter->pszHostName,
(USHORT )strlen( pAdapter->pszHostName ),
pCurAvp );
pCurAvp += BuildAvpAch(
ATTR_VendorName, FALSE,
L2TP_VendorName, (USHORT )strlen( L2TP_VendorName ), pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedTunnelId, TRUE, pTunnel->usTunnelId, pCurAvp );
if (pAdapter->usControlReceiveWindow)
{
pCurAvp += BuildAvpUs(
ATTR_RWindowSize, TRUE,
pAdapter->usControlReceiveWindow, pCurAvp );
}
if (pAdapter->pszPassword)
{
pCurAvp += BuildAvpAch(
ATTR_Challenge, TRUE,
pTunnel->achChallengeToSend,
sizeof(pTunnel->achChallengeToSend),
pCurAvp );
}
if (ulArg1)
{
pCurAvp += BuildAvpAch(
ATTR_ChallengeResponse, TRUE,
pTunnel->achResponseToSend,
sizeof(pTunnel->achResponseToSend),
pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildSccrqAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Start-Cc-Request control
// message. 'PTunnel' is the tunnel control block. 'PVc', 'ulArgX' and 'pvArg3'
// are ignored. 'PAvpBuffer' is the address of the buffer to receive the
// built AVPs. '*PulAvpLength' is set to the length of the built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
TRACE( TL_V, TM_Send, ( "BuildSccrqAvps" ) );
pAdapter = pTunnel->pAdapter;
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_SCCRQ, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_ProtocolVersion, TRUE, L2TP_ProtocolVersion, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_FramingCaps, TRUE, pAdapter->ulFramingCaps, pCurAvp );
pCurAvp += BuildAvpUl(
ATTR_BearerCaps, TRUE, pAdapter->ulBearerCaps, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_FirmwareRevision, FALSE, L2TP_FirmwareRevision, pCurAvp );
if (pAdapter->pszHostName)
{
pCurAvp += BuildAvpAch(
ATTR_HostName, TRUE,
pAdapter->pszHostName,
(USHORT )strlen( pAdapter->pszHostName ),
pCurAvp );
}
pCurAvp += BuildAvpAch(
ATTR_VendorName, FALSE,
L2TP_VendorName, (USHORT )strlen( L2TP_VendorName ), pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedTunnelId, TRUE, pTunnel->usTunnelId, pCurAvp );
if (pAdapter->usControlReceiveWindow)
{
pCurAvp += BuildAvpUs(
ATTR_RWindowSize, TRUE, pAdapter->usControlReceiveWindow, pCurAvp );
}
if (pAdapter->pszPassword)
{
pCurAvp += BuildAvpAch(
ATTR_Challenge, TRUE,
pTunnel->achChallengeToSend,
sizeof(pTunnel->achChallengeToSend),
pCurAvp );
}
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildStopccnAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Stop-Cc-Notify control
// message. 'PTunnel' is the tunnel control block. 'PVc' is ignored.
// 'ulArg1' and 'ulArg2' are the result and error codes to be sent.
// 'pvArg3' is ignored. 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
USHORT usResult;
USHORT usError;
TRACE( TL_V, TM_Send, ( "BuildStopCcReqAvps" ) );
usResult = (USHORT )ulArg1;
usError = (USHORT )ulArg2;
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_StopCCN, pCurAvp );
pCurAvp += BuildAvpUs(
ATTR_AssignedTunnelId, TRUE, pTunnel->usTunnelId, pCurAvp );
pCurAvp += BuildAvp2UsAndAch(
ATTR_Result, TRUE, usResult, usError, NULL, 0, pCurAvp );
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
BuildWenAvps(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG ulArg1,
IN ULONG ulArg2,
IN PVOID pvArg3,
IN OUT CHAR* pAvpBuffer,
OUT ULONG* pulAvpLength )
// PBUILDAVPS handler to add AVPs to an outgoing Wan-Error-Notify control
// message. 'PTunnel' and 'pVc' are the tunnel/VC control block.
// 'pvArg3' is the address of an array of 6 error ULONGs, i.e. CRC,
// framing, hardware overrun, buffer overrun, timeouts, and alignment
// errors that this routine FREE_NONPAGEDs after use. 'ulArgX' are ignored.
// 'PAvpBuffer' is the address of the buffer to
// receive the built AVPs. '*PulAvpLength' is set to the length of the
// built AVPs.
//
{
CHAR* pCurAvp;
ULONG ulAvpLength;
ADAPTERCB* pAdapter;
UNALIGNED ULONG* pul;
pAdapter = pTunnel->pAdapter;
pul = (UNALIGNED ULONG* )pvArg3;
TRACE( TL_V, TM_Send, ( "BuildWenAvps" ) );
pCurAvp = pAvpBuffer;
pCurAvp += BuildAvpUs(
ATTR_MsgType, TRUE, CMT_WEN, pCurAvp );
pCurAvp += BuildAvpAul(
ATTR_CallErrors, TRUE, pul, 6, pCurAvp );
FREE_NONPAGED( pul );
*pulAvpLength = (ULONG )(pCurAvp - pAvpBuffer);
}
VOID
CompletePayloadSent(
IN TUNNELWORK* pWork,
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN ULONG_PTR* punpArgs )
// A PTUNNELWORK routine to complete a "sent payload". Arg0 is the
// PAYLOADSENT context which has already been de-queued from the
// "outstanding send" list.
//
// This routine is called only at PASSIVE IRQL.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
PAYLOADSENT* pPs;
NDIS_BUFFER* pNdisBuffer;
// Unpack context information then free the work item.
//
pAdapter = pTunnel->pAdapter;
pPs = (PAYLOADSENT* )(punpArgs[ 0 ]);
FREE_TUNNELWORK( pAdapter, pWork );
TRACE( TL_N, TM_Send, ( "CompletePayloadSent(Ns=%d)", (UINT )pPs->usNs ) );
// Undo the adjustments made before the send so the owner of each
// component resource gets back what they originally provided for clean-up
// and recycling.
//
NdisUnchainBufferAtFront( pPs->pPacket, &pNdisBuffer );
NdisAdjustBufferLength(
pNdisBuffer, BufferSizeFromBuffer( pPs->pBuffer ) );
FreeBufferToPool( &pAdapter->poolHeaderBuffers, pPs->pBuffer, TRUE );
// Notify sending driver of the result.
//
NDIS_SET_PACKET_STATUS( pPs->pPacket, pPs->status );
TRACE( TL_N, TM_Send, ("NdisMCoSendComp(s=$%x)", pPs->status ) );
NdisMCoSendComplete( pPs->status, pPs->pVc->NdisVcHandle, pPs->pPacket );
TRACE( TL_N, TM_Send, ("NdisMCoSendComp done" ) );
DereferenceCall( pVc );
DereferenceTunnel( pPs->pTunnel );
DereferenceVc( pPs->pVc );
#ifdef PSDEBUG
{
extern LIST_ENTRY g_listDebugPs;
extern NDIS_SPIN_LOCK g_lockDebugPs;
NdisAcquireSpinLock( &g_lockDebugPs );
{
RemoveEntryList( &pPs->linkDebugPs );
InitializeListHead( &pPs->linkDebugPs );
}
NdisReleaseSpinLock( &g_lockDebugPs );
}
#endif
FREE_TIMERQITEM( pAdapter, pPs->pTqiSendTimeout );
FREE_PAYLOADSENT( pAdapter, pPs );
}
VOID
SendControlComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer )
// PTDIXSENDCOMPLETE handler for sends that send only a single buffer from
// the 'ADAPTERCB.poolFrameBuffers' pool.
//
{
CONTROLSENT* pCs;
ULONG ulSendTimeoutMs;
TRACE( TL_V, TM_Send, ( "SendControlComp" ) );
pCs = (CONTROLSENT* )pContext1;
pCs->pIrp = NULL;
// "Instant expire" the timer if the message is longer queued as an
// outstanding send, i.e. it's been cancelled or terminated. This is the
// easiest way to clean up quickly yet reliably in this odd case.
// Accessing the link and the send timeout without locks held is
// technically not allowed, but the consequence of a misread is just a
// very slight additional delay. This is judged preferable to adding the
// cost of taking and releasing a spinlock to every send.
//
if (pCs->linkSendsOut.Flink == &pCs->linkSendsOut)
{
ulSendTimeoutMs = 0;
TRACE( TL_A, TM_Send,
( "Instant expire pCs=$%p pT=%p", pCs, pCs->pTunnel ) );
}
else
{
ulSendTimeoutMs = pCs->pTunnel->ulSendTimeoutMs;
}
// Schedule a retransmit of the packet, should it go unacknowledged. This
// occurs here rather than in SendPending to remove any chance of having
// the same MDL chain outstanding in two separate calls to the IP stack.
//
// Note: The logical code commented out below can be omitted for
// efficiency because the ReferenceControlSent for this scheduled timer
// and the DereferenceControlSent for this completed send cancel each
// other out.
//
// ReferenceControlSent( pCs );
// DereferenceControlSent( pCs );
//
ASSERT( pCs->pTqiSendTimeout );
TimerQScheduleItem(
pCs->pTunnel->pTimerQ,
pCs->pTqiSendTimeout,
ulSendTimeoutMs,
SendControlTimerEvent,
pCs );
}
VOID
SendControlTimerEvent(
IN TIMERQITEM* pItem,
IN VOID* pContext,
IN TIMERQEVENT event )
// PTIMERQEVENT handler set to expire when it's time to give up on
// receiving an acknowledge to the sent control packet indicated by
// 'pContext'.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
TUNNELCB* pTunnel;
CONTROLSENT* pCs;
TRACE( TL_N, TM_Send,
( "SendControlTimerEvent(%s)", TimerQPszFromEvent( event ) ) );
// Unpack context information. The timer item is owned by the "control
// sent" context and freed indirectly by dereferencing below.
//
pCs = (CONTROLSENT* )pContext;
pTunnel = pCs->pTunnel;
pAdapter = pTunnel->pAdapter;
if (event == TE_Expire)
{
// Timer expired, meaning it's time to give up on ever receiving an
// acknowledge to the sent packet. Per the draft/RFC, adjustments to
// the send window and send timeouts are necessary.
//
NdisAcquireSpinLock( &pTunnel->lockT );
do
{
if (pCs->linkSendsOut.Flink == &pCs->linkSendsOut)
{
// The context is not on the out queue, so it must have been
// cancelled or terminated while the expire handling was being
// set up. Do nothing.
//
TRACE( TL_I, TM_Send,
( "T%d: Timeout aborted", (ULONG )pTunnel->usTunnelId ) );
break;
}
AdjustTimeoutsAndSendWindowAtTimeout(
pAdapter->ulMaxSendTimeoutMs,
pTunnel->lDeviationMs,
&pTunnel->ulSendTimeoutMs,
&pTunnel->ulRoundTripMs,
&pTunnel->ulSendWindow,
&pTunnel->ulAcksSinceSendTimeout );
--pTunnel->ulSendsOut;
++pCs->ulRetransmits;
TRACE( TL_I, TM_Send,
( "T%d: TIMEOUT(%d) -sout=%d +retry=%d rtt=%d ato=%d sw=%d",
(ULONG )pTunnel->usTunnelId, (ULONG )pCs->usNs,
pTunnel->ulSendsOut, pCs->ulRetransmits,
pTunnel->ulRoundTripMs, pTunnel->ulSendTimeoutMs,
pTunnel->ulSendWindow ) );
// Retransmit the packet, or close the tunnel if retries are
// exhausted.
//
if (pCs->ulRetransmits > pAdapter->ulMaxRetransmits)
{
// Retries are exhausted. Give up and close the tunnel. No
// point in trying to be graceful since peer is not
// responding.
//
SetFlags( &pTunnel->ulFlags, TCBF_PeerNotResponding );
RemoveEntryList( &pCs->linkSendsOut );
InitializeListHead( &pCs->linkSendsOut );
DereferenceControlSent( pCs );
ScheduleTunnelWork(
pTunnel, NULL, CloseTunnel,
0, 0, 0, 0, FALSE, FALSE );
}
else
{
// Retries remaining. Mark the packet as pending
// retransmission, then see if the send window allows the
// retransmit to go now.
//
pCs->ulFlags |= CSF_Pending;
ScheduleTunnelWork(
pTunnel, NULL, SendPending,
0, 0, 0, 0, FALSE, FALSE );
}
}
while (FALSE);
NdisReleaseSpinLock( &pTunnel->lockT );
}
// Remove the reference covering the scheduled timer.
//
DereferenceControlSent( pCs );
}
VOID
SendHeaderComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer )
// PTDIXSENDCOMPLETE handler for sends that send only a single buffer from
// the 'ADAPTERCB.poolHeaderBuffers' pool.
//
{
ADAPTERCB* pAdapter;
VCCB* pVc;
NDIS_BUFFER* pNdisBuffer;
TRACE( TL_V, TM_Send, ( "SendHeaderComp" ) );
pAdapter = (ADAPTERCB* )pContext1;
pVc = (VCCB* )pContext2;
// Undo the adjustments made before the send the buffer is ready for
// re-use.
//
pNdisBuffer = NdisBufferFromBuffer( pBuffer );
NdisAdjustBufferLength( pNdisBuffer, BufferSizeFromBuffer( pBuffer ) );
FreeBufferToPool( &pAdapter->poolHeaderBuffers, pBuffer, TRUE );
if (pVc)
{
DereferenceCall( pVc );
}
}
VOID
SendPayloadSeqComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer )
// PTDIXSENDCOMPLETE handler for sequenced payloads.
//
{
PAYLOADSENT* pPs;
TRACE( TL_V, TM_Send, ( "SendPayloadSeqComp" ) );
pPs = (PAYLOADSENT* )pContext1;
pPs->pIrp = NULL;
DereferencePayloadSent( pPs );
}
VOID
SendPayloadUnseqComplete(
IN TDIXCONTEXT* pTdix,
IN VOID* pContext1,
IN VOID* pContext2,
IN CHAR* pBuffer )
// PTDIXSENDCOMPLETE handler for unsequenced payloads.
//
{
ADAPTERCB* pAdapter;
VCCB* pVc;
NDIS_PACKET* pPacket;
NDIS_BUFFER* pNdisBuffer;
TRACE( TL_V, TM_Send, ( "SendPayloadUnseqComp" ) );
pVc = (VCCB* )pContext1;
pPacket = (NDIS_PACKET* )pContext2;
pAdapter = pVc->pAdapter;
// Undo the adjustments made before the send so the owner of each
// component resource gets back what they originally provided for clean-up
// and recycling.
//
NdisUnchainBufferAtFront( pPacket, &pNdisBuffer );
NdisAdjustBufferLength( pNdisBuffer, BufferSizeFromBuffer( pBuffer ) );
FreeBufferToPool( &pAdapter->poolHeaderBuffers, pBuffer, TRUE );
// Notify sending driver of the result. Without sequencing, just trying
// to send it is enough to claim success.
//
NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_SUCCESS );
TRACE( TL_N, TM_Send, ("NdisMCoSendComp($%x)", NDIS_STATUS_SUCCESS ) );
NdisMCoSendComplete( NDIS_STATUS_SUCCESS, pVc->NdisVcHandle, pPacket );
TRACE( TL_N, TM_Send, ("NdisMCoSendComp done" ) );
DereferenceCall( pVc );
}
VOID
SendPayloadTimerEvent(
IN TIMERQITEM* pItem,
IN VOID* pContext,
IN TIMERQEVENT event )
// PTIMERQEVENT handler set to expire when it's time to give up on
// receiving an acknowledge to the sent payload packet indicated in the
// PAYLOADSENT* 'pContext'.
//
{
PAYLOADSENT* pPs;
ADAPTERCB* pAdapter;
TUNNELCB* pTunnel;
VCCB* pVc;
TRACE( TL_N, TM_Send,
( "SendPayloadTimerEvent(%s)", TimerQPszFromEvent( event ) ) );
// Unpack context information. The timer item is owned by the "payload
// sent" context and freed indirectly by the de-referencing of that
// context below.
//
pPs = (PAYLOADSENT* )pContext;
pVc = pPs->pVc;
pTunnel = pPs->pTunnel;
pAdapter = pVc->pAdapter;
if (event == TE_Expire)
{
LONG lOldSendWindow;
LONG lSwChange;
BOOLEAN fCallActive;
LINKSTATUSINFO info;
// Timer expired, meaning it's time to give up on ever receiving an
// acknowledge to the sent packet.
//
NdisAcquireSpinLock( &pVc->lockV );
do
{
if (pPs->linkSendsOut.Flink == &pPs->linkSendsOut)
{
// The context is not on the "outstanding send" list, so it
// must have been cancelled or terminated while the expire
// handling was being set up. Do nothing.
//
TRACE( TL_I, TM_Send,
( "C%d: Timeout aborted", (ULONG )pVc->usCallId ) );
fCallActive = FALSE;
break;
}
// This packet was not acknowledged.
//
pPs->status = NDIS_STATUS_FAILURE;
// Remove the context from the "outstanding send" list. The
// corresponding dereference occurs below.
//
RemoveEntryList( &pPs->linkSendsOut );
InitializeListHead( &pPs->linkSendsOut );
// The rest has to do with call related fields so get a reference
// now. This is removed by the "reset" send completion.
//
fCallActive = ReferenceCall( pVc );
if (fCallActive)
{
// Per the draft/RFC, adjustments to the send window and send
// timeouts are necessary when a send times out.
//
lOldSendWindow = (LONG )pVc->ulSendWindow;
AdjustTimeoutsAndSendWindowAtTimeout(
pAdapter->ulMaxSendTimeoutMs,
pVc->lDeviationMs,
&pVc->ulSendTimeoutMs,
&pVc->ulRoundTripMs,
&pVc->ulSendWindow,
&pVc->ulAcksSinceSendTimeout );
lSwChange = ((LONG )pVc->ulSendWindow) - lOldSendWindow;
TRACE( TL_I, TM_Send,
( "C%d: TIMEOUT(%d) new rtt=%d ato=%d sw=%d(%+d)",
(ULONG )pVc->usCallId, (ULONG )pPs->usNs,
pVc->ulRoundTripMs, pVc->ulSendTimeoutMs,
pVc->ulSendWindow, lSwChange ) );
if (lSwChange != 0)
{
// The send window changed, i.e. it closed some because of
// the timeout. Update the statistics accordingly.
//
++pVc->stats.ulSendWindowChanges;
if (pVc->ulSendWindow > pVc->stats.ulMaxSendWindow)
{
pVc->stats.ulMaxSendWindow = pVc->ulSendWindow;
}
else if (pVc->ulSendWindow < pVc->stats.ulMinSendWindow)
{
pVc->stats.ulMinSendWindow = pVc->ulSendWindow;
}
// Need to release the lock before indicating the link
// status change outside our driver, so make a "safe" copy
// of the link status information.
//
TransferLinkStatusInfo( pVc, &info );
}
// Send a zero length payload with the R-bit set to reset the
// peer's Nr to the packet after this one. The call reference
// will be removed when the send completes.
//
ScheduleTunnelWork(
pTunnel, pVc, SendPayloadReset,
(ULONG_PTR )(pPs->usNs + 1), 0, 0, 0, FALSE, FALSE );
++pVc->stats.ulSentResets;
++pVc->stats.ulSentPacketsTimedOut;
}
// Remove the reference for linkage in the "outstanding send"
// list.
//
DereferencePayloadSent( pPs );
}
while (FALSE);
NdisReleaseSpinLock( &pVc->lockV );
if (fCallActive && lSwChange != 0)
{
// Inform NDISWAN of the new send window since it's the component
// that actually does the throttling.
//
IndicateLinkStatus( pVc, &info );
}
}
// Remove the reference covering the scheduled timer event.
//
DereferencePayloadSent( pPs );
}
VOID
SendZlb(
IN TUNNELCB* pTunnel,
IN VCCB* pVc,
IN USHORT usNs,
IN USHORT usNr,
IN BOOLEAN fReset )
// Send a data-less packet with sequence 'usNs' and 'usNr' on 'pTunnel'.
// 'PVc' is the associated VC, or NULL if none. When 'pVc' is provided,
// 'fReset' may be set to indicate a payload reset is to be built,
// otherwise a simple acknowledge is built.
//
// This routine is called only at PASSIVE IRQL.
//
// IMPORTANT: Caller must take a call reference before calling that is
// removed by the send completion handler.
//
{
NDIS_STATUS status;
ADAPTERCB* pAdapter;
CHAR* pBuffer;
ULONG ulLength;
USHORT usAssignedCallId;
BOOLEAN fControl;
NDIS_BUFFER* pNdisBuffer;
pAdapter = pTunnel->pAdapter;
usAssignedCallId = (pVc) ? pVc->usAssignedCallId : 0;
fControl = (usAssignedCallId == 0);
ASSERT( !(fReset && fControl) );
if (!fControl && !(ReadFlags( &pTunnel->ulFlags ) & TCBF_HostRouteAdded))
{
TRACE( TL_A, TM_Send, ( "SendZlb w/o host route?" ) );
++g_ulSendZlbWithoutHostRoute;
if (pVc)
{
DereferenceCall( pVc );
}
return;
}
// Get an NDIS_BUFFER to hold the L2TP header.
//
pBuffer = GetBufferFromPool( &pAdapter->poolHeaderBuffers );
if (!pBuffer)
{
ASSERT( "GetBfP?" );
if (pVc)
{
DereferenceCall( pVc );
}
return;
}
// Fill in 'pBuffer' with the L2TP header.
//
ulLength =
BuildL2tpHeader(
pBuffer,
fControl,
fReset,
&pTunnel->usAssignedTunnelId,
&usAssignedCallId,
&usNs,
usNr );
// Pare down the buffer to the actual length used.
//
pNdisBuffer = NdisBufferFromBuffer( pBuffer );
NdisAdjustBufferLength( pNdisBuffer, (UINT )ulLength );
// Call TDI to send the bare L2TP header.
//
TRACE( TL_A, TM_Msg,
( "%sSEND ZLB(Nr=%d) CID=%d R=%d",
(g_ulTraceLevel <= TL_I) ? "" : "\nL2TP: ",
(ULONG )usNr, (ULONG )usAssignedCallId, (ULONG )fReset ) );
DUMPW( TL_A, TM_MDmp, pBuffer, ulLength );
{
PTDIX_SEND_HANDLER SendFunc;
FILE_OBJECT* FileObj;
if (ReadFlags(&pTunnel->ulFlags) & TCBF_SendConnected) {
ASSERT(pTunnel->pRoute != NULL);
SendFunc = TdixSend;
if (fControl)
{
FileObj =
CtrlObjFromUdpContext(&pTunnel->udpContext);
}
else
{
FileObj =
PayloadObjFromUdpContext(&pTunnel->udpContext);
}
} else {
FileObj = NULL;
SendFunc = TdixSendDatagram;
}
status =
SendFunc(
&pAdapter->tdix,
FileObj,
SendHeaderComplete,
pAdapter,
pVc,
&pTunnel->address.ulIpAddress,
pBuffer,
ulLength,
NULL );
}
ASSERT( status == NDIS_STATUS_PENDING );
}
VOID
UpdateControlHeaderNr(
IN CHAR* pBuffer,
IN USHORT usNr )
// Updates the 'Next Receive' field of control message buffer 'pBuffer'
// with the value 'usNr'.
//
{
USHORT* pusNr;
// Fortunately, the control header up to 'Next Receive' is fixed so a
// simple offset calculation can be used.
//
pusNr = ((USHORT* )pBuffer) + 5;
*pusNr = htons( usNr );
}
VOID
UpdateHeaderLength(
IN CHAR* pBuffer,
IN USHORT usLength )
// Updates the 'Length' field of the L2TP message buffer 'pBuffer' to the
// value 'usLength'.
//
{
USHORT* pusLength;
// Fortunately, the control header up to 'Length' is fixed so a simple
// offset calculation can be used.
//
pusLength = ((USHORT* )pBuffer) + 1;
*pusLength = htons( usLength );
}