// Copyright (c) 1997, Microsoft Corporation, all rights reserved // // mp.c // RAS L2TP WAN mini-port/call-manager driver // Mini-port routines // // 01/07/97 Steve Cobb #include "l2tpp.h" #include "mp.tmh" // The adapter control block address is recorded in this global as a debugging // aid. This global must not be read by any code. // ADAPTERCB* g_pDebugAdapter; // The number of packets indicated up to and returned from the driver above. // LONG g_lPacketsIndicated = 0; LONG g_lPacketsReturned = 0; // Call statistics totals for all calls since loading, calls and the lock // protecting access to them. For this global only, the 'ullCallUp' field is // the number of calls recorded, rather than a time. // CALLSTATS g_stats; NDIS_SPIN_LOCK g_lockStats; // Default settings for the NDIS_WAN_CO_INFO capabilities of an adapter. // static NDIS_WAN_CO_INFO g_infoDefaults = { L2TP_MaxFrameSize, // MaxFrameSize 0, // MaxSendWindow (placeholder) PPP_FRAMING // FramingBits | PPP_COMPRESS_ADDRESS_CONTROL | PPP_COMPRESS_PROTOCOL_FIELD, 0, // DesiredACCM }; //----------------------------------------------------------------------------- // Local prototypes (alphabetically) //----------------------------------------------------------------------------- VOID FreeAdapter( IN ADAPTERCB* pAdapter ); NDIS_STATUS GetRegistrySettings( IN NDIS_HANDLE WrapperConfigurationContext, OUT USHORT* pusMaxVcs, OUT TDIXMEDIATYPE* pMediaType, OUT L2TPROLE* pOutgoingRole, OUT ULONG* pulMaxSendTimeoutMs, OUT ULONG* pulInitialSendTimeoutMs, OUT ULONG* pulMaxRetransmits, OUT ULONG* pulHelloMs, OUT ULONG* pulMaxAckDelayMs, OUT SHORT* psMaxOutOfOrder, OUT USHORT* pusControlReceiveWindow, OUT USHORT* pusPayloadReceiveWindow, OUT ULONG* pulPayloadSendWindow, OUT USHORT* pusLlistDepth, OUT CHAR** ppszHostName, OUT CHAR** ppszPassword, OUT BOOLEAN* pfIgnoreFramingMismatch, OUT BOOLEAN* pfExclusiveTunnels, OUT HOSTROUTEEXISTS* phre, OUT BOOLEAN* pfDisableUdpXsums, OUT WCHAR** ppszDriverDesc ); NDIS_STATUS QueryInformation( IN ADAPTERCB* pAdapter, IN VCCB* pLink, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ); NDIS_STATUS SetInformation( IN ADAPTERCB* pAdapter, IN VCCB* pLink, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ); //----------------------------------------------------------------------------- // Mini-port handlers //----------------------------------------------------------------------------- NDIS_STATUS LmpInitialize( OUT PNDIS_STATUS OpenErrorStatus, OUT PUINT SelectedMediumIndex, IN PNDIS_MEDIUM MediumArray, IN UINT MediumArraySize, IN NDIS_HANDLE MiniportAdapterHandle, IN NDIS_HANDLE WrapperConfigurationContext ) // Standard 'MiniportInitialize' routine called by NDIS to initialize a // new WAN adapter. See DDK doc. The driver will receive no requests // until this initialization has completed. // { NDIS_STATUS status; ADAPTERCB* pAdapter; TRACE( TL_I, TM_Init, ( "LmpInit" ) ); status = *OpenErrorStatus = NDIS_STATUS_SUCCESS; // Find the medium index in the array of media, looking for the only one // we support, 'NdisMediumCoWan'. // { UINT i; for (i = 0; i < MediumArraySize; ++i) { if (MediumArray[ i ] == NdisMediumCoWan) { break; } } if (i >= MediumArraySize) { TRACE( TL_A, TM_Init, ( "medium?" ) ); return NDIS_STATUS_FAILURE; } *SelectedMediumIndex = i; } // Allocate and zero a control block for the new adapter. // pAdapter = ALLOC_NONPAGED( sizeof(*pAdapter), MTAG_ADAPTERCB ); TRACE( TL_N, TM_Init, ( "Acb=$%p", pAdapter ) ); if (!pAdapter) { return NDIS_STATUS_RESOURCES; } NdisZeroMemory( pAdapter, sizeof(*pAdapter) ); // The adapter control block address is recorded in 'g_pDebugAdapter' as a // debugging aid only. This global is not to be read by any code. // g_pDebugAdapter = pAdapter; // Set a marker for easier memory dump browsing and future assertions. // pAdapter->ulTag = MTAG_ADAPTERCB; // Save the NDIS handle associated with this adapter for use in future // NdisXxx calls. // pAdapter->MiniportAdapterHandle = MiniportAdapterHandle; // Initialize the list of active tunnels and it's lock. // InitializeListHead( &pAdapter->listTunnels ); NdisAllocateSpinLock( &pAdapter->lockTunnels ); // Copy default NDISWAN information. Some of these are updated below. // NdisMoveMemory( &pAdapter->info, &g_infoDefaults, sizeof(pAdapter->info) ); pAdapter->info.MaxFrameSize = 1400; do { TDIXMEDIATYPE tmt; L2TPROLE role; USHORT usLlistDepth; BOOLEAN fIgnoreFramingMismatch; BOOLEAN fExclusiveTunnels; HOSTROUTEEXISTS hre; BOOLEAN fDisableUdpXsums; // Read this adapter's registry settings. // status = GetRegistrySettings( WrapperConfigurationContext, &pAdapter->usMaxVcs, &tmt, &role, &pAdapter->ulMaxSendTimeoutMs, &pAdapter->ulInitialSendTimeoutMs, &pAdapter->ulMaxRetransmits, &pAdapter->ulHelloMs, &pAdapter->ulMaxAckDelayMs, &pAdapter->sMaxOutOfOrder, &pAdapter->usControlReceiveWindow, &pAdapter->usPayloadReceiveWindow, &pAdapter->info.MaxSendWindow, &usLlistDepth, &pAdapter->pszHostName, &pAdapter->pszPassword, &fIgnoreFramingMismatch, &fExclusiveTunnels, &hre, &fDisableUdpXsums, &pAdapter->pszDriverDesc ); if (status != NDIS_STATUS_SUCCESS) { // Set 'usMaxVcs' to 0 as an indication to FreeAdapter that the // lookaside lists and pools were not initialized. // pAdapter->usMaxVcs = 0; break; } // Convert the outgoing call role and mismatch flags to the equivalent // control block flag settings. // if (role == LR_Lac) { pAdapter->ulFlags |= ACBF_OutgoingRoleLac; } if (fIgnoreFramingMismatch) { pAdapter->ulFlags |= ACBF_IgnoreFramingMismatch; } if (fExclusiveTunnels) { pAdapter->ulFlags |= ACBF_ExclusiveTunnels; } // Initialize our framing and bearer capability bit masks. NDISWAN // supports only synchronous framing. Until we add the full LAC // support, we have no bearer capabilities for both the LAC and LNS // roles. // pAdapter->ulFramingCaps = FBM_Sync; pAdapter->ulBearerCaps = 0; // Initialize lookaside lists, buffer pools, and packet pool. On NT, // lookaside depths are optimized by the system based on usage // regardless of the depth set, but choose something reasonable // anyway. // { if (pAdapter->usMaxVcs < usLlistDepth) { usLlistDepth = pAdapter->usMaxVcs; } NdisInitializeNPagedLookasideList( &pAdapter->llistWorkItems, NULL, NULL, 0, sizeof(NDIS_WORK_ITEM), MTAG_WORKITEM, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistTimerQItems, NULL, NULL, 0, sizeof(TIMERQITEM), MTAG_TIMERQITEM, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistControlSents, NULL, NULL, 0, sizeof(CONTROLSENT), MTAG_CTRLSENT, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistPayloadSents, NULL, NULL, 0, sizeof(PAYLOADSENT), MTAG_PAYLSENT, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistTunnelWorks, NULL, NULL, 0, sizeof(TUNNELWORK), MTAG_TUNNELWORK, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistControlMsgInfos, NULL, NULL, 0, sizeof(CONTROLMSGINFO), MTAG_CTRLMSGINFO, usLlistDepth ); #if LLISTALL NdisInitializeNPagedLookasideList( &pAdapter->llistTunnels, NULL, NULL, 0, sizeof(TUNNELCB), MTAG_TUNNELCB, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistVcs, NULL, NULL, 0, sizeof(VCCB), MTAG_VCCB, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistTimerQs, NULL, NULL, 0, sizeof(TIMERQ), MTAG_TIMERQ, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistControlReceiveds, NULL, NULL, 0, sizeof(CONTROLRECEIVED), MTAG_CTRLRECD,, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistPayloadReceiveds, NULL, NULL, 0, sizeof(PAYLOADRECEIVED), MTAG_PAYLRECD, usLlistDepth ); NdisInitializeNPagedLookasideList( &pAdapter->llistInCallSetups, NULL, NULL, 0, sizeof(INCALLSETUP), MTAG_INCALL, usLlistDepth ); #endif InitBufferPool( &pAdapter->poolFrameBuffers, L2TP_FrameBufferSize + ((tmt == TMT_RawIp) ? IpFixedHeaderSize : 0), 0, 10, 0, TRUE, MTAG_FBUFPOOL ); InitBufferPool( &pAdapter->poolHeaderBuffers, L2TP_HeaderBufferSize, 0, 20, 0, TRUE, MTAG_HBUFPOOL ); InitPacketPool( &pAdapter->poolPackets, 0, 0, 30, 0, MTAG_PACKETPOOL ); } // Initialize the TDI extension context. // TdixInitialize( tmt, hre, ((fDisableUdpXsums) ? TDIXF_DisableUdpXsums : 0), L2tpReceive, &pAdapter->poolFrameBuffers, &pAdapter->tdix ); // Allocate and zero the VC control block address array. // { ULONG ulSize; ulSize = pAdapter->usMaxVcs * sizeof(VCCB*); pAdapter->ppVcs = ALLOC_NONPAGED( ulSize, MTAG_VCTABLE ); TRACE( TL_V, TM_Init, ( "VcTable=$%p", pAdapter->ppVcs ) ); if (!pAdapter->ppVcs) { status = NDIS_STATUS_RESOURCES; break; } NdisZeroMemory( pAdapter->ppVcs, ulSize ); // Allocate the lock that guards the table. // NdisAllocateSpinLock( &pAdapter->lockVcs ); // At this point, all VC slots in the table are available. // pAdapter->lAvailableVcSlots = (LONG )pAdapter->usMaxVcs; // Set the initial value of the termination call ID counter. See // GetNextTerminationCallId. // pAdapter->usNextTerminationCallId = pAdapter->usMaxVcs + 1; } // Inform NDIS of the attributes of our adapter. Set the // 'MiniportAdapterContext' returned to us by NDIS when it calls our // handlers to the address of our adapter control block. Turn off // hardware oriented timeouts. // NdisMSetAttributesEx( MiniportAdapterHandle, (NDIS_HANDLE)pAdapter, (UINT)-1, NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT, NdisInterfaceInternal ); // Register the address family of our call manager with NDIS for the // newly bound adapter. We use the mini-port form of // RegisterAddressFamily instead of the protocol form, though that // would also work. With the protocol form, our internal call manager // would have to go thru NDIS to talk to the mini-port instead of just // calling directly. Since the L2TP call manager is not likely to be // useful with anything but the L2TP mini-port, this would be a waste. // The mini-port form also causes the call manager VC context to // automatically map to the mini-port VC context, which is exactly // what we want. // // NDIS notifies all call manager clients of the new family we // register. The TAPI proxy is the only client expected to be // interested. NDISWAN will receive the notification, but ignore it // and wait for the TAPI proxy to notify it of the proxied version. // { NDIS_CALL_MANAGER_CHARACTERISTICS ncmc; CO_ADDRESS_FAMILY family; NdisZeroMemory( &family, sizeof(family) ); family.MajorVersion = NDIS_MajorVersion; family.MinorVersion = NDIS_MinorVersion; family.AddressFamily = CO_ADDRESS_FAMILY_TAPI_PROXY; NdisZeroMemory( &ncmc, sizeof(ncmc) ); ncmc.MajorVersion = NDIS_MajorVersion; ncmc.MinorVersion = NDIS_MinorVersion; ncmc.CmCreateVcHandler = LcmCmCreateVc; ncmc.CmDeleteVcHandler = LcmCmDeleteVc; ncmc.CmOpenAfHandler = LcmCmOpenAf; ncmc.CmCloseAfHandler = LcmCmCloseAf; ncmc.CmRegisterSapHandler = LcmCmRegisterSap; ncmc.CmDeregisterSapHandler = LcmCmDeregisterSap; ncmc.CmMakeCallHandler = LcmCmMakeCall; ncmc.CmCloseCallHandler = LcmCmCloseCall; ncmc.CmIncomingCallCompleteHandler = LcmCmIncomingCallComplete; // no CmAddPartyHandler // no CmDropPartyHandler ncmc.CmActivateVcCompleteHandler = LcmCmActivateVcComplete; ncmc.CmDeactivateVcCompleteHandler = LcmCmDeactivateVcComplete; ncmc.CmModifyCallQoSHandler = LcmCmModifyCallQoS; ncmc.CmRequestHandler = LcmCmRequest; // no CmRequestCompleteHandler TRACE( TL_I, TM_Cm, ( "NdisMCmRegAf" ) ); status = NdisMCmRegisterAddressFamily( MiniportAdapterHandle, &family, &ncmc, sizeof(ncmc) ); TRACE( TL_I, TM_Cm, ( "NdisMCmRegAf=$%x", status ) ); } } while (FALSE); if (status == NDIS_STATUS_SUCCESS) { // Add a reference that will eventually be removed by an NDIS call to // the LmpHalt handler. // ReferenceAdapter( pAdapter ); } else { // Failed, so undo whatever portion succeeded. // if (pAdapter) { FreeAdapter( pAdapter ); } } TRACE( TL_V, TM_Init, ( "LmpInit=$%08x", status ) ); return status; } VOID LmpHalt( IN NDIS_HANDLE MiniportAdapterContext ) // Standard 'MiniportHalt' routine called by NDIS to deallocate all // resources attached to the adapter. NDIS does not make any other calls // for this mini-port adapter during or after this call. NDIS will not // call this routine when packets indicated as received have not been // returned, or when any VC is created and known to NDIS. Runs at PASSIVE // IRQL. // { ADAPTERCB* pAdapter; TRACE( TL_I, TM_Mp, ( "LmpHalt" ) ); pAdapter = (ADAPTERCB* )MiniportAdapterContext; if (!pAdapter || pAdapter->ulTag != MTAG_ADAPTERCB) { ASSERT( !"Atag?" ); return; } // Don't allow the halt to complete before all timers have completed as // this can result in a 0xC7 bugcheck if the driver is immediately // unloaded. All timers should be in the process of terminating before // NDIS calls this handler, so this should occur very quickly. // while (pAdapter->ulTimers) { TRACE( TL_A, TM_Mp, ( "LmpHalt timers=%d", pAdapter->ulTimers ) ); WPLOG( LL_A, LM_Mp, ( "LmpHalt timers=%d", pAdapter->ulTimers ) ); NdisMSleep( 100000 ); } DereferenceAdapter( pAdapter ); TRACE( TL_V, TM_Mp, ( "LmpHalt done" ) ); } NDIS_STATUS LmpReset( OUT PBOOLEAN AddressingReset, IN NDIS_HANDLE MiniportAdapterContext ) // Standard 'MiniportReset' routine called by NDIS to reset the driver's // software state. // { TRACE( TL_I, TM_Mp, ( "LmpReset" ) ); return NDIS_STATUS_NOT_RESETTABLE; } VOID LmpReturnPacket( IN NDIS_HANDLE MiniportAdapterContext, IN PNDIS_PACKET Packet ) // Standard 'MiniportReturnPacket' routine called by NDIS when a packet // used to indicate a receive has been released by the driver above. // { VCCB* pVc; CHAR* pBuffer; ADAPTERCB* pAdapter; NDIS_BUFFER* pTrimmedBuffer; PACKETHEAD* pHead; PACKETPOOL* pPool; TRACE( TL_N, TM_Mp, ( "LmpReturnPacket" ) ); // Unpack the context information we stashed earlier. // pHead = *((PACKETHEAD** )(&Packet->MiniportReserved[ 0 ])); pBuffer = *((CHAR** )(&Packet->MiniportReserved[ sizeof(VOID*) ])); // Find the adapter from the PACKETHEAD address. // pPool = PacketPoolFromPacketHead( pHead ); pAdapter = CONTAINING_RECORD( pPool, ADAPTERCB, poolPackets ); ASSERT( pAdapter->ulTag == MTAG_ADAPTERCB ); // Free the descriptor created by NdisCopyBuffer. // NdisUnchainBufferAtFront( Packet, &pTrimmedBuffer ); if (pTrimmedBuffer) { extern ULONG g_ulNdisFreeBuffers; NdisFreeBuffer( pTrimmedBuffer ); NdisInterlockedIncrement( &g_ulNdisFreeBuffers ); } // Free the buffer and packet back to the pools. // FreeBufferToPool( &pAdapter->poolFrameBuffers, pBuffer, TRUE ); FreePacketToPool( &pAdapter->poolPackets, pHead, TRUE ); NdisInterlockedIncrement( &g_lPacketsReturned ); TRACE( TL_V, TM_Mp, ( "LmpReturnPacket done" ) ); } NDIS_STATUS LmpCoActivateVc( IN NDIS_HANDLE MiniportVcContext, IN OUT PCO_CALL_PARAMETERS CallParameters ) // Standard 'MiniportCoActivateVc' routine called by NDIS in response to a // protocol's request to activate a virtual circuit. // { ASSERT( !"LmpCoActVc?" ); return NDIS_STATUS_SUCCESS; } NDIS_STATUS LmpCoDeactivateVc( IN NDIS_HANDLE MiniportVcContext ) // Standard 'MiniportCoDeactivateVc' routine called by NDIS in response to // a protocol's request to de-activate a virtual circuit. // { ASSERT( !"LmpCoDeactVc?" ); return NDIS_STATUS_SUCCESS; } VOID LmpCoSendPackets( IN NDIS_HANDLE MiniportVcContext, IN PPNDIS_PACKET PacketArray, IN UINT NumberOfPackets ) // Standard 'MiniportCoDeactivateVc' routine called by NDIS in response to // a protocol's request to send packets on a virtual circuit. // { UINT i; NDIS_STATUS status; NDIS_PACKET** ppPacket; VCCB* pVc; TRACE( TL_N, TM_Send, ( "LmpCoSendPackets(%d)", NumberOfPackets ) ); pVc = (VCCB* )MiniportVcContext; ASSERT( pVc->ulTag == MTAG_VCCB ); for (i = 0, ppPacket = PacketArray; i < NumberOfPackets; ++i, ++ppPacket) { NDIS_PACKET* pPacket = *ppPacket; // SendPayload sends the packet and eventually calls // NdisMCoSendComplete to notify caller of the result. // NDIS_SET_PACKET_STATUS( pPacket, NDIS_STATUS_PENDING ); SendPayload( pVc, pPacket ); } TRACE( TL_V, TM_Send, ( "LmpCoSendPackets done" ) ); } NDIS_STATUS LmpCoRequest( IN NDIS_HANDLE MiniportAdapterContext, IN NDIS_HANDLE MiniportVcContext, IN OUT PNDIS_REQUEST NdisRequest ) // Standard 'MiniportCoRequestHandler' routine called by NDIS in response // to a protocol's request information from the mini-port. Unlike the // Query/SetInformation handlers that this routine obsoletes, requests are // not serialized. // { ADAPTERCB* pAdapter; VCCB* pVc; NDIS_STATUS status; TRACE( TL_N, TM_Mp, ( "LmpCoReq" ) ); pAdapter = (ADAPTERCB* )MiniportAdapterContext; if (pAdapter->ulTag != MTAG_ADAPTERCB) { ASSERT( !"Atag?" ); return NDIS_STATUS_INVALID_DATA; } pVc = (VCCB* )MiniportVcContext; if (pVc && pVc->ulTag != MTAG_VCCB) { ASSERT( !"Vtag?" ); return NDIS_STATUS_INVALID_DATA; } switch (NdisRequest->RequestType) { case NdisRequestQueryInformation: { status = QueryInformation( pAdapter, pVc, NdisRequest->DATA.QUERY_INFORMATION.Oid, NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer, NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength, &NdisRequest->DATA.QUERY_INFORMATION.BytesWritten, &NdisRequest->DATA.QUERY_INFORMATION.BytesNeeded ); break; } case NdisRequestSetInformation: { status = SetInformation( pAdapter, pVc, NdisRequest->DATA.SET_INFORMATION.Oid, NdisRequest->DATA.SET_INFORMATION.InformationBuffer, NdisRequest->DATA.SET_INFORMATION.InformationBufferLength, &NdisRequest->DATA.SET_INFORMATION.BytesRead, &NdisRequest->DATA.SET_INFORMATION.BytesNeeded ); break; } default: { status = NDIS_STATUS_NOT_SUPPORTED; TRACE( TL_A, TM_Mp, ( "type=%d?", NdisRequest->RequestType ) ); break; } } TRACE( TL_V, TM_Mp, ( "LmpCoReq=$%x", status ) ); return status; } //----------------------------------------------------------------------------- // Mini-port utility routines (alphabetically) // Some are used externally //----------------------------------------------------------------------------- VOID DereferenceAdapter( IN ADAPTERCB* pAdapter ) // Removes a reference from the adapter control block 'pAdapter', and when // frees the adapter resources when the last reference is removed. // { LONG lRef; lRef = NdisInterlockedDecrement( &pAdapter->lRef ); TRACE( TL_N, TM_Ref, ( "DerefA to %d", lRef ) ); ASSERT( lRef >= 0 ); if (lRef == 0) { FreeAdapter( pAdapter ); } } VOID FreeAdapter( IN ADAPTERCB* pAdapter ) // Frees all resources allocated for adapter 'pAdapter', including // 'pAdapter' itself. // { BOOLEAN fSuccess; ASSERT( IsListEmpty( &pAdapter->listTunnels ) ); if (pAdapter->ppVcs) { FREE_NONPAGED( pAdapter->ppVcs ); } // Setting 'usMaxVcs' to 0 is LmpInitialize's way of telling us that the // lookaside lists and pools were not initialized. // if (pAdapter->usMaxVcs) { NdisDeleteNPagedLookasideList( &pAdapter->llistWorkItems ); NdisDeleteNPagedLookasideList( &pAdapter->llistTimerQItems ); NdisDeleteNPagedLookasideList( &pAdapter->llistControlSents ); NdisDeleteNPagedLookasideList( &pAdapter->llistPayloadSents ); NdisDeleteNPagedLookasideList( &pAdapter->llistTunnelWorks ); NdisDeleteNPagedLookasideList( &pAdapter->llistControlMsgInfos ); #if LLISTALL NdisDeleteNPagedLookasideList( &pAdapter->llistTunnels ); NdisDeleteNPagedLookasideList( &pAdapter->llistVcs ); NdisDeleteNPagedLookasideList( &pAdapter->llistTimerQs ); NdisDeleteNPagedLookasideList( &pAdapter->llistControlReceiveds ); NdisDeleteNPagedLookasideList( &pAdapter->llistPayloadReceiveds ); NdisDeleteNPagedLookasideList( &pAdapter->llistInCallSetups ); #endif fSuccess = FreeBufferPool( &pAdapter->poolFrameBuffers ); ASSERT( fSuccess ); fSuccess = FreeBufferPool( &pAdapter->poolHeaderBuffers ); ASSERT( fSuccess ); fSuccess = FreePacketPool( &pAdapter->poolPackets ); ASSERT( fSuccess ); } if (pAdapter->pszPassword) { FREE_NONPAGED( pAdapter->pszPassword ); } if (pAdapter->pszDriverDesc) { FREE_NONPAGED( pAdapter->pszDriverDesc ); } if (pAdapter->pszHostName) { FREE_NONPAGED( pAdapter->pszHostName ); } pAdapter->ulTag = MTAG_FREED; FREE_NONPAGED( pAdapter ); } NDIS_STATUS GetRegistrySettings( IN NDIS_HANDLE WrapperConfigurationContext, OUT USHORT* pusMaxVcs, OUT TDIXMEDIATYPE* pMediaType, OUT L2TPROLE* pOutgoingRole, OUT ULONG* pulMaxSendTimeoutMs, OUT ULONG* pulInitialSendTimeoutMs, OUT ULONG* pulMaxRetransmits, OUT ULONG* pulHelloMs, OUT ULONG* pulMaxAckDelayMs, OUT SHORT* psMaxOutOfOrder, OUT USHORT* pusControlReceiveWindow, OUT USHORT* pusPayloadReceiveWindow, OUT ULONG* pulPayloadSendWindow, OUT USHORT* pusLlistDepth, OUT CHAR** ppszHostName, OUT CHAR** ppszPassword, OUT BOOLEAN* pfIgnoreFramingMismatch, OUT BOOLEAN* pfExclusiveTunnels, OUT HOSTROUTEEXISTS* phre, OUT BOOLEAN* pfDisableUdpXsums, OUT WCHAR** ppszDriverDesc ) // Read this mini-port's registry settings into caller's output variables. // 'WrapperConfigurationContext' is the handle to passed to // MiniportInitialize. // { NDIS_STATUS status; NDIS_HANDLE hCfg; NDIS_CONFIGURATION_PARAMETER* pncp; NdisOpenConfiguration( &status, &hCfg, WrapperConfigurationContext ); if (status != NDIS_STATUS_SUCCESS) return status; do { // (recommended) The number of VCs we must be able to provide. // { NDIS_STRING nstr = NDIS_STRING_CONST( "MaxWanEndpoints" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pusMaxVcs = (USHORT )pncp->ParameterData.IntegerData; // Make sure it's a valid value. The implicit upper bound // imposed by the protocol's Tunnel-Id/Call-ID sizes is 65535. // Settings above 1000 are not recommended, but will work if // huge amounts of memory and bandwidth are available. // if (*pusMaxVcs < 1) { status = NDIS_STATUS_INVALID_DATA; break; } } else { *pusMaxVcs = 1000; status = NDIS_STATUS_SUCCESS; } } // (recommended) The media type to run L2TP over. // { NDIS_STRING nstr = NDIS_STRING_CONST( "VpnMediaType" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pMediaType = (TDIXMEDIATYPE )pncp->ParameterData.IntegerData; // Make sure it's a valid type. // if (*pMediaType != TMT_Udp && *pMediaType != TMT_RawIp) { status = NDIS_STATUS_INVALID_DATA; break; } } else { // No media type in registry. Default to UDP. // *pMediaType = TMT_Udp; status = NDIS_STATUS_SUCCESS; } } // (optional) The maximum send timeout in milliseconds. // { NDIS_STRING nstr = NDIS_STRING_CONST( "MaxSendTimeoutMs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pulMaxSendTimeoutMs = pncp->ParameterData.IntegerData; // Make sure it's a valid value. // if (*pulMaxSendTimeoutMs == 0) { *pulMaxSendTimeoutMs = 0x7FFFFFFF; } } else { // No value in registry. Set a reasonable default. // *pulMaxSendTimeoutMs = L2TP_DefaultMaxSendTimeoutMs; status = NDIS_STATUS_SUCCESS; } } // (optional) The initial send timeout in milliseconds. // { NDIS_STRING nstr = NDIS_STRING_CONST( "InitialSendTimeoutMs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pulInitialSendTimeoutMs = pncp->ParameterData.IntegerData; // Make sure it's a valid value. // if (*pulInitialSendTimeoutMs == 0) { *pulInitialSendTimeoutMs = 0x7FFFFFFF; } if (*pulInitialSendTimeoutMs > *pulMaxSendTimeoutMs) *pulInitialSendTimeoutMs = *pulMaxSendTimeoutMs; } else { // No value in registry. Set a reasonable default. // *pulInitialSendTimeoutMs = L2TP_DefaultSendTimeoutMs; status = NDIS_STATUS_SUCCESS; } } // (optional) The maximum number of control message retransmissions // before the tunnel is reset. // { NDIS_STRING nstr = NDIS_STRING_CONST( "MaxRetransmits" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pulMaxRetransmits = pncp->ParameterData.IntegerData; } else { // No value in registry. Set a reasonable default. // *pulMaxRetransmits = L2TP_DefaultMaxRetransmits; status = NDIS_STATUS_SUCCESS; } } // (optional) The control Hello timeout in milliseconds. // { NDIS_STRING nstr = NDIS_STRING_CONST( "HelloMs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pulHelloMs = pncp->ParameterData.IntegerData; } else { // No value in registry. Set a reasonable default. // *pulHelloMs = L2TP_HelloMs; status = STATUS_SUCCESS; } } // (optional) The maximum piggyback delay in milliseconds before // sending a zero payload acknowledgement. // { NDIS_STRING nstr = NDIS_STRING_CONST( "MaxAckDelayMs" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pulMaxAckDelayMs = pncp->ParameterData.IntegerData; } else { // No value in registry. Set a reasonable default. // *pulMaxAckDelayMs = L2TP_MaxAckDelay; status = NDIS_STATUS_SUCCESS; } } // (optional) The maximum number of out-of-order packets to queue. // { NDIS_STRING nstr = NDIS_STRING_CONST( "MaxOutOfOrder" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *psMaxOutOfOrder = (SHORT )pncp->ParameterData.IntegerData; // Make sure it's not negative and within 1/4 of the possible // sequence values to avoid aliasing. Zero effectively // disables out of order handling. // if (*psMaxOutOfOrder < 0 || *psMaxOutOfOrder > 0x4000) { status = NDIS_STATUS_INVALID_DATA; break; } } else { // No value in registry. Set a reasonable default. // *psMaxOutOfOrder = 100; status = NDIS_STATUS_SUCCESS; } } // (optional) The role (LNS or LAC) that the adapter will play in // outgoing calls. The role played for incoming calls is // determined by the role the peer plays in his call // request. // { NDIS_STRING nstr = NDIS_STRING_CONST( "OutgoingRole" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pOutgoingRole = (L2TPROLE )pncp->ParameterData.IntegerData; // Make sure it's a valid role. // if (*pOutgoingRole != LR_Lac && *pOutgoingRole != LR_Lns) { status = NDIS_STATUS_INVALID_DATA; break; } } else { // No role in registry. Default to LAC. // *pOutgoingRole = LR_Lac; status = NDIS_STATUS_SUCCESS; } } // (optional) The control receive window sent to peer to indicate how // many sent control messages peer may have outstanding. // { NDIS_STRING nstr = NDIS_STRING_CONST( "ControlReceiveWindow" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pusControlReceiveWindow = (USHORT )pncp->ParameterData.IntegerData; } else { // No setting in registry. Set a reasonable default. // *pusControlReceiveWindow = 8; status = NDIS_STATUS_SUCCESS; } } // (optional) The payload receive window sent to peer to indicate how // many send payloads peer may have outstanding on any one // call. A value of 0 disables all Ns/Nr sequencing on the // payload channel for locally requested calls. // { NDIS_STRING nstr = NDIS_STRING_CONST( "PayloadReceiveWindow" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pusPayloadReceiveWindow = (USHORT )pncp->ParameterData.IntegerData; } else { // No setting in registry. Set a reasonable default. // // Note: Default changed to 0 (off) from 16 due to performance // study that shows significantly better results without // flow control, presumably due to interference with // higher level timers. // *pusPayloadReceiveWindow = 0; status = NDIS_STATUS_SUCCESS; } } // (optional) The maximum payload send window size reported to // NDISWAN. Peer may set the actual send window higher or // lower, but if higher this is the actual maximum. // { NDIS_STRING nstr = NDIS_STRING_CONST( "PayloadSendWindow" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pulPayloadSendWindow = (ULONG )pncp->ParameterData.IntegerData; } else { // No setting in registry. Set a reasonable default. // *pulPayloadSendWindow = 16; status = NDIS_STATUS_SUCCESS; } } // (optional) The lookaside list depth ceiling, where higher values // allow this driver to consume more non-paged pool in // return for performance gain at high volumes. Setting // this value above 'MaxVcs' has no effect. // { NDIS_STRING nstr = NDIS_STRING_CONST( "LookasideDepth" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pusLlistDepth = (USHORT )pncp->ParameterData.IntegerData; } else { // No setting in registry. Set a reasonable default. // *pusLlistDepth = 30; status = NDIS_STATUS_SUCCESS; } } // The host name passed to peer and used as the base of the // call serial number. // Host name is required as this is used in hostname MUST AVP. { NDIS_STRING nstr = NDIS_STRING_CONST( "HostName" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterString ); if (status == NDIS_STATUS_SUCCESS) { *ppszHostName = StrDupNdisStringToA( &pncp->ParameterData.StringData ); if(!*ppszHostName) { status = NDIS_STATUS_RESOURCES; } } else { // No setting in registry, so use a default. // *ppszHostName = GetFullHostNameFromRegistry(); if (!*ppszHostName) { *ppszHostName = StrDupSized("NONE", sizeof("NONE"), 0); } if(*ppszHostName) { status = NDIS_STATUS_SUCCESS; } else { status = NDIS_STATUS_RESOURCES; } } if(status != NDIS_STATUS_SUCCESS) { break; } } // (optional) The single password shared with peer for use in // verifying peer's identity. If specified, authentication // of peer is required, and if not, authentication is not // provided. // { NDIS_STRING nstr = NDIS_STRING_CONST( "Password" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterString ); if (status == NDIS_STATUS_SUCCESS) { *ppszPassword = StrDupNdisStringToA( &pncp->ParameterData.StringData ); } else { // No setting in registry...and no default. // *ppszPassword = NULL; status = NDIS_STATUS_SUCCESS; } } // (optional) Buggy peer hedge flag to ignore framing mismatches. // { NDIS_STRING nstr = NDIS_STRING_CONST( "IgnoreFramingMismatch" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pfIgnoreFramingMismatch = (BOOLEAN )!!(pncp->ParameterData.IntegerData); } else { // No value in registry. Set a reasonable default. // *pfIgnoreFramingMismatch = TRUE; status = NDIS_STATUS_SUCCESS; } } // (optional) Flag indicating whether, by default, separate tunnels // are to be created for each outgoing call even if a // tunnel already exists to the same peer. This setting // can be overridden via L2TP-specific call parameters. // { NDIS_STRING nstr = NDIS_STRING_CONST( "ExclusiveTunnels" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pfExclusiveTunnels = (BOOLEAN )!!(pncp->ParameterData.IntegerData); } else { // No value in registry. Set a default. // *pfExclusiveTunnels = FALSE; status = NDIS_STATUS_SUCCESS; } } // (optional) Flag indicating whether routes created outside this // driver may be used as L2TP host routes. If the flag is // not set, the pre-existing host routes will cause the // tunnel to close. // { NDIS_STRING nstr = NDIS_STRING_CONST( "UseExistingRoutes" ); BOOLEAN fDefault; fDefault = FALSE; NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *phre = (HOSTROUTEEXISTS )pncp->ParameterData.IntegerData; if (*phre != HRE_Use && *phre != HRE_Fail #if ROUTEWITHREF && *phre != HRE_Reference #endif ) { // Bad value in registry. // fDefault = TRUE; } } else { // No value in registry. // status = NDIS_STATUS_SUCCESS; fDefault = TRUE; } if (fDefault) { #if ROUTEWITHREF // Set default to "reference" as this allows simultaneous L2TP // and PPTP connections between the same two peers without // host route trashing. // *phre = HRE_Reference; #else // Set default to "fail" to prevent L2TP from stomping on a // PPTP host route. // *phre = HRE_Fail; #endif } } // (optional) Flag indicating whether UDP checksums should be disabled // on L2TP payload traffic. // { NDIS_STRING nstr = NDIS_STRING_CONST( "DisableUdpChecksums" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterInteger ); if (status == NDIS_STATUS_SUCCESS) { *pfDisableUdpXsums = (BOOLEAN )!!(pncp->ParameterData.IntegerData); } else { // No value in registry. Set a default. The L2TP draft says // implementation MUST default to "enabled". // *pfDisableUdpXsums = TRUE; status = NDIS_STATUS_SUCCESS; } } // (required) The driver description string, which is reported to TAPI // as the L2TP line name. // { NDIS_STRING nstr = NDIS_STRING_CONST( "DriverDesc" ); NdisReadConfiguration( &status, &pncp, hCfg, &nstr, NdisParameterString ); if (status == NDIS_STATUS_SUCCESS) { *ppszDriverDesc = StrDupNdisString( &pncp->ParameterData.StringData ); } else { // No setting in registry...and no default. // *ppszDriverDesc = NULL; status = NDIS_STATUS_SUCCESS; } } } while (FALSE); NdisCloseConfiguration( hCfg ); TRACE( TL_N, TM_Init, ( "Reg: vcs=%d mt=%d or=%d lld=%d hto=%d ooo=%d mad=%d dx=%d", *pusMaxVcs, *pMediaType, *pOutgoingRole, (ULONG )*pusLlistDepth, *pulHelloMs, (INT )*psMaxOutOfOrder, *pulMaxAckDelayMs, (UINT )*pfDisableUdpXsums ) ); TRACE( TL_N, TM_Init, ( "Reg: mto=%d ito=%d mrt=%d crw=%d prw=%d psw=%d ifm=%d xt=%d xr=%d", *pulMaxSendTimeoutMs, *pulInitialSendTimeoutMs, *pulMaxRetransmits, (UINT )*pusControlReceiveWindow, (UINT )*pusPayloadReceiveWindow, (UINT )*pulPayloadSendWindow, (UINT )*pfIgnoreFramingMismatch, (UINT )*pfExclusiveTunnels, (UINT )*phre)); TRACE( TL_N, TM_Init, ( "Reg: hn=\"%s\" pw=\"%s\"", ((*ppszHostName) ? *ppszHostName : ""), ((*ppszPassword) ? *ppszPassword : "") ) ); return status; } NDIS_STATUS QueryInformation( IN ADAPTERCB* pAdapter, IN VCCB* pVc, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesWritten, OUT PULONG BytesNeeded ) // Handle QueryInformation requests. Arguments are as for the standard // NDIS 'MiniportQueryInformation' handler except this routine does not // count on being serialized with respect to other requests. // { NDIS_STATUS status; ULONG ulInfo; VOID* pInfo; ULONG ulInfoLen; status = NDIS_STATUS_SUCCESS; // The cases in this switch statement find or create a buffer containing // the requested information and point 'pInfo' at it, noting it's length // in 'ulInfoLen'. Since many of the OIDs return a ULONG, a 'ulInfo' // buffer is set up as the default. // ulInfo = 0; pInfo = &ulInfo; ulInfoLen = sizeof(ulInfo); switch (Oid) { case OID_GEN_MAXIMUM_LOOKAHEAD: { // Report the maximum number of bytes we can always provide as // lookahead data on receive indications. We always indicate full // packets so this is the same as the receive block size. And // since we always allocate enough for a full packet, the receive // block size is the same as the frame size. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_MAXIMUM_LOOKAHEAD)" ) ); ulInfo = L2TP_MaxFrameSize; break; } case OID_GEN_MAC_OPTIONS: { // Report a bitmask defining optional properties of the driver. // // NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA promises that our receive // buffer is not on a device-specific card. // // NDIS_MAC_OPTION_TRANSFERS_NOT_PEND promises we won't return // NDIS_STATUS_PENDING from our TransferData handler which is true // since we don't have one. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_MAC_OPTIONS)" ) ); ulInfo = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | NDIS_MAC_OPTION_TRANSFERS_NOT_PEND; break; } case OID_WAN_MEDIUM_SUBTYPE: { // Report the media subtype we support. NDISWAN may use this in // the future (doesn't now) to provide framing differences for // different media. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_MEDIUM_SUBTYPE)" ) ); ulInfo = NdisWanMediumL2TP; break; } case OID_WAN_CO_GET_INFO: { // Report the capabilities of the adapter. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_INFO)" ) ); pInfo = &pAdapter->info; ulInfoLen = sizeof(NDIS_WAN_CO_INFO); break; } case OID_WAN_CO_GET_LINK_INFO: { // Report the current state of the link. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_LINK_INFO)" ) ); if (!pVc) { return NDIS_STATUS_INVALID_DATA; } pInfo = &pVc->linkinfo; ulInfoLen = sizeof(NDIS_WAN_CO_GET_LINK_INFO); break; } case OID_WAN_CO_GET_COMP_INFO: { // Report the type of compression we provide, which is none. // TRACE( TL_N, TM_Mp, ( "QInfo(OID_WAN_CO_GET_COMP_INFO)" ) ); status = NDIS_STATUS_NOT_SUPPORTED; ulInfoLen = 0; break; } case OID_WAN_CO_GET_STATS_INFO: { // Because L2TP doesn't do compression, NDISWAN will use it's own // statistics and not query ours. // ASSERT( !"OID_WAN_CO_GET_STATS_INFO?" ); status = NDIS_STATUS_NOT_SUPPORTED; ulInfoLen = 0; break; } case OID_GEN_SUPPORTED_LIST: { static ULONG aulSupportedOids[] = { OID_GEN_SUPPORTED_LIST, OID_GEN_MAXIMUM_LOOKAHEAD, OID_GEN_MAC_OPTIONS, OID_WAN_MEDIUM_SUBTYPE, OID_WAN_CO_GET_INFO, OID_WAN_CO_GET_LINK_INFO, OID_WAN_CO_SET_LINK_INFO, OID_CO_TAPI_CM_CAPS, OID_CO_TAPI_LINE_CAPS, OID_CO_TAPI_ADDRESS_CAPS, OID_CO_TAPI_GET_CALL_DIAGNOSTICS }; TRACE( TL_N, TM_Mp, ( "QInfo(OID_GEN_SUPPORTED_LIST)" ) ); pInfo = aulSupportedOids; ulInfoLen = sizeof(aulSupportedOids); break; } #if 0 // These OIDs are mandatory according to current doc, but since // NDISWAN never requests them they are omitted. // case OID_GEN_HARDWARE_STATUS: case OID_GEN_MEDIA_SUPPORTED: case OID_GEN_MEDIA_IN_USE: case OID_GEN_MEDIA_IN_USE: case OID_GEN_MAXIMUM_FRAME_SIZE: case OID_GEN_LINK_SPEED: case OID_GEN_TRANSMIT_BUFFER_SPACE: case OID_GEN_RECEIVE_BUFFER_SPACE: case OID_GEN_TRANSMIT_BLOCK_SIZE: case OID_GEN_RECEIVE_BLOCK_SIZE: case OID_GEN_VENDOR_ID: case OID_GEN_VENDOR_DESCRIPTION: case OID_GEN_VENDOR_DRIVER_VERSION: case OID_GEN_CURRENT_PACKET_FILTER: case OID_GEN_CURRENT_LOOKAHEAD: case OID_GEN_DRIVER_VERSION: case OID_GEN_MAXIMUM_TOTAL_SIZE: case OID_GEN_MAC_OPTIONS: case OID_GEN_MEDIA_CONNECT_STATUS: case OID_GEN_MAXIMUM_SEND_PACKETS: case OID_WAN_PERMANENT_ADDRESS: case OID_WAN_CURRENT_ADDRESS: case OID_WAN_QUALITY_OF_SERVICE: case OID_WAN_LINE_COUNT: #endif default: { TRACE( TL_A, TM_Mp, ( "Q-OID=$%08x?", Oid ) ); status = NDIS_STATUS_NOT_SUPPORTED; ulInfoLen = 0; break; } } if (ulInfoLen > InformationBufferLength) { // Caller's buffer is too small. Tell him what he needs. // *BytesNeeded = ulInfoLen; status = NDIS_STATUS_INVALID_LENGTH; } else { // Copy the found result to caller's buffer. // if (ulInfoLen > 0) { NdisMoveMemory( InformationBuffer, pInfo, ulInfoLen ); DUMPDW( TL_N, TM_Mp, pInfo, ulInfoLen ); } *BytesNeeded = *BytesWritten = ulInfoLen; } return status; } VOID ReferenceAdapter( IN ADAPTERCB* pAdapter ) // Adds areference to the adapter block, 'pAdapter'. // { LONG lRef; lRef = NdisInterlockedIncrement( &pAdapter->lRef ); TRACE( TL_N, TM_Ref, ( "RefA to %d", lRef ) ); } NDIS_STATUS SetInformation( IN ADAPTERCB* pAdapter, IN VCCB* pVc, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, OUT PULONG BytesRead, OUT PULONG BytesNeeded ) // Handle SetInformation requests. Arguments are as for the standard NDIS // 'MiniportQueryInformation' handler except this routine does not count // on being serialized with respect to other requests. // { NDIS_STATUS status; status = NDIS_STATUS_SUCCESS; switch (Oid) { case OID_WAN_CO_SET_LINK_INFO: { // Read new link state settings. // TRACE( TL_N, TM_Mp, ( "SInfo(OID_WAN_CO_SET_LINK_INFO)" ) ); if (InformationBufferLength < sizeof(NDIS_WAN_CO_SET_LINK_INFO)) { status = NDIS_STATUS_INVALID_LENGTH; *BytesRead = 0; } else { if (!pVc) return NDIS_STATUS_INVALID_DATA; ASSERT( sizeof(pVc->linkinfo) == sizeof(NDIS_WAN_CO_SET_LINK_INFO) ); NdisMoveMemory( &pVc->linkinfo, InformationBuffer, sizeof(pVc->linkinfo) ); DUMPB( TL_N, TM_Mp, &pVc->linkinfo, sizeof(pVc->linkinfo) ); *BytesRead = sizeof(NDIS_WAN_CO_SET_LINK_INFO); } *BytesNeeded = sizeof(NDIS_WAN_CO_SET_LINK_INFO); } break; case OID_WAN_CO_SET_COMP_INFO: { // L2TP doesn't provide compression. // TRACE( TL_N, TM_Mp, ( "SInfo(OID_WAN_CO_SET_COMP_INFO)" ) ); status = NDIS_STATUS_NOT_SUPPORTED; *BytesRead = *BytesNeeded = 0; break; } #if 0 // These OIDs are mandatory according to current doc, but since // NDISWAN never requests them they are omitted. // case OID_GEN_CURRENT_PACKET_FILTER: case OID_GEN_CURRENT_LOOKAHEAD: case OID_GEN_PROTOCOL_OPTIONS: case OID_WAN_PROTOCOL_TYPE: case OID_WAN_HEADER_FORMAT: #endif default: { TRACE( TL_A, TM_Mp, ( "S-OID=$%08x?", Oid ) ); status = NDIS_STATUS_NOT_SUPPORTED; *BytesRead = *BytesNeeded = 0; break; } } return status; }