Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

4521 lines
116 KiB

/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
ultci.cxx - 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. In other
word i've put it into a
shape to be a component
--*/
#include "precomp.h"
//
// A nonpaged resource - TciIfcResource - guards the interface list
// and its flows.
//
LIST_ENTRY g_TciIfcListHead = {NULL,NULL};
//LIST_ENTRY g_TcCGroupListHead = {NULL,NULL};
BOOLEAN g_InitTciCalled = FALSE;
//
// GPC handles to talk to
//
HANDLE g_GpcFileHandle; // result of CreateFile on GPC device
GPC_HANDLE g_GpcClientHandle; // 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
//
LONG g_PSchedInstalled = 0;
//
// Shows if Global Bandwidth Throttling is enabled or not
//
LONG g_GlobalThrottling = 0;
//
// For interface notifications
//
PVOID g_TcInterfaceUpNotificationObject = NULL;
PVOID g_TcInterfaceDownNotificationObject = NULL;
PVOID g_TcInterfaceChangeNotificationObject = NULL;
#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)
#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
)
{
NTSTATUS Status;
PAGED_CODE();
Status = STATUS_SUCCESS;
ASSERT(!g_InitTciCalled);
g_GlobalThrottling = 0;
g_PSchedInstalled = 0;
if (!g_InitTciCalled)
{
InitializeListHead(&g_TciIfcListHead);
//InitializeListHead(&g_TcCGroupListHead);
Status = UlInitializeResource(
&g_pUlNonpagedData->TciIfcResource,
"TciIfcResource",
0,
UL_TCI_RESOURCE_TAG
);
ASSERT(NT_SUCCESS(Status));
Status = UlpTcInitializeGpc();
if (!NT_SUCCESS(Status))
goto cleanup;
UlTrace( TC, ("Ul!UlTcInitialize: InitializeGpc Status %08lx \n", Status ));
Status = UlpTcInitializeTcpDevice();
if (!NT_SUCCESS(Status))
goto cleanup;
UlTrace( TC, ("Ul!UlTcInitialize: InitializeTcp Status %08lx \n", Status ));
Status = UlpTcGetFriendlyNames();
if (!NT_SUCCESS(Status))
goto cleanup;
UlTrace( TC, ("Ul!UlTcInitialize: GetFriendlyNames Status %08lx \n", Status ));
Status = UlpTcRegisterForCallbacks();
if (!NT_SUCCESS(Status))
goto cleanup;
UlTrace( TC, ("Ul!UlTcInitialize: UlpTcRegisterForCallbacks Status %08lx \n", Status ));
//
// Success !
//
g_InitTciCalled = TRUE;
}
cleanup:
if (!NT_SUCCESS(Status))
{
NTSTATUS TempStatus;
UlTrace( TC, ("Ul!UlTcInitialize: FAILURE %08lx \n", Status ));
TempStatus = UlDeleteResource( &g_pUlNonpagedData->TciIfcResource );
ASSERT(NT_SUCCESS(TempStatus));
//
// Do not forget to DeRegister Gpc Client & Close Device Handle
//
if (g_GpcClientHandle != NULL)
{
UlpTcDeRegisterGpcClient();
ASSERT(g_GpcFileHandle);
ZwClose(g_GpcFileHandle);
UlTrace( TC, ("Ul!UlTcInitialize: Gpc Device Handle Closed.\n" ));
}
if (g_TcpDeviceHandle != NULL)
{
ZwClose(g_TcpDeviceHandle);
g_TcpDeviceHandle=NULL;
UlTrace( TC, ("Ul!UlTcInitialize: Tcp Device Handle Closed.\n" ));
}
}
return Status;
}
/***************************************************************************++
Routine Description:
UlTcTerminate :
Terminates the TCI module by releasing our TCI resource and
cleaning up all the qos stuff.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
VOID
UlTcTerminate(
VOID
)
{
NTSTATUS Status;
PAGED_CODE();
Status = STATUS_SUCCESS;
UlTrace( TC, ("Ul!UlTcTerminate: ... \n" ));
if (g_InitTciCalled)
{
//
// 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 terminate all the QoS stuff
//
Status = UlpTcReleaseAll();
ASSERT(NT_SUCCESS(Status));
if (g_TcpDeviceHandle != NULL)
{
ZwClose(g_TcpDeviceHandle);
g_TcpDeviceHandle = NULL;
}
Status = UlDeleteResource(
&g_pUlNonpagedData->TciIfcResource
);
ASSERT(NT_SUCCESS(Status));
g_InitTciCalled = FALSE;
}
UlTrace( TC, ("Ul!UlTcTerminate: Completed.\n" ));
}
/***************************************************************************++
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
//
RtlInitUnicodeString(&GpcNameString, DD_GPC_DEVICE_NAME);
InitializeObjectAttributes(&GpcObjAttribs,
&GpcNameString,
OBJ_CASE_INSENSITIVE | UL_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, ("Ul!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))
{
UlTrace( TC, ("Ul!UlpTcRegisterGpcClient: FAILURE 1 %08lx \n", Status ));
goto end;
}
Status = GpcRes.Status;
if ( NT_SUCCESS(Status) )
{
g_GpcClientHandle = GpcRes.ClientHandle;
UlTrace( TC, ("Ul!UlpTcRegisterGpcClient: Gpc Client %p Registered.\n",
g_GpcClientHandle
));
}
else
{
g_GpcClientHandle = NULL;
UlTrace( TC, ("Ul!UlpTcRegisterGpcClient: FAILURE 2 %08lx \n", Status ));
}
end:
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))
{
goto end;
}
Status = GpcRes.Status;
if ( NT_SUCCESS(Status) )
{
g_GpcClientHandle = NULL;
UlTrace( TC, ("Ul!UlpTcDeRegisterGpcClient: Client DeRegistered.\n" ));
}
else
{
UlTrace( TC, ("Ul!UlpTcDeRegisterGpcClient: FAILURE %08lx \n", Status ));
}
end:
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcGetIpAddr :
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
BOOLEAN
UlpTcGetIpAddr(
IN PADDRESS_LIST_DESCRIPTOR pAddressListDesc,
OUT PULONG pIn_addr,
OUT PULONG pSpecificLinkCtx
)
{
NETWORK_ADDRESS UNALIGNED64 *pAddr;
NETWORK_ADDRESS_IP UNALIGNED64 *pIpNetAddr = NULL;
NETWORK_ADDRESS_IP UNALIGNED64 *p2ndIpNetAddr = NULL;
ULONG cAddr;
ULONG index;
cAddr = pAddressListDesc->AddressList.AddressCount;
if (cAddr == 0)
{
return FALSE;
}
pAddr = (UNALIGNED64 NETWORK_ADDRESS *) &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];
break;
}
pAddr = (UNALIGNED64 NETWORK_ADDRESS *)(((PUCHAR)pAddr)
+ pAddr->AddressLength
+ FIELD_OFFSET(NETWORK_ADDRESS, Address));
}
// Findout the SpecificLinkCtx (Remote IP address) for WAN links
if( pAddressListDesc->MediaType == NdisMediumWan &&
index+1 < cAddr )
{
//
// There is another address that contains
// the remote client address
// this should be used as the link ID
//
pAddr = (UNALIGNED64 NETWORK_ADDRESS *)(((PUCHAR)pAddr)
+ pAddr->AddressLength
+ FIELD_OFFSET(NETWORK_ADDRESS, Address));
if (pAddr->AddressType == NDIS_PROTOCOL_ID_TCP_IP)
{
//
// Parse the second IP address,
// this would be the remote IP address for dialin WAN
//
p2ndIpNetAddr = (UNALIGNED64 NETWORK_ADDRESS_IP *)&pAddr->Address[0];
*pSpecificLinkCtx = p2ndIpNetAddr->in_addr;
}
}
if ( pIpNetAddr )
{
(*pIn_addr) = pIpNetAddr->in_addr;
return TRUE;
}
else
{
return FALSE;
}
}
/***************************************************************************++
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
//
RtlInitUnicodeString(&TcpNameString, DD_TCP_DEVICE_NAME);
InitializeObjectAttributes(&TcpObjAttribs,
&TcpNameString,
OBJ_CASE_INSENSITIVE | UL_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:
UlpTcUpdateInterfaceMTU :
Helper function to get the interface MTU sizes by querrying the TCP.
Acquire the TciIfcResource before callling this function.
Make sure that the if_indexes are correct in the individual tc_flow
structures.
WORKITEM:
Current approach is not really scalable in terms of interface count.
But assuming the #of interfaces will be few simplify the code a lot.
--***************************************************************************/
NTSTATUS
UlpTcUpdateInterfaceMTU(
VOID
)
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatBlock;
TDIObjectID *ID;
TCP_REQUEST_QUERY_INFORMATION_EX trqiInBuf;
TDIEntityID *EntityTable;
TDIEntityID *pEntity;
IFEntry *pIFEntry;
ULONG InBufLen;
ULONG OutBufLen;
ULONG NumEntities;
ULONG NumInterfacesUpdated;
ULONG index;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
//
// Initialize & Sanity check first
//
Status = STATUS_SUCCESS;
ASSERT( g_TcpDeviceHandle != NULL );
pIFEntry = NULL;
NumInterfacesUpdated = 0;
UlTrace(TC,("Ul!UlpTcUpdateInterfaceMTU ...\n" ));
//
// Enumerate the interfaces by querying the TCP. Get TDI entity count
// search through entities to find out the IF_ENTITYs. The make yet
// another query to get the full interface info including the MTU size
//
InBufLen = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX);
OutBufLen = sizeof(TDIEntityID) * MAX_TDI_ENTITIES;
EntityTable = (TDIEntityID *) UL_ALLOCATE_ARRAY(
PagedPool,
UCHAR,
OutBufLen,
UL_TCI_GENERIC_POOL_TAG
);
if (EntityTable == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
RtlZeroMemory(EntityTable,OutBufLen);
RtlZeroMemory(&trqiInBuf,sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
ID = &(trqiInBuf.ID);
ID->toi_entity.tei_entity = GENERIC_ENTITY;
ID->toi_entity.tei_instance = 0;
ID->toi_class = INFO_CLASS_GENERIC;
ID->toi_type = INFO_TYPE_PROVIDER;
ID->toi_id = ENTITY_LIST_ID;
Status = UlpTcDeviceControl(
g_TcpDeviceHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_TCP_QUERY_INFORMATION_EX,
&trqiInBuf,
InBufLen,
EntityTable,
OutBufLen
);
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!UlpTcUpdateInterfaceMTU: Get TDIEntityID failed\n"));
goto end;
}
// Now we have all the TDI entities
NumEntities = ((ULONG)(IoStatBlock.Information)) / sizeof(TDIEntityID);
UlTrace(TC,("Ul!UlpTcUpdateInterfaceMTU: #Of TDI Entities %d\n", NumEntities));
// Search through the interface entries
for (index=0,pEntity=EntityTable; index < NumEntities; index++,pEntity++)
{
if (pEntity->tei_entity == IF_ENTITY)
{
// Allocate a buffer for the querry
if (pIFEntry == NULL)
{
OutBufLen = sizeof(IFEntry) + MAX_IFDESCR_LEN;
pIFEntry = (IFEntry *) UL_ALLOCATE_ARRAY(
PagedPool,
UCHAR,
OutBufLen,
UL_TCI_GENERIC_POOL_TAG
);
if (pIFEntry == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
RtlZeroMemory(pIFEntry,OutBufLen);
}
// Get the full IFEntry. It's a pitty that we only look at the
// Mtu size after getting such a big structure.
InBufLen = sizeof(TCP_REQUEST_QUERY_INFORMATION_EX);
RtlZeroMemory(&trqiInBuf,sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
ID = &(trqiInBuf.ID);
ID->toi_entity.tei_entity = IF_ENTITY;
ID->toi_entity.tei_instance = pEntity->tei_instance;
ID->toi_class = INFO_CLASS_PROTOCOL;
ID->toi_type = INFO_TYPE_PROVIDER;
ID->toi_id = IF_MIB_STATS_ID;
Status = UlpTcDeviceControl(
g_TcpDeviceHandle,
NULL,
NULL,
NULL,
&IoStatBlock,
IOCTL_TCP_QUERY_INFORMATION_EX,
&trqiInBuf,
InBufLen,
pIFEntry,
OutBufLen
);
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!UlpTcUpdateInterfaceMTU: Get_IF_MIB_STATS_ID failed\n" ));
goto end;
}
// Now we have the interface info including the mtu size for this entity
// Find the corresponding UL_TCI_INTERFACE by looking at the index
pInterfaceEntry = g_TciIfcListHead.Flink;
while ( pInterfaceEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
if (pIFEntry->if_index == pInterface->IfIndex)
{
pInterface->MTUSize = pIFEntry->if_mtu;
UlTrace(TC,
("Ul!UlpTcUpdateInterfaceMTU: if_index %d if_mtu %d if_speed %d\n",
pIFEntry->if_index, pIFEntry->if_mtu, pIFEntry->if_speed ));
UL_DUMP_TC_INTERFACE(pInterface);
NumInterfacesUpdated++;
}
// search through next interface
pInterfaceEntry = pInterfaceEntry->Flink;
}
}
}
UlTrace(TC,("Ul!UlpTcUpdateInterfaceMTU: %d interfaces updated.\n",
NumInterfacesUpdated ));
end:
// Whine about the problems
if (!NT_SUCCESS(Status))
{
UlTrace( TC,("Ul!UlpTcUpdateInterfaceMTU: FAILED Status %08lx\n",
Status ));
}
// Release the private buffers
if ( pIFEntry != NULL )
{
UL_FREE_POOL( pIFEntry, UL_TCI_GENERIC_POOL_TAG );
}
if ( EntityTable != NULL )
{
UL_FREE_POOL( EntityTable, UL_TCI_GENERIC_POOL_TAG );
}
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 n,k;
IO_STATUS_BLOCK IoStatBlock;
TDIObjectID *ID;
TCP_REQUEST_QUERY_INFORMATION_EX trqiInBuf;
ULONG InBuffLen;
ULONG NumEntries;
//
// Initialize & Sanity check first
//
Status = STATUS_SUCCESS;
NumEntries = 0;
pIpAddrTbl = NULL;
//
// BUGBUG should get ip address size and allocate enough buffer.
// Or handle the STATUS_BUFFER_TOO_SMALL
//
IpAddrTblSize = sizeof(IPAddrEntry) * 1024;
UlTrace(TC,("Ul!UlpTcGetInterfaceIndex: ....\n" ));
ASSERT( g_TcpDeviceHandle != NULL );
if (pIntfc->IpAddr)
{
// Allocate a private buffer to retrieve Ip Address table from TCP
pIpAddrTbl = (IPAddrEntry *) UL_ALLOCATE_ARRAY(
PagedPool,
UCHAR,
IpAddrTblSize,
UL_TCI_GENERIC_POOL_TAG
);
if (pIpAddrTbl == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
// Send down an Ioctl to Tcp to get the ip address table
RtlZeroMemory(pIpAddrTbl,IpAddrTblSize);
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;
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 (!NT_SUCCESS(Status))
{
goto end;
}
// Look at how many entries were written to the output buffer (pIpAddrTbl)
NumEntries = (((ULONG)IoStatBlock.Information)/sizeof(IPAddrEntry));
UlTrace(TC,("Ul!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++)
{
if (pIpAddrTbl[k].iae_addr == pIntfc->IpAddr)
{
// Found it found it! Get the index baby.
pIntfc->IfIndex = pIpAddrTbl[k].iae_index;
UlTrace(TC,("Ul!UlpTcGetInterfaceIndex: got for index %d\n",
pIntfc->IfIndex ));
break;
}
}
}
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!UlpTcGetInterfaceIndex: FAILED Status %08lx\n",
Status));
}
if ( pIpAddrTbl != NULL )
{
UL_FREE_POOL( pIpAddrTbl, UL_TCI_GENERIC_POOL_TAG );
}
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcAllocateInterface :
... fairly straightforward ...
Argument:
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
//
ASSERT(NameLength <= MAX_STRING_LENGTH);
ASSERT(InstanceIDLength <= MAX_STRING_LENGTH);
//
// 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;
}
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 =
UlpTcGetIpAddr( pTcIfc->pAddressListDesc,
&pTcIfc->IpAddr,
&pTcIfc->SpecificLinkCtx
);
return pTcIfc;
}
VOID
UlpTcFreeInterface(
IN OUT PUL_TCI_INTERFACE pTcIfc
)
{
// Clean up the interface & addreslist pointer
if (pTcIfc)
{
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:
UlpTcGetFriendlyNames :
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,("Ul!UlpTcGetFriendlyNames: PSCHED hasn't been installed !\n"));
}
else
{
UlTrace(TC,("Ul!UlpTcGetFriendlyNames:IoWMIOpenBlock FAILED Status %08lx\n",
Status));
}
return Status;
}
//
// Mark that PSched is installed
//
g_PSchedInstalled = 1;
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,
("Ul!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;
PULONG lpdwNameOffsets;
BOOLEAN bFixedSize = FALSE;
USHORT usNameLength;
ULONG DescSize;
PTC_SUPPORTED_INFO_BUFFER pTcInfoBuffer;
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
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
InsertTailList(&g_TciIfcListHead, &pTcIfc->Linkage );
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
//
// 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) );
//
// Update the mtu sizes for all interfaces now.
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
UlpTcUpdateInterfaceMTU();
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace(TC,("Ul!UlpTcGetFriendlyNames: got all the names.\n"));
}
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!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, ("Ul!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;
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
//
// 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 );
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
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;
ASSERT(IS_VALID_TCI_INTERFACE(pInterface));
//
// 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 cg's flowlist
// as well if it's not a global flow. We understand
// that by looking at its config group pointer.
//
if (pFlow->pConfigGroup)
{
ASSERT(IS_VALID_CONFIG_GROUP(pFlow->pConfigGroup));
RemoveEntryList(&pFlow->Siblings);
pFlow->Siblings.Flink = pFlow->Siblings.Blink = NULL;
pFlow->pConfigGroup = NULL;
}
//
// Now remove from the interface.
//
Status = UlpTcDeleteFlow(pFlow);
ASSERT(NT_SUCCESS(Status));
}
UlTrace(TC,("Ul!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,("Ul!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,("Ul!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,("Ul!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,("Ul!UlpTcHandleIfcUp: Adding %ws %d\n", Name, BufferSize ));
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
//
// 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,("Ul!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 ((pTcIfc->IpAddr != 0 && pTcIfcTemp->IpAddr == pTcIfc->IpAddr) ||
(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 );
//
// Update the MTU Size
//
UlpTcUpdateInterfaceMTU();
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!UlpTcHandleIfcUp: FAILURE %08lx \n", Status ));
if (pTcIfc != NULL)
{
UlpTcFreeInterface(pTcIfc);
}
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
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;
ULONG AddrListDescSize;
PTC_SUPPORTED_INFO_BUFFER pTcInfoBuffer;
PUL_TCI_INTERFACE pTcIfc;
PUL_TCI_INTERFACE pTcIfcTemp;
PLIST_ENTRY pEntry;
Status = STATUS_SUCCESS;
UlTrace(TC,("Ul!UlpTcHandleIfcDown: Removing %ws\n", Name ));
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
//
// 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;
}
//
// Remove this interface and its flows etc ...
//
UlpTcCloseInterface( pTcIfc );
RemoveEntryList( &pTcIfc->Linkage );
UlpTcFreeInterface( pTcIfc );
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!UlpTcHandleIfcDown: FAILURE %08lx \n", Status ));
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
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,("Ul!UlpTcHandleIfcChange: Updating %ws\n", Name ));
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
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;
}
// 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 =
UlpTcGetIpAddr( pTcIfc->pAddressListDesc,
&pTcIfc->IpAddr,
&pTcIfc->SpecificLinkCtx
);
// ReFresh the interface index from TCP.
Status = UlpTcGetInterfaceIndex( pTcIfc );
if (!NT_SUCCESS(Status))
goto end;
// Update the MTU Size
UlpTcUpdateInterfaceMTU();
end:
if (!NT_SUCCESS(Status))
{
UlTrace(TC,("Ul!UlpTcHandleIfcChange: FAILURE %08lx \n", Status ));
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
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;
UlTrace( TC, ("Ul!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, ("Ul!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,("Ul!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 TciIfcResource 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
//
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, ("Ul!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, ("Ul!UlpTcDeleteFlow: FAILURE %08lx \n", Status ));
}
else
{
UlTrace( TC, ("Ul!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))
{
UlTrace( TC, ("Ul!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,
IN ULONG MtuSize
)
{
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,MtuSize);
//
// 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->pConfigGroup = 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
//
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) )
{
UlTrace( TC, ("Ul!UlpModifyFlow: flow %p modified on interface %p \n",
pFlow,
pInterface
));
}
else
{
UlTrace( TC, ("Ul!UlpModifyFlow: FAILURE %08lx for GpcClient %u\n",
Status,
g_GpcClientHandle
));
}
UL_FREE_POOL( pGpcReq, UL_TCI_GENERIC_POOL_TAG );
return Status;
}
/***************************************************************************++
Routine Description:
UlpAddFlow :
Add a flow on existing QoS Enabled interface
Arguments:
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
//
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= GPC_CF_QOS; // ?? Not sure about this
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))
{
(*pHandle) = (HANDLE) GpcRes.GpcCfInfoHandle;
UlTrace( TC, ("Ul!UlpAddFlow: a new flow added %p on interface %p \n",
pGenericFlow,
pInterface
));
}
else
{
UlTrace( TC, ("Ul!UlpAddFlow: FAILURE %08lx for GpcClient %u\n",
Status,
g_GpcClientHandle
));
}
UL_FREE_POOL( pGpcReq, UL_TCI_GENERIC_POOL_TAG );
return Status;
}
/***************************************************************************++
Routine Description:
UlTcAddFlowsForSite :
Add a flow on existing QoS Enabled interface
Arguments:
pConfigGroup - The config group of the site
MaxBandwidth - The Max bandwidth we are going to enforce by a FlowSpec
in B/s
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlTcAddFlowsForSite(
IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
IN HTTP_BANDWIDTH_LIMIT MaxBandwidth
)
{
NTSTATUS Status;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
PUL_TCI_FLOW pFlow;
//
// Sanity check first
//
Status = STATUS_SUCCESS;
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
UlTrace(TC,("Ul!UlTcAddFlowsForSite: For cgroup %p BWT %d KB/s\n",
pConfigGroup,
MaxBandwidth
));
//
// Visit each interface and add a flow for this site. Following lock also
// protects the FlowListHead of the ConfigGroup object. We only change it
// when we acquire this lock here. Or when removing the site's flows.
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
// TODO: Remember this cgroup incase interface goes up/down later on,we can still
// TODO: recover and reinstall the flows of the cgroup properly.
// InsertTailList(&g_TcCGroupListHead, &pConfigGroup->Linkage );
// Proceed and add the flows to the inetrfaces
pInterfaceEntry = g_TciIfcListHead.Flink;
while ( pInterfaceEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
//
// Allocate a flow sturcture
//
pFlow = UlpTcAllocateFlow( MaxBandwidth, pInterface->MTUSize );
if ( pFlow == NULL )
{
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
return STATUS_NO_MEMORY;
}
//
// Add the flow by making a TC call down to gpc
//
Status = UlpAddFlow( pInterface,
pFlow,
&pFlow->FlowHandle
);
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Ul!UlTcAddFlowsForSite: FAILURE %08lx \n", Status ));
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UL_FREE_POOL_WITH_SIG( pFlow, UL_TCI_FLOW_POOL_TAG );
return Status;
}
//
// Proceed with further initialization as we have successfully installed
// the flow. First link the flow back to its owner interface.
//
pFlow->pInterface = pInterface;
//
// Add this to the interface's flowlist as well
//
InsertHeadList( &pInterface->FlowList, &pFlow->Linkage );
pInterface->FlowListSize += 1;
//
// Also add this to the cgroup's flowlist. Set the cgroup pointer.
// Do not bump up the cgroup refcount. Otherwise cgroup cannot be
// cleaned up until Tc terminates. And flows cannot be removed un
// till termination.
//
InsertHeadList( &pConfigGroup->FlowListHead, &pFlow->Siblings );
pFlow->pConfigGroup = pConfigGroup;
UlTrace( TC,
("Ul!UlTcAddFlowsForSite: Added the pFlow %p on pInterface %p\n",
pFlow,
pInterface
));
UL_DUMP_TC_FLOW(pFlow);
//
// Proceed to the next interface
//
pInterfaceEntry = pInterfaceEntry->Flink;
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace( TC, ("Ul!UlTcAddFlowsForSite: SUCCESS \n" ));
return Status;
}
/***************************************************************************++
Routine Description:
UlTcModifyFlowsForSite :
This function will be called when there's a cgroup change happening for
the site's bandwidth throttling settings.
Its caller responsiblity to remember the new settings in the cgroup.
We will update the FlowSpec on the existing flows
Arguments:
PUL_CONFIG_GROUP_OBJECT - Pointer to the cgroup of the site.
HTTP_BANDWIDTH_LIMIT - The new bandwidth throttling setting
in B/s
--***************************************************************************/
NTSTATUS
UlTcModifyFlowsForSite(
IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
IN HTTP_BANDWIDTH_LIMIT NewBandwidth
)
{
NTSTATUS Status;
PLIST_ENTRY pFlowEntry;
PUL_TCI_FLOW pFlow;
HTTP_BANDWIDTH_LIMIT OldBandwidth;
//
// Sanity check
//
Status = STATUS_SUCCESS;
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
//
// We do not handle the remove case. it should be handled by cgroup
//
ASSERT(NewBandwidth != HTTP_LIMIT_INFINITE);
UlTrace(TC,("Ul!UlTcModifyFlowsForSite: For cgroup %p.\n",pConfigGroup));
//
// Modify the flow list in the cgroup as it shows the flows of this site
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
pFlowEntry = pConfigGroup->FlowListHead.Flink;
while ( pFlowEntry != &pConfigGroup->FlowListHead )
{
pFlow = CONTAINING_RECORD(
pFlowEntry,
UL_TCI_FLOW,
Siblings
);
// Yet another sanity check
ASSERT(IS_VALID_CONFIG_GROUP(pFlow->pConfigGroup));
ASSERT(pConfigGroup == pFlow->pConfigGroup);
// Overwrite the new bandwidth but remember the old
OldBandwidth = UL_GET_BW_FRM_FLOWSPEC(pFlow->GenFlow);
UL_SET_FLOWSPEC(pFlow->GenFlow,NewBandwidth,pFlow->pInterface->MTUSize);
Status = UlpModifyFlow(pFlow->pInterface, pFlow);
if (!NT_SUCCESS(Status))
{
// Whine about it, but still continue
UlTrace( TC, ("Ul!UlTcModifyFlowsForSite: FAILURE %08lx \n", Status ));
// Restore the original flowspec back
UL_SET_FLOWSPEC(pFlow->GenFlow,OldBandwidth,pFlow->pInterface->MTUSize);
}
UL_DUMP_TC_FLOW(pFlow);
// Proceed to the next flow
pFlowEntry = pFlowEntry->Flink;
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace( TC, ("Ul!UlTcModifyFlowsForSite: Frm %d KB/s To %d KB/s done.\n",
OldBandwidth,
NewBandwidth
));
return Status;
}
/***************************************************************************++
Routine Description:
UlTcRemoveFlowsForSite :
Add a flow on existing QoS Enabled interface
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlTcRemoveFlowsForSite(
IN PUL_CONFIG_GROUP_OBJECT pConfigGroup
)
{
NTSTATUS Status;
PLIST_ENTRY pFlowEntry;
PUL_TCI_FLOW pFlow;
//
// Sanity check first
//
Status = STATUS_SUCCESS;
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
UlTrace(TC,("Ul!UlTcRemoveFlowsForSite: For cgroup %p\n", pConfigGroup));
//
// Remove frm the cgroup list and remove frm the interface list
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
while (!IsListEmpty(&pConfigGroup->FlowListHead))
{
pFlowEntry = pConfigGroup->FlowListHead.Flink;
pFlow = CONTAINING_RECORD(
pFlowEntry,
UL_TCI_FLOW,
Siblings
);
// Yet another sanity check
ASSERT(pConfigGroup == pFlow->pConfigGroup);
ASSERT(IS_VALID_CONFIG_GROUP(pFlow->pConfigGroup));
// Remove frm cgroup's flowlist and release our reference
RemoveEntryList(&pFlow->Siblings);
pFlow->Siblings.Flink = pFlow->Siblings.Blink = NULL;
pFlow->pConfigGroup = NULL;
// Now frm interface list. This will also make the TC call
Status = UlpTcDeleteFlow(pFlow);
ASSERT(NT_SUCCESS(Status));
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace(TC,("Ul!UlTcRemoveFlowsForSite: The cgroup %p 's flows are removed.\n",
pConfigGroup
));
return Status;
}
/***************************************************************************++
Routine Description:
To see whether packet scheduler is installed or not. We also bail
out if we weren't able to initialize properly.
TODO we might want to handle the change (PSched installed later)
on-the-fly w/o requiring driver to be restarted.
--***************************************************************************/
BOOLEAN
UlTcPSchedInstalled(
VOID
)
{
return (g_InitTciCalled && g_PSchedInstalled);
}
/***************************************************************************++
Routine Description:
UlTcGlobalThrottlingEnabled :
Will return TRUE if global bandwidth throttling is enabled in TC.
Its enabled when global flows are installed and TC is initialized
otherwise disabled.
Make sure that if !UlTcPSchedInstalled() then Global throttling is
always disabled.
--***************************************************************************/
__inline BOOLEAN
UlTcGlobalThrottlingEnabled(
VOID
)
{
return (UL_IS_GLOBAL_THROTTLING_ENABLED());
}
/***************************************************************************++
Routine Description:
UlTcAddGlobalFlows :
Visits and creates the global flow on each interface
Arguments:
HTTP_BANDWIDTH_LIMIT - The bandwidth throttling limit in KB/s
--***************************************************************************/
NTSTATUS
UlTcAddGlobalFlows(
IN HTTP_BANDWIDTH_LIMIT MaxBandwidth
)
{
NTSTATUS Status;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
PUL_TCI_FLOW pFlow;
TC_GEN_FLOW TcGenFlow;
//
// Sanity Check
//
Status = STATUS_SUCCESS;
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
UlTrace(TC,("Ul!UlTcAddGlobalFlows: Installing for %d KB/s\n", MaxBandwidth));
//
// To ensure the new filters can get attached to the global flows
//
UL_ENABLE_GLOBAL_THROTTLING();
//
// Visit each interface and add a global flow for this site
// Acquire Exclusive because we will add a flow to the list
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
pInterfaceEntry = g_TciIfcListHead.Flink;
while ( pInterfaceEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
//
// Nobody should try to add a global flow when there's already one
//
ASSERT(pInterface->pGlobalFlow == NULL);
//
// Allocate a flow structure
//
pFlow = UlpTcAllocateFlow( MaxBandwidth, pInterface->MTUSize );
if ( pFlow == NULL )
{
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
return STATUS_NO_MEMORY;
}
//
// Add the flow by making a TC call down to gpc
//
Status = UlpAddFlow( pInterface,
pFlow,
&pFlow->FlowHandle
);
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Ul!UlTcAddGlobalFlows: FAILURE %08lx \n", Status ));
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UL_FREE_POOL_WITH_SIG( pFlow, UL_TCI_FLOW_POOL_TAG );
return Status;
}
//
// Proceed with further initialization as we have successfully installed
// the flow. First link the flow back to its owner interface and remeber
// this was a global flow for the interface. Make sure that the config
// group pointer is null for the global flows.
//
pFlow->pInterface = pInterface;
pFlow->pConfigGroup = NULL;
pInterface->pGlobalFlow = pFlow;
//
// Add this to the interface's flowlist as well
//
InsertHeadList( &pInterface->FlowList, &pFlow->Linkage );
pInterface->FlowListSize += 1;
UlTrace( TC,
("Ul!UlTcAddGlobalFlows: Added the pGlobalFlow %p on pInterface %p\n",
pFlow,
pInterface
));
UL_DUMP_TC_FLOW(pFlow);
//
// search through next interface
//
pInterfaceEntry = pInterfaceEntry->Flink;
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace( TC, ("Ul!UlTcAddGlobalFlows: Installed.\n" ));
return Status;
}
/***************************************************************************++
Routine Description:
UlTcModifyGlobalFlows :
This function will be called when there's a config channel change
happening for the global bandwidth throttling setting.
Its caller responsiblity to remember the new settings in the control
channel.
We will simply update the FlowSpec on the existing global flows.
Arguments:
HTTP_BANDWIDTH_LIMIT - The new bandwidth throttling setting
in B/s
--***************************************************************************/
NTSTATUS
UlTcModifyGlobalFlows(
IN HTTP_BANDWIDTH_LIMIT NewBandwidth
)
{
NTSTATUS Status;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
PUL_TCI_FLOW pFlow;
HTTP_BANDWIDTH_LIMIT OldBandwidth;
//
// Sanity check
//
Status = STATUS_SUCCESS;
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
//
// We do not handle the remove case.It should be handled by control channel
//
ASSERT(NewBandwidth != HTTP_LIMIT_INFINITE);
UlTrace(TC,("Ul!UlTcModifyGlobalFlows: to %d KB/s \n",NewBandwidth));
//
// Modify the global flows of all the interfaces
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
pInterfaceEntry = g_TciIfcListHead.Flink;
while ( pInterfaceEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
ASSERT(pInterface->pGlobalFlow != NULL);
pFlow = pInterface->pGlobalFlow;
// Overwrite the old bandwidth limit but remember it
OldBandwidth = UL_GET_BW_FRM_FLOWSPEC(pFlow->GenFlow);
UL_SET_FLOWSPEC(pFlow->GenFlow, NewBandwidth,pFlow->pInterface->MTUSize);
// Pass it down to low level modifier
Status = UlpModifyFlow(pInterface, pFlow);
if (!NT_SUCCESS(Status))
{
// Whine about it, but still continue
UlTrace(TC,("Ul!UlTcModifyGlobalFlows: FAILURE %08lx \n",Status));
// Restore the original flowspec back
UL_SET_FLOWSPEC(pFlow->GenFlow,OldBandwidth,pFlow->pInterface->MTUSize);
}
UL_DUMP_TC_FLOW(pFlow);
// Proceed to the next interface's global_flow
pInterfaceEntry = pInterfaceEntry->Flink;
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace( TC, ("Ul!UlTcModifyGlobalFlows: Modified.\n" ));
return Status;
}
/***************************************************************************++
Routine Description:
UlTcRemoveGlobalFlows :
Add a flow on existing QoS Enabled interface
--***************************************************************************/
NTSTATUS
UlTcRemoveGlobalFlows(
VOID
)
{
NTSTATUS Status;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
PUL_TCI_FLOW pFlow;
//
// Sanity check first
//
Status = STATUS_SUCCESS;
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
UlTrace(TC,("Ul!UlTcRemoveGlobalFlows: ...\n"));
//
// To ensure no new filters can get attached to the global flows
//
UL_DISABLE_GLOBAL_THROTTLING();
//
// Remove each interface's global flow
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->TciIfcResource, TRUE);
pInterfaceEntry = g_TciIfcListHead.Flink;
while ( pInterfaceEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
pFlow = pInterface->pGlobalFlow;
if (pFlow)
{
ASSERT(IS_VALID_TCI_FLOW(pFlow));
// Remove from interface list and make the TC call down.
Status = UlpTcDeleteFlow(pFlow);
ASSERT(NT_SUCCESS(Status));
// No more global flow
pInterface->pGlobalFlow = NULL;
}
// Goto the next interface on the list
pInterfaceEntry = pInterfaceEntry->Flink;
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
UlTrace(TC,("Ul!UlTcRemoveGlobalFlows: Flows are removed.\n" ));
return Status;
}
/***************************************************************************++
Routine Description:
UlTcAddFilter :
//
// 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
pCgroup - optional - NULL means add to the global flow
Return Values:
STATUS_INVALID_DEVICE_REQUEST- If TC not initialized
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 PUL_CONFIG_GROUP_OBJECT pCgroup OPTIONAL
)
{
NTSTATUS Status;
ULONG IpAddress;
ULONG IpAddressTemp;
TC_GEN_FILTER TcGenericFilter;
PUL_TCI_FLOW pFlow;
PUL_TCI_INTERFACE pInterface;
IP_PATTERN Pattern;
IP_PATTERN Mask;
PUL_TCI_FILTER pFilter;
PUL_CONFIG_GROUP_OBJECT pOldCgroup;
//
// A lot of sanity & early checks
//
Status = STATUS_SUCCESS;
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
//
// If we have been called w/o being initialized
//
ASSERT(g_InitTciCalled);
//
// If BWT or GBWT disabled then just bail out.
//
if ((pCgroup == NULL && !UlTcGlobalThrottlingEnabled()) ||
(pCgroup != NULL && (pCgroup->MaxBandwidth.Flags.Present == 0 ||
pCgroup->MaxBandwidth.MaxBandwidth == HTTP_LIMIT_INFINITE))
)
{
return Status;
}
//
// We need the local & remote IPs. Connection keeps the ip and port in
// host order, but we need in network order here. To be competible with
// the rest of the BWT code. Convert it back.
//
IpAddress = SWAP_LONG( pHttpConnection->pConnection->LocalAddress );
IpAddressTemp = pHttpConnection->pConnection->LocalAddress;
UlTrace(TC,("Ul!UlTcAddFilter: Local %d.%d.%d.%d:%d\n",
(UCHAR)(IpAddressTemp >> 24),
(UCHAR)(IpAddressTemp >> 16),
(UCHAR)(IpAddressTemp >> 8),
(UCHAR)(IpAddressTemp >> 0),
pHttpConnection->pConnection->LocalPort
));
IpAddressTemp = pHttpConnection->pConnection->RemoteAddress;
UlTrace(TC,("Ul!UlTcAddFilter: Remote %d.%d.%d.%d:%d\n",
(UCHAR)(IpAddressTemp >> 24),
(UCHAR)(IpAddressTemp >> 16),
(UCHAR)(IpAddressTemp >> 8),
(UCHAR)(IpAddressTemp >> 0),
pHttpConnection->pConnection->RemotePort
));
if ( IpAddress == LOOPBACK_ADDR )
{
//
// Make sure that new Filter is not trying to go the Local_loopback
// if that is the case skip this. There's no qos on local_loopbacks
// PSched doesn't receive any packets for local_loopback address.
//
UlTrace( TC,
("Ul!UlTcAddFilter: LocalLoopback not supported."
"Not adding filter for pHttpConnection %p\n",
pHttpConnection
));
return STATUS_NOT_SUPPORTED;
}
//
// At this point we will be refering to the flows & filters
// in our list therefore we need to acquire the lock
//
UlAcquireResourceShared(&g_pUlNonpagedData->TciIfcResource, TRUE);
// If connection already has a filter attached
if (pHttpConnection->pFlow)
{
ASSERT(IS_VALID_TCI_FLOW(pHttpConnection->pFlow));
ASSERT(IS_VALID_TCI_FILTER(pHttpConnection->pFilter));
// To see if we have a new cgroup, if that's the case then
// we have to go to a new flow. If pCgroup is null we will
// still skip adding the same global filter again
pOldCgroup = pHttpConnection->pFlow->pConfigGroup;
if (pOldCgroup == pCgroup)
{
// No need to add a new filter we are done
UlTrace( TC,
("Ul!UlTcAddFilter: Skipping same pFlow %p & pFilter %p already exist\n",
pHttpConnection->pFlow,
pHttpConnection->pFilter,
pHttpConnection
));
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
// old one to the new flow.
//
UlpTcDeleteFilter(pHttpConnection->pFlow, pHttpConnection->pFilter);
}
}
//
// Search through the cgroup's flowlist to find the one we need. The one
// on the interface we want.
//
pFlow = UlpFindFlow( pCgroup, IpAddress );
if ( pFlow == NULL )
{
IpAddressTemp = SWAP_LONG(IpAddress);
UlTrace( TC,
("Ul!UlTcAddFilter: Unable to find interface (%x) %d.%d.%d.%d \n",
IpAddress,
(UCHAR)(IpAddressTemp >> 24),
(UCHAR)(IpAddressTemp >> 16),
(UCHAR)(IpAddressTemp >> 8),
(UCHAR)(IpAddressTemp >> 0)
));
// It's possible that we might not find out a flow
// after all the interfaces went down, even though
// qos configured on the cgroup.
// ASSERT(FALSE);
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
// Some more initialization
pFilter = NULL;
pInterface = pFlow->pInterface;
RtlZeroMemory( &Pattern, sizeof(IP_PATTERN) );
RtlZeroMemory( &Mask, sizeof(IP_PATTERN) );
//
// Everything is fine.
// Now setup the filter with proper pattern & mask
//
Pattern.SrcAddr = SWAP_LONG(pHttpConnection->pConnection->LocalAddress);
Pattern.S_un.S_un_ports.s_srcport = SWAP_SHORT(pHttpConnection->pConnection->LocalPort);
Pattern.DstAddr = SWAP_LONG(pHttpConnection->pConnection->RemoteAddress);
Pattern.S_un.S_un_ports.s_dstport = SWAP_SHORT(pHttpConnection->pConnection->RemotePort);
Pattern.ProtocolId = IPPROTO_TCP;
/* Mask */
RtlFillMemory(&Mask, sizeof(IP_PATTERN), 0xff);
//Mask.SrcAddr = 0x00000000;
//Mask.S_un.S_un_ports.s_srcport = 0; // WHY ??
//Mask.DstAddr = 0x00000000;
//Mask.S_un.S_un_ports.s_dstport = 0; // WHY ??
TcGenericFilter.AddressType = NDIS_PROTOCOL_ID_TCP_IP;
TcGenericFilter.PatternSize = sizeof( IP_PATTERN );
TcGenericFilter.Pattern = &Pattern;
TcGenericFilter.Mask = &Mask;
Status = UlpTcAddFilter(
pFlow,
&TcGenericFilter,
&pFilter
);
if (!NT_SUCCESS(Status))
{
UlTrace( TC,
("Ul!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 the connection's private pointers as
// well
//
pFilter->pHttpConnection = pHttpConnection;
//
// Sweet smell of success !
//
UlTrace(TC,
("Ul!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, ("Ul!UlTcAddFilter: FAILURE %08lx \n", Status ));
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
return Status;
}
/***************************************************************************++
Routine Description:
UlpTcAddFilter :
Add a filter on existing flow
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpTcAddFilter(
IN PUL_TCI_FLOW pFlow,
IN PTC_GEN_FILTER pGenericFilter,
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;
ULONG IfIndex;
PUCHAR pTemp;
PIP_PATTERN pIpPattern;
HANDLE RetHandle;
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(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 = (PIP_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->Reserved1 = pFlow->pInterface->IfIndex;
pIpPattern->Reserved2 = pFlow->pInterface->SpecificLinkCtx;
pIpPattern->Reserved3[0] = pIpPattern->Reserved3[1] = pIpPattern->Reserved3[2] = 0;
// Fill in the mask
pTemp += PatternSize;
RtlCopyMemory( pTemp, pGenericFilter->Mask, PatternSize );
pIpPattern = (PIP_PATTERN) pTemp;
pIpPattern->Reserved1 = pIpPattern->Reserved2 = 0xffffffff;
pIpPattern->Reserved3[0] = pIpPattern->Reserved3[1] = pIpPattern->Reserved3[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))
goto end;
//
// Insert the freshly created filter to flow
//
pFilter->FilterHandle = (HANDLE) GpcRes.GpcPatternHandle;
UlpInsertFilterEntry( pFilter, pFlow );
//
// Success!
//
*ppFilter = pFilter;
end:
if (!NT_SUCCESS(Status))
{
UlTrace( TC, ("Ul!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,("Ul!UlTcDeleteFilter: for connection %p\n", pHttpConnection));
UlAcquireResourceShared(&g_pUlNonpagedData->TciIfcResource, TRUE);
if (pHttpConnection->pFlow)
{
Status = UlpTcDeleteFilter(
pHttpConnection->pFlow,
pHttpConnection->pFilter
);
}
UlReleaseResource(&g_pUlNonpagedData->TciIfcResource);
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, ("Ul!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))
{
UlTrace( TC, ("Ul!UlpTcDeleteGpcFilter: FAILURE %08lx \n", Status ));
}
else
{
UlTrace( TC, ("Ul!UlpTcDeleteGpcFilter: FilterHandle %d deleted in TC as well.\n",
FilterHandle
));
}
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, ("Ul!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:
UlpFindFlow :
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:
pCGroup - The config group of the site
IpAddress - The address we are searching for
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
PUL_TCI_FLOW
UlpFindFlow(
IN PUL_CONFIG_GROUP_OBJECT pCgroup OPTIONAL,
IN ULONG IpAddress
)
{
PLIST_ENTRY pFlowEntry;
PUL_TCI_FLOW pFlow;
PLIST_ENTRY pInterfaceEntry;
PUL_TCI_INTERFACE pInterface;
//
// Drop the lookup if the IP is zero. Basically by doing this
// we are rejecting any filters attached to flows on interfaces
// with zero IPs. The interface list may have interface(s)
// with zero IPs. They are just idle until ip change notification
// comes.
//
if (IpAddress == 0)
{
return NULL;
}
//
// Otherwise proceed with the flow lookup on current interfaces.
//
if (pCgroup)
{
// Look in the cgroup's flows
ASSERT(IS_VALID_CONFIG_GROUP(pCgroup));
pFlowEntry = pCgroup->FlowListHead.Flink;
while ( pFlowEntry != &pCgroup->FlowListHead )
{
pFlow = CONTAINING_RECORD(
pFlowEntry,
UL_TCI_FLOW,
Siblings
);
if (pFlow->pInterface->IpAddr == IpAddress)
{
return pFlow;
}
pFlowEntry = pFlowEntry->Flink;
}
}
else
{
// Or go through the interface list to find the global flow
pInterfaceEntry = g_TciIfcListHead.Flink;
while ( pInterfaceEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pInterfaceEntry,
UL_TCI_INTERFACE,
Linkage
);
if (pInterface->IpAddr == IpAddress)
{
return pInterface->pGlobalFlow;
}
pInterfaceEntry = pInterfaceEntry->Flink;
}
}
return NULL;
}
/***************************************************************************++
Routine Description:
UlpFindInterface :
Find the interface in our global link list by looking at its IP
Arguments:
IpAddr - The ip address to be find among the interfaces
Return Value:
Pointer to interface if found or else null
--***************************************************************************/
PUL_TCI_INTERFACE
UlpFindInterface(
IN ULONG IpAddr
)
{
PLIST_ENTRY pEntry;
PUL_TCI_INTERFACE pInterface;
pEntry = g_TciIfcListHead.Flink;
while ( pEntry != &g_TciIfcListHead )
{
pInterface = CONTAINING_RECORD(
pEntry,
UL_TCI_INTERFACE,
Linkage
);
if ( pInterface->IpAddr == IpAddr )
{
return pInterface;
}
pEntry = pEntry->Flink;
}
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;
//
// Sanity check.
//
PAGED_CODE();
Status = STATUS_SUCCESS;
UlAttachToSystemProcess();
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;
}
UlDetachFromSystemProcess();
return Status;
}
/***************************************************************************++
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
)
{
ULONG IpAddress;
ASSERT(IS_VALID_TCI_INTERFACE(pTcIfc));
IpAddress = SWAP_LONG(pTcIfc->IpAddr);
UlTrace( TC,("Ul!UlDumpTCInterface: \n pTcIfc @ %p\n"
"\t Signature = %08lx \n",
pTcIfc, pTcIfc->Signature));
UlTrace( TC,(
"\t IsQoSEnabled: = %u \n"
"\t IpAddr: = (%x) %d.%d.%d.%d \n"
"\t IfIndex: = %d \n"
"\t SpecificLinkCtx: = %d \n"
"\t MTUSize: = %d \n"
"\t NameLength: = %u \n"
"\t Name: = %ws \n"
"\t InstanceIDLength: = %u \n"
"\t InstanceID: = %ws \n"
"\t pGlobalFlow = %p \n"
"\t FlowListSize: = %d \n"
"\t AddrListBytesCount: = %d \n"
"\t pAddressListDesc: = %p \n",
pTcIfc->IsQoSEnabled,
pTcIfc->IpAddr,
(UCHAR)(IpAddress >> 24),
(UCHAR)(IpAddress >> 16),
(UCHAR)(IpAddress >> 8),
(UCHAR)(IpAddress >> 0),
pTcIfc->IfIndex,
pTcIfc->SpecificLinkCtx,
pTcIfc->MTUSize,
pTcIfc->NameLength,
pTcIfc->Name,
pTcIfc->InstanceIDLength,
pTcIfc->InstanceID,
pTcIfc->pGlobalFlow,
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,
("Ul!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 pConfigGroup = %p \n"
,
pFlow,
pFlow->Signature,
pFlow->pInterface,
pFlow->FlowHandle,
&pFlow->GenFlow,
pFlow->GenFlow.SendingFlowspec.TokenRate / 1024,
pFlow->FilterListSize,
pFlow->pConfigGroup
));
}
/***************************************************************************++
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,
("Ul!UlDumpTCFilter: \n"
" pFilter @ %p\n"
"\t Signature = %08lx \n"
"\t pHttpConnection = %p \n"
"\t FilterHandle = %d \n",
pFilter,
pFilter->Signature,
pFilter->pHttpConnection,
pFilter->FilterHandle
));
}