3204 lines
92 KiB
C
3204 lines
92 KiB
C
|
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
|
||
|
//
|
||
|
// Copyright (c) 1998-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:
|
||
|
//
|
||
|
// Generic support for running IPv6 over IPv4.
|
||
|
//
|
||
|
|
||
|
|
||
|
#include "oscfg.h"
|
||
|
#include "ndis.h"
|
||
|
#include "ip6imp.h"
|
||
|
#include "ip6def.h"
|
||
|
#include "llip6if.h"
|
||
|
#include "tdi.h"
|
||
|
#include "tdiinfo.h"
|
||
|
#include "tdikrnl.h"
|
||
|
#include "tdistat.h"
|
||
|
#include "tunnel.h"
|
||
|
#include "ntddtcp.h"
|
||
|
#include "tcpinfo.h"
|
||
|
#include "icmp.h"
|
||
|
#include "neighbor.h"
|
||
|
#include "route.h"
|
||
|
#include "security.h"
|
||
|
#include <stdio.h>
|
||
|
#include "ntddip6.h"
|
||
|
#include "icmp.h"
|
||
|
|
||
|
//
|
||
|
// Our globals are all in one structure.
|
||
|
//
|
||
|
|
||
|
TunnelGlobals Tunnel;
|
||
|
|
||
|
//* TunnelSetAddressObjectInformation
|
||
|
//
|
||
|
// Set information on the TDI address object.
|
||
|
//
|
||
|
// Our caller should initialize the ID.toi_id, BufferSize, Buffer
|
||
|
// fields of the SetInfo structure, but we initialize the rest.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetAddressObjectInformation(
|
||
|
PFILE_OBJECT AO,
|
||
|
PTCP_REQUEST_SET_INFORMATION_EX SetInfo,
|
||
|
ULONG SetInfoSize)
|
||
|
{
|
||
|
IO_STATUS_BLOCK iosb;
|
||
|
KEVENT event;
|
||
|
NTSTATUS status;
|
||
|
PIRP irp;
|
||
|
PIO_STACK_LOCATION irpSp;
|
||
|
|
||
|
//
|
||
|
// Finish initialization of the request structure for this IOCTL.
|
||
|
//
|
||
|
SetInfo->ID.toi_entity.tei_entity = CL_TL_ENTITY;
|
||
|
SetInfo->ID.toi_entity.tei_instance = 0;
|
||
|
SetInfo->ID.toi_class = INFO_CLASS_PROTOCOL;
|
||
|
SetInfo->ID.toi_type = INFO_TYPE_ADDRESS_OBJECT;
|
||
|
|
||
|
//
|
||
|
// Initialize the event that we use to wait.
|
||
|
//
|
||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
|
||
|
//
|
||
|
// Create and initialize the IRP for this operation.
|
||
|
//
|
||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_TCP_SET_INFORMATION_EX,
|
||
|
AO->DeviceObject,
|
||
|
SetInfo,
|
||
|
SetInfoSize,
|
||
|
NULL, // output buffer
|
||
|
0, // output buffer length
|
||
|
FALSE, // internal device control?
|
||
|
&event,
|
||
|
&iosb);
|
||
|
if (irp == NULL)
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
|
||
|
iosb.Status = STATUS_UNSUCCESSFUL;
|
||
|
iosb.Information = (ULONG)-1;
|
||
|
|
||
|
irpSp = IoGetNextIrpStackLocation(irp);
|
||
|
irpSp->FileObject = AO;
|
||
|
|
||
|
//
|
||
|
// Make the IOCTL, waiting for it to finish if necessary.
|
||
|
//
|
||
|
status = IoCallDriver(AO->DeviceObject, irp);
|
||
|
if (status == STATUS_PENDING) {
|
||
|
KeWaitForSingleObject(&event, Executive, KernelMode,
|
||
|
FALSE, NULL);
|
||
|
status = iosb.Status;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//* TunnelSetAddressObjectUCastIF
|
||
|
//
|
||
|
// Binds the TDI address object to a particular interface.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetAddressObjectUCastIF(PFILE_OBJECT AO, IPAddr Address)
|
||
|
{
|
||
|
PTCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
union { // get correct alignment
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
char bytes[sizeof *setInfo - sizeof setInfo->Buffer + sizeof(IPAddr)];
|
||
|
} buffer;
|
||
|
|
||
|
setInfo = &buffer.setInfo;
|
||
|
setInfo->ID.toi_id = AO_OPTION_IP_UCASTIF;
|
||
|
setInfo->BufferSize = sizeof(IPAddr);
|
||
|
* (IPAddr *) setInfo->Buffer = Address;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, setInfo, sizeof buffer);
|
||
|
}
|
||
|
|
||
|
//* TunnelSetAddressObjectTTL
|
||
|
//
|
||
|
// Set the unicast TTL on a TDI address object.
|
||
|
// This sets the v4 header TTL that will be used
|
||
|
// for unicast packets sent via this TDI address object.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetAddressObjectTTL(PFILE_OBJECT AO, uchar TTL)
|
||
|
{
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
|
||
|
setInfo.ID.toi_id = AO_OPTION_TTL;
|
||
|
setInfo.BufferSize = 1;
|
||
|
setInfo.Buffer[0] = TTL;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, &setInfo, sizeof setInfo);
|
||
|
}
|
||
|
|
||
|
//* TunnelSetAddressObjectMCastTTL
|
||
|
//
|
||
|
// Set the multicast TTL on a TDI address object.
|
||
|
// This sets the v4 header TTL that will be used
|
||
|
// for multicast packets sent via this TDI address object.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetAddressObjectMCastTTL(PFILE_OBJECT AO, uchar TTL)
|
||
|
{
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
|
||
|
setInfo.ID.toi_id = AO_OPTION_MCASTTTL;
|
||
|
setInfo.BufferSize = 1;
|
||
|
setInfo.Buffer[0] = TTL;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, &setInfo, sizeof setInfo);
|
||
|
}
|
||
|
|
||
|
//* TunnelSetAddressObjectMCastIF
|
||
|
//
|
||
|
// Set the multicast interface on a TDI address object.
|
||
|
// This sets the v4 source address that will be used
|
||
|
// for multicast packets sent via this TDI address object.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetAddressObjectMCastIF(PFILE_OBJECT AO, IPAddr Address)
|
||
|
{
|
||
|
PTCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
UDPMCastIFReq *req;
|
||
|
union { // get correct alignment
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
char bytes[sizeof *setInfo - sizeof setInfo->Buffer + sizeof *req];
|
||
|
} buffer;
|
||
|
|
||
|
setInfo = &buffer.setInfo;
|
||
|
setInfo->ID.toi_id = AO_OPTION_MCASTIF;
|
||
|
setInfo->BufferSize = sizeof *req;
|
||
|
req = (UDPMCastIFReq *) setInfo->Buffer;
|
||
|
req->umi_addr = Address;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, setInfo, sizeof buffer);
|
||
|
}
|
||
|
|
||
|
//* TunnelSetAddressObjectMCastLoop
|
||
|
//
|
||
|
// Controls multicast loopback on a TDI address object.
|
||
|
// This controls whether looped-back multicast packets
|
||
|
// can be received via this address object.
|
||
|
// (IPv4 multicast loopback uses Winsock semantics, not BSD semantics.)
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetAddressObjectMCastLoop(PFILE_OBJECT AO, int Loop)
|
||
|
{
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
|
||
|
setInfo.ID.toi_id = AO_OPTION_MCASTLOOP;
|
||
|
setInfo.BufferSize = 1;
|
||
|
setInfo.Buffer[0] = (uchar)Loop;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, &setInfo, sizeof setInfo);
|
||
|
}
|
||
|
|
||
|
//* TunnelAddMulticastAddress
|
||
|
//
|
||
|
// Indicate to the v4 stack that we would like to receive
|
||
|
// on a multicast address.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelAddMulticastAddress(
|
||
|
PFILE_OBJECT AO,
|
||
|
IPAddr IfAddress,
|
||
|
IPAddr MCastAddress)
|
||
|
{
|
||
|
PTCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
UDPMCastReq *req;
|
||
|
union { // get correct alignment
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
char bytes[sizeof *setInfo - sizeof setInfo->Buffer + sizeof *req];
|
||
|
} buffer;
|
||
|
|
||
|
setInfo = &buffer.setInfo;
|
||
|
setInfo->ID.toi_id = AO_OPTION_ADD_MCAST;
|
||
|
setInfo->BufferSize = sizeof *req;
|
||
|
req = (UDPMCastReq *) setInfo->Buffer;
|
||
|
req->umr_if = IfAddress;
|
||
|
req->umr_addr = MCastAddress;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, setInfo, sizeof buffer);
|
||
|
}
|
||
|
|
||
|
//* TunnelDelMulticastAddress
|
||
|
//
|
||
|
// Indicate to the v4 stack that we are no longer
|
||
|
// interested in a multicast address.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelDelMulticastAddress(
|
||
|
PFILE_OBJECT AO,
|
||
|
IPAddr IfAddress,
|
||
|
IPAddr MCastAddress)
|
||
|
{
|
||
|
PTCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
UDPMCastReq *req;
|
||
|
union { // get correct alignment
|
||
|
TCP_REQUEST_SET_INFORMATION_EX setInfo;
|
||
|
char bytes[sizeof *setInfo - sizeof setInfo->Buffer + sizeof *req];
|
||
|
} buffer;
|
||
|
|
||
|
setInfo = &buffer.setInfo;
|
||
|
setInfo->ID.toi_id = AO_OPTION_DEL_MCAST;
|
||
|
setInfo->BufferSize = sizeof *req;
|
||
|
req = (UDPMCastReq *) setInfo->Buffer;
|
||
|
req->umr_if = IfAddress;
|
||
|
req->umr_addr = MCastAddress;
|
||
|
|
||
|
return TunnelSetAddressObjectInformation(AO, setInfo, sizeof buffer);
|
||
|
}
|
||
|
|
||
|
//* TunnelGetAddressObjectInformation
|
||
|
//
|
||
|
// Get information from the TDI address object.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelGetAddressObjectInformation(
|
||
|
PFILE_OBJECT AO,
|
||
|
PTCP_REQUEST_QUERY_INFORMATION_EX GetInfo,
|
||
|
ULONG GetInfoSize,
|
||
|
PVOID Buffer,
|
||
|
ULONG BufferSize)
|
||
|
{
|
||
|
IO_STATUS_BLOCK iosb;
|
||
|
KEVENT event;
|
||
|
NTSTATUS status;
|
||
|
PIRP irp;
|
||
|
PIO_STACK_LOCATION irpSp;
|
||
|
|
||
|
//
|
||
|
// Initialize the event that we use to wait.
|
||
|
//
|
||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
|
||
|
//
|
||
|
// Create and initialize the IRP for this operation.
|
||
|
//
|
||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_TCP_QUERY_INFORMATION_EX,
|
||
|
AO->DeviceObject,
|
||
|
GetInfo,
|
||
|
GetInfoSize,
|
||
|
Buffer, // output buffer
|
||
|
BufferSize, // output buffer length
|
||
|
FALSE, // internal device control?
|
||
|
&event,
|
||
|
&iosb);
|
||
|
if (irp == NULL)
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
|
||
|
iosb.Status = STATUS_UNSUCCESSFUL;
|
||
|
iosb.Information = (ULONG)-1;
|
||
|
|
||
|
irpSp = IoGetNextIrpStackLocation(irp);
|
||
|
irpSp->FileObject = AO;
|
||
|
|
||
|
//
|
||
|
// Make the IOCTL, waiting for it to finish if necessary.
|
||
|
//
|
||
|
status = IoCallDriver(AO->DeviceObject, irp);
|
||
|
if (status == STATUS_PENDING) {
|
||
|
KeWaitForSingleObject(&event, Executive, KernelMode,
|
||
|
FALSE, NULL);
|
||
|
status = iosb.Status;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//* TunnelGetSourceAddress
|
||
|
//
|
||
|
// Finds the source address that the IPv4 stack
|
||
|
// would use to send to the destination address.
|
||
|
// Returns FALSE upon failure.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
int
|
||
|
TunnelGetSourceAddress(IPAddr Dest, IPAddr *Source)
|
||
|
{
|
||
|
PTCP_REQUEST_QUERY_INFORMATION_EX getInfo;
|
||
|
IPAddr *req;
|
||
|
union { // get correct alignment
|
||
|
TCP_REQUEST_QUERY_INFORMATION_EX getInfo;
|
||
|
char bytes[sizeof *getInfo - sizeof getInfo->Context + sizeof *req];
|
||
|
} buffer;
|
||
|
IPRouteEntry route;
|
||
|
|
||
|
getInfo = &buffer.getInfo;
|
||
|
getInfo->ID.toi_entity.tei_entity = CL_NL_ENTITY;
|
||
|
getInfo->ID.toi_entity.tei_instance = 0;
|
||
|
getInfo->ID.toi_class = INFO_CLASS_PROTOCOL;
|
||
|
getInfo->ID.toi_type = INFO_TYPE_PROVIDER;
|
||
|
getInfo->ID.toi_id = IP_GET_BEST_SOURCE;
|
||
|
|
||
|
req = (IPAddr *) &getInfo->Context;
|
||
|
*req = Dest;
|
||
|
|
||
|
return (NT_SUCCESS(TunnelGetAddressObjectInformation(
|
||
|
Tunnel.List.AOFile,
|
||
|
getInfo, sizeof buffer,
|
||
|
Source, sizeof *Source)) &&
|
||
|
(*Source != INADDR_ANY));
|
||
|
}
|
||
|
|
||
|
//* TunnelOpenAddressObject
|
||
|
//
|
||
|
// Opens a raw IPv4 address object,
|
||
|
// returning a handle (or NULL on error).
|
||
|
//
|
||
|
HANDLE
|
||
|
TunnelOpenAddressObject(IPAddr Address, WCHAR *DeviceName)
|
||
|
{
|
||
|
UNICODE_STRING objectName;
|
||
|
OBJECT_ATTRIBUTES objectAttributes;
|
||
|
IO_STATUS_BLOCK iosb;
|
||
|
PTRANSPORT_ADDRESS transportAddress;
|
||
|
TA_IP_ADDRESS taIPAddress;
|
||
|
union { // get correct alignment
|
||
|
FILE_FULL_EA_INFORMATION ea;
|
||
|
char bytes[sizeof(FILE_FULL_EA_INFORMATION) - 1 +
|
||
|
TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
|
||
|
sizeof taIPAddress];
|
||
|
} eaBuffer;
|
||
|
PFILE_FULL_EA_INFORMATION ea = &eaBuffer.ea;
|
||
|
HANDLE tdiHandle;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
//
|
||
|
// Initialize an IPv4 address.
|
||
|
//
|
||
|
taIPAddress.TAAddressCount = 1;
|
||
|
taIPAddress.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
|
||
|
taIPAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
||
|
taIPAddress.Address[0].Address[0].sin_port = 0;
|
||
|
taIPAddress.Address[0].Address[0].in_addr = Address;
|
||
|
|
||
|
//
|
||
|
// Initialize the extended-attributes information,
|
||
|
// to indicate that we are opening an address object
|
||
|
// with the specified IPv4 address.
|
||
|
//
|
||
|
ea->NextEntryOffset = 0;
|
||
|
ea->Flags = 0;
|
||
|
ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
|
||
|
ea->EaValueLength = (USHORT)sizeof taIPAddress;
|
||
|
|
||
|
RtlMoveMemory(ea->EaName, TdiTransportAddress, ea->EaNameLength + 1);
|
||
|
|
||
|
transportAddress = (PTRANSPORT_ADDRESS)(&ea->EaName[ea->EaNameLength + 1]);
|
||
|
|
||
|
RtlMoveMemory(transportAddress, &taIPAddress, sizeof taIPAddress);
|
||
|
|
||
|
//
|
||
|
// Open a raw IP address object.
|
||
|
//
|
||
|
|
||
|
RtlInitUnicodeString(&objectName, DeviceName);
|
||
|
|
||
|
InitializeObjectAttributes(&objectAttributes,
|
||
|
&objectName,
|
||
|
OBJ_CASE_INSENSITIVE, // Attributes
|
||
|
NULL, // RootDirectory
|
||
|
NULL); // SecurityDescriptor
|
||
|
|
||
|
status = ZwCreateFile(&tdiHandle,
|
||
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
||
|
&objectAttributes,
|
||
|
&iosb,
|
||
|
NULL, // AllocationSize
|
||
|
0, // FileAttributes
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
|
FILE_CREATE,
|
||
|
0, // CreateOptions
|
||
|
ea,
|
||
|
sizeof eaBuffer);
|
||
|
if (!NT_SUCCESS(status))
|
||
|
return NULL;
|
||
|
|
||
|
return tdiHandle;
|
||
|
}
|
||
|
|
||
|
//* TunnelObjectAddRef
|
||
|
//
|
||
|
// Adds another reference to an existing file object.
|
||
|
//
|
||
|
// Callable from thread or DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelObjectAddRef(FILE_OBJECT *File)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
Status = ObReferenceObjectByPointer(File,
|
||
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
||
|
NULL, // object type
|
||
|
KernelMode);
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
}
|
||
|
|
||
|
//* TunnelObjectFromHandle
|
||
|
//
|
||
|
// Converts a handle to an object pointer.
|
||
|
//
|
||
|
FILE_OBJECT *
|
||
|
TunnelObjectFromHandle(HANDLE Handle)
|
||
|
{
|
||
|
PVOID Object;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
Status = ObReferenceObjectByHandle(
|
||
|
Handle,
|
||
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
||
|
NULL, // object type
|
||
|
KernelMode,
|
||
|
&Object,
|
||
|
NULL); // handle info
|
||
|
ASSERT(NT_SUCCESS(Status));
|
||
|
ASSERT(Object != NULL);
|
||
|
|
||
|
return Object;
|
||
|
}
|
||
|
|
||
|
typedef struct TunnelOpenAddressContext {
|
||
|
WORK_QUEUE_ITEM WQItem;
|
||
|
IPAddr Addr;
|
||
|
HANDLE AOHandle;
|
||
|
FILE_OBJECT *AOFile;
|
||
|
KEVENT Event;
|
||
|
} TunnelOpenAddressContext;
|
||
|
|
||
|
//* TunnelOpenAddressHelper
|
||
|
//
|
||
|
// Opens a tunnel address object.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
// Callable in kernel process context only.
|
||
|
// Called with the tunnel mutex held.
|
||
|
//
|
||
|
void
|
||
|
TunnelOpenAddressHelper(TunnelOpenAddressContext *oac)
|
||
|
{
|
||
|
oac->AOHandle = TunnelOpenAddressObject(oac->Addr,
|
||
|
TUNNEL_DEVICE_NAME(IP_PROTOCOL_V6));
|
||
|
if (oac->AOHandle != NULL)
|
||
|
oac->AOFile = TunnelObjectFromHandle(oac->AOHandle);
|
||
|
else
|
||
|
oac->AOFile = NULL;
|
||
|
}
|
||
|
|
||
|
//* TunnelOpenAddressWorker
|
||
|
//
|
||
|
// Executes the open operations in a worker thread context.
|
||
|
//
|
||
|
void
|
||
|
TunnelOpenAddressWorker(void *Context)
|
||
|
{
|
||
|
TunnelOpenAddressContext *oac =
|
||
|
(TunnelOpenAddressContext *) Context;
|
||
|
|
||
|
TunnelOpenAddressHelper(oac);
|
||
|
KeSetEvent(&oac->Event, 0, FALSE);
|
||
|
}
|
||
|
|
||
|
//* TunnelOpenAddress
|
||
|
//
|
||
|
// Address objects must be opened in the kernel process context,
|
||
|
// so they will not be tied to a particular user process.
|
||
|
//
|
||
|
// The main input is tc->SrcAddr, but also uses tc->DstAddr.
|
||
|
// Initializes tc->AOHandle and tc->AOFile.
|
||
|
// If there is an error opening the address object,
|
||
|
// they are both initialized to NULL.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelOpenAddress(TunnelContext *tc)
|
||
|
{
|
||
|
TunnelOpenAddressContext oac;
|
||
|
KIRQL OldIrql;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
oac.Addr = tc->SrcAddr;
|
||
|
|
||
|
if (IoGetCurrentProcess() != Tunnel.KernelProcess) {
|
||
|
//
|
||
|
// We are in the wrong process context, so
|
||
|
// punt this operation to a worker thread.
|
||
|
// Initialize and queue the work item -
|
||
|
// it will execute asynchronously.
|
||
|
//
|
||
|
ExInitializeWorkItem(&oac.WQItem, TunnelOpenAddressWorker, &oac);
|
||
|
KeInitializeEvent(&oac.Event, SynchronizationEvent, FALSE);
|
||
|
ExQueueWorkItem(&oac.WQItem, CriticalWorkQueue);
|
||
|
|
||
|
//
|
||
|
// Wait for the work item to finish.
|
||
|
//
|
||
|
(void) KeWaitForSingleObject(&oac.Event, UserRequest,
|
||
|
KernelMode, FALSE, NULL);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// It's safe for us to open the address object directly.
|
||
|
//
|
||
|
TunnelOpenAddressHelper(&oac);
|
||
|
}
|
||
|
|
||
|
if (oac.AOFile != NULL) {
|
||
|
//
|
||
|
// Tunnel.V4Device might be null if TunnelOpenV4 failed.
|
||
|
// Which would be bizarre but conceivable.
|
||
|
// It would mean we could send tunneled packets but not receive.
|
||
|
//
|
||
|
ASSERT((Tunnel.V4Device == NULL) ||
|
||
|
(oac.AOFile->DeviceObject == Tunnel.V4Device));
|
||
|
|
||
|
//
|
||
|
// Finish initializing the new address object.
|
||
|
//
|
||
|
Status = TunnelSetAddressObjectUCastIF(oac.AOFile, oac.Addr);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenAddress(%s): "
|
||
|
"TunnelSetAddressObjectUCastIF -> %x\n",
|
||
|
FormatV4Address(oac.Addr), Status));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For 6over4 interfaces, set additional options.
|
||
|
//
|
||
|
if (tc->DstAddr == INADDR_ANY) {
|
||
|
|
||
|
Status = TunnelSetAddressObjectTTL(oac.AOFile,
|
||
|
TUNNEL_6OVER4_TTL);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenAddress(%s): "
|
||
|
"TunnelSetAddressObjectTTL -> %x\n",
|
||
|
FormatV4Address(oac.Addr), Status));
|
||
|
}
|
||
|
|
||
|
Status = TunnelSetAddressObjectMCastTTL(oac.AOFile,
|
||
|
TUNNEL_6OVER4_TTL);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenAddress(%s): "
|
||
|
"TunnelSetAddressObjectMCastTTL -> %x\n",
|
||
|
FormatV4Address(oac.Addr), Status));
|
||
|
}
|
||
|
|
||
|
Status = TunnelSetAddressObjectMCastIF(oac.AOFile, oac.Addr);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenAddress(%s): "
|
||
|
"TunnelSetAddressObjectMCastIF -> %x\n",
|
||
|
FormatV4Address(oac.Addr), Status));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now that the address object is initialized,
|
||
|
// we can update the tunnel context.
|
||
|
// We need both the mutex and spinlock for update.
|
||
|
// NB: In some paths, the tunnel context is not yet
|
||
|
// on a list and so the locks are not needed.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
tc->AOHandle = oac.AOHandle;
|
||
|
tc->AOFile = oac.AOFile;
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
}
|
||
|
|
||
|
typedef struct TunnelCloseAddressObjectContext {
|
||
|
WORK_QUEUE_ITEM WQItem;
|
||
|
HANDLE Handle;
|
||
|
KEVENT Event;
|
||
|
} TunnelCloseAddressObjectContext;
|
||
|
|
||
|
//* TunnelCloseAddressObjectWorker
|
||
|
//
|
||
|
// Executes the close operation in a worker thread context.
|
||
|
//
|
||
|
void
|
||
|
TunnelCloseAddressObjectWorker(void *Context)
|
||
|
{
|
||
|
TunnelCloseAddressObjectContext *chc =
|
||
|
(TunnelCloseAddressObjectContext *) Context;
|
||
|
|
||
|
ZwClose(chc->Handle);
|
||
|
KeSetEvent(&chc->Event, 0, FALSE);
|
||
|
}
|
||
|
|
||
|
//* TunnelCloseAddressObject
|
||
|
//
|
||
|
// Because the address object handles are opened in the kernel process
|
||
|
// context, we must always close them in the kernel process context.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelCloseAddressObject(HANDLE Handle)
|
||
|
{
|
||
|
if (IoGetCurrentProcess() != Tunnel.KernelProcess) {
|
||
|
TunnelCloseAddressObjectContext chc;
|
||
|
|
||
|
//
|
||
|
// We are in the wrong process context, so
|
||
|
// punt this operation to a worker thread.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Initialize and queue the work item -
|
||
|
// it will execute asynchronously.
|
||
|
//
|
||
|
ExInitializeWorkItem(&chc.WQItem,
|
||
|
TunnelCloseAddressObjectWorker, &chc);
|
||
|
chc.Handle = Handle;
|
||
|
KeInitializeEvent(&chc.Event, SynchronizationEvent, FALSE);
|
||
|
ExQueueWorkItem(&chc.WQItem, CriticalWorkQueue);
|
||
|
|
||
|
//
|
||
|
// Wait for the work item to finish.
|
||
|
//
|
||
|
(void) KeWaitForSingleObject(&chc.Event, UserRequest,
|
||
|
KernelMode, FALSE, NULL);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// It's safe for us to close the handle directly.
|
||
|
//
|
||
|
ZwClose(Handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//* TunnelInsertTunnel
|
||
|
//
|
||
|
// Insert a tunnel on the global list.
|
||
|
// Called with both tunnel locks held.
|
||
|
//
|
||
|
void
|
||
|
TunnelInsertTunnel(TunnelContext *tc, TunnelContext *List)
|
||
|
{
|
||
|
tc->Next = List->Next;
|
||
|
tc->Prev = List;
|
||
|
List->Next->Prev = tc;
|
||
|
List->Next = tc;
|
||
|
}
|
||
|
|
||
|
//* TunnelRemoveTunnel
|
||
|
//
|
||
|
// Remove a tunnel from the global list.
|
||
|
// Called with both tunnel locks held.
|
||
|
//
|
||
|
void
|
||
|
TunnelRemoveTunnel(TunnelContext *tc)
|
||
|
{
|
||
|
tc->Next->Prev = tc->Prev;
|
||
|
tc->Prev->Next = tc->Next;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Context information that we pass to the IPv4 stack
|
||
|
// when transmitting.
|
||
|
//
|
||
|
typedef struct TunnelTransmitContext {
|
||
|
PNDIS_PACKET Packet;
|
||
|
TA_IP_ADDRESS taIPAddress;
|
||
|
TDI_CONNECTION_INFORMATION tdiConnInfo;
|
||
|
} TunnelTransmitContext;
|
||
|
|
||
|
//* TunnelTransmitComplete
|
||
|
//
|
||
|
// Completion function for TunnelTransmit,
|
||
|
// called when the IPv4 stack completes our IRP.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelTransmitComplete(
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PIRP Irp,
|
||
|
PVOID Context)
|
||
|
{
|
||
|
TunnelTransmitContext *ttc = (TunnelTransmitContext *) Context;
|
||
|
PNDIS_PACKET Packet = ttc->Packet;
|
||
|
TDI_STATUS TDIStatus = Irp->IoStatus.Status;
|
||
|
IP_STATUS IPStatus;
|
||
|
uchar ICMPCode;
|
||
|
|
||
|
//
|
||
|
// Free the state that we allocated in TunnelTransmit.
|
||
|
//
|
||
|
ExFreePool(ttc);
|
||
|
IoFreeIrp(Irp);
|
||
|
|
||
|
//
|
||
|
// Undo our adjustment before letting upper-layer code
|
||
|
// see the packet.
|
||
|
//
|
||
|
UndoAdjustPacketBuffer(Packet);
|
||
|
|
||
|
//
|
||
|
// Convert the error code.
|
||
|
// For some errors, we send an ICMPv6 message so that the error
|
||
|
// can be forwarded. For most errors we just complete the packet.
|
||
|
//
|
||
|
switch (TDIStatus) {
|
||
|
case TDI_SUCCESS:
|
||
|
IPStatus = IP_SUCCESS;
|
||
|
goto CallSendComplete;
|
||
|
case TDI_BUFFER_TOO_BIG:
|
||
|
//
|
||
|
// TODO: It would be preferable to generate an ICMPv6 Packet Too Big,
|
||
|
// but TDI does not give us the MTU value. This needs to be solved
|
||
|
// before we can set the dont-fragment bit and do PMTU discovery.
|
||
|
//
|
||
|
IPStatus = IP_PACKET_TOO_BIG;
|
||
|
goto CallSendComplete;
|
||
|
default:
|
||
|
IPStatus = IP_GENERAL_FAILURE;
|
||
|
|
||
|
CallSendComplete:
|
||
|
//
|
||
|
// Let IPv6 know that the send completed.
|
||
|
//
|
||
|
IPv6SendComplete(PC(Packet)->IF, Packet, IPStatus);
|
||
|
break;
|
||
|
|
||
|
case TDI_DEST_NET_UNREACH:
|
||
|
case TDI_DEST_HOST_UNREACH:
|
||
|
case TDI_DEST_PROT_UNREACH:
|
||
|
case TDI_DEST_PORT_UNREACH:
|
||
|
//
|
||
|
// Generate an ICMPv6 error.
|
||
|
// Because this is a link-specific error,
|
||
|
// we use address-unreachable.
|
||
|
// NB: At the moment, the IPv4 stack does
|
||
|
// not return these errors to us.
|
||
|
// This will call IPv6SendComplete for us.
|
||
|
//
|
||
|
IPv6SendAbort(CastFromIF(PC(Packet)->IF),
|
||
|
Packet,
|
||
|
PC(Packet)->pc_offset,
|
||
|
ICMPv6_DESTINATION_UNREACHABLE,
|
||
|
ICMPv6_ADDRESS_UNREACHABLE,
|
||
|
0, FALSE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Tell IoCompleteRequest to stop working on the IRP.
|
||
|
//
|
||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
}
|
||
|
|
||
|
//* TunnelTransmitViaAO
|
||
|
//
|
||
|
// Encapsulate a v6 packet in a v4 packet and send it
|
||
|
// to the specified v4 address, using the specified
|
||
|
// TDI address object. The address object may be bound
|
||
|
// to a v4 address, or else the v4 stack chooses
|
||
|
// the v4 source address.
|
||
|
//
|
||
|
// Callable from thread or DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelTransmitViaAO(
|
||
|
FILE_OBJECT *File, // Pointer to TDI address object.
|
||
|
PNDIS_PACKET Packet, // Pointer to packet to be transmitted.
|
||
|
uint Offset, // Offset from start of packet to IPv6 header.
|
||
|
IPAddr Address) // Link-layer (IPv4) destination address.
|
||
|
{
|
||
|
TunnelTransmitContext *ttc;
|
||
|
PIRP irp;
|
||
|
PMDL mdl;
|
||
|
ULONG SendLen;
|
||
|
|
||
|
//
|
||
|
// We do not need any space for a link-layer header,
|
||
|
// because the IPv4 code takes care of that transparently.
|
||
|
//
|
||
|
(void) AdjustPacketBuffer(Packet, Offset, 0);
|
||
|
|
||
|
//
|
||
|
// TdiBuildSendDatagram needs an MDL and the total amount
|
||
|
// of data that the MDL represents.
|
||
|
//
|
||
|
NdisQueryPacket(Packet, NULL, NULL, &mdl, &SendLen);
|
||
|
|
||
|
//
|
||
|
// Allocate the context that we will pass to the IPv4 stack.
|
||
|
//
|
||
|
ttc = ExAllocatePool(NonPagedPool, sizeof *ttc);
|
||
|
if (ttc == NULL) {
|
||
|
ErrorReturn:
|
||
|
UndoAdjustPacketBuffer(Packet);
|
||
|
IPv6SendComplete(PC(Packet)->IF, Packet, IP_GENERAL_FAILURE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate an IRP that we will pass to the IPv4 stack.
|
||
|
//
|
||
|
irp = IoAllocateIrp(File->DeviceObject->StackSize, FALSE);
|
||
|
if (irp == NULL) {
|
||
|
ExFreePool(ttc);
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize the context information.
|
||
|
// Note that many fields of the "connection info" are unused.
|
||
|
//
|
||
|
ttc->Packet = Packet;
|
||
|
|
||
|
ttc->taIPAddress.TAAddressCount = 1;
|
||
|
ttc->taIPAddress.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
|
||
|
ttc->taIPAddress.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
|
||
|
ttc->taIPAddress.Address[0].Address[0].sin_port = 0;
|
||
|
ttc->taIPAddress.Address[0].Address[0].in_addr = Address;
|
||
|
|
||
|
ttc->tdiConnInfo.RemoteAddressLength = sizeof ttc->taIPAddress;
|
||
|
ttc->tdiConnInfo.RemoteAddress = &ttc->taIPAddress;
|
||
|
|
||
|
//
|
||
|
// Initialize the IRP.
|
||
|
//
|
||
|
TdiBuildSendDatagram(irp,
|
||
|
File->DeviceObject, File,
|
||
|
TunnelTransmitComplete, ttc,
|
||
|
mdl, SendLen, &ttc->tdiConnInfo);
|
||
|
|
||
|
//
|
||
|
// Pass the IRP to the IPv4 stack.
|
||
|
// Note that unlike NDIS's asynchronous operations,
|
||
|
// our completion routine will always be called,
|
||
|
// no matter what the return code from IoCallDriver.
|
||
|
//
|
||
|
(void) IoCallDriver(File->DeviceObject, irp);
|
||
|
}
|
||
|
|
||
|
//* TunnelTransmitViaTC
|
||
|
//
|
||
|
// Extracts a file object reference from a tunnel context
|
||
|
// and calls TunnelTransmitViaAO.
|
||
|
//
|
||
|
void
|
||
|
TunnelTransmitViaTC(
|
||
|
TunnelContext *tc,
|
||
|
PNDIS_PACKET Packet, // Pointer to packet to be transmitted.
|
||
|
uint Offset, // Offset from start of packet to IPv6 header.
|
||
|
IPAddr Address) // Link-layer (IPv4) destination address.
|
||
|
{
|
||
|
Interface *IF = tc->IF;
|
||
|
PFILE_OBJECT File;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
//
|
||
|
// Get a reference to the TDI address object.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
File = tc->AOFile;
|
||
|
if (File == NULL) {
|
||
|
ASSERT(IF->Flags & IF_FLAG_MEDIA_DISCONNECTED);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
IPv6SendComplete(IF, Packet, IP_GENERAL_FAILURE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TunnelObjectAddRef(File);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
TunnelTransmitViaAO(File, Packet, Offset, Address);
|
||
|
|
||
|
ObDereferenceObject(File);
|
||
|
}
|
||
|
|
||
|
//* TunnelSearchAOList
|
||
|
//
|
||
|
// Search the list of TDI address objects
|
||
|
// for one bound to the specified v4 address.
|
||
|
// If successful, the TDI address object
|
||
|
// is returned with a reference for the caller.
|
||
|
//
|
||
|
// REVIEW: This design is inefficient on
|
||
|
// machines with thousands of v4 addresses.
|
||
|
//
|
||
|
FILE_OBJECT *
|
||
|
TunnelSearchAOList(IPAddr Addr)
|
||
|
{
|
||
|
FILE_OBJECT *File;
|
||
|
TunnelContext *tc;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
for (tc = Tunnel.AOList.Next; ; tc = tc->Next) {
|
||
|
|
||
|
if (tc == &Tunnel.AOList) {
|
||
|
File = NULL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (tc->SrcAddr == Addr) {
|
||
|
File = tc->AOFile;
|
||
|
TunnelObjectAddRef(File);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
return File;
|
||
|
}
|
||
|
|
||
|
//* TunnelTransmit
|
||
|
//
|
||
|
// Translates the arguments of our link-layer transmit function
|
||
|
// to the internal TunnelTransmitViaTC/AO.
|
||
|
//
|
||
|
void
|
||
|
TunnelTransmit(
|
||
|
void *Context, // Pointer to tunnel interface.
|
||
|
PNDIS_PACKET Packet, // Pointer to packet to be transmitted.
|
||
|
uint Offset, // Offset from start of packet to IPv6 header.
|
||
|
const void *LinkAddress) // Link-layer address.
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *) Context;
|
||
|
IPAddr Address = * (IPAddr *) LinkAddress;
|
||
|
|
||
|
//
|
||
|
// Suppress packets sent to various illegal destination types.
|
||
|
// REVIEW - It would be good to suppress subnet broadcasts,
|
||
|
// but we don't know the v4 net mask.
|
||
|
//
|
||
|
if ((Address == INADDR_ANY) ||
|
||
|
IsV4Broadcast(Address) ||
|
||
|
IsV4Multicast(Address)) {
|
||
|
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_USER_ERROR,
|
||
|
"TunnelTransmit: illegal destination %s\n",
|
||
|
FormatV4Address(Address)));
|
||
|
IPv6SendAbort(CastFromIF(tc->IF), Packet, Offset,
|
||
|
ICMPv6_DESTINATION_UNREACHABLE,
|
||
|
ICMPv6_COMMUNICATION_PROHIBITED,
|
||
|
0,
|
||
|
FALSE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// It would be nice to suppress transmission of packets
|
||
|
// that will result in v4 loopback, but we don't have a
|
||
|
// convenient way of doing that here. We could check
|
||
|
// if Address == tc->SrcAddr, but that won't catch most cases.
|
||
|
// Instead TunnelReceivePacket checks for this.
|
||
|
//
|
||
|
|
||
|
if (tc->IF->Type == IF_TYPE_TUNNEL_AUTO) {
|
||
|
IPv6Header Buffer;
|
||
|
IPv6Header UNALIGNED *IP;
|
||
|
IPAddr DesiredSrc;
|
||
|
FILE_OBJECT *File;
|
||
|
|
||
|
//
|
||
|
// tc->AOFile is not bound to a particular v4 address,
|
||
|
// so the v4 stack can choose a source address.
|
||
|
// But it might choose a source address that is not
|
||
|
// consistent with the v6 source address.
|
||
|
// To prevent this, we keep a stash of TDI address
|
||
|
// objects bound to v4 addresses and when appropriate,
|
||
|
// use a bound TDI address object.
|
||
|
//
|
||
|
IP = GetIPv6Header(Packet, Offset, &Buffer);
|
||
|
if ((IP != NULL) &&
|
||
|
(IsV4Compatible(AlignAddr(&IP->Source)) ||
|
||
|
IsISATAP(AlignAddr(&IP->Source)))) {
|
||
|
|
||
|
//
|
||
|
// Search for a TDI address object bound to
|
||
|
// the desired v4 source address.
|
||
|
//
|
||
|
DesiredSrc = ExtractV4Address(AlignAddr(&IP->Source));
|
||
|
File = TunnelSearchAOList(DesiredSrc);
|
||
|
if (File != NULL) {
|
||
|
|
||
|
//
|
||
|
// Encapsulate and transmit the packet,
|
||
|
// using the desired v4 source address.
|
||
|
//
|
||
|
TunnelTransmitViaAO(File, Packet, Offset, Address);
|
||
|
ObDereferenceObject(File);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Encapsulate and transmit the packet.
|
||
|
//
|
||
|
TunnelTransmitViaTC(tc, Packet, Offset, Address);
|
||
|
}
|
||
|
|
||
|
//* TunnelTransmitND
|
||
|
//
|
||
|
// Translates the arguments of our link-layer transmit function
|
||
|
// to the internal TunnelTransmitViaTC.
|
||
|
//
|
||
|
// This is just like TunnelTransmit, except it doesn't have
|
||
|
// the checks for bad destination addresses. 6over4 destination
|
||
|
// addresses are handled via Neighbor Discovery and
|
||
|
// multicast is needed.
|
||
|
//
|
||
|
void
|
||
|
TunnelTransmitND(
|
||
|
void *Context, // Pointer to tunnel interface.
|
||
|
PNDIS_PACKET Packet, // Pointer to packet to be transmitted.
|
||
|
uint Offset, // Offset from start of packet to IPv6 header.
|
||
|
const void *LinkAddress) // Link-layer address.
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *) Context;
|
||
|
IPAddr Address = * (IPAddr *) LinkAddress;
|
||
|
|
||
|
//
|
||
|
// Encapsulate and transmit the packet.
|
||
|
//
|
||
|
TunnelTransmitViaTC(tc, Packet, Offset, Address);
|
||
|
}
|
||
|
|
||
|
//* TunnelCreateReceiveIrp
|
||
|
//
|
||
|
// Creates an IRP for TunnelReceive/TunnelReceiveComplete.
|
||
|
//
|
||
|
PIRP
|
||
|
TunnelCreateReceiveIrp(DEVICE_OBJECT *Device)
|
||
|
{
|
||
|
PIRP irp;
|
||
|
PMDL mdl;
|
||
|
void *buffer;
|
||
|
|
||
|
irp = IoAllocateIrp(Device->StackSize, FALSE);
|
||
|
if (irp == NULL)
|
||
|
goto ErrorReturn;
|
||
|
|
||
|
buffer = ExAllocatePool(NonPagedPool, TUNNEL_RECEIVE_BUFFER);
|
||
|
if (buffer == NULL)
|
||
|
goto ErrorReturnFreeIrp;
|
||
|
|
||
|
mdl = IoAllocateMdl(buffer, TUNNEL_RECEIVE_BUFFER,
|
||
|
FALSE, // This is the irp's primary MDL.
|
||
|
FALSE, // Do not charge quota.
|
||
|
irp);
|
||
|
if (mdl == NULL)
|
||
|
goto ErrorReturnFreeBuffer;
|
||
|
|
||
|
MmBuildMdlForNonPagedPool(mdl);
|
||
|
|
||
|
return irp;
|
||
|
|
||
|
ErrorReturnFreeBuffer:
|
||
|
ExFreePool(buffer);
|
||
|
ErrorReturnFreeIrp:
|
||
|
IoFreeIrp(irp);
|
||
|
ErrorReturn:
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//* TunnelSelectTunnel
|
||
|
//
|
||
|
// Try to choose a tunnel on which to deliver a packet.
|
||
|
//
|
||
|
// Called with the tunnel lock held.
|
||
|
//
|
||
|
NetTableEntryOrInterface *
|
||
|
TunnelSelectTunnel(
|
||
|
IPv6Addr *V6Dest, // May be NULL.
|
||
|
IPAddr V4Dest,
|
||
|
IPAddr V4Src,
|
||
|
uint Flags)
|
||
|
{
|
||
|
TunnelContext *tc;
|
||
|
Interface *IF;
|
||
|
|
||
|
//
|
||
|
// First try to receive the packet on a point-to-point interface.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
IF = tc->IF;
|
||
|
|
||
|
//
|
||
|
// Restrict the point-to-point tunnel to only receiving
|
||
|
// packets that are sent from & to the proper link-layer
|
||
|
// addresses. That is, make it really point-to-point.
|
||
|
//
|
||
|
if (((IF->Flags & Flags) == Flags) &&
|
||
|
(IF->Type == IF_TYPE_TUNNEL_V6V4) &&
|
||
|
(V4Src == tc->DstAddr) &&
|
||
|
(V4Dest == tc->SrcAddr)) {
|
||
|
|
||
|
AddRefIF(IF);
|
||
|
return CastFromIF(IF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Next try to receive the packet on a 6-over-4 interface.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
IF = tc->IF;
|
||
|
|
||
|
//
|
||
|
// Restrict the 6-over-4 interface to only receiving
|
||
|
// packets that are sent to proper link-layer addresses.
|
||
|
// This is our v4 address and multicast addresses
|
||
|
// from TunnelConvertAddress.
|
||
|
//
|
||
|
if (((Flags == 0) || (IF->Flags & Flags)) &&
|
||
|
(IF->Type == IF_TYPE_TUNNEL_6OVER4) &&
|
||
|
((V4Dest == tc->SrcAddr) ||
|
||
|
((((uchar *)&V4Dest)[0] == 239) &&
|
||
|
(((uchar *)&V4Dest)[1] == 192)))) {
|
||
|
|
||
|
AddRefIF(IF);
|
||
|
return CastFromIF(IF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Finally, try to receive the packet on a tunnel pseudo-interface.
|
||
|
// This is a fall-back for forwarding situations
|
||
|
// or when V6Dest is NULL. In the latter case,
|
||
|
// we only consider automatic tunneling interfaces
|
||
|
// because they usually have link-local addresses.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
IF = tc->IF;
|
||
|
|
||
|
if (((Flags == 0) || (IF->Flags & Flags)) &&
|
||
|
((IF->Type == IF_TYPE_TUNNEL_AUTO) ||
|
||
|
((V6Dest != NULL) && (IF->Type == IF_TYPE_TUNNEL_6TO4)))) {
|
||
|
|
||
|
AddRefIF(IF);
|
||
|
return CastFromIF(IF);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
//* TunnelFindReceiver
|
||
|
//
|
||
|
// Finds the NTEorIF that should receive an encapsulated packet.
|
||
|
// Returns the NTEorIF with a reference, or NULL.
|
||
|
// Called at DPC level.
|
||
|
//
|
||
|
NetTableEntryOrInterface *
|
||
|
TunnelFindReceiver(
|
||
|
IPv6Addr *V6Dest, // May be NULL.
|
||
|
IPAddr V4Dest,
|
||
|
IPAddr V4Src)
|
||
|
{
|
||
|
NetTableEntryOrInterface *NTEorIF;
|
||
|
TunnelContext *tc;
|
||
|
|
||
|
//
|
||
|
// So we can access the global list of tunnels.
|
||
|
//
|
||
|
KeAcquireSpinLockAtDpcLevel(&Tunnel.Lock);
|
||
|
|
||
|
if (V6Dest != NULL) {
|
||
|
//
|
||
|
// First try to receive the packet directly (not forwarding)
|
||
|
// on a tunnel pseudo-interface.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
Interface *IF = tc->IF;
|
||
|
|
||
|
if ((IF->Type == IF_TYPE_TUNNEL_AUTO) ||
|
||
|
(IF->Type == IF_TYPE_TUNNEL_6TO4)) {
|
||
|
ushort Type;
|
||
|
|
||
|
NTEorIF = FindAddressOnInterface(IF, V6Dest, &Type);
|
||
|
if (NTEorIF != NULL) {
|
||
|
if (Type != ADE_NONE)
|
||
|
goto UnlockAndReturn;
|
||
|
ReleaseIF(CastToIF(NTEorIF));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Next try to receive the packet on a tunnel interface which
|
||
|
// is marked as forwarding.
|
||
|
//
|
||
|
NTEorIF = TunnelSelectTunnel(V6Dest, V4Dest, V4Src, IF_FLAG_FORWARDS);
|
||
|
if (NTEorIF != NULL)
|
||
|
goto UnlockAndReturn;
|
||
|
|
||
|
//
|
||
|
// Finally try to receive the packet on any matching tunnel interface.
|
||
|
//
|
||
|
NTEorIF = TunnelSelectTunnel(V6Dest, V4Dest, V4Src, 0);
|
||
|
|
||
|
UnlockAndReturn:
|
||
|
KeReleaseSpinLockFromDpcLevel(&Tunnel.Lock);
|
||
|
return NTEorIF;
|
||
|
}
|
||
|
|
||
|
//* TunnelReceiveIPv6Helper
|
||
|
//
|
||
|
// Called when we receive an encapsulated IPv6 packet,
|
||
|
// when we have identified the IPv6 header and found
|
||
|
// the NTEorIF that will receive the packet.
|
||
|
//
|
||
|
// Called at DPC level.
|
||
|
//
|
||
|
void
|
||
|
TunnelReceiveIPv6Helper(
|
||
|
IPHeader UNALIGNED *IPv4H,
|
||
|
IPv6Header UNALIGNED *IPv6H,
|
||
|
NetTableEntryOrInterface *NTEorIF,
|
||
|
void *Data,
|
||
|
uint Length)
|
||
|
{
|
||
|
IPv6Packet IPPacket;
|
||
|
uint Flags;
|
||
|
|
||
|
//
|
||
|
// Check if the packet was received as a link-layer multicast/broadcast.
|
||
|
//
|
||
|
if (IsV4Broadcast(IPv4H->iph_dest) ||
|
||
|
IsV4Multicast(IPv4H->iph_dest))
|
||
|
Flags = PACKET_NOT_LINK_UNICAST;
|
||
|
else
|
||
|
Flags = 0;
|
||
|
|
||
|
RtlZeroMemory(&IPPacket, sizeof IPPacket);
|
||
|
IPPacket.FlatData = Data;
|
||
|
IPPacket.Data = Data;
|
||
|
IPPacket.ContigSize = Length;
|
||
|
IPPacket.TotalSize = Length;
|
||
|
IPPacket.Flags = Flags;
|
||
|
IPPacket.NTEorIF = NTEorIF;
|
||
|
|
||
|
//
|
||
|
// We want to prevent any loopback in the v4 stack.
|
||
|
// Loopback should be handled in our v6 routing table.
|
||
|
// For example, we want to prevent loops where 6to4
|
||
|
// addresses are routed around and around and around.
|
||
|
// Without this code, the hop count would eventually
|
||
|
// catch the loop and report a strange ICMP error.
|
||
|
//
|
||
|
if (IPv4H->iph_dest == IPv4H->iph_src) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR,
|
||
|
"TunnelReceiveIPv6Helper: suppressed loopback\n"));
|
||
|
|
||
|
//
|
||
|
// Send an ICMP error. This requires some setup.
|
||
|
//
|
||
|
IPPacket.IP = IPv6H;
|
||
|
IPPacket.SrcAddr = AlignAddr(&IPv6H->Source);
|
||
|
IPPacket.IPPosition = IPPacket.Position;
|
||
|
AdjustPacketParams(&IPPacket, sizeof(IPv6Header));
|
||
|
|
||
|
ICMPv6SendError(&IPPacket,
|
||
|
ICMPv6_DESTINATION_UNREACHABLE,
|
||
|
ICMPv6_NO_ROUTE_TO_DESTINATION,
|
||
|
0, IPv6H->NextHeader, FALSE);
|
||
|
}
|
||
|
else {
|
||
|
int PktRefs;
|
||
|
|
||
|
PktRefs = IPv6Receive(&IPPacket);
|
||
|
ASSERT(PktRefs == 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//* TunnelReceiveIPv6
|
||
|
//
|
||
|
// Called when we receive an encapsulated IPv6 packet.
|
||
|
// Called at DPC level.
|
||
|
//
|
||
|
// We select a single tunnel interface to receive the packet.
|
||
|
// It's difficult to select the correct interface in all situations.
|
||
|
//
|
||
|
void
|
||
|
TunnelReceiveIPv6(
|
||
|
IPHeader UNALIGNED *IPv4H,
|
||
|
void *Data,
|
||
|
uint Length)
|
||
|
{
|
||
|
IPv6Header UNALIGNED *IPv6H;
|
||
|
NetTableEntryOrInterface *NTEorIF;
|
||
|
|
||
|
//
|
||
|
// If the packet does not contain a full IPv6 header,
|
||
|
// just ignore it. We need to look at the IPv6 header
|
||
|
// below to demultiplex the packet to the proper
|
||
|
// tunnel interface.
|
||
|
//
|
||
|
if (Length < sizeof *IPv6H) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceiveIPv6: too small to contain IPv6 hdr\n"));
|
||
|
return;
|
||
|
}
|
||
|
IPv6H = (IPv6Header UNALIGNED *) Data;
|
||
|
|
||
|
//
|
||
|
// Find the NTEorIF that will receive the packet.
|
||
|
//
|
||
|
NTEorIF = TunnelFindReceiver(AlignAddr(&IPv6H->Dest),
|
||
|
IPv4H->iph_dest,
|
||
|
IPv4H->iph_src);
|
||
|
if (NTEorIF == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceiveIPv6: no receiver\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TunnelReceiveIPv6Helper(IPv4H, IPv6H, NTEorIF, Data, Length);
|
||
|
|
||
|
if (IsNTE(NTEorIF))
|
||
|
ReleaseNTE(CastToNTE(NTEorIF));
|
||
|
else
|
||
|
ReleaseIF(CastToIF(NTEorIF));
|
||
|
}
|
||
|
|
||
|
//* TunnelFindPutativeSource
|
||
|
//
|
||
|
// Finds an address to use as the "source" of an error
|
||
|
// for completed echo requests.
|
||
|
// Returns FALSE if no address is available.
|
||
|
//
|
||
|
int
|
||
|
TunnelFindPutativeSource(
|
||
|
IPAddr V4Dest,
|
||
|
IPAddr V4Src,
|
||
|
IPv6Addr *Source,
|
||
|
uint *ScopeId)
|
||
|
{
|
||
|
NetTableEntryOrInterface *NTEorIF;
|
||
|
Interface *IF;
|
||
|
int rc;
|
||
|
|
||
|
//
|
||
|
// First find an interface that would receive
|
||
|
// a tunneled packet.
|
||
|
//
|
||
|
NTEorIF = TunnelFindReceiver(NULL, V4Dest, V4Src);
|
||
|
if (NTEorIF == NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
IF = NTEorIF->IF;
|
||
|
|
||
|
//
|
||
|
// Then get a link-local address on the interface.
|
||
|
//
|
||
|
rc = GetLinkLocalAddress(IF, Source);
|
||
|
*ScopeId = IF->ZoneIndices[ADE_LINK_LOCAL];
|
||
|
|
||
|
if (IsNTE(NTEorIF))
|
||
|
ReleaseNTE(CastToNTE(NTEorIF));
|
||
|
else
|
||
|
ReleaseIF(IF);
|
||
|
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
//* TunnelFindSourceAddress
|
||
|
//
|
||
|
// Finds a source address to use in a constructed ICMPv6 error,
|
||
|
// given the NTEorIF that is receiving the ICMPv6 error
|
||
|
// and the IPv6 destination of the error.
|
||
|
// Returns FALSE if no address is available.
|
||
|
//
|
||
|
int
|
||
|
TunnelFindSourceAddress(
|
||
|
NetTableEntryOrInterface *NTEorIF,
|
||
|
IPv6Addr *V6Dest,
|
||
|
IPv6Addr *V6Src)
|
||
|
{
|
||
|
RouteCacheEntry *RCE;
|
||
|
IP_STATUS Status;
|
||
|
|
||
|
//
|
||
|
// REVIEW: In the MIPV6 code base, eliminate this check.
|
||
|
//
|
||
|
if (IsNTE(NTEorIF)) {
|
||
|
*V6Src = CastToNTE(NTEorIF)->Address;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
Status = RouteToDestination(V6Dest, 0, NTEorIF, RTD_FLAG_NORMAL, &RCE);
|
||
|
if (Status != IP_SUCCESS)
|
||
|
return FALSE;
|
||
|
|
||
|
*V6Src = RCE->NTE->Address;
|
||
|
ReleaseRCE(RCE);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//* TunnelReceiveICMPv4
|
||
|
//
|
||
|
// Called when we receive an ICMPv4 packet.
|
||
|
// Called at DPC level.
|
||
|
//
|
||
|
// If an encapsulated IPv6 packet triggered
|
||
|
// this ICMPv4 error, then we construct an ICMPv6 error
|
||
|
// based on the ICMPv4 error and process the constructed packet.
|
||
|
//
|
||
|
void
|
||
|
TunnelReceiveICMPv4(
|
||
|
IPHeader UNALIGNED *IPv4H,
|
||
|
void *Data,
|
||
|
uint Length)
|
||
|
{
|
||
|
ICMPHeader UNALIGNED *ICMPv4H;
|
||
|
IPHeader UNALIGNED *ErrorIPv4H;
|
||
|
uint ErrorHeaderLength;
|
||
|
IPv6Header UNALIGNED *ErrorIPv6H;
|
||
|
void *NewData;
|
||
|
uint NewLength;
|
||
|
uint NewPayloadLength;
|
||
|
IPv6Header *NewIPv6H;
|
||
|
ICMPv6Header *NewICMPv6H;
|
||
|
uint *NewICMPv6Param;
|
||
|
void *NewErrorData;
|
||
|
IPv6Addr V6Src;
|
||
|
NetTableEntryOrInterface *NTEorIF;
|
||
|
|
||
|
//
|
||
|
// If the packet does not contain a full ICMPv4 header,
|
||
|
// just ignore it.
|
||
|
//
|
||
|
if (Length < sizeof *ICMPv4H) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceiveICMPv4: too small to contain ICMPv4 hdr\n"));
|
||
|
return;
|
||
|
}
|
||
|
ICMPv4H = (ICMPHeader UNALIGNED *) Data;
|
||
|
Length -= sizeof *ICMPv4H;
|
||
|
(char *)Data += sizeof *ICMPv4H;
|
||
|
|
||
|
//
|
||
|
// Ignore everything but selected ICMP errors.
|
||
|
//
|
||
|
if ((ICMPv4H->ich_type != ICMP_DEST_UNREACH) &&
|
||
|
(ICMPv4H->ich_type != ICMP_SOURCE_QUENCH) &&
|
||
|
(ICMPv4H->ich_type != ICMP_TIME_EXCEED) &&
|
||
|
(ICMPv4H->ich_type != ICMP_PARAM_PROBLEM))
|
||
|
return;
|
||
|
|
||
|
//
|
||
|
// We need sufficient data from the error packet:
|
||
|
// at least the IPv4 header.
|
||
|
//
|
||
|
if (Length < sizeof *ErrorIPv4H) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceiveICMPv4: "
|
||
|
"too small to contain error IPv4 hdr\n"));
|
||
|
return;
|
||
|
}
|
||
|
ErrorIPv4H = (IPHeader UNALIGNED *) Data;
|
||
|
ErrorHeaderLength = ((ErrorIPv4H->iph_verlen & 0xf) << 2);
|
||
|
if ((ErrorHeaderLength < sizeof *ErrorIPv4H) ||
|
||
|
(ErrorIPv4H->iph_length < ErrorHeaderLength)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceiveICMPv4: "
|
||
|
"error IPv4 hdr length too small\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We are only interested if this error is in response
|
||
|
// to an IPv6-in-IPv4 packet.
|
||
|
//
|
||
|
if (ErrorIPv4H->iph_protocol != IP_PROTOCOL_V6)
|
||
|
return;
|
||
|
|
||
|
//
|
||
|
// Ignore the packet if the ICMPv4 checksum fails.
|
||
|
// We do this check after the cheaper checks above,
|
||
|
// when we know that we really want to process the error.
|
||
|
//
|
||
|
if (Cksum(ICMPv4H, sizeof *ICMPv4H + Length) != 0xffff) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceiveICMPv4: bad checksum\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Ensure that we do not look at garbage bytes
|
||
|
// at the end of the ICMP packet.
|
||
|
// We must adjust Length after checking the checksum.
|
||
|
//
|
||
|
if (ErrorIPv4H->iph_length < Length)
|
||
|
Length = ErrorIPv4H->iph_length;
|
||
|
|
||
|
//
|
||
|
// Ideally we also have the encapsulated IPv6 header.
|
||
|
// But often IPv4 routers will return insufficient information.
|
||
|
// In that case, we make a best effort to identify
|
||
|
// and complete any outstanding echo requests.
|
||
|
// Yes, this is a hack.
|
||
|
//
|
||
|
if (Length < ErrorHeaderLength + sizeof *ErrorIPv6H) {
|
||
|
uint ScopeId;
|
||
|
IP_STATUS Status;
|
||
|
|
||
|
if (! TunnelFindPutativeSource(IPv4H->iph_dest,
|
||
|
ErrorIPv4H->iph_dest,
|
||
|
&V6Src,
|
||
|
&ScopeId)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
||
|
"TunnelReceiveICMPv4: no putative source\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// The status code here should be the same as if
|
||
|
// we constructed an ICMPv6 error below and then
|
||
|
// converted to a status code in ICMPv6ErrorReceive.
|
||
|
//
|
||
|
if ((ICMPv4H->ich_type == ICMP_DEST_UNREACH) &&
|
||
|
(ICMPv4H->ich_code == ICMP_FRAG_NEEDED) &&
|
||
|
(net_long(ICMPv4H->ich_param) >=
|
||
|
ErrorHeaderLength + IPv6_MINIMUM_MTU))
|
||
|
Status = IP_PACKET_TOO_BIG;
|
||
|
else
|
||
|
Status = IP_DEST_ADDR_UNREACHABLE;
|
||
|
|
||
|
ICMPv6ProcessTunnelError(ErrorIPv4H->iph_dest,
|
||
|
&V6Src, ScopeId,
|
||
|
Status);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Move past the IPv4 header in the error data.
|
||
|
// Everything past this point, including the error IPv6 header,
|
||
|
// will become data in the constructed ICMPv6 error.
|
||
|
//
|
||
|
Length -= ErrorHeaderLength;
|
||
|
(char *)Data += ErrorHeaderLength;
|
||
|
ErrorIPv6H = (IPv6Header UNALIGNED *) Data;
|
||
|
|
||
|
//
|
||
|
// Determine who will receive the constructed ICMPv6 error.
|
||
|
//
|
||
|
NTEorIF = TunnelFindReceiver(AlignAddr(&ErrorIPv6H->Source),
|
||
|
IPv4H->iph_dest,
|
||
|
ErrorIPv4H->iph_dest);
|
||
|
if (NTEorIF == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
||
|
"TunnelReceiveICMPv4: no receiver\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Find a source address for the constructed ICMPv6 error.
|
||
|
//
|
||
|
if (! TunnelFindSourceAddress(NTEorIF, AlignAddr(&ErrorIPv6H->Source),
|
||
|
&V6Src)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR,
|
||
|
"TunnelReceiveICMPv4: no source address\n"));
|
||
|
goto ReleaseAndReturn;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate memory for the constructed ICMPv6 error.
|
||
|
//
|
||
|
NewPayloadLength = sizeof *NewICMPv6H + sizeof *NewICMPv6Param + Length;
|
||
|
NewLength = sizeof *NewIPv6H + NewPayloadLength;
|
||
|
NewData = ExAllocatePool(NonPagedPool, NewLength);
|
||
|
if (NewData == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelReceiveICMPv4: no pool\n"));
|
||
|
goto ReleaseAndReturn;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Build the IPv6 header.
|
||
|
//
|
||
|
NewIPv6H = (IPv6Header *) NewData;
|
||
|
NewIPv6H->VersClassFlow = IP_VERSION;
|
||
|
NewIPv6H->PayloadLength = net_short((ushort)NewPayloadLength);
|
||
|
NewIPv6H->NextHeader = IP_PROTOCOL_ICMPv6;
|
||
|
NewIPv6H->HopLimit = DEFAULT_CUR_HOP_LIMIT;
|
||
|
NewIPv6H->Source = V6Src;
|
||
|
NewIPv6H->Dest = ErrorIPv6H->Source;
|
||
|
|
||
|
//
|
||
|
// Build the ICMPv6 header.
|
||
|
//
|
||
|
NewICMPv6H = (ICMPv6Header *) (NewIPv6H + 1);
|
||
|
NewICMPv6Param = (uint *) (NewICMPv6H + 1);
|
||
|
|
||
|
if ((ICMPv4H->ich_type == ICMP_DEST_UNREACH) &&
|
||
|
(ICMPv4H->ich_code == ICMP_FRAG_NEEDED)) {
|
||
|
uint MTU;
|
||
|
|
||
|
//
|
||
|
// Calculate the MTU as seen by the IPv6 packet.
|
||
|
// The MTU can not be smaller than IPv6_MINIMUM_MTU.
|
||
|
// NB: In old-style frag-needed errors,
|
||
|
// ich_param should be zero.
|
||
|
// NB: Actually, this code should not be exercised since
|
||
|
// we do not set the dont-fragment bit in our IPv4 packets.
|
||
|
//
|
||
|
MTU = net_long(ICMPv4H->ich_param);
|
||
|
if (MTU < ErrorHeaderLength + IPv6_MINIMUM_MTU) {
|
||
|
//
|
||
|
// If we were setting the dont-fragment bit,
|
||
|
// we should clear it in this case.
|
||
|
// We need to allow the IPv4 layer to fragment.
|
||
|
//
|
||
|
goto GenerateAddressUnreachable;
|
||
|
}
|
||
|
MTU -= ErrorHeaderLength;
|
||
|
|
||
|
NewICMPv6H->Type = ICMPv6_PACKET_TOO_BIG;
|
||
|
NewICMPv6H->Code = 0;
|
||
|
*NewICMPv6Param = net_long(MTU);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// For everything else, we use address-unreachable.
|
||
|
// It is the appropriate code for a link-specific error.
|
||
|
//
|
||
|
GenerateAddressUnreachable:
|
||
|
NewICMPv6H->Type = ICMPv6_DESTINATION_UNREACHABLE;
|
||
|
NewICMPv6H->Code = ICMPv6_ADDRESS_UNREACHABLE;
|
||
|
*NewICMPv6Param = 0;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Copy the error data to the new packet.
|
||
|
//
|
||
|
NewErrorData = (void *) (NewICMPv6Param + 1);
|
||
|
RtlCopyMemory(NewErrorData, Data, Length);
|
||
|
|
||
|
//
|
||
|
// Calculate the ICMPv6 checksum.
|
||
|
//
|
||
|
NewICMPv6H->Checksum = 0;
|
||
|
NewICMPv6H->Checksum = ChecksumPacket(NULL, 0,
|
||
|
(uchar *)NewICMPv6H, NewPayloadLength,
|
||
|
&NewIPv6H->Source, &NewIPv6H->Dest,
|
||
|
IP_PROTOCOL_ICMPv6);
|
||
|
|
||
|
//
|
||
|
// Receive the constructed packet.
|
||
|
//
|
||
|
TunnelReceiveIPv6Helper(IPv4H, NewIPv6H, NTEorIF, NewData, NewLength);
|
||
|
|
||
|
ExFreePool(NewData);
|
||
|
ReleaseAndReturn:
|
||
|
if (IsNTE(NTEorIF))
|
||
|
ReleaseNTE(CastToNTE(NTEorIF));
|
||
|
else
|
||
|
ReleaseIF(CastToIF(NTEorIF));
|
||
|
}
|
||
|
|
||
|
//* TunnelReceivePacket
|
||
|
//
|
||
|
// Called when we receive an encapsulated IPv6 packet OR
|
||
|
// we receive an ICMPv4 packet.
|
||
|
// Called at DPC level.
|
||
|
//
|
||
|
// We select a single tunnel interface to receive the packet.
|
||
|
// It's difficult to select the correct interface in all situations.
|
||
|
//
|
||
|
void
|
||
|
TunnelReceivePacket(void *Data, uint Length)
|
||
|
{
|
||
|
IPHeader UNALIGNED *IPv4H;
|
||
|
uint HeaderLength;
|
||
|
|
||
|
//
|
||
|
// The incoming data includes the IPv4 header.
|
||
|
// We should only get properly-formed IPv4 packets.
|
||
|
//
|
||
|
ASSERT(Length >= sizeof *IPv4H);
|
||
|
IPv4H = (IPHeader UNALIGNED *) Data;
|
||
|
HeaderLength = ((IPv4H->iph_verlen & 0xf) << 2);
|
||
|
ASSERT(Length >= HeaderLength);
|
||
|
Length -= HeaderLength;
|
||
|
(char *)Data += HeaderLength;
|
||
|
|
||
|
if (IPv4H->iph_src == INADDR_ANY) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceivePacket: null v4 source\n"));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (IPv4H->iph_protocol == IP_PROTOCOL_V6) {
|
||
|
//
|
||
|
// Process the encapsulated IPv6 packet.
|
||
|
//
|
||
|
TunnelReceiveIPv6(IPv4H, Data, Length);
|
||
|
}
|
||
|
else if (IPv4H->iph_protocol == IP_PROTOCOL_ICMPv4) {
|
||
|
//
|
||
|
// Process the ICMPv4 packet.
|
||
|
//
|
||
|
TunnelReceiveICMPv4(IPv4H, Data, Length);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// We should not receive stray packets.
|
||
|
//
|
||
|
ASSERT(! "bad iph_protocol");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//* TunnelReceiveComplete
|
||
|
//
|
||
|
// Completion function for TunnelReceive,
|
||
|
// called when the IPv4 stack completes our IRP.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelReceiveComplete(
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PIRP Irp,
|
||
|
PVOID Context)
|
||
|
{
|
||
|
TDI_STATUS status = Irp->IoStatus.Status;
|
||
|
void *Data;
|
||
|
ULONG BytesRead;
|
||
|
|
||
|
ASSERT(Context == NULL);
|
||
|
|
||
|
if (status == TDI_SUCCESS) {
|
||
|
//
|
||
|
// The incoming data includes the IPv4 header.
|
||
|
// We should only get properly-formed IPv4 packets.
|
||
|
//
|
||
|
BytesRead = (ULONG)Irp->IoStatus.Information;
|
||
|
Data = Irp->MdlAddress->MappedSystemVa;
|
||
|
|
||
|
TunnelReceivePacket(Data, BytesRead);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Put the IRP back so that TunnelReceive can use it again.
|
||
|
//
|
||
|
KeAcquireSpinLockAtDpcLevel(&Tunnel.Lock);
|
||
|
ASSERT(Tunnel.ReceiveIrp == NULL);
|
||
|
Tunnel.ReceiveIrp = Irp;
|
||
|
KeReleaseSpinLockFromDpcLevel(&Tunnel.Lock);
|
||
|
|
||
|
//
|
||
|
// Tell IoCompleteRequest to stop working on the IRP.
|
||
|
//
|
||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
}
|
||
|
|
||
|
//* TunnelReceive
|
||
|
//
|
||
|
// Called from the IPv4 protocol stack, when it receives
|
||
|
// an encapsulated v6 packet.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelReceive(
|
||
|
IN PVOID TdiEventContext, // The event context
|
||
|
IN LONG SourceAddressLength, // Length of SourceAddress field.
|
||
|
IN PVOID SourceAddress, // Describes the datagram's originator.
|
||
|
IN LONG OptionsLength, // Length of Options field.
|
||
|
IN PVOID Options, // Options for the receive.
|
||
|
IN ULONG ReceiveDatagramFlags, //
|
||
|
IN ULONG BytesIndicated, // Number of bytes this indication.
|
||
|
IN ULONG BytesAvailable, // Number of bytes in complete Tsdu.
|
||
|
OUT ULONG *BytesTaken, // Number of bytes used.
|
||
|
IN PVOID Tsdu, // Pointer describing this TSDU,
|
||
|
// typically a lump of bytes
|
||
|
OUT PIRP *IoRequestPacket) // TdiReceive IRP if
|
||
|
// MORE_PROCESSING_REQUIRED.
|
||
|
{
|
||
|
PIRP irp;
|
||
|
|
||
|
ASSERT(TdiEventContext == NULL);
|
||
|
ASSERT(BytesIndicated <= BytesAvailable);
|
||
|
|
||
|
//
|
||
|
// If the packet is too large, refuse to receive it.
|
||
|
//
|
||
|
if (BytesAvailable > TUNNEL_RECEIVE_BUFFER) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET,
|
||
|
"TunnelReceive - too big %x\n", BytesAvailable));
|
||
|
*BytesTaken = BytesAvailable;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if we already have the entire packet to work with.
|
||
|
// If so, we can directly call TunnelReceivePacket.
|
||
|
//
|
||
|
if (BytesIndicated == BytesAvailable) {
|
||
|
|
||
|
TunnelReceivePacket(Tsdu, BytesIndicated);
|
||
|
|
||
|
//
|
||
|
// Tell our caller that we took the data
|
||
|
// and that we are done.
|
||
|
//
|
||
|
*BytesTaken = BytesAvailable;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We need an IRP to receive the entire packet.
|
||
|
// The IRP has a pre-allocated MDL.
|
||
|
//
|
||
|
// NB: We may get here before TunnelOpenV4 has
|
||
|
// finished initializing. In that case,
|
||
|
// we will not find an IRP.
|
||
|
//
|
||
|
KeAcquireSpinLockAtDpcLevel(&Tunnel.Lock);
|
||
|
irp = Tunnel.ReceiveIrp;
|
||
|
Tunnel.ReceiveIrp = NULL;
|
||
|
KeReleaseSpinLockFromDpcLevel(&Tunnel.Lock);
|
||
|
|
||
|
//
|
||
|
// If we don't have an IRP available to us,
|
||
|
// just drop the packet. This doesn't happen in practice.
|
||
|
//
|
||
|
if (irp == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE,
|
||
|
"TunnelReceive - no irp\n"));
|
||
|
*BytesTaken = BytesAvailable;
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Build the receive datagram request.
|
||
|
//
|
||
|
TdiBuildReceiveDatagram(irp,
|
||
|
Tunnel.V4Device,
|
||
|
Tunnel.List.AOFile,
|
||
|
TunnelReceiveComplete,
|
||
|
NULL, // Context
|
||
|
irp->MdlAddress,
|
||
|
BytesAvailable,
|
||
|
&Tunnel.ReceiveInputInfo,
|
||
|
&Tunnel.ReceiveOutputInfo,
|
||
|
0); // ReceiveFlags
|
||
|
|
||
|
//
|
||
|
// Make the next stack location current. Normally IoCallDriver would
|
||
|
// do this, but since we're bypassing that, we do it directly.
|
||
|
//
|
||
|
IoSetNextIrpStackLocation(irp);
|
||
|
|
||
|
//
|
||
|
// Return the irp to our caller.
|
||
|
//
|
||
|
*IoRequestPacket = irp;
|
||
|
*BytesTaken = 0;
|
||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
}
|
||
|
|
||
|
//* TunnelSetReceiveHandler
|
||
|
//
|
||
|
// Request notification of received IPv4 datagrams
|
||
|
// using the specified TDI address object.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetReceiveHandler(
|
||
|
FILE_OBJECT *File, // TDI address object.
|
||
|
PVOID EventHandler) // Receive handler.
|
||
|
{
|
||
|
IO_STATUS_BLOCK iosb;
|
||
|
KEVENT event;
|
||
|
NTSTATUS status;
|
||
|
PIRP irp;
|
||
|
|
||
|
//
|
||
|
// Initialize the event that we use to wait.
|
||
|
//
|
||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
||
|
|
||
|
//
|
||
|
// Create and initialize the IRP for this operation.
|
||
|
//
|
||
|
irp = IoBuildDeviceIoControlRequest(0, // dummy ioctl
|
||
|
File->DeviceObject,
|
||
|
NULL, // input buffer
|
||
|
0, // input buffer length
|
||
|
NULL, // output buffer
|
||
|
0, // output buffer length
|
||
|
TRUE, // internal device control?
|
||
|
&event,
|
||
|
&iosb);
|
||
|
if (irp == NULL)
|
||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
||
|
|
||
|
iosb.Status = STATUS_UNSUCCESSFUL;
|
||
|
iosb.Information = (ULONG)-1;
|
||
|
|
||
|
TdiBuildSetEventHandler(irp,
|
||
|
File->DeviceObject, File,
|
||
|
NULL, NULL, // comp routine/context
|
||
|
TDI_EVENT_RECEIVE_DATAGRAM,
|
||
|
EventHandler, NULL);
|
||
|
|
||
|
//
|
||
|
// Make the IOCTL, waiting for it to finish if necessary.
|
||
|
//
|
||
|
status = IoCallDriver(File->DeviceObject, irp);
|
||
|
if (status == STATUS_PENDING) {
|
||
|
KeWaitForSingleObject(&event, Executive, KernelMode,
|
||
|
FALSE, NULL);
|
||
|
status = iosb.Status;
|
||
|
}
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
//* TunnelCreateToken
|
||
|
//
|
||
|
// Given a link-layer address, creates a 64-bit "interface token"
|
||
|
// in the low eight bytes of an IPv6 address.
|
||
|
// Does not modify the other bytes in the IPv6 address.
|
||
|
//
|
||
|
void
|
||
|
TunnelCreateToken(
|
||
|
void *Context,
|
||
|
IPv6Addr *Address)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
uchar *IPAddress = (uchar *)&tc->TokenAddr;
|
||
|
|
||
|
//
|
||
|
// Embed the link's interface index in the interface identifier.
|
||
|
// This makes the interface identifier unique.
|
||
|
// Otherwise point-to-point tunnel and 6-over-4 links
|
||
|
// could have the same link-layer address,
|
||
|
// which is awkward.
|
||
|
//
|
||
|
*(ULONG UNALIGNED *)&Address->s6_bytes[8] = net_long(tc->IF->Index);
|
||
|
*(IPAddr UNALIGNED *)&Address->s6_bytes[12] = tc->TokenAddr;
|
||
|
}
|
||
|
|
||
|
//* TunnelCreateIsatapToken
|
||
|
//
|
||
|
// Given a link-layer address, creates a 64-bit "interface token"
|
||
|
// in the low eight bytes of an IPv6 address.
|
||
|
// Does not modify the other bytes in the IPv6 address.
|
||
|
//
|
||
|
void
|
||
|
TunnelCreateIsatapToken(
|
||
|
void *Context,
|
||
|
IPv6Addr *Address)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
|
||
|
ASSERT(tc->IF->Type == IF_TYPE_TUNNEL_AUTO);
|
||
|
|
||
|
Address->s6_words[4] = 0;
|
||
|
Address->s6_words[5] = 0xfe5e;
|
||
|
* (IPAddr UNALIGNED *) &Address->s6_words[6] = tc->TokenAddr;
|
||
|
}
|
||
|
|
||
|
//* TunnelReadLinkLayerAddressOption
|
||
|
//
|
||
|
// Parses a Neighbor Discovery link-layer address option
|
||
|
// and if valid, returns a pointer to the link-layer address.
|
||
|
//
|
||
|
const void *
|
||
|
TunnelReadLinkLayerAddressOption(
|
||
|
void *Context,
|
||
|
const uchar *OptionData)
|
||
|
{
|
||
|
//
|
||
|
// Check that the option length is correct.
|
||
|
//
|
||
|
if (OptionData[1] != 1)
|
||
|
return NULL;
|
||
|
|
||
|
//
|
||
|
// Check the must-be-zero padding bytes.
|
||
|
//
|
||
|
if ((OptionData[2] != 0) || (OptionData[3] != 0))
|
||
|
return NULL;
|
||
|
|
||
|
//
|
||
|
// Return a pointer to the embedded IPv4 address.
|
||
|
//
|
||
|
return OptionData + 4;
|
||
|
}
|
||
|
|
||
|
//* TunnelWriteLinkLayerAddressOption
|
||
|
//
|
||
|
// Creates a Neighbor Discovery link-layer address option.
|
||
|
// Our caller takes care of the option type & length fields.
|
||
|
// We handle the padding/alignment/placement of the link address
|
||
|
// into the option data.
|
||
|
//
|
||
|
// (Our caller allocates space for the option by adding 2 to the
|
||
|
// link address length and rounding up to a multiple of 8.)
|
||
|
//
|
||
|
void
|
||
|
TunnelWriteLinkLayerAddressOption(
|
||
|
void *Context,
|
||
|
uchar *OptionData,
|
||
|
const void *LinkAddress)
|
||
|
{
|
||
|
const uchar *IPAddress = (uchar *)LinkAddress;
|
||
|
|
||
|
//
|
||
|
// Place the address after the option type/length bytes
|
||
|
// and two bytes of zero padding.
|
||
|
//
|
||
|
OptionData[2] = 0;
|
||
|
OptionData[3] = 0;
|
||
|
OptionData[4] = IPAddress[0];
|
||
|
OptionData[5] = IPAddress[1];
|
||
|
OptionData[6] = IPAddress[2];
|
||
|
OptionData[7] = IPAddress[3];
|
||
|
}
|
||
|
|
||
|
//* TunnelConvertAddress
|
||
|
//
|
||
|
// Converts an IPv6 address to a link-layer address.
|
||
|
//
|
||
|
ushort
|
||
|
TunnelConvertAddress(
|
||
|
void *Context,
|
||
|
const IPv6Addr *Address,
|
||
|
void *LinkAddress)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
Interface *IF = tc->IF;
|
||
|
IPAddr UNALIGNED *IPAddress = (IPAddr UNALIGNED *)LinkAddress;
|
||
|
|
||
|
switch (IF->Type) {
|
||
|
case IF_TYPE_TUNNEL_AUTO:
|
||
|
if (IsV4Compatible(Address) || IsISATAP(Address)) {
|
||
|
//
|
||
|
// Extract the IPv4 address from the interface identifier.
|
||
|
//
|
||
|
*IPAddress = ExtractV4Address(Address);
|
||
|
return ND_STATE_PERMANENT;
|
||
|
}
|
||
|
else if ((tc->DstAddr != INADDR_ANY) &&
|
||
|
IP6_ADDR_EQUAL(Address, &AllRoutersOnLinkAddr)) {
|
||
|
//
|
||
|
// Return the IPv4 address from TunnelSetRouterLLAddress.
|
||
|
//
|
||
|
*IPAddress = tc->DstAddr;
|
||
|
return ND_STATE_PERMANENT;
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// We can't guess at the correct link-layer address.
|
||
|
// This value will cause IPv6SendND to drop the packet.
|
||
|
//
|
||
|
return ND_STATE_INCOMPLETE;
|
||
|
}
|
||
|
|
||
|
case IF_TYPE_TUNNEL_6TO4:
|
||
|
if (Is6to4(Address)) {
|
||
|
//
|
||
|
// Extract the IPv4 address from the prefix.
|
||
|
//
|
||
|
*IPAddress = Extract6to4Address(Address);
|
||
|
return ND_STATE_PERMANENT;
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// We can't guess at the correct link-layer address.
|
||
|
// This value will cause IPv6SendND to drop the packet.
|
||
|
//
|
||
|
return ND_STATE_INCOMPLETE;
|
||
|
}
|
||
|
|
||
|
case IF_TYPE_TUNNEL_6OVER4:
|
||
|
//
|
||
|
// This is a 6-over-4 link, which uses IPv4 multicast.
|
||
|
//
|
||
|
if (IsMulticast(Address)) {
|
||
|
uchar *IPAddressBytes = (uchar *)LinkAddress;
|
||
|
|
||
|
IPAddressBytes[0] = 239;
|
||
|
IPAddressBytes[1] = 192; // REVIEW: or 128 or 64??
|
||
|
IPAddressBytes[2] = Address->s6_bytes[14];
|
||
|
IPAddressBytes[3] = Address->s6_bytes[15];
|
||
|
return ND_STATE_PERMANENT;
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Let Neighbor Discovery do its thing for unicast.
|
||
|
//
|
||
|
return ND_STATE_INCOMPLETE;
|
||
|
}
|
||
|
|
||
|
case IF_TYPE_TUNNEL_V6V4:
|
||
|
//
|
||
|
// This is a point-to-point tunnel, so write in
|
||
|
// the address of the other side of the tunnel.
|
||
|
//
|
||
|
*IPAddress = tc->DstAddr;
|
||
|
if (!(IF->Flags & IF_FLAG_NEIGHBOR_DISCOVERS) || IsMulticast(Address))
|
||
|
return ND_STATE_PERMANENT;
|
||
|
else
|
||
|
return ND_STATE_STALE;
|
||
|
|
||
|
default:
|
||
|
ASSERT(!"TunnelConvertAddress: bad IF type");
|
||
|
return ND_STATE_INCOMPLETE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//* TunnelSetMulticastAddressList
|
||
|
//
|
||
|
// Takes an array of link-layer multicast addresses
|
||
|
// (from TunnelConvertAddress) from which we should
|
||
|
// receive packets. Passes them to the IPv4 stack.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
NDIS_STATUS
|
||
|
TunnelSetMulticastAddressList(
|
||
|
void *Context,
|
||
|
const void *LinkAddresses,
|
||
|
uint NumKeep,
|
||
|
uint NumAdd,
|
||
|
uint NumDel)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
IPAddr *Addresses = (IPAddr *)LinkAddresses;
|
||
|
NTSTATUS Status;
|
||
|
uint i;
|
||
|
|
||
|
//
|
||
|
// We only do something for 6-over-4 links.
|
||
|
//
|
||
|
ASSERT(tc->IF->Type == IF_TYPE_TUNNEL_6OVER4);
|
||
|
|
||
|
//
|
||
|
// The IPv6 layer serializes calls to TunnelSetMulticastAddressList
|
||
|
// and TunnelResetMulticastAddressListDone, so we can safely check
|
||
|
// SetMCListOK to handle races with TunnelOpenV4.
|
||
|
//
|
||
|
if (tc->SetMCListOK) {
|
||
|
//
|
||
|
// We add the multicast addresses to Tunnel.List.AOFile,
|
||
|
// instead of tc->AOFile, because we are only receiving
|
||
|
// from the first address object.
|
||
|
//
|
||
|
for (i = 0; i < NumAdd; i++) {
|
||
|
Status = TunnelAddMulticastAddress(
|
||
|
Tunnel.List.AOFile,
|
||
|
tc->SrcAddr,
|
||
|
Addresses[NumKeep + i]);
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
goto Return;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < NumDel; i++) {
|
||
|
Status = TunnelDelMulticastAddress(
|
||
|
Tunnel.List.AOFile,
|
||
|
tc->SrcAddr,
|
||
|
Addresses[NumKeep + NumAdd + i]);
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
goto Return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
Return:
|
||
|
return (NDIS_STATUS) Status;
|
||
|
}
|
||
|
|
||
|
//* TunnelResetMulticastAddressListDone
|
||
|
//
|
||
|
// Indicates that RestartLinkLayerMulticast has finished,
|
||
|
// and subsequent calls to TunnelSetMulticastAddressList
|
||
|
// will inform us of the link-layer multicast addresses.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelResetMulticastAddressListDone(void *Context)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
|
||
|
tc->SetMCListOK = TRUE;
|
||
|
}
|
||
|
|
||
|
//* TunnelClose
|
||
|
//
|
||
|
// Shuts down a tunnel.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelClose(void *Context)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
//
|
||
|
// Remove the tunnel from our data structures.
|
||
|
//
|
||
|
KeWaitForSingleObject(&Tunnel.Mutex, Executive, KernelMode, FALSE, NULL);
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
TunnelRemoveTunnel(tc);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
|
||
|
ReleaseIF(tc->IF);
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelCleanup
|
||
|
//
|
||
|
// Performs final cleanup of the tunnel context.
|
||
|
//
|
||
|
void
|
||
|
TunnelCleanup(void *Context)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *)Context;
|
||
|
|
||
|
if (tc->AOHandle == NULL) {
|
||
|
//
|
||
|
// No references to release.
|
||
|
//
|
||
|
ASSERT(tc->AOFile == NULL);
|
||
|
}
|
||
|
else if (tc->AOHandle == Tunnel.List.AOHandle) {
|
||
|
//
|
||
|
// No references to release.
|
||
|
//
|
||
|
ASSERT(tc->AOFile == Tunnel.List.AOFile);
|
||
|
}
|
||
|
else {
|
||
|
ObDereferenceObject(tc->AOFile);
|
||
|
TunnelCloseAddressObject(tc->AOHandle);
|
||
|
}
|
||
|
|
||
|
ExFreePool(tc);
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelSetRouterLLAddress
|
||
|
//
|
||
|
// Sets the ISATAP router's IPv4 address.
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelSetRouterLLAddress(
|
||
|
void *Context,
|
||
|
const void *TokenLinkAddress,
|
||
|
const void *RouterLinkAddress)
|
||
|
{
|
||
|
TunnelContext *tc = (TunnelContext *) Context;
|
||
|
IPv6Addr LinkLocalAddress;
|
||
|
KIRQL OldIrql;
|
||
|
NetTableEntry *NTE;
|
||
|
Interface *IF = tc->IF;
|
||
|
|
||
|
ASSERT(IF->Type == IF_TYPE_TUNNEL_AUTO);
|
||
|
|
||
|
//
|
||
|
// We should not set/reset one without the other.
|
||
|
//
|
||
|
if ((*((IPAddr *) RouterLinkAddress) == INADDR_ANY) !=
|
||
|
(*((IPAddr *) TokenLinkAddress) == INADDR_ANY))
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
|
||
|
RtlCopyMemory(&tc->DstAddr, RouterLinkAddress, sizeof(IPAddr));
|
||
|
RtlCopyMemory(&tc->TokenAddr, TokenLinkAddress, sizeof(IPAddr));
|
||
|
|
||
|
KeAcquireSpinLock(&IF->Lock, &OldIrql);
|
||
|
|
||
|
if (tc->DstAddr != INADDR_ANY) {
|
||
|
//
|
||
|
// Look for a link-local NTE matching the TokenAddr.
|
||
|
// If we find one, set the preferred link-local NTE to that one,
|
||
|
// so that the IPv6 source address of RS's will match the IPv4
|
||
|
// source address of the outer header.
|
||
|
//
|
||
|
LinkLocalAddress = LinkLocalPrefix;
|
||
|
TunnelCreateIsatapToken(Context, &LinkLocalAddress);
|
||
|
NTE = (NetTableEntry *) *FindADE(IF, &LinkLocalAddress);
|
||
|
if ((NTE != NULL) && (NTE->Type == ADE_UNICAST))
|
||
|
IF->LinkLocalNTE = NTE;
|
||
|
|
||
|
//
|
||
|
// Enable address auto-configuration.
|
||
|
//
|
||
|
IF->CreateToken = TunnelCreateIsatapToken;
|
||
|
|
||
|
//
|
||
|
// Enable Router Discovery.
|
||
|
//
|
||
|
IF->Flags |= IF_FLAG_ROUTER_DISCOVERS;
|
||
|
|
||
|
//
|
||
|
// Trigger a Router Solicitation.
|
||
|
//
|
||
|
if (!(IF->Flags & IF_FLAG_ADVERTISES)) {
|
||
|
IF->RSCount = 0;
|
||
|
IF->RSTimer = 1;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Disable address auto-configuration.
|
||
|
//
|
||
|
IF->CreateToken = NULL;
|
||
|
|
||
|
//
|
||
|
// Disable Router Discovery.
|
||
|
//
|
||
|
IF->Flags &= ~IF_FLAG_ROUTER_DISCOVERS;
|
||
|
|
||
|
//
|
||
|
// Stop sending Router Solicitations.
|
||
|
//
|
||
|
if (!(IF->Flags & IF_FLAG_ADVERTISES)) {
|
||
|
IF->RSTimer = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove addresses & routes that were auto-configured from
|
||
|
// Router Advertisements.
|
||
|
//
|
||
|
AddrConfResetAutoConfig(IF, 0);
|
||
|
RouteTableResetAutoConfig(IF, 0);
|
||
|
InterfaceResetAutoConfig(IF);
|
||
|
|
||
|
KeReleaseSpinLock(&IF->Lock, OldIrql);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelCreatePseudoInterface
|
||
|
//
|
||
|
// Creates a pseudo-interface. Type can either be
|
||
|
// IF_TYPE_TUNNEL_AUTO (v4-compatible/ISATAP) or
|
||
|
// IF_TYPE_TUNNEL_6TO4 (6to4 tunneling).
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
// Return codes:
|
||
|
// STATUS_INSUFFICIENT_RESOURCES
|
||
|
// STATUS_UNSUCCESSFUL
|
||
|
// STATUS_SUCCESS
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelCreatePseudoInterface(const char *InterfaceName, uint Type)
|
||
|
{
|
||
|
GUID Guid;
|
||
|
LLIPBindInfo BindInfo;
|
||
|
TunnelContext *tc;
|
||
|
NTSTATUS Status;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
ASSERT((Type == IF_TYPE_TUNNEL_AUTO) ||
|
||
|
(Type == IF_TYPE_TUNNEL_6TO4));
|
||
|
|
||
|
//
|
||
|
// Allocate memory for the TunnelContext.
|
||
|
//
|
||
|
tc = ExAllocatePool(NonPagedPool, sizeof *tc);
|
||
|
if (tc == NULL) {
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Tunnel pseudo-interfaces need a dummy link-layer address.
|
||
|
// It must be distinct from any address assigned to other nodes,
|
||
|
// so that the loopback check in IPv6SendLL works.
|
||
|
//
|
||
|
tc->SrcAddr = INADDR_LOOPBACK;
|
||
|
tc->TokenAddr = INADDR_ANY;
|
||
|
tc->DstAddr = INADDR_ANY;
|
||
|
tc->SetMCListOK = FALSE;
|
||
|
|
||
|
//
|
||
|
// Prepare the binding info for CreateInterface.
|
||
|
//
|
||
|
BindInfo.lip_context = tc;
|
||
|
BindInfo.lip_maxmtu = TUNNEL_MAX_MTU;
|
||
|
BindInfo.lip_defmtu = TUNNEL_DEFAULT_MTU;
|
||
|
BindInfo.lip_flags = IF_FLAG_PSEUDO;
|
||
|
BindInfo.lip_type = Type;
|
||
|
BindInfo.lip_hdrsize = 0;
|
||
|
BindInfo.lip_addrlen = sizeof(IPAddr);
|
||
|
BindInfo.lip_addr = (uchar *) &tc->SrcAddr;
|
||
|
BindInfo.lip_dadxmit = 0;
|
||
|
BindInfo.lip_pref = TUNNEL_DEFAULT_PREFERENCE;
|
||
|
BindInfo.lip_token = NULL;
|
||
|
BindInfo.lip_rdllopt = NULL;
|
||
|
BindInfo.lip_wrllopt = NULL;
|
||
|
BindInfo.lip_cvaddr = TunnelConvertAddress;
|
||
|
if (Type == IF_TYPE_TUNNEL_AUTO)
|
||
|
BindInfo.lip_setrtrlladdr = TunnelSetRouterLLAddress;
|
||
|
else
|
||
|
BindInfo.lip_setrtrlladdr = NULL;
|
||
|
BindInfo.lip_transmit = TunnelTransmit;
|
||
|
BindInfo.lip_mclist = NULL;
|
||
|
BindInfo.lip_close = TunnelClose;
|
||
|
BindInfo.lip_cleanup = TunnelCleanup;
|
||
|
|
||
|
CreateGUIDFromName(InterfaceName, &Guid);
|
||
|
|
||
|
//
|
||
|
// Prevent races with TunnelClose by taking the mutex
|
||
|
// before calling CreateInterface.
|
||
|
//
|
||
|
KeWaitForSingleObject(&Tunnel.Mutex, Executive, KernelMode, FALSE, NULL);
|
||
|
|
||
|
if (Tunnel.List.AOHandle == NULL) {
|
||
|
//
|
||
|
// TunnelOpenV4 has not yet happened.
|
||
|
// Create the interface in the disconnected state.
|
||
|
//
|
||
|
tc->AOHandle = NULL;
|
||
|
tc->AOFile = NULL;
|
||
|
BindInfo.lip_flags |= IF_FLAG_MEDIA_DISCONNECTED;
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// No need to open a new address object.
|
||
|
// Just reuse the global Tunnel.List address object.
|
||
|
//
|
||
|
tc->AOHandle = Tunnel.List.AOHandle;
|
||
|
tc->AOFile = Tunnel.List.AOFile;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the IPv6 interface.
|
||
|
// We can hold the mutex across this call, but not a spinlock.
|
||
|
//
|
||
|
Status = CreateInterface(&Guid, &BindInfo, (void **)&tc->IF);
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
goto ErrorReturnUnlock;
|
||
|
|
||
|
//
|
||
|
// Once we unlock, the interface could be gone.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
TunnelInsertTunnel(tc, &Tunnel.List);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
|
||
|
ErrorReturnUnlock:
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
ExFreePool(tc);
|
||
|
ErrorReturn:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelOpenV4
|
||
|
//
|
||
|
// Establishes our connection to the IPv4 stack,
|
||
|
// so we can send and receive tunnelled packets.
|
||
|
//
|
||
|
// Called with the tunnel mutex held.
|
||
|
//
|
||
|
void
|
||
|
TunnelOpenV4(void)
|
||
|
{
|
||
|
HANDLE Handle, IcmpHandle;
|
||
|
FILE_OBJECT *File, *IcmpFile;
|
||
|
DEVICE_OBJECT *Device;
|
||
|
IRP *ReceiveIrp;
|
||
|
TunnelContext *tc;
|
||
|
KIRQL OldIrql;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
//
|
||
|
// We use a single address object to receive all tunnelled packets.
|
||
|
//
|
||
|
Handle = TunnelOpenAddressObject(INADDR_ANY,
|
||
|
TUNNEL_DEVICE_NAME(IP_PROTOCOL_V6));
|
||
|
if (Handle == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenV4: TunnelOpenAddressObject(%u) failed\n",
|
||
|
IP_PROTOCOL_V6));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
File = TunnelObjectFromHandle(Handle);
|
||
|
|
||
|
//
|
||
|
// We use a second address object to receive ICMPv4 packets.
|
||
|
//
|
||
|
IcmpHandle = TunnelOpenAddressObject(INADDR_ANY,
|
||
|
TUNNEL_DEVICE_NAME(IP_PROTOCOL_ICMPv4));
|
||
|
if (IcmpHandle == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenV4: TunnelOpenAddressObject(%u) failed\n",
|
||
|
IP_PROTOCOL_ICMPv4));
|
||
|
goto ReturnReleaseHandle;
|
||
|
}
|
||
|
|
||
|
IcmpFile = TunnelObjectFromHandle(IcmpHandle);
|
||
|
|
||
|
//
|
||
|
// Disable reception of multicast loopback packets.
|
||
|
//
|
||
|
Status = TunnelSetAddressObjectMCastLoop(File, FALSE);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenV4: "
|
||
|
"TunnelSetAddressObjectMCastLoop: %x\n", Status));
|
||
|
goto ReturnReleaseBothHandles;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// After TunnelSetReceiveHandler, we will start receiving
|
||
|
// encapsulated v6 packets. However they will be dropped
|
||
|
// until we finish our initialization here.
|
||
|
//
|
||
|
Status = TunnelSetReceiveHandler(File, TunnelReceive);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenV4: "
|
||
|
"TunnelSetReceiveHandler: %x\n", Status));
|
||
|
goto ReturnReleaseBothHandles;
|
||
|
}
|
||
|
|
||
|
Status = TunnelSetReceiveHandler(IcmpFile, TunnelReceive);
|
||
|
if (! NT_SUCCESS(Status)) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenV4: "
|
||
|
"TunnelSetReceiveHandler(2): %x\n", Status));
|
||
|
goto ReturnReleaseBothHandles;
|
||
|
}
|
||
|
|
||
|
Device = File->DeviceObject;
|
||
|
ASSERT(Device == IcmpFile->DeviceObject);
|
||
|
ReceiveIrp = TunnelCreateReceiveIrp(Device);
|
||
|
if (ReceiveIrp == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenV4: TunnelCreateReceiveIrp failed\n"));
|
||
|
|
||
|
ReturnReleaseBothHandles:
|
||
|
ObDereferenceObject(IcmpFile);
|
||
|
TunnelCloseAddressObject(IcmpHandle);
|
||
|
ReturnReleaseHandle:
|
||
|
ObDereferenceObject(File);
|
||
|
TunnelCloseAddressObject(Handle);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We have successfully opened a connection to the IPv4 stack.
|
||
|
// Update our data structures.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
Tunnel.List.AOHandle = Handle;
|
||
|
Tunnel.List.AOFile = File;
|
||
|
Tunnel.V4Device = Device;
|
||
|
Tunnel.ReceiveIrp = ReceiveIrp;
|
||
|
Tunnel.IcmpHandle = IcmpHandle;
|
||
|
Tunnel.IcmpFile = IcmpFile;
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
//
|
||
|
// Now search our list of interfaces and transition
|
||
|
// pseudo-interfaces to the connected state.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
Interface *IF = tc->IF;
|
||
|
|
||
|
if ((IF->Type == IF_TYPE_TUNNEL_AUTO) ||
|
||
|
(IF->Type == IF_TYPE_TUNNEL_6TO4)) {
|
||
|
//
|
||
|
// The pseudo-interface contexts do not hold
|
||
|
// separate references for the main TDI address object.
|
||
|
//
|
||
|
ASSERT(tc->AOHandle == NULL);
|
||
|
ASSERT(tc->AOFile == NULL);
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
tc->AOHandle = Handle;
|
||
|
tc->AOFile = File;
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
SetInterfaceLinkStatus(IF, TRUE);
|
||
|
}
|
||
|
else if (IF->Type == IF_TYPE_TUNNEL_6OVER4) {
|
||
|
//
|
||
|
// We must start listening to multicast addresses
|
||
|
// for this 6over4 interface.
|
||
|
//
|
||
|
RestartLinkLayerMulticast(IF, TunnelResetMulticastAddressListDone);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelAddAddress
|
||
|
//
|
||
|
// Called by TDI when a transport registers an address.
|
||
|
//
|
||
|
void
|
||
|
TunnelAddAddress(
|
||
|
TA_ADDRESS *Address,
|
||
|
UNICODE_STRING *DeviceName,
|
||
|
TDI_PNP_CONTEXT *Context)
|
||
|
{
|
||
|
if (Address->AddressType == TDI_ADDRESS_TYPE_IP) {
|
||
|
TDI_ADDRESS_IP *TdiAddr = (TDI_ADDRESS_IP *) Address->Address;
|
||
|
IPAddr V4Addr = TdiAddr->in_addr;
|
||
|
TunnelContext *tc;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
KeWaitForSingleObject(&Tunnel.Mutex, Executive, KernelMode,
|
||
|
FALSE, NULL);
|
||
|
|
||
|
//
|
||
|
// First, open a connection to the IPv4 stack if needed.
|
||
|
//
|
||
|
if (Tunnel.List.AOHandle == NULL)
|
||
|
TunnelOpenV4();
|
||
|
|
||
|
//
|
||
|
// Next, search for disconnected interfaces that should be connected.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
if (tc->SrcAddr == V4Addr) {
|
||
|
Interface *IF = tc->IF;
|
||
|
|
||
|
if (tc->AOHandle == NULL) {
|
||
|
ASSERT(IF->Flags & IF_FLAG_MEDIA_DISCONNECTED);
|
||
|
|
||
|
TunnelOpenAddress(tc);
|
||
|
|
||
|
//
|
||
|
// Did TunnelOpenAddress succeed?
|
||
|
// If not, leave the interface disconnected.
|
||
|
//
|
||
|
if (tc->AOHandle == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelAddAddress(%s): "
|
||
|
"TunnelOpenAddress failed\n",
|
||
|
FormatV4Address(V4Addr)));
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Connect the interface.
|
||
|
//
|
||
|
SetInterfaceLinkStatus(IF, TRUE);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// This is unusual... it indicates a race
|
||
|
// with TunnelCreateTunnel.
|
||
|
//
|
||
|
ASSERT(!(IF->Flags & IF_FLAG_MEDIA_DISCONNECTED));
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE,
|
||
|
"TunnelAddAddress(%s) IF %p connected?\n",
|
||
|
FormatV4Address(V4Addr), IF));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Finally, add an address object to the list.
|
||
|
// Maintain the invariant that an address is present at most once.
|
||
|
//
|
||
|
for (tc = Tunnel.AOList.Next; ; tc = tc->Next) {
|
||
|
|
||
|
if (tc == &Tunnel.AOList) {
|
||
|
//
|
||
|
// Add a new address object.
|
||
|
//
|
||
|
tc = ExAllocatePool(NonPagedPool, sizeof *tc);
|
||
|
if (tc != NULL) {
|
||
|
|
||
|
//
|
||
|
// Open the address object.
|
||
|
//
|
||
|
tc->SrcAddr = V4Addr;
|
||
|
tc->DstAddr = V4Addr;
|
||
|
TunnelOpenAddress(tc);
|
||
|
|
||
|
if (tc->AOFile != NULL) {
|
||
|
//
|
||
|
// Put the address object on the list.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
TunnelInsertTunnel(tc, &Tunnel.AOList);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
}
|
||
|
else {
|
||
|
//
|
||
|
// Cleanup the context. We will not
|
||
|
// put an address object on the list.
|
||
|
//
|
||
|
ExFreePool(tc);
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (tc->SrcAddr == V4Addr) {
|
||
|
//
|
||
|
// It already exists.
|
||
|
// REVIEW: Can this happen?
|
||
|
//
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE,
|
||
|
"TunnelAddAddress(%s) already on AOList?\n",
|
||
|
FormatV4Address(V4Addr)));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelDelAddress
|
||
|
//
|
||
|
// Called by TDI when a transport unregisters an address.
|
||
|
//
|
||
|
void
|
||
|
TunnelDelAddress(
|
||
|
TA_ADDRESS *Address,
|
||
|
UNICODE_STRING *DeviceName,
|
||
|
TDI_PNP_CONTEXT *Context)
|
||
|
{
|
||
|
if (Address->AddressType == TDI_ADDRESS_TYPE_IP) {
|
||
|
TDI_ADDRESS_IP *TdiAddr = (TDI_ADDRESS_IP *) Address->Address;
|
||
|
IPAddr V4Addr = TdiAddr->in_addr;
|
||
|
TunnelContext *tc;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
KeWaitForSingleObject(&Tunnel.Mutex, Executive, KernelMode,
|
||
|
FALSE, NULL);
|
||
|
|
||
|
//
|
||
|
// Search for connected interfaces that should be disconnected.
|
||
|
//
|
||
|
for (tc = Tunnel.List.Next;
|
||
|
tc != &Tunnel.List;
|
||
|
tc = tc->Next) {
|
||
|
if (tc->SrcAddr == V4Addr) {
|
||
|
Interface *IF = tc->IF;
|
||
|
|
||
|
if (tc->AOHandle == NULL) {
|
||
|
//
|
||
|
// The interface is already disconnected.
|
||
|
//
|
||
|
ASSERT(IF->Flags & IF_FLAG_MEDIA_DISCONNECTED);
|
||
|
}
|
||
|
else {
|
||
|
HANDLE Handle;
|
||
|
FILE_OBJECT *File;
|
||
|
|
||
|
//
|
||
|
// The interface is connected.
|
||
|
//
|
||
|
ASSERT(!(IF->Flags & IF_FLAG_MEDIA_DISCONNECTED));
|
||
|
|
||
|
//
|
||
|
// Disconnect the interface.
|
||
|
//
|
||
|
SetInterfaceLinkStatus(IF, FALSE);
|
||
|
|
||
|
//
|
||
|
// Release the address object.
|
||
|
//
|
||
|
|
||
|
Handle = tc->AOHandle;
|
||
|
File = tc->AOFile;
|
||
|
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
tc->AOHandle = NULL;
|
||
|
tc->AOFile = NULL;
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
ObDereferenceObject(File);
|
||
|
TunnelCloseAddressObject(Handle);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove an address object from the list.
|
||
|
// There can be at most one.
|
||
|
//
|
||
|
for (tc = Tunnel.AOList.Next;
|
||
|
tc != &Tunnel.AOList;
|
||
|
tc = tc->Next) {
|
||
|
if (tc->SrcAddr == V4Addr) {
|
||
|
//
|
||
|
// Remove this cache entry.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
TunnelRemoveTunnel(tc);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
|
||
|
ObDereferenceObject(tc->AOFile);
|
||
|
TunnelCloseAddressObject(tc->AOHandle);
|
||
|
ExFreePool(tc);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelInit - Initialize the tunnel module.
|
||
|
//
|
||
|
// This functions initializes the tunnel module.
|
||
|
//
|
||
|
// Returns FALSE if we fail to init.
|
||
|
// This should "never" happen, so we are not
|
||
|
// careful about cleanup in that case.
|
||
|
//
|
||
|
// Note we return TRUE if IPv4 is not available,
|
||
|
// but then tunnel functionality will not be available.
|
||
|
//
|
||
|
int
|
||
|
TunnelInit(void)
|
||
|
{
|
||
|
TDI_CLIENT_INTERFACE_INFO Handlers;
|
||
|
NTSTATUS status;
|
||
|
|
||
|
Tunnel.KernelProcess = IoGetCurrentProcess();
|
||
|
|
||
|
KeInitializeSpinLock(&Tunnel.Lock);
|
||
|
KeInitializeMutex(&Tunnel.Mutex, 0);
|
||
|
|
||
|
//
|
||
|
// Initialize the global list of tunnels.
|
||
|
//
|
||
|
Tunnel.List.Next = Tunnel.List.Prev = &Tunnel.List;
|
||
|
|
||
|
//
|
||
|
// Initialize the global list of address objects.
|
||
|
//
|
||
|
Tunnel.AOList.Next = Tunnel.AOList.Prev = &Tunnel.AOList;
|
||
|
|
||
|
//
|
||
|
// Initialize the pseudo-interfaces used
|
||
|
// for automatic/ISATAP tunneling
|
||
|
// and 6to4 tunneling.
|
||
|
//
|
||
|
|
||
|
status = TunnelCreatePseudoInterface("Auto Tunnel Pseudo-Interface",
|
||
|
IF_TYPE_TUNNEL_AUTO);
|
||
|
if (! NT_SUCCESS(status))
|
||
|
return FALSE;
|
||
|
ASSERT(IFList->Index == 2); // 6to4svc and scripts depend on this.
|
||
|
|
||
|
status = TunnelCreatePseudoInterface("6to4 Tunnel Pseudo-Interface",
|
||
|
IF_TYPE_TUNNEL_6TO4);
|
||
|
if (! NT_SUCCESS(status))
|
||
|
return FALSE;
|
||
|
ASSERT(IFList->Index == 3); // 6to4svc and scripts depend on this.
|
||
|
|
||
|
//
|
||
|
// Request address notifications from TDI.
|
||
|
// REVIEW - What should ClientName be? Does it matter?
|
||
|
//
|
||
|
|
||
|
memset(&Handlers, 0, sizeof Handlers);
|
||
|
Handlers.MajorTdiVersion = TDI_CURRENT_MAJOR_VERSION;
|
||
|
Handlers.MinorTdiVersion = TDI_CURRENT_MINOR_VERSION;
|
||
|
Handlers.ClientName = &Tunnel.List.Next->IF->DeviceName;
|
||
|
Handlers.AddAddressHandlerV2 = TunnelAddAddress;
|
||
|
Handlers.DelAddressHandlerV2 = TunnelDelAddress;
|
||
|
|
||
|
status = TdiRegisterPnPHandlers(&Handlers, sizeof Handlers,
|
||
|
&Tunnel.TdiHandle);
|
||
|
if (!NT_SUCCESS(status))
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelUnload
|
||
|
//
|
||
|
// Called to cleanup when the driver is unloading.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
void
|
||
|
TunnelUnload(void)
|
||
|
{
|
||
|
TunnelContext *tc;
|
||
|
|
||
|
//
|
||
|
// All interfaces are already destroyed.
|
||
|
//
|
||
|
ASSERT(Tunnel.List.Next == &Tunnel.List);
|
||
|
ASSERT(Tunnel.List.Prev == &Tunnel.List);
|
||
|
|
||
|
//
|
||
|
// Stop TDI notifications.
|
||
|
// REVIEW: How to handle failure, esp. STATUS_NETWORK_BUSY?
|
||
|
//
|
||
|
(void) TdiDeregisterPnPHandlers(Tunnel.TdiHandle);
|
||
|
|
||
|
//
|
||
|
// Cleanup any remaining address objects.
|
||
|
//
|
||
|
while ((tc = Tunnel.AOList.Next) != &Tunnel.AOList) {
|
||
|
TunnelRemoveTunnel(tc);
|
||
|
ObDereferenceObject(tc->AOFile);
|
||
|
TunnelCloseAddressObject(tc->AOHandle);
|
||
|
ExFreePool(tc);
|
||
|
}
|
||
|
ASSERT(Tunnel.AOList.Prev == &Tunnel.AOList);
|
||
|
|
||
|
//
|
||
|
// Cleanup if TunnelOpenV4 has succeeded.
|
||
|
//
|
||
|
if (Tunnel.List.AOHandle != NULL) {
|
||
|
void *buffer;
|
||
|
TunnelContext *tc;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
//
|
||
|
// Stop receiving encapsulated (v6 in v4) and ICMPv4 packets.
|
||
|
// This should block until any current TunnelReceive
|
||
|
// callbacks return, and prevent new callbacks.
|
||
|
// REVIEW: It is really legal to change a receive handler?
|
||
|
// Would just closing the address objects have the proper
|
||
|
// synchronization behavior?
|
||
|
//
|
||
|
(void) TunnelSetReceiveHandler(Tunnel.IcmpFile, NULL);
|
||
|
(void) TunnelSetReceiveHandler(Tunnel.List.AOFile, NULL);
|
||
|
|
||
|
ObDereferenceObject(Tunnel.IcmpFile);
|
||
|
TunnelCloseAddressObject(Tunnel.IcmpHandle);
|
||
|
|
||
|
ObDereferenceObject(Tunnel.List.AOFile);
|
||
|
TunnelCloseAddressObject(Tunnel.List.AOHandle);
|
||
|
|
||
|
buffer = Tunnel.ReceiveIrp->MdlAddress->MappedSystemVa;
|
||
|
IoFreeMdl(Tunnel.ReceiveIrp->MdlAddress);
|
||
|
IoFreeIrp(Tunnel.ReceiveIrp);
|
||
|
ExFreePool(buffer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//* TunnelCreateTunnel
|
||
|
//
|
||
|
// Creates a tunnel. If DstAddr is INADDR_ANY,
|
||
|
// then it's a 6-over-4 tunnel. Otherwise it's point-to-point.
|
||
|
//
|
||
|
// Callable from thread context, not DPC context.
|
||
|
//
|
||
|
// Return codes:
|
||
|
// STATUS_ADDRESS_ALREADY_EXISTS The tunnel already exists.
|
||
|
// STATUS_INSUFFICIENT_RESOURCES
|
||
|
// STATUS_UNSUCCESSFUL
|
||
|
// STATUS_SUCCESS
|
||
|
//
|
||
|
NTSTATUS
|
||
|
TunnelCreateTunnel(IPAddr SrcAddr, IPAddr DstAddr,
|
||
|
uint Flags, Interface **ReturnIF)
|
||
|
{
|
||
|
char SrcAddrStr[16], DstAddrStr[16];
|
||
|
char InterfaceName[128];
|
||
|
GUID Guid;
|
||
|
LLIPBindInfo BindInfo;
|
||
|
TunnelContext *tc, *tcTmp;
|
||
|
KIRQL OldIrql;
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
//
|
||
|
// 6over4 interfaces must use Neighbor Discovery
|
||
|
// and may use Router Discovery but should not have other flags set.
|
||
|
// p2p interfaces may use ND, RD, and/or periodic MLD.
|
||
|
//
|
||
|
ASSERT(SrcAddr != INADDR_ANY);
|
||
|
ASSERT((DstAddr == INADDR_ANY) ?
|
||
|
((Flags & IF_FLAG_NEIGHBOR_DISCOVERS) &&
|
||
|
!(Flags &~ IF_FLAGS_DISCOVERS)) :
|
||
|
!(Flags &~ (IF_FLAGS_DISCOVERS|IF_FLAG_PERIODICMLD)));
|
||
|
|
||
|
FormatV4AddressWorker(SrcAddrStr, SrcAddr);
|
||
|
FormatV4AddressWorker(DstAddrStr, DstAddr);
|
||
|
|
||
|
tc = ExAllocatePool(NonPagedPool, sizeof *tc);
|
||
|
if (tc == NULL) {
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto ErrorReturn;
|
||
|
}
|
||
|
|
||
|
tc->DstAddr = DstAddr;
|
||
|
tc->TokenAddr = tc->SrcAddr = SrcAddr;
|
||
|
tc->SetMCListOK = FALSE;
|
||
|
|
||
|
//
|
||
|
// Prepare the binding info for CreateInterface.
|
||
|
//
|
||
|
BindInfo.lip_context = tc;
|
||
|
BindInfo.lip_maxmtu = TUNNEL_MAX_MTU;
|
||
|
BindInfo.lip_defmtu = TUNNEL_DEFAULT_MTU;
|
||
|
if (DstAddr == INADDR_ANY) {
|
||
|
BindInfo.lip_type = IF_TYPE_TUNNEL_6OVER4;
|
||
|
BindInfo.lip_flags = IF_FLAG_MULTICAST;
|
||
|
|
||
|
sprintf(InterfaceName, "6over4 %hs", SrcAddrStr);
|
||
|
} else {
|
||
|
BindInfo.lip_type = IF_TYPE_TUNNEL_V6V4;
|
||
|
BindInfo.lip_flags = IF_FLAG_P2P | IF_FLAG_MULTICAST;
|
||
|
|
||
|
sprintf(InterfaceName, "v6v4 %hs %hs", SrcAddrStr, DstAddrStr);
|
||
|
}
|
||
|
BindInfo.lip_flags |= Flags;
|
||
|
|
||
|
CreateGUIDFromName(InterfaceName, &Guid);
|
||
|
|
||
|
//
|
||
|
// We do not want IPv6 to reserve space for our link-layer header.
|
||
|
//
|
||
|
BindInfo.lip_hdrsize = 0;
|
||
|
//
|
||
|
// For point-to-point interfaces, the remote link-layer address
|
||
|
// must follow the local link-layer address in memory.
|
||
|
// So we rely on the TunnelContext layout of SrcAddr & DstAddr.
|
||
|
//
|
||
|
BindInfo.lip_addrlen = sizeof(IPAddr);
|
||
|
BindInfo.lip_addr = (uchar *) &tc->SrcAddr;
|
||
|
BindInfo.lip_dadxmit = 1; // Per RFC 2462.
|
||
|
BindInfo.lip_pref = TUNNEL_DEFAULT_PREFERENCE;
|
||
|
|
||
|
BindInfo.lip_token = TunnelCreateToken;
|
||
|
BindInfo.lip_cvaddr = TunnelConvertAddress;
|
||
|
BindInfo.lip_transmit = TunnelTransmitND;
|
||
|
if (DstAddr == INADDR_ANY) {
|
||
|
BindInfo.lip_mclist = TunnelSetMulticastAddressList;
|
||
|
BindInfo.lip_rdllopt = TunnelReadLinkLayerAddressOption;
|
||
|
BindInfo.lip_wrllopt = TunnelWriteLinkLayerAddressOption;
|
||
|
}
|
||
|
else {
|
||
|
BindInfo.lip_mclist = NULL;
|
||
|
BindInfo.lip_rdllopt = NULL;
|
||
|
BindInfo.lip_wrllopt = NULL;
|
||
|
}
|
||
|
BindInfo.lip_close = TunnelClose;
|
||
|
BindInfo.lip_cleanup = TunnelCleanup;
|
||
|
|
||
|
KeWaitForSingleObject(&Tunnel.Mutex, Executive, KernelMode, FALSE, NULL);
|
||
|
|
||
|
//
|
||
|
// Open an IPv4 TDI Address Object that is bound
|
||
|
// to this address. Packets sent with this AO
|
||
|
// will use this address as the v4 source.
|
||
|
// If the open fails, we create the interface disconnected.
|
||
|
//
|
||
|
TunnelOpenAddress(tc);
|
||
|
if (tc->AOHandle == NULL) {
|
||
|
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR,
|
||
|
"TunnelOpenAddress(%s) failed\n",
|
||
|
FormatV4Address(SrcAddr)));
|
||
|
BindInfo.lip_flags |= IF_FLAG_MEDIA_DISCONNECTED;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check that an equivalent tunnel doesn't already exist.
|
||
|
//
|
||
|
for (tcTmp = Tunnel.List.Next;
|
||
|
tcTmp != &Tunnel.List;
|
||
|
tcTmp = tcTmp->Next) {
|
||
|
|
||
|
if ((tcTmp->SrcAddr == SrcAddr) &&
|
||
|
(tcTmp->DstAddr == DstAddr)) {
|
||
|
|
||
|
Status = STATUS_ADDRESS_ALREADY_EXISTS;
|
||
|
goto ErrorReturnUnlock;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// For 6over4 interfaces, start receiving multicasts.
|
||
|
//
|
||
|
if (DstAddr == INADDR_ANY) {
|
||
|
//
|
||
|
// Synchronize with TunnelOpenV4.
|
||
|
//
|
||
|
if (Tunnel.List.AOHandle != NULL)
|
||
|
tc->SetMCListOK = TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Create the IPv6 interface.
|
||
|
// We can hold the mutex across this call, but not a spinlock.
|
||
|
//
|
||
|
Status = CreateInterface(&Guid, &BindInfo, (void **)&tc->IF);
|
||
|
if (! NT_SUCCESS(Status))
|
||
|
goto ErrorReturnUnlock;
|
||
|
|
||
|
//
|
||
|
// Return a reference to the interface, if requested.
|
||
|
//
|
||
|
if (ReturnIF != NULL) {
|
||
|
Interface *IF = tc->IF;
|
||
|
AddRefIF(IF);
|
||
|
*ReturnIF = IF;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Put this tunnel on our global list.
|
||
|
// Note that once we unlock, it could be immediately deleted.
|
||
|
//
|
||
|
KeAcquireSpinLock(&Tunnel.Lock, &OldIrql);
|
||
|
TunnelInsertTunnel(tc, &Tunnel.List);
|
||
|
KeReleaseSpinLock(&Tunnel.Lock, OldIrql);
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
|
||
|
ErrorReturnUnlock:
|
||
|
KeReleaseMutex(&Tunnel.Mutex, FALSE);
|
||
|
if (tc->AOFile != NULL)
|
||
|
ObDereferenceObject(tc->AOFile);
|
||
|
if (tc->AOHandle != NULL)
|
||
|
TunnelCloseAddressObject(tc->AOHandle);
|
||
|
ExFreePool(tc);
|
||
|
ErrorReturn:
|
||
|
return Status;
|
||
|
}
|