|
|
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// IPv6 loopback interface pseudo-driver.
//
#include "oscfg.h"
#include "ndis.h"
#include "ip6imp.h"
#include "ip6def.h"
#include "llip6if.h"
#include "route.h"
#include "icmp.h"
#include "neighbor.h"
#include "security.h"
#include <ntddip6.h>
//
// We set the default loopback MTU to be smaller than the maximum,
// to avoid the use of jumbograms. In fact, we use the ethernet MTU
// because it appears TCP behaves poorly with large MTUs.
//
#define DEFAULT_LOOPBACK_MTU 1500 // Same as ethernet.
#define MAX_LOOPBACK_MTU ((uint)-1) // Effectively infinite.
KSPIN_LOCK LoopLock; PNDIS_PACKET LoopTransmitHead = (PNDIS_PACKET)NULL; PNDIS_PACKET LoopTransmitTail = (PNDIS_PACKET)NULL; WORK_QUEUE_ITEM LoopWorkItem; int LoopTransmitRunning = 0;
Interface *LoopInterface; // Loopback interface.
//* LoopTransmit
//
// This is the work item routine called for a loopback transmit.
// Pull packets off the transmit queue and "send" them to ourselves
// by the expedient of receiving them locally.
//
void LoopTransmit(void *Context) // Unused.
{ KIRQL OriginalIrql; PNDIS_PACKET Packet; IPv6Packet IPPacket; int Rcvd = FALSE; int PktRefs; // Packet references
UNREFERENCED_PARAMETER(Context);
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// All receive processing normally happens at DPC level,
// so we must pretend to be a DPC, so we raise IRQL.
// (System worker threads typically run at PASSIVE_LEVEL).
//
// Also block APCs while we're here, to make sure previous I/O requests
// issued from this thread don't block the work-item queue.
//
KeEnterCriticalRegion(); KeAcquireSpinLock(&LoopLock, &OriginalIrql); ASSERT(LoopTransmitRunning);
for (;;) { //
// Get the next packet from the queue.
//
Packet = LoopTransmitHead; if (Packet == NULL) break;
LoopTransmitHead = *(PNDIS_PACKET *)Packet->MacReserved; KeReleaseSpinLockFromDpcLevel(&LoopLock);
Rcvd = TRUE;
//
// Prepare IPv6Packet notification info from NDIS packet.
//
InitializePacketFromNdis(&IPPacket, Packet, PC(Packet)->pc_offset); IPPacket.NTEorIF = CastFromIF(PC(Packet)->IF); IPPacket.Flags |= PACKET_LOOPED_BACK;
PktRefs = IPv6Receive(&IPPacket);
ASSERT(PktRefs == 0);
//
// Prevent the packet from being sent again via loopback
// from IPv6SendComplete.
//
PC(Packet)->Flags |= NDIS_FLAGS_DONT_LOOPBACK; IPv6SendComplete(PC(Packet)->IF, Packet, IP_SUCCESS);
//
// Give other threads a chance to run.
//
KeLowerIrql(OriginalIrql); KeAcquireSpinLock(&LoopLock, &OriginalIrql); }
LoopTransmitRunning = FALSE; KeReleaseSpinLockFromDpcLevel(&LoopLock);
if (Rcvd) IPv6ReceiveComplete();
KeLowerIrql(OriginalIrql); KeLeaveCriticalRegion(); }
//* LoopQueueTransmit
//
// This is the routine called when we need to transmit a packet to ourselves.
// We put the packet on our loopback queue, and schedule an event to deal
// with it. All the real work is done in LoopTransmit.
//
// LoopQueueTransmit is called directly from IPv6SendLL.
// It is never called via LoopInterface->Transmit.
//
void LoopQueueTransmit(PNDIS_PACKET Packet) { PNDIS_PACKET *PacketPtr; KIRQL OldIrql;
PacketPtr = (PNDIS_PACKET *)Packet->MacReserved; *PacketPtr = (PNDIS_PACKET)NULL;
KeAcquireSpinLock(&LoopLock, &OldIrql);
//
// Add the packet to the end of the transmit queue.
//
if (LoopTransmitHead == (PNDIS_PACKET)NULL) { // Transmit queue is empty.
LoopTransmitHead = Packet; } else { // Transmit queue is not empty.
PacketPtr = (PNDIS_PACKET *)LoopTransmitTail->MacReserved; *PacketPtr = Packet; } LoopTransmitTail = Packet;
//
// If LoopTransmit is not already running, schedule it.
//
if (!LoopTransmitRunning) { ExQueueWorkItem(&LoopWorkItem, DelayedWorkQueue); LoopTransmitRunning = TRUE; } KeReleaseSpinLock(&LoopLock, OldIrql); }
//* LoopbackTransmit
//
// LoopbackTransmit can be called when a multicast packet is sent
// on the loopback interface. It does nothing, because
// loopback processing actually happens in LoopTransmit.
//
void LoopbackTransmit( void *Context, // Pointer to loopback interface.
PNDIS_PACKET Packet, // Pointer to packet to be transmitted.
uint Offset, // Offset from start of packet to IPv6 header.
const void *LinkAddress) // Link-level address.
{ Interface *IF = (Interface *) Context;
UNREFERENCED_PARAMETER(Offset); UNREFERENCED_PARAMETER(LinkAddress);
IPv6SendComplete(IF, Packet, IP_SUCCESS); }
//* LoopbackConvertAddr
//
// Loopback does not use Neighbor Discovery or link-layer addresses.
//
ushort LoopbackConvertAddr( void *Context, const IPv6Addr *Address, void *LinkAddress) { UNREFERENCED_PARAMETER(Context); UNREFERENCED_PARAMETER(Address); UNREFERENCED_PARAMETER(LinkAddress);
return ND_STATE_PERMANENT; }
//* LoopbackCreateToken
//
// Initializes the interface identifer in the address.
// For loopback, we use the interface index.
//
void LoopbackCreateToken(void *Context, IPv6Addr *Address) { Interface *IF = (Interface *)Context;
*(ULONG UNALIGNED *)&Address->s6_bytes[8] = 0; *(ULONG UNALIGNED *)&Address->s6_bytes[12] = net_long(IF->Index); }
#pragma BEGIN_INIT
//* CreateLoopbackInterface
//
// Create a loopback interface.
//
Interface * CreateLoopbackInterface(const char *InterfaceName) { GUID Guid; LLIPv6BindInfo BindInfo; Interface *IF; NDIS_STATUS Status;
//
// A NULL lip_context indicates that we want to use
// the IPv6 Interface structure instead.
//
BindInfo.lip_context = NULL; BindInfo.lip_maxmtu = MAX_LOOPBACK_MTU; BindInfo.lip_defmtu = DEFAULT_LOOPBACK_MTU; BindInfo.lip_flags = IF_FLAG_MULTICAST; BindInfo.lip_type = IF_TYPE_LOOPBACK; BindInfo.lip_hdrsize = 0; BindInfo.lip_addrlen = 0; BindInfo.lip_dadxmit = 0; BindInfo.lip_pref = 0; BindInfo.lip_addr = (uchar *)&LoopbackAddr; BindInfo.lip_token = LoopbackCreateToken; BindInfo.lip_rdllopt = NULL; BindInfo.lip_wrllopt = NULL; BindInfo.lip_cvaddr = LoopbackConvertAddr; BindInfo.lip_setrtrlladdr = NULL; BindInfo.lip_transmit = LoopbackTransmit; BindInfo.lip_mclist = NULL; BindInfo.lip_close = NULL; BindInfo.lip_cleanup = NULL;
CreateGUIDFromName(InterfaceName, &Guid);
Status = CreateInterface(&Guid, &BindInfo, (void **)&IF); if (Status != NDIS_STATUS_SUCCESS) return NULL; else return IF; }
//* LoopbackInit
//
// This function initializes the loopback interface.
//
// Returns FALSE if we fail to init properly.
//
int LoopbackInit(void) { int rc;
//
// Prepare a work item that we will later enqueue when we want
// to execute LoopTransmit.
//
ExInitializeWorkItem(&LoopWorkItem, LoopTransmit, NULL); KeInitializeSpinLock(&LoopLock);
//
// Create the loopback interface.
//
LoopInterface = CreateLoopbackInterface("Loopback Pseudo-Interface"); if (LoopInterface == NULL) return FALSE;
//
// Create the usual loopback address.
//
rc = FindOrCreateNTE(LoopInterface, &LoopbackAddr, ADDR_CONF_WELLKNOWN, INFINITE_LIFETIME, INFINITE_LIFETIME);
//
// Release the reference from CreateInterface.
// The interface still has a reference for itself
// by virtue of being active.
//
ReleaseIF(LoopInterface);
return rc; }
#pragma END_INIT
|