|
|
// Copyright (c) 1997, Microsoft Corporation, all rights reserved
//
// tdix.c
// RAS L2TP WAN mini-port/call-manager driver
// TDI extensions interface
//
// 01/07/97 Steve Cobb
//
// These routines encapsulate L2TP's usage of TDI, with the intent of
// minimalizing the change required to support other TDI transports in the
// future, e.g. Frame Relay.
//
//
// About ALLOCATEIRPS:
//
// This driver is lower level code than typical TDI client drivers. It has
// locked MDL-mapped input buffers readily available and does not need to
// provide any mapping to user mode client requests on completion. This
// allows a performance gain from allocating and deallocating IRPs directly,
// thus avoiding unnecessary setup in TdiBuildInternalDeviceControlIrp and
// unnecessary APC queuing in IoCompleteRequest. Define ALLOCATEIRPs 1 (in
// sources file) to make this optimization, or define it 0 to use the strictly
// TDI-compliant TdiBuildInternalDeviceControlIrp method.
//
//
// About NDISBUFFERISMDL:
//
// Calls to TdiBuildSendDatagram assume the NDIS_BUFFER can be passed in place
// of an MDL which avoids a pointless copy. If this is not the case, an
// explicit MDL buffer would need to be allocated and caller's buffer copied
// to the MDL buffer before sending. Same issue for TdiBuildReceiveDatagram,
// except of course that the copy would be from the MDL buffer to caller's
// buffer after receiving.
//
//
// About ROUTEWITHREF:
//
// Calls the IP_SET_ROUTEWITHREF IOCTLs rather than the TCP_SET_INFORMATION_EX
// IOCTLs to set up the host route. The referenced route IOCTLs prevent PPTP
// and L2TP from walking on each others routes. This setting provided only as
// a hedge against failure of the ROUTEWITHREF IOCTL. Assuming it works it
// should always be preferable.
//
#include "l2tpp.h"
#include "tdix.tmh"
#define IP_PKTINFO 19 // receive packet information
typedef struct in_pktinfo { ULONG ipi_addr; // destination IPv4 address
UINT ipi_ifindex; // received interface index
} IN_PKTINFO;
// Debug count of errors that should not be happening.
//
ULONG g_ulTdixOpenFailures = 0; ULONG g_ulTdixSendDatagramFailures = 0; ULONG g_ulTdixAddHostRouteFailures = 0; ULONG g_ulTdixDeleteHostRouteFailures = 0; ULONG g_ulTdixOpenCtrlAddrFailures = 0; ULONG g_ulTdixOpenPayloadAddrFailures = 0; ULONG g_ulTdixSetInterfaceFailures = 0; ULONG g_ulTdixConnectAddrFailures = 0; ULONG g_ulTdixAddHostRouteSuccesses = 0; ULONG g_ulTdixDeleteHostRouteSuccesses = 0; ULONG g_ulTdixOpenCtrlAddrSuccesses = 0; ULONG g_ulTdixOpenPayloadAddrSuccesses = 0; ULONG g_ulTdixSetInterfaceSuccesses = 0; ULONG g_ulTdixConnectAddrSuccesses = 0; ULONG g_ulNoBestRoute = 0; NTSTATUS g_statusLastAhrSetRouteFailure = 0; NTSTATUS g_statusLastAhrTcpQueryInfoExFailure = 0; NTSTATUS g_statusLastDhrSetRouteFailure = 0; NTSTATUS g_statusLastDhrTcpQueryInfoExFailure = 0;
#if NDISBUFFERISMDL
#else
#error Additional code to copy NDIS_BUFFER to/from MDL NYI.
#endif
//-----------------------------------------------------------------------------
// Local datatypes
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Local prototypes (alphabetically)
//-----------------------------------------------------------------------------
NTSTATUS TdixConnectAddrInterface( FILE_OBJECT* pFileObj, HANDLE hFileHandle, TDIXROUTE* pTdixRoute );
VOID TdixDisableUdpChecksums( IN FILE_OBJECT* pAddress);
VOID TdixDoClose( TDIXCONTEXT* pTdix);
VOID TdixEnableIpPktInfo( IN FILE_OBJECT* pAddress);
VOID TdixEnableIpHdrIncl( IN FILE_OBJECT* pAddress);
VOID TdixExtractAddress( IN TDIXCONTEXT* pTdix, OUT TDIXRDGINFO* pRdg, IN VOID* pTransportAddress, IN LONG lTransportAddressLen, IN VOID* Options, IN LONG OptionsLength);
NTSTATUS TdixInstallEventHandler( IN FILE_OBJECT* pAddress, IN INT nEventType, IN VOID* pfuncEventHandler, IN VOID* pEventContext );
NTSTATUS TdixOpenIpAddress( IN UNICODE_STRING* puniDevice, IN TDIXIPADDRESS* pTdixAddr, OUT HANDLE* phAddress, OUT FILE_OBJECT** ppFileObject );
NTSTATUS TdixReceiveDatagramComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
NTSTATUS TdixReceiveDatagramHandler( IN PVOID TdiEventContext, IN LONG SourceAddressLength, IN PVOID SourceAddress, IN LONG OptionsLength, IN PVOID Options, IN ULONG ReceiveDatagramFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG* BytesTaken, IN PVOID Tsdu, OUT PIRP* IoRequestPacket );
TDIXROUTE* TdixRouteFromIpAddress( IN TDIXCONTEXT* pTdix, IN ULONG ulIpAddress);
NTSTATUS TdixSendComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
NTSTATUS TdixSendDatagramComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );
//-----------------------------------------------------------------------------
// Interface routines
//-----------------------------------------------------------------------------
VOID TdixInitialize( IN TDIXMEDIATYPE mediatype, IN HOSTROUTEEXISTS hre, IN ULONG ulFlags, IN PTDIXRECEIVE pReceiveHandler, IN BUFFERPOOL* pPoolNdisBuffers, IN OUT TDIXCONTEXT* pTdix )
// Initialize caller's 'pTdix' buffer for future sessions using media type
// 'mediatype', the 'hre' existing host route strategy, and TDIXF_*
// options 'ulFlags'. Caller's receive datagram callback
// 'pReceiveHandler' is called with a buffer allocated from caller's
// buffer pool 'pPoolNdisBuffers'.
//
{ TRACE( TL_N, TM_Tdi, ( "TdixInit" ) );
pTdix->lRef = 0; pTdix->hAddress = NULL; pTdix->pAddress = NULL; pTdix->hRawAddress = NULL; pTdix->pRawAddress = NULL; pTdix->mediatype = mediatype; pTdix->hre = hre; pTdix->ulFlags |= ulFlags; pTdix->ulFlags = 0; InitializeListHead( &pTdix->listRoutes ); NdisAllocateSpinLock( &pTdix->lock ); pTdix->pPoolNdisBuffers = pPoolNdisBuffers; pTdix->pReceiveHandler = pReceiveHandler;
// The 'llistRdg' and 'llistSdg' lookaside lists are initialized at
// TdixOpen.
}
NDIS_STATUS TdixOpen( OUT TDIXCONTEXT* pTdix )
// Open the TDI transport address matching the selected media and register
// to receive datagrams at the selected handler. 'PTdix' is the
// previously intialized context.
//
// This call must be made at PASSIVE IRQL.
//
// Returns NDIS_STATUS_SUCCESS if successful, or NDIS_STATUS_FAILURE.
//
{ NTSTATUS status; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK iosb; FILE_FULL_EA_INFORMATION* pEa; ULONG ulEaLength; TA_IP_ADDRESS* pTaIp; TDI_ADDRESS_IP* pTdiIp; CHAR achEa[ 100 ]; UNICODE_STRING uniDevice; UNICODE_STRING uniProtocolNumber; WCHAR achRawIpDevice[ sizeof(DD_RAW_IP_DEVICE_NAME) + 10 ]; WCHAR achProtocolNumber[ 10 ]; SHORT sPort; LONG lRef;
// Open the TDI extensions or notice that it's already been requested
// and/or completed.
//
for (;;) { BOOLEAN fPending; BOOLEAN fDoOpen;
fPending = FALSE; fDoOpen = FALSE;
NdisAcquireSpinLock( &pTdix->lock ); { if (ReadFlags( &pTdix->ulFlags) & TDIXF_Pending) { fPending = TRUE; } else { lRef = ++pTdix->lRef; TRACE( TL_N, TM_Tdi, ( "TdixOpen, refs=%d", lRef ) ); if (lRef == 1) { SetFlags( &pTdix->ulFlags, TDIXF_Pending ); fDoOpen = TRUE; } } } NdisReleaseSpinLock( &pTdix->lock );
if (fDoOpen) { // Go on and open the transport address.
//
break; }
if (!fPending) { // It's already open, so report success.
//
return NDIS_STATUS_SUCCESS; }
// An operation is already in progress. Give it some time to finish
// then check again.
//
TRACE( TL_I, TM_Tdi, ( "NdisMSleep(open)" ) ); NdisMSleep( 100000 ); TRACE( TL_I, TM_Tdi, ( "NdisMSleep(open) done" ) ); }
do { // Set up parameters needed to open the UDP transport address. First, the
// object attributes.
//
if(pTdix->mediatype == TMT_Udp) { TDIXIPADDRESS TdixIpAddress;
TRACE( TL_V, TM_Tdi, ( "UDP" ) );
// Build the UDP device name as a counted string.
//
uniDevice.Buffer = DD_UDP_DEVICE_NAME; uniDevice.Length = sizeof(DD_UDP_DEVICE_NAME) - sizeof(WCHAR);
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress)); TdixIpAddress.sUdpPort = (SHORT)( htons( L2TP_UdpPort ));
status = TdixOpenIpAddress( &uniDevice, &TdixIpAddress, &pTdix->hAddress, &pTdix->pAddress );
if (status != STATUS_SUCCESS) { break; }
TdixEnableIpPktInfo(pTdix->pAddress); } // Set up parameters needed to open the raw IP address.
{ TDIXIPADDRESS TdixIpAddress;
TRACE( TL_A, TM_Tdi, ( "Raw IP" ) );
// Build the raw IP device name as a counted string. The device
// name is followed by a path separator then the protocol number
// of interest.
//
uniDevice.Buffer = achRawIpDevice; uniDevice.Length = 0; uniDevice.MaximumLength = sizeof(achRawIpDevice); RtlAppendUnicodeToString( &uniDevice, DD_RAW_IP_DEVICE_NAME );
uniDevice.Buffer[ uniDevice.Length / sizeof(WCHAR) ] = OBJ_NAME_PATH_SEPARATOR; uniDevice.Length += sizeof(WCHAR);
uniProtocolNumber.Buffer = achProtocolNumber; uniProtocolNumber.MaximumLength = sizeof(achProtocolNumber); RtlIntegerToUnicodeString( (ULONG )L2TP_IpProtocol, 10, &uniProtocolNumber );
RtlAppendUnicodeStringToString( &uniDevice, &uniProtocolNumber );
ASSERT( uniDevice.Length < sizeof(achRawIpDevice) );
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress));
status = TdixOpenIpAddress( &uniDevice, &TdixIpAddress, &pTdix->hRawAddress, &pTdix->pRawAddress );
if (status != STATUS_SUCCESS) { break; } // Enable IP header inclusion
TdixEnableIpHdrIncl(pTdix->pRawAddress); }
// Initialize the lookaside lists of read/send-datagram contexts.
//
NdisInitializeNPagedLookasideList( &pTdix->llistRdg, NULL, NULL, 0, sizeof(TDIXRDGINFO), MTAG_TDIXRDG, 10 );
NdisInitializeNPagedLookasideList( &pTdix->llistSdg, NULL, NULL, 0, sizeof(TDIXSDGINFO), MTAG_TDIXSDG, 10 );
// Install our receive datagram handler. Caller's 'pReceiveHandler' will
// be called by our handler when a datagram arrives and TDI business is
// out of the way.
//
status = TdixInstallEventHandler( pTdix->pAddress, TDI_EVENT_RECEIVE_DATAGRAM, TdixReceiveDatagramHandler, pTdix );
#if ROUTEWITHREF
{ TDIXIPADDRESS TdixIpAddress;
// Open the IP stack address which is needed in both UDP and raw IP
// mode for referenced route management.
//
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress));
uniDevice.Buffer = DD_IP_DEVICE_NAME; uniDevice.Length = sizeof(DD_IP_DEVICE_NAME) - sizeof(WCHAR);
status = TdixOpenIpAddress( &uniDevice, &TdixIpAddress, &pTdix->hIpStackAddress, &pTdix->pIpStackAddress );
if (status != STATUS_SUCCESS) { break; } } #endif
} while (FALSE);
// Report results after marking the operation complete.
//
{ BOOLEAN fDoClose;
fDoClose = FALSE; NdisAcquireSpinLock( &pTdix->lock ); { if (status == STATUS_SUCCESS) { ClearFlags( &pTdix->ulFlags, TDIXF_Pending ); } else { ++g_ulTdixOpenFailures; ASSERT( pTdix->lRef == 1) pTdix->lRef = 0; fDoClose = TRUE; } } NdisReleaseSpinLock( &pTdix->lock );
if (status != STATUS_SUCCESS) { TdixDoClose( pTdix ); } }
TRACE( TL_N, TM_Tdi, ( "TdixOpen=$%08x", status ) ); return (status == STATUS_SUCCESS) ? NDIS_STATUS_SUCCESS : NDIS_STATUS_FAILURE; }
VOID TdixReference( IN TDIXCONTEXT* pTdix )
// Increments the TDI extension reference count, like TdixOpen, except
// this routine may be called at DISPATCH IRQL.
//
// This call must only be made if it is known that the TDI context is
// already fully open.
//
{ NdisAcquireSpinLock( &pTdix->lock ); { ASSERT( pTdix->lRef > 0 ); ++pTdix->lRef; } NdisReleaseSpinLock( &pTdix->lock ); }
VOID TdixClose( IN TDIXCONTEXT* pTdix )
// Undo TdixOpen actions for transport context 'pTdix'.
//
// This call must be made at PASSIVE IRQL.
//
{ for (;;) { LONG lRef; BOOLEAN fPending; BOOLEAN fDoClose;
fPending = FALSE; fDoClose = FALSE;
NdisAcquireSpinLock( &pTdix->lock ); { if (ReadFlags( &pTdix->ulFlags ) & TDIXF_Pending) { fPending = TRUE; } else { lRef = --pTdix->lRef; ASSERT( lRef >= 0 ); TRACE( TL_N, TM_Tdi, ( "TdixClose, refs=%d", lRef ) ); if (lRef == 0) { SetFlags( &pTdix->ulFlags, TDIXF_Pending ); fDoClose = TRUE; } } } NdisReleaseSpinLock( &pTdix->lock );
if (fDoClose) { // Go on and close the transport address.
//
break; }
if (!fPending) { // It's still got references, so just return;
//
return; }
// An operation is already in progress. Give it some time to finish
// then check again.
//
TRACE( TL_I, TM_Tdi, ( "NdisMSleep(close)" ) ); NdisMSleep( 100000 ); TRACE( TL_I, TM_Tdi, ( "NdisMSleep(close) done" ) ); }
ASSERT( IsListEmpty( &pTdix->listRoutes ) ); TdixDoClose( pTdix ); }
NDIS_STATUS TdixSend( IN TDIXCONTEXT* pTdix, IN FILE_OBJECT* pFileObj, IN PTDIXSENDCOMPLETE pSendCompleteHandler, IN VOID* pContext1, IN VOID* pContext2, IN VOID* pAddress, IN CHAR* pBuffer, IN ULONG ulBufferLength, OUT IRP** ppIrp ) // Send a datagram buffer 'pBuffer', 'ulBufferLength' bytes long, to
// remote address 'pAddress'. The buffer must be from a BUFFERPOOL of
// NDIS_BUFFERs. 'PTdix' is the transport context.
// 'PSendDatagramCompleteHander' is caller's completion handler which is
// passed 'pContext1' and 'pContext2'. If 'ppIrp' is non-NULL '*ppIrp' is
// set to the address of the posted IRP, this for debugging purposes.
//
// This call must be made at PASSIVE IRQL.
//
// Returns NDIS_STATUS_SUCCESS if successful, or NDIS_STATUS_FAILURE.
//
{ NDIS_STATUS status; NTSTATUS iostatus; TDIXSDGINFO* pSdg; SHORT sPort; PIRP pIrp; TDI_ADDRESS_IP* pTdiIp; DEVICE_OBJECT* DeviceObj;
TRACE( TL_N, TM_Tdi, ( "TdixSend(dst=%d.%d.%d.%d/%d,len=%d)", IPADDRTRACE( ((TDIXIPADDRESS* )pAddress)->ulIpAddress ), (ULONG )(ntohs( ((TDIXIPADDRESS* )pAddress)->sUdpPort )), ulBufferLength ) );
ASSERT(pFileObj != NULL);
do { // Allocate a context for this send-datagram from our lookaside list.
//
pSdg = ALLOC_TDIXSDGINFO( pTdix ); if (pSdg) { // Fill in the send-datagram context.
//
pSdg->pTdix = pTdix; pSdg->pSendCompleteHandler = pSendCompleteHandler; pSdg->pContext1 = pContext1; pSdg->pContext2 = pContext2; pSdg->pBuffer = pBuffer; } else { status = NDIS_STATUS_RESOURCES; break; }
#if 0
// Put the destination IP address in the "connection" structure as TDI
// expects. The "connection" is part of our context as it must be
// available to TDI until the request completes.
//
pSdg->taip.TAAddressCount = 1; pSdg->taip.Address[ 0 ].AddressLength = TDI_ADDRESS_LENGTH_IP; pSdg->taip.Address[ 0 ].AddressType = TDI_ADDRESS_TYPE_IP;
pTdiIp = &pSdg->taip.Address[ 0 ].Address[ 0 ];
sPort = ((TDIXIPADDRESS* )pAddress)->sUdpPort; if (sPort == 0 && pTdix->mediatype == TMT_Udp) { sPort = (SHORT )(htons( L2TP_UdpPort )); }
pTdiIp->sin_port = sPort; pTdiIp->in_addr = ((TDIXIPADDRESS* )pAddress)->ulIpAddress; NdisZeroMemory( pTdiIp->sin_zero, sizeof(pTdiIp->sin_zero) );
pSdg->tdiconninfo.UserDataLength = 0; pSdg->tdiconninfo.UserData = NULL; pSdg->tdiconninfo.OptionsLength = 0; pSdg->tdiconninfo.Options = NULL; pSdg->tdiconninfo.RemoteAddressLength = sizeof(pSdg->taip); pSdg->tdiconninfo.RemoteAddress = &pSdg->taip; #endif
DeviceObj = pFileObj->DeviceObject;
#if ALLOCATEIRPS
// Allocate the IRP directly.
//
pIrp = IoAllocateIrp(DeviceObj->StackSize, FALSE ); #else
// Allocate a "send datagram" IRP with base initialization.
//
pIrp = TdiBuildInternalDeviceControlIrp( TDI_SEND, DeviceObj, FileObj, NULL, NULL ); #endif
if (!pIrp) { TRACE( TL_A, TM_Res, ( "Failed to allocate TDI_SEND IRP" ) ); WPLOG( LL_A, LM_Res, ( "Failed to allocate TDI_SEND IRP" ) ); status = NDIS_STATUS_RESOURCES; break; }
// Complete the "send datagram" IRP initialization.
//
TdiBuildSend( pIrp, DeviceObj, pFileObj, TdixSendComplete, pSdg, NdisBufferFromBuffer( pBuffer ), 0, ulBufferLength);
if (ppIrp) { *ppIrp = pIrp; }
// Tell the I/O manager to pass our IRP to the transport for
// processing.
//
iostatus = IoCallDriver( DeviceObj, pIrp ); ASSERT( iostatus == STATUS_PENDING );
status = NDIS_STATUS_SUCCESS; } while (FALSE);
if (status != NDIS_STATUS_SUCCESS) { // Pull a half Jameel, i.e. convert a synchronous failure to an
// asynchronous failure from client's perspective. However, clean up
// context here.
//
++g_ulTdixSendDatagramFailures; if (pSdg) { FREE_TDIXSDGINFO( pTdix, pSdg ); }
pSendCompleteHandler( pTdix, pContext1, pContext2, pBuffer ); }
return NDIS_STATUS_PENDING; }
NDIS_STATUS TdixSendDatagram( IN TDIXCONTEXT* pTdix, IN FILE_OBJECT* FileObj, IN PTDIXSENDCOMPLETE pSendCompleteHandler, IN VOID* pContext1, IN VOID* pContext2, IN VOID* pAddress, IN CHAR* pBuffer, IN ULONG ulBufferLength, OUT IRP** ppIrp )
// Send a datagram buffer 'pBuffer', 'ulBufferLength' bytes long, to
// remote address 'pAddress'. The buffer must be from a BUFFERPOOL of
// NDIS_BUFFERs. 'PTdix' is the transport context.
// 'PSendDatagramCompleteHander' is caller's completion handler which is
// passed 'pContext1' and 'pContext2'. If 'ppIrp' is non-NULL '*ppIrp' is
// set to the address of the posted IRP, this for debugging purposes.
//
// This call must be made at PASSIVE IRQL.
//
// Returns NDIS_STATUS_SUCCESS if successful, or NDIS_STATUS_FAILURE.
//
{ NDIS_STATUS status; NTSTATUS iostatus; TDIXSDGINFO* pSdg; SHORT sPort; PIRP pIrp; TDI_ADDRESS_IP* pTdiIp;
TRACE( TL_N, TM_Tdi, ( "TdixSendDg(dst=%d.%d.%d.%d/%d,len=%d)", IPADDRTRACE( ((TDIXIPADDRESS* )pAddress)->ulIpAddress ), (ULONG )(ntohs( ((TDIXIPADDRESS* )pAddress)->sUdpPort )), ulBufferLength ) );
ASSERT(FileObj); do { // Allocate a context for this send-datagram from our lookaside list.
//
pSdg = ALLOC_TDIXSDGINFO( pTdix ); if (pSdg) { // Fill in the send-datagram context.
//
pSdg->pTdix = pTdix; pSdg->pSendCompleteHandler = pSendCompleteHandler; pSdg->pContext1 = pContext1; pSdg->pContext2 = pContext2; pSdg->pBuffer = pBuffer; } else { status = NDIS_STATUS_RESOURCES; break; }
// Put the destination IP address in the "connection" structure as TDI
// expects. The "connection" is part of our context as it must be
// available to TDI until the request completes.
//
pSdg->taip.TAAddressCount = 1; pSdg->taip.Address[ 0 ].AddressLength = TDI_ADDRESS_LENGTH_IP; pSdg->taip.Address[ 0 ].AddressType = TDI_ADDRESS_TYPE_IP;
pTdiIp = &pSdg->taip.Address[ 0 ].Address[ 0 ];
sPort = ((TDIXIPADDRESS* )pAddress)->sUdpPort; if (sPort == 0 && pTdix->mediatype == TMT_Udp) { sPort = (SHORT )(htons( L2TP_UdpPort )); }
pTdiIp->sin_port = sPort; pTdiIp->in_addr = ((TDIXIPADDRESS* )pAddress)->ulIpAddress; NdisZeroMemory( pTdiIp->sin_zero, sizeof(pTdiIp->sin_zero) );
pSdg->tdiconninfo.UserDataLength = 0; pSdg->tdiconninfo.UserData = NULL; pSdg->tdiconninfo.OptionsLength = 0; pSdg->tdiconninfo.Options = NULL; pSdg->tdiconninfo.RemoteAddressLength = sizeof(pSdg->taip); pSdg->tdiconninfo.RemoteAddress = &pSdg->taip;
#if ALLOCATEIRPS
// Allocate the IRP directly.
//
pIrp = IoAllocateIrp( FileObj->DeviceObject->StackSize, FALSE ); #else
// Allocate a "send datagram" IRP with base initialization.
//
pIrp = TdiBuildInternalDeviceControlIrp( TDI_SEND_DATAGRAM, FileObject->DeviceObject, pTdix->pAddress, NULL, NULL ); #endif
if (!pIrp) { TRACE( TL_A, TM_Res, ( "Failed to allocate TDI_SEND_DATAGRAM IRP" ) ); WPLOG( LL_A, LM_Res, ( "Failed to allocate TDI_SEND_DATAGRAM IRP" ) ); status = NDIS_STATUS_RESOURCES; break; }
// Complete the "send datagram" IRP initialization.
//
TdiBuildSendDatagram( pIrp, FileObj->DeviceObject, FileObj, TdixSendDatagramComplete, pSdg, NdisBufferFromBuffer( pBuffer ), ulBufferLength, &pSdg->tdiconninfo );
if (ppIrp) { *ppIrp = pIrp; }
// Tell the I/O manager to pass our IRP to the transport for
// processing.
//
iostatus = IoCallDriver( FileObj->DeviceObject, pIrp ); ASSERT( iostatus == STATUS_PENDING );
status = NDIS_STATUS_SUCCESS; } while (FALSE);
if (status != NDIS_STATUS_SUCCESS) { // Pull a half Jameel, i.e. convert a synchronous failure to an
// asynchronous failure from client's perspective. However, clean up
// context here.
//
++g_ulTdixSendDatagramFailures; if (pSdg) { FREE_TDIXSDGINFO( pTdix, pSdg ); }
pSendCompleteHandler( pTdix, pContext1, pContext2, pBuffer ); }
return NDIS_STATUS_PENDING; }
VOID TdixDestroyConnection( TDIXUDPCONNECTCONTEXT *pUdpContext) { if (pUdpContext->fUsePayloadAddr) {
ASSERT(pUdpContext->hPayloadAddr != NULL); //TdixInstallEventHandler(pUdpContext->pPayloadAddr,
// TDI_EVENT_RECEIVE_DATAGRAM, NULL, NULL);
ObDereferenceObject( pUdpContext->pPayloadAddr );
// Close the payload address object
//
ZwClose(pUdpContext->hPayloadAddr); pUdpContext->hPayloadAddr = NULL; pUdpContext->fUsePayloadAddr = FALSE; }
if (pUdpContext->hCtrlAddr != NULL) { //TdixInstallEventHandler(pUdpContext->pCtrlAddr,
// TDI_EVENT_RECEIVE_DATAGRAM, NULL, NULL);
// Close the Ctrl address object
//
ObDereferenceObject( pUdpContext->pCtrlAddr ); ZwClose (pUdpContext->hCtrlAddr); pUdpContext->hCtrlAddr = NULL; } }
NDIS_STATUS TdixSetupConnection( IN TDIXCONTEXT* pTdix, IN TDIXROUTE *pTdixRoute, IN ULONG ulLocalIpAddress, IN TDIXUDPCONNECTCONTEXT* pUdpContext) { NDIS_STATUS status = STATUS_SUCCESS; ASSERT(pUdpContext != NULL); if (pTdix->mediatype == TMT_Udp) {
do { UNICODE_STRING uniDevice; UNICODE_STRING uniProtocolNumber; TDIXIPADDRESS TdixIpAddress;
// Create an address object that we can send across. If we have udp xsums
// disabled we will need to create two address objects, one for control
// and one for payload. This allows payload specific features to be
// implemented.
//
uniDevice.Buffer = DD_UDP_DEVICE_NAME; uniDevice.Length = sizeof(DD_UDP_DEVICE_NAME) - sizeof(WCHAR);
TdixIpAddress.sUdpPort = (SHORT)(htons(L2TP_UdpPort)); TdixIpAddress.ulIpAddress = ulLocalIpAddress; // Build the UDP device name as a counted string.
//
status = TdixOpenIpAddress(&uniDevice, &TdixIpAddress, &pUdpContext->hCtrlAddr, &pUdpContext->pCtrlAddr );
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "TdixOpenIpAddress failed %x?", status ) ); WPLOG( LL_A, LM_Tdi, ( "TdixOpenIpAddress failed %x?", status ) ); break; }
TdixEnableIpPktInfo(pUdpContext->pCtrlAddr); status = TdixInstallEventHandler( pUdpContext->pCtrlAddr, TDI_EVENT_RECEIVE_DATAGRAM, TdixReceiveDatagramHandler, pTdix); if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "AHR InstallEventHandler=%x?", status ) ); break; }
//
// Associate a particular "send" IP interface index with the address
// object, so that if that interface disappears traffic will not be
// "re-routed" often back into the tunnel producing disastrous
// looping.
//
status = TdixConnectAddrInterface(pUdpContext->pCtrlAddr, pUdpContext->hCtrlAddr, pTdixRoute);
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "TdixConnectAddrInterface failed %x", status ) ); WPLOG( LL_A, LM_Tdi, ( "TdixConnectAddrInterface failed %x", status ) ); break; }
// If udp xsums are disabled we need to create another address object.
// We will set this object to disable udp xsums and then use it to
// send payload data.
//
// If udp xsums are enabled we can use the same address object for
// payloads that we use for contrl frames.
//
if (pTdix->ulFlags & TDIXF_DisableUdpXsums) { // Open the address object
//
status = TdixOpenIpAddress(&uniDevice, &TdixIpAddress, &pUdpContext->hPayloadAddr, &pUdpContext->pPayloadAddr );
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "AHR OpenPayloadAddr=%x?", status ) ); WPLOG( LL_A, LM_Tdi, ( "AHR OpenPayloadAddr=%x?", status ) ); pUdpContext->hPayloadAddr = NULL; break; } pUdpContext->fUsePayloadAddr = TRUE;
TdixDisableUdpChecksums( pUdpContext->pPayloadAddr ); TdixEnableIpPktInfo(pUdpContext->pPayloadAddr); status = TdixInstallEventHandler( pUdpContext->pPayloadAddr, TDI_EVENT_RECEIVE_DATAGRAM, TdixReceiveDatagramHandler, pTdix); if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "AHR InstallEventHandler=%x?", status ) ); break; }
// Associate a particular "send" IP interface index with the address
// object, so that if that interface disappears traffic will not be
// "re-routed" often back into the tunnel producing disastrous
// looping.
//
status = TdixConnectAddrInterface(pUdpContext->pPayloadAddr, pUdpContext->hPayloadAddr, pTdixRoute );
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "AHR ConnectPayloadAddr=%x?", status ) ); WPLOG( LL_A, LM_Tdi, ( "AHR ConnectPayloadAddr=%x?", status ) ); break; } } else { pUdpContext->hPayloadAddr = pUdpContext->hCtrlAddr; pUdpContext->pPayloadAddr = pUdpContext->pCtrlAddr;
TRACE( TL_I, TM_Tdi, ( "AHR Ctrl==Payload") ); }
} while ( FALSE );
}
return status; }
VOID* TdixAddHostRoute( IN TDIXCONTEXT* pTdix, IN ULONG ulIpAddress, IN ULONG ulIfIndex)
// Adds a host route for the remote peer's network byte-ordered IP address
// 'ulIpAddress', i.e. routes packets directed to the L2TP peer to the LAN
// card rather than back into the tunnel where it would loop infinitely.
// 'PTdix' is the is caller's TDI extension context.
//
// Returns true if the route was added, false otherwise.
//
// Note: This routine borrows heavily from PPTP.
//
{ TCP_REQUEST_QUERY_INFORMATION_EX QueryBuf; TCP_REQUEST_SET_INFORMATION_EX* pSetBuf; VOID* pBuffer2; PIO_STACK_LOCATION pIrpSp; PDEVICE_OBJECT pDeviceObject; PDEVICE_OBJECT pIpDeviceObject; NTSTATUS status = STATUS_SUCCESS; PIRP pIrp; IO_STATUS_BLOCK iosb; IPRouteEntry* pBuffer; IPRouteEntry* pRouteEntry; IPRouteEntry* pNewRouteEntry; IPRouteEntry* pBestRoute; ULONG ulRouteCount; ULONG ulSize; ULONG i; ULONG ulBestMask; ULONG ulBestMetric; TDIXROUTE* pTdixRoute; BOOLEAN fNewRoute; BOOLEAN fPending; BOOLEAN fOpenPending; BOOLEAN fUsedNonL2tpRoute; LONG lRef; KEVENT event;
if (ulIpAddress == 0) { TRACE( TL_A, TM_Tdi, ( "IP == 0?" ) ); return ((VOID*)NULL); }
TRACE( TL_N, TM_Tdi, ( "TdixAddHostRoute(ip=%d.%d.%d.%d)", IPADDRTRACE( ulIpAddress ) ) );
// Host routes are referenced since more than one tunnel to the same peer
// (allowed by L2TP) shares the same system route. See if this is just a
// reference or the actual add of the system host route.
//
for (;;) { fPending = FALSE; fOpenPending = FALSE; pTdixRoute = NULL; fNewRoute = FALSE;
NdisAcquireSpinLock( &pTdix->lock ); do { if (pTdix->lRef <= 0) { // TDIX is closed or closing, so the add route fails.
//
break; }
if (ReadFlags( &pTdix->ulFlags ) & TDIXF_Pending) { // A TdixOpen is pending. Wait for it to finish before
// adding the route.
//
fOpenPending = TRUE; break; }
pTdixRoute = TdixRouteFromIpAddress( pTdix, ulIpAddress ); if (pTdixRoute) { // Found an existing route context.
//
fPending = pTdixRoute->fPending; if (!fPending) { // No other operation is pending on the route context.
// Take a reference.
//
++pTdixRoute->lRef; } break; } // No existing route context. Create and link a new one.
//
pTdixRoute = ALLOC_TDIXROUTE( pTdix ); if (pTdixRoute) { NdisZeroMemory(pTdixRoute, sizeof(TDIXROUTE));
pTdixRoute->ulIpAddress = ulIpAddress; pTdixRoute->lRef = 1; pTdixRoute->fPending = TRUE; pTdixRoute->fUsedNonL2tpRoute = FALSE;
InsertTailList( &pTdix->listRoutes, &pTdixRoute->linkRoutes ); lRef = ++pTdix->lRef; TRACE( TL_N, TM_Tdi, ( "TdixAHR, refs=%d", lRef ) ); WPLOG( LL_M, LM_Tdi, ( "TdixAHR, refs=%d", lRef ) );
fPending = pTdixRoute->fPending; fNewRoute = TRUE; } } while (FALSE); NdisReleaseSpinLock( &pTdix->lock );
if (!fOpenPending) { if (!pTdixRoute) { // TDIX is closed or we couldn't find an existing route
// context or create a new one. Report failure.
//
return ((VOID*)NULL); }
if (fNewRoute) { // Created a new route context so go on to make the IOCTL
// calls to add the associated system host route.
//
break; }
if (!fPending) { // Took a reference on an existing route context. Report
// success.
//
return (pTdixRoute); } }
// An operation is already pending. Give it some time to finish then
// check again.
//
TRACE( TL_I, TM_Tdi, ( "NdisMSleep(add)" ) ); NdisMSleep( 100000 ); TRACE( TL_I, TM_Tdi, ( "NdisMSleep(add) done" ) ); }
// Do the IOCTLs to add the host route.
//
pBuffer = NULL; pBuffer2 = NULL; fUsedNonL2tpRoute = FALSE;
do {
// Get the routing table from the IP stack. This make take a few
// iterations since the size of the buffer required is not known. Set
// up the static request information first.
//
QueryBuf.ID.toi_entity.tei_entity = CL_NL_ENTITY; QueryBuf.ID.toi_entity.tei_instance = 0; QueryBuf.ID.toi_class = INFO_CLASS_PROTOCOL; QueryBuf.ID.toi_type = INFO_TYPE_PROVIDER; pDeviceObject = IoGetRelatedDeviceObject( pTdix->pAddress );
status = !STATUS_SUCCESS; ulRouteCount = 20; for (;;) { // Allocate a buffer big enough for 'ulRouteCount' routes.
//
ulSize = sizeof(IPRouteEntry) * ulRouteCount; QueryBuf.ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; NdisZeroMemory( &QueryBuf.Context, CONTEXT_SIZE );
pBuffer = (IPRouteEntry* )ALLOC_NONPAGED( ulSize, MTAG_ROUTEQUERY ); if (!pBuffer) { TRACE( TL_A, TM_Res, ( "Alloc RQ?" ) ); WPLOG( LL_A, LM_Res, ( "Alloc RQ?" ) ); break; }
// Set up a request to the IP stack to fill the buffer with the
// routing table and send it to the stack.
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_QUERY_INFORMATION_EX, pDeviceObject, (PVOID )&QueryBuf, sizeof(QueryBuf), pBuffer, ulSize, FALSE, &event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Res, ( "Build Q Irp?" ) ); WPLOG( LL_A, LM_Res, ( "Build Q Irp?" ) ); break; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pAddress;
status = IoCallDriver( pDeviceObject, pIrp );
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = iosb.Status; }
if (status != STATUS_BUFFER_OVERFLOW) { if (status != STATUS_SUCCESS) { g_statusLastAhrTcpQueryInfoExFailure = status; } break; }
// The buffer didn't hold the routing table. Undo in preparation
// for another try with twice as big a buffer.
//
ulRouteCount <<= 1; FREE_NONPAGED( pBuffer ); }
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "AHR Q_INFO_EX=%d?", status ) ); WPLOG( LL_A, LM_Tdi, ( "AHR Q_INFO_EX=%d?", status ) ); break; }
status = !STATUS_SUCCESS;
// Calculate how many routes were loaded into our buffer.
//
ulRouteCount = (ULONG )(iosb.Information / sizeof(IPRouteEntry));
// Walk the route table looking for the "best route" that will be used
// to route packets to the peer, i.e. the one with the highest
// priority metric, and within that, the highest class address mask.
//
pBestRoute = NULL; ulBestMask = 0; ulBestMetric = (ULONG )-1;
for (i = 0, pRouteEntry = pBuffer; i < ulRouteCount; ++i, ++pRouteEntry) { if (pRouteEntry->ire_dest == (ulIpAddress & pRouteEntry->ire_mask) && ulIfIndex == pRouteEntry->ire_index) { // Found a route that applies to peer's IP address.
//
if (!pBestRoute || (ulBestMask == pRouteEntry->ire_mask) && (pRouteEntry->ire_metric1 < ulBestMetric)) { // The route has a lower (higher priority) metric than
// anything found so far.
//
pBestRoute = pRouteEntry; ulBestMask = pRouteEntry->ire_mask; ulBestMetric = pRouteEntry->ire_metric1; continue; }
if (ntohl( pRouteEntry->ire_mask ) > ntohl( ulBestMask )) { // The route has a higher address class mask than anything
// found so far.
//
pBestRoute = pRouteEntry; ulBestMask = pRouteEntry->ire_mask; ulBestMetric = pRouteEntry->ire_metric1; } } }
if (pBestRoute) { // Found the route that will be used to route peer's address.
//
if (pBestRoute->ire_dest == ulIpAddress && pBestRoute->ire_mask == 0xFFFFFFFF) { // The host route already exists.
//
if (pTdix->hre == HRE_Use) { TRACE( TL_I, TM_Tdi, ( "Route exists (use as is)" ) ); status = STATUS_SUCCESS; fUsedNonL2tpRoute = TRUE; break; } else if (pTdix->hre == HRE_Fail) { TRACE( TL_I, TM_Tdi, ( "Route exists (fail)" ) ); break; }
// If we reach here then we are in HRE_Reference mode, so drop
// thru and re-add the route so it's reference in the IP stack
// will be incremented.
}
pTdixRoute->InterfaceIndex = pBestRoute->ire_index;
#if ROUTEWITHREF
// Allocate a buffer to hold our request to add a new route.
//
ulSize = sizeof(IPRouteEntry); pBuffer2 = ALLOC_NONPAGED( ulSize, MTAG_ROUTESET ); if (!pBuffer2) { TRACE( TL_A, TM_Res, ( "Alloc SI?" ) ); WPLOG( LL_A, LM_Res, ( "Alloc SI?" ) ); break; }
// Fill in the request buffer with the information about the new
// specific route. The best route is used as a template.
//
pNewRouteEntry = (IPRouteEntry* )pBuffer2; NdisMoveMemory( pNewRouteEntry, pBestRoute, sizeof(IPRouteEntry) );
pNewRouteEntry->ire_dest = ulIpAddress; pNewRouteEntry->ire_mask = 0xFFFFFFFF;
// Check DIRECT/INDIRECT only if this is not a host route
if(pBestRoute->ire_mask != 0xFFFFFFFF) { if ((pBestRoute->ire_nexthop & pBestRoute->ire_mask) == (ulIpAddress & pBestRoute->ire_mask)) { pNewRouteEntry->ire_type = IRE_TYPE_DIRECT; } else { pNewRouteEntry->ire_type = IRE_TYPE_INDIRECT; } } pNewRouteEntry->ire_proto = IRE_PROTO_NETMGMT;
pIpDeviceObject = IoGetRelatedDeviceObject( pTdix->pIpStackAddress );
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_IP_SET_ROUTEWITHREF, pIpDeviceObject, pNewRouteEntry, ulSize, NULL, 0, FALSE, &event, &iosb); if (!pIrp) { TRACE( TL_A, TM_Res, ( "Build SET_ROUTEWITHREF Irp?" ) ); WPLOG( LL_A, LM_Res, ( "Build SET_ROUTEWITHREF Irp?" ) ); break; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pIpStackAddress;
// Send the request to the IP stack.
//
status = IoCallDriver( pIpDeviceObject, pIrp ); #else
// Allocate a buffer to hold our request to add a new route.
//
ulSize = sizeof(TCP_REQUEST_SET_INFORMATION_EX) + sizeof(IPRouteEntry); pBuffer2 = ALLOC_NONPAGED( ulSize, MTAG_ROUTESET ); if (!pBuffer2) { TRACE( TL_A, TM_Res, ( "Alloc SI?" ) ); WPLOG( LL_A, LM_Res, ( "Alloc SI?" ) ); break; }
// Fill in the request buffer with the information about the new
// specific route. The best route is used as a template.
//
pSetBuf = (TCP_REQUEST_SET_INFORMATION_EX* )pBuffer2; NdisZeroMemory( pSetBuf, ulSize );
pSetBuf->ID.toi_entity.tei_entity = CL_NL_ENTITY; pSetBuf->ID.toi_entity.tei_instance = 0; pSetBuf->ID.toi_class = INFO_CLASS_PROTOCOL; pSetBuf->ID.toi_type = INFO_TYPE_PROVIDER; pSetBuf->ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; pSetBuf->BufferSize = sizeof(IPRouteEntry);
pNewRouteEntry = (IPRouteEntry* )&pSetBuf->Buffer[ 0 ]; NdisMoveMemory( pNewRouteEntry, pBestRoute, sizeof(IPRouteEntry) );
pNewRouteEntry->ire_dest = ulIpAddress; pNewRouteEntry->ire_mask = 0xFFFFFFFF;
// Check DIRECT/INDIRECT only if this is not a host route
if(pBestRoute->ire_mask != 0xFFFFFFFF) { if ((pBestRoute->ire_nexthop & pBestRoute->ire_mask) == (ulIpAddress & pBestRoute->ire_mask)) { pNewRouteEntry->ire_type = IRE_TYPE_DIRECT; } else { pNewRouteEntry->ire_type = IRE_TYPE_INDIRECT; } } pNewRouteEntry->ire_proto = IRE_PROTO_NETMGMT;
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_SET_INFORMATION_EX, pDeviceObject, pSetBuf, ulSize, NULL, 0, FALSE, &event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Res, ( "Build TCP_SET_INFORMATION_EX Irp?" ) ); WPLOG( LL_A, LM_Res, ( "Build TCP_SET_INFORMATION_EX Irp?" ) ); break; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pAddress;
// Send the request to the IP stack.
//
status = IoCallDriver( pDeviceObject, pIrp ); #endif
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = iosb.Status; }
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "AHR SET_ROUTE=$%08x?", status ) ); WPLOG( LL_A, LM_Tdi, ( "AHR SET_ROUTE=$%08x?", status ) ); g_statusLastAhrSetRouteFailure = status; break; }
TRACE( TL_N, TM_Tdi, ( "Add host route %d.%d.%d.%d type %d nexthop %d.%d.%d.%d index %d", IPADDRTRACE( pNewRouteEntry->ire_dest ), pNewRouteEntry->ire_type, IPADDRTRACE( pNewRouteEntry->ire_nexthop ), pNewRouteEntry->ire_index ) ); WPLOG( LL_M, LM_Tdi, ( "Add host route %!IPADDR! type %d nexthop %!IPADDR! index %d", pNewRouteEntry->ire_dest, pNewRouteEntry->ire_type, pNewRouteEntry->ire_nexthop, pNewRouteEntry->ire_index ) ); } else { ++g_ulNoBestRoute; TRACE( TL_A, TM_Tdi, ( "No best route for $%08x?", ulIpAddress ) ); WPLOG( LL_A, LM_Tdi, ( "No best route for %!IPADDR!?", ulIpAddress ) ); break; } } while (FALSE);
if (pBuffer) { FREE_NONPAGED( pBuffer ); }
if (pBuffer2) { FREE_NONPAGED( pBuffer2 ); }
// Update the route context.
//
{ BOOLEAN fDoClose; LONG lRefTemp;
fDoClose = FALSE; NdisAcquireSpinLock( &pTdix->lock ); { pTdixRoute->fUsedNonL2tpRoute = fUsedNonL2tpRoute;
if (status == STATUS_SUCCESS) { ++g_ulTdixAddHostRouteSuccesses; pTdixRoute->fPending = FALSE; } else { ++g_ulTdixAddHostRouteFailures; RemoveEntryList( &pTdixRoute->linkRoutes ); lRefTemp = --pTdix->lRef; TRACE( TL_A, TM_Tdi, ( "TdixAHR fail, refs=%d", lRefTemp ) ); if (lRefTemp <= 0) { fDoClose = TRUE; } FREE_TDIXROUTE( pTdxi, pTdixRoute ); pTdixRoute = NULL; } } NdisReleaseSpinLock( &pTdix->lock );
if (fDoClose) { TdixDoClose( pTdix ); } }
return (pTdixRoute); }
VOID TdixDeleteHostRoute( IN TDIXCONTEXT* pTdix, IN ULONG ulIpAddress)
// Deletes the host route added for network byte-ordered IP address
// 'ulIpAddress'. 'PTdix' is caller's TDI extension context.
//
// Note: This routine borrows heavily from PPTP.
//
{ TCP_REQUEST_QUERY_INFORMATION_EX QueryBuf; TCP_REQUEST_SET_INFORMATION_EX *pSetBuf; VOID* pBuffer2; PIO_STACK_LOCATION pIrpSp; PDEVICE_OBJECT pDeviceObject; PDEVICE_OBJECT pIpDeviceObject; UCHAR context[ CONTEXT_SIZE ]; NTSTATUS status; PIRP pIrp; IO_STATUS_BLOCK iosb; IPRouteEntry* pBuffer; IPRouteEntry* pRouteEntry; IPRouteEntry* pNewRouteEntry; ULONG ulRouteCount; ULONG ulSize; ULONG i; TDIXROUTE* pTdixRoute; BOOLEAN fPending; BOOLEAN fDoDelete; KEVENT event;
TRACE( TL_N, TM_Tdi, ( "TdixDeleteHostRoute(%d.%d.%d.%d)", IPADDRTRACE( ulIpAddress ) ) );
if (!ulIpAddress) { TRACE( TL_A, TM_Tdi, ( "!IP?" ) ); return; }
// Host routes are referenced since more than one tunnel to the same peer
// (allowed by L2TP) shares the same system route. First, see if this is
// just a dereference or the final deletion of the system host route.
//
for (;;) { fDoDelete = FALSE; fPending = FALSE;
NdisAcquireSpinLock( &pTdix->lock ); do { // These asserts hold because we never delete routes we didn't
// add, and since the route we added holds a TDIX reference, TDIX
// cannot be opening or closing.
//
ASSERT( pTdix->lRef > 0 ); ASSERT( !(ReadFlags( &pTdix->ulFlags) & TDIXF_Pending) );
pTdixRoute = TdixRouteFromIpAddress( pTdix, ulIpAddress ); if (pTdixRoute) { // Route exists. Remove a reference.
//
fPending = pTdixRoute->fPending; if (!fPending) { if (--pTdixRoute->lRef <= 0) { // Last "add" reference has been removed so call the
// IOCTLs to delete the system route.
//
pTdixRoute->fPending = TRUE; fDoDelete = TRUE; } } } DBG_else { ASSERT( FALSE ); } } while (FALSE); NdisReleaseSpinLock( &pTdix->lock );
if (fDoDelete) { // This is the last reference, so go on and issue the IOCTLs to
// delete the system host route.
//
break; }
if (!fPending) { // Just remove a reference.
//
return; }
// An operation is already pending. Give it some time to finish then
// check again.
//
TRACE( TL_I, TM_Tdi, ( "NdisMSleep(del)" ) ); NdisMSleep( 100000 ); TRACE( TL_I, TM_Tdi, ( "NdisMSleep(del)" ) ); }
pBuffer = NULL;
do { if (pTdixRoute->fUsedNonL2tpRoute) { // Used a route we didn't add so don't delete it either.
//
status = STATUS_SUCCESS; break; }
// Get the routing table from the IP stack. This make take a few
// iterations since the size of the buffer required is not known. Set
// up the static request information first.
//
QueryBuf.ID.toi_entity.tei_entity = CL_NL_ENTITY; QueryBuf.ID.toi_entity.tei_instance = 0; QueryBuf.ID.toi_class = INFO_CLASS_PROTOCOL; QueryBuf.ID.toi_type = INFO_TYPE_PROVIDER; pDeviceObject = IoGetRelatedDeviceObject( pTdix->pAddress );
status = !STATUS_SUCCESS; ulRouteCount = 20; for (;;) { // Allocate a buffer big enough for 'ulRouteCount' routes.
//
ulSize = sizeof(IPRouteEntry) * ulRouteCount; QueryBuf.ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; NdisZeroMemory( &QueryBuf.Context, CONTEXT_SIZE );
pBuffer = (IPRouteEntry* )ALLOC_NONPAGED( ulSize, MTAG_ROUTEQUERY ); if (!pBuffer) { TRACE( TL_A, TM_Res, ( "Alloc RQ?" ) ); WPLOG( LL_A, LM_Res, ( "Alloc RQ?" ) ); break; }
// Set up a request to the IP stack to fill the buffer with the
// routing table and send it to the stack.
//
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_QUERY_INFORMATION_EX, pDeviceObject, (PVOID )&QueryBuf, sizeof(QueryBuf), pBuffer, ulSize, FALSE, &event, &iosb );
if (!pIrp) { TRACE( TL_A, TM_Res, ( "TCP_QI Irp?" ) ); WPLOG( LL_A, LM_Res, ( "TCP_QI Irp?" ) ); break; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pAddress;
status = IoCallDriver( pDeviceObject, pIrp );
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = iosb.Status; }
if (status != STATUS_BUFFER_OVERFLOW) { if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "DHR Q_INFO_EX=%d?", status ) ); WPLOG( LL_A, LM_Tdi, ( "DHR Q_INFO_EX=%d?", status ) ); g_statusLastDhrTcpQueryInfoExFailure = status; } break; }
// The buffer didn't hold the routing table. Undo in preparation for
// another try with twice as big a buffer.
//
ulRouteCount <<= 1; FREE_NONPAGED( pBuffer ); }
if (status != STATUS_SUCCESS) { break; }
// Calculate how many routes were loaded into our buffer.
//
ulRouteCount = (ULONG )(iosb.Information / sizeof(IPRouteEntry));
// Walk the route table looking for the route we added in
// TdixAddHostRoute.
//
status = !STATUS_SUCCESS; pBuffer2 = NULL; for (i = 0, pRouteEntry = pBuffer; i < ulRouteCount; ++i, ++pRouteEntry) { if (pRouteEntry->ire_dest == ulIpAddress && pRouteEntry->ire_proto == IRE_PROTO_NETMGMT) { #if ROUTEWITHREF
// Found the added route. Allocate a buffer to hold our
// request to delete the route.
//
ulSize = sizeof(IPRouteEntry); pBuffer2 = ALLOC_NONPAGED( ulSize, MTAG_ROUTESET ); if (!pBuffer2) { TRACE( TL_A, TM_Res, ( "Failed to allocate IPRouteEntry" ) ); WPLOG( LL_A, LM_Res, ( "Failed to allocate IPRouteEntry" ) ); break; }
// Use the found route as a template for the route entry
// marked for deletion.
//
pNewRouteEntry = (IPRouteEntry* )pBuffer2; NdisMoveMemory( pNewRouteEntry, pRouteEntry, sizeof(IPRouteEntry) ); pNewRouteEntry->ire_type = IRE_TYPE_INVALID;
pIpDeviceObject = IoGetRelatedDeviceObject( pTdix->pIpStackAddress );
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_IP_SET_ROUTEWITHREF, pIpDeviceObject, pNewRouteEntry, ulSize, NULL, 0, FALSE, &event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Res, ( "TCP_SI Irp?" ) ); WPLOG( LL_A, LM_Res, ( "TCP_SI Irp?" ) ); break; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pIpStackAddress;
// Send the request to the IP stack.
//
status = IoCallDriver( pIpDeviceObject, pIrp ); #else
// Found the added route. Allocate a buffer to hold our
// request to delete the route.
//
ulSize = sizeof(TCP_REQUEST_SET_INFORMATION_EX) + sizeof(IPRouteEntry); pBuffer2 = ALLOC_NONPAGED( ulSize, MTAG_ROUTESET ); if (!pBuffer2) { TRACE( TL_A, TM_Res, ( "!pSetBuf" ) ); WPLOG( LL_A, LM_Res, ( "!pSetBuf" ) ); break; }
// Fill in the request buffer with static information about
// changing routes.
//
pSetBuf = (TCP_REQUEST_SET_INFORMATION_EX *)pBuffer2; NdisZeroMemory( pSetBuf, ulSize );
pSetBuf->ID.toi_entity.tei_entity = CL_NL_ENTITY; pSetBuf->ID.toi_entity.tei_instance = 0; pSetBuf->ID.toi_class = INFO_CLASS_PROTOCOL; pSetBuf->ID.toi_type = INFO_TYPE_PROVIDER; pSetBuf->ID.toi_id = IP_MIB_RTTABLE_ENTRY_ID; pSetBuf->BufferSize = sizeof(IPRouteEntry);
// Use the found route as a template for the route entry marked
// for deletion.
//
pNewRouteEntry = (IPRouteEntry* )&pSetBuf->Buffer[ 0 ]; NdisMoveMemory( pNewRouteEntry, pRouteEntry, sizeof(IPRouteEntry) ); pNewRouteEntry->ire_type = IRE_TYPE_INVALID;
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_SET_INFORMATION_EX, pDeviceObject, pSetBuf, ulSize, NULL, 0, FALSE, &event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Res, ( "TCP_SI Irp?" ) ); WPLOG( LL_A, LM_Res, ( "TCP_SI Irp?" ) ); break; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pAddress;
// Send the request to the IP stack.
//
status = IoCallDriver( pDeviceObject, pIrp ); #endif
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = iosb.Status; }
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "DHR SET_ROUTE=%d?", status ) ); WPLOG( LL_A, LM_Tdi, ( "DHR SET_ROUTE=%d?", status ) ); g_statusLastDhrSetRouteFailure = status; break; }
break; } }
if (pBuffer2) { FREE_NONPAGED( pBuffer2 ); }
TRACE( TL_V, TM_Tdi, ( "TdixDeleteHostRoute done" ) ); } while (FALSE);
if (pBuffer) { FREE_NONPAGED( pBuffer ); }
if (pTdixRoute->fUsePayloadAddr) {
ASSERT(pTdixRoute->hPayloadAddr != NULL);
ObDereferenceObject( pTdixRoute->pPayloadAddr );
// Close the payload address object
//
ZwClose(pTdixRoute->hPayloadAddr); pTdixRoute->hPayloadAddr = NULL; pTdixRoute->fUsePayloadAddr = FALSE; }
if (pTdixRoute->hCtrlAddr != NULL) {
// Close the Ctrl address object
//
ObDereferenceObject( pTdixRoute->pCtrlAddr ); ZwClose (pTdixRoute->hCtrlAddr); pTdixRoute->hCtrlAddr = NULL; }
// Remove the route context effectively unpending the operation.
//
{ BOOLEAN fDoClose; LONG lRef;
fDoClose = FALSE; NdisAcquireSpinLock( &pTdix->lock ); { if (status == STATUS_SUCCESS) { ++g_ulTdixDeleteHostRouteSuccesses; } else { ++g_ulTdixDeleteHostRouteFailures; }
ASSERT( pTdixRoute->lRef == 0 ); RemoveEntryList( &pTdixRoute->linkRoutes );
lRef = --pTdix->lRef; TRACE( TL_N, TM_Tdi, ( "TdixDHR, refs=%d", lRef ) ); if (lRef == 0) { fDoClose = TRUE; }
FREE_TDIXROUTE( pTdix, pTdixRoute ); } NdisReleaseSpinLock( &pTdix->lock );
if (fDoClose) { TdixDoClose( pTdix ); } } }
NTSTATUS TdixGetInterfaceInfo( IN TDIXCONTEXT* pTdix, IN ULONG ulIpAddress, OUT PULONG pulSpeed) { TCP_REQUEST_QUERY_INFORMATION_EX QueryBuf; PDEVICE_OBJECT pDeviceObject; NTSTATUS status; PIRP pIrp; PIO_STACK_LOCATION pIrpSp; IO_STATUS_BLOCK iosb; UCHAR pBuffer[256]; KEVENT event; IPInterfaceInfo* pInterfaceInfo;
// Get the routing table from the IP stack. This make take a few
// iterations since the size of the buffer required is not known. Set
// up the static request information first.
//
QueryBuf.ID.toi_entity.tei_entity = CL_NL_ENTITY; QueryBuf.ID.toi_entity.tei_instance = 0; QueryBuf.ID.toi_class = INFO_CLASS_PROTOCOL; QueryBuf.ID.toi_type = INFO_TYPE_PROVIDER; QueryBuf.ID.toi_id = IP_INTFC_INFO_ID; *(ULONG *)QueryBuf.Context = ulIpAddress;
pDeviceObject = IoGetRelatedDeviceObject( pTdix->pAddress ); // Set up a request to the IP stack to fill the buffer with the
// routing table and send it to the stack.
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_QUERY_INFORMATION_EX, pDeviceObject, (PVOID )&QueryBuf, sizeof(QueryBuf), pBuffer, sizeof(pBuffer), FALSE, &event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Res, ( "Build Q Irp?" ) ); WPLOG( LL_A, LM_Res, ( "Build Q Irp?" ) ); return NDIS_STATUS_RESOURCES; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pTdix->pAddress;
status = IoCallDriver( pDeviceObject, pIrp );
if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
status = iosb.Status; }
if (status == STATUS_SUCCESS) { *pulSpeed = ((IPInterfaceInfo *)pBuffer)->iii_speed; }
return status; }
//-----------------------------------------------------------------------------
// Local utility routines (alphabetically)
//-----------------------------------------------------------------------------
NTSTATUS TdixSetTdiAOOption( IN FILE_OBJECT* pAddress, IN ULONG ulOption, IN ULONG ulValue)
// Turn off UDP checksums on open UDP address object 'pAddress'.
//
{ NTSTATUS status; PDEVICE_OBJECT pDeviceObject; PIO_STACK_LOCATION pIrpSp; IO_STATUS_BLOCK iosb; PIRP pIrp; TCP_REQUEST_SET_INFORMATION_EX* pInfo; CHAR achBuf[ sizeof(*pInfo) + sizeof(ULONG) ];
pInfo = (TCP_REQUEST_SET_INFORMATION_EX* )achBuf; pInfo->ID.toi_entity.tei_entity = CL_TL_ENTITY; pInfo->ID.toi_entity.tei_instance = 0; pInfo->ID.toi_class = INFO_CLASS_PROTOCOL; pInfo->ID.toi_type = INFO_TYPE_ADDRESS_OBJECT; pInfo->ID.toi_id = ulOption;
NdisMoveMemory( pInfo->Buffer, &ulValue, sizeof(ulValue) ); pInfo->BufferSize = sizeof(ulValue);
pDeviceObject = IoGetRelatedDeviceObject( pAddress );
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_WSH_SET_INFORMATION_EX, pDeviceObject, (PVOID )pInfo, sizeof(*pInfo) + sizeof(ulValue), NULL, 0, FALSE, NULL, &iosb );
if (!pIrp) { TRACE( TL_A, TM_Res, ( "TdixSetTdiAOOption Irp?" ) ); WPLOG( LL_A, LM_Res, ( "TdixSetTdiAOOption Irp?" ) ); return NDIS_STATUS_RESOURCES; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pAddress;
status = IoCallDriver( pDeviceObject, pIrp );
if(NT_SUCCESS(status)) { status = iosb.Status; }
return status; }
VOID TdixDisableUdpChecksums( IN FILE_OBJECT* pAddress )
// Turn off UDP checksums on open UDP address object 'pAddress'.
//
{ NTSTATUS status;
status = TdixSetTdiAOOption(pAddress, AO_OPTION_XSUM, FALSE);
TRACE( TL_I, TM_Tdi, ( "Disable XSUMs($%p)=$%08x", pAddress, status ) ); }
VOID TdixEnableIpPktInfo( IN FILE_OBJECT* pAddress )
// Turn on IP_PKTINFO on open UDP address object 'pAddress'.
//
{ NTSTATUS status;
status = TdixSetTdiAOOption(pAddress, AO_OPTION_IP_PKTINFO, TRUE);
TRACE( TL_I, TM_Tdi, ( "Enable IP_PKTINFO ($%p)=$%08x", pAddress, status ) ); }
VOID TdixEnableIpHdrIncl( IN FILE_OBJECT* pAddress )
// Turn on IP_HDRINCL on raw IP address object.
//
{ NTSTATUS status;
status = TdixSetTdiAOOption(pAddress, AO_OPTION_IP_HDRINCL, TRUE);
TRACE( TL_I, TM_Tdi, ( "Enable IP_HDRINCL($%p)=$%08x", pAddress, status ) ); }
VOID TdixDoClose( TDIXCONTEXT* pTdix )
// Called when 'pTdix->lRef' reaches 0 to close down the TDI session.
// 'PTdix' is the transport context for the session.
//
{ TRACE( TL_N, TM_Tdi, ( "TdixDoClose" ) );
if (pTdix->pAddress) { // Install a NULL handler, effectively uninstalling.
//
TdixInstallEventHandler( pTdix->pAddress, TDI_EVENT_RECEIVE_DATAGRAM, NULL, pTdix );
ObDereferenceObject( pTdix->pAddress ); pTdix->pAddress = NULL;
// If have a valid transport address, the lookaside lists were also
// initialized.
//
NdisDeleteNPagedLookasideList( &pTdix->llistRdg ); NdisDeleteNPagedLookasideList( &pTdix->llistSdg ); }
if (pTdix->hAddress) { ZwClose( pTdix->hAddress ); pTdix->hAddress = NULL; } if (pTdix->hRawAddress) { ZwClose( pTdix->hRawAddress ); pTdix->hRawAddress = NULL; }
#if ROUTEWITHREF
if (pTdix->hIpStackAddress) { ZwClose( pTdix->hIpStackAddress ); pTdix->hIpStackAddress = NULL; } #endif
if (pTdix->pIpStackAddress) { ObDereferenceObject( pTdix->pIpStackAddress ); pTdix->pIpStackAddress = NULL; }
// Mark the operation complete.
//
NdisAcquireSpinLock( &pTdix->lock ); { ASSERT( pTdix->lRef == 0 ); ClearFlags( &pTdix->ulFlags, TDIXF_Pending ); } NdisReleaseSpinLock( &pTdix->lock ); }
VOID TdixExtractAddress( IN TDIXCONTEXT* pTdix, OUT TDIXRDGINFO* pRdg, IN VOID* pTransportAddress, IN LONG lTransportAddressLen, IN VOID* Options, IN LONG OptionsLength) // Fills callers '*pAddress' with the useful part of the transport address
// 'pTransportAddress' of length 'lTransportAddressLen'. 'PTdix' is our
// context.
//
{ TDIXIPADDRESS* pAddress = &pRdg->source; TA_IP_ADDRESS* pTAddress = (TA_IP_ADDRESS* )pTransportAddress;
ASSERT( lTransportAddressLen == sizeof(TA_IP_ADDRESS) ); ASSERT( pTAddress->TAAddressCount == 1 ); ASSERT( pTAddress->Address[ 0 ].AddressType == TDI_ADDRESS_TYPE_IP ); ASSERT( pTAddress->Address[ 0 ].AddressLength == TDI_ADDRESS_LENGTH_IP );
// source address
pAddress->ulIpAddress = pTAddress->Address[ 0 ].Address[ 0 ].in_addr; pAddress->sUdpPort = pTAddress->Address[ 0 ].Address[ 0 ].sin_port;
// dest address
if(Options) { IN_PKTINFO* pktinfo = (IN_PKTINFO*)TDI_CMSG_DATA(Options);
ASSERT(((PTDI_CMSGHDR)Options)->cmsg_type == IP_PKTINFO);
// Fill in the ancillary data object header information.
pRdg->dest.ulIpAddress = pktinfo->ipi_addr;
// Get the index of the local interface on which the packet arrived.
pRdg->dest.ifindex = pktinfo->ipi_ifindex; } }
NTSTATUS TdixInstallEventHandler( IN FILE_OBJECT* pAddress, IN INT nEventType, IN VOID* pfuncEventHandler, IN VOID* pEventContext )
// Install a TDI event handler routine 'pfuncEventHandler' to be called
// when events of type 'nEventType' occur. 'PEventContext' is passed to
// the handler. 'PAddress' is the transport address object.
//
// This call must be made at PASSIVE IRQL.
//
// Returns 0 if successful or an error code.
//
{ NTSTATUS status; PIRP pIrp;
TRACE( TL_N, TM_Tdi, ( "TdixInstallEventHandler" ) );
// Allocate a "set event" IRP with base initialization.
//
pIrp = TdiBuildInternalDeviceControlIrp( TDI_SET_EVENT_HANDLER, pAddress->DeviceObject, pAddress, NULL, NULL );
if (!pIrp) { TRACE( TL_A, TM_Res, ( "TdiBuildIDCIrp?" ) ); WPLOG( LL_A, LM_Res, ( "TdiBuildIDCIrp?" ) ); return NDIS_STATUS_RESOURCES; }
// Complete the "set event" IRP initialization.
//
TdiBuildSetEventHandler( pIrp, pAddress->DeviceObject, pAddress, NULL, NULL, nEventType, pfuncEventHandler, pEventContext );
// Tell the I/O manager to pass our IRP to the transport for processing.
//
status = IoCallDriver( pAddress->DeviceObject, pIrp ); if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "IoCallDriver=$%08x?", status ) ); WPLOG( LL_A, LM_Tdi, ( "IoCallDriver=$%08x?", status ) ); return status; }
TRACE( TL_V, TM_Tdi, ( "TdixInstallEventHandler=0" ) ); return STATUS_SUCCESS; }
NTSTATUS TdixOpenIpAddress( IN UNICODE_STRING* puniDevice, IN TDIXIPADDRESS* pTdixAddr, OUT HANDLE* phAddress, OUT FILE_OBJECT** ppFileObject )
// Open a transport address for the IP-based protocol with name
// '*puniDevice' and port 'sPort'. 'SPort' may be 0 indicating "any"
// port. "Any" address is assumed. Loads the open address object handle
// into '*phAddress' and the referenced file object into '*ppFileObject'.
//
// Returns STATUS_SUCCESS or an error code.
//
{ NTSTATUS status; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK iosb; FILE_FULL_EA_INFORMATION *pEa; ULONG ulEaLength; TA_IP_ADDRESS UNALIGNED *pTaIp; TDI_ADDRESS_IP UNALIGNED *pTdiIp; CHAR achEa[ 100 ]; HANDLE hAddress; FILE_OBJECT* pFileObject;
hAddress = NULL; pFileObject = NULL;
// Initialize object attributes, a parameter needed to open the device.
//
InitializeObjectAttributes( &oa, puniDevice, OBJ_CASE_INSENSITIVE, NULL, NULL );
// Set up the extended attribute that tells the IP stack the IP
// address/port from which we want to receive. For raw IP we say "any
// address and port" and for UDP we say "any address on the L2TP
// port". Is this an ugly structure or what?
//
ASSERT( sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS) <= 100);
pEa = (FILE_FULL_EA_INFORMATION* )achEa; pEa->NextEntryOffset = 0; pEa->Flags = 0; pEa->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; pEa->EaValueLength = sizeof(TA_IP_ADDRESS); NdisMoveMemory( pEa->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH );
// Note: The ZwCreateFile wants the sized name to have a null
// terminator character (go figure), so add it and account for
// it with the "+ 1" below.
//
pEa->EaName[ TDI_TRANSPORT_ADDRESS_LENGTH ] = '\0';
pTaIp = (TA_IP_ADDRESS UNALIGNED* ) (pEa->EaName + TDI_TRANSPORT_ADDRESS_LENGTH + 1); pTaIp->TAAddressCount = 1; pTaIp->Address[ 0 ].AddressLength = TDI_ADDRESS_LENGTH_IP; pTaIp->Address[ 0 ].AddressType = TDI_ADDRESS_TYPE_IP;
pTdiIp = &pTaIp->Address[ 0 ].Address[ 0 ]; pTdiIp->sin_port = pTdixAddr->sUdpPort; pTdiIp->in_addr = pTdixAddr->ulIpAddress; NdisZeroMemory( pTdiIp->sin_zero, sizeof(pTdiIp->sin_zero) );
ulEaLength = (ULONG )((CHAR* )(pTaIp + 1) - (CHAR* )pEa);
// Open the transport address.
//
status = ZwCreateFile( &hAddress, FILE_READ_DATA | FILE_WRITE_DATA, &oa, &iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_WRITE, FILE_OPEN, 0, pEa, ulEaLength );
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "ZwCreateFile(%S)=$%08x,ios=$%x?", puniDevice->Buffer, status, iosb.Information ) ); WPLOG( LL_A, LM_Tdi, ( "ZwCreateFile(%S)=$%08x", puniDevice->Buffer, status) ); return status; }
// Get the object address from the handle. This also checks our
// permissions on the object.
//
status = ObReferenceObjectByHandle( hAddress, 0, NULL, KernelMode, &pFileObject, NULL );
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "ObRefObjByHandle(%S)=$%08x?", puniDevice->Buffer, status ) ); WPLOG( LL_A, LM_Tdi, ( "ObRefObjByHandle(%S)=$%08x?", puniDevice->Buffer, status ) ); ZwClose( hAddress ); return status; }
*phAddress = hAddress; *ppFileObject = pFileObject; return STATUS_SUCCESS; }
NTSTATUS TdixReceiveDatagramHandler( IN PVOID TdiEventContext, IN LONG SourceAddressLength, IN PVOID SourceAddress, IN LONG OptionsLength, IN PVOID Options, IN ULONG ReceiveDatagramFlags, IN ULONG BytesIndicated, IN ULONG BytesAvailable, OUT ULONG* BytesTaken, IN PVOID Tsdu, OUT PIRP* IoRequestPacket )
// Standard TDI ClientEventReceiveDatagram indication handler. See TDI
// doc. Runs at DISPATCH IRQL.
//
{ TDIXCONTEXT* pTdix; TDIXRDGINFO* pRdg; CHAR* pBuffer; NDIS_BUFFER* pNdisBuffer; PIRP pIrp;
TRACE( TL_N, TM_Tdi, ( "TdixRecvDg, f=$%08x bi=%d, ba=%d", ReceiveDatagramFlags, BytesIndicated, BytesAvailable ) );
if (BytesAvailable > L2TP_FrameBufferSize) {
// We received a larger datagram then expected or can handle,
// so we just ignore the datagram.
//
ASSERT( !"BytesAvailable > L2TP_FrameBufferSize?" ); *IoRequestPacket = NULL; *BytesTaken = 0; return STATUS_SUCCESS; }
pTdix = (TDIXCONTEXT* )TdiEventContext;
// Allocate a receive pBuffer from TDIX client's pool.
//
pBuffer = GetBufferFromPool( pTdix->pPoolNdisBuffers ); if (!pBuffer) { // Not a whole lot we can do with this unlikely error from inside this
// handler, so we just ignore the datagram.
//
return STATUS_SUCCESS; }
// Allocate a context for this read-datagram from our lookaside list.
//
pRdg = ALLOC_TDIXRDGINFO( pTdix ); if (pRdg) { // Fill in the read-datagram context with the information that won't
// otherwise be available in the completion routine.
//
pRdg->pTdix = pTdix; pRdg->pBuffer = pBuffer; pRdg->ulBufferLen = BytesAvailable;
// Extract the useful IP address from the more general transport
// address information.
//
TdixExtractAddress( pTdix, pRdg, SourceAddress, SourceAddressLength, Options, OptionsLength); } else { // Not a whole lot we can do with this unlikely error from inside this
// handler, so we just ignore the datagram.
//
FreeBufferToPool( pTdix->pPoolNdisBuffers, pBuffer, TRUE ); return STATUS_SUCCESS; }
if (BytesIndicated < BytesAvailable) { // The less common case where all the information is not immediately
// available. Allocate an IRP to request the data.
//
#if ALLOCATEIRPS
// Allocate the IRP directly.
//
pIrp = IoAllocateIrp( pTdix->pAddress->DeviceObject->StackSize, FALSE ); #else
// Allocate a "receive datagram" IRP with base initialization.
//
pIrp = TdiBuildInternalDeviceControlIrp( TDI_RECEIVE_DATAGRAM, pTdix->pAddress->DeviceObject, pTdix->pAddress, NULL, NULL ); #endif
if (!pIrp) { // Not a whole lot we can do with this unlikely error from inside
// this handler, so we just ignore the datagram.
//
FreeBufferToPool( pTdix->pPoolNdisBuffers, pBuffer, TRUE ); FREE_TDIXRDGINFO( pTdix, pRdg ); return STATUS_SUCCESS; }
pNdisBuffer = NdisBufferFromBuffer( pBuffer );
// Complete the "receive datagram" IRP initialization.
//
TdiBuildReceiveDatagram( pIrp, pTdix->pAddress->DeviceObject, pTdix->pAddress, TdixReceiveDatagramComplete, pRdg, pNdisBuffer, 0, NULL, NULL, 0 );
// Adjust the IRP's stack location to make the transport's stack
// current. Normally IoCallDriver handles this, but this IRP doesn't
// go thru IoCallDriver. Seems like it would be the transport's job
// to make this adjustment, but IP for one doesn't seem to do it.
// There is a similar adjustment in both the redirector and PPTP.
//
IoSetNextIrpStackLocation( pIrp );
*IoRequestPacket = pIrp; *BytesTaken = 0;
return STATUS_MORE_PROCESSING_REQUIRED; } else { // The common case where all the information is immediately available.
// Copy it to from the transport buffer and call client's completion
// handler directly. See bug 329371.
//
NdisMoveMemory( pBuffer, (CHAR* )Tsdu, BytesIndicated ); TdixReceiveDatagramComplete( NULL, NULL, pRdg );
*IoRequestPacket = NULL; *BytesTaken = BytesIndicated;
return STATUS_SUCCESS; }
// Not reached.
}
NTSTATUS TdixReceiveDatagramComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
// Standard NT I/O completion routine. See DDK doc. Called with a NULL
// 'DeviceObject' and 'Irp' to complete the fast-past Irp-less receives.
//
{ TDIXRDGINFO* pRdg; BOOLEAN fBad; ULONG ulOffset;
pRdg = (TDIXRDGINFO* )Context;
TRACE( TL_N, TM_Tdi, ( "TdixRecvDgComp" ) );
fBad = FALSE; ulOffset = 0;
if (pRdg->pTdix->mediatype == TMT_RawIp) { UCHAR uchVersion;
// The raw IP stack doesn't strip the IP header from the received
// datagram for some reason, so calculate the offset to the "real"
// data at the end of the IP header.
//
uchVersion = *((UCHAR* )pRdg->pBuffer) >> 4; if (uchVersion == 4) { // Good, it's IP version 4. Find the length of the IP header,
// which can vary depending on the presence of option fields.
//
ulOffset = (*((UCHAR* )pRdg->pBuffer) & 0x0F) * sizeof(ULONG); } else { // It's not IP version 4, the only version we handle.
//
TRACE( TL_A, TM_Tdi, ( "Not IPv4? v=%d?", (ULONG )uchVersion ) ); WPLOG( LL_A, LM_Tdi, ( "Not IPv4? v=%d?", (ULONG )uchVersion ) ); fBad = TRUE; } }
if (!fBad && (!Irp || Irp->IoStatus.Status == STATUS_SUCCESS)) { // Pass the result to the TDIX client's handler.
//
pRdg->pTdix->pReceiveHandler( pRdg->pTdix, pRdg, pRdg->pBuffer, ulOffset, pRdg->ulBufferLen ); }
// Free the read-datagram context.
//
FREE_TDIXRDGINFO( pRdg->pTdix, pRdg );
#if ALLOCATEIRPS
// Release the IRP resources, if any, and tell the I/O manager to forget
// it existed in the standard way.
//
if (Irp) { IoFreeIrp( Irp ); return STATUS_MORE_PROCESSING_REQUIRED; } #endif
// Let the I/O manager release the IRP resources, if any.
//
return STATUS_SUCCESS; }
TDIXROUTE* TdixRouteFromIpAddress( IN TDIXCONTEXT* pTdix, IN ULONG ulIpAddress)
// Returns the host route context associated with IP address 'ulIpAddress'
// from the TDIX context 'pTdix's list of host routes, or NULL if none.
// 'UlIpAddress' is in network byte order.
//
// IMPORTANT: The caller must hold 'pTdix->lock'.
//
{ LIST_ENTRY* pLink;
for (pLink = pTdix->listRoutes.Flink; pLink != &pTdix->listRoutes; pLink = pLink->Flink) { TDIXROUTE* pRoute;
pRoute = CONTAINING_RECORD( pLink, TDIXROUTE, linkRoutes ); if (pRoute->ulIpAddress == ulIpAddress) { return pRoute; } }
return NULL; }
NTSTATUS TdixSendComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
// Standard NT I/O completion routine. See DDK doc.
//
{ TDIXSDGINFO* pSdg;
if (Irp->IoStatus.Status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "TdixSendComp, s=$%08x?", Irp->IoStatus.Status ) ); WPLOG( LL_A, LM_Tdi, ( "TdixSendComp, s=$%08x?", Irp->IoStatus.Status ) ); }
pSdg = (TDIXSDGINFO* )Context;
// Pass the result to the TDIX client's handler.
//
pSdg->pSendCompleteHandler( pSdg->pTdix, pSdg->pContext1, pSdg->pContext2, pSdg->pBuffer );
// Free the send-complete context.
//
FREE_TDIXSDGINFO( pSdg->pTdix, pSdg );
#if ALLOCATEIRPS
// Release the IRP resources and tell the I/O manager to forget it existed
// in the standard way.
//
IoFreeIrp( Irp ); return STATUS_MORE_PROCESSING_REQUIRED; #else
// Let the I/O manager release the IRP resources.
//
return STATUS_SUCCESS; #endif
}
NTSTATUS TdixSendDatagramComplete( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context )
// Standard NT I/O completion routine. See DDK doc.
//
{ TDIXSDGINFO* pSdg;
if (Irp->IoStatus.Status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "TdixSendDgComp, s=$%08x?", Irp->IoStatus.Status ) ); WPLOG( LL_A, LM_Tdi, ( "TdixSendDgComp, s=$%08x?", Irp->IoStatus.Status ) ); }
pSdg = (TDIXSDGINFO* )Context;
// Pass the result to the TDIX client's handler.
//
pSdg->pSendCompleteHandler( pSdg->pTdix, pSdg->pContext1, pSdg->pContext2, pSdg->pBuffer );
// Free the send-complete context.
//
FREE_TDIXSDGINFO( pSdg->pTdix, pSdg );
#if ALLOCATEIRPS
// Release the IRP resources and tell the I/O manager to forget it existed
// in the standard way.
//
IoFreeIrp( Irp ); return STATUS_MORE_PROCESSING_REQUIRED; #else
// Let the I/O manager release the IRP resources.
//
return STATUS_SUCCESS; #endif
}
NTSTATUS TdixConnectAddrInterface( FILE_OBJECT* pFileObj, HANDLE hFileHandle, TDIXROUTE* pTdixRoute ) { NTSTATUS status; PDEVICE_OBJECT pDeviceObj; PIO_STACK_LOCATION pIrpSp; IO_STATUS_BLOCK iosb; PIRP pIrp; TCP_REQUEST_SET_INFORMATION_EX* pInfo; CHAR achBuf[ sizeof(*pInfo) + sizeof(ULONG) ]; ULONG ulValue; TDI_CONNECTION_INFORMATION RequestConnInfo; KEVENT Event; TA_IP_ADDRESS taip; TDI_ADDRESS_IP* pTdiIp;
pDeviceObj = IoGetRelatedDeviceObject( pFileObj );
#if 0
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
pIrp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS, pDeviceObj, pFileObj, &Event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Tdi, ( "SetIfcIndex Associate Irp?" ) ); return !STATUS_SUCCESS; }
TdiBuildAssociateAddress(pIrp, pDeviceObj, pFileObj, NULL, NULL, hFileHandle);
status = IoCallDriver( pDeviceObj, pIrp );
if (status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0); }
if (iosb.Status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "SetIfcIndex Associate=%x?", status ) ); return (iosb.Status); } #endif
pInfo = (TCP_REQUEST_SET_INFORMATION_EX* )achBuf; pInfo->ID.toi_entity.tei_entity = CL_TL_ENTITY; pInfo->ID.toi_entity.tei_instance = 0; pInfo->ID.toi_class = INFO_CLASS_PROTOCOL; pInfo->ID.toi_type = INFO_TYPE_ADDRESS_OBJECT; pInfo->ID.toi_id = AO_OPTION_IP_UCASTIF;
ulValue = pTdixRoute->InterfaceIndex;
NdisMoveMemory( pInfo->Buffer, &ulValue, sizeof(ulValue) ); pInfo->BufferSize = sizeof(ulValue);
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
pIrp = IoBuildDeviceIoControlRequest( IOCTL_TCP_WSH_SET_INFORMATION_EX, pDeviceObj, (PVOID )pInfo, sizeof(*pInfo) + sizeof(ulValue), NULL, 0, FALSE, &Event, &iosb );
if (!pIrp) { TRACE( TL_A, TM_Tdi, ( "SetIfcIndex Irp?" ) ); return !STATUS_SUCCESS; }
pIrpSp = IoGetNextIrpStackLocation( pIrp ); pIrpSp->FileObject = pFileObj;
status = IoCallDriver( pDeviceObj, pIrp );
if (status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); status = iosb.Status; }
if (status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "SetIfcIndex=%x?", status ) ); return status; }
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
pIrp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT, pDeviceObj, pFileObj, &Event, &iosb);
if (!pIrp) { TRACE( TL_A, TM_Res, ( "SetIfcIndex ConnectIrp?" ) ); WPLOG( LL_A, LM_Res, ( "SetIfcIndex ConnectIrp?" ) ); return !STATUS_SUCCESS; }
// Put the destination IP address in the "connection" structure as TDI
// expects.
//
taip.TAAddressCount = 1; taip.Address[ 0 ].AddressLength = TDI_ADDRESS_LENGTH_IP; taip.Address[ 0 ].AddressType = TDI_ADDRESS_TYPE_IP;
pTdiIp = &taip.Address[ 0 ].Address[ 0 ]; pTdiIp->sin_port = pTdixRoute->sPort; pTdiIp->in_addr = pTdixRoute->ulIpAddress; NdisZeroMemory( pTdiIp->sin_zero, sizeof(pTdiIp->sin_zero) );
RequestConnInfo.Options = NULL; RequestConnInfo.OptionsLength = 0; RequestConnInfo.RemoteAddress = &taip; RequestConnInfo.RemoteAddressLength = sizeof(taip); RequestConnInfo.UserData = NULL; RequestConnInfo.UserDataLength = 0;
TdiBuildConnect(pIrp, pDeviceObj, pFileObj, NULL, NULL, 0, &RequestConnInfo, NULL);
status = IoCallDriver( pDeviceObj, pIrp );
if (status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 0); }
if (iosb.Status != STATUS_SUCCESS) { TRACE( TL_A, TM_Tdi, ( "SetIfcIndex Connect=%x?", status ) ); WPLOG( LL_A, LM_Tdi, ( "SetIfcIndex Connect=%x?", status ) ); return (iosb.Status); }
return (STATUS_SUCCESS); }
|