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.
 
 
 
 
 
 

7423 lines
208 KiB

// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// NT specific routines for dispatching and handling IRPs.
//
#include <oscfg.h>
#include <ndis.h>
#include <ip6imp.h>
#include "ip6def.h"
#include "icmp.h"
#include "ipsec.h"
#include "security.h"
#include "route.h"
#include "select.h"
#include "neighbor.h"
#include <ntddip6.h>
#include "ntreg.h"
#include <string.h>
#include <wchar.h>
#include "fragment.h"
//
// Local structures.
//
typedef struct pending_irp {
LIST_ENTRY Linkage;
PIRP Irp;
PFILE_OBJECT FileObject;
PVOID Context;
} PENDING_IRP, *PPENDING_IRP;
//
// Global variables.
//
LIST_ENTRY PendingEchoList;
//
// Local prototypes.
//
NTSTATUS
IPDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS
IPDispatchDeviceControl(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IPCreate(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IPCleanup(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IPClose(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
DispatchEchoRequest(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
void
CompleteEchoRequest(void *Context, IP_STATUS Status,
const IPv6Addr *Address, uint ScopeId,
void *Data, uint DataSize);
NTSTATUS
IoctlQueryInterface(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlPersistentQueryInterface(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQueryAddress(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlPersistentQueryAddress(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQueryNeighborCache(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQueryRouteCache(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlCreateSecurityPolicy(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQuerySecurityPolicyList(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlDeleteSecurityPolicy(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlCreateSecurityAssociation(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQuerySecurityAssociationList(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlDeleteSecurityAssociation(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQueryRouteTable(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlPersistentQueryRouteTable(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlUpdateRouteTable(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlUpdateAddress(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlQueryBindingCache(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlCreateInterface(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlUpdateInterface(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlDeleteInterface(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlFlushNeighborCache(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlFlushRouteCache(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlSortDestAddrs(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQuerySitePrefix(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlUpdateSitePrefix(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlRtChangeNotifyRequest(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlQueryGlobalParameters(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlUpdateGlobalParameters(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlQueryPrefixPolicy(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlPersistentQueryPrefixPolicy(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlUpdatePrefixPolicy(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlDeletePrefixPolicy(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlUpdateRouterLLAddress(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
NTSTATUS
IoctlResetManualConfig(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp,
IN int Persistent);
NTSTATUS
IoctlRenewInterface(IN PIRP Irp, IN PIO_STACK_LOCATION IrpSp);
//
// All of this code is pageable.
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, IPDispatch)
#pragma alloc_text(PAGE, IPDispatchDeviceControl)
#pragma alloc_text(PAGE, IPCreate)
#pragma alloc_text(PAGE, IPClose)
#pragma alloc_text(PAGE, DispatchEchoRequest)
#endif // ALLOC_PRAGMA
//
// Dispatch function definitions.
//
//* IPDispatch
//
// This is the dispatch routine for IP.
//
NTSTATUS // Returns: whether the request was successfully queued.
IPDispatch(
IN PDEVICE_OBJECT DeviceObject, // Device object for target device.
IN PIRP Irp) // I/O request packet.
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
irpSp = IoGetCurrentIrpStackLocation(Irp);
switch (irpSp->MajorFunction) {
case IRP_MJ_DEVICE_CONTROL:
case IRP_MJ_INTERNAL_DEVICE_CONTROL:
return IPDispatchDeviceControl(Irp, irpSp);
case IRP_MJ_CREATE:
status = IPCreate(Irp, irpSp);
break;
case IRP_MJ_CLEANUP:
status = IPCleanup(Irp, irpSp);
break;
case IRP_MJ_CLOSE:
status = IPClose(Irp, irpSp);
break;
default:
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"IPDispatch: Invalid major function %d\n",
irpSp->MajorFunction));
status = STATUS_NOT_IMPLEMENTED;
break;
}
Irp->IoStatus.Status = status;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return(status);
} // IPDispatch
//* IPDispatchDeviceControl
//
NTSTATUS // Returns: whether the request was successfully queued.
IPDispatchDeviceControl(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
NTSTATUS status;
ULONG code;
PAGED_CODE();
code = IrpSp->Parameters.DeviceIoControl.IoControlCode;
switch (code) {
case IOCTL_ICMPV6_ECHO_REQUEST:
return DispatchEchoRequest(Irp, IrpSp);
case IOCTL_IPV6_QUERY_INTERFACE:
return IoctlQueryInterface(Irp, IrpSp);
case IOCTL_IPV6_PERSISTENT_QUERY_INTERFACE:
return IoctlPersistentQueryInterface(Irp, IrpSp);
case IOCTL_IPV6_QUERY_ADDRESS:
return IoctlQueryAddress(Irp, IrpSp);
case IOCTL_IPV6_PERSISTENT_QUERY_ADDRESS:
return IoctlPersistentQueryAddress(Irp, IrpSp);
case IOCTL_IPV6_QUERY_NEIGHBOR_CACHE:
return IoctlQueryNeighborCache(Irp, IrpSp);
case IOCTL_IPV6_QUERY_ROUTE_CACHE:
return IoctlQueryRouteCache(Irp, IrpSp);
case IOCTL_IPV6_CREATE_SECURITY_POLICY:
return IoctlCreateSecurityPolicy(Irp, IrpSp);
case IOCTL_IPV6_QUERY_SECURITY_POLICY_LIST:
return IoctlQuerySecurityPolicyList(Irp, IrpSp);
case IOCTL_IPV6_DELETE_SECURITY_POLICY:
return IoctlDeleteSecurityPolicy(Irp, IrpSp);
case IOCTL_IPV6_CREATE_SECURITY_ASSOCIATION:
return IoctlCreateSecurityAssociation(Irp, IrpSp);
case IOCTL_IPV6_QUERY_SECURITY_ASSOCIATION_LIST:
return IoctlQuerySecurityAssociationList(Irp, IrpSp);
case IOCTL_IPV6_DELETE_SECURITY_ASSOCIATION:
return IoctlDeleteSecurityAssociation(Irp, IrpSp);
case IOCTL_IPV6_QUERY_ROUTE_TABLE:
return IoctlQueryRouteTable(Irp, IrpSp);
case IOCTL_IPV6_PERSISTENT_QUERY_ROUTE_TABLE:
return IoctlPersistentQueryRouteTable(Irp, IrpSp);
case IOCTL_IPV6_UPDATE_ROUTE_TABLE:
return IoctlUpdateRouteTable(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_UPDATE_ROUTE_TABLE:
return IoctlUpdateRouteTable(Irp, IrpSp, TRUE);
case IOCTL_IPV6_UPDATE_ADDRESS:
return IoctlUpdateAddress(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_UPDATE_ADDRESS:
return IoctlUpdateAddress(Irp, IrpSp, TRUE);
case IOCTL_IPV6_QUERY_BINDING_CACHE:
return IoctlQueryBindingCache(Irp, IrpSp);
case IOCTL_IPV6_CREATE_INTERFACE:
return IoctlCreateInterface(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_CREATE_INTERFACE:
return IoctlCreateInterface(Irp, IrpSp, TRUE);
case IOCTL_IPV6_UPDATE_INTERFACE:
return IoctlUpdateInterface(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_UPDATE_INTERFACE:
return IoctlUpdateInterface(Irp, IrpSp, TRUE);
case IOCTL_IPV6_DELETE_INTERFACE:
return IoctlDeleteInterface(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_DELETE_INTERFACE:
return IoctlDeleteInterface(Irp, IrpSp, TRUE);
case IOCTL_IPV6_FLUSH_NEIGHBOR_CACHE:
return IoctlFlushNeighborCache(Irp, IrpSp);
case IOCTL_IPV6_FLUSH_ROUTE_CACHE:
return IoctlFlushRouteCache(Irp, IrpSp);
case IOCTL_IPV6_SORT_DEST_ADDRS:
return IoctlSortDestAddrs(Irp, IrpSp);
case IOCTL_IPV6_QUERY_SITE_PREFIX:
return IoctlQuerySitePrefix(Irp, IrpSp);
case IOCTL_IPV6_UPDATE_SITE_PREFIX:
return IoctlUpdateSitePrefix(Irp, IrpSp);
case IOCTL_IPV6_RTCHANGE_NOTIFY_REQUEST:
return IoctlRtChangeNotifyRequest(Irp, IrpSp);
case IOCTL_IPV6_QUERY_GLOBAL_PARAMETERS:
return IoctlQueryGlobalParameters(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_QUERY_GLOBAL_PARAMETERS:
return IoctlQueryGlobalParameters(Irp, IrpSp, TRUE);
case IOCTL_IPV6_UPDATE_GLOBAL_PARAMETERS:
return IoctlUpdateGlobalParameters(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_UPDATE_GLOBAL_PARAMETERS:
return IoctlUpdateGlobalParameters(Irp, IrpSp, TRUE);
case IOCTL_IPV6_QUERY_PREFIX_POLICY:
return IoctlQueryPrefixPolicy(Irp, IrpSp);
case IOCTL_IPV6_PERSISTENT_QUERY_PREFIX_POLICY:
return IoctlPersistentQueryPrefixPolicy(Irp, IrpSp);
case IOCTL_IPV6_UPDATE_PREFIX_POLICY:
return IoctlUpdatePrefixPolicy(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_UPDATE_PREFIX_POLICY:
return IoctlUpdatePrefixPolicy(Irp, IrpSp, TRUE);
case IOCTL_IPV6_DELETE_PREFIX_POLICY:
return IoctlDeletePrefixPolicy(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_DELETE_PREFIX_POLICY:
return IoctlDeletePrefixPolicy(Irp, IrpSp, TRUE);
case IOCTL_IPV6_UPDATE_ROUTER_LL_ADDRESS:
return IoctlUpdateRouterLLAddress(Irp, IrpSp);
case IOCTL_IPV6_RESET:
return IoctlResetManualConfig(Irp, IrpSp, FALSE);
case IOCTL_IPV6_PERSISTENT_RESET:
return IoctlResetManualConfig(Irp, IrpSp, TRUE);
case IOCTL_IPV6_RENEW_INTERFACE:
return IoctlRenewInterface(Irp, IrpSp);
default:
status = STATUS_NOT_IMPLEMENTED;
break;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return status;
} // IPDispatchDeviceControl
//* IPCreate
//
NTSTATUS // Returns: whether the request was successfully queued.
IPCreate(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
PAGED_CODE();
return(STATUS_SUCCESS);
} // IPCreate
//* IPCleanup
//
NTSTATUS // Returns: whether the request was successfully queued.
IPCleanup(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
PPENDING_IRP pendingIrp;
PLIST_ENTRY entry, nextEntry;
KIRQL oldIrql;
LIST_ENTRY completeList;
PIRP cancelledIrp;
InitializeListHead(&completeList);
//
// Collect all of the pending IRPs on this file object.
//
IoAcquireCancelSpinLock(&oldIrql);
entry = PendingEchoList.Flink;
while ( entry != &PendingEchoList ) {
pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
if (pendingIrp->FileObject == IrpSp->FileObject) {
nextEntry = entry->Flink;
RemoveEntryList(entry);
IoSetCancelRoutine(pendingIrp->Irp, NULL);
InsertTailList(&completeList, &(pendingIrp->Linkage));
entry = nextEntry;
}
else {
entry = entry->Flink;
}
}
IoReleaseCancelSpinLock(oldIrql);
//
// Complete them.
//
entry = completeList.Flink;
while ( entry != &completeList ) {
pendingIrp = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
cancelledIrp = pendingIrp->Irp;
entry = entry->Flink;
//
// Free the PENDING_IRP structure. The control block will be freed
// when the request completes.
//
ExFreePool(pendingIrp);
//
// Complete the IRP.
//
cancelledIrp->IoStatus.Information = 0;
cancelledIrp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(cancelledIrp, IO_NETWORK_INCREMENT);
}
return(STATUS_SUCCESS);
} // IPCleanup
//* IPClose
//
NTSTATUS // Returns: whether the request was successfully queued.
IPClose(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
PAGED_CODE();
return(STATUS_SUCCESS);
} // IPClose
//
// ICMP Echo function definitions
//
//* CancelEchoRequest
//
// This function is called with cancel spinlock held. It must be
// released before the function returns.
//
// The echo control block associated with this request cannot be
// freed until the request completes. The completion routine will
// free it.
//
VOID
CancelEchoRequest(
IN PDEVICE_OBJECT Device, // Device on which the request was issued.
IN PIRP Irp) // I/O request packet to cancel.
{
PPENDING_IRP pendingIrp = NULL;
PPENDING_IRP item;
PLIST_ENTRY entry;
for ( entry = PendingEchoList.Flink;
entry != &PendingEchoList;
entry = entry->Flink
) {
item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
if (item->Irp == Irp) {
pendingIrp = item;
RemoveEntryList(entry);
IoSetCancelRoutine(pendingIrp->Irp, NULL);
break;
}
}
IoReleaseCancelSpinLock(Irp->CancelIrql);
if (pendingIrp != NULL) {
//
// Free the PENDING_IRP structure. The control block will be freed
// when the request completes.
//
ExFreePool(pendingIrp);
//
// Complete the IRP.
//
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
}
return;
} // CancelEchoRequest
//* CompleteEchoRequest
//
// Handles the completion of an ICMP Echo request.
//
void
CompleteEchoRequest(
void *Context, // EchoControl structure for this request.
IP_STATUS Status, // Status of the transmission.
const IPv6Addr *Address, // Source of the echo reply.
uint ScopeId, // Scope of the echo reply source.
void *Data, // Pointer to data returned in the echo reply.
uint DataSize) // Lengh of the returned data.
{
KIRQL oldIrql;
PIRP irp;
EchoControl *controlBlock;
PPENDING_IRP pendingIrp = NULL;
PPENDING_IRP item;
PLIST_ENTRY entry;
ULONG bytesReturned;
controlBlock = (EchoControl *) Context;
//
// Find the echo request IRP on the pending list.
//
IoAcquireCancelSpinLock(&oldIrql);
for ( entry = PendingEchoList.Flink;
entry != &PendingEchoList;
entry = entry->Flink
) {
item = CONTAINING_RECORD(entry, PENDING_IRP, Linkage);
if (item->Context == controlBlock) {
pendingIrp = item;
irp = pendingIrp->Irp;
IoSetCancelRoutine(irp, NULL);
RemoveEntryList(entry);
break;
}
}
IoReleaseCancelSpinLock(oldIrql);
if (pendingIrp == NULL) {
//
// IRP must have been cancelled. PENDING_IRP struct
// was freed by cancel routine. Free control block.
//
ExFreePool(controlBlock);
return;
}
irp->IoStatus.Status = ICMPv6EchoComplete(
controlBlock,
Status,
Address,
ScopeId,
Data,
DataSize,
&irp->IoStatus.Information
);
ExFreePool(pendingIrp);
ExFreePool(controlBlock);
//
// Complete the IRP.
//
IoCompleteRequest(irp, IO_NETWORK_INCREMENT);
} // CompleteEchoRequest
//* PrepareEchoIrpForCancel
//
// Prepares an Echo IRP for cancellation.
//
BOOLEAN // Returns: TRUE if IRP was already cancelled, FALSE otherwise.
PrepareEchoIrpForCancel(
PIRP Irp, // I/O request packet to init for cancellation.
PPENDING_IRP PendingIrp) // PENDING_IRP structure for this IRP.
{
BOOLEAN cancelled = TRUE;
KIRQL oldIrql;
IoAcquireCancelSpinLock(&oldIrql);
ASSERT(Irp->CancelRoutine == NULL);
if (!Irp->Cancel) {
IoSetCancelRoutine(Irp, CancelEchoRequest);
InsertTailList(&PendingEchoList, &(PendingIrp->Linkage));
cancelled = FALSE;
}
IoReleaseCancelSpinLock(oldIrql);
return(cancelled);
} // PrepareEchoIrpForCancel
//* DispatchEchoRequest
//
// Processes an ICMP request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
DispatchEchoRequest(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
NTSTATUS ntStatus = STATUS_SUCCESS;
IP_STATUS ipStatus;
PPENDING_IRP pendingIrp;
EchoControl *controlBlock;
PICMPV6_ECHO_REPLY replyBuffer;
BOOLEAN cancelled;
PAGED_CODE();
pendingIrp = ExAllocatePool(NonPagedPool, sizeof(PENDING_IRP));
if (pendingIrp == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto echo_error;
}
controlBlock = ExAllocatePool(NonPagedPool, sizeof(EchoControl));
if (controlBlock == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto echo_error_free_pending;
}
pendingIrp->Irp = Irp;
pendingIrp->FileObject = IrpSp->FileObject;
pendingIrp->Context = controlBlock;
controlBlock->WhenIssued = KeQueryPerformanceCounter(NULL);
controlBlock->ReplyBuf = Irp->AssociatedIrp.SystemBuffer;
controlBlock->ReplyBufLen =
IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
IoMarkIrpPending(Irp);
cancelled = PrepareEchoIrpForCancel(Irp, pendingIrp);
if (!cancelled) {
ICMPv6EchoRequest(
Irp->AssociatedIrp.SystemBuffer, // request buf
IrpSp->Parameters.DeviceIoControl.InputBufferLength, // request len
controlBlock, // echo ctrl
CompleteEchoRequest // cmplt rtn
);
return STATUS_PENDING;
}
//
// Irp has already been cancelled.
//
ntStatus = STATUS_CANCELLED;
ExFreePool(controlBlock);
echo_error_free_pending:
ExFreePool(pendingIrp);
echo_error:
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = ntStatus;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
return(ntStatus);
} // DispatchEchoRequest
//* FindInterfaceFromQuery
//
// Given an IPV6_QUERY_INTERFACE structure,
// finds the specified interface.
// The interface (if found) is returned with a reference.
//
Interface *
FindInterfaceFromQuery(
IPV6_QUERY_INTERFACE *Query)
{
Interface *IF;
if (Query->Index == 0)
IF = FindInterfaceFromGuid(&Query->Guid);
else
IF = FindInterfaceFromIndex(Query->Index);
return IF;
}
//* ReturnQueryInterface
//
// Initializes a returned IPV6_QUERY_INTERFACE structure
// with query information for the specified interface.
//
void
ReturnQueryInterface(
Interface *IF,
IPV6_QUERY_INTERFACE *Query)
{
if (IF == NULL) {
Query->Index = (uint)-1;
RtlZeroMemory(&Query->Guid, sizeof Query->Guid);
}
else {
Query->Index = IF->Index;
Query->Guid = IF->Guid;
}
}
//* ReturnQueryInterfaceNext
//
// Initializes a returned IPV6_QUERY_INTERFACE structure
// with query information for the next interface
// after the specified interface. (Or the first interface,
// if the specified interface is NULL.)
//
void
ReturnQueryInterfaceNext(
Interface *IF,
IPV6_QUERY_INTERFACE *Query)
{
IF = FindNextInterface(IF);
ReturnQueryInterface(IF, Query);
if (IF != NULL)
ReleaseIF(IF);
}
//* IoctlQueryInterface
//
// Processes an IOCTL_IPV6_QUERY_INTERFACE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryInterface(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_INTERFACE *Query;
IPV6_INFO_INTERFACE *Info;
Interface *IF;
NTSTATUS Status;
uint LinkLayerAddressesLength;
uchar *LinkLayerAddress;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
if (Query->Index == (uint)-1) {
//
// Return query information for the first interface.
//
ReturnQueryInterfaceNext(NULL, &Info->Next);
Irp->IoStatus.Information = sizeof Info->Next;
} else {
//
// Return information about the specified interface.
//
IF = FindInterfaceFromQuery(Query);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
Irp->IoStatus.Information = sizeof *Info;
Info->Length = sizeof *Info;
//
// Return query information for the next interface.
//
ReturnQueryInterfaceNext(IF, &Info->Next);
//
// Return miscellaneous information about the interface.
//
ReturnQueryInterface(IF, &Info->This);
RtlCopyMemory(Info->ZoneIndices, IF->ZoneIndices,
sizeof Info->ZoneIndices);
Info->TrueLinkMTU = IF->TrueLinkMTU;
Info->LinkMTU = IF->LinkMTU;
Info->CurHopLimit = IF->CurHopLimit;
Info->BaseReachableTime = IF->BaseReachableTime;
Info->ReachableTime = ConvertTicksToMillis(IF->ReachableTime);
Info->RetransTimer = ConvertTicksToMillis(IF->RetransTimer);
Info->DupAddrDetectTransmits = IF->DupAddrDetectTransmits;
Info->Type = IF->Type;
Info->RouterDiscovers = !!(IF->Flags & IF_FLAG_ROUTER_DISCOVERS);
Info->NeighborDiscovers = !!(IF->Flags & IF_FLAG_NEIGHBOR_DISCOVERS);
Info->PeriodicMLD = !!(IF->Flags & IF_FLAG_PERIODICMLD);
Info->Advertises = !!(IF->Flags & IF_FLAG_ADVERTISES);
Info->Forwards = !!(IF->Flags & IF_FLAG_FORWARDS);
Info->OtherStatefulConfig = !!(IF->Flags & IF_FLAG_OTHER_STATEFUL_CONFIG);
if (IF->Flags & IF_FLAG_MEDIA_DISCONNECTED)
Info->MediaStatus = IPV6_IF_MEDIA_STATUS_DISCONNECTED;
else if (IF->Flags & IF_FLAG_MEDIA_RECONNECTED)
Info->MediaStatus = IPV6_IF_MEDIA_STATUS_RECONNECTED;
else
Info->MediaStatus = IPV6_IF_MEDIA_STATUS_CONNECTED;
Info->Preference = IF->Preference;
//
// Return the interface's link-layer addresses,
// if there is room in the user's buffer.
//
Info->LinkLayerAddressLength = IF->LinkAddressLength;
Info->LocalLinkLayerAddress = 0;
Info->RemoteLinkLayerAddress = 0;
if (IF->Type == IF_TYPE_TUNNEL_AUTO) {
LinkLayerAddressesLength = 2 * IF->LinkAddressLength;
}
else {
LinkLayerAddressesLength = 0;
if (!(IF->Flags & IF_FLAG_PSEUDO))
LinkLayerAddressesLength += IF->LinkAddressLength;
if (IF->Flags & IF_FLAG_P2P)
LinkLayerAddressesLength += IF->LinkAddressLength;
}
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof *Info + LinkLayerAddressesLength) {
//
// Return the fixed-size portion of the structure.
//
Status = STATUS_BUFFER_OVERFLOW;
ReleaseIF(IF);
goto Return;
}
LinkLayerAddress = (uchar *)(Info + 1);
if (IF->Type == IF_TYPE_TUNNEL_AUTO) {
//
// For ISATAP (automatic tunnels), TokenAddr corresponds to
// LocalLinkLayerAddress and DstAddr to RemoteLinkLayerAddress.
//
RtlCopyMemory(LinkLayerAddress,
IF->LinkAddress + IF->LinkAddressLength,
2 * IF->LinkAddressLength);
Info->RemoteLinkLayerAddress = (uint)
(LinkLayerAddress - (uchar *)Info);
Info->LocalLinkLayerAddress = Info->RemoteLinkLayerAddress +
IF->LinkAddressLength;
}
else {
if (!(IF->Flags & IF_FLAG_PSEUDO)) {
RtlCopyMemory(LinkLayerAddress, IF->LinkAddress,
IF->LinkAddressLength);
Info->LocalLinkLayerAddress = (uint)
(LinkLayerAddress - (uchar *)Info);
LinkLayerAddress += IF->LinkAddressLength;
}
if (IF->Flags & IF_FLAG_P2P) {
RtlCopyMemory(LinkLayerAddress,
IF->LinkAddress + IF->LinkAddressLength,
IF->LinkAddressLength);
Info->RemoteLinkLayerAddress = (uint)
(LinkLayerAddress - (uchar *)Info);
LinkLayerAddress += IF->LinkAddressLength;
}
}
Irp->IoStatus.Information += LinkLayerAddressesLength;
ReleaseIF(IF);
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryInterface
//* OpenInterfaceRegKey
//
// Given an interface guid, opens the registry key that holds
// persistent configuration information for the interface.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
OpenInterfaceRegKey(
const GUID *Guid,
HANDLE *RegKey,
OpenRegKeyAction Action)
{
UNICODE_STRING GuidName;
HANDLE InterfacesKey;
NTSTATUS Status;
PAGED_CODE();
Status = OpenTopLevelRegKey(L"Interfaces", &InterfacesKey,
((Action == OpenRegKeyCreate) ?
OpenRegKeyCreate : OpenRegKeyRead));
if (! NT_SUCCESS(Status))
return Status;
//
// Convert the guid to string form.
// It will be null-terminated.
//
Status = RtlStringFromGUID(Guid, &GuidName);
if (! NT_SUCCESS(Status))
goto ReturnCloseKey;
ASSERT(GuidName.MaximumLength == GuidName.Length + sizeof(WCHAR));
ASSERT(((WCHAR *)GuidName.Buffer)[GuidName.Length/sizeof(WCHAR)] == UNICODE_NULL);
Status = OpenRegKey(RegKey, InterfacesKey,
(WCHAR *)GuidName.Buffer, Action);
RtlFreeUnicodeString(&GuidName);
ReturnCloseKey:
ZwClose(InterfacesKey);
return Status;
}
//* ReadPersistentInterface
//
// Reads interface attributes from the registry key.
// Initializes all the fields except This and Next.
//
// On input, the Length field should contain the remaining space
// for link-layer addresses. On output, it contains the amount
// of space for link-layer addresses that was actually used.
//
// Returns:
// STATUS_INVALID_PARAMETER Could not read the interface.
// STATUS_BUFFER_OVERFLOW No room for link-layer addresses.
// STATUS_SUCCESS
//
NTSTATUS
ReadPersistentInterface(
HANDLE IFKey,
IPV6_INFO_INTERFACE *Info)
{
uint LinkLayerAddressSpace;
NTSTATUS Status;
InitRegDWORDParameter(IFKey, L"Type",
(ULONG *)&Info->Type, (uint)-1);
InitRegDWORDParameter(IFKey, L"RouterDiscovers",
&Info->RouterDiscovers, (uint)-1);
InitRegDWORDParameter(IFKey, L"NeighborDiscovers",
&Info->NeighborDiscovers, (uint)-1);
InitRegDWORDParameter(IFKey, L"PeriodicMLD",
&Info->PeriodicMLD, (uint)-1);
InitRegDWORDParameter(IFKey, L"Advertises",
&Info->Advertises, (uint)-1);
InitRegDWORDParameter(IFKey, L"Forwards",
&Info->Forwards, (uint)-1);
Info->MediaStatus = (uint)-1;
memset(Info->ZoneIndices, 0, sizeof Info->ZoneIndices);
Info->TrueLinkMTU = 0;
InitRegDWORDParameter(IFKey, L"LinkMTU",
&Info->LinkMTU, 0);
InitRegDWORDParameter(IFKey, L"CurHopLimit",
&Info->CurHopLimit, (uint)-1);
InitRegDWORDParameter(IFKey, L"BaseReachableTime",
&Info->BaseReachableTime, 0);
Info->ReachableTime = 0;
InitRegDWORDParameter(IFKey, L"RetransTimer",
&Info->RetransTimer, 0);
InitRegDWORDParameter(IFKey, L"DupAddrDetectTransmits",
&Info->DupAddrDetectTransmits, (uint)-1);
InitRegDWORDParameter(IFKey, L"Preference",
&Info->Preference, (uint)-1);
//
// Start by assuming we will not return link-layer addresses.
//
Info->LocalLinkLayerAddress = 0;
Info->RemoteLinkLayerAddress = 0;
//
// But depending on the interface type they may be in the registry.
//
switch (Info->Type) {
case IF_TYPE_TUNNEL_6OVER4: {
IPAddr *SrcAddr;
Info->LinkLayerAddressLength = sizeof(IPAddr);
LinkLayerAddressSpace = Info->LinkLayerAddressLength;
if (Info->Length < LinkLayerAddressSpace)
return STATUS_BUFFER_OVERFLOW;
Info->Length = LinkLayerAddressSpace;
//
// Read the source address.
//
SrcAddr = (IPAddr *)(Info + 1);
Status = GetRegIPAddrValue(IFKey, L"SrcAddr", SrcAddr);
if (! NT_SUCCESS(Status))
return STATUS_NO_MORE_ENTRIES;
Info->LocalLinkLayerAddress = (uint)
((uchar *)SrcAddr - (uchar *)Info);
break;
}
case IF_TYPE_TUNNEL_V6V4: {
IPAddr *SrcAddr, *DstAddr;
Info->LinkLayerAddressLength = sizeof(IPAddr);
LinkLayerAddressSpace = 2 * Info->LinkLayerAddressLength;
if (Info->Length < LinkLayerAddressSpace)
return STATUS_BUFFER_OVERFLOW;
Info->Length = LinkLayerAddressSpace;
//
// Read the source address.
//
SrcAddr = (IPAddr *)(Info + 1);
Status = GetRegIPAddrValue(IFKey, L"SrcAddr", SrcAddr);
if (! NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
Info->LocalLinkLayerAddress = (uint)
((uchar *)SrcAddr - (uchar *)Info);
//
// Read the destination address.
//
DstAddr = SrcAddr + 1;
Status = GetRegIPAddrValue(IFKey, L"DstAddr", DstAddr);
if (! NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER;
Info->RemoteLinkLayerAddress = (uint)
((uchar *)DstAddr - (uchar *)Info);
break;
}
default:
Info->LinkLayerAddressLength = (uint) -1;
Info->Length = 0;
break;
}
return STATUS_SUCCESS;
}
//* OpenPersistentInterface
//
// Parses an interface key name into a guid
// and opens the interface key.
//
NTSTATUS
OpenPersistentInterface(
HANDLE ParentKey,
WCHAR *SubKeyName,
GUID *Guid,
HANDLE *IFKey,
OpenRegKeyAction Action)
{
UNICODE_STRING UGuid;
NTSTATUS Status;
PAGED_CODE();
//
// First, parse the interface guid.
//
RtlInitUnicodeString(&UGuid, SubKeyName);
Status = RtlGUIDFromString(&UGuid, Guid);
if (! NT_SUCCESS(Status)) {
//
// Not a valid guid.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"OpenPersistentInterface: bad syntax %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
//
// Open the interface key.
//
Status = OpenRegKey(IFKey, ParentKey, SubKeyName, Action);
if (! NT_SUCCESS(Status)) {
//
// Could not open the interface key.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"OpenPersistentInterface: bad key %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
return STATUS_SUCCESS;
}
//* EnumPersistentInterface
//
// Helper function for FindPersistentInterfaceFromQuery,
// wrapping OpenPersistentInterface for EnumRegKeyIndex.
//
NTSTATUS
EnumPersistentInterface(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
struct {
GUID *Guid;
HANDLE *IFKey;
OpenRegKeyAction Action;
} *Args = Context;
PAGED_CODE();
return OpenPersistentInterface(ParentKey, SubKeyName,
Args->Guid,
Args->IFKey,
Args->Action);
}
//* FindPersistentInterfaceFromQuery
//
// Given an IPV6_PERSISTENT_QUERY_INTERFACE structure,
// finds the specified interface key in the registry.
// If the interface key is found, then Query->Guid is returned.
//
NTSTATUS
FindPersistentInterfaceFromQuery(
IPV6_PERSISTENT_QUERY_INTERFACE *Query,
HANDLE *IFKey)
{
NTSTATUS Status;
if (Query->RegistryIndex == (uint)-1) {
//
// Persistent query via guid.
//
return OpenInterfaceRegKey(&Query->Guid, IFKey, OpenRegKeyRead);
}
else {
HANDLE InterfacesKey;
struct {
GUID *Guid;
HANDLE *IFKey;
OpenRegKeyAction Action;
} Args;
//
// Persistent query via registry index.
//
Status = OpenTopLevelRegKey(L"Interfaces", &InterfacesKey,
OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
//
// If the Interfaces subkey is not present,
// then the index is not present.
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
Status = STATUS_NO_MORE_ENTRIES;
return Status;
}
Args.Guid = &Query->Guid;
Args.IFKey = IFKey;
Args.Action = OpenRegKeyRead;
Status = EnumRegKeyIndex(InterfacesKey,
Query->RegistryIndex,
EnumPersistentInterface,
&Args);
ZwClose(InterfacesKey);
return Status;
}
}
//* IoctlPersistentQueryInterface
//
// Processes an IOCTL_IPV6_PERSISTENT_QUERY_INTERFACE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlPersistentQueryInterface(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_PERSISTENT_QUERY_INTERFACE *Query;
IPV6_INFO_INTERFACE *Info;
HANDLE IFKey;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_PERSISTENT_QUERY_INTERFACE *)
Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_INTERFACE *)
Irp->AssociatedIrp.SystemBuffer;
Status = FindPersistentInterfaceFromQuery(Query, &IFKey);
if (! NT_SUCCESS(Status))
goto Return;
//
// Let ReadPersistentInterface know how much space is available
// for link-layer addresses. It will use this field to return
// how much space it actually used.
//
Info->Length = (IrpSp->Parameters.DeviceIoControl.OutputBufferLength -
sizeof *Info);
//
// The interface index is not returned for persistent queries.
//
Info->This.Index = 0;
Info->This.Guid = Query->Guid;
Status = ReadPersistentInterface(IFKey, Info);
ZwClose(IFKey);
if (NT_SUCCESS(Status)) {
//
// Return link-layer addresses too.
//
Irp->IoStatus.Information = sizeof *Info + Info->Length;
Status = STATUS_SUCCESS;
}
else if (Status == STATUS_BUFFER_OVERFLOW) {
//
// Return the fixed-size structure.
//
Irp->IoStatus.Information = sizeof *Info;
}
else
goto Return;
//
// Do not return query information for the next interface,
// since persistent iteration uses RegistryIndex.
//
ReturnQueryInterface(NULL, &Info->Next);
Info->Length = sizeof *Info;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlPersistentQueryInterface
//* ReturnQueryAddress
//
// Initializes a returned IPV6_QUERY_ADDRESS structure
// with query information for the specified address.
// Does NOT initialize Query->IF.
//
void
ReturnQueryAddress(
AddressEntry *ADE,
IPV6_QUERY_ADDRESS *Query)
{
if (ADE == NULL)
Query->Address = UnspecifiedAddr;
else
Query->Address = ADE->Address;
}
//* IoctlQueryAddress
//
// Processes an IOCTL_IPV6_QUERY_ADDRESS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryAddress(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_ADDRESS *Query;
IPV6_INFO_ADDRESS *Info;
Interface *IF = NULL;
AddressEntry *ADE;
KIRQL OldIrql;
NTSTATUS Status;
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Next structures overlap!
//
Query = (IPV6_QUERY_ADDRESS *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_ADDRESS *) Irp->AssociatedIrp.SystemBuffer;
//
// Return information about the specified interface.
//
IF = FindInterfaceFromQuery(&Query->IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
if (IsUnspecified(&Query->Address)) {
//
// Return the address of the first ADE.
//
KeAcquireSpinLock(&IF->Lock, &OldIrql);
ReturnQueryAddress(IF->ADE, &Info->Next);
KeReleaseSpinLock(&IF->Lock, OldIrql);
Irp->IoStatus.Information = sizeof Info->Next;
} else {
//
// Find the specified ADE.
//
KeAcquireSpinLock(&IF->Lock, &OldIrql);
for (ADE = IF->ADE; ; ADE = ADE->Next) {
if (ADE == NULL) {
KeReleaseSpinLock(&IF->Lock, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto ReturnReleaseIF;
}
if (IP6_ADDR_EQUAL(&Query->Address, &ADE->Address))
break;
}
//
// Return misc. information about the ADE.
//
Info->This = *Query;
Info->Type = ADE->Type;
Info->Scope = ADE->Scope;
Info->ScopeId = DetermineScopeId(&ADE->Address, IF);
switch (ADE->Type) {
case ADE_UNICAST: {
NetTableEntry *NTE = (NetTableEntry *)ADE;
struct AddrConfEntry AddrConf;
Info->DADState = NTE->DADState;
AddrConf.Value = NTE->AddrConf;
Info->PrefixConf = AddrConf.PrefixConf;
Info->InterfaceIdConf = AddrConf.InterfaceIdConf;
Info->ValidLifetime = ConvertTicksToSeconds(NTE->ValidLifetime);
Info->PreferredLifetime = ConvertTicksToSeconds(NTE->PreferredLifetime);
break;
}
case ADE_MULTICAST: {
MulticastAddressEntry *MAE = (MulticastAddressEntry *)ADE;
Info->MCastRefCount = MAE->MCastRefCount;
Info->MCastFlags = MAE->MCastFlags;
Info->MCastTimer = ConvertTicksToSeconds(MAE->MCastTimer);
break;
}
}
//
// Return address of the next ADE.
//
ReturnQueryAddress(ADE->Next, &Info->Next);
KeReleaseSpinLock(&IF->Lock, OldIrql);
Irp->IoStatus.Information = sizeof *Info;
}
ReturnQueryInterface(IF, &Info->Next.IF);
Status = STATUS_SUCCESS;
ReturnReleaseIF:
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryAddress
//* OpenAddressRegKey
//
// Given an interface's registry key and an IPv6 address,
// opens the registry key with configuration info for the address.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
OpenAddressRegKey(HANDLE IFKey, const IPv6Addr *Addr,
OUT HANDLE *RegKey, OpenRegKeyAction Action)
{
WCHAR AddressName[64];
HANDLE AddressesKey;
NTSTATUS Status;
PAGED_CODE();
Status = OpenRegKey(&AddressesKey, IFKey, L"Addresses",
((Action == OpenRegKeyCreate) ?
OpenRegKeyCreate : OpenRegKeyRead));
if (! NT_SUCCESS(Status))
return Status;
//
// The output of RtlIpv6AddressToString may change
// over time with improvements/changes in the pretty-printing,
// and we need a consistent mapping.
// It doesn't need to be pretty.
//
swprintf(AddressName,
L"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
net_short(Addr->s6_words[0]), net_short(Addr->s6_words[1]),
net_short(Addr->s6_words[2]), net_short(Addr->s6_words[3]),
net_short(Addr->s6_words[4]), net_short(Addr->s6_words[5]),
net_short(Addr->s6_words[6]), net_short(Addr->s6_words[7]));
Status = OpenRegKey(RegKey, AddressesKey, AddressName, Action);
ZwClose(AddressesKey);
return Status;
}
//* OpenPersistentAddress
//
// Parses an address key name into an address
// and opens the address key.
//
NTSTATUS
OpenPersistentAddress(
HANDLE ParentKey,
WCHAR *SubKeyName,
IPv6Addr *Address,
HANDLE *AddrKey,
OpenRegKeyAction Action)
{
WCHAR *Terminator;
NTSTATUS Status;
PAGED_CODE();
//
// First, parse the address.
//
if (! ParseV6Address(SubKeyName, &Terminator, Address) ||
(*Terminator != UNICODE_NULL)) {
//
// Not a valid address.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"OpenPersistentAddress: bad syntax %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
//
// Open the address key.
//
Status = OpenRegKey(AddrKey, ParentKey, SubKeyName, Action);
if (! NT_SUCCESS(Status)) {
//
// Could not open the address key.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"OpenPersistentAddress: bad key %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
return STATUS_SUCCESS;
}
//* EnumPersistentAddress
//
// Helper function for FindPersistentAddressFromQuery,
// wrapping OpenPersistentAddress for EnumRegKeyIndex.
//
NTSTATUS
EnumPersistentAddress(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
struct {
IPv6Addr *Address;
HANDLE *AddrKey;
OpenRegKeyAction Action;
} *Args = Context;
PAGED_CODE();
return OpenPersistentAddress(ParentKey, SubKeyName,
Args->Address,
Args->AddrKey,
Args->Action);
}
//* FindPersistentAddressFromQuery
//
// Given an IPV6_PERSISTENT_QUERY_ADDRESS structure,
// finds the specified address key in the registry.
// If the address key is found, then Query->IF.Guid and
// Query->Address are returned.
//
NTSTATUS
FindPersistentAddressFromQuery(
IPV6_PERSISTENT_QUERY_ADDRESS *Query,
HANDLE *AddrKey)
{
HANDLE IFKey;
NTSTATUS Status;
PAGED_CODE();
//
// First get the interface key.
//
Status = FindPersistentInterfaceFromQuery(&Query->IF, &IFKey);
if (! NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER_1;
if (Query->RegistryIndex == (uint)-1) {
//
// Persistent query via address.
//
Status = OpenAddressRegKey(IFKey, &Query->Address,
AddrKey, OpenRegKeyRead);
}
else {
HANDLE AddressesKey;
//
// Open the Addresses subkey.
//
Status = OpenRegKey(&AddressesKey, IFKey,
L"Addresses", OpenRegKeyRead);
if (NT_SUCCESS(Status)) {
struct {
IPv6Addr *Address;
HANDLE *AddrKey;
OpenRegKeyAction Action;
} Args;
//
// Persistent query via registry index.
//
Args.Address = &Query->Address;
Args.AddrKey = AddrKey;
Args.Action = OpenRegKeyRead;
Status = EnumRegKeyIndex(AddressesKey,
Query->RegistryIndex,
EnumPersistentAddress,
&Args);
ZwClose(AddressesKey);
}
else {
//
// If the Addresses subkey is not present,
// then the index is not present.
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
Status = STATUS_NO_MORE_ENTRIES;
}
}
ZwClose(IFKey);
return Status;
}
//* GetPersistentLifetimes
//
// Read valid and preferred lifetimes from the registry key.
//
void
GetPersistentLifetimes(
HANDLE RegKey,
int Immortal,
uint *ValidLifetime,
uint *PreferredLifetime)
{
LARGE_INTEGER ValidLifetime64;
LARGE_INTEGER PreferredLifetime64;
//
// Read the 64-bit lifetimes.
//
ValidLifetime64.QuadPart = (LONGLONG) (LONG)INFINITE_LIFETIME;
InitRegQUADParameter(RegKey, L"ValidLifetime", &ValidLifetime64);
PreferredLifetime64.QuadPart = (LONGLONG) (LONG)INFINITE_LIFETIME;
InitRegQUADParameter(RegKey, L"PreferredLifetime", &PreferredLifetime64);
//
// Convert the lifetimes from 64-bit times to seconds.
// If the lifetimes are Immortal, then the persisted values
// are relative lifetimes. Otherwise they are absolute lifetimes.
//
if (Immortal) {
if (ValidLifetime64.QuadPart == (LONGLONG) (LONG)INFINITE_LIFETIME)
*ValidLifetime = INFINITE_LIFETIME;
else
*ValidLifetime = (uint)
(ValidLifetime64.QuadPart / (10*1000*1000));
if (PreferredLifetime64.QuadPart == (LONGLONG) (LONG)INFINITE_LIFETIME)
*PreferredLifetime = INFINITE_LIFETIME;
else
*PreferredLifetime = (uint)
(PreferredLifetime64.QuadPart / (10*1000*1000));
}
else {
LARGE_INTEGER Now64;
KeQuerySystemTime(&Now64);
if (ValidLifetime64.QuadPart == (LONGLONG) (LONG)INFINITE_LIFETIME)
*ValidLifetime = INFINITE_LIFETIME;
else if (ValidLifetime64.QuadPart < Now64.QuadPart)
*ValidLifetime = 0;
else
*ValidLifetime = (uint)
((ValidLifetime64.QuadPart - Now64.QuadPart) / (10*1000*1000));
if (PreferredLifetime64.QuadPart == (LONGLONG) (LONG)INFINITE_LIFETIME)
*PreferredLifetime = INFINITE_LIFETIME;
else if (PreferredLifetime64.QuadPart < Now64.QuadPart)
*PreferredLifetime = 0;
else
*PreferredLifetime = (uint)
((PreferredLifetime64.QuadPart - Now64.QuadPart) / (10*1000*1000));
}
}
//* SetPersistentLifetimes
//
// Write valid and preferred lifetimes to the registry key.
//
NTSTATUS
SetPersistentLifetimes(
HANDLE RegKey,
int Immortal,
uint ValidLifetime,
uint PreferredLifetime)
{
LARGE_INTEGER ValidLifetime64;
LARGE_INTEGER PreferredLifetime64;
NTSTATUS Status;
//
// Persist the lifetimes as 64-bit times.
// If the lifetimes are Immortal, then we persist
// relative lifetimes. Otherwise we persist
// absolute lifetimes.
//
if (Immortal) {
if (ValidLifetime == INFINITE_LIFETIME)
ValidLifetime64.QuadPart = (LONGLONG) (LONG)INFINITE_LIFETIME;
else
ValidLifetime64.QuadPart = (10*1000*1000) *
(LONGLONG) ValidLifetime;
if (PreferredLifetime == INFINITE_LIFETIME)
PreferredLifetime64.QuadPart = (LONGLONG) (LONG)INFINITE_LIFETIME;
else
PreferredLifetime64.QuadPart = (10*1000*1000) *
(LONGLONG) PreferredLifetime;
}
else {
LARGE_INTEGER Now64;
KeQuerySystemTime(&Now64);
if (ValidLifetime == INFINITE_LIFETIME)
ValidLifetime64.QuadPart = (LONGLONG) (LONG)INFINITE_LIFETIME;
else
ValidLifetime64.QuadPart = Now64.QuadPart + (10*1000*1000) *
(LONGLONG) ValidLifetime;
if (PreferredLifetime == INFINITE_LIFETIME)
PreferredLifetime64.QuadPart = (LONGLONG) (LONG)INFINITE_LIFETIME;
else
PreferredLifetime64.QuadPart = Now64.QuadPart + (10*1000*1000) *
(LONGLONG) PreferredLifetime;
}
//
// Persist the valid lifetime.
//
Status = SetRegQUADValue(RegKey, L"ValidLifetime",
&ValidLifetime64);
if (! NT_SUCCESS(Status))
return Status;
//
// Persist the preferred lifetime.
//
Status = SetRegQUADValue(RegKey, L"PreferredLifetime",
&PreferredLifetime64);
return Status;
}
//* ReadPersistentAddress
//
// Reads address attributes from the registry key.
// Initializes all the fields except This.
//
void
ReadPersistentAddress(
HANDLE AddrKey,
IPV6_UPDATE_ADDRESS *Info)
{
InitRegDWORDParameter(AddrKey, L"Type",
(ULONG *)&Info->Type, ADE_UNICAST);
Info->PrefixConf = PREFIX_CONF_MANUAL;
Info->InterfaceIdConf = IID_CONF_MANUAL;
GetPersistentLifetimes(AddrKey, FALSE,
&Info->ValidLifetime,
&Info->PreferredLifetime);
}
//* IoctlPersistentQueryAddress
//
// Processes an IOCTL_IPV6_PERSISTENT_QUERY_ADDRESS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlPersistentQueryAddress(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_PERSISTENT_QUERY_ADDRESS *Query;
IPV6_UPDATE_ADDRESS *Info;
IPV6_QUERY_ADDRESS This;
HANDLE AddrKey;
NTSTATUS Status;
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->This structures overlap!
//
Query = (IPV6_PERSISTENT_QUERY_ADDRESS *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_UPDATE_ADDRESS *) Irp->AssociatedIrp.SystemBuffer;
//
// Get the registry key for the specified address.
//
Status = FindPersistentAddressFromQuery(Query, &AddrKey);
if (! NT_SUCCESS(Status))
goto Return;
//
// The interface index is not returned for persistent queries.
//
This.IF.Index = 0;
This.IF.Guid = Query->IF.Guid;
This.Address = Query->Address;
Info->This = This;
//
// Read address information from the registry key.
//
ReadPersistentAddress(AddrKey, Info);
ZwClose(AddrKey);
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof *Info;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlPersistentQueryAddress
//* IoctlQueryNeighborCache
//
// Processes an IOCTL_IPV6_QUERY_NEIGHBOR_CACHE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryNeighborCache(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_NEIGHBOR_CACHE *Query;
IPV6_INFO_NEIGHBOR_CACHE *Info;
Interface *IF = NULL;
NeighborCacheEntry *NCE;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Query structures overlap!
//
Query = (IPV6_QUERY_NEIGHBOR_CACHE *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_NEIGHBOR_CACHE *) Irp->AssociatedIrp.SystemBuffer;
//
// Return information about the specified interface.
//
IF = FindInterfaceFromQuery(&Query->IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
if (IsUnspecified(&Query->Address)) {
//
// Return the address of the first NCE.
//
KeAcquireSpinLock(&IF->LockNC, &OldIrql);
if (IF->FirstNCE != SentinelNCE(IF))
Info->Query.Address = IF->FirstNCE->NeighborAddress;
KeReleaseSpinLock(&IF->LockNC, OldIrql);
Irp->IoStatus.Information = sizeof Info->Query;
} else {
uint Now = IPv6TickCount;
//
// Find the specified NCE.
//
KeAcquireSpinLock(&IF->LockNC, &OldIrql);
for (NCE = IF->FirstNCE; ; NCE = NCE->Next) {
if (NCE == SentinelNCE(IF)) {
KeReleaseSpinLock(&IF->LockNC, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
if (IP6_ADDR_EQUAL(&Query->Address, &NCE->NeighborAddress))
break;
}
Irp->IoStatus.Information = sizeof *Info;
//
// Return the neighbor's link-layer address,
// if there is room in the user's buffer.
//
Info->LinkLayerAddressLength = IF->LinkAddressLength;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
sizeof *Info + IF->LinkAddressLength) {
RtlCopyMemory(Info + 1, NCE->LinkAddress, IF->LinkAddressLength);
Irp->IoStatus.Information += IF->LinkAddressLength;
}
//
// Return miscellaneous information about the NCE.
//
Info->IsRouter = NCE->IsRouter;
Info->IsUnreachable = NCE->IsUnreachable;
if ((NCE->NDState == ND_STATE_REACHABLE) &&
((uint)(Now - NCE->LastReachability) > IF->ReachableTime))
Info->NDState = ND_STATE_STALE;
else if ((NCE->NDState == ND_STATE_PROBE) &&
(NCE->NSCount == 0))
Info->NDState = ND_STATE_DELAY;
else
Info->NDState = NCE->NDState;
Info->ReachableTimer = ConvertTicksToMillis(IF->ReachableTime -
(Now - NCE->LastReachability));
//
// Return address of the next NCE (or zero).
//
if (NCE->Next == SentinelNCE(IF))
Info->Query.Address = UnspecifiedAddr;
else
Info->Query.Address = NCE->Next->NeighborAddress;
KeReleaseSpinLock(&IF->LockNC, OldIrql);
}
Status = STATUS_SUCCESS;
Return:
if (IF != NULL)
ReleaseIF(IF);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryNeighborCache
//* IoctlQueryRouteCache
//
// Processes an IOCTL_IPV6_QUERY_ROUTE_CACHE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryRouteCache(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_ROUTE_CACHE *Query;
IPV6_INFO_ROUTE_CACHE *Info;
RouteCacheEntry *RCE;
BindingCacheEntry *BCE;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Query structures overlap!
//
Query = (IPV6_QUERY_ROUTE_CACHE *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_ROUTE_CACHE *) Irp->AssociatedIrp.SystemBuffer;
if (Query->IF.Index == 0) {
//
// Return the index and address of the first RCE.
//
KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
if (RouteCache.First != SentinelRCE) {
Info->Query.IF.Index = RouteCache.First->NTE->IF->Index;
Info->Query.Address = RouteCache.First->Destination;
}
KeReleaseSpinLock(&RouteCacheLock, OldIrql);
Irp->IoStatus.Information = sizeof Info->Query;
} else {
uint Now = IPv6TickCount;
//
// Find the specified RCE.
//
KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
for (RCE = RouteCache.First; ; RCE = RCE->Next) {
if (RCE == SentinelRCE) {
KeReleaseSpinLock(&RouteCacheLock, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
if (IP6_ADDR_EQUAL(&Query->Address, &RCE->Destination) &&
(Query->IF.Index == RCE->NTE->IF->Index))
break;
}
//
// Return misc. information about the RCE.
//
Info->Type = RCE->Type;
Info->Flags = RCE->Flags;
Info->Valid = (RCE->Valid == RouteCacheValidationCounter);
Info->SourceAddress = RCE->NTE->Address;
Info->NextHopAddress = RCE->NCE->NeighborAddress;
Info->NextHopInterface = RCE->NCE->IF->Index;
Info->PathMTU = RCE->PathMTU;
if (RCE->PMTULastSet != 0) {
uint SinceLastSet = Now - RCE->PMTULastSet;
ASSERT((int)SinceLastSet >= 0);
if (SinceLastSet < PATH_MTU_RETRY_TIME)
Info->PMTUProbeTimer =
ConvertTicksToMillis(PATH_MTU_RETRY_TIME - SinceLastSet);
else
Info->PMTUProbeTimer = 0; // Fires on next packet.
} else
Info->PMTUProbeTimer = INFINITE_LIFETIME; // Not set.
if (RCE->LastError != 0)
Info->ICMPLastError = ConvertTicksToMillis(Now - RCE->LastError);
else
Info->ICMPLastError = 0;
if (RCE->BCE != NULL) {
Info->CareOfAddress = RCE->BCE->CareOfRCE->Destination;
Info->BindingSeqNumber = RCE->BCE->BindingSeqNumber;
Info->BindingLifetime = ConvertTicksToSeconds(RCE->BCE->BindingLifetime);
} else {
Info->CareOfAddress = UnspecifiedAddr;
Info->BindingSeqNumber = 0;
Info->BindingLifetime = 0;
}
//
// Return index and address of the next RCE (or zero).
//
if (RCE->Next == SentinelRCE) {
Info->Query.IF.Index = 0;
} else {
Info->Query.IF.Index = RCE->Next->NTE->IF->Index;
Info->Query.Address = RCE->Next->Destination;
}
KeReleaseSpinLock(&RouteCacheLock, OldIrql);
Irp->IoStatus.Information = sizeof *Info;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryRouteCache
//* IoctlCreateSecurityPolicy
//
NTSTATUS
IoctlCreateSecurityPolicy(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_CREATE_SECURITY_POLICY *CreateSP;
SecurityPolicy *SP, *BundledSP;
NTSTATUS Status;
KIRQL OldIrql;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *CreateSP) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
CreateSP = (IPV6_CREATE_SECURITY_POLICY *)Irp->AssociatedIrp.SystemBuffer;
//
// Sanity check the user-supplied input values.
//
if ((CreateSP->RemoteAddrField != WILDCARD_VALUE) &&
(CreateSP->RemoteAddrField != SINGLE_VALUE) &&
(CreateSP->RemoteAddrField != RANGE_VALUE)) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
if ((CreateSP->LocalAddrField != WILDCARD_VALUE) &&
(CreateSP->LocalAddrField != SINGLE_VALUE) &&
(CreateSP->LocalAddrField != RANGE_VALUE)) {
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
// TransportProto can be anything.
// Port values can be anything.
//
// We do not support IPSEC_APPCHOICE.
//
if ((CreateSP->IPSecAction != IPSEC_DISCARD) &&
(CreateSP->IPSecAction != IPSEC_APPLY) &&
(CreateSP->IPSecAction != IPSEC_BYPASS)) {
Status = STATUS_INVALID_PARAMETER_3;
goto Return;
}
if ((CreateSP->IPSecProtocol != IP_PROTOCOL_AH) &&
(CreateSP->IPSecProtocol != IP_PROTOCOL_ESP) &&
(CreateSP->IPSecProtocol != NONE)) {
Status = STATUS_INVALID_PARAMETER_4;
goto Return;
}
if ((CreateSP->IPSecMode != TRANSPORT) &&
(CreateSP->IPSecMode != TUNNEL) &&
(CreateSP->IPSecMode != NONE)) {
Status = STATUS_INVALID_PARAMETER_5;
goto Return;
}
if (CreateSP->IPSecAction == IPSEC_APPLY) {
if ((CreateSP->IPSecProtocol == NONE) ||
(CreateSP->IPSecMode == NONE)) {
Status = STATUS_INVALID_PARAMETER_MIX;
goto Return;
}
}
if ((CreateSP->Direction != INBOUND) &&
(CreateSP->Direction != OUTBOUND) &&
(CreateSP->Direction != BIDIRECTIONAL)) {
Status = STATUS_INVALID_PARAMETER_6;
goto Return;
}
if (((CreateSP->RemoteAddrSelector != PACKET_SELECTOR) &&
(CreateSP->RemoteAddrSelector != POLICY_SELECTOR)) ||
((CreateSP->LocalAddrSelector != PACKET_SELECTOR) &&
(CreateSP->LocalAddrSelector != POLICY_SELECTOR)) ||
((CreateSP->RemotePortSelector != PACKET_SELECTOR) &&
(CreateSP->RemotePortSelector != POLICY_SELECTOR)) ||
((CreateSP->LocalPortSelector != PACKET_SELECTOR) &&
(CreateSP->LocalPortSelector != POLICY_SELECTOR)) ||
((CreateSP->TransportProtoSelector != PACKET_SELECTOR) &&
(CreateSP->TransportProtoSelector != POLICY_SELECTOR))) {
Status = STATUS_INVALID_PARAMETER_7;
goto Return;
}
// Get Security Lock.
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// REVIEW: This considers a non-existent interface an error. Should it?
//
if (CreateSP->SPInterface != 0) {
Interface *IF;
IF = FindInterfaceFromIndex(CreateSP->SPInterface);
if (IF == NULL) {
//
// Unknown interface.
//
Status = STATUS_NOT_FOUND;
goto ReturnUnlock;
}
ReleaseIF(IF);
}
//
// Allocate memory for Security Policy.
//
SP = ExAllocatePool(NonPagedPool, sizeof *SP);
if (SP == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ReturnUnlock;
}
//
// Copy CreateSP to SP.
//
SP->Index = CreateSP->SPIndex;
SP->RemoteAddr = CreateSP->RemoteAddr;
SP->RemoteAddrData = CreateSP->RemoteAddrData;
SP->RemoteAddrSelector = CreateSP->RemoteAddrSelector;
SP->RemoteAddrField = CreateSP->RemoteAddrField;
SP->LocalAddr = CreateSP->LocalAddr;
SP->LocalAddrData = CreateSP->LocalAddrData;
SP->LocalAddrSelector = CreateSP->LocalAddrSelector;
SP->LocalAddrField = CreateSP->LocalAddrField;
SP->TransportProto = CreateSP->TransportProto;
SP->TransportProtoSelector = CreateSP->TransportProtoSelector;
SP->RemotePort = CreateSP->RemotePort;
SP->RemotePortData = CreateSP->RemotePortData;
SP->RemotePortSelector = CreateSP->RemotePortSelector;
SP->RemotePortField = CreateSP->RemotePortField;
SP->LocalPort = CreateSP->LocalPort;
SP->LocalPortData = CreateSP->LocalPortData;
SP->LocalPortSelector = CreateSP->LocalPortSelector;
SP->LocalPortField = CreateSP->LocalPortField;
SP->SecPolicyFlag = CreateSP->IPSecAction;
SP->IPSecSpec.Protocol = CreateSP->IPSecProtocol;
SP->IPSecSpec.Mode = CreateSP->IPSecMode;
SP->IPSecSpec.RemoteSecGWIPAddr = CreateSP->RemoteSecurityGWAddr;
SP->DirectionFlag = CreateSP->Direction;
SP->OutboundSA = NULL;
SP->InboundSA = NULL;
SP->PrevSABundle = NULL;
SP->RefCnt = 0;
SP->NestCount = 1;
SP->IFIndex = CreateSP->SPInterface;
//
// Insert SP into the global list.
//
if (!InsertSecurityPolicy(SP)) {
//
// Couldn't insert, free up failed SP memory.
//
ExFreePool(SP);
Status = STATUS_OBJECT_NAME_COLLISION;
goto ReturnUnlock;
}
//
// Convert SABundleIndex to the SABundle pointer.
//
if (CreateSP->SABundleIndex == 0) {
SP->SABundle = NULL;
} else {
// Search the SP List starting at the first SP.
BundledSP = FindSecurityPolicyMatch(SecurityPolicyList, 0,
CreateSP->SABundleIndex);
if (BundledSP == NULL) {
//
// Policy with which this new one was supposed to bundle
// does not exist. Abort creation of this new policy.
//
RemoveSecurityPolicy(SP);
ExFreePool(SP);
Status = STATUS_INVALID_PARAMETER;
goto ReturnUnlock;
} else {
SP->SABundle = BundledSP;
BundledSP->RefCnt++;
SP->NestCount = BundledSP->NestCount + 1;
//
// The bundle entry list is doubly linked to facilitate
// ease of entry deletion.
//
BundledSP->PrevSABundle = SP;
SP->RefCnt++;
}
}
Status = STATUS_SUCCESS;
ReturnUnlock:
// Release lock.
KeReleaseSpinLock(&IPSecLock, OldIrql);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlCreateSecurityPolicy
//* IoctlCreateSecurityAssociation
//
NTSTATUS
IoctlCreateSecurityAssociation(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_CREATE_SECURITY_ASSOCIATION *CreateSA;
SecurityAssociation *SA;
SecurityPolicy *SP;
uint KeySize;
uchar *RawKey;
NTSTATUS Status;
KIRQL OldIrql;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof *CreateSA) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
CreateSA = (IPV6_CREATE_SECURITY_ASSOCIATION *)Irp->AssociatedIrp.SystemBuffer;
//
// Sanity check the user-supplied input values.
//
if ((CreateSA->Direction != INBOUND) &&
(CreateSA->Direction != OUTBOUND)) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
if (CreateSA->AlgorithmId >= NUM_ALGORITHMS) {
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
KeySize = AlgorithmTable[CreateSA->AlgorithmId].KeySize;
if (CreateSA->RawKeySize > MAX_KEY_SIZE) {
//
// We cap the RawKeySize at something rational.
//
Status = STATUS_INVALID_PARAMETER_3;
goto Return;
}
//
// RawKey should be passed in the Ioctl immediately after CreateSA.
//
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength !=
(sizeof(*CreateSA) + CreateSA->RawKeySize)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
RawKey = (uchar *)(CreateSA + 1);
//
// Allocate memory for Security Association and the Key.
// The Key will immediately follow the SA in memory.
//
#ifdef IPSEC_DEBUG
SA = ExAllocatePool(NonPagedPool,
sizeof(*SA) + KeySize + CreateSA->RawKeySize);
#else
SA = ExAllocatePool(NonPagedPool, sizeof(*SA) + KeySize);
#endif
if (SA == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Return;
}
SA->Key = (uchar *)(SA + 1);
//
// Copy CreateSA to SA.
//
SA->Index = CreateSA->SAIndex;
SA->SPI = CreateSA->SPI;
SA->SequenceNum = 0;
SA->SADestAddr = CreateSA->SADestAddr;
SA->DestAddr = CreateSA->DestAddr;
SA->SrcAddr = CreateSA->SrcAddr;
SA->TransportProto = CreateSA->TransportProto;
SA->DestPort = CreateSA->DestPort;
SA->SrcPort = CreateSA->SrcPort;
SA->DirectionFlag = CreateSA->Direction;
SA->RefCnt = 0;
SA->AlgorithmId = CreateSA->AlgorithmId;
SA->KeyLength = KeySize;
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC,
"SA %d prepped KeySize is %d\n",
CreateSA->SAIndex, KeySize));
SA->RawKey = (uchar *)(SA->Key + KeySize);
SA->RawKeyLength = CreateSA->RawKeySize;
//
// Copy raw key to SA.
//
memcpy(SA->RawKey, RawKey, SA->RawKeyLength);
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC,
"SA %d RawKey (%d bytes): ",
CreateSA->SAIndex, SA->RawKeyLength));
DumpKey(SA->RawKey, SA->RawKeyLength);
#endif
//
// Prepare the manual key.
//
(*AlgorithmTable[SA->AlgorithmId].PrepareKey)
(RawKey, CreateSA->RawKeySize, SA->Key);
//
// Get Security Lock.
//
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// Find policy which this association instantiates.
//
SP = FindSecurityPolicyMatch(SecurityPolicyList, 0,
CreateSA->SecPolicyIndex);
if (SP == NULL) {
//
// No matching policy exists.
//
Status = STATUS_INVALID_PARAMETER_4;
ExFreePool(SA);
goto ReturnUnlock;
}
// Set the SA's IPSecProto to match that of the SP.
SA->IPSecProto = SP->IPSecSpec.Protocol;
//
// Check that direction of SA is legitimate for this SP.
//
if ((SA->DirectionFlag & SP->DirectionFlag) == 0) {
//
// Direction of SA is incompatible with SP's.
// Abort creation of this new association.
//
Status = STATUS_INVALID_PARAMETER_MIX;
ExFreePool(SA);
goto ReturnUnlock;
}
//
// Add this association to the global list.
//
if (!InsertSecurityAssociation(SA)) {
//
// Couldn't insert, free up failed SP memory.
//
Status = STATUS_OBJECT_NAME_COLLISION;
ExFreePool(SA);
goto ReturnUnlock;
}
//
// Add this association to policy's instantiated associations list.
//
if (SA->DirectionFlag == INBOUND) {
// Add the SA to the policy's inbound list.
SA->ChainedSecAssoc = SP->InboundSA;
SP->InboundSA = SA;
AddRefSA(SA);
// The SA keeps a pointer to the SP it instantiates.
SA->SecPolicy = SP;
SA->SecPolicy->RefCnt++;
} else {
// Add the SA to the policy's outbound list.
SA->ChainedSecAssoc = SP->OutboundSA;
SP->OutboundSA = SA;
AddRefSA(SA);
// Add the SP to the SA SecPolicy pointer.
SA->SecPolicy = SP;
SA->SecPolicy->RefCnt++;
}
SA->Valid = SA_VALID;
Status = STATUS_SUCCESS;
ReturnUnlock:
// Release lock.
KeReleaseSpinLock(&IPSecLock, OldIrql);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlCreateSecurityAssociation
//* IoctlQuerySecurityPolicyList
//
NTSTATUS
IoctlQuerySecurityPolicyList(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_SECURITY_POLICY_LIST *Query;
IPV6_INFO_SECURITY_POLICY_LIST *Info;
SecurityPolicy *SP, *NextSP;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_SECURITY_POLICY_LIST *)Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_SECURITY_POLICY_LIST *)Irp->AssociatedIrp.SystemBuffer;
//
// REVIEW: This considers a non-existent interface an error. Should it?
//
if (Query->SPInterface != 0) {
Interface *IF;
IF = FindInterfaceFromIndex(Query->SPInterface);
if (IF == NULL) {
//
// Unknown interface.
//
Status = STATUS_NOT_FOUND;
goto Return;
}
ReleaseIF(IF);
}
//
// Get Security Lock.
//
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// Find matching policy.
//
SP = FindSecurityPolicyMatch(SecurityPolicyList, Query->SPInterface,
Query->Index);
if (SP == NULL) {
//
// No matching policy exists.
//
Status = STATUS_NO_MATCH;
goto ReturnUnlock;
}
//
// Get the next index to query.
//
NextSP = FindSecurityPolicyMatch(SP->Next, Query->SPInterface, 0);
if (NextSP == NULL) {
Info->NextSPIndex = 0;
} else {
Info->NextSPIndex = NextSP->Index;
}
//
// Copy SP to Info.
//
Info->SPIndex = SP->Index;
Info->RemoteAddr = SP->RemoteAddr;
Info->RemoteAddrData = SP->RemoteAddrData;
Info->RemoteAddrSelector = SP->RemoteAddrSelector;
Info->RemoteAddrField = SP->RemoteAddrField;
Info->LocalAddr = SP->LocalAddr;
Info->LocalAddrData = SP->LocalAddrData;
Info->LocalAddrSelector = SP->LocalAddrSelector;
Info->LocalAddrField = SP->LocalAddrField;
Info->TransportProto = SP->TransportProto;
Info->TransportProtoSelector = SP->TransportProtoSelector;
Info->RemotePort = SP->RemotePort;
Info->RemotePortData = SP->RemotePortData;
Info->RemotePortSelector = SP->RemotePortSelector;
Info->RemotePortField = SP->RemotePortField;
Info->LocalPort = SP->LocalPort;
Info->LocalPortData = SP->LocalPortData;
Info->LocalPortSelector = SP->LocalPortSelector;
Info->LocalPortField = SP->LocalPortField;
Info->IPSecProtocol = SP->IPSecSpec.Protocol;
Info->IPSecMode = SP->IPSecSpec.Mode;
Info->RemoteSecurityGWAddr = SP->IPSecSpec.RemoteSecGWIPAddr;
Info->Direction = SP->DirectionFlag;
Info->IPSecAction = SP->SecPolicyFlag;
Info->SABundleIndex = GetSecurityPolicyIndex(SP->SABundle);
Info->SPInterface = SP->IFIndex;
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof *Info;
ReturnUnlock:
KeReleaseSpinLock(&IPSecLock, OldIrql);
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQuerySecurityPolicyList
//* IoctlDeleteSecurityPolicy
//
NTSTATUS
IoctlDeleteSecurityPolicy(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_SECURITY_POLICY_LIST *Query;
SecurityPolicy *SP;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_SECURITY_POLICY_LIST *)Irp->AssociatedIrp.SystemBuffer;
//
// Get Security Lock.
//
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// Find the policy in question.
//
SP = FindSecurityPolicyMatch(SecurityPolicyList, 0, Query->Index);
if (SP == NULL) {
//
// The policy does not exist.
//
Status = STATUS_NO_MATCH;
goto ReturnUnlock;
}
//
// Remove the SP.
//
if (DeleteSP(SP)) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_UNSUCCESSFUL;
}
ReturnUnlock:
KeReleaseSpinLock(&IPSecLock, OldIrql);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
//* IoctlQuerySecurityAssociationList
//
NTSTATUS
IoctlQuerySecurityAssociationList(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_SECURITY_ASSOCIATION_LIST *Query;
IPV6_INFO_SECURITY_ASSOCIATION_LIST *Info;
SecurityAssociation *SA;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_SECURITY_ASSOCIATION_LIST *)Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_SECURITY_ASSOCIATION_LIST *)Irp->AssociatedIrp.SystemBuffer;
//
// Get Security Lock.
//
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// Find matching association.
//
SA = FindSecurityAssociationMatch(Query->Index);
if (SA == NULL) {
//
// No matching association exists.
//
Status = STATUS_NO_MATCH;
goto ReturnUnlock;
}
//
// Get the next index to query.
//
if (SA->Next == NULL) {
// No more SAs after this one.
Info->NextSAIndex = 0;
} else {
// Return the next SA.
Info->NextSAIndex = SA->Next->Index;
}
//
// Copy SA to Info.
//
Info->SAIndex = SA->Index;
Info->SPI = SA->SPI;
Info->SADestAddr = SA->SADestAddr;
Info->DestAddr = SA->DestAddr;
Info->SrcAddr = SA->SrcAddr;
Info->TransportProto = SA->TransportProto;
Info->DestPort = SA->DestPort;
Info->SrcPort = SA->SrcPort;
Info->Direction = SA->DirectionFlag;
Info->SecPolicyIndex = GetSecurityPolicyIndex(SA->SecPolicy);
Info->AlgorithmId = SA->AlgorithmId;
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof *Info;
ReturnUnlock:
KeReleaseSpinLock(&IPSecLock, OldIrql);
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQuerySecurityAssociationList
//* IoctlDeleteSecurityAssociation
//
NTSTATUS
IoctlDeleteSecurityAssociation(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_SECURITY_ASSOCIATION_LIST *Query;
SecurityAssociation *SA;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_SECURITY_ASSOCIATION_LIST *)Irp->AssociatedIrp.SystemBuffer;
//
// Get Security Lock.
//
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// Find the association in question.
//
SA = FindSecurityAssociationMatch(Query->Index);
if (SA == NULL) {
//
// The association does not exist.
//
Status = STATUS_NO_MATCH;
goto ReturnUnlock;
}
//
// Remove the SA.
//
if (DeleteSA(SA)) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_UNSUCCESSFUL;
}
ReturnUnlock:
KeReleaseSpinLock(&IPSecLock, OldIrql);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
//* RouteTableInfo
//
// Return information about a route.
//
// We allow Info->This to be filled in from a different RTE
// than the other fields.
//
void
RouteTableInfo(RouteTableEntry *ThisRTE, RouteTableEntry *InfoRTE,
IPV6_INFO_ROUTE_TABLE *Info)
{
if (ThisRTE == NULL) {
Info->This.Neighbor.IF.Index = 0;
} else {
Info->This.Prefix = ThisRTE->Prefix;
Info->This.PrefixLength = ThisRTE->PrefixLength;
Info->This.Neighbor.IF.Index = ThisRTE->IF->Index;
if (!IsOnLinkRTE(ThisRTE))
Info->This.Neighbor.Address = ThisRTE->NCE->NeighborAddress;
else
Info->This.Neighbor.Address = UnspecifiedAddr;
}
if (InfoRTE != NULL) {
Info->SitePrefixLength = InfoRTE->SitePrefixLength;;
Info->ValidLifetime =
ConvertTicksToSeconds(InfoRTE->ValidLifetime);
Info->PreferredLifetime =
ConvertTicksToSeconds(InfoRTE->PreferredLifetime);
Info->Preference = InfoRTE->Preference;
Info->Publish = !!(InfoRTE->Flags & RTE_FLAG_PUBLISH);
Info->Immortal = !!(InfoRTE->Flags & RTE_FLAG_IMMORTAL);
Info->Type = InfoRTE->Type;
}
}
//* IoctlQueryRouteTable
//
// Processes an IOCTL_IPV6_QUERY_ROUTE_TABLE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryRouteTable(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_ROUTE_TABLE *Query;
IPV6_INFO_ROUTE_TABLE *Info;
RouteTableEntry *RTE;
KIRQL OldIrql;
NTSTATUS Status;
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->This structures overlap!
//
Query = (IPV6_QUERY_ROUTE_TABLE *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_ROUTE_TABLE *) Irp->AssociatedIrp.SystemBuffer;
if (Query->Neighbor.IF.Index == 0) {
//
// Return the prefix and neighbor of the first RTE.
//
KeAcquireSpinLock(&RouteTableLock, &OldIrql);
RouteTableInfo(RouteTable.First, NULL, Info);
KeReleaseSpinLock(&RouteTableLock, OldIrql);
Irp->IoStatus.Information = sizeof Info->This;
} else {
//
// Find the specified RTE.
//
KeAcquireSpinLock(&RouteTableLock, &OldIrql);
for (RTE = RouteTable.First; ; RTE = RTE->Next) {
if (RTE == NULL) {
KeReleaseSpinLock(&RouteTableLock, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
if (IP6_ADDR_EQUAL(&Query->Prefix, &RTE->Prefix) &&
(Query->PrefixLength == RTE->PrefixLength) &&
(Query->Neighbor.IF.Index == RTE->IF->Index) &&
IP6_ADDR_EQUAL(&Query->Neighbor.Address,
(IsOnLinkRTE(RTE) ?
&UnspecifiedAddr :
&RTE->NCE->NeighborAddress)))
break;
}
//
// Return misc. information about the RTE.
//
RouteTableInfo(RTE->Next, RTE, Info);
KeReleaseSpinLock(&RouteTableLock, OldIrql);
Irp->IoStatus.Information = sizeof *Info;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryRouteTable
//* OpenRouteRegKey
//
// Given an interface's registry key and route information
// opens the registry key with configuration info for the route.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
OpenRouteRegKey(
HANDLE IFKey,
const IPv6Addr *Prefix,
uint PrefixLength,
const IPv6Addr *Neighbor,
OUT HANDLE *RegKey,
OpenRegKeyAction Action)
{
WCHAR RouteName[128];
HANDLE RoutesKey;
NTSTATUS Status;
PAGED_CODE();
Status = OpenRegKey(&RoutesKey, IFKey, L"Routes",
((Action == OpenRegKeyCreate) ?
OpenRegKeyCreate : OpenRegKeyRead));
if (! NT_SUCCESS(Status))
return Status;
//
// The output of RtlIpv6AddressToString may change
// over time with improvements/changes in the pretty-printing,
// and we need a consistent mapping.
// It doesn't need to be pretty.
//
swprintf(RouteName,
L"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%u->"
L"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
net_short(Prefix->s6_words[0]), net_short(Prefix->s6_words[1]),
net_short(Prefix->s6_words[2]), net_short(Prefix->s6_words[3]),
net_short(Prefix->s6_words[4]), net_short(Prefix->s6_words[5]),
net_short(Prefix->s6_words[6]), net_short(Prefix->s6_words[7]),
PrefixLength,
net_short(Neighbor->s6_words[0]), net_short(Neighbor->s6_words[1]),
net_short(Neighbor->s6_words[2]), net_short(Neighbor->s6_words[3]),
net_short(Neighbor->s6_words[4]), net_short(Neighbor->s6_words[5]),
net_short(Neighbor->s6_words[6]), net_short(Neighbor->s6_words[7]));
Status = OpenRegKey(RegKey, RoutesKey, RouteName, Action);
ZwClose(RoutesKey);
return Status;
}
//* OpenPersistentRoute
//
// Parses a route key name into a prefix and prefix length plus
// a next-hop neighbor address and opens the route key.
//
NTSTATUS
OpenPersistentRoute(
HANDLE ParentKey,
WCHAR *SubKeyName,
IPv6Addr *Prefix,
uint *PrefixLength,
IPv6Addr *Neighbor,
HANDLE *RouteKey,
OpenRegKeyAction Action)
{
WCHAR *Terminator;
NTSTATUS Status;
PAGED_CODE();
//
// First, parse the prefix.
//
if (! ParseV6Address(SubKeyName, &Terminator, Prefix) ||
(*Terminator != L'/')) {
//
// Not a valid prefix.
//
SyntaxError:
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"OpenPersistentRoute: bad syntax %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
//
// Next, parse the prefix length.
//
Terminator++; // Move past the L'/'.
*PrefixLength = 0;
for (;;) {
WCHAR Char = *Terminator++;
if (Char == L'-') {
Char = *Terminator++;
if (Char == L'>')
break;
else
goto SyntaxError;
}
else if ((L'0' <= Char) && (Char <= L'9')) {
*PrefixLength *= 10;
*PrefixLength += Char - L'0';
if (*PrefixLength > IPV6_ADDRESS_LENGTH)
goto SyntaxError;
}
else
goto SyntaxError;
}
//
// Finally, parse the neighbor address.
//
if (! ParseV6Address(Terminator, &Terminator, Neighbor) ||
(*Terminator != UNICODE_NULL))
goto SyntaxError;
//
// Open the route key.
//
Status = OpenRegKey(RouteKey, ParentKey, SubKeyName, Action);
if (! NT_SUCCESS(Status)) {
//
// Could not open the route key.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"OpenPersistentRoute: bad key %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
return STATUS_SUCCESS;
}
//* EnumPersistentRoute
//
// Helper function for FindPersistentRouteFromQuery,
// wrapping OpenPersistentRoute for EnumRegKeyIndex.
//
NTSTATUS
EnumPersistentRoute(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
struct {
IPv6Addr *Prefix;
uint *PrefixLength;
IPv6Addr *Neighbor;
HANDLE *RouteKey;
OpenRegKeyAction Action;
} *Args = Context;
PAGED_CODE();
return OpenPersistentRoute(ParentKey, SubKeyName,
Args->Prefix,
Args->PrefixLength,
Args->Neighbor,
Args->RouteKey,
Args->Action);
}
//* FindPersistentRouteFromQuery
//
// Given an IPV6_PERSISTENT_QUERY_ROUTE_TABLE structure,
// finds the specified route key in the registry.
// If the route key is found, then Query->IF.Guid and
// Query->Address are returned.
//
NTSTATUS
FindPersistentRouteFromQuery(
IPV6_PERSISTENT_QUERY_ROUTE_TABLE *Query,
HANDLE *RouteKey)
{
HANDLE IFKey;
NTSTATUS Status;
PAGED_CODE();
//
// First get the interface key.
//
Status = FindPersistentInterfaceFromQuery(&Query->IF, &IFKey);
if (! NT_SUCCESS(Status))
return STATUS_INVALID_PARAMETER_1;
if (Query->RegistryIndex == (uint)-1) {
//
// Persistent query via prefix & next-hop.
//
Status = OpenRouteRegKey(IFKey,
&Query->Prefix, Query->PrefixLength,
&Query->Neighbor,
RouteKey, OpenRegKeyRead);
}
else {
HANDLE RoutesKey;
//
// Open the Routes subkey.
//
Status = OpenRegKey(&RoutesKey, IFKey,
L"Routes", OpenRegKeyRead);
if (NT_SUCCESS(Status)) {
struct {
IPv6Addr *Prefix;
uint *PrefixLength;
IPv6Addr *Neighbor;
HANDLE *RouteKey;
OpenRegKeyAction Action;
} Args;
//
// Persistent query via registry index.
//
Args.Prefix = &Query->Prefix;
Args.PrefixLength = &Query->PrefixLength;
Args.Neighbor = &Query->Neighbor;
Args.RouteKey = RouteKey;
Args.Action = OpenRegKeyRead;
Status = EnumRegKeyIndex(RoutesKey,
Query->RegistryIndex,
EnumPersistentRoute,
&Args);
ZwClose(RoutesKey);
}
else {
//
// If the Routes subkey is not present,
// then the index is not present.
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
Status = STATUS_NO_MORE_ENTRIES;
}
}
ZwClose(IFKey);
return Status;
}
//* ReadPersistentRoute
//
// Reads route attributes from the registry key.
// Initializes all the fields except This.
//
void
ReadPersistentRoute(
HANDLE RouteKey,
IPV6_INFO_ROUTE_TABLE *Info)
{
//
// Read the route preference.
//
InitRegDWORDParameter(RouteKey, L"Preference",
&Info->Preference, ROUTE_PREF_HIGHEST);
//
// Read the site prefix length.
//
InitRegDWORDParameter(RouteKey, L"SitePrefixLength",
&Info->SitePrefixLength, 0);
//
// Read the Publish flag.
//
InitRegDWORDParameter(RouteKey, L"Publish",
&Info->Publish, FALSE);
//
// Read the Immortal flag.
//
InitRegDWORDParameter(RouteKey, L"Immortal",
&Info->Immortal, FALSE);
//
// Read the lifetimes.
//
GetPersistentLifetimes(RouteKey, Info->Immortal,
&Info->ValidLifetime, &Info->PreferredLifetime);
//
// The route type is not persisted.
//
Info->Type = RTE_TYPE_MANUAL;
}
//* IoctlPersistentQueryRouteTable
//
// Processes an IOCTL_IPV6_PERSISTENT_QUERY_ROUTE_TABLE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlPersistentQueryRouteTable(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_PERSISTENT_QUERY_ROUTE_TABLE *Query;
IPV6_INFO_ROUTE_TABLE *Info;
IPV6_QUERY_ROUTE_TABLE This;
HANDLE RouteKey;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->This structures overlap!
//
Query = (IPV6_PERSISTENT_QUERY_ROUTE_TABLE *)
Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_ROUTE_TABLE *)
Irp->AssociatedIrp.SystemBuffer;
//
// Get the registry key for the specified route.
//
Status = FindPersistentRouteFromQuery(Query, &RouteKey);
if (! NT_SUCCESS(Status))
goto Return;
//
// The interface index is not returned for persistent queries.
//
This.Neighbor.IF.Index = 0;
This.Neighbor.IF.Guid = Query->IF.Guid;
This.Neighbor.Address = Query->Neighbor;
This.Prefix = Query->Prefix;
This.PrefixLength = Query->PrefixLength;
Info->This = This;
//
// Read route information from the registry key.
//
ReadPersistentRoute(RouteKey, Info);
ZwClose(RouteKey);
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof *Info;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlPersistentQueryRouteTable
//* InternalUpdateRouteTable
//
// Common helper function for IoctlUpdateRouteTable
// and CreatePersistentRoute, consolidating
// parameter validation in one place.
//
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
// Return codes:
// STATUS_INVALID_PARAMETER_1 Bad Interface.
// STATUS_INVALID_PARAMETER_2 Bad Neighbor.
// STATUS_INVALID_PARAMETER_3 Bad PrefixLength.
// STATUS_INVALID_PARAMETER_4 Bad PreferredLifetime.
// STATUS_INVALID_PARAMETER_5 Bad Preference.
// STATUS_INVALID_PARAMETER_6 Bad Type.
// STATUS_INVALID_PARAMETER_7 Bad Prefix.
// STATUS_INSUFFICIENT_RESOURCES No pool.
// STATUS_ACCESS_DENIED Invalid system route update.
//
NTSTATUS
InternalUpdateRouteTable(
FILE_OBJECT *FileObject,
Interface *IF,
IPV6_INFO_ROUTE_TABLE *Info)
{
NeighborCacheEntry *NCE;
uint ValidLifetime;
uint PreferredLifetime;
NTSTATUS Status;
PAGED_CODE();
//
// Convert the lifetime from seconds to ticks.
//
ValidLifetime = ConvertSecondsToTicks(Info->ValidLifetime);
PreferredLifetime = ConvertSecondsToTicks(Info->PreferredLifetime);
//
// Sanity check the arguments.
//
if ((Info->This.PrefixLength > IPV6_ADDRESS_LENGTH) ||
(Info->SitePrefixLength > Info->This.PrefixLength))
return STATUS_INVALID_PARAMETER_3;
if (PreferredLifetime > ValidLifetime)
return STATUS_INVALID_PARAMETER_4;
if (! IsValidPreference(Info->Preference))
return STATUS_INVALID_PARAMETER_5;
if (! IsValidRouteTableType(Info->Type))
return STATUS_INVALID_PARAMETER_6;
if ((IsLinkLocal(&Info->This.Prefix) && Info->Publish) ||
(IsMulticast(&Info->This.Prefix) && Info->Publish) ||
(IsSiteLocal(&Info->This.Prefix) && (Info->SitePrefixLength != 0)))
return STATUS_INVALID_PARAMETER_7;
if (IsUnspecified(&Info->This.Neighbor.Address)) {
//
// The prefix is on-link.
//
NCE = NULL;
}
else {
//
// REVIEW - Sanity check that the specified neighbor address
// is reasonably on-link to the specified interface?
// Perhaps only allow link-local next-hop addresses,
// and other next-hops would imply recursive routing lookups?
//
if (IsInvalidSourceAddress(&Info->This.Neighbor.Address) ||
IsLoopback(&Info->This.Neighbor.Address)) {
return STATUS_INVALID_PARAMETER_2;
}
//
// Find or create the specified neighbor.
//
NCE = FindOrCreateNeighbor(IF, &Info->This.Neighbor.Address);
if (NCE == NULL)
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Create/update the specified route.
//
Status = RouteTableUpdate(FileObject,
IF, NCE,
&Info->This.Prefix,
Info->This.PrefixLength,
Info->SitePrefixLength,
ValidLifetime, PreferredLifetime,
Info->Preference,
Info->Type,
Info->Publish, Info->Immortal);
if (NCE != NULL)
ReleaseNCE(NCE);
return Status;
}
//* CreatePersistentRoute
//
// Creates a persistent route on an interface.
//
// SubKeyName has the following syntax:
// prefix/length->neighbor
// where prefix and neighbor are literal IPv6 addresses.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
CreatePersistentRoute(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
Interface *IF = (Interface *) Context;
IPV6_INFO_ROUTE_TABLE Info;
HANDLE RouteKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the route key. We might want to delete it.
//
Status = OpenPersistentRoute(ParentKey, SubKeyName,
&Info.This.Prefix,
&Info.This.PrefixLength,
&Info.This.Neighbor.Address,
&RouteKey,
OpenRegKeyDeleting);
if (! NT_SUCCESS(Status)) {
//
// Could not open the route key.
// But we return success so the enumeration continues.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentRoute(IF %u/%p %ls): bad key %ls\n",
IF->Index, IF, IF->DeviceName.Buffer, SubKeyName));
return STATUS_SUCCESS;
}
//
// Read route attributes.
//
ReadPersistentRoute(RouteKey, &Info);
//
// Create the route.
//
Status = InternalUpdateRouteTable(NULL, IF, &Info);
if (! NT_SUCCESS(Status)) {
if ((STATUS_INVALID_PARAMETER_1 <= Status) &&
(Status <= STATUS_INVALID_PARAMETER_12)) {
//
// Invalid parameter.
// But we return success so the enumeration continues.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentRoute(IF %u/%p %ls): bad param %ls\n",
IF->Index, IF, IF->DeviceName.Buffer, SubKeyName));
Status = STATUS_SUCCESS;
}
else {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
"CreatePersistentRoute(IF %u/%p %ls): error %ls\n",
IF->Index, IF, IF->DeviceName.Buffer, SubKeyName));
}
}
else {
//
// If the route lifetime in the registry has expired,
// so that the persistent route is now stale,
// remove it from the registry.
//
if ((Info.ValidLifetime == 0) && !Info.Publish)
(void) ZwDeleteKey(RouteKey);
}
ZwClose(RouteKey);
return Status;
}
//* PersistUpdateRouteTable
//
// Helper function for persisting route information in the registry.
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistUpdateRouteTable(
Interface *IF,
IPV6_INFO_ROUTE_TABLE *Info)
{
HANDLE IFKey;
HANDLE RouteKey;
NTSTATUS Status;
PAGED_CODE();
//
// For persistent routes, we have some extra restrictions.
//
if (Info->Type != RTE_TYPE_MANUAL)
return STATUS_CANNOT_MAKE;
//
// Open/create the interface key.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey,
OpenRegKeyCreate);
if (! NT_SUCCESS(Status))
return Status;
//
// Open/create the route key.
//
Status = OpenRouteRegKey(IFKey,
&Info->This.Prefix,
Info->This.PrefixLength,
&Info->This.Neighbor.Address,
&RouteKey, OpenRegKeyCreate);
ZwClose(IFKey);
if (! NT_SUCCESS(Status))
return Status;
//
// Persist the route preference.
//
Status = SetRegDWORDValue(RouteKey, L"Preference",
Info->Preference);
if (! NT_SUCCESS(Status))
goto ReturnReleaseRouteKey;
//
// Persist the site prefix length.
//
Status = SetRegDWORDValue(RouteKey, L"SitePrefixLength",
Info->SitePrefixLength);
if (! NT_SUCCESS(Status))
goto ReturnReleaseRouteKey;
//
// Persist the Publish flag.
//
Status = SetRegDWORDValue(RouteKey, L"Publish", Info->Publish);
if (! NT_SUCCESS(Status))
goto ReturnReleaseRouteKey;
//
// Persist the Immortal flag.
//
Status = SetRegDWORDValue(RouteKey, L"Immortal", Info->Immortal);
if (! NT_SUCCESS(Status))
goto ReturnReleaseRouteKey;
//
// Persist the lifetimes.
//
Status = SetPersistentLifetimes(RouteKey, Info->Immortal,
Info->ValidLifetime,
Info->PreferredLifetime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseRouteKey;
Status = STATUS_SUCCESS;
ReturnReleaseRouteKey:
ZwClose(RouteKey);
return Status;
}
//* PersistDeleteRouteTable
//
// Helper function for deleting route information from the registry.
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistDeleteRouteTable(
Interface *IF,
IPV6_INFO_ROUTE_TABLE *Info)
{
HANDLE IFKey;
HANDLE RouteKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the interface key. It's OK if it doesn't exist.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey,
OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
return STATUS_SUCCESS;
else
return Status;
}
//
// Open the route key. It's OK if it doesn't exist.
//
Status = OpenRouteRegKey(IFKey,
&Info->This.Prefix,
Info->This.PrefixLength,
&Info->This.Neighbor.Address,
&RouteKey, OpenRegKeyDeleting);
ZwClose(IFKey);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
return STATUS_SUCCESS;
else
return Status;
}
//
// Delete the route key.
//
Status = ZwDeleteKey(RouteKey);
ZwClose(RouteKey);
return Status;
}
//* IoctlUpdateRouteTable
//
// Processes an IOCTL_IPV6_UPDATE_ROUTE_TABLE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdateRouteTable(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_INFO_ROUTE_TABLE *Info;
Interface *IF = NULL;
NeighborCacheEntry *NCE;
uint ValidLifetime;
uint PreferredLifetime;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Info) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_INFO_ROUTE_TABLE *) Irp->AssociatedIrp.SystemBuffer;
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(&Info->This.Neighbor.IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// Update the routing table.
//
Status = InternalUpdateRouteTable(IrpSp->FileObject, IF, Info);
if (! NT_SUCCESS(Status))
goto ReturnReleaseIF;
//
// Make the change persistent?
// This needs to happen after updating the running data structures,
// to ensure that the change is correct before persisting it.
//
if (Persistent) {
//
// If the lifetime is zero and the route is not published,
// then the route should be deleted. Otherwise we create the key.
//
if ((Info->ValidLifetime == 0) && !Info->Publish)
Status = PersistDeleteRouteTable(IF, Info);
else
Status = PersistUpdateRouteTable(IF, Info);
if (! NT_SUCCESS(Status))
goto ReturnReleaseIF;
}
Status = STATUS_SUCCESS;
ReturnReleaseIF:
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdateRouteTable
//* InternalUpdateAddress
//
// Common helper function for IoctlUpdateAddress
// and CreatePersistentAddr, consolidating
// parameter validation in one place.
//
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
// Return codes:
// STATUS_INVALID_PARAMETER_2 Bad lifetime.
// STATUS_INVALID_PARAMETER_3 Bad address.
// STATUS_INVALID_PARAMETER_4 Bad type.
// STATUS_INVALID_PARAMETER_5 Bad prefix origin.
// STATUS_INVALID_PARAMETER_6 Bad interface id origin.
// STATUS_UNSUCCESSFUL Failure.
//
NTSTATUS
InternalUpdateAddress(
Interface *IF,
IPV6_UPDATE_ADDRESS *Info)
{
uint ValidLifetime;
uint PreferredLifetime;
struct AddrConfEntry AddrConf;
int rc;
//
// Convert the lifetime from seconds to ticks.
//
ValidLifetime = ConvertSecondsToTicks(Info->ValidLifetime);
PreferredLifetime = ConvertSecondsToTicks(Info->PreferredLifetime);
if (PreferredLifetime > ValidLifetime)
return STATUS_INVALID_PARAMETER_2;
//
// Sanity check the address.
//
if (IsNotManualAddress(&Info->This.Address))
return STATUS_INVALID_PARAMETER_3;
AddrConf.PrefixConf = (uchar)Info->PrefixConf;
AddrConf.InterfaceIdConf = (uchar)Info->InterfaceIdConf;
//
// We only support unicast and anycast addresses here.
// Use the socket apis to join a multicast address.
//
if (Info->Type == ADE_UNICAST) {
if (IsKnownAnycast(&Info->This.Address))
return STATUS_INVALID_PARAMETER_3;
if (! IsValidPrefixConfValue(Info->PrefixConf))
return STATUS_INVALID_PARAMETER_5;
if (! IsValidInterfaceIdConfValue(Info->InterfaceIdConf))
return STATUS_INVALID_PARAMETER_6;
if (AddrConf.Value == ADDR_CONF_ANONYMOUS)
return STATUS_INVALID_PARAMETER_6;
}
else if (Info->Type == ADE_ANYCAST) {
if ((ValidLifetime != PreferredLifetime) ||
((ValidLifetime != 0) &&
(ValidLifetime != INFINITE_LIFETIME)))
return STATUS_INVALID_PARAMETER_2;
if (Info->PrefixConf != PREFIX_CONF_MANUAL)
return STATUS_INVALID_PARAMETER_5;
if (Info->InterfaceIdConf != IID_CONF_MANUAL)
return STATUS_INVALID_PARAMETER_6;
}
else {
return STATUS_INVALID_PARAMETER_4;
}
//
// Create/update/delete the address.
//
if (Info->Type == ADE_ANYCAST) {
if (Info->ValidLifetime == 0)
rc = FindAndDeleteAAE(IF, &Info->This.Address);
else
rc = FindOrCreateAAE(IF, &Info->This.Address, NULL);
}
else {
rc = FindOrCreateNTE(IF, &Info->This.Address, AddrConf.Value,
ValidLifetime, PreferredLifetime);
}
if (rc)
return STATUS_SUCCESS;
else
return STATUS_UNSUCCESSFUL;
}
//* CreatePersistentAddr
//
// Given the name of a persistent address,
// creates the address on an interface.
//
// SubKeyName is a literal IPv6 address.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
CreatePersistentAddr(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
Interface *IF = (Interface *) Context;
IPV6_UPDATE_ADDRESS Info;
int Preferred;
HANDLE AddrKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the address key. We might want to delete it.
//
Status = OpenPersistentAddress(ParentKey, SubKeyName,
&Info.This.Address,
&AddrKey,
OpenRegKeyDeleting);
if (! NT_SUCCESS(Status)) {
//
// Could not open the address key.
// But we return success so the enumeration continues.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentAddr(IF %u/%p %ls): bad key %ls\n",
IF->Index, IF, IF->DeviceName.Buffer, SubKeyName));
return STATUS_SUCCESS;
}
//
// Read address attributes.
//
ReadPersistentAddress(AddrKey, &Info);
//
// Create the address.
//
Status = InternalUpdateAddress(IF, &Info);
if (! NT_SUCCESS(Status)) {
if ((STATUS_INVALID_PARAMETER_1 <= Status) &&
(Status <= STATUS_INVALID_PARAMETER_12)) {
//
// Invalid parameter.
// But we return success so the enumeration continues.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentAddr(IF %u/%p %ls): bad param %ls\n",
IF->Index, IF, IF->DeviceName.Buffer, SubKeyName));
Status = STATUS_SUCCESS;
}
else {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
"CreatePersistentAddr(IF %u/%p %ls): error %ls\n",
IF->Index, IF, IF->DeviceName.Buffer, SubKeyName));
}
}
else {
//
// If the address lifetime in the registry has expired,
// so that the persistent address is now stale,
// remove it from the registry.
//
if (Info.ValidLifetime == 0)
(void) ZwDeleteKey(AddrKey);
}
ZwClose(AddrKey);
return Status;
}
//* PersistUpdateAddress
//
// Helper function for persisting an address in the registry.
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistUpdateAddress(
Interface *IF,
IPV6_UPDATE_ADDRESS *Info)
{
HANDLE IFKey;
HANDLE AddrKey;
NTSTATUS Status;
PAGED_CODE();
//
// For persistent addresses, we have extra restrictions.
//
if ((Info->PrefixConf != PREFIX_CONF_MANUAL) ||
(Info->InterfaceIdConf != IID_CONF_MANUAL))
return STATUS_CANNOT_MAKE;
//
// Open/create the interface key.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey,
OpenRegKeyCreate);
if (! NT_SUCCESS(Status))
return Status;
//
// Open/create the address key.
//
Status = OpenAddressRegKey(IFKey, &Info->This.Address,
&AddrKey, OpenRegKeyCreate);
ZwClose(IFKey);
if (! NT_SUCCESS(Status))
return Status;
//
// Persist the address type.
//
Status = SetRegDWORDValue(AddrKey, L"Type", Info->Type);
if (! NT_SUCCESS(Status))
goto ReturnReleaseAddrKey;
//
// Persist the address lifetimes.
//
Status = SetPersistentLifetimes(AddrKey, FALSE,
Info->ValidLifetime,
Info->PreferredLifetime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseAddrKey;
Status = STATUS_SUCCESS;
ReturnReleaseAddrKey:
ZwClose(AddrKey);
return Status;
}
//* PersistDeleteAddress
//
// Helper function for deleting an address from the registry.
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistDeleteAddress(
Interface *IF,
IPV6_UPDATE_ADDRESS *Info)
{
HANDLE IFKey;
HANDLE AddrKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the interface key. It's OK if it doesn't exist.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey,
OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
return STATUS_SUCCESS;
else
return Status;
}
//
// Open the address key. It's OK if it doesn't exist.
//
Status = OpenAddressRegKey(IFKey, &Info->This.Address,
&AddrKey, OpenRegKeyDeleting);
ZwClose(IFKey);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
return STATUS_SUCCESS;
else
return Status;
}
//
// Delete the address key.
//
Status = ZwDeleteKey(AddrKey);
ZwClose(AddrKey);
return Status;
}
//* IoctlUpdateAddress
//
// Processes an IOCTL_IPV6_UPDATE_ADDRESS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdateAddress(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_UPDATE_ADDRESS *Info;
Interface *IF;
NTSTATUS Status;
PAGED_CODE();
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Info) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_UPDATE_ADDRESS *) Irp->AssociatedIrp.SystemBuffer;
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(&Info->This.IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// Update the address on the interface.
//
Status = InternalUpdateAddress(IF, Info);
if (! NT_SUCCESS(Status))
goto ReturnReleaseIF;
//
// Make the change persistent?
// This needs to happen after updating the running data structures,
// to ensure that the change is correct before persisting it.
//
if (Persistent) {
//
// If the lifetime is zero, we delete the address's key.
// Otherwise the lifetime is infinite and we create the key.
//
if (Info->ValidLifetime == 0)
Status = PersistDeleteAddress(IF, Info);
else
Status = PersistUpdateAddress(IF, Info);
if (! NT_SUCCESS(Status))
goto ReturnReleaseIF;
}
Status = STATUS_SUCCESS;
ReturnReleaseIF:
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdateAddress
//* IoctlQueryBindingCache
//
// Processes an IOCTL_IPV6_QUERY_BINDING_CACHE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryBindingCache(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_BINDING_CACHE *Query;
IPV6_INFO_BINDING_CACHE *Info;
BindingCacheEntry *BCE;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Query structures overlap!
//
Query = (IPV6_QUERY_BINDING_CACHE *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_BINDING_CACHE *) Irp->AssociatedIrp.SystemBuffer;
if (IsUnspecified(&Query->HomeAddress)) {
//
// Return the home address of the first BCE.
//
KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
if (BindingCache.First != SentinelBCE) {
Info->Query.HomeAddress = BindingCache.First->HomeAddr;
}
KeReleaseSpinLock(&RouteCacheLock, OldIrql);
Irp->IoStatus.Information = sizeof Info->Query;
} else {
//
// Find the specified BCE.
//
KeAcquireSpinLock(&RouteCacheLock, &OldIrql);
for (BCE = BindingCache.First; ; BCE = BCE->Next) {
if (BCE == SentinelBCE) {
KeReleaseSpinLock(&RouteCacheLock, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
if (IP6_ADDR_EQUAL(&Query->HomeAddress, &BCE->HomeAddr))
break;
}
//
// Return misc. information about the BCE.
//
Info->HomeAddress = BCE->HomeAddr;
Info->CareOfAddress = BCE->CareOfRCE->Destination;
Info->BindingSeqNumber = BCE->BindingSeqNumber;
Info->BindingLifetime = ConvertTicksToSeconds(BCE->BindingLifetime);
//
// Return home address of the next BCE (or Unspecified).
//
if (BCE->Next == SentinelBCE) {
Info->Query.HomeAddress = UnspecifiedAddr;
} else {
Info->Query.HomeAddress = BCE->Next->HomeAddr;
}
KeReleaseSpinLock(&RouteCacheLock, OldIrql);
Irp->IoStatus.Information = sizeof *Info;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryBindingCache
//* InternalCreateInterface
//
// Common helper function for IoctlCreateInterface
// and CreatePersistentInterface, consolidating
// parameter validation in one place.
//
// Callable from thread context, not DPC context.
//
// Return codes:
// STATUS_INVALID_PARAMETER_1 Bad Type.
// STATUS_INVALID_PARAMETER_2 Bad Flags.
// STATUS_INVALID_PARAMETER_3 Bad SrcAddr.
// STATUS_INVALID_PARAMETER_4 Bad DstAddr.
// STATUS_ADDRESS_ALREADY_EXISTS The interface already exists.
// STATUS_INSUFFICIENT_RESOURCES
// STATUS_UNSUCCESSFUL
// STATUS_SUCCESS
//
NTSTATUS
InternalCreateInterface(
IPV6_INFO_INTERFACE *Info,
Interface **ReturnIF)
{
IPAddr SrcAddr, DstAddr;
int RouterDiscovers = Info->RouterDiscovers;
int NeighborDiscovers = Info->NeighborDiscovers;
int PeriodicMLD = Info->PeriodicMLD;
uint Flags;
if (Info->LinkLayerAddressLength != sizeof(IPAddr))
return STATUS_INVALID_PARAMETER_1;
switch (Info->Type) {
case IF_TYPE_TUNNEL_V6V4:
//
// Set default values.
//
if (RouterDiscovers == -1)
RouterDiscovers = FALSE;
if (NeighborDiscovers == -1)
NeighborDiscovers = FALSE;
if (PeriodicMLD == -1)
PeriodicMLD = FALSE;
//
// For now, require the ND and RD flags to be set the same.
// Setting them differently should work, but it's not an important
// scenario at the moment, and it would be more work to test.
// This check can be removed in the future if desired.
//
if (NeighborDiscovers != RouterDiscovers)
return STATUS_INVALID_PARAMETER_2;
if (Info->LocalLinkLayerAddress == 0)
return STATUS_INVALID_PARAMETER_3;
if (Info->RemoteLinkLayerAddress == 0)
return STATUS_INVALID_PARAMETER_4;
SrcAddr = * (IPAddr UNALIGNED *)
((char *)Info + Info->LocalLinkLayerAddress);
DstAddr = * (IPAddr UNALIGNED *)
((char *)Info + Info->RemoteLinkLayerAddress);
break;
case IF_TYPE_TUNNEL_6OVER4:
//
// Set default values.
//
if (RouterDiscovers == -1)
RouterDiscovers = TRUE;
if (NeighborDiscovers == -1)
NeighborDiscovers = TRUE;
if (PeriodicMLD == -1)
PeriodicMLD = FALSE;
//
// For now, require the RD flag to be set in addition to ND.
// PeriodicMLD is not allowed.
//
if (!RouterDiscovers || !NeighborDiscovers || PeriodicMLD)
return STATUS_INVALID_PARAMETER_2;
if (Info->LocalLinkLayerAddress == 0)
return STATUS_INVALID_PARAMETER_3;
if (Info->RemoteLinkLayerAddress != 0)
return STATUS_INVALID_PARAMETER_4;
SrcAddr = * (IPAddr UNALIGNED *)
((char *)Info + Info->LocalLinkLayerAddress);
DstAddr = 0;
break;
default:
return STATUS_INVALID_PARAMETER_1;
}
Flags = ((RouterDiscovers ? IF_FLAG_ROUTER_DISCOVERS : 0) |
(NeighborDiscovers ? IF_FLAG_NEIGHBOR_DISCOVERS : 0) |
(PeriodicMLD ? IF_FLAG_PERIODICMLD : 0));
return TunnelCreateTunnel(SrcAddr, DstAddr, Flags, ReturnIF);
}
//* CreatePersistentInterface
//
// Creates a persistent interface.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
CreatePersistentInterface(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
struct {
IPV6_INFO_INTERFACE Info;
IPAddr SrcAddr;
IPAddr DstAddr;
} Create;
HANDLE IFKey;
Interface *IF;
WCHAR *InterfaceName;
NTSTATUS Status;
UNREFERENCED_PARAMETER(Context);
PAGED_CODE();
//
// Open the interface key.
//
Status = OpenPersistentInterface(ParentKey, SubKeyName,
&Create.Info.This.Guid,
&IFKey, OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
//
// Could not open the interface key.
// But we return success so the enumeration continues.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentInterface: bad key %ls\n",
SubKeyName));
return STATUS_SUCCESS;
}
//
// Let ReadPersistentInterface know how much space is available
// for link-layer addresses.
//
Create.Info.Length = sizeof Create - sizeof Create.Info;
//
// Read interface attributes.
//
Status = ReadPersistentInterface(IFKey, &Create.Info);
ZwClose(IFKey);
if (! NT_SUCCESS(Status)) {
//
// Could not read the interface key.
// But we return success so the enumeration continues.
//
goto InvalidParameter;
}
//
// Should we create an interface?
//
if (Create.Info.Type == (uint)-1)
return STATUS_SUCCESS;
//
// Create the persistent interface.
//
Status = InternalCreateInterface(&Create.Info, &IF);
if (! NT_SUCCESS(Status)) {
if (((STATUS_INVALID_PARAMETER_1 <= Status) &&
(Status <= STATUS_INVALID_PARAMETER_12)) ||
(Status == STATUS_ADDRESS_ALREADY_EXISTS)) {
//
// Invalid parameter.
// But we return success so the enumeration continues.
//
InvalidParameter:
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentInterface: bad param %ls\n",
SubKeyName));
return STATUS_SUCCESS;
}
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
"CreatePersistentInterface: error %ls\n",
SubKeyName));
return Status;
}
//
// Consistency check. This is not an assertion because
// someone editing the registry can make this fail.
//
InterfaceName = (WCHAR *)IF->DeviceName.Buffer +
(sizeof IPV6_EXPORT_STRING_PREFIX / sizeof(WCHAR)) - 1;
if (wcscmp(SubKeyName, InterfaceName) != 0) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentInterface: inconsistency %ls IF %u/%p\n",
SubKeyName, IF->Index, IF));
}
ReleaseIF(IF);
return STATUS_SUCCESS;
}
//* ConfigurePersistentInterfaces
//
// Configures persistent interfaces from the registry.
//
// Callable from thread context, not DPC context.
//
void
ConfigurePersistentInterfaces(void)
{
HANDLE RegKey;
NTSTATUS Status;
//
// Create persistent interfaces.
//
Status = OpenTopLevelRegKey(L"Interfaces", &RegKey, OpenRegKeyRead);
if (NT_SUCCESS(Status)) {
(void) EnumRegKeys(RegKey, CreatePersistentInterface, NULL);
ZwClose(RegKey);
}
}
//* PersistCreateInterface
//
// Helper function for persisting an interface in the registry.
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistCreateInterface(
Interface *IF,
IPV6_INFO_INTERFACE *Info)
{
HANDLE IFKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open/create the interface key.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey,
OpenRegKeyCreate);
if (! NT_SUCCESS(Status))
return Status;
//
// Persist the interface type.
//
Status = SetRegDWORDValue(IFKey, L"Type", Info->Type);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
//
// Persist the interface flags.
//
if (Info->RouterDiscovers != -1) {
Status = SetRegDWORDValue(IFKey, L"RouterDiscovers",
Info->RouterDiscovers);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->NeighborDiscovers != -1) {
Status = SetRegDWORDValue(IFKey, L"NeighborDiscovers",
Info->NeighborDiscovers);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->PeriodicMLD != -1) {
Status = SetRegDWORDValue(IFKey, L"PeriodicMLD",
Info->PeriodicMLD);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
switch (Info->Type) {
case IF_TYPE_TUNNEL_6OVER4: {
IPAddr SrcAddr = * (IPAddr UNALIGNED *)
((char *)Info + Info->LocalLinkLayerAddress);
//
// Persist the source address.
//
Status = SetRegIPAddrValue(IFKey, L"SrcAddr", SrcAddr);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
break;
}
case IF_TYPE_TUNNEL_V6V4: {
IPAddr SrcAddr = * (IPAddr UNALIGNED *)
((char *)Info + Info->LocalLinkLayerAddress);
IPAddr DstAddr = * (IPAddr UNALIGNED *)
((char *)Info + Info->RemoteLinkLayerAddress);
//
// Persist the source address.
//
Status = SetRegIPAddrValue(IFKey, L"SrcAddr", SrcAddr);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
//
// Persist the destination address.
//
Status = SetRegIPAddrValue(IFKey, L"DstAddr", DstAddr);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
break;
}
}
Status = STATUS_SUCCESS;
ReturnReleaseKey:
ZwClose(IFKey);
return Status;
}
//* IoctlCreateInterface
//
// Processes an IOCTL_IPV6_CREATE_INTERFACE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlCreateInterface(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_INFO_INTERFACE *Info;
IPV6_QUERY_INTERFACE *Result;
Interface *IF;
NTSTATUS Status;
PAGED_CODE();
//
// Initialize now for error paths.
//
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof *Info) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Result)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_INFO_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
Result = (IPV6_QUERY_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
//
// Check that the structure and link-layer addresses, if supplied,
// fit in the buffer. Watch out for addition overflow.
//
if ((Info->Length < sizeof *Info) ||
(Info->Length > IrpSp->Parameters.DeviceIoControl.InputBufferLength) ||
((Info->LocalLinkLayerAddress != 0) &&
(((Info->LocalLinkLayerAddress + Info->LinkLayerAddressLength) >
IrpSp->Parameters.DeviceIoControl.InputBufferLength) ||
((Info->LocalLinkLayerAddress + Info->LinkLayerAddressLength) <
Info->LocalLinkLayerAddress))) ||
((Info->RemoteLinkLayerAddress != 0) &&
(((Info->RemoteLinkLayerAddress + Info->LinkLayerAddressLength) >
IrpSp->Parameters.DeviceIoControl.InputBufferLength) ||
((Info->RemoteLinkLayerAddress + Info->LinkLayerAddressLength) <
Info->RemoteLinkLayerAddress)))) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Create the interface.
//
Status = InternalCreateInterface(Info, &IF);
if (! NT_SUCCESS(Status))
goto Return;
//
// Make the change persistent?
// This needs to happen after updating the running data structures,
// to ensure that the change is correct before persisting it.
//
if (Persistent) {
Status = PersistCreateInterface(IF, Info);
if (! NT_SUCCESS(Status))
goto ReturnReleaseIF;
}
//
// Return query information for the new interface.
//
ReturnQueryInterface(IF, Result);
Irp->IoStatus.Information = sizeof *Result;
Status = STATUS_SUCCESS;
ReturnReleaseIF:
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlCreateInterface
//* AreIndicesSpecified
//
// Are there any non-zero zone indices in the array?
//
int
AreIndicesSpecified(uint ZoneIndices[ADE_NUM_SCOPES])
{
ushort Scope;
for (Scope = ADE_SMALLEST_SCOPE; Scope <= ADE_LARGEST_SCOPE; Scope++)
if (ZoneIndices[Scope] != 0)
return TRUE;
return FALSE;
}
//* CheckZoneIndices
//
// Checks consistency of a zone update,
// and fills in unspecified values.
// Returns FALSE if there is an inconsistency.
//
// The logic for filling in unspecified values makes it
// more convenient for a user to change zone indices.
// For example, an user can change an interface's site index
// and the subnet & admin indices will be automatically changed.
//
// Called with the global ZoneUpdateLock held.
//
int
CheckZoneIndices(Interface *IF, uint ZoneIndices[ADE_NUM_SCOPES])
{
Interface *OtherIF;
uint Scope, i;
//
// Zone indices 0 (ADE_SMALLEST_SCOPE) and 1 (ADE_INTERFACE_LOCAL)
// are special and must have the value IF->Index.
//
if (ZoneIndices[ADE_SMALLEST_SCOPE] == 0)
ZoneIndices[ADE_SMALLEST_SCOPE] = IF->Index;
else if (ZoneIndices[ADE_SMALLEST_SCOPE] != IF->Index)
return FALSE;
if (ZoneIndices[ADE_INTERFACE_LOCAL] == 0)
ZoneIndices[ADE_INTERFACE_LOCAL] = IF->Index;
else if (ZoneIndices[ADE_INTERFACE_LOCAL] != IF->Index)
return FALSE;
//
// Zone indices 14 (ADE_GLOBAL) and 15 (ADE_LARGEST_SCOPE) are special
// and must have the value one.
//
if (ZoneIndices[ADE_GLOBAL] == 0)
ZoneIndices[ADE_GLOBAL] = 1;
else if (ZoneIndices[ADE_GLOBAL] != 1)
return FALSE;
if (ZoneIndices[ADE_LARGEST_SCOPE] == 0)
ZoneIndices[ADE_LARGEST_SCOPE] = 1;
else if (ZoneIndices[ADE_LARGEST_SCOPE] != 1)
return FALSE;
for (Scope = ADE_LINK_LOCAL; Scope < ADE_GLOBAL; Scope++) {
if (ZoneIndices[Scope] == 0) {
//
// The user did not specify the zone index for this scope.
// If leaving the current zone index unchanged works,
// then we prefer to do that. However, the user may be changing
// the zone index for a larger scope. If necessary
// for consistency, then we use a new zone index at this scope.
//
for (i = Scope+1; i < ADE_GLOBAL; i++) {
if (ZoneIndices[i] != 0) {
//
// If we use the current value at level Scope,
// would it cause an inconsistency at level i?
//
OtherIF = FindInterfaceFromZone(IF,
Scope, IF->ZoneIndices[Scope]);
if (OtherIF != NULL) {
if (OtherIF->ZoneIndices[i] != ZoneIndices[i]) {
Interface *ExistingIF;
//
// Yes. We need a different zone index.
// Is there an existing one that we can reuse?
//
ExistingIF = FindInterfaceFromZone(IF,
i, ZoneIndices[i]);
if (ExistingIF != NULL) {
//
// Yes, reuse the existing zone index.
//
ZoneIndices[Scope] = ExistingIF->ZoneIndices[Scope];
ReleaseIF(ExistingIF);
}
else {
//
// No, we need a new zone index.
//
ZoneIndices[Scope] = FindNewZoneIndex(Scope);
}
}
ReleaseIF(OtherIF);
}
break;
}
}
if (ZoneIndices[Scope] == 0) {
//
// Use the current value from the interface.
//
ZoneIndices[Scope] = IF->ZoneIndices[Scope];
}
}
OtherIF = FindInterfaceFromZone(IF, Scope, ZoneIndices[Scope]);
if (OtherIF != NULL) {
//
// Enforce the zone containment invariant.
//
while (++Scope < ADE_GLOBAL) {
if (ZoneIndices[Scope] == 0)
ZoneIndices[Scope] = OtherIF->ZoneIndices[Scope];
else if (ZoneIndices[Scope] != OtherIF->ZoneIndices[Scope]) {
ReleaseIF(OtherIF);
return FALSE;
}
}
ReleaseIF(OtherIF);
return TRUE;
}
}
return TRUE;
}
//* InternalUpdateInterface
//
// Common helper function for IoctlUpdateInterface
// and ConfigureInterface, consolidating
// parameter validation in one place.
//
// The IF argument supercedes Info->This.IF.
// Does not implement Info->Renew.
//
// Callable from thread context, not DPC context.
//
// Return codes:
// STATUS_INVALID_PARAMETER_1 Bad Interface.
// STATUS_INVALID_PARAMETER_2 Bad Preference.
// STATUS_INVALID_PARAMETER_3 Bad LinkMTU.
// STATUS_INVALID_PARAMETER_4 Bad BaseReachableTime.
// STATUS_INVALID_PARAMETER_5 Bad CurHopLimit.
// STATUS_INSUFFICIENT_RESOURCES
// STATUS_SUCCESS
//
NTSTATUS
InternalUpdateInterface(
Interface *IF,
IPV6_INFO_INTERFACE *Info)
{
KIRQL OldIrql;
NTSTATUS Status;
if ((Info->Preference != (uint)-1) &&
! IsValidPreference(Info->Preference))
return STATUS_INVALID_PARAMETER_2;
if ((Info->LinkMTU != 0) &&
! ((IPv6_MINIMUM_MTU <= Info->LinkMTU) &&
(Info->LinkMTU <= IF->TrueLinkMTU)))
return STATUS_INVALID_PARAMETER_3;
if ((Info->BaseReachableTime != 0) &&
(Info->BaseReachableTime > MAX_REACHABLE_TIME))
return STATUS_INVALID_PARAMETER_4;
if ((Info->CurHopLimit != (uint)-1) &&
(Info->CurHopLimit >= 256))
return STATUS_INVALID_PARAMETER_5;
if (AreIndicesSpecified(Info->ZoneIndices)) {
//
// Fill in unspecified values in the ZoneIndices array
// and check for illegal values.
// The global lock ensures consistency across interfaces.
//
KeAcquireSpinLock(&ZoneUpdateLock, &OldIrql);
if (! CheckZoneIndices(IF, Info->ZoneIndices)) {
KeReleaseSpinLock(&ZoneUpdateLock, OldIrql);
return STATUS_INVALID_PARAMETER_3;
}
//
// Update the ZoneIndices.
//
RtlCopyMemory(IF->ZoneIndices, Info->ZoneIndices,
sizeof IF->ZoneIndices);
InvalidateRouteCache();
KeReleaseSpinLock(&ZoneUpdateLock, OldIrql);
}
//
// Update the forwarding and advertising attributes.
// We must update the advertising attribute before
// any auto-configured attributes, because
// InterfaceResetAutoConfig will reset them.
//
Status = UpdateInterface(IF, Info->Advertises, Info->Forwards);
if (! NT_SUCCESS(Status))
return Status;
//
// Update the link MTU.
//
if (Info->LinkMTU != 0)
UpdateLinkMTU(IF, Info->LinkMTU);
//
// Update the interface's routing preference.
//
if (Info->Preference != (uint)-1) {
//
// No lock needed.
//
IF->Preference = Info->Preference;
InvalidateRouteCache();
}
//
// Update the base reachable time.
//
if (Info->BaseReachableTime != 0) {
KeAcquireSpinLock(&IF->Lock, &OldIrql);
IF->BaseReachableTime = Info->BaseReachableTime;
IF->ReachableTime = CalcReachableTime(Info->BaseReachableTime);
KeReleaseSpinLock(&IF->Lock, OldIrql);
}
//
// Update the ND retransmission timer.
//
if (Info->RetransTimer != 0) {
//
// No lock needed.
//
IF->RetransTimer = ConvertMillisToTicks(Info->RetransTimer);
}
//
// Update the number of DAD transmissions.
//
if (Info->DupAddrDetectTransmits != (uint)-1) {
//
// No lock needed.
//
IF->DupAddrDetectTransmits = Info->DupAddrDetectTransmits;
}
//
// Update the default hop limit.
//
if (Info->CurHopLimit != (uint)-1) {
//
// No lock needed.
//
IF->CurHopLimit = Info->CurHopLimit;
}
return STATUS_SUCCESS;
}
//* ConfigureInterface
//
// Configures a newly-created interface from the registry.
// The interface has not yet been added to the global list,
// but it is otherwise fully initialized.
//
// Callable from thread context, not DPC context.
//
void
ConfigureInterface(Interface *IF)
{
IPV6_INFO_INTERFACE Info;
HANDLE IFKey;
HANDLE RegKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the interface key.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey, OpenRegKeyRead);
if (! NT_SUCCESS(Status))
return;
//
// Read interface attributes.
//
Info.Length = 0;
Status = ReadPersistentInterface(IFKey, &Info);
ASSERT(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_OVERFLOW));
//
// Update the interface.
//
Status = InternalUpdateInterface(IF, &Info);
if (! NT_SUCCESS(Status)) {
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"ConfigureInterface: bad params %x\n", Status));
}
//
// Create persistent addresses.
//
Status = OpenRegKey(&RegKey, IFKey, L"Addresses", OpenRegKeyRead);
if (NT_SUCCESS(Status)) {
(void) EnumRegKeys(RegKey, CreatePersistentAddr, IF);
ZwClose(RegKey);
}
//
// Create persistent routes.
//
Status = OpenRegKey(&RegKey, IFKey, L"Routes", OpenRegKeyRead);
if (NT_SUCCESS(Status)) {
(void) EnumRegKeys(RegKey, CreatePersistentRoute, IF);
ZwClose(RegKey);
}
InitRegDWORDParameter(IFKey, L"TcpInitialRTT",
&IF->TcpInitialRTT, 0);
ZwClose(IFKey);
}
//* PersistUpdateInterface
//
// Helper function for persisting interface attributes in the registry.
// The IF argument supercedes Info->This.IF.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistUpdateInterface(
Interface *IF,
IPV6_INFO_INTERFACE *Info)
{
HANDLE RegKey;
NTSTATUS Status;
PAGED_CODE();
Status = OpenInterfaceRegKey(&IF->Guid, &RegKey,
OpenRegKeyCreate);
if (! NT_SUCCESS(Status))
return Status;
if (Info->Advertises != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"Advertises",
Info->Advertises);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->Forwards != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"Forwards",
Info->Forwards);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->LinkMTU != 0) {
Status = SetRegDWORDValue(RegKey, L"LinkMTU",
Info->LinkMTU);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->Preference != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"Preference",
Info->Preference);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->BaseReachableTime != 0) {
Status = SetRegDWORDValue(RegKey, L"BaseReachableTime",
Info->BaseReachableTime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->RetransTimer != 0) {
Status = SetRegDWORDValue(RegKey, L"RetransTimer",
Info->RetransTimer);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->DupAddrDetectTransmits != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"DupAddrDetectTransmits",
Info->DupAddrDetectTransmits);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Info->CurHopLimit != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"CurHopLimit",
Info->CurHopLimit);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
Status = STATUS_SUCCESS;
ReturnReleaseKey:
ZwClose(RegKey);
return Status;
}
//* IoctlUpdateInterface
//
// Processes an IOCTL_IPV6_UPDATE_INTERFACE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdateInterface(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_INFO_INTERFACE *Info;
Interface *IF;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Info) {
Status = STATUS_INVALID_PARAMETER;
goto ErrorReturn;
}
Info = (IPV6_INFO_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(&Info->This);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto ErrorReturn;
}
//
// Validate parameters and update the interface.
//
Status = InternalUpdateInterface(IF, Info);
if (! NT_SUCCESS(Status))
goto ErrorReturnReleaseIF;
//
// Make the changes persistent?
//
if (Persistent) {
Status = PersistUpdateInterface(IF, Info);
if (! NT_SUCCESS(Status))
goto ErrorReturnReleaseIF;
}
Status = STATUS_SUCCESS;
ErrorReturnReleaseIF:
ReleaseIF(IF);
ErrorReturn:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdateInterface
//* PersistDeleteInterface
//
// Helper function for deleting an interface in the registry.
// We do not delete the interface key.
// Instead we just delete the Type value.
// This way persistent interface attributes (if any) remain.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistDeleteInterface(
Interface *IF)
{
HANDLE IFKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the interface key.
//
Status = OpenInterfaceRegKey(&IF->Guid, &IFKey, OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
return STATUS_SUCCESS;
else
return Status;
}
//
// Delete the Type value.
//
Status = RegDeleteValue(IFKey, L"Type");
ZwClose(IFKey);
return Status;
}
//* IoctlDeleteInterface
//
// Processes an IOCTL_IPV6_DELETE_INTERFACE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlDeleteInterface(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_QUERY_INTERFACE *Info;
Interface *IF;
NTSTATUS Status;
PAGED_CODE();
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Info) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_QUERY_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
//
// Can not delete some predefined interfaces.
// 6to4svc and other user-level things depend
// on these standard interfaces.
//
if (Info->Index <= 3) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(Info);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// This will disable the interface, so it will effectively
// disappear. When the last ref is gone it will be freed.
//
DestroyIF(IF);
//
// Make the changes persistent?
//
if (Persistent) {
Status = PersistDeleteInterface(IF);
if (! NT_SUCCESS(Status))
goto ReturnReleaseIF;
}
Status = STATUS_SUCCESS;
ReturnReleaseIF:
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlDeleteInterface
//* IoctlRenewInterface
//
// Processes an IOCTL_IPV6_RENEW_INTERFACE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlRenewInterface(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_INTERFACE *Query;
Interface *IF;
KIRQL OldIrql;
NTSTATUS Status;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_INTERFACE *) Irp->AssociatedIrp.SystemBuffer;
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(Query);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// Pretend as if the interface received a media reconnect
// event, but only if the interface is already connected.
//
// This IOCTL is used by 802.1x to indicate successful data link
// authentication of this interface. Any data packets already sent
// on this interface would have been dropped by the authenticator,
// and hence IPv6 needs to restart its protocol mechanisms, i.e.
// resend Router Solicitation|Advertisement, Multicast Listener
// Discovery, and Duplicate Address Detection messages.
//
KeAcquireSpinLock(&IF->Lock, &OldIrql);
if (!IsDisabledIF(IF) && !(IF->Flags & IF_FLAG_MEDIA_DISCONNECTED))
ReconnectInterface(IF);
KeReleaseSpinLock(&IF->Lock, OldIrql);
Status = STATUS_SUCCESS;
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlRenewInterface
//* IoctlFlushNeighborCache
//
// Processes an IOCTL_IPV6_FLUSH_NEIGHBOR_CACHE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlFlushNeighborCache(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_NEIGHBOR_CACHE *Query;
Interface *IF;
const IPv6Addr *Address;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_NEIGHBOR_CACHE *) Irp->AssociatedIrp.SystemBuffer;
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(&Query->IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
if (IsUnspecified(&Query->Address))
Address = NULL;
else
Address = &Query->Address;
NeighborCacheFlush(IF, Address);
ReleaseIF(IF);
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlFlushNeighborCache
//* IoctlFlushRouteCache
//
// Processes an IOCTL_IPV6_FLUSH_ROUTE_CACHE request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlFlushRouteCache(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_ROUTE_CACHE *Query;
Interface *IF;
const IPv6Addr *Address;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_ROUTE_CACHE *) Irp->AssociatedIrp.SystemBuffer;
if (Query->IF.Index == (uint)-1) {
IF = NULL;
}
else {
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(&Query->IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
}
if (IsUnspecified(&Query->Address))
Address = NULL;
else
Address = &Query->Address;
FlushRouteCache(IF, Address);
if (IF != NULL)
ReleaseIF(IF);
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlFlushRouteCache
//* IoctlSortDestAddrs
//
// Processes an IOCTL_IPV6_SORT_DEST_ADDRS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlSortDestAddrs(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
TDI_ADDRESS_IP6 *Addrs;
uint *Key;
uint NumAddrsIn, NumAddrsOut;
uint i;
NTSTATUS Status;
PAGED_CODE();
NumAddrsIn = IrpSp->Parameters.DeviceIoControl.InputBufferLength /
sizeof(TDI_ADDRESS_IP6);
NumAddrsOut = NumAddrsIn;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength !=
NumAddrsIn * sizeof(TDI_ADDRESS_IP6)) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength !=
ALIGN_UP(NumAddrsIn * sizeof(TDI_ADDRESS_IP6), uint) +
NumAddrsOut * sizeof(uint))) {
Irp->IoStatus.Information = 0;
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Addrs = Irp->AssociatedIrp.SystemBuffer;
Key = (uint *)ALIGN_UP_POINTER(Addrs + NumAddrsIn, uint);
//
// Initialize key array.
//
for (i = 0; i < NumAddrsIn; i++)
Key[i] = i;
if (NumAddrsOut > 1) {
//
// Remove inappropriate site-local addresses
// and set the scope-id of site-local addresses.
//
ProcessSiteLocalAddresses(Addrs, Key, &NumAddrsOut);
//
// Sort the remaining addresses.
//
if (NumAddrsOut > 1)
SortDestAddresses(Addrs, Key, NumAddrsOut);
}
Irp->IoStatus.Information = ALIGN_UP(NumAddrsIn * sizeof(TDI_ADDRESS_IP6), uint)
+ (NumAddrsOut * sizeof(uint));
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlSortDestAddrs.
//* IoctlQuerySitePrefix
//
// Processes an IOCTL_IPV6_QUERY_SITE_PREFIX request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQuerySitePrefix(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_SITE_PREFIX *Query;
IPV6_INFO_SITE_PREFIX *Info;
SitePrefixEntry *SPE;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Query structures overlap!
//
Query = (IPV6_QUERY_SITE_PREFIX *) Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_SITE_PREFIX *) Irp->AssociatedIrp.SystemBuffer;
if (Query->IF.Index == 0) {
//
// Return query parameters of the first SPE.
//
KeAcquireSpinLock(&RouteTableLock, &OldIrql);
if ((SPE = SitePrefixTable) != NULL) {
Info->Query.Prefix = SPE->Prefix;
Info->Query.PrefixLength = SPE->SitePrefixLength;
Info->Query.IF.Index = SPE->IF->Index;
}
KeReleaseSpinLock(&RouteTableLock, OldIrql);
Irp->IoStatus.Information = sizeof Info->Query;
} else {
//
// Find the specified SPE.
//
KeAcquireSpinLock(&RouteTableLock, &OldIrql);
for (SPE = SitePrefixTable; ; SPE = SPE->Next) {
if (SPE == NULL) {
KeReleaseSpinLock(&RouteTableLock, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
if (IP6_ADDR_EQUAL(&Query->Prefix, &SPE->Prefix) &&
(Query->PrefixLength == SPE->SitePrefixLength) &&
(Query->IF.Index == SPE->IF->Index))
break;
}
//
// Return misc. information about the SPE.
//
Info->ValidLifetime = ConvertTicksToSeconds(SPE->ValidLifetime);
//
// Return query parameters of the next SPE (or zero).
//
if ((SPE = SPE->Next) == NULL) {
Info->Query.IF.Index = 0;
} else {
Info->Query.Prefix = SPE->Prefix;
Info->Query.PrefixLength = SPE->SitePrefixLength;
Info->Query.IF.Index = SPE->IF->Index;
}
KeReleaseSpinLock(&RouteTableLock, OldIrql);
Irp->IoStatus.Information = sizeof *Info;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQuerySitePrefix
//* IoctlUpdateSitePrefix
//
// Processes an IOCTL_IPV6_UPDATE_SITE_PREFIX request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdateSitePrefix(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_INFO_SITE_PREFIX *Info;
Interface *IF = NULL;
SitePrefixEntry *SPE;
uint ValidLifetime;
KIRQL OldIrql;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Info) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_INFO_SITE_PREFIX *) Irp->AssociatedIrp.SystemBuffer;
//
// Sanity check the arguments.
//
if (Info->Query.PrefixLength > IPV6_ADDRESS_LENGTH) {
Status = STATUS_INVALID_PARAMETER_3;
goto Return;
}
//
// Find the specified interface.
//
IF = FindInterfaceFromQuery(&Info->Query.IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// Convert the lifetime from seconds to ticks.
//
ValidLifetime = ConvertSecondsToTicks(Info->ValidLifetime);
//
// Create/update the specified site prefix.
//
SitePrefixUpdate(IF,
&Info->Query.Prefix,
Info->Query.PrefixLength,
ValidLifetime);
Irp->IoStatus.Information = 0;
Status = STATUS_SUCCESS;
Return:
if (IF != NULL)
ReleaseIF(IF);
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdateSitePrefix
//* CancelRtChangeNotifyRequest
//
// The IO manager calls this function when a route change
// notification request is cancelled.
//
// Called with the cancel spinlock held.
//
void
CancelRtChangeNotifyRequest(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)
{
int ShouldComplete;
ASSERT(Irp->Cancel);
ASSERT(Irp->CancelRoutine == NULL);
//
// The route lock protects the queue.
//
KeAcquireSpinLockAtDpcLevel(&RouteTableLock);
ShouldComplete = (Irp->Tail.Overlay.ListEntry.Flink != NULL);
if (ShouldComplete) {
//
// CheckRtChangeNotifyRequests has not removed
// this request from the queue. So we remove the request
// and complete it below.
//
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
}
else {
//
// CheckRtChangeNotifyRequests has removed
// this request from the queue. We must not
// touch the Irp after unlocking because
// CompleteRtChangeNotifyRequests could complete it.
//
}
KeReleaseSpinLockFromDpcLevel(&RouteTableLock);
IoReleaseCancelSpinLock(Irp->CancelIrql);
if (ShouldComplete) {
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
}
}
//* CheckFileObjectInIrpList
//
// Looks to see if an Irp in the list has the given file object.
//
int
CheckFileObjectInIrpList(PFILE_OBJECT FileObject, PIRP Irp)
{
PIO_STACK_LOCATION IrpSp;
while (Irp != NULL) {
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (IrpSp->FileObject == FileObject)
return TRUE;
Irp = (PIRP) Irp->Tail.Overlay.ListEntry.Blink;
}
return FALSE;
}
//* CheckRtChangeNotifyRequests
//
// Searches the queue of route change notification requests.
// Moves any matching requests (that should be completed)
// to a temporary list kept in the context structure.
//
// Called with the route lock held.
//
void
CheckRtChangeNotifyRequests(
CheckRtChangeContext *Context,
PFILE_OBJECT FileObject,
RouteTableEntry *RTE)
{
LIST_ENTRY *ListEntry;
LIST_ENTRY *NextListEntry;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
IPV6_RTCHANGE_NOTIFY_REQUEST *Request;
PIRP *ThisChangeList;
//
// *ThisChangeList is the tail of Context->RequestList
// that was added as a result of this change.
//
ThisChangeList = Context->LastRequest;
for (ListEntry = RouteNotifyQueue.Flink;
ListEntry != &RouteNotifyQueue;
ListEntry = NextListEntry) {
NextListEntry = ListEntry->Flink;
Irp = CONTAINING_RECORD(ListEntry, IRP, Tail.Overlay.ListEntry);
IrpSp = IoGetCurrentIrpStackLocation(Irp);
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength >=
sizeof *Request)
Request = (IPV6_RTCHANGE_NOTIFY_REQUEST *)
Irp->AssociatedIrp.SystemBuffer;
else
Request = NULL;
if ((Request == NULL) ||
(IntersectPrefix(&RTE->Prefix, RTE->PrefixLength,
&Request->Prefix, Request->PrefixLength) &&
((Request->ScopeId == 0) ||
(Request->ScopeId == DetermineScopeId(&Request->Prefix,
RTE->IF))))) {
//
// This request matches the route change.
// But we might still suppress notification.
//
if ((Request != NULL) &&
(((Request->Flags &
IPV6_RTCHANGE_NOTIFY_REQUEST_FLAG_SUPPRESS_MINE) &&
(IrpSp->FileObject == FileObject)) ||
((Request->Flags &
IPV6_RTCHANGE_NOTIFY_REQUEST_FLAG_SYNCHRONIZE) &&
CheckFileObjectInIrpList(IrpSp->FileObject,
*ThisChangeList)))) {
//
// The request matches, but suppress notification.
//
}
else {
//
// Before we remove the Irp from RouteNotifyQueue,
// may need to allocate a work item & work context.
// If the allocation fails, we can bail without doing anything.
//
if ((Context->OldIrql >= DISPATCH_LEVEL) &&
(Context->Context == NULL)) {
CompleteRtChangeContext *MyContext;
MyContext = ExAllocatePool(NonPagedPool,
sizeof *MyContext);
if (MyContext == NULL)
return;
MyContext->WorkItem = IoAllocateWorkItem(IPDeviceObject);
if (MyContext->WorkItem == NULL) {
ExFreePool(MyContext);
return;
}
Context->Context = MyContext;
}
//
// We will complete this pending notification,
// so remove it from RouteNotifyQueue and
// put it on our private list.
//
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
Irp->Tail.Overlay.ListEntry.Flink = NULL;
Irp->Tail.Overlay.ListEntry.Blink = NULL;
*Context->LastRequest = Irp;
Context->LastRequest = (PIRP *)
&Irp->Tail.Overlay.ListEntry.Blink;
//
// Return output information, if requested.
//
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
sizeof(IPV6_INFO_ROUTE_TABLE)) {
IPV6_INFO_ROUTE_TABLE *Info = (IPV6_INFO_ROUTE_TABLE *)
Irp->AssociatedIrp.SystemBuffer;
//
// Return misc. information about the RTE.
//
RouteTableInfo(RTE, RTE, Info);
Irp->IoStatus.Information = sizeof *Info;
}
else
Irp->IoStatus.Information = 0;
}
}
}
}
//* CompleteRtChangeNotifyRequestsHelper
//
// Completes a list of route change notification requests.
//
// Callable from thread context, not DPC context.
// May NOT be called with the route lock held.
//
void
CompleteRtChangeNotifyRequestsHelper(PIRP RequestList)
{
PIRP Irp;
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// RequestList is singly-linked through the Blink field.
// The Flink field is NULL; CancelRtChangeNotifyRequest
// looks at this.
//
while ((Irp = RequestList) != NULL) {
ASSERT(Irp->Tail.Overlay.ListEntry.Flink == NULL);
RequestList = (PIRP) Irp->Tail.Overlay.ListEntry.Blink;
IoAcquireCancelSpinLock(&OldIrql);
if (Irp->Cancel) {
//
// The Irp is being cancelled.
//
ASSERT(Irp->CancelRoutine == NULL);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_CANCELLED;
}
else {
//
// The Irp is not yet cancelled.
// We must prevent CancelRtChangeNotifyRequest
// from being called after we release the cancel lock.
//
ASSERT(Irp->CancelRoutine == CancelRtChangeNotifyRequest);
IoSetCancelRoutine(Irp, NULL);
//
// Irp->IoStatus.Information and the output structure
// are already initialized.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
}
IoReleaseCancelSpinLock(OldIrql);
IoCompleteRequest(Irp, IO_NETWORK_INCREMENT);
}
}
//* CompleteRtChangeNotifyRequestsWorker
//
// Worker thread function - cleans up the work item
// and calls CompleteRtChangeNotifyRequestsHelper.
//
void
CompleteRtChangeNotifyRequestsWorker(
PDEVICE_OBJECT DeviceObject,
PVOID Context)
{
CompleteRtChangeContext *MyContext = Context;
CompleteRtChangeNotifyRequestsHelper(MyContext->RequestList);
IoFreeWorkItem(MyContext->WorkItem);
ExFreePool(MyContext);
}
//* CompleteRtChangeNotifyRequests
//
// Completes a list of route change notification requests.
//
// Callable from a thread or DPC context.
// May NOT be called with the route lock held.
//
void
CompleteRtChangeNotifyRequests(CheckRtChangeContext *Context)
{
ASSERT(Context->OldIrql == KeGetCurrentIrql());
if (Context->OldIrql >= DISPATCH_LEVEL) {
CompleteRtChangeContext *MyContext = Context->Context;
//
// We can't complete Irps at dispatch level,
// so punt to a worker thread.
// The work item was already allocated.
//
MyContext->RequestList = Context->RequestList;
IoQueueWorkItem(MyContext->WorkItem,
CompleteRtChangeNotifyRequestsWorker,
CriticalWorkQueue,
MyContext);
}
else {
//
// We can complete the Irps directly.
//
ASSERT(Context->Context == NULL);
CompleteRtChangeNotifyRequestsHelper(Context->RequestList);
}
}
//* IoctlRtChangeNotifyRequest
//
// Processes an IOCTL_IPV6_RTCHANGE_NOTIFY_REQUEST request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlRtChangeNotifyRequest(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
NTSTATUS Status;
KIRQL OldIrql;
PAGED_CODE();
if (((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof(IPV6_RTCHANGE_NOTIFY_REQUEST)) &&
(IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0)) ||
((IrpSp->Parameters.DeviceIoControl.OutputBufferLength != sizeof(IPV6_INFO_ROUTE_TABLE)) &&
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0))) {
Status = STATUS_INVALID_PARAMETER;
goto ErrorReturn;
}
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength == sizeof(IPV6_RTCHANGE_NOTIFY_REQUEST)) {
IPV6_RTCHANGE_NOTIFY_REQUEST *Request;
Request = (IPV6_RTCHANGE_NOTIFY_REQUEST *)
Irp->AssociatedIrp.SystemBuffer;
//
// Sanity check the arguments.
//
if (Request->PrefixLength > IPV6_ADDRESS_LENGTH) {
Status = STATUS_INVALID_PARAMETER_1;
goto ErrorReturn;
}
if (Request->ScopeId != 0) {
//
// If a ScopeId is specified, it must be
// unambiguously a link-local or site-local prefix.
//
if ((Request->PrefixLength < 10) ||
!(IsLinkLocal(&Request->Prefix) ||
IsSiteLocal(&Request->Prefix))) {
Status = STATUS_INVALID_PARAMETER_2;
goto ErrorReturn;
}
}
}
IoAcquireCancelSpinLock(&OldIrql);
ASSERT(Irp->CancelRoutine == NULL);
if (Irp->Cancel) {
IoReleaseCancelSpinLock(OldIrql);
Status = STATUS_CANCELLED;
goto ErrorReturn;
}
//
// Add this Irp to the queue of notification requests.
// Acquire route lock, which protects the queue.
//
KeAcquireSpinLockAtDpcLevel(&RouteTableLock);
InsertTailList(&RouteNotifyQueue, &Irp->Tail.Overlay.ListEntry);
KeReleaseSpinLockFromDpcLevel(&RouteTableLock);
//
// We return pending to indicate that we've queued the Irp
// and it will be completed later.
// Must mark the Irp before unlocking, because once unlocked
// the Irp might be completed and deallocated.
//
IoMarkIrpPending(Irp);
IoSetCancelRoutine(Irp, CancelRtChangeNotifyRequest);
IoReleaseCancelSpinLock(OldIrql);
return STATUS_PENDING;
ErrorReturn:
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlRtChangeNotifyRequest
//* ReadPersistentGlobalParameters
//
// Reads global parameters from the registry.
//
void
ReadPersistentGlobalParameters(IPV6_GLOBAL_PARAMETERS *Params)
{
HANDLE RegKey = NULL;
NTSTATUS Status;
Status = OpenTopLevelRegKey(L"GlobalParams", &RegKey, OpenRegKeyRead);
ASSERT(NT_SUCCESS(Status) || (RegKey == NULL));
//
// Read global parameters from the registry.
//
InitRegDWORDParameter(RegKey,
L"DefaultCurHopLimit",
&Params->DefaultCurHopLimit,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"UseAnonymousAddresses",
&Params->UseAnonymousAddresses,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"MaxAnonDADAttempts",
&Params->MaxAnonDADAttempts,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"MaxAnonValidLifetime",
&Params->MaxAnonValidLifetime,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"MaxAnonPreferredLifetime",
&Params->MaxAnonPreferredLifetime,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"AnonRegenerateTime",
&Params->AnonRegenerateTime,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"MaxAnonRandomTime",
&Params->MaxAnonRandomTime,
(uint)-1);
Params->AnonRandomTime = 0;
InitRegDWORDParameter(RegKey,
L"NeighborCacheLimit",
&Params->NeighborCacheLimit,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"RouteCacheLimit",
&Params->RouteCacheLimit,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"BindingCacheLimit",
&Params->BindingCacheLimit,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"ReassemblyLimit",
&Params->ReassemblyLimit,
(uint)-1);
InitRegDWORDParameter(RegKey,
L"MobilitySecurity",
(ULONG *)&Params->MobilitySecurity,
(uint)-1);
if (RegKey != NULL)
ZwClose(RegKey);
}
//* IoctlQueryGlobalParameters
//
// Processes an IOCTL_IPV6_QUERY_GLOBAL_PARAMETERS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryGlobalParameters(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_GLOBAL_PARAMETERS *Params;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != sizeof *Params)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Params = (IPV6_GLOBAL_PARAMETERS *)Irp->AssociatedIrp.SystemBuffer;
if (Persistent) {
//
// Read global parameters from the registry.
//
ReadPersistentGlobalParameters(Params);
}
else {
//
// Return the current values of the parameters.
//
Params->DefaultCurHopLimit = DefaultCurHopLimit;
Params->UseAnonymousAddresses = UseAnonymousAddresses;
Params->MaxAnonDADAttempts = MaxAnonDADAttempts;
Params->MaxAnonValidLifetime = ConvertTicksToSeconds(MaxAnonValidLifetime);
Params->MaxAnonPreferredLifetime = ConvertTicksToSeconds(MaxAnonPreferredLifetime);
Params->AnonRegenerateTime = ConvertTicksToSeconds(AnonRegenerateTime);
Params->MaxAnonRandomTime = ConvertTicksToSeconds(MaxAnonRandomTime);
Params->AnonRandomTime = ConvertTicksToSeconds(AnonRandomTime);
Params->NeighborCacheLimit = NeighborCacheLimit;
Params->RouteCacheLimit = RouteCache.Limit;
Params->BindingCacheLimit = BindingCache.Limit;
Params->ReassemblyLimit = ReassemblyList.Limit;
Params->MobilitySecurity = MobilitySecurity;
}
Irp->IoStatus.Information = sizeof *Params;
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryGlobalParameters.
//* InternalUpdateGlobalParameters
//
// Common helper function for IoctlUpdateGlobalParameters
// and ConfigureGlobalParameters, consolidating
// parameter validation in one place.
//
// Callable from thread context, not DPC context.
//
// Return codes:
// STATUS_INVALID_PARAMETER_1 Bad DefaultCurHopLimit.
// STATUS_INVALID_PARAMETER_2 Bad UseAnonymousAddresses.
// STATUS_INVALID_PARAMETER_3 Bad anonymous times.
// STATUS_SUCCESS
//
NTSTATUS
InternalUpdateGlobalParameters(IPV6_GLOBAL_PARAMETERS *Params)
{
uint NewMaxAnonValidLifetime;
uint NewMaxAnonPreferredLifetime;
uint NewAnonRegenerateTime;
uint NewMaxAnonRandomTime;
uint NewAnonRandomTime;
PAGED_CODE();
//
// Sanity check the new parameters.
//
if (Params->DefaultCurHopLimit != (uint)-1) {
if ((Params->DefaultCurHopLimit == 0) ||
(Params->DefaultCurHopLimit > 0xff))
return STATUS_INVALID_PARAMETER_1;
}
if (Params->UseAnonymousAddresses != (uint)-1) {
if (Params->UseAnonymousAddresses > USE_ANON_COUNTER)
return STATUS_INVALID_PARAMETER_2;
}
if (Params->MaxAnonValidLifetime != (uint)-1)
NewMaxAnonValidLifetime =
ConvertSecondsToTicks(Params->MaxAnonValidLifetime);
else
NewMaxAnonValidLifetime = MaxAnonValidLifetime;
if (Params->MaxAnonPreferredLifetime != (uint)-1)
NewMaxAnonPreferredLifetime =
ConvertSecondsToTicks(Params->MaxAnonPreferredLifetime);
else
NewMaxAnonPreferredLifetime = MaxAnonPreferredLifetime;
if (Params->AnonRegenerateTime != (uint)-1)
NewAnonRegenerateTime =
ConvertSecondsToTicks(Params->AnonRegenerateTime);
else
NewAnonRegenerateTime = AnonRegenerateTime;
if (Params->MaxAnonRandomTime != (uint)-1)
NewMaxAnonRandomTime =
ConvertSecondsToTicks(Params->MaxAnonRandomTime);
else
NewMaxAnonRandomTime = MaxAnonRandomTime;
if (Params->AnonRandomTime == 0)
NewAnonRandomTime = RandomNumber(0, NewMaxAnonRandomTime);
else if (Params->AnonRandomTime == (uint)-1)
NewAnonRandomTime = AnonRandomTime;
else
NewAnonRandomTime = ConvertSecondsToTicks(Params->AnonRandomTime);
if (!(NewAnonRandomTime <= NewMaxAnonRandomTime) ||
!(NewAnonRegenerateTime + NewMaxAnonRandomTime <
NewMaxAnonPreferredLifetime) ||
!(NewMaxAnonPreferredLifetime <= NewMaxAnonValidLifetime))
return STATUS_INVALID_PARAMETER_3;
//
// Set the new values.
//
if (Params->DefaultCurHopLimit != (uint)-1)
DefaultCurHopLimit = Params->DefaultCurHopLimit;
if (Params->UseAnonymousAddresses != (uint)-1)
UseAnonymousAddresses = Params->UseAnonymousAddresses;
if (Params->MaxAnonDADAttempts != (uint)-1)
MaxAnonDADAttempts = Params->MaxAnonDADAttempts;
MaxAnonValidLifetime = NewMaxAnonValidLifetime;
MaxAnonPreferredLifetime = NewMaxAnonPreferredLifetime;
AnonRegenerateTime = NewAnonRegenerateTime;
MaxAnonRandomTime = NewMaxAnonRandomTime;
AnonRandomTime = NewAnonRandomTime;
if (Params->NeighborCacheLimit != (uint)-1)
NeighborCacheLimit = Params->NeighborCacheLimit;
if (Params->RouteCacheLimit != (uint)-1)
RouteCache.Limit = Params->RouteCacheLimit;
if (Params->BindingCacheLimit != (uint)-1)
BindingCache.Limit = Params->BindingCacheLimit;
if (Params->ReassemblyLimit != (uint)-1)
ReassemblyList.Limit = Params->ReassemblyLimit;
if (Params->MobilitySecurity != -1)
MobilitySecurity = Params->MobilitySecurity;
return STATUS_SUCCESS;
}
//* DefaultReassemblyLimit
//
// Computes the default memory limit for reassembly buffers, based
// on the amount of physical memory in the system.
//
uint
DefaultReassemblyLimit(void)
{
SYSTEM_BASIC_INFORMATION Info;
ULONG MemSize;
NTSTATUS Status;
Status = ZwQuerySystemInformation(SystemBasicInformation,
&Info,
sizeof(Info),
NULL);
if (!NT_SUCCESS(Status)) {
//
// If this failed, then we're probably really resource constrained,
// so use only 256K.
//
return (256 * 1024);
}
//
// By default, limit the reassembly buffers to a maximum size equal
// to 1/128th of the physical memory. On a machine with only 128M of
// memory, this is 1M of memory maximum (enough to reassemble
// 16 64K packets, or 128 8K packets, for example). In contrast,
// the IPv4 stack currently allows reassembling a fixed maximum of
// 100 packets, regardless of packet size or available memory.
//
return (uint)(Info.NumberOfPhysicalPages * (Info.PageSize / 128));
}
//* GlobalParametersReset
//
// Resets global parameters to their default values.
// Also used to initialize them at boot.
//
void
GlobalParametersReset(void)
{
IPV6_GLOBAL_PARAMETERS Params;
NTSTATUS Status;
Params.DefaultCurHopLimit = DEFAULT_CUR_HOP_LIMIT;
Params.UseAnonymousAddresses = USE_ANON_YES;
Params.MaxAnonDADAttempts = MAX_ANON_DAD_ATTEMPTS;
Params.MaxAnonValidLifetime = MAX_ANON_VALID_LIFETIME;
Params.MaxAnonPreferredLifetime = MAX_ANON_PREFERRED_LIFETIME;
Params.AnonRegenerateTime = ANON_REGENERATE_TIME;
Params.MaxAnonRandomTime = MAX_ANON_RANDOM_TIME;
Params.AnonRandomTime = 0;
Params.NeighborCacheLimit = NEIGHBOR_CACHE_LIMIT;
Params.RouteCacheLimit = ROUTE_CACHE_LIMIT;
Params.BindingCacheLimit = BINDING_CACHE_LIMIT;
Params.ReassemblyLimit = DefaultReassemblyLimit();
Params.MobilitySecurity = TRUE;
Status = InternalUpdateGlobalParameters(&Params);
ASSERT(NT_SUCCESS(Status));
}
//* ConfigureGlobalParameters
//
// Configures global parameters from the registry.
//
// Callable from thread context, not DPC context.
//
void
ConfigureGlobalParameters(void)
{
IPV6_GLOBAL_PARAMETERS Params;
NTSTATUS Status;
//
// First initialize global parameters to default values.
//
GlobalParametersReset();
//
// Read global parameters from the registry.
//
ReadPersistentGlobalParameters(&Params);
Status = InternalUpdateGlobalParameters(&Params);
if (! NT_SUCCESS(Status)) {
//
// This should only happen if someone played with the registry.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"ConfigureGlobalParameters: bad params %x\n", Status));
}
}
//* PersistUpdateGlobalParameters
//
// Helper function for persisting global parameters in the registry.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistUpdateGlobalParameters(IPV6_GLOBAL_PARAMETERS *Params)
{
HANDLE RegKey;
NTSTATUS Status;
Status = OpenTopLevelRegKey(L"GlobalParams", &RegKey, OpenRegKeyCreate);
if (! NT_SUCCESS(Status))
return Status;
if (Params->DefaultCurHopLimit != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"DefaultCurHopLimit",
Params->DefaultCurHopLimit);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->UseAnonymousAddresses != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"UseAnonymousAddresses",
Params->UseAnonymousAddresses);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->MaxAnonDADAttempts != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"MaxAnonDADAttempts",
Params->MaxAnonDADAttempts);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->MaxAnonValidLifetime != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"MaxAnonValidLifetime",
Params->MaxAnonValidLifetime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->MaxAnonPreferredLifetime != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"MaxAnonPreferredLifetime",
Params->MaxAnonPreferredLifetime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->AnonRegenerateTime != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"AnonRegenerateTime",
Params->AnonRegenerateTime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->MaxAnonRandomTime != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"MaxAnonRandomTime",
Params->MaxAnonRandomTime);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->NeighborCacheLimit != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"NeighborCacheLimit",
Params->NeighborCacheLimit);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->RouteCacheLimit != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"RouteCacheLimit",
Params->RouteCacheLimit);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->BindingCacheLimit != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"BindingCacheLimit",
Params->BindingCacheLimit);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->ReassemblyLimit != (uint)-1) {
Status = SetRegDWORDValue(RegKey, L"ReassemblyLimit",
Params->ReassemblyLimit);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
if (Params->MobilitySecurity != -1) {
Status = SetRegDWORDValue(RegKey, L"MobilitySecurity",
Params->MobilitySecurity);
if (! NT_SUCCESS(Status))
goto ReturnReleaseKey;
}
Status = STATUS_SUCCESS;
ReturnReleaseKey:
ZwClose(RegKey);
return Status;
}
//* IoctlUpdateGlobalParameters
//
// Processes an IOCTL_IPV6_UPDATE_GLOBAL_PARAMETERS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdateGlobalParameters(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_GLOBAL_PARAMETERS *Params;
NTSTATUS Status;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Params) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Params = (IPV6_GLOBAL_PARAMETERS *)Irp->AssociatedIrp.SystemBuffer;
Status = InternalUpdateGlobalParameters(Params);
if (! NT_SUCCESS(Status))
goto Return;
if (Persistent) {
Status = PersistUpdateGlobalParameters(Params);
if (! NT_SUCCESS(Status))
goto Return;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdateGlobalParameters.
//* ReturnQueryPrefixPolicy
//
// Initializes a returned IPV6_QUERY_PREFIX_POLICY structure
// with query information for the specified prefix policy.
//
void
ReturnQueryPrefixPolicy(
PrefixPolicyEntry *PPE,
IPV6_QUERY_PREFIX_POLICY *Query)
{
if (PPE == NULL) {
Query->Prefix = UnspecifiedAddr;
Query->PrefixLength = (uint)-1;
}
else {
Query->Prefix = PPE->Prefix;
Query->PrefixLength = PPE->PrefixLength;
}
}
//* IoctlQueryPrefixPolicy
//
// Processes an IOCTL_IPV6_QUERY_PREFIX_POLICY request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlQueryPrefixPolicy(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_QUERY_PREFIX_POLICY *Query;
IPV6_INFO_PREFIX_POLICY *Info;
PrefixPolicyEntry *PPE;
KIRQL OldIrql;
NTSTATUS Status;
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Next structures overlap!
//
Query = (IPV6_QUERY_PREFIX_POLICY *)Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_PREFIX_POLICY *)Irp->AssociatedIrp.SystemBuffer;
if (Query->PrefixLength == (uint)-1) {
//
// Return query information for the first PPE.
//
KeAcquireSpinLock(&SelectLock, &OldIrql);
ReturnQueryPrefixPolicy(PrefixPolicyTable, &Info->Next);
KeReleaseSpinLock(&SelectLock, OldIrql);
Irp->IoStatus.Information = sizeof Info->Next;
} else {
//
// Find the specified PPE.
//
KeAcquireSpinLock(&SelectLock, &OldIrql);
for (PPE = PrefixPolicyTable; ; PPE = PPE->Next) {
if (PPE == NULL) {
KeReleaseSpinLock(&SelectLock, OldIrql);
Status = STATUS_INVALID_PARAMETER_2;
goto Return;
}
if (IP6_ADDR_EQUAL(&Query->Prefix, &PPE->Prefix) &&
(Query->PrefixLength == PPE->PrefixLength))
break;
}
//
// Return misc. information about the PPE.
//
Info->This = *Query;
Info->Precedence = PPE->Precedence;
Info->SrcLabel = PPE->SrcLabel;
Info->DstLabel = PPE->DstLabel;
//
// Return query information for the next PPE.
//
ReturnQueryPrefixPolicy(PPE->Next, &Info->Next);
KeReleaseSpinLock(&SelectLock, OldIrql);
Irp->IoStatus.Information = sizeof *Info;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlQueryPrefixPolicy
//* ReadPersistentPrefixPolicy
//
// Reads a prefix policy from the registry.
//
// Returns:
// STATUS_NO_MORE_ENTRIES Could not read the prefix policy.
// STATUS_SUCCESS
//
NTSTATUS
ReadPersistentPrefixPolicy(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
IPV6_INFO_PREFIX_POLICY *Info = (IPV6_INFO_PREFIX_POLICY *) Context;
WCHAR *Terminator;
HANDLE PolicyKey;
NTSTATUS Status;
PAGED_CODE();
//
// First, parse the prefix.
//
if (! ParseV6Address(SubKeyName, &Terminator, &Info->This.Prefix) ||
(*Terminator != L'/')) {
//
// Not a valid prefix.
//
SyntaxError:
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"ReadPersistentPrefixPolicy: bad syntax %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
//
// Next, parse the prefix length.
//
Terminator++; // Move past the L'/'.
Info->This.PrefixLength = 0;
for (;;) {
WCHAR Char = *Terminator++;
if (Char == UNICODE_NULL)
break;
else if ((L'0' <= Char) && (Char <= L'9')) {
Info->This.PrefixLength *= 10;
Info->This.PrefixLength += Char - L'0';
if (Info->This.PrefixLength > IPV6_ADDRESS_LENGTH)
goto SyntaxError;
}
else
goto SyntaxError;
}
//
// Open the policy key.
//
Status = OpenRegKey(&PolicyKey, ParentKey, SubKeyName, OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
//
// Could not open the policy key.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"ReadPersistentPrefixPolicy: bad key %ls\n",
SubKeyName));
return STATUS_NO_MORE_ENTRIES;
}
//
// Read prefix policy attributes.
//
InitRegDWORDParameter(PolicyKey, L"Precedence",
(ULONG *)&Info->Precedence, 0);
InitRegDWORDParameter(PolicyKey, L"SrcLabel",
(ULONG *)&Info->SrcLabel, 0);
InitRegDWORDParameter(PolicyKey, L"DstLabel",
(ULONG *)&Info->DstLabel, 0);
//
// Done reading the policy attributes.
//
ZwClose(PolicyKey);
return STATUS_SUCCESS;
}
//* IoctlPersistentQueryPrefixPolicy
//
// Processes an IOCTL_IPV6_PERSISTENT_QUERY_PREFIX_POLICY request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlPersistentQueryPrefixPolicy(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_PERSISTENT_QUERY_PREFIX_POLICY *Query;
IPV6_INFO_PREFIX_POLICY *Info;
HANDLE RegKey;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof *Info)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Note that the Query and Info->Next structures overlap!
//
Query = (IPV6_PERSISTENT_QUERY_PREFIX_POLICY *)
Irp->AssociatedIrp.SystemBuffer;
Info = (IPV6_INFO_PREFIX_POLICY *)
Irp->AssociatedIrp.SystemBuffer;
Status = OpenTopLevelRegKey(L"PrefixPolicies", &RegKey, OpenRegKeyRead);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
Status = STATUS_NO_MORE_ENTRIES;
goto Return;
}
Status = EnumRegKeyIndex(RegKey, Query->RegistryIndex,
ReadPersistentPrefixPolicy, Info);
ZwClose(RegKey);
if (! NT_SUCCESS(Status))
goto Return;
//
// Do not return query information for a next policy,
// since an iteration uses RegistryIndex.
//
ReturnQueryPrefixPolicy(NULL, &Info->Next);
Status = STATUS_SUCCESS;
Irp->IoStatus.Information = sizeof *Info;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlPersistentQueryPrefixPolicy
struct PrefixPolicyDefault {
IPv6Addr *Prefix;
uint PrefixLength;
uint Precedence;
uint SrcLabel;
uint DstLabel;
} PrefixPolicyDefault[] = {
{ &LoopbackAddr, 128, 50, 0, 0 }, // ::1/128 (loopback)
{ &UnspecifiedAddr, 0, 40, 1, 1 }, // ::/0
{ &SixToFourPrefix, 16, 30, 2, 2 }, // 2002::/16 (6to4)
{ &UnspecifiedAddr, 96, 20, 3, 3 }, // ::/96 (v4-compatible)
{ &V4MappedPrefix, 96, 10, 4, 4 }, // ::ffff:0.0.0.0/96 (v4-mapped)
};
int UsingDefaultPrefixPolicies;
#define NUM_DEFAULT_PREFIX_POLICIES \
(sizeof PrefixPolicyDefault / sizeof PrefixPolicyDefault[0])
//* ConfigureDefaultPrefixPolicies
//
// Installs the default prefix policies.
//
void
ConfigureDefaultPrefixPolicies(void)
{
uint i;
for (i = 0; i < NUM_DEFAULT_PREFIX_POLICIES; i++) {
struct PrefixPolicyDefault *Policy = &PrefixPolicyDefault[i];
PrefixPolicyUpdate(Policy->Prefix,
Policy->PrefixLength,
Policy->Precedence,
Policy->SrcLabel,
Policy->DstLabel);
}
UsingDefaultPrefixPolicies = TRUE;
}
//* InternalUpdatePrefixPolicy
//
// Common helper function for IoctlUpdatePrefixPolicy
// and CreatePersistentPrefixPolicy, consolidating
// parameter validation in one place.
//
// Callable from thread context, not DPC context.
//
// Return codes:
// STATUS_INVALID_PARAMETER_1 Bad PrefixLength.
// STATUS_INVALID_PARAMETER_2 Bad Precedence.
// STATUS_INVALID_PARAMETER_3 Bad SrcLabel.
// STATUS_INVALID_PARAMETER_4 Bad DstLabel.
//
NTSTATUS
InternalUpdatePrefixPolicy(IPV6_INFO_PREFIX_POLICY *Info)
{
if (Info->This.PrefixLength > IPV6_ADDRESS_LENGTH)
return STATUS_INVALID_PARAMETER_1;
//
// Disallow the value -1. It's used internally.
//
if (Info->Precedence == (uint)-1)
return STATUS_INVALID_PARAMETER_2;
if (Info->SrcLabel == (uint)-1)
return STATUS_INVALID_PARAMETER_3;
if (Info->DstLabel == (uint)-1)
return STATUS_INVALID_PARAMETER_4;
if (UsingDefaultPrefixPolicies) {
//
// The user is changing the default policies for the first time.
// Remove the default policies.
//
UsingDefaultPrefixPolicies = FALSE;
PrefixPolicyReset();
}
//
// Create/update the specified prefix policy.
//
PrefixPolicyUpdate(&Info->This.Prefix,
Info->This.PrefixLength,
Info->Precedence,
Info->SrcLabel,
Info->DstLabel);
return STATUS_SUCCESS;
}
//* CreatePersistentPrefixPolicy
//
// Creates a persistent prefix policy.
//
// SubKeyName has the following syntax:
// prefix/length
// where prefix is a literal IPv6 address.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
CreatePersistentPrefixPolicy(
void *Context,
HANDLE ParentKey,
WCHAR *SubKeyName)
{
IPV6_INFO_PREFIX_POLICY Info;
NTSTATUS Status;
UNREFERENCED_PARAMETER(Context);
PAGED_CODE();
//
// Read the prefix policy from the registry.
//
Status = ReadPersistentPrefixPolicy(&Info, ParentKey, SubKeyName);
if (! NT_SUCCESS(Status)) {
//
// If there was an error reading this policy,
// continue the enumeration.
//
if (Status == STATUS_NO_MORE_ENTRIES)
Status = STATUS_SUCCESS;
return Status;
}
//
// Create the prefix policy.
//
Status = InternalUpdatePrefixPolicy(&Info);
if (! NT_SUCCESS(Status)) {
if ((STATUS_INVALID_PARAMETER_1 <= Status) &&
(Status <= STATUS_INVALID_PARAMETER_12)) {
//
// Invalid parameter.
// But we return success so the enumeration continues.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
"CreatePersistentPrefixPolicy: bad param %ls\n",
SubKeyName));
return STATUS_SUCCESS;
}
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
"CreatePersistentPrefixPolicy: error %ls\n",
SubKeyName));
}
return Status;
}
//* ConfigurePrefixPolicies
//
// Configures prefix policies from the registry.
//
// Callable from thread context, not DPC context.
//
void
ConfigurePrefixPolicies(void)
{
HANDLE RegKey;
NTSTATUS Status;
Status = OpenTopLevelRegKey(L"PrefixPolicies", &RegKey, OpenRegKeyRead);
if (NT_SUCCESS(Status)) {
//
// Create persistent policies.
//
(void) EnumRegKeys(RegKey, CreatePersistentPrefixPolicy, NULL);
ZwClose(RegKey);
}
else {
//
// There are no persistent policies,
// so install the default policies.
//
ConfigureDefaultPrefixPolicies();
}
}
//* OpenPrefixPolicyRegKey
//
// Given a prefix with prefix length,
// opens the registry key with configuration info
// for the prefix policy.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
OpenPrefixPolicyRegKey(const IPv6Addr *Prefix, uint PrefixLength,
OUT HANDLE *RegKey, OpenRegKeyAction Action)
{
WCHAR PrefixPolicyName[64];
HANDLE PrefixPoliciesKey;
NTSTATUS Status;
PAGED_CODE();
//
// Note that if we are deleting a prefix policy,
// then we must create the top-level key if it
// doesn't exist yet. This is for ConfigurePrefixPolicies.
//
Status = OpenTopLevelRegKey(L"PrefixPolicies", &PrefixPoliciesKey,
((Action != OpenRegKeyRead) ?
OpenRegKeyCreate : OpenRegKeyRead));
if (! NT_SUCCESS(Status))
return Status;
//
// The output of RtlIpv6AddressToString may change
// over time with improvements/changes in the pretty-printing,
// and we need a consistent mapping.
// It doesn't need to be pretty.
//
swprintf(PrefixPolicyName,
L"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x/%u",
net_short(Prefix->s6_words[0]), net_short(Prefix->s6_words[1]),
net_short(Prefix->s6_words[2]), net_short(Prefix->s6_words[3]),
net_short(Prefix->s6_words[4]), net_short(Prefix->s6_words[5]),
net_short(Prefix->s6_words[6]), net_short(Prefix->s6_words[7]),
PrefixLength);
Status = OpenRegKey(RegKey, PrefixPoliciesKey, PrefixPolicyName, Action);
ZwClose(PrefixPoliciesKey);
return Status;
}
//* PersistUpdatePrefixPolicy
//
// Helper function for persisting a prefix policy in the registry.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistUpdatePrefixPolicy(IPV6_INFO_PREFIX_POLICY *Info)
{
HANDLE PolicyKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open/create the policy key.
//
Status = OpenPrefixPolicyRegKey(&Info->This.Prefix,
Info->This.PrefixLength,
&PolicyKey, OpenRegKeyCreate);
if (! NT_SUCCESS(Status))
return Status;
//
// Persist the prefix policy precedence.
//
Status = SetRegDWORDValue(PolicyKey, L"Precedence", Info->Precedence);
if (! NT_SUCCESS(Status))
goto ReturnReleasePolicyKey;
//
// Persist the prefix policy source label.
//
Status = SetRegDWORDValue(PolicyKey, L"SrcLabel", Info->SrcLabel);
if (! NT_SUCCESS(Status))
goto ReturnReleasePolicyKey;
//
// Persist the prefix policy destination label.
//
Status = SetRegDWORDValue(PolicyKey, L"DstLabel", Info->DstLabel);
if (! NT_SUCCESS(Status))
goto ReturnReleasePolicyKey;
Status = STATUS_SUCCESS;
ReturnReleasePolicyKey:
ZwClose(PolicyKey);
return Status;
}
//* IoctlUpdatePrefixPolicy
//
// Processes an IOCTL_IPV6_UPDATE_PREFIX_POLICY request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdatePrefixPolicy(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_INFO_PREFIX_POLICY *Info;
NTSTATUS Status;
PAGED_CODE();
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Info) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_INFO_PREFIX_POLICY *) Irp->AssociatedIrp.SystemBuffer;
//
// Update the prefix policy.
//
Status = InternalUpdatePrefixPolicy(Info);
if (! NT_SUCCESS(Status))
goto Return;
//
// Make the change persistent?
//
if (Persistent) {
Status = PersistUpdatePrefixPolicy(Info);
if (! NT_SUCCESS(Status))
goto Return;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdatePrefixPolicy
//* PersistDeletePrefixPolicy
//
// Helper function for deleting a prefix policy from the registry.
//
// Callable from thread context, not DPC context.
//
NTSTATUS
PersistDeletePrefixPolicy(IPV6_QUERY_PREFIX_POLICY *Query)
{
HANDLE PolicyKey;
NTSTATUS Status;
PAGED_CODE();
//
// Open the policy key. It's OK if it doesn't exist.
//
Status = OpenPrefixPolicyRegKey(&Query->Prefix, Query->PrefixLength,
&PolicyKey, OpenRegKeyDeleting);
if (! NT_SUCCESS(Status)) {
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
return STATUS_SUCCESS;
else
return Status;
}
//
// Delete the policy key.
//
Status = ZwDeleteKey(PolicyKey);
ZwClose(PolicyKey);
return Status;
}
//* IoctlDeletePrefixPolicy
//
// Processes an IOCTL_IPV6_DELETE_PREFIX_POLICY request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlDeletePrefixPolicy(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
IPV6_QUERY_PREFIX_POLICY *Query;
NTSTATUS Status;
PAGED_CODE();
Irp->IoStatus.Information = 0;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength != sizeof *Query) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Query = (IPV6_QUERY_PREFIX_POLICY *) Irp->AssociatedIrp.SystemBuffer;
if (UsingDefaultPrefixPolicies) {
//
// The user is changing the default policies for the first time.
// Remove the default policies.
//
UsingDefaultPrefixPolicies = FALSE;
PrefixPolicyReset();
}
//
// Delete the specified prefix policy.
//
PrefixPolicyDelete(&Query->Prefix, Query->PrefixLength);
//
// Make the change persistent?
//
if (Persistent) {
Status = PersistDeletePrefixPolicy(Query);
if (! NT_SUCCESS(Status))
goto Return;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlDeletePrefixPolicy
//* IoctlUpdateRouterLLAddress
//
// Processes an IOCTL_IPV6_UPDATE_ROUTER_LL_ADDRESS request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlUpdateRouterLLAddress(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp) // Current stack location in the Irp.
{
IPV6_UPDATE_ROUTER_LL_ADDRESS *Info;
NTSTATUS Status;
Interface *IF;
char *LinkAddress;
KIRQL OldIrql;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof *Info) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
Info = (IPV6_UPDATE_ROUTER_LL_ADDRESS *) Irp->AssociatedIrp.SystemBuffer;
IF = FindInterfaceFromQuery(&Info->IF);
if (IF == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Return;
}
//
// Verify that this ioctl is legal on the interface.
//
if (IF->SetRouterLLAddress == NULL) {
Status = STATUS_INVALID_PARAMETER_1;
goto Cleanup;
}
//
// Verify link-layer address length matches interface's.
//
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength !=
sizeof *Info + 2 * IF->LinkAddressLength) {
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
LinkAddress = (char *)(Info + 1);
Status = (*IF->SetRouterLLAddress)(IF->LinkContext, LinkAddress,
LinkAddress + IF->LinkAddressLength);
Cleanup:
ReleaseIF(IF);
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlUpdateRouterLLAddress
//* IoctlResetManualConfig
//
// Processes an IOCTL_IPV6_RESET request.
//
// Note: Return value indicates whether NT-specific processing of the
// request was successful. The status of the actual request is returned
// in the request buffers.
//
NTSTATUS
IoctlResetManualConfig(
IN PIRP Irp, // I/O request packet.
IN PIO_STACK_LOCATION IrpSp, // Current stack location in the Irp.
IN int Persistent)
{
HANDLE RegKey;
NTSTATUS Status;
PAGED_CODE();
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength != 0) ||
(IrpSp->Parameters.DeviceIoControl.OutputBufferLength != 0)) {
Status = STATUS_INVALID_PARAMETER;
goto Return;
}
//
// Reset the running data structures.
//
GlobalParametersReset();
InterfaceReset();
RouteTableReset();
PrefixPolicyReset();
ConfigureDefaultPrefixPolicies();
if (Persistent) {
//
// Delete all persistent configuration information.
//
Status = DeleteTopLevelRegKey(L"GlobalParams");
if (! NT_SUCCESS(Status))
goto Return;
Status = DeleteTopLevelRegKey(L"Interfaces");
if (! NT_SUCCESS(Status))
goto Return;
Status = DeleteTopLevelRegKey(L"PrefixPolicies");
if (! NT_SUCCESS(Status))
goto Return;
}
Status = STATUS_SUCCESS;
Return:
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
} // IoctlPersistentReset