|
|
// -*- 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"
#include "mobile.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.
//
BOOLEAN IsRunningOnWorkstation(VOID);
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
#ifdef DEBUG_OBJECTS
LIST_ENTRY FileObjectList; KSPIN_LOCK FileObjectLock;
typedef struct _FILE_OBJECT_ENTRY { LIST_ENTRY Link; PFILE_OBJECT FileObject; } FILE_OBJECT_ENTRY, *PFILE_OBJECT_ENTRY; #endif
//* 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.
{ #ifdef DEBUG_OBJECTS
PFILE_OBJECT_ENTRY Foe; KIRQL OldIrql;
Foe = ExAllocatePool(NonPagedPool, sizeof(FILE_OBJECT_ENTRY)); if (Foe == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } Foe->FileObject = IrpSp->FileObject;
KeAcquireSpinLock(&FileObjectLock, &OldIrql); InsertTailList(&FileObjectList, &Foe->Link); KeReleaseSpinLock(&FileObjectLock, OldIrql); #else
UNREFERENCED_PARAMETER(IrpSp); #endif
UNREFERENCED_PARAMETER(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;
UNREFERENCED_PARAMETER(Irp);
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.
{ #ifdef DEBUG_OBJECTS
PFILE_OBJECT_ENTRY Foe = NULL; KIRQL OldIrql; PLIST_ENTRY ple;
KeAcquireSpinLock(&FileObjectLock, &OldIrql);
for (ple = FileObjectList.Flink; ple != &FileObjectList; ple = ple->Flink) { Foe = CONTAINING_RECORD(ple, FILE_OBJECT_ENTRY, Link); if (Foe->FileObject == IrpSp->FileObject) { break; } } ASSERT(ple != &FileObjectList);
if (Foe != NULL) { RemoveEntryList(&Foe->Link); ExFreePool(Foe); }
KeReleaseSpinLock(&FileObjectLock, OldIrql); #else
UNREFERENCED_PARAMETER(IrpSp); #endif
UNREFERENCED_PARAMETER(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;
UNREFERENCED_PARAMETER(Device);
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 = NULL; EchoControl *controlBlock; PPENDING_IRP pendingIrp = NULL; PPENDING_IRP item; PLIST_ENTRY entry;
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; PPENDING_IRP pendingIrp; EchoControl *controlBlock; 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; Info->FirewallEnabled = !!(IF->Flags & IF_FLAG_FIREWALL_ENABLED); Info->DefSitePrefixLength = IF->DefSitePrefixLength;
//
// 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", (uint *)&Info->Type, (uint)-1); InitRegDWORDParameter(IFKey, L"RouterDiscovers", (uint *)&Info->RouterDiscovers, (uint)-1); InitRegDWORDParameter(IFKey, L"NeighborDiscovers", (uint *)&Info->NeighborDiscovers, (uint)-1); InitRegDWORDParameter(IFKey, L"PeriodicMLD", (uint *)&Info->PeriodicMLD, (uint)-1); InitRegDWORDParameter(IFKey, L"Advertises", (uint *)&Info->Advertises, (uint)-1); InitRegDWORDParameter(IFKey, L"Forwards", (uint *)&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); InitRegDWORDParameter(IFKey, L"FirewallEnabled", (uint *)&Info->FirewallEnabled, (uint)-1); InitRegDWORDParameter(IFKey, L"DefSitePrefixLength", &Info->DefSitePrefixLength, (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", (uint *)&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; 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", (uint *)&Info->Publish, FALSE);
//
// Read the Immortal flag.
//
InitRegDWORDParameter(RouteKey, L"Immortal", (uint *)&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; 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_TEMPORARY) 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; 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; int FirewallEnabled = Info->FirewallEnabled; 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; if (FirewallEnabled == -1) FirewallEnabled = 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; if (FirewallEnabled == -1) FirewallEnabled = 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) | (FirewallEnabled ? IF_FLAG_FIREWALL_ENABLED : 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; }
if (Info->FirewallEnabled != -1) { Status = SetRegDWORDValue(IFKey, L"FirewallEnabled", Info->FirewallEnabled); 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_INVALID_PARAMETER_6 Bad DefSitePrefixLength.
// 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 ((Info->DefSitePrefixLength != (uint)-1) && (Info->DefSitePrefixLength > IPV6_ADDRESS_LENGTH)) return STATUS_INVALID_PARAMETER_6;
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.
//
KeAcquireSpinLockAtDpcLevel(&IF->Lock); UpdateZoneIndices(IF, Info->ZoneIndices); KeReleaseSpinLockFromDpcLevel(&IF->Lock); 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; }
//
// Update the firewall mode.
//
if (Info->FirewallEnabled != -1) { KeAcquireSpinLock(&IF->Lock, &OldIrql); if (Info->FirewallEnabled) IF->Flags |= IF_FLAG_FIREWALL_ENABLED; else IF->Flags &= ~IF_FLAG_FIREWALL_ENABLED; KeReleaseSpinLock(&IF->Lock, OldIrql); }
//
// Update the default site prefix length.
//
if (Info->DefSitePrefixLength != (uint)-1) { //
// No lock needed.
//
IF->DefSitePrefixLength = Info->DefSitePrefixLength; }
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->FirewallEnabled != -1) { Status = SetRegDWORDValue(RegKey, L"FirewallEnabled", Info->FirewallEnabled); 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; }
if (Info->DefSitePrefixLength != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"DefSitePrefixLength", Info->DefSitePrefixLength); 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; 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; SIZE_T GapSize; PAGED_CODE();
NumAddrsIn = IrpSp->Parameters.DeviceIoControl.InputBufferLength / sizeof(TDI_ADDRESS_IP6); NumAddrsOut = NumAddrsIn;
//
// The addition in the 3rd part of the if statement can overflow,
// to prevent this, we limit the InputBufferLength to MAXLONG.
//
if ((IrpSp->Parameters.DeviceIoControl.InputBufferLength > (ULONG) MAXLONG) || (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);
//
// Zero the unitialized portion of the output buffer.
//
GapSize = (ULONG_PTR) Key - (ULONG_PTR) (Addrs + NumAddrsIn); if (GapSize > 0) { RtlZeroMemory(Addrs + NumAddrsIn, GapSize); } //
// 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; uint ValidLifetime; 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;
UNREFERENCED_PARAMETER(DeviceObject);
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;
UNREFERENCED_PARAMETER(DeviceObject);
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"UseTemporaryAddresses", &Params->UseTemporaryAddresses, (uint)-1);
InitRegDWORDParameter(RegKey, L"MaxTempDADAttempts", &Params->MaxTempDADAttempts, (uint)-1);
InitRegDWORDParameter(RegKey, L"MaxTempValidLifetime", &Params->MaxTempValidLifetime, (uint)-1);
InitRegDWORDParameter(RegKey, L"MaxTempPreferredLifetime", &Params->MaxTempPreferredLifetime, (uint)-1);
InitRegDWORDParameter(RegKey, L"TempRegenerateTime", &Params->TempRegenerateTime, (uint)-1);
InitRegDWORDParameter(RegKey, L"MaxTempRandomTime", &Params->MaxTempRandomTime, (uint)-1);
Params->TempRandomTime = 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", (uint *)&Params->MobilitySecurity, (uint)-1);
InitRegDWORDParameter(RegKey, L"MobileIPv6Mode", (uint *)&Params->MobileIPv6Mode, (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->UseTemporaryAddresses = UseTemporaryAddresses; Params->MaxTempDADAttempts = MaxTempDADAttempts; Params->MaxTempValidLifetime = ConvertTicksToSeconds(MaxTempValidLifetime); Params->MaxTempPreferredLifetime = ConvertTicksToSeconds(MaxTempPreferredLifetime); Params->TempRegenerateTime = ConvertTicksToSeconds(TempRegenerateTime); Params->MaxTempRandomTime = ConvertTicksToSeconds(MaxTempRandomTime); Params->TempRandomTime = ConvertTicksToSeconds(TempRandomTime); Params->NeighborCacheLimit = NeighborCacheLimit; Params->RouteCacheLimit = RouteCache.Limit; Params->BindingCacheLimit = BindingCache.Limit; Params->ReassemblyLimit = ReassemblyList.Limit; Params->MobilitySecurity = MobilitySecurity; Params->MobileIPv6Mode = MobileIPv6Mode; }
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 UseTemporaryAddresses.
// STATUS_INVALID_PARAMETER_3 Bad temporary times.
// STATUS_INVALID_PARAMETER_4 Bad MobileIPv6Mode.
// STATUS_SUCCESS
//
NTSTATUS InternalUpdateGlobalParameters(IPV6_GLOBAL_PARAMETERS *Params) { uint NewMaxTempValidLifetime; uint NewMaxTempPreferredLifetime; uint NewTempRegenerateTime; uint NewMaxTempRandomTime; uint NewTempRandomTime;
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->UseTemporaryAddresses != (uint)-1) { if (Params->UseTemporaryAddresses > USE_TEMP_COUNTER) return STATUS_INVALID_PARAMETER_2; }
if (Params->MaxTempValidLifetime != (uint)-1) NewMaxTempValidLifetime = ConvertSecondsToTicks(Params->MaxTempValidLifetime); else NewMaxTempValidLifetime = MaxTempValidLifetime;
if (Params->MaxTempPreferredLifetime != (uint)-1) NewMaxTempPreferredLifetime = ConvertSecondsToTicks(Params->MaxTempPreferredLifetime); else NewMaxTempPreferredLifetime = MaxTempPreferredLifetime;
if (Params->TempRegenerateTime != (uint)-1) NewTempRegenerateTime = ConvertSecondsToTicks(Params->TempRegenerateTime); else NewTempRegenerateTime = TempRegenerateTime;
if (Params->MaxTempRandomTime != (uint)-1) NewMaxTempRandomTime = ConvertSecondsToTicks(Params->MaxTempRandomTime); else NewMaxTempRandomTime = MaxTempRandomTime;
if (Params->TempRandomTime == 0) NewTempRandomTime = RandomNumber(0, NewMaxTempRandomTime); else if (Params->TempRandomTime == (uint)-1) NewTempRandomTime = TempRandomTime; else NewTempRandomTime = ConvertSecondsToTicks(Params->TempRandomTime);
if (!(NewTempRandomTime <= NewMaxTempRandomTime) || !(NewTempRegenerateTime + NewMaxTempRandomTime < NewMaxTempPreferredLifetime) || !(NewMaxTempPreferredLifetime <= NewMaxTempValidLifetime)) return STATUS_INVALID_PARAMETER_3;
if (Params->MobileIPv6Mode != (uint)-1) { //
// For now, we only support correspondent operation.
//
if (Params->MobileIPv6Mode &~ MOBILE_CORRESPONDENT) return STATUS_INVALID_PARAMETER_4; }
//
// Set the new values.
//
if (Params->DefaultCurHopLimit != (uint)-1) DefaultCurHopLimit = Params->DefaultCurHopLimit;
if (Params->UseTemporaryAddresses != (uint)-1) UseTemporaryAddresses = Params->UseTemporaryAddresses;
if (Params->MaxTempDADAttempts != (uint)-1) MaxTempDADAttempts = Params->MaxTempDADAttempts;
MaxTempValidLifetime = NewMaxTempValidLifetime; MaxTempPreferredLifetime = NewMaxTempPreferredLifetime; TempRegenerateTime = NewTempRegenerateTime; MaxTempRandomTime = NewMaxTempRandomTime; TempRandomTime = NewTempRandomTime;
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;
if (Params->MobileIPv6Mode != (uint)-1) MobileIPv6Mode = Params->MobileIPv6Mode;
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; 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.UseTemporaryAddresses = (IsRunningOnWorkstation() ? USE_TEMP_YES : USE_TEMP_NO); Params.MaxTempDADAttempts = MAX_TEMP_DAD_ATTEMPTS; Params.MaxTempValidLifetime = MAX_TEMP_VALID_LIFETIME; Params.MaxTempPreferredLifetime = MAX_TEMP_PREFERRED_LIFETIME; Params.TempRegenerateTime = TEMP_REGENERATE_TIME; Params.MaxTempRandomTime = MAX_TEMP_RANDOM_TIME; Params.TempRandomTime = 0; Params.NeighborCacheLimit = NEIGHBOR_CACHE_LIMIT; Params.RouteCacheLimit = ROUTE_CACHE_LIMIT; Params.BindingCacheLimit = BINDING_CACHE_LIMIT; Params.ReassemblyLimit = DefaultReassemblyLimit(); Params.MobilitySecurity = TRUE; Params.MobileIPv6Mode = MIPV6_DEFAULT_MODE;
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->UseTemporaryAddresses != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"UseTemporaryAddresses", Params->UseTemporaryAddresses); if (! NT_SUCCESS(Status)) goto ReturnReleaseKey; }
if (Params->MaxTempDADAttempts != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"MaxTempDADAttempts", Params->MaxTempDADAttempts); if (! NT_SUCCESS(Status)) goto ReturnReleaseKey; }
if (Params->MaxTempValidLifetime != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"MaxTempValidLifetime", Params->MaxTempValidLifetime); if (! NT_SUCCESS(Status)) goto ReturnReleaseKey; }
if (Params->MaxTempPreferredLifetime != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"MaxTempPreferredLifetime", Params->MaxTempPreferredLifetime); if (! NT_SUCCESS(Status)) goto ReturnReleaseKey; }
if (Params->TempRegenerateTime != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"TempRegenerateTime", Params->TempRegenerateTime); if (! NT_SUCCESS(Status)) goto ReturnReleaseKey; }
if (Params->MaxTempRandomTime != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"MaxTempRandomTime", Params->MaxTempRandomTime); 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; }
if (Params->MobileIPv6Mode != (uint)-1) { Status = SetRegDWORDValue(RegKey, L"MobileIPv6Mode", Params->MobileIPv6Mode); 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", (uint *)&Info->Precedence, 0); InitRegDWORDParameter(PolicyKey, L"SrcLabel", (uint *)&Info->SrcLabel, 0); InitRegDWORDParameter(PolicyKey, L"DstLabel", (uint *)&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;
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) { 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
|