You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3291 lines
91 KiB
3291 lines
91 KiB
// 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"
|
|
|
|
#include "send.tmh"
|
|
|
|
//* Structure of a UDP header.
|
|
typedef struct UDPHeader {
|
|
ushort uh_src; // Source port.
|
|
ushort uh_dest; // Destination port.
|
|
ushort uh_length; // Length
|
|
ushort uh_xsum; // Checksum.
|
|
} UDPHeader;
|
|
|
|
#define IP_VERSION 0x40
|
|
|
|
//* IP Header format.
|
|
typedef struct IPHeader {
|
|
uchar iph_verlen; // Version and length.
|
|
uchar iph_tos; // Type of service.
|
|
ushort iph_length; // Total length of datagram.
|
|
ushort iph_id; // Identification.
|
|
ushort iph_offset; // Flags and fragment offset.
|
|
uchar iph_ttl; // Time to live.
|
|
uchar iph_protocol; // Protocol.
|
|
ushort iph_xsum; // Header checksum.
|
|
IPAddr iph_src; // Source address.
|
|
IPAddr iph_dest; // Destination address.
|
|
} IPHeader;
|
|
|
|
#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 );
|
|
|
|
ULONG BuildIpUdpHeaders(
|
|
IN TUNNELCB* pTunnel,
|
|
IN OUT CHAR* pBuffer,
|
|
IN ULONG ulLength);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
CHAR* pCurrBuffer;
|
|
|
|
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_CMsg, ( "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)
|
|
{
|
|
WPLOG( LL_A, LM_Res, ( "Failed to allocate buffer"));
|
|
status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
// Get an "unacknowledged send timeout" timer event descriptor.
|
|
//
|
|
pTqiSendTimeout = ALLOC_TIMERQITEM( pAdapter );
|
|
if (!pTqiSendTimeout)
|
|
{
|
|
WPLOG( LL_A, LM_CMsg, ( "Failed to allocate timer event descriptor"));
|
|
status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
// Get a "control message sent" context.
|
|
//
|
|
pCs = ALLOC_CONTROLSENT( pAdapter );
|
|
if (!pCs)
|
|
{
|
|
WPLOG( LL_A, LM_CMsg, ( "Failed to allocate CONTROLSENT"));
|
|
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 IP & UDP headers?
|
|
if ((ReadFlags(&pTunnel->ulFlags) & (TCBF_SendConnected | TCBF_LocalAddrSet)) ==
|
|
TCBF_LocalAddrSet) {
|
|
ulFlags |= CSF_IpUdpHeaders;
|
|
pCurrBuffer = pBuffer + sizeof(IPHeader) + sizeof(UDPHeader);
|
|
} else {
|
|
pCurrBuffer = pBuffer;
|
|
}
|
|
|
|
// Build an L2TP control header in 'pCurrBuffer'. 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(
|
|
pCurrBuffer,
|
|
TRUE,
|
|
FALSE,
|
|
&pTunnel->usAssignedTunnelId,
|
|
&usAssignedCallId,
|
|
&pTunnel->usNs,
|
|
pTunnel->usNr );
|
|
|
|
WPLOG( LL_M, LM_CMsg, ( "SEND -> %!IPADDR!/%d %s Tid %d, Peer's Tid %d, Peer's Cid %d, Ns=%d, Nr=%d",
|
|
pTunnel->address.ulIpAddress, ntohs(pTunnel->address.sUdpPort),
|
|
MsgTypePszFromUs( usMsgType ),
|
|
pTunnel->usTunnelId, 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,
|
|
pCurrBuffer + ulLength, &ulAvpLength );
|
|
ulLength += ulAvpLength;
|
|
UpdateHeaderLength( pCurrBuffer, (USHORT )ulLength );
|
|
|
|
// Build IP & UDP headers if necessary
|
|
if(ulFlags & CSF_IpUdpHeaders)
|
|
{
|
|
ulLength = BuildIpUdpHeaders(pTunnel, pBuffer, 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_CMsg, ( "SendPending(sout=%d,sw=%d)",
|
|
pTunnel->ulSendsOut, pTunnel->ulSendWindow ) );
|
|
|
|
TRACE( TL_V, TM_CMsg, ( "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->localaddress.ulIpAddress,
|
|
&pTunnel->ulMediaSpeed);
|
|
}
|
|
|
|
{
|
|
FILE_OBJECT* FileObj;
|
|
PTDIX_SEND_HANDLER SendFunc;
|
|
|
|
// Call TDI to send the control message.
|
|
//
|
|
if (pCs->ulFlags & CSF_IpUdpHeaders) {
|
|
FileObj = pAdapter->tdix.pRawAddress;
|
|
SendFunc = TdixSendDatagram;
|
|
}
|
|
else if (ReadFlags(&pTunnel->ulFlags) & TCBF_SendConnected) {
|
|
ASSERT(pTunnel->pRoute != NULL);
|
|
FileObj = CtrlObjFromUdpContext(&pTunnel->udpContext);
|
|
SendFunc = TdixSend;
|
|
} else {
|
|
FileObj = pAdapter->tdix.pAddress;
|
|
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)
|
|
{
|
|
WPLOG( LL_A, LM_Res, ( "Failed to allocate buffer"));
|
|
DereferenceCall( pVc );
|
|
status = NDIS_STATUS_RESOURCES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( TL_A, TM_Send, ( "Send on inactive $%p", pVc ) );
|
|
WPLOG( LL_A, LM_Send, ( "Send on inactive %p", pVc ) );
|
|
status = NDIS_STATUS_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( TL_A, TM_Send, ( "SendPayload w/o host route?" ) );
|
|
WPLOG( LL_A, LM_Send, ( "SendPayload w/o host route?" ) );
|
|
status = NDIS_STATUS_FAILURE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( TL_A, TM_Send, ( "Send $%p w/o pT?", pVc ) );
|
|
WPLOG( LL_A, LM_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 ) );
|
|
WPLOG( LL_A, LM_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)
|
|
{
|
|
status = NDIS_STATUS_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
// Get a "payload message sent" context.
|
|
//
|
|
pPs = ALLOC_PAYLOADSENT( pAdapter );
|
|
if (!pPs)
|
|
{
|
|
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 ) );
|
|
WPLOG( LL_A, LM_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 = pAdapter->tdix.pAddress;
|
|
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 = pAdapter->tdix.pAddress;
|
|
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 ) );
|
|
WPLOG( LL_A, LM_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 );
|
|
|
|
WPLOG( LL_M, LM_CMsg, ( "Result=%d, Error=%d", usResult, usError));
|
|
|
|
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 );
|
|
|
|
WPLOG( LL_M, LM_CMsg, ( "Result=%d, Error=%d", usResult, usError));
|
|
|
|
*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,
|
|
( "Tid %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 ) );
|
|
|
|
WPLOG( LL_M, LM_Send,
|
|
( "Tid %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;
|
|
CHAR* pCurrBuffer;
|
|
ULONG ulLength;
|
|
USHORT usAssignedCallId;
|
|
BOOLEAN fControl, fIpUdpHeaders;
|
|
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?" ) );
|
|
WPLOG( LL_A, LM_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)
|
|
{
|
|
WPLOG( LL_A, LM_Res, ( "Failed to allocate buffer"));
|
|
if (pVc)
|
|
{
|
|
DereferenceCall( pVc );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((ReadFlags(&pTunnel->ulFlags) & (TCBF_SendConnected | TCBF_LocalAddrSet)) ==
|
|
TCBF_LocalAddrSet) {
|
|
pCurrBuffer = pBuffer + sizeof(IPHeader) + sizeof(UDPHeader);
|
|
fIpUdpHeaders = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pCurrBuffer = pBuffer;
|
|
fIpUdpHeaders = FALSE;
|
|
}
|
|
|
|
// Fill in 'pBuffer' with the L2TP header.
|
|
//
|
|
ulLength =
|
|
BuildL2tpHeader(
|
|
pCurrBuffer,
|
|
fControl,
|
|
fReset,
|
|
&pTunnel->usAssignedTunnelId,
|
|
&usAssignedCallId,
|
|
&usNs,
|
|
usNr );
|
|
|
|
// Build IP & UDP headers if necessary
|
|
if(fIpUdpHeaders)
|
|
{
|
|
ulLength = BuildIpUdpHeaders(pTunnel, pBuffer, ulLength);
|
|
}
|
|
|
|
// 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(fIpUdpHeaders)
|
|
{
|
|
FileObj = pAdapter->tdix.pRawAddress;
|
|
SendFunc = TdixSendDatagram;
|
|
}
|
|
else 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 = pAdapter->tdix.pAddress;
|
|
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 );
|
|
}
|
|
|
|
|
|
// ** xsum - Checksum a flat buffer.
|
|
//
|
|
// This is the lowest level checksum routine. It returns the uncomplemented
|
|
// checksum of a flat buffer.
|
|
//
|
|
// Entry: Buffer - Buffer to be checksummed.
|
|
// Size - Size in bytes of Buffer.
|
|
// InitialValue - Value of previous Xsum to add this Xsum to.
|
|
//
|
|
// Returns: The uncomplemented checksum of buffer.
|
|
//
|
|
USHORT
|
|
xsumComp(void *Buffer, int Size, USHORT InitialValue)
|
|
{
|
|
USHORT UNALIGNED *Buffer1 = (USHORT UNALIGNED *)Buffer; // Buffer expressed as shorts.
|
|
ULONG csum = InitialValue;
|
|
|
|
USHORT tmp;
|
|
UCHAR tmp2[2];
|
|
|
|
while (Size > 1) {
|
|
|
|
tmp=*Buffer1;
|
|
Buffer1++;
|
|
//csum += *Buffer1++;
|
|
csum +=htons(tmp);
|
|
Size -= sizeof(USHORT);
|
|
}
|
|
|
|
if (Size) {
|
|
tmp2[0]=*(UCHAR *)Buffer1; // For odd buffers, add in last byte.
|
|
tmp2[1]=0;
|
|
tmp=*(USHORT*)tmp2;
|
|
csum += htons(tmp);
|
|
}
|
|
|
|
csum = (csum >> 16) + (csum & 0xffff);
|
|
csum += (csum >> 16);
|
|
return (USHORT)csum;
|
|
}
|
|
|
|
|
|
ULONG BuildIpUdpHeaders(
|
|
IN TUNNELCB* pTunnel,
|
|
IN OUT CHAR* pBuffer,
|
|
IN ULONG ulLength)
|
|
{
|
|
IPHeader *IPH = (IPHeader *) pBuffer;
|
|
UDPHeader *UDPH = (UDPHeader *) (pBuffer + sizeof(IPHeader));
|
|
|
|
IPH->iph_verlen = IP_VERSION + (sizeof(IPHeader) >> 2);
|
|
IPH->iph_tos=0;
|
|
IPH->iph_length=htons((USHORT)ulLength + sizeof(IPHeader) + sizeof(UDPHeader));
|
|
IPH->iph_id=0; // filled by TCPIP
|
|
IPH->iph_offset=0;
|
|
IPH->iph_ttl=128;
|
|
IPH->iph_protocol=17;
|
|
IPH->iph_xsum = 0; // filled by TCPIP
|
|
IPH->iph_src = pTunnel->localaddress.ulIpAddress;
|
|
IPH->iph_dest = pTunnel->address.ulIpAddress;
|
|
|
|
UDPH->uh_src = pTunnel->localaddress.sUdpPort;
|
|
UDPH->uh_dest = pTunnel->address.sUdpPort;
|
|
UDPH->uh_length = htons((USHORT)ulLength + sizeof(UDPHeader));
|
|
UDPH->uh_xsum = 0;
|
|
|
|
// Fill in fields of UDP Header. Calculate XSum over pseudoheader and UDP header.
|
|
{
|
|
USHORT pseudoHeaderXSum;
|
|
UCHAR pshTmp[4];
|
|
USHORT udpXSum;
|
|
|
|
// Compute UDP pseudo header checksum
|
|
pseudoHeaderXSum=xsumComp(&IPH->iph_src, 8, 0);
|
|
pshTmp[0] = 0;
|
|
pshTmp[1] = IPH->iph_protocol;
|
|
NdisMoveMemory((void *)&pshTmp[2], (void *)&UDPH->uh_length, 2);
|
|
|
|
pseudoHeaderXSum = xsumComp(pshTmp, 4, pseudoHeaderXSum);
|
|
|
|
// Compute UDP header checksum
|
|
udpXSum=xsumComp(UDPH, 8, pseudoHeaderXSum);
|
|
|
|
// Compute UDP data checksum
|
|
udpXSum = xsumComp(pBuffer + sizeof(IPHeader) + sizeof(UDPHeader), (int)ulLength, udpXSum);
|
|
|
|
UDPH->uh_xsum = htons(~udpXSum);
|
|
}
|
|
|
|
ulLength += IpFixedHeaderSize + UDP_HEADER_SIZE;
|
|
return ulLength;
|
|
}
|
|
|
|
|