/********************************************************************/
/** Microsoft LAN Manager **/
/** Copyright(c) Microsoft Corp., 1990-1992 **/
/********************************************************************/
/* :ts=4 */
//*** iploop.c - IP loopback routines.
//
// This file contains all the routines related to loopback
#include "oscfg.h"
#include "cxport.h"
#include "ndis.h"
#include "ip.h"
#include "tdistat.h"
#include "ipdef.h"
#include "ipinit.h"
#include "llipif.h"
#include "iprtdef.h"
#include "iproute.h"
#include "tdiinfo.h"
#include "llinfo.h"
#define LOOP_LOOKAHEAD MAX_HDR_SIZE + 8
extern int NumNTE;
extern Interface *IFList;
extern uint NumIF;
extern void IPRcv(void *, void *, uint, uint, NDIS_HANDLE, uint, uint);
extern void IPSendComplete(void *, PNDIS_PACKET, NDIS_STATUS);
extern void IPRcvComplete(void);
extern PNDIS_BUFFER CopyToNdis(PNDIS_BUFFER DestBuf, uchar *SrcBuf, uint Size,
uint *StartOffset);
DEFINE_LOCK_STRUCTURE(LoopLock)
PNDIS_PACKET LoopXmitHead = (PNDIS_PACKET)NULL;
PNDIS_PACKET LoopXmitTail = (PNDIS_PACKET)NULL;
CTEEvent LoopXmitEvent;
RouteInterface LoopInterface; // Loopback interface.
uint LoopXmitRtnRunning = 0;
#ifdef NT
#ifdef ALLOC_PRAGMA
//
// Make init code disposable.
//
NetTableEntry *InitLoopback(IPConfigInfo *ConfigInfo);
#pragma alloc_text(INIT, InitLoopback)
#endif // ALLOC_PRAGMA
#endif // NT
uint LoopIndex; // Index of loop I/F.
uint LoopInstance; // I/F instance of loopback I/F.
NetTableEntry *LoopNTE; // Pointer to loopback NTE.
IFEntry LoopIFE; // Loopback IF Entry.
uchar LoopName[] = "MS TCP Loopback interface";
uint LoopEntityType = IF_MIB;
//* LoopXmitRtn - Loopback xmit event routine.
//
// This is the delayed event routine called for a loopback transmit.
//
// Entry: Event - Pointer to event structure.
// Context - Pointer to loopback NTE
//
// Returns: Nothing.
//
void
LoopXmitRtn(CTEEvent *Event, void *Context)
{
PNDIS_PACKET Packet; // Pointer to packet being transmitted
CTELockHandle Handle;
PNDIS_BUFFER Buffer; // Current NDIS buffer being processed.
uint TotalLength; // Total length of send.
uint LookaheadLength; // Bytes in lookahead.
uint Copied; // Bytes copied so far.
uchar *CopyPtr; // Pointer to buffer being copied into.
uchar *SrcPtr; // Pointer to buffer being copied from.
uint SrcLength; // Length of src buffer.
uchar LookaheadBuffer[LOOP_LOOKAHEAD];
uchar Rcvd = FALSE;
#ifdef NT
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
//
// Raise IRQL so we can acquire locks at DPC level in the receive code.
//
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
#endif // NT
CTEGetLock(&LoopLock, &Handle);
if (LoopXmitRtnRunning) {
CTEFreeLock(&LoopLock, Handle);
#ifdef NT
KeLowerIrql(OldIrql);
#endif // NT
return;
}
LoopXmitRtnRunning = 1;
for (;;) {
Packet = LoopXmitHead; // Get the next packet from the list.
if (Packet != (PNDIS_PACKET)NULL) {
LoopXmitHead = *(PNDIS_PACKET *)Packet->MacReserved;
LoopIFE.if_outqlen--;
CTEFreeLock(&LoopLock, Handle);
} else { // Nothing left to do.
LoopXmitRtnRunning = 0;
CTEFreeLock(&LoopLock, Handle);
break;
}
NdisQueryPacket(Packet, NULL, NULL, &Buffer, &TotalLength);
LoopIFE.if_outoctets += TotalLength;
LoopIFE.if_inoctets += TotalLength;
// See if the interface is up. If it's not, we can't deliver it.
if (LoopIFE.if_adminstatus == IF_STATUS_UP) {
LookaheadLength = MIN(LOOP_LOOKAHEAD, TotalLength);
Copied = 0;
CopyPtr = LookaheadBuffer;
while (Copied < LookaheadLength) {
uint ThisCopy; // Bytes to copy this time.
#ifdef DEBUG
if (!Buffer) {
DEBUGCHK;
CTEGetLock(&LoopLock, &Handle);
LoopXmitRtnRunning = 0;
CTEFreeLock(&LoopLock, Handle);
#ifdef NT
KeLowerIrql(OldIrql);
#endif // NT
return;
}
#endif
NdisQueryBuffer(Buffer, &SrcPtr, &SrcLength);
ThisCopy = MIN(SrcLength, LookaheadLength - Copied);
CTEMemCopy(CopyPtr, SrcPtr, ThisCopy);
Copied += ThisCopy;
CopyPtr += ThisCopy;
NdisGetNextBuffer(Buffer, &Buffer);
}
Rcvd = TRUE;
LoopIFE.if_inucastpkts++;
IPRcv(Context, LookaheadBuffer, LookaheadLength, TotalLength,
(NDIS_HANDLE) Packet, 0, FALSE);
} else {
LoopIFE.if_indiscards++;
}
IPSendComplete(Context, Packet, NDIS_STATUS_SUCCESS);
#ifdef NT
//
// Give other threads a chance to run.
//
KeLowerIrql(OldIrql);
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
#endif // NT
CTEGetLock(&LoopLock, &Handle);
}
if (Rcvd) {
IPRcvComplete();
}
#ifdef NT
KeLowerIrql(OldIrql);
#endif // NT
}
//** LoopXmit - Transmit a loopback packet.
//
// This is the routine called when we need to transmit a packet to ourselves. We put
// the packet on our loopback list, and schedule an event to deal with it.
//
// Entry: Context - Pointer to the loopback NTE.
// Packet - Pointer to packet to be transmitted.
// Dest - Destination addres of packet.
// RCE - Pointer to RCE (should be NULL).
//
// Returns: NDIS_STATUS_PENDING
//
NDIS_STATUS
LoopXmit(void *Context, PNDIS_PACKET Packet, IPAddr Dest, RouteCacheEntry *RCE)
{
PNDIS_PACKET *PacketPtr;
CTELockHandle Handle;
LoopIFE.if_outucastpkts++;
if (LoopIFE.if_adminstatus == IF_STATUS_UP) {
PacketPtr = (PNDIS_PACKET *)Packet->MacReserved;
*PacketPtr = (PNDIS_PACKET)NULL;
CTEGetLock(&LoopLock, &Handle);
if (LoopXmitHead == (PNDIS_PACKET)NULL) { // Xmit. Q is empty
LoopXmitHead = Packet;
} else { // Xmit. Q is not empty
PacketPtr = (PNDIS_PACKET *)LoopXmitTail->MacReserved;
*PacketPtr = Packet;
}
LoopXmitTail = Packet;
LoopIFE.if_outqlen++;
if (!LoopXmitRtnRunning) {
CTEScheduleEvent(&LoopXmitEvent, Context);
}
CTEFreeLock(&LoopLock, Handle);
return NDIS_STATUS_PENDING;
} else {
LoopIFE.if_outdiscards++;
return NDIS_STATUS_SUCCESS;
}
}
//* LoopXfer - Loopback transfer data routine.
//
// Called when we need to transfer data for the loopback net. The input TDContext is
// the original packet.
//
// Entry: Context - Pointer to loopback NTE.
// TDContext - Original packet that was sent.
// Dummy - Unused
// Offset - Offset in frame from which to start copying.
// BytesToCopy - Number of bytes to copy.
// DestPacket - Packet describing buffer to copy into.
// BytesCopied - Place to return bytes copied.
//
// Returns: NDIS_STATUS_SUCCESS
//
NDIS_STATUS
LoopXfer(void *Context, NDIS_HANDLE TDContext, uint Dummy, uint Offset, uint BytesToCopy,
PNDIS_PACKET DestPacket, uint *BytesCopied)
{
PNDIS_BUFFER SrcBuffer; // Current buffer we're copying from.
PNDIS_PACKET SrcPacket = (PNDIS_PACKET)TDContext;
uchar *SrcPtr; // Where we're copying from.
uint SrcLength; // Length of current src buffer.
PNDIS_BUFFER DestBuffer; // Buffer we're copying to.
uchar *DestPtr; // Where we're copying to.
uint DestLength; // Length of current dest. buffer.
uint Copied; // Length we've copied so far.
// First, skip over Offset bytes in the packet.
NdisQueryPacket(SrcPacket, NULL, NULL, &SrcBuffer, NULL);
#ifdef DEBUG
if (!SrcBuffer)
DEBUGCHK;
#endif
NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength);
while (Offset >= SrcLength) {
Offset -= SrcLength;
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
#ifdef DEBUG
if (!SrcBuffer)
DEBUGCHK;
#endif
NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength);
}
// Update Src pointer and length.
SrcPtr += Offset;
SrcLength -= Offset;
// Set up the destination pointers and lengths.
NdisQueryPacket(DestPacket, NULL, NULL, &DestBuffer, NULL);
NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength);
Copied = 0;
while (BytesToCopy) {
uint ThisCopy; // What we're copying this time.
ThisCopy = MIN(SrcLength, DestLength);
CTEMemCopy(DestPtr, SrcPtr, ThisCopy);
Copied += ThisCopy;
DestPtr += ThisCopy;
SrcPtr += ThisCopy;
BytesToCopy -= ThisCopy;
SrcLength -= ThisCopy;
DestLength -= ThisCopy;
if (!SrcLength) { // We've exhausted the source buffer.
NdisGetNextBuffer(SrcBuffer, &SrcBuffer);
if (!SrcBuffer) {
#ifdef DEBUG
if (BytesToCopy)
DEBUGCHK;
#endif
break; // Copy is done.
}
NdisQueryBuffer(SrcBuffer, &SrcPtr, &SrcLength);
}
if (!DestLength) { // We've exhausted the destination buffer.
NdisGetNextBuffer(DestBuffer, &DestBuffer);
if (!DestBuffer) {
#ifdef DEBUG
if (BytesToCopy)
DEBUGCHK;
#endif
break; // Copy is done.
}
NdisQueryBuffer(DestBuffer, &DestPtr, &DestLength);
}
}
*BytesCopied = Copied;
return NDIS_STATUS_SUCCESS;
}
//* LoopClose - Loopback close routine.
//
// This is the loopback close routine. It does nothing but return.
//
// Entry: Context - Unused.
//
// Returns: Nothing.
//
void
LoopClose(void *Context)
{
}
//* LoopInvalidate - Invalidate an RCE.
//
// The loopback invalidate RCE routine. It also does nothing.
//
// Entry: Context - Unused.
// RCE - Pointer to RCE to be invalidated.
//
// Returns: Nothing.
//
void
LoopInvalidate(void *Context, RouteCacheEntry *RCE)
{
}
//* LoopQInfo - Loopback query information handler.
//
// Called when the upper layer wants to query information about the loopback
// interface.
//
// Input: IFContext - Interface context (unused).
// ID - TDIObjectID for object.
// Buffer - Buffer to put data into.
// Size - Pointer to size of buffer. On return, filled with
// bytes copied.
// Context - Pointer to context block.
//
// Returns: Status of attempt to query information.
//
int
LoopQInfo(void *IFContext, TDIObjectID *ID, PNDIS_BUFFER Buffer, uint *Size,
void *Context)
{
uint Offset = 0;
uint BufferSize = *Size;
uint Entity;
uint Instance;
Entity = ID->toi_entity.tei_entity;
Instance = ID->toi_entity.tei_instance;
// First, make sure it's possibly an ID we can handle.
if (Entity != IF_ENTITY || Instance != LoopInstance) {
return TDI_INVALID_REQUEST;
}
*Size = 0; // In case of an error.
if (ID->toi_type != INFO_TYPE_PROVIDER)
return TDI_INVALID_PARAMETER;
if (ID->toi_class == INFO_CLASS_GENERIC) {
if (ID->toi_id == ENTITY_TYPE_ID) {
// He's trying to see what type we are.
if (BufferSize >= sizeof(uint)) {
(void)CopyToNdis(Buffer, (uchar *)&LoopEntityType, sizeof(uint),
&Offset);
return TDI_SUCCESS;
} else
return TDI_BUFFER_TOO_SMALL;
}
return TDI_INVALID_PARAMETER;
} else
if (ID->toi_class != INFO_CLASS_PROTOCOL)
return TDI_INVALID_PARAMETER;
// If he's asking for MIB statistics, then return them, otherwise fail
// the request.
if (ID->toi_id == IF_MIB_STATS_ID) {
// He's asking for statistics. Make sure his buffer is at least big
// enough to hold the fixed part.
if (BufferSize < IFE_FIXED_SIZE) {
return TDI_BUFFER_TOO_SMALL;
}
// He's got enough to hold the fixed part. Copy our IFE structure
// into his buffer.
Buffer = CopyToNdis(Buffer, (uchar *)&LoopIFE, IFE_FIXED_SIZE, &Offset);
// See if he has room for the descriptor string.
if (BufferSize >= (IFE_FIXED_SIZE + sizeof(LoopName))) {
// He has room. Copy it.
(void)CopyToNdis(Buffer, LoopName, sizeof(LoopName), &Offset);
*Size = IFE_FIXED_SIZE + sizeof(LoopName);
return TDI_SUCCESS;
} else {
// Not enough room to copy the desc. string.
*Size = IFE_FIXED_SIZE;
return TDI_BUFFER_OVERFLOW;
}
}
return TDI_INVALID_PARAMETER;
}
//* LoopSetInfo - Loopback set information handler.
//
// The loopback set information handler. We support setting of an I/F admin
// status.
//
// Input: Context - Pointer to I/F to set on.
// ID - The object ID
// Buffer - Pointer to buffer containing value to set.
// Size - Size in bytes of Buffer.
//
// Returns: Status of attempt to set information.
//
int
LoopSetInfo(void *Context, TDIObjectID *ID, void *Buffer, uint Size)
{
IFEntry *IFE = (IFEntry *)Buffer;
uint Entity, Instance, Status;
Entity = ID->toi_entity.tei_entity;
Instance = ID->toi_entity.tei_instance;
// First, make sure it's possibly an ID we can handle.
if (Entity != IF_ENTITY || Instance != LoopInstance) {
return TDI_INVALID_REQUEST;
}
if (ID->toi_class != INFO_CLASS_PROTOCOL ||
ID->toi_type != INFO_TYPE_PROVIDER) {
return TDI_INVALID_PARAMETER;
}
// It's for the I/F level, see if it's for the statistics.
if (ID->toi_id == IF_MIB_STATS_ID) {
// It's for the stats. Make sure it's a valid size.
if (Size >= IFE_FIXED_SIZE) {
// It's a valid size. See what he wants to do.
Status = IFE->if_adminstatus;
if (Status == IF_STATUS_UP || Status == IF_STATUS_DOWN)
LoopIFE.if_adminstatus = Status;
else
if (Status != IF_STATUS_TESTING)
return TDI_INVALID_PARAMETER;
return TDI_SUCCESS;
} else
return TDI_INVALID_PARAMETER;
}
return TDI_INVALID_PARAMETER;
}
//* LoopAddAddr - Dummy loopback add address routine.
//
// Called at init time when we need to initialize ourselves.
//
uint
LoopAddAddr(void *Context, uint Type, IPAddr Address, IPMask Mask, void *Context2)
{
return TRUE;
}
//* LoopDelAddr - Dummy loopback del address routine.
//
// Called at init time when we need to initialize ourselves.
//
uint
LoopDelAddr(void *Context, uint Type, IPAddr Address, IPMask Mask)
{
return TRUE;
}
#pragma BEGIN_INIT
extern int InitNTE(NetTableEntry *);
extern int InitInterface(NetTableEntry *);
#ifdef CHICAGO
#pragma END_INIT
#pragma code_seg("_LTEXT", "LCODE")
#endif
//* LoopGetEList - Get the entity list.
//
// Called at init time to get an entity list. We fill our stuff in and return.
//
// Input: Context - Unused.
// EntityList - Pointer to entity list to be filled in.
// Count - Pointer to number of entries in the list.
//
// Returns Status of attempt to get the info.
//
int
LoopGetEList(void *Context, TDIEntityID *EntityList, uint *Count)
{
uint ECount;
uint MyIFBase;
uint i;
ECount = *Count;
// Walk down the list, looking for existing IF entities, and
// adjust our base instance accordingly.
MyIFBase = 0;
for (i = 0; i < ECount; i++, EntityList++) {
if (EntityList->tei_entity == IF_ENTITY)
MyIFBase = MAX(MyIFBase, EntityList->tei_instance + 1);
}
// EntityList points to the start of where we want to begin filling in.
// Make sure we have enough room.
if ((ECount + 1) > MAX_TDI_ENTITIES)
return FALSE;
// At this point we've figure out our base instance. Save for later use.
LoopInstance = MyIFBase;
// Now fill it in.
EntityList->tei_entity = IF_ENTITY;
EntityList->tei_instance = MyIFBase;
(*Count)++;
return TRUE;
}
#ifdef CHICAGO
#pragma BEGIN_INIT
#endif
//** InitLoopback - Initialize the loopback NTE.
//
// This function initialized the loopback NTE. We set up the the MSS and pointer to
// the various pseudo-link routines, then call InitNTE and return.
//
// Entry: ConfigInfo - Pointer to config. info structure.
//
// Returns: TRUE if we initialized, FALSE if we didn't.
//
NetTableEntry *
InitLoopback(IPConfigInfo *ConfigInfo)
{
LLIPBindInfo ARPInfo;
CTERefillMem();
LoopNTE = CTEAllocMem(sizeof(NetTableEntry));
if (LoopNTE == NULL)
return LoopNTE;
CTEMemSet(LoopNTE, 0, sizeof(NetTableEntry));
LoopNTE->nte_addr = LOOPBACK_ADDR;
LoopNTE->nte_mask = CLASSA_MASK;
LoopNTE->nte_icmpseq = 1;
LoopNTE->nte_flags = NTE_VALID | NTE_ACTIVE | NTE_PRIMARY;
CTEInitLock(&LoopNTE->nte_lock);
CTEInitLock(&LoopInterface.ri_if.if_lock);
LoopNTE->nte_mss = LOOPBACK_MSS;
LoopNTE->nte_if = (Interface *)&LoopInterface;
LoopInterface.ri_if.if_lcontext = LoopNTE;
LoopInterface.ri_if.if_xmit = LoopXmit;
LoopInterface.ri_if.if_transfer = LoopXfer;
LoopInterface.ri_if.if_close = LoopClose;
LoopInterface.ri_if.if_invalidate = LoopInvalidate;
LoopInterface.ri_if.if_qinfo = LoopQInfo;
LoopInterface.ri_if.if_setinfo = LoopSetInfo;
LoopInterface.ri_if.if_getelist = LoopGetEList;
LoopInterface.ri_if.if_addaddr = LoopAddAddr;
LoopInterface.ri_if.if_deladdr = LoopDelAddr;
LoopInterface.ri_if.if_bcast = IP_LOCAL_BCST;
LoopInterface.ri_if.if_speed = 10000000;
LoopInterface.ri_if.if_mtu = LOOPBACK_MSS;
LoopInterface.ri_if.if_llipflags = LIP_COPY_FLAG;
#ifdef _PNP_POWER
LoopInterface.ri_if.if_refcount = 1;
LoopInterface.ri_if.if_pnpcontext = 0;
#endif
ARPInfo.lip_mss = LOOPBACK_MSS + sizeof(IPHeader);
ARPInfo.lip_index = LoopIndex;
ARPInfo.lip_close = LoopClose;
ARPInfo.lip_addaddr = LoopAddAddr;
ARPInfo.lip_deladdr = LoopDelAddr;
ARPInfo.lip_flags = LIP_COPY_FLAG;
LoopIndex = NumIF + 1;
LoopInterface.ri_if.if_index = LoopIndex;
CTEInitEvent(&LoopXmitEvent, LoopXmitRtn);
CTEInitLock(&LoopLock);
LoopIFE.if_index = LoopIndex;
LoopIFE.if_type = IF_TYPE_LOOPBACK;
LoopIFE.if_mtu = ARPInfo.lip_mss;
LoopIFE.if_speed = 10000000;
LoopIFE.if_adminstatus = IF_STATUS_UP;
LoopIFE.if_operstatus = IF_STATUS_UP;
LoopIFE.if_descrlen = sizeof(LoopName);
IFList = (Interface *)&LoopInterface;
NumIF++;
NumNTE++;
if (!InitInterface(LoopNTE))
return NULL;
if (!InitNTE(LoopNTE))
return NULL;
return LoopNTE;
}
#pragma END_INIT