// 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); }