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