Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4281 lines
113 KiB

/*++
Copyright (c) 2000-2002 Microsoft Corporation
Module Name:
ultci.c - UL TrafficControl Interface
Abstract:
This module implements a wrapper for QoS TC (Traffic Control)
Interface since the Kernel level API don't exist at this time.
Any HTTP module can use this interface to invoke QoS calls.
Author:
Ali Ediz Turkoglu (aliTu) 28-Jul-2000 Created a draft
version
Revision History:
Ali Ediz Turkoglu (aliTu) 03-11-2000 Modified to handle
Flow & Filter (re)config
as well as various other
major changes.
--*/
#include "precomp.h"
LIST_ENTRY g_TciIfcListHead = {NULL,NULL};
BOOLEAN g_InitTciCalled = FALSE;
//
// GPC handles to talk to
//
HANDLE g_GpcFileHandle = NULL; // result of CreateFile on GPC device
GPC_HANDLE g_GpcClientHandle = NULL; // result of GPC client registration
//
// For querying the interface info like index & mtu size
//
HANDLE g_TcpDeviceHandle = NULL;
//
// Shows if PSCHED is installed or not, protected by its
// private push lock.
//
BOOLEAN g_PSchedInstalled = FALSE;
UL_PUSH_LOCK g_PSchedStatePushLock;
//
// Optional Filter Stats
//
#if ENABLE_TC_STATS
typedef struct _TC_FILTER_STATS {
LONG Add;
LONG Delete;
LONG AddFailure;
LONG DeleteFailure;
} TC_FILTER_STATS, *PTC_FILTER_STATS;
TC_FILTER_STATS g_TcStats = { 0, 0, 0, 0 };
#define INCREMENT_FILTER_ADD() \
InterlockedIncrement( &g_TcStats.Add )
#define INCREMENT_FILTER_ADD_FAILURE() \
InterlockedIncrement( &g_TcStats.AddFailure )
#define INCREMENT_FILTER_DELETE() \
InterlockedIncrement( &g_TcStats.Delete )
#define INCREMENT_FILTER_DELETE_FAILURE() \
InterlockedIncrement( &g_TcStats.DeleteFailure )
#else
#define INCREMENT_FILTER_ADD()
#define INCREMENT_FILTER_ADD_FAILURE()
#define INCREMENT_FILTER_DELETE()
#define INCREMENT_FILTER_DELETE_FAILURE()
#endif
//
// For interface notifications
//
PVOID g_TcInterfaceUpNotificationObject = NULL;
PVOID g_TcInterfaceDownNotificationObject = NULL;
PVOID g_TcInterfaceChangeNotificationObject = NULL;
//
// Simple macro for interface (ref) tracing.
// Actually we don't have the ref yet, but when
// and if we have we can use the 3rd param.
//
#define INT_TRACE(pTcIfc,Action) \
WRITE_REF_TRACE_LOG( \
(pTcIfc)->pTraceLog, \
REF_ACTION_ ## Action, \
0, \
(pTcIfc), \
__FILE__, \
__LINE__ \
)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, UlTcInitialize)
#pragma alloc_text(PAGE, UlTcTerminate)
#pragma alloc_text(PAGE, UlpTcInitializeGpc)
#pragma alloc_text(PAGE, UlpTcRegisterGpcClient)
#pragma alloc_text(PAGE, UlpTcDeRegisterGpcClient)
#pragma alloc_text(PAGE, UlpTcGetFriendlyNames)
#pragma alloc_text(PAGE, UlpTcReleaseAll)
#pragma alloc_text(PAGE, UlpTcCloseInterface)
#pragma alloc_text(PAGE, UlpTcCloseAllInterfaces)
#pragma alloc_text(PAGE, UlpTcDeleteFlow)
#pragma alloc_text(PAGE, UlpAddFlow)
#pragma alloc_text(PAGE, UlpModifyFlow)
#pragma alloc_text(PAGE, UlTcAddFlows)
#pragma alloc_text(PAGE, UlTcModifyFlows)
#pragma alloc_text(PAGE, UlTcRemoveFlows)
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlpRemoveFilterEntry
NOT PAGEABLE -- UlpInsertFilterEntry
#endif
//
// Init & Terminate stuff comes here.
//
/***************************************************************************++
Routine Description:
UlTcInitialize :
Will also initiate the Gpc client registration and make few WMI calls
down to psched.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlTcInitialize (
VOID
)
{
PAGED_CODE();
ASSERT(!g_InitTciCalled);
if (!g_InitTciCalled)
{
InitializeListHead(&g_TciIfcListHead);
//
// Init locks, they will be used until termination.
//
UlInitializePushLock(
&g_pUlNonpagedData->TciIfcPushLock,
"TciIfcPushLock",
0,
UL_TCI_PUSHLOCK_TAG
);
UlInitializePushLock(
&g_PSchedStatePushLock,
"PSchedStatePushLock",
0,
UL_PSCHED_STATE_PUSHLOCK_TAG
);
//
// Attempt to init PSched and interface settings.
// It may fail if Psched's not installed.
//
UlTcInitPSched();
g_InitTciCalled = TRUE;
}
return STATUS_SUCCESS;
}
/***************************************************************************++
Routine Description:
Terminates the TCI module by releasing our TCI resource and
cleaning up all the qos stuff.
--***************************************************************************/
VOID
UlTcTerminate(
VOID
)
{
NTSTATUS Status;
PAGED_CODE();
if (g_InitTciCalled)
{
//
// Terminate the PSched related global state
//
UlAcquirePushLockExclusive(&g_PSchedStatePushLock);
if (g_PSchedInstalled)
{
//
// No more Wmi callbacks for interface changes
//
if (g_TcInterfaceUpNotificationObject!=NULL)
{
ObDereferenceObject(g_TcInterfaceUpNotificationObject);
g_TcInterfaceUpNotificationObject=NULL;
}
if(g_TcInterfaceDownNotificationObject!=NULL)
{
ObDereferenceObject(g_TcInterfaceDownNotificationObject);
g_TcInterfaceDownNotificationObject = NULL;
}
if(g_TcInterfaceChangeNotificationObject!=NULL)
{
ObDereferenceObject(g_TcInterfaceChangeNotificationObject);
g_TcInterfaceChangeNotificationObject = NULL;
}
//
// Make sure to terminate all the QoS stuff.
//
Status = UlpTcReleaseAll();
ASSERT(NT_SUCCESS(Status));
if (g_TcpDeviceHandle != NULL)
{
ZwClose(g_TcpDeviceHandle);
g_TcpDeviceHandle = NULL;
}
}
g_PSchedInstalled = FALSE;
g_InitTciCalled = FALSE;
UlReleasePushLockExclusive(&g_PSchedStatePushLock);
//
// Now terminate the global locks.
//
UlDeletePushLock( &g_pUlNonpagedData->TciIfcPushLock );
UlDeletePushLock( &g_PSchedStatePushLock );
}
UlTrace( TC, ("Http!UlTcTerminate.\n" ));
}
/***************************************************************************++
Routine Description:
Try to init global tc state. Fails if PSched is not initialized.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlTcInitPSched(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
UlAcquirePushLockExclusive(&g_PSchedStatePushLock);
if (g_PSchedInstalled) // do not attempt to reinit
goto cleanup;
Status = UlpTcInitializeGpc();
if (!NT_SUCCESS(Status))
{
UlTrace(TC,
("Http!UlTcInitPSched: InitializeGpc FAILED %08lx \n",
Status ));
goto cleanup;
}
Status = UlpTcInitializeTcpDevice();
if (!NT_SUCCESS(Status))
{
UlTrace(TC,
("Http!UlTcInitPSched: InitializeTcp FAILED %08lx \n",
Status ));
goto cleanup;
}
Status = UlpTcGetFriendlyNames();
if (!NT_SUCCESS(Status))
{
UlTrace(TC,
("Http!UlTcInitialize: GetFriendlyNames FAILED %08lx \n",
Status ));
goto cleanup;
}
Status = UlpTcRegisterForCallbacks();
if (!NT_SUCCESS(Status))
{
UlTrace(TC,
("Http!UlTcInitialize: RegisterForCallbacks FAILED %08lx \n",
Status ));
goto cleanup;
}
//
// Mark that PSched is installed & interface state is initialized !
//
g_PSchedInstalled = TRUE;
cleanup:
if (!NT_SUCCESS(Status))
{
//
// Do not forget to deregister Gpc client
// and close tcp device handle.
//
if (g_GpcClientHandle != NULL)
{
NTSTATUS TempStatus;
TempStatus = UlpTcDeRegisterGpcClient();
ASSERT(NT_SUCCESS(TempStatus));
ASSERT(g_GpcFileHandle);
ZwClose(g_GpcFileHandle);
g_GpcFileHandle= NULL;
}
if (g_TcpDeviceHandle != NULL)
{
ZwClose(g_TcpDeviceHandle);
g_TcpDeviceHandle=NULL;
}
}
UlReleasePushLockExclusive(&g_PSchedStatePushLock);
UlTrace(TC,
("Http!UlTcInitPSched: Initializing global Psched state. Status %08lx\n",
Status
));
return Status;
}
/***************************************************************************++
To check whether packet scheduler is installed and global interface state
is initialized properly or not.
Caller may decide to attempt to reinit the setting if we return FALSE.
--***************************************************************************/
BOOLEAN
UlTcPSchedInstalled(
VOID
)
{
BOOLEAN Installed;
//
// Probe the value inside the lock.
//
UlAcquirePushLockShared(&g_PSchedStatePushLock);
Installed = ( g_InitTciCalled && g_PSchedInstalled );
UlReleasePushLockShared(&g_PSchedStatePushLock);
return Installed;
}
/***************************************************************************++
Routine Description:
UlpTcInitializeGpc :
It will open the Gpc file handle and attempt to register as Gpc
client.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcInitializeGpc(
VOID
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING GpcNameString;
OBJECT_ATTRIBUTES GpcObjAttribs;
Status = STATUS_SUCCESS;
//
// Open Gpc Device Handle
//
Status = UlInitUnicodeStringEx(&GpcNameString, DD_GPC_DEVICE_NAME);
if (!NT_SUCCESS(Status))
{
goto end;
}
InitializeObjectAttributes(&GpcObjAttribs,
&GpcNameString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL
);
Status = ZwCreateFile(&g_GpcFileHandle,
SYNCHRONIZE | FILE_READ_DATA | FILE_WRITE_DATA,
&GpcObjAttribs,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0
);
if (!NT_SUCCESS(Status))
{
goto end;
}
ASSERT( g_GpcFileHandle != NULL );
UlTrace( TC, ("Http!UlpTcInitializeGpc: Gpc Device Opened. %p\n",
g_GpcFileHandle ));
//
// Register as GPC_CF_QOS Gpc Client
//
Status = UlpTcRegisterGpcClient(GPC_CF_QOS);
end:
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcRegisterGpcClient :
Will build up the necessary structures and make a register call down
to Gpc
Arguments:
CfInfoType - Should be GPC_CF_QOS for our purposes.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcRegisterGpcClient(
IN ULONG CfInfoType
)
{
NTSTATUS Status;
GPC_REGISTER_CLIENT_REQ GpcReq;
GPC_REGISTER_CLIENT_RES GpcRes;
ULONG InBuffSize;
ULONG OutBuffSize;
IO_STATUS_BLOCK IoStatBlock;
Status = STATUS_SUCCESS;
if ( g_GpcFileHandle == NULL )
{
return STATUS_INVALID_PARAMETER;
}
InBuffSize = sizeof(GPC_REGISTER_CLIENT_REQ);
OutBuffSize = sizeof(GPC_REGISTER_CLIENT_RES);
//
// In HTTP we should only register for GPC_CF_QOS.
//
ASSERT(CfInfoType == GPC_CF_QOS);
GpcReq.CfId = CfInfoType;
GpcReq.Flags = GPC_FLAGS_FRAGMENT;
GpcReq.MaxPriorities = 1;
GpcReq.ClientContext = (GPC_CLIENT_HANDLE) 0; // ???????? Possible BUGBUG ...
//GpcReq.ClientContext = (GPC_CLIENT_HANDLE)GetCurrentProcessId(); // process id
Status = UlpTcDeviceControl(g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_GPC_REGISTER_CLIENT,
&GpcReq,
InBuffSize,
&GpcRes,
OutBuffSize
);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
if (NT_SUCCESS(Status))
{
g_GpcClientHandle = GpcRes.ClientHandle;
UlTrace(TC,
("Http!UlpTcRegisterGpcClient: Gpc Client %p Registered.\n",
g_GpcClientHandle
));
}
}
if (!NT_SUCCESS(Status))
{
g_GpcClientHandle = NULL;
UlTrace(TC,
("Http!UlpTcRegisterGpcClient: FAILURE %08lx \n", Status ));
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcDeRegisterGpcClient :
Self explainatory.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcDeRegisterGpcClient(
VOID
)
{
NTSTATUS Status;
GPC_DEREGISTER_CLIENT_REQ GpcReq;
GPC_DEREGISTER_CLIENT_RES GpcRes;
ULONG InBuffSize;
ULONG OutBuffSize;
IO_STATUS_BLOCK IoStatBlock;
Status = STATUS_SUCCESS;
if (g_GpcFileHandle == NULL && g_GpcClientHandle == NULL)
{
return STATUS_INVALID_PARAMETER;
}
InBuffSize = sizeof(GPC_REGISTER_CLIENT_REQ);
OutBuffSize = sizeof(GPC_REGISTER_CLIENT_RES);
GpcReq.ClientHandle = g_GpcClientHandle;
Status = UlpTcDeviceControl(g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_GPC_DEREGISTER_CLIENT,
&GpcReq,
InBuffSize,
&GpcRes,
OutBuffSize
);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
if (NT_SUCCESS(Status))
{
g_GpcClientHandle = NULL;
UlTrace(TC,
("Http!UlpTcDeRegisterGpcClient: Client Deregistered.\n" ));
}
}
if (!NT_SUCCESS(Status))
{
UlTrace(TC,
("Http!UlpTcDeRegisterGpcClient: FAILURE %08lx \n", Status ));
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcInitializeTcpDevice :
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcInitializeTcpDevice(
VOID
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING TcpNameString;
OBJECT_ATTRIBUTES TcpObjAttribs;
Status = STATUS_SUCCESS;
//
// Open Gpc Device
//
Status = UlInitUnicodeStringEx(&TcpNameString, DD_TCP_DEVICE_NAME);
if ( !NT_SUCCESS(Status) )
{
goto end;
}
InitializeObjectAttributes(&TcpObjAttribs,
&TcpNameString,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
Status = ZwCreateFile( &g_TcpDeviceHandle,
GENERIC_EXECUTE,
&TcpObjAttribs,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN_IF,
0,
NULL,
0);
if ( !NT_SUCCESS(Status) )
{
goto end;
}
ASSERT( g_TcpDeviceHandle != NULL );
end:
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcGetInterfaceIndex :
Helper function to get the interface index from TCP for our internal
interface structure.
Arguments:
PUL_TCI_INTERFACE pIntfc - The interface we will find the index for.
--***************************************************************************/
NTSTATUS
UlpTcGetInterfaceIndex(
IN PUL_TCI_INTERFACE pIntfc
)
{
NTSTATUS Status;
IPAddrEntry *pIpAddrTbl;
ULONG IpAddrTblSize;
ULONG k;
IO_STATUS_BLOCK IoStatBlock;
TDIObjectID *ID;
TCP_REQUEST_QUERY_INFORMATION_EX trqiInBuf;
ULONG InBuffLen;
ULONG NumEntries;
IPSNMPInfo IPSnmpInfo;
NETWORK_ADDRESS UNALIGNED64 *pAddr;
NETWORK_ADDRESS_IP UNALIGNED64 *pIpNetAddr = NULL;
ULONG cAddr;
ULONG index;
//
// Initialize & Sanity check first
//
Status = STATUS_SUCCESS;
NumEntries = 0;
pIpAddrTbl = NULL;
UlTrace(TC,("Http!UlpTcGetInterfaceIndex: ....\n" ));
ASSERT( g_TcpDeviceHandle != NULL );
if (!pIntfc->pAddressListDesc->AddressList.AddressCount)
{
return Status;
}
RtlZeroMemory(&trqiInBuf,sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
InBuffLen = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX);
ID = &(trqiInBuf.ID);
ID->toi_entity.tei_entity = CL_NL_ENTITY;
ID->toi_entity.tei_instance = 0;
ID->toi_class = INFO_CLASS_PROTOCOL;
ID->toi_type = INFO_TYPE_PROVIDER;
for(;;)
{
// First, get the count of addresses.
ID->toi_id = IP_MIB_STATS_ID;
Status = UlpTcDeviceControl(
g_TcpDeviceHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_TCP_QUERY_INFORMATION_EX,
&trqiInBuf,
InBuffLen,
(PVOID)&IPSnmpInfo,
sizeof(IPSnmpInfo)
);
if (!NT_SUCCESS(Status))
{
break;
}
// Allocate a private buffer to retrieve Ip Address table from TCP
IpAddrTblSize = IPSnmpInfo.ipsi_numaddr * sizeof(IPAddrEntry);
ASSERT(NULL == pIpAddrTbl);
pIpAddrTbl = (IPAddrEntry *) UL_ALLOCATE_ARRAY(
PagedPool,
UCHAR,
IpAddrTblSize,
UL_TCI_GENERIC_POOL_TAG
);
if (pIpAddrTbl == NULL)
{
Status = STATUS_NO_MEMORY;
break;
}
RtlZeroMemory(pIpAddrTbl,IpAddrTblSize);
// Now, get the addresses.
ID->toi_id = IP_MIB_ADDRTABLE_ENTRY_ID;
Status = UlpTcDeviceControl(
g_TcpDeviceHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_TCP_QUERY_INFORMATION_EX,
&trqiInBuf,
InBuffLen,
pIpAddrTbl,
IpAddrTblSize
);
if(STATUS_BUFFER_OVERFLOW == Status)
{
// Someone has added a few more IP addresses. Let's re-do
// the count query. Free the old buffer, we'll loop back &
// re-do the count query.
UL_FREE_POOL(pIpAddrTbl, UL_TCI_GENERIC_POOL_TAG);
pIpAddrTbl = NULL;
}
else
{
break;
}
}
if(NT_SUCCESS(Status))
{
// Look at how many entries were written to the output buffer
// (pIpAddrTbl)
NumEntries = (((ULONG)IoStatBlock.Information)/sizeof(IPAddrEntry));
UlTrace(TC,
("Http!UlpTcGetInterfaceIndex: NumEntries %d\n", NumEntries ));
//
// Search for the matching IP address to IpAddr
// in the table we got back from the stack
//
for (k=0; k<NumEntries; k++)
{
cAddr = pIntfc->pAddressListDesc->AddressList.AddressCount;
pAddr = (UNALIGNED64 NETWORK_ADDRESS *)
&pIntfc->pAddressListDesc->AddressList.Address[0];
for (index = 0; index < cAddr; index++)
{
if (pAddr->AddressType == NDIS_PROTOCOL_ID_TCP_IP)
{
pIpNetAddr =
(UNALIGNED64 NETWORK_ADDRESS_IP *)&pAddr->Address[0];
if(pIpNetAddr->in_addr == pIpAddrTbl[k].iae_addr)
{
pIntfc->IfIndex = pIpAddrTbl[k].iae_index;
UlTrace(TC,
("Http!UlpTcGetInterfaceIndex: got for index %d\n",
pIntfc->IfIndex ));
goto end;
}
}
pAddr = (UNALIGNED64 NETWORK_ADDRESS *)(((PUCHAR)pAddr)
+ pAddr->AddressLength
+ FIELD_OFFSET(NETWORK_ADDRESS, Address));
}
}
}
else
{
UlTrace(TC,("Http!UlpTcGetInterfaceIndex: FAILED Status %08lx\n",
Status));
}
end:
if ( pIpAddrTbl != NULL )
{
UL_FREE_POOL( pIpAddrTbl, UL_TCI_GENERIC_POOL_TAG );
}
return Status;
}
/***************************************************************************++
Routine Description:
Allocates a interface structure from the given arguments.
Marks the interface enabled, if its address count is non-zero.
Argument:
DescSize Address list desc size in bytes
Desc Pointer to address list desc
NameLength Length is in bytes
Name Interface Name (Unicode buffer)
InstanceIDLength Length is in bytes.
InstanceID Instance Id is also unicode buffer
Return Value:
PUL_TCI_INTERFACE - Newly allocated interface structure
--***************************************************************************/
PUL_TCI_INTERFACE
UlpTcAllocateInterface(
IN ULONG DescSize,
IN PADDRESS_LIST_DESCRIPTOR Desc,
IN ULONG NameLength,
IN PUCHAR Name,
IN ULONG InstanceIDLength,
IN PUCHAR InstanceID
)
{
PUL_TCI_INTERFACE pTcIfc;
//
// Sanity Checks
//
PAGED_CODE();
ASSERT(NameLength <= MAX_STRING_LENGTH);
ASSERT(InstanceIDLength <= MAX_STRING_LENGTH);
if (NameLength > MAX_STRING_LENGTH ||
InstanceIDLength > MAX_STRING_LENGTH)
{
return NULL;
}
//
// Allocate a new interface structure & initialize it
//
pTcIfc = UL_ALLOCATE_STRUCT(
PagedPool,
UL_TCI_INTERFACE,
UL_TCI_INTERFACE_POOL_TAG
);
if ( pTcIfc == NULL )
{
return NULL;
}
RtlZeroMemory( pTcIfc, sizeof(UL_TCI_INTERFACE) );
pTcIfc->Signature = UL_TCI_INTERFACE_POOL_TAG;
InitializeListHead( &pTcIfc->FlowList );
// Variable size addresslist
pTcIfc->pAddressListDesc = (PADDRESS_LIST_DESCRIPTOR)
UL_ALLOCATE_ARRAY(
PagedPool,
UCHAR,
DescSize,
UL_TCI_INTERFACE_POOL_TAG
);
if ( pTcIfc->pAddressListDesc == NULL )
{
UL_FREE_POOL_WITH_SIG(pTcIfc, UL_TCI_INTERFACE_POOL_TAG);
return NULL;
}
CREATE_REF_TRACE_LOG(
pTcIfc->pTraceLog,
96 - REF_TRACE_OVERHEAD,
0,
TRACELOG_LOW_PRIORITY,
UL_TCI_INTERFACE_REF_TRACE_LOG_POOL_TAG
);
INT_TRACE(pTcIfc, TC_ALLOC);
pTcIfc->AddrListBytesCount = DescSize;
// Copy the instance name string data
RtlCopyMemory(pTcIfc->Name,Name,NameLength);
pTcIfc->NameLength = (USHORT)NameLength;
pTcIfc->Name[NameLength/sizeof(WCHAR)] = UNICODE_NULL;
// Copy the instance ID string data
RtlCopyMemory(pTcIfc->InstanceID,InstanceID,InstanceIDLength);
pTcIfc->InstanceIDLength = (USHORT)InstanceIDLength;
pTcIfc->InstanceID[InstanceIDLength/sizeof(WCHAR)] = UNICODE_NULL;
// Copy the Description data and extract the corresponding ip address
RtlCopyMemory(pTcIfc->pAddressListDesc, Desc, DescSize);
// IP Address of the interface is hidden in this desc data
// we will find out and save it for faster lookup.
pTcIfc->IsQoSEnabled = (BOOLEAN)
(pTcIfc->pAddressListDesc->AddressList.AddressCount != 0);
return pTcIfc;
}
/***************************************************************************++
Routine Description:
Frees up the
- Adress list descriptor
- RefTrace log
- The interface structure
Argument:
pTcIfc Pointer to the interface struct.
--***************************************************************************/
VOID
UlpTcFreeInterface(
IN OUT PUL_TCI_INTERFACE pTcIfc
)
{
PAGED_CODE();
//
// Do the cleanup.
//
if (pTcIfc)
{
DESTROY_REF_TRACE_LOG(
pTcIfc->pTraceLog,
UL_TCI_INTERFACE_REF_TRACE_LOG_POOL_TAG
);
if (pTcIfc->pAddressListDesc)
{
UL_FREE_POOL(pTcIfc->pAddressListDesc,
UL_TCI_INTERFACE_POOL_TAG
);
}
UL_FREE_POOL_WITH_SIG(pTcIfc, UL_TCI_INTERFACE_POOL_TAG);
}
}
/***************************************************************************++
Routine Description:
Make a Wmi Querry to get the firendly names of all interfaces.
Its basically replica of the tcdll enumerate interfaces call.
This function also allocates the global interface list. If it's not
successfull it doesn't though.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcGetFriendlyNames(
VOID
)
{
NTSTATUS Status;
PVOID WmiObject;
ULONG MyBufferSize;
PWNODE_ALL_DATA pWnode;
PWNODE_ALL_DATA pWnodeBuffer;
PUL_TCI_INTERFACE pTcIfc;
GUID QoSGuid;
PLIST_ENTRY pEntry;
PUL_TCI_INTERFACE pInterface;
//
// Initialize defaults
//
Status = STATUS_SUCCESS;
WmiObject = NULL;
pWnodeBuffer = NULL;
pTcIfc = NULL;
MyBufferSize = UL_DEFAULT_WMI_QUERY_BUFFER_SIZE;
QoSGuid = GUID_QOS_TC_SUPPORTED;
//
// Get a WMI block handle to the GUID_QOS_SUPPORTED
//
Status = IoWMIOpenBlock( (GUID *) &QoSGuid, 0, &WmiObject );
if (!NT_SUCCESS(Status))
{
if (Status == STATUS_WMI_GUID_NOT_FOUND)
{
// This means there is no TC data provider (which's Psched)
UlTrace(
TC,
("Http!UlpTcGetFriendlyNames: PSCHED hasn't been "
"installed !\n"));
}
else
{
UlTrace(
TC,
("Http!UlpTcGetFriendlyNames:IoWMIOpenBlock FAILED Status "
"%08lx\n", Status));
}
return Status;
}
do
{
//
// Allocate a private buffer to retrieve all wnodes
//
pWnodeBuffer = (PWNODE_ALL_DATA) UL_ALLOCATE_ARRAY(
NonPagedPool,
UCHAR,
MyBufferSize,
UL_TCI_WMI_POOL_TAG
);
if (pWnodeBuffer == NULL)
{
ObDereferenceObject(WmiObject);
return STATUS_NO_MEMORY;
}
__try
{
Status = IoWMIQueryAllData(WmiObject, &MyBufferSize, pWnodeBuffer);
UlTrace( TC,
("Http!UlpTcGetFriendlyNames: IoWMIQueryAllData Status %08lx\n",
Status
));
}
__except ( UL_EXCEPTION_FILTER() )
{
Status = GetExceptionCode();
}
if (Status == STATUS_BUFFER_TOO_SMALL)
{
//
// Failed since the buffer was too small.
// Release the buffer and double the size.
//
MyBufferSize *= 2;
UL_FREE_POOL( pWnodeBuffer, UL_TCI_WMI_POOL_TAG );
pWnodeBuffer = NULL;
}
} while (Status == STATUS_BUFFER_TOO_SMALL);
if (NT_SUCCESS(Status))
{
ULONG dwInstanceNum;
ULONG InstanceSize = 0;
PULONG lpdwNameOffsets;
BOOLEAN bFixedSize = FALSE;
USHORT usNameLength;
ULONG DescSize;
PTC_SUPPORTED_INFO_BUFFER pTcInfoBuffer = NULL;
pWnode = pWnodeBuffer;
ASSERT(pWnode->WnodeHeader.Flags & WNODE_FLAG_ALL_DATA);
do
{
//
// Check for fixed instance size
//
if (pWnode->WnodeHeader.Flags & WNODE_FLAG_FIXED_INSTANCE_SIZE)
{
InstanceSize = pWnode->FixedInstanceSize;
bFixedSize = TRUE;
pTcInfoBuffer =
(PTC_SUPPORTED_INFO_BUFFER)OffsetToPtr(
pWnode,
pWnode->DataBlockOffset);
}
//
// Get a pointer to the array of offsets to the instance names
//
lpdwNameOffsets = (PULONG) OffsetToPtr(
pWnode,
pWnode->OffsetInstanceNameOffsets);
for ( dwInstanceNum = 0;
dwInstanceNum < pWnode->InstanceCount;
dwInstanceNum++ )
{
usNameLength = *(PUSHORT)OffsetToPtr(
pWnode,
lpdwNameOffsets[dwInstanceNum]);
//
// Length and offset for variable data
//
if ( !bFixedSize )
{
InstanceSize =
pWnode->OffsetInstanceDataAndLength[
dwInstanceNum].LengthInstanceData;
pTcInfoBuffer = (PTC_SUPPORTED_INFO_BUFFER)
OffsetToPtr(
(PBYTE)pWnode,
pWnode->OffsetInstanceDataAndLength[
dwInstanceNum].OffsetInstanceData);
}
//
// We have all that is needed.
//
ASSERT(usNameLength < MAX_STRING_LENGTH);
DescSize = InstanceSize - FIELD_OFFSET(
TC_SUPPORTED_INFO_BUFFER,
AddrListDesc
);
//
// Allocate a new interface structure & initialize it with
// the wmi data we have acquired.
//
pTcIfc = UlpTcAllocateInterface(
DescSize,
&pTcInfoBuffer->AddrListDesc,
usNameLength,
(PUCHAR) OffsetToPtr(
pWnode,
lpdwNameOffsets[dwInstanceNum] +
sizeof(USHORT)),
pTcInfoBuffer->InstanceIDLength,
(PUCHAR) &pTcInfoBuffer->InstanceID[0]
);
if ( pTcIfc == NULL )
{
Status = STATUS_NO_MEMORY;
goto end;
}
//
// Get the interface index from TCP
//
Status = UlpTcGetInterfaceIndex( pTcIfc );
ASSERT(NT_SUCCESS(Status));
//
// Add this interface to the global interface list
//
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
InsertTailList(&g_TciIfcListHead, &pTcIfc->Linkage );
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
//
// Set to Null so we don't try to cleanup after we insert it
// to the global list.
//
pTcIfc = NULL;
}
//
// Update Wnode to point to next node
//
if ( pWnode->WnodeHeader.Linkage != 0)
{
pWnode = (PWNODE_ALL_DATA) OffsetToPtr( pWnode,
pWnode->WnodeHeader.Linkage);
}
else
{
pWnode = NULL;
}
}
while ( pWnode != NULL && NT_SUCCESS(Status) );
UlTrace(TC,("Http!UlpTcGetFriendlyNames: got all the names.\n"));
}
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Http!UlpTcGetFriendlyNames: FAILED Status %08lx\n",
Status
));
if (pTcIfc)
{
UlpTcFreeInterface( pTcIfc );
}
//
// Cleanup the partially done interface list if not empty
//
while ( !IsListEmpty( &g_TciIfcListHead ) )
{
pEntry = g_TciIfcListHead.Flink;
pInterface = CONTAINING_RECORD( pEntry,
UL_TCI_INTERFACE,
Linkage
);
RemoveEntryList( pEntry );
UlpTcFreeInterface( pInterface );
}
}
//
// Release resources and close WMI handle
//
if (WmiObject != NULL)
{
ObDereferenceObject(WmiObject);
}
if (pWnodeBuffer)
{
UL_FREE_POOL(pWnodeBuffer, UL_TCI_WMI_POOL_TAG);
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcReleaseAll :
Close all interfaces, all flows and all filters.
Also deregister GPC clients and release all TC ineterfaces.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcReleaseAll(
VOID
)
{
NTSTATUS Status;
//
// Close all interfaces their flows & filters
//
UlpTcCloseAllInterfaces();
//
// DeRegister the QoS GpcClient
//
Status = UlpTcDeRegisterGpcClient();
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Http!UlpTcReleaseAll: FAILURE %08lx \n", Status ));
}
//
// Finally close our gpc file handle
//
ZwClose(g_GpcFileHandle);
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcCloseAllInterfaces :
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcCloseAllInterfaces(
VOID
)
{
NTSTATUS Status;
PLIST_ENTRY pEntry;
PUL_TCI_INTERFACE pInterface;
Status = STATUS_SUCCESS;
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
//
// Close all interfaces in our global list
//
while ( !IsListEmpty( &g_TciIfcListHead ) )
{
pEntry = g_TciIfcListHead.Flink;
pInterface = CONTAINING_RECORD( pEntry,
UL_TCI_INTERFACE,
Linkage
);
UlpTcCloseInterface( pInterface );
RemoveEntryList( pEntry );
UlpTcFreeInterface( pInterface );
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
return Status;
}
/***************************************************************************++
Routine Description:
Cleans up all the flows on the interface.
Arguments:
pInterface - to be closed
--***************************************************************************/
NTSTATUS
UlpTcCloseInterface(
PUL_TCI_INTERFACE pInterface
)
{
NTSTATUS Status;
PLIST_ENTRY pEntry;
PUL_TCI_FLOW pFlow;
PAGED_CODE();
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
INT_TRACE(pInterface, TC_CLOSE);
//
// Go clean up all flows for the interface and remove itself as well
//
Status = STATUS_SUCCESS;
while (!IsListEmpty(&pInterface->FlowList))
{
pEntry= pInterface->FlowList.Flink;
pFlow = CONTAINING_RECORD(
pEntry,
UL_TCI_FLOW,
Linkage
);
ASSERT(IS_VALID_TCI_FLOW(pFlow));
//
// Remove flow from the corresponding owner's flowlist
// as well. Owner pointer should not be null for a flow.
//
ASSERT_FLOW_OWNER(pFlow->pOwner);
RemoveEntryList(&pFlow->Siblings);
pFlow->Siblings.Flink = pFlow->Siblings.Blink = NULL;
pFlow->pOwner = NULL;
//
// Now remove from the interface.
//
Status = UlpTcDeleteFlow(pFlow);
//
// Above call may fail,if GPC removes the flow based on PSCHED's
// notification before we get a chance to close our GPC handle.
//
}
UlTrace(TC,("Http!UlpTcCloseInterface: All flows deleted on Ifc @ %p\n",
pInterface ));
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcWalkWnode :
Arguments:
... the WMI provided data buffer ...
--***************************************************************************/
NTSTATUS
UlpTcWalkWnode(
IN PWNODE_HEADER pWnodeHdr,
IN PUL_TC_NOTIF_HANDLER pNotifHandler
)
{
NTSTATUS Status;
PWCHAR NamePtr;
USHORT NameSize;
PUCHAR DataBuffer;
ULONG DataSize;
ULONG Flags;
PULONG NameOffset;
//
// Try to capture the data frm WMI Buffer
//
ASSERT(pNotifHandler);
Status = STATUS_SUCCESS;
Flags = pWnodeHdr->Flags;
if (Flags & WNODE_FLAG_ALL_DATA)
{
//
// WNODE_ALL_DATA structure has multiple interfaces
//
PWNODE_ALL_DATA pWnode = (PWNODE_ALL_DATA)pWnodeHdr;
ULONG Instance;
UlTrace(TC,("Http!UlpTcWalkWnode: ALL_DATA ... \n" ));
NameOffset = (PULONG) OffsetToPtr(pWnode,
pWnode->OffsetInstanceNameOffsets );
DataBuffer = (PUCHAR) OffsetToPtr(pWnode,
pWnode->DataBlockOffset);
for (Instance = 0;
Instance < pWnode->InstanceCount;
Instance++)
{
// Instance Name
NamePtr = (PWCHAR) OffsetToPtr(pWnode,NameOffset[Instance] + sizeof(USHORT));
NameSize = * (PUSHORT) OffsetToPtr(pWnode,NameOffset[Instance]);
// Instance Data
if ( Flags & WNODE_FLAG_FIXED_INSTANCE_SIZE )
{
DataSize = pWnode->FixedInstanceSize;
}
else
{
DataSize =
pWnode->OffsetInstanceDataAndLength[Instance].LengthInstanceData;
DataBuffer =
(PUCHAR)OffsetToPtr(pWnode,
pWnode->OffsetInstanceDataAndLength[Instance].OffsetInstanceData);
}
// Call the handler
pNotifHandler( NamePtr, NameSize, (PTC_INDICATION_BUFFER) DataBuffer, DataSize );
}
}
else if (Flags & WNODE_FLAG_SINGLE_INSTANCE)
{
//
// WNODE_SINGLE_INSTANCE structure has only one instance
//
PWNODE_SINGLE_INSTANCE pWnode = (PWNODE_SINGLE_INSTANCE)pWnodeHdr;
if (Flags & WNODE_FLAG_STATIC_INSTANCE_NAMES)
{
return STATUS_SUCCESS;
}
UlTrace(TC,("Http!UlpTcWalkWnode: SINGLE_INSTANCE ... \n" ));
NamePtr = (PWCHAR)OffsetToPtr(pWnode,pWnode->OffsetInstanceName + sizeof(USHORT));
NameSize = * (USHORT *) OffsetToPtr(pWnode,pWnode->OffsetInstanceName);
// Instance Data
DataSize = pWnode->SizeDataBlock;
DataBuffer = (PUCHAR)OffsetToPtr (pWnode, pWnode->DataBlockOffset);
// Call the handler
pNotifHandler( NamePtr, NameSize, (PTC_INDICATION_BUFFER) DataBuffer, DataSize );
}
else if (Flags & WNODE_FLAG_SINGLE_ITEM)
{
//
// WNODE_SINGLE_ITEM is almost identical to single_instance
//
PWNODE_SINGLE_ITEM pWnode = (PWNODE_SINGLE_ITEM)pWnodeHdr;
if (Flags & WNODE_FLAG_STATIC_INSTANCE_NAMES)
{
return STATUS_SUCCESS;
}
UlTrace(TC,("Http!UlpTcWalkWnode: SINGLE_ITEM ... \n" ));
NamePtr = (PWCHAR)OffsetToPtr(pWnode,pWnode->OffsetInstanceName + sizeof(USHORT));
NameSize = * (USHORT *) OffsetToPtr(pWnode, pWnode->OffsetInstanceName);
// Instance Data
DataSize = pWnode->SizeDataItem;
DataBuffer = (PUCHAR)OffsetToPtr (pWnode, pWnode->DataBlockOffset);
// Call the handler
pNotifHandler( NamePtr, NameSize, (PTC_INDICATION_BUFFER) DataBuffer, DataSize );
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcHandleIfcUp :
This functions handles the interface change notifications.
We register for the corresponding notifications during init.
Arguments:
PVOID Wnode - PSched data provided with WMI way
--***************************************************************************/
VOID
UlpTcHandleIfcUp(
IN PWSTR Name,
IN ULONG NameSize,
IN PTC_INDICATION_BUFFER pTcBuffer,
IN ULONG BufferSize
)
{
NTSTATUS Status;
ULONG AddrListDescSize;
PTC_SUPPORTED_INFO_BUFFER pTcInfoBuffer;
PUL_TCI_INTERFACE pTcIfc;
PUL_TCI_INTERFACE pTcIfcTemp;
PLIST_ENTRY pEntry;
Status = STATUS_SUCCESS;
UlTrace(TC,("Http!UlpTcHandleIfcUp: Adding %ws %d\n", Name, BufferSize ));
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
//
// Allocate a new interface structure for the newcoming interface
//
AddrListDescSize = BufferSize
- FIELD_OFFSET(TC_INDICATION_BUFFER,InfoBuffer)
- FIELD_OFFSET(TC_SUPPORTED_INFO_BUFFER, AddrListDesc);
UlTrace(TC,("Http!UlpTcHandleIfcUp: AddrListDescSize %d\n", AddrListDescSize ));
pTcInfoBuffer = & pTcBuffer->InfoBuffer;
pTcIfc = UlpTcAllocateInterface(
AddrListDescSize,
&pTcInfoBuffer->AddrListDesc,
NameSize,
(PUCHAR) Name,
pTcInfoBuffer->InstanceIDLength,
(PUCHAR) &pTcInfoBuffer->InstanceID[0]
);
if ( pTcIfc == NULL )
{
Status = STATUS_NO_MEMORY;
goto end;
}
UL_DUMP_TC_INTERFACE( pTcIfc );
//
// If we are receiving a notification for an interface already exist then
// drop this call. Prevent global interface list corruption if we receive
// inconsistent notifications. But there may be multiple interfaces with
// same zero IPs.
//
pEntry = g_TciIfcListHead.Flink;
while ( pEntry != &g_TciIfcListHead )
{
pTcIfcTemp = CONTAINING_RECORD( pEntry, UL_TCI_INTERFACE, Linkage );
if (wcsncmp(pTcIfcTemp->Name, pTcIfc->Name, NameSize/sizeof(WCHAR))==0)
{
ASSERT(!"Conflict in the global interface list !");
Status = STATUS_CONFLICTING_ADDRESSES;
goto end;
}
pEntry = pEntry->Flink;
}
//
// Get the interface index from TCP.
//
Status = UlpTcGetInterfaceIndex( pTcIfc );
if (!NT_SUCCESS(Status))
goto end;
//
// Insert to the global interface list
//
InsertTailList( &g_TciIfcListHead, &pTcIfc->Linkage );
INT_TRACE(pTcIfc, TC_UP);
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Http!UlpTcHandleIfcUp: FAILURE %08lx \n", Status ));
if (pTcIfc != NULL)
{
UlpTcFreeInterface(pTcIfc);
}
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
return;
}
/***************************************************************************++
Routine Description:
UlpTcHandleIfcDown :
This functions handles the interface change notifications.
We register for the corresponding notifications during init.
Arguments:
PVOID Wnode - PSched data provided with WMI way
--***************************************************************************/
VOID
UlpTcHandleIfcDown(
IN PWSTR Name,
IN ULONG NameSize,
IN PTC_INDICATION_BUFFER pTcBuffer,
IN ULONG BufferSize
)
{
NTSTATUS Status;
PUL_TCI_INTERFACE pTcIfc;
PUL_TCI_INTERFACE pTcIfcTemp;
PLIST_ENTRY pEntry;
UNREFERENCED_PARAMETER(pTcBuffer);
UNREFERENCED_PARAMETER(BufferSize);
Status = STATUS_SUCCESS;
UlTrace(TC,("Http!UlpTcHandleIfcDown: Removing %ws\n", Name ));
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
//
// Find the corresponding ifc structure we keep.
//
pTcIfc = NULL;
pEntry = g_TciIfcListHead.Flink;
while ( pEntry != &g_TciIfcListHead )
{
pTcIfcTemp = CONTAINING_RECORD( pEntry, UL_TCI_INTERFACE, Linkage );
if ( wcsncmp(pTcIfcTemp->Name, Name, NameSize) == 0 )
{
pTcIfc = pTcIfcTemp;
break;
}
pEntry = pEntry->Flink;
}
if (pTcIfc == NULL)
{
ASSERT(FALSE);
Status = STATUS_NOT_FOUND;
goto end;
}
INT_TRACE(pTcIfc, TC_DOWN);
//
// Remove this interface and its flows etc ...
//
UlpTcCloseInterface( pTcIfc );
RemoveEntryList( &pTcIfc->Linkage );
UlpTcFreeInterface( pTcIfc );
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Http!UlpTcHandleIfcDown: FAILURE %08lx \n", Status ));
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
return;
}
/***************************************************************************++
Routine Description:
UlpTcHandleIfcChange :
This functions handles the interface change notifications.
We register for the corresponding notifications during init.
Arguments:
PVOID Wnode - PSched data provided with WMI way
--***************************************************************************/
VOID
UlpTcHandleIfcChange(
IN PWSTR Name,
IN ULONG NameSize,
IN PTC_INDICATION_BUFFER pTcBuffer,
IN ULONG BufferSize
)
{
NTSTATUS Status;
ULONG AddrListDescSize;
PTC_SUPPORTED_INFO_BUFFER pTcInfoBuffer;
PUL_TCI_INTERFACE pTcIfc;
PUL_TCI_INTERFACE pTcIfcTemp;
PLIST_ENTRY pEntry;
PADDRESS_LIST_DESCRIPTOR pAddressListDesc;
Status = STATUS_SUCCESS;
UlTrace(TC,("Http!UlpTcHandleIfcChange: Updating %ws\n", Name ));
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
AddrListDescSize = BufferSize
- FIELD_OFFSET(TC_INDICATION_BUFFER,InfoBuffer)
- FIELD_OFFSET(TC_SUPPORTED_INFO_BUFFER, AddrListDesc);
pTcInfoBuffer = & pTcBuffer->InfoBuffer;
// Find the corresponding ifc structure we keep.
pTcIfc = NULL;
pEntry = g_TciIfcListHead.Flink;
while ( pEntry != &g_TciIfcListHead )
{
pTcIfcTemp = CONTAINING_RECORD( pEntry, UL_TCI_INTERFACE, Linkage );
if ( wcsncmp(pTcIfcTemp->Name, Name, NameSize) == 0 )
{
pTcIfc = pTcIfcTemp;
break;
}
pEntry = pEntry->Flink;
}
if (pTcIfc == NULL)
{
ASSERT(FALSE);
Status = STATUS_NOT_FOUND;
goto end;
}
INT_TRACE(pTcIfc, TC_CHANGE);
// Instance id
RtlCopyMemory(pTcIfc->InstanceID,
pTcInfoBuffer->InstanceID,
pTcInfoBuffer->InstanceIDLength
);
pTcIfc->InstanceIDLength = pTcInfoBuffer->InstanceIDLength;
pTcIfc->InstanceID[pTcIfc->InstanceIDLength/sizeof(WCHAR)] = UNICODE_NULL;
// The Description data and extract the corresponding ip address
// ReWrite the fresh data. Size of the description data might be changed
// so wee need to dynamically allocate it everytime changes
pAddressListDesc =
(PADDRESS_LIST_DESCRIPTOR) UL_ALLOCATE_ARRAY(
PagedPool,
UCHAR,
AddrListDescSize,
UL_TCI_INTERFACE_POOL_TAG
);
if ( pAddressListDesc == NULL )
{
Status = STATUS_NO_MEMORY;
goto end;
}
if (pTcIfc->pAddressListDesc)
{
UL_FREE_POOL(pTcIfc->pAddressListDesc,UL_TCI_INTERFACE_POOL_TAG);
}
pTcIfc->pAddressListDesc = pAddressListDesc;
pTcIfc->AddrListBytesCount = AddrListDescSize;
RtlCopyMemory( pTcIfc->pAddressListDesc,
&pTcInfoBuffer->AddrListDesc,
AddrListDescSize
);
// IP Address of the interface is hidden in this desc data
pTcIfc->IsQoSEnabled = (BOOLEAN)
(pTcIfc->pAddressListDesc->AddressList.AddressCount != 0);
// ReFresh the interface index from TCP.
Status = UlpTcGetInterfaceIndex( pTcIfc );
if (!NT_SUCCESS(Status))
goto end;
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Http!UlpTcHandleIfcChange: FAILURE %08lx \n", Status ));
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
return;
}
/***************************************************************************++
Routine Description:
UlTcNotifyCallback :
This callback functions handles the interface change notifications.
We register for the corresponding notifications during init.
Arguments:
PVOID Wnode - PSched data provided with WMI way
--***************************************************************************/
VOID
UlTcNotifyCallback(
IN PVOID pWnode,
IN PVOID Context
)
{
GUID *pGuid;
PWNODE_HEADER pWnodeHeader;
UNREFERENCED_PARAMETER(Context);
UlTrace( TC, ("Http!UlTcNotifyCallback: ... \n" ));
pWnodeHeader = (PWNODE_HEADER) pWnode;
pGuid = &pWnodeHeader->Guid;
if (UL_COMPARE_QOS_NOTIFICATION(pGuid,&GUID_QOS_TC_INTERFACE_UP_INDICATION))
{
UlpTcWalkWnode( pWnodeHeader, UlpTcHandleIfcUp );
}
else if
(UL_COMPARE_QOS_NOTIFICATION(pGuid, &GUID_QOS_TC_INTERFACE_DOWN_INDICATION))
{
UlpTcWalkWnode( pWnodeHeader, UlpTcHandleIfcDown );
}
else if
(UL_COMPARE_QOS_NOTIFICATION(pGuid, &GUID_QOS_TC_INTERFACE_CHANGE_INDICATION))
{
UlpTcWalkWnode( pWnodeHeader, UlpTcHandleIfcChange );
}
UlTrace( TC, ("Http!UlTcNotifyCallback: Handled.\n" ));
}
/***************************************************************************++
Routine Description:
UlpTcRegisterForCallbacks :
We will open Block object until termination for each type of
notification. And we will deref each object upon termination
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcRegisterForCallbacks(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
GUID Guid;
//
// Get a WMI block handle register all the callback functions.
//
Guid = GUID_QOS_TC_INTERFACE_UP_INDICATION;
Status = IoWMIOpenBlock(&Guid,
WMIGUID_NOTIFICATION,
&g_TcInterfaceUpNotificationObject
);
if (NT_SUCCESS(Status))
{
Status = IoWMISetNotificationCallback(
g_TcInterfaceUpNotificationObject,
(WMI_NOTIFICATION_CALLBACK) UlTcNotifyCallback,
NULL
);
if (!NT_SUCCESS(Status))
goto end;
}
Guid = GUID_QOS_TC_INTERFACE_DOWN_INDICATION;
Status = IoWMIOpenBlock(&Guid,
WMIGUID_NOTIFICATION,
&g_TcInterfaceDownNotificationObject
);
if (NT_SUCCESS(Status))
{
Status = IoWMISetNotificationCallback(
g_TcInterfaceDownNotificationObject,
(WMI_NOTIFICATION_CALLBACK) UlTcNotifyCallback,
NULL
);
if (!NT_SUCCESS(Status))
goto end;
}
Guid = GUID_QOS_TC_INTERFACE_CHANGE_INDICATION;
Status = IoWMIOpenBlock(&Guid,
WMIGUID_NOTIFICATION,
&g_TcInterfaceChangeNotificationObject
);
if (NT_SUCCESS(Status))
{
Status = IoWMISetNotificationCallback(
g_TcInterfaceChangeNotificationObject,
(WMI_NOTIFICATION_CALLBACK) UlTcNotifyCallback,
NULL
);
if (!NT_SUCCESS(Status))
goto end;
}
end:
// Cleanup if necessary
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Http!UlpTcRegisterForCallbacks: FAILED %08lx\n",Status));
if(g_TcInterfaceUpNotificationObject!=NULL)
{
ObDereferenceObject(g_TcInterfaceUpNotificationObject);
g_TcInterfaceUpNotificationObject = NULL;
}
if(g_TcInterfaceDownNotificationObject!=NULL)
{
ObDereferenceObject(g_TcInterfaceDownNotificationObject);
g_TcInterfaceDownNotificationObject = NULL;
}
if(g_TcInterfaceChangeNotificationObject!=NULL)
{
ObDereferenceObject(g_TcInterfaceChangeNotificationObject);
g_TcInterfaceChangeNotificationObject = NULL;
}
}
return Status;
}
//
// Following functions provide public/private interfaces for flow & filter
// creation/removal/modification for site & global flows.
//
/***************************************************************************++
Routine Description:
UlpTcDeleteFlow :
you should own the TciIfcPushLock exclusively before calling
this function
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcDeleteFlow(
IN PUL_TCI_FLOW pFlow
)
{
NTSTATUS Status;
PLIST_ENTRY pEntry;
PUL_TCI_FILTER pFilter;
HANDLE FlowHandle;
PUL_TCI_INTERFACE pInterface;
//
// Initialize
//
PAGED_CODE();
Status = STATUS_SUCCESS;
ASSERT(g_InitTciCalled);
ASSERT(IS_VALID_TCI_FLOW(pFlow));
//
// First remove all the filters belong to us
//
while (!IsListEmpty(&pFlow->FilterList))
{
pEntry = pFlow->FilterList.Flink;
pFilter = CONTAINING_RECORD(
pEntry,
UL_TCI_FILTER,
Linkage
);
Status = UlpTcDeleteFilter( pFlow, pFilter );
ASSERT(NT_SUCCESS(Status));
}
//
// Now remove the flow itself from our flowlist on the interface
//
pInterface = pFlow->pInterface;
ASSERT( pInterface != NULL );
RemoveEntryList( &pFlow->Linkage );
ASSERT(pInterface->FlowListSize > 0);
pInterface->FlowListSize -= 1;
pFlow->Linkage.Flink = pFlow->Linkage.Blink = NULL;
FlowHandle = pFlow->FlowHandle;
UlTrace( TC, ("Http!UlpTcDeleteFlow: Flow deleted. %p\n", pFlow ));
UL_FREE_POOL_WITH_SIG( pFlow, UL_TCI_FLOW_POOL_TAG );
//
// Finally talk to TC
//
Status = UlpTcDeleteGpcFlow( FlowHandle );
if (!NT_SUCCESS(Status))
{
UlTrace(TC, ("Http!UlpTcDeleteFlow: FAILURE %08lx \n",
Status ));
}
else
{
UlTrace(TC,
("Http!UlpTcDeleteFlow: FlowHandle %d deleted in TC as well.\n",
FlowHandle
));
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcDeleteFlow :
remove a flow from existing QoS Enabled interface
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcDeleteGpcFlow(
IN HANDLE FlowHandle
)
{
NTSTATUS Status;
ULONG InBuffSize;
ULONG OutBuffSize;
GPC_REMOVE_CF_INFO_REQ GpcReq;
GPC_REMOVE_CF_INFO_RES GpcRes;
IO_STATUS_BLOCK IoStatusBlock;
//
// Remove the flow frm psched
//
InBuffSize = sizeof(GPC_REMOVE_CF_INFO_REQ);
OutBuffSize = sizeof(GPC_REMOVE_CF_INFO_RES);
GpcReq.ClientHandle = g_GpcClientHandle;
GpcReq.GpcCfInfoHandle = FlowHandle;
Status = UlpTcDeviceControl( g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
IOCTL_GPC_REMOVE_CF_INFO,
&GpcReq,
InBuffSize,
&GpcRes,
OutBuffSize
);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
}
if (!NT_SUCCESS(Status))
{
UlTrace(TC,
("Http!UlpTcDeleteGpcFlow: FAILURE %08lx \n", Status ));
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcAllocateFlow :
Allocates a flow and setup the FlowSpec according the passed BWT
parameter
Arguments:
HTTP_BANDWIDTH_LIMIT - FlowSpec will be created using this BWT limit
in B/s
Return Value
PUL_TCI_FLOW - The newly allocated flow
NULL - If memory allocation failed
--***************************************************************************/
PUL_TCI_FLOW
UlpTcAllocateFlow(
IN HTTP_BANDWIDTH_LIMIT MaxBandwidth
)
{
PUL_TCI_FLOW pFlow;
TC_GEN_FLOW TcGenFlow;
//
// Setup the FlowSpec frm MaxBandwidth passed by the config handler
//
RtlZeroMemory(&TcGenFlow,sizeof(TcGenFlow));
UL_SET_FLOWSPEC(TcGenFlow,MaxBandwidth);
//
// Since we hold a spinlock inside the flow structure allocating from
// NonPagedPool. We will have this allocation only for bt enabled sites.
//
pFlow = UL_ALLOCATE_STRUCT(
NonPagedPool,
UL_TCI_FLOW,
UL_TCI_FLOW_POOL_TAG
);
if( pFlow == NULL )
{
return NULL;
}
// Initialize the rest
RtlZeroMemory( pFlow, sizeof(UL_TCI_FLOW) );
pFlow->Signature = UL_TCI_FLOW_POOL_TAG;
pFlow->GenFlow = TcGenFlow;
UlInitializeSpinLock( &pFlow->FilterListSpinLock, "FilterListSpinLock" );
InitializeListHead( &pFlow->FilterList );
pFlow->pOwner = NULL;
return pFlow;
}
/***************************************************************************++
Routine Description:
UlpModifyFlow :
Modify an existing flow by sending an IOCTL down to GPC. Basically
what this function does is to provide an updated TC_GEN_FLOW field
to GPC for an existing flow.
Arguments:
PUL_TCI_INTERFACE - Required to get the interfaces friendly name.
PUL_TCI_FLOW - To get the GPC flow handle as well as to be able to
update the new flow parameters.
--***************************************************************************/
NTSTATUS
UlpModifyFlow(
IN PUL_TCI_INTERFACE pInterface,
IN PUL_TCI_FLOW pFlow
)
{
PCF_INFO_QOS Kflow;
PGPC_MODIFY_CF_INFO_REQ pGpcReq;
GPC_MODIFY_CF_INFO_RES GpcRes;
ULONG InBuffSize;
ULONG OutBuffSize;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
//
// Sanity check
//
PAGED_CODE();
ASSERT(g_GpcClientHandle);
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
ASSERT(IS_VALID_TCI_FLOW(pFlow));
InBuffSize = sizeof(GPC_MODIFY_CF_INFO_REQ) + sizeof(CF_INFO_QOS);
OutBuffSize = sizeof(GPC_MODIFY_CF_INFO_RES);
pGpcReq = UL_ALLOCATE_STRUCT_WITH_SPACE(
PagedPool,
GPC_MODIFY_CF_INFO_REQ,
sizeof(CF_INFO_QOS),
UL_TCI_GENERIC_POOL_TAG
);
if (pGpcReq == NULL)
{
return STATUS_NO_MEMORY;
}
RtlZeroMemory(pGpcReq, InBuffSize);
RtlZeroMemory(&GpcRes, OutBuffSize);
pGpcReq->ClientHandle = g_GpcClientHandle;
pGpcReq->GpcCfInfoHandle = pFlow->FlowHandle;
pGpcReq->CfInfoSize = sizeof(CF_INFO_QOS);
Kflow = (PCF_INFO_QOS)&pGpcReq->CfInfo;
Kflow->InstanceNameLength = (USHORT) pInterface->NameLength;
RtlCopyMemory(Kflow->InstanceName,
pInterface->Name,
pInterface->NameLength* sizeof(WCHAR));
RtlCopyMemory(&Kflow->GenFlow,
&pFlow->GenFlow,
sizeof(TC_GEN_FLOW));
Status = UlpTcDeviceControl( g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
IOCTL_GPC_MODIFY_CF_INFO,
pGpcReq,
InBuffSize,
&GpcRes,
OutBuffSize
);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
}
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Http!UlpModifyFlow: FAILURE %08lx\n",
Status
));
}
else
{
UlTrace( TC, ("Http!UlpModifyFlow: flow %p modified on interface %p \n",
pFlow,
pInterface
));
}
UL_FREE_POOL( pGpcReq, UL_TCI_GENERIC_POOL_TAG );
return Status;
}
/***************************************************************************++
Routine Description:
Builds the GPC structure and tries to add a QoS flow.
Updates the handle if call is successfull.
Arguments:
pInterface
pGenericFlow
pHandle
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpAddFlow(
IN PUL_TCI_INTERFACE pInterface,
IN PUL_TCI_FLOW pGenericFlow,
OUT PHANDLE pHandle
)
{
NTSTATUS Status;
PCF_INFO_QOS Kflow;
PGPC_ADD_CF_INFO_REQ pGpcReq;
GPC_ADD_CF_INFO_RES GpcRes;
ULONG InBuffSize;
ULONG OutBuffSize;
IO_STATUS_BLOCK IoStatusBlock;
//
// Find the interface from handle
//
PAGED_CODE();
ASSERT(g_GpcClientHandle);
InBuffSize = sizeof(GPC_ADD_CF_INFO_REQ) + sizeof(CF_INFO_QOS);
OutBuffSize = sizeof(GPC_ADD_CF_INFO_RES);
pGpcReq = UL_ALLOCATE_STRUCT_WITH_SPACE(
PagedPool,
GPC_ADD_CF_INFO_REQ,
sizeof(CF_INFO_QOS),
UL_TCI_GENERIC_POOL_TAG
);
if (pGpcReq == NULL)
{
return STATUS_NO_MEMORY;
}
RtlZeroMemory( pGpcReq, InBuffSize);
RtlZeroMemory( &GpcRes, OutBuffSize);
pGpcReq->ClientHandle = g_GpcClientHandle;
pGpcReq->ClientCfInfoContext= pGenericFlow; // GPC_CF_QOS;
pGpcReq->CfInfoSize = sizeof( CF_INFO_QOS);
Kflow = (PCF_INFO_QOS)&pGpcReq->CfInfo;
Kflow->InstanceNameLength = (USHORT) pInterface->NameLength;
RtlCopyMemory( Kflow->InstanceName,
pInterface->Name,
pInterface->NameLength* sizeof(WCHAR)
);
RtlCopyMemory( &Kflow->GenFlow,
&pGenericFlow->GenFlow,
sizeof(TC_GEN_FLOW)
);
Status = UlpTcDeviceControl( g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
IOCTL_GPC_ADD_CF_INFO,
pGpcReq,
InBuffSize,
&GpcRes,
OutBuffSize
);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
if (NT_SUCCESS(Status))
{
(*pHandle) = (HANDLE) GpcRes.GpcCfInfoHandle;
UlTrace( TC,
("Http!UlpAddFlow: a new flow added %p on interface %p \n",
pGenericFlow,
pInterface
));
}
}
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Http!UlpAddFlow: FAILURE %08lx\n",
Status
));
}
UL_FREE_POOL( pGpcReq, UL_TCI_GENERIC_POOL_TAG );
return Status;
}
/***************************************************************************++
Routine Description:
Add a flow on existing QoS Enabled interfaces for the caller. And updates
the callers list. Caller is either cgroup or control channel.
Will return (the last) error if * all * of the flow additions fail.
If at least one flow addition is successfull, it will return success.
Consider a machine with 2 NICs, if media is disconnected on one NIC, you
would still expect to see QoS running properly on the other one. Returning
success here means, there is at least one NIC with bandwidth throttling is
properly enforced.
Arguments:
pOwner - Pointer to the cgroup or control channel.
NewBandwidth - The new bandwidth throttling setting in B/s
Global - TRUE if this call is for global flows.
Return Value:
NTSTATUS - Completion status. (Last failure if all additions are failed)
- Success if at least one flow addition was success.
--***************************************************************************/
NTSTATUS
UlTcAddFlows(
IN PVOID pOwner,
IN HTTP_BANDWIDTH_LIMIT MaxBandwidth,
IN BOOLEAN Global
)
{
NTSTATUS Status;
BOOLEAN FlowAdded;
PLIST_ENTRY pFlowListHead;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
PUL_TCI_FLOW pFlow;
//
// Sanity check and init first.
//
PAGED_CODE();
Status = STATUS_SUCCESS;
FlowAdded = FALSE;
ASSERT(MaxBandwidth != HTTP_LIMIT_INFINITE);
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
if (Global)
{
PUL_CONTROL_CHANNEL
pControlChannel = (PUL_CONTROL_CHANNEL) pOwner;
ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
UlTrace(TC,("Http!UlTcAddFlows: For ControlChannel: %p"
"@ bwt-rate of %d B/s\n",
pControlChannel,
MaxBandwidth
));
pFlowListHead = &pControlChannel->FlowListHead;
}
else
{
PUL_CONFIG_GROUP_OBJECT
pConfigGroup = (PUL_CONFIG_GROUP_OBJECT) pOwner;
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
UlTrace(TC,("Http!UlTcAddFlows: For CGroup: %p"
"@ bwt-rate of %d B/s\n",
pConfigGroup,
MaxBandwidth
));
pFlowListHead = &pConfigGroup->FlowListHead;
}
//
// Visit each interface and add a flow for the caller.
//
pInterfaceEntry = g_TciIfcListHead.Flink;
while (pInterfaceEntry != &g_TciIfcListHead)
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
//
// Only if interface has a valid IP address, we attempt to add a
// flow for it. Otherwise we skip adding a flow for the interface.
//
if (!pInterface->IsQoSEnabled)
{
UlTrace(TC,
("Http!UlTcAddFlows: Skipping for interface %p !\n",
pInterface
));
goto proceed;
}
//
// Allocate a http flow structure.
//
pFlow = UlpTcAllocateFlow(MaxBandwidth);
if (pFlow == NULL)
{
Status = STATUS_NO_MEMORY;
UlTrace(TC, ("Http!UlTcAddFlows: Failure %08lx \n",
Status
));
goto proceed;
}
//
// Create the corresponding QoS flow as well. If GPC call fails
// cleanup the allocated Http flow.
//
Status = UlpAddFlow(
pInterface,
pFlow,
&pFlow->FlowHandle
);
if (!NT_SUCCESS(Status))
{
UlTrace(TC, ("Http!UlTcAddFlows: Failure %08lx \n",
Status
));
UL_FREE_POOL_WITH_SIG(pFlow, UL_TCI_FLOW_POOL_TAG);
goto proceed;
}
if (Global)
{
INT_TRACE(pInterface, TC_GFLOW_ADD);
}
else
{
INT_TRACE(pInterface, TC_FLOW_ADD);
}
//
// Proceed with further initialization as we have successfully
// installed the flow. First link the flow back to its owner
// interface. And add this to the interface's flowlist.
//
pFlow->pInterface = pInterface;
InsertHeadList(&pInterface->FlowList, &pFlow->Linkage);
pInterface->FlowListSize++;
//
// Also add this to the owner's flowlist. Set the owner pointer.
// Do not bump up the owner's refcount. Otherwise owner cannot be
// cleaned up until Tc terminates. And flows cannot be removed
// untill termination.
//
InsertHeadList(pFlowListHead, &pFlow->Siblings);
pFlow->pOwner = pOwner;
//
// Mark that there's at least one interface on which we were able
// to install a flow.
//
FlowAdded = TRUE;
UlTrace( TC,
("Http!UlTcAddFlows: Flow %p on pInterface %p\n",
pFlow,
pInterface
));
UL_DUMP_TC_FLOW(pFlow);
proceed:
//
// Proceed to the next interface.
//
pInterfaceEntry = pInterfaceEntry->Flink;
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
if (FlowAdded)
{
//
// Return success if at least one flow installed.
//
return STATUS_SUCCESS;
}
return Status;
}
/***************************************************************************++
Routine Description:
Will walk the caller's flow list and update the existing flows with the
new flowspec.
Its caller responsiblity to remember the new settings in the store.
Caller is either cgroup or control channel.
Will return error if one or some of the updates fails, but proceed walking
the whole list.
Arguments:
pOwner - Pointer to the cgroup or control channel.
NewBandwidth - The new bandwidth throttling setting in B/s
Global - TRUE if this call is for global flows.
Return Value:
NTSTATUS - Completion status. (Last failure if there was any)
--***************************************************************************/
NTSTATUS
UlTcModifyFlows(
IN PVOID pOwner,
IN HTTP_BANDWIDTH_LIMIT NewBandwidth,
IN BOOLEAN Global
)
{
NTSTATUS Status;
BOOLEAN FlowModified;
PLIST_ENTRY pFlowListHead;
PLIST_ENTRY pFlowEntry;
PUL_TCI_FLOW pFlow;
HTTP_BANDWIDTH_LIMIT OldBandwidth;
//
// Sanity check and init.
//
PAGED_CODE();
Status = STATUS_SUCCESS;
FlowModified = FALSE;
OldBandwidth = 0;
ASSERT(NewBandwidth != HTTP_LIMIT_INFINITE); // we do not remove flows.
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
if (Global)
{
PUL_CONTROL_CHANNEL
pControlChannel = (PUL_CONTROL_CHANNEL) pOwner;
ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
UlTrace(TC,("Http!UlTcModifyFlows: For ControlChannel: %p"
"to bwt-rate of %d B/s\n",
pControlChannel,
NewBandwidth
));
pFlowListHead = &pControlChannel->FlowListHead;
}
else
{
PUL_CONFIG_GROUP_OBJECT
pConfigGroup = (PUL_CONFIG_GROUP_OBJECT) pOwner;
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
UlTrace(TC,("Http!UlTcModifyFlows: For CGroup: %p"
"to bwt-rate of %d B/s\n",
pConfigGroup,
NewBandwidth
));
pFlowListHead = &pConfigGroup->FlowListHead;
}
//
// Walk the list and attempt to modify the flows.
//
pFlowEntry = pFlowListHead->Flink;
while (pFlowEntry != pFlowListHead)
{
PUL_TCI_INTERFACE pInterface;
pFlow = CONTAINING_RECORD(
pFlowEntry,
UL_TCI_FLOW,
Siblings
);
ASSERT(IS_VALID_TCI_FLOW(pFlow));
ASSERT(pOwner == pFlow->pOwner);
pInterface = pFlow->pInterface;
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
if (Global)
{
INT_TRACE(pInterface, TC_GFLOW_MODIFY);
}
else
{
INT_TRACE(pInterface, TC_FLOW_MODIFY);
}
//
// Save the old bandwidth before attempting to modify.
//
OldBandwidth = UL_GET_BW_FRM_FLOWSPEC(pFlow->GenFlow);
UL_SET_FLOWSPEC(pFlow->GenFlow, NewBandwidth);
Status = UlpModifyFlow(pFlow->pInterface, pFlow);
if (!NT_SUCCESS(Status))
{
//
// Whine about it, but still continue. Restore the original
// flowspec back.
//
UlTrace(TC,("Http!UlTcModifyFlowsForSite: FAILURE %08lx \n",
Status
));
UL_SET_FLOWSPEC(pFlow->GenFlow, OldBandwidth);
}
else
{
FlowModified = TRUE;
}
UL_DUMP_TC_FLOW(pFlow);
//
// Proceed to the next flow
//
pFlowEntry = pFlowEntry->Flink;
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
if (FlowModified)
{
//
// Return success if at least one flow modified.
//
return STATUS_SUCCESS;
}
return Status;
}
/***************************************************************************++
Routine Description:
Walks the caller's list (either cgroup or control channel) and removes
the flows on the list. Since this flows are always added to these lists
while holding the Interface lock exclusive, we will also acquire the
Interface lock exclusive here.
Arguments:
pOwner : Either points to pConfigGroup or pControlChannel.
Global : Must be true if this call is for removing global flows.
In that case pOwner points to pControlChannel
--***************************************************************************/
VOID
UlTcRemoveFlows(
IN PVOID pOwner,
IN BOOLEAN Global
)
{
NTSTATUS Status;
PLIST_ENTRY pFlowListHead;
PLIST_ENTRY pFlowEntry;
PUL_TCI_FLOW pFlow;
//
// Sanity check and dispatch the flow type.
//
PAGED_CODE();
Status = STATUS_SUCCESS;
UlAcquirePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
if (Global)
{
PUL_CONTROL_CHANNEL
pControlChannel = (PUL_CONTROL_CHANNEL) pOwner;
ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
UlTrace(TC,("Http!UlTcRemoveFlows: For ControlChannel: %p \n",
pControlChannel
));
pFlowListHead = &pControlChannel->FlowListHead;
}
else
{
PUL_CONFIG_GROUP_OBJECT
pConfigGroup = (PUL_CONFIG_GROUP_OBJECT) pOwner;
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
UlTrace(TC,("Http!UlTcRemoveFlows: For CGroup %p\n",
pConfigGroup
));
pFlowListHead = &pConfigGroup->FlowListHead;
}
//
// Walk the list and remove the flows.
//
while (!IsListEmpty(pFlowListHead))
{
PUL_TCI_INTERFACE pInterface;
pFlowEntry = pFlowListHead->Flink;
pFlow = CONTAINING_RECORD(
pFlowEntry,
UL_TCI_FLOW,
Siblings
);
ASSERT(IS_VALID_TCI_FLOW(pFlow));
ASSERT(pOwner == pFlow->pOwner);
pInterface = pFlow->pInterface;
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
if (Global)
{
INT_TRACE(pInterface, TC_GFLOW_REMOVE);
}
else
{
INT_TRACE(pInterface, TC_FLOW_REMOVE);
}
//
// Remove this from the owner's flowlist.
//
RemoveEntryList(&pFlow->Siblings);
pFlow->Siblings.Flink = pFlow->Siblings.Blink = NULL;
pFlow->pOwner = NULL;
//
// Remove it from the interface list as well.And delete
// the corresponding QoS flow. This should not fail.
//
Status = UlpTcDeleteFlow(pFlow);
//
// Above call may fail,if GPC removes the flow based on PSCHED's
// notification before we get a chance to close our GPC handle.
//
}
UlReleasePushLockExclusive(&g_pUlNonpagedData->TciIfcPushLock);
return;
}
/***************************************************************************++
Routine Description:
//
// There are two possibilities. The request could be served frm
// cache or can be routed to the user. In either case we need a
// flow installed if the BW is enabled for this request's site
// and there's no filter installed for this connection yet. We
// will remove the filter as soon as the connection dropped. But
// yes there's always a but,if the client is attempting to make
// requests to different sites using the same connection then we
// need to drop the filter frm the old site and move it to the
// newly requested site. This is a rare case but lets handle it
// anyway.
//
It's callers responsibility to ensure proper removal of the filter,
after it's done.
Algorithm:
1. Find the flow from the flow list of cgroup (or from global flows)
2. Add filter to that flow
Arguments:
pHttpConnection - required - Filter will be attached for this connection
pOwner - Either points to cgroup or control channel.
Global - must be true if the flow owner is a control channel.
Return Values:
STATUS_NOT_SUPPORTED - For attempts on Local Loopback
STATUS_OBJECT_NAME_NOT_FOUND - If flow has not been found for the cgroup
STATUS_SUCCESS - In other cases
--***************************************************************************/
NTSTATUS
UlTcAddFilter(
IN PUL_HTTP_CONNECTION pHttpConnection,
IN PVOID pOwner,
IN BOOLEAN Global
)
{
NTSTATUS Status;
TC_GEN_FILTER TcGenericFilter;
PUL_TCI_FLOW pFlow;
PUL_TCI_INTERFACE pInterface;
IP_PATTERN Pattern;
IP_PATTERN Mask;
PUL_TCI_FILTER pFilter;
ULONG InterfaceId;
ULONG LinkId;
//
// Sanity check
//
PAGED_CODE();
Status = STATUS_SUCCESS;
ASSERT_FLOW_OWNER(pOwner);
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
ASSERT(pHttpConnection->pConnection->AddressType == TDI_ADDRESS_TYPE_IP);
//
// Need to get the routing info to find the interface.
//
Status = UlGetConnectionRoutingInfo(
pHttpConnection->pConnection,
&InterfaceId,
&LinkId
);
if(!NT_SUCCESS(Status))
{
return Status;
}
//
// At this point we will be refering to the flows & filters
// in our list therefore we need to acquire the lock
//
UlAcquirePushLockShared(&g_pUlNonpagedData->TciIfcPushLock);
//
// If connection already has a filter attached, need do few
// more checks.
//
if (pHttpConnection->pFlow)
{
//
// Already present flow and filter must be valid.
//
ASSERT(IS_VALID_TCI_FLOW(pHttpConnection->pFlow));
ASSERT(IS_VALID_TCI_FILTER(pHttpConnection->pFilter));
ASSERT_FLOW_OWNER(pHttpConnection->pFlow->pOwner);
//
// If pOwner is same with the old filter's, then
// we will skip adding the same filter again.
//
if (pOwner == pHttpConnection->pFlow->pOwner)
{
//
// No need to add a new filter we are done.
//
UlTrace( TC,
("Http!UlTcAddFilter: Skipping same pFlow %p and"
"pFilter %p already exist\n",
pHttpConnection->pFlow,
pHttpConnection->pFilter,
pHttpConnection
));
ASSERT(NT_SUCCESS(Status));
goto end;
}
else
{
//
// If there was another filter before and this newly coming request
// is being going to a different site/flow. Then move the filter frm
// the old one to the new flow.
//
Status = UlpTcDeleteFilter(
pHttpConnection->pFlow,
pHttpConnection->pFilter
);
ASSERT(NT_SUCCESS(Status)); // We trust MSGPC.SYS
}
}
//
// Search through the cgroup's flowlist to find the one we need.
// This must find the flow which is installed on the interface
// on which we will send the outgoing packets. Not necessarily
// the same ip we have received the request. See the routing call
// above.
//
pFlow = UlpFindFlow(pOwner, Global, InterfaceId, LinkId);
if ( pFlow == NULL )
{
//
// Note: We'll come here for loopback interfaces, since we will not
// find a Traffic Control interfaces for loopback.
//
UlTrace( TC,
("Http!UlTcAddFilter: Unable to find interface %x \n",
InterfaceId
));
//
// It's possible that we might not find out a flow
// after all the interfaces went down, even though
// qos configured on the cgroup.
//
Status = STATUS_SUCCESS;
goto end;
}
pFilter = NULL;
pInterface = pFlow->pInterface;
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
RtlZeroMemory( &Pattern, sizeof(IP_PATTERN) );
RtlZeroMemory( &Mask, sizeof(IP_PATTERN) );
// Setup the filter's pattern
Pattern.SrcAddr = pHttpConnection->pConnection->LocalAddrIn.in_addr;
Pattern.S_un.S_un_ports.s_srcport = pHttpConnection->pConnection->LocalAddrIn.sin_port;
Pattern.DstAddr = pHttpConnection->pConnection->RemoteAddrIn.in_addr;
Pattern.S_un.S_un_ports.s_dstport = pHttpConnection->pConnection->RemoteAddrIn.sin_port;
Pattern.ProtocolId = IPPROTO_TCP;
// Setup the filter's Mask
RtlFillMemory(&Mask, sizeof(IP_PATTERN), 0xff);
TcGenericFilter.AddressType = NDIS_PROTOCOL_ID_TCP_IP;
TcGenericFilter.PatternSize = sizeof( IP_PATTERN );
TcGenericFilter.Pattern = &Pattern;
TcGenericFilter.Mask = &Mask;
Status = UlpTcAddFilter(
pFlow,
&TcGenericFilter,
LinkId,
&pFilter
);
if (!NT_SUCCESS(Status))
{
//
// Now this is a real failure, we will reject the connection.
//
UlTrace( TC,
("Http!UlTcAddFilter: Unable to add filter for;\n"
"\t pInterface : %p\n"
"\t pFlow : %p\n",
pInterface,
pFlow
));
goto end;
}
//
// Update the connection's pointers here.
//
pHttpConnection->pFlow = pFlow;
pHttpConnection->pFilter = pFilter;
pHttpConnection->BandwidthThrottlingEnabled = 1;
//
// Remember the connection for cleanup. If flow & filter get
// removed aynscly when connection still pointing to them
// we can go and null out the connection's private pointers.
//
pFilter->pHttpConnection = pHttpConnection;
//
// Sweet smell of success !
//
UlTrace(TC,
("Http!UlTcAddFilter: Success for;\n"
"\t pInterface : %p\n"
"\t pFlow : %p\n",
pInterface,
pFlow
));
UL_DUMP_TC_FILTER(pFilter);
end:
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Http!UlTcAddFilter: FAILURE %08lx \n", Status ));
}
UlReleasePushLockShared(&g_pUlNonpagedData->TciIfcPushLock);
return Status;
}
/***************************************************************************++
Routine Description:
Add a filter on an existing flow.
Arguments:
pFlow - Filter will be added on to this flow.
pGenericFilter - Generic filter parameters.
ppFilter - if everything goes ok. A new filter will be allocated.
--***************************************************************************/
NTSTATUS
UlpTcAddFilter(
IN PUL_TCI_FLOW pFlow,
IN PTC_GEN_FILTER pGenericFilter,
IN ULONG LinkId,
OUT PUL_TCI_FILTER *ppFilter
)
{
NTSTATUS Status;
PGPC_ADD_PATTERN_REQ pGpcReq;
GPC_ADD_PATTERN_RES GpcRes;
ULONG InBuffSize;
ULONG OutBuffSize;
ULONG PatternSize;
IO_STATUS_BLOCK IoStatBlock;
PUCHAR pTemp;
PGPC_IP_PATTERN pIpPattern;
PUL_TCI_FILTER pFilter;
//
// Sanity check
//
Status = STATUS_SUCCESS;
pGpcReq = NULL;
if ( !pGenericFilter || !pFlow || !g_GpcClientHandle )
{
return STATUS_INVALID_PARAMETER;
}
// Allocate a space for the filter
pFilter = UL_ALLOCATE_STRUCT(
NonPagedPool,
UL_TCI_FILTER,
UL_TCI_FILTER_POOL_TAG
);
if ( pFilter == NULL )
{
Status = STATUS_NO_MEMORY;
goto end;
}
pFilter->Signature = UL_TCI_FILTER_POOL_TAG;
// Buffer monkeying
PatternSize = sizeof(GPC_IP_PATTERN);
InBuffSize = sizeof(GPC_ADD_PATTERN_REQ) + (2 * PatternSize);
OutBuffSize = sizeof(GPC_ADD_PATTERN_RES);
pGpcReq = UL_ALLOCATE_STRUCT_WITH_SPACE(
PagedPool,
GPC_ADD_PATTERN_REQ,
(2 * PatternSize),
UL_TCI_GENERIC_POOL_TAG
);
if (pGpcReq == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
RtlZeroMemory( pGpcReq, InBuffSize);
RtlZeroMemory( &GpcRes, OutBuffSize);
pGpcReq->ClientHandle = g_GpcClientHandle;
pGpcReq->GpcCfInfoHandle = pFlow->FlowHandle;
pGpcReq->PatternSize = PatternSize;
pGpcReq->ProtocolTemplate = GPC_PROTOCOL_TEMPLATE_IP;
pTemp = (PUCHAR) &pGpcReq->PatternAndMask;
// Fill in the IP Pattern first
RtlCopyMemory( pTemp, pGenericFilter->Pattern, PatternSize );
pIpPattern = (PGPC_IP_PATTERN) pTemp;
//
// According to QoS Tc.dll ;
// This is a work around so that TCPIP wil not to find the index/link
// for ICMP/IGMP packets
//
pIpPattern->InterfaceId.InterfaceId = pFlow->pInterface->IfIndex;
pIpPattern->InterfaceId.LinkId = LinkId;
pIpPattern->Reserved[0] = 0;
pIpPattern->Reserved[1] = 0;
pIpPattern->Reserved[2] = 0;
// Fill in the mask
pTemp += PatternSize;
RtlCopyMemory( pTemp, pGenericFilter->Mask, PatternSize );
pIpPattern = (PGPC_IP_PATTERN) pTemp;
pIpPattern->InterfaceId.InterfaceId = 0xffffffff;
pIpPattern->InterfaceId.LinkId = 0xffffffff;
pIpPattern->Reserved[0] = 0xff;
pIpPattern->Reserved[1] = 0xff;
pIpPattern->Reserved[2] = 0xff;
// Time to invoke Gpsy
Status = UlpTcDeviceControl( g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_GPC_ADD_PATTERN,
pGpcReq,
InBuffSize,
&GpcRes,
OutBuffSize);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
if (NT_SUCCESS(Status))
{
//
// Insert the freshly created filter to the flow.
//
pFilter->FilterHandle = (HANDLE) GpcRes.GpcPatternHandle;
UlpInsertFilterEntry( pFilter, pFlow );
//
// Success!
//
*ppFilter = pFilter;
INCREMENT_FILTER_ADD();
}
}
end:
if (!NT_SUCCESS(Status))
{
INCREMENT_FILTER_ADD_FAILURE();
UlTrace( TC, ("Http!UlpTcAddFilter: FAILURE %08lx \n", Status ));
// Cleanup filter only if we failed, otherwise it will go to
// the filterlist of the flow.
if (pFilter)
{
UL_FREE_POOL( pFilter, UL_TCI_FILTER_POOL_TAG );
}
}
// Cleanup the temp Gpc buffer which we used to pass down filter info
// to GPC. We don't need it anymore.
if (pGpcReq)
{
UL_FREE_POOL( pGpcReq, UL_TCI_GENERIC_POOL_TAG );
}
return Status;
}
/***************************************************************************++
Routine Description:
UlTcDeleteFilter :
Connection only deletes the filter prior to deleting itself. Any
operation initiated by the connection requires tc resource shared
and none of those cause race condition.
Anything other than this, such as flow & filter removal because of
BW disabling on the site will acquire the lock exclusively. Hence
the pFlow & pFilter are safe as long as we acquire the tc resource
shared.
Arguments:
connection object to get the flow & filter after we acquire the tc lock
--***************************************************************************/
NTSTATUS
UlTcDeleteFilter(
IN PUL_HTTP_CONNECTION pHttpConnection
)
{
NTSTATUS Status;
//
// Sanity check
//
Status = STATUS_SUCCESS;
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
UlTrace(TC,("Http!UlTcDeleteFilter: for connection %p\n", pHttpConnection));
UlAcquirePushLockShared(&g_pUlNonpagedData->TciIfcPushLock);
if (pHttpConnection->pFlow)
{
Status = UlpTcDeleteFilter(
pHttpConnection->pFlow,
pHttpConnection->pFilter
);
}
UlReleasePushLockShared(&g_pUlNonpagedData->TciIfcPushLock);
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcRemoveFilter :
Arguments:
flow & filter
--***************************************************************************/
NTSTATUS
UlpTcDeleteFilter(
IN PUL_TCI_FLOW pFlow,
IN PUL_TCI_FILTER pFilter
)
{
NTSTATUS Status;
HANDLE FilterHandle;
//
// Sanity check
//
Status = STATUS_SUCCESS;
ASSERT(IS_VALID_TCI_FLOW(pFlow));
ASSERT(IS_VALID_TCI_FILTER(pFilter));
if (pFlow == NULL || pFilter == NULL)
{
return STATUS_INVALID_PARAMETER;
}
FilterHandle = pFilter->FilterHandle;
pFilter->pHttpConnection->pFlow = NULL;
pFilter->pHttpConnection->pFilter = NULL;
//
// Now call the actual worker for us
//
UlpRemoveFilterEntry( pFilter, pFlow );
Status = UlpTcDeleteGpcFilter( FilterHandle );
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Http!UlpTcDeleteFilter: FAILURE %08lx \n", Status ));
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcRemoveFilter :
This procedure builds up the structure necessary to delete a filter.
It then calls a routine to pass this info to the GPC.
Arguments:
FilterHandle - Handle of the filter to be deleted
--***************************************************************************/
NTSTATUS
UlpTcDeleteGpcFilter(
IN HANDLE FilterHandle
)
{
NTSTATUS Status;
ULONG InBuffSize;
ULONG OutBuffSize;
GPC_REMOVE_PATTERN_REQ GpcReq;
GPC_REMOVE_PATTERN_RES GpcRes;
IO_STATUS_BLOCK IoStatBlock;
Status = STATUS_SUCCESS;
ASSERT(FilterHandle != NULL);
InBuffSize = sizeof(GPC_REMOVE_PATTERN_REQ);
OutBuffSize = sizeof(GPC_REMOVE_PATTERN_RES);
GpcReq.ClientHandle = g_GpcClientHandle;
GpcReq.GpcPatternHandle = FilterHandle;
ASSERT(g_GpcFileHandle);
ASSERT(GpcReq.ClientHandle);
ASSERT(GpcReq.GpcPatternHandle);
Status = UlpTcDeviceControl( g_GpcFileHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_GPC_REMOVE_PATTERN,
&GpcReq,
InBuffSize,
&GpcRes,
OutBuffSize
);
if (NT_SUCCESS(Status))
{
Status = GpcRes.Status;
if (NT_SUCCESS(Status))
{
UlTrace( TC,
("Http!UlpTcDeleteGpcFilter: FilterHandle %d deleted in TC as well.\n",
FilterHandle
));
INCREMENT_FILTER_DELETE();
}
}
if (!NT_SUCCESS(Status))
{
INCREMENT_FILTER_DELETE_FAILURE();
UlTrace( TC, ("Http!UlpTcDeleteGpcFilter: FAILURE %08lx \n", Status ));
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpInsertFilterEntry :
Inserts a filter entry to the filter list of the flow.
Arguments:
pEntry - The filter entry to be added to the flow list
--***************************************************************************/
VOID
UlpInsertFilterEntry(
IN PUL_TCI_FILTER pEntry,
IN OUT PUL_TCI_FLOW pFlow
)
{
LONGLONG listSize;
KIRQL oldIrql;
//
// Sanity check.
//
ASSERT(pEntry);
ASSERT(IS_VALID_TCI_FILTER(pEntry));
ASSERT(pFlow);
//
// add to the list
//
UlAcquireSpinLock( &pFlow->FilterListSpinLock, &oldIrql );
InsertHeadList( &pFlow->FilterList, &pEntry->Linkage );
pFlow->FilterListSize += 1;
listSize = pFlow->FilterListSize;
UlReleaseSpinLock( &pFlow->FilterListSpinLock, oldIrql );
ASSERT( listSize >= 1);
}
/***************************************************************************++
Routine Description:
UlRemoveFilterEntry :
Removes a filter entry frm the filter list of the flow.
Arguments:
pEntry - The filter entry to be removed from the flow list
--***************************************************************************/
VOID
UlpRemoveFilterEntry(
IN PUL_TCI_FILTER pEntry,
IN OUT PUL_TCI_FLOW pFlow
)
{
LONGLONG listSize;
KIRQL oldIrql;
//
// Sanity check.
//
ASSERT(IS_VALID_TCI_FLOW(pFlow));
ASSERT(IS_VALID_TCI_FILTER(pEntry));
//
// And the work
//
UlAcquireSpinLock( &pFlow->FilterListSpinLock, &oldIrql );
RemoveEntryList( &pEntry->Linkage );
pFlow->FilterListSize -= 1;
listSize = pFlow->FilterListSize;
pEntry->Linkage.Flink = pEntry->Linkage.Blink = NULL;
UlReleaseSpinLock( &pFlow->FilterListSpinLock, oldIrql );
ASSERT( listSize >= 0 );
UlTrace( TC, ("Http!UlpRemoveFilterEntry: FilterEntry %p removed/deleted.\n",
pEntry
));
UL_FREE_POOL_WITH_SIG( pEntry, UL_TCI_FILTER_POOL_TAG );
}
//
// Various helpful utilities for TCI module
//
/***************************************************************************++
Routine Description:
Find the flow in the cgroups flow list by looking at the IP address
of each flows interface. The rule is cgroup will install one flow
on each interface available.
By having a flow list in each cgroup we are able to do a faster
flow lookup. This is more scalable than doing a linear search for
all the flows of the interface.
Arguments:
pOwner - The config group of the site OR the control channel
Global - Must be TRUE if the owner is control channel.
InterfaceID - Interface ID
LinkID - LinkID
Return Value:
PUL_TCI_FLOW - The flow we found OR NULL if we couldn't find one.
--***************************************************************************/
PUL_TCI_FLOW
UlpFindFlow(
IN PVOID pOwner,
IN BOOLEAN Global,
IN ULONG InterfaceId,
IN ULONG LinkId
)
{
PLIST_ENTRY pFlowListHead;
PLIST_ENTRY pFlowEntry;
PUL_TCI_FLOW pFlow;
//
// Sanity check and dispatch the flowlist.
//
PAGED_CODE();
if (Global)
{
PUL_CONTROL_CHANNEL
pControlChannel = (PUL_CONTROL_CHANNEL) pOwner;
ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
pFlowListHead = &pControlChannel->FlowListHead;
}
else
{
PUL_CONFIG_GROUP_OBJECT
pConfigGroup = (PUL_CONFIG_GROUP_OBJECT) pOwner;
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
pFlowListHead = &pConfigGroup->FlowListHead;
}
//
// Walk the list and try to find the flow.
//
pFlowEntry = pFlowListHead->Flink;
while ( pFlowEntry != pFlowListHead )
{
pFlow = CONTAINING_RECORD(
pFlowEntry,
UL_TCI_FLOW,
Siblings
);
ASSERT(IS_VALID_TCI_FLOW(pFlow));
ASSERT(IS_VALID_TCI_INTERFACE(pFlow->pInterface));
if(UlpMatchTcInterface(
pFlow->pInterface,
InterfaceId,
LinkId
))
{
return pFlow;
}
pFlowEntry = pFlowEntry->Flink;
}
//
// Couldn't find one. Actually this could be the right time to
// refresh the flow list of this cgroup or control channel !!
//
return NULL;
}
/***************************************************************************++
Routine Description:
UlpTcDeviceControl :
Arguments:
As usual
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcDeviceControl(
IN HANDLE FileHandle,
IN HANDLE EventHandle,
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
IN ULONG Ioctl,
IN PVOID InBuffer,
IN ULONG InBufferSize,
IN PVOID OutBuffer,
IN ULONG OutBufferSize
)
{
NTSTATUS Status;
UNREFERENCED_PARAMETER(EventHandle);
UNREFERENCED_PARAMETER(ApcRoutine);
UNREFERENCED_PARAMETER(ApcContext);
//
// Sanity check.
//
PAGED_CODE();
Status = STATUS_SUCCESS;
Status = ZwDeviceIoControlFile(
FileHandle, // FileHandle
NULL, // Event
NULL, // ApcRoutine
NULL, // ApcContext
pIoStatusBlock, // IoStatusBlock
Ioctl, // IoControlCode
InBuffer, // InputBuffer
InBufferSize, // InputBufferLength
OutBuffer, // OutputBuffer
OutBufferSize // OutputBufferLength
);
if (Status == STATUS_PENDING)
{
Status = ZwWaitForSingleObject(
FileHandle, // Handle
TRUE, // Alertable
NULL // Timeout
);
Status = pIoStatusBlock->Status;
}
return Status;
}
#if DBG
/***************************************************************************++
Routine Description:
UlDumpTCInterface :
Helper utility to display interface content.
Arguments:
PUL_TCI_INTERFACE - TC Interface to be dumped
--***************************************************************************/
VOID
UlDumpTCInterface(
IN PUL_TCI_INTERFACE pTcIfc
)
{
ASSERT(IS_VALID_TCI_INTERFACE(pTcIfc));
UlTrace( TC,("Http!UlDumpTCInterface: \n pTcIfc @ %p\n"
"\t Signature = %08lx \n",
pTcIfc, pTcIfc->Signature));
UlTrace( TC,(
"\t IsQoSEnabled: = %u \n"
"\t IfIndex: = %d \n"
"\t NameLength: = %u \n"
"\t Name: = %ws \n"
"\t InstanceIDLength: = %u \n"
"\t InstanceID: = %ws \n"
"\t FlowListSize: = %d \n"
"\t AddrListBytesCount: = %d \n"
"\t pAddressListDesc: = %p \n",
pTcIfc->IsQoSEnabled,
pTcIfc->IfIndex,
pTcIfc->NameLength,
pTcIfc->Name,
pTcIfc->InstanceIDLength,
pTcIfc->InstanceID,
pTcIfc->FlowListSize,
pTcIfc->AddrListBytesCount,
pTcIfc->pAddressListDesc
));
}
/***************************************************************************++
Routine Description:
UlDumpTCFlow :
Helper utility to display interface content.
Arguments:
PUL_TCI_FLOW - TC Flow to be dumped
--***************************************************************************/
VOID
UlDumpTCFlow(
IN PUL_TCI_FLOW pFlow
)
{
ASSERT(IS_VALID_TCI_FLOW(pFlow));
UlTrace( TC,
("Http!UlDumpTCFlow: \n"
" pFlow @ %p\n"
"\t Signature = %08lx \n"
"\t pInterface @ %p \n"
"\t FlowHandle = %d \n"
"\t GenFlow @ %p \n"
"\t FlowRate KB/s = %d \n"
"\t FilterListSize = %I64d \n"
"\t pOwner (store) = %p \n"
,
pFlow,
pFlow->Signature,
pFlow->pInterface,
pFlow->FlowHandle,
&pFlow->GenFlow,
pFlow->GenFlow.SendingFlowspec.TokenRate / 1024,
pFlow->FilterListSize,
pFlow->pOwner
));
UNREFERENCED_PARAMETER(pFlow);
}
/***************************************************************************++
Routine Description:
UlDumpTCFilter :
Helper utility to display filter structure content.
Arguments:
PUL_TCI_FILTER pFilter
--***************************************************************************/
VOID
UlDumpTCFilter(
IN PUL_TCI_FILTER pFilter
)
{
ASSERT(IS_VALID_TCI_FILTER(pFilter));
UlTrace( TC,
("Http!UlDumpTCFilter: \n"
" pFilter @ %p\n"
"\t Signature = %08lx \n"
"\t pHttpConnection = %p \n"
"\t FilterHandle = %d \n",
pFilter,
pFilter->Signature,
pFilter->pHttpConnection,
pFilter->FilterHandle
));
UNREFERENCED_PARAMETER(pFilter);
}
#endif // DBG
/***************************************************************************++
Routine Description:
See if the Traffic Interface matches the InterfaceID & LinkID. LinkId
matches are done only for WAN connections.
Arguments:
pIntfc - The TC inteface
InterfaceId - Interface index.
LinkId - Link ID
Return Value:
TRUE - Matches.
FALSE - Does not match.
--***************************************************************************/
BOOLEAN
UlpMatchTcInterface(
IN PUL_TCI_INTERFACE pIntfc,
IN ULONG InterfaceId,
IN ULONG LinkId
)
{
NETWORK_ADDRESS UNALIGNED64 *pAddr;
NETWORK_ADDRESS_IP UNALIGNED64 *pIpNetAddr = NULL;
ULONG cAddr;
ULONG index;
if(pIntfc->IfIndex == InterfaceId)
{
// InterfaceID's matched. If it's a wan-link, we need to compare the
// LinkId with the remote address.
if(pIntfc->pAddressListDesc->MediaType == NdisMediumWan)
{
cAddr = pIntfc->pAddressListDesc->AddressList.AddressCount;
pAddr = (UNALIGNED64 NETWORK_ADDRESS *)
&pIntfc->pAddressListDesc->AddressList.Address[0];
for (index = 0; index < cAddr; index++)
{
if (pAddr->AddressType == NDIS_PROTOCOL_ID_TCP_IP)
{
pIpNetAddr =
(UNALIGNED64 NETWORK_ADDRESS_IP *)&pAddr->Address[0];
if(pIpNetAddr->in_addr == LinkId)
{
return TRUE;
}
}
pAddr = (UNALIGNED64 NETWORK_ADDRESS *)(((PUCHAR)pAddr)
+ pAddr->AddressLength
+ FIELD_OFFSET(NETWORK_ADDRESS, Address));
}
}
else
{
return TRUE;
}
}
return FALSE;
}