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

3102 lines
90 KiB
C

// 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"
#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
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->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 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);
}
else
{
TDIXIPADDRESS TdixIpAddress;
ASSERT( pTdix->mediatype == TMT_RawIp );
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->hAddress,
&pTdix->pAddress );
if (status != STATUS_SUCCESS)
{
break;
}
}
// 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
{
ASSERT( !"Alloc SDG?" );
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_Tdi, ( "Alloc 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 ) );
// Not used in this function!
//
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
{
ASSERT( !"Alloc SDG?" );
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(
pTdix->pAddress->DeviceObject->StackSize, FALSE );
#else
// Allocate a "send datagram" IRP with base initialization.
//
pIrp =
TdiBuildInternalDeviceControlIrp(
TDI_SEND_DATAGRAM,
pTdix->pAddress->DeviceObject,
pTdix->pAddress,
NULL,
NULL );
#endif
if (!pIrp)
{
TRACE( TL_A, TM_Tdi, ( "Alloc IRP?" ) );
status = NDIS_STATUS_RESOURCES;
break;
}
// Complete the "send datagram" IRP initialization.
//
TdiBuildSendDatagram(
pIrp,
pTdix->pAddress->DeviceObject,
pTdix->pAddress,
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( pTdix->pAddress->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);
ObDereferenceObject( pUdpContext->pPayloadAddr );
// Close the payload address object
//
ZwClose(pUdpContext->hPayloadAddr);
pUdpContext->hPayloadAddr = NULL;
pUdpContext->fUsePayloadAddr = FALSE;
}
if (pUdpContext->hCtrlAddr != NULL) {
// Close the Ctrl address object
//
ObDereferenceObject( pUdpContext->pCtrlAddr );
ZwClose (pUdpContext->hCtrlAddr);
pUdpContext->hCtrlAddr = NULL;
}
}
NDIS_STATUS
TdixSetupConnection(
IN TDIXCONTEXT* pTdix,
IN ULONG ulIpAddress,
IN SHORT sPort,
IN TDIXROUTE *pTdixRoute,
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);
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress));
if(sPort != 0)
{
pTdixRoute->sPort = sPort;
}
else
{
pTdixRoute->sPort = (SHORT)(htons(L2TP_UdpPort));
}
TdixIpAddress.sUdpPort = (SHORT)(htons(L2TP_UdpPort));
TRACE( TL_A, TM_Tdi, ( "sPort for $%08x set to %d",
ulIpAddress, (UINT) ntohs(pTdixRoute->sPort ) ));
// 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, ( "AHR OpenCtrlAddr=%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, ( "AHR ConnectCtrlAddr=%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)
{
TDIXIPADDRESS TdixIpAddress;
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress));
TdixIpAddress.sUdpPort = (SHORT)(htons(L2TP_UdpPort));
// 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 ) );
pUdpContext->hPayloadAddr = NULL;
break;
}
pUdpContext->fUsePayloadAddr = TRUE;
TdixDisableUdpChecksums( pUdpContext->pPayloadAddr );
// 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 ) );
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 SHORT sPort)
// 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 ) );
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_Tdi, ( "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_Tdi, ( "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 ) );
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))
{
// 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_Tdi, ( "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_Tdi, ( "Build S 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_Tdi, ( "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_Tdi, ( "Build S 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 ) );
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 ) );
}
else
{
++g_ulNoBestRoute;
TRACE( TL_A, TM_Tdi, ( "No best route for $%08x?", ulIpAddress ) );
break;
}
}
while (FALSE);
if (pBuffer)
{
FREE_NONPAGED( pBuffer );
}
if (pBuffer2)
{
FREE_NONPAGED( pBuffer2 );
}
// Update the route context.
//
{
BOOLEAN fDoClose;
LONG lRef;
fDoClose = FALSE;
NdisAcquireSpinLock( &pTdix->lock );
{
pTdixRoute->fUsedNonL2tpRoute = fUsedNonL2tpRoute;
if (status == STATUS_SUCCESS)
{
++g_ulTdixAddHostRouteSuccesses;
pTdixRoute->fPending = FALSE;
}
else
{
++g_ulTdixAddHostRouteFailures;
RemoveEntryList( &pTdixRoute->linkRoutes );
lRef = --pTdix->lRef;
TRACE( TL_A, TM_Tdi, ( "TdixAHR fail, refs=%d", lRef ) );
if (lRef <= 0)
{
fDoClose = TRUE;
}
FREE_TDIXROUTE( pTdxi, pTdixRoute );
pTdixRoute = NULL;
}
}
NdisReleaseSpinLock( &pTdix->lock );
if (fDoClose)
{
TdixDoClose( pTdix );
}
}
#if 0
if ((status == STATUS_SUCCESS) &&
(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);
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress));
if(sPort != 0)
{
pTdixRoute->sPort = sPort;
}
else
{
pTdixRoute->sPort = (SHORT)(htons(L2TP_UdpPort));
}
TdixIpAddress.sUdpPort = (SHORT)(htons(L2TP_UdpPort));
TRACE( TL_A, TM_Tdi, ( "sPort for $%08x set to %d",
ulIpAddress, (UINT) ntohs(pTdixRoute->sPort ) ));
// Build the UDP device name as a counted string.
//
status = TdixOpenIpAddress(&uniDevice,
&TdixIpAddress,
&pTdixRoute->hCtrlAddr,
&pTdixRoute->pCtrlAddr );
if (status != STATUS_SUCCESS)
{
TRACE( TL_A, TM_Tdi, ( "AHR OpenCtrlAddr=%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(pTdixRoute->pCtrlAddr,
pTdixRoute->hCtrlAddr,
pTdixRoute);
if (status != STATUS_SUCCESS)
{
TRACE( TL_A, TM_Tdi, ( "AHR ConnectCtrlAddr=%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)
{
TDIXIPADDRESS TdixIpAddress;
NdisZeroMemory(&TdixIpAddress, sizeof(TdixIpAddress));
TdixIpAddress.sUdpPort = (SHORT)(htons(L2TP_UdpPort));
// Open the address object
//
status = TdixOpenIpAddress(&uniDevice,
&TdixIpAddress,
&pTdixRoute->hPayloadAddr,
&pTdixRoute->pPayloadAddr );
if (status != STATUS_SUCCESS)
{
TRACE( TL_A, TM_Tdi, ( "AHR OpenPayloadAddr=%x?", status ) );
pTdixRoute->hPayloadAddr = NULL;
break;
}
pTdixRoute->fUsePayloadAddr = TRUE;
TdixDisableUdpChecksums( pTdixRoute->pPayloadAddr );
// 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(pTdixRoute->pPayloadAddr,
pTdixRoute->hPayloadAddr,
pTdixRoute );
if (status != STATUS_SUCCESS)
{
TRACE( TL_A, TM_Tdi, ( "AHR ConnectPayloadAddr=%x?", status ) );
break;
}
}
else
{
pTdixRoute->hPayloadAddr = pTdixRoute->hCtrlAddr;
pTdixRoute->pPayloadAddr = pTdixRoute->pCtrlAddr;
TRACE( TL_I, TM_Tdi, ( "AHR Ctrl==Payload") );
}
} while ( FALSE );
if (status != STATUS_SUCCESS) {
TdixDeleteHostRoute(pTdix, ulIpAddress);
pTdixRoute = NULL;
}
}
#endif
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_Tdi, ( "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_Tdi, ( "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 ) );
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_Tdi, ( "!pBuffer2" ) );
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_Tdi, ( "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_Tdi, ( "!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_Tdi, ( "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 ) );
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_Tdi, ( "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_Tdi, ( "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
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 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_Tdi, ( "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 ) );
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=$%08x?",
puniDevice->Buffer, status, iosb.Information ) );
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 ) );
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.
//
ASSERT( !"GetBfromP?" );
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 );
ASSERT( !"AllocRdg?" );
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 );
ASSERT( !"Alloc IRP?" );
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 ) );
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;
DBG_if (Irp->IoStatus.Status != STATUS_SUCCESS)
{
TRACE( TL_A, TM_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;
DBG_if (Irp->IoStatus.Status != STATUS_SUCCESS)
{
TRACE( TL_A, TM_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;
KEVENT event;
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_Tdi, ( "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 ) );
return (iosb.Status);
}
return (STATUS_SUCCESS);
}