windows-nt/Source/XPSP1/NT/net/tcpip/tpipv6/tcpip6/ip6/tunnel.c
2020-09-26 16:20:57 +08:00

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;
}